Skip to content

Instantly share code, notes, and snippets.

@TomCrypto
Last active July 23, 2019 15:15
Show Gist options
  • Save TomCrypto/29d5f1f29a0a5504dc52a9bf55de6ad0 to your computer and use it in GitHub Desktop.
Save TomCrypto/29d5f1f29a0a5504dc52a9bf55de6ad0 to your computer and use it in GitHub Desktop.
Find optimal UART configuration for a given baud rate on LPC1114
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
@TomCrypto
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment