From d3654c2d172c1fca91f4c2eb87c41bdd732531f8 Mon Sep 17 00:00:00 2001 From: decaday Date: Wed, 22 Jan 2025 15:35:19 +0800 Subject: [PATCH 1/3] feat: embassy-time-driver 0.2.0 impl --- src/embassy/mod.rs | 4 + src/{ => embassy}/time_driver.rs | 230 +++++++++++-------------------- src/lib.rs | 18 +-- 3 files changed, 90 insertions(+), 162 deletions(-) create mode 100644 src/embassy/mod.rs rename src/{ => embassy}/time_driver.rs (68%) diff --git a/src/embassy/mod.rs b/src/embassy/mod.rs new file mode 100644 index 0000000..72518a5 --- /dev/null +++ b/src/embassy/mod.rs @@ -0,0 +1,4 @@ +#[cfg(all(feature = "_time-driver", not(feature = "time-driver-systick")))] +pub mod time_driver; +#[cfg(feature = "time-driver-systick")] +pub mod systick_time_driver; \ No newline at end of file diff --git a/src/time_driver.rs b/src/embassy/time_driver.rs similarity index 68% rename from src/time_driver.rs rename to src/embassy/time_driver.rs index 718eed0..7d4c3a1 100644 --- a/src/time_driver.rs +++ b/src/embassy/time_driver.rs @@ -1,14 +1,14 @@ #![allow(non_snake_case)] -use core::cell::Cell; -use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; -use core::{mem, ptr}; +use core::cell::{Cell, RefCell}; +use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; -use crate::pac::timer::{regs, TimGp16}; 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 embassy_time_driver::{Driver, TICK_HZ}; +use embassy_time_queue_utils::Queue; +use py32_metapac::timer::{regs, TimGp16}; use crate::interrupt::typelevel::Interrupt; use crate::pac::timer::vals; @@ -24,18 +24,6 @@ use crate::{interrupt, peripherals}; // additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST // one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not // candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.) -// -// The values of ALARM_COUNT below are not the TOTAL CC registers available, but rather the number -// available after reserving CC1 for regular time keeping. For example, TIM2 has four CC registers: -// CC1, CC2, CC3, and CC4, so it can provide ALARM_COUNT = 3. - -cfg_if::cfg_if! { - if #[cfg(any(time_driver_tim15))] { - const ALARM_COUNT: usize = 1; - } else { - const ALARM_COUNT: usize = 3; - } -} #[cfg(time_driver_tim1)] type T = peripherals::TIM1; @@ -67,14 +55,6 @@ type T = peripherals::TIM23; type T = peripherals::TIM24; foreach_interrupt! { - (TIM1, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim1)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; (TIM1, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim1)] #[cfg(feature = "rt")] @@ -123,14 +103,6 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM8, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim8)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; (TIM9, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim9)] #[cfg(feature = "rt")] @@ -224,11 +196,6 @@ fn calc_now(period: u32, counter: u16) -> u64 { struct AlarmState { timestamp: Cell, - - // 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 {} @@ -237,8 +204,6 @@ impl AlarmState { const fn new() -> Self { Self { timestamp: Cell::new(u64::MAX), - callback: Cell::new(ptr::null()), - ctx: Cell::new(ptr::null_mut()), } } } @@ -246,22 +211,18 @@ impl AlarmState { pub(crate) struct RtcDriver { /// Number of 2^15 periods elapsed since boot. period: AtomicU32, - alarm_count: AtomicU8, - /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. - alarms: Mutex, + alarm: Mutex, // #[cfg(feature = "low-power")] // rtc: Mutex>>, + queue: Mutex>, } -#[allow(clippy::declare_interior_mutable_const)] -const ALARM_STATE_NEW: AlarmState = AlarmState::new(); - embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { period: AtomicU32::new(0), - alarm_count: AtomicU8::new(0), - alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), + alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), // #[cfg(feature = "low-power")] // rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), + queue: Mutex::new(RefCell::new(Queue::new())) }); impl RtcDriver { @@ -307,7 +268,6 @@ impl RtcDriver { fn on_interrupt(&self) { let r = regs_gp16(); - // XXX: reduce the size of this critical section ? critical_section::with(|cs| { let sr = r.sr().read(); let dier = r.dier().read(); @@ -327,10 +287,9 @@ impl RtcDriver { self.next_period(); } - for n in 0..ALARM_COUNT { - if sr.ccif(n + 1) && dier.ccie(n + 1) { - self.trigger_alarm(n, cs); - } + let n = 0; + if sr.ccif(n + 1) && dier.ccie(n + 1) { + self.trigger_alarm(cs); } }) } @@ -345,36 +304,23 @@ impl RtcDriver { critical_section::with(move |cs| { r.dier().modify(move |w| { - for n in 0..ALARM_COUNT { - let alarm = &self.alarms.borrow(cs)[n]; - let at = alarm.timestamp.get(); - - if at < t + 0xc000 { - // just enable it. `set_alarm` has already set the correct CCR val. - w.set_ccie(n + 1, true); - } + let n = 0; + let alarm = self.alarm.borrow(cs); + let at = alarm.timestamp.get(); + + if at < t + 0xc000 { + // just enable it. `set_alarm` has already set the correct CCR val. + w.set_ccie(n + 1, true); } }) }) } - 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 alarm = &self.alarms.borrow(cs)[n]; - alarm.timestamp.set(u64::MAX); - - // Call after clearing 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 trigger_alarm(&self, cs: CriticalSection) { + let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + } } /* @@ -386,14 +332,7 @@ impl RtcDriver { // fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration { // let now = self.now() + 32; - // embassy_time::Duration::from_ticks( - // self.alarms - // .borrow(cs) - // .iter() - // .map(|alarm: &AlarmState| alarm.timestamp.get().saturating_sub(now)) - // .min() - // .unwrap_or(u64::MAX), - // ) + // embassy_time::Duration::from_ticks(self.alarm.borrow(cs).timestamp.get().saturating_sub(now)) // } // #[cfg(feature = "low-power")] @@ -428,12 +367,12 @@ impl RtcDriver { // self.period.store(period, Ordering::SeqCst); // regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); - // // Now, recompute all alarms - // for i in 0..ALARM_COUNT { - // let alarm_handle = unsafe { AlarmHandle::new(i as u8) }; - // let alarm = self.get_alarm(cs, alarm_handle); + // // Now, recompute alarm + // let alarm = self.alarm.borrow(cs); - // self.set_alarm(alarm_handle, alarm.timestamp.get()); + // if !self.set_alarm(cs, alarm.timestamp.get()) { + // // If the alarm timestamp has passed, we need to trigger it + // self.trigger_alarm(cs); // } // } @@ -505,82 +444,71 @@ impl RtcDriver { // regs_gp16().cr1().modify(|w| w.set_cen(true)); // }) // } -} -impl Driver for RtcDriver { - fn now(&self) -> u64 { + fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { let r = regs_gp16(); - let period = self.period.load(Ordering::Relaxed); - compiler_fence(Ordering::Acquire); - let counter = r.cnt().read().cnt(); - calc_now(period, counter) - } + let n = 0; + self.alarm.borrow(cs).timestamp.set(timestamp); - unsafe fn allocate_alarm(&self) -> Option { - 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 - } - }) - } + 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. + r.dier().modify(|w| w.set_ccie(n + 1, false)); - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - critical_section::with(|cs| { - let alarm = self.get_alarm(cs, alarm); + self.alarm.borrow(cs).timestamp.set(u64::MAX); - alarm.callback.set(callback as *const ()); - alarm.ctx.set(ctx); - }) - } + return false; + } - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { - critical_section::with(|cs| { - let r = regs_gp16(); + // Write the CCR 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. + r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16)); - let n = alarm.id() as usize; - let alarm = self.get_alarm(cs, alarm); - alarm.timestamp.set(timestamp); + // Enable it if it'll happen soon. Otherwise, `next_period` will enable it. + let diff = timestamp - t; + r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)); - 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. - r.dier().modify(|w| w.set_ccie(n + 1, false)); + // 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(|w| w.set_ccie(n + 1, false)); - alarm.timestamp.set(u64::MAX); + self.alarm.borrow(cs).timestamp.set(u64::MAX); - return false; - } + return false; + } - // Write the CCR 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. - r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16)); + // We're confident the alarm will ring in the future. + true + } +} - // Enable it if it'll happen soon. Otherwise, `next_period` will enable it. - let diff = timestamp - t; - r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)); +impl Driver for RtcDriver { + fn now(&self) -> u64 { + let r = regs_gp16(); - // 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(|w| w.set_ccie(n + 1, false)); + let period = self.period.load(Ordering::Relaxed); + compiler_fence(Ordering::Acquire); + let counter = r.cnt().read().cnt(); + calc_now(period, counter) + } - alarm.timestamp.set(u64::MAX); + fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { + critical_section::with(|cs| { + let mut queue = self.queue.borrow(cs).borrow_mut(); - return false; + if queue.schedule_wake(at, waker) { + let mut next = queue.next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = queue.next_expiration(self.now()); + } } - - // We're confident the alarm will ring in the future. - true }) } } @@ -592,4 +520,4 @@ impl Driver for RtcDriver { pub(crate) fn init(cs: CriticalSection) { DRIVER.init(cs) -} +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index e1b4de9..b96e323 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,15 +38,9 @@ pub mod dma; pub mod flash; pub mod i2c; pub mod rcc; -pub mod time; -#[cfg(feature = "_time-driver")] -pub mod time_driver; pub mod timer; pub mod usart; - pub mod gpio; -#[cfg(feature = "time-driver-systick")] -pub mod systick_time_driver; #[cfg(any(feature = "embassy-usb-driver-impl", feature = "usb-device-impl"))] pub mod usb; @@ -54,10 +48,12 @@ pub mod usb; #[cfg(feature = "exti")] pub mod exti; -#[cfg(all(feature = "_time-driver", feature = "time-driver-systick"))] -compile_error!( - "The `time-driver-systick` feature is incompatible with the `time-driver-timxx` feature. " -); +pub mod time; +pub mod embassy; +#[cfg(all(feature = "_time-driver", not(feature = "time-driver-systick")))] +pub use embassy::time_driver; +#[cfg(feature = "time-driver-systick")] +pub use embassy::systick_time_driver; #[cfg(feature = "time-driver-systick")] use cortex_m::peripheral::SYST; @@ -124,7 +120,7 @@ pub fn init(config: Config, #[cfg(feature = "time-driver-systick")] systick: SYS gpio::init(cs); // must be after rcc init - #[cfg(feature = "_time-driver")] + #[cfg(all(feature = "_time-driver", not(feature = "time-driver-systick")))] time_driver::init(cs); #[cfg(feature = "time-driver-systick")] From 3460f8535b2ab73377f08e202e62913a963879f2 Mon Sep 17 00:00:00 2001 From: decaday Date: Wed, 22 Jan 2025 15:36:23 +0800 Subject: [PATCH 2/3] chore: bump embassy deps version, rm unused --- .github/workflows/build.yml | 2 +- Cargo.toml | 31 ++++++------------- examples/heap-alloc-f030/Cargo.toml | 6 ++-- examples/py32f030/.cargo/config.toml | 1 - examples/py32f030/Cargo.toml | 6 ++-- examples/py32f072/Cargo.toml | 8 ++--- examples/systick-time-driver-f030/Cargo.toml | 6 ++-- examples/systick-time-driver-f030/src/main.rs | 1 - examples/usbd-f072/Cargo.toml | 7 ++--- 9 files changed, 26 insertions(+), 42 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 191b644..307b707 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,7 +25,7 @@ jobs: rustup component add rust-src rustup target add thumbv6m-none-eabi - name: Build Only - run: cargo build --features py32f030k28,rt,embassy --target thumbv6m-none-eabi + run: cargo build --features py32f030k28 --target thumbv6m-none-eabi - name: Build Examples run: | for d in $(ls -1 ./examples); do diff --git a/Cargo.toml b/Cargo.toml index 5ab6439..7d3293e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,16 +54,13 @@ embassy-hal-internal = { version = "0.2.0", features = [ "cortex-m", "prio-bits-4", ] } -embassy-sync = { version = "0.6.0", optional = true } -embassy-futures = { version = "0.1.1", optional = true } -embassy-time-driver = { version = "0.1.0", optional = true } -embassy-time = { version = "0.3.2", optional = true } -embassy-executor = { version = "0.6", features = [ - "nightly", - "integrated-timers", - "arch-cortex-m", -] } -embassy-embedded-hal = { version = "0.2.0", default-features = false } +embassy-sync = { version = "0.6.2" } +embassy-futures = { version = "0.1.1" } +embassy-time-driver = { version = "0.2.0", optional = true } +embassy-time = { version = "0.4.0", optional = true } +embassy-time-queue-utils = { version = "0.1", optional = true } + +embassy-embedded-hal = { version = "0.3.0", default-features = false } embedded-storage = "0.3.1" @@ -82,7 +79,7 @@ critical-section = { version = "1.1", features = ["std"] } # cargo test --no-default-features --features "embassy time exti embassy-usb-driver-impl py32f072c1b" [features] -default = ["rt", "memory-x", "defmt", "embassy", "time", "exti"] +default = ["rt", "memory-x", "defmt", "time", "exti"] rt = ["py32-metapac/rt"] @@ -90,8 +87,6 @@ defmt = ["dep:defmt", "dep:defmt-rtt", "embassy-usb-driver/defmt", "musb?/defmt" memory-x = ["py32-metapac/memory-x"] -embassy = ["dep:embassy-sync", "dep:embassy-futures", "dep:embassy-time-driver"] - time = ["dep:embassy-time", "embassy-embedded-hal/time"] exti = [] @@ -111,15 +106,9 @@ time-driver-any = ["_time-driver"] time-driver-tim1 = ["_time-driver"] time-driver-tim3 = ["_time-driver"] time-driver-tim15 = ["_time-driver"] +time-driver-systick = ["dep:portable-atomic", "_time-driver"] -time-driver-systick = ["portable-atomic"] - -# td == time-driver, to avoid confliction -# By default, only one alarm is provided (similar to a 2-channel timer). Enabling this feature provides three alarms (similar to a 4-channel timer). -# Of course, this will also increase the execution time of the interrupt handler. -td-systick-multi-alarms = ["time-driver-systick"] - -_time-driver = [] +_time-driver = ["dep:embassy-time-driver", "time", "dep:embassy-time-queue-utils"] [package.metadata.docs.rs] diff --git a/examples/heap-alloc-f030/Cargo.toml b/examples/heap-alloc-f030/Cargo.toml index 2df9bc2..d5f8256 100644 --- a/examples/heap-alloc-f030/Cargo.toml +++ b/examples/heap-alloc-f030/Cargo.toml @@ -7,9 +7,9 @@ edition = "2021" cortex-m-rt = "0.7" panic-probe = { version = "0.3", features = ["print-defmt"] } -embassy-sync = { version = "0.6.0", features = ["defmt"] } -embassy-executor = { version = "0.6.1", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.3.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-1_000"] } +embassy-sync = { version = "0.6.2", features = ["defmt"] } +embassy-executor = { version = "0.7.0", features = ["task-arena-size-2048", "arch-cortex-m", "executor-thread", "defmt"] } +embassy-time = { version = "0.4.0", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } py32-hal = { path = "../../", features = [ "time-driver-tim3", "py32f030k28"]} defmt = { version = "0.3", features = ["alloc"] } diff --git a/examples/py32f030/.cargo/config.toml b/examples/py32f030/.cargo/config.toml index e8e20c9..8ce61b8 100644 --- a/examples/py32f030/.cargo/config.toml +++ b/examples/py32f030/.cargo/config.toml @@ -1,7 +1,6 @@ [target.thumbv6m-none-eabi] # probe-rs chip list | grep -i PY32 runner = 'probe-rs run --chip PY32F030x8' - # rustflags = [ # "-C", "linker=flip-link", # ] diff --git a/examples/py32f030/Cargo.toml b/examples/py32f030/Cargo.toml index 38eb04c..a1b0556 100644 --- a/examples/py32f030/Cargo.toml +++ b/examples/py32f030/Cargo.toml @@ -13,9 +13,9 @@ cortex-m-rt = "0.7.3" cortex-m-semihosting = { version = "0.5" } panic-probe = { version = "0.3", features = ["print-defmt"] } -embassy-sync = { version = "0.6.0", features = ["defmt"] } -embassy-executor = { version = "0.6.1", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.3.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-sync = { version = "0.6.2", features = ["defmt"] } +embassy-executor = { version = "0.7.0", features = ["task-arena-size-2048", "arch-cortex-m", "executor-thread", "defmt"] } +embassy-time = { version = "0.4.0", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embedded-io = { version = "0.6.0" } embedded-io-async = { version = "0.6.1" } diff --git a/examples/py32f072/Cargo.toml b/examples/py32f072/Cargo.toml index 9f2a7bf..29f73cf 100644 --- a/examples/py32f072/Cargo.toml +++ b/examples/py32f072/Cargo.toml @@ -13,9 +13,9 @@ cortex-m-rt = "0.7.3" cortex-m-semihosting = { version = "0.5" } panic-probe = { version = "0.3", features = ["print-defmt"] } -embassy-sync = { version = "0.6.0", features = ["defmt"] } -embassy-executor = { version = "0.6.1", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.3.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-sync = { version = "0.6.2", features = ["defmt"] } +embassy-executor = { version = "0.7.0", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt"] } +embassy-time = { version = "0.4.0", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embedded-io = { version = "0.6.0" } embedded-io-async = { version = "0.6.1" } @@ -25,7 +25,7 @@ py32-hal = { path = "../../", features = [ "time-driver-tim15", "py32f072c1b", " defmt = "0.3" defmt-rtt = "0.4" embassy-futures = "0.1.1" -embassy-usb = { version = "0.3.0", features = [ "defmt"]} +embassy-usb = { version = "0.4.0", features = [ "defmt"]} usbd-hid = "0.8.2" # embassy-usb-logger = "0.2.0" diff --git a/examples/systick-time-driver-f030/Cargo.toml b/examples/systick-time-driver-f030/Cargo.toml index 1d558ba..4a3f2b6 100644 --- a/examples/systick-time-driver-f030/Cargo.toml +++ b/examples/systick-time-driver-f030/Cargo.toml @@ -13,9 +13,9 @@ cortex-m-rt = "0.7.3" cortex-m-semihosting = { version = "0.5" } panic-probe = { version = "0.3", features = ["print-defmt"] } -embassy-sync = { version = "0.6.0", features = ["defmt"] } -embassy-executor = { version = "0.6.1", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.3.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-1_000"] } +embassy-sync = { version = "0.6.2", features = ["defmt"] } +embassy-executor = { version = "0.7.0", features = ["task-arena-size-2048", "arch-cortex-m", "executor-thread", "defmt"] } +embassy-time = { version = "0.4.0", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embedded-io = { version = "0.6.0" } embedded-io-async = { version = "0.6.1" } diff --git a/examples/systick-time-driver-f030/src/main.rs b/examples/systick-time-driver-f030/src/main.rs index 246a22b..72f93c5 100644 --- a/examples/systick-time-driver-f030/src/main.rs +++ b/examples/systick-time-driver-f030/src/main.rs @@ -6,7 +6,6 @@ use embassy_executor::Spawner; use embassy_time::Timer; use py32_hal::gpio::{Level, Output, Speed}; use py32_hal::rcc::{HsiFs, Pll, PllSource, Sysclk}; -use py32_hal::time::Hertz; use cortex_m::Peripherals; use defmt::*; diff --git a/examples/usbd-f072/Cargo.toml b/examples/usbd-f072/Cargo.toml index e242fa0..57862be 100644 --- a/examples/usbd-f072/Cargo.toml +++ b/examples/usbd-f072/Cargo.toml @@ -13,19 +13,16 @@ cortex-m-rt = "0.7.3" cortex-m-semihosting = { version = "0.5" } panic-probe = { version = "0.3", features = ["print-defmt"] } -py32-hal = { path = "../../", default-features = false,features = [ "py32f072c1b", - "time-driver-tim15", +py32-hal = { path = "../../", default-features = false, features = [ + "py32f072c1b", "defmt", "rt", "memory-x", "usb-device-impl", - "embassy" ]} defmt = "0.3" defmt-rtt = "0.4" -embassy-futures = "0.1.1" -embassy-usb = { version = "0.3.0", features = [ "defmt"]} usbd-human-interface-device = { version = "0.5.0", features = [ "defmt"]} portable-atomic = { version = "1.5", features = ["critical-section"] } From 628f7552005598a16654af259bafa76c6899e367 Mon Sep 17 00:00:00 2001 From: decaday Date: Sat, 25 Jan 2025 15:52:41 +0800 Subject: [PATCH 3/3] feat: systick embassy-time-driver 0.2.0 impl --- src/embassy/mod.rs | 1 + src/embassy/systick_time_driver.rs | 154 ++++++++++++++++++++ src/embassy/time_driver.rs | 14 +- src/lib.rs | 8 +- src/systick_time_driver.rs | 219 ----------------------------- 5 files changed, 170 insertions(+), 226 deletions(-) create mode 100644 src/embassy/systick_time_driver.rs delete mode 100644 src/systick_time_driver.rs diff --git a/src/embassy/mod.rs b/src/embassy/mod.rs index 72518a5..0d6170c 100644 --- a/src/embassy/mod.rs +++ b/src/embassy/mod.rs @@ -1,4 +1,5 @@ #[cfg(all(feature = "_time-driver", not(feature = "time-driver-systick")))] pub mod time_driver; + #[cfg(feature = "time-driver-systick")] pub mod systick_time_driver; \ No newline at end of file diff --git a/src/embassy/systick_time_driver.rs b/src/embassy/systick_time_driver.rs new file mode 100644 index 0000000..b878c41 --- /dev/null +++ b/src/embassy/systick_time_driver.rs @@ -0,0 +1,154 @@ +use core::cell::{Cell, RefCell}; +use core::task::Waker; + +use cortex_m::peripheral::syst::SystClkSource; +use cortex_m::peripheral::SYST; +use cortex_m_rt::exception; + +use critical_section::CriticalSection; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use embassy_time_driver::{Driver, TICK_HZ}; +use embassy_time_queue_utils::Queue; +use portable_atomic::{AtomicU64, Ordering}; + +// Alarm state structure to manage individual alarms +struct AlarmState { + timestamp: Cell, +} + +unsafe impl Send for AlarmState {} + +impl AlarmState { + const fn new() -> Self { + Self { + timestamp: Cell::new(u64::MAX), + } + } +} + +// SysTick-based time driver implementation +pub(crate) struct SysTickDriver { + // Total number of ticks since system start + ticks: AtomicU64, + // Number of allocated alarms + alarm: Mutex, + queue: Mutex>, +} + +// Constant initialization for alarm states +#[allow(clippy::declare_interior_mutable_const)] +const ALARM_STATE_NEW: AlarmState = AlarmState::new(); + +// Macro to create a static driver instance +embassy_time_driver::time_driver_impl!(static DRIVER: SysTickDriver = SysTickDriver { + ticks: AtomicU64::new(0), + alarm: Mutex::const_new(CriticalSectionRawMutex::new(), ALARM_STATE_NEW), + queue: Mutex::new(RefCell::new(Queue::new())) +}); + +impl SysTickDriver { + // Initialize the SysTick driver + fn init(&'static self, _cs: CriticalSection, mut systick: SYST) -> bool { + // Calculate the reload value + let core_clock = unsafe { crate::rcc::get_freqs() } + .hclk1 + .to_hertz() + .unwrap() + .0; + + let reload_value = match (core_clock as u64).checked_div(TICK_HZ) { + Some(div) if div > 0 && div <= 0x00FFFFFF => (div - 1) as u32, + _ => panic!("Invalid SysTick reload value"), // Frequency not achievable + }; + // let peripherals = unsafe { cortex_m::Peripherals::steal() }; + // let mut systick = peripherals.SYST; + + // Configure SysTick + systick.set_clock_source(SystClkSource::Core); // Use processor clock + systick.set_reload(reload_value); + systick.clear_current(); + systick.enable_counter(); + systick.enable_interrupt(); + + true + } + + // SysTick interrupt handler + fn on_systick(&self) { + critical_section::with(|cs| { + // Increment global tick counter + let current_ticks = self.ticks.fetch_add(1, Ordering::Relaxed); + self.check_and_trigger_alarm(current_ticks, cs); + }); + } + + // Check if an alarm is due and trigger it if necessary + #[inline] + fn check_and_trigger_alarm(&self, current_time: u64, cs: CriticalSection) { + let alarm = &self.alarm.borrow(cs); + let alarm_timestamp = alarm.timestamp.get(); + + // Check if alarm is scheduled and due + if alarm_timestamp != u64::MAX && current_time >= alarm_timestamp { + let mut next = self + .queue + .borrow(cs) + .borrow_mut() + .next_expiration(current_time); + while !self.set_alarm(cs, next) { + next = self + .queue + .borrow(cs) + .borrow_mut() + .next_expiration(self.now()); + } + } + } + + // Set alarm timestamp + fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { + if self.now() >= timestamp { + // Alarm time has passed, cannot set + return false; + } + self.alarm.borrow(cs).timestamp.set(timestamp); + if self.now() >= timestamp { + self.alarm.borrow(cs).timestamp.set(u64::MAX); + return false; + } + true + } +} + +// Implement the Driver trait for SysTickDriver +impl Driver for SysTickDriver { + // Get current system time in ticks + fn now(&self) -> u64 { + self.ticks.load(Ordering::Relaxed) + } + + fn schedule_wake(&self, at: u64, waker: &Waker) { + critical_section::with(|cs| { + let mut queue = self.queue.borrow(cs).borrow_mut(); + + if queue.schedule_wake(at, waker) { + let mut next = queue.next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = queue.next_expiration(self.now()); + } + } + }) + } +} + +// Initialization function +pub(crate) fn init(cs: CriticalSection, systick: SYST) { + DRIVER.init(cs, systick); +} + +// SysTick interrupt handler (to be implemented in your interrupt vector) +#[exception] +fn SysTick() { + DRIVER.on_systick(); +} diff --git a/src/embassy/time_driver.rs b/src/embassy/time_driver.rs index 7d4c3a1..14c7244 100644 --- a/src/embassy/time_driver.rs +++ b/src/embassy/time_driver.rs @@ -317,9 +317,17 @@ impl RtcDriver { } fn trigger_alarm(&self, cs: CriticalSection) { - let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + let mut next = self + .queue + .borrow(cs) + .borrow_mut() + .next_expiration(self.now()); while !self.set_alarm(cs, next) { - next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + next = self + .queue + .borrow(cs) + .borrow_mut() + .next_expiration(self.now()); } } @@ -520,4 +528,4 @@ impl Driver for RtcDriver { pub(crate) fn init(cs: CriticalSection) { DRIVER.init(cs) -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index b96e323..9416982 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,11 +36,11 @@ pub mod mode { pub mod adc; pub mod dma; pub mod flash; +pub mod gpio; pub mod i2c; pub mod rcc; pub mod timer; pub mod usart; -pub mod gpio; #[cfg(any(feature = "embassy-usb-driver-impl", feature = "usb-device-impl"))] pub mod usb; @@ -48,12 +48,12 @@ pub mod usb; #[cfg(feature = "exti")] pub mod exti; -pub mod time; pub mod embassy; -#[cfg(all(feature = "_time-driver", not(feature = "time-driver-systick")))] -pub use embassy::time_driver; +pub mod time; #[cfg(feature = "time-driver-systick")] pub use embassy::systick_time_driver; +#[cfg(all(feature = "_time-driver", not(feature = "time-driver-systick")))] +pub use embassy::time_driver; #[cfg(feature = "time-driver-systick")] use cortex_m::peripheral::SYST; diff --git a/src/systick_time_driver.rs b/src/systick_time_driver.rs deleted file mode 100644 index 170bc2f..0000000 --- a/src/systick_time_driver.rs +++ /dev/null @@ -1,219 +0,0 @@ -use core::cell::Cell; -use core::ptr; - -use cortex_m::peripheral::syst::SystClkSource; -use cortex_m::peripheral::SYST; -use cortex_m_rt::exception; - -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 portable_atomic::{AtomicU64, AtomicU8, Ordering}; - -// Maximum number of supported alarms -#[cfg(feature = "td-systick-multi-alarms")] -const ALARM_COUNT: usize = 3; - -// Alarm state structure to manage individual alarms -struct AlarmState { - timestamp: Cell, - 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()), - } - } -} - -// SysTick-based time driver implementation -pub(crate) struct SysTickDriver { - // Total number of ticks since system start - ticks: AtomicU64, - // Number of allocated alarms - alarm_count: AtomicU8, - // Mutex-protected array of alarms - #[cfg(feature = "td-systick-multi-alarms")] - alarms: Mutex, - #[cfg(not(feature = "td-systick-multi-alarms"))] - alarm: Mutex, -} - -// Constant initialization for alarm states -#[allow(clippy::declare_interior_mutable_const)] -const ALARM_STATE_NEW: AlarmState = AlarmState::new(); - -// Macro to create a static driver instance -embassy_time_driver::time_driver_impl!(static DRIVER: SysTickDriver = SysTickDriver { - ticks: AtomicU64::new(0), - alarm_count: AtomicU8::new(0), - #[cfg(feature = "td-systick-multi-alarms")] - alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), - #[cfg(not(feature = "td-systick-multi-alarms"))] - alarm: Mutex::const_new(CriticalSectionRawMutex::new(), ALARM_STATE_NEW), -}); - -impl SysTickDriver { - // Initialize the SysTick driver - fn init(&'static self, _cs: CriticalSection, mut systick: SYST) -> bool { - // Calculate the reload value - let core_clock = unsafe { crate::rcc::get_freqs() } - .hclk1 - .to_hertz() - .unwrap() - .0; - - let reload_value = match (core_clock as u64).checked_div(TICK_HZ) { - Some(div) if div > 0 && div <= 0x00FFFFFF => (div - 1) as u32, - _ => panic!("Invalid SysTick reload value"), // Frequency not achievable - }; - // let peripherals = unsafe { cortex_m::Peripherals::steal() }; - // let mut systick = peripherals.SYST; - - // Configure SysTick - systick.set_clock_source(SystClkSource::Core); // Use processor clock - systick.set_reload(reload_value); - systick.clear_current(); - systick.enable_counter(); - systick.enable_interrupt(); - - true - } - - // SysTick interrupt handler - fn on_systick(&self) { - critical_section::with(|cs| { - // Increment global tick counter - let current_ticks = self.ticks.fetch_add(1, Ordering::Relaxed); - - // Check and trigger any due alarms - #[cfg(feature = "td-systick-multi-alarms")] - for n in 0..ALARM_COUNT { - self.check_and_trigger_alarm(n, current_ticks, cs); - } - - #[cfg(not(feature = "td-systick-multi-alarms"))] - self.check_and_trigger_alarm(current_ticks, cs); - }); - } - - // Check if an alarm is due and trigger it if necessary - #[inline] - fn check_and_trigger_alarm( - &self, - #[cfg(feature = "td-systick-multi-alarms")] n: usize, - current_time: u64, - cs: CriticalSection, - ) { - #[cfg(feature = "td-systick-multi-alarms")] - let alarm = &self.alarms.borrow(cs)[n]; - #[cfg(not(feature = "td-systick-multi-alarms"))] - let alarm = &self.alarm.borrow(cs); - - let alarm_timestamp = alarm.timestamp.get(); - - // Check if alarm is scheduled and due - if alarm_timestamp != u64::MAX && current_time >= alarm_timestamp { - // Reset timestamp - alarm.timestamp.set(u64::MAX); - - // Safety: We know the callback is valid when set - let f: fn(*mut ()) = unsafe { core::mem::transmute(alarm.callback.get()) }; - f(alarm.ctx.get()); - } - } -} - -// Implement the Driver trait for SysTickDriver -impl Driver for SysTickDriver { - // Get current system time in ticks - fn now(&self) -> u64 { - self.ticks.load(Ordering::Relaxed) - } - - // Allocate a new alarm - unsafe fn allocate_alarm(&self) -> Option { - critical_section::with(|_| { - #[cfg(feature = "td-systick-multi-alarms")] - { - 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 as u8)) - } else { - None - } - } - - #[cfg(not(feature = "td-systick-multi-alarms"))] - { - if self.alarm_count.load(Ordering::Relaxed) < 1 { - self.alarm_count.store(1, Ordering::Relaxed); - Some(AlarmHandle::new(0)) - } else { - None - } - } - }) - } - - // Set alarm callback - #[allow(unused_variables)] - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - critical_section::with(|cs| { - #[cfg(feature = "td-systick-multi-alarms")] - let alarm_state = &self.alarms.borrow(cs)[alarm.id() as usize]; - #[cfg(not(feature = "td-systick-multi-alarms"))] - let alarm_state = &self.alarm.borrow(cs); - alarm_state.callback.set(callback as *const ()); - alarm_state.ctx.set(ctx); - }); - } - - // Set alarm timestamp - #[allow(unused_variables)] - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { - critical_section::with(|cs| { - let alarm_state = { - #[cfg(feature = "td-systick-multi-alarms")] - { - let n = alarm.id() as usize; - &self.alarms.borrow(cs)[n] - } - #[cfg(not(feature = "td-systick-multi-alarms"))] - { - &self.alarm.borrow(cs) - } - }; - - let current_time = self.now(); - if timestamp <= current_time { - // Alarm time has passed, cannot set - return false; - } - - // Set alarm timestamp - alarm_state.timestamp.set(timestamp); - true - }) - } -} - -// Initialization function -pub(crate) fn init(cs: CriticalSection, systick: SYST) { - DRIVER.init(cs, systick); -} - -// SysTick interrupt handler (to be implemented in your interrupt vector) -#[exception] -fn SysTick() { - DRIVER.on_systick(); -}