From 6f2ad3b696936ff2a3995fda3c8aee5728bd8f53 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Tue, 25 Nov 2025 11:18:01 +0800 Subject: chore: bump usbd-hid version Signed-off-by: Haobo Gu --- embassy-usb/CHANGELOG.md | 1 + embassy-usb/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp235x/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- 9 files changed, 9 insertions(+), 8 deletions(-) diff --git a/embassy-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md index cfb1bf021..3dd71ffbc 100644 --- a/embassy-usb/CHANGELOG.md +++ b/embassy-usb/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate - Add support for USB HID Boot Protocol Mode +- Bump usbd-hid from 0.8.1 to 0.9.0 ## 0.5.1 - 2025-08-26 diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index aeb7392f1..3d1e005e4 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -69,5 +69,5 @@ heapless = "0.8" embedded-io-async = "0.6.1" # for HID -usbd-hid = { version = "0.8.1", optional = true } +usbd-hid = { version = "0.9.0", optional = true } ssmarshal = { version = "1.0", default-features = false, optional = true } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index a026d6352..1fe3d2419 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -28,7 +28,7 @@ cortex-m-rt = "0.7.0" panic-probe = { version = "1.0.0", features = ["print-defmt"] } rand = { version = "0.9.0", default-features = false } embedded-storage = "0.3.1" -usbd-hid = "0.8.1" +usbd-hid = "0.9.0" serde = { version = "1.0.136", default-features = false } embedded-hal = { version = "1.0" } embedded-hal-async = { version = "1.0" } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 4dcbdd715..97efe58e8 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -23,7 +23,7 @@ cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-sing cortex-m-rt = "0.7.0" panic-probe = { version = "1.0.0", features = ["print-defmt"] } embedded-storage = "0.3.1" -usbd-hid = "0.8.1" +usbd-hid = "0.9.0" serde = { version = "1.0.136", default-features = false } [profile.release] diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 640addb28..9d7d99259 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -45,7 +45,7 @@ display-interface = "0.5.0" byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.4.0" heapless = "0.8" -usbd-hid = "0.8.1" +usbd-hid = "0.9.0" embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = "1.0" diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index 39a4f421a..ad396275b 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -46,7 +46,7 @@ display-interface = "0.5.0" byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.3.0" heapless = "0.8" -usbd-hid = "0.8.1" +usbd-hid = "0.9.0" embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = "1.0" diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index d06b7505c..b4555045a 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -32,7 +32,7 @@ critical-section = "1.1" nb = "1.0.0" embedded-storage = "0.3.1" micromath = "2.0.0" -usbd-hid = "0.8.1" +usbd-hid = "0.9.0" static_cell = "2" chrono = { version = "^0.4", default-features = false} diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 8bbeb594c..d1c19582b 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -13,7 +13,7 @@ embassy-executor = { path = "../../embassy-executor", features = ["arch-cortex-m embassy-time = { path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { path = "../../embassy-futures" } -usbd-hid = "0.8.1" +usbd-hid = "0.9.0" defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index b6158c854..586b00836 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -14,7 +14,7 @@ embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["de embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } -usbd-hid = "0.8.1" +usbd-hid = "0.9.0" defmt = "1.0.1" defmt-rtt = "1.0.0" -- cgit From 3ba8bb866a19a09f25e0b21419a068fd765a9033 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 25 Nov 2025 07:45:01 -0600 Subject: stm32: allow split irqs for time driver --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/build.rs | 49 +++++++++++---- embassy-stm32/src/time_driver.rs | 126 +++------------------------------------ 3 files changed, 46 insertions(+), 130 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index a66b2d437..0f19b14b3 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- fix: fix incorrect handling of split interrupts in timer driver - change: low power: store stop mode for dma channels - fix: Fixed ADC4 enable() for WBA - feat: allow use of anyadcchannel for adc4 diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 109571e8f..46d6290e7 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -9,7 +9,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use stm32_metapac::metadata::ir::BitOffset; use stm32_metapac::metadata::{ - ALL_CHIPS, ALL_PERIPHERAL_VERSIONS, METADATA, MemoryRegion, MemoryRegionKind, PeripheralRccKernelClock, + ALL_CHIPS, ALL_PERIPHERAL_VERSIONS, METADATA, MemoryRegion, MemoryRegionKind, Peripheral, PeripheralRccKernelClock, PeripheralRccRegister, PeripheralRegisters, StopMode, }; @@ -133,6 +133,9 @@ fn main() { cfgs.enable("backup_sram") } + // compile a map of peripherals + let peripheral_map: BTreeMap<&str, &Peripheral> = METADATA.peripherals.iter().map(|p| (p.name, p)).collect(); + // generate one singleton per peripheral (with many exceptions...) for p in METADATA.peripherals { if let Some(r) = &p.registers { @@ -319,9 +322,33 @@ fn main() { _ => panic!("unknown time_driver {:?}", time_driver), }; - if !time_driver_singleton.is_empty() { + let time_driver_irq_decl = if !time_driver_singleton.is_empty() { cfgs.enable(format!("time_driver_{}", time_driver_singleton.to_lowercase())); - } + + let p = peripheral_map.get(time_driver_singleton).unwrap(); + let irqs: BTreeSet<_> = p + .interrupts + .iter() + .filter(|i| i.signal == "CC" || i.signal == "UP") + .map(|i| i.interrupt.to_ascii_uppercase()) + .collect(); + + irqs.iter() + .map(|i| { + let irq = format_ident!("{}", i); + quote! { + #[cfg(feature = "rt")] + #[interrupt] + fn #irq() { + crate::time_driver::get_driver().on_interrupt(); + } + } + }) + .collect() + } else { + TokenStream::new() + }; + for tim in [ "tim1", "tim2", "tim3", "tim4", "tim5", "tim8", "tim9", "tim12", "tim15", "tim20", "tim21", "tim22", "tim23", "tim24", @@ -371,6 +398,8 @@ fn main() { ); }); + g.extend(time_driver_irq_decl); + // ======== // Generate FLASH regions cfgs.declare("flash"); @@ -1862,7 +1891,7 @@ fn main() { flash_regions_table.push(row); } - let gpio_base = METADATA.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as u32; + let gpio_base = peripheral_map.get("GPIOA").unwrap().address as u32; let gpio_stride = 0x400; for pin in METADATA.pins { @@ -1980,11 +2009,11 @@ fn main() { continue; } - let stop_mode = METADATA - .peripherals - .iter() - .find(|p| p.name == ch.dma) - .map(|p| p.rcc.as_ref().map(|rcc| rcc.stop_mode.clone()).unwrap_or_default()) + let dma_peri = peripheral_map.get(ch.dma).unwrap(); + let stop_mode = dma_peri + .rcc + .as_ref() + .map(|rcc| rcc.stop_mode.clone()) .unwrap_or_default(); let stop_mode = match stop_mode { @@ -2009,8 +2038,6 @@ fn main() { let dma = format_ident!("{}", ch.dma); let ch_num = ch.channel as usize; - - let dma_peri = METADATA.peripherals.iter().find(|p| p.name == ch.dma).unwrap(); let bi = dma_peri.registers.as_ref().unwrap(); let dma_info = match bi.kind { diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 0b75aef92..cfcf5f3fd 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -14,11 +14,11 @@ use stm32_metapac::timer::{TimGp16, regs}; use crate::interrupt::typelevel::Interrupt; use crate::pac::timer::vals; +use crate::peripherals; use crate::rcc::{self, SealedRccPeripheral}; #[cfg(feature = "low-power")] use crate::rtc::Rtc; use crate::timer::{CoreInstance, GeneralInstance1Channel}; -use crate::{interrupt, peripherals}; // NOTE regarding ALARM_COUNT: // @@ -56,121 +56,6 @@ type T = peripherals::TIM23; #[cfg(time_driver_tim24)] type T = peripherals::TIM24; -foreach_interrupt! { - (TIM1, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim1)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; - (TIM2, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim2)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; - (TIM3, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim3)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; - (TIM4, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim4)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; - (TIM5, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim5)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; - (TIM8, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim8)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; - (TIM9, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim9)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; - (TIM12, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim12)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; - (TIM15, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim15)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; - (TIM20, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim20)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; - (TIM21, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim21)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; - (TIM22, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim22)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; - (TIM23, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim23)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; - (TIM24, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim24)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; -} - fn regs_gp16() -> TimGp16 { unsafe { TimGp16::from_ptr(T::regs()) } } @@ -282,7 +167,11 @@ impl RtcDriver { r.cnt().write(|w| w.set_cnt(self.saved_count.load(Ordering::SeqCst))); ::CaptureCompareInterrupt::unpend(); - unsafe { ::CaptureCompareInterrupt::enable() }; + ::UpdateInterrupt::unpend(); + unsafe { + ::CaptureCompareInterrupt::enable(); + ::UpdateInterrupt::enable(); + } } fn init(&'static self, cs: CriticalSection) { @@ -290,7 +179,7 @@ impl RtcDriver { regs_gp16().cr1().modify(|w| w.set_cen(true)); } - fn on_interrupt(&self) { + pub(crate) fn on_interrupt(&self) { let r = regs_gp16(); critical_section::with(|cs| { @@ -508,7 +397,6 @@ impl Driver for RtcDriver { } } -#[cfg(feature = "low-power")] pub(crate) const fn get_driver() -> &'static RtcDriver { &DRIVER } -- cgit From d2d00b57c8bf5b6879c5df5021f44652d1fd52ee Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 25 Nov 2025 11:47:44 -0600 Subject: stm32: allow granular stop for uart --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/rcc/mod.rs | 39 ++++++++++++++++++++++++++++----- embassy-stm32/src/usart/mod.rs | 20 +++++++++++++---- embassy-stm32/src/usart/ringbuffered.rs | 3 +++ 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index d3e5ba48d..5c31b5a11 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- feat: allow granular stop for regular usart - feat: Add continuous waveform method to SimplePWM - change: remove waveform timer method - change: low power: store stop mode for dma channels diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 85434fa83..f38d9078d 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -234,9 +234,6 @@ impl RccInfo { } } - #[cfg(feature = "low-power")] - increment_stop_refcount(_cs, self.stop_mode); - // set the xxxRST bit let reset_ptr = self.reset_ptr(); if let Some(reset_ptr) = reset_ptr { @@ -292,9 +289,6 @@ impl RccInfo { } } - #[cfg(feature = "low-power")] - decrement_stop_refcount(_cs, self.stop_mode); - // clear the xxxEN bit let enable_ptr = self.enable_ptr(); unsafe { @@ -303,13 +297,46 @@ impl RccInfo { } } + pub(crate) fn increment_stop_refcount_with_cs(&self, _cs: CriticalSection) { + #[cfg(feature = "low-power")] + increment_stop_refcount(_cs, self.stop_mode); + } + + pub(crate) fn increment_stop_refcount(&self) { + critical_section::with(|cs| self.increment_stop_refcount_with_cs(cs)) + } + + pub(crate) fn decrement_stop_refcount_with_cs(&self, _cs: CriticalSection) { + #[cfg(feature = "low-power")] + decrement_stop_refcount(_cs, self.stop_mode); + } + + pub(crate) fn decrement_stop_refcount(&self) { + critical_section::with(|cs| self.decrement_stop_refcount_with_cs(cs)) + } + // TODO: should this be `unsafe`? pub(crate) fn enable_and_reset(&self) { + critical_section::with(|cs| { + self.enable_and_reset_with_cs(cs); + self.increment_stop_refcount_with_cs(cs); + }) + } + + pub(crate) fn enable_and_reset_without_stop(&self) { critical_section::with(|cs| self.enable_and_reset_with_cs(cs)) } // TODO: should this be `unsafe`? pub(crate) fn disable(&self) { + critical_section::with(|cs| { + self.disable_with_cs(cs); + self.decrement_stop_refcount_with_cs(cs); + }) + } + + // TODO: should this be `unsafe`? + pub(crate) fn disable_without_stop(&self) { critical_section::with(|cs| self.disable_with_cs(cs)) } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 0e7da634d..1af78b358 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -491,6 +491,9 @@ impl<'d> UartTx<'d, Async> { /// Initiate an asynchronous UART write pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.info.rcc.increment_stop_refcount(); + let _ = OnDrop::new(|| self.info.rcc.decrement_stop_refcount()); + let r = self.info.regs; half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback)); @@ -508,6 +511,9 @@ impl<'d> UartTx<'d, Async> { /// Wait until transmission complete pub async fn flush(&mut self) -> Result<(), Error> { + self.info.rcc.increment_stop_refcount(); + let _ = OnDrop::new(|| self.info.rcc.decrement_stop_refcount()); + flush(&self.info, &self.state).await } } @@ -569,7 +575,7 @@ impl<'d, M: Mode> UartTx<'d, M> { let state = self.state; state.tx_rx_refcount.store(1, Ordering::Relaxed); - info.rcc.enable_and_reset(); + info.rcc.enable_and_reset_without_stop(); info.regs.cr3().modify(|w| { w.set_ctse(self.cts.is_some()); @@ -726,6 +732,9 @@ impl<'d> UartRx<'d, Async> { /// Initiate an asynchronous UART read pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.info.rcc.increment_stop_refcount(); + let _ = OnDrop::new(|| self.info.rcc.decrement_stop_refcount()); + self.inner_read(buffer, false).await?; Ok(()) @@ -733,6 +742,9 @@ impl<'d> UartRx<'d, Async> { /// Initiate an asynchronous read with idle line detection enabled pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { + self.info.rcc.increment_stop_refcount(); + let _ = OnDrop::new(|| self.info.rcc.decrement_stop_refcount()); + self.inner_read(buffer, true).await } @@ -1004,7 +1016,7 @@ impl<'d, M: Mode> UartRx<'d, M> { .eager_reads .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); - info.rcc.enable_and_reset(); + info.rcc.enable_and_reset_without_stop(); info.regs.cr3().write(|w| { w.set_rtse(self.rts.is_some()); @@ -1143,7 +1155,7 @@ fn drop_tx_rx(info: &Info, state: &State) { refcount == 1 }); if is_last_drop { - info.rcc.disable(); + info.rcc.disable_without_stop(); } } @@ -1506,7 +1518,7 @@ impl<'d, M: Mode> Uart<'d, M> { .eager_reads .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); - info.rcc.enable_and_reset(); + info.rcc.enable_and_reset_without_stop(); info.regs.cr3().write(|w| { w.set_rtse(self.rx.rts.is_some()); diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index bac570d27..cc5224b69 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -117,6 +117,8 @@ impl<'d> UartRx<'d, Async> { let rx = unsafe { self.rx.as_ref().map(|x| x.clone_unchecked()) }; let rts = unsafe { self.rts.as_ref().map(|x| x.clone_unchecked()) }; + info.rcc.increment_stop_refcount(); + // Don't disable the clock mem::forget(self); @@ -324,6 +326,7 @@ impl<'d> RingBufferedUartRx<'d> { impl Drop for RingBufferedUartRx<'_> { fn drop(&mut self) { + self.info.rcc.decrement_stop_refcount(); self.stop_uart(); self.rx.as_ref().map(|x| x.set_as_disconnected()); self.rts.as_ref().map(|x| x.set_as_disconnected()); -- cgit From 5298671b0c132f58f3f76273bcd35656dc6e6d3d Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 25 Nov 2025 11:57:59 -0600 Subject: fmt --- embassy-stm32/src/rcc/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index f38d9078d..b6ecc6c18 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -302,6 +302,7 @@ impl RccInfo { increment_stop_refcount(_cs, self.stop_mode); } + #[allow(dead_code)] pub(crate) fn increment_stop_refcount(&self) { critical_section::with(|cs| self.increment_stop_refcount_with_cs(cs)) } @@ -311,6 +312,7 @@ impl RccInfo { decrement_stop_refcount(_cs, self.stop_mode); } + #[allow(dead_code)] pub(crate) fn decrement_stop_refcount(&self) { critical_section::with(|cs| self.decrement_stop_refcount_with_cs(cs)) } @@ -323,6 +325,7 @@ impl RccInfo { }) } + #[allow(dead_code)] pub(crate) fn enable_and_reset_without_stop(&self) { critical_section::with(|cs| self.enable_and_reset_with_cs(cs)) } @@ -336,6 +339,7 @@ impl RccInfo { } // TODO: should this be `unsafe`? + #[allow(dead_code)] pub(crate) fn disable_without_stop(&self) { critical_section::with(|cs| self.disable_with_cs(cs)) } -- cgit From 5792daf3afb9366c362fc57c89870ffb05df8b8c Mon Sep 17 00:00:00 2001 From: Bjorn Beishline <75190918+BjornTheProgrammer@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:28:55 -0700 Subject: Remove atomic-polyfill --- embassy-rp/Cargo.toml | 3 +-- embassy-rp/src/pio/mod.rs | 26 ++++++++++++++++++-------- embassy-rp/src/uart/buffered.rs | 13 ++++++++++--- embassy-rp/src/uart/mod.rs | 28 +++++++++++++++++++++++----- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 9ad4b47a3..421f0b0f6 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -47,7 +47,7 @@ rt = [ "rp-pac/rt" ] defmt = ["dep:defmt", "embassy-usb-driver/defmt", "embassy-hal-internal/defmt"] ## Enable log support log = ["dep:log"] -## Enable chrono support +## 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. @@ -159,7 +159,6 @@ embassy-futures = { version = "0.1.2", path = "../embassy-futures" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } -atomic-polyfill = "1.0.1" defmt = { version = "1.0.1", optional = true } log = { version = "0.4.14", optional = true } nb = "1.1.0" diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index 92b2c603e..fd0b4c072 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -1,11 +1,11 @@ //! PIO driver. +use core::cell::Cell; use core::future::Future; use core::marker::PhantomData; use core::pin::Pin as FuturePin; -use core::sync::atomic::{Ordering, compiler_fence}; +use core::sync::atomic::{AtomicU8, Ordering, compiler_fence}; use core::task::{Context, Poll}; -use atomic_polyfill::{AtomicU8, AtomicU64}; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; use fixed::FixedU32; @@ -1232,7 +1232,10 @@ impl<'d, PIO: Instance> Common<'d, PIO> { w.set_pde(false); }); // we can be relaxed about this because we're &mut here and nothing is cached - PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); + critical_section::with(|_| { + let val = PIO::state().used_pins.get(); + PIO::state().used_pins.set(val | 1 << pin.pin_bank()); + }); Pin { pin: pin.into(), pio: PhantomData::default(), @@ -1369,7 +1372,7 @@ impl<'d, PIO: Instance> Pio<'d, PIO> { /// Create a new instance of a PIO peripheral. pub fn new(_pio: Peri<'d, PIO>, _irq: impl Binding>) -> Self { PIO::state().users.store(5, Ordering::Release); - PIO::state().used_pins.store(0, Ordering::Release); + critical_section::with(|_| PIO::state().used_pins.set(0)); PIO::Interrupt::unpend(); unsafe { PIO::Interrupt::enable() }; @@ -1413,13 +1416,20 @@ impl<'d, PIO: Instance> Pio<'d, PIO> { // other way. pub struct State { users: AtomicU8, - used_pins: AtomicU64, + used_pins: Cell, } +unsafe impl Sync for State {} + fn on_pio_drop() { let state = PIO::state(); - if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { - let used_pins = state.used_pins.load(Ordering::Relaxed); + let users_state = critical_section::with(|_| { + let val = state.users.load(Ordering::Acquire) - 1; + state.users.store(val, Ordering::Release); + val + }); + if users_state == 1 { + let used_pins = critical_section::with(|_| state.used_pins.get()); let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _; for i in 0..crate::gpio::BANK0_PIN_COUNT { if used_pins & (1 << i) != 0 { @@ -1444,7 +1454,7 @@ trait SealedInstance { fn state() -> &'static State { static STATE: State = State { users: AtomicU8::new(0), - used_pins: AtomicU64::new(0), + used_pins: Cell::new(0), }; &STATE diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 02649ad81..fdb8ce776 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,8 +1,8 @@ //! Buffered UART driver. use core::future::Future; use core::slice; +use core::sync::atomic::{AtomicU8, Ordering}; -use atomic_polyfill::AtomicU8; use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use super::*; @@ -241,7 +241,11 @@ impl BufferedUartRx { } fn get_rx_error(state: &State) -> Option { - let errs = state.rx_error.swap(0, Ordering::Relaxed); + let errs = critical_section::with(|_| { + let val = state.rx_error.load(Ordering::Relaxed); + state.rx_error.store(0, Ordering::Relaxed); + val + }); if errs & RXE_OVERRUN != 0 { Some(Error::Overrun) } else if errs & RXE_BREAK != 0 { @@ -555,7 +559,10 @@ impl interrupt::typelevel::Handler for BufferedInterr } let dr = r.uartdr().read(); if (dr.0 >> 8) != 0 { - s.rx_error.fetch_or((dr.0 >> 8) as u8, Ordering::Relaxed); + critical_section::with(|_| { + let val = s.rx_error.load(Ordering::Relaxed); + s.rx_error.store(val | ((dr.0 >> 8) as u8), Ordering::Relaxed); + }); error = true; // only fill the buffer with valid characters. the current character is fine // if the error is an overrun, but if we add it to the buffer we'll report diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 8be87a5d2..b7b569dd5 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -1,9 +1,9 @@ //! UART driver. use core::future::poll_fn; use core::marker::PhantomData; +use core::sync::atomic::{AtomicU16, Ordering}; use core::task::Poll; -use atomic_polyfill::{AtomicU16, Ordering}; use embassy_futures::select::{Either, select}; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; @@ -456,7 +456,12 @@ impl<'d> UartRx<'d, Async> { transfer, poll_fn(|cx| { self.dma_state.rx_err_waker.register(cx.waker()); - match self.dma_state.rx_errs.swap(0, Ordering::Relaxed) { + let rx_errs = critical_section::with(|_| { + let val = self.dma_state.rx_errs.load(Ordering::Relaxed); + self.dma_state.rx_errs.store(0, Ordering::Relaxed); + val + }); + match rx_errs { 0 => Poll::Pending, e => Poll::Ready(Uartris(e as u32)), } @@ -468,7 +473,11 @@ impl<'d> UartRx<'d, Async> { Either::First(()) => { // We're here because the DMA finished, BUT if an error occurred on the LAST // byte, then we may still need to grab the error state! - Uartris(self.dma_state.rx_errs.swap(0, Ordering::Relaxed) as u32) + Uartris(critical_section::with(|_| { + let val = self.dma_state.rx_errs.load(Ordering::Relaxed); + self.dma_state.rx_errs.store(0, Ordering::Relaxed); + val + }) as u32) } Either::Second(e) => { // We're here because we errored, which means this is the error that @@ -616,7 +625,12 @@ impl<'d> UartRx<'d, Async> { transfer, poll_fn(|cx| { self.dma_state.rx_err_waker.register(cx.waker()); - match self.dma_state.rx_errs.swap(0, Ordering::Relaxed) { + let rx_errs = critical_section::with(|_| { + let val = self.dma_state.rx_errs.load(Ordering::Relaxed); + self.dma_state.rx_errs.store(0, Ordering::Relaxed); + val + }); + match rx_errs { 0 => Poll::Pending, e => Poll::Ready(Uartris(e as u32)), } @@ -629,7 +643,11 @@ impl<'d> UartRx<'d, Async> { Either::First(()) => { // We're here because the DMA finished, BUT if an error occurred on the LAST // byte, then we may still need to grab the error state! - Uartris(self.dma_state.rx_errs.swap(0, Ordering::Relaxed) as u32) + Uartris(critical_section::with(|_| { + let val = self.dma_state.rx_errs.load(Ordering::Relaxed); + self.dma_state.rx_errs.store(0, Ordering::Relaxed); + val + }) as u32) } Either::Second(e) => { // We're here because we errored, which means this is the error that -- cgit From f8ec795741663f559295327911a51559c526ba8f Mon Sep 17 00:00:00 2001 From: Bjorn Beishline <75190918+BjornTheProgrammer@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:37:22 -0700 Subject: Update CHANGELOG.md --- embassy-rp/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index fa8609bbf..7480b729f 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add fix #4822 in PIO onewire. Change to disable the state machine before setting y register ([#4824](https://github.com/embassy-rs/embassy/pull/4824)) - Add PIO::Ws2812 color order support - Add TX-only, no SCK SPI support +- Remove atomic-polyfill with critical-section instead ([#4948](https://github.com/embassy-rs/embassy/pull/4948)) ## 0.8.0 - 2025-08-26 @@ -116,4 +117,3 @@ Small release fixing a few gnarly bugs, upgrading is strongly recommended. - rename the Channel trait to Slice and the PwmPin to PwmChannel - i2c: Fix race condition that appears on fast repeated transfers. - Add a basic "read to break" function - -- cgit From edd8878f8cbd15a56a6c845a2a8772a016e24d4b Mon Sep 17 00:00:00 2001 From: Bjorn Beishline <75190918+BjornTheProgrammer@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:05:52 -0700 Subject: Use two AtomicU32 instead --- embassy-rp/src/pio/mod.rs | 56 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index fd0b4c072..1c370fdfc 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -1,9 +1,8 @@ //! PIO driver. -use core::cell::Cell; use core::future::Future; use core::marker::PhantomData; use core::pin::Pin as FuturePin; -use core::sync::atomic::{AtomicU8, Ordering, compiler_fence}; +use core::sync::atomic::{AtomicU8, AtomicU32, Ordering, compiler_fence}; use core::task::{Context, Poll}; use embassy_hal_internal::{Peri, PeripheralType}; @@ -1233,9 +1232,12 @@ impl<'d, PIO: Instance> Common<'d, PIO> { }); // we can be relaxed about this because we're &mut here and nothing is cached critical_section::with(|_| { - let val = PIO::state().used_pins.get(); - PIO::state().used_pins.set(val | 1 << pin.pin_bank()); + let val = PIO::state().used_pins.load(Ordering::Relaxed); + PIO::state() + .used_pins + .store(val | 1 << pin.pin_bank(), Ordering::Relaxed); }); + Pin { pin: pin.into(), pio: PhantomData::default(), @@ -1372,7 +1374,7 @@ impl<'d, PIO: Instance> Pio<'d, PIO> { /// Create a new instance of a PIO peripheral. pub fn new(_pio: Peri<'d, PIO>, _irq: impl Binding>) -> Self { PIO::state().users.store(5, Ordering::Release); - critical_section::with(|_| PIO::state().used_pins.set(0)); + PIO::state().used_pins.store(0, Ordering::Release); PIO::Interrupt::unpend(); unsafe { PIO::Interrupt::enable() }; @@ -1407,6 +1409,42 @@ impl<'d, PIO: Instance> Pio<'d, PIO> { } } +struct AtomicU64 { + upper_32: AtomicU32, + lower_32: AtomicU32, +} + +impl AtomicU64 { + const fn new(val: u64) -> Self { + let upper_32 = (val >> 32) as u32; + let lower_32 = val as u32; + + Self { + upper_32: AtomicU32::new(upper_32), + lower_32: AtomicU32::new(lower_32), + } + } + + fn load(&self, order: Ordering) -> u64 { + let (upper, lower) = critical_section::with(|_| (self.upper_32.load(order), self.lower_32.load(order))); + + let upper = (upper as u64) << 32; + let lower = lower as u64; + + upper | lower + } + + fn store(&self, val: u64, order: Ordering) { + let upper_32 = (val >> 32) as u32; + let lower_32 = val as u32; + + critical_section::with(|_| { + self.upper_32.store(upper_32, order); + self.lower_32.store(lower_32, order); + }); + } +} + /// Representation of the PIO state keeping a record of which pins are assigned to /// each PIO. // make_pio_pin notionally takes ownership of the pin it is given, but the wrapped pin @@ -1416,11 +1454,9 @@ impl<'d, PIO: Instance> Pio<'d, PIO> { // other way. pub struct State { users: AtomicU8, - used_pins: Cell, + used_pins: AtomicU64, } -unsafe impl Sync for State {} - fn on_pio_drop() { let state = PIO::state(); let users_state = critical_section::with(|_| { @@ -1429,7 +1465,7 @@ fn on_pio_drop() { val }); if users_state == 1 { - let used_pins = critical_section::with(|_| state.used_pins.get()); + let used_pins = state.used_pins.load(Ordering::Relaxed); let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _; for i in 0..crate::gpio::BANK0_PIN_COUNT { if used_pins & (1 << i) != 0 { @@ -1454,7 +1490,7 @@ trait SealedInstance { fn state() -> &'static State { static STATE: State = State { users: AtomicU8::new(0), - used_pins: Cell::new(0), + used_pins: AtomicU64::new(0), }; &STATE -- cgit From 0847f4ca4657ea2174fc160f96a69f4c916d146e Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 25 Nov 2025 16:53:26 -0600 Subject: stm32: extract busychannel into common api --- embassy-stm32/src/dma/dma_bdma.rs | 15 ++++--- embassy-stm32/src/dma/gpdma/mod.rs | 10 ++--- embassy-stm32/src/dma/gpdma/ringbuffered.rs | 11 ++--- embassy-stm32/src/dma/mod.rs | 50 ++++----------------- embassy-stm32/src/low_power.rs | 22 +++------- embassy-stm32/src/rcc/mod.rs | 68 ++++++++++++++++++++++++++++- embassy-stm32/src/usart/mod.rs | 12 ++--- 7 files changed, 105 insertions(+), 83 deletions(-) diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index b46ae2813..adc084474 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -8,8 +8,9 @@ use embassy_sync::waitqueue::AtomicWaker; use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use super::word::{Word, WordSize}; -use super::{AnyChannel, BusyChannel, Channel, Dir, Request, STATE}; +use super::{AnyChannel, Channel, Dir, Request, STATE}; use crate::interrupt::typelevel::Interrupt; +use crate::rcc::BusyPeripheral; use crate::{interrupt, pac}; pub(crate) struct ChannelInfo { @@ -602,7 +603,7 @@ impl AnyChannel { /// DMA transfer. #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Transfer<'a> { - channel: BusyChannel<'a>, + channel: BusyPeripheral>, } impl<'a> Transfer<'a> { @@ -714,7 +715,7 @@ impl<'a> Transfer<'a> { ); channel.start(); Self { - channel: BusyChannel::new(channel), + channel: BusyPeripheral::new(channel), } } @@ -818,7 +819,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> { /// Ringbuffer for receiving data using DMA circular mode. pub struct ReadableRingBuffer<'a, W: Word> { - channel: BusyChannel<'a>, + channel: BusyPeripheral>, ringbuf: ReadableDmaRingBuffer<'a, W>, } @@ -855,7 +856,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { ); Self { - channel: BusyChannel::new(channel), + channel: BusyPeripheral::new(channel), ringbuf: ReadableDmaRingBuffer::new(buffer), } } @@ -974,7 +975,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { /// Ringbuffer for writing data using DMA circular mode. pub struct WritableRingBuffer<'a, W: Word> { - channel: BusyChannel<'a>, + channel: BusyPeripheral>, ringbuf: WritableDmaRingBuffer<'a, W>, } @@ -1011,7 +1012,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { ); Self { - channel: BusyChannel::new(channel), + channel: BusyPeripheral::new(channel), ringbuf: WritableDmaRingBuffer::new(buffer), } } diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs index 383c74a78..bfd0570f8 100644 --- a/embassy-stm32/src/dma/gpdma/mod.rs +++ b/embassy-stm32/src/dma/gpdma/mod.rs @@ -11,10 +11,10 @@ use linked_list::Table; use super::word::{Word, WordSize}; use super::{AnyChannel, Channel, Dir, Request, STATE}; -use crate::dma::BusyChannel; use crate::interrupt::typelevel::Interrupt; use crate::pac; use crate::pac::gpdma::vals; +use crate::rcc::BusyPeripheral; pub mod linked_list; pub mod ringbuffered; @@ -409,7 +409,7 @@ impl AnyChannel { /// Linked-list DMA transfer. #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { - channel: BusyChannel<'a>, + channel: BusyPeripheral>, } impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { @@ -431,7 +431,7 @@ impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { channel.start(); Self { - channel: BusyChannel::new(channel), + channel: BusyPeripheral::new(channel), } } @@ -508,7 +508,7 @@ impl<'a, const ITEM_COUNT: usize> Future for LinkedListTransfer<'a, ITEM_COUNT> /// DMA transfer. #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Transfer<'a> { - channel: BusyChannel<'a>, + channel: BusyPeripheral>, } impl<'a> Transfer<'a> { @@ -629,7 +629,7 @@ impl<'a> Transfer<'a> { channel.start(); Self { - channel: BusyChannel::new(channel), + channel: BusyPeripheral::new(channel), } } diff --git a/embassy-stm32/src/dma/gpdma/ringbuffered.rs b/embassy-stm32/src/dma/gpdma/ringbuffered.rs index 54e4d5f71..c150d0b95 100644 --- a/embassy-stm32/src/dma/gpdma/ringbuffered.rs +++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs @@ -12,7 +12,8 @@ use super::{AnyChannel, STATE, TransferOptions}; use crate::dma::gpdma::linked_list::{RunMode, Table}; use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use crate::dma::word::Word; -use crate::dma::{BusyChannel, Channel, Dir, Request}; +use crate::dma::{Channel, Dir, Request}; +use crate::rcc::BusyPeripheral; struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); @@ -49,7 +50,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> { /// Ringbuffer for receiving data using GPDMA linked-list mode. pub struct ReadableRingBuffer<'a, W: Word> { - channel: BusyChannel<'a>, + channel: BusyPeripheral>, ringbuf: ReadableDmaRingBuffer<'a, W>, table: Table<2>, options: TransferOptions, @@ -70,7 +71,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { let table = Table::<2>::new_ping_pong::(request, peri_addr, buffer, Dir::PeripheralToMemory); Self { - channel: BusyChannel::new(channel), + channel: BusyPeripheral::new(channel), ringbuf: ReadableDmaRingBuffer::new(buffer), table, options, @@ -189,7 +190,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { /// Ringbuffer for writing data using GPDMA linked-list mode. pub struct WritableRingBuffer<'a, W: Word> { - channel: BusyChannel<'a>, + channel: BusyPeripheral>, ringbuf: WritableDmaRingBuffer<'a, W>, table: Table<2>, options: TransferOptions, @@ -210,7 +211,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { let table = Table::<2>::new_ping_pong::(request, peri_addr, buffer, Dir::MemoryToPeripheral); Self { - channel: BusyChannel::new(channel), + channel: BusyPeripheral::new(channel), ringbuf: WritableDmaRingBuffer::new(buffer), table, options, diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 4becc2d87..efb324fa6 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -3,14 +3,12 @@ #[cfg(any(bdma, dma))] mod dma_bdma; -use core::ops; #[cfg(any(bdma, dma))] pub use dma_bdma::*; #[cfg(gpdma)] pub(crate) mod gpdma; -use embassy_hal_internal::Peri; #[cfg(gpdma)] pub use gpdma::ringbuffered::*; #[cfg(gpdma)] @@ -27,9 +25,10 @@ pub(crate) use util::*; pub(crate) mod ringbuffer; pub mod word; -use embassy_hal_internal::{PeripheralType, impl_peripheral}; +use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; use crate::interrupt; +use crate::rcc::StoppablePeripheral; /// The direction of a DMA transfer. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -48,6 +47,13 @@ pub type Request = u8; #[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))] pub type Request = (); +impl<'a> StoppablePeripheral for Peri<'a, AnyChannel> { + #[cfg(feature = "low-power")] + fn stop_mode(&self) -> crate::rcc::StopMode { + self.stop_mode + } +} + pub(crate) trait SealedChannel { #[cfg(not(stm32n6))] fn id(&self) -> u8; @@ -103,44 +109,6 @@ macro_rules! dma_channel_impl { }; } -pub(crate) struct BusyChannel<'a> { - channel: Peri<'a, AnyChannel>, -} - -impl<'a> BusyChannel<'a> { - pub fn new(channel: Peri<'a, AnyChannel>) -> Self { - #[cfg(feature = "low-power")] - critical_section::with(|cs| { - crate::rcc::increment_stop_refcount(cs, channel.stop_mode); - }); - - Self { channel } - } -} - -impl<'a> Drop for BusyChannel<'a> { - fn drop(&mut self) { - #[cfg(feature = "low-power")] - critical_section::with(|cs| { - crate::rcc::decrement_stop_refcount(cs, self.stop_mode); - }); - } -} - -impl<'a> ops::Deref for BusyChannel<'a> { - type Target = Peri<'a, AnyChannel>; - - fn deref(&self) -> &Self::Target { - &self.channel - } -} - -impl<'a> ops::DerefMut for BusyChannel<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.channel - } -} - /// Type-erased DMA channel. pub struct AnyChannel { pub(crate) id: u8, diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index bd8290da0..cdf3323fb 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -51,7 +51,7 @@ use embassy_executor::*; use crate::interrupt; pub use crate::rcc::StopMode; -use crate::rcc::{RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2, decrement_stop_refcount, increment_stop_refcount}; +use crate::rcc::{BusyPeripheral, RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2}; use crate::time_driver::get_driver; const THREAD_PENDER: usize = usize::MAX; @@ -59,7 +59,9 @@ const THREAD_PENDER: usize = usize::MAX; static mut EXECUTOR_TAKEN: bool = false; /// Prevent the device from going into the stop mode if held -pub struct DeviceBusy(StopMode); +pub struct DeviceBusy { + _stop_mode: BusyPeripheral, +} impl DeviceBusy { /// Create a new DeviceBusy with stop1. @@ -74,19 +76,9 @@ impl DeviceBusy { /// Create a new DeviceBusy. pub fn new(stop_mode: StopMode) -> Self { - critical_section::with(|cs| { - increment_stop_refcount(cs, stop_mode); - }); - - Self(stop_mode) - } -} - -impl Drop for DeviceBusy { - fn drop(&mut self) { - critical_section::with(|cs| { - decrement_stop_refcount(cs, self.0); - }); + Self { + _stop_mode: BusyPeripheral::new(stop_mode), + } } } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index b6ecc6c18..1dd634cfe 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -4,6 +4,7 @@ #![allow(missing_docs)] // TODO use core::mem::MaybeUninit; +use core::ops; mod bd; pub use bd::*; @@ -112,7 +113,7 @@ pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clo } #[cfg(feature = "low-power")] -pub(crate) fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { +fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { match stop_mode { StopMode::Standby => {} StopMode::Stop2 => unsafe { @@ -125,7 +126,7 @@ pub(crate) fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) } #[cfg(feature = "low-power")] -pub(crate) fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { +fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { match stop_mode { StopMode::Standby => {} StopMode::Stop2 => unsafe { @@ -182,6 +183,12 @@ pub enum StopMode { Standby, } +#[cfg(feature = "low-power")] +type BusyRccPeripheral = BusyPeripheral; + +#[cfg(not(feature = "low-power"))] +type BusyRccPeripheral = (); + impl RccInfo { /// Safety: /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit @@ -297,6 +304,7 @@ impl RccInfo { } } + #[allow(dead_code)] pub(crate) fn increment_stop_refcount_with_cs(&self, _cs: CriticalSection) { #[cfg(feature = "low-power")] increment_stop_refcount(_cs, self.stop_mode); @@ -304,9 +312,11 @@ impl RccInfo { #[allow(dead_code)] pub(crate) fn increment_stop_refcount(&self) { + #[cfg(feature = "low-power")] critical_section::with(|cs| self.increment_stop_refcount_with_cs(cs)) } + #[allow(dead_code)] pub(crate) fn decrement_stop_refcount_with_cs(&self, _cs: CriticalSection) { #[cfg(feature = "low-power")] decrement_stop_refcount(_cs, self.stop_mode); @@ -314,6 +324,7 @@ impl RccInfo { #[allow(dead_code)] pub(crate) fn decrement_stop_refcount(&self) { + #[cfg(feature = "low-power")] critical_section::with(|cs| self.decrement_stop_refcount_with_cs(cs)) } @@ -344,6 +355,12 @@ impl RccInfo { critical_section::with(|cs| self.disable_with_cs(cs)) } + #[allow(dead_code)] + pub(crate) fn block_stop(&self) -> BusyRccPeripheral { + #[cfg(feature = "low-power")] + BusyPeripheral::new(self.stop_mode) + } + fn reset_ptr(&self) -> Option<*mut u32> { if self.reset_offset_or_0xff != 0xff { Some(unsafe { (RCC.as_ptr() as *mut u32).add(self.reset_offset_or_0xff as _) }) @@ -357,6 +374,53 @@ impl RccInfo { } } +pub(crate) trait StoppablePeripheral { + #[cfg(feature = "low-power")] + #[allow(dead_code)] + fn stop_mode(&self) -> StopMode; +} + +#[cfg(feature = "low-power")] +impl<'a> StoppablePeripheral for StopMode { + fn stop_mode(&self) -> StopMode { + *self + } +} + +pub(crate) struct BusyPeripheral { + peripheral: T, +} + +impl BusyPeripheral { + pub fn new(peripheral: T) -> Self { + #[cfg(feature = "low-power")] + critical_section::with(|cs| increment_stop_refcount(cs, peripheral.stop_mode())); + + Self { peripheral } + } +} + +impl Drop for BusyPeripheral { + fn drop(&mut self) { + #[cfg(feature = "low-power")] + critical_section::with(|cs| decrement_stop_refcount(cs, self.peripheral.stop_mode())); + } +} + +impl ops::Deref for BusyPeripheral { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.peripheral + } +} + +impl ops::DerefMut for BusyPeripheral { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.peripheral + } +} + #[allow(unused)] mod util { use crate::time::Hertz; diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 1af78b358..8047d6005 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -491,8 +491,7 @@ impl<'d> UartTx<'d, Async> { /// Initiate an asynchronous UART write pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - self.info.rcc.increment_stop_refcount(); - let _ = OnDrop::new(|| self.info.rcc.decrement_stop_refcount()); + let _ = self.info.rcc.block_stop(); let r = self.info.regs; @@ -511,8 +510,7 @@ impl<'d> UartTx<'d, Async> { /// Wait until transmission complete pub async fn flush(&mut self) -> Result<(), Error> { - self.info.rcc.increment_stop_refcount(); - let _ = OnDrop::new(|| self.info.rcc.decrement_stop_refcount()); + let _ = self.info.rcc.block_stop(); flush(&self.info, &self.state).await } @@ -732,8 +730,7 @@ impl<'d> UartRx<'d, Async> { /// Initiate an asynchronous UART read pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - self.info.rcc.increment_stop_refcount(); - let _ = OnDrop::new(|| self.info.rcc.decrement_stop_refcount()); + let _ = self.info.rcc.block_stop(); self.inner_read(buffer, false).await?; @@ -742,8 +739,7 @@ impl<'d> UartRx<'d, Async> { /// Initiate an asynchronous read with idle line detection enabled pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - self.info.rcc.increment_stop_refcount(); - let _ = OnDrop::new(|| self.info.rcc.decrement_stop_refcount()); + let _ = self.info.rcc.block_stop(); self.inner_read(buffer, true).await } -- cgit From 660fc4f7602c0be689e2c6cb4b20e55a26127636 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 26 Nov 2025 14:35:18 +0100 Subject: feat: support nrf54 GRTC as time-driver * Refactor GRTC peripheral splitting it into multiple channels * Reserve channel 1 for time-driver if enabled * Implement time-driver using GRTC (RTC peripheral is now removed). * Add timer example to nrf54l15 --- embassy-nrf/Cargo.toml | 18 ++-- embassy-nrf/src/chips/nrf54l15_app.rs | 28 +++--- embassy-nrf/src/lib.rs | 1 + embassy-nrf/src/time_driver.rs | 157 ++++++++++++++++++++++++++-------- examples/nrf54l15/Cargo.toml | 2 +- examples/nrf54l15/src/bin/rtc.rs | 56 ------------ examples/nrf54l15/src/bin/timer.rs | 30 +++++++ 7 files changed, 178 insertions(+), 114 deletions(-) delete mode 100644 examples/nrf54l15/src/bin/rtc.rs create mode 100644 examples/nrf54l15/src/bin/timer.rs diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 08f4b280b..0a71094eb 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -23,8 +23,8 @@ build = [ {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf5340-app-s", "time", "time-driver-rtc1"]}, {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf5340-app-ns", "time", "time-driver-rtc1"]}, {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf5340-net", "time", "time-driver-rtc1"]}, - {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l15-app-s", "time", "time-driver-rtc1"]}, - {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l15-app-ns", "time", "time-driver-rtc1"]}, + {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l15-app-s", "time", "time-driver-grtc"]}, + {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l15-app-ns", "time", "time-driver-grtc"]}, {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time"]}, {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time-driver-rtc1"]}, {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time", "time-driver-rtc1"]}, @@ -80,9 +80,10 @@ unstable-pac = [] gpiote = [] ## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz -## -## Note: For nRF54L, it's actually RTC30 -time-driver-rtc1 = ["_time-driver"] +time-driver-rtc1 = ["_time-driver", "embassy-time-driver?/tick-hz-32_768"] + +## Use GRTC (CC n=1, GRTC_1 irq) as the time driver for `embassy-time`, with a tick rate of 1 MHz +time-driver-grtc = ["_time-driver", "embassy-time-driver?/tick-hz-1_000_000"] ## Enable embassy-net 802.15.4 driver net-driver = ["_net-driver"] @@ -152,7 +153,7 @@ _nrf5340-net = ["_nrf5340", "nrf-pac/nrf5340-net"] _nrf5340 = ["_gpio-p1", "_dppi"] _nrf54l15-app = ["_nrf54l15", "nrf-pac/nrf54l15-app"] _nrf54l15 = ["_nrf54l", "_gpio-p1", "_gpio-p2"] -_nrf54l = ["_dppi"] +_nrf54l = ["_dppi", "_grtc"] _nrf9160 = ["nrf-pac/nrf9160", "_dppi", "_spi-v1"] _nrf9120 = ["nrf-pac/nrf9120", "_dppi", "_spi-v1"] @@ -160,7 +161,7 @@ _nrf52 = ["_ppi"] _nrf51 = ["_ppi", "_spi-v1"] _nrf91 = [] -_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"] +_time-driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"] _net-driver = ["dep:embassy-net-driver-channel","dep:embassy-futures"] @@ -173,6 +174,7 @@ _dppi = [] _gpio-p1 = [] _gpio-p2 = [] _spi-v1 = [] +_grtc = [] # Errata workarounds _nrf52832_anomaly_109 = [] @@ -200,7 +202,7 @@ embedded-io-async = { version = "0.6.1" } rand-core-06 = { package = "rand_core", version = "0.6" } rand-core-09 = { package = "rand_core", version = "0.9" } -nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "58198c23bce72edc10b4e1656d1b54441fc74e7c" } +nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "0068816c482932e3c59964817610ef41e79a6843" } defmt = { version = "1.0.1", optional = true } bitflags = "2.4.2" diff --git a/embassy-nrf/src/chips/nrf54l15_app.rs b/embassy-nrf/src/chips/nrf54l15_app.rs index 8846717db..b6c9fd110 100644 --- a/embassy-nrf/src/chips/nrf54l15_app.rs +++ b/embassy-nrf/src/chips/nrf54l15_app.rs @@ -26,7 +26,6 @@ pub mod pac { PPIB10_NS as PPIB10, PPIB11_NS as PPIB11, TIMER10_NS as TIMER10, - RTC10_NS as RTC10, EGU10_NS as EGU10, RADIO_NS as RADIO, DPPIC20_NS as DPPIC20, @@ -76,7 +75,6 @@ pub mod pac { TWIM30_NS as TWIM30, TWIS30_NS as TWIS30, UARTE30_NS as UARTE30, - RTC30_NS as RTC30, COMP_NS as COMP, LPCOMP_NS as LPCOMP, WDT31_NS as WDT31, @@ -127,7 +125,6 @@ pub mod pac { PPIB10_S as PPIB10, PPIB11_S as PPIB11, TIMER10_S as TIMER10, - RTC10_S as RTC10, EGU10_S as EGU10, RADIO_S as RADIO, SPU20_S as SPU20, @@ -180,7 +177,6 @@ pub mod pac { TWIM30_S as TWIM30, TWIS30_S as TWIS30, UARTE30_S as UARTE30, - RTC30_S as RTC30, COMP_S as COMP, LPCOMP_S as LPCOMP, WDT30_S as WDT30, @@ -417,11 +413,19 @@ embassy_hal_internal::peripherals! { P2_10, // GRTC - GRTC, - - // RTC - RTC10, - RTC30, + GRTC_CH0, + #[cfg(not(feature = "time-driver-grtc"))] + GRTC_CH1, + GRTC_CH2, + GRTC_CH3, + GRTC_CH4, + GRTC_CH5, + GRTC_CH6, + GRTC_CH7, + GRTC_CH8, + GRTC_CH9, + GRTC_CH10, + GRTC_CH11, // PWM PWM20, @@ -548,9 +552,6 @@ cfg_if::cfg_if! { } } -impl_rtc!(RTC10, RTC10, RTC10); -impl_rtc!(RTC30, RTC30, RTC30); - #[cfg(feature = "_ns")] impl_wdt!(WDT, WDT31, WDT31, 0); #[cfg(feature = "_s")] @@ -703,7 +704,6 @@ embassy_hal_internal::interrupt_mod!( TIMER00, SPU10, TIMER10, - RTC10, EGU10, RADIO_0, RADIO_1, @@ -737,11 +737,9 @@ embassy_hal_internal::interrupt_mod!( GRTC_3, SPU30, SERIAL30, - RTC30, COMP_LPCOMP, WDT30, WDT31, GPIOTE30_0, GPIOTE30_1, - CLOCK_POWER, ); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 28d2119ae..e8762b00d 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -153,6 +153,7 @@ pub mod reset; #[cfg(not(feature = "_nrf54l"))] #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] pub mod rng; +#[cfg(not(feature = "_nrf54l"))] pub mod rtc; #[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod saadc; diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index b723e2334..de1974fb7 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -1,4 +1,5 @@ use core::cell::{Cell, RefCell}; +#[cfg(not(feature = "_grtc"))] use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; use critical_section::CriticalSection; @@ -8,21 +9,27 @@ use embassy_time_driver::Driver; use embassy_time_queue_utils::Queue; use crate::interrupt::InterruptExt; +#[cfg(feature = "_grtc")] +use crate::pac::grtc::vals::Busy; use crate::{interrupt, pac}; -#[cfg(feature = "_nrf54l")] -fn rtc() -> pac::rtc::Rtc { - pac::RTC30 +#[cfg(feature = "_grtc")] +fn rtc() -> pac::grtc::Grtc { + pac::GRTC } -#[cfg(not(feature = "_nrf54l"))] + +#[cfg(not(feature = "_grtc"))] fn rtc() -> pac::rtc::Rtc { pac::RTC1 } /// Calculate the timestamp from the period count and the tick count. /// -/// The RTC counter is 24 bit. Ticking at 32768hz, it overflows every ~8 minutes. This is -/// too short. We must make it "never" overflow. +/// For nRF54 devices and newer, the GRTC counter is 52 bits, so the time driver uses the +/// syscounter and ignores the periods handling, since it overflows every 142 years. +/// +/// For most other devices, the RTC counter is 24 bit. Ticking at 32768hz, it overflows every ~8 minutes. +/// This is too short. We must make it "never" overflow. /// /// The obvious way would be to count overflow periods. Every time the counter overflows, /// increase a `periods` variable. `now()` simply does `periods << 24 + counter`. So, the logic @@ -64,14 +71,39 @@ fn rtc() -> pac::rtc::Rtc { /// `period` is a 32bit integer, so It overflows on 2^32 * 2^23 / 32768 seconds of uptime, which is 34865 /// years. For comparison, flash memory like the one containing your firmware is usually rated to retain /// data for only 10-20 years. 34865 years is long enough! +#[cfg(not(feature = "_grtc"))] fn calc_now(period: u32, counter: u32) -> u64 { ((period as u64) << 23) + ((counter ^ ((period & 1) << 23)) as u64) } +#[cfg(feature = "_grtc")] +fn syscounter() -> u64 { + let r = rtc(); + r.syscounter(0).active().write(|w| w.set_active(true)); + loop { + let countl: u32 = r.syscounter(0).syscounterl().read(); + let counth = r.syscounter(0).syscounterh().read(); + + if counth.busy() == Busy::READY && !counth.overflow() { + let counth: u32 = counth.value(); + let count = countl as u64 | ((counth as u64) << 32); + r.syscounter(0).active().write(|w| w.set_active(false)); + return count; + } + // If overflow or not ready, loop will re-read both registers + } +} + +#[cfg(not(feature = "_grtc"))] fn compare_n(n: usize) -> u32 { 1 << (n + 16) } +#[cfg(feature = "_grtc")] +fn compare_n(n: usize) -> u32 { + 1 << n // GRTC uses bits 0-11 for COMPARE[0-11] +} + #[cfg(test)] mod test { use super::*; @@ -108,6 +140,7 @@ impl AlarmState { struct RtcDriver { /// Number of 2^23 periods elapsed since boot. + #[cfg(not(feature = "_grtc"))] period: AtomicU32, /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. alarms: Mutex, @@ -115,6 +148,7 @@ struct RtcDriver { } embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { + #[cfg(not(feature = "_grtc"))] period: AtomicU32::new(0), alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), queue: Mutex::new(RefCell::new(Queue::new())), @@ -123,25 +157,48 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { impl RtcDriver { fn init(&'static self, irq_prio: crate::interrupt::Priority) { let r = rtc(); - r.cc(3).write(|w| w.set_compare(0x800000)); + // Chips without GRTC needs to deal with overflow + #[cfg(not(feature = "_grtc"))] + { + r.cc(3).write(|w| w.set_compare(0x800000)); - r.intenset().write(|w| { - w.set_ovrflw(true); - w.set_compare(3, true); - }); + r.intenset().write(|w| { + w.set_ovrflw(true); + w.set_compare(3, true); + }); + } + #[cfg(feature = "_grtc")] + { + r.mode().write(|w| { + w.set_syscounteren(true); + }); + } r.tasks_clear().write_value(1); r.tasks_start().write_value(1); // Wait for clear + #[cfg(not(feature = "_grtc"))] while r.counter().read().0 != 0 {} - #[cfg(feature = "_nrf54l")] + #[cfg(feature = "_grtc")] + { + // According to datasheet, we need to wait for STATUS.LFTIMER.READY + // before the timer is actually started + loop { + if r.status().lftimer().read().ready() { + break; + } + } + // Keep SYSCOUNTER[0] always active so we can read it anytime without BUSY waits + } + + #[cfg(feature = "_grtc")] { - interrupt::RTC30.set_priority(irq_prio); - unsafe { interrupt::RTC30.enable() }; + interrupt::GRTC_1.set_priority(irq_prio); + unsafe { interrupt::GRTC_1.enable() }; } - #[cfg(not(feature = "_nrf54l"))] + #[cfg(not(feature = "_grtc"))] { interrupt::RTC1.set_priority(irq_prio); unsafe { interrupt::RTC1.enable() }; @@ -150,11 +207,14 @@ impl RtcDriver { fn on_interrupt(&self) { let r = rtc(); + + #[cfg(not(feature = "_grtc"))] if r.events_ovrflw().read() == 1 { r.events_ovrflw().write_value(0); self.next_period(); } + #[cfg(not(feature = "_grtc"))] if r.events_compare(3).read() == 1 { r.events_compare(3).write_value(0); self.next_period(); @@ -169,6 +229,7 @@ impl RtcDriver { } } + #[cfg(not(feature = "_grtc"))] fn next_period(&self) { critical_section::with(|cs| { let r = rtc(); @@ -190,7 +251,10 @@ impl RtcDriver { fn trigger_alarm(&self, cs: CriticalSection) { let n = 0; let r = rtc(); + #[cfg(not(feature = "_grtc"))] r.intenclr().write(|w| w.0 = compare_n(n)); + #[cfg(feature = "_grtc")] + r.intenclr(1).write(|w| w.0 = compare_n(n)); let alarm = &self.alarms.borrow(cs); alarm.timestamp.set(u64::MAX); @@ -214,7 +278,10 @@ impl RtcDriver { if timestamp <= t { // If alarm timestamp has passed the alarm will not fire. // Disarm the alarm and return `false` to indicate that. + #[cfg(not(feature = "_grtc"))] r.intenclr().write(|w| w.0 = compare_n(n)); + #[cfg(feature = "_grtc")] + r.intenclr(1).write(|w| w.0 = compare_n(n)); alarm.timestamp.set(u64::MAX); @@ -226,7 +293,7 @@ impl RtcDriver { // Write the CC value regardless of whether we're going to enable it now or not. // This way, when we enable it later, the right value is already set. - // nrf52 docs say: + // nrf52 docs say : // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event. // To workaround this, we never write a timestamp smaller than N+3. // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc. @@ -238,22 +305,39 @@ impl RtcDriver { // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time, // and we don't do that here. - let safe_timestamp = timestamp.max(t + 3); - r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF)); - - let diff = timestamp - t; - if diff < 0xc00000 { - r.intenset().write(|w| w.0 = compare_n(n)); - - // If we have not passed the timestamp, we can be sure the alarm will be invoked. Otherwise, - // we need to retry setting the alarm. - if self.now() + 2 <= timestamp { + #[cfg(not(feature = "_grtc"))] + { + let safe_timestamp = timestamp.max(t + 3); + r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF)); + let diff = timestamp - t; + if diff < 0xc00000 { + r.intenset().write(|w| w.0 = compare_n(n)); + + // If we have not passed the timestamp, we can be sure the alarm will be invoked. Otherwise, + // we need to retry setting the alarm. + if self.now() + 2 <= timestamp { + return true; + } + } else { + // If it's too far in the future, don't setup the compare channel yet. + // It will be setup later by `next_period`. + r.intenclr().write(|w| w.0 = compare_n(n)); return true; } - } else { - // If it's too far in the future, don't setup the compare channel yet. - // It will be setup later by `next_period`. - r.intenclr().write(|w| w.0 = compare_n(n)); + } + + // The nRF54 datasheet states that 'The EVENTS_COMPARE[n] event is generated immediately if the + // configured compare value at CC[n] is less than the current SYSCOUNTER value.'. This means we + // can write the expected timestamp and be sure the alarm is triggered. + #[cfg(feature = "_grtc")] + { + let ccl = timestamp as u32; + let cch = (timestamp >> 32) as u32 & 0xFFFFF; // 20 bits for CCH + + r.cc(n).ccl().write_value(ccl); + r.cc(n).cch().write(|w| w.set_cch(cch)); + r.intenset(1).write(|w| w.0 = compare_n(n)); + return true; } } @@ -261,6 +345,7 @@ impl RtcDriver { } impl Driver for RtcDriver { + #[cfg(not(feature = "_grtc"))] fn now(&self) -> u64 { // `period` MUST be read before `counter`, see comment at the top for details. let period = self.period.load(Ordering::Relaxed); @@ -269,10 +354,14 @@ impl Driver for RtcDriver { calc_now(period, counter) } + #[cfg(feature = "_grtc")] + fn now(&self) -> u64 { + syscounter() + } + fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { critical_section::with(|cs| { let mut queue = self.queue.borrow(cs).borrow_mut(); - if queue.schedule_wake(at, waker) { let mut next = queue.next_expiration(self.now()); while !self.set_alarm(cs, next) { @@ -283,14 +372,14 @@ impl Driver for RtcDriver { } } -#[cfg(feature = "_nrf54l")] +#[cfg(feature = "_grtc")] #[cfg(feature = "rt")] #[interrupt] -fn RTC30() { +fn GRTC_1() { DRIVER.on_interrupt() } -#[cfg(not(feature = "_nrf54l"))] +#[cfg(not(feature = "_grtc"))] #[cfg(feature = "rt")] #[interrupt] fn RTC1() { diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml index 4ef77279f..f34df0f26 100644 --- a/examples/nrf54l15/Cargo.toml +++ b/examples/nrf54l15/Cargo.toml @@ -10,7 +10,7 @@ embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } 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-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } -embassy-nrf = { version = "0.8.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-grtc", "gpiote", "unstable-pac", "time"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } diff --git a/examples/nrf54l15/src/bin/rtc.rs b/examples/nrf54l15/src/bin/rtc.rs deleted file mode 100644 index a45aaca52..000000000 --- a/examples/nrf54l15/src/bin/rtc.rs +++ /dev/null @@ -1,56 +0,0 @@ -#![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::Mutex; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -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!("nRF54L15 RTC example"); - let p = embassy_nrf::init(Default::default()); - let mut led = Output::new(p.P2_09, Level::High, OutputDrive::Standard); - // Counter resolution is 125 ms. - let mut rtc = Rtc::new(p.RTC10, (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 RTC10() { - // 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); - }); -} diff --git a/examples/nrf54l15/src/bin/timer.rs b/examples/nrf54l15/src/bin/timer.rs new file mode 100644 index 000000000..68acc91c1 --- /dev/null +++ b/examples/nrf54l15/src/bin/timer.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn run1() { + loop { + info!("BIG INFREQUENT TICK"); + Timer::after_secs(10).await; + } +} + +#[embassy_executor::task] +async fn run2() { + loop { + info!("tick"); + Timer::after_secs(1).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let _p = embassy_nrf::init(Default::default()); + spawner.spawn(unwrap!(run1())); + spawner.spawn(unwrap!(run2())); +} -- cgit From 9842b8b8927ff3de0bde2bf78a49b3d52bd20e5a Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 26 Nov 2025 14:36:57 +0100 Subject: fix: update saadc after API changes --- embassy-nrf/src/saadc.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index ca8cbd73e..ab0e2b86a 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -771,7 +771,7 @@ pub(crate) trait SealedInput { fn port(&self) -> u8; #[cfg(feature = "_nrf54l")] - fn internal(&self) -> vals::Internal; + fn internal(&self) -> vals::PselpInternal; #[cfg(feature = "_nrf54l")] fn connect(&self) -> vals::PselpConnect; @@ -832,7 +832,7 @@ pub struct AnyInput<'a> { pub struct AnyInput<'a> { pin: u8, port: u8, - internal: vals::Internal, + internal: vals::PselpInternal, connect: vals::PselpConnect, _phantom: PhantomData<&'a ()>, } @@ -881,7 +881,7 @@ impl SealedInput for AnyInput<'_> { } #[cfg(feature = "_nrf54l")] - fn internal(&self) -> vals::Internal { + fn internal(&self) -> vals::PselpInternal { self.internal } @@ -923,8 +923,8 @@ macro_rules! impl_saadc_input { $port } - fn internal(&self) -> crate::pac::saadc::vals::Internal { - crate::pac::saadc::vals::Internal::$internal + fn internal(&self) -> crate::pac::saadc::vals::PselpInternal { + crate::pac::saadc::vals::PselpInternal::$internal } fn connect(&self) -> crate::pac::saadc::vals::PselpConnect { -- cgit From cb293d866ed26d2b6c8018450ece26d84089e137 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 26 Nov 2025 14:41:11 +0100 Subject: chore: update changelog --- embassy-nrf/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index cfb040ef5..5657ddcfb 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -8,7 +8,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate -- added: Add basic RTC support for nRF54L - changed: apply trimming values from FICR.TRIMCNF on nrf53/54l - changed: do not panic on BufferedUarte overrun - added: allow direct access to the input pin of `gpiote::InputChannel` @@ -29,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - bugfix: use correct flash size for nRF54l - changed: add workaround for anomaly 66 on nrf52 - added: expose PPI events available on SPIS peripheral +- added: add basic GRTC time driver support for nRF54L ## 0.8.0 - 2025-09-30 -- cgit From da96ef9d51730975b52fa2c92ec27236b68f1daf Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 26 Nov 2025 14:42:46 +0100 Subject: chore: cleanup --- embassy-nrf/src/time_driver.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index de1974fb7..35f65bd64 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -182,15 +182,10 @@ impl RtcDriver { while r.counter().read().0 != 0 {} #[cfg(feature = "_grtc")] - { - // According to datasheet, we need to wait for STATUS.LFTIMER.READY - // before the timer is actually started - loop { - if r.status().lftimer().read().ready() { - break; - } + loop { + if r.status().lftimer().read().ready() { + break; } - // Keep SYSCOUNTER[0] always active so we can read it anytime without BUSY waits } #[cfg(feature = "_grtc")] -- cgit From 372e436985938d4a0de4403b31177e97762b2c79 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 26 Nov 2025 15:07:50 +0100 Subject: fix: update pac version for nrf91-net --- embassy-net-nrf91/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml index 75b7aeeb2..5c0c2e6b2 100644 --- a/embassy-net-nrf91/Cargo.toml +++ b/embassy-net-nrf91/Cargo.toml @@ -18,7 +18,7 @@ log = ["dep:log"] defmt = { version = "1.0.1", optional = true } log = { version = "0.4.14", optional = true } -nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "58198c23bce72edc10b4e1656d1b54441fc74e7c" } +nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "0068816c482932e3c59964817610ef41e79a6843" } cortex-m = "0.7.7" embassy-time = { version = "0.5.0", path = "../embassy-time" } -- cgit From dfd0d31ff61b2efa5561fb44fc08a6d7ea4e5d41 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 26 Nov 2025 09:26:55 -0600 Subject: remove rp test --- ci.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci.sh b/ci.sh index b4ed0dc18..cee761500 100755 --- a/ci.sh +++ b/ci.sh @@ -65,6 +65,7 @@ rm out/tests/pimoroni-pico-plus-2/pwm # flaky rm out/tests/rpi-pico/pwm rm out/tests/rpi-pico/cyw43-perf +rm out/tests/rpi-pico/uart_buffered # tests are implemented but the HIL test farm doesn't actually have these boards, yet rm -rf out/tests/stm32c071rb -- cgit From ec93a56a2e5ba4403a603b9015daa2e94712917b Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 27 Nov 2025 09:22:24 +0100 Subject: fix: patch back clock power irq --- embassy-net-nrf91/Cargo.toml | 2 +- embassy-nrf/Cargo.toml | 2 +- embassy-nrf/src/chips/nrf54l15_app.rs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml index 5c0c2e6b2..8cf11f1fb 100644 --- a/embassy-net-nrf91/Cargo.toml +++ b/embassy-net-nrf91/Cargo.toml @@ -18,7 +18,7 @@ log = ["dep:log"] defmt = { version = "1.0.1", optional = true } log = { version = "0.4.14", optional = true } -nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "0068816c482932e3c59964817610ef41e79a6843" } +nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "176dc4afe1dd8df78f3cbda4479ab5151aa32252" } cortex-m = "0.7.7" embassy-time = { version = "0.5.0", path = "../embassy-time" } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 0a71094eb..f73772682 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -202,7 +202,7 @@ embedded-io-async = { version = "0.6.1" } rand-core-06 = { package = "rand_core", version = "0.6" } rand-core-09 = { package = "rand_core", version = "0.9" } -nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "0068816c482932e3c59964817610ef41e79a6843" } +nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "176dc4afe1dd8df78f3cbda4479ab5151aa32252" } defmt = { version = "1.0.1", optional = true } bitflags = "2.4.2" diff --git a/embassy-nrf/src/chips/nrf54l15_app.rs b/embassy-nrf/src/chips/nrf54l15_app.rs index b6c9fd110..8e6595248 100644 --- a/embassy-nrf/src/chips/nrf54l15_app.rs +++ b/embassy-nrf/src/chips/nrf54l15_app.rs @@ -742,4 +742,5 @@ embassy_hal_internal::interrupt_mod!( WDT31, GPIOTE30_0, GPIOTE30_1, + CLOCK_POWER, ); -- cgit From e5651b8644877969859505efc6d6c872b5c71b7a Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 27 Nov 2025 14:21:09 +0100 Subject: chore: release embassy-rp, cyw43 and cyw43-pio --- cyw43-pio/CHANGELOG.md | 2 ++ cyw43-pio/Cargo.toml | 6 +++--- cyw43/CHANGELOG.md | 2 ++ cyw43/Cargo.toml | 2 +- embassy-boot-rp/CHANGELOG.md | 2 ++ embassy-boot-rp/Cargo.toml | 4 ++-- embassy-rp/CHANGELOG.md | 2 ++ embassy-rp/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 4 ++-- examples/rp/Cargo.toml | 6 +++--- examples/rp235x/Cargo.toml | 6 +++--- tests/rp/Cargo.toml | 2 +- 12 files changed, 24 insertions(+), 16 deletions(-) diff --git a/cyw43-pio/CHANGELOG.md b/cyw43-pio/CHANGELOG.md index c2d18919c..80343e02e 100644 --- a/cyw43-pio/CHANGELOG.md +++ b/cyw43-pio/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.9.0 - 2025-11-27 + - Select pio program based on core clock speed #4792 ## 0.8.0 - 2025-08-28 diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index a10a091e9..3001fa92b 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cyw43-pio" -version = "0.8.0" +version = "0.9.0" edition = "2024" description = "RP2040 PIO SPI implementation for cyw43" keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] @@ -10,8 +10,8 @@ repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/cyw43-pio" [dependencies] -cyw43 = { version = "0.5.0", path = "../cyw43" } -embassy-rp = { version = "0.8.0", path = "../embassy-rp" } +cyw43 = { version = "0.6.0", path = "../cyw43" } +embassy-rp = { version = "0.9.0", path = "../embassy-rp" } fixed = "1.23.1" defmt = { version = "1.0.1", optional = true } diff --git a/cyw43/CHANGELOG.md b/cyw43/CHANGELOG.md index 1045fd30b..9fe341357 100644 --- a/cyw43/CHANGELOG.md +++ b/cyw43/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.6.0 - 2025-11-27 + - Updated documentation for Control::join() #4678 - Bump bt-hci to 0.6.0. - Add error handling to HCI transport implementation. diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index 314427611..6d7647697 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cyw43" -version = "0.5.0" +version = "0.6.0" edition = "2024" description = "Rust driver for the CYW43439 WiFi chip, used in the Raspberry Pi Pico W." keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] diff --git a/embassy-boot-rp/CHANGELOG.md b/embassy-boot-rp/CHANGELOG.md index 8cc1e73c0..068075b5c 100644 --- a/embassy-boot-rp/CHANGELOG.md +++ b/embassy-boot-rp/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.9.0 - 2025-11-27 + ## 0.8.0 - 2025-08-26 ## 0.1.1 - 2025-08-15 diff --git a/embassy-boot-rp/Cargo.toml b/embassy-boot-rp/Cargo.toml index e80c79374..2f63dea4c 100644 --- a/embassy-boot-rp/Cargo.toml +++ b/embassy-boot-rp/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2024" name = "embassy-boot-rp" -version = "0.8.0" +version = "0.9.0" description = "Bootloader lib for RP2040 chips" license = "MIT OR Apache-2.0" repository = "https://github.com/embassy-rs/embassy" @@ -31,7 +31,7 @@ defmt = { version = "1.0.1", optional = true } log = { version = "0.4", optional = true } embassy-sync = { version = "0.7.2", path = "../embassy-sync" } -embassy-rp = { version = "0.8.0", path = "../embassy-rp", default-features = false } +embassy-rp = { version = "0.9.0", path = "../embassy-rp", default-features = false } embassy-boot = { version = "0.6.1", path = "../embassy-boot" } embassy-time = { version = "0.5.0", path = "../embassy-time" } diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index 7480b729f..773301b0f 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.9.0 - 2025-11-27 + - Add documentation for pio `get_x` about autopush. - Fix several minor typos in documentation - Add PIO SPI diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 421f0b0f6..8e4bb927f 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-rp" -version = "0.8.0" +version = "0.9.0" edition = "2024" license = "MIT OR Apache-2.0" description = "Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 or RP235x microcontroller" diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 70a2c28c3..292d1abec 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -9,8 +9,8 @@ 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"] } embassy-time = { version = "0.5.0", path = "../../../../embassy-time", features = [] } -embassy-rp = { version = "0.8.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } -embassy-boot-rp = { version = "0.8.0", path = "../../../../embassy-boot-rp", features = [] } +embassy-rp = { version = "0.9.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } +embassy-boot-rp = { version = "0.9.0", path = "../../../../embassy-boot-rp", features = [] } embassy-embedded-hal = { version = "0.5.0", path = "../../../../embassy-embedded-hal" } defmt = "1.0.1" diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 9d7d99259..e247f6f7a 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -11,14 +11,14 @@ embassy-embedded-hal = { version = "0.5.0", path = "../../embassy-embedded-hal", 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-rp = { version = "0.8.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } +embassy-rp = { version = "0.9.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] } embassy-net-wiznet = { version = "0.2.1", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.5.1", path = "../../embassy-usb-logger" } -cyw43 = { version = "0.5.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } -cyw43-pio = { version = "0.8.0", path = "../../cyw43-pio", features = ["defmt"] } +cyw43 = { version = "0.6.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } +cyw43-pio = { version = "0.9.0", path = "../../cyw43-pio", features = ["defmt"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index ad396275b..16dfb5b77 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -11,14 +11,14 @@ embassy-embedded-hal = { version = "0.5.0", path = "../../embassy-embedded-hal", 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-rp = { version = "0.8.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } +embassy-rp = { version = "0.9.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } embassy-net-wiznet = { version = "0.2.1", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.5.1", path = "../../embassy-usb-logger" } -cyw43 = { version = "0.5.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } -cyw43-pio = { version = "0.8.0", path = "../../cyw43-pio", features = ["defmt"] } +cyw43 = { version = "0.6.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } +cyw43-pio = { version = "0.9.0", path = "../../cyw43-pio", features = ["defmt"] } defmt = "1.0.1" defmt-rtt = "1.0.0" diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 640e58f11..4dfe6904e 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -16,7 +16,7 @@ teleprobe-meta = "1.1" 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", ] } -embassy-rp = { version = "0.8.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } +embassy-rp = { version = "0.9.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } embassy-net-wiznet = { version = "0.2.1", path = "../../embassy-net-wiznet", features = ["defmt"] } -- cgit From e34c43d72b77e78cb971a73c59efa3e746db331f Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Thu, 27 Nov 2025 20:53:02 +0100 Subject: Add error messages to calc_can_timings --- embassy-stm32/src/can/enums.rs | 37 +++++++++++++++++++++++++++++++++++++ embassy-stm32/src/can/util.rs | 27 +++++++++++++++------------ 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/embassy-stm32/src/can/enums.rs b/embassy-stm32/src/can/enums.rs index 6d91020fc..0b2fd9901 100644 --- a/embassy-stm32/src/can/enums.rs +++ b/embassy-stm32/src/can/enums.rs @@ -82,3 +82,40 @@ pub enum RefCountOp { /// Notify sender destroyed NotifySenderDestroyed, } + +/// Error returned when calculating the can timing fails +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TimingCalcError { + /// Bitrate is lower than 1000 + BitrateTooLow { + /// The set bitrate + bitrate: u32 + }, + /// No solution possible + NoSolution { + /// The sum of BS1 and BS2 + bs1_bs2_sum: u8 + }, + /// Prescaler is not 1 < prescaler < 1024 + InvalidPrescaler { + /// The calculated prescaler value + prescaler: u32 + }, + /// BS1 or BS2 are not in the range 0 < BSx < BSx_MAX + BSNotInRange { + /// The value of BS1 + bs1: u8, + /// The value of BS2 + bs2: u8 + }, + /// Final bitrate doesn't match the requested bitrate + NoMatch { + /// The requested bitrate + requested: u32, + /// The calculated bitrate + final_calculated: u32 + }, + /// core::num::NonZeroUxx::new error + CoreNumNew, +} diff --git a/embassy-stm32/src/can/util.rs b/embassy-stm32/src/can/util.rs index 6d7f0c16a..e93fe607d 100644 --- a/embassy-stm32/src/can/util.rs +++ b/embassy-stm32/src/can/util.rs @@ -2,6 +2,8 @@ use core::num::{NonZeroU8, NonZeroU16}; +use crate::can::enums::TimingCalcError; + /// Shared struct to represent bit timings used by calc_can_timings. #[derive(Clone, Copy, Debug)] pub struct NominalBitTiming { @@ -17,7 +19,7 @@ pub struct NominalBitTiming { } /// Calculate nominal CAN bit timing based on CAN bitrate and periphial clock frequency -pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> Option { +pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> Result { const BS1_MAX: u8 = 16; const BS2_MAX: u8 = 8; const MAX_SAMPLE_POINT_PERMILL: u16 = 900; @@ -25,7 +27,7 @@ pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> O let periph_clock = periph_clock.0; if can_bitrate < 1000 { - return None; + return Err(TimingCalcError::BitrateTooLow { bitrate: can_bitrate }); } // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG @@ -53,14 +55,14 @@ pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> O let mut bs1_bs2_sum = max_quanta_per_bit - 1; while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 { if bs1_bs2_sum <= 2 { - return None; // No solution + return Err(TimingCalcError::NoSolution { bs1_bs2_sum }); // No solution } bs1_bs2_sum -= 1; } let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32; if (prescaler < 1) || (prescaler > 1024) { - return None; // No solution + return Err(TimingCalcError::InvalidPrescaler { prescaler }); // No solution } // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. @@ -93,22 +95,23 @@ pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> O // Check is BS1 and BS2 are in range if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) { - return None; + return Err(TimingCalcError::BSNotInRange { bs1, bs2 }); } + let calculated = periph_clock / (prescaler * (1 + bs1 + bs2) as u32); // Check if final bitrate matches the requested - if can_bitrate != (periph_clock / (prescaler * (1 + bs1 + bs2) as u32)) { - return None; + if can_bitrate != calculated { + return Err(TimingCalcError::NoMatch { requested: can_bitrate, final_calculated: calculated }); } // One is recommended by DS-015, CANOpen, and DeviceNet - let sync_jump_width = core::num::NonZeroU8::new(1)?; + let sync_jump_width = core::num::NonZeroU8::new(1).ok_or(TimingCalcError::CoreNumNew)?; - let seg1 = core::num::NonZeroU8::new(bs1)?; - let seg2 = core::num::NonZeroU8::new(bs2)?; - let nz_prescaler = core::num::NonZeroU16::new(prescaler as u16)?; + let seg1 = core::num::NonZeroU8::new(bs1).ok_or(TimingCalcError::CoreNumNew)?; + let seg2 = core::num::NonZeroU8::new(bs2).ok_or(TimingCalcError::CoreNumNew)?; + let nz_prescaler = core::num::NonZeroU16::new(prescaler as u16).ok_or(TimingCalcError::CoreNumNew)?; - Some(NominalBitTiming { + Ok(NominalBitTiming { sync_jump_width, prescaler: nz_prescaler, seg1, -- cgit From 39e9e80150d937d48e287ef913e1b0673cba91b1 Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Thu, 27 Nov 2025 23:17:47 +0100 Subject: Fmt files, add changelog entry --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/can/enums.rs | 28 ++++++++++++++-------------- embassy-stm32/src/can/util.rs | 10 ++++++++-- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 4c38b0add..2b273482c 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -84,6 +84,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - removal: ExtiInput no longer accepts AnyPin/AnyChannel; AnyChannel removed entirely - fix: build script ensures EXTI2_TSC is listed as the IRQ of EXTI2 even if the PAC doesn't - feat: stm32/lcd: added implementation +- change: add error messages to can timing calculations ([#4961](https://github.com/embassy-rs/embassy/pull/4961)) ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/can/enums.rs b/embassy-stm32/src/can/enums.rs index 0b2fd9901..c5900cadc 100644 --- a/embassy-stm32/src/can/enums.rs +++ b/embassy-stm32/src/can/enums.rs @@ -88,33 +88,33 @@ pub enum RefCountOp { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum TimingCalcError { /// Bitrate is lower than 1000 - BitrateTooLow { - /// The set bitrate - bitrate: u32 + BitrateTooLow { + /// The set bitrate + bitrate: u32, }, /// No solution possible - NoSolution { + NoSolution { /// The sum of BS1 and BS2 - bs1_bs2_sum: u8 + bs1_bs2_sum: u8, }, /// Prescaler is not 1 < prescaler < 1024 - InvalidPrescaler { - /// The calculated prescaler value - prescaler: u32 + InvalidPrescaler { + /// The calculated prescaler value + prescaler: u32, }, /// BS1 or BS2 are not in the range 0 < BSx < BSx_MAX - BSNotInRange { + BSNotInRange { /// The value of BS1 bs1: u8, /// The value of BS2 - bs2: u8 + bs2: u8, }, /// Final bitrate doesn't match the requested bitrate - NoMatch { - /// The requested bitrate + NoMatch { + /// The requested bitrate requested: u32, - /// The calculated bitrate - final_calculated: u32 + /// The calculated bitrate + final_calculated: u32, }, /// core::num::NonZeroUxx::new error CoreNumNew, diff --git a/embassy-stm32/src/can/util.rs b/embassy-stm32/src/can/util.rs index e93fe607d..beca4c34e 100644 --- a/embassy-stm32/src/can/util.rs +++ b/embassy-stm32/src/can/util.rs @@ -19,7 +19,10 @@ pub struct NominalBitTiming { } /// Calculate nominal CAN bit timing based on CAN bitrate and periphial clock frequency -pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> Result { +pub fn calc_can_timings( + periph_clock: crate::time::Hertz, + can_bitrate: u32, +) -> Result { const BS1_MAX: u8 = 16; const BS2_MAX: u8 = 8; const MAX_SAMPLE_POINT_PERMILL: u16 = 900; @@ -101,7 +104,10 @@ pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> R let calculated = periph_clock / (prescaler * (1 + bs1 + bs2) as u32); // Check if final bitrate matches the requested if can_bitrate != calculated { - return Err(TimingCalcError::NoMatch { requested: can_bitrate, final_calculated: calculated }); + return Err(TimingCalcError::NoMatch { + requested: can_bitrate, + final_calculated: calculated, + }); } // One is recommended by DS-015, CANOpen, and DeviceNet -- cgit