raspberrypi / pico-sdk

BSD 3-Clause "New" or "Revised" License
3.64k stars 903 forks source link

Faulty UART baudrate divisor formula #1774

Closed KisImre closed 1 month ago

KisImre commented 1 month ago

The fractional part of the UART baudrate divisor can overflow for certain baudrates.

baudrate=57861 ibrd=135 fbrd=1
baudrate=57868 ibrd=135 fbrd=0
baudrate=57871 ibrd=134 fbrd=64 <----
baudrate=57874 ibrd=134 fbrd=63
baudrate=57881 ibrd=134 fbrd=62

The used formula results in a fractional divisor value of 64 which gets truncated once it's written into the register, so the effective fractional value will be zero.

The issue is in this line. baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2 The recommended +0.5 correction is only added to the fractional part but the overflowing carry bit is not added to the integer part. Adding 1 and dividing by 2 before splitting into integer and fractional parts would solve the issue.

I've tested the issue on a Raspberry Pi Pico board using the hello_uart example and changing the baudrate in line 15. I've measured the actual baudrate (with ~10Hz precision) using a logic analyzer and I got the following results:

baudrate=57861 -> 57870
baudrate=57868 -> 57870
baudrate=57871 -> 58300 <----
baudrate=57874 -> 57870
baudrate=57881 -> 57870

The issue occurs for each baudrate where the integer divisor is about to change, or more precisely, where (baud_rate_div & 0x7f) == 0x7f. The baudrate error will be around 1 / ibrd.

Note: Originally I found this issue in rp-hal where they've pointed out that their implementation was inspired by pico-sdk so it might by also affected.

kilograham commented 1 month ago

fixed thank you