STM32 USARTv1/USARTv2 LLD BRR calculation incorrect when using USART_CR1_OVER8 Topic is solved

Report here problems in any of ChibiOS components. This forum is NOT for support.
apmorton
Posts: 32
Joined: Fri Sep 29, 2017 10:26 am
Been thanked: 15 times

STM32 USARTv1/USARTv2 LLD BRR calculation incorrect when using USART_CR1_OVER8  Topic is solved

Postby apmorton » Fri May 11, 2018 9:37 pm

The BRR register is a fixed point decimal, with the lower 4 bits being reserved for the fractional portion.

When USART_CR1_OVER8 = 0, the fraction portion uses the full 4 bits.
When USART_CR1_OVER8 = 1, the fraction portion uses only the lower 3 bits. However, the field is still 4 bits wide.

One correct way to calculate the BRR register when using USART_CR1_OVER8 is to double the Mantissa (upper 12 bits), but leave the Fraction as is (lower 3 bits). (The reference manual doubles the entire calculation, and then shifts the lower 4 bits to the right by one).

The reference manuals for USARTv1 and USARTv2 parts describe the calculation differently, but the result is the same.

The patch below is for the USARTv1 driver in ChibiOS 17.6.x (and may not apply cleanly to trunk), but I have confirmed the issue is still present in trunk.
Additionally, the patch needs to be applied to the serial drivers as well.

Code: Select all

 os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.c b/os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.c
index 54c1632b3..85a535160 100644
--- a/os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.c
+++ b/os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.c
@@ -196,6 +196,7 @@ static void usart_stop(UARTDriver *uartp) {
  * @param[in] uartp     pointer to the @p UARTDriver object
  */
 static void usart_start(UARTDriver *uartp) {
+  uint32_t fck;
   uint16_t cr1;
   USART_TypeDef *u = uartp->usart;
 
@@ -208,9 +209,17 @@ static void usart_start(UARTDriver *uartp) {
 #else
   if (uartp->usart == USART1)
 #endif
-    u->BRR = STM32_PCLK2 / uartp->config->speed;
+    fck = STM32_PCLK2 / uartp->config->speed;
   else
-    u->BRR = STM32_PCLK1 / uartp->config->speed;
+    fck = STM32_PCLK1 / uartp->config->speed;
+
+  /* Correcting USARTDIV when oversampling by 8 instead of 16.
+     Fraction is still 4 bits wide, but only lower 3 bits used.
+     Mantissa is doubled, but Fraction is left the same. */
+  if (uartp->config->cr1 & USART_CR1_OVER8)
+    fck = ((fck & ~7) * 2) | (fck & 7);
+
+  u->BRR = fck;
 
   /* Resetting eventual pending status flags.*/
   (void)u->SR;  /* SR reset step 1.*/


apmorton
Posts: 32
Joined: Fri Sep 29, 2017 10:26 am
Been thanked: 15 times

Re: STM32 USARTv1/USARTv2 LLD BRR calculation incorrect when using USART_CR1_OVER8

Postby apmorton » Fri May 11, 2018 9:48 pm

A mostly unrelated idea that came to mind:

it would be nice to have the driver perform % error calculations as a debug assert/check and throw an error if they are outside of some acceptable range.

I don't think this should be super difficult to do, as its basically running the calculations in reverse and comparing to the originally given baud rate with % error calculation.

Code: Select all

err = ((abs(actual - expected) * 100) / expected)

User avatar
Giovanni
Site Admin
Posts: 11224
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 401 times
Been thanked: 333 times
Contact:

Re: STM32 USARTv1/USARTv2 LLD BRR calculation incorrect when using USART_CR1_OVER8

Postby Giovanni » Sat May 26, 2018 5:38 pm

Hi,

Fixed bug as #951. All branches, both USARTv1 and USARTv2, both Serial and UART drivers.

Giovanni


Return to “Bug Reports”

Who is online

Users browsing this forum: No registered users and 3 guests