STM32 USARTv1/USARTv2 LLD BRR calculation incorrect when using USART_CR1_OVER8 Topic is solved
Posted: 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.
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.*/