stm32l0xx-hal
stm32l0xx-hal copied to clipboard
Timer::start panics
The Timer::start()
implementation breaks if the ticks
value calculated inside the start function is an exact multiple of (1 << 16)
.
Numerical example
If ticks
is 1 << 16 == 65536
then psc
, which is (ticks - 1) / (1 << 16)
becomes 0
because the division result is ever so slightly less than one and thus trucated.
Now we calculate u16(ticks / u32(psc + 1))
. But since psc
is 0
,psc + 1
is 1
, so ticks / u32(psc + 1)
is still 65536
, which is then cast to a u16
.
But since 65536
is 1 << 16
, it doesn't fit in the u16
range from 0
to 1 << 16 - 1
and we unwrapp an Err(Overflow)
value.
Clock configuration example
Pretty simple actually, the MSI
clock source outputs frequencies that (except for MSI range 0) are all multiple of 1 << 16
.
For example, MSI clock source of range 1 produces a 65536 Hz clock.
When attempting to start a timer with frequency 1 with this clock, the error occurs.
Code example
use cast::{u16, u32, Error};
// simplified version of calculations inside Timer::start()
fn timer_settings(sysclk: u32, frequency: u32) -> Result<(u16, u16), Error> {
let ticks = sysclk / frequency;
println!("{}", ticks % (1 << 16));
let psc = u16((ticks - 1) / (1 << 16))?;
let arr = u16(ticks / u32(psc + 1))?;
Ok((psc, arr))
}
fn main() {
// all of these return Err()
for n in 1..100 {
println!("{n} {:?}", timer_settings((2 << 16) * n, n).unwrap_err());
}
// all of these return Ok()
for n in 1..100 {
println!("{n} {:?}", timer_settings((2 << 16) * n - 1, n).unwrap());
}
}
Possible solution
I'm not 100% sure on this, but I think u16(ticks / u32(psc + 1))
can be changed to u16((ticks - 1) / u32(psc + 1))
, such that both divisions have the same numerator.