Skip to content

Commit

Permalink
STM32: add support for LPTIM time driver
Browse files Browse the repository at this point in the history
  • Loading branch information
chrenderle committed Nov 7, 2024
1 parent 05d3623 commit ed50f8a
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 5 deletions.
2 changes: 2 additions & 0 deletions embassy-stm32/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ time-driver-tim22 = ["_time-driver"]
time-driver-tim23 = ["_time-driver"]
## Use TIM24 as time driver
time-driver-tim24 = ["_time-driver"]
## Use LPTIM1 as time driver
time-driver-lptim1 = ["_time-driver"]


#! ## Analog Switch Pins (Pxy_C) on STM32H7 series
Expand Down
3 changes: 2 additions & 1 deletion embassy-stm32/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ fn main() {
Some("tim22") => "TIM22",
Some("tim23") => "TIM23",
Some("tim24") => "TIM24",
Some("lptim1") => "LPTIM1",
Some("any") => {
// Order of TIM candidators:
// 1. 2CH -> 2CH_CMP -> GP16 -> GP32 -> ADV
Expand All @@ -236,7 +237,7 @@ fn main() {
}
for tim in [
"tim1", "tim2", "tim3", "tim4", "tim5", "tim8", "tim9", "tim12", "tim15", "tim20", "tim21", "tim22", "tim23",
"tim24",
"tim24", "lptim1",
] {
cfgs.declare(format!("time_driver_{}", tim));
}
Expand Down
2 changes: 2 additions & 0 deletions embassy-stm32/src/lptim/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pin_trait!(Channel2Pin, BasicInstance);

pub(crate) trait SealedInstance: RccPeripheral {
fn regs() -> crate::pac::lptim::Lptim;
type Interrupt: crate::interrupt::typelevel::Interrupt;
}
pub(crate) trait SealedBasicInstance: RccPeripheral {}

Expand All @@ -34,6 +35,7 @@ foreach_interrupt! {
fn regs() -> crate::pac::lptim::Lptim {
crate::pac::$inst
}
type Interrupt = crate::interrupt::typelevel::$irq;
}
impl SealedBasicInstance for crate::peripherals::$inst {
}
Expand Down
122 changes: 118 additions & 4 deletions embassy-stm32/src/time_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ use critical_section::CriticalSection;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embassy_time_driver::{AlarmHandle, Driver, TICK_HZ};
use stm32_metapac::lptim;
use stm32_metapac::lptim::regs::{IcrAdv, IsrAdv};
use stm32_metapac::timer::{regs, TimGp16};

use crate::interrupt::typelevel::Interrupt;
use crate::lptim::SealedInstance;
use crate::pac::timer::vals;
use crate::rcc::{self, SealedRccPeripheral};
#[cfg(feature = "low-power")]
Expand All @@ -30,7 +33,7 @@ use crate::{interrupt, peripherals};
// CC1, CC2, CC3, and CC4, so it can provide ALARM_COUNT = 3.

cfg_if::cfg_if! {
if #[cfg(any(time_driver_tim9, time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22))] {
if #[cfg(any(time_driver_tim9, time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22, time_driver_lptim1))] {
const ALARM_COUNT: usize = 1;
} else {
const ALARM_COUNT: usize = 3;
Expand Down Expand Up @@ -65,6 +68,8 @@ type T = peripherals::TIM22;
type T = peripherals::TIM23;
#[cfg(time_driver_tim24)]
type T = peripherals::TIM24;
#[cfg(time_driver_lptim1)]
type T = peripherals::LPTIM1;

foreach_interrupt! {
(TIM1, timer, $block:ident, CC, $irq:ident) => {
Expand Down Expand Up @@ -203,12 +208,26 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(LPTIM1, lptim, $block:ident, GLOBAL, $irq:ident) => {
#[cfg(time_driver_lptim1)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
}

#[cfg(not(time_driver_lptim1))]
fn regs_gp16() -> TimGp16 {
unsafe { TimGp16::from_ptr(T::regs()) }
}

#[cfg(time_driver_lptim1)]
fn regs_gp16() -> stm32_metapac::lptim::Lptim {
T::regs()
}

// Clock timekeeping works with something we call "periods", which are time intervals
// of 2^15 ticks. The Clock counter value is 16 bits, so one "overflow cycle" is 2 periods.
//
Expand Down Expand Up @@ -273,6 +292,82 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
});

impl RtcDriver {
#[cfg(time_driver_lptim1)]
fn init(&'static self, cs: critical_section::CriticalSection) {
let r = T::regs();

rcc::enable_and_reset_with_cs::<T>(cs);

<T as SealedInstance>::Interrupt::unpend();
unsafe {
<T as SealedInstance>::Interrupt::enable();
};

let timer_freq = T::frequency();

// disable timer to write to CFGR register
r.cr().modify(|w| w.set_enable(false));
// set counter to 0
r.cnt().write(|w| w.set_cnt(0));

// calculate the prescaler value
let prescaler = if timer_freq.0 < TICK_HZ as u32 {
panic!("lptim1 (= {}) is clocked too slow for desired TICK_HZ (= {})", timer_freq, TICK_HZ);
} else if timer_freq.0 % TICK_HZ as u32 != 0 {
panic!("frequency of lptim1 (= {}) must be a multiple of TICK_HZ (= {})", timer_freq, TICK_HZ);
} else {
use crate::pac::lptim::vals::Presc;
match timer_freq.0 / TICK_HZ as u32 {
1 => Presc::DIV1,
2 => Presc::DIV2,
4 => Presc::DIV4,
8 => Presc::DIV8,
16 => Presc::DIV16,
32 => Presc::DIV32,
64 => Presc::DIV64,
128 => Presc::DIV128,
_ => panic!("no valid prescaler value found for lptim1 (= {}) and TICK_HZ (= {})", timer_freq.0, TICK_HZ),
}
};
// set the prescaler
r.cfgr().write(|w| w.set_presc(prescaler));

debug!("timer_freq: {}", timer_freq);


// TODO: check what the URS stuff is for



// enable timer to write do DIER
r.cr().modify(|w| w.set_enable(true));
for _ in 0..10 {

}
info!("dier: {}", r.dier().read().0);
info!("isr: {}", r.isr().read().0);
r.arr().write(|w| w.set_arr(u16::MAX));
while r.isr().read().arrok() == false {}
// Mid-way point
r.ccr(0).write(|w| w.set_ccr(0x8000));
// unpend interrupts
r.icr().write(|w| {
w.set_uecf(true);
w.set_cccf(0, true);
w.set_dierokcf(true);
});
// Enable overflow and half-overflow interrupts
r.dier().write(|w| {
w.set_ueie(true);
w.set_ccie(0, true);
});
while r.isr().read().dierok() == false {}

// start continuous mode
r.cr().modify(|w| w.set_cntstrt(true));
}

#[cfg(not(time_driver_lptim1))]
fn init(&'static self, cs: critical_section::CriticalSection) {
let r = regs_gp16();

Expand Down Expand Up @@ -317,26 +412,39 @@ impl RtcDriver {

// XXX: reduce the size of this critical section ?
critical_section::with(|cs| {
#[cfg(not(time_driver_lptim1))]
let sr = r.sr().read();
#[cfg(time_driver_lptim1)]
let isr = r.isr().read();

let dier = r.dier().read();

// Clear all interrupt flags. Bits in SR are "write 0 to clear", so write the bitwise NOT.
// Other approaches such as writing all zeros, or RMWing won't work, they can
// miss interrupts.
#[cfg(not(time_driver_lptim1))]
r.sr().write_value(regs::SrGp16(!sr.0));

#[cfg(time_driver_lptim1)]
r.icr().write_value(IcrAdv(isr.0));

// Overflow
#[cfg(not(time_driver_lptim1))]
if sr.uif() {
self.next_period();
}
#[cfg(time_driver_lptim1)]
if isr.up() {
self.next_period();
}

// Half overflow
if sr.ccif(0) {
if isr.ccif(0) {
self.next_period();
}

for n in 0..ALARM_COUNT {
if sr.ccif(n + 1) && dier.ccie(n + 1) {
if isr.ccif(n + 1) && dier.ccie(n + 1) {
self.trigger_alarm(n, cs);
}
}
Expand Down Expand Up @@ -517,14 +625,17 @@ impl RtcDriver {

impl Driver for RtcDriver {
fn now(&self) -> u64 {
#[cfg(not(time_driver_lptim1))]
let r = regs_gp16();
#[cfg(time_driver_lptim1)]
let r = T::regs();

let period = self.period.load(Ordering::Relaxed);
compiler_fence(Ordering::Acquire);
let counter = r.cnt().read().cnt();
calc_now(period, counter)
}

unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
critical_section::with(|_| {
let id = self.alarm_count.load(Ordering::Relaxed);
Expand All @@ -548,7 +659,10 @@ impl Driver for RtcDriver {

fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
critical_section::with(|cs| {
#[cfg(not(time_driver_lptim1))]
let r = regs_gp16();
#[cfg(time_driver_lptim1)]
let r = T::regs();

let n = alarm.id() as usize;
let alarm = self.get_alarm(cs, alarm);
Expand Down

0 comments on commit ed50f8a

Please sign in to comment.