Skip to content

Commit 13455b4

Browse files
bors[bot]James Munns
andauthored
Merge #224
224: Correct Serial Baudrate calculation r=therealprof a=jamesmunns I think the baudrate calculation before was... ~~really wrong~~ slightly misunderstood. As far as I can tell, this new method is correct according to the stm32F411 datasheet, I'm not sure if the calculation is consistent across all stm32f4xx devices. This was validated with an stm32f411, a pclk2 of 96MHz, and a baudrate of 12mbps. Co-authored-by: James Munns <james.munns@ferrous-systems.com>
2 parents e809257 + 4bb360a commit 13455b4

File tree

1 file changed

+56
-3
lines changed

1 file changed

+56
-3
lines changed

src/serial.rs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,10 +1424,61 @@ macro_rules! halUsartImpl {
14241424
bb::set(&rcc.$apbXenr, $rcc_bit);
14251425
}
14261426

1427+
let pclk_freq = clocks.$pclkX().0;
1428+
let baud = config.baudrate.0;
1429+
1430+
// The frequency to calculate USARTDIV is this:
1431+
//
1432+
// (Taken from STM32F411xC/E Reference Manual,
1433+
// Section 19.3.4, Equation 1)
1434+
//
1435+
// 16 bit oversample: OVER8 = 0
1436+
// 8 bit oversample: OVER8 = 1
1437+
//
1438+
// USARTDIV = (pclk)
1439+
// ------------------------
1440+
// 8 x (2 - OVER8) x (baud)
1441+
//
1442+
// BUT, the USARTDIV has 4 "fractional" bits, which effectively
1443+
// means that we need to "correct" the equation as follows:
1444+
//
1445+
// USARTDIV = (pclk) * 16
1446+
// ------------------------
1447+
// 8 x (2 - OVER8) x (baud)
1448+
//
1449+
// When OVER8 is enabled, we can only use the lowest three
1450+
// fractional bits, so we'll need to shift those last four bits
1451+
// right one bit
1452+
14271453
// Calculate correct baudrate divisor on the fly
1428-
let div = (clocks.$pclkX().0 + config.baudrate.0 / 2)
1429-
/ config.baudrate.0;
1430-
usart.brr.write(|w| unsafe { w.bits(div) });
1454+
let (over8, div) = if (pclk_freq / 16) >= baud {
1455+
// We have the ability to oversample to 16 bits, take
1456+
// advantage of it.
1457+
//
1458+
// We also add `baud / 2` to the `pclk_freq` to ensure
1459+
// rounding of values to the closest scale, rather than the
1460+
// floored behavior of normal integer division.
1461+
let div = (pclk_freq + (baud / 2)) / baud;
1462+
(false, div)
1463+
} else if (pclk_freq / 8) >= baud {
1464+
// We are close enough to pclk where we can only
1465+
// oversample 8.
1466+
//
1467+
// See note above regarding `baud` and rounding.
1468+
let div = ((pclk_freq * 2) + (baud / 2)) / baud;
1469+
1470+
// Ensure the the fractional bits (only 3) are
1471+
// right-aligned.
1472+
let frac = (div & 0xF);
1473+
let div = (div & !0xF) | (frac >> 1);
1474+
(true, div)
1475+
} else {
1476+
return Err(config::InvalidConfig)
1477+
};
1478+
1479+
usart.brr.write(|w| unsafe {
1480+
w.bits(div)
1481+
});
14311482

14321483
// Reset other registers to disable advanced USART features
14331484
usart.cr2.reset();
@@ -1438,6 +1489,8 @@ macro_rules! halUsartImpl {
14381489
usart.cr1.write(|w| {
14391490
w.ue()
14401491
.set_bit()
1492+
.over8()
1493+
.bit(over8)
14411494
.te()
14421495
.set_bit()
14431496
.re()

0 commit comments

Comments
 (0)