Last active
July 23, 2019 15:15
-
-
Save TomCrypto/29d5f1f29a0a5504dc52a9bf55de6ad0 to your computer and use it in GitHub Desktop.
Find optimal UART configuration for a given baud rate on LPC1114
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
FRACTIONAL_DIVIDER_RANGE = [ | |
[ 0, 1], [ 1, 2], [ 1, 3], [ 2, 3], | |
[ 1, 4], [ 2, 4], [ 3, 4], [ 1, 5], | |
[ 2, 5], [ 3, 5], [ 4, 5], [ 1, 6], | |
[ 2, 6], [ 3, 6], [ 4, 6], [ 5, 6], | |
[ 1, 7], [ 2, 7], [ 3, 7], [ 4, 7], | |
[ 5, 7], [ 6, 7], [ 1, 8], [ 2, 8], | |
[ 3, 8], [ 4, 8], [ 5, 8], [ 6, 8], | |
[ 7, 8], [ 1, 9], [ 2, 9], [ 3, 9], | |
[ 4, 9], [ 5, 9], [ 6, 9], [ 7, 9], | |
[ 8, 9], [ 1, 10], [ 2, 10], [ 3, 10], | |
[ 4, 10], [ 5, 10], [ 6, 10], [ 7, 10], | |
[ 8, 10], [ 9, 10], [ 1, 11], [ 2, 11], | |
[ 3, 11], [ 4, 11], [ 5, 11], [ 6, 11], | |
[ 7, 11], [ 8, 11], [ 9, 11], [10, 11], | |
[ 1, 12], [ 2, 12], [ 3, 12], [ 4, 12], | |
[ 5, 12], [ 6, 12], [ 7, 12], [ 8, 12], | |
[ 9, 12], [10, 12], [11, 12], [ 1, 13], | |
[ 2, 13], [ 3, 13], [ 4, 13], [ 5, 13], | |
[ 6, 13], [ 7, 13], [ 8, 13], [ 9, 13], | |
[10, 13], [11, 13], [12, 13], [ 1, 14], | |
[ 2, 14], [ 3, 14], [ 4, 14], [ 5, 14], | |
[ 6, 14], [ 7, 14], [ 8, 14], [ 9, 14], | |
[10, 14], [11, 14], [12, 14], [13, 14], | |
[ 1, 15], [ 2, 15], [ 3, 15], [ 4, 15], | |
[ 5, 15], [ 6, 15], [ 7, 15], [ 8, 15], | |
[ 9, 15], [10, 15], [11, 15], [12, 15], | |
[13, 15], [14, 15] | |
].freeze | |
# Given the UART_PCLK and the desired baud rate, this method will return | |
# the best (DL, DIVADDVAL, MULVAL) register values that can be achieved. | |
def calculate_optimal_registers(uart_pclk, baud_rate) | |
optimal = [0, 0, 0] | |
score = uart_pclk | |
FRACTIONAL_DIVIDER_RANGE.each do |divaddval, mulval| | |
break if score.zero? # perfect timings achieved | |
fractional = 1.0 + divaddval.to_f / mulval.to_f | |
dl = (uart_pclk.to_f / (16 * baud_rate * fractional)).round | |
pclk = 16 * baud_rate * dl * fractional # "effective" clock | |
next unless dl.between?(1, 65_535) | |
new_score = (pclk - uart_pclk).abs | |
if new_score < score | |
score = new_score | |
optimal = [dl, divaddval, mulval] | |
end | |
end | |
raise 'baud rate cannot be generated with this UART_PCLK' if optimal[0].zero? | |
optimal | |
end | |
def display_optimal_registers(uart_pclk, baud_rate) | |
puts "Register settings for #{baud_rate} baud @ #{uart_pclk} Hz:" | |
dl, divaddval, mulval = calculate_optimal_registers(uart_pclk, baud_rate) | |
# bonus: calculate the error as a relative difference to the target baud rate | |
real_baud_rate = uart_pclk / (16 * dl * (1.0 + divaddval.to_f / mulval.to_f)) | |
puts "\tDLL = #{dl % 256}" | |
puts "\tDLM = #{dl / 256}" | |
puts "\tDIVADDVAL = #{divaddval}" | |
puts "\tMULVAL = #{mulval}" | |
puts "\terror = #{(real_baud_rate - baud_rate) / baud_rate * 100}%" | |
rescue StandardError | |
puts "\t(not achievable)" | |
end | |
display_optimal_registers 12_000_000, 9_600 # suitable for an LPC1114 out of reset | |
display_optimal_registers 48_000_000, 115_200 | |
display_optimal_registers 11_393_192, 81_928 | |
display_optimal_registers 12_000_000, 2 # this baud rate is far too low to be generated | |
display_optimal_registers 1_000_000, 2 # unless you divide the UART_PCLK with UARTCLKDIV | |
display_optimal_registers 12_000_000, 4_608_000 # much too fast for this little microcontroller | |
=begin | |
This program will generate the following output: | |
Register settings for 9600 baud @ 12000000 Hz: | |
DLL = 71 | |
DLM = 0 | |
DIVADDVAL = 1 | |
MULVAL = 10 | |
error = 0.03201024327783368% | |
Register settings for 115200 baud @ 48000000 Hz: | |
DLL = 23 | |
DLM = 0 | |
DIVADDVAL = 2 | |
MULVAL = 15 | |
error = -0.09590792838874526% | |
Register settings for 81928 baud @ 11393192 Hz: | |
DLL = 5 | |
DLM = 0 | |
DIVADDVAL = 11 | |
MULVAL = 15 | |
error = 0.28615651266025977% | |
Register settings for 2 baud @ 12000000 Hz: | |
(not achievable) | |
Register settings for 2 baud @ 1000000 Hz: | |
DLL = 18 | |
DLM = 122 | |
DIVADDVAL = 0 | |
MULVAL = 1 | |
error = 0.0% | |
Register settings for 4608000 baud @ 12000000 Hz: | |
(not achievable) | |
=end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Also see UM10398 13.5.15 (the user manual) for more details. I found this little program useful to calculate appropriate UART register values to configure it to a given baud rate given its peripheral clock. Unlike the algorithm provided in the user manual, this one just brute-forces all possible configurations and finds the one that best approximates the target baud rate. So it's not really suitable for being implemented on-chip, but usually you know the peripheral clock in advance anyway so it's easy to just hardcode one or more available baud rate configurations.
Note that the program will return any (valid) best approximation even if it is unusably bad; it only raises an error when the baud rate cannot reasonably be generated (too high a baud rate = peripheral clock is not fast enough to sample the signal, too low a baud rate = the peripheral clock can't be divided enough). A relative baud rate error is calculated to help decide if a configuration is viable, anything less than 2% will do for most baud rates.
This program can probably be reused or adapted for other similar UART controllers from the LPC family or elsewhere.