Skip to content

Commit

Permalink
stm32/common: Implement handling for setting the baud rate correctly …
Browse files Browse the repository at this point in the history
…when in 8x oversampling mode
  • Loading branch information
dragonmux committed Aug 11, 2024
1 parent 11f9908 commit aa4878d
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 1 deletion.
3 changes: 2 additions & 1 deletion include/libopencm3/stm32/common/usart_common_f24.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,11 @@ specific memorymap.h header before including this header file.*/
/* ONEBIT: One sample bit method enable */
#define USART_CR3_ONEBIT (1 << 11)

#define USART_BRR_UPPER_MASK (0x0000fff0U)
#define USART_BRR_LOWER_MASK (0x0000000fU)
#endif
/** @cond */
#else
#warning "usart_common_f24.h should not be included directly, only via usart.h"
#endif
/** @endcond */

3 changes: 3 additions & 0 deletions include/libopencm3/stm32/common/usart_common_v2.h
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,9 @@

/**@}*/

#define USART_BRR_UPPER_MASK (0x0000fff0U)
#define USART_BRR_LOWER_MASK (0x0000000fU)

/** @defgroup usart_gtpr_values USART_GTPR Values
* @ingroup usart_defines
* @{
Expand Down
20 changes: 20 additions & 0 deletions lib/stm32/common/usart_common_all.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,27 @@ void usart_set_baudrate(uint32_t usart, uint32_t baud)
}
#endif

#ifdef USART_CR1_OVER8
if (USART_CR1(usart) & USART_CR1_OVER8) {
/*
* When using 8x oversampling instead of 16x, the calculation works slightly differently.
* We do the same main calculation as for 16x, but with the clock rate effectively doubled.
* This keeps accuracy up and gives us the best possible divider. However, to set BRR,
* we have to do some shenanigans - specifically, USARTDIV[15:4] = BRR[15:4], but
* UARTDIV[3:0] = BRR[2:0] << 1 and we are required to keep BRR[3] as 0.
*/
const uint32_t divider = ((2 * clock) + (baud / 2)) / baud;
USART_BRR(usart) = (divider & USART_BRR_UPPER_MASK) | ((divider & USART_BRR_LOWER_MASK) >> 1U);
} else {
/*
* Modified version of the formula from the datasheets that tries to improve
* the accuracy of the calculated dividers by introducing a rounding factor
*/
USART_BRR(usart) = (clock + baud / 2) / baud;
}
#else
USART_BRR(usart) = (clock + baud / 2) / baud;
#endif
}

/*---------------------------------------------------------------------------*/
Expand Down

0 comments on commit aa4878d

Please sign in to comment.