2
\$\begingroup\$

Trying to understand the USART_BRR oversampling calculation. According to the reference manual, if we know what value we want in the BRR, we can deduce USART_DIV which contains the mantissa and the fraction part but i'm confused between the following examples shown. The upper one determines USARTDIV from USART_BRR while the other the opposite. Say, I want a baud rate of 921600 bps. Meaning, USARTDIV = 8MHz / (8*921600) = 1.085. So wouldn't I have to follow the bottom example given I know what USART_DIV I need to program?

enter image description here But when I do the calculations, the math doesn't work out fine.

USARTDIV = 1.085
Frac = 0.085*8 = 0.68 = d0.7
Mantissa = 1 = 0xA
USART_BRR = 0xA7 = d(1.7)

According to the reference manual, it's 1.125.

enter image description here MCU: STM32F4

Also, the easier way to determine whether 8x sampling results in higher baud rates than 16x is by using Clock / (samplingRate * baud rate). So for 8MHz clock, with 16x oversampling, we can't have a baud rate of 921600 cause of the following?

DIV = 8MHz / (16 * 92600)= 5.39 = ~5 (assume no fraction generator)
Baud = 8MHz / (16 * 5) = 0.1Mps

Whereas with 8x sampling:
DIV = 8e6/ (8 * 92600) = 10.7 = ~11
Baud = 8MHz / (8 * 11) = 90.909 Kbps
\$\endgroup\$
1
  • \$\begingroup\$ Looks like you dropped a 1 in those last two examples. Makes a difference. \$\endgroup\$
    – GB - AE7OO
    May 27, 2020 at 5:03

5 Answers 5

1
\$\begingroup\$

You have an error calculating the fractional and the manual is right.

For 1.085 divisor, integer part is 1. Fractional part is 8×0.085=0.68, but in no way that is 0.7, nearest integer is 1. Therefore the fractional is 1/8 or 0.125. The BRR would be 0x11.

Using 8MHz clock, the 921600 baud rate is not possible at 16x oversampling. With 8x oversampling, the error between the actual vs requested baud rate is over 3.5%, so I would not expect it to work either.

\$\endgroup\$
2
  • \$\begingroup\$ ah, gotcha. Is there any logical reasoning behind multiplying and dividing by 8 the fractional part only? \$\endgroup\$
    – MKD
    May 27, 2020 at 13:23
  • \$\begingroup\$ There is some logic, but it does not matter much which way you end up with identical register value. You can of course calculate 8e6/921600 which rounds to nearest integer 9. But you don't load 9 to the register. Register has 4 bits 0..3 for fraction and the integer part follows from bit 4 onwards, so the value still needs to be 0x11. \$\endgroup\$
    – Justme
    May 27, 2020 at 15:23
1
\$\begingroup\$

Here I will share my knowledge about Uart of STM32F4 hope it helps you and everyone. I will choose OVER8 = 0 use 16 division for easy visualization. Baudrate calculation formula: enter image description here

Numerator = fCK (depending on which UART you choose, fCK will be different because STM32 allocates clocks to UART modules according to APB1 and APB2)

Denominator = 8 x 2 x USARTDIV(OVER8 = 0) = 16 x USARTDIV (1)

So to calculate Baudrate I need to choose USARTDIV.

For example, I have fCK = 42,000,000(42MHz), I need a Baudrate of 115200 bps. Then I will have:

Baudrate = fCK / (16 * USARTDIV) => USARTDIV = fCK / (Baudrate * 16) = 42 Mhz / (115200 * 16) = 22.79

So to have baud rate = 115200, we need to write USARTDIV = 22.78 to the BRR register of STM32.

enter image description here

DIV_Mantissa is the integer part so 22 will be written directly here

DIV_Fration is the decimal part so 0.79 will be written here. However, the computer cannot write the number 0.79 to the DIV_Fraction, we can only update the status 0 or 1 for it. So here the Computer will take 16 * DIV_Mantissa + 16 * 0.79(=12.64 ~= 13 = 1101) => DIV_Fration = 1101

So, Mantissa will be the integer part of USARTDIV

Fration will be 16 x Decimal part(OVER8 = 0) or 8 x Decimal part(OVER8 = 1)

\$\endgroup\$
1
\$\begingroup\$

Actually, BRR is a Q12.4 format fixed point number, which needs one additional operation when OVER8 mode is selected.

OP's question is about STM32F4, but if one inspects STM32F030 reference manual, he/she can find the same logic but represented by a different formula. IMO, that one (F030) is more clear and easier to understand.

To convert uint16_t (Q16.0) to Q12.4, you need to multiply it by 16.

Basically, for OVER16 case, BRR is simply becomes PeripheralClock / BaudRate.

For OVER8 case, you first need to calculate a temporary variable as temp = 2 * PeripheralClock / BaudRate. When writing it into BRR, you need to right-shift its lower 4 bits once.

See the example code:

void setBaudRate(uint32_t baud, bool over8)
{
    const uint32_t pClock = 12000000ull; // Hard-code or call a function to obtain it
    uint32_t usartDiv = (over8) ? (2 * pClock / baud) : (pClock / baud);

    uint32_t reg = USART1->BRR;
    reg &= ~0xffff; // Clear the lower 16 bits
    if (over8) {
        reg |= (usartDiv & 0xfff0) | ((usartDiv & 0xf) >> 1);
    }
    else { // over16
        reg |= usartDiv;
    }
    USART1->BRR = reg;
}
\$\endgroup\$
0
\$\begingroup\$

It's little bit more involved.
First off you have to determine if your attempted USartDiv is valid, meaning over 16.
If you are using 16x sampling for a higher tolerance then Div is Clock/Speed.
If your using a 8x for higher possible speed, Div is 2 x (Clock/Speed).

Lets do those last two examples. (Note: some of specialty modes can not be 8x)
16 x The purpose of the 16 times(Over8=0) over sampling is to allow a greater tolerance for clock deviation at the expense of speed.
8000000/921600 = 8.6 USartDiv
Invalid USartDiv, less than 16.
If it was valid, USartBRR = USartDiv = 0x0009

8x Using an 8 times(Over8=1) oversampling will allow for a greater speed, with less clock tolerance.

2 * (8000000/921600) = d17.2 = d17 = USartDiv = 0x0011

When Over8=1 then the following method is used to determine BRR.
USartBRR [2:0] equals USartDiv[3:0] shifted to the right by 1 bit.
USartBRR [2:0] = USartDiv[3:0] >> 1 = 0x1 >> 1 = 0x0
USartBRR [3] must be clear
USartBRR[15:4] = USartDiv[15:4] = 0x001
So we get BRR = 0x0010 = b0000'0000'0001'0000

I'm pretty sure I've got that right.

\$\endgroup\$
0
\$\begingroup\$

In my opinion the reference manual is making things a bit harder than they really are.

8 times oversampling will always lead to a higher possible baudrate. Exactly two times higher.

The limit is that the mantissa part needs to be at least 1. So you can calculate the maximum possible baudrate you can achieve given a input frequency:

$$ B_{max}~\text{in baud} = \frac{\text{input frequency in Hz}}{\text{oversampling}} $$

So for 8 MHz input frequency you can get at a maximum 1 MBaud for oversampling by 8 and 500 kBaud for oversampling by 16.


If you use 16 times oversampling, it all turns out very nicely with the register value you want to write being equal to the input frequency divided by the baudrate.

BRR = UART input frequency in Hz / baudrate in baud

The result of this calculation has to be higher than 16 for the UART to work. So that at least a 1 ends up in the mantissa part of the BRR register.


The "trouble" starts if you use oversampling by 8. What I do is use the same approach:

BRR8 = UART input frequency in Hz / baudrate in baud

But basically now the most significant bit of the fractional part is disabled and the mantissa value has to be shifted by one bit:

BRR = ((BRR8 & !(0x3)) << 1) | (BRR8 & 0x3)

The resulting value of BRR must be higher as 16 as well for the UART to work.


So for 921600 baud with 8 MHz and oversampling by 8:

BRR8 = 8e6 / 921600 = 8,6 so either 8 for 1000000 baud or 9 for 888888 baud, usually normal rounding will result in a closer result, but sometimes it is easier to switch to something different 1000000 baud for example is a usual value as well.

But let's go with: BRR8 = 9 = 0b1001

Then BRR = ((0b1001& !(0x3)) << 1) | (0b1001 & 0x3) = (0b1000 << 1) | (0b001) = 0b10000 | 0b001 = 0b10001 = 17

\$\endgroup\$
6
  • \$\begingroup\$ Shouldn't you be shifting mantissa value to the left by 4 regardless of whether it's over8 or not? the 3rd fractional bit mustn't be touched in case of over8 as I see in the RM. So, not sure if I fully understand your bit manipulation expression. Also, did you intentionally skip 8 in the denominator from BRR8 = 8e6 / 921600 ? \$\endgroup\$
    – MKD
    May 27, 2020 at 18:50
  • \$\begingroup\$ @kol no shifting is required in case of the Over16, the bits will fall into place like they need to be. Only in case of Over8 (where you only have 3 bits fractional) you have to shift the mantissa part by one bit (to skip over the 4th fractional bit, which must be 0 in this case). You don't need to divide by 8 for BRR8 because you want to know the fractional bits as well, which you would loose if you divide by 8. \$\endgroup\$
    – Arsenal
    May 28, 2020 at 8:55
  • \$\begingroup\$ I asked about dividing by 8 since the equation in the RM includes USARTDIV which you don't seem to include (unless i'm missing the subtlety): imgur.com/a/xrFLu4A. And here's why I mentioned about shifting the mantissa by 4: imgur.com/a/MQmlAfS. Plus the value to be written in the BRR for your example should be 1.125 \$\endgroup\$
    – MKD
    May 28, 2020 at 19:24
  • \$\begingroup\$ Well the difference is, that I calculate the register result directly and not the thing which you have to convert to the register value. If you look at my result it ends up as "1.125" you have a 1 bit in the mantissa and a 1 bit in the fractional part. My results are identical to those provided by ST in their table. I only need to shift the mantissa by one bit in case of over8 because of the fourth bit in the fractional part, which isn't used in over8. I think it is a design flaw, that the fractional bits change in value depending on over8. With over8 the LSB of the fractional part is 0.125 \$\endgroup\$
    – Arsenal
    May 29, 2020 at 6:03
  • \$\begingroup\$ @kol with over16 the LSB is 0.0625. The way I do my calculations prevents my brain from melting while thinking about it. \$\endgroup\$
    – Arsenal
    May 29, 2020 at 6:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.