AnsweredAssumed Answered

Bug: UART BRR calculate error when oversampling by 8. And Poor algorithm

Question asked by zhang.jianjun on Apr 25, 2015
This error lasts a long long time, and maybe still exists other version of stdperiph or Cube.
In STM32Cube_FW_F4_V1.5.0 the error is in this line of stm32f4xx_hal_uart.h:
#define UART_BRR_SAMPLING8(_PCLK_, _BAUD_)             ((UART_DIVMANT_SAMPLING8((_PCLK_), (_BAUD_)) << 4)|(UART_DIVFRAQ_SAMPLING8((_PCLK_), (_BAUD_)) & 0x0F))
should be:
#define UART_BRR_SAMPLING8(_PCLK_, _BAUD_)             ((UART_DIVMANT_SAMPLING8((_PCLK_), (_BAUD_)) << 4)|(UART_DIVFRAQ_SAMPLING8((_PCLK_), (_BAUD_)) & 0x07))

Can I complain about the too complex algorithm?
When oversampling by 16, the divide rate is PCLK/(BAUD*16). We have 4 bit fraction in BRR register, then:
BRR = ((PCLK/(BAUD*16))<<4 = PCLK/BAUD. Isn't that simple?
In most case when PCLK/BAUD>100, you can ignore the truncation error.
Or you can use use the rounding to be more accurate: BRR = (PCLK+BAUD/2)/BAUD

Now we can replace UART_BRR_SAMPLING16 macro with:
#define UART_BRR_SAMPLING16(_PCLK_, _BAUD_)     (((_PCLK_)+(_BAUD_)/2)/_BAUD_)

When oversampling by 8, the divide rate is PCLK/(BAUD*8).
In manual: "When OVER8=1, the DIV_Fraction3 bit is not considered and must be kept cleared." means BRR register has 3 bit fraction, and 1 bit blank.
Then BBR=(PCLK/(BAUD*8))<<3 = PCLK/BAUD, same result except that one blank bit is inserted in bit3.
Following inline function can replace the UART_BRR_SAMPLING8 Macro:

__INLINE static uint16_t UART_BRR_SAMPLING8(uint32_t _PCLK_, uint32_t _BAUD_)
    uint16_t Div = (_PCLK_ + _BAUD_/2)/_BAUD_;    //result in 3 bit fraction, 15 bit Mantissa
    //When OVER8=1, the DIV_Fraction3 bit is not considered and must be kept cleared.
    uint16_t DIV_Mantissa = (Div & ~0x7)<<1;
    uint16_t DIV_Fraction = Div & 0x07    
    return (DIV_Mantissa | DIV_Fraction);

Variable DIV_Mantissa and DIV_Fraction are used for more clear.

my git commit: