From 6e1d6be315957e587051df7bf96f4a87fc748117 Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Sat, 20 Sep 2025 11:55:59 -0500 Subject: nrf: add persist() method for gpio and ppi --- embassy-nrf/CHANGELOG.md | 1 + embassy-nrf/src/gpio.rs | 27 +++++++++++++++++++++++++++ embassy-nrf/src/ppi/dppi.rs | 9 +++++++++ embassy-nrf/src/ppi/mod.rs | 8 ++++++++ embassy-nrf/src/ppi/ppi.rs | 9 +++++++++ embassy-nrf/src/timer.rs | 4 ++-- 6 files changed, 56 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index befa34ecf..de8dfd391 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - changed: nrf54l: Disable glitch detection and enable DC/DC in init. - changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4 +- changed: add persist() method for gpio and ppi ## 0.7.0 - 2025-08-26 diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 65f2d99f7..0cea38777 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -75,6 +75,15 @@ impl<'d> Input<'d> { } } +impl Input<'static> { + /// Persist the pin's configuration for the rest of the program's lifetime. This method should + /// be preferred over [`core::mem::forget()`] because the `'static` bound prevents accidental + /// reuse of the underlying peripheral. + pub fn persist(self) { + self.pin.persist() + } +} + /// Digital input or output level. #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -264,6 +273,15 @@ impl<'d> Output<'d> { } } +impl Output<'static> { + /// Persist the pin's configuration for the rest of the program's lifetime. This method should + /// be preferred over [`core::mem::forget()`] because the `'static` bound prevents accidental + /// reuse of the underlying peripheral. + pub fn persist(self) { + self.pin.persist() + } +} + pub(crate) fn convert_drive(w: &mut pac::gpio::regs::PinCnf, drive: OutputDrive) { #[cfg(not(feature = "_nrf54l"))] { @@ -447,6 +465,15 @@ impl<'d> Flex<'d> { } } +impl Flex<'static> { + /// Persist the pin's configuration for the rest of the program's lifetime. This method should + /// be preferred over [`core::mem::forget()`] because the `'static` bound prevents accidental + /// reuse of the underlying peripheral. + pub fn persist(self) { + core::mem::forget(self); + } +} + impl<'d> Drop for Flex<'d> { fn drop(&mut self) { self.set_as_disconnected(); diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index 686f66987..078d2fd1c 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -59,6 +59,15 @@ impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, } } +impl Ppi<'static, C, EVENT_COUNT, TASK_COUNT> { + /// Persist the channel's configuration for the rest of the program's lifetime. This method + /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents + /// accidental reuse of the underlying peripheral. + pub fn persist(self) { + core::mem::forget(self); + } +} + impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop for Ppi<'d, C, EVENT_COUNT, TASK_COUNT> { fn drop(&mut self) { self.disable(); diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index 531777205..2bcf72e9c 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -108,6 +108,14 @@ impl<'d, G: Group> PpiGroup<'d, G> { Task::from_reg(regs().tasks_chg(n).dis()) } } +impl PpiGroup<'static, G> { + /// Persist this group's configuration for the rest of the program's lifetime. This method + /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents + /// accidental reuse of the underlying peripheral. + pub fn persist(self) { + core::mem::forget(self); + } +} impl<'d, G: Group> Drop for PpiGroup<'d, G> { fn drop(&mut self) { diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index e04dacbc0..531c25444 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -68,6 +68,15 @@ impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, } } +impl Ppi<'static, C, EVENT_COUNT, TASK_COUNT> { + /// Persist the channel's configuration for the rest of the program's lifetime. This method + /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents + /// accidental reuse of the underlying peripheral. + pub fn persist(self) { + core::mem::forget(self); + } +} + impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop for Ppi<'d, C, EVENT_COUNT, TASK_COUNT> { fn drop(&mut self) { self.disable(); diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 5b58b0a50..de2875765 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -90,7 +90,7 @@ pub struct Timer<'d, T: Instance> { impl<'d, T: Instance> Timer<'d, T> { /// Create a new `Timer` driver. /// - /// This can be useful for triggering tasks via PPI + /// This can be useful for triggering tasks via PPI. /// `Uarte` uses this internally. pub fn new(timer: Peri<'d, T>) -> Self { Self::new_inner(timer, false) @@ -98,7 +98,7 @@ impl<'d, T: Instance> Timer<'d, T> { /// Create a new `Timer` driver in counter mode. /// - /// This can be useful for triggering tasks via PPI + /// This can be useful for triggering tasks via PPI. /// `Uarte` uses this internally. pub fn new_counter(timer: Peri<'d, T>) -> Self { Self::new_inner(timer, true) -- cgit From 9bb77f36f9697dd301f6c68a649030e2b5487782 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sun, 21 Sep 2025 13:24:29 +1000 Subject: rp: Use msplim for rp235x core1 stack guard --- embassy-rp/src/lib.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 6fb680b34..d03ba1fef 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -565,18 +565,10 @@ unsafe fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { #[cfg(all(feature = "_rp235x", not(feature = "_test")))] #[inline(always)] unsafe fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { - let core = unsafe { cortex_m::Peripherals::steal() }; + // The RP2350 arm cores are cortex-m33 and can use the MSPLIM register to guard the end of stack. + // We'll need to do something else for the riscv cores. + cortex_m::register::msplim::write(stack_bottom.addr() as u32); - // Fail if MPU is already configured - if core.MPU.ctrl.read() != 0 { - return Err(()); - } - - unsafe { - core.MPU.ctrl.write(5); // enable mpu with background default map - core.MPU.rbar.write(stack_bottom as u32 & !0xff); // set address - core.MPU.rlar.write(((stack_bottom as usize + 255) as u32) | 1); - } Ok(()) } -- cgit From c69d17e5fb11852ba14ddc3369a0c2dbfff4e29d Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sun, 21 Sep 2025 13:27:54 +1000 Subject: examples/rp235x: Add multicore stack overflow example --- .../rp235x/src/bin/multicore_stack_overflow.rs | 72 ++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 examples/rp235x/src/bin/multicore_stack_overflow.rs diff --git a/examples/rp235x/src/bin/multicore_stack_overflow.rs b/examples/rp235x/src/bin/multicore_stack_overflow.rs new file mode 100644 index 000000000..dba44aa23 --- /dev/null +++ b/examples/rp235x/src/bin/multicore_stack_overflow.rs @@ -0,0 +1,72 @@ +//! This example tests stack overflow handling on core1 of the RP235x chip. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Executor; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_time::Timer; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +const CORE1_STACK_LENGTH: usize = 4096; + +static mut CORE1_STACK: Stack = Stack::new(); +static EXECUTOR0: StaticCell = StaticCell::new(); +static EXECUTOR1: StaticCell = StaticCell::new(); + +#[cortex_m_rt::entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + let led = Output::new(p.PIN_25, Level::Low); + + spawn_core1( + p.CORE1, + unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) }, + move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| spawner.spawn(unwrap!(core1_task()))); + }, + ); + + let executor0 = EXECUTOR0.init(Executor::new()); + executor0.run(|spawner| spawner.spawn(unwrap!(core0_task(led)))); +} + +#[embassy_executor::task] +async fn core0_task(mut led: Output<'static>) { + info!("Hello from core 0"); + loop { + info!("core 0 still alive"); + led.set_high(); + Timer::after_millis(500).await; + led.set_low(); + Timer::after_millis(500).await; + } +} + +fn blow_my_stack() { + // Allocating an array a little larger than our stack should ensure a stack overflow when it is used. + let t = [0u8; CORE1_STACK_LENGTH + 64]; + + info!("Array initialised without error"); + // We need to use black_box to otherwise the compiler is too smart and will optimise all of this away. + // We shouldn't get to this code - the initialisation above will touch the stack guard. + for ref i in t { + let _data = core::hint::black_box(*i) + 1; + } +} + +#[embassy_executor::task] +async fn core1_task() { + info!("Hello from core 1"); + + blow_my_stack(); + + loop { + info!("core 1 still alive"); + Timer::after_millis(1000).await; + } +} -- cgit From a3ee22be1374222f68893b7c76bbb45914e6fead Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sun, 21 Sep 2025 19:19:15 +1000 Subject: chore: update changelog --- embassy-rp/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index d1265ffc4..ea62c2387 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add PIO I2S input - Add PIO onewire parasite power strong pullup - add `wait_for_alarm` and `alarm_scheduled` methods to rtc module ([#4216](https://github.com/embassy-rs/embassy/pull/4216)) +- rp235x: use msplim for stack guard instead of MPU ## 0.8.0 - 2025-08-26 -- cgit From 9e94c1bda38ed3e41458f1681cd1714d1c03e1e8 Mon Sep 17 00:00:00 2001 From: Narottam Royal Date: Sun, 21 Sep 2025 23:13:06 +1200 Subject: Add support for USB CRS sync --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/rcc/c0.rs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 190e68d6d..8fcc088fd 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: handle address and data-length errors in OSPI - feat: Allow OSPI DMA writes larger than 64kB using chunking - feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times +- feat: Add USB CRS sync support for STM32C071 ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index c2295bab6..99f22273d 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -49,6 +49,10 @@ pub struct Config { /// System Clock Configuration pub sys: Sysclk, + /// HSI48 Configuration + #[cfg(crs)] + pub hsi48: Option, + pub ahb_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, @@ -68,6 +72,8 @@ impl Config { }), hse: None, sys: Sysclk::HSISYS, + #[cfg(crs)] + hsi48: Some(crate::rcc::Hsi48Config::new()), ahb_pre: AHBPrescaler::DIV1, apb1_pre: APBPrescaler::DIV1, ls: crate::rcc::LsConfig::new(), @@ -127,6 +133,10 @@ pub(crate) unsafe fn init(config: Config) { } }; + // Configure HSI48 if required + #[cfg(crs)] + let hsi48 = config.hsi48.map(super::init_hsi48); + let rtc = config.ls.init(); let sys = match config.sys { @@ -185,13 +195,13 @@ pub(crate) unsafe fn init(config: Config) { hsi: hsi, hsiker: hsiker, hse: hse, + #[cfg(crs)] + hsi48: hsi48, rtc: rtc, // TODO lsi: None, lse: None, - #[cfg(crs)] - hsi48: None, ); RCC.ccipr() -- cgit From f7c3005345df07bad5c42612fd73974bd569affb Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 19 Sep 2025 17:38:24 +0200 Subject: add basic RTC driver for nRF --- embassy-nrf/CHANGELOG.md | 1 + embassy-nrf/src/chips/nrf51.rs | 3 + embassy-nrf/src/chips/nrf52805.rs | 3 + embassy-nrf/src/chips/nrf52810.rs | 3 + embassy-nrf/src/chips/nrf52811.rs | 3 + embassy-nrf/src/chips/nrf52820.rs | 3 + embassy-nrf/src/chips/nrf52832.rs | 4 + embassy-nrf/src/chips/nrf52833.rs | 4 + embassy-nrf/src/chips/nrf52840.rs | 4 + embassy-nrf/src/chips/nrf5340_app.rs | 3 + embassy-nrf/src/chips/nrf5340_net.rs | 3 + embassy-nrf/src/chips/nrf9120.rs | 3 + embassy-nrf/src/chips/nrf9160.rs | 3 + embassy-nrf/src/lib.rs | 2 + embassy-nrf/src/rtc.rs | 272 +++++++++++++++++++++++++++++++++++ examples/nrf52840/Cargo.toml | 1 + examples/nrf52840/src/bin/rtc.rs | 57 ++++++++ 17 files changed, 372 insertions(+) create mode 100644 embassy-nrf/src/rtc.rs create mode 100644 examples/nrf52840/src/bin/rtc.rs diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index de8dfd391..7c783c2d4 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - changed: nrf54l: Disable glitch detection and enable DC/DC in init. - changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4 - changed: add persist() method for gpio and ppi +- added: basic RTC driver ## 0.7.0 - 2025-08-26 diff --git a/embassy-nrf/src/chips/nrf51.rs b/embassy-nrf/src/chips/nrf51.rs index fd13ae5c4..393af4e75 100644 --- a/embassy-nrf/src/chips/nrf51.rs +++ b/embassy-nrf/src/chips/nrf51.rs @@ -110,6 +110,9 @@ impl_timer!(TIMER2, TIMER2, TIMER2); impl_rng!(RNG, RNG, RNG); +impl_rtc!(RTC0, RTC0, RTC0); +impl_rtc!(RTC1, RTC1, RTC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 7e72df8fc..427298a56 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -156,6 +156,9 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +impl_rtc!(RTC1, RTC1, RTC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index e388e44e8..c99b54265 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -166,6 +166,9 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +impl_rtc!(RTC1, RTC1, RTC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 96b8df30b..5f7a67f06 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -168,6 +168,9 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +impl_rtc!(RTC1, RTC1, RTC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index ad461b153..1677fcf02 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -164,6 +164,9 @@ impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER3, TIMER3, TIMER3, extended); +impl_rtc!(RTC0, RTC0, RTC0); +impl_rtc!(RTC1, RTC1, RTC1); + impl_qdec!(QDEC, QDEC, QDEC); impl_rng!(RNG, RNG, RNG); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index cf2abf23a..6b735e218 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -182,6 +182,10 @@ impl_twim!(TWISPI1, TWIM1, TWISPI1); impl_twis!(TWISPI0, TWIS0, TWISPI0); impl_twis!(TWISPI1, TWIS1, TWISPI1); +impl_rtc!(RTC0, RTC0, RTC0); +impl_rtc!(RTC1, RTC1, RTC1); +impl_rtc!(RTC2, RTC2, RTC2); + impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index e46eb1d2b..b9c75f54b 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -223,6 +223,10 @@ impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_timer!(TIMER4, TIMER4, TIMER4, extended); +impl_rtc!(RTC0, RTC0, RTC0); +impl_rtc!(RTC1, RTC1, RTC1); +impl_rtc!(RTC2, RTC2, RTC2); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 88747843d..ccfa1f747 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -220,6 +220,10 @@ impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_timer!(TIMER4, TIMER4, TIMER4, extended); +impl_rtc!(RTC0, RTC0, RTC0); +impl_rtc!(RTC1, RTC1, RTC1); +impl_rtc!(RTC2, RTC2, RTC2); + impl_qspi!(QSPI, QSPI, QSPI); impl_pdm!(PDM, PDM, PDM); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index c0290b7a4..eb429db21 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -369,6 +369,9 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +impl_rtc!(RTC1, RTC1, RTC1); + impl_qspi!(QSPI, QSPI, QSPI); impl_pdm!(PDM0, PDM0, PDM0); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index d4c3e5353..a8de288a9 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -218,6 +218,9 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +impl_rtc!(RTC1, RTC1, RTC1); + impl_rng!(RNG, RNG, RNG); impl_pin!(P0_00, 0, 0); diff --git a/embassy-nrf/src/chips/nrf9120.rs b/embassy-nrf/src/chips/nrf9120.rs index e8ddbf86f..9008a23a8 100644 --- a/embassy-nrf/src/chips/nrf9120.rs +++ b/embassy-nrf/src/chips/nrf9120.rs @@ -276,6 +276,9 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +impl_rtc!(RTC1, RTC1, RTC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 5d04a72e5..dbb06d71a 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -276,6 +276,9 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +impl_rtc!(RTC1, RTC1, RTC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 897e660b8..e0847a312 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -155,6 +155,8 @@ pub mod reset; #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] pub mod rng; #[cfg(not(feature = "_nrf54l"))] // TODO +pub mod rtc; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod saadc; #[cfg(not(feature = "_nrf54l"))] // TODO diff --git a/embassy-nrf/src/rtc.rs b/embassy-nrf/src/rtc.rs new file mode 100644 index 000000000..5dd427fde --- /dev/null +++ b/embassy-nrf/src/rtc.rs @@ -0,0 +1,272 @@ +//! Low-level RTC driver. + +#![macro_use] + +use embassy_hal_internal::{Peri, PeripheralType}; + +use crate::chip::interrupt::typelevel::Interrupt as _; +use crate::pac; + +/// Prescaler has an invalid value which exceeds 12 bits. +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PrescalerOutOfRangeError(u32); + +/// Compare value has an invalid value which exceeds 24 bits. +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct CompareOutOfRangeError(u32); + +/// Compare register is not implemented for RTC0. +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct CompareRegNotImplementedError; + +/// Interrupts/Events that can be generated by the RTCn peripheral. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Interrupt { + /// Tick interrupt. + Tick, + /// Overflow interrupt. + Overflow, + /// Compare 0 interrupt. + Compare0, + /// Compare 1 interrupt. + Compare1, + /// Compare 2 interrupt. + Compare2, + /// Compare 3 interrupt. Only implemented for RTC1 and RTC2. + Compare3, +} + +/// Compare registers available on the RTCn. +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum CompareChannel { + /// Channel 0 + _0, + /// Channel 1 + _1, + /// Channel 2 + _2, + /// Channel 3. Only implemented for RTC1 and RTC2. + _3, +} + +pub(crate) trait SealedInstance { + fn regs() -> pac::rtc::Rtc; +} + +/// Basic RTC instance. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: crate::interrupt::typelevel::Interrupt; + + /// Unsafely create a peripheral instance. + /// + /// # Safety + /// + /// Potentially allows to create multiple instances of the driver for the same peripheral + /// which can lead to undefined behavior. + unsafe fn steal() -> Peri<'static, Self>; +} + +macro_rules! impl_rtc { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::rtc::SealedInstance for peripherals::$type { + #[inline] + fn regs() -> pac::rtc::Rtc { + unsafe { pac::rtc::Rtc::from_ptr(pac::$pac_type.as_ptr()) } + } + } + + impl crate::rtc::Instance for peripherals::$type { + type Interrupt = crate::interrupt::typelevel::$irq; + + unsafe fn steal() -> embassy_hal_internal::Peri<'static, Self> { + unsafe { peripherals::$type::steal() } + } + } + }; +} + +/// nRF RTC driver. +/// +/// If the `time-driver-rtc1` feature is enabled, the creation of a driver with RTC1 will conflict +/// with the time driver implementation. +pub struct Rtc<'d, T: Instance>(Peri<'d, T>); + +impl<'d, T: Instance> Rtc<'d, T> { + /// Create a new `Rtc` driver. + /// + /// fRTC = 32_768 / (`prescaler` + 1 ) + pub fn new(rtc: Peri<'d, T>, prescaler: u32) -> Result { + if prescaler >= (1 << 12) { + return Err(PrescalerOutOfRangeError(prescaler)); + } + + T::regs().prescaler().write(|w| w.set_prescaler(prescaler as u16)); + Ok(Self(rtc)) + } + + /// Create a new `Rtc` driver, configuring it to run at the given frequency. + pub fn new_for_freq(rtc: Peri<'d, T>, freq_hz: u32) -> Result { + let prescaler = (32_768 / freq_hz).saturating_sub(1); + Self::new(rtc, prescaler) + } + + /// Steal the RTC peripheral, without checking if it's already taken. + /// + /// # Safety + /// + /// Potentially allows to create multiple instances of the driver for the same peripheral + /// which can lead to undefined behavior. + pub unsafe fn steal() -> Self { + Self(unsafe { T::steal() }) + } + + /// Direct access to the RTC registers. + #[inline] + pub fn regs(&mut self) -> pac::rtc::Rtc { + T::regs() + } + + /// Enable the RTC. + #[inline] + pub fn enable(&mut self) { + T::regs().tasks_start().write_value(1); + } + + /// Disable the RTC. + #[inline] + pub fn disable(&mut self) { + T::regs().tasks_stop().write_value(1); + } + + /// Enables interrupts for the given [RtcInterrupt] source. + /// + /// Optionally also enables the interrupt in the NVIC. + pub fn enable_interrupt(&mut self, int: Interrupt, enable_in_nvic: bool) { + let regs = T::regs(); + match int { + Interrupt::Tick => regs.intenset().write(|w| w.set_tick(true)), + Interrupt::Overflow => regs.intenset().write(|w| w.set_ovrflw(true)), + Interrupt::Compare0 => regs.intenset().write(|w| w.set_compare(0, true)), + Interrupt::Compare1 => regs.intenset().write(|w| w.set_compare(1, true)), + Interrupt::Compare2 => regs.intenset().write(|w| w.set_compare(2, true)), + Interrupt::Compare3 => regs.intenset().write(|w| w.set_compare(3, true)), + } + if enable_in_nvic { + unsafe { T::Interrupt::enable() }; + } + } + + /// Disables interrupts for the given [RtcInterrupt] source. + /// + /// Optionally also disables the interrupt in the NVIC. + pub fn disable_interrupt(&mut self, int: Interrupt, disable_in_nvic: bool) { + let regs = T::regs(); + match int { + Interrupt::Tick => regs.intenclr().write(|w| w.set_tick(true)), + Interrupt::Overflow => regs.intenclr().write(|w| w.set_ovrflw(true)), + Interrupt::Compare0 => regs.intenclr().write(|w| w.set_compare(0, true)), + Interrupt::Compare1 => regs.intenclr().write(|w| w.set_compare(1, true)), + Interrupt::Compare2 => regs.intenclr().write(|w| w.set_compare(2, true)), + Interrupt::Compare3 => regs.intenclr().write(|w| w.set_compare(3, true)), + } + if disable_in_nvic { + T::Interrupt::disable(); + } + } + + /// Enable the generation of a hardware event from a given stimulus. + pub fn enable_event(&mut self, evt: Interrupt) { + let regs = T::regs(); + match evt { + Interrupt::Tick => regs.evtenset().write(|w| w.set_tick(true)), + Interrupt::Overflow => regs.evtenset().write(|w| w.set_ovrflw(true)), + Interrupt::Compare0 => regs.evtenset().write(|w| w.set_compare(0, true)), + Interrupt::Compare1 => regs.evtenset().write(|w| w.set_compare(1, true)), + Interrupt::Compare2 => regs.evtenset().write(|w| w.set_compare(2, true)), + Interrupt::Compare3 => regs.evtenset().write(|w| w.set_compare(3, true)), + } + } + + /// Disable the generation of a hardware event from a given stimulus. + pub fn disable_event(&mut self, evt: Interrupt) { + let regs = T::regs(); + match evt { + Interrupt::Tick => regs.evtenclr().write(|w| w.set_tick(true)), + Interrupt::Overflow => regs.evtenclr().write(|w| w.set_ovrflw(true)), + Interrupt::Compare0 => regs.evtenclr().write(|w| w.set_compare(0, true)), + Interrupt::Compare1 => regs.evtenclr().write(|w| w.set_compare(1, true)), + Interrupt::Compare2 => regs.evtenclr().write(|w| w.set_compare(2, true)), + Interrupt::Compare3 => regs.evtenclr().write(|w| w.set_compare(3, true)), + } + } + + /// Resets the given event. + pub fn reset_event(&mut self, evt: Interrupt) { + let regs = T::regs(); + match evt { + Interrupt::Tick => regs.events_tick().write_value(0), + Interrupt::Overflow => regs.events_ovrflw().write_value(0), + Interrupt::Compare0 => regs.events_compare(0).write_value(0), + Interrupt::Compare1 => regs.events_compare(1).write_value(0), + Interrupt::Compare2 => regs.events_compare(2).write_value(0), + Interrupt::Compare3 => regs.events_compare(3).write_value(0), + } + } + + /// Checks if the given event has been triggered. + pub fn is_event_triggered(&self, evt: Interrupt) -> bool { + let regs = T::regs(); + let val = match evt { + Interrupt::Tick => regs.events_tick().read(), + Interrupt::Overflow => regs.events_ovrflw().read(), + Interrupt::Compare0 => regs.events_compare(0).read(), + Interrupt::Compare1 => regs.events_compare(1).read(), + Interrupt::Compare2 => regs.events_compare(2).read(), + Interrupt::Compare3 => regs.events_compare(3).read(), + }; + val == 1 + } + + /// Set the compare value of a given register. The compare registers have a width + /// of 24 bits. + pub fn set_compare(&mut self, reg: CompareChannel, val: u32) -> Result<(), CompareOutOfRangeError> { + if val >= (1 << 24) { + return Err(CompareOutOfRangeError(val)); + } + + let reg = match reg { + CompareChannel::_0 => 0, + CompareChannel::_1 => 1, + CompareChannel::_2 => 2, + CompareChannel::_3 => 3, + }; + + T::regs().cc(reg).write(|w| w.set_compare(val)); + Ok(()) + } + + /// Clear the Real Time Counter. + #[inline] + pub fn clear(&self) { + T::regs().tasks_clear().write_value(1); + } + + /// Obtain the current value of the Real Time Counter, 24 bits of range. + #[inline] + pub fn read(&self) -> u32 { + T::regs().counter().read().counter() + } + + /// Relase the RTC, returning the underlying peripheral instance. + #[inline] + pub fn release(self) -> Peri<'d, T> { + self.0 + } +} diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 452e83b7e..ca3c6f863 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -35,6 +35,7 @@ embedded-hal-async = { version = "1.0" } embedded-hal-bus = { version = "0.1", features = ["async"] } num-integer = { version = "0.1.45", default-features = false } microfft = "0.5.0" +portable-atomic = "1" [profile.release] debug = 2 diff --git a/examples/nrf52840/src/bin/rtc.rs b/examples/nrf52840/src/bin/rtc.rs new file mode 100644 index 000000000..a3df7da14 --- /dev/null +++ b/examples/nrf52840/src/bin/rtc.rs @@ -0,0 +1,57 @@ +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::interrupt; +use embassy_nrf::rtc::Rtc; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use portable_atomic::AtomicU64; +use {defmt_rtt as _, panic_probe as _}; + +// 64 bit counter which will never overflow. +static TICK_COUNTER: AtomicU64 = AtomicU64::new(0); +static RTC: Mutex>>> = + Mutex::new(RefCell::new(None)); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + defmt::println!("nRF52840 RTC example"); + let p = embassy_nrf::init(Default::default()); + let mut led = Output::new(p.P0_13, Level::High, OutputDrive::Standard); + // Counter resolution is 125 ms. + let mut rtc = Rtc::new(p.RTC0, (1 << 12) - 1).unwrap(); + rtc.enable_interrupt(embassy_nrf::rtc::Interrupt::Tick, true); + rtc.enable_event(embassy_nrf::rtc::Interrupt::Tick); + rtc.enable(); + RTC.lock(|r| { + let mut rtc_borrow = r.borrow_mut(); + *rtc_borrow = Some(rtc); + }); + + let mut last_counter_val = 0; + loop { + let current = TICK_COUNTER.load(core::sync::atomic::Ordering::Relaxed); + if current != last_counter_val { + led.toggle(); + last_counter_val = current; + } + } +} + +#[interrupt] +fn RTC0() { + // For 64-bit, we do not need to worry about overflowing, at least not for realistic program + // lifetimes. + TICK_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed); + RTC.lock(|r| { + let mut rtc_borrow = r.borrow_mut(); + rtc_borrow + .as_mut() + .unwrap() + .reset_event(embassy_nrf::rtc::Interrupt::Tick); + }); +} -- cgit From d1b55faace4b5059c726d94dd2d7f1ad3805ab48 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 22 Sep 2025 00:43:02 +0200 Subject: code review changes --- embassy-nrf/src/chips/nrf51.rs | 2 ++ embassy-nrf/src/chips/nrf52805.rs | 4 +++- embassy-nrf/src/chips/nrf52810.rs | 2 ++ embassy-nrf/src/chips/nrf52811.rs | 2 ++ embassy-nrf/src/chips/nrf52820.rs | 2 ++ embassy-nrf/src/chips/nrf52832.rs | 2 ++ embassy-nrf/src/chips/nrf52833.rs | 2 ++ embassy-nrf/src/chips/nrf52840.rs | 2 ++ embassy-nrf/src/chips/nrf5340_app.rs | 2 ++ embassy-nrf/src/chips/nrf5340_net.rs | 2 ++ embassy-nrf/src/chips/nrf9120.rs | 2 ++ embassy-nrf/src/chips/nrf9160.rs | 2 ++ embassy-nrf/src/rtc.rs | 15 ++++----------- 13 files changed, 29 insertions(+), 12 deletions(-) diff --git a/embassy-nrf/src/chips/nrf51.rs b/embassy-nrf/src/chips/nrf51.rs index 393af4e75..3976e8ff0 100644 --- a/embassy-nrf/src/chips/nrf51.rs +++ b/embassy-nrf/src/chips/nrf51.rs @@ -8,6 +8,7 @@ pub const FLASH_SIZE: usize = 128 * 1024; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, // WDT @@ -111,6 +112,7 @@ impl_timer!(TIMER2, TIMER2, TIMER2); impl_rng!(RNG, RNG, RNG); impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] impl_rtc!(RTC1, RTC1, RTC1); impl_pin!(P0_00, 0, 0); diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 427298a56..63ba6999a 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -12,6 +12,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'B'; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature="time-driver-rtc1"))] RTC1, // WDT @@ -157,6 +158,7 @@ impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] impl_rtc!(RTC1, RTC1, RTC1); impl_pin!(P0_00, 0, 0); @@ -237,12 +239,12 @@ embassy_hal_internal::interrupt_mod!( TIMER0, TIMER1, TIMER2, - RTC0, TEMP, RNG, ECB, AAR_CCM, WDT, + RTC0, RTC1, QDEC, EGU0_SWI0, diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index c99b54265..7f744f9fb 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -12,6 +12,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'E'; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature="time-driver-rtc1"))] RTC1, // WDT @@ -167,6 +168,7 @@ impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] impl_rtc!(RTC1, RTC1, RTC1); impl_pin!(P0_00, 0, 0); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 5f7a67f06..908167e31 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -12,6 +12,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'B'; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature="time-driver-rtc1"))] RTC1, // WDT @@ -169,6 +170,7 @@ impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] impl_rtc!(RTC1, RTC1, RTC1); impl_pin!(P0_00, 0, 0); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 1677fcf02..22360575b 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -15,6 +15,7 @@ embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature="time-driver-rtc1"))] RTC1, // WDT @@ -165,6 +166,7 @@ impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] impl_rtc!(RTC1, RTC1, RTC1); impl_qdec!(QDEC, QDEC, QDEC); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 6b735e218..1598df3fe 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -16,6 +16,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'G'; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature="time-driver-rtc1"))] RTC1, RTC2, @@ -183,6 +184,7 @@ impl_twis!(TWISPI0, TWIS0, TWISPI0); impl_twis!(TWISPI1, TWIS1, TWISPI1); impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] impl_rtc!(RTC1, RTC1, RTC1); impl_rtc!(RTC2, RTC2, RTC2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index b9c75f54b..6931fb064 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -15,6 +15,7 @@ embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, RTC2, @@ -224,6 +225,7 @@ impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_timer!(TIMER4, TIMER4, TIMER4, extended); impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] impl_rtc!(RTC1, RTC1, RTC1); impl_rtc!(RTC2, RTC2, RTC2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index ccfa1f747..5fa521aae 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -15,6 +15,7 @@ embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, RTC2, @@ -221,6 +222,7 @@ impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_timer!(TIMER4, TIMER4, TIMER4, extended); impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] impl_rtc!(RTC1, RTC1, RTC1); impl_rtc!(RTC2, RTC2, RTC2); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index eb429db21..730c9842d 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -168,6 +168,7 @@ embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, // WDT @@ -370,6 +371,7 @@ impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] impl_rtc!(RTC1, RTC1, RTC1); impl_qspi!(QSPI, QSPI, QSPI); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index a8de288a9..413afc5c5 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -59,6 +59,7 @@ pub const FLASH_SIZE: usize = 256 * 1024; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, // WDT @@ -219,6 +220,7 @@ impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] impl_rtc!(RTC1, RTC1, RTC1); impl_rng!(RNG, RNG, RNG); diff --git a/embassy-nrf/src/chips/nrf9120.rs b/embassy-nrf/src/chips/nrf9120.rs index 9008a23a8..5aee19d97 100644 --- a/embassy-nrf/src/chips/nrf9120.rs +++ b/embassy-nrf/src/chips/nrf9120.rs @@ -131,6 +131,7 @@ pub const FLASH_SIZE: usize = 1024 * 1024; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, // WDT @@ -277,6 +278,7 @@ impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] impl_rtc!(RTC1, RTC1, RTC1); impl_pin!(P0_00, 0, 0); diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index dbb06d71a..64aec217c 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -131,6 +131,7 @@ pub const FLASH_SIZE: usize = 1024 * 1024; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, // WDT @@ -277,6 +278,7 @@ impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] impl_rtc!(RTC1, RTC1, RTC1); impl_pin!(P0_00, 0, 0); diff --git a/embassy-nrf/src/rtc.rs b/embassy-nrf/src/rtc.rs index 5dd427fde..1a90d1e24 100644 --- a/embassy-nrf/src/rtc.rs +++ b/embassy-nrf/src/rtc.rs @@ -17,11 +17,6 @@ pub struct PrescalerOutOfRangeError(u32); #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CompareOutOfRangeError(u32); -/// Compare register is not implemented for RTC0. -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct CompareRegNotImplementedError; - /// Interrupts/Events that can be generated by the RTCn peripheral. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -93,15 +88,12 @@ macro_rules! impl_rtc { } /// nRF RTC driver. -/// -/// If the `time-driver-rtc1` feature is enabled, the creation of a driver with RTC1 will conflict -/// with the time driver implementation. pub struct Rtc<'d, T: Instance>(Peri<'d, T>); impl<'d, T: Instance> Rtc<'d, T> { /// Create a new `Rtc` driver. /// - /// fRTC = 32_768 / (`prescaler` + 1 ) + /// fRTC \[Hz\] = 32_768 / (`prescaler` + 1 ) pub fn new(rtc: Peri<'d, T>, prescaler: u32) -> Result { if prescaler >= (1 << 12) { return Err(PrescalerOutOfRangeError(prescaler)); @@ -128,6 +120,7 @@ impl<'d, T: Instance> Rtc<'d, T> { } /// Direct access to the RTC registers. + #[cfg(feature = "unstable-pac")] #[inline] pub fn regs(&mut self) -> pac::rtc::Rtc { T::regs() @@ -145,7 +138,7 @@ impl<'d, T: Instance> Rtc<'d, T> { T::regs().tasks_stop().write_value(1); } - /// Enables interrupts for the given [RtcInterrupt] source. + /// Enables interrupts for the given [Interrupt] source. /// /// Optionally also enables the interrupt in the NVIC. pub fn enable_interrupt(&mut self, int: Interrupt, enable_in_nvic: bool) { @@ -163,7 +156,7 @@ impl<'d, T: Instance> Rtc<'d, T> { } } - /// Disables interrupts for the given [RtcInterrupt] source. + /// Disables interrupts for the given [Interrupt] source. /// /// Optionally also disables the interrupt in the NVIC. pub fn disable_interrupt(&mut self, int: Interrupt, disable_in_nvic: bool) { -- cgit From fd1c1635419f8281edda3892f08a91f0e315667a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 22 Sep 2025 00:32:50 +0200 Subject: ci: add cargo manifest check --- .github/ci/build-nightly.sh | 2 +- .github/ci/build-xtensa.sh | 2 +- .github/ci/build.sh | 2 +- .github/ci/crlf.sh | 17 ----------------- .github/ci/janitor.sh | 15 +++++++++++++++ 5 files changed, 18 insertions(+), 20 deletions(-) delete mode 100755 .github/ci/crlf.sh create mode 100755 .github/ci/janitor.sh diff --git a/.github/ci/build-nightly.sh b/.github/ci/build-nightly.sh index 2d7c4db3f..801d470a1 100755 --- a/.github/ci/build-nightly.sh +++ b/.github/ci/build-nightly.sh @@ -23,7 +23,7 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 280829ad163f1444999468a57d28fb7c412babbe +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 2357881abb81a0a3672ff992e199963f9f63bb10 ./ci-nightly.sh diff --git a/.github/ci/build-xtensa.sh b/.github/ci/build-xtensa.sh index b6626639d..1a97b21b1 100755 --- a/.github/ci/build-xtensa.sh +++ b/.github/ci/build-xtensa.sh @@ -25,7 +25,7 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 280829ad163f1444999468a57d28fb7c412babbe +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 2357881abb81a0a3672ff992e199963f9f63bb10 ./ci-xtensa.sh diff --git a/.github/ci/build.sh b/.github/ci/build.sh index 59bcefed6..72ffa9f1b 100755 --- a/.github/ci/build.sh +++ b/.github/ci/build.sh @@ -28,7 +28,7 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 280829ad163f1444999468a57d28fb7c412babbe +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 2357881abb81a0a3672ff992e199963f9f63bb10 ./ci.sh diff --git a/.github/ci/crlf.sh b/.github/ci/crlf.sh deleted file mode 100755 index 69838ce88..000000000 --- a/.github/ci/crlf.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -## on push branch~=gh-readonly-queue/main/.* -## on pull_request - -set -euo pipefail - -FILES_WITH_CRLF=$(find ! -path "./.git/*" -not -type d | xargs file -N | (grep " CRLF " || true)) - -if [ -z "$FILES_WITH_CRLF" ]; then - echo -e "No files with CRLF endings found." - exit 0 -else - NR_FILES=$(echo "$FILES_WITH_CRLF" | wc -l) - echo -e "ERROR: Found ${NR_FILES} files with CRLF endings." - echo "$FILES_WITH_CRLF" - exit "$NR_FILES" -fi diff --git a/.github/ci/janitor.sh b/.github/ci/janitor.sh new file mode 100755 index 000000000..58ecb8475 --- /dev/null +++ b/.github/ci/janitor.sh @@ -0,0 +1,15 @@ +#!/bin/bash +## on push branch~=gh-readonly-queue/main/.* +## on pull_request + +set -euo pipefail + +export RUSTUP_HOME=/ci/cache/rustup +export CARGO_HOME=/ci/cache/cargo +export CARGO_TARGET_DIR=/ci/cache/target +export PATH=$CARGO_HOME/bin:$PATH + +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 2357881abb81a0a3672ff992e199963f9f63bb10 + +cargo embassy-devtool check-crlf +cargo embassy-devtool check-manifest -- cgit From 4d71f432ad05cd8cce50b13cf6de6a1422f3b401 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 22 Sep 2025 00:47:08 +0200 Subject: Update manifests to satisfy new checks. --- cyw43-pio/Cargo.toml | 3 +++ embassy-boot-nrf/Cargo.toml | 3 ++- embassy-boot/Cargo.toml | 2 ++ embassy-embedded-hal/Cargo.toml | 1 + embassy-executor/Cargo.toml | 6 ++++++ embassy-futures/Cargo.toml | 4 ++++ embassy-hal-internal/Cargo.toml | 3 +++ embassy-imxrt/Cargo.toml | 2 +- embassy-net-driver-channel/Cargo.toml | 4 ++++ embassy-net-driver/Cargo.toml | 3 +++ embassy-net-enc28j60/Cargo.toml | 4 ++++ embassy-net-wiznet/Cargo.toml | 3 +++ embassy-net/Cargo.toml | 2 ++ embassy-nrf/Cargo.toml | 2 ++ embassy-nxp/Cargo.toml | 1 + embassy-rp/Cargo.toml | 4 ++++ embassy-stm32/Cargo.toml | 4 ++++ embassy-sync/Cargo.toml | 2 ++ embassy-time/Cargo.toml | 5 +++++ embassy-usb-dfu/Cargo.toml | 3 +++ embassy-usb-driver/Cargo.toml | 3 +++ embassy-usb-synopsys-otg/Cargo.toml | 4 ++++ embassy-usb/Cargo.toml | 1 + examples/mimxrt1011/Cargo.toml | 2 +- examples/mimxrt1062-evk/Cargo.toml | 2 +- examples/mimxrt6/Cargo.toml | 2 +- tests/perf-client/Cargo.toml | 1 + tests/perf-server/Cargo.toml | 1 + tests/utils/Cargo.toml | 1 + 29 files changed, 73 insertions(+), 5 deletions(-) diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index 40dd02f61..6ab5c453e 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -15,6 +15,9 @@ embassy-rp = { version = "0.8.0", path = "../embassy-rp" } fixed = "1.23.1" defmt = { version = "1.0.1", optional = true } +[features] +defmt = ["dep:defmt"] + [package.metadata.embassy] build = [ {target = "thumbv6m-none-eabi", features = ["embassy-rp/rp2040"]}, diff --git a/embassy-boot-nrf/Cargo.toml b/embassy-boot-nrf/Cargo.toml index 3b631ef0c..49dff061a 100644 --- a/embassy-boot-nrf/Cargo.toml +++ b/embassy-boot-nrf/Cargo.toml @@ -52,6 +52,7 @@ defmt = [ "embassy-boot/defmt", "embassy-nrf/defmt", ] +log = ["dep:log"] softdevice = [ - "nrf-softdevice-mbr", + "dep:nrf-softdevice-mbr", ] diff --git a/embassy-boot/Cargo.toml b/embassy-boot/Cargo.toml index ed0242c13..a18438c81 100644 --- a/embassy-boot/Cargo.toml +++ b/embassy-boot/Cargo.toml @@ -45,6 +45,8 @@ critical-section = { version = "1.1.1", features = ["std"] } ed25519-dalek = { version = "2", default-features = false, features = ["std", "rand_core", "digest"] } [features] +defmt = ["dep:defmt"] +log = ["dep:log"] ed25519-dalek = ["dep:ed25519-dalek", "_verify"] ed25519-salty = ["dep:salty", "_verify"] flash-erase-zero = [] diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index a66e01717..8b8122567 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -25,6 +25,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-embed target = "x86_64-unknown-linux-gnu" [features] +defmt = ["dep:defmt"] time = ["dep:embassy-time"] [dependencies] diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index f6dce5c0e..61d060630 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -123,6 +123,12 @@ rustversion = "1.0.21" ## Enable nightly-only features nightly = ["embassy-executor-macros/nightly"] +## Enable defmt logging +defmt = ["dep:defmt"] + +## Enable log logging +log = ["dep:log"] + # Enables turbo wakers, which requires patching core. Not surfaced in the docs by default due to # being an complicated advanced and undocumented feature. # See: https://github.com/embassy-rs/embassy/pull/1263 diff --git a/embassy-futures/Cargo.toml b/embassy-futures/Cargo.toml index 2d62b28e5..07d5219cf 100644 --- a/embassy-futures/Cargo.toml +++ b/embassy-futures/Cargo.toml @@ -26,3 +26,7 @@ features = ["defmt"] [dependencies] defmt = { version = "1.0.1", optional = true } log = { version = "0.4.14", optional = true } + +[features] +defmt = ["dep:defmt"] +log = ["dep:log"] diff --git a/embassy-hal-internal/Cargo.toml b/embassy-hal-internal/Cargo.toml index b4c52ccfa..11dcc2466 100644 --- a/embassy-hal-internal/Cargo.toml +++ b/embassy-hal-internal/Cargo.toml @@ -14,6 +14,9 @@ categories = [ [features] +defmt = ["dep:defmt"] +log = ["dep:log"] + # Define the number of NVIC priority bits. prio-bits-0 = [] prio-bits-1 = [] diff --git a/embassy-imxrt/Cargo.toml b/embassy-imxrt/Cargo.toml index 8a9c1252e..7561640dd 100644 --- a/embassy-imxrt/Cargo.toml +++ b/embassy-imxrt/Cargo.toml @@ -2,7 +2,7 @@ name = "embassy-imxrt" version = "0.1.0" edition = "2021" -license = "MIT" +license = "MIT OR Apache-2.0" description = "Embassy Hardware Abstraction Layer (HAL) for the IMXRT microcontroller" keywords = ["embedded", "async", "imxrt", "rt600", "embedded-hal"] categories = ["embedded", "hardware-support", "no-std", "asynchronous"] diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml index cf498c59f..1e40c2d87 100644 --- a/embassy-net-driver-channel/Cargo.toml +++ b/embassy-net-driver-channel/Cargo.toml @@ -28,3 +28,7 @@ log = { version = "0.4.14", optional = true } embassy-sync = { version = "0.7.2", path = "../embassy-sync" } embassy-futures = { version = "0.1.2", path = "../embassy-futures" } embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } + +[features] +defmt = ["dep:defmt"] +log = ["dep:log"] diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml index 34bc6c91a..a36e412ad 100644 --- a/embassy-net-driver/Cargo.toml +++ b/embassy-net-driver/Cargo.toml @@ -23,3 +23,6 @@ features = ["defmt"] [dependencies] defmt = { version = "1.0.1", optional = true } + +[features] +defmt = ["dep:defmt"] diff --git a/embassy-net-enc28j60/Cargo.toml b/embassy-net-enc28j60/Cargo.toml index a3e3285a3..e7bad118b 100644 --- a/embassy-net-enc28j60/Cargo.toml +++ b/embassy-net-enc28j60/Cargo.toml @@ -19,6 +19,10 @@ embassy-futures = { version = "0.1.2", path = "../embassy-futures" } defmt = { version = "1.0.1", optional = true } log = { version = "0.4.14", optional = true } +[features] +defmt = ["dep:defmt", "embassy-net-driver/defmt"] +log = ["dep:log"] + [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-enc28j60-v$VERSION/embassy-net-enc28j60/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-enc28j60/src/" diff --git a/embassy-net-wiznet/Cargo.toml b/embassy-net-wiznet/Cargo.toml index 36c349df1..6e6dccebd 100644 --- a/embassy-net-wiznet/Cargo.toml +++ b/embassy-net-wiznet/Cargo.toml @@ -17,6 +17,9 @@ embassy-time = { version = "0.5.0", path = "../embassy-time" } embassy-futures = { version = "0.1.2", path = "../embassy-futures" } defmt = { version = "1.0.1", optional = true } +[features] +defmt = ["dep:defmt", "embassy-net-driver-channel/defmt"] + [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-wiznet-v$VERSION/embassy-net-wiznet/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-wiznet/src/" diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 61a2c858a..31ce7e9a6 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -53,6 +53,8 @@ features = ["defmt", "tcp", "udp", "raw", "dns", "icmp", "dhcpv4", "proto-ipv6", [features] ## Enable defmt defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt", "embassy-time/defmt", "heapless/defmt-03", "defmt?/ip_in_core"] +## Enable log +log = ["dep:log"] ## Trace all raw received and transmitted packets using defmt or log. packet-trace = [] diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 4afd28fbd..1af633500 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -66,6 +66,8 @@ time = ["dep:embassy-time", "embassy-embedded-hal/time"] ## Enable defmt defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt", "embassy-usb-driver/defmt", "embassy-embedded-hal/defmt"] +## Enable log +log = ["dep:log"] ## Reexport the PAC for the currently enabled chip at `embassy_nrf::pac` (unstable) unstable-pac = [] diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index 455915f29..073fdabe4 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -2,6 +2,7 @@ name = "embassy-nxp" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" publish = false diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 101914a36..f6b0900f2 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -45,6 +45,10 @@ rt = [ "rp-pac/rt" ] ## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. defmt = ["dep:defmt", "embassy-usb-driver/defmt", "embassy-hal-internal/defmt"] +## Enable log support +log = ["dep:log"] +## Enable chrono support +chrono = ["dep:chrono"] ## Configure the [`critical-section`](https://docs.rs/critical-section) crate to use an implementation that is safe for multicore use on rp2040. critical-section-impl = ["critical-section/restore-state-u8"] diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 6cd8ed262..369fabc50 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -225,6 +225,10 @@ defmt = [ "embassy-usb-synopsys-otg/defmt", "stm32-metapac/defmt" ] +## Use log for logging +log = ["dep:log"] +## Enable chrono support +chrono = ["dep:chrono"] exti = [] low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ] diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 6494da727..64d76baba 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -27,6 +27,8 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-sync/ target = "thumbv7em-none-eabi" [features] +defmt = ["dep:defmt"] +log = ["dep:log"] std = [] turbowakers = [] diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 2d7c3c1fa..bad6ecf97 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -31,6 +31,11 @@ target = "x86_64-unknown-linux-gnu" features = ["defmt", "std"] [features] +## Enable defmt +defmt = ["dep:defmt"] +## Enable log +log = ["dep:log"] + ## Display the time since startup next to defmt log messages. ## At most 1 `defmt-timestamp-uptime-*` feature can be used. ## `defmt-timestamp-uptime` is provided for backwards compatibility (provides the same format as `uptime-us`). diff --git a/embassy-usb-dfu/Cargo.toml b/embassy-usb-dfu/Cargo.toml index 4a65c6895..e70ab970e 100644 --- a/embassy-usb-dfu/Cargo.toml +++ b/embassy-usb-dfu/Cargo.toml @@ -49,6 +49,9 @@ esp32c3-hal = { version = "0.13.0", optional = true, default-features = false } dfu = [] application = [] defmt = ["dep:defmt", "embassy-boot/defmt", "embassy-usb/defmt"] +log = ["dep:log"] +cortex-m = ["dep:cortex-m"] +esp32c3-hal = ["dep:esp32c3-hal"] ed25519-dalek = ["embassy-boot/ed25519-dalek", "_verify"] ed25519-salty = ["embassy-boot/ed25519-salty", "_verify"] diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml index de69bf694..6e4c31273 100644 --- a/embassy-usb-driver/Cargo.toml +++ b/embassy-usb-driver/Cargo.toml @@ -21,3 +21,6 @@ features = ["defmt"] [dependencies] embedded-io-async = "0.6.1" defmt = { version = "1", optional = true } + +[features] +defmt = ["dep:defmt"] diff --git a/embassy-usb-synopsys-otg/Cargo.toml b/embassy-usb-synopsys-otg/Cargo.toml index ec518ac93..61b14a215 100644 --- a/embassy-usb-synopsys-otg/Cargo.toml +++ b/embassy-usb-synopsys-otg/Cargo.toml @@ -23,3 +23,7 @@ embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } defmt = { version = "1.0.1", optional = true } log = { version = "0.4.14", optional = true } + +[features] +defmt = ["dep:defmt", "embassy-usb-driver/defmt"] +log = ["dep:log"] diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index e309eec93..bff48c4a6 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -31,6 +31,7 @@ features = ["defmt", "usbd-hid"] [features] defmt = ["dep:defmt", "embassy-usb-driver/defmt"] +log = ["dep:log"] usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] default = ["usbd-hid"] diff --git a/examples/mimxrt1011/Cargo.toml b/examples/mimxrt1011/Cargo.toml index 488f3167b..3038f5d4d 100644 --- a/examples/mimxrt1011/Cargo.toml +++ b/examples/mimxrt1011/Cargo.toml @@ -2,7 +2,7 @@ name = "embassy-imxrt1011-examples" version = "0.1.0" edition = "2021" -license = "MIT or Apache-2.0" +license = "MIT OR Apache-2.0" publish = false [dependencies] diff --git a/examples/mimxrt1062-evk/Cargo.toml b/examples/mimxrt1062-evk/Cargo.toml index ec6c5c872..82a24490d 100644 --- a/examples/mimxrt1062-evk/Cargo.toml +++ b/examples/mimxrt1062-evk/Cargo.toml @@ -2,7 +2,7 @@ name = "embassy-imxrt1062-evk-examples" version = "0.1.0" edition = "2021" -license = "MIT or Apache-2.0" +license = "MIT OR Apache-2.0" publish = false [dependencies] diff --git a/examples/mimxrt6/Cargo.toml b/examples/mimxrt6/Cargo.toml index 28de9d273..3f7ad8485 100644 --- a/examples/mimxrt6/Cargo.toml +++ b/examples/mimxrt6/Cargo.toml @@ -2,7 +2,7 @@ name = "embassy-imxrt-examples" version = "0.1.0" edition = "2021" -license = "MIT or Apache-2.0" +license = "MIT OR Apache-2.0" publish = false [dependencies] diff --git a/tests/perf-client/Cargo.toml b/tests/perf-client/Cargo.toml index 581b60d6f..c426fdd74 100644 --- a/tests/perf-client/Cargo.toml +++ b/tests/perf-client/Cargo.toml @@ -2,6 +2,7 @@ name = "perf-client" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" publish = false [dependencies] diff --git a/tests/perf-server/Cargo.toml b/tests/perf-server/Cargo.toml index 22614a33a..f048eade2 100644 --- a/tests/perf-server/Cargo.toml +++ b/tests/perf-server/Cargo.toml @@ -2,6 +2,7 @@ name = "perf-server" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" publish = false [dependencies] diff --git a/tests/utils/Cargo.toml b/tests/utils/Cargo.toml index f76feaa20..ddb990e0f 100644 --- a/tests/utils/Cargo.toml +++ b/tests/utils/Cargo.toml @@ -2,6 +2,7 @@ name = "test-utils" version = "0.1.0" edition = "2021" +license = "MIT OR Apache-2.0" publish = false [dependencies] -- cgit From 27df9288f343c2855d1daec00d127541826d664f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 22 Sep 2025 00:56:14 +0200 Subject: aaaaaa --- .github/ci/build-nightly.sh | 2 +- .github/ci/build-xtensa.sh | 2 +- .github/ci/build.sh | 2 +- .github/ci/janitor.sh | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ci/build-nightly.sh b/.github/ci/build-nightly.sh index 801d470a1..257d7ebd3 100755 --- a/.github/ci/build-nightly.sh +++ b/.github/ci/build-nightly.sh @@ -23,7 +23,7 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 2357881abb81a0a3672ff992e199963f9f63bb10 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 3ca80f7065acbe0b69b7da463fab60e744f9de79 ./ci-nightly.sh diff --git a/.github/ci/build-xtensa.sh b/.github/ci/build-xtensa.sh index 1a97b21b1..339e28467 100755 --- a/.github/ci/build-xtensa.sh +++ b/.github/ci/build-xtensa.sh @@ -25,7 +25,7 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 2357881abb81a0a3672ff992e199963f9f63bb10 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 3ca80f7065acbe0b69b7da463fab60e744f9de79 ./ci-xtensa.sh diff --git a/.github/ci/build.sh b/.github/ci/build.sh index 72ffa9f1b..d7201aedb 100755 --- a/.github/ci/build.sh +++ b/.github/ci/build.sh @@ -28,7 +28,7 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 2357881abb81a0a3672ff992e199963f9f63bb10 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 3ca80f7065acbe0b69b7da463fab60e744f9de79 ./ci.sh diff --git a/.github/ci/janitor.sh b/.github/ci/janitor.sh index 58ecb8475..bd04f47fc 100755 --- a/.github/ci/janitor.sh +++ b/.github/ci/janitor.sh @@ -9,7 +9,7 @@ export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target export PATH=$CARGO_HOME/bin:$PATH -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 2357881abb81a0a3672ff992e199963f9f63bb10 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 3ca80f7065acbe0b69b7da463fab60e744f9de79 cargo embassy-devtool check-crlf cargo embassy-devtool check-manifest -- cgit From 54a95927f054dd7229fd5e26c3acee509f71c82f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 22 Sep 2025 00:56:34 +0200 Subject: ci: use devtool to build docs. --- .github/ci/doc.sh | 54 +++--------------------------------------------------- 1 file changed, 3 insertions(+), 51 deletions(-) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 876c261a1..70ce110d1 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -9,62 +9,14 @@ set -euxo pipefail export RUSTUP_HOME=/ci/cache/rustup export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target -export BUILDER_THREADS=4 -export BUILDER_COMPRESS=true +export PATH=$CARGO_HOME/bin:$PATH mv rust-toolchain-nightly.toml rust-toolchain.toml -# force rustup to download the toolchain before starting building. -# Otherwise, the docs builder is running multiple instances of cargo rustdoc concurrently. -# They all see the toolchain is not installed and try to install it in parallel -# which makes rustup very sad -rustc --version > /dev/null +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 3ca80f7065acbe0b69b7da463fab60e744f9de79 -docserver build -i ./embassy-boot -o webroot/crates/embassy-boot/git.zup -docserver build -i ./embassy-boot-nrf -o webroot/crates/embassy-boot-nrf/git.zup -docserver build -i ./embassy-boot-rp -o webroot/crates/embassy-boot-rp/git.zup -docserver build -i ./embassy-boot-stm32 -o webroot/crates/embassy-boot-stm32/git.zup -docserver build -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup -docserver build -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup -docserver build -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup -docserver build -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup -docserver build -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup -docserver build -i ./embassy-mspm0 -o webroot/crates/embassy-mspm0/git.zup -docserver build -i ./embassy-nxp -o webroot/crates/embassy-nxp/git.zup -docserver build -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup -docserver build -i ./cyw43 -o webroot/crates/cyw43/git.zup -docserver build -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup -docserver build -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static - -docserver build -i ./embassy-time -o webroot/crates/embassy-time/git.zup -docserver build -i ./embassy-time-driver -o webroot/crates/embassy-time-driver/git.zup -docserver build -i ./embassy-time-queue-utils -o webroot/crates/embassy-time-queue-utils/git.zup - -docserver build -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup -docserver build -i ./embassy-usb-dfu -o webroot/crates/embassy-usb-dfu/git.zup -docserver build -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup -docserver build -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup -docserver build -i ./embassy-usb-synopsys-otg -o webroot/crates/embassy-usb-synopsys-otg/git.zup - -docserver build -i ./embassy-net -o webroot/crates/embassy-net/git.zup -docserver build -i ./embassy-net-nrf91 -o webroot/crates/embassy-net-nrf91/git.zup -docserver build -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup -docserver build -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup -docserver build -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup -docserver build -i ./embassy-net-ppp -o webroot/crates/embassy-net-ppp/git.zup -docserver build -i ./embassy-net-tuntap -o webroot/crates/embassy-net-tuntap/git.zup -docserver build -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup -docserver build -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup -docserver build -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup +cargo embassy-devtool doc -o webroot export KUBECONFIG=/ci/secrets/kubeconfig.yml POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) kubectl cp webroot/crates $POD:/data kubectl cp webroot/static $POD:/data - -# build and upload stm32 last -# so that it doesn't prevent other crates from getting docs updates when it breaks. - -rm -rf webroot -docserver build -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup -POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) -kubectl cp webroot/crates $POD:/data -- cgit From 0850f3b537feb66160b721c280d07d7c85518151 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 22 Sep 2025 01:00:38 +0200 Subject: Update Rust. --- embassy-executor/tests/ui/spawn_nonsend.stderr | 6 +++--- embassy-executor/tests/ui/unsafe_op_in_unsafe_task.stderr | 2 +- embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr | 8 ++++---- rust-toolchain-nightly.toml | 2 +- rust-toolchain.toml | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embassy-executor/tests/ui/spawn_nonsend.stderr b/embassy-executor/tests/ui/spawn_nonsend.stderr index 25bd7d78d..5a3131602 100644 --- a/embassy-executor/tests/ui/spawn_nonsend.stderr +++ b/embassy-executor/tests/ui/spawn_nonsend.stderr @@ -9,7 +9,7 @@ warning: unused import: `core::future::Future` error[E0277]: `*mut ()` cannot be sent between threads safely --> tests/ui/spawn_nonsend.rs:13:13 | -7 | #[embassy_executor::task] + 7 | #[embassy_executor::task] | ------------------------- within this `impl Sized` ... 13 | s.spawn(task(core::ptr::null_mut()).unwrap()); @@ -21,7 +21,7 @@ error[E0277]: `*mut ()` cannot be sent between threads safely note: required because it's used within this closure --> tests/ui/spawn_nonsend.rs:7:1 | -7 | #[embassy_executor::task] + 7 | #[embassy_executor::task] | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: required because it appears within the type `impl Sized` --> src/raw/mod.rs @@ -31,7 +31,7 @@ note: required because it appears within the type `impl Sized` note: required because it appears within the type `impl Sized` --> tests/ui/spawn_nonsend.rs:7:1 | -7 | #[embassy_executor::task] + 7 | #[embassy_executor::task] | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `SendSpawner::spawn` --> src/spawner.rs diff --git a/embassy-executor/tests/ui/unsafe_op_in_unsafe_task.stderr b/embassy-executor/tests/ui/unsafe_op_in_unsafe_task.stderr index d987a4b95..033395584 100644 --- a/embassy-executor/tests/ui/unsafe_op_in_unsafe_task.stderr +++ b/embassy-executor/tests/ui/unsafe_op_in_unsafe_task.stderr @@ -4,7 +4,7 @@ error[E0133]: call to unsafe function `std::ptr::const_ptr::::rea 7 | (&x as *const i32).read(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function | - = note: for more information, see + = note: for more information, see = note: consult the function's documentation for information on how to avoid undefined behavior note: an unsafe function restricts its caller, but its body is safe by default --> tests/ui/unsafe_op_in_unsafe_task.rs:5:1 diff --git a/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr index daf79ad28..417fb8e31 100644 --- a/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr +++ b/embassy-sync/tests/ui/sync_impl/lazy_lock_function.stderr @@ -1,10 +1,10 @@ error[E0277]: `*const u8` cannot be shared between threads safely --> tests/ui/sync_impl/lazy_lock_function.rs:8:16 | -6 | let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) }; + 6 | let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) }; | -- within this `{closure@$DIR/tests/ui/sync_impl/lazy_lock_function.rs:6:47: 6:49}` -7 | -8 | check_sync(LazyLock::new(closure_capturing_non_sync_variable)); + 7 | + 8 | check_sync(LazyLock::new(closure_capturing_non_sync_variable)); | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const u8` cannot be shared between threads safely | | | required by a bound introduced by this call @@ -14,7 +14,7 @@ error[E0277]: `*const u8` cannot be shared between threads safely note: required because it's used within this closure --> tests/ui/sync_impl/lazy_lock_function.rs:6:47 | -6 | let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) }; + 6 | let closure_capturing_non_sync_variable = || unsafe { core::ptr::read(x_ptr) }; | ^^ = note: required for `embassy_sync::lazy_lock::LazyLock` to implement `Sync` note: required by a bound in `check_sync` diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml index 411cc6946..d3e88c7e1 100644 --- a/rust-toolchain-nightly.toml +++ b/rust-toolchain-nightly.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2025-06-29" +channel = "nightly-2025-08-05" components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] targets = [ "thumbv7em-none-eabi", diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e24864037..5d925c934 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.88" +channel = "1.90" components = [ "rust-src", "rustfmt", "llvm-tools" ] targets = [ "thumbv7em-none-eabi", -- cgit From a0a204f586301ee4e014c3673c2ecd3ad90c504c Mon Sep 17 00:00:00 2001 From: Rogan Morrow Date: Mon, 22 Sep 2025 13:36:22 +1000 Subject: add as_nanos and from_nanos where missing --- embassy-time/src/duration.rs | 5 +++++ embassy-time/src/instant.rs | 14 +++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/embassy-time/src/duration.rs b/embassy-time/src/duration.rs index 5b140eeff..b3ea0468d 100644 --- a/embassy-time/src/duration.rs +++ b/embassy-time/src/duration.rs @@ -37,6 +37,11 @@ impl Duration { self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M) } + /// Convert the `Duration` to nanoseconds, rounding down. + pub const fn as_nanos(&self) -> u64 { + self.ticks * (1_000_000_000 / GCD_1G) / (TICK_HZ / GCD_1G) + } + /// Creates a duration from the specified number of clock ticks pub const fn from_ticks(ticks: u64) -> Duration { Duration { ticks } diff --git a/embassy-time/src/instant.rs b/embassy-time/src/instant.rs index 6571bea62..a311b365c 100644 --- a/embassy-time/src/instant.rs +++ b/embassy-time/src/instant.rs @@ -1,7 +1,7 @@ use core::fmt; use core::ops::{Add, AddAssign, Sub, SubAssign}; -use super::{Duration, GCD_1K, GCD_1M, TICK_HZ}; +use super::{Duration, GCD_1K, GCD_1M, GCD_1G, TICK_HZ}; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -29,6 +29,13 @@ impl Instant { Self { ticks } } + /// Create an Instant from a nanosecond count since system boot. + pub const fn from_nanos(nanos: u64) -> Self { + Self { + ticks: nanos * (TICK_HZ / GCD_1G) / (1_000_000_000 / GCD_1G), + } + } + /// Create an Instant from a microsecond count since system boot. pub const fn from_micros(micros: u64) -> Self { Self { @@ -101,6 +108,11 @@ impl Instant { self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M) } + /// Nanoseconds since system boot. + pub const fn as_nanos(&self) -> u64 { + self.ticks * (1_000_000_000 / GCD_1G) / (TICK_HZ / GCD_1G) + } + /// Duration between this Instant and another Instant /// Panics on over/underflow. pub fn duration_since(&self, earlier: Instant) -> Duration { -- cgit From 57f1517f70bafb0709014f7a44e2d1c8a4841739 Mon Sep 17 00:00:00 2001 From: Rogan Morrow Date: Mon, 22 Sep 2025 13:43:57 +1000 Subject: fix rustfmt and add changelog --- embassy-time/CHANGELOG.md | 2 ++ embassy-time/src/instant.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-time/CHANGELOG.md b/embassy-time/CHANGELOG.md index 572571215..4a50da8ef 100644 --- a/embassy-time/CHANGELOG.md +++ b/embassy-time/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- Add as_nanos and from_nanos where missing + ## 0.5.0 - 2025-08-26 - Allow inlining on time driver boundary diff --git a/embassy-time/src/instant.rs b/embassy-time/src/instant.rs index a311b365c..4e6670032 100644 --- a/embassy-time/src/instant.rs +++ b/embassy-time/src/instant.rs @@ -1,7 +1,7 @@ use core::fmt; use core::ops::{Add, AddAssign, Sub, SubAssign}; -use super::{Duration, GCD_1K, GCD_1M, GCD_1G, TICK_HZ}; +use super::{Duration, GCD_1G, GCD_1K, GCD_1M, TICK_HZ}; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -- cgit From c692a97b654adc50f997852a360ce3277cb73db4 Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Sun, 21 Sep 2025 23:01:03 -0500 Subject: nrf: impl Drop for Timer --- embassy-nrf/src/timer.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index de2875765..b6a77bd2e 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -104,7 +104,7 @@ impl<'d, T: Instance> Timer<'d, T> { Self::new_inner(timer, true) } - fn new_inner(timer: Peri<'d, T>, _is_counter: bool) -> Self { + fn new_inner(timer: Peri<'d, T>, is_counter: bool) -> Self { let regs = T::regs(); let this = Self { _p: timer }; @@ -114,7 +114,7 @@ impl<'d, T: Instance> Timer<'d, T> { this.stop(); regs.mode().write(|w| { - w.set_mode(match _is_counter { + w.set_mode(match is_counter { #[cfg(not(feature = "_nrf51"))] true => vals::Mode::LOW_POWER_COUNTER, #[cfg(feature = "_nrf51")] @@ -218,6 +218,12 @@ impl<'d, T: Instance> Timer<'d, T> { } } +impl<'d, T: Instance> Drop for Timer<'d, T> { + fn drop(&mut self) { + self.stop(); + } +} + /// A representation of a timer's Capture/Compare (CC) register. /// /// A CC register holds a 32-bit value. -- cgit From d463a57879c0e02c188f63ab99c40d6ab91ea54e Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Sun, 21 Sep 2025 23:01:11 -0500 Subject: nrf: add persist() method for gpiote and timer --- embassy-nrf/src/gpiote.rs | 18 ++++++++++++++++++ embassy-nrf/src/timer.rs | 9 +++++++++ 2 files changed, 27 insertions(+) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index d169b49f9..43e43f0bf 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -193,6 +193,15 @@ pub struct InputChannel<'d> { pin: Input<'d>, } +impl InputChannel<'static> { + /// Persist the channel's configuration for the rest of the program's lifetime. This method + /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents + /// accidental reuse of the underlying peripheral. + pub fn persist(self) { + core::mem::forget(self); + } +} + impl<'d> Drop for InputChannel<'d> { fn drop(&mut self) { let g = regs(); @@ -263,6 +272,15 @@ pub struct OutputChannel<'d> { _pin: Output<'d>, } +impl OutputChannel<'static> { + /// Persist the channel's configuration for the rest of the program's lifetime. This method + /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents + /// accidental reuse of the underlying peripheral. + pub fn persist(self) { + core::mem::forget(self); + } +} + impl<'d> Drop for OutputChannel<'d> { fn drop(&mut self) { let g = regs(); diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index b6a77bd2e..5d6afe49b 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -218,6 +218,15 @@ impl<'d, T: Instance> Timer<'d, T> { } } +impl Timer<'static, T> { + /// Persist the timer's configuration for the rest of the program's lifetime. This method + /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents + /// accidental reuse of the underlying peripheral. + pub fn persist(self) { + core::mem::forget(self); + } +} + impl<'d, T: Instance> Drop for Timer<'d, T> { fn drop(&mut self) { self.stop(); -- cgit From 99f7e150c56b09a968e05f98b9f924f17c43296e Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Sun, 21 Sep 2025 23:04:03 -0500 Subject: nrf: Update changelog --- embassy-nrf/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index de8dfd391..0cc1d56bb 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -10,7 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - changed: nrf54l: Disable glitch detection and enable DC/DC in init. - changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4 -- changed: add persist() method for gpio and ppi +- changed: add persist() method for gpio, gpiote, timer and ppi +- changed: impl Drop for Timer ## 0.7.0 - 2025-08-26 -- cgit From 4d9563805cee8a14f7c59a5b28227b99a6ffbf75 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 22 Sep 2025 12:55:20 +0200 Subject: time: add Instant::try_from_nanos --- embassy-time/src/instant.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/embassy-time/src/instant.rs b/embassy-time/src/instant.rs index 4e6670032..de5ebebf8 100644 --- a/embassy-time/src/instant.rs +++ b/embassy-time/src/instant.rs @@ -57,6 +57,17 @@ impl Instant { } } + /// Try to create an Instant from a nanosecond count since system boot. + /// Fails if the number of nanoseconds is too large. + pub const fn try_from_nanos(nanos: u64) -> Option { + let Some(value) = nanos.checked_mul(TICK_HZ / GCD_1G) else { + return None; + }; + Some(Self { + ticks: value / (1_000_000_000 / GCD_1G), + }) + } + /// Try to create an Instant from a microsecond count since system boot. /// Fails if the number of microseconds is too large. pub const fn try_from_micros(micros: u64) -> Option { -- cgit From 76d47ea1fa84b0940f3d9e3d830aa0b182f280b8 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 20 Sep 2025 22:07:02 +0200 Subject: add missing timer API --- embassy-nrf/CHANGELOG.md | 2 ++ embassy-nrf/src/timer.rs | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 0fedf9360..825d9d713 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - added: basic RTC driver - changed: add persist() method for gpio, gpiote, timer and ppi - changed: impl Drop for Timer +- added: expose `regs` for timer driver +- added: timer driver CC `clear_events` method ## 0.7.0 - 2025-08-26 diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 5d6afe49b..1d1f77ea8 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -81,8 +81,6 @@ pub enum Frequency { /// /// It has either 4 or 6 Capture/Compare registers, which can be used to capture the current state of the counter /// or trigger an event when the counter reaches a certain value. - -/// Timer driver. pub struct Timer<'d, T: Instance> { _p: Peri<'d, T>, } @@ -145,6 +143,13 @@ impl<'d, T: Instance> Timer<'d, T> { this } + /// Direct access to the register block. + #[cfg(feature = "unstable-pac")] + #[inline] + pub fn regs(&mut self) -> pac::timer::Timer { + T::regs() + } + /// Starts the timer. pub fn start(&self) { T::regs().tasks_start().write_value(1) @@ -248,7 +253,7 @@ pub struct Cc<'d, T: Instance> { impl<'d, T: Instance> Cc<'d, T> { /// Get the current value stored in the register. pub fn read(&self) -> u32 { - return T::regs().cc(self.n).read(); + T::regs().cc(self.n).read() } /// Set the value stored in the register. @@ -278,6 +283,12 @@ impl<'d, T: Instance> Cc<'d, T> { Event::from_reg(T::regs().events_compare(self.n)) } + /// Clear the COMPARE event for this CC register. + #[inline] + pub fn clear_events(&self) { + T::regs().events_compare(self.n).write_value(0); + } + /// Enable the shortcut between this CC register's COMPARE event and the timer's CLEAR task. /// /// This means that when the COMPARE event is fired, the CLEAR task will be triggered. -- cgit From 9ae4edfa73d33f6fe66eb70a896b14267f6cdec2 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 23 Sep 2025 11:21:57 +0200 Subject: doc fixes --- embassy-nrf/src/gpio.rs | 4 ++-- embassy-nrf/src/i2s.rs | 2 +- embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/twim.rs | 4 ++-- embassy-nrf/src/twis.rs | 2 +- embassy-nrf/src/usb/vbus_detect.rs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 0cea38777..ab5e7ed4b 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -217,7 +217,7 @@ pub struct Output<'d> { } impl<'d> Output<'d> { - /// Create GPIO output driver for a [Pin] with the provided [Level] and [OutputDriver] configuration. + /// Create GPIO output driver for a [Pin] with the provided [Level] and [OutputDrive] configuration. #[inline] pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level, drive: OutputDrive) -> Self { let mut pin = Flex::new(pin); @@ -781,7 +781,7 @@ impl<'d> embedded_hal_1::digital::ErrorType for Flex<'d> { type Error = Infallible; } -/// Implement [`InputPin`] for [`Flex`]; +/// Implement [embedded_hal_1::digital::InputPin] for [`Flex`]; /// /// If the pin is not in input mode the result is unspecified. impl<'d> embedded_hal_1::digital::InputPin for Flex<'d> { diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index a7dde8cd7..53de8deee 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -252,7 +252,7 @@ impl ApproxSampleRate { /// /// Those are non standard sample rates that can be configured without error. /// -/// For custom master clock configuration, please refer to [Mode]. +/// For custom master clock configuration, please refer to [vals::Mode]. #[derive(Clone, Copy)] pub enum ExactSampleRate { /// 8000 Hz diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index e0847a312..7c26a6184 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -207,7 +207,7 @@ mod chip; /// Macro to bind interrupts to handlers. /// /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) -/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to +/// and implements the right [crate::interrupt::typelevel::Binding]s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. /// /// Example of how to bind one interrupt: diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 3d5e841d1..3fc59a39a 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -558,7 +558,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// Execute the provided operations on the I2C bus with timeout. /// - /// See [`blocking_transaction`]. + /// See [Self::blocking_transaction]. #[cfg(feature = "time")] pub fn blocking_transaction_timeout( &mut self, @@ -632,7 +632,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// Write to an I2C slave with timeout. /// - /// See [`blocking_write`]. + /// See [Self::blocking_write]. #[cfg(feature = "time")] pub fn blocking_write_timeout(&mut self, address: u8, buffer: &[u8], timeout: Duration) -> Result<(), Error> { self.blocking_transaction_timeout(address, &mut [Operation::Write(buffer)], timeout) diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index 3e4d537ae..c77d0f048 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -700,7 +700,7 @@ impl<'d, T: Instance> Twis<'d, T> { /// Respond to an I2C master READ command with timeout. /// Returns the number of bytes written. - /// See [`blocking_respond_to_read`]. + /// See [Self::blocking_respond_to_read]. #[cfg(feature = "time")] pub fn blocking_respond_to_read_timeout(&mut self, buffer: &[u8], timeout: Duration) -> Result { self.setup_respond(buffer, false)?; diff --git a/embassy-nrf/src/usb/vbus_detect.rs b/embassy-nrf/src/usb/vbus_detect.rs index 8794beb2d..33cf91ee2 100644 --- a/embassy-nrf/src/usb/vbus_detect.rs +++ b/embassy-nrf/src/usb/vbus_detect.rs @@ -68,7 +68,7 @@ impl interrupt::typelevel::Handler for InterruptHandler { /// [`VbusDetect`] implementation using the native hardware POWER peripheral. /// /// Unsuitable for usage with the nRF softdevice, since it reserves exclusive acces -/// to POWER. In that case, use [`VbusDetectSignal`]. +/// to POWER. In that case, use [SoftwareVbusDetect]. pub struct HardwareVbusDetect { _private: (), } -- cgit From 99febbe3a42ac05b723e6e76088d159eb0acfa5e Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Tue, 23 Sep 2025 13:55:55 +0200 Subject: more docs fixes --- embassy-embedded-hal/src/adapter/yielding_async.rs | 2 +- embassy-sync/src/blocking_mutex/mod.rs | 4 ++-- embassy-sync/src/blocking_mutex/raw.rs | 4 ++-- embassy-sync/src/rwlock.rs | 2 -- embassy-time-driver/src/lib.rs | 2 +- embassy-usb-driver/src/lib.rs | 4 ++-- 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/embassy-embedded-hal/src/adapter/yielding_async.rs b/embassy-embedded-hal/src/adapter/yielding_async.rs index 87f822a63..e0ca3aedc 100644 --- a/embassy-embedded-hal/src/adapter/yielding_async.rs +++ b/embassy-embedded-hal/src/adapter/yielding_async.rs @@ -2,7 +2,7 @@ use embassy_futures::yield_now; /// Wrapper that yields for each operation to the wrapped instance /// -/// This can be used in combination with BlockingAsync to enforce yields +/// This can be used in combination with [super::BlockingAsync] to enforce yields /// between long running blocking operations. pub struct YieldingAsync { wrapped: T, diff --git a/embassy-sync/src/blocking_mutex/mod.rs b/embassy-sync/src/blocking_mutex/mod.rs index 11809c763..62bfc26fb 100644 --- a/embassy-sync/src/blocking_mutex/mod.rs +++ b/embassy-sync/src/blocking_mutex/mod.rs @@ -135,9 +135,9 @@ impl Mutex { // There's still a ThreadModeRawMutex for use with the generic Mutex (handy with Channel, for example), // but that will require T: Send even though it shouldn't be needed. -#[cfg(any(cortex_m, feature = "std"))] +#[cfg(any(cortex_m, doc, feature = "std"))] pub use thread_mode_mutex::*; -#[cfg(any(cortex_m, feature = "std"))] +#[cfg(any(cortex_m, doc, feature = "std"))] mod thread_mode_mutex { use super::*; diff --git a/embassy-sync/src/blocking_mutex/raw.rs b/embassy-sync/src/blocking_mutex/raw.rs index 50f965e00..fbb9ece15 100644 --- a/embassy-sync/src/blocking_mutex/raw.rs +++ b/embassy-sync/src/blocking_mutex/raw.rs @@ -89,7 +89,7 @@ unsafe impl RawMutex for NoopRawMutex { // ================ -#[cfg(any(cortex_m, feature = "std"))] +#[cfg(any(cortex_m, doc, feature = "std"))] mod thread_mode { use super::*; @@ -147,5 +147,5 @@ mod thread_mode { return unsafe { (0xE000ED04 as *const u32).read_volatile() } & 0x1FF == 0; } } -#[cfg(any(cortex_m, feature = "std"))] +#[cfg(any(cortex_m, doc, feature = "std"))] pub use thread_mode::*; diff --git a/embassy-sync/src/rwlock.rs b/embassy-sync/src/rwlock.rs index 0d784a7dc..e43388c4d 100644 --- a/embassy-sync/src/rwlock.rs +++ b/embassy-sync/src/rwlock.rs @@ -37,8 +37,6 @@ struct State { /// Use [`NoopRawMutex`](crate::blocking_mutex::raw::NoopRawMutex) when data is only shared between tasks running on the same executor. /// /// Use [`ThreadModeRawMutex`](crate::blocking_mutex::raw::ThreadModeRawMutex) when data is shared between tasks running on the same executor but you want a singleton. -/// - pub struct RwLock where M: RawMutex, diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index 32cb68296..44d9a156a 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs @@ -6,7 +6,7 @@ //! //! - Define a struct `MyDriver` //! - Implement [`Driver`] for it -//! - Register it as the global driver with [`time_driver_impl`](crate::time_driver_impl). +//! - Register it as the global driver with [`time_driver_impl`]. //! //! If your driver has a single set tick rate, enable the corresponding [`tick-hz-*`](crate#tick-rate) feature, //! which will prevent users from needing to configure it themselves (or selecting an incorrect configuration). diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 3ad96c61d..59845a268 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -205,7 +205,7 @@ pub trait Bus { /// /// # Errors /// - /// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support + /// * [`Unsupported`] - This UsbBus implementation doesn't support /// simulating a disconnect or it has not been enabled at creation time. fn force_reset(&mut self) -> Result<(), Unsupported> { Err(Unsupported) @@ -215,7 +215,7 @@ pub trait Bus { /// /// # Errors /// - /// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support + /// * [`Unsupported`] - This UsbBus implementation doesn't support /// remote wakeup or it has not been enabled at creation time. async fn remote_wakeup(&mut self) -> Result<(), Unsupported>; } -- cgit From d5e4558f1807a99a10636efcc1ff6ec514b07d56 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Tue, 23 Sep 2025 16:23:18 +0200 Subject: Fix docs --- embassy-executor/src/raw/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 9f36c60bc..dbd70cbf4 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -564,8 +564,6 @@ impl Executor { /// /// # Safety /// - /// You must call `initialize` before calling this method. - /// /// You must NOT call `poll` reentrantly on the same executor. /// /// In particular, note that `poll` may call the pender synchronously. Therefore, you -- cgit From 1d494594e8b96255fefb994ddcc2f46e4dec3772 Mon Sep 17 00:00:00 2001 From: Bjorn Beishline <75190918+BjornTheProgrammer@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:17:33 -0700 Subject: Add reset_to_usb_boot to rp235x --- embassy-rp/CHANGELOG.md | 1 + embassy-rp/src/rom_data/rp235x.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index ea62c2387..e932bcaa3 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add PIO onewire parasite power strong pullup - add `wait_for_alarm` and `alarm_scheduled` methods to rtc module ([#4216](https://github.com/embassy-rs/embassy/pull/4216)) - rp235x: use msplim for stack guard instead of MPU +- Add reset_to_usb_boot for rp235x ([#4705](https://github.com/embassy-rs/embassy/pull/4705)) ## 0.8.0 - 2025-08-26 diff --git a/embassy-rp/src/rom_data/rp235x.rs b/embassy-rp/src/rom_data/rp235x.rs index b16fee8f7..c0a1ed6fb 100644 --- a/embassy-rp/src/rom_data/rp235x.rs +++ b/embassy-rp/src/rom_data/rp235x.rs @@ -750,3 +750,35 @@ pub fn is_secure_mode() -> bool { pub fn is_secure_mode() -> bool { false } + +// These and the reset_to_usb_boot function are found from https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_bootrom/bootrom.c#L35-L51 +// The following has just been translated to rust from the original c++ +const BOOTSEL_FLAG_GPIO_PIN_SPECIFIED: u32 = 0x20; +const REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL: u32 = 0x2; +const REBOOT2_FLAG_NO_RETURN_ON_SUCCESS: u32 = 0x100; + +/// Resets the RP235x and uses the watchdog facility to re-start in BOOTSEL mode: +/// * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED +/// for the USB Mass Storage Device: +/// * 0 No pins are used as per cold boot. +/// * Otherwise a single bit set indicating which GPIO pin should be set to output and +/// raised whenever there is mass storage activity from the host. +/// * disable_interface_mask may be used to control the exposed USB interfaces: +/// * 0 To enable both interfaces (as per cold boot). +/// * 1 To disable the USB Mass Storage Interface. +/// * 2 to Disable the USB PICOBOOT Interface. +pub fn reset_to_usb_boot(mut usb_activity_gpio_pin_mask: u32, disable_interface_mask: u32) { + let mut flags = disable_interface_mask; + + if usb_activity_gpio_pin_mask != 0 { + flags = flags | BOOTSEL_FLAG_GPIO_PIN_SPECIFIED; + usb_activity_gpio_pin_mask = usb_activity_gpio_pin_mask.trailing_zeros() + } + + reboot( + REBOOT2_FLAG_REBOOT_TYPE_BOOTSEL | REBOOT2_FLAG_NO_RETURN_ON_SUCCESS, + 10, + flags, + usb_activity_gpio_pin_mask, + ); +} -- cgit From 987009df7bf77dc963e5cff5c3cbdf565839c17c Mon Sep 17 00:00:00 2001 From: Abraham Hamidi Date: Fri, 19 Sep 2025 16:13:34 -0500 Subject: feat(nrf/spim): erase Instance type from Spim --- embassy-nrf/CHANGELOG.md | 1 + embassy-nrf/src/spim.rs | 64 +++++++++++++++----------- examples/nrf52840/src/bin/ethernet_enc28j60.rs | 2 +- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 2 +- tests/nrf/src/bin/ethernet_enc28j60_perf.rs | 2 +- tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 2 +- 6 files changed, 41 insertions(+), 32 deletions(-) diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 825d9d713..b8d03a1f8 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- changed: erase Instance type from Spim - changed: nrf54l: Disable glitch detection and enable DC/DC in init. - changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4 - changed: add persist() method for gpio and ppi diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 59f5b6d58..c410e49fd 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -99,13 +99,16 @@ impl interrupt::typelevel::Handler for InterruptHandl } /// SPIM driver. -pub struct Spim<'d, T: Instance> { - _p: Peri<'d, T>, +pub struct Spim<'d> { + r: pac::spim::Spim, + irq: interrupt::Interrupt, + state: &'static State, + _p: PhantomData<&'d ()>, } -impl<'d, T: Instance> Spim<'d, T> { +impl<'d> Spim<'d> { /// Create a new SPIM driver. - pub fn new( + pub fn new( spim: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, sck: Peri<'d, impl GpioPin>, @@ -117,7 +120,7 @@ impl<'d, T: Instance> Spim<'d, T> { } /// Create a new SPIM driver, capable of TX only (MOSI only). - pub fn new_txonly( + pub fn new_txonly( spim: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, sck: Peri<'d, impl GpioPin>, @@ -128,7 +131,7 @@ impl<'d, T: Instance> Spim<'d, T> { } /// Create a new SPIM driver, capable of RX only (MISO only). - pub fn new_rxonly( + pub fn new_rxonly( spim: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, sck: Peri<'d, impl GpioPin>, @@ -139,7 +142,7 @@ impl<'d, T: Instance> Spim<'d, T> { } /// Create a new SPIM driver, capable of TX only (MOSI only), without SCK pin. - pub fn new_txonly_nosck( + pub fn new_txonly_nosck( spim: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, mosi: Peri<'d, impl GpioPin>, @@ -148,8 +151,8 @@ impl<'d, T: Instance> Spim<'d, T> { Self::new_inner(spim, None, None, Some(mosi.into()), config) } - fn new_inner( - spim: Peri<'d, T>, + fn new_inner( + _spim: Peri<'d, T>, sck: Option>, miso: Option>, mosi: Option>, @@ -201,7 +204,12 @@ impl<'d, T: Instance> Spim<'d, T> { // Enable SPIM instance. r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); - let mut spim = Self { _p: spim }; + let mut spim = Self { + r: T::regs(), + irq: T::Interrupt::IRQ, + state: T::state(), + _p: PhantomData {}, + }; // Apply runtime peripheral configuration Self::set_config(&mut spim, &config).unwrap(); @@ -218,7 +226,7 @@ impl<'d, T: Instance> Spim<'d, T> { fn prepare_dma_transfer(&mut self, rx: *mut [u8], tx: *const [u8], offset: usize, length: usize) { compiler_fence(Ordering::SeqCst); - let r = T::regs(); + let r = self.r; fn xfer_params(ptr: u32, total: usize, offset: usize, length: usize) -> (u32, usize) { if total > offset { @@ -246,7 +254,7 @@ impl<'d, T: Instance> Spim<'d, T> { #[cfg(feature = "_nrf52832_anomaly_109")] if offset == 0 { - let s = T::state(); + let s = self.state; r.events_started().write_value(0); @@ -279,7 +287,7 @@ impl<'d, T: Instance> Spim<'d, T> { } // Wait for 'end' event. - while T::regs().events_end().read() == 0 {} + while self.r.events_end().read() == 0 {} compiler_fence(Ordering::SeqCst); } @@ -315,7 +323,7 @@ impl<'d, T: Instance> Spim<'d, T> { #[cfg(feature = "_nrf52832_anomaly_109")] if offset == 0 { poll_fn(|cx| { - let s = T::state(); + let s = self.state; s.waker.register(cx.waker()); @@ -326,8 +334,8 @@ impl<'d, T: Instance> Spim<'d, T> { // Wait for 'end' event. poll_fn(|cx| { - T::state().waker.register(cx.waker()); - if T::regs().events_end().read() != 0 { + self.state.waker.register(cx.waker()); + if self.r.events_end().read() != 0 { return Poll::Ready(()); } @@ -430,9 +438,9 @@ impl<'d, T: Instance> Spim<'d, T> { #[cfg(feature = "_nrf52832_anomaly_109")] fn nrf52832_dma_workaround_status(&mut self) -> Poll<()> { - let r = T::regs(); + let r = self.r; if r.events_started().read() != 0 { - let s = T::state(); + let s = self.state; // Handle the first "fake" transmission r.events_started().write_value(0); @@ -451,14 +459,14 @@ impl<'d, T: Instance> Spim<'d, T> { } } -impl<'d, T: Instance> Drop for Spim<'d, T> { +impl<'d> Drop for Spim<'d> { fn drop(&mut self) { trace!("spim drop"); // TODO check for abort, wait for xxxstopped // disable! - let r = T::regs(); + let r = self.r; r.enable().write(|w| w.set_enable(vals::Enable::DISABLED)); gpio::deconfigure_pin(r.psel().sck().read()); @@ -466,7 +474,7 @@ impl<'d, T: Instance> Drop for Spim<'d, T> { gpio::deconfigure_pin(r.psel().mosi().read()); // Disable all events interrupts - T::Interrupt::disable(); + cortex_m::peripheral::NVIC::mask(self.irq); trace!("spim drop: done"); } @@ -526,7 +534,7 @@ macro_rules! impl_spim { mod eh02 { use super::*; - impl<'d, T: Instance> embedded_hal_02::blocking::spi::Transfer for Spim<'d, T> { + impl<'d> embedded_hal_02::blocking::spi::Transfer for Spim<'d> { type Error = Error; fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { self.blocking_transfer_in_place(words)?; @@ -534,7 +542,7 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::blocking::spi::Write for Spim<'d, T> { + impl<'d> embedded_hal_02::blocking::spi::Write for Spim<'d> { type Error = Error; fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { @@ -551,11 +559,11 @@ impl embedded_hal_1::spi::Error for Error { } } -impl<'d, T: Instance> embedded_hal_1::spi::ErrorType for Spim<'d, T> { +impl<'d> embedded_hal_1::spi::ErrorType for Spim<'d> { type Error = Error; } -impl<'d, T: Instance> embedded_hal_1::spi::SpiBus for Spim<'d, T> { +impl<'d> embedded_hal_1::spi::SpiBus for Spim<'d> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } @@ -577,7 +585,7 @@ impl<'d, T: Instance> embedded_hal_1::spi::SpiBus for Spim<'d, T> { } } -impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spim<'d, T> { +impl<'d> embedded_hal_async::spi::SpiBus for Spim<'d> { async fn flush(&mut self) -> Result<(), Error> { Ok(()) } @@ -599,11 +607,11 @@ impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spim<'d, T> { } } -impl<'d, T: Instance> SetConfig for Spim<'d, T> { +impl<'d> SetConfig for Spim<'d> { type Config = Config; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { - let r = T::regs(); + let r = self.r; // Configure mode. let mode = config.mode; r.config().write(|w| { diff --git a/examples/nrf52840/src/bin/ethernet_enc28j60.rs b/examples/nrf52840/src/bin/ethernet_enc28j60.rs index 3bb255a72..e59afd37f 100644 --- a/examples/nrf52840/src/bin/ethernet_enc28j60.rs +++ b/examples/nrf52840/src/bin/ethernet_enc28j60.rs @@ -25,7 +25,7 @@ bind_interrupts!(struct Irqs { async fn net_task( mut runner: embassy_net::Runner< 'static, - Enc28j60, Output<'static>, Delay>, Output<'static>>, + Enc28j60, Output<'static>, Delay>, Output<'static>>, >, ) -> ! { runner.run().await diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index 2dd9abfaa..1bc35746a 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -27,7 +27,7 @@ bind_interrupts!(struct Irqs { async fn wifi_task( runner: hosted::Runner< 'static, - ExclusiveDevice, Output<'static>, Delay>, + ExclusiveDevice, Output<'static>, Delay>, Input<'static>, Output<'static>, >, diff --git a/tests/nrf/src/bin/ethernet_enc28j60_perf.rs b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs index 5f3fa1fd3..bd6a2effd 100644 --- a/tests/nrf/src/bin/ethernet_enc28j60_perf.rs +++ b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs @@ -22,7 +22,7 @@ bind_interrupts!(struct Irqs { RNG => embassy_nrf::rng::InterruptHandler; }); -type MyDriver = Enc28j60, Output<'static>, Delay>, Output<'static>>; +type MyDriver = Enc28j60, Output<'static>, Delay>, Output<'static>>; #[embassy_executor::task] async fn net_task(mut runner: embassy_net::Runner<'static, MyDriver>) -> ! { diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index 34c33a4ad..091a70ce9 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -29,7 +29,7 @@ const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; async fn wifi_task( runner: hosted::Runner< 'static, - ExclusiveDevice, Output<'static>, Delay>, + ExclusiveDevice, Output<'static>, Delay>, Input<'static>, Output<'static>, >, -- cgit From fc8c3e1e4babaa6344bdbab49a9542a29c88cc25 Mon Sep 17 00:00:00 2001 From: Piotr Esden-Tempski Date: Wed, 24 Sep 2025 16:43:19 -0700 Subject: stm32-metapack: Corrects the RTC register map for l4p and l4q. It also includes improvements in accuracy of the l412 and l422 RTC register map. --- embassy-stm32/CHANGELOG.md | 2 ++ embassy-stm32/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 8fcc088fd..835d9c704 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -23,6 +23,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: Allow OSPI DMA writes larger than 64kB using chunking - feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times - feat: Add USB CRS sync support for STM32C071 +- fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map. +- fix: Cut down the capabilities of the STM32L412 and L422 RTC as those are missing binary timer mode and underflow interrupt. ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 369fabc50..b029f33b0 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -174,7 +174,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "18" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cf72eac610259fd78ef16f1c63be69a144d75f7" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b46fcc32f329f05fbdca4c007ed4bc305b0ade85" } vcell = "0.1.3" nb = "1.0.0" @@ -204,7 +204,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-3cf72eac610259fd78ef16f1c63be69a144d75f7", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b46fcc32f329f05fbdca4c007ed4bc305b0ade85", default-features = false, features = ["metadata"] } [features] default = ["rt"] -- cgit From f3d3b8899358ce8540bf5b2107a71b02ff941213 Mon Sep 17 00:00:00 2001 From: Roi Bachynskyi Date: Mon, 15 Sep 2025 00:38:23 +0300 Subject: lpc55: dma and async usart --- embassy-nxp/CHANGELOG.md | 1 + embassy-nxp/Cargo.toml | 1 + embassy-nxp/src/chips/lpc55.rs | 30 +++ embassy-nxp/src/dma.rs | 5 + embassy-nxp/src/dma/lpc55.rs | 377 +++++++++++++++++++++++++++++++ embassy-nxp/src/lib.rs | 72 ++++++ embassy-nxp/src/usart/lpc55.rs | 310 +++++++++++++++++++++++-- examples/lpc55s69/src/bin/usart_async.rs | 70 ++++++ 8 files changed, 846 insertions(+), 20 deletions(-) create mode 100644 embassy-nxp/src/dma.rs create mode 100644 embassy-nxp/src/dma/lpc55.rs create mode 100644 examples/lpc55s69/src/bin/usart_async.rs diff --git a/embassy-nxp/CHANGELOG.md b/embassy-nxp/CHANGELOG.md index ab97c4185..0fb677cd8 100644 --- a/embassy-nxp/CHANGELOG.md +++ b/embassy-nxp/CHANGELOG.md @@ -7,5 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- LPC55: DMA Controller and asynchronous version of USART - Moved NXP LPC55S69 from `lpc55-pac` to `nxp-pac` - First release with changelog. diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index 073fdabe4..f3c828313 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -29,6 +29,7 @@ cortex-m-rt = "0.7.0" critical-section = "1.1.2" embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-sync = { version = "0.7.2", path = "../embassy-sync" } +embassy-futures = { version = "0.1.2", path = "../embassy-futures"} defmt = { version = "1", optional = true } log = { version = "0.4.27", optional = true } embassy-time = { version = "0.5.0", path = "../embassy-time", optional = true } diff --git a/embassy-nxp/src/chips/lpc55.rs b/embassy-nxp/src/chips/lpc55.rs index 711bff3e7..9f4e7269f 100644 --- a/embassy-nxp/src/chips/lpc55.rs +++ b/embassy-nxp/src/chips/lpc55.rs @@ -1,5 +1,9 @@ pub use nxp_pac as pac; +embassy_hal_internal::interrupt_mod!( + FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7 +); + embassy_hal_internal::peripherals! { // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other // peripheral types (e.g. I2C). @@ -68,6 +72,32 @@ embassy_hal_internal::peripherals! { PIO1_30, PIO1_31, + // Direct Memory Access (DMA) channels. They are used for asynchronous modes of peripherals. + DMA_CH0, + DMA_CH1, + DMA_CH2, + DMA_CH3, + DMA_CH4, + DMA_CH5, + DMA_CH6, + DMA_CH7, + DMA_CH8, + DMA_CH9, + DMA_CH10, + DMA_CH11, + DMA_CH12, + DMA_CH13, + DMA_CH14, + DMA_CH15, + DMA_CH16, + DMA_CH17, + DMA_CH18, + DMA_CH19, + DMA_CH20, + DMA_CH21, + DMA_CH22, + + // Universal Synchronous/Asynchronous Receiver/Transmitter (USART) instances. USART0, USART1, USART2, diff --git a/embassy-nxp/src/dma.rs b/embassy-nxp/src/dma.rs new file mode 100644 index 000000000..e2df65fc9 --- /dev/null +++ b/embassy-nxp/src/dma.rs @@ -0,0 +1,5 @@ +//! Direct Memory Access (DMA) driver. + +#[cfg_attr(feature = "lpc55-core0", path = "./dma/lpc55.rs")] +mod inner; +pub use inner::*; diff --git a/embassy-nxp/src/dma/lpc55.rs b/embassy-nxp/src/dma/lpc55.rs new file mode 100644 index 000000000..578d1fd88 --- /dev/null +++ b/embassy-nxp/src/dma/lpc55.rs @@ -0,0 +1,377 @@ +use core::cell::RefCell; +use core::future::Future; +use core::pin::Pin; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::{Context, Poll}; + +use critical_section::Mutex; +use embassy_hal_internal::interrupt::InterruptExt; +use embassy_hal_internal::{impl_peripheral, PeripheralType}; +use embassy_sync::waitqueue::AtomicWaker; + +use crate::pac::{DMA0, SYSCON, *}; +use crate::{peripherals, Peri}; + +#[interrupt] +fn DMA0() { + let inta = DMA0.inta0().read().ia(); + for channel in 0..CHANNEL_COUNT { + if (DMA0.errint0().read().err() & (1 << channel)) != 0 { + panic!("DMA: error on DMA_0 channel {}", channel); + } + + if (inta & (1 << channel)) != 0 { + CHANNEL_WAKERS[channel].wake(); + DMA0.inta0().modify(|w| w.set_ia(1 << channel)); + } + } +} + +pub(crate) fn init() { + assert_eq!(core::mem::size_of::(), 16, "Descriptor must be 16 bytes"); + assert_eq!( + core::mem::align_of::(), + 16, + "Descriptor must be 16-byte aligned" + ); + assert_eq!( + core::mem::align_of::(), + 512, + "Table must be 512-byte aligned" + ); + // Start clock for DMA + SYSCON.ahbclkctrl0().modify(|w| w.set_dma0(true)); + // Reset DMA + SYSCON + .presetctrl0() + .modify(|w| w.set_dma0_rst(syscon::vals::Dma0Rst::ASSERTED)); + SYSCON + .presetctrl0() + .modify(|w| w.set_dma0_rst(syscon::vals::Dma0Rst::RELEASED)); + + // Address bits 31:9 of the beginning of the DMA descriptor table + critical_section::with(|cs| { + DMA0.srambase() + .write(|w| w.set_offset((DMA_DESCRIPTORS.borrow(cs).as_ptr() as u32) >> 9)); + }); + // Enable DMA controller + DMA0.ctrl().modify(|w| w.set_enable(true)); + + unsafe { + crate::pac::interrupt::DMA0.enable(); + } + info!("DMA initialized"); +} + +/// DMA read. +/// +/// SAFETY: Slice must point to a valid location reachable by DMA. +pub unsafe fn read<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const W, to: *mut [W]) -> Transfer<'a, C> { + copy_inner( + ch, + from as *const u32, + to as *mut W as *mut u32, + to.len(), + W::size(), + false, + true, + ) +} + +/// DMA write. +/// +/// SAFETY: Slice must point to a valid location reachable by DMA. +pub unsafe fn write<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const [W], to: *mut W) -> Transfer<'a, C> { + copy_inner( + ch, + from as *const W as *const u32, + to as *mut u32, + from.len(), + W::size(), + true, + false, + ) +} + +/// DMA copy between slices. +/// +/// SAFETY: Slices must point to locations reachable by DMA. +pub unsafe fn copy<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: &[W], to: &mut [W]) -> Transfer<'a, C> { + let from_len = from.len(); + let to_len = to.len(); + assert_eq!(from_len, to_len); + copy_inner( + ch, + from.as_ptr() as *const u32, + to.as_mut_ptr() as *mut u32, + from_len, + W::size(), + true, + true, + ) +} + +fn copy_inner<'a, C: Channel>( + ch: Peri<'a, C>, + from: *const u32, + to: *mut u32, + len: usize, + data_size: crate::pac::dma::vals::Width, + incr_src: bool, + incr_dest: bool, +) -> Transfer<'a, C> { + let p = ch.regs(); + + // Buffer ending address = buffer starting address + (XFERCOUNT * the transfer increment) + // XREFCOUNT = the number of transfers performed - 1. + // The 1st transfer is included in the starting address. + let source_end_addr = if incr_src { + from as u32 + len as u32 - 1 + } else { + from as u32 + }; + let dest_end_addr = if incr_dest { + to as u32 + len as u32 - 1 + } else { + to as u32 + }; + + compiler_fence(Ordering::SeqCst); + + critical_section::with(|cs| { + DMA_DESCRIPTORS.borrow(cs).borrow_mut().descriptors[ch.number() as usize] = DmaDescriptor { + reserved: 0, + source_end_addr, + dest_end_addr, + next_desc: 0, // Since only single transfers are made, there is no need for reload descriptor address. + } + }); + + compiler_fence(Ordering::SeqCst); + + p.cfg().modify(|w| { + // Peripheral DMA requests are enabled. + // DMA requests that pace transfers can be interpreted then. + w.set_periphreqen(true); + // There is no need to have them on. + // No complex transfers are performed for now. + w.set_hwtrigen(false); + w.set_chpriority(0); + }); + + p.xfercfg().modify(|w| { + // This bit indicates whether the current channel descriptor is + // valid and can potentially be acted upon, + // if all other activation criteria are fulfilled. + w.set_cfgvalid(true); + // Indicates whether the channel’s control structure will be reloaded + // when the current descriptor is exhausted. + // Reloading allows ping-pong and linked transfers. + w.set_reload(false); + // There is no hardware distinction between interrupt A and B. + // They can be used by software to assist with more complex descriptor usage. + // By convention, interrupt A may be used when only one interrupt flag is needed. + w.set_setinta(true); + w.set_setintb(false); + w.set_width(data_size); + w.set_srcinc(if incr_src { + dma::vals::Srcinc::WIDTH_X_1 + } else { + dma::vals::Srcinc::NO_INCREMENT + }); + w.set_dstinc(if incr_dest { + dma::vals::Dstinc::WIDTH_X_1 + } else { + dma::vals::Dstinc::NO_INCREMENT + }); + // Total number of transfers to be performed, minus 1 encoded. + w.set_xfercount((len as u16) - 1); + // Before triggering the channel, it has to be enabled. + w.set_swtrig(false); + }); + + compiler_fence(Ordering::SeqCst); + DMA0.enableset0().write(|w| w.set_ena(1 << ch.number())); + DMA0.intenset0().write(|w| w.set_inten(1 << ch.number())); + + compiler_fence(Ordering::SeqCst); + // Start transfer. + DMA0.settrig0().write(|w| w.set_trig(1 << ch.number())); + compiler_fence(Ordering::SeqCst); + Transfer::new(ch) +} + +/// DMA transfer driver. +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Transfer<'a, C: Channel> { + channel: Peri<'a, C>, +} + +impl<'a, C: Channel> Transfer<'a, C> { + pub(crate) fn new(channel: Peri<'a, C>) -> Self { + Self { channel } + } +} + +impl<'a, C: Channel> Drop for Transfer<'a, C> { + fn drop(&mut self) { + DMA0.enableclr0().write(|w| w.set_clr(1 << self.channel.number())); + while (DMA0.busy0().read().bsy() & (1 << self.channel.number())) != 0 {} + DMA0.abort0().write(|w| w.set_abortctrl(1 << self.channel.number())); + } +} + +impl<'a, C: Channel> Unpin for Transfer<'a, C> {} +impl<'a, C: Channel> Future for Transfer<'a, C> { + type Output = (); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // We need to register/re-register the waker for each poll because any + // calls to wake will deregister the waker. + CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker()); + // Check if it is busy or not. + if (DMA0.busy0().read().bsy() & (1 << self.channel.number())) != 0 { + Poll::Pending + } else { + Poll::Ready(()) + } + } +} + +// Total number of channles including both DMA0 and DMA1. +// In spite of using only DMA0 channels, the descriptor table +// should be of this size. +pub(crate) const CHANNEL_COUNT: usize = 32; + +static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT]; + +// See section 22.5.2 (table 450) +// UM11126, Rev. 2.8 +// The size of a descriptor must be aligned to a multiple of 16 bytes. +#[repr(C, align(16))] +#[derive(Clone, Copy)] +struct DmaDescriptor { + /// 0x0 Reserved. + reserved: u32, + /// 0x4 Source data end address. + source_end_addr: u32, + /// 0x8 Destination end address. + dest_end_addr: u32, + /// 0xC Link to next descriptor. + next_desc: u32, +} + +// See section 22.6.3 +// UM11126, Rev. 2.8 +// The table must begin on a 512 byte boundary. +#[repr(C, align(512))] +struct DmaDescriptorTable { + descriptors: [DmaDescriptor; CHANNEL_COUNT], +} + +// DMA descriptors are stored in on-chip SRAM. +static DMA_DESCRIPTORS: Mutex> = Mutex::new(RefCell::new(DmaDescriptorTable { + descriptors: [DmaDescriptor { + reserved: 0, + source_end_addr: 0, + dest_end_addr: 0, + next_desc: 0, + }; CHANNEL_COUNT], +})); + +trait SealedChannel {} +trait SealedWord {} + +/// DMA channel interface. +#[allow(private_bounds)] +pub trait Channel: PeripheralType + SealedChannel + Into + Sized + 'static { + /// Channel number. + fn number(&self) -> u8; + + /// Channel registry block. + fn regs(&self) -> crate::pac::dma::Channel { + crate::pac::DMA0.channel(self.number() as _) + } +} + +/// DMA word. +#[allow(private_bounds)] +pub trait Word: SealedWord { + /// Word size. + fn size() -> crate::pac::dma::vals::Width; +} + +impl SealedWord for u8 {} +impl Word for u8 { + fn size() -> crate::pac::dma::vals::Width { + crate::pac::dma::vals::Width::BIT_8 + } +} + +impl SealedWord for u16 {} +impl Word for u16 { + fn size() -> crate::pac::dma::vals::Width { + crate::pac::dma::vals::Width::BIT_16 + } +} + +impl SealedWord for u32 {} +impl Word for u32 { + fn size() -> crate::pac::dma::vals::Width { + crate::pac::dma::vals::Width::BIT_32 + } +} + +/// Type erased DMA channel. +pub struct AnyChannel { + number: u8, +} + +impl_peripheral!(AnyChannel); + +impl SealedChannel for AnyChannel {} +impl Channel for AnyChannel { + fn number(&self) -> u8 { + self.number + } +} + +macro_rules! channel { + ($name:ident, $num:expr) => { + impl SealedChannel for peripherals::$name {} + impl Channel for peripherals::$name { + fn number(&self) -> u8 { + $num + } + } + + impl From for crate::dma::AnyChannel { + fn from(val: peripherals::$name) -> Self { + Self { number: val.number() } + } + } + }; +} + +channel!(DMA_CH0, 0); +channel!(DMA_CH1, 1); +channel!(DMA_CH2, 2); +channel!(DMA_CH3, 3); +channel!(DMA_CH4, 4); +channel!(DMA_CH5, 5); +channel!(DMA_CH6, 6); +channel!(DMA_CH7, 7); +channel!(DMA_CH8, 8); +channel!(DMA_CH9, 9); +channel!(DMA_CH10, 10); +channel!(DMA_CH11, 11); +channel!(DMA_CH12, 12); +channel!(DMA_CH13, 13); +channel!(DMA_CH14, 14); +channel!(DMA_CH15, 15); +channel!(DMA_CH16, 16); +channel!(DMA_CH17, 17); +channel!(DMA_CH18, 18); +channel!(DMA_CH19, 19); +channel!(DMA_CH20, 20); +channel!(DMA_CH21, 21); +channel!(DMA_CH22, 22); diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index 74142a10b..f0f0afb6c 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -3,6 +3,8 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +#[cfg(feature = "lpc55-core0")] +pub mod dma; pub mod gpio; #[cfg(feature = "lpc55-core0")] pub mod pint; @@ -20,6 +22,9 @@ mod time_driver; #[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")] mod chip; +// TODO: Remove when this module is implemented for other chips +#[cfg(feature = "lpc55-core0")] +pub use chip::interrupt; #[cfg(feature = "unstable-pac")] pub use chip::pac; #[cfg(not(feature = "unstable-pac"))] @@ -27,6 +32,67 @@ pub(crate) use chip::pac; pub use chip::{peripherals, Peripherals}; pub use embassy_hal_internal::{Peri, PeripheralType}; +/// Macro to bind interrupts to handlers. +/// (Copied from `embassy-rp`) +/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) +/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to +/// prove at compile-time that the right interrupts have been bound. +/// +/// Example of how to bind one interrupt: +/// +/// ```rust,ignore +/// use embassy_nxp::{bind_interrupts, usart, peripherals}; +/// +/// bind_interrupts!( +/// /// Binds the USART Interrupts. +/// struct Irqs { +/// FLEXCOMM0 => usart::InterruptHandler; +/// } +/// ); +/// ``` +#[macro_export] +macro_rules! bind_interrupts { + ($(#[$attr:meta])* $vis:vis struct $name:ident { + $( + $(#[cfg($cond_irq:meta)])? + $irq:ident => $( + $(#[cfg($cond_handler:meta)])? + $handler:ty + ),*; + )* + }) => { + #[derive(Copy, Clone)] + $(#[$attr])* + $vis struct $name; + + $( + #[allow(non_snake_case)] + #[no_mangle] + $(#[cfg($cond_irq)])? + unsafe extern "C" fn $irq() { + unsafe { + $( + $(#[cfg($cond_handler)])? + <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + + )* + } + } + + $(#[cfg($cond_irq)])? + $crate::bind_interrupts!(@inner + $( + $(#[cfg($cond_handler)])? + unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} + )* + ); + )* + }; + (@inner $($t:tt)*) => { + $($t)* + } +} + /// Initialize the `embassy-nxp` HAL with the provided configuration. /// /// This returns the peripheral singletons that can be used for creating drivers. @@ -92,6 +158,9 @@ pub fn init(_config: config::Config) -> Peripherals { #[cfg(feature = "_time_driver")] time_driver::init(); + #[cfg(feature = "lpc55-core0")] + dma::init(); + peripherals } @@ -133,5 +202,8 @@ macro_rules! impl_mode { /// Blocking mode. pub struct Blocking; +/// Asynchronous mode. +pub struct Async; impl_mode!(Blocking); +impl_mode!(Async); diff --git a/embassy-nxp/src/usart/lpc55.rs b/embassy-nxp/src/usart/lpc55.rs index 428b80c4b..9034ed429 100644 --- a/embassy-nxp/src/usart/lpc55.rs +++ b/embassy-nxp/src/usart/lpc55.rs @@ -1,14 +1,24 @@ +use core::fmt::Debug; +use core::future::poll_fn; use core::marker::PhantomData; +use core::sync::atomic::{AtomicBool, Ordering}; +use core::task::Poll; +use embassy_futures::select::{select, Either}; +use embassy_hal_internal::interrupt::InterruptExt; use embassy_hal_internal::{Peri, PeripheralType}; +use embassy_sync::waitqueue::AtomicWaker; use embedded_io::{self, ErrorKind}; +use crate::dma::{AnyChannel, Channel}; use crate::gpio::{match_iocon, AnyPin, Bank, SealedPin}; +use crate::interrupt::typelevel::{Binding, Interrupt as _}; +use crate::interrupt::Interrupt; use crate::pac::flexcomm::Flexcomm as FlexcommReg; use crate::pac::iocon::vals::PioFunc; use crate::pac::usart::Usart as UsartReg; use crate::pac::*; -use crate::{Blocking, Mode}; +use crate::{Async, Blocking, Mode}; /// Serial error #[derive(Debug, Eq, PartialEq, Copy, Clone)] @@ -101,6 +111,12 @@ impl Default for Config { } } +/// Internal DMA state of UART RX. +pub struct DmaState { + rx_err_waker: AtomicWaker, + rx_err: AtomicBool, +} + /// # Type parameters /// 'd: the lifetime marker ensuring correct borrow checking for peripherals used at compile time /// T: the peripheral instance type allowing usage of peripheral specific registers @@ -112,24 +128,33 @@ pub struct Usart<'d, M: Mode> { pub struct UsartTx<'d, M: Mode> { info: &'static Info, - phantom: PhantomData<(&'d (), M)>, + tx_dma: Option>, + phantom: PhantomData, } pub struct UsartRx<'d, M: Mode> { info: &'static Info, - phantom: PhantomData<(&'d (), M)>, + dma_state: &'static DmaState, + rx_dma: Option>, + phantom: PhantomData, } impl<'d, M: Mode> UsartTx<'d, M> { - pub fn new(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, config: Config) -> Self { + pub fn new( + _usart: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + tx_dma: Peri<'d, impl Channel>, + config: Config, + ) -> Self { Usart::::init::(Some(tx.into()), None, config); - Self::new_inner(T::info()) + Self::new_inner(T::info(), Some(tx_dma.into())) } #[inline] - fn new_inner(info: &'static Info) -> Self { + fn new_inner(info: &'static Info, tx_dma: Option>) -> Self { Self { info, + tx_dma, phantom: PhantomData, } } @@ -155,20 +180,65 @@ impl<'d, M: Mode> UsartTx<'d, M> { impl<'d> UsartTx<'d, Blocking> { pub fn new_blocking(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, config: Config) -> Self { Usart::::init::(Some(tx.into()), None, config); - Self::new_inner(T::info()) + Self::new_inner(T::info(), None) } } +impl<'d> UsartTx<'d, Async> { + /// Write to UART TX from the provided buffer using DMA. + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + // Unwrap() can be used because UsartTx::new() in Async mode always sets it to Some + let ch = self.tx_dma.as_mut().unwrap().reborrow(); + let transfer = unsafe { + // Enable to pace DMA transfers. + self.info.usart_reg.fifocfg().modify(|w| w.set_dmatx(true)); + // If future is not assigned to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::write(ch, buffer, self.info.usart_reg.fifowr().as_ptr() as *mut _) + }; + transfer.await; + Ok(()) + } +} impl<'d, M: Mode> UsartRx<'d, M> { - pub fn new(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, config: Config) -> Self { + pub fn new( + _usart: Peri<'d, T>, + rx: Peri<'d, impl RxPin>, + has_irq: bool, + rx_dma: Peri<'d, impl Channel>, + config: Config, + ) -> Self { Usart::::init::(None, Some(rx.into()), config); - Self::new_inner(T::info()) + Self::new_inner(T::info(), T::dma_state(), has_irq, Some(rx_dma.into())) } - #[inline] - fn new_inner(info: &'static Info) -> Self { + fn new_inner( + info: &'static Info, + dma_state: &'static DmaState, + has_irq: bool, + rx_dma: Option>, + ) -> Self { + core::debug_assert_eq!(has_irq, rx_dma.is_some()); + if has_irq { + // Disable all the related interrupts for now. + info.usart_reg.intenclr().write(|w| { + w.set_framerrclr(true); + w.set_parityerrclr(true); + w.set_rxnoiseclr(true); + }); + info.usart_reg.fifointenclr().modify(|w| { + w.set_rxlvl(true); + w.set_rxerr(true); + }); + info.interrupt.unpend(); + unsafe { + info.interrupt.enable(); + } + } Self { info, + dma_state, + rx_dma, phantom: PhantomData, } } @@ -211,7 +281,120 @@ impl<'d, M: Mode> UsartRx<'d, M> { impl<'d> UsartRx<'d, Blocking> { pub fn new_blocking(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, config: Config) -> Self { Usart::::init::(None, Some(rx.into()), config); - Self::new_inner(T::info()) + Self::new_inner(T::info(), T::dma_state(), false, None) + } +} + +/// Interrupt handler. +pub struct InterruptHandler { + _uart: PhantomData, +} + +impl crate::interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let regs = T::info().usart_reg; + if !regs.fifocfg().read().dmarx() { + return; + } + let state = T::dma_state(); + state.rx_err.store(true, Ordering::Relaxed); + state.rx_err_waker.wake(); + // Disable the error interrupts instead of clearing the flags. Clearing the + // flags would allow the DMA transfer to continue, potentially signaling + // completion before we can check for errors that happened *during* the transfer. + regs.intenclr().write(|w| { + w.set_framerrclr(true); + w.set_rxnoiseclr(true); + w.set_parityerrclr(true); + }); + regs.fifointenclr().write(|w| w.set_rxerr(true)); + } +} + +impl<'d> UsartRx<'d, Async> { + /// Read from USART RX into the provided buffer. + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + // Clear error flags before the FIFO is drained. Errors that have accumulated + // in the flags will also be present in the FIFO. + self.dma_state.rx_err.store(false, Ordering::Relaxed); + self.info.usart_reg.intenclr().write(|w| { + w.set_framerrclr(true); + w.set_parityerrclr(true); + w.set_rxnoiseclr(true); + }); + self.info.usart_reg.fifointenclr().modify(|w| w.set_rxerr(true)); + // Then drain the fifo. It is necessary to read at most 16 bytes (the size of FIFO). + // Errors that apply to FIFO bytes will be reported directly. + let buffer = match { + let limit = buffer.len().min(16); + self.drain_fifo(&mut buffer[0..limit]) + } { + Ok(len) if len < buffer.len() => &mut buffer[len..], + Ok(_) => return Ok(()), + Err((_i, e)) => return Err(e), + }; + + // Start a DMA transfer. If errors have happened in the interim some error + // interrupt flags will have been raised, and those will be picked up immediately + // by the interrupt handler. + // Unwrap() can be used because UsartRx::new() in Async mode always sets it to Some + let ch = self.rx_dma.as_mut().unwrap().reborrow(); + + self.info.usart_reg.intenset().write(|w| { + w.set_framerren(true); + w.set_parityerren(true); + w.set_rxnoiseen(true); + }); + self.info.usart_reg.fifointenset().modify(|w| w.set_rxerr(true)); + self.info.usart_reg.fifocfg().modify(|w| w.set_dmarx(true)); + let transfer = unsafe { + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + crate::dma::read(ch, self.info.usart_reg.fiford().as_ptr() as *const _, buffer) + }; + + // wait for either the transfer to complete or an error to happen. + let transfer_result = select( + transfer, + poll_fn(|cx| { + self.dma_state.rx_err_waker.register(cx.waker()); + match self.dma_state.rx_err.swap(false, Ordering::Relaxed) { + false => Poll::Pending, + e => Poll::Ready(e), + } + }), + ) + .await; + + let errors = match transfer_result { + Either::First(()) => { + // The DMA controller finished, BUT if an error occurred on the LAST + // byte, then we may still need to grab the error state! + self.dma_state.rx_err.swap(false, Ordering::Relaxed) + } + Either::Second(e) => { + // There is an error, which means this is the error that + // was problematic. + e + } + }; + + // If we got no error, just return at this point + if !errors { + return Ok(()); + } + + // If we DID get an error, we need to figure out which one it was. + if self.info.usart_reg.intstat().read().framerrint() { + return Err(Error::Framing); + } else if self.info.usart_reg.intstat().read().parityerrint() { + return Err(Error::Parity); + } else if self.info.usart_reg.intstat().read().rxnoiseint() { + return Err(Error::Noise); + } else if self.info.usart_reg.fifointstat().read().rxerr() { + return Err(Error::Overrun); + } + unreachable!("unrecognized rx error"); } } @@ -222,7 +405,29 @@ impl<'d> Usart<'d, Blocking> { rx: Peri<'d, impl RxPin>, config: Config, ) -> Self { - Self::new_inner(usart, tx.into(), rx.into(), config) + Self::new_inner(usart, tx.into(), rx.into(), false, None, None, config) + } +} + +impl<'d> Usart<'d, Async> { + pub fn new( + uart: Peri<'d, T>, + tx: Peri<'d, impl TxPin>, + rx: Peri<'d, impl RxPin>, + _irq: impl Binding>, + tx_dma: Peri<'d, impl TxChannel>, + rx_dma: Peri<'d, impl RxChannel>, + config: Config, + ) -> Self { + Self::new_inner( + uart, + tx.into(), + rx.into(), + true, + Some(tx_dma.into()), + Some(rx_dma.into()), + config, + ) } } @@ -231,12 +436,15 @@ impl<'d, M: Mode> Usart<'d, M> { _usart: Peri<'d, T>, mut tx: Peri<'d, AnyPin>, mut rx: Peri<'d, AnyPin>, + has_irq: bool, + tx_dma: Option>, + rx_dma: Option>, config: Config, ) -> Self { Self::init::(Some(tx.reborrow()), Some(rx.reborrow()), config); Self { - tx: UsartTx::new_inner(T::info()), - rx: UsartRx::new_inner(T::info()), + tx: UsartTx::new_inner(T::info(), tx_dma), + rx: UsartRx::new_inner(T::info(), T::dma_state(), has_irq, rx_dma), } } @@ -390,9 +598,11 @@ impl<'d, M: Mode> Usart<'d, M> { SYSCON .presetctrl1() .modify(|w| w.set_fc_rst(instance_number, syscon::vals::FcRst::RELEASED)); - flexcomm_register - .pselid() - .modify(|w| w.set_persel(flexcomm::vals::Persel::USART)); + flexcomm_register.pselid().modify(|w| { + w.set_persel(flexcomm::vals::Persel::USART); + // This will lock the peripheral PERSEL and will not allow any changes until the board is reset. + w.set_lock(true); + }); } fn configure_usart(info: &'static Info, config: &Config) { @@ -471,6 +681,8 @@ impl<'d, M: Mode> Usart<'d, M> { }); registers.cfg().modify(|w| w.set_enable(true)); + registers.fifointenset().modify(|w| w.set_rxerr(true)); + // Drain RX FIFO in case it still has some unrelevant data while registers.fifostat().read().rxnotempty() { let _ = registers.fiford().read().0; @@ -513,6 +725,17 @@ impl<'d, M: Mode> Usart<'d, M> { } } +impl<'d> Usart<'d, Async> { + /// Write to UART TX from the provided buffer. + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.tx.write(buffer).await + } + + /// Read from UART RX into the provided buffer. + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.rx.read(buffer).await + } +} impl<'d, M: Mode> embedded_hal_02::blocking::serial::Write for UsartTx<'d, M> { type Error = Error; @@ -584,10 +807,12 @@ impl<'d> embedded_io::Read for Usart<'d, Blocking> { struct Info { usart_reg: UsartReg, fc_reg: FlexcommReg, + interrupt: Interrupt, } trait SealedInstance { fn info() -> &'static Info; + fn dma_state() -> &'static DmaState; fn instance_number() -> usize; fn tx_pin_func() -> PioFunc; fn rx_pin_func() -> PioFunc; @@ -595,7 +820,10 @@ trait SealedInstance { /// UART instance. #[allow(private_bounds)] -pub trait Instance: SealedInstance + PeripheralType {} +pub trait Instance: SealedInstance + PeripheralType { + /// Interrupt for this instance. + type Interrupt: crate::interrupt::typelevel::Interrupt; +} macro_rules! impl_instance { ($inst:ident, $fc:ident, $tx_pin:ident, $rx_pin:ident, $fc_num:expr) => { @@ -604,9 +832,18 @@ macro_rules! impl_instance { static INFO: Info = Info { usart_reg: crate::pac::$inst, fc_reg: crate::pac::$fc, + interrupt: crate::interrupt::typelevel::$fc::IRQ, }; &INFO } + + fn dma_state() -> &'static DmaState { + static STATE: DmaState = DmaState { + rx_err_waker: AtomicWaker::new(), + rx_err: AtomicBool::new(false), + }; + &STATE + } #[inline] fn instance_number() -> usize { $fc_num @@ -620,7 +857,9 @@ macro_rules! impl_instance { PioFunc::$rx_pin } } - impl $crate::usart::Instance for $crate::peripherals::$inst {} + impl $crate::usart::Instance for $crate::peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$fc; + } }; } @@ -663,3 +902,34 @@ impl_pin!(PIO1_16, USART6, Tx); impl_pin!(PIO1_13, USART6, Rx); impl_pin!(PIO0_19, USART7, Tx); impl_pin!(PIO0_20, USART7, Rx); + +/// Trait for TX DMA channels. +pub trait TxChannel: crate::dma::Channel {} +/// Trait for RX DMA channels. +pub trait RxChannel: crate::dma::Channel {} + +macro_rules! impl_channel { + ($dma:ident, $instance:ident, Tx) => { + impl TxChannel for crate::peripherals::$dma {} + }; + ($dma:ident, $instance:ident, Rx) => { + impl RxChannel for crate::peripherals::$dma {} + }; +} + +impl_channel!(DMA_CH4, USART0, Rx); +impl_channel!(DMA_CH5, USART0, Tx); +impl_channel!(DMA_CH6, USART1, Rx); +impl_channel!(DMA_CH7, USART1, Tx); +impl_channel!(DMA_CH10, USART2, Rx); +impl_channel!(DMA_CH11, USART2, Tx); +impl_channel!(DMA_CH8, USART3, Rx); +impl_channel!(DMA_CH9, USART3, Tx); +impl_channel!(DMA_CH12, USART4, Rx); +impl_channel!(DMA_CH13, USART4, Tx); +impl_channel!(DMA_CH14, USART5, Rx); +impl_channel!(DMA_CH15, USART5, Tx); +impl_channel!(DMA_CH16, USART6, Rx); +impl_channel!(DMA_CH17, USART6, Tx); +impl_channel!(DMA_CH18, USART7, Rx); +impl_channel!(DMA_CH19, USART7, Tx); diff --git a/examples/lpc55s69/src/bin/usart_async.rs b/examples/lpc55s69/src/bin/usart_async.rs new file mode 100644 index 000000000..b06abd477 --- /dev/null +++ b/examples/lpc55s69/src/bin/usart_async.rs @@ -0,0 +1,70 @@ +#![no_std] +#![no_main] + +use core::str::from_utf8_mut; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nxp::bind_interrupts; +use embassy_nxp::gpio::{Level, Output}; +use embassy_nxp::peripherals::USART2; +use embassy_nxp::usart::{Config, InterruptHandler, Usart}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_halt as _}; + +bind_interrupts!(struct Irqs { + FLEXCOMM2 => InterruptHandler; + } +); + +#[embassy_executor::task] +async fn blinky_task(mut led: Output<'static>) { + loop { + info!("[TASK] led off!"); + led.set_high(); + Timer::after_millis(500).await; + + info!("[TASK] led on!"); + led.set_low(); + Timer::after_millis(500).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nxp::init(Default::default()); + let mut usart = Usart::new( + p.USART2, + p.PIO0_27, + p.PIO1_24, + Irqs, + p.DMA_CH11, + p.DMA_CH10, + Config::default(), + ); + let led = Output::new(p.PIO1_6, Level::Low); + spawner.spawn(blinky_task(led).unwrap()); + info!("[MAIN] Entering main loop"); + loop { + let tx_buf = b"Hello, Ferris!"; + let mut rx_buf = [0u8; 14]; + info!("[MAIN] Write a message"); + usart.write(tx_buf).await.unwrap(); + Timer::after_millis(500).await; + + info!("[MAIN] Read a message"); + match usart.read(&mut rx_buf).await { + Ok(_) => match from_utf8_mut(&mut rx_buf) { + Ok(str) => { + info!("[MAIN] The message is: {}", str); + } + Err(_) => { + error!("[MAIN] Error in converting to UTF8"); + } + }, + Err(e) => warn!("[MAIN] Error: {}", e), + } + + Timer::after_millis(500).await; + } +} -- cgit From 085ab9f617357cf94b8b4e44424850e48add2fae Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 Sep 2025 01:14:12 +0200 Subject: stm32: update metapac, add L4+ to CI. --- embassy-stm32/Cargo.toml | 8 ++++++-- embassy-stm32/src/rcc/bd.rs | 10 +++++----- embassy-stm32/src/rtc/low_power.rs | 36 +++++++++++------------------------- embassy-stm32/src/rtc/mod.rs | 21 ++++++++------------- embassy-stm32/src/rtc/v2.rs | 8 ++++---- 5 files changed, 34 insertions(+), 49 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b029f33b0..82bc73708 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -66,6 +66,10 @@ build = [ {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l431cb", "time", "time-driver-any"]}, {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l476vg", "time", "time-driver-any"]}, {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l422cb", "time", "time-driver-any"]}, + {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l4p5ae", "time", "time-driver-any", "single-bank"]}, + {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l4q5zg", "time", "time-driver-any", "single-bank"]}, + {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l4r9vi", "time", "time-driver-any", "dual-bank"]}, + {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32l4s7vi", "time", "time-driver-any", "dual-bank"]}, {target = "thumbv7em-none-eabi", features = ["defmt", "exti", "stm32wb15cc", "time", "time-driver-any"]}, {target = "thumbv6m-none-eabi", features = ["defmt", "exti", "stm32l072cz", "time", "time-driver-any"]}, {target = "thumbv6m-none-eabi", features = ["defmt", "exti", "stm32l041f6", "time", "time-driver-any"]}, @@ -174,7 +178,7 @@ futures-util = { version = "0.3.30", default-features = false } sdio-host = "0.9.0" critical-section = "1.1" #stm32-metapac = { version = "18" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b46fcc32f329f05fbdca4c007ed4bc305b0ade85" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b9f6b0c542d85ee695d71c35ced195e0cef51ac0" } vcell = "0.1.3" nb = "1.0.0" @@ -204,7 +208,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b46fcc32f329f05fbdca4c007ed4bc305b0ade85", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b9f6b0c542d85ee695d71c35ced195e0cef51ac0", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index e2c704405..63fc195dd 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs @@ -52,9 +52,9 @@ impl From for crate::pac::rcc::vals::Lsedrv { } } -#[cfg(not(any(rtc_v2l0, rtc_v2l1, stm32c0)))] +#[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0)))] type Bdcr = crate::pac::rcc::regs::Bdcr; -#[cfg(any(rtc_v2l0, rtc_v2l1))] +#[cfg(any(rtc_v2_l0, rtc_v2_l1))] type Bdcr = crate::pac::rcc::regs::Csr; #[cfg(any(stm32c0))] type Bdcr = crate::pac::rcc::regs::Csr1; @@ -76,9 +76,9 @@ fn unlock() { } fn bdcr() -> Reg { - #[cfg(any(rtc_v2l0, rtc_v2l1))] + #[cfg(any(rtc_v2_l0, rtc_v2_l1))] return crate::pac::RCC.csr(); - #[cfg(not(any(rtc_v2l0, rtc_v2l1, stm32c0)))] + #[cfg(not(any(rtc_v2_l0, rtc_v2_l1, stm32c0)))] return crate::pac::RCC.bdcr(); #[cfg(any(stm32c0))] return crate::pac::RCC.csr1(); @@ -273,7 +273,7 @@ impl LsConfig { if self.rtc != RtcClockSource::DISABLE { bdcr().modify(|w| { - #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))] + #[cfg(any(rtc_v2_h7, rtc_v2_l4, rtc_v2_wb, rtc_v3_base, rtc_v3_u5))] assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet."); #[cfg(not(rcc_wba))] diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index 78ccd3e6c..a81ac6746 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs @@ -1,4 +1,8 @@ +#[cfg(feature = "time")] +use embassy_time::{Duration, TICK_HZ}; + use super::{bcd2_to_byte, DateTimeError, Rtc, RtcError}; +use crate::interrupt::typelevel::Interrupt; use crate::peripherals::RTC; use crate::rtc::SealedInstance; @@ -11,7 +15,7 @@ pub(super) struct RtcInstant { } impl RtcInstant { - #[cfg(not(rtc_v2f2))] + #[cfg(not(rtc_v2_f2))] const fn from(second: u8, subsecond: u16) -> Result { if second > 59 { Err(DateTimeError::InvalidSecond) @@ -38,8 +42,6 @@ impl core::ops::Sub for RtcInstant { type Output = embassy_time::Duration; fn sub(self, rhs: Self) -> Self::Output { - use embassy_time::{Duration, TICK_HZ}; - let second = if self.second < rhs.second { self.second + 60 } else { @@ -129,11 +131,6 @@ impl Rtc { requested_duration: embassy_time::Duration, cs: critical_section::CriticalSection, ) { - use embassy_time::{Duration, TICK_HZ}; - - #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] - use crate::pac::rtc::vals::Calrf; - // Panic if the rcc mod knows we're not using low-power rtc #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] unsafe { crate::rcc::get_freqs() }.rtc.to_hertz().unwrap(); @@ -150,17 +147,15 @@ impl Rtc { self.write(false, |regs| { regs.cr().modify(|w| w.set_wute(false)); - #[cfg(any( - rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb - ))] + #[cfg(rtc_v2)] { regs.isr().modify(|w| w.set_wutf(false)); while !regs.isr().read().wutwf() {} } - #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] + #[cfg(rtc_v3)] { - regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); + regs.scr().write(|w| w.set_cwutf(crate::pac::rtc::vals::Calrf::CLEAR)); while !regs.icsr().read().wutwf() {} } @@ -185,10 +180,6 @@ impl Rtc { /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` /// was called, otherwise none pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option { - use crate::interrupt::typelevel::Interrupt; - #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] - use crate::pac::rtc::vals::Calrf; - let instant = self.instant().unwrap(); if RTC::regs().cr().read().wute() { trace!("rtc: stop wakeup alarm at {}", instant); @@ -197,13 +188,10 @@ impl Rtc { regs.cr().modify(|w| w.set_wutie(false)); regs.cr().modify(|w| w.set_wute(false)); - #[cfg(any( - rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb - ))] + #[cfg(rtc_v2)] regs.isr().modify(|w| w.set_wutf(false)); - - #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] - regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); + #[cfg(rtc_v3)] + regs.scr().write(|w| w.set_cwutf(crate::pac::rtc::vals::Calrf::CLEAR)); // Check RM for EXTI and/or NVIC section, "Event event input mapping" or "EXTI interrupt/event mapping" or something similar, // there is a table for every "Event input" / "EXTI Line". @@ -222,8 +210,6 @@ impl Rtc { } pub(crate) fn enable_wakeup_line(&self) { - use crate::interrupt::typelevel::Interrupt; - ::WakeupInterrupt::unpend(); unsafe { ::WakeupInterrupt::enable() }; diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 449f3008a..92dec0960 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -18,14 +18,9 @@ use crate::pac::rtc::regs::{Dr, Tr}; use crate::time::Hertz; /// refer to AN4759 to compare features of RTC2 and RTC3 -#[cfg_attr(any(rtc_v1), path = "v1.rs")] -#[cfg_attr( - any( - rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb - ), - path = "v2.rs" -)] -#[cfg_attr(any(rtc_v3, rtc_v3u5, rtc_v3l5, rtc_v3h7rs, rtc_v3c0), path = "v3.rs")] +#[cfg_attr(rtc_v1, path = "v1.rs")] +#[cfg_attr(rtc_v2, path = "v2.rs")] +#[cfg_attr(rtc_v3, path = "v3.rs")] mod _version; #[allow(unused_imports)] pub use _version::*; @@ -72,12 +67,12 @@ impl RtcTimeProvider { // Calculate second fraction and multiply to microseconds // Formula from RM0410 - #[cfg(not(rtc_v2f2))] + #[cfg(not(rtc_v2_f2))] let us = { let prediv = RTC::regs().prer().read().prediv_s() as f32; (((prediv - _ss as f32) / (prediv + 1.0)) * 1e6).min(999_999.0) as u32 }; - #[cfg(rtc_v2f2)] + #[cfg(rtc_v2_f2)] let us = 0; DateTime::from(year, month, day, weekday, hour, minute, second, us).map_err(RtcError::InvalidDateTime) @@ -87,9 +82,9 @@ impl RtcTimeProvider { fn read(&self, mut f: impl FnMut(Dr, Tr, u16) -> Result) -> Result { let r = RTC::regs(); - #[cfg(not(rtc_v2f2))] + #[cfg(not(rtc_v2_f2))] let read_ss = || r.ssr().read().ss(); - #[cfg(rtc_v2f2)] + #[cfg(rtc_v2_f2)] let read_ss = || 0; let mut ss = read_ss(); @@ -168,7 +163,7 @@ impl Rtc { this.configure(async_psc, sync_psc); // Wait for the clock to update after initialization - #[cfg(not(rtc_v2f2))] + #[cfg(not(rtc_v2_f2))] { let now = this.time_provider().read(|_, _, ss| Ok(ss)).unwrap(); while now == this.time_provider().read(|_, _, ss| Ok(ss)).unwrap() {} diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 28380a3c0..23f6ccb0c 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -11,11 +11,11 @@ impl super::Rtc { pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { self.write(true, |rtc| { rtc.cr().modify(|w| { - #[cfg(not(rtc_v2f2))] + #[cfg(not(rtc_v2_f2))] w.set_bypshad(true); - #[cfg(rtc_v2f2)] + #[cfg(rtc_v2_f2)] w.set_fmt(false); - #[cfg(not(rtc_v2f2))] + #[cfg(not(rtc_v2_f2))] w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR); w.set_osel(Osel::DISABLED); w.set_pol(Pol::HIGH); @@ -36,7 +36,7 @@ impl super::Rtc { /// /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler` /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12). - #[cfg(not(rtc_v2f2))] + #[cfg(not(rtc_v2_f2))] pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) { const RTC_CALR_MIN_PPM: f32 = -487.1; const RTC_CALR_MAX_PPM: f32 = 488.5; -- cgit From 0cb88e7daf2cb46b9f6e06905b3d0113bb8dd643 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 26 Sep 2025 11:00:26 +0200 Subject: fix: update bt-hci to 0.6 Fixes an issue for controllers that emit unexpected command complete events. --- cyw43/CHANGELOG.md | 2 +- cyw43/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cyw43/CHANGELOG.md b/cyw43/CHANGELOG.md index dcf84b9ba..5c77b7093 100644 --- a/cyw43/CHANGELOG.md +++ b/cyw43/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate - Updated documentation for Control::join() #4678 -- Bump bt-hci to 0.5.0. +- Bump bt-hci to 0.6.0. - Add error handling to HCI transport implementation. ## 0.5.0 - 2025-08-28 diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index 0d0c26089..c59c15a71 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -36,7 +36,7 @@ heapless = "0.8.0" # Bluetooth deps embedded-io-async = { version = "0.6.0", optional = true } -bt-hci = { version = "0.5.0", optional = true } +bt-hci = { version = "0.6.0", optional = true } [package.metadata.embassy] build = [ -- cgit From 03d0637d6eed2fe9d44f077aa9ad39bdc8631304 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 Sep 2025 21:21:53 +0200 Subject: Update nightly. --- embassy-executor/tests/ui/spawn_nonsend.rs | 2 -- embassy-executor/tests/ui/spawn_nonsend.stderr | 22 +++++++--------------- rust-toolchain-nightly.toml | 2 +- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/embassy-executor/tests/ui/spawn_nonsend.rs b/embassy-executor/tests/ui/spawn_nonsend.rs index 601041941..a06c0b37a 100644 --- a/embassy-executor/tests/ui/spawn_nonsend.rs +++ b/embassy-executor/tests/ui/spawn_nonsend.rs @@ -1,7 +1,5 @@ #![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] -use core::future::Future; - use embassy_executor::SendSpawner; #[embassy_executor::task] diff --git a/embassy-executor/tests/ui/spawn_nonsend.stderr b/embassy-executor/tests/ui/spawn_nonsend.stderr index 5a3131602..31efadd49 100644 --- a/embassy-executor/tests/ui/spawn_nonsend.stderr +++ b/embassy-executor/tests/ui/spawn_nonsend.stderr @@ -1,27 +1,19 @@ -warning: unused import: `core::future::Future` - --> tests/ui/spawn_nonsend.rs:3:5 - | -3 | use core::future::Future; - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[warn(unused_imports)]` on by default - error[E0277]: `*mut ()` cannot be sent between threads safely - --> tests/ui/spawn_nonsend.rs:13:13 + --> tests/ui/spawn_nonsend.rs:11:13 | - 7 | #[embassy_executor::task] + 5 | #[embassy_executor::task] | ------------------------- within this `impl Sized` ... -13 | s.spawn(task(core::ptr::null_mut()).unwrap()); +11 | s.spawn(task(core::ptr::null_mut()).unwrap()); | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*mut ()` cannot be sent between threads safely | | | required by a bound introduced by this call | = help: within `impl Sized`, the trait `Send` is not implemented for `*mut ()` note: required because it's used within this closure - --> tests/ui/spawn_nonsend.rs:7:1 + --> tests/ui/spawn_nonsend.rs:5:1 | - 7 | #[embassy_executor::task] + 5 | #[embassy_executor::task] | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: required because it appears within the type `impl Sized` --> src/raw/mod.rs @@ -29,9 +21,9 @@ note: required because it appears within the type `impl Sized` | pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> Result, SpawnError> | ^^^^^^^^^^ note: required because it appears within the type `impl Sized` - --> tests/ui/spawn_nonsend.rs:7:1 + --> tests/ui/spawn_nonsend.rs:5:1 | - 7 | #[embassy_executor::task] + 5 | #[embassy_executor::task] | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `SendSpawner::spawn` --> src/spawner.rs diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml index d3e88c7e1..dde431bba 100644 --- a/rust-toolchain-nightly.toml +++ b/rust-toolchain-nightly.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2025-08-05" +channel = "nightly-2025-09-26" components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] targets = [ "thumbv7em-none-eabi", -- cgit From 34911c581c1066a9650ac2103d125d5bbb9a229c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 Sep 2025 21:49:40 +0200 Subject: Fix docs build. --- .github/ci/build-nightly.sh | 2 +- .github/ci/build-xtensa.sh | 2 +- .github/ci/build.sh | 2 +- .github/ci/doc.sh | 2 +- .github/ci/janitor.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ci/build-nightly.sh b/.github/ci/build-nightly.sh index 257d7ebd3..8cca1b445 100755 --- a/.github/ci/build-nightly.sh +++ b/.github/ci/build-nightly.sh @@ -23,7 +23,7 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 3ca80f7065acbe0b69b7da463fab60e744f9de79 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 676e6d602bf016dc71f1e98f2c7f191d7bd20707 ./ci-nightly.sh diff --git a/.github/ci/build-xtensa.sh b/.github/ci/build-xtensa.sh index 339e28467..dbd2f7ffc 100755 --- a/.github/ci/build-xtensa.sh +++ b/.github/ci/build-xtensa.sh @@ -25,7 +25,7 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 3ca80f7065acbe0b69b7da463fab60e744f9de79 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 676e6d602bf016dc71f1e98f2c7f191d7bd20707 ./ci-xtensa.sh diff --git a/.github/ci/build.sh b/.github/ci/build.sh index d7201aedb..d5e0e0bd2 100755 --- a/.github/ci/build.sh +++ b/.github/ci/build.sh @@ -28,7 +28,7 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 3ca80f7065acbe0b69b7da463fab60e744f9de79 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 676e6d602bf016dc71f1e98f2c7f191d7bd20707 ./ci.sh diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 70ce110d1..dab47e86d 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -12,7 +12,7 @@ export CARGO_TARGET_DIR=/ci/cache/target export PATH=$CARGO_HOME/bin:$PATH mv rust-toolchain-nightly.toml rust-toolchain.toml -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 3ca80f7065acbe0b69b7da463fab60e744f9de79 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 676e6d602bf016dc71f1e98f2c7f191d7bd20707 cargo embassy-devtool doc -o webroot diff --git a/.github/ci/janitor.sh b/.github/ci/janitor.sh index bd04f47fc..305c6b227 100755 --- a/.github/ci/janitor.sh +++ b/.github/ci/janitor.sh @@ -9,7 +9,7 @@ export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target export PATH=$CARGO_HOME/bin:$PATH -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 3ca80f7065acbe0b69b7da463fab60e744f9de79 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 676e6d602bf016dc71f1e98f2c7f191d7bd20707 cargo embassy-devtool check-crlf cargo embassy-devtool check-manifest -- cgit From fff0a1522dd9e32a15004130d0c097f903e422b0 Mon Sep 17 00:00:00 2001 From: Nathan Samson Date: Sat, 27 Sep 2025 00:36:20 +0200 Subject: Fixes #4709. Creating an open AP after joining a WPA network doesn't work This fixes the issue by always resetting the security value when creating a new open AP. --- cyw43/CHANGELOG.md | 1 + cyw43/src/control.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/cyw43/CHANGELOG.md b/cyw43/CHANGELOG.md index 5c77b7093..1045fd30b 100644 --- a/cyw43/CHANGELOG.md +++ b/cyw43/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated documentation for Control::join() #4678 - Bump bt-hci to 0.6.0. - Add error handling to HCI transport implementation. +- Reset WPA security on AP creation #4709 ## 0.5.0 - 2025-08-28 diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs index e4dc0804f..fd0d4d532 100644 --- a/cyw43/src/control.rs +++ b/cyw43/src/control.rs @@ -470,6 +470,8 @@ impl<'a> Control<'a> { pfi.passphrase[..passphrase.as_bytes().len()].copy_from_slice(passphrase.as_bytes()); self.ioctl(IoctlType::Set, Ioctl::SetWsecPmk, 0, &mut pfi.to_bytes()) .await; + } else { + self.ioctl_set_u32(Ioctl::SetAuth, 0, 0).await; } // Change mutlicast rate from 1 Mbps to 11 Mbps -- cgit From e5328c78259c7e288bf54c83bc80c2d2311abdf2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Sep 2025 17:34:36 +0200 Subject: nrf/twim: erase instance generics --- embassy-nrf/src/twim.rs | 75 +++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 3fc59a39a..ffc9b39f6 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -2,7 +2,7 @@ #![macro_use] -use core::future::{poll_fn, Future}; +use core::future::poll_fn; use core::marker::PhantomData; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; @@ -112,12 +112,15 @@ impl interrupt::typelevel::Handler for InterruptHandl } /// TWI driver. -pub struct Twim<'d, T: Instance> { - _p: Peri<'d, T>, +pub struct Twim<'d> { + r: pac::twim::Twim, + irq: interrupt::Interrupt, + state: &'static State, tx_ram_buffer: &'d mut [u8], + _p: PhantomData<&'d ()>, } -impl<'d, T: Instance> Twim<'d, T> { +impl<'d> Twim<'d> { /// Create a new TWI driver. /// /// `tx_ram_buffer` is required if any write operations will be performed with data that is not in RAM. @@ -125,8 +128,8 @@ impl<'d, T: Instance> Twim<'d, T> { /// needs to be at least as large as the largest write operation that will be executed with a buffer /// that is not in RAM. If all write operations will be performed from RAM, an empty buffer (`&[]`) may /// be used. - pub fn new( - twim: Peri<'d, T>, + pub fn new( + _twim: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, sda: Peri<'d, impl GpioPin>, scl: Peri<'d, impl GpioPin>, @@ -167,8 +170,11 @@ impl<'d, T: Instance> Twim<'d, T> { r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); let mut twim = Self { - _p: twim, + r: T::regs(), + irq: T::Interrupt::IRQ, + state: T::state(), tx_ram_buffer, + _p: PhantomData {}, }; // Apply runtime peripheral configuration @@ -201,7 +207,7 @@ impl<'d, T: Instance> Twim<'d, T> { return Err(Error::TxBufferTooLong); } - let r = T::regs(); + let r = self.r; // We're giving the register a pointer to the stack. Since we're // waiting for the I2C transaction to end before this stack pointer @@ -228,7 +234,7 @@ impl<'d, T: Instance> Twim<'d, T> { return Err(Error::RxBufferTooLong); } - let r = T::regs(); + let r = self.r; // We're giving the register a pointer to the stack. Since we're // waiting for the I2C transaction to end before this stack pointer @@ -250,7 +256,7 @@ impl<'d, T: Instance> Twim<'d, T> { } fn clear_errorsrc(&mut self) { - let r = T::regs(); + let r = self.r; r.errorsrc().write(|w| { w.set_anack(true); w.set_dnack(true); @@ -259,8 +265,8 @@ impl<'d, T: Instance> Twim<'d, T> { } /// Get Error instance, if any occurred. - fn check_errorsrc() -> Result<(), Error> { - let r = T::regs(); + fn check_errorsrc(&mut self) -> Result<(), Error> { + let r = self.r; let err = r.errorsrc().read(); if err.anack() { @@ -276,7 +282,7 @@ impl<'d, T: Instance> Twim<'d, T> { } fn check_rx(&self, len: usize) -> Result<(), Error> { - let r = T::regs(); + let r = self.r; if r.rxd().amount().read().0 != len as u32 { Err(Error::Receive) } else { @@ -285,7 +291,7 @@ impl<'d, T: Instance> Twim<'d, T> { } fn check_tx(&self, len: usize) -> Result<(), Error> { - let r = T::regs(); + let r = self.r; if r.txd().amount().read().0 != len as u32 { Err(Error::Transmit) } else { @@ -295,7 +301,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// Wait for stop or error fn blocking_wait(&mut self) { - let r = T::regs(); + let r = self.r; loop { if r.events_suspended().read() != 0 || r.events_stopped().read() != 0 { r.events_suspended().write_value(0); @@ -312,7 +318,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// Wait for stop or error #[cfg(feature = "time")] fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result<(), Error> { - let r = T::regs(); + let r = self.r; let deadline = Instant::now() + timeout; loop { if r.events_suspended().read() != 0 || r.events_stopped().read() != 0 { @@ -333,10 +339,10 @@ impl<'d, T: Instance> Twim<'d, T> { } /// Wait for stop or error - fn async_wait(&mut self) -> impl Future> { - poll_fn(move |cx| { - let r = T::regs(); - let s = T::state(); + async fn async_wait(&mut self) -> Result<(), Error> { + poll_fn(|cx| { + let r = self.r; + let s = self.state; s.end_waker.register(cx.waker()); if r.events_suspended().read() != 0 || r.events_stopped().read() != 0 { @@ -349,15 +355,16 @@ impl<'d, T: Instance> Twim<'d, T> { if r.events_error().read() != 0 { r.events_error().write_value(0); r.tasks_stop().write_value(1); - if let Err(e) = Self::check_errorsrc() { + if let Err(e) = self.check_errorsrc() { return Poll::Ready(Err(e)); } else { - panic!("Found events_error bit without an error in errorsrc reg"); + return Poll::Ready(Err(Error::Timeout)); } } Poll::Pending }) + .await } fn setup_operations( @@ -367,7 +374,7 @@ impl<'d, T: Instance> Twim<'d, T> { last_op: Option<&Operation<'_>>, inten: bool, ) -> Result { - let r = T::regs(); + let r = self.r; compiler_fence(SeqCst); @@ -511,7 +518,7 @@ impl<'d, T: Instance> Twim<'d, T> { fn check_operations(&mut self, operations: &[Operation<'_>]) -> Result<(), Error> { compiler_fence(SeqCst); - Self::check_errorsrc()?; + self.check_errorsrc()?; assert!(operations.len() == 1 || operations.len() == 2); match operations { @@ -696,14 +703,14 @@ impl<'d, T: Instance> Twim<'d, T> { } } -impl<'a, T: Instance> Drop for Twim<'a, T> { +impl<'a> Drop for Twim<'a> { fn drop(&mut self) { trace!("twim drop"); // TODO: check for abort // disable! - let r = T::regs(); + let r = self.r; r.enable().write(|w| w.set_enable(vals::Enable::DISABLED)); gpio::deconfigure_pin(r.psel().sda().read()); @@ -759,7 +766,7 @@ macro_rules! impl_twim { mod eh02 { use super::*; - impl<'a, T: Instance> embedded_hal_02::blocking::i2c::Write for Twim<'a, T> { + impl<'a> embedded_hal_02::blocking::i2c::Write for Twim<'a> { type Error = Error; fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { @@ -767,7 +774,7 @@ mod eh02 { } } - impl<'a, T: Instance> embedded_hal_02::blocking::i2c::Read for Twim<'a, T> { + impl<'a> embedded_hal_02::blocking::i2c::Read for Twim<'a> { type Error = Error; fn read(&mut self, addr: u8, bytes: &mut [u8]) -> Result<(), Error> { @@ -775,7 +782,7 @@ mod eh02 { } } - impl<'a, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for Twim<'a, T> { + impl<'a> embedded_hal_02::blocking::i2c::WriteRead for Twim<'a> { type Error = Error; fn write_read<'w>(&mut self, addr: u8, bytes: &'w [u8], buffer: &'w mut [u8]) -> Result<(), Error> { @@ -804,27 +811,27 @@ impl embedded_hal_1::i2c::Error for Error { } } -impl<'d, T: Instance> embedded_hal_1::i2c::ErrorType for Twim<'d, T> { +impl<'d> embedded_hal_1::i2c::ErrorType for Twim<'d> { type Error = Error; } -impl<'d, T: Instance> embedded_hal_1::i2c::I2c for Twim<'d, T> { +impl<'d> embedded_hal_1::i2c::I2c for Twim<'d> { fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { self.blocking_transaction(address, operations) } } -impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> { +impl<'d> embedded_hal_async::i2c::I2c for Twim<'d> { async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { self.transaction(address, operations).await } } -impl<'d, T: Instance> SetConfig for Twim<'d, T> { +impl<'d> SetConfig for Twim<'d> { type Config = Config; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { - let r = T::regs(); + let r = self.r; r.frequency().write(|w| w.set_frequency(config.frequency)); Ok(()) -- cgit From b07192079f0fc6ce210104786540aa7be8938d40 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Sep 2025 18:37:33 +0200 Subject: nrf/uart,timer: erase instance generics. --- embassy-nrf/src/buffered_uarte.rs | 229 ++++++++++++++++----------- embassy-nrf/src/timer.rs | 82 +++++----- embassy-nrf/src/twim.rs | 2 - embassy-nrf/src/uarte.rs | 148 ++++++++++------- examples/nrf52840/src/bin/uart_split.rs | 2 +- examples/nrf9160/src/bin/modem_tcp_client.rs | 2 +- 6 files changed, 274 insertions(+), 191 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 29e126903..40c679190 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -23,6 +23,7 @@ pub use pac::uarte::vals::{Baudrate, ConfigParity as Parity}; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::typelevel::Interrupt; +use crate::interrupt::InterruptExt; use crate::ppi::{ self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, }; @@ -207,21 +208,21 @@ impl interrupt::typelevel::Handler for Interrupt } /// Buffered UARTE driver. -pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> { - tx: BufferedUarteTx<'d, U>, - rx: BufferedUarteRx<'d, U, T>, +pub struct BufferedUarte<'d> { + tx: BufferedUarteTx<'d>, + rx: BufferedUarteRx<'d>, } -impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {} +impl<'d> Unpin for BufferedUarte<'d> {} -impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { +impl<'d> BufferedUarte<'d> { /// Create a new BufferedUarte without hardware flow control. /// /// # Panics /// /// Panics if `rx_buffer.len()` is odd. #[allow(clippy::too_many_arguments)] - pub fn new( + pub fn new( uarte: Peri<'d, U>, timer: Peri<'d, T>, ppi_ch1: Peri<'d, impl ConfigurableChannel>, @@ -256,7 +257,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { /// /// Panics if `rx_buffer.len()` is odd. #[allow(clippy::too_many_arguments)] - pub fn new_with_rtscts( + pub fn new_with_rtscts( uarte: Peri<'d, U>, timer: Peri<'d, T>, ppi_ch1: Peri<'d, impl ConfigurableChannel>, @@ -288,7 +289,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { } #[allow(clippy::too_many_arguments)] - fn new_inner( + fn new_inner( peri: Peri<'d, U>, timer: Peri<'d, T>, ppi_ch1: Peri<'d, AnyConfigurableChannel>, @@ -302,30 +303,33 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { rx_buffer: &'d mut [u8], tx_buffer: &'d mut [u8], ) -> Self { - configure(U::regs(), config, cts.is_some()); + let r = U::regs(); + let irq = U::Interrupt::IRQ; + let state = U::state(); + + configure(r, config, cts.is_some()); let tx = BufferedUarteTx::new_innerer(unsafe { peri.clone_unchecked() }, txd, cts, tx_buffer); let rx = BufferedUarteRx::new_innerer(peri, timer, ppi_ch1, ppi_ch2, ppi_group, rxd, rts, rx_buffer); - U::regs().enable().write(|w| w.set_enable(vals::Enable::ENABLED)); - U::Interrupt::pend(); - unsafe { U::Interrupt::enable() }; + r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); + irq.pend(); + unsafe { irq.enable() }; - U::state().tx_rx_refcount.store(2, Ordering::Relaxed); + state.tx_rx_refcount.store(2, Ordering::Relaxed); Self { tx, rx } } /// Adjust the baud rate to the provided value. pub fn set_baudrate(&mut self, baudrate: Baudrate) { - let r = U::regs(); - r.baudrate().write(|w| w.set_baudrate(baudrate)); + self.tx.set_baudrate(baudrate); } /// Split the UART in reader and writer parts. /// /// This allows reading and writing concurrently from independent tasks. - pub fn split(self) -> (BufferedUarteRx<'d, U, T>, BufferedUarteTx<'d, U>) { + pub fn split(self) -> (BufferedUarteRx<'d>, BufferedUarteTx<'d>) { (self.rx, self.tx) } @@ -333,7 +337,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { /// /// The returned halves borrow from `self`, so you can drop them and go back to using /// the "un-split" `self`. This allows temporarily splitting the UART. - pub fn split_by_ref(&mut self) -> (&mut BufferedUarteRx<'d, U, T>, &mut BufferedUarteTx<'d, U>) { + pub fn split_by_ref(&mut self) -> (&mut BufferedUarteRx<'d>, &mut BufferedUarteTx<'d>) { (&mut self.rx, &mut self.tx) } @@ -369,13 +373,17 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { } /// Reader part of the buffered UARTE driver. -pub struct BufferedUarteTx<'d, U: UarteInstance> { - _peri: Peri<'d, U>, +pub struct BufferedUarteTx<'d> { + r: pac::uarte::Uarte, + _irq: interrupt::Interrupt, + state: &'static crate::uarte::State, + buffered_state: &'static State, + _p: PhantomData<&'d ()>, } -impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> { +impl<'d> BufferedUarteTx<'d> { /// Create a new BufferedUarteTx without hardware flow control. - pub fn new( + pub fn new( uarte: Peri<'d, U>, txd: Peri<'d, impl GpioPin>, _irq: impl interrupt::typelevel::Binding> + 'd, @@ -390,7 +398,7 @@ impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> { /// # Panics /// /// Panics if `rx_buffer.len()` is odd. - pub fn new_with_cts( + pub fn new_with_cts( uarte: Peri<'d, U>, txd: Peri<'d, impl GpioPin>, cts: Peri<'d, impl GpioPin>, @@ -401,41 +409,48 @@ impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> { Self::new_inner(uarte, txd.into(), Some(cts.into()), config, tx_buffer) } - fn new_inner( + fn new_inner( peri: Peri<'d, U>, txd: Peri<'d, AnyPin>, cts: Option>, config: Config, tx_buffer: &'d mut [u8], ) -> Self { - configure(U::regs(), config, cts.is_some()); + let r = U::regs(); + let irq = U::Interrupt::IRQ; + let state = U::state(); + let _buffered_state = U::buffered_state(); + + configure(r, config, cts.is_some()); let this = Self::new_innerer(peri, txd, cts, tx_buffer); - U::regs().enable().write(|w| w.set_enable(vals::Enable::ENABLED)); - U::Interrupt::pend(); - unsafe { U::Interrupt::enable() }; + r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); + irq.pend(); + unsafe { irq.enable() }; - U::state().tx_rx_refcount.store(1, Ordering::Relaxed); + state.tx_rx_refcount.store(1, Ordering::Relaxed); this } - fn new_innerer( - peri: Peri<'d, U>, + fn new_innerer( + _peri: Peri<'d, U>, txd: Peri<'d, AnyPin>, cts: Option>, tx_buffer: &'d mut [u8], ) -> Self { let r = U::regs(); + let irq = U::Interrupt::IRQ; + let state = U::state(); + let buffered_state = U::buffered_state(); configure_tx_pins(r, txd, cts); // Initialize state - let s = U::buffered_state(); - s.tx_count.store(0, Ordering::Relaxed); + buffered_state.tx_count.store(0, Ordering::Relaxed); let len = tx_buffer.len(); - unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + unsafe { buffered_state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; r.events_txstarted().write_value(0); @@ -444,15 +459,21 @@ impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> { w.set_endtx(true); }); - Self { _peri: peri } + Self { + r, + _irq: irq, + state, + buffered_state, + _p: PhantomData, + } } /// Write a buffer into this writer, returning how many bytes were written. - pub fn write<'a>(&'a mut self, buf: &'a [u8]) -> impl Future> + 'a { + pub fn write<'a>(&'a mut self, buf: &'a [u8]) -> impl Future> + 'a + use<'a, 'd> { poll_fn(move |cx| { //trace!("poll_write: {:?}", buf.len()); - let ss = U::state(); - let s = U::buffered_state(); + let ss = self.state; + let s = self.buffered_state; let mut tx = unsafe { s.tx_buf.writer() }; let tx_buf = tx.push_slice(); @@ -469,7 +490,7 @@ impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> { //trace!("poll_write: queued {:?}", n); compiler_fence(Ordering::SeqCst); - U::Interrupt::pend(); + self._irq.pend(); Poll::Ready(Ok(n)) }) @@ -478,7 +499,7 @@ impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> { /// Try writing a buffer without waiting, returning how many bytes were written. pub fn try_write(&mut self, buf: &[u8]) -> Result { //trace!("poll_write: {:?}", buf.len()); - let s = U::buffered_state(); + let s = self.buffered_state; let mut tx = unsafe { s.tx_buf.writer() }; let tx_buf = tx.push_slice(); @@ -493,17 +514,17 @@ impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> { //trace!("poll_write: queued {:?}", n); compiler_fence(Ordering::SeqCst); - U::Interrupt::pend(); + self._irq.pend(); Ok(n) } /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. pub fn flush(&mut self) -> impl Future> + '_ { + let ss = self.state; + let s = self.buffered_state; poll_fn(move |cx| { //trace!("poll_flush"); - let ss = U::state(); - let s = U::buffered_state(); if !s.tx_buf.is_empty() { //trace!("poll_flush: pending"); ss.tx_waker.register(cx.waker()); @@ -513,11 +534,16 @@ impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> { Poll::Ready(Ok(())) }) } + + /// Adjust the baud rate to the provided value. + pub fn set_baudrate(&mut self, baudrate: Baudrate) { + self.r.baudrate().write(|w| w.set_baudrate(baudrate)); + } } -impl<'a, U: UarteInstance> Drop for BufferedUarteTx<'a, U> { +impl<'a> Drop for BufferedUarteTx<'a> { fn drop(&mut self) { - let r = U::regs(); + let r = self.r; r.intenclr().write(|w| { w.set_txdrdy(true); @@ -528,31 +554,34 @@ impl<'a, U: UarteInstance> Drop for BufferedUarteTx<'a, U> { r.tasks_stoptx().write_value(1); while r.events_txstopped().read() == 0 {} - let s = U::buffered_state(); + let s = self.buffered_state; unsafe { s.tx_buf.deinit() } - let s = U::state(); + let s = self.state; drop_tx_rx(r, s); } } /// Reader part of the buffered UARTE driver. -pub struct BufferedUarteRx<'d, U: UarteInstance, T: TimerInstance> { - _peri: Peri<'d, U>, - timer: Timer<'d, T>, +pub struct BufferedUarteRx<'d> { + r: pac::uarte::Uarte, + state: &'static crate::uarte::State, + buffered_state: &'static State, + timer: Timer<'d>, _ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 1>, _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 2>, _ppi_group: PpiGroup<'d, AnyGroup>, + _p: PhantomData<&'d ()>, } -impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { +impl<'d> BufferedUarteRx<'d> { /// Create a new BufferedUarte without hardware flow control. /// /// # Panics /// /// Panics if `rx_buffer.len()` is odd. #[allow(clippy::too_many_arguments)] - pub fn new( + pub fn new( uarte: Peri<'d, U>, timer: Peri<'d, T>, ppi_ch1: Peri<'d, impl ConfigurableChannel>, @@ -582,7 +611,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { /// /// Panics if `rx_buffer.len()` is odd. #[allow(clippy::too_many_arguments)] - pub fn new_with_rts( + pub fn new_with_rts( uarte: Peri<'d, U>, timer: Peri<'d, T>, ppi_ch1: Peri<'d, impl ConfigurableChannel>, @@ -608,7 +637,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { } #[allow(clippy::too_many_arguments)] - fn new_inner( + fn new_inner( peri: Peri<'d, U>, timer: Peri<'d, T>, ppi_ch1: Peri<'d, AnyConfigurableChannel>, @@ -619,22 +648,27 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { config: Config, rx_buffer: &'d mut [u8], ) -> Self { - configure(U::regs(), config, rts.is_some()); + let r = U::regs(); + let irq = U::Interrupt::IRQ; + let state = U::state(); + let _buffered_state = U::buffered_state(); + + configure(r, config, rts.is_some()); let this = Self::new_innerer(peri, timer, ppi_ch1, ppi_ch2, ppi_group, rxd, rts, rx_buffer); - U::regs().enable().write(|w| w.set_enable(vals::Enable::ENABLED)); - U::Interrupt::pend(); - unsafe { U::Interrupt::enable() }; + r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); + irq.pend(); + unsafe { irq.enable() }; - U::state().tx_rx_refcount.store(1, Ordering::Relaxed); + state.tx_rx_refcount.store(1, Ordering::Relaxed); this } #[allow(clippy::too_many_arguments)] - fn new_innerer( - peri: Peri<'d, U>, + fn new_innerer( + _peri: Peri<'d, U>, timer: Peri<'d, T>, ppi_ch1: Peri<'d, AnyConfigurableChannel>, ppi_ch2: Peri<'d, AnyConfigurableChannel>, @@ -646,16 +680,17 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { assert!(rx_buffer.len() % 2 == 0); let r = U::regs(); + let state = U::state(); + let buffered_state = U::buffered_state(); configure_rx_pins(r, rxd, rts); // Initialize state - let s = U::buffered_state(); - s.rx_started_count.store(0, Ordering::Relaxed); - s.rx_ended_count.store(0, Ordering::Relaxed); - s.rx_started.store(false, Ordering::Relaxed); + buffered_state.rx_started_count.store(0, Ordering::Relaxed); + buffered_state.rx_ended_count.store(0, Ordering::Relaxed); + buffered_state.rx_started.store(false, Ordering::Relaxed); let rx_len = rx_buffer.len().min(EASY_DMA_SIZE * 2); - unsafe { s.rx_buf.init(rx_buffer.as_mut_ptr(), rx_len) }; + unsafe { buffered_state.rx_buf.init(rx_buffer.as_mut_ptr(), rx_len) }; // clear errors let errors = r.errorsrc().read(); @@ -683,7 +718,9 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(r.events_rxdrdy()), timer.task_count()); ppi_ch1.enable(); - s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); + buffered_state + .rx_ppi_ch + .store(ppi_ch2.number() as u8, Ordering::Relaxed); let mut ppi_group = PpiGroup::new(ppi_group); let mut ppi_ch2 = Ppi::new_one_to_two( ppi_ch2, @@ -695,11 +732,14 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { ppi_group.add_channel(&ppi_ch2); Self { - _peri: peri, + r, + state, + buffered_state, timer, _ppi_ch1: ppi_ch1, _ppi_ch2: ppi_ch2, _ppi_group: ppi_group, + _p: PhantomData, } } @@ -714,17 +754,17 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. pub fn fill_buf(&mut self) -> impl Future> { + let r = self.r; + let s = self.buffered_state; + let ss = self.state; + let timer = &self.timer; poll_fn(move |cx| { compiler_fence(Ordering::SeqCst); //trace!("poll_read"); - let r = U::regs(); - let s = U::buffered_state(); - let ss = U::state(); - // Read the RXDRDY counter. - T::regs().tasks_capture(0).write_value(1); - let mut end = T::regs().cc(0).read() as usize; + timer.cc(0).capture(); + let mut end = timer.cc(0).read() as usize; //trace!(" rxdrdy count = {:?}", end); // We've set a compare channel that resets the counter to 0 when it reaches `len*2`. @@ -771,24 +811,24 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { return; } - let s = U::buffered_state(); + let s = self.buffered_state; let mut rx = unsafe { s.rx_buf.reader() }; rx.pop_done(amt); - U::regs().intenset().write(|w| w.set_rxstarted(true)); + self.r.intenset().write(|w| w.set_rxstarted(true)); } /// we are ready to read if there is data in the buffer - fn read_ready() -> Result { - let state = U::buffered_state(); + fn read_ready(&self) -> Result { + let state = self.buffered_state; Ok(!state.rx_buf.is_empty()) } } -impl<'a, U: UarteInstance, T: TimerInstance> Drop for BufferedUarteRx<'a, U, T> { +impl<'a> Drop for BufferedUarteRx<'a> { fn drop(&mut self) { self._ppi_group.disable_all(); - let r = U::regs(); + let r = self.r; self.timer.stop(); @@ -801,10 +841,10 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for BufferedUarteRx<'a, U, T> r.tasks_stoprx().write_value(1); while r.events_rxto().read() == 0 {} - let s = U::buffered_state(); + let s = self.buffered_state; unsafe { s.rx_buf.deinit() } - let s = U::state(); + let s = self.state; drop_tx_rx(r, s); } } @@ -818,43 +858,44 @@ mod _embedded_io { } } - impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarte<'d, U, T> { + impl<'d> embedded_io_async::ErrorType for BufferedUarte<'d> { type Error = Error; } - impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarteRx<'d, U, T> { + impl<'d> embedded_io_async::ErrorType for BufferedUarteRx<'d> { type Error = Error; } - impl<'d, U: UarteInstance> embedded_io_async::ErrorType for BufferedUarteTx<'d, U> { + impl<'d> embedded_io_async::ErrorType for BufferedUarteTx<'d> { type Error = Error; } - impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::Read for BufferedUarte<'d, U, T> { + impl<'d> embedded_io_async::Read for BufferedUarte<'d> { async fn read(&mut self, buf: &mut [u8]) -> Result { self.read(buf).await } } - impl<'d: 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::Read for BufferedUarteRx<'d, U, T> { + impl<'d> embedded_io_async::Read for BufferedUarteRx<'d> { async fn read(&mut self, buf: &mut [u8]) -> Result { self.read(buf).await } } - impl<'d, U: UarteInstance, T: TimerInstance + 'd> embedded_io_async::ReadReady for BufferedUarte<'d, U, T> { + impl<'d> embedded_io_async::ReadReady for BufferedUarte<'d> { fn read_ready(&mut self) -> Result { - BufferedUarteRx::<'d, U, T>::read_ready() + self.rx.read_ready() } } - impl<'d, U: UarteInstance, T: TimerInstance + 'd> embedded_io_async::ReadReady for BufferedUarteRx<'d, U, T> { + impl<'d> embedded_io_async::ReadReady for BufferedUarteRx<'d> { fn read_ready(&mut self) -> Result { - Self::read_ready() + let state = self.buffered_state; + Ok(!state.rx_buf.is_empty()) } } - impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarte<'d, U, T> { + impl<'d> embedded_io_async::BufRead for BufferedUarte<'d> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { self.fill_buf().await } @@ -864,7 +905,7 @@ mod _embedded_io { } } - impl<'d: 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarteRx<'d, U, T> { + impl<'d> embedded_io_async::BufRead for BufferedUarteRx<'d> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { self.fill_buf().await } @@ -874,7 +915,7 @@ mod _embedded_io { } } - impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::Write for BufferedUarte<'d, U, T> { + impl<'d> embedded_io_async::Write for BufferedUarte<'d> { async fn write(&mut self, buf: &[u8]) -> Result { self.write(buf).await } @@ -884,7 +925,7 @@ mod _embedded_io { } } - impl<'d: 'd, U: UarteInstance> embedded_io_async::Write for BufferedUarteTx<'d, U> { + impl<'d> embedded_io_async::Write for BufferedUarteTx<'d> { async fn write(&mut self, buf: &[u8]) -> Result { self.write(buf).await } diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 1d1f77ea8..0b0bb9780 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -6,6 +6,8 @@ #![macro_use] +use core::marker::PhantomData; + use embassy_hal_internal::{Peri, PeripheralType}; use crate::pac; @@ -81,16 +83,18 @@ pub enum Frequency { /// /// It has either 4 or 6 Capture/Compare registers, which can be used to capture the current state of the counter /// or trigger an event when the counter reaches a certain value. -pub struct Timer<'d, T: Instance> { - _p: Peri<'d, T>, +pub struct Timer<'d> { + r: pac::timer::Timer, + ccs: usize, + _p: PhantomData<&'d ()>, } -impl<'d, T: Instance> Timer<'d, T> { +impl<'d> Timer<'d> { /// Create a new `Timer` driver. /// /// This can be useful for triggering tasks via PPI. /// `Uarte` uses this internally. - pub fn new(timer: Peri<'d, T>) -> Self { + pub fn new(timer: Peri<'d, T>) -> Self { Self::new_inner(timer, false) } @@ -98,14 +102,18 @@ impl<'d, T: Instance> Timer<'d, T> { /// /// This can be useful for triggering tasks via PPI. /// `Uarte` uses this internally. - pub fn new_counter(timer: Peri<'d, T>) -> Self { + pub fn new_counter(timer: Peri<'d, T>) -> Self { Self::new_inner(timer, true) } - fn new_inner(timer: Peri<'d, T>, is_counter: bool) -> Self { + fn new_inner(_timer: Peri<'d, T>, is_counter: bool) -> Self { let regs = T::regs(); - let this = Self { _p: timer }; + let this = Self { + r: regs, + ccs: T::CCS, + _p: PhantomData, + }; // Stop the timer before doing anything else, // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification. @@ -131,7 +139,7 @@ impl<'d, T: Instance> Timer<'d, T> { // Default to the max frequency of the lower power clock this.set_frequency(Frequency::F1MHz); - for n in 0..T::CCS { + for n in 0..this.ccs { let cc = this.cc(n); // Initialize all the shorts as disabled. cc.unshort_compare_clear(); @@ -147,43 +155,43 @@ impl<'d, T: Instance> Timer<'d, T> { #[cfg(feature = "unstable-pac")] #[inline] pub fn regs(&mut self) -> pac::timer::Timer { - T::regs() + self.r } /// Starts the timer. pub fn start(&self) { - T::regs().tasks_start().write_value(1) + self.r.tasks_start().write_value(1) } /// Stops the timer. pub fn stop(&self) { - T::regs().tasks_stop().write_value(1) + self.r.tasks_stop().write_value(1) } /// Reset the timer's counter to 0. pub fn clear(&self) { - T::regs().tasks_clear().write_value(1) + self.r.tasks_clear().write_value(1) } /// Returns the START task, for use with PPI. /// /// When triggered, this task starts the timer. pub fn task_start(&self) -> Task<'d> { - Task::from_reg(T::regs().tasks_start()) + Task::from_reg(self.r.tasks_start()) } /// Returns the STOP task, for use with PPI. /// /// When triggered, this task stops the timer. pub fn task_stop(&self) -> Task<'d> { - Task::from_reg(T::regs().tasks_stop()) + Task::from_reg(self.r.tasks_stop()) } /// Returns the CLEAR task, for use with PPI. /// /// When triggered, this task resets the timer's counter to 0. pub fn task_clear(&self) -> Task<'d> { - Task::from_reg(T::regs().tasks_clear()) + Task::from_reg(self.r.tasks_clear()) } /// Returns the COUNT task, for use with PPI. @@ -191,7 +199,7 @@ impl<'d, T: Instance> Timer<'d, T> { /// When triggered, this task increments the timer's counter by 1. /// Only works in counter mode. pub fn task_count(&self) -> Task<'d> { - Task::from_reg(T::regs().tasks_count()) + Task::from_reg(self.r.tasks_count()) } /// Change the timer's frequency. @@ -201,7 +209,7 @@ impl<'d, T: Instance> Timer<'d, T> { pub fn set_frequency(&self, frequency: Frequency) { self.stop(); - T::regs() + self.r .prescaler() // SAFETY: `frequency` is a variant of `Frequency`, // whose values are all in the range of 0-9 (the valid range of `prescaler`). @@ -212,18 +220,19 @@ impl<'d, T: Instance> Timer<'d, T> { /// /// # Panics /// Panics if `n` >= the number of CC registers this timer has (4 for a normal timer, 6 for an extended timer). - pub fn cc(&self, n: usize) -> Cc<'d, T> { - if n >= T::CCS { - panic!("Cannot get CC register {} of timer with {} CC registers.", n, T::CCS); + pub fn cc(&self, n: usize) -> Cc<'d> { + if n >= self.ccs { + panic!("Cannot get CC register {} of timer with {} CC registers.", n, self.ccs); } Cc { n, - _p: unsafe { self._p.clone_unchecked() }, + r: self.r, + _p: PhantomData, } } } -impl Timer<'static, T> { +impl Timer<'static> { /// Persist the timer's configuration for the rest of the program's lifetime. This method /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents /// accidental reuse of the underlying peripheral. @@ -232,7 +241,7 @@ impl Timer<'static, T> { } } -impl<'d, T: Instance> Drop for Timer<'d, T> { +impl<'d> Drop for Timer<'d> { fn drop(&mut self) { self.stop(); } @@ -245,27 +254,28 @@ impl<'d, T: Instance> Drop for Timer<'d, T> { /// /// The timer will fire the register's COMPARE event when its counter reaches the value stored in the register. /// When the register's CAPTURE task is triggered, the timer will store the current value of its counter in the register -pub struct Cc<'d, T: Instance> { +pub struct Cc<'d> { n: usize, - _p: Peri<'d, T>, + r: pac::timer::Timer, + _p: PhantomData<&'d ()>, } -impl<'d, T: Instance> Cc<'d, T> { +impl<'d> Cc<'d> { /// Get the current value stored in the register. pub fn read(&self) -> u32 { - T::regs().cc(self.n).read() + self.r.cc(self.n).read() } /// Set the value stored in the register. /// /// `event_compare` will fire when the timer's counter reaches this value. pub fn write(&self, value: u32) { - T::regs().cc(self.n).write_value(value); + self.r.cc(self.n).write_value(value); } /// Capture the current value of the timer's counter in this register, and return it. pub fn capture(&self) -> u32 { - T::regs().tasks_capture(self.n).write_value(1); + self.r.tasks_capture(self.n).write_value(1); self.read() } @@ -273,20 +283,20 @@ impl<'d, T: Instance> Cc<'d, T> { /// /// When triggered, this task will capture the current value of the timer's counter in this register. pub fn task_capture(&self) -> Task<'d> { - Task::from_reg(T::regs().tasks_capture(self.n)) + Task::from_reg(self.r.tasks_capture(self.n)) } /// Returns this CC register's COMPARE event, for use with PPI. /// /// This event will fire when the timer's counter reaches the value in this CC register. pub fn event_compare(&self) -> Event<'d> { - Event::from_reg(T::regs().events_compare(self.n)) + Event::from_reg(self.r.events_compare(self.n)) } /// Clear the COMPARE event for this CC register. #[inline] pub fn clear_events(&self) { - T::regs().events_compare(self.n).write_value(0); + self.r.events_compare(self.n).write_value(0); } /// Enable the shortcut between this CC register's COMPARE event and the timer's CLEAR task. @@ -295,12 +305,12 @@ impl<'d, T: Instance> Cc<'d, T> { /// /// So, when the timer's counter reaches the value stored in this register, the timer's counter will be reset to 0. pub fn short_compare_clear(&self) { - T::regs().shorts().modify(|w| w.set_compare_clear(self.n, true)) + self.r.shorts().modify(|w| w.set_compare_clear(self.n, true)) } /// Disable the shortcut between this CC register's COMPARE event and the timer's CLEAR task. pub fn unshort_compare_clear(&self) { - T::regs().shorts().modify(|w| w.set_compare_clear(self.n, false)) + self.r.shorts().modify(|w| w.set_compare_clear(self.n, false)) } /// Enable the shortcut between this CC register's COMPARE event and the timer's STOP task. @@ -309,11 +319,11 @@ impl<'d, T: Instance> Cc<'d, T> { /// /// So, when the timer's counter reaches the value stored in this register, the timer will stop counting up. pub fn short_compare_stop(&self) { - T::regs().shorts().modify(|w| w.set_compare_stop(self.n, true)) + self.r.shorts().modify(|w| w.set_compare_stop(self.n, true)) } /// Disable the shortcut between this CC register's COMPARE event and the timer's STOP task. pub fn unshort_compare_stop(&self) { - T::regs().shorts().modify(|w| w.set_compare_stop(self.n, false)) + self.r.shorts().modify(|w| w.set_compare_stop(self.n, false)) } } diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index ffc9b39f6..943ea9d31 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -114,7 +114,6 @@ impl interrupt::typelevel::Handler for InterruptHandl /// TWI driver. pub struct Twim<'d> { r: pac::twim::Twim, - irq: interrupt::Interrupt, state: &'static State, tx_ram_buffer: &'d mut [u8], _p: PhantomData<&'d ()>, @@ -171,7 +170,6 @@ impl<'d> Twim<'d> { let mut twim = Self { r: T::regs(), - irq: T::Interrupt::IRQ, state: T::state(), tx_ram_buffer, _p: PhantomData {}, diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 927a0ac08..66fb3b3f2 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -132,28 +132,32 @@ impl interrupt::typelevel::Handler for InterruptHandl } /// UARTE driver. -pub struct Uarte<'d, T: Instance> { - tx: UarteTx<'d, T>, - rx: UarteRx<'d, T>, +pub struct Uarte<'d> { + tx: UarteTx<'d>, + rx: UarteRx<'d>, } /// Transmitter part of the UARTE driver. /// /// This can be obtained via [`Uarte::split`], or created directly. -pub struct UarteTx<'d, T: Instance> { - _p: Peri<'d, T>, +pub struct UarteTx<'d> { + r: pac::uarte::Uarte, + state: &'static State, + _p: PhantomData<&'d ()>, } /// Receiver part of the UARTE driver. /// /// This can be obtained via [`Uarte::split`], or created directly. -pub struct UarteRx<'d, T: Instance> { - _p: Peri<'d, T>, +pub struct UarteRx<'d> { + r: pac::uarte::Uarte, + state: &'static State, + _p: PhantomData<&'d ()>, } -impl<'d, T: Instance> Uarte<'d, T> { +impl<'d> Uarte<'d> { /// Create a new UARTE without hardware flow control - pub fn new( + pub fn new( uarte: Peri<'d, T>, rxd: Peri<'d, impl GpioPin>, txd: Peri<'d, impl GpioPin>, @@ -164,7 +168,7 @@ impl<'d, T: Instance> Uarte<'d, T> { } /// Create a new UARTE with hardware flow control (RTS/CTS) - pub fn new_with_rtscts( + pub fn new_with_rtscts( uarte: Peri<'d, T>, rxd: Peri<'d, impl GpioPin>, txd: Peri<'d, impl GpioPin>, @@ -183,8 +187,8 @@ impl<'d, T: Instance> Uarte<'d, T> { ) } - fn new_inner( - uarte: Peri<'d, T>, + fn new_inner( + _uarte: Peri<'d, T>, rxd: Peri<'d, AnyPin>, txd: Peri<'d, AnyPin>, cts: Option>, @@ -211,16 +215,22 @@ impl<'d, T: Instance> Uarte<'d, T> { Self { tx: UarteTx { - _p: unsafe { uarte.clone_unchecked() }, + r: T::regs(), + state: T::state(), + _p: PhantomData {}, + }, + rx: UarteRx { + r: T::regs(), + state: T::state(), + _p: PhantomData {}, }, - rx: UarteRx { _p: uarte }, } } /// Split the Uarte into the transmitter and receiver parts. /// /// This is useful to concurrently transmit and receive from independent tasks. - pub fn split(self) -> (UarteTx<'d, T>, UarteRx<'d, T>) { + pub fn split(self) -> (UarteTx<'d>, UarteRx<'d>) { (self.tx, self.rx) } @@ -228,7 +238,7 @@ impl<'d, T: Instance> Uarte<'d, T> { /// /// The returned halves borrow from `self`, so you can drop them and go back to using /// the "un-split" `self`. This allows temporarily splitting the UART. - pub fn split_by_ref(&mut self) -> (&mut UarteTx<'d, T>, &mut UarteRx<'d, T>) { + pub fn split_by_ref(&mut self) -> (&mut UarteTx<'d>, &mut UarteRx<'d>) { (&mut self.tx, &mut self.rx) } @@ -240,13 +250,13 @@ impl<'d, T: Instance> Uarte<'d, T> { timer: Peri<'d, U>, ppi_ch1: Peri<'d, impl ConfigurableChannel + 'd>, ppi_ch2: Peri<'d, impl ConfigurableChannel + 'd>, - ) -> (UarteTx<'d, T>, UarteRxWithIdle<'d, T, U>) { + ) -> (UarteTx<'d>, UarteRxWithIdle<'d>) { (self.tx, self.rx.with_idle(timer, ppi_ch1, ppi_ch2)) } /// Return the endtx event for use with PPI pub fn event_endtx(&self) -> Event<'_> { - let r = T::regs(); + let r = self.tx.r; Event::from_reg(r.events_endtx()) } @@ -343,9 +353,9 @@ pub(crate) fn configure(r: pac::uarte::Uarte, config: Config, hardware_flow_cont apply_workaround_for_enable_anomaly(r); } -impl<'d, T: Instance> UarteTx<'d, T> { +impl<'d> UarteTx<'d> { /// Create a new tx-only UARTE without hardware flow control - pub fn new( + pub fn new( uarte: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, txd: Peri<'d, impl GpioPin>, @@ -355,7 +365,7 @@ impl<'d, T: Instance> UarteTx<'d, T> { } /// Create a new tx-only UARTE with hardware flow control (RTS/CTS) - pub fn new_with_rtscts( + pub fn new_with_rtscts( uarte: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, txd: Peri<'d, impl GpioPin>, @@ -365,7 +375,12 @@ impl<'d, T: Instance> UarteTx<'d, T> { Self::new_inner(uarte, txd.into(), Some(cts.into()), config) } - fn new_inner(uarte: Peri<'d, T>, txd: Peri<'d, AnyPin>, cts: Option>, config: Config) -> Self { + fn new_inner( + _uarte: Peri<'d, T>, + txd: Peri<'d, AnyPin>, + cts: Option>, + config: Config, + ) -> Self { let r = T::regs(); configure(r, config, cts.is_some()); @@ -378,7 +393,11 @@ impl<'d, T: Instance> UarteTx<'d, T> { let s = T::state(); s.tx_rx_refcount.store(1, Ordering::Relaxed); - Self { _p: uarte } + Self { + r: T::regs(), + state: T::state(), + _p: PhantomData {}, + } } /// Write all bytes in the buffer. @@ -409,8 +428,8 @@ impl<'d, T: Instance> UarteTx<'d, T> { let ptr = buffer.as_ptr(); let len = buffer.len(); - let r = T::regs(); - let s = T::state(); + let r = self.r; + let s = self.state; let drop = OnDrop::new(move || { trace!("write drop: stopping"); @@ -479,7 +498,7 @@ impl<'d, T: Instance> UarteTx<'d, T> { let ptr = buffer.as_ptr(); let len = buffer.len(); - let r = T::regs(); + let r = self.r; r.txd().ptr().write_value(ptr as u32); r.txd().maxcnt().write(|w| w.set_maxcnt(len as _)); @@ -501,11 +520,11 @@ impl<'d, T: Instance> UarteTx<'d, T> { } } -impl<'a, T: Instance> Drop for UarteTx<'a, T> { +impl<'a> Drop for UarteTx<'a> { fn drop(&mut self) { trace!("uarte tx drop"); - let r = T::regs(); + let r = self.r; let did_stoptx = r.events_txstarted().read() != 0; trace!("did_stoptx {}", did_stoptx); @@ -513,15 +532,15 @@ impl<'a, T: Instance> Drop for UarteTx<'a, T> { // Wait for txstopped, if needed. while did_stoptx && r.events_txstopped().read() == 0 {} - let s = T::state(); + let s = self.state; drop_tx_rx(r, s); } } -impl<'d, T: Instance> UarteRx<'d, T> { +impl<'d> UarteRx<'d> { /// Create a new rx-only UARTE without hardware flow control - pub fn new( + pub fn new( uarte: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, rxd: Peri<'d, impl GpioPin>, @@ -531,7 +550,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { } /// Create a new rx-only UARTE with hardware flow control (RTS/CTS) - pub fn new_with_rtscts( + pub fn new_with_rtscts( uarte: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, rxd: Peri<'d, impl GpioPin>, @@ -543,13 +562,18 @@ impl<'d, T: Instance> UarteRx<'d, T> { /// Check for errors and clear the error register if an error occured. fn check_and_clear_errors(&mut self) -> Result<(), Error> { - let r = T::regs(); + let r = self.r; let err_bits = r.errorsrc().read(); r.errorsrc().write_value(err_bits); ErrorSource::from_bits_truncate(err_bits.0).check() } - fn new_inner(uarte: Peri<'d, T>, rxd: Peri<'d, AnyPin>, rts: Option>, config: Config) -> Self { + fn new_inner( + _uarte: Peri<'d, T>, + rxd: Peri<'d, AnyPin>, + rts: Option>, + config: Config, + ) -> Self { let r = T::regs(); configure(r, config, rts.is_some()); @@ -562,7 +586,11 @@ impl<'d, T: Instance> UarteRx<'d, T> { let s = T::state(); s.tx_rx_refcount.store(1, Ordering::Relaxed); - Self { _p: uarte } + Self { + r: T::regs(), + state: T::state(), + _p: PhantomData {}, + } } /// Upgrade to an instance that supports idle line detection. @@ -571,10 +599,10 @@ impl<'d, T: Instance> UarteRx<'d, T> { timer: Peri<'d, U>, ppi_ch1: Peri<'d, impl ConfigurableChannel + 'd>, ppi_ch2: Peri<'d, impl ConfigurableChannel + 'd>, - ) -> UarteRxWithIdle<'d, T, U> { + ) -> UarteRxWithIdle<'d> { let timer = Timer::new(timer); - let r = T::regs(); + let r = self.r; // BAUDRATE register values are `baudrate * 2^32 / 16000000` // source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values @@ -605,11 +633,15 @@ impl<'d, T: Instance> UarteRx<'d, T> { ); ppi_ch2.enable(); + let state = self.state; + UarteRxWithIdle { rx: self, timer, - ppi_ch1, + ppi_ch1: ppi_ch1, _ppi_ch2: ppi_ch2, + r: r, + state: state, } } @@ -625,8 +657,8 @@ impl<'d, T: Instance> UarteRx<'d, T> { let ptr = buffer.as_ptr(); let len = buffer.len(); - let r = T::regs(); - let s = T::state(); + let r = self.r; + let s = self.state; let drop = OnDrop::new(move || { trace!("read drop: stopping"); @@ -692,7 +724,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { let ptr = buffer.as_ptr(); let len = buffer.len(); - let r = T::regs(); + let r = self.r; r.rxd().ptr().write_value(ptr as u32); r.rxd().maxcnt().write(|w| w.set_maxcnt(len as _)); @@ -718,11 +750,11 @@ impl<'d, T: Instance> UarteRx<'d, T> { } } -impl<'a, T: Instance> Drop for UarteRx<'a, T> { +impl<'a> Drop for UarteRx<'a> { fn drop(&mut self) { trace!("uarte rx drop"); - let r = T::regs(); + let r = self.r; let did_stoprx = r.events_rxstarted().read() != 0; trace!("did_stoprx {}", did_stoprx); @@ -730,7 +762,7 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> { // Wait for rxto, if needed. while did_stoprx && r.events_rxto().read() == 0 {} - let s = T::state(); + let s = self.state; drop_tx_rx(r, s); } @@ -739,14 +771,16 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> { /// Receiver part of the UARTE driver, with `read_until_idle` support. /// /// This can be obtained via [`Uarte::split_with_idle`]. -pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> { - rx: UarteRx<'d, T>, - timer: Timer<'d, U>, +pub struct UarteRxWithIdle<'d> { + rx: UarteRx<'d>, + timer: Timer<'d>, ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>, _ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>, + r: pac::uarte::Uarte, + state: &'static State, } -impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { +impl<'d> UarteRxWithIdle<'d> { /// Read bytes until the buffer is filled. pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.ppi_ch1.disable(); @@ -773,8 +807,8 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { let ptr = buffer.as_ptr(); let len = buffer.len(); - let r = T::regs(); - let s = T::state(); + let r = self.r; + let s = self.state; self.ppi_ch1.enable(); @@ -846,7 +880,7 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { let ptr = buffer.as_ptr(); let len = buffer.len(); - let r = T::regs(); + let r = self.r; self.ppi_ch1.enable(); @@ -997,7 +1031,7 @@ macro_rules! impl_uarte { mod eh02 { use super::*; - impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for Uarte<'d, T> { + impl<'d> embedded_hal_02::blocking::serial::Write for Uarte<'d> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { @@ -1009,7 +1043,7 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::blocking::serial::Write for UarteTx<'d, T> { + impl<'d> embedded_hal_02::blocking::serial::Write for UarteTx<'d> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { @@ -1038,22 +1072,22 @@ mod _embedded_io { } } - impl<'d, U: Instance> embedded_io_async::ErrorType for Uarte<'d, U> { + impl<'d> embedded_io_async::ErrorType for Uarte<'d> { type Error = Error; } - impl<'d, U: Instance> embedded_io_async::ErrorType for UarteTx<'d, U> { + impl<'d> embedded_io_async::ErrorType for UarteTx<'d> { type Error = Error; } - impl<'d, U: Instance> embedded_io_async::Write for Uarte<'d, U> { + impl<'d> embedded_io_async::Write for Uarte<'d> { async fn write(&mut self, buf: &[u8]) -> Result { self.write(buf).await?; Ok(buf.len()) } } - impl<'d: 'd, U: Instance> embedded_io_async::Write for UarteTx<'d, U> { + impl<'d> embedded_io_async::Write for UarteTx<'d> { async fn write(&mut self, buf: &[u8]) -> Result { self.write(buf).await?; Ok(buf.len()) diff --git a/examples/nrf52840/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs index 51af90727..d75143126 100644 --- a/examples/nrf52840/src/bin/uart_split.rs +++ b/examples/nrf52840/src/bin/uart_split.rs @@ -52,7 +52,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn reader(mut rx: UarteRx<'static, UARTE0>) { +async fn reader(mut rx: UarteRx<'static>) { let mut buf = [0; 8]; loop { info!("reading..."); diff --git a/examples/nrf9160/src/bin/modem_tcp_client.rs b/examples/nrf9160/src/bin/modem_tcp_client.rs index 7d4815699..460a4cfae 100644 --- a/examples/nrf9160/src/bin/modem_tcp_client.rs +++ b/examples/nrf9160/src/bin/modem_tcp_client.rs @@ -32,7 +32,7 @@ bind_interrupts!(struct Irqs { }); #[embassy_executor::task] -async fn trace_task(mut uart: BufferedUarteTx<'static, peripherals::SERIAL0>, reader: TraceReader<'static>) -> ! { +async fn trace_task(mut uart: BufferedUarteTx<'static>, reader: TraceReader<'static>) -> ! { let mut rx = [0u8; 1024]; loop { let n = reader.read(&mut rx[..]).await; -- cgit From fd574ffd091b579f30655e349aef16fbfc2bbb75 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Sep 2025 18:48:24 +0200 Subject: nrf/spis: erase instance generics --- embassy-nrf/src/spis.rs | 50 +++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 2a3928d25..713163a55 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -96,14 +96,16 @@ impl interrupt::typelevel::Handler for InterruptHandl } } -/// SPIS driver. -pub struct Spis<'d, T: Instance> { - _p: Peri<'d, T>, +/// Serial Peripheral Interface in slave mode. +pub struct Spis<'d> { + r: pac::spis::Spis, + state: &'static State, + _p: PhantomData<&'d ()>, } -impl<'d, T: Instance> Spis<'d, T> { +impl<'d> Spis<'d> { /// Create a new SPIS driver. - pub fn new( + pub fn new( spis: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, cs: Peri<'d, impl GpioPin>, @@ -123,7 +125,7 @@ impl<'d, T: Instance> Spis<'d, T> { } /// Create a new SPIS driver, capable of TX only (MISO only). - pub fn new_txonly( + pub fn new_txonly( spis: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, cs: Peri<'d, impl GpioPin>, @@ -135,7 +137,7 @@ impl<'d, T: Instance> Spis<'d, T> { } /// Create a new SPIS driver, capable of RX only (MOSI only). - pub fn new_rxonly( + pub fn new_rxonly( spis: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, cs: Peri<'d, impl GpioPin>, @@ -147,7 +149,7 @@ impl<'d, T: Instance> Spis<'d, T> { } /// Create a new SPIS driver, capable of TX only (MISO only) without SCK pin. - pub fn new_txonly_nosck( + pub fn new_txonly_nosck( spis: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, cs: Peri<'d, impl GpioPin>, @@ -157,8 +159,8 @@ impl<'d, T: Instance> Spis<'d, T> { Self::new_inner(spis, cs.into(), None, Some(miso.into()), None, config) } - fn new_inner( - spis: Peri<'d, T>, + fn new_inner( + _spis: Peri<'d, T>, cs: Peri<'d, AnyPin>, sck: Option>, miso: Option>, @@ -191,10 +193,14 @@ impl<'d, T: Instance> Spis<'d, T> { // Enable SPIS instance. r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); - let mut spis = Self { _p: spis }; + let mut spis = Self { + r: T::regs(), + state: T::state(), + _p: PhantomData, + }; // Apply runtime peripheral configuration - Self::set_config(&mut spis, &config).unwrap(); + spis.set_config(&config).unwrap(); // Disable all events interrupts. r.intenclr().write(|w| w.0 = 0xFFFF_FFFF); @@ -212,7 +218,7 @@ impl<'d, T: Instance> Spis<'d, T> { compiler_fence(Ordering::SeqCst); - let r = T::regs(); + let r = self.r; // Set up the DMA write. if tx.len() > EASY_DMA_SIZE { @@ -239,7 +245,7 @@ impl<'d, T: Instance> Spis<'d, T> { fn blocking_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> { compiler_fence(Ordering::SeqCst); - let r = T::regs(); + let r = self.r; // Acquire semaphore. if r.semstat().read().0 != 1 { @@ -276,8 +282,8 @@ impl<'d, T: Instance> Spis<'d, T> { } async fn async_inner_from_ram(&mut self, rx: *mut [u8], tx: *const [u8]) -> Result<(usize, usize), Error> { - let r = T::regs(); - let s = T::state(); + let r = self.r; + let s = self.state; // Clear status register. r.status().write(|w| { @@ -420,21 +426,21 @@ impl<'d, T: Instance> Spis<'d, T> { /// Checks if last transaction overread. pub fn is_overread(&mut self) -> bool { - T::regs().status().read().overread() + self.r.status().read().overread() } /// Checks if last transaction overflowed. pub fn is_overflow(&mut self) -> bool { - T::regs().status().read().overflow() + self.r.status().read().overflow() } } -impl<'d, T: Instance> Drop for Spis<'d, T> { +impl<'d> Drop for Spis<'d> { fn drop(&mut self) { trace!("spis drop"); // Disable - let r = T::regs(); + let r = self.r; r.enable().write(|w| w.set_enable(vals::Enable::DISABLED)); gpio::deconfigure_pin(r.psel().sck().read()); @@ -489,11 +495,11 @@ macro_rules! impl_spis { // ==================== -impl<'d, T: Instance> SetConfig for Spis<'d, T> { +impl<'d> SetConfig for Spis<'d> { type Config = Config; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { - let r = T::regs(); + let r = self.r; // Configure mode. let mode = config.mode; r.config().write(|w| { -- cgit From 00db482c2b701b6e8f7013ba63d6e1efe3b5c62d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Sep 2025 19:20:11 +0200 Subject: nrf/twis: erase instance generics --- embassy-nrf/src/twis.rs | 65 ++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index c77d0f048..dd4978b3e 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -140,14 +140,16 @@ impl interrupt::typelevel::Handler for InterruptHandl } /// TWIS driver. -pub struct Twis<'d, T: Instance> { - _p: Peri<'d, T>, +pub struct Twis<'d> { + r: pac::twis::Twis, + state: &'static State, + _p: PhantomData<&'d ()>, } -impl<'d, T: Instance> Twis<'d, T> { +impl<'d> Twis<'d> { /// Create a new TWIS driver. - pub fn new( - twis: Peri<'d, T>, + pub fn new( + _twis: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, sda: Peri<'d, impl GpioPin>, scl: Peri<'d, impl GpioPin>, @@ -206,7 +208,11 @@ impl<'d, T: Instance> Twis<'d, T> { T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - Self { _p: twis } + Self { + r: T::regs(), + state: T::state(), + _p: PhantomData, + } } /// Set TX buffer, checking that it is in RAM and has suitable length. @@ -217,7 +223,7 @@ impl<'d, T: Instance> Twis<'d, T> { return Err(Error::TxBufferTooLong); } - let r = T::regs(); + let r = self.r; // We're giving the register a pointer to the stack. Since we're // waiting for the I2C transaction to end before this stack pointer @@ -244,7 +250,7 @@ impl<'d, T: Instance> Twis<'d, T> { return Err(Error::RxBufferTooLong); } - let r = T::regs(); + let r = self.r; // We're giving the register a pointer to the stack. Since we're // waiting for the I2C transaction to end before this stack pointer @@ -266,7 +272,7 @@ impl<'d, T: Instance> Twis<'d, T> { } fn clear_errorsrc(&mut self) { - let r = T::regs(); + let r = self.r; r.errorsrc().write(|w| { w.set_overflow(true); w.set_overread(true); @@ -276,18 +282,18 @@ impl<'d, T: Instance> Twis<'d, T> { /// Returns matched address for latest command. pub fn address_match(&self) -> u8 { - let r = T::regs(); + let r = self.r; r.address(r.match_().read().0 as usize).read().address() } /// Returns the index of the address matched in the latest command. pub fn address_match_index(&self) -> usize { - T::regs().match_().read().0 as _ + self.r.match_().read().0 as _ } /// Wait for read, write, stop or error fn blocking_listen_wait(&mut self) -> Result { - let r = T::regs(); + let r = self.r; loop { if r.events_error().read() != 0 { r.events_error().write_value(0); @@ -312,7 +318,7 @@ impl<'d, T: Instance> Twis<'d, T> { /// Wait for stop, repeated start or error fn blocking_listen_wait_end(&mut self, status: Status) -> Result { - let r = T::regs(); + let r = self.r; loop { // stop if an error occurred if r.events_error().read() != 0 { @@ -338,7 +344,7 @@ impl<'d, T: Instance> Twis<'d, T> { /// Wait for stop or error fn blocking_wait(&mut self) -> Result { - let r = T::regs(); + let r = self.r; loop { // stop if an error occurred if r.events_error().read() != 0 { @@ -363,7 +369,7 @@ impl<'d, T: Instance> Twis<'d, T> { /// Wait for stop or error with timeout #[cfg(feature = "time")] fn blocking_wait_timeout(&mut self, timeout: Duration) -> Result { - let r = T::regs(); + let r = self.r; let deadline = Instant::now() + timeout; loop { // stop if an error occurred @@ -392,7 +398,7 @@ impl<'d, T: Instance> Twis<'d, T> { /// Wait for read, write, stop or error with timeout #[cfg(feature = "time")] fn blocking_listen_wait_timeout(&mut self, timeout: Duration) -> Result { - let r = T::regs(); + let r = self.r; let deadline = Instant::now() + timeout; loop { if r.events_error().read() != 0 { @@ -423,7 +429,7 @@ impl<'d, T: Instance> Twis<'d, T> { /// Wait for stop, repeated start or error with timeout #[cfg(feature = "time")] fn blocking_listen_wait_end_timeout(&mut self, status: Status, timeout: Duration) -> Result { - let r = T::regs(); + let r = self.r; let deadline = Instant::now() + timeout; loop { // stop if an error occurred @@ -453,10 +459,9 @@ impl<'d, T: Instance> Twis<'d, T> { /// Wait for stop or error fn async_wait(&mut self) -> impl Future> { + let r = self.r; + let s = self.state; poll_fn(move |cx| { - let r = T::regs(); - let s = T::state(); - s.waker.register(cx.waker()); // stop if an error occurred @@ -483,10 +488,9 @@ impl<'d, T: Instance> Twis<'d, T> { /// Wait for read or write fn async_listen_wait(&mut self) -> impl Future> { + let r = self.r; + let s = self.state; poll_fn(move |cx| { - let r = T::regs(); - let s = T::state(); - s.waker.register(cx.waker()); // stop if an error occurred @@ -510,10 +514,9 @@ impl<'d, T: Instance> Twis<'d, T> { /// Wait for stop, repeated start or error fn async_listen_wait_end(&mut self, status: Status) -> impl Future> { + let r = self.r; + let s = self.state; poll_fn(move |cx| { - let r = T::regs(); - let s = T::state(); - s.waker.register(cx.waker()); // stop if an error occurred @@ -540,7 +543,7 @@ impl<'d, T: Instance> Twis<'d, T> { } fn setup_respond_from_ram(&mut self, buffer: &[u8], inten: bool) -> Result<(), Error> { - let r = T::regs(); + let r = self.r; compiler_fence(SeqCst); @@ -584,7 +587,7 @@ impl<'d, T: Instance> Twis<'d, T> { } fn setup_listen(&mut self, buffer: &mut [u8], inten: bool) -> Result<(), Error> { - let r = T::regs(); + let r = self.r; compiler_fence(SeqCst); // Set up the DMA read. @@ -620,7 +623,7 @@ impl<'d, T: Instance> Twis<'d, T> { } fn setup_listen_end(&mut self, inten: bool) -> Result<(), Error> { - let r = T::regs(); + let r = self.r; compiler_fence(SeqCst); // Clear events @@ -753,14 +756,14 @@ impl<'d, T: Instance> Twis<'d, T> { } } -impl<'a, T: Instance> Drop for Twis<'a, T> { +impl<'a> Drop for Twis<'a> { fn drop(&mut self) { trace!("twis drop"); // TODO: check for abort // disable! - let r = T::regs(); + let r = self.r; r.enable().write(|w| w.set_enable(vals::Enable::DISABLED)); gpio::deconfigure_pin(r.psel().sda().read()); -- cgit From 65a80f698e9087f09689a616a9f383c32b59ede8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Sep 2025 21:13:53 +0200 Subject: nrf/ipc: erase instance generics --- embassy-nrf/src/ipc.rs | 132 ++++++++++++++++++++++++++----------------------- 1 file changed, 70 insertions(+), 62 deletions(-) diff --git a/embassy-nrf/src/ipc.rs b/embassy-nrf/src/ipc.rs index a8a08c911..a40c36c99 100644 --- a/embassy-nrf/src/ipc.rs +++ b/embassy-nrf/src/ipc.rs @@ -134,97 +134,99 @@ impl interrupt::typelevel::Handler for InterruptHandl /// IPC driver #[non_exhaustive] -pub struct Ipc<'d, T: Instance> { +pub struct Ipc<'d> { /// Event 0 - pub event0: Event<'d, T>, + pub event0: Event<'d>, /// Event 1 - pub event1: Event<'d, T>, + pub event1: Event<'d>, /// Event 2 - pub event2: Event<'d, T>, + pub event2: Event<'d>, /// Event 3 - pub event3: Event<'d, T>, + pub event3: Event<'d>, /// Event 4 - pub event4: Event<'d, T>, + pub event4: Event<'d>, /// Event 5 - pub event5: Event<'d, T>, + pub event5: Event<'d>, /// Event 6 - pub event6: Event<'d, T>, + pub event6: Event<'d>, /// Event 7 - pub event7: Event<'d, T>, + pub event7: Event<'d>, /// Event 8 - pub event8: Event<'d, T>, + pub event8: Event<'d>, /// Event 9 - pub event9: Event<'d, T>, + pub event9: Event<'d>, /// Event 10 - pub event10: Event<'d, T>, + pub event10: Event<'d>, /// Event 11 - pub event11: Event<'d, T>, + pub event11: Event<'d>, /// Event 12 - pub event12: Event<'d, T>, + pub event12: Event<'d>, /// Event 13 - pub event13: Event<'d, T>, + pub event13: Event<'d>, /// Event 14 - pub event14: Event<'d, T>, + pub event14: Event<'d>, /// Event 15 - pub event15: Event<'d, T>, + pub event15: Event<'d>, } -impl<'d, T: Instance> Ipc<'d, T> { +impl<'d> Ipc<'d> { /// Create a new IPC driver. - pub fn new( + pub fn new( _p: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, ) -> Self { T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - let _phantom = PhantomData; + let r = T::regs(); + let state = T::state(); #[rustfmt::skip] - let r = Self { // attributes on expressions are experimental - event0: Event { number: EventNumber::Event0, _phantom }, - event1: Event { number: EventNumber::Event1, _phantom }, - event2: Event { number: EventNumber::Event2, _phantom }, - event3: Event { number: EventNumber::Event3, _phantom }, - event4: Event { number: EventNumber::Event4, _phantom }, - event5: Event { number: EventNumber::Event5, _phantom }, - event6: Event { number: EventNumber::Event6, _phantom }, - event7: Event { number: EventNumber::Event7, _phantom }, - event8: Event { number: EventNumber::Event8, _phantom }, - event9: Event { number: EventNumber::Event9, _phantom }, - event10: Event { number: EventNumber::Event10, _phantom }, - event11: Event { number: EventNumber::Event11, _phantom }, - event12: Event { number: EventNumber::Event12, _phantom }, - event13: Event { number: EventNumber::Event13, _phantom }, - event14: Event { number: EventNumber::Event14, _phantom }, - event15: Event { number: EventNumber::Event15, _phantom }, + let result = Self { // attributes on expressions are experimental + event0: Event { number: EventNumber::Event0, r, state, _phantom: PhantomData }, + event1: Event { number: EventNumber::Event1, r, state, _phantom: PhantomData }, + event2: Event { number: EventNumber::Event2, r, state, _phantom: PhantomData }, + event3: Event { number: EventNumber::Event3, r, state, _phantom: PhantomData }, + event4: Event { number: EventNumber::Event4, r, state, _phantom: PhantomData }, + event5: Event { number: EventNumber::Event5, r, state, _phantom: PhantomData }, + event6: Event { number: EventNumber::Event6, r, state, _phantom: PhantomData }, + event7: Event { number: EventNumber::Event7, r, state, _phantom: PhantomData }, + event8: Event { number: EventNumber::Event8, r, state, _phantom: PhantomData }, + event9: Event { number: EventNumber::Event9, r, state, _phantom: PhantomData }, + event10: Event { number: EventNumber::Event10, r, state, _phantom: PhantomData }, + event11: Event { number: EventNumber::Event11, r, state, _phantom: PhantomData }, + event12: Event { number: EventNumber::Event12, r, state, _phantom: PhantomData }, + event13: Event { number: EventNumber::Event13, r, state, _phantom: PhantomData }, + event14: Event { number: EventNumber::Event14, r, state, _phantom: PhantomData }, + event15: Event { number: EventNumber::Event15, r, state, _phantom: PhantomData }, }; - r + result } } /// IPC event -pub struct Event<'d, T: Instance> { +pub struct Event<'d> { number: EventNumber, - _phantom: PhantomData<&'d T>, + r: pac::ipc::Ipc, + state: &'static State, + _phantom: PhantomData<&'d ()>, } -impl<'d, T: Instance> Event<'d, T> { +impl<'d> Event<'d> { /// Trigger the event. pub fn trigger(&self) { let nr = self.number; - T::regs().tasks_send(nr as usize).write_value(1); + self.r.tasks_send(nr as usize).write_value(1); } /// Wait for the event to be triggered. pub async fn wait(&mut self) { - let regs = T::regs(); let nr = self.number as usize; - regs.intenset().write(|w| w.0 = 1 << nr); + self.r.intenset().write(|w| w.0 = 1 << nr); poll_fn(|cx| { - T::state().wakers[nr].register(cx.waker()); + self.state.wakers[nr].register(cx.waker()); - if regs.events_receive(nr).read() == 1 { - regs.events_receive(nr).write_value(0x00); + if self.r.events_receive(nr).read() == 1 { + self.r.events_receive(nr).write_value(0x00); Poll::Ready(()) } else { Poll::Pending @@ -239,16 +241,17 @@ impl<'d, T: Instance> Event<'d, T> { } /// Create a handle that can trigger the event. - pub fn trigger_handle(&self) -> EventTrigger<'d, T> { + pub fn trigger_handle(&self) -> EventTrigger<'d> { EventTrigger { number: self.number, + r: self.r, _phantom: PhantomData, } } /// Configure the channels the event will broadcast to pub fn configure_trigger>(&mut self, channels: I) { - T::regs().send_cnf(self.number as usize).write(|w| { + self.r.send_cnf(self.number as usize).write(|w| { for channel in channels { w.0 |= channel.mask(); } @@ -257,7 +260,7 @@ impl<'d, T: Instance> Event<'d, T> { /// Configure the channels the event will listen on pub fn configure_wait>(&mut self, channels: I) { - T::regs().receive_cnf(self.number as usize).write(|w| { + self.r.receive_cnf(self.number as usize).write(|w| { for channel in channels { w.0 |= channel.mask(); } @@ -267,22 +270,25 @@ impl<'d, T: Instance> Event<'d, T> { /// Get the task for the IPC event to use with PPI. pub fn task(&self) -> ppi::Task<'d> { let nr = self.number as usize; - let regs = T::regs(); - ppi::Task::from_reg(regs.tasks_send(nr)) + ppi::Task::from_reg(self.r.tasks_send(nr)) } /// Get the event for the IPC event to use with PPI. pub fn event(&self) -> ppi::Event<'d> { let nr = self.number as usize; - let regs = T::regs(); - ppi::Event::from_reg(regs.events_receive(nr)) + ppi::Event::from_reg(self.r.events_receive(nr)) } /// Reborrow into a "child" Event. /// /// `self` will stay borrowed until the child Event is dropped. - pub fn reborrow(&mut self) -> Event<'_, T> { - Self { ..*self } + pub fn reborrow(&mut self) -> Event<'_> { + Event { + number: self.number, + r: self.r, + state: self.state, + _phantom: PhantomData, + } } /// Steal an IPC event by number. @@ -290,9 +296,11 @@ impl<'d, T: Instance> Event<'d, T> { /// # Safety /// /// The event number must not be in use by another [`Event`]. - pub unsafe fn steal(number: EventNumber) -> Self { + pub unsafe fn steal(number: EventNumber) -> Self { Self { number, + r: T::regs(), + state: T::state(), _phantom: PhantomData, } } @@ -301,17 +309,17 @@ impl<'d, T: Instance> Event<'d, T> { /// A handle that can trigger an IPC event. /// /// This `struct` is returned by [`Event::trigger_handle`]. -#[derive(Debug, Copy, Clone)] -pub struct EventTrigger<'d, T: Instance> { +pub struct EventTrigger<'d> { number: EventNumber, - _phantom: PhantomData<&'d T>, + r: pac::ipc::Ipc, + _phantom: PhantomData<&'d ()>, } -impl EventTrigger<'_, T> { +impl EventTrigger<'_> { /// Trigger the event. pub fn trigger(&self) { let nr = self.number; - T::regs().tasks_send(nr as usize).write_value(1); + self.r.tasks_send(nr as usize).write_value(1); } /// Returns the [`EventNumber`] of the event. -- cgit From 34ed1a1f05d909cba7ce3198b434126d47faf264 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Sep 2025 21:14:03 +0200 Subject: nrf/egu: erase instance generics --- embassy-nrf/src/egu.rs | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/embassy-nrf/src/egu.rs b/embassy-nrf/src/egu.rs index 028396c7c..f7372fca1 100644 --- a/embassy-nrf/src/egu.rs +++ b/embassy-nrf/src/egu.rs @@ -13,21 +13,26 @@ use crate::ppi::{Event, Task}; use crate::{interrupt, pac, Peri}; /// An instance of the EGU. -pub struct Egu<'d, T: Instance> { - _p: Peri<'d, T>, +pub struct Egu<'d> { + r: pac::egu::Egu, + _phantom: PhantomData<&'d ()>, } -impl<'d, T: Instance> Egu<'d, T> { +impl<'d> Egu<'d> { /// Create a new EGU instance. - pub fn new(_p: Peri<'d, T>) -> Self { - Self { _p } + pub fn new(_p: Peri<'d, T>) -> Self { + Self { + r: T::regs(), + _phantom: PhantomData, + } } /// Get a handle to a trigger for the EGU. - pub fn trigger(&mut self, number: TriggerNumber) -> Trigger<'d, T> { + pub fn trigger(&mut self, number: TriggerNumber) -> Trigger<'d> { Trigger { number, - _p: PhantomData, + r: self.r, + _phantom: PhantomData, } } } @@ -57,36 +62,37 @@ macro_rules! impl_egu { } /// Represents a trigger within the EGU. -pub struct Trigger<'d, T: Instance> { +pub struct Trigger<'d> { number: TriggerNumber, - _p: PhantomData<&'d T>, + r: pac::egu::Egu, + _phantom: PhantomData<&'d ()>, } -impl<'d, T: Instance> Trigger<'d, T> { +impl<'d> Trigger<'d> { /// Get task for this trigger to use with PPI. pub fn task(&self) -> Task<'d> { let nr = self.number as usize; - let regs = T::regs(); - Task::from_reg(regs.tasks_trigger(nr)) + Task::from_reg(self.r.tasks_trigger(nr)) } /// Get event for this trigger to use with PPI. pub fn event(&self) -> Event<'d> { let nr = self.number as usize; - let regs = T::regs(); - Event::from_reg(regs.events_triggered(nr)) + Event::from_reg(self.r.events_triggered(nr)) } /// Enable interrupts for this trigger pub fn enable_interrupt(&mut self) { - let regs = T::regs(); - regs.intenset().modify(|w| w.set_triggered(self.number as usize, true)); + self.r + .intenset() + .modify(|w| w.set_triggered(self.number as usize, true)); } /// Enable interrupts for this trigger pub fn disable_interrupt(&mut self) { - let regs = T::regs(); - regs.intenset().modify(|w| w.set_triggered(self.number as usize, false)); + self.r + .intenset() + .modify(|w| w.set_triggered(self.number as usize, false)); } } -- cgit From b552373f9a7c5491367d8f5a04111eb27b63f5e2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Sep 2025 21:15:53 +0200 Subject: nrf/pdm: erase instance generics --- embassy-nrf/src/pdm.rs | 107 ++++++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 51 deletions(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index c2a4ba65f..b6ee52850 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -53,8 +53,10 @@ impl interrupt::typelevel::Handler for InterruptHandl } /// PDM microphone interface -pub struct Pdm<'d, T: Instance> { - _peri: Peri<'d, T>, +pub struct Pdm<'d> { + r: pac::pdm::Pdm, + state: &'static State, + _phantom: PhantomData<&'d ()>, } /// PDM error @@ -86,9 +88,9 @@ pub enum SamplerState { Stopped, } -impl<'d, T: Instance> Pdm<'d, T> { +impl<'d> Pdm<'d> { /// Create PDM driver - pub fn new( + pub fn new( pdm: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, clk: Peri<'d, impl GpioPin>, @@ -98,7 +100,7 @@ impl<'d, T: Instance> Pdm<'d, T> { Self::new_inner(pdm, clk.into(), din.into(), config) } - fn new_inner(pdm: Peri<'d, T>, clk: Peri<'d, AnyPin>, din: Peri<'d, AnyPin>, config: Config) -> Self { + fn new_inner(_pdm: Peri<'d, T>, clk: Peri<'d, AnyPin>, din: Peri<'d, AnyPin>, config: Config) -> Self { let r = T::regs(); // setup gpio pins @@ -133,7 +135,11 @@ impl<'d, T: Instance> Pdm<'d, T> { r.enable().write(|w| w.set_enable(true)); - Self { _peri: pdm } + Self { + r: T::regs(), + state: T::state(), + _phantom: PhantomData, + } } fn _set_gain(r: pac::pdm::Pdm, gain_left: I7F1, gain_right: I7F1) { @@ -147,26 +153,26 @@ impl<'d, T: Instance> Pdm<'d, T> { /// Adjust the gain of the PDM microphone on the fly pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) { - Self::_set_gain(T::regs(), gain_left, gain_right) + Self::_set_gain(self.r, gain_left, gain_right) } /// Start sampling microphone data into a dummy buffer. /// Useful to start the microphone and keep it active between recording samples. pub async fn start(&mut self) { - let r = T::regs(); - // start dummy sampling because microphone needs some setup time - r.sample().ptr().write_value(DUMMY_BUFFER.as_ptr() as u32); - r.sample().maxcnt().write(|w| w.set_buffsize(DUMMY_BUFFER.len() as _)); + self.r.sample().ptr().write_value(DUMMY_BUFFER.as_ptr() as u32); + self.r + .sample() + .maxcnt() + .write(|w| w.set_buffsize(DUMMY_BUFFER.len() as _)); - r.tasks_start().write_value(1); + self.r.tasks_start().write_value(1); } /// Stop sampling microphone data inta a dummy buffer pub async fn stop(&mut self) { - let r = T::regs(); - r.tasks_stop().write_value(1); - r.events_started().write_value(0); + self.r.tasks_stop().write_value(1); + self.r.events_started().write_value(0); } /// Sample data into the given buffer @@ -178,12 +184,11 @@ impl<'d, T: Instance> Pdm<'d, T> { return Err(Error::BufferTooLong); } - let r = T::regs(); - - if r.events_started().read() == 0 { + if self.r.events_started().read() == 0 { return Err(Error::NotRunning); } + let r = self.r; let drop = OnDrop::new(move || { r.intenclr().write(|w| w.set_end(true)); r.events_stopped().write_value(0); @@ -198,34 +203,37 @@ impl<'d, T: Instance> Pdm<'d, T> { // setup user buffer let ptr = buffer.as_ptr(); let len = buffer.len(); - r.sample().ptr().write_value(ptr as u32); - r.sample().maxcnt().write(|w| w.set_buffsize(len as _)); + self.r.sample().ptr().write_value(ptr as u32); + self.r.sample().maxcnt().write(|w| w.set_buffsize(len as _)); // wait till the current sample is finished and the user buffer sample is started - Self::wait_for_sample().await; + self.wait_for_sample().await; // reset the buffer back to the dummy buffer - r.sample().ptr().write_value(DUMMY_BUFFER.as_ptr() as u32); - r.sample().maxcnt().write(|w| w.set_buffsize(DUMMY_BUFFER.len() as _)); + self.r.sample().ptr().write_value(DUMMY_BUFFER.as_ptr() as u32); + self.r + .sample() + .maxcnt() + .write(|w| w.set_buffsize(DUMMY_BUFFER.len() as _)); // wait till the user buffer is sampled - Self::wait_for_sample().await; + self.wait_for_sample().await; drop.defuse(); Ok(()) } - async fn wait_for_sample() { - let r = T::regs(); - - r.events_end().write_value(0); - r.intenset().write(|w| w.set_end(true)); + async fn wait_for_sample(&mut self) { + self.r.events_end().write_value(0); + self.r.intenset().write(|w| w.set_end(true)); compiler_fence(Ordering::SeqCst); + let state = self.state; + let r = self.r; poll_fn(|cx| { - T::state().waker.register(cx.waker()); + state.waker.register(cx.waker()); if r.events_end().read() != 0 { return Poll::Ready(()); } @@ -255,20 +263,18 @@ impl<'d, T: Instance> Pdm<'d, T> { where S: FnMut(&[i16; N]) -> SamplerState, { - let r = T::regs(); - - if r.events_started().read() != 0 { + if self.r.events_started().read() != 0 { return Err(Error::AlreadyRunning); } - r.sample().ptr().write_value(bufs[0].as_mut_ptr() as u32); - r.sample().maxcnt().write(|w| w.set_buffsize(N as _)); + self.r.sample().ptr().write_value(bufs[0].as_mut_ptr() as u32); + self.r.sample().maxcnt().write(|w| w.set_buffsize(N as _)); // Reset and enable the events - r.events_end().write_value(0); - r.events_started().write_value(0); - r.events_stopped().write_value(0); - r.intenset().write(|w| { + self.r.events_end().write_value(0); + self.r.events_started().write_value(0); + self.r.events_stopped().write_value(0); + self.r.intenset().write(|w| { w.set_end(true); w.set_started(true); w.set_stopped(true); @@ -278,23 +284,24 @@ impl<'d, T: Instance> Pdm<'d, T> { // wouldn't happen anyway compiler_fence(Ordering::SeqCst); - r.tasks_start().write_value(1); + self.r.tasks_start().write_value(1); let mut current_buffer = 0; let mut done = false; - let drop = OnDrop::new(|| { + let r = self.r; + let drop = OnDrop::new(move || { r.tasks_stop().write_value(1); // N.B. It would be better if this were async, but Drop only support sync code while r.events_stopped().read() != 0 {} }); + let state = self.state; + let r = self.r; // Wait for events and complete when the sampler indicates it has had enough poll_fn(|cx| { - let r = T::regs(); - - T::state().waker.register(cx.waker()); + state.waker.register(cx.waker()); if r.events_end().read() != 0 { compiler_fence(Ordering::SeqCst); @@ -411,16 +418,14 @@ impl From for vals::Edge { } } -impl<'d, T: Instance> Drop for Pdm<'d, T> { +impl<'d> Drop for Pdm<'d> { fn drop(&mut self) { - let r = T::regs(); - - r.tasks_stop().write_value(1); + self.r.tasks_stop().write_value(1); - r.enable().write(|w| w.set_enable(false)); + self.r.enable().write(|w| w.set_enable(false)); - r.psel().din().write_value(DISCONNECTED); - r.psel().clk().write_value(DISCONNECTED); + self.r.psel().din().write_value(DISCONNECTED); + self.r.psel().clk().write_value(DISCONNECTED); } } -- cgit From 6ee0d15f4975daa52a22c42e57c673f5ecc86b9d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Sep 2025 21:17:11 +0200 Subject: nrf/pwm: erase instance generics --- embassy-nrf/src/pwm.rs | 147 ++++++++++++++++++++----------------------------- 1 file changed, 60 insertions(+), 87 deletions(-) diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index d6b40b5c0..d67cb546b 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -15,8 +15,8 @@ use crate::{interrupt, pac}; /// SimplePwm is the traditional pwm interface you're probably used to, allowing /// to simply set a duty cycle across up to four channels. -pub struct SimplePwm<'d, T: Instance> { - _peri: Peri<'d, T>, +pub struct SimplePwm<'d> { + r: pac::pwm::Pwm, duty: [u16; 4], ch0: Option>, ch1: Option>, @@ -26,8 +26,8 @@ pub struct SimplePwm<'d, T: Instance> { /// SequencePwm allows you to offload the updating of a sequence of duty cycles /// to up to four channels, as well as repeat that sequence n times. -pub struct SequencePwm<'d, T: Instance> { - _peri: Peri<'d, T>, +pub struct SequencePwm<'d> { + r: pac::pwm::Pwm, ch0: Option>, ch1: Option>, ch2: Option>, @@ -51,16 +51,16 @@ const MAX_SEQUENCE_LEN: usize = 32767; /// The used pwm clock frequency pub const PWM_CLK_HZ: u32 = 16_000_000; -impl<'d, T: Instance> SequencePwm<'d, T> { +impl<'d> SequencePwm<'d> { /// Create a new 1-channel PWM #[allow(unused_unsafe)] - pub fn new_1ch(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, config: Config) -> Result { + pub fn new_1ch(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, config: Config) -> Result { Self::new_inner(pwm, Some(ch0.into()), None, None, None, config) } /// Create a new 2-channel PWM #[allow(unused_unsafe)] - pub fn new_2ch( + pub fn new_2ch( pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, ch1: Peri<'d, impl GpioPin>, @@ -71,7 +71,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Create a new 3-channel PWM #[allow(unused_unsafe)] - pub fn new_3ch( + pub fn new_3ch( pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, ch1: Peri<'d, impl GpioPin>, @@ -83,7 +83,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Create a new 4-channel PWM #[allow(unused_unsafe)] - pub fn new_4ch( + pub fn new_4ch( pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, ch1: Peri<'d, impl GpioPin>, @@ -101,7 +101,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { ) } - fn new_inner( + fn new_inner( _pwm: Peri<'d, T>, ch0: Option>, ch1: Option>, @@ -174,7 +174,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { r.countertop().write(|w| w.set_countertop(config.max_duty)); Ok(Self { - _peri: _pwm, + r: T::regs(), ch0, ch1, ch2, @@ -185,57 +185,43 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `Stopped` event endpoint for PPI. #[inline(always)] pub fn event_stopped(&self) -> Event<'d> { - let r = T::regs(); - - Event::from_reg(r.events_stopped()) + Event::from_reg(self.r.events_stopped()) } /// Returns reference to `LoopsDone` event endpoint for PPI. #[inline(always)] pub fn event_loops_done(&self) -> Event<'d> { - let r = T::regs(); - - Event::from_reg(r.events_loopsdone()) + Event::from_reg(self.r.events_loopsdone()) } /// Returns reference to `PwmPeriodEnd` event endpoint for PPI. #[inline(always)] pub fn event_pwm_period_end(&self) -> Event<'d> { - let r = T::regs(); - - Event::from_reg(r.events_pwmperiodend()) + Event::from_reg(self.r.events_pwmperiodend()) } /// Returns reference to `Seq0 End` event endpoint for PPI. #[inline(always)] pub fn event_seq_end(&self) -> Event<'d> { - let r = T::regs(); - - Event::from_reg(r.events_seqend(0)) + Event::from_reg(self.r.events_seqend(0)) } /// Returns reference to `Seq1 End` event endpoint for PPI. #[inline(always)] pub fn event_seq1_end(&self) -> Event<'d> { - let r = T::regs(); - - Event::from_reg(r.events_seqend(1)) + Event::from_reg(self.r.events_seqend(1)) } /// Returns reference to `Seq0 Started` event endpoint for PPI. #[inline(always)] pub fn event_seq0_started(&self) -> Event<'d> { - let r = T::regs(); - - Event::from_reg(r.events_seqstarted(0)) + Event::from_reg(self.r.events_seqstarted(0)) } /// Returns reference to `Seq1 Started` event endpoint for PPI. #[inline(always)] pub fn event_seq1_started(&self) -> Event<'d> { - let r = T::regs(); - - Event::from_reg(r.events_seqstarted(1)) + Event::from_reg(self.r.events_seqstarted(1)) } /// Returns reference to `Seq0 Start` task endpoint for PPI. @@ -244,9 +230,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] pub unsafe fn task_start_seq0(&self) -> Task<'d> { - let r = T::regs(); - - Task::from_reg(r.tasks_seqstart(0)) + Task::from_reg(self.r.tasks_seqstart(0)) } /// Returns reference to `Seq1 Started` task endpoint for PPI. @@ -255,9 +239,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] pub unsafe fn task_start_seq1(&self) -> Task<'d> { - let r = T::regs(); - - Task::from_reg(r.tasks_seqstart(1)) + Task::from_reg(self.r.tasks_seqstart(1)) } /// Returns reference to `NextStep` task endpoint for PPI. @@ -266,9 +248,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] pub unsafe fn task_next_step(&self) -> Task<'d> { - let r = T::regs(); - - Task::from_reg(r.tasks_nextstep()) + Task::from_reg(self.r.tasks_nextstep()) } /// Returns reference to `Stop` task endpoint for PPI. @@ -277,36 +257,34 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] pub unsafe fn task_stop(&self) -> Task<'d> { - let r = T::regs(); - - Task::from_reg(r.tasks_stop()) + Task::from_reg(self.r.tasks_stop()) } } -impl<'a, T: Instance> Drop for SequencePwm<'a, T> { +impl<'a> Drop for SequencePwm<'a> { fn drop(&mut self) { - let r = T::regs(); - if let Some(pin) = &self.ch0 { pin.set_low(); pin.conf().write(|_| ()); - r.psel().out(0).write_value(DISCONNECTED); + self.r.psel().out(0).write_value(DISCONNECTED); } if let Some(pin) = &self.ch1 { pin.set_low(); pin.conf().write(|_| ()); - r.psel().out(1).write_value(DISCONNECTED); + self.r.psel().out(1).write_value(DISCONNECTED); } if let Some(pin) = &self.ch2 { pin.set_low(); pin.conf().write(|_| ()); - r.psel().out(2).write_value(DISCONNECTED); + self.r.psel().out(2).write_value(DISCONNECTED); } if let Some(pin) = &self.ch3 { pin.set_low(); pin.conf().write(|_| ()); - r.psel().out(3).write_value(DISCONNECTED); + self.r.psel().out(3).write_value(DISCONNECTED); } + + self.r.enable().write(|w| w.set_enable(false)); } } @@ -384,13 +362,13 @@ impl<'s> Sequence<'s> { /// A single sequence that can be started and stopped. /// Takes one sequence along with its configuration. #[non_exhaustive] -pub struct SingleSequencer<'d, 's, T: Instance> { - sequencer: Sequencer<'d, 's, T>, +pub struct SingleSequencer<'d, 's> { + sequencer: Sequencer<'d, 's>, } -impl<'d, 's, T: Instance> SingleSequencer<'d, 's, T> { +impl<'d, 's> SingleSequencer<'d, 's> { /// Create a new sequencer - pub fn new(pwm: &'s mut SequencePwm<'d, T>, words: &'s [u16], config: SequenceConfig) -> Self { + pub fn new(pwm: &'s mut SequencePwm<'d>, words: &'s [u16], config: SequenceConfig) -> Self { Self { sequencer: Sequencer::new(pwm, Sequence::new(words, config), None), } @@ -423,16 +401,16 @@ impl<'d, 's, T: Instance> SingleSequencer<'d, 's, T> { /// In the case where no second sequence is provided then the first sequence /// is used. #[non_exhaustive] -pub struct Sequencer<'d, 's, T: Instance> { - _pwm: &'s mut SequencePwm<'d, T>, +pub struct Sequencer<'d, 's> { + _pwm: &'s mut SequencePwm<'d>, sequence0: Sequence<'s>, sequence1: Option>, } -impl<'d, 's, T: Instance> Sequencer<'d, 's, T> { +impl<'d, 's> Sequencer<'d, 's> { /// Create a new double sequence. In the absence of sequence 1, sequence 0 /// will be used twice in the one loop. - pub fn new(pwm: &'s mut SequencePwm<'d, T>, sequence0: Sequence<'s>, sequence1: Option>) -> Self { + pub fn new(pwm: &'s mut SequencePwm<'d>, sequence0: Sequence<'s>, sequence1: Option>) -> Self { Sequencer { _pwm: pwm, sequence0, @@ -459,7 +437,7 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> { self.stop(); - let r = T::regs(); + let r = self._pwm.r; r.seq(0).refresh().write(|w| w.0 = sequence0.config.refresh); r.seq(0).enddelay().write(|w| w.0 = sequence0.config.end_delay); @@ -499,7 +477,7 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> { /// `start` so that they may be further mutated. #[inline(always)] pub fn stop(&self) { - let r = T::regs(); + let r = self._pwm.r; r.shorts().write(|_| ()); @@ -510,7 +488,7 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> { } } -impl<'d, 's, T: Instance> Drop for Sequencer<'d, 's, T> { +impl<'d, 's> Drop for Sequencer<'d, 's> { fn drop(&mut self) { self.stop(); } @@ -589,22 +567,22 @@ pub enum CounterMode { UpAndDown, } -impl<'d, T: Instance> SimplePwm<'d, T> { +impl<'d> SimplePwm<'d> { /// Create a new 1-channel PWM #[allow(unused_unsafe)] - pub fn new_1ch(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>) -> Self { + pub fn new_1ch(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>) -> Self { unsafe { Self::new_inner(pwm, Some(ch0.into()), None, None, None) } } /// Create a new 2-channel PWM #[allow(unused_unsafe)] - pub fn new_2ch(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, ch1: Peri<'d, impl GpioPin>) -> Self { + pub fn new_2ch(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, ch1: Peri<'d, impl GpioPin>) -> Self { Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), None, None) } /// Create a new 3-channel PWM #[allow(unused_unsafe)] - pub fn new_3ch( + pub fn new_3ch( pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, ch1: Peri<'d, impl GpioPin>, @@ -615,7 +593,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { /// Create a new 4-channel PWM #[allow(unused_unsafe)] - pub fn new_4ch( + pub fn new_4ch( pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, ch1: Peri<'d, impl GpioPin>, @@ -633,7 +611,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { } } - fn new_inner( + fn new_inner( _pwm: Peri<'d, T>, ch0: Option>, ch1: Option>, @@ -656,7 +634,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { } let pwm = Self { - _peri: _pwm, + r: T::regs(), ch0, ch1, ch2, @@ -691,22 +669,19 @@ impl<'d, T: Instance> SimplePwm<'d, T> { /// Returns the enable state of the pwm counter #[inline(always)] pub fn is_enabled(&self) -> bool { - let r = T::regs(); - r.enable().read().enable() + self.r.enable().read().enable() } /// Enables the PWM generator. #[inline(always)] pub fn enable(&self) { - let r = T::regs(); - r.enable().write(|w| w.set_enable(true)); + self.r.enable().write(|w| w.set_enable(true)); } /// Disables the PWM generator. Does NOT clear the last duty cycle from the pin. #[inline(always)] pub fn disable(&self) { - let r = T::regs(); - r.enable().write(|w| w.set_enable(false)); + self.r.enable().write(|w| w.set_enable(false)); } /// Returns the current duty of the channel @@ -716,32 +691,30 @@ impl<'d, T: Instance> SimplePwm<'d, T> { /// Sets duty cycle (15 bit) for a PWM channel. pub fn set_duty(&mut self, channel: usize, duty: u16) { - let r = T::regs(); - self.duty[channel] = duty & 0x7FFF; // reload ptr in case self was moved - r.seq(0).ptr().write_value((self.duty).as_ptr() as u32); + self.r.seq(0).ptr().write_value((self.duty).as_ptr() as u32); // defensive before seqstart compiler_fence(Ordering::SeqCst); - r.events_seqend(0).write_value(0); + self.r.events_seqend(0).write_value(0); // tasks_seqstart() doesn't exist in all svds so write its bit instead - r.tasks_seqstart(0).write_value(1); + self.r.tasks_seqstart(0).write_value(1); // defensive wait until waveform is loaded after seqstart so set_duty // can't be called again while dma is still reading if self.is_enabled() { - while r.events_seqend(0).read() == 0 {} + while self.r.events_seqend(0).read() == 0 {} } } /// Sets the PWM clock prescaler. #[inline(always)] pub fn set_prescaler(&self, div: Prescaler) { - T::regs() + self.r .prescaler() .write(|w| w.set_prescaler(vals::Prescaler::from_bits(div as u8))); } @@ -749,7 +722,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { /// Gets the PWM clock prescaler. #[inline(always)] pub fn prescaler(&self) -> Prescaler { - match T::regs().prescaler().read().prescaler().to_bits() { + match self.r.prescaler().read().prescaler().to_bits() { 0 => Prescaler::Div1, 1 => Prescaler::Div2, 2 => Prescaler::Div4, @@ -765,13 +738,13 @@ impl<'d, T: Instance> SimplePwm<'d, T> { /// Sets the maximum duty cycle value. #[inline(always)] pub fn set_max_duty(&self, duty: u16) { - T::regs().countertop().write(|w| w.set_countertop(duty.min(32767u16))); + self.r.countertop().write(|w| w.set_countertop(duty.min(32767u16))); } /// Returns the maximum duty cycle value. #[inline(always)] pub fn max_duty(&self) -> u16 { - T::regs().countertop().read().countertop() + self.r.countertop().read().countertop() } /// Sets the PWM output frequency. @@ -823,9 +796,9 @@ impl<'d, T: Instance> SimplePwm<'d, T> { } } -impl<'a, T: Instance> Drop for SimplePwm<'a, T> { +impl<'a> Drop for SimplePwm<'a> { fn drop(&mut self) { - let r = T::regs(); + let r = &self.r; self.disable(); -- cgit From 6a262521811a435f2cfedd7e25fb70a1aab957ca Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Sep 2025 21:17:54 +0200 Subject: nrf/qdec: erase instance generics --- embassy-nrf/src/qdec.rs | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 69bfab0bb..0ebd7afb8 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -16,8 +16,10 @@ use crate::pac::qdec::vals; use crate::{interrupt, pac}; /// Quadrature decoder driver. -pub struct Qdec<'d, T: Instance> { - _p: Peri<'d, T>, +pub struct Qdec<'d> { + r: pac::qdec::Qdec, + state: &'static State, + _phantom: PhantomData<&'d ()>, } /// QDEC config @@ -59,9 +61,9 @@ impl interrupt::typelevel::Handler for InterruptHandl } } -impl<'d, T: Instance> Qdec<'d, T> { +impl<'d> Qdec<'d> { /// Create a new QDEC. - pub fn new( + pub fn new( qdec: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, a: Peri<'d, impl GpioPin>, @@ -72,7 +74,7 @@ impl<'d, T: Instance> Qdec<'d, T> { } /// Create a new QDEC, with a pin for LED output. - pub fn new_with_led( + pub fn new_with_led( qdec: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, a: Peri<'d, impl GpioPin>, @@ -83,8 +85,8 @@ impl<'d, T: Instance> Qdec<'d, T> { Self::new_inner(qdec, a.into(), b.into(), Some(led.into()), config) } - fn new_inner( - p: Peri<'d, T>, + fn new_inner( + _p: Peri<'d, T>, a: Peri<'d, AnyPin>, b: Peri<'d, AnyPin>, led: Option>, @@ -147,7 +149,11 @@ impl<'d, T: Instance> Qdec<'d, T> { // Start sampling r.tasks_start().write_value(1); - Self { _p: p } + Self { + r: T::regs(), + state: T::state(), + _phantom: PhantomData, + } } /// Perform an asynchronous read of the decoder. @@ -173,17 +179,18 @@ impl<'d, T: Instance> Qdec<'d, T> { /// # }; /// ``` pub async fn read(&mut self) -> i16 { - let t = T::regs(); - t.intenset().write(|w| w.set_reportrdy(true)); - t.tasks_readclracc().write_value(1); + self.r.intenset().write(|w| w.set_reportrdy(true)); + self.r.tasks_readclracc().write_value(1); - poll_fn(|cx| { - T::state().waker.register(cx.waker()); - if t.events_reportrdy().read() == 0 { + let state = self.state; + let r = self.r; + poll_fn(move |cx| { + state.waker.register(cx.waker()); + if r.events_reportrdy().read() == 0 { Poll::Pending } else { - t.events_reportrdy().write_value(0); - let acc = t.accread().read(); + r.events_reportrdy().write_value(0); + let acc = r.accread().read(); Poll::Ready(acc as i16) } }) -- cgit From 19fdd8d96c778697610386c829f6a0a655c39940 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Sep 2025 21:18:29 +0200 Subject: nrf/qspi: erase instance generics --- embassy-nrf/src/qspi.rs | 109 +++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 57 deletions(-) diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index e6e829f6e..94ad3f0d6 100755 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -138,16 +138,18 @@ impl interrupt::typelevel::Handler for InterruptHandl } /// QSPI flash driver. -pub struct Qspi<'d, T: Instance> { - _peri: Peri<'d, T>, +pub struct Qspi<'d> { + r: pac::qspi::Qspi, + state: &'static State, dpm_enabled: bool, capacity: u32, + _phantom: PhantomData<&'d ()>, } -impl<'d, T: Instance> Qspi<'d, T> { +impl<'d> Qspi<'d> { /// Create a new QSPI driver. - pub fn new( - qspi: Peri<'d, T>, + pub fn new( + _qspi: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, sck: Peri<'d, impl GpioPin>, csn: Peri<'d, impl GpioPin>, @@ -214,9 +216,11 @@ impl<'d, T: Instance> Qspi<'d, T> { r.enable().write(|w| w.set_enable(true)); let res = Self { - _peri: qspi, + r: T::regs(), + state: T::state(), dpm_enabled: config.deep_power_down.is_some(), capacity: config.capacity, + _phantom: PhantomData, }; r.events_ready().write_value(0); @@ -274,14 +278,13 @@ impl<'d, T: Instance> Qspi<'d, T> { } } - let r = T::regs(); - r.cinstrdat0().write(|w| w.0 = dat0); - r.cinstrdat1().write(|w| w.0 = dat1); + self.r.cinstrdat0().write(|w| w.0 = dat0); + self.r.cinstrdat1().write(|w| w.0 = dat1); - r.events_ready().write_value(0); - r.intenset().write(|w| w.set_ready(true)); + self.r.events_ready().write_value(0); + self.r.intenset().write(|w| w.set_ready(true)); - r.cinstrconf().write(|w| { + self.r.cinstrconf().write(|w| { w.set_opcode(opcode); w.set_length(vals::Length::from_bits(len + 1)); w.set_lio2(true); @@ -295,10 +298,8 @@ impl<'d, T: Instance> Qspi<'d, T> { } fn custom_instruction_finish(&mut self, resp: &mut [u8]) -> Result<(), Error> { - let r = T::regs(); - - let dat0 = r.cinstrdat0().read().0; - let dat1 = r.cinstrdat1().read().0; + let dat0 = self.r.cinstrdat0().read().0; + let dat1 = self.r.cinstrdat1().read().0; for i in 0..4 { if i < resp.len() { resp[i] = (dat0 >> (i * 8)) as u8; @@ -313,9 +314,9 @@ impl<'d, T: Instance> Qspi<'d, T> { } fn wait_ready(&mut self) -> impl Future { + let r = self.r; + let s = self.state; poll_fn(move |cx| { - let r = T::regs(); - let s = T::state(); s.waker.register(cx.waker()); if r.events_ready().read() != 0 { return Poll::Ready(()); @@ -326,7 +327,7 @@ impl<'d, T: Instance> Qspi<'d, T> { fn blocking_wait_ready() { loop { - let r = T::regs(); + let r = pac::QSPI; if r.events_ready().read() != 0 { break; } @@ -339,15 +340,13 @@ impl<'d, T: Instance> Qspi<'d, T> { assert_eq!(data.len() as u32 % 4, 0); assert_eq!(address % 4, 0); - let r = T::regs(); - - r.read().src().write_value(address); - r.read().dst().write_value(data.as_ptr() as u32); - r.read().cnt().write(|w| w.set_cnt(data.len() as u32)); + self.r.read().src().write_value(address); + self.r.read().dst().write_value(data.as_ptr() as u32); + self.r.read().cnt().write(|w| w.set_cnt(data.len() as u32)); - r.events_ready().write_value(0); - r.intenset().write(|w| w.set_ready(true)); - r.tasks_readstart().write_value(1); + self.r.events_ready().write_value(0); + self.r.intenset().write(|w| w.set_ready(true)); + self.r.tasks_readstart().write_value(1); Ok(()) } @@ -358,14 +357,13 @@ impl<'d, T: Instance> Qspi<'d, T> { assert_eq!(data.len() as u32 % 4, 0); assert_eq!(address % 4, 0); - let r = T::regs(); - r.write().src().write_value(data.as_ptr() as u32); - r.write().dst().write_value(address); - r.write().cnt().write(|w| w.set_cnt(data.len() as u32)); + self.r.write().src().write_value(data.as_ptr() as u32); + self.r.write().dst().write_value(address); + self.r.write().cnt().write(|w| w.set_cnt(data.len() as u32)); - r.events_ready().write_value(0); - r.intenset().write(|w| w.set_ready(true)); - r.tasks_writestart().write_value(1); + self.r.events_ready().write_value(0); + self.r.intenset().write(|w| w.set_ready(true)); + self.r.tasks_writestart().write_value(1); Ok(()) } @@ -374,13 +372,12 @@ impl<'d, T: Instance> Qspi<'d, T> { // TODO: Return these as errors instead. assert_eq!(address % 4096, 0); - let r = T::regs(); - r.erase().ptr().write_value(address); - r.erase().len().write(|w| w.set_len(vals::Len::_4KB)); + self.r.erase().ptr().write_value(address); + self.r.erase().len().write(|w| w.set_len(vals::Len::_4KB)); - r.events_ready().write_value(0); - r.intenset().write(|w| w.set_ready(true)); - r.tasks_erasestart().write_value(1); + self.r.events_ready().write_value(0); + self.r.intenset().write(|w| w.set_ready(true)); + self.r.tasks_erasestart().write_value(1); Ok(()) } @@ -520,19 +517,17 @@ impl<'d, T: Instance> Qspi<'d, T> { } } -impl<'d, T: Instance> Drop for Qspi<'d, T> { +impl<'d> Drop for Qspi<'d> { fn drop(&mut self) { - let r = T::regs(); - if self.dpm_enabled { trace!("qspi: doing deep powerdown..."); - r.ifconfig1().modify(|w| w.set_dpmen(true)); + self.r.ifconfig1().modify(|w| w.set_dpmen(true)); // Wait for DPM enter. // Unfortunately we must spin. There's no way to do this interrupt-driven. // The READY event does NOT fire on DPM enter (but it does fire on DPM exit :shrug:) - while !r.status().read().dpm() {} + while !self.r.status().read().dpm() {} // Wait MORE for DPM enter. // I have absolutely no idea why, but the wait above is not enough :'( @@ -541,29 +536,29 @@ impl<'d, T: Instance> Drop for Qspi<'d, T> { } // it seems events_ready is not generated in response to deactivate. nrfx doesn't wait for it. - r.tasks_deactivate().write_value(1); + self.r.tasks_deactivate().write_value(1); // Workaround https://docs.nordicsemi.com/bundle/errata_nRF52840_Rev3/page/ERR/nRF52840/Rev3/latest/anomaly_840_122.html // Note that the doc has 2 register writes, but the first one is really the write to tasks_deactivate, // so we only do the second one here. unsafe { ptr::write_volatile(0x40029054 as *mut u32, 1) } - r.enable().write(|w| w.set_enable(false)); + self.r.enable().write(|w| w.set_enable(false)); // Note: we do NOT deconfigure CSN here. If DPM is in use and we disconnect CSN, // leaving it floating, the flash chip might read it as zero which would cause it to // spuriously exit DPM. - gpio::deconfigure_pin(r.psel().sck().read()); - gpio::deconfigure_pin(r.psel().io0().read()); - gpio::deconfigure_pin(r.psel().io1().read()); - gpio::deconfigure_pin(r.psel().io2().read()); - gpio::deconfigure_pin(r.psel().io3().read()); + gpio::deconfigure_pin(self.r.psel().sck().read()); + gpio::deconfigure_pin(self.r.psel().io0().read()); + gpio::deconfigure_pin(self.r.psel().io1().read()); + gpio::deconfigure_pin(self.r.psel().io2().read()); + gpio::deconfigure_pin(self.r.psel().io3().read()); trace!("qspi: dropped"); } } -impl<'d, T: Instance> ErrorType for Qspi<'d, T> { +impl<'d> ErrorType for Qspi<'d> { type Error = Error; } @@ -573,7 +568,7 @@ impl NorFlashError for Error { } } -impl<'d, T: Instance> ReadNorFlash for Qspi<'d, T> { +impl<'d> ReadNorFlash for Qspi<'d> { const READ_SIZE: usize = 4; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -586,7 +581,7 @@ impl<'d, T: Instance> ReadNorFlash for Qspi<'d, T> { } } -impl<'d, T: Instance> NorFlash for Qspi<'d, T> { +impl<'d> NorFlash for Qspi<'d> { const WRITE_SIZE: usize = 4; const ERASE_SIZE: usize = 4096; @@ -611,7 +606,7 @@ mod _eh1 { use super::*; - impl<'d, T: Instance> AsyncNorFlash for Qspi<'d, T> { + impl<'d> AsyncNorFlash for Qspi<'d> { const WRITE_SIZE: usize = ::WRITE_SIZE; const ERASE_SIZE: usize = ::ERASE_SIZE; @@ -627,7 +622,7 @@ mod _eh1 { } } - impl<'d, T: Instance> AsyncReadNorFlash for Qspi<'d, T> { + impl<'d> AsyncReadNorFlash for Qspi<'d> { const READ_SIZE: usize = 4; async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Self::Error> { self.read(address, data).await -- cgit From 37fd0c7bce19d4618669a29bdd01945fb477eea6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 27 Sep 2025 21:19:13 +0200 Subject: nrf/rng: erase instance generics --- embassy-nrf/src/rng.rs | 56 +++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index 9d3130e6e..8070c1afe 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -56,21 +56,23 @@ impl interrupt::typelevel::Handler for InterruptHandl /// A wrapper around an nRF RNG peripheral. /// /// It has a non-blocking API, and a blocking api through `rand`. -pub struct Rng<'d, T: Instance, M: Mode> { - _peri: Peri<'d, T>, - _phantom: PhantomData, +pub struct Rng<'d, M: Mode> { + r: pac::rng::Rng, + state: &'static State, + _phantom: PhantomData<(&'d (), M)>, } -impl<'d, T: Instance> Rng<'d, T, Blocking> { +impl<'d> Rng<'d, Blocking> { /// Creates a new RNG driver from the `RNG` peripheral and interrupt. /// /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor, /// e.g. using `mem::forget`. /// /// The synchronous API is safe. - pub fn new_blocking(rng: Peri<'d, T>) -> Self { + pub fn new_blocking(_rng: Peri<'d, T>) -> Self { let this = Self { - _peri: rng, + r: T::regs(), + state: T::state(), _phantom: PhantomData, }; @@ -80,19 +82,20 @@ impl<'d, T: Instance> Rng<'d, T, Blocking> { } } -impl<'d, T: Instance> Rng<'d, T, Async> { +impl<'d> Rng<'d, Async> { /// Creates a new RNG driver from the `RNG` peripheral and interrupt. /// /// SAFETY: The future returned from `fill_bytes` must not have its lifetime end without running its destructor, /// e.g. using `mem::forget`. /// /// The synchronous API is safe. - pub fn new( - rng: Peri<'d, T>, + pub fn new( + _rng: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, ) -> Self { let this = Self { - _peri: rng, + r: T::regs(), + state: T::state(), _phantom: PhantomData, }; @@ -106,11 +109,11 @@ impl<'d, T: Instance> Rng<'d, T, Async> { } fn enable_irq(&self) { - T::regs().intenset().write(|w| w.set_valrdy(true)); + self.r.intenset().write(|w| w.set_valrdy(true)); } fn disable_irq(&self) { - T::regs().intenclr().write(|w| w.set_valrdy(true)); + self.r.intenclr().write(|w| w.set_valrdy(true)); } /// Fill the buffer with random bytes. @@ -120,10 +123,11 @@ impl<'d, T: Instance> Rng<'d, T, Async> { } let range = dest.as_mut_ptr_range(); + let state = self.state; // Even if we've preempted the interrupt, it can't preempt us again, // so we don't need to worry about the order we write these in. critical_section::with(|cs| { - let mut state = T::state().borrow_mut(cs); + let mut state = state.borrow_mut(cs); state.ptr = range.start; state.end = range.end; }); @@ -136,7 +140,7 @@ impl<'d, T: Instance> Rng<'d, T, Async> { self.disable_irq(); critical_section::with(|cs| { - let mut state = T::state().borrow_mut(cs); + let mut state = state.borrow_mut(cs); state.ptr = ptr::null_mut(); state.end = ptr::null_mut(); }); @@ -144,7 +148,7 @@ impl<'d, T: Instance> Rng<'d, T, Async> { poll_fn(|cx| { critical_section::with(|cs| { - let mut s = T::state().borrow_mut(cs); + let mut s = state.borrow_mut(cs); s.waker.register(cx.waker()); if s.ptr == s.end { // We're done. @@ -161,13 +165,13 @@ impl<'d, T: Instance> Rng<'d, T, Async> { } } -impl<'d, T: Instance, M: Mode> Rng<'d, T, M> { +impl<'d, M: Mode> Rng<'d, M> { fn stop(&self) { - T::regs().tasks_stop().write_value(1) + self.r.tasks_stop().write_value(1) } fn start(&self) { - T::regs().tasks_start().write_value(1) + self.r.tasks_start().write_value(1) } /// Enable or disable the RNG's bias correction. @@ -177,7 +181,7 @@ impl<'d, T: Instance, M: Mode> Rng<'d, T, M> { /// /// Defaults to disabled. pub fn set_bias_correction(&self, enable: bool) { - T::regs().config().write(|w| w.set_dercen(enable)) + self.r.config().write(|w| w.set_dercen(enable)) } /// Fill the buffer with random bytes, blocking version. @@ -185,7 +189,7 @@ impl<'d, T: Instance, M: Mode> Rng<'d, T, M> { self.start(); for byte in dest.iter_mut() { - let regs = T::regs(); + let regs = self.r; while regs.events_valrdy().read() == 0 {} regs.events_valrdy().write_value(0); *byte = regs.value().read().value(); @@ -210,18 +214,18 @@ impl<'d, T: Instance, M: Mode> Rng<'d, T, M> { } } -impl<'d, T: Instance, M: Mode> Drop for Rng<'d, T, M> { +impl<'d, M: Mode> Drop for Rng<'d, M> { fn drop(&mut self) { self.stop(); critical_section::with(|cs| { - let mut state = T::state().borrow_mut(cs); + let mut state = self.state.borrow_mut(cs); state.ptr = ptr::null_mut(); state.end = ptr::null_mut(); }); } } -impl<'d, T: Instance, M: Mode> rand_core_06::RngCore for Rng<'d, T, M> { +impl<'d, M: Mode> rand_core_06::RngCore for Rng<'d, M> { fn fill_bytes(&mut self, dest: &mut [u8]) { self.blocking_fill_bytes(dest); } @@ -237,9 +241,9 @@ impl<'d, T: Instance, M: Mode> rand_core_06::RngCore for Rng<'d, T, M> { } } -impl<'d, T: Instance, M: Mode> rand_core_06::CryptoRng for Rng<'d, T, M> {} +impl<'d, M: Mode> rand_core_06::CryptoRng for Rng<'d, M> {} -impl<'d, T: Instance, M: Mode> rand_core_09::RngCore for Rng<'d, T, M> { +impl<'d, M: Mode> rand_core_09::RngCore for Rng<'d, M> { fn fill_bytes(&mut self, dest: &mut [u8]) { self.blocking_fill_bytes(dest); } @@ -251,7 +255,7 @@ impl<'d, T: Instance, M: Mode> rand_core_09::RngCore for Rng<'d, T, M> { } } -impl<'d, T: Instance, M: Mode> rand_core_09::CryptoRng for Rng<'d, T, M> {} +impl<'d, M: Mode> rand_core_09::CryptoRng for Rng<'d, M> {} /// Peripheral static state pub(crate) struct State { -- cgit From efe5b18f5d050bf19a5031f683ac24e23aad6746 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 28 Sep 2025 21:03:12 +0200 Subject: nrf/rtc: erase instance generic --- embassy-nrf/src/rtc.rs | 67 +++++++++++++++++++++++----------------- examples/nrf52840/src/bin/rtc.rs | 3 +- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/embassy-nrf/src/rtc.rs b/embassy-nrf/src/rtc.rs index 1a90d1e24..652de511b 100644 --- a/embassy-nrf/src/rtc.rs +++ b/embassy-nrf/src/rtc.rs @@ -2,10 +2,13 @@ #![macro_use] +use core::marker::PhantomData; + +use embassy_hal_internal::interrupt::InterruptExt; use embassy_hal_internal::{Peri, PeripheralType}; -use crate::chip::interrupt::typelevel::Interrupt as _; -use crate::pac; +use crate::interrupt::typelevel::Interrupt as _; +use crate::{interrupt, pac}; /// Prescaler has an invalid value which exceeds 12 bits. #[derive(Debug, PartialEq, Eq)] @@ -88,23 +91,31 @@ macro_rules! impl_rtc { } /// nRF RTC driver. -pub struct Rtc<'d, T: Instance>(Peri<'d, T>); +pub struct Rtc<'d> { + r: pac::rtc::Rtc, + irq: interrupt::Interrupt, + _phantom: PhantomData<&'d ()>, +} -impl<'d, T: Instance> Rtc<'d, T> { +impl<'d> Rtc<'d> { /// Create a new `Rtc` driver. /// /// fRTC \[Hz\] = 32_768 / (`prescaler` + 1 ) - pub fn new(rtc: Peri<'d, T>, prescaler: u32) -> Result { + pub fn new(_rtc: Peri<'d, T>, prescaler: u32) -> Result { if prescaler >= (1 << 12) { return Err(PrescalerOutOfRangeError(prescaler)); } T::regs().prescaler().write(|w| w.set_prescaler(prescaler as u16)); - Ok(Self(rtc)) + Ok(Self { + r: T::regs(), + irq: T::Interrupt::IRQ, + _phantom: PhantomData, + }) } /// Create a new `Rtc` driver, configuring it to run at the given frequency. - pub fn new_for_freq(rtc: Peri<'d, T>, freq_hz: u32) -> Result { + pub fn new_for_freq(rtc: Peri<'d, T>, freq_hz: u32) -> Result { let prescaler = (32_768 / freq_hz).saturating_sub(1); Self::new(rtc, prescaler) } @@ -115,34 +126,38 @@ impl<'d, T: Instance> Rtc<'d, T> { /// /// Potentially allows to create multiple instances of the driver for the same peripheral /// which can lead to undefined behavior. - pub unsafe fn steal() -> Self { - Self(unsafe { T::steal() }) + pub unsafe fn steal() -> Self { + Self { + r: T::regs(), + irq: T::Interrupt::IRQ, + _phantom: PhantomData, + } } /// Direct access to the RTC registers. #[cfg(feature = "unstable-pac")] #[inline] pub fn regs(&mut self) -> pac::rtc::Rtc { - T::regs() + self.r } /// Enable the RTC. #[inline] pub fn enable(&mut self) { - T::regs().tasks_start().write_value(1); + self.r.tasks_start().write_value(1); } /// Disable the RTC. #[inline] pub fn disable(&mut self) { - T::regs().tasks_stop().write_value(1); + self.r.tasks_stop().write_value(1); } /// Enables interrupts for the given [Interrupt] source. /// /// Optionally also enables the interrupt in the NVIC. pub fn enable_interrupt(&mut self, int: Interrupt, enable_in_nvic: bool) { - let regs = T::regs(); + let regs = self.r; match int { Interrupt::Tick => regs.intenset().write(|w| w.set_tick(true)), Interrupt::Overflow => regs.intenset().write(|w| w.set_ovrflw(true)), @@ -152,7 +167,7 @@ impl<'d, T: Instance> Rtc<'d, T> { Interrupt::Compare3 => regs.intenset().write(|w| w.set_compare(3, true)), } if enable_in_nvic { - unsafe { T::Interrupt::enable() }; + unsafe { self.irq.enable() }; } } @@ -160,7 +175,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// /// Optionally also disables the interrupt in the NVIC. pub fn disable_interrupt(&mut self, int: Interrupt, disable_in_nvic: bool) { - let regs = T::regs(); + let regs = self.r; match int { Interrupt::Tick => regs.intenclr().write(|w| w.set_tick(true)), Interrupt::Overflow => regs.intenclr().write(|w| w.set_ovrflw(true)), @@ -170,13 +185,13 @@ impl<'d, T: Instance> Rtc<'d, T> { Interrupt::Compare3 => regs.intenclr().write(|w| w.set_compare(3, true)), } if disable_in_nvic { - T::Interrupt::disable(); + self.irq.disable(); } } /// Enable the generation of a hardware event from a given stimulus. pub fn enable_event(&mut self, evt: Interrupt) { - let regs = T::regs(); + let regs = self.r; match evt { Interrupt::Tick => regs.evtenset().write(|w| w.set_tick(true)), Interrupt::Overflow => regs.evtenset().write(|w| w.set_ovrflw(true)), @@ -189,7 +204,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// Disable the generation of a hardware event from a given stimulus. pub fn disable_event(&mut self, evt: Interrupt) { - let regs = T::regs(); + let regs = self.r; match evt { Interrupt::Tick => regs.evtenclr().write(|w| w.set_tick(true)), Interrupt::Overflow => regs.evtenclr().write(|w| w.set_ovrflw(true)), @@ -202,7 +217,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// Resets the given event. pub fn reset_event(&mut self, evt: Interrupt) { - let regs = T::regs(); + let regs = self.r; match evt { Interrupt::Tick => regs.events_tick().write_value(0), Interrupt::Overflow => regs.events_ovrflw().write_value(0), @@ -215,7 +230,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// Checks if the given event has been triggered. pub fn is_event_triggered(&self, evt: Interrupt) -> bool { - let regs = T::regs(); + let regs = self.r; let val = match evt { Interrupt::Tick => regs.events_tick().read(), Interrupt::Overflow => regs.events_ovrflw().read(), @@ -241,25 +256,19 @@ impl<'d, T: Instance> Rtc<'d, T> { CompareChannel::_3 => 3, }; - T::regs().cc(reg).write(|w| w.set_compare(val)); + self.r.cc(reg).write(|w| w.set_compare(val)); Ok(()) } /// Clear the Real Time Counter. #[inline] pub fn clear(&self) { - T::regs().tasks_clear().write_value(1); + self.r.tasks_clear().write_value(1); } /// Obtain the current value of the Real Time Counter, 24 bits of range. #[inline] pub fn read(&self) -> u32 { - T::regs().counter().read().counter() - } - - /// Relase the RTC, returning the underlying peripheral instance. - #[inline] - pub fn release(self) -> Peri<'d, T> { - self.0 + self.r.counter().read().counter() } } diff --git a/examples/nrf52840/src/bin/rtc.rs b/examples/nrf52840/src/bin/rtc.rs index a3df7da14..9d475df7f 100644 --- a/examples/nrf52840/src/bin/rtc.rs +++ b/examples/nrf52840/src/bin/rtc.rs @@ -14,8 +14,7 @@ use {defmt_rtt as _, panic_probe as _}; // 64 bit counter which will never overflow. static TICK_COUNTER: AtomicU64 = AtomicU64::new(0); -static RTC: Mutex>>> = - Mutex::new(RefCell::new(None)); +static RTC: Mutex>>> = Mutex::new(RefCell::new(None)); #[embassy_executor::main] async fn main(_spawner: Spawner) { -- cgit From 2b7e27db4ae8b0a39606490b17dbad562d64f1bd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 28 Sep 2025 21:03:59 +0200 Subject: nrf/wdt: erase instance generic --- embassy-nrf/src/wdt.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/embassy-nrf/src/wdt.rs b/embassy-nrf/src/wdt.rs index 7ab9adc29..dc99a16f5 100644 --- a/embassy-nrf/src/wdt.rs +++ b/embassy-nrf/src/wdt.rs @@ -66,11 +66,11 @@ impl Default for Config { } /// Watchdog driver. -pub struct Watchdog { - _wdt: Peri<'static, T>, +pub struct Watchdog { + r: pac::wdt::Wdt, } -impl Watchdog { +impl Watchdog { /// Try to create a new watchdog driver. /// /// This function will return an error if the watchdog is already active @@ -79,7 +79,7 @@ impl Watchdog { /// /// `N` must be between 1 and 8, inclusive. #[inline] - pub fn try_new( + pub fn try_new( wdt: Peri<'static, T>, config: Config, ) -> Result<(Self, [WatchdogHandle; N]), Peri<'static, T>> { @@ -116,7 +116,7 @@ impl Watchdog { r.tasks_start().write_value(1); } - let this = Self { _wdt: wdt }; + let this = Self { r: T::REGS }; let mut handles = [const { WatchdogHandle { index: 0 } }; N]; for i in 0..N { @@ -135,7 +135,7 @@ impl Watchdog { /// interrupt has been enabled. #[inline(always)] pub fn enable_interrupt(&mut self) { - T::REGS.intenset().write(|w| w.set_timeout(true)); + self.r.intenset().write(|w| w.set_timeout(true)); } /// Disable the watchdog interrupt. @@ -143,7 +143,7 @@ impl Watchdog { /// NOTE: This has no effect on the reset caused by the Watchdog. #[inline(always)] pub fn disable_interrupt(&mut self) { - T::REGS.intenclr().write(|w| w.set_timeout(true)); + self.r.intenclr().write(|w| w.set_timeout(true)); } /// Is the watchdog still awaiting pets from any handle? @@ -152,9 +152,8 @@ impl Watchdog { /// handles to prevent a reset this time period. #[inline(always)] pub fn awaiting_pets(&self) -> bool { - let r = T::REGS; - let enabled = r.rren().read().0; - let status = r.reqstatus().read().0; + let enabled = self.r.rren().read().0; + let status = self.r.reqstatus().read().0; (status & enabled) == 0 } } -- cgit From 6d9bf03a3becbb2ffd35a6f42a03da4e383dc46e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 28 Sep 2025 22:11:27 +0200 Subject: nrf/i2s: erase instance generic --- embassy-nrf/src/i2s.rs | 176 +++++++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 78 deletions(-) diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 53de8deee..1bfa18491 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -381,7 +381,7 @@ pub struct InterruptHandler { impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - let device = Device::::new(); + let device = Device::new(T::regs()); let s = T::state(); if device.is_tx_ptr_updated() { @@ -405,8 +405,9 @@ impl interrupt::typelevel::Handler for InterruptHandl } /// I2S driver. -pub struct I2S<'d, T: Instance> { - i2s: Peri<'d, T>, +pub struct I2S<'d> { + r: pac::i2s::I2s, + state: &'static State, mck: Option>, sck: Peri<'d, AnyPin>, lrck: Peri<'d, AnyPin>, @@ -416,10 +417,10 @@ pub struct I2S<'d, T: Instance> { config: Config, } -impl<'d, T: Instance> I2S<'d, T> { +impl<'d> I2S<'d> { /// Create a new I2S in master mode - pub fn new_master( - i2s: Peri<'d, T>, + pub fn new_master( + _i2s: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, mck: Peri<'d, impl GpioPin>, sck: Peri<'d, impl GpioPin>, @@ -427,8 +428,12 @@ impl<'d, T: Instance> I2S<'d, T> { master_clock: MasterClock, config: Config, ) -> Self { + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + Self { - i2s, + r: T::regs(), + state: T::state(), mck: Some(mck.into()), sck: sck.into(), lrck: lrck.into(), @@ -440,15 +445,19 @@ impl<'d, T: Instance> I2S<'d, T> { } /// Create a new I2S in slave mode - pub fn new_slave( - i2s: Peri<'d, T>, + pub fn new_slave( + _i2s: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, sck: Peri<'d, impl GpioPin>, lrck: Peri<'d, impl GpioPin>, config: Config, ) -> Self { + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + Self { - i2s, + r: T::regs(), + state: T::state(), mck: None, sck: sck.into(), lrck: lrck.into(), @@ -464,10 +473,13 @@ impl<'d, T: Instance> I2S<'d, T> { mut self, sdout: Peri<'d, impl GpioPin>, buffers: MultiBuffering, - ) -> OutputStream<'d, T, S, NB, NS> { + ) -> OutputStream<'d, S, NB, NS> { self.sdout = Some(sdout.into()); + let p = self.build(); OutputStream { - _p: self.build(), + r: p.0, + state: p.1, + _phantom: PhantomData, buffers, } } @@ -477,11 +489,14 @@ impl<'d, T: Instance> I2S<'d, T> { mut self, sdin: Peri<'d, impl GpioPin>, buffers: MultiBuffering, - ) -> InputStream<'d, T, S, NB, NS> { + ) -> InputStream<'d, S, NB, NS> { self.sdin = Some(sdin.into()); + let p = self.build(); InputStream { - _p: self.build(), + r: p.0, + state: p.1, buffers, + _phantom: PhantomData, } } @@ -492,30 +507,33 @@ impl<'d, T: Instance> I2S<'d, T> { sdout: Peri<'d, impl GpioPin>, buffers_out: MultiBuffering, buffers_in: MultiBuffering, - ) -> FullDuplexStream<'d, T, S, NB, NS> { + ) -> FullDuplexStream<'d, S, NB, NS> { self.sdout = Some(sdout.into()); self.sdin = Some(sdin.into()); + let p = self.build(); FullDuplexStream { - _p: self.build(), + r: p.0, + state: p.1, + _phantom: PhantomData, buffers_out, buffers_in, } } - fn build(self) -> Peri<'d, T> { + fn build(self) -> (pac::i2s::I2s, &'static State) { self.apply_config(); self.select_pins(); self.setup_interrupt(); - let device = Device::::new(); + let device = Device::new(self.r); device.enable(); - self.i2s + (self.r, self.state) } fn apply_config(&self) { - let c = T::regs().config(); + let c = self.r.config(); match &self.master_clock { Some(MasterClock { freq, ratio }) => { c.mode().write(|w| w.set_mode(vals::Mode::MASTER)); @@ -535,7 +553,7 @@ impl<'d, T: Instance> I2S<'d, T> { } fn select_pins(&self) { - let psel = T::regs().psel(); + let psel = self.r.psel(); psel.mck().write_value(self.mck.psel_bits()); psel.sck().write_value(self.sck.psel_bits()); psel.lrck().write_value(self.lrck.psel_bits()); @@ -544,10 +562,9 @@ impl<'d, T: Instance> I2S<'d, T> { } fn setup_interrupt(&self) { - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; + // Interrupt is already set up in constructor - let device = Device::::new(); + let device = Device::new(self.r); device.disable_tx_ptr_interrupt(); device.disable_rx_ptr_interrupt(); device.disable_stopped_interrupt(); @@ -561,16 +578,16 @@ impl<'d, T: Instance> I2S<'d, T> { device.enable_stopped_interrupt(); } - async fn stop() { + async fn stop(r: pac::i2s::I2s, state: &State) { compiler_fence(Ordering::SeqCst); - let device = Device::::new(); + let device = Device::new(r); device.stop(); - T::state().started.store(false, Ordering::Relaxed); + state.started.store(false, Ordering::Relaxed); poll_fn(|cx| { - T::state().stop_waker.register(cx.waker()); + state.stop_waker.register(cx.waker()); if device.is_stopped() { trace!("STOP: Ready"); @@ -586,7 +603,7 @@ impl<'d, T: Instance> I2S<'d, T> { device.disable(); } - async fn send_from_ram(buffer_ptr: *const [S]) -> Result<(), Error> + async fn send_from_ram(r: pac::i2s::I2s, state: &State, buffer_ptr: *const [S]) -> Result<(), Error> where S: Sample, { @@ -596,22 +613,22 @@ impl<'d, T: Instance> I2S<'d, T> { compiler_fence(Ordering::SeqCst); - let device = Device::::new(); + let device = Device::new(r); device.update_tx(buffer_ptr)?; - Self::wait_tx_ptr_update().await; + Self::wait_tx_ptr_update(r, state).await; compiler_fence(Ordering::SeqCst); Ok(()) } - async fn wait_tx_ptr_update() { + async fn wait_tx_ptr_update(r: pac::i2s::I2s, state: &State) { let drop = OnDrop::new(move || { trace!("TX DROP: Stopping"); - let device = Device::::new(); + let device = Device::new(r); device.disable_tx_ptr_interrupt(); device.reset_tx_ptr_event(); device.disable_tx(); @@ -623,9 +640,9 @@ impl<'d, T: Instance> I2S<'d, T> { }); poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); + state.tx_waker.register(cx.waker()); - let device = Device::::new(); + let device = Device::new(r); if device.is_tx_ptr_updated() { trace!("TX POLL: Ready"); device.reset_tx_ptr_event(); @@ -641,7 +658,7 @@ impl<'d, T: Instance> I2S<'d, T> { drop.defuse(); } - async fn receive_from_ram(buffer_ptr: *mut [S]) -> Result<(), Error> + async fn receive_from_ram(r: pac::i2s::I2s, state: &State, buffer_ptr: *mut [S]) -> Result<(), Error> where S: Sample, { @@ -652,22 +669,22 @@ impl<'d, T: Instance> I2S<'d, T> { compiler_fence(Ordering::SeqCst); - let device = Device::::new(); + let device = Device::new(r); device.update_rx(buffer_ptr)?; - Self::wait_rx_ptr_update().await; + Self::wait_rx_ptr_update(r, state).await; compiler_fence(Ordering::SeqCst); Ok(()) } - async fn wait_rx_ptr_update() { + async fn wait_rx_ptr_update(r: pac::i2s::I2s, state: &State) { let drop = OnDrop::new(move || { trace!("RX DROP: Stopping"); - let device = Device::::new(); + let device = Device::new(r); device.disable_rx_ptr_interrupt(); device.reset_rx_ptr_event(); device.disable_rx(); @@ -679,9 +696,9 @@ impl<'d, T: Instance> I2S<'d, T> { }); poll_fn(|cx| { - T::state().rx_waker.register(cx.waker()); + state.rx_waker.register(cx.waker()); - let device = Device::::new(); + let device = Device::new(r); if device.is_rx_ptr_updated() { trace!("RX POLL: Ready"); device.reset_rx_ptr_event(); @@ -699,12 +716,14 @@ impl<'d, T: Instance> I2S<'d, T> { } /// I2S output -pub struct OutputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { - _p: Peri<'d, T>, +pub struct OutputStream<'d, S: Sample, const NB: usize, const NS: usize> { + r: pac::i2s::I2s, + state: &'static State, buffers: MultiBuffering, + _phantom: PhantomData<&'d ()>, } -impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> OutputStream<'d, T, S, NB, NS> { +impl<'d, S: Sample, const NB: usize, const NS: usize> OutputStream<'d, S, NB, NS> { /// Get a mutable reference to the current buffer. pub fn buffer(&mut self) -> &mut [S] { self.buffers.get_mut() @@ -715,10 +734,9 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> OutputStream< where S: Sample, { - let device = Device::::new(); + let device = Device::new(self.r); - let s = T::state(); - if s.started.load(Ordering::Relaxed) { + if self.state.started.load(Ordering::Relaxed) { self.stop().await; } @@ -727,11 +745,11 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> OutputStream< device.update_tx(self.buffers.switch())?; - s.started.store(true, Ordering::Relaxed); + self.state.started.store(true, Ordering::Relaxed); device.start(); - I2S::::wait_tx_ptr_update().await; + I2S::wait_tx_ptr_update(self.r, self.state).await; Ok(()) } @@ -739,7 +757,7 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> OutputStream< /// Stops the I2S transfer and waits until it has stopped. #[inline(always)] pub async fn stop(&self) { - I2S::::stop().await + I2S::stop(self.r, self.state).await } /// Sends the current buffer for transmission in the DMA. @@ -748,17 +766,19 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> OutputStream< where S: Sample, { - I2S::::send_from_ram(self.buffers.switch()).await + I2S::send_from_ram(self.r, self.state, self.buffers.switch()).await } } /// I2S input -pub struct InputStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { - _p: Peri<'d, T>, +pub struct InputStream<'d, S: Sample, const NB: usize, const NS: usize> { + r: pac::i2s::I2s, + state: &'static State, buffers: MultiBuffering, + _phantom: PhantomData<&'d ()>, } -impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> InputStream<'d, T, S, NB, NS> { +impl<'d, S: Sample, const NB: usize, const NS: usize> InputStream<'d, S, NB, NS> { /// Get a mutable reference to the current buffer. pub fn buffer(&mut self) -> &mut [S] { self.buffers.get_mut() @@ -769,10 +789,9 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> InputStream<' where S: Sample, { - let device = Device::::new(); + let device = Device::new(self.r); - let s = T::state(); - if s.started.load(Ordering::Relaxed) { + if self.state.started.load(Ordering::Relaxed) { self.stop().await; } @@ -781,11 +800,11 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> InputStream<' device.update_rx(self.buffers.switch())?; - s.started.store(true, Ordering::Relaxed); + self.state.started.store(true, Ordering::Relaxed); device.start(); - I2S::::wait_rx_ptr_update().await; + I2S::wait_rx_ptr_update(self.r, self.state).await; Ok(()) } @@ -793,7 +812,7 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> InputStream<' /// Stops the I2S transfer and waits until it has stopped. #[inline(always)] pub async fn stop(&self) { - I2S::::stop().await + I2S::stop(self.r, self.state).await } /// Sets the current buffer for reception from the DMA. @@ -803,18 +822,20 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> InputStream<' where S: Sample, { - I2S::::receive_from_ram(self.buffers.switch_mut()).await + I2S::receive_from_ram(self.r, self.state, self.buffers.switch_mut()).await } } /// I2S full duplex stream (input & output) -pub struct FullDuplexStream<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> { - _p: Peri<'d, T>, +pub struct FullDuplexStream<'d, S: Sample, const NB: usize, const NS: usize> { + r: pac::i2s::I2s, + state: &'static State, buffers_out: MultiBuffering, buffers_in: MultiBuffering, + _phantom: PhantomData<&'d ()>, } -impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> FullDuplexStream<'d, T, S, NB, NS> { +impl<'d, S: Sample, const NB: usize, const NS: usize> FullDuplexStream<'d, S, NB, NS> { /// Get the current output and input buffers. pub fn buffers(&mut self) -> (&mut [S], &[S]) { (self.buffers_out.get_mut(), self.buffers_in.get()) @@ -825,10 +846,9 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> FullDuplexStr where S: Sample, { - let device = Device::::new(); + let device = Device::new(self.r); - let s = T::state(); - if s.started.load(Ordering::Relaxed) { + if self.state.started.load(Ordering::Relaxed) { self.stop().await; } @@ -839,12 +859,12 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> FullDuplexStr device.update_tx(self.buffers_out.switch())?; device.update_rx(self.buffers_in.switch_mut())?; - s.started.store(true, Ordering::Relaxed); + self.state.started.store(true, Ordering::Relaxed); device.start(); - I2S::::wait_tx_ptr_update().await; - I2S::::wait_rx_ptr_update().await; + I2S::wait_tx_ptr_update(self.r, self.state).await; + I2S::wait_rx_ptr_update(self.r, self.state).await; Ok(()) } @@ -852,7 +872,7 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> FullDuplexStr /// Stops the I2S transfer and waits until it has stopped. #[inline(always)] pub async fn stop(&self) { - I2S::::stop().await + I2S::stop(self.r, self.state).await } /// Sets the current buffers for output and input for transmission/reception from the DMA. @@ -861,18 +881,18 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> FullDuplexStr where S: Sample, { - I2S::::send_from_ram(self.buffers_out.switch()).await?; - I2S::::receive_from_ram(self.buffers_in.switch_mut()).await?; + I2S::send_from_ram(self.r, self.state, self.buffers_out.switch()).await?; + I2S::receive_from_ram(self.r, self.state, self.buffers_in.switch_mut()).await?; Ok(()) } } /// Helper encapsulating common I2S device operations. -struct Device(pac::i2s::I2s, PhantomData); +struct Device(pac::i2s::I2s); -impl Device { - fn new() -> Self { - Self(T::regs(), PhantomData) +impl Device { + fn new(r: pac::i2s::I2s) -> Self { + Self(r) } #[inline(always)] -- cgit From b2727640ed5ff514ca2bbaacc058bfa546767e4d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 28 Sep 2025 22:25:25 +0200 Subject: nrf/radio: erase instance generic --- embassy-nrf/src/embassy_net_802154_driver.rs | 8 ++-- embassy-nrf/src/radio/ieee802154.rs | 58 +++++++++++++++------------- examples/nrf52840/src/bin/sixlowpan.rs | 2 +- 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/embassy-nrf/src/embassy_net_802154_driver.rs b/embassy-nrf/src/embassy_net_802154_driver.rs index 8662be787..b3fc5df2c 100644 --- a/embassy-nrf/src/embassy_net_802154_driver.rs +++ b/embassy-nrf/src/embassy_net_802154_driver.rs @@ -32,12 +32,12 @@ impl State { /// Background runner for the driver. /// /// You must call `.run()` in a background task for the driver to operate. -pub struct Runner<'d, T: nrf::radio::Instance> { - radio: nrf::radio::ieee802154::Radio<'d, T>, +pub struct Runner<'d> { + radio: nrf::radio::ieee802154::Radio<'d>, ch: ch::Runner<'d, MTU>, } -impl<'d, T: nrf::radio::Instance> Runner<'d, T> { +impl<'d> Runner<'d> { /// Drives the radio. Needs to run to use the driver. pub async fn run(mut self) -> ! { let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); @@ -84,7 +84,7 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, T: nrf::radio::Instan radio: nrf::Peri<'a, T>, irq: Irq, state: &'a mut State, -) -> Result<(Device<'a>, Runner<'a, T>), ()> +) -> Result<(Device<'a>, Runner<'a>), ()> where Irq: interrupt::typelevel::Binding> + 'a, { diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs index 7f4f8f462..62af03a5a 100644 --- a/embassy-nrf/src/radio/ieee802154.rs +++ b/embassy-nrf/src/radio/ieee802154.rs @@ -1,15 +1,17 @@ //! IEEE 802.15.4 radio driver +use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_hal_internal::drop::OnDrop; -use super::{Error, Instance, InterruptHandler, TxPower}; +use super::{Error, InterruptHandler, TxPower}; use crate::interrupt::typelevel::Interrupt; use crate::interrupt::{self}; use crate::pac::radio::vals; pub use crate::pac::radio::vals::State as RadioState; +use crate::radio::Instance; use crate::Peri; /// Default (IEEE compliant) Start of Frame Delimiter @@ -32,18 +34,20 @@ pub enum Cca { } /// IEEE 802.15.4 radio driver. -pub struct Radio<'d, T: Instance> { - _p: Peri<'d, T>, +pub struct Radio<'d> { + r: crate::pac::radio::Radio, + state: &'static crate::radio::State, needs_enable: bool, + phantom: PhantomData<&'d ()>, } -impl<'d, T: Instance> Radio<'d, T> { +impl<'d> Radio<'d> { /// Create a new IEEE 802.15.4 radio driver. - pub fn new( - radio: Peri<'d, T>, + pub fn new( + _radio: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, ) -> Self { - let r = T::regs(); + let r = crate::pac::RADIO; // Disable and enable to reset peripheral r.power().write(|w| w.set_power(false)); @@ -89,12 +93,14 @@ impl<'d, T: Instance> Radio<'d, T> { }); // Enable NVIC interrupt - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; + crate::interrupt::typelevel::RADIO::unpend(); + unsafe { crate::interrupt::typelevel::RADIO::enable() }; let mut radio = Self { - _p: radio, + r: crate::pac::RADIO, + state: T::state(), needs_enable: false, + phantom: PhantomData, }; radio.set_sfd(DEFAULT_SFD); @@ -107,7 +113,7 @@ impl<'d, T: Instance> Radio<'d, T> { /// Changes the radio channel pub fn set_channel(&mut self, channel: u8) { - let r = T::regs(); + let r = self.r; if channel < 11 || channel > 26 { panic!("Bad 802.15.4 channel"); } @@ -121,7 +127,7 @@ impl<'d, T: Instance> Radio<'d, T> { /// Changes the Clear Channel Assessment method pub fn set_cca(&mut self, cca: Cca) { - let r = T::regs(); + let r = self.r; self.needs_enable = true; match cca { Cca::CarrierSense => r.ccactrl().write(|w| w.set_ccamode(vals::Ccamode::CARRIER_MODE)), @@ -138,19 +144,19 @@ impl<'d, T: Instance> Radio<'d, T> { /// Changes the Start of Frame Delimiter (SFD) pub fn set_sfd(&mut self, sfd: u8) { - let r = T::regs(); + let r = self.r; r.sfd().write(|w| w.set_sfd(sfd)); } /// Clear interrupts pub fn clear_all_interrupts(&mut self) { - let r = T::regs(); + let r = self.r; r.intenclr().write(|w| w.0 = 0xffff_ffff); } /// Changes the radio transmission power pub fn set_transmission_power(&mut self, power: i8) { - let r = T::regs(); + let r = self.r; self.needs_enable = true; let tx_power: TxPower = match power { @@ -201,12 +207,12 @@ impl<'d, T: Instance> Radio<'d, T> { /// Get the current radio state fn state(&self) -> RadioState { - T::regs().state().read().state() + self.r.state().read().state() } /// Moves the radio from any state to the DISABLED state fn disable(&mut self) { - let r = T::regs(); + let r = self.r; // See figure 110 in nRF52840-PS loop { match self.state() { @@ -238,15 +244,15 @@ impl<'d, T: Instance> Radio<'d, T> { } fn set_buffer(&mut self, buffer: &[u8]) { - let r = T::regs(); + let r = self.r; r.packetptr().write_value(buffer.as_ptr() as u32); } /// Moves the radio to the RXIDLE state fn receive_prepare(&mut self) { // clear related events - T::regs().events_ccabusy().write_value(0); - T::regs().events_phyend().write_value(0); + self.r.events_ccabusy().write_value(0); + self.r.events_phyend().write_value(0); // NOTE to avoid errata 204 (see rev1 v1.4) we do TX_IDLE -> DISABLED -> RXIDLE let disable = match self.state() { RadioState::DISABLED => false, @@ -263,7 +269,7 @@ impl<'d, T: Instance> Radio<'d, T> { fn receive_start(&mut self, packet: &mut Packet) { // NOTE we do NOT check the address of `packet` because the mutable reference ensures it's // allocated in RAM - let r = T::regs(); + let r = self.r; self.receive_prepare(); @@ -290,7 +296,7 @@ impl<'d, T: Instance> Radio<'d, T> { /// Cancel receiving packet fn receive_cancel() { - let r = T::regs(); + let r = crate::pac::RADIO; r.shorts().write(|_| {}); r.tasks_stop().write_value(1); loop { @@ -309,8 +315,8 @@ impl<'d, T: Instance> Radio<'d, T> { /// validated by the hardware; otherwise it returns the `Err` variant. In either case, `packet` /// will be updated with the received packet's data pub async fn receive(&mut self, packet: &mut Packet) -> Result<(), Error> { - let s = T::state(); - let r = T::regs(); + let s = self.state; + let r = self.r; // Start the read self.receive_start(packet); @@ -356,8 +362,8 @@ impl<'d, T: Instance> Radio<'d, T> { // NOTE we do NOT check the address of `packet` because the mutable reference ensures it's // allocated in RAM pub async fn try_send(&mut self, packet: &mut Packet) -> Result<(), Error> { - let s = T::state(); - let r = T::regs(); + let s = self.state; + let r = self.r; // enable radio to perform cca self.receive_prepare(); diff --git a/examples/nrf52840/src/bin/sixlowpan.rs b/examples/nrf52840/src/bin/sixlowpan.rs index 00a597366..12e385e01 100644 --- a/examples/nrf52840/src/bin/sixlowpan.rs +++ b/examples/nrf52840/src/bin/sixlowpan.rs @@ -21,7 +21,7 @@ bind_interrupts!(struct Irqs { }); #[embassy_executor::task] -async fn ieee802154_task(runner: net::Runner<'static, peripherals::RADIO>) -> ! { +async fn ieee802154_task(runner: net::Runner<'static>) -> ! { runner.run().await } -- cgit From 864eaef3a3f7678bcc4ff20b5180b1ce50332fce Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 28 Sep 2025 22:43:10 +0200 Subject: nrf/usb: erase instance generics --- embassy-nrf/CHANGELOG.md | 2 +- embassy-nrf/src/usb/mod.rs | 149 ++++++++++++---------- examples/nrf52840/src/bin/usb_ethernet.rs | 2 +- examples/nrf52840/src/bin/usb_serial.rs | 6 +- examples/nrf52840/src/bin/usb_serial_multitask.rs | 2 +- examples/nrf52840/src/bin/usb_serial_winusb.rs | 6 +- 6 files changed, 87 insertions(+), 80 deletions(-) diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index b8d03a1f8..21b299f36 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate -- changed: erase Instance type from Spim +- changed: Remove `T: Instance` generic params in all drivers. - changed: nrf54l: Disable glitch detection and enable DC/DC in init. - changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4 - changed: add persist() method for gpio and ppi diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs index c6970fc0f..2a32fe922 100644 --- a/embassy-nrf/src/usb/mod.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -86,17 +86,18 @@ impl interrupt::typelevel::Handler for InterruptHandl } /// USB driver. -pub struct Driver<'d, T: Instance, V: VbusDetect> { - _p: Peri<'d, T>, +pub struct Driver<'d, V: VbusDetect> { + regs: pac::usbd::Usbd, alloc_in: Allocator, alloc_out: Allocator, vbus_detect: V, + _phantom: PhantomData<&'d ()>, } -impl<'d, T: Instance, V: VbusDetect> Driver<'d, T, V> { +impl<'d, V: VbusDetect> Driver<'d, V> { /// Create a new USB driver. - pub fn new( - usb: Peri<'d, T>, + pub fn new( + _usb: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, vbus_detect: V, ) -> Self { @@ -104,19 +105,20 @@ impl<'d, T: Instance, V: VbusDetect> Driver<'d, T, V> { unsafe { T::Interrupt::enable() }; Self { - _p: usb, + regs: crate::pac::USBD, alloc_in: Allocator::new(), alloc_out: Allocator::new(), vbus_detect, + _phantom: PhantomData, } } } -impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V> { - type EndpointOut = Endpoint<'d, T, Out>; - type EndpointIn = Endpoint<'d, T, In>; - type ControlPipe = ControlPipe<'d, T>; - type Bus = Bus<'d, T, V>; +impl<'d, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, V> { + type EndpointOut = Endpoint<'d, Out>; + type EndpointIn = Endpoint<'d, In>; + type ControlPipe = ControlPipe<'d>; + type Bus = Bus<'d, V>; fn alloc_endpoint_in( &mut self, @@ -127,12 +129,15 @@ impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V ) -> Result { let index = self.alloc_in.allocate(ep_type, ep_addr)?; let ep_addr = EndpointAddress::from_parts(index, Direction::In); - Ok(Endpoint::new(EndpointInfo { - addr: ep_addr, - ep_type, - max_packet_size: packet_size, - interval_ms, - })) + Ok(Endpoint::new( + self.regs, + EndpointInfo { + addr: ep_addr, + ep_type, + max_packet_size: packet_size, + interval_ms, + }, + )) } fn alloc_endpoint_out( @@ -144,39 +149,45 @@ impl<'d, T: Instance, V: VbusDetect + 'd> driver::Driver<'d> for Driver<'d, T, V ) -> Result { let index = self.alloc_out.allocate(ep_type, ep_addr)?; let ep_addr = EndpointAddress::from_parts(index, Direction::Out); - Ok(Endpoint::new(EndpointInfo { - addr: ep_addr, - ep_type, - max_packet_size: packet_size, - interval_ms, - })) + Ok(Endpoint::new( + self.regs, + EndpointInfo { + addr: ep_addr, + ep_type, + max_packet_size: packet_size, + interval_ms, + }, + )) } fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { ( Bus { - _p: unsafe { self._p.clone_unchecked() }, + regs: self.regs, power_available: false, vbus_detect: self.vbus_detect, + _phantom: PhantomData, }, ControlPipe { - _p: self._p, + regs: self.regs, max_packet_size: control_max_packet_size, + _phantom: PhantomData, }, ) } } /// USB bus. -pub struct Bus<'d, T: Instance, V: VbusDetect> { - _p: Peri<'d, T>, +pub struct Bus<'d, V: VbusDetect> { + regs: pac::usbd::Usbd, power_available: bool, vbus_detect: V, + _phantom: PhantomData<&'d ()>, } -impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { +impl<'d, V: VbusDetect> driver::Bus for Bus<'d, V> { async fn enable(&mut self) { - let regs = T::regs(); + let regs = self.regs; errata::pre_enable(); @@ -215,14 +226,14 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { } async fn disable(&mut self) { - let regs = T::regs(); + let regs = self.regs; regs.enable().write(|x| x.set_enable(false)); } fn poll(&mut self) -> impl Future { poll_fn(|cx| { BUS_WAKER.register(cx.waker()); - let regs = T::regs(); + let regs = self.regs; if regs.events_usbreset().read() != 0 { regs.events_usbreset().write_value(0); @@ -280,7 +291,7 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { } fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { - let regs = T::regs(); + let regs = self.regs; if ep_addr.index() == 0 { if stalled { regs.tasks_ep0stall().write_value(1); @@ -298,7 +309,7 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { } fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { - let regs = T::regs(); + let regs = self.regs; let i = ep_addr.index(); match ep_addr.direction() { Direction::Out => regs.halted().epout(i).read().getstatus() == vals::Getstatus::HALTED, @@ -307,7 +318,7 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { } fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { - let regs = T::regs(); + let regs = self.regs; let i = ep_addr.index(); let mask = 1 << i; @@ -359,7 +370,7 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { #[inline] async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { - let regs = T::regs(); + let regs = self.regs; if regs.lowpower().read().lowpower() == vals::Lowpower::LOW_POWER { errata::pre_wakeup(); @@ -368,7 +379,7 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { poll_fn(|cx| { BUS_WAKER.register(cx.waker()); - let regs = T::regs(); + let regs = self.regs; let r = regs.eventcause().read(); if regs.events_usbreset().read() != 0 { @@ -441,21 +452,23 @@ impl EndpointDir for Out { } /// USB endpoint. -pub struct Endpoint<'d, T: Instance, Dir> { - _phantom: PhantomData<(&'d mut T, Dir)>, +pub struct Endpoint<'d, Dir> { + regs: pac::usbd::Usbd, info: EndpointInfo, + _phantom: PhantomData<(&'d (), Dir)>, } -impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> { - fn new(info: EndpointInfo) -> Self { +impl<'d, Dir> Endpoint<'d, Dir> { + fn new(regs: pac::usbd::Usbd, info: EndpointInfo) -> Self { Self { + regs, info, _phantom: PhantomData, } } } -impl<'d, T: Instance, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, T, Dir> { +impl<'d, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, Dir> { fn info(&self) -> &EndpointInfo { &self.info } @@ -466,14 +479,14 @@ impl<'d, T: Instance, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, T, Dir } #[allow(private_bounds)] -impl<'d, T: Instance, Dir: EndpointDir> Endpoint<'d, T, Dir> { - fn wait_enabled_state(&mut self, state: bool) -> impl Future { +impl<'d, Dir: EndpointDir> Endpoint<'d, Dir> { + fn wait_enabled_state(&mut self, state: bool) -> impl Future + use<'_, 'd, Dir> { let i = self.info.addr.index(); assert!(i != 0); poll_fn(move |cx| { Dir::waker(i).register(cx.waker()); - if Dir::is_enabled(T::regs(), i) == state { + if Dir::is_enabled(self.regs, i) == state { Poll::Ready(()) } else { Poll::Pending @@ -482,12 +495,12 @@ impl<'d, T: Instance, Dir: EndpointDir> Endpoint<'d, T, Dir> { } /// Wait for the endpoint to be disabled - pub fn wait_disabled(&mut self) -> impl Future { + pub fn wait_disabled(&mut self) -> impl Future + use<'_, 'd, Dir> { self.wait_enabled_state(false) } } -impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> { +impl<'d, Dir> Endpoint<'d, Dir> { async fn wait_data_ready(&mut self) -> Result<(), ()> where Dir: EndpointDir, @@ -497,7 +510,7 @@ impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> { poll_fn(|cx| { Dir::waker(i).register(cx.waker()); let r = READY_ENDPOINTS.load(Ordering::Acquire); - if !Dir::is_enabled(T::regs(), i) { + if !Dir::is_enabled(self.regs, i) { Poll::Ready(Err(())) } else if r & Dir::mask(i) != 0 { Poll::Ready(Ok(())) @@ -514,9 +527,7 @@ impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> { } } -unsafe fn read_dma(i: usize, buf: &mut [u8]) -> Result { - let regs = T::regs(); - +unsafe fn read_dma(regs: pac::usbd::Usbd, i: usize, buf: &mut [u8]) -> Result { // Check that the packet fits into the buffer let size = regs.size().epout(i).read().0 as usize; if size > buf.len() { @@ -539,8 +550,7 @@ unsafe fn read_dma(i: usize, buf: &mut [u8]) -> Result(i: usize, buf: &[u8]) { - let regs = T::regs(); +unsafe fn write_dma(regs: pac::usbd::Usbd, i: usize, buf: &[u8]) { assert!(buf.len() <= 64); let mut ram_buf: MaybeUninit<[u8; 64]> = MaybeUninit::uninit(); @@ -566,43 +576,44 @@ unsafe fn write_dma(i: usize, buf: &[u8]) { dma_end(); } -impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { +impl<'d> driver::EndpointOut for Endpoint<'d, Out> { async fn read(&mut self, buf: &mut [u8]) -> Result { let i = self.info.addr.index(); assert!(i != 0); self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; - unsafe { read_dma::(i, buf) } + unsafe { read_dma(self.regs, i, buf) } } } -impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { +impl<'d> driver::EndpointIn for Endpoint<'d, In> { async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { let i = self.info.addr.index(); assert!(i != 0); self.wait_data_ready().await.map_err(|_| EndpointError::Disabled)?; - unsafe { write_dma::(i, buf) } + unsafe { write_dma(self.regs, i, buf) } Ok(()) } } /// USB control pipe. -pub struct ControlPipe<'d, T: Instance> { - _p: Peri<'d, T>, +pub struct ControlPipe<'d> { + regs: pac::usbd::Usbd, max_packet_size: u16, + _phantom: PhantomData<&'d ()>, } -impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { +impl<'d> driver::ControlPipe for ControlPipe<'d> { fn max_packet_size(&self) -> usize { usize::from(self.max_packet_size) } async fn setup(&mut self) -> [u8; 8] { - let regs = T::regs(); + let regs = self.regs; // Reset shorts regs.shorts().write(|_| ()); @@ -611,7 +622,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { regs.intenset().write(|w| w.set_ep0setup(true)); poll_fn(|cx| { EP0_WAKER.register(cx.waker()); - let regs = T::regs(); + let regs = self.regs; if regs.events_ep0setup().read() != 0 { Poll::Ready(()) } else { @@ -636,7 +647,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { } async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result { - let regs = T::regs(); + let regs = self.regs; regs.events_ep0datadone().write_value(0); @@ -651,7 +662,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { }); poll_fn(|cx| { EP0_WAKER.register(cx.waker()); - let regs = T::regs(); + let regs = self.regs; if regs.events_ep0datadone().read() != 0 { Poll::Ready(Ok(())) } else if regs.events_usbreset().read() != 0 { @@ -666,17 +677,17 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { }) .await?; - unsafe { read_dma::(0, buf) } + unsafe { read_dma(self.regs, 0, buf) } } async fn data_in(&mut self, buf: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> { - let regs = T::regs(); + let regs = self.regs; regs.events_ep0datadone().write_value(0); regs.shorts().write(|w| w.set_ep0datadone_ep0status(last)); // This starts a TX on EP0. events_ep0datadone notifies when done. - unsafe { write_dma::(0, buf) } + unsafe { write_dma(self.regs, 0, buf) } regs.intenset().write(|w| { w.set_usbreset(true); @@ -687,7 +698,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { poll_fn(|cx| { cx.waker().wake_by_ref(); EP0_WAKER.register(cx.waker()); - let regs = T::regs(); + let regs = self.regs; if regs.events_ep0datadone().read() != 0 { Poll::Ready(Ok(())) } else if regs.events_usbreset().read() != 0 { @@ -704,12 +715,12 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { } async fn accept(&mut self) { - let regs = T::regs(); + let regs = self.regs; regs.tasks_ep0status().write_value(1); } async fn reject(&mut self) { - let regs = T::regs(); + let regs = self.regs; regs.tasks_ep0stall().write_value(1); } diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 87aa4c6c5..a75b967b4 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -22,7 +22,7 @@ bind_interrupts!(struct Irqs { RNG => rng::InterruptHandler; }); -type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; +type MyDriver = Driver<'static, HardwareVbusDetect>; const MTU: usize = 1514; diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 8d05df791..e7c2d0854 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -5,7 +5,7 @@ use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; -use embassy_nrf::usb::{Driver, Instance}; +use embassy_nrf::usb::Driver; use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; @@ -89,9 +89,7 @@ impl From for Disconnected { } } -async fn echo<'d, T: Instance + 'd, P: VbusDetect + 'd>( - class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, -) -> Result<(), Disconnected> { +async fn echo<'d, V: VbusDetect + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, V>>) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { let n = class.read_packet(&mut buf).await?; diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 00a91a233..b6a983854 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -17,7 +17,7 @@ bind_interrupts!(struct Irqs { CLOCK_POWER => usb::vbus_detect::InterruptHandler; }); -type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; +type MyDriver = Driver<'static, HardwareVbusDetect>; #[embassy_executor::task] async fn usb_task(mut device: UsbDevice<'static, MyDriver>) { diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index 8a20ce673..e30e08a01 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -5,7 +5,7 @@ use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; -use embassy_nrf::usb::{Driver, Instance}; +use embassy_nrf::usb::Driver; use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; @@ -108,9 +108,7 @@ impl From for Disconnected { } } -async fn echo<'d, T: Instance + 'd, P: VbusDetect + 'd>( - class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, -) -> Result<(), Disconnected> { +async fn echo<'d, V: VbusDetect + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, V>>) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { let n = class.read_packet(&mut buf).await?; -- cgit From 1fe05cfedbcfb35dba3bee3ed6b7f4f293e9bb78 Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Mon, 29 Sep 2025 21:11:09 +0900 Subject: Make the Qei struct own the channel 1 and 2 pins --- embassy-stm32/src/timer/qei.rs | 25 ++++++++++++++++++------- tests/stm32/src/bin/afio.rs | 4 ++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index 82b5968b0..d63a2b45d 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -55,20 +55,27 @@ impl SealedQeiChannel for Ch2 {} /// Quadrature decoder driver. pub struct Qei<'d, T: GeneralInstance4Channel> { inner: Timer<'d, T>, + _ch1: Peri<'d, AnyPin>, + _ch2: Peri<'d, AnyPin>, } impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { /// Create a new quadrature decoder driver. #[allow(unused)] - pub fn new<#[cfg(afio)] A>( + pub fn new( tim: Peri<'d, T>, - ch1: if_afio!(QeiPin<'d, T, Ch1, A>), - ch2: if_afio!(QeiPin<'d, T, Ch2, A>), + ch1: Peri<'d, if_afio!(impl TimerPin)>, + ch2: Peri<'d, if_afio!(impl TimerPin)>, ) -> Self { - Self::new_inner(tim) - } + // Configure the pins to be used for the QEI peripheral. + critical_section::with(|_| { + ch1.set_low(); + set_as_af!(ch1, AfType::input(Pull::None)); + + ch2.set_low(); + set_as_af!(ch2, AfType::input(Pull::None)); + }); - fn new_inner(tim: Peri<'d, T>) -> Self { let inner = Timer::new(tim); let r = inner.regs_gp16(); @@ -94,7 +101,11 @@ impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { r.arr().modify(|w| w.set_arr(u16::MAX)); r.cr1().modify(|w| w.set_cen(true)); - Self { inner } + Self { + inner, + _ch1: ch1.into(), + _ch2: ch2.into(), + } } /// Get direction. diff --git a/tests/stm32/src/bin/afio.rs b/tests/stm32/src/bin/afio.rs index cc44dc59c..356c39443 100644 --- a/tests/stm32/src/bin/afio.rs +++ b/tests/stm32/src/bin/afio.rs @@ -260,8 +260,8 @@ async fn main(_spawner: Spawner) { reset_afio_registers(); Qei::new::>( p.TIM1.reborrow(), - QeiPin::new(p.PA8.reborrow()), - QeiPin::new(p.PA9.reborrow()), + p.PA8.reborrow(), + p.PA9.reborrow(), ); defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1); } -- cgit From a791e9f1fc17eda2b05df97a72b94264ee6a4c12 Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Mon, 29 Sep 2025 21:17:15 +0900 Subject: Remove the QeiPin struct --- embassy-stm32/src/timer/qei.rs | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index d63a2b45d..25e8c3705 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -1,7 +1,5 @@ //! Quadrature decoder using a timer. -use core::marker::PhantomData; - use stm32_metapac::timer::vals; use super::low_level::Timer; @@ -19,27 +17,6 @@ pub enum Direction { Downcounting, } -/// Wrapper for using a pin with QEI. -pub struct QeiPin<'d, T, Channel, #[cfg(afio)] A> { - #[allow(unused)] - pin: Peri<'d, AnyPin>, - phantom: PhantomData, -} - -impl<'d, T: GeneralInstance4Channel, C: QeiChannel, #[cfg(afio)] A> if_afio!(QeiPin<'d, T, C, A>) { - /// Create a new QEI pin instance. - pub fn new(pin: Peri<'d, if_afio!(impl TimerPin)>) -> Self { - critical_section::with(|_| { - pin.set_low(); - set_as_af!(pin, AfType::input(Pull::None)); - }); - QeiPin { - pin: pin.into(), - phantom: PhantomData, - } - } -} - trait SealedQeiChannel: TimerChannel {} /// Marker trait for a timer channel eligible for use with QEI. -- cgit From b8f78c4f7c1f13beb4af8ef50f992f62c0ab53f6 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 29 Sep 2025 14:53:24 +0200 Subject: fix: remove instance generic for multiwrite implementation --- embassy-nrf/Cargo.toml | 1 + embassy-nrf/src/qspi.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 1af633500..e2580a36e 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -34,6 +34,7 @@ build = [ {target = "thumbv7em-none-eabi", features = ["defmt", "gpiote", "nrf52840", "time"]}, {target = "thumbv7em-none-eabi", features = ["defmt", "gpiote", "nrf52840", "time-driver-rtc1"]}, {target = "thumbv7em-none-eabi", features = ["defmt", "gpiote", "nrf52840", "time", "time-driver-rtc1"]}, + {target = "thumbv7em-none-eabi", features = ["defmt", "gpiote", "nrf52840", "time", "time-driver-rtc1","qspi-multiwrite-flash"]}, {target = "thumbv6m-none-eabi", features = ["defmt", "nrf51", "time", "time-driver-rtc1"]}, {target = "thumbv6m-none-eabi", features = ["defmt", "nrf51", "time"]}, {target = "thumbv6m-none-eabi", features = ["nrf51", "time"]}, diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 94ad3f0d6..6f4524716 100755 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -599,7 +599,7 @@ impl<'d> NorFlash for Qspi<'d> { } #[cfg(feature = "qspi-multiwrite-flash")] -impl<'d, T: Instance> embedded_storage::nor_flash::MultiwriteNorFlash for Qspi<'d, T> {} +impl<'d> embedded_storage::nor_flash::MultiwriteNorFlash for Qspi<'d> {} mod _eh1 { use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; @@ -634,7 +634,7 @@ mod _eh1 { } #[cfg(feature = "qspi-multiwrite-flash")] - impl<'d, T: Instance> embedded_storage_async::nor_flash::MultiwriteNorFlash for Qspi<'d, T> {} + impl<'d> embedded_storage_async::nor_flash::MultiwriteNorFlash for Qspi<'d> {} } /// Peripheral static state -- cgit From 165fa1debd7f8aa6131c467825477a25e1862539 Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Mon, 29 Sep 2025 22:05:27 +0900 Subject: Add a Config struct for the Qei peripheral --- embassy-stm32/src/timer/qei.rs | 65 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index 25e8c3705..2e438bc74 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -1,6 +1,6 @@ //! Quadrature decoder using a timer. -use stm32_metapac::timer::vals; +use stm32_metapac::timer::vals::{self, Sms}; use super::low_level::Timer; pub use super::{Ch1, Ch2}; @@ -9,6 +9,51 @@ use crate::gpio::{AfType, AnyPin, Pull}; use crate::timer::TimerChannel; use crate::Peri; +/// Qei driver config. +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy)] +pub struct Config { + /// Configures the internal pull up/down resistor for Qei's channel 1 pin. + pub ch1_pull: Pull, + /// Configures the internal pull up/down resistor for Qei's channel 2 pin. + pub ch2_pull: Pull, + /// Specifies the encoder mode to use for the Qei peripheral. + pub mode: QeiMode, +} + +impl Default for Config { + /// Arbitrary defaults to preserve backwards compatibility + fn default() -> Self { + Self { + ch1_pull: Pull::None, + ch2_pull: Pull::None, + mode: QeiMode::Mode3, + } + } +} + +/// See STMicro AN4013 for §2.3 for more information +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy)] +pub enum QeiMode { + /// Direct alias for [`Sms::ENCODER_MODE_1`] + Mode1, + /// Direct alias for [`Sms::ENCODER_MODE_2`] + Mode2, + /// Direct alias for [`Sms::ENCODER_MODE_3`] + Mode3, +} + +impl From for Sms { + fn from(mode: QeiMode) -> Self { + match mode { + QeiMode::Mode1 => Sms::ENCODER_MODE_1, + QeiMode::Mode2 => Sms::ENCODER_MODE_2, + QeiMode::Mode3 => Sms::ENCODER_MODE_3, + } + } +} + /// Counting direction pub enum Direction { /// Counting up. @@ -37,20 +82,30 @@ pub struct Qei<'d, T: GeneralInstance4Channel> { } impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { - /// Create a new quadrature decoder driver. + /// Create a new quadrature decoder driver, with a given [`Config`]. #[allow(unused)] pub fn new( tim: Peri<'d, T>, ch1: Peri<'d, if_afio!(impl TimerPin)>, ch2: Peri<'d, if_afio!(impl TimerPin)>, + ) -> Self { + Self::new_with_config(tim, ch1, ch2, Default::default()) + } + /// Create a new quadrature decoder driver, with a given [`Config`]. + #[allow(unused)] + pub fn new_with_config( + tim: Peri<'d, T>, + ch1: Peri<'d, if_afio!(impl TimerPin)>, + ch2: Peri<'d, if_afio!(impl TimerPin)>, + config: Config, ) -> Self { // Configure the pins to be used for the QEI peripheral. critical_section::with(|_| { ch1.set_low(); - set_as_af!(ch1, AfType::input(Pull::None)); + set_as_af!(ch1, AfType::input(config.ch1_pull)); ch2.set_low(); - set_as_af!(ch2, AfType::input(Pull::None)); + set_as_af!(ch2, AfType::input(config.ch2_pull)); }); let inner = Timer::new(tim); @@ -72,7 +127,7 @@ impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { }); r.smcr().modify(|w| { - w.set_sms(vals::Sms::ENCODER_MODE_3); + w.set_sms(config.mode.into()); }); r.arr().modify(|w| w.set_arr(u16::MAX)); -- cgit From fc34aa360a28a293211f037204317f9090b876e7 Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Mon, 29 Sep 2025 22:16:29 +0900 Subject: Update embassy-stm32/CHANGELOG.md --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 835d9c704..80261ae41 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: Add USB CRS sync support for STM32C071 - fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map. - fix: Cut down the capabilities of the STM32L412 and L422 RTC as those are missing binary timer mode and underflow interrupt. +- fix: Allow configuration of the internal pull up/down resistors on the pins for the Qei peripheral, as well as the Qei decoder mode. ## 0.4.0 - 2025-08-26 -- cgit From 6bb08523c9d93fbddc9fac41b3d33c7c4bf91520 Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Mon, 29 Sep 2025 22:22:56 +0900 Subject: Code formatting --- tests/stm32/src/bin/afio.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/stm32/src/bin/afio.rs b/tests/stm32/src/bin/afio.rs index 356c39443..feddbc802 100644 --- a/tests/stm32/src/bin/afio.rs +++ b/tests/stm32/src/bin/afio.rs @@ -258,11 +258,7 @@ async fn main(_spawner: Spawner) { { // partial remap reset_afio_registers(); - Qei::new::>( - p.TIM1.reborrow(), - p.PA8.reborrow(), - p.PA9.reborrow(), - ); + Qei::new::>(p.TIM1.reborrow(), p.PA8.reborrow(), p.PA9.reborrow()); defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1); } -- cgit From 61bc254879b328a944a36a81080abbadc6431ccf Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Mon, 29 Sep 2025 22:24:19 +0900 Subject: Remove 'new_with_config()', just use 'new()' --- embassy-stm32/src/timer/qei.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index 2e438bc74..bb152731c 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -88,15 +88,6 @@ impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { tim: Peri<'d, T>, ch1: Peri<'d, if_afio!(impl TimerPin)>, ch2: Peri<'d, if_afio!(impl TimerPin)>, - ) -> Self { - Self::new_with_config(tim, ch1, ch2, Default::default()) - } - /// Create a new quadrature decoder driver, with a given [`Config`]. - #[allow(unused)] - pub fn new_with_config( - tim: Peri<'d, T>, - ch1: Peri<'d, if_afio!(impl TimerPin)>, - ch2: Peri<'d, if_afio!(impl TimerPin)>, config: Config, ) -> Self { // Configure the pins to be used for the QEI peripheral. -- cgit From 2cd2894d33e69c8481c360026015397f0742876a Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Mon, 29 Sep 2025 22:42:59 +0900 Subject: Fix an afio test --- tests/stm32/src/bin/afio.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/stm32/src/bin/afio.rs b/tests/stm32/src/bin/afio.rs index feddbc802..81d50874b 100644 --- a/tests/stm32/src/bin/afio.rs +++ b/tests/stm32/src/bin/afio.rs @@ -12,8 +12,9 @@ use embassy_stm32::time::khz; use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; use embassy_stm32::timer::pwm_input::PwmInput; -use embassy_stm32::timer::qei::{Qei, QeiPin}; +use embassy_stm32::timer::qei::Qei; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::timer::{Ch1, Ch2}; use embassy_stm32::usart::{Uart, UartRx, UartTx}; use embassy_stm32::{bind_interrupts, Peripherals}; @@ -258,7 +259,12 @@ async fn main(_spawner: Spawner) { { // partial remap reset_afio_registers(); - Qei::new::>(p.TIM1.reborrow(), p.PA8.reborrow(), p.PA9.reborrow()); + Qei::new::>( + p.TIM1.reborrow(), + p.PA8.reborrow(), + p.PA9.reborrow(), + Default::default(), + ); defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1); } -- cgit From 8fae6f5a3f7055dc8250cd8926624010b14db6dc Mon Sep 17 00:00:00 2001 From: Michael Kefeder Date: Sun, 28 Sep 2025 18:32:40 +0200 Subject: Reset SAADC in Drop impl for nrf52, workaround for anomaly 241. --- embassy-nrf/CHANGELOG.md | 1 + embassy-nrf/src/saadc.rs | 13 ++++++ examples/nrf52810/src/bin/saadc_lowpower.rs | 62 +++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 examples/nrf52810/src/bin/saadc_lowpower.rs diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index b8d03a1f8..3ad3c8005 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - changed: impl Drop for Timer - added: expose `regs` for timer driver - added: timer driver CC `clear_events` method +- changed: Saadc reset in Drop impl, anomaly 241 - high power usage ## 0.7.0 - 2025-08-26 diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 92b6fb01f..d84246572 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -457,6 +457,19 @@ impl<'d> Saadc<'d, 1> { impl<'d, const N: usize> Drop for Saadc<'d, N> { fn drop(&mut self) { + // Reset of SAADC. + // + // This is needed when more than one pin is sampled to avoid needless power consumption. + // More information can be found in [nrf52 Anomaly 241](https://docs.nordicsemi.com/bundle/errata_nRF52810_Rev1/page/ERR/nRF52810/Rev1/latest/anomaly_810_241.html). + // The workaround seems like it copies the configuration before reset and reapplies it after. + // This method consumes the instance forcing a reconfiguration at compile time, hence we only + // call what is the reset portion of the workaround. + #[cfg(feature = "_nrf52")] + { + unsafe { core::ptr::write_volatile(0x40007FFC as *mut u32, 0) } + unsafe { core::ptr::read_volatile(0x40007FFC as *const ()) } + unsafe { core::ptr::write_volatile(0x40007FFC as *mut u32, 1) } + } let r = Self::regs(); r.enable().write(|w| w.set_enable(false)); for i in 0..N { diff --git a/examples/nrf52810/src/bin/saadc_lowpower.rs b/examples/nrf52810/src/bin/saadc_lowpower.rs new file mode 100644 index 000000000..d7e7f09a4 --- /dev/null +++ b/examples/nrf52810/src/bin/saadc_lowpower.rs @@ -0,0 +1,62 @@ +//! Run SAADC on multiple pins only every 3rd time, to show anomaly 241 workaround. +//! +//! To correctly measure the MCU current on the NRF52DK follow the instructions +//! +//! otherwise you will measure the whole board, including the segger j-link chip for example + +#![no_std] +#![no_main] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::saadc::{Oversample, Saadc}; +use embassy_nrf::{bind_interrupts, saadc}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + SAADC => saadc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_p: Spawner) { + let mut p = embassy_nrf::init(Default::default()); + + // For PPK2 digital channel plot to track when SAADC is on/off. + let mut ppk2_d0 = Output::new(p.P0_27, Level::Low, OutputDrive::Standard); + let mut num_loops: usize = 0; + loop { + num_loops += 1; + if num_loops.is_multiple_of(3) { + ppk2_d0.set_high(); + let battery_pin = p.P0_02.reborrow(); + let sensor1_pin = p.P0_03.reborrow(); + let mut adc_config = saadc::Config::default(); + adc_config.oversample = Oversample::OVER4X; + let battery = saadc::ChannelConfig::single_ended(battery_pin); + let sensor1 = saadc::ChannelConfig::single_ended(sensor1_pin); + let mut saadc = Saadc::new(p.SAADC.reborrow(), Irqs, adc_config, [battery, sensor1]); + // Indicated: wait for ADC calibration. + saadc.calibrate().await; + let mut buf = [0; 2]; + info!("sampling..."); + saadc.sample(&mut buf).await; + info!("data: {:x}", buf); + + // Sleep to show the high power usage on the plot, even though sampling is done. + Timer::after_millis(100).await; + ppk2_d0.set_low(); + // disable the following line to show the anomaly on the power profiler plot. + core::mem::drop(saadc); + // Sleep to show the power usage when drop did not happen. + Timer::after_millis(100).await; + // worst case drop happens here + } else { + info!("waiting"); + } + // Sleep for 1 second. The executor ensures the core sleeps with a WFE when it has nothing to do. + // During this sleep, the nRF chip should only use ~3uA + Timer::after_secs(1).await; + } +} -- cgit From 55ee252434834cc9548acdc7b5da8ccd09043ca1 Mon Sep 17 00:00:00 2001 From: Michael Kefeder Date: Mon, 29 Sep 2025 21:30:24 +0200 Subject: fixup: documentation more precise --- embassy-nrf/src/saadc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index d84246572..fd48faabb 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -462,7 +462,7 @@ impl<'d, const N: usize> Drop for Saadc<'d, N> { // This is needed when more than one pin is sampled to avoid needless power consumption. // More information can be found in [nrf52 Anomaly 241](https://docs.nordicsemi.com/bundle/errata_nRF52810_Rev1/page/ERR/nRF52810/Rev1/latest/anomaly_810_241.html). // The workaround seems like it copies the configuration before reset and reapplies it after. - // This method consumes the instance forcing a reconfiguration at compile time, hence we only + // The instance is dropped, forcing a reconfiguration at compile time, hence we only // call what is the reset portion of the workaround. #[cfg(feature = "_nrf52")] { -- cgit From 0ba74a4127a244781fc125208a73cc51c8a0deac Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 30 Sep 2025 10:31:33 +0200 Subject: chore: prepare crate releases --- docs/examples/basic/Cargo.toml | 2 +- embassy-boot-nrf/CHANGELOG.md | 4 ++++ embassy-boot-nrf/Cargo.toml | 4 ++-- embassy-nrf/CHANGELOG.md | 2 ++ embassy-nrf/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 4 ++-- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf51/Cargo.toml | 2 +- examples/nrf52810/Cargo.toml | 2 +- examples/nrf52840-edf/Cargo.toml | 2 +- examples/nrf52840-rtic/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/nrf54l15/Cargo.toml | 2 +- examples/nrf9151/ns/Cargo.toml | 2 +- examples/nrf9151/s/Cargo.toml | 2 +- examples/nrf9160/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- 18 files changed, 24 insertions(+), 18 deletions(-) diff --git a/docs/examples/basic/Cargo.toml b/docs/examples/basic/Cargo.toml index b6dbeda2a..b90180853 100644 --- a/docs/examples/basic/Cargo.toml +++ b/docs/examples/basic/Cargo.toml @@ -9,7 +9,7 @@ publish = false [dependencies] embassy-executor = { version = "0.9.0", path = "../../../embassy-executor", features = ["defmt", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.5.0", path = "../../../embassy-time", features = ["defmt"] } -embassy-nrf = { version = "0.7.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } +embassy-nrf = { version = "0.8.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/embassy-boot-nrf/CHANGELOG.md b/embassy-boot-nrf/CHANGELOG.md index 8cc1e73c0..54b7c8067 100644 --- a/embassy-boot-nrf/CHANGELOG.md +++ b/embassy-boot-nrf/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.9.0 - 2025-09-30 + +- Bumped embassy-nrf to 0.8.0 + ## 0.8.0 - 2025-08-26 ## 0.1.1 - 2025-08-15 diff --git a/embassy-boot-nrf/Cargo.toml b/embassy-boot-nrf/Cargo.toml index 49dff061a..466f18631 100644 --- a/embassy-boot-nrf/Cargo.toml +++ b/embassy-boot-nrf/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2021" name = "embassy-boot-nrf" -version = "0.8.0" +version = "0.9.0" description = "Bootloader lib for nRF chips" license = "MIT OR Apache-2.0" repository = "https://github.com/embassy-rs/embassy" @@ -36,7 +36,7 @@ defmt = { version = "1.0.1", optional = true } log = { version = "0.4.17", optional = true } embassy-sync = { version = "0.7.2", path = "../embassy-sync" } -embassy-nrf = { version = "0.7.0", path = "../embassy-nrf", default-features = false } +embassy-nrf = { version = "0.8.0", path = "../embassy-nrf", default-features = false } embassy-boot = { version = "0.6.1", path = "../embassy-boot" } cortex-m = { version = "0.7.6" } cortex-m-rt = { version = "0.7" } diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index bdd031480..b3d4045fa 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.8.0 - 2025-09-30 + - changed: Remove `T: Instance` generic params in all drivers. - changed: nrf54l: Disable glitch detection and enable DC/DC in init. - changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4 diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index e2580a36e..362fabcf7 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-nrf" -version = "0.7.0" +version = "0.8.0" edition = "2021" license = "MIT OR Apache-2.0" description = "Embassy Hardware Abstraction Layer (HAL) for nRF series microcontrollers" diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index b0cc63a6c..f5f89ecb5 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -9,9 +9,9 @@ publish = false embassy-sync = { version = "0.7.2", path = "../../../../embassy-sync" } embassy-executor = { version = "0.9.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.5.0", path = "../../../../embassy-time", features = [] } -embassy-nrf = { version = "0.7.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } +embassy-nrf = { version = "0.8.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } embassy-boot = { version = "0.6.1", path = "../../../../embassy-boot", features = [] } -embassy-boot-nrf = { version = "0.8.0", path = "../../../../embassy-boot-nrf", features = [] } +embassy-boot-nrf = { version = "0.9.0", path = "../../../../embassy-boot-nrf", features = [] } embassy-embedded-hal = { version = "0.5.0", path = "../../../../embassy-embedded-hal" } defmt = { version = "1.0.1", optional = true } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index c9eeaaac7..d2d0ae093 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -19,7 +19,7 @@ log = [ embassy-sync = { version = "0.7.2", path = "../../embassy-sync" } embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace"] } embassy-time = { version = "0.5.0", path = "../../embassy-time" } -embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml index dded6de59..082d85e5b 100644 --- a/examples/nrf51/Cargo.toml +++ b/examples/nrf51/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } +embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index aa1a4bf73..7835320e5 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml @@ -10,7 +10,7 @@ embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52840-edf/Cargo.toml b/examples/nrf52840-edf/Cargo.toml index 1e8803233..67a624d6d 100644 --- a/examples/nrf52840-edf/Cargo.toml +++ b/examples/nrf52840-edf/Cargo.toml @@ -9,7 +9,7 @@ publish = false # NOTE: "scheduler-deadline" and "embassy-time-driver" features are enabled embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "scheduler-deadline", "embassy-time-driver"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index f6937c263..d860626a1 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -12,7 +12,7 @@ embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } embassy-time-queue-utils = { version = "0.3.0", path = "../../embassy-time-queue-utils", features = ["generic-queue-8"] } -embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index ca3c6f863..5b3e176c0 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -10,7 +10,7 @@ embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time", "net-driver"] } +embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time", "net-driver"] } embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet","udp", "medium-ieee802154", "proto-ipv6"] } embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 425015667..256fee08d 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -10,7 +10,7 @@ embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } embedded-io-async = { version = "0.6.1" } diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml index 7f67b41f6..9c24cdab4 100644 --- a/examples/nrf54l15/Cargo.toml +++ b/examples/nrf54l15/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml index 8e420477f..89a6c7c94 100644 --- a/examples/nrf9151/ns/Cargo.toml +++ b/examples/nrf9151/ns/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] embassy-executor = { version = "0.9.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.7.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.8.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml index e4ca85553..870311c5d 100644 --- a/examples/nrf9151/s/Cargo.toml +++ b/examples/nrf9151/s/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] embassy-executor = { version = "0.9.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.7.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.8.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index d7b63a7ac..274e26dd6 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } +embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index efc297ccf..8acf27ce7 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -12,7 +12,7 @@ embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt", ] } embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.7.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } embassy-net-esp-hosted = { version = "0.2.1", path = "../../embassy-net-esp-hosted", features = ["defmt"] } -- cgit From 02b08a5a43427de77c194849674d0da0de84e07d Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Wed, 17 Sep 2025 20:52:40 +0200 Subject: stm32: add config to MCO to control the drive strength. --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/rcc/mco.rs | 23 ++++++++++++++++++++--- examples/stm32f4/src/bin/mco.rs | 18 +++++++++++++++--- examples/stm32h5/src/bin/mco.rs | 29 +++++++++++++++++++++++++++++ examples/stm32h7/src/bin/camera.rs | 11 +++++++++-- examples/stm32h7/src/bin/mco.rs | 10 ++++++++-- examples/stm32h7rs/src/bin/mco.rs | 10 ++++++++-- examples/stm32l4/src/bin/mco.rs | 10 ++++++++-- 8 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 examples/stm32h5/src/bin/mco.rs diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 80261ae41..1443472f5 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map. - fix: Cut down the capabilities of the STM32L412 and L422 RTC as those are missing binary timer mode and underflow interrupt. - fix: Allow configuration of the internal pull up/down resistors on the pins for the Qei peripheral, as well as the Qei decoder mode. +- feat: stm32/rcc/mco: Added support for IO driver strength when using Master Clock Out IO. This changes signature on Mco::new taking a McoConfig struct ([#4679](https://github.com/embassy-rs/embassy/pull/4679)) ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index 59ccc8cb5..fa4b45a20 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs @@ -91,12 +91,29 @@ pub struct Mco<'d, T: McoInstance> { impl<'d, T: McoInstance> Mco<'d, T> { /// Create a new MCO instance. - pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin>, source: T::Source, prescaler: McoPrescaler) -> Self { + pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin>, source: T::Source, config: McoConfig) -> Self { critical_section::with(|_| unsafe { - T::_apply_clock_settings(source, prescaler); - set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); + T::_apply_clock_settings(source, config.prescaler); + set_as_af!(pin, AfType::output(OutputType::PushPull, config.speed)); }); Self { phantom: PhantomData } } } + +#[non_exhaustive] +pub struct McoConfig { + /// Master Clock Out prescaler + pub prescaler: McoPrescaler, + /// IO Drive Strength + pub speed: Speed, +} + +impl Default for McoConfig { + fn default() -> Self { + Self { + prescaler: McoPrescaler::DIV1, + speed: Speed::VeryHigh, + } + } +} diff --git a/examples/stm32f4/src/bin/mco.rs b/examples/stm32f4/src/bin/mco.rs index eb7bb6261..a2e229770 100644 --- a/examples/stm32f4/src/bin/mco.rs +++ b/examples/stm32f4/src/bin/mco.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::rcc::{Mco, Mco1Source, Mco2Source, McoPrescaler}; +use embassy_stm32::rcc::{Mco, Mco1Source, Mco2Source, McoConfig, McoPrescaler}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -13,8 +13,20 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let _mco1 = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, McoPrescaler::DIV1); - let _mco2 = Mco::new(p.MCO2, p.PC9, Mco2Source::PLL, McoPrescaler::DIV4); + let config_mco1 = { + let mut config = McoConfig::default(); + config.prescaler = McoPrescaler::DIV1; + config + }; + + let config_mco2 = { + let mut config = McoConfig::default(); + config.prescaler = McoPrescaler::DIV4; + config + }; + + let _mco1 = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, config_mco1); + let _mco2 = Mco::new(p.MCO2, p.PC9, Mco2Source::PLL, config_mco2); let mut led = Output::new(p.PB7, Level::High, Speed::Low); loop { diff --git a/examples/stm32h5/src/bin/mco.rs b/examples/stm32h5/src/bin/mco.rs new file mode 100644 index 000000000..1137ba25c --- /dev/null +++ b/examples/stm32h5/src/bin/mco.rs @@ -0,0 +1,29 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::Speed; +use embassy_stm32::rcc::{Mco, Mco2Source, McoConfig}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + + /* Default "VeryHigh" drive strength and prescaler DIV1 */ + // let _mco = Mco::new(p.MCO2, p.PC9, Mco2Source::SYS, McoConfig::default()); + + /* Choose Speed::Low drive strength */ + let config = { + let mut config = McoConfig::default(); + config.speed = Speed::Low; + config + }; + + let _mco = Mco::new(p.MCO2, p.PC9, Mco2Source::SYS, config); + + info!("Clock out with low drive strength set on Master Clock Out 2 pin as AF on PC9"); + + loop {} +} diff --git a/examples/stm32h7/src/bin/camera.rs b/examples/stm32h7/src/bin/camera.rs index 8f2e265d6..039008d17 100644 --- a/examples/stm32h7/src/bin/camera.rs +++ b/examples/stm32h7/src/bin/camera.rs @@ -5,7 +5,7 @@ use embassy_executor::Spawner; use embassy_stm32::dcmi::{self, *}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::i2c::I2c; -use embassy_stm32::rcc::{Mco, Mco1Source, McoPrescaler}; +use embassy_stm32::rcc::{Mco, Mco1Source, McoConfig, McoPrescaler}; use embassy_stm32::{bind_interrupts, i2c, peripherals, Config}; use embassy_time::Timer; use ov7725::*; @@ -48,7 +48,14 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); defmt::info!("Hello World!"); - let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, McoPrescaler::DIV3); + + let mco_config = { + let mut config = McoConfig::default(); + config.prescaler = McoPrescaler::DIV3; + config + }; + + let mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, mco_config); let mut led = Output::new(p.PE3, Level::High, Speed::Low); let cam_i2c = I2c::new(p.I2C1, p.PB8, p.PB9, Irqs, p.DMA1_CH1, p.DMA1_CH2, Default::default()); diff --git a/examples/stm32h7/src/bin/mco.rs b/examples/stm32h7/src/bin/mco.rs index a6ee27625..cafcb90f6 100644 --- a/examples/stm32h7/src/bin/mco.rs +++ b/examples/stm32h7/src/bin/mco.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::rcc::{Mco, Mco1Source, McoPrescaler}; +use embassy_stm32::rcc::{Mco, Mco1Source, McoConfig, McoPrescaler}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +15,13 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB14, Level::High, Speed::Low); - let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, McoPrescaler::DIV8); + let config = { + let mut config = McoConfig::default(); + config.prescaler = McoPrescaler::DIV8; + config + }; + + let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, config); loop { info!("high"); diff --git a/examples/stm32h7rs/src/bin/mco.rs b/examples/stm32h7rs/src/bin/mco.rs index a6ee27625..cafcb90f6 100644 --- a/examples/stm32h7rs/src/bin/mco.rs +++ b/examples/stm32h7rs/src/bin/mco.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::rcc::{Mco, Mco1Source, McoPrescaler}; +use embassy_stm32::rcc::{Mco, Mco1Source, McoConfig, McoPrescaler}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -15,7 +15,13 @@ async fn main(_spawner: Spawner) { let mut led = Output::new(p.PB14, Level::High, Speed::Low); - let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, McoPrescaler::DIV8); + let config = { + let mut config = McoConfig::default(); + config.prescaler = McoPrescaler::DIV8; + config + }; + + let _mco = Mco::new(p.MCO1, p.PA8, Mco1Source::HSI, config); loop { info!("high"); diff --git a/examples/stm32l4/src/bin/mco.rs b/examples/stm32l4/src/bin/mco.rs index 36c002952..4cdeaa440 100644 --- a/examples/stm32l4/src/bin/mco.rs +++ b/examples/stm32l4/src/bin/mco.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::rcc::{Mco, McoPrescaler, McoSource}; +use embassy_stm32::rcc::{Mco, McoConfig, McoPrescaler, McoSource}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -13,7 +13,13 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let _mco = Mco::new(p.MCO, p.PA8, McoSource::HSI, McoPrescaler::DIV1); + let config = { + let mut config = McoConfig::default(); + config.prescaler = McoPrescaler::DIV1; + config + }; + + let _mco = Mco::new(p.MCO, p.PA8, McoSource::HSI, config); let mut led = Output::new(p.PB14, Level::High, Speed::Low); -- cgit From 2e9f3a815d440f33126d47cdcbf3bf1c9eab0ee1 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Mon, 15 Sep 2025 03:32:23 +0100 Subject: STM32: USART: Add `eager_reads` config option --- embassy-hal-internal/src/atomic_ring_buffer.rs | 39 +++++++++- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/usart/buffered.rs | 18 ++++- embassy-stm32/src/usart/mod.rs | 20 ++++- embassy-stm32/src/usart/ringbuffered.rs | 104 ++++++++++++++----------- 5 files changed, 133 insertions(+), 49 deletions(-) diff --git a/embassy-hal-internal/src/atomic_ring_buffer.rs b/embassy-hal-internal/src/atomic_ring_buffer.rs index 00b7a1249..7de96e4e2 100644 --- a/embassy-hal-internal/src/atomic_ring_buffer.rs +++ b/embassy-hal-internal/src/atomic_ring_buffer.rs @@ -142,6 +142,19 @@ impl RingBuffer { self.wrap(start + len) == end } + /// Check if buffer is at least half full. + pub fn is_half_full(&self) -> bool { + let len = self.len.load(Ordering::Relaxed); + let start = self.start.load(Ordering::Relaxed); + let end = self.end.load(Ordering::Relaxed); + let n = if end >= start { + end - start + } else { + 2 * len - start + end + }; + n >= len / 2 + } + /// Check if buffer is empty. pub fn is_empty(&self) -> bool { let start = self.start.load(Ordering::Relaxed); @@ -394,6 +407,7 @@ mod tests { rb.init(b.as_mut_ptr(), 4); assert_eq!(rb.is_empty(), true); + assert_eq!(rb.is_half_full(), false); assert_eq!(rb.is_full(), false); rb.writer().push(|buf| { @@ -406,6 +420,7 @@ mod tests { }); assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_half_full(), true); assert_eq!(rb.is_full(), true); rb.writer().push(|buf| { @@ -415,6 +430,7 @@ mod tests { }); assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_half_full(), true); assert_eq!(rb.is_full(), true); rb.reader().pop(|buf| { @@ -424,6 +440,7 @@ mod tests { }); assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_half_full(), true); assert_eq!(rb.is_full(), false); rb.reader().pop(|buf| { @@ -432,6 +449,7 @@ mod tests { }); assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_half_full(), true); assert_eq!(rb.is_full(), false); rb.reader().pop(|buf| { @@ -447,6 +465,7 @@ mod tests { }); assert_eq!(rb.is_empty(), true); + assert_eq!(rb.is_half_full(), false); assert_eq!(rb.is_full(), false); rb.reader().pop(|buf| { @@ -460,14 +479,28 @@ mod tests { 1 }); + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_half_full(), false); + assert_eq!(rb.is_full(), false); + rb.writer().push(|buf| { assert_eq!(3, buf.len()); buf[0] = 11; - buf[1] = 12; - 2 + 1 + }); + + assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_half_full(), true); + assert_eq!(rb.is_full(), false); + + rb.writer().push(|buf| { + assert_eq!(2, buf.len()); + buf[0] = 12; + 1 }); assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_half_full(), true); assert_eq!(rb.is_full(), false); rb.writer().push(|buf| { @@ -477,6 +510,7 @@ mod tests { }); assert_eq!(rb.is_empty(), false); + assert_eq!(rb.is_half_full(), true); assert_eq!(rb.is_full(), true); } } @@ -490,6 +524,7 @@ mod tests { rb.init(b.as_mut_ptr(), b.len()); assert_eq!(rb.is_empty(), true); + assert_eq!(rb.is_half_full(), true); assert_eq!(rb.is_full(), true); rb.writer().push(|buf| { diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 1443472f5..f4cfc14cc 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: Cut down the capabilities of the STM32L412 and L422 RTC as those are missing binary timer mode and underflow interrupt. - fix: Allow configuration of the internal pull up/down resistors on the pins for the Qei peripheral, as well as the Qei decoder mode. - feat: stm32/rcc/mco: Added support for IO driver strength when using Master Clock Out IO. This changes signature on Mco::new taking a McoConfig struct ([#4679](https://github.com/embassy-rs/embassy/pull/4679)) +- feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index c734eed49..83aa4439b 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -68,8 +68,14 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { // FIXME: Should we disable any further RX interrupts when the buffer becomes full. } - if !state.rx_buf.is_empty() { - state.rx_waker.wake(); + if state.eager_reads.load(Ordering::Relaxed) { + if !state.rx_buf.is_empty() { + state.rx_waker.wake(); + } + } else { + if state.rx_buf.is_half_full() { + state.rx_waker.wake(); + } } } @@ -132,6 +138,7 @@ pub(super) struct State { tx_done: AtomicBool, tx_rx_refcount: AtomicU8, half_duplex_readback: AtomicBool, + eager_reads: AtomicBool, } impl State { @@ -144,6 +151,7 @@ impl State { tx_done: AtomicBool::new(true), tx_rx_refcount: AtomicU8::new(0), half_duplex_readback: AtomicBool::new(false), + eager_reads: AtomicBool::new(false), } } } @@ -419,6 +427,7 @@ impl<'d> BufferedUart<'d> { let state = T::buffered_state(); let kernel_clock = T::frequency(); + state.eager_reads.store(config.eager_reads, Ordering::Relaxed); state.half_duplex_readback.store( config.duplex == Duplex::Half(HalfDuplexReadback::Readback), Ordering::Relaxed, @@ -456,6 +465,7 @@ impl<'d> BufferedUart<'d> { let info = self.rx.info; let state = self.rx.state; state.tx_rx_refcount.store(2, Ordering::Relaxed); + state.eager_reads.store(config.eager_reads, Ordering::Relaxed); info.rcc.enable_and_reset(); @@ -527,6 +537,8 @@ impl<'d> BufferedUart<'d> { pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { reconfigure(self.rx.info, self.rx.kernel_clock, config)?; + self.rx.state.eager_reads.store(config.eager_reads, Ordering::Relaxed); + self.rx.info.regs.cr1().modify(|w| { w.set_rxneie(true); w.set_idleie(true); @@ -633,6 +645,8 @@ impl<'d> BufferedUartRx<'d> { pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { reconfigure(self.info, self.kernel_clock, config)?; + self.state.eager_reads.store(config.eager_reads, Ordering::Relaxed); + self.info.regs.cr1().modify(|w| { w.set_rxneie(true); w.set_idleie(true); diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index ff211e0c9..e439f2cee 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -4,7 +4,7 @@ use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, AtomicU8, Ordering}; +use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU8, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; @@ -206,6 +206,18 @@ pub struct Config { /// If false: the error is ignored and cleared pub detect_previous_overrun: bool, + /// If true then read-like calls on `BufferedUartRx` and `RingBufferedUartRx` + /// are woken/return as soon as any data is available in the buffer. + /// + /// If false (the default) then reads started typically only wake/return after + /// line idle or after the buffer is at least half full (`BufferedUartRx`) or + /// the DMA buffer is written at the half or full positions (`RingBufferedUartRx`), + /// though it may also wake/return earlier in some circumstances. + /// + /// Has no effect on plain `Uart` or `UartRx` reads, which are specified to either + /// return a single word, a full buffer, or after line idle. + pub eager_reads: bool, + /// Set this to true if the line is considered noise free. /// This will increase the receiver’s tolerance to clock deviations, /// but will effectively disable noise detection. @@ -270,6 +282,7 @@ impl Default for Config { parity: Parity::ParityNone, // historical behavior detect_previous_overrun: false, + eager_reads: false, #[cfg(not(usart_v1))] assume_noise_free: false, #[cfg(any(usart_v3, usart_v4))] @@ -966,6 +979,7 @@ impl<'d, M: Mode> UartRx<'d, M> { let info = self.info; let state = self.state; state.tx_rx_refcount.store(1, Ordering::Relaxed); + state.eager_reads.store(config.eager_reads, Ordering::Relaxed); info.rcc.enable_and_reset(); @@ -982,6 +996,7 @@ impl<'d, M: Mode> UartRx<'d, M> { /// Reconfigure the driver pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.state.eager_reads.store(config.eager_reads, Ordering::Relaxed); reconfigure(self.info, self.kernel_clock, config) } @@ -1462,6 +1477,7 @@ impl<'d, M: Mode> Uart<'d, M> { let info = self.rx.info; let state = self.rx.state; state.tx_rx_refcount.store(2, Ordering::Relaxed); + state.eager_reads.store(config.eager_reads, Ordering::Relaxed); info.rcc.enable_and_reset(); @@ -2022,6 +2038,7 @@ struct State { rx_waker: AtomicWaker, tx_waker: AtomicWaker, tx_rx_refcount: AtomicU8, + eager_reads: AtomicBool, } impl State { @@ -2030,6 +2047,7 @@ impl State { rx_waker: AtomicWaker::new(), tx_waker: AtomicWaker::new(), tx_rx_refcount: AtomicU8::new(0), + eager_reads: AtomicBool::new(false), } } } diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 5f4e87834..d818e0bcc 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -26,9 +26,9 @@ use crate::Peri; /// contain enough bytes to fill the buffer passed by the caller of /// the function, or is empty. /// -/// Waiting for bytes operates in one of two modes, depending on -/// the behavior of the sender and the size of the buffer passed -/// to the function: +/// Waiting for bytes operates in one of three modes, depending on +/// the behavior of the sender, the size of the buffer passed +/// to the function, and the configuration: /// /// - If the sender sends intermittently, the 'idle line' /// condition will be detected when the sender stops, and any @@ -47,7 +47,11 @@ use crate::Peri; /// interrupt when those specific buffer addresses have been /// written. /// -/// In both cases this will result in variable latency due to the +/// - If `eager_reads` is enabled in `config`, the UART interrupt +/// is enabled on all data reception and the call will only wait +/// for at least one byte to be available before returning. +/// +/// In the first two cases this will result in variable latency due to the /// buffering effect. For example, if the baudrate is 2400 bps, and /// the configuration is 8 data bits, no parity bit, and one stop bit, /// then a byte will be received every ~4.16ms. If the ring buffer is @@ -68,15 +72,10 @@ use crate::Peri; /// sending, but would be falsely triggered in the worst-case /// buffer delay scenario. /// -/// Note: This latency is caused by the limited capabilities of the -/// STM32 DMA controller; since it cannot generate an interrupt when -/// it stores a byte into an empty ring buffer, or in any other -/// configurable conditions, it is not possible to take notice of the -/// contents of the ring buffer more quickly without introducing -/// polling. As a result the latency can be reduced by calling the -/// read functions repeatedly with smaller buffers to receive the -/// available bytes, as each call to a read function will explicitly -/// check the ring buffer for available bytes. +/// Note: Enabling `eager_reads` with `RingBufferedUartRx` will enable +/// an UART RXNE interrupt, which will cause an interrupt to occur on +/// every received data byte. The data is still copied using DMA, but +/// there is nevertheless additional processing overhead for each byte. pub struct RingBufferedUartRx<'d> { info: &'static Info, state: &'static State, @@ -133,6 +132,7 @@ impl<'d> UartRx<'d, Async> { impl<'d> RingBufferedUartRx<'d> { /// Reconfigure the driver pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.state.eager_reads.store(config.eager_reads, Ordering::Relaxed); reconfigure(self.info, self.kernel_clock, config) } @@ -148,8 +148,8 @@ impl<'d> RingBufferedUartRx<'d> { let r = self.info.regs; // clear all interrupts and DMA Rx Request r.cr1().modify(|w| { - // disable RXNE interrupt - w.set_rxneie(false); + // use RXNE only when returning reads early + w.set_rxneie(self.state.eager_reads.load(Ordering::Relaxed)); // enable parity interrupt if not ParityNone w.set_peie(w.pce()); // enable idle line interrupt @@ -248,39 +248,55 @@ impl<'d> RingBufferedUartRx<'d> { async fn wait_for_data_or_idle(&mut self) -> Result<(), Error> { compiler_fence(Ordering::SeqCst); - // Future which completes when idle line is detected - let s = self.state; - let uart = poll_fn(|cx| { - s.rx_waker.register(cx.waker()); - - compiler_fence(Ordering::SeqCst); - - if check_idle_and_errors(self.info.regs)? { - // Idle line is detected - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - }); + loop { + // Future which completes when idle line is detected + let s = self.state; + let mut uart_init = false; + let uart = poll_fn(|cx| { + s.rx_waker.register(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + // We may have been woken by IDLE or, if eager_reads is set, by RXNE. + // However, DMA will clear RXNE, so we can't check directly, and because + // the other future borrows `ring_buf`, we can't check `len()` here either. + // Instead, return from this future and we'll check the length afterwards. + let eager = s.eager_reads.load(Ordering::Relaxed); + + if check_idle_and_errors(self.info.regs)? || (eager && uart_init) { + // Idle line is detected, or eager reads is set and some data is available. + Poll::Ready(Ok(())) + } else { + uart_init = true; + Poll::Pending + } + }); - let mut dma_init = false; - // Future which completes when the DMA controller indicates it - // has written to the ring buffer's middle byte, or last byte - let dma = poll_fn(|cx| { - self.ring_buf.set_waker(cx.waker()); + let mut dma_init = false; + // Future which completes when the DMA controller indicates it + // has written to the ring buffer's middle byte, or last byte + let dma = poll_fn(|cx| { + self.ring_buf.set_waker(cx.waker()); - let status = match dma_init { - false => Poll::Pending, - true => Poll::Ready(()), - }; + let status = match dma_init { + false => Poll::Pending, + true => Poll::Ready(()), + }; - dma_init = true; - status - }); + dma_init = true; + status + }); - match select(uart, dma).await { - Either::Left((result, _)) => result, - Either::Right(((), _)) => Ok(()), + match select(uart, dma).await { + Either::Left((result, _)) => { + if self.ring_buf.len().unwrap_or(0) > 0 || result.is_err() { + return result; + } else { + continue; + } + } + Either::Right(((), _)) => return Ok(()), + } } } -- cgit From c87051eecb50b80114c69cedc71ed9e3fcd7bb45 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Thu, 18 Sep 2025 01:50:48 +0100 Subject: STM32: USART: Add de_assertion_time and de_deassertion_time configs --- embassy-stm32/src/usart/mod.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index e439f2cee..c8012a9b4 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -185,6 +185,12 @@ pub enum ConfigError { RxOrTxNotEnabled, /// Data bits and parity combination not supported DataParityNotSupported, + /// DE assertion time too high + #[cfg(not(any(usart_v1, usart_v2)))] + DeAssertionTimeTooHigh, + /// DE deassertion time too high + #[cfg(not(any(usart_v1, usart_v2)))] + DeDeassertionTimeTooHigh, } #[non_exhaustive] @@ -251,6 +257,14 @@ pub struct Config { /// Set the pin configuration for the DE pin. pub de_config: OutputConfig, + /// Set DE assertion time before the first start bit, 0-31 16ths of a bit period. + #[cfg(not(any(usart_v1, usart_v2)))] + pub de_assertion_time: u8, + + /// Set DE deassertion time after the last stop bit, 0-31 16ths of a bit period. + #[cfg(not(any(usart_v1, usart_v2)))] + pub de_deassertion_time: u8, + // private: set by new_half_duplex, not by the user. duplex: Duplex, } @@ -296,6 +310,10 @@ impl Default for Config { tx_config: OutputConfig::PushPull, rts_config: OutputConfig::PushPull, de_config: OutputConfig::PushPull, + #[cfg(not(any(usart_v1, usart_v2)))] + de_assertion_time: 0, + #[cfg(not(any(usart_v1, usart_v2)))] + de_deassertion_time: 0, duplex: Duplex::Full, } } @@ -1706,6 +1724,16 @@ fn configure( return Err(ConfigError::RxOrTxNotEnabled); } + #[cfg(not(any(usart_v1, usart_v2)))] + let dem = r.cr3().read().dem(); + + #[cfg(not(any(usart_v1, usart_v2)))] + if config.de_assertion_time > 31 { + return Err(ConfigError::DeAssertionTimeTooHigh); + } else if config.de_deassertion_time > 31 { + return Err(ConfigError::DeDeassertionTimeTooHigh); + } + // UART must be disabled during configuration. r.cr1().modify(|w| { w.set_ue(false); @@ -1754,6 +1782,20 @@ fn configure( w.set_re(enable_rx); } + #[cfg(not(any(usart_v1, usart_v2)))] + if dem { + w.set_deat(if over8 { + config.de_assertion_time / 2 + } else { + config.de_assertion_time + }); + w.set_dedt(if over8 { + config.de_assertion_time / 2 + } else { + config.de_assertion_time + }); + } + // configure word size and parity, since the parity bit is inserted into the MSB position, // it increases the effective word size match (config.parity, config.data_bits) { -- cgit From ef06ff43a14fd016d271c491bd830823ee96b740 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Thu, 18 Sep 2025 02:04:05 +0100 Subject: STM32: USART: Make BufferedUartRx return all available bytes when they wrap around the internal buffer end --- embassy-stm32/CHANGELOG.md | 3 +++ embassy-stm32/src/usart/buffered.rs | 37 +++++++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index f4cfc14cc..a6ee5c4b8 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -27,7 +27,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: Cut down the capabilities of the STM32L412 and L422 RTC as those are missing binary timer mode and underflow interrupt. - fix: Allow configuration of the internal pull up/down resistors on the pins for the Qei peripheral, as well as the Qei decoder mode. - feat: stm32/rcc/mco: Added support for IO driver strength when using Master Clock Out IO. This changes signature on Mco::new taking a McoConfig struct ([#4679](https://github.com/embassy-rs/embassy/pull/4679)) +- feat: derive Clone, Copy and defmt::Format for all SPI-related configs - feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) +- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options +- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 83aa4439b..165f2e88e 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -565,24 +565,30 @@ impl<'d> BufferedUartRx<'d> { poll_fn(move |cx| { let state = self.state; let mut rx_reader = unsafe { state.rx_buf.reader() }; - let data = rx_reader.pop_slice(); + let mut buf_len = 0; + let mut data = rx_reader.pop_slice(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); + while !data.is_empty() && buf_len < buf.len() { + let data_len = data.len().min(buf.len() - buf_len); + buf[buf_len..buf_len + data_len].copy_from_slice(&data[..data_len]); + buf_len += data_len; let do_pend = state.rx_buf.is_full(); - rx_reader.pop_done(len); + rx_reader.pop_done(data_len); if do_pend { self.info.interrupt.pend(); } - return Poll::Ready(Ok(len)); + data = rx_reader.pop_slice(); } - state.rx_waker.register(cx.waker()); - Poll::Pending + if buf_len != 0 { + Poll::Ready(Ok(buf_len)) + } else { + state.rx_waker.register(cx.waker()); + Poll::Pending + } }) .await } @@ -591,21 +597,24 @@ impl<'d> BufferedUartRx<'d> { loop { let state = self.state; let mut rx_reader = unsafe { state.rx_buf.reader() }; - let data = rx_reader.pop_slice(); + let mut buf_len = 0; + let mut data = rx_reader.pop_slice(); - if !data.is_empty() { - let len = data.len().min(buf.len()); - buf[..len].copy_from_slice(&data[..len]); + while !data.is_empty() && buf_len < buf.len() { + let data_len = data.len().min(buf.len() - buf_len); + buf[buf_len..buf_len + data_len].copy_from_slice(&data[..data_len]); + buf_len += data_len; let do_pend = state.rx_buf.is_full(); - rx_reader.pop_done(len); + rx_reader.pop_done(data_len); if do_pend { self.info.interrupt.pend(); } - return Ok(len); + data = rx_reader.pop_slice(); } + return Ok(buf_len); } } -- cgit From 775123467b0ce6e5daede0795493df9577077a09 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Fri, 19 Sep 2025 02:10:56 +0100 Subject: STM32: USART: Change eager_reads from bool to Option --- embassy-hal-internal/src/atomic_ring_buffer.rs | 22 ++++++++------- embassy-stm32/src/usart/buffered.rs | 28 ++++++++++++------- embassy-stm32/src/usart/mod.rs | 37 ++++++++++++++++---------- embassy-stm32/src/usart/ringbuffered.rs | 30 +++++++++++++++------ 4 files changed, 77 insertions(+), 40 deletions(-) diff --git a/embassy-hal-internal/src/atomic_ring_buffer.rs b/embassy-hal-internal/src/atomic_ring_buffer.rs index 7de96e4e2..8c3889b85 100644 --- a/embassy-hal-internal/src/atomic_ring_buffer.rs +++ b/embassy-hal-internal/src/atomic_ring_buffer.rs @@ -133,6 +133,18 @@ impl RingBuffer { self.len.load(Ordering::Relaxed) } + /// Return number of items available to read. + pub fn available(&self) -> usize { + let end = self.end.load(Ordering::Relaxed); + let len = self.len.load(Ordering::Relaxed); + let start = self.start.load(Ordering::Relaxed); + if end >= start { + end - start + } else { + 2 * len - start + end + } + } + /// Check if buffer is full. pub fn is_full(&self) -> bool { let len = self.len.load(Ordering::Relaxed); @@ -144,15 +156,7 @@ impl RingBuffer { /// Check if buffer is at least half full. pub fn is_half_full(&self) -> bool { - let len = self.len.load(Ordering::Relaxed); - let start = self.start.load(Ordering::Relaxed); - let end = self.end.load(Ordering::Relaxed); - let n = if end >= start { - end - start - } else { - 2 * len - start + end - }; - n >= len / 2 + self.available() >= self.len.load(Ordering::Relaxed) / 2 } /// Check if buffer is empty. diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 165f2e88e..10dc02334 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,7 +1,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::slice; -use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; @@ -68,8 +68,9 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { // FIXME: Should we disable any further RX interrupts when the buffer becomes full. } - if state.eager_reads.load(Ordering::Relaxed) { - if !state.rx_buf.is_empty() { + let eager = state.eager_reads.load(Ordering::Relaxed); + if eager > 0 { + if state.rx_buf.available() >= eager { state.rx_waker.wake(); } } else { @@ -138,7 +139,7 @@ pub(super) struct State { tx_done: AtomicBool, tx_rx_refcount: AtomicU8, half_duplex_readback: AtomicBool, - eager_reads: AtomicBool, + eager_reads: AtomicUsize, } impl State { @@ -151,7 +152,7 @@ impl State { tx_done: AtomicBool::new(true), tx_rx_refcount: AtomicU8::new(0), half_duplex_readback: AtomicBool::new(false), - eager_reads: AtomicBool::new(false), + eager_reads: AtomicUsize::new(0), } } } @@ -427,7 +428,9 @@ impl<'d> BufferedUart<'d> { let state = T::buffered_state(); let kernel_clock = T::frequency(); - state.eager_reads.store(config.eager_reads, Ordering::Relaxed); + state + .eager_reads + .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); state.half_duplex_readback.store( config.duplex == Duplex::Half(HalfDuplexReadback::Readback), Ordering::Relaxed, @@ -465,7 +468,9 @@ impl<'d> BufferedUart<'d> { let info = self.rx.info; let state = self.rx.state; state.tx_rx_refcount.store(2, Ordering::Relaxed); - state.eager_reads.store(config.eager_reads, Ordering::Relaxed); + state + .eager_reads + .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); info.rcc.enable_and_reset(); @@ -537,7 +542,10 @@ impl<'d> BufferedUart<'d> { pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { reconfigure(self.rx.info, self.rx.kernel_clock, config)?; - self.rx.state.eager_reads.store(config.eager_reads, Ordering::Relaxed); + self.rx + .state + .eager_reads + .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); self.rx.info.regs.cr1().modify(|w| { w.set_rxneie(true); @@ -654,7 +662,9 @@ impl<'d> BufferedUartRx<'d> { pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { reconfigure(self.info, self.kernel_clock, config)?; - self.state.eager_reads.store(config.eager_reads, Ordering::Relaxed); + self.state + .eager_reads + .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); self.info.regs.cr1().modify(|w| { w.set_rxneie(true); diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index c8012a9b4..0d2d86aca 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -4,7 +4,7 @@ use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU8, Ordering}; +use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; @@ -212,17 +212,20 @@ pub struct Config { /// If false: the error is ignored and cleared pub detect_previous_overrun: bool, - /// If true then read-like calls on `BufferedUartRx` and `RingBufferedUartRx` - /// are woken/return as soon as any data is available in the buffer. + /// If `None` (the default) then read-like calls on `BufferedUartRx` and `RingBufferedUartRx` + /// typically only wake/return after line idle or after the buffer is at least half full + /// (for `BufferedUartRx`) or the DMA buffer is written at the half or full positions + /// (for `RingBufferedUartRx`), though it may also wake/return earlier in some circumstances. /// - /// If false (the default) then reads started typically only wake/return after - /// line idle or after the buffer is at least half full (`BufferedUartRx`) or - /// the DMA buffer is written at the half or full positions (`RingBufferedUartRx`), - /// though it may also wake/return earlier in some circumstances. + /// If `Some(n)` then such reads are also woken/return as soon as at least `n` words are + /// available in the buffer, in addition to waking/returning when the conditions described + /// above are met. `Some(0)` is treated as `None`. Setting this for `RingBufferedUartRx` + /// will trigger an interrupt for every received word to check the buffer level, which may + /// impact performance at high data rates. /// /// Has no effect on plain `Uart` or `UartRx` reads, which are specified to either /// return a single word, a full buffer, or after line idle. - pub eager_reads: bool, + pub eager_reads: Option, /// Set this to true if the line is considered noise free. /// This will increase the receiver’s tolerance to clock deviations, @@ -296,7 +299,7 @@ impl Default for Config { parity: Parity::ParityNone, // historical behavior detect_previous_overrun: false, - eager_reads: false, + eager_reads: None, #[cfg(not(usart_v1))] assume_noise_free: false, #[cfg(any(usart_v3, usart_v4))] @@ -997,7 +1000,9 @@ impl<'d, M: Mode> UartRx<'d, M> { let info = self.info; let state = self.state; state.tx_rx_refcount.store(1, Ordering::Relaxed); - state.eager_reads.store(config.eager_reads, Ordering::Relaxed); + state + .eager_reads + .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); info.rcc.enable_and_reset(); @@ -1014,7 +1019,9 @@ impl<'d, M: Mode> UartRx<'d, M> { /// Reconfigure the driver pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { - self.state.eager_reads.store(config.eager_reads, Ordering::Relaxed); + self.state + .eager_reads + .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); reconfigure(self.info, self.kernel_clock, config) } @@ -1495,7 +1502,9 @@ impl<'d, M: Mode> Uart<'d, M> { let info = self.rx.info; let state = self.rx.state; state.tx_rx_refcount.store(2, Ordering::Relaxed); - state.eager_reads.store(config.eager_reads, Ordering::Relaxed); + state + .eager_reads + .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); info.rcc.enable_and_reset(); @@ -2080,7 +2089,7 @@ struct State { rx_waker: AtomicWaker, tx_waker: AtomicWaker, tx_rx_refcount: AtomicU8, - eager_reads: AtomicBool, + eager_reads: AtomicUsize, } impl State { @@ -2089,7 +2098,7 @@ impl State { rx_waker: AtomicWaker::new(), tx_waker: AtomicWaker::new(), tx_rx_refcount: AtomicU8::new(0), - eager_reads: AtomicBool::new(false), + eager_reads: AtomicUsize::new(0), } } } diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index d818e0bcc..27071fb31 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -132,7 +132,9 @@ impl<'d> UartRx<'d, Async> { impl<'d> RingBufferedUartRx<'d> { /// Reconfigure the driver pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { - self.state.eager_reads.store(config.eager_reads, Ordering::Relaxed); + self.state + .eager_reads + .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); reconfigure(self.info, self.kernel_clock, config) } @@ -149,7 +151,7 @@ impl<'d> RingBufferedUartRx<'d> { // clear all interrupts and DMA Rx Request r.cr1().modify(|w| { // use RXNE only when returning reads early - w.set_rxneie(self.state.eager_reads.load(Ordering::Relaxed)); + w.set_rxneie(self.state.eager_reads.load(Ordering::Relaxed) > 0); // enable parity interrupt if not ParityNone w.set_peie(w.pce()); // enable idle line interrupt @@ -261,11 +263,12 @@ impl<'d> RingBufferedUartRx<'d> { // However, DMA will clear RXNE, so we can't check directly, and because // the other future borrows `ring_buf`, we can't check `len()` here either. // Instead, return from this future and we'll check the length afterwards. - let eager = s.eager_reads.load(Ordering::Relaxed); + let eager = s.eager_reads.load(Ordering::Relaxed) > 0; - if check_idle_and_errors(self.info.regs)? || (eager && uart_init) { + let idle = check_idle_and_errors(self.info.regs)?; + if idle || (eager && uart_init) { // Idle line is detected, or eager reads is set and some data is available. - Poll::Ready(Ok(())) + Poll::Ready(Ok(idle)) } else { uart_init = true; Poll::Pending @@ -288,13 +291,24 @@ impl<'d> RingBufferedUartRx<'d> { }); match select(uart, dma).await { - Either::Left((result, _)) => { - if self.ring_buf.len().unwrap_or(0) > 0 || result.is_err() { - return result; + // UART woke with line idle + Either::Left((Ok(true), _)) => { + return Ok(()); + } + // UART woke without idle or error: word received + Either::Left((Ok(false), _)) => { + let eager = self.state.eager_reads.load(Ordering::Relaxed); + if eager > 0 && self.ring_buf.len().unwrap_or(0) >= eager { + return Ok(()); } else { continue; } } + // UART woke with error + Either::Left((Err(e), _)) => { + return Err(e); + } + // DMA woke Either::Right(((), _)) => return Ok(()), } } -- cgit