start impl embassy time driver
Some checks failed
Rust/va416xx-rs/pipeline/pr-main There was a failure building this commit
Some checks failed
Rust/va416xx-rs/pipeline/pr-main There was a failure building this commit
This commit is contained in:
parent
b9d5243fef
commit
f8a1a56c41
@ -9,9 +9,11 @@ cortex-m-rt = "0.7"
|
|||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
rtt-target = { version = "0.5" }
|
rtt-target = { version = "0.5" }
|
||||||
panic-rtt-target = { version = "0.1" }
|
panic-rtt-target = { version = "0.1" }
|
||||||
|
critical-section = "1"
|
||||||
|
|
||||||
embassy-sync = { version = "0.6.0" }
|
embassy-sync = { version = "0.6.0" }
|
||||||
embassy-time = { version = "0.3.2", features = ["tick-hz-32_768"] }
|
embassy-time = { version = "0.3.2", features = ["tick-hz-32_768"] }
|
||||||
|
embassy-time-driver = { version = "0.1" }
|
||||||
|
|
||||||
[dependencies.embassy-executor]
|
[dependencies.embassy-executor]
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
202
examples/embassy/src/lib.rs
Normal file
202
examples/embassy/src/lib.rs
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
#![no_std]
|
||||||
|
use core::{
|
||||||
|
cell::Cell,
|
||||||
|
mem, ptr,
|
||||||
|
sync::atomic::{AtomicU32, AtomicU8, Ordering}, u32,
|
||||||
|
};
|
||||||
|
use critical_section::CriticalSection;
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
|
|
||||||
|
use embassy_time_driver::{time_driver_impl, AlarmHandle, Driver};
|
||||||
|
use va416xx_hal::pac;
|
||||||
|
|
||||||
|
fn alarm_tim() -> &'static pac::tim0::RegisterBlock {
|
||||||
|
unsafe { &*pac::Tim23::ptr() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn timekeeping_tim() -> &'static pac::tim0::RegisterBlock {
|
||||||
|
unsafe { &*pac::Tim23::ptr() }
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlarmState {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
timestamp: Cell::new(u64::MAX),
|
||||||
|
callback: Cell::new(ptr::null()),
|
||||||
|
ctx: Cell::new(ptr::null_mut()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for AlarmState {}
|
||||||
|
|
||||||
|
const ALARM_COUNT: usize = 1;
|
||||||
|
|
||||||
|
pub struct EmbassyVa416xxTimeDriver {
|
||||||
|
periods: AtomicU32,
|
||||||
|
alarm_count: AtomicU8,
|
||||||
|
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
|
||||||
|
alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EmbassyVa416xxTimeDriver {
|
||||||
|
fn on_interrupt_timekeeping(&self) {
|
||||||
|
self.next_period();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt_alarm(&self, idx: usize) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let alarm = &self.alarms.borrow(cs)[idx];
|
||||||
|
let at = alarm.timestamp.get();
|
||||||
|
let period = self.periods.load(Ordering::Relaxed);
|
||||||
|
let t = (period as u64) << 32;
|
||||||
|
|
||||||
|
if at < t + u32::MAX as u64 {
|
||||||
|
//alarm_tim().
|
||||||
|
// just enable it. `set_alarm` has already set the correct CC val.
|
||||||
|
alarm_tim().ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_period(&self) {
|
||||||
|
self.periods.fetch_add(1, Ordering::Release);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
|
||||||
|
let r = alarm_tim();
|
||||||
|
r.ctrl().modify(|_, w| w.irq_enb().clear_bit());
|
||||||
|
|
||||||
|
let alarm = &self.alarms.borrow(cs)[n];
|
||||||
|
// Setting the maximum value disables the alarm.
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
|
||||||
|
// Call after clearing alarm, so the callback can set another alarm.
|
||||||
|
|
||||||
|
// safety:
|
||||||
|
// - we can ignore the possiblity 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Driver for EmbassyVa416xxTimeDriver {
|
||||||
|
fn now(&self) -> u64 {
|
||||||
|
let mut period1: u32;
|
||||||
|
let mut period2: u32;
|
||||||
|
let mut counter_val: u32;
|
||||||
|
loop {
|
||||||
|
// Acquire ensures that we get the latest value of `periods` and
|
||||||
|
// no instructions can be reordered before the load.
|
||||||
|
period1 = self.periods.load(Ordering::Acquire);
|
||||||
|
|
||||||
|
counter_val = timekeeping_tim().cnt_value().read().bits();
|
||||||
|
|
||||||
|
period2 = self.periods.load(Ordering::Acquire);
|
||||||
|
if period1 == period2 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
((period1 as u64) << 32) | counter_val as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn allocate_alarm(&self) -> Option<embassy_time_driver::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: embassy_time_driver::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: embassy_time_driver::AlarmHandle, timestamp: u64) -> bool {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let n = alarm.id() as _;
|
||||||
|
let alarm = self.get_alarm(cs, alarm);
|
||||||
|
alarm.timestamp.set(timestamp);
|
||||||
|
|
||||||
|
let r = alarm_tim();
|
||||||
|
|
||||||
|
let t = self.now();
|
||||||
|
if timestamp <= t {
|
||||||
|
r.ctrl().modify(|_, w| w.irq_enb().clear_bit());
|
||||||
|
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it hasn't triggered yet, setup it in the compare channel.
|
||||||
|
|
||||||
|
// Write the CC value regardless of whether we're going to enable it now or not.
|
||||||
|
// This way, when we enable it later, the right value is already set.
|
||||||
|
|
||||||
|
// nrf52 docs say:
|
||||||
|
// If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event.
|
||||||
|
// To workaround this, we never write a timestamp smaller than N+3.
|
||||||
|
// N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc.
|
||||||
|
//
|
||||||
|
// It is impossible for rtc to tick more than once because
|
||||||
|
// - this code takes less time than 1 tick
|
||||||
|
// - it runs with interrupts disabled so nothing else can preempt it.
|
||||||
|
//
|
||||||
|
// This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
|
||||||
|
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
|
||||||
|
// and we don't do that here.
|
||||||
|
let safe_timestamp = timestamp.max(t + 3);
|
||||||
|
r.cc[n].write(|w| unsafe { w.bits(safe_timestamp as u32 & 0xFFFFFF) });
|
||||||
|
|
||||||
|
let diff = timestamp - t;
|
||||||
|
if diff < 0xc00000 {
|
||||||
|
r.intenset.write(|w| unsafe { w.bits(compare_n(n)) });
|
||||||
|
} else {
|
||||||
|
// If it's too far in the future, don't setup the compare channel yet.
|
||||||
|
// It will be setup later by `next_period`.
|
||||||
|
r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) });
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time_driver_impl!(
|
||||||
|
static DRIVER: EmbassyVa416xxTimeDriver = EmbassyVa416xxTimeDriver {
|
||||||
|
periods: AtomicU32::new(0),
|
||||||
|
alarm_count: AtomicU8::new(0),
|
||||||
|
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [AlarmState::new(); ALARM_COUNT])
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user