I wrote some code to link two 16 bit timers on an STM32L0 in order to form a 32 bit upcounting timer that can be used to measure relative time. The goal is to use this abstraction in order to implement the Monotonic trait for the RTIC scheduler.
Here's the current code, needs more testing & should be made more general (there are other timer pairs that allow linking), but seems to work already:
/// Two linked 16 bit timers that form a 32 bit upcounter.
pub struct LinkedTimer {
/// Timer in master mode
tim2: TIM2,
/// Timer in slave mode
tim3: TIM3,
}
impl LinkedTimer {
/// Create a new `LinkedTimer` with TIM2 as master and TIM3 as slave.
pub fn new(tim2: TIM2, tim3: TIM3, rcc: &mut Rcc) -> Self {
// Enable timers
rcc.rb.apb1enr.modify(|_, w| w.tim2en().set_bit());
rcc.rb.apb1enr.modify(|_, w| w.tim3en().set_bit());
// Reset timers
rcc.rb.apb1rstr.modify(|_, w| w.tim2rst().set_bit());
rcc.rb.apb1rstr.modify(|_, w| w.tim2rst().clear_bit());
rcc.rb.apb1rstr.modify(|_, w| w.tim3rst().set_bit());
rcc.rb.apb1rstr.modify(|_, w| w.tim3rst().clear_bit());
// Enable counter
tim2.cr1.modify(|_, w| w.cen().set_bit());
tim3.cr1.modify(|_, w| w.cen().set_bit());
// In the MMS (Master Mode Selection) register, set the master mode so
// that a rising edge is output on the trigger output TRGO every time
// an update event is generated.
tim2.cr2.modify(|_, w| w.mms().variant(tim2::cr2::MMS_A::UPDATE));
// In the SMCR (Slave Mode Control Register), select the internal
// trigger 0 (ITR0) as trigger source (TS).
//
// See table 76 ("TIM2/TIM3 internal trigger connection") in the
// reference manual RM0377.
tim3.smcr.modify(|_, w| w.ts().variant(tim2::smcr::TS_A::ITR0));
// Set the SMS (Slave Mode Selection) register to "external clock mode 1",
// where the rising edges of the selected trigger (TRGI) clock the counter.
tim3.smcr.modify(|_, w| w.sms().variant(tim2::smcr::SMS_A::EXT_CLOCK_MODE));
Self { tim2, tim3 }
}
fn master(&self) -> &TIM2 {
&self.tim2
}
fn slave(&self) -> &TIM3 {
&self.tim3
}
/// Return the current 32 bit counter value.
pub fn get_counter(&self) -> u32 {
let lsb = self.master().cnt.read().cnt().bits() as u32;
let msb = self.slave().cnt.read().cnt().bits() as u32;
(msb << 16) | lsb
}
}
Should this go into the stm32l0-hal? If yes, how should it be called? Should there be a trait involved that's implemented for all timers that allow linking? Or a trait for upcounting timers that cannot be reset?
If yes, should there also be logic for detecting overflows in the HAL, or should that be part of user code?
Right now there's no API in embedded-hal that matches this pattern (see also this discussion). It also cannot be combined with CountDown because resetting the timer (on start) also resets the counter.
I wrote some code to link two 16 bit timers on an STM32L0 in order to form a 32 bit upcounting timer that can be used to measure relative time. The goal is to use this abstraction in order to implement the
Monotonic
trait for the RTIC scheduler.Here's the current code, needs more testing & should be made more general (there are other timer pairs that allow linking), but seems to work already:
When polling the value with 10 Hz:
Should this go into the stm32l0-hal? If yes, how should it be called? Should there be a trait involved that's implemented for all timers that allow linking? Or a trait for upcounting timers that cannot be reset?
If yes, should there also be logic for detecting overflows in the HAL, or should that be part of user code?
Right now there's no API in embedded-hal that matches this pattern (see also this discussion). It also cannot be combined with
CountDown
because resetting the timer (on start) also resets the counter.CC @astro @hannobraun @rnestler