mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-21 22:32:29 +00:00
STM32: add support for LPTIM time driver
This commit is contained in:
parent
05d36233fc
commit
7fc279c026
@ -167,6 +167,10 @@ time-driver-tim22 = ["_time-driver"]
|
|||||||
time-driver-tim23 = ["_time-driver"]
|
time-driver-tim23 = ["_time-driver"]
|
||||||
## Use TIM24 as time driver
|
## Use TIM24 as time driver
|
||||||
time-driver-tim24 = ["_time-driver"]
|
time-driver-tim24 = ["_time-driver"]
|
||||||
|
## Use LPTIM1 as time driver
|
||||||
|
time-driver-lptim1 = ["_time-driver"]
|
||||||
|
## Use LPTIM2 as time driver
|
||||||
|
time-driver-lptim2 = ["_time-driver"]
|
||||||
|
|
||||||
|
|
||||||
#! ## Analog Switch Pins (Pxy_C) on STM32H7 series
|
#! ## Analog Switch Pins (Pxy_C) on STM32H7 series
|
||||||
|
@ -214,6 +214,8 @@ fn main() {
|
|||||||
Some("tim22") => "TIM22",
|
Some("tim22") => "TIM22",
|
||||||
Some("tim23") => "TIM23",
|
Some("tim23") => "TIM23",
|
||||||
Some("tim24") => "TIM24",
|
Some("tim24") => "TIM24",
|
||||||
|
Some("lptim1") => "LPTIM1",
|
||||||
|
Some("lptim2") => "LPTIM2",
|
||||||
Some("any") => {
|
Some("any") => {
|
||||||
// Order of TIM candidators:
|
// Order of TIM candidators:
|
||||||
// 1. 2CH -> 2CH_CMP -> GP16 -> GP32 -> ADV
|
// 1. 2CH -> 2CH_CMP -> GP16 -> GP32 -> ADV
|
||||||
@ -236,7 +238,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
for tim in [
|
for tim in [
|
||||||
"tim1", "tim2", "tim3", "tim4", "tim5", "tim8", "tim9", "tim12", "tim15", "tim20", "tim21", "tim22", "tim23",
|
"tim1", "tim2", "tim3", "tim4", "tim5", "tim8", "tim9", "tim12", "tim15", "tim20", "tim21", "tim22", "tim23",
|
||||||
"tim24",
|
"tim24", "lptim1", "lptim2",
|
||||||
] {
|
] {
|
||||||
cfgs.declare(format!("time_driver_{}", tim));
|
cfgs.declare(format!("time_driver_{}", tim));
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ pin_trait!(Channel2Pin, BasicInstance);
|
|||||||
|
|
||||||
pub(crate) trait SealedInstance: RccPeripheral {
|
pub(crate) trait SealedInstance: RccPeripheral {
|
||||||
fn regs() -> crate::pac::lptim::Lptim;
|
fn regs() -> crate::pac::lptim::Lptim;
|
||||||
|
type Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||||
}
|
}
|
||||||
pub(crate) trait SealedBasicInstance: RccPeripheral {}
|
pub(crate) trait SealedBasicInstance: RccPeripheral {}
|
||||||
|
|
||||||
@ -34,6 +35,7 @@ foreach_interrupt! {
|
|||||||
fn regs() -> crate::pac::lptim::Lptim {
|
fn regs() -> crate::pac::lptim::Lptim {
|
||||||
crate::pac::$inst
|
crate::pac::$inst
|
||||||
}
|
}
|
||||||
|
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||||
}
|
}
|
||||||
impl SealedBasicInstance for crate::peripherals::$inst {
|
impl SealedBasicInstance for crate::peripherals::$inst {
|
||||||
}
|
}
|
||||||
|
461
embassy-stm32/src/time_driver/lptim.rs
Normal file
461
embassy-stm32/src/time_driver/lptim.rs
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
use core::mem;
|
||||||
|
use core::sync::atomic::{AtomicU16, AtomicU32, AtomicU8, Ordering};
|
||||||
|
|
||||||
|
use critical_section::CriticalSection;
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
|
use embassy_time::TICK_HZ;
|
||||||
|
use embassy_time_driver::{AlarmHandle, Driver};
|
||||||
|
use stm32_metapac::lptim::regs::IcrAdv;
|
||||||
|
|
||||||
|
use super::{AlarmState, ALARM_STATE_NEW};
|
||||||
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
|
use crate::lptim::SealedInstance;
|
||||||
|
use crate::rcc::{self, SealedRccPeripheral};
|
||||||
|
use crate::{interrupt, peripherals};
|
||||||
|
|
||||||
|
#[cfg(time_driver_lptim1)]
|
||||||
|
type T = peripherals::LPTIM1;
|
||||||
|
#[cfg(time_driver_lptim2)]
|
||||||
|
type T = peripherals::LPTIM1;
|
||||||
|
|
||||||
|
foreach_interrupt! {
|
||||||
|
(LPTIM1, lptim, $block:ident, GLOBAL, $irq:ident) => {
|
||||||
|
#[cfg(time_driver_lptim1)]
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
#[interrupt]
|
||||||
|
fn $irq() {
|
||||||
|
DRIVER.on_interrupt()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(LPTIM2, lptim, $block:ident, GLOBAL, $irq:ident) => {
|
||||||
|
#[cfg(time_driver_lptim2)]
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
#[interrupt]
|
||||||
|
fn $irq() {
|
||||||
|
DRIVER.on_interrupt()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn init(cs: CriticalSection) {
|
||||||
|
DRIVER.init(cs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*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));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt(&self) {
|
||||||
|
let r = regs_gp16();
|
||||||
|
|
||||||
|
// 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.arrm() {
|
||||||
|
self.next_period();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Half overflow
|
||||||
|
if isr.ccif(0) {
|
||||||
|
self.next_period();
|
||||||
|
}
|
||||||
|
|
||||||
|
for n in 0..ALARM_COUNT {
|
||||||
|
if isr.ccif(n + 1) && dier.ccie(n + 1) {
|
||||||
|
self.trigger_alarm(n, cs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
// TODO: let the user choose somehow
|
||||||
|
const ALARM_COUNT: usize = 1;
|
||||||
|
|
||||||
|
/// The LptimTimeDriver depends on a 16bit hardware counter. The software counters are increased on overflow.
|
||||||
|
/// The capture and compare channel is used for the alarms. It is not associated with any specific alarm, but
|
||||||
|
/// all alarms are checked (only on capture and compare match).
|
||||||
|
pub(crate) struct LptimTimeDriver {
|
||||||
|
ticks_upper: AtomicU32,
|
||||||
|
ticks_lower: AtomicU16,
|
||||||
|
alarm_count: AtomicU8,
|
||||||
|
alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
embassy_time_driver::time_driver_impl!(static DRIVER: LptimTimeDriver = LptimTimeDriver {
|
||||||
|
ticks_upper: AtomicU32::new(0),
|
||||||
|
ticks_lower: AtomicU16::new(0),
|
||||||
|
alarm_count: AtomicU8::new(0),
|
||||||
|
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]),
|
||||||
|
});
|
||||||
|
|
||||||
|
fn calc_now(ticks_upper: u32, ticks_lower: u16, counter: u16) -> u64 {
|
||||||
|
((ticks_upper as u64) << 32) + ((ticks_lower as u64) << 16) + (counter as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_ticks_from_timestamp(timestamp: u64) -> (u32, u16) {
|
||||||
|
let ticks_upper = (timestamp >> 32) as u32;
|
||||||
|
let ticks_lower = ((timestamp & 0x00000000_FFFF0000) >> 16) as u16;
|
||||||
|
(ticks_upper, ticks_lower)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_counter_from_timestamp(timestamp: u64) -> u16 {
|
||||||
|
(timestamp & 0x00000000_0000FFFF) as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LptimTimeDriver {
|
||||||
|
pub(crate) 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 and set the prescaler value
|
||||||
|
r.cfgr().write(|w| {
|
||||||
|
w.set_presc(if timer_freq.0 < TICK_HZ as u32 {
|
||||||
|
panic!(
|
||||||
|
"lptim (= {}) 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
|
||||||
|
),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// enable timer to write to DIER, CCR, ARR
|
||||||
|
r.cr().modify(|w| w.set_enable(true));
|
||||||
|
|
||||||
|
// set ARR
|
||||||
|
r.arr().write(|w| w.set_arr(u16::MAX));
|
||||||
|
// check that write is finished
|
||||||
|
while r.isr().read().arrok() == false {}
|
||||||
|
|
||||||
|
// unpend interrupts
|
||||||
|
r.icr().write(|w| {
|
||||||
|
w.set_arrmcf(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enable overflow interrupt
|
||||||
|
r.dier().write(|w| {
|
||||||
|
w.set_arrmie(true);
|
||||||
|
});
|
||||||
|
// check that write is finished
|
||||||
|
while r.isr().read().dierok() == false {}
|
||||||
|
|
||||||
|
// start continuous mode
|
||||||
|
r.cr().modify(|w| w.set_cntstrt(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt(&self) {
|
||||||
|
// TODO: check all the memory ordering
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let isr = r.isr().read();
|
||||||
|
|
||||||
|
// Clear all interrupts. Bits in ISR are "write 1 to clear", so write ISR to ICR.
|
||||||
|
// Other approaches such as writing all zeros, or RMWing won't work, they can
|
||||||
|
// miss interrupts.
|
||||||
|
r.icr().write_value(IcrAdv(isr.0));
|
||||||
|
|
||||||
|
let ticks_upper = self.ticks_upper.load(Ordering::Relaxed);
|
||||||
|
let ticks_lower = self.ticks_lower.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
// Overflow
|
||||||
|
let now = if isr.arrm() {
|
||||||
|
let new_ticks_lower = ticks_lower.overflowing_add(1);
|
||||||
|
|
||||||
|
self.ticks_lower
|
||||||
|
.compare_exchange(ticks_lower, new_ticks_lower.0, Ordering::Relaxed, Ordering::Relaxed)
|
||||||
|
.expect("No one else is writing them so it shouldn't fail to write them.");
|
||||||
|
|
||||||
|
// add 1 to ticks_upper if ticks_lower had an overflow
|
||||||
|
let new_ticks_upper = if new_ticks_lower.1 {
|
||||||
|
// add 1 to ticks_upper, ignoring an overflow since it is very far in the future
|
||||||
|
let new_ticks_upper = ticks_upper.overflowing_add(1).0;
|
||||||
|
|
||||||
|
self.ticks_upper
|
||||||
|
.compare_exchange(ticks_upper, new_ticks_upper, Ordering::Relaxed, Ordering::Relaxed)
|
||||||
|
.expect("No one else is writing these so it shouldn't fail to write them.");
|
||||||
|
new_ticks_upper
|
||||||
|
} else {
|
||||||
|
// no overflow -> ticks_upper stays the same
|
||||||
|
ticks_upper
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if next alarms is before the next counter overflow
|
||||||
|
self.set_ccp_interrupt_for_next_alarm_if_before_overflow(cs, new_ticks_upper, new_ticks_lower.0);
|
||||||
|
|
||||||
|
calc_now(new_ticks_upper, new_ticks_lower.0, r.cnt().read().cnt())
|
||||||
|
} else {
|
||||||
|
calc_now(ticks_upper, ticks_lower, r.cnt().read().cnt())
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if an alarm is ready
|
||||||
|
for n in 0..ALARM_COUNT {
|
||||||
|
self.trigger_alarm_if_ready(now, n, cs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_ccp_interrupt_if_before_overflow(&self, alarm_timestamp: u64, ticks_upper: u32, ticks_lower: u16) {
|
||||||
|
// skip u64::MAX immediately
|
||||||
|
if alarm_timestamp == u64::MAX {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
let (alarm_ticks_upper, alarm_ticks_lower) = calc_ticks_from_timestamp(alarm_timestamp);
|
||||||
|
if alarm_ticks_upper == ticks_upper && alarm_ticks_lower == ticks_lower {
|
||||||
|
// alarm is before next counter overflow
|
||||||
|
|
||||||
|
// set capture compare value
|
||||||
|
r.ccr(0)
|
||||||
|
.modify(|m| m.set_ccr(calc_counter_from_timestamp(alarm_timestamp)));
|
||||||
|
|
||||||
|
// enable capture compare alarm
|
||||||
|
r.dier().modify(|m| m.set_ccie(0, true));
|
||||||
|
while r.isr().read().dierok() == false {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_ccp_interrupt_for_next_alarm_if_before_overflow(
|
||||||
|
&self,
|
||||||
|
cs: CriticalSection,
|
||||||
|
ticks_upper: u32,
|
||||||
|
ticks_lower: u16,
|
||||||
|
) {
|
||||||
|
let next_timestamp = self.next_alarm_timestamp(cs);
|
||||||
|
self.set_ccp_interrupt_if_before_overflow(next_timestamp, ticks_upper, ticks_lower);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_alarm_if_ready(&self, now: u64, n: usize, cs: CriticalSection) {
|
||||||
|
let alarm = &self.alarms.borrow(cs)[n];
|
||||||
|
|
||||||
|
if alarm.timestamp.get() <= now {
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the callback after clearing the alarm, so the callback can set another alarm.
|
||||||
|
|
||||||
|
// safety:
|
||||||
|
// - we can ignore the possibility of `f` being unset (null) because of the safety contract of `allocate_alarm`.
|
||||||
|
// - other than that we only store valid function pointers into alarm.callback
|
||||||
|
let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
|
||||||
|
f(alarm.ctx.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_alarm_timestamp(&self, cs: CriticalSection) -> u64 {
|
||||||
|
self.alarms.borrow(cs).iter().map(|a| a.timestamp.get()).min().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState {
|
||||||
|
// safety: we're allowed to assume the AlarmState is created by us, and
|
||||||
|
// we never create one that's out of bounds.
|
||||||
|
unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Driver for LptimTimeDriver {
|
||||||
|
fn now(&self) -> u64 {
|
||||||
|
// TODO: check memory ordering
|
||||||
|
|
||||||
|
let r = T::regs();
|
||||||
|
loop {
|
||||||
|
let ticks_lower = self.ticks_lower.load(Ordering::Acquire);
|
||||||
|
|
||||||
|
let ticks_upper = self.ticks_upper.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
let ticks_lower_2 = self.ticks_lower.load(Ordering::Acquire);
|
||||||
|
|
||||||
|
if ticks_lower == ticks_lower_2 {
|
||||||
|
return calc_now(ticks_upper, ticks_lower, r.cnt().read().cnt());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
let id = self.alarm_count.load(Ordering::Relaxed);
|
||||||
|
if id < ALARM_COUNT as u8 {
|
||||||
|
self.alarm_count.store(id + 1, Ordering::Relaxed);
|
||||||
|
Some(AlarmHandle::new(id))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let alarm = self.get_alarm(cs, alarm);
|
||||||
|
|
||||||
|
alarm.callback.set(callback as *const ());
|
||||||
|
alarm.ctx.set(ctx);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
let n = alarm.id() as usize;
|
||||||
|
let alarm = self.get_alarm(cs, alarm);
|
||||||
|
alarm.timestamp.set(timestamp);
|
||||||
|
|
||||||
|
let t = self.now();
|
||||||
|
if timestamp <= t {
|
||||||
|
// If alarm timestamp has passed the alarm will not fire.
|
||||||
|
// Disarm the alarm and return `false` to indicate that.
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recompute the ccp interrupt
|
||||||
|
let (ticks_upper, ticks_lower) = calc_ticks_from_timestamp(t);
|
||||||
|
self.set_ccp_interrupt_for_next_alarm_if_before_overflow(cs, ticks_upper, ticks_lower);
|
||||||
|
|
||||||
|
// Reevaluate if the alarm timestamp is still in the future
|
||||||
|
let t = self.now();
|
||||||
|
if timestamp <= t {
|
||||||
|
// If alarm timestamp has passed since we set it, we have a race condition and
|
||||||
|
// the alarm may or may not have fired.
|
||||||
|
// Disarm the alarm and return 'false' to indicate that.
|
||||||
|
// It is the caller's responsibility to handle this ambiguity.
|
||||||
|
r.dier().modify(|m| m.set_ccie(0, false));
|
||||||
|
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're confident the alarm will ring in the future
|
||||||
|
true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
52
embassy-stm32/src/time_driver/mod.rs
Normal file
52
embassy-stm32/src/time_driver/mod.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use core::cell::Cell;
|
||||||
|
use core::ptr;
|
||||||
|
|
||||||
|
#[cfg_attr(
|
||||||
|
any(
|
||||||
|
time_driver_tim1,
|
||||||
|
time_driver_tim2,
|
||||||
|
time_driver_tim3,
|
||||||
|
time_driver_tim4,
|
||||||
|
time_driver_tim5,
|
||||||
|
time_driver_tim8,
|
||||||
|
time_driver_tim9,
|
||||||
|
time_driver_tim12,
|
||||||
|
time_driver_tim15,
|
||||||
|
time_driver_tim20,
|
||||||
|
time_driver_tim21,
|
||||||
|
time_driver_tim22,
|
||||||
|
time_driver_tim23,
|
||||||
|
time_driver_tim24
|
||||||
|
),
|
||||||
|
path = "tim.rs"
|
||||||
|
)]
|
||||||
|
#[cfg_attr(any(time_driver_lptim1, time_driver_lptim2), path = "lptim.rs")]
|
||||||
|
mod _timer;
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub use _timer::*;
|
||||||
|
|
||||||
|
struct AlarmState {
|
||||||
|
timestamp: Cell<u64>,
|
||||||
|
|
||||||
|
// This is really a Option<(fn(*mut ()), *mut ())>
|
||||||
|
// but fn pointers aren't allowed in const yet
|
||||||
|
callback: Cell<*const ()>,
|
||||||
|
ctx: Cell<*mut ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for AlarmState {}
|
||||||
|
|
||||||
|
impl AlarmState {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
timestamp: Cell::new(u64::MAX),
|
||||||
|
callback: Cell::new(ptr::null()),
|
||||||
|
ctx: Cell::new(ptr::null_mut()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::declare_interior_mutable_const)]
|
||||||
|
const ALARM_STATE_NEW: AlarmState = AlarmState::new();
|
@ -1,22 +1,23 @@
|
|||||||
#![allow(non_snake_case)]
|
#[cfg(feature = "low-power")]
|
||||||
|
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
|
use core::mem;
|
||||||
use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering};
|
use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering};
|
||||||
use core::{mem, ptr};
|
|
||||||
|
|
||||||
use critical_section::CriticalSection;
|
use critical_section::CriticalSection;
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
use embassy_time_driver::{AlarmHandle, Driver, TICK_HZ};
|
use embassy_time::TICK_HZ;
|
||||||
|
use embassy_time_driver::{AlarmHandle, Driver};
|
||||||
use stm32_metapac::timer::{regs, TimGp16};
|
use stm32_metapac::timer::{regs, TimGp16};
|
||||||
|
|
||||||
|
use super::{AlarmState, ALARM_STATE_NEW};
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
use crate::pac::timer::vals;
|
use crate::pac::timer::vals;
|
||||||
use crate::rcc::{self, SealedRccPeripheral};
|
use crate::rcc::SealedRccPeripheral;
|
||||||
#[cfg(feature = "low-power")]
|
#[cfg(feature = "low-power")]
|
||||||
use crate::rtc::Rtc;
|
use crate::rtc::Rtc;
|
||||||
use crate::timer::{CoreInstance, GeneralInstance1Channel};
|
use crate::timer::{CoreInstance, GeneralInstance1Channel};
|
||||||
use crate::{interrupt, peripherals};
|
use crate::{interrupt, peripherals, rcc};
|
||||||
|
|
||||||
// NOTE regarding ALARM_COUNT:
|
// NOTE regarding ALARM_COUNT:
|
||||||
//
|
//
|
||||||
@ -31,9 +32,9 @@ use crate::{interrupt, peripherals};
|
|||||||
|
|
||||||
cfg_if::cfg_if! {
|
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))] {
|
||||||
const ALARM_COUNT: usize = 1;
|
pub(crate) const ALARM_COUNT: usize = 1;
|
||||||
} else {
|
} else {
|
||||||
const ALARM_COUNT: usize = 3;
|
pub(crate) const ALARM_COUNT: usize = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,27 +231,6 @@ fn calc_now(period: u32, counter: u16) -> u64 {
|
|||||||
((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64)
|
((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AlarmState {
|
|
||||||
timestamp: Cell<u64>,
|
|
||||||
|
|
||||||
// This is really a Option<(fn(*mut ()), *mut ())>
|
|
||||||
// but fn pointers aren't allowed in const yet
|
|
||||||
callback: Cell<*const ()>,
|
|
||||||
ctx: Cell<*mut ()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for AlarmState {}
|
|
||||||
|
|
||||||
impl AlarmState {
|
|
||||||
const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
timestamp: Cell::new(u64::MAX),
|
|
||||||
callback: Cell::new(ptr::null()),
|
|
||||||
ctx: Cell::new(ptr::null_mut()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct RtcDriver {
|
pub(crate) struct RtcDriver {
|
||||||
/// Number of 2^15 periods elapsed since boot.
|
/// Number of 2^15 periods elapsed since boot.
|
||||||
period: AtomicU32,
|
period: AtomicU32,
|
||||||
@ -261,9 +241,6 @@ pub(crate) struct RtcDriver {
|
|||||||
rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>,
|
rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::declare_interior_mutable_const)]
|
|
||||||
const ALARM_STATE_NEW: AlarmState = AlarmState::new();
|
|
||||||
|
|
||||||
embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
|
embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
|
||||||
period: AtomicU32::new(0),
|
period: AtomicU32::new(0),
|
||||||
alarm_count: AtomicU8::new(0),
|
alarm_count: AtomicU8::new(0),
|
||||||
@ -273,7 +250,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
|
|||||||
});
|
});
|
||||||
|
|
||||||
impl RtcDriver {
|
impl RtcDriver {
|
||||||
fn init(&'static self, cs: critical_section::CriticalSection) {
|
pub(crate) fn init(&'static self, cs: critical_section::CriticalSection) {
|
||||||
let r = regs_gp16();
|
let r = regs_gp16();
|
||||||
|
|
||||||
rcc::enable_and_reset_with_cs::<T>(cs);
|
rcc::enable_and_reset_with_cs::<T>(cs);
|
||||||
@ -318,6 +295,7 @@ impl RtcDriver {
|
|||||||
// XXX: reduce the size of this critical section ?
|
// XXX: reduce the size of this critical section ?
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let sr = r.sr().read();
|
let sr = r.sr().read();
|
||||||
|
|
||||||
let dier = r.dier().read();
|
let dier = r.dier().read();
|
||||||
|
|
||||||
// Clear all interrupt flags. Bits in SR are "write 0 to clear", so write the bitwise NOT.
|
// Clear all interrupt flags. Bits in SR are "write 0 to clear", so write the bitwise NOT.
|
Loading…
Reference in New Issue
Block a user