From d680c5049ed7318dea4dec460709a9f34d4c3fd2 Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Mon, 10 Mar 2025 19:03:18 -0700 Subject: [PATCH 01/21] it works don't touch anything --- boards/pyportal/.cargo/config.toml | 3 +- boards/pyportal/Cargo.toml | 19 +++- boards/pyportal/examples/blinky_embassy.rs | 0 boards/pyportal/examples/embassy_timer.rs | 50 +++++++++ boards/pyportal/examples/interrupt_timer.rs | 81 +++++++++++++++ boards/pyportal/examples/semihosting.rs | 17 +++ hal/Cargo.toml | 5 + hal/src/rtc/embassy/mod.rs | 108 ++++++++++++++++++++ hal/src/rtc/mod.rs | 3 + hal/src/rtc/modes.rs | 7 +- 10 files changed, 289 insertions(+), 4 deletions(-) create mode 100644 boards/pyportal/examples/blinky_embassy.rs create mode 100644 boards/pyportal/examples/embassy_timer.rs create mode 100644 boards/pyportal/examples/interrupt_timer.rs create mode 100644 boards/pyportal/examples/semihosting.rs create mode 100644 hal/src/rtc/embassy/mod.rs diff --git a/boards/pyportal/.cargo/config.toml b/boards/pyportal/.cargo/config.toml index 4cf66548fe50..c05737c6886c 100644 --- a/boards/pyportal/.cargo/config.toml +++ b/boards/pyportal/.cargo/config.toml @@ -1,7 +1,8 @@ # vim:ft=toml: [target.thumbv7em-none-eabihf] -runner = "hf2 elf" +# runner = "hf2 elf" # runner = 'probe-rs run --chip ATSAMD51J20A' +runner = "gdb -q -x ./openocd.gdb" [build] target = "thumbv7em-none-eabihf" diff --git a/boards/pyportal/Cargo.toml b/boards/pyportal/Cargo.toml index f32f7611f0fc..776e6d710dd6 100644 --- a/boards/pyportal/Cargo.toml +++ b/boards/pyportal/Cargo.toml @@ -19,7 +19,8 @@ version = "0.7.5" optional = true [dependencies.atsamd-hal] -version = "0.21.0" +# version = "0.21.0" +path = "../../hal" default-features = false [dependencies.display-interface-parallel-gpio] @@ -40,11 +41,21 @@ panic-semihosting = "0.5" smart-leds = "~0.3" usbd-serial = "0.2.2" embedded-graphics = "0.8.1" +embassy-time = "0.4" +critical-section = "1.2.0" + + +[dev-dependencies.embassy-executor] +version = "0.7" +features = ["arch-cortex-m", "executor-thread", "task-arena-size-256"] [dev-dependencies.cortex-m] features = ["critical-section-single-core"] version = "0.7.5" +[dev-dependencies.cortex-m-semihosting] +version = "0.5" + [dev-dependencies.ws2812-timer-delay] version = "~0.3" @@ -55,6 +66,8 @@ rt = ["cortex-m-rt", "atsamd-hal/samd51j-rt"] usb = ["atsamd-hal/usb", "usb-device"] display = ["display-interface-parallel-gpio", "ili9341"] use_semihosting = [] +rtic = ["atsamd-hal/rtic"] +embassy-time = ["atsamd-hal/embassy-time"] # for cargo flash [package.metadata] @@ -73,3 +86,7 @@ name = "neopixel_rainbow" [[example]] name = "usb_echo" required-features = ["usb"] + +[[example]] +name = "embassy_timer" +required-features = ["embassy-time"] \ No newline at end of file diff --git a/boards/pyportal/examples/blinky_embassy.rs b/boards/pyportal/examples/blinky_embassy.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/boards/pyportal/examples/embassy_timer.rs b/boards/pyportal/examples/embassy_timer.rs new file mode 100644 index 000000000000..bf4f9155d983 --- /dev/null +++ b/boards/pyportal/examples/embassy_timer.rs @@ -0,0 +1,50 @@ +#![no_std] +#![no_main] + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::{hal, pac, pin_alias}; +use pyportal as bsp; + +use hal::{ + ehal::digital::StatefulOutputPin, + clock::{GenericClockController, ClockGenId, ClockSource}, + rtc::modes::{mode0::RtcMode0, RtcMode} +}; + +use cortex_m_semihosting::hprintln; + +use embassy_time::Timer; + +#[embassy_executor::main] +async fn main(_s: embassy_executor::Spawner) { + let mut peripherals = pac::Peripherals::take().unwrap(); + let _core = pac::CorePeripherals::take().unwrap(); + + let mut clocks = GenericClockController::with_internal_32kosc( + peripherals.gclk, + &mut peripherals.mclk, + &mut peripherals.osc32kctrl, + &mut peripherals.oscctrl, + &mut peripherals.nvmctrl + ); + let pins = bsp::Pins::new(peripherals.port); + let mut red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); + + // Select the 32khz source + peripherals.osc32kctrl.rtcctrl().write(|w| w.rtcsel().ulp32k()); + + peripherals.rtc.mode0().dbgctrl().write(|w| w.dbgrun().set_bit()); + + unsafe { hal::rtc::embassy::init(); } + + + loop { + red_led.toggle().unwrap(); + hprintln!("The time is {}", RtcMode0::count(&peripherals.rtc)); + Timer::after_secs(1).await; + } +} diff --git a/boards/pyportal/examples/interrupt_timer.rs b/boards/pyportal/examples/interrupt_timer.rs new file mode 100644 index 000000000000..248dfde04cd1 --- /dev/null +++ b/boards/pyportal/examples/interrupt_timer.rs @@ -0,0 +1,81 @@ +#![no_std] +#![no_main] + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::{hal, pac, pin_alias}; +use pyportal as bsp; + +use hal::{ + clock::GenericClockController, + ehal::digital::StatefulOutputPin, + rtc::modes::{ + mode0::{Compare0, RtcMode0}, + RtcMode, + }, +}; +use pac::{interrupt, Interrupt, NVIC}; + +use cortex_m_rt::entry; +use cortex_m_semihosting::hprintln; + +#[entry] +fn main() -> ! { + let mut peripherals = pac::Peripherals::take().unwrap(); + + let _clocks = GenericClockController::with_internal_32kosc( + peripherals.gclk, + &mut peripherals.mclk, + &mut peripherals.osc32kctrl, + &mut peripherals.oscctrl, + &mut peripherals.nvmctrl, + ); + + // Select the 32khz source + peripherals + .osc32kctrl + .rtcctrl() + .write(|w| w.rtcsel().ulp1k()); + + let rtc = peripherals.rtc; + + rtc.mode0().dbgctrl().write(|w| w.dbgrun().set_bit()); + RtcMode0::disable(&rtc); + RtcMode0::reset(&rtc); + RtcMode0::set_mode(&rtc); + + RtcMode0::start_and_initialize(&rtc); + RtcMode0::clear_interrupt_flag::(&rtc); + RtcMode0::enable_interrupt::(&rtc); + rtc.mode0().evctrl().write(|w| w.cmpeo0().set_bit()); + while rtc.mode0().syncbusy().read(). + + unsafe { + let mut nvic: cortex_m::peripheral::NVIC = core::mem::transmute(()); + nvic.set_priority(Interrupt::RTC, 1); + NVIC::unmask(Interrupt::RTC); + } + + RtcMode0::enable(&rtc); + + let now = RtcMode0::count(&rtc); + let at = now + 2_000; + hprintln!("Currently {}, alarm set at {}", now, at); + + RtcMode0::set_compare(&rtc, 0, at); + + loop {} +} + +#[interrupt] +fn RTC() { + hprintln!("Hello"); + let peripherals = unsafe { pac::Peripherals::steal() }; + let pins = bsp::Pins::new(peripherals.port); + let mut red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); + + red_led.toggle().unwrap(); +} diff --git a/boards/pyportal/examples/semihosting.rs b/boards/pyportal/examples/semihosting.rs new file mode 100644 index 000000000000..682a3fc69c46 --- /dev/null +++ b/boards/pyportal/examples/semihosting.rs @@ -0,0 +1,17 @@ +#![no_main] +#![no_std] + +use bsp::hal; +use pyportal as bsp; + +use panic_semihosting as _; + +use cortex_m_rt::entry; +use cortex_m_semihosting::hprintln; + +#[entry] +fn main() -> ! { + hprintln!("Hello, world!"); + + loop {} +} diff --git a/hal/Cargo.toml b/hal/Cargo.toml index 6856d7ae1cf1..7ec6f657bee0 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -37,6 +37,7 @@ bitflags = "2.6.0" cipher = "0.4" cortex-m = "0.7" critical-section = "1.2.0" +cortex-m-semihosting = "0.5" embedded-hal-02 = {package = "embedded-hal", version = "0.2", features = ["unproven"]} embedded-hal-1 = {package = "embedded-hal", version = "1.0.0"} embedded-hal-nb = "1.0.0" @@ -71,6 +72,9 @@ rtic-monotonic = {version = "1.0", optional = true} rtic-time = {version = "2.0", optional = true} usb-device = {version = "0.3.2", optional = true} +embassy-time-driver = { version = "0.2", optional = true, features = ["tick-hz-32_768"] } +embassy-time-queue-utils = { version = "0.1", optional = true } + #=============================================================================== # PACs #=============================================================================== @@ -194,6 +198,7 @@ defmt = ["dep:defmt"] dma = [] max-channels = ["dma"] rtic = ["rtic-monotonic", "rtic-time", "portable-atomic"] +embassy-time = ["embassy-time-driver", "embassy-time-queue-utils"] sdmmc = ["embedded-sdmmc"] use_rtt = ["jlink_rtt"] undoc-features = [] diff --git a/hal/src/rtc/embassy/mod.rs b/hal/src/rtc/embassy/mod.rs new file mode 100644 index 000000000000..4c590f5ba6d5 --- /dev/null +++ b/hal/src/rtc/embassy/mod.rs @@ -0,0 +1,108 @@ +use core::cell::RefCell; +use core::task::Waker; + +use cortex_m_semihosting::hprintln; +use crate::pac::{NVIC, interrupt, Rtc, Interrupt}; +use crate::rtc::modes::{mode0::{RtcMode0, Compare0}, RtcMode}; +use critical_section::{CriticalSection, Mutex}; +use embassy_time_driver::Driver; +use embassy_time_queue_utils::Queue; + +embassy_time_driver::time_driver_impl!(static DRIVER: AtmelDriver = AtmelDriver{ + queue: Mutex::new(RefCell::new(Queue::new())) +}); + +struct AtmelDriver { + queue: Mutex>, +} + +impl AtmelDriver { + fn set_alarm(&self, _cs: &CriticalSection, at: u64, rtc: &Rtc) -> bool { + // SAFETY: inside a CriticalSection + let rtc = unsafe { + Rtc::steal() + }; + + let at = match u32::try_from(at) { + Ok(at) => at, + Err(_) => return false, + }; + + RtcMode0::set_compare(&rtc, 0, at); + true + + } +} + +fn handle_interrupt() { + hprintln!("interrupt handled"); + + // safety: inside a critical section + let rtc = unsafe { + Rtc::steal() + }; + if RtcMode0::check_interrupt_flag::(&rtc) { + RtcMode0::clear_interrupt_flag::(&rtc); + let now = RtcMode0::count(&rtc) as u64; + + critical_section::with(|cs| { + let next = DRIVER.queue.borrow_ref_mut(cs).next_expiration(now); + DRIVER.set_alarm(&cs, next, &rtc); + }); + } +} + +#[interrupt] +fn RTC() { + handle_interrupt() +} + +impl Driver for AtmelDriver { + fn now(&self) -> u64 { + critical_section::with(|cs| { + let rtc = unsafe { + Rtc::steal() + }; + let now = RtcMode0::count(&rtc) as u64; + now + }) + } + + fn schedule_wake(&self, at: u64, waker: &Waker) { + critical_section::with(|cs| { + let rtc = unsafe { + Rtc::steal() + }; + let mut queue = self.queue.borrow(cs).borrow_mut(); + if queue.schedule_wake(at, waker) { + let next = queue.next_expiration(self.now()); + // We can only handle one alarm at a time right now + self.set_alarm(&cs, next, &rtc); + } + }); + } +} + +pub unsafe fn init() { + // TODO: ensure CLK_RTC_APB is started in MCLK, and the prescaler is set to 1 + let rtc = Rtc::steal(); + RtcMode0::disable(&rtc); + RtcMode0::reset(&rtc); + RtcMode0::set_mode(&rtc); + + + critical_section::with(|_| { + RtcMode0::start_and_initialize(&rtc); + RtcMode0::clear_interrupt_flag::(&rtc); + RtcMode0::enable_interrupt::(&rtc); + rtc.mode0().evctrl().write(|w| w.cmpeo0().set_bit()); + + unsafe { + let mut nvic: cortex_m::peripheral::NVIC = core::mem::transmute(()); + nvic.set_priority(Interrupt::RTC, 128); + NVIC::unmask(Interrupt::RTC); + } + + RtcMode0::enable(&rtc); + }); +} diff --git a/hal/src/rtc/mod.rs b/hal/src/rtc/mod.rs index 72535e5d82cc..15e9a65d0185 100644 --- a/hal/src/rtc/mod.rs +++ b/hal/src/rtc/mod.rs @@ -24,6 +24,9 @@ mod modes; #[cfg(feature = "rtic")] pub mod rtic; +#[cfg(feature = "embassy-time")] +pub mod embassy; + // SAMx5x imports #[hal_cfg("rtc-d5x")] use crate::pac::{Mclk as Pm, rtc::mode0::ctrla::Prescalerselect}; diff --git a/hal/src/rtc/modes.rs b/hal/src/rtc/modes.rs index f30b1ce0acb6..b6aadee21c2e 100644 --- a/hal/src/rtc/modes.rs +++ b/hal/src/rtc/modes.rs @@ -60,8 +60,9 @@ macro_rules! create_rtc_interrupt { impl RtcInterrupt for $name { #[inline] fn enable(rtc: &Rtc) { - // SYNC: None + // SYNC: write rtc.$mode().intenset().write(|w| w.$bit().set_bit()); + while rtc.$mode().syncbusy().read().enable().bit_is_set() {} } #[inline] @@ -395,10 +396,12 @@ pub mod mode0 { #[inline] fn set_compare(rtc: &Rtc, number: usize, value: Self::Count) { // SYNC: Write - Self::sync(rtc); unsafe { rtc.mode0().comp(number).write(|w| w.comp().bits(value)); } + + // TODO(stillinbeta) handle other `number` + while rtc.mode0().syncbusy().read().comp0().bit_is_set() {} } #[inline] From 9b6554ebfbfb2d443b7dd059efc54f69d6a1a450 Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Mon, 24 Mar 2025 19:57:21 -0700 Subject: [PATCH 02/21] blinky interrupt --- boards/pyportal/examples/blinky_interrupt.rs | 161 +++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 boards/pyportal/examples/blinky_interrupt.rs diff --git a/boards/pyportal/examples/blinky_interrupt.rs b/boards/pyportal/examples/blinky_interrupt.rs new file mode 100644 index 000000000000..8130ed5ee5bf --- /dev/null +++ b/boards/pyportal/examples/blinky_interrupt.rs @@ -0,0 +1,161 @@ +//! Turn on and off with an LED +#![no_std] +#![no_main] + +use core::{cell::RefCell, mem}; + + +use rtt_target::rprintln; +use atsamd_hal::ehal::digital::{OutputPin, StatefulOutputPin}; +use pyportal as bsp; +use critical_section::Mutex; +use bsp::{ + pac::{interrupt, CorePeripherals, Interrupt, Peripherals}, + pin_alias, + RedLed, +}; +use cortex_m::peripheral::NVIC; +use cortex_m_rt::entry; + +use panic_semihosting as _; + +macro_rules! sync_wait { + ($mode0:expr, $register:ident) => { + while $mode0.syncbusy().read().$register().bit_is_set() {} + } +} + +static BACKLIGHT_PIN: Mutex> = Mutex::new(RefCell::new(unsafe { mem::zeroed() })); + +#[entry] +fn main() -> ! { + // rtt_target::rtt_init_print!(); + + let peripherals = Peripherals::take().unwrap(); + let mut core = CorePeripherals::take().unwrap(); + + let pins = bsp::Pins::new(peripherals.port); + let mut red_led: bsp::TftBacklight = pin_alias!(pins.tft_backlight).into(); + + critical_section::with(|cs| { + let _ = BACKLIGHT_PIN.replace(cs, red_led); + BACKLIGHT_PIN.borrow_ref_mut(cs).set_low(); + }); + + // rprintln!("starting up"); + + // enable global interrupts + unsafe { + NVIC::mask(Interrupt::RTC); + // core.NVIC.set_priority(Interrupt::RTC, 8); + cortex_m::interrupt::enable(); + }; + + // use the 32k clock + peripherals + .osc32kctrl + .rtcctrl() + .write(|w| w.rtcsel().ulp32k()); + + let mode0 = peripherals.rtc.mode0(); + + // Run RTC when main chip is paused + mode0.dbgctrl().write(|w| w.dbgrun().set_bit()); + + + + // disable the clock + mode0.ctrla().write(|w| w.enable().clear_bit()); + // write sync for RTC enable + sync_wait!(mode0, enable); + + + + // trigger a reset + mode0.ctrla().modify(|_, w| w.swrst().set_bit()); + // write sync reset + sync_wait!(mode0, swrst); + + // set the mode + mode0.ctrla().modify(|_, w| w.mode().count32()); + + mode0.ctrla().modify(|_, w| { + // Use 32 bit counter + w.prescaler().div1(); + // The COUNT register requires synchronization when reading. + // Disabling the synchronization will prevent reading valid values from the COUNT register. + w.countsync().set_bit(); + w.matchclr().clear_bit(); + w + }); + // write sync for countsync + sync_wait!(mode0, countsync); + + // clear flag + mode0.intflag().write(|w| w.cmp0().set_bit()); + + // Enable the RTC + mode0.ctrla().modify(|_, w| w.enable().set_bit()); + // write sync for RTC enable + sync_wait!(mode0, enable); + + // wait for count to be ready + sync_wait!(mode0, count); + // read the current count + let count: u32 = mode0.count().read().count().bits(); + // add 5 seconds + let next = count + (5 * 32_768); + // rprintln!("count is {}, waking up at {}", count, next); + + + mode0.comp(0).write(|w| unsafe { w.comp().bits(next) }); + // wait write + + + sync_wait!(mode0, comp0); + + + mode0.intflag().write(|w| w.cmp0().set_bit()); + // enable interupt + mode0.intenset().write(|w| w.cmp0().set_bit()); + + // critical_section::with(|cs| { + // BACKLIGHT_PIN.borrow_ref_mut(cs).set_low(); + // }); + + + unsafe { NVIC::unmask(Interrupt::RTC); } + + loop {} +} + +#[interrupt] +fn RTC() { + // unsafe { NVIC::mask(Interrupt::RTC) }; + + let peripherals = unsafe { Peripherals::steal() }; + + let mode0 = peripherals.rtc.mode0(); + + // is this actually an RTC compare0 interrupt + if mode0.intflag().read().cmp0().bit_is_set() { + // clear the interrupt bit + + critical_section::with(|cs| { + BACKLIGHT_PIN.borrow_ref_mut(cs).toggle(); + }); + + + sync_wait!(mode0, count); + let count: u32 = mode0.count().read().count().bits(); + // add 5 seconds + let next = count + (2 * 32_768); + // rprintln!("count is {}, waking up at {}", count, next); + + mode0.comp(0).write(|w| unsafe { w.comp().bits(next) }); + // wait write + sync_wait!(mode0, comp0); + mode0.intflag().write(|w| w.cmp0().set_bit()); + } + // unsafe { NVIC::unmask(Interrupt::RTC) }; +} From b6c0818d26d28df28ecb98aa18c1c913c46641af Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Tue, 25 Mar 2025 07:25:03 -0700 Subject: [PATCH 03/21] Working embassy-interrupt implementation (for at least one board) update interrupt handling code close up modes Remove unneeded examples don't need semihosting remove rtt-target roll back config.toml --- boards/pyportal/.cargo/config.toml | 5 +- boards/pyportal/Cargo.toml | 3 +- boards/pyportal/examples/blinky_embassy.rs | 0 boards/pyportal/examples/blinky_interrupt.rs | 25 +- boards/pyportal/examples/embassy_timer.rs | 26 +- boards/pyportal/examples/semihosting.rs | 17 -- hal/Cargo.toml | 1 - hal/src/rtc/embassy/mod.rs | 95 +++--- hal/src/rtc/modes.rs | 286 +++---------------- 9 files changed, 100 insertions(+), 358 deletions(-) delete mode 100644 boards/pyportal/examples/blinky_embassy.rs delete mode 100644 boards/pyportal/examples/semihosting.rs diff --git a/boards/pyportal/.cargo/config.toml b/boards/pyportal/.cargo/config.toml index c05737c6886c..1e4c1e4efe25 100644 --- a/boards/pyportal/.cargo/config.toml +++ b/boards/pyportal/.cargo/config.toml @@ -1,8 +1,7 @@ # vim:ft=toml: [target.thumbv7em-none-eabihf] -# runner = "hf2 elf" -# runner = 'probe-rs run --chip ATSAMD51J20A' -runner = "gdb -q -x ./openocd.gdb" +runner = "hf2 elf" +#runner = 'probe-run --chip ATSAMD51J20A' [build] target = "thumbv7em-none-eabihf" diff --git a/boards/pyportal/Cargo.toml b/boards/pyportal/Cargo.toml index 776e6d710dd6..240754bc6a15 100644 --- a/boards/pyportal/Cargo.toml +++ b/boards/pyportal/Cargo.toml @@ -44,7 +44,6 @@ embedded-graphics = "0.8.1" embassy-time = "0.4" critical-section = "1.2.0" - [dev-dependencies.embassy-executor] version = "0.7" features = ["arch-cortex-m", "executor-thread", "task-arena-size-256"] @@ -89,4 +88,4 @@ required-features = ["usb"] [[example]] name = "embassy_timer" -required-features = ["embassy-time"] \ No newline at end of file +required-features = ["embassy-time"] diff --git a/boards/pyportal/examples/blinky_embassy.rs b/boards/pyportal/examples/blinky_embassy.rs deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/boards/pyportal/examples/blinky_interrupt.rs b/boards/pyportal/examples/blinky_interrupt.rs index 8130ed5ee5bf..cf6128c4ff30 100644 --- a/boards/pyportal/examples/blinky_interrupt.rs +++ b/boards/pyportal/examples/blinky_interrupt.rs @@ -29,7 +29,7 @@ static BACKLIGHT_PIN: Mutex> = Mutex::new(RefCell::ne #[entry] fn main() -> ! { - // rtt_target::rtt_init_print!(); + rtt_target::rtt_init_print!(); let peripherals = Peripherals::take().unwrap(); let mut core = CorePeripherals::take().unwrap(); @@ -89,15 +89,15 @@ fn main() -> ! { w }); // write sync for countsync + let init = mode0.count().read().count().bits(); sync_wait!(mode0, countsync); + // When CTRLA.COUNTSYNC is enabled, the first COUNT value is not correctly synchronized and thus it + // is a wrong value. + // clear flag mode0.intflag().write(|w| w.cmp0().set_bit()); - // Enable the RTC - mode0.ctrla().modify(|_, w| w.enable().set_bit()); - // write sync for RTC enable - sync_wait!(mode0, enable); // wait for count to be ready sync_wait!(mode0, count); @@ -105,7 +105,7 @@ fn main() -> ! { let count: u32 = mode0.count().read().count().bits(); // add 5 seconds let next = count + (5 * 32_768); - // rprintln!("count is {}, waking up at {}", count, next); + rprintln!("count is {}, waking up at {}", count, next); mode0.comp(0).write(|w| unsafe { w.comp().bits(next) }); @@ -126,6 +126,14 @@ fn main() -> ! { unsafe { NVIC::unmask(Interrupt::RTC); } + // Enable the RTC + mode0.ctrla().modify(|_, w| w.enable().set_bit()); + + // Block to wait for countsync to be correct + while mode0.count().read().count().bits() == init {} + // write sync for RTC enable + sync_wait!(mode0, enable); + loop {} } @@ -133,6 +141,8 @@ fn main() -> ! { fn RTC() { // unsafe { NVIC::mask(Interrupt::RTC) }; + critical_section::with(|_cs| { + let peripherals = unsafe { Peripherals::steal() }; let mode0 = peripherals.rtc.mode0(); @@ -150,7 +160,7 @@ fn RTC() { let count: u32 = mode0.count().read().count().bits(); // add 5 seconds let next = count + (2 * 32_768); - // rprintln!("count is {}, waking up at {}", count, next); + rprintln!("count is {}, waking up at {}", count, next); mode0.comp(0).write(|w| unsafe { w.comp().bits(next) }); // wait write @@ -158,4 +168,5 @@ fn RTC() { mode0.intflag().write(|w| w.cmp0().set_bit()); } // unsafe { NVIC::unmask(Interrupt::RTC) }; + }) } diff --git a/boards/pyportal/examples/embassy_timer.rs b/boards/pyportal/examples/embassy_timer.rs index bf4f9155d983..2d667fb680fb 100644 --- a/boards/pyportal/examples/embassy_timer.rs +++ b/boards/pyportal/examples/embassy_timer.rs @@ -8,43 +8,27 @@ use panic_semihosting as _; use bsp::{hal, pac, pin_alias}; use pyportal as bsp; - use hal::{ ehal::digital::StatefulOutputPin, - clock::{GenericClockController, ClockGenId, ClockSource}, - rtc::modes::{mode0::RtcMode0, RtcMode} }; -use cortex_m_semihosting::hprintln; - use embassy_time::Timer; #[embassy_executor::main] async fn main(_s: embassy_executor::Spawner) { - let mut peripherals = pac::Peripherals::take().unwrap(); + let peripherals = pac::Peripherals::take().unwrap(); let _core = pac::CorePeripherals::take().unwrap(); - - let mut clocks = GenericClockController::with_internal_32kosc( - peripherals.gclk, - &mut peripherals.mclk, - &mut peripherals.osc32kctrl, - &mut peripherals.oscctrl, - &mut peripherals.nvmctrl - ); let pins = bsp::Pins::new(peripherals.port); let mut red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); // Select the 32khz source - peripherals.osc32kctrl.rtcctrl().write(|w| w.rtcsel().ulp32k()); - - peripherals.rtc.mode0().dbgctrl().write(|w| w.dbgrun().set_bit()); - - unsafe { hal::rtc::embassy::init(); } - + // SAFETY: not in a critical section + unsafe { + hal::rtc::embassy::init(); + } loop { red_led.toggle().unwrap(); - hprintln!("The time is {}", RtcMode0::count(&peripherals.rtc)); Timer::after_secs(1).await; } } diff --git a/boards/pyportal/examples/semihosting.rs b/boards/pyportal/examples/semihosting.rs deleted file mode 100644 index 682a3fc69c46..000000000000 --- a/boards/pyportal/examples/semihosting.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![no_main] -#![no_std] - -use bsp::hal; -use pyportal as bsp; - -use panic_semihosting as _; - -use cortex_m_rt::entry; -use cortex_m_semihosting::hprintln; - -#[entry] -fn main() -> ! { - hprintln!("Hello, world!"); - - loop {} -} diff --git a/hal/Cargo.toml b/hal/Cargo.toml index 7ec6f657bee0..eb113b85a130 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -37,7 +37,6 @@ bitflags = "2.6.0" cipher = "0.4" cortex-m = "0.7" critical-section = "1.2.0" -cortex-m-semihosting = "0.5" embedded-hal-02 = {package = "embedded-hal", version = "0.2", features = ["unproven"]} embedded-hal-1 = {package = "embedded-hal", version = "1.0.0"} embedded-hal-nb = "1.0.0" diff --git a/hal/src/rtc/embassy/mod.rs b/hal/src/rtc/embassy/mod.rs index 4c590f5ba6d5..86b773b4a21b 100644 --- a/hal/src/rtc/embassy/mod.rs +++ b/hal/src/rtc/embassy/mod.rs @@ -1,15 +1,18 @@ use core::cell::RefCell; use core::task::Waker; -use cortex_m_semihosting::hprintln; -use crate::pac::{NVIC, interrupt, Rtc, Interrupt}; -use crate::rtc::modes::{mode0::{RtcMode0, Compare0}, RtcMode}; +use crate::pac::{interrupt, Interrupt, Rtc, NVIC, Osc32kctrl}; +use crate::rtc::modes::{ + mode0::{Compare0, RtcMode0}, + RtcMode, +}; use critical_section::{CriticalSection, Mutex}; use embassy_time_driver::Driver; use embassy_time_queue_utils::Queue; embassy_time_driver::time_driver_impl!(static DRIVER: AtmelDriver = AtmelDriver{ - queue: Mutex::new(RefCell::new(Queue::new())) + queue: Mutex::new(RefCell::new(Queue::new())), + }); struct AtmelDriver { @@ -18,61 +21,45 @@ struct AtmelDriver { impl AtmelDriver { fn set_alarm(&self, _cs: &CriticalSection, at: u64, rtc: &Rtc) -> bool { - // SAFETY: inside a CriticalSection - let rtc = unsafe { - Rtc::steal() - }; - + // Embassy uses u64::MAX as a "no upcoming interrupt" sentinel let at = match u32::try_from(at) { Ok(at) => at, + _ if at == u64::MAX => u32::MAX, Err(_) => return false, }; - RtcMode0::set_compare(&rtc, 0, at); + RtcMode0::set_compare(rtc, 0, at); true - } } -fn handle_interrupt() { - hprintln!("interrupt handled"); - - // safety: inside a critical section - let rtc = unsafe { - Rtc::steal() - }; - if RtcMode0::check_interrupt_flag::(&rtc) { - RtcMode0::clear_interrupt_flag::(&rtc); - let now = RtcMode0::count(&rtc) as u64; - - critical_section::with(|cs| { +#[interrupt] +fn RTC() { + critical_section::with(|cs| { + let rtc = unsafe { Rtc::steal() }; + if RtcMode0::check_interrupt_flag::(&rtc) { + // Due to synchronization delay, the RTC may be slightly behind + // Assume the current time is the time the interrupt is set for + let now = RtcMode0::get_compare(&rtc, 0) as u64; let next = DRIVER.queue.borrow_ref_mut(cs).next_expiration(now); DRIVER.set_alarm(&cs, next, &rtc); - }); - } -} + RtcMode0::clear_interrupt_flag::(&rtc); + } + }); -#[interrupt] -fn RTC() { - handle_interrupt() } impl Driver for AtmelDriver { fn now(&self) -> u64 { - critical_section::with(|cs| { - let rtc = unsafe { - Rtc::steal() - }; - let now = RtcMode0::count(&rtc) as u64; - now + critical_section::with(|_cs| { + let rtc = unsafe { Rtc::steal() }; + RtcMode0::count(&rtc) as u64 }) } fn schedule_wake(&self, at: u64, waker: &Waker) { critical_section::with(|cs| { - let rtc = unsafe { - Rtc::steal() - }; + let rtc = unsafe { Rtc::steal() }; let mut queue = self.queue.borrow(cs).borrow_mut(); if queue.schedule_wake(at, waker) { let next = queue.next_expiration(self.now()); @@ -83,26 +70,28 @@ impl Driver for AtmelDriver { } } +/// # Safety +/// +/// This enables interrupts, which can break out of critical sections pub unsafe fn init() { - // TODO: ensure CLK_RTC_APB is started in MCLK, and the prescaler is set to 1 + let osc32 = Osc32kctrl::steal(); + osc32.rtcctrl().write(|w| w.rtcsel().ulp32k()); + let rtc = Rtc::steal(); + RtcMode0::disable(&rtc); RtcMode0::reset(&rtc); RtcMode0::set_mode(&rtc); + RtcMode0::start_and_initialize(&rtc); + RtcMode0::clear_interrupt_flag::(&rtc); + RtcMode0::enable_interrupt::(&rtc); - critical_section::with(|_| { - RtcMode0::start_and_initialize(&rtc); - RtcMode0::clear_interrupt_flag::(&rtc); - RtcMode0::enable_interrupt::(&rtc); - rtc.mode0().evctrl().write(|w| w.cmpeo0().set_bit()); - - unsafe { - let mut nvic: cortex_m::peripheral::NVIC = core::mem::transmute(()); - nvic.set_priority(Interrupt::RTC, 128); - NVIC::unmask(Interrupt::RTC); - } - - RtcMode0::enable(&rtc); - }); + RtcMode0::enable(&rtc); + unsafe { + cortex_m::interrupt::enable(); + let mut nvic: cortex_m::peripheral::NVIC = core::mem::transmute(()); + nvic.set_priority(Interrupt::RTC, 1); + NVIC::unmask(Interrupt::RTC); + } } diff --git a/hal/src/rtc/modes.rs b/hal/src/rtc/modes.rs index b6aadee21c2e..e38e43081388 100644 --- a/hal/src/rtc/modes.rs +++ b/hal/src/rtc/modes.rs @@ -33,18 +33,10 @@ use crate::pac; use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use pac::Rtc; -// Import prescaler divider enum -#[hal_cfg(any("rtc-d11", "rtc-d21"))] -use crate::pac::rtc::mode0::ctrl::Prescalerselect; -#[hal_cfg("rtc-d5x")] -use crate::pac::rtc::mode0::ctrla::Prescalerselect; - /// Type-level enum for RTC interrupts. pub trait RtcInterrupt { /// Enable this interrupt. fn enable(rtc: &Rtc); - /// Disable this interrupt. - fn disable(rtc: &Rtc); /// Returns whether the interrupt has been triggered. fn check_flag(rtc: &Rtc) -> bool; /// Clears the interrupt flag so the ISR will not be called again @@ -62,13 +54,7 @@ macro_rules! create_rtc_interrupt { fn enable(rtc: &Rtc) { // SYNC: write rtc.$mode().intenset().write(|w| w.$bit().set_bit()); - while rtc.$mode().syncbusy().read().enable().bit_is_set() {} - } - - #[inline] - fn disable(rtc: &Rtc) { - // SYNC: None - rtc.$mode().intenclr().write(|w| w.$bit().set_bit()); + sync_wait!(rtc, enable) } #[inline] @@ -86,6 +72,12 @@ macro_rules! create_rtc_interrupt { }; } +macro_rules! sync_wait { + ($rtc:expr, $register:ident) => { + while $rtc.mode0().syncbusy().read().$register().bit_is_set() {} + } +} + /// An abstraction of an RTC in a particular mode that provides low-level /// access and handles all register syncing issues using only associated /// functions. @@ -97,8 +89,8 @@ pub trait RtcMode { /// /// # Safety /// - /// This should only be called when the RTC is disabled, and is typically - /// only called once before calling most other methods. + /// This can be called any time but is typically only called once before + /// calling most other methods. fn set_mode(rtc: &Rtc); /// Sets a compare value. @@ -115,7 +107,6 @@ pub trait RtcMode { /// /// Should be called only after setting the RTC mode using /// [`set_mode`](RtcMode::set_mode). - #[cfg(feature = "rtic")] fn get_compare(rtc: &Rtc, number: usize) -> Self::Count; /// Returns the current synced COUNT value. @@ -126,14 +117,6 @@ pub trait RtcMode { /// [`set_mode`](RtcMode::set_mode). fn count(rtc: &Rtc) -> Self::Count; - /// Sets the current synced COUNT value. - /// - /// # Safety - /// - /// Should be called only after setting the RTC mode using - /// [`set_mode`](RtcMode::set_mode). - fn set_count(rtc: &Rtc, count: Self::Count); - /// Returns whether register syncing is currently happening. /// /// # Safety @@ -162,7 +145,6 @@ pub trait RtcMode { // Reset RTC back to initial settings, which disables it and enters mode 0. // NOTE: This register and field are the same in all modes. // SYNC: Write - Self::sync(rtc); #[hal_cfg(any("rtc-d11", "rtc-d21"))] rtc.mode0().ctrl().modify(|_, w| w.swrst().set_bit()); #[hal_cfg("rtc-d5x")] @@ -173,29 +155,10 @@ pub trait RtcMode { #[hal_cfg(any("rtc-d11", "rtc-d21"))] while rtc.mode0().ctrl().read().swrst().bit_is_set() {} #[hal_cfg("rtc-d5x")] - // NOTE: There is also a SWRST bit in the SYNCBUSY register but the bit CTRLA register - // is the one that clears when the reset is complete. - while rtc.mode0().ctrla().read().swrst().bit_is_set() {} - } - - /// Sets the clock prescaler divider to lower the tick rate. - /// - /// # Safety - /// - /// Should be called only when the RTC is disabled. - #[inline] - #[hal_macro_helper] - fn set_prescaler(rtc: &Rtc, divider: Prescalerselect) { - // NOTE: This register and field are the same in all modes. - // SYNC: None - #[hal_cfg(any("rtc-d11", "rtc-d21"))] - rtc.mode0() - .ctrl() - .modify(|_, w| w.prescaler().variant(divider)); - #[hal_cfg("rtc-d5x")] - rtc.mode0() - .ctrla() - .modify(|_, w| w.prescaler().variant(divider)); + { + while rtc.mode0().ctrla().read().swrst().bit_is_set() {} + sync_wait!(rtc, swrst) + } } /// Starts the RTC and does any required initialization for this mode. @@ -207,7 +170,6 @@ pub trait RtcMode { #[inline] #[hal_macro_helper] fn start_and_initialize(rtc: &Rtc) { - Self::enable(rtc); // Enable counter sync on SAMx5x, the counter cannot be read otherwise. #[hal_cfg("rtc-d5x")] @@ -215,13 +177,14 @@ pub trait RtcMode { // Enable counter synchronization // NOTE: This register and field are the same in all modes. // SYNC: Write - Self::sync(rtc); rtc.mode0().ctrla().modify(|_, w| { - // Notifications may not work with prescaler disabled w.prescaler().div1(); w.countsync().set_bit(); w }); + sync_wait!(rtc, countsync); + + Self::enable(rtc); // Errata: The first read of the count is incorrect so we need to read it // then wait for it to change. @@ -240,17 +203,6 @@ pub trait RtcMode { I::enable(rtc); } - /// Disables an RTC interrupt. - /// - /// # Safety - /// - /// Should be called only after setting the RTC mode using - /// [`set_mode`](RtcMode::set_mode). - #[inline] - fn disable_interrupt(rtc: &Rtc) { - I::disable(rtc); - } - /// Returns whether an RTC interrupt has been triggered. /// /// # Safety @@ -294,12 +246,15 @@ pub trait RtcMode { #[hal_macro_helper] fn disable(rtc: &Rtc) { // NOTE: This register and field are the same in all modes. - // SYNC: Write - Self::sync(rtc); #[hal_cfg(any("rtc-d11", "rtc-d21"))] rtc.mode0().ctrl().modify(|_, w| w.enable().clear_bit()); #[hal_cfg("rtc-d5x")] - rtc.mode0().ctrla().modify(|_, w| w.enable().clear_bit()); + { + rtc.mode0().ctrla().modify(|_, w| w.enable().clear_bit()); + + sync_wait!(rtc, enable) + } + } /// Enables the RTC. @@ -311,13 +266,13 @@ pub trait RtcMode { #[hal_macro_helper] fn enable(rtc: &Rtc) { // NOTE: This register and field are the same in all modes. - // SYNC: Write - Self::sync(rtc); - #[hal_cfg(any("rtc-d11", "rtc-d21"))] rtc.mode0().ctrl().modify(|_, w| w.enable().set_bit()); #[hal_cfg("rtc-d5x")] - rtc.mode0().ctrla().modify(|_, w| w.enable().set_bit()); + { + rtc.mode0().ctrla().modify(|_, w| w.enable().set_bit()); + sync_wait!(rtc, enable) + } } /// Waits until the COUNT register changes. @@ -351,34 +306,14 @@ pub mod mode0 { use super::*; create_rtc_interrupt!(mode0, Compare0, cmp0); - #[cfg(feature = "rtic")] #[hal_cfg("rtc-d5x")] create_rtc_interrupt!(mode0, Compare1, cmp1); - #[cfg(feature = "rtic")] #[hal_cfg("rtc-d5x")] create_rtc_interrupt!(mode0, Overflow, ovf); /// The RTC operating in MODE0 (32-bit COUNT) pub struct RtcMode0; - impl RtcMode0 { - /// Sets or resets the match clear bit, which clears the counter when a - /// compare value matches. - /// - /// # Safety - /// - /// This should only be called when the RTC is disabled. - #[inline] - #[hal_macro_helper] - pub fn set_match_clear(rtc: &Rtc, enable: bool) { - // SYNC: None - #[hal_cfg(any("rtc-d11", "rtc-d21"))] - rtc.mode0().ctrl().modify(|_, w| w.matchclr().bit(enable)); - #[hal_cfg("rtc-d5x")] - rtc.mode0().ctrla().modify(|_, w| w.matchclr().bit(enable)); - } - } - impl RtcMode for RtcMode0 { type Count = u32; @@ -386,7 +321,6 @@ pub mod mode0 { #[hal_macro_helper] fn set_mode(rtc: &Rtc) { // NOTE: This register and field are the same in all modes. - // SYNC: None (for these bits) #[hal_cfg(any("rtc-d11", "rtc-d21"))] rtc.mode0().ctrl().modify(|_, w| w.mode().count32()); #[hal_cfg("rtc-d5x")] @@ -400,12 +334,14 @@ pub mod mode0 { rtc.mode0().comp(number).write(|w| w.comp().bits(value)); } - // TODO(stillinbeta) handle other `number` - while rtc.mode0().syncbusy().read().comp0().bit_is_set() {} + match number { + 0 => sync_wait!(rtc, comp0), + 1 => sync_wait!(rtc, comp1), + _ => {} + } } #[inline] - #[cfg(feature = "rtic")] fn get_compare(rtc: &Rtc, number: usize) -> Self::Count { // SYNC: Write (we just read though) rtc.mode0().comp(number).read().bits() @@ -422,29 +358,18 @@ pub mod mode0 { } // SYNC: Read/Write - Self::sync(rtc); + sync_wait!(rtc, count); rtc.mode0().count().read().bits() } - - #[inline] - fn set_count(rtc: &Rtc, count: Self::Count) { - // SYNC: Read/Write - Self::sync(rtc); - unsafe { rtc.mode0().count().write(|w| w.count().bits(count)) }; - } } } /// Interface for using the RTC in MODE1 (16-bit COUNT) -#[hal_cfg(any("rtc-d11", "rtc-d21"))] -#[cfg(feature = "rtic")] pub mod mode1 { use super::*; create_rtc_interrupt!(mode1, Compare0, cmp0); - #[cfg(feature = "rtic")] create_rtc_interrupt!(mode1, Compare1, cmp1); - #[cfg(feature = "rtic")] create_rtc_interrupt!(mode1, Overflow, ovf); /// The RTC operating in MODE1 (16-bit COUNT) @@ -478,7 +403,6 @@ pub mod mode1 { } #[inline] - #[cfg(feature = "rtic")] fn get_compare(rtc: &Rtc, number: usize) -> Self::Count { // SYNC: Write (we just read though) rtc.mode1().comp(number).read().bits() @@ -498,151 +422,5 @@ pub mod mode1 { Self::sync(rtc); rtc.mode1().count().read().bits() } - - #[inline] - fn set_count(rtc: &Rtc, count: Self::Count) { - // SYNC: Read/Write - Self::sync(rtc); - unsafe { rtc.mode1().count().write(|w| w.count().bits(count)) }; - } - } -} - -/// Interface for using the RTC in MODE2 (Clock/Calendar) -pub mod mode2 { - use super::*; - - // These actually aren't needed for anything right now - //create_rtc_interrupt!(mode2, Alarm0, alarm0); - //create_rtc_interrupt!(mode2, Alarm1, alarm1); - - /// Datetime represents an RTC clock/calendar value. - #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] - pub struct Datetime { - pub seconds: u8, - pub minutes: u8, - pub hours: u8, - pub day: u8, - pub month: u8, - pub year: u8, - } - - /// Macro to read from to the clock or alarm registers. - macro_rules! from_reg_datetime { - ($regr:ident) => { - impl From for Datetime { - fn from(clock: pac::rtc::mode2::$regr::R) -> Datetime { - Datetime { - seconds: clock.second().bits(), - minutes: clock.minute().bits(), - hours: clock.hour().bits(), - day: clock.day().bits(), - month: clock.month().bits(), - year: clock.year().bits(), - } - } - } - }; - } - - from_reg_datetime!(clock); - #[hal_cfg(any("rtc-d11", "rtc-d21"))] - from_reg_datetime!(alarm); - #[hal_cfg("rtc-d5x")] - from_reg_datetime!(alarm0); - #[hal_cfg("rtc-d5x")] - from_reg_datetime!(alarm1); - - /// Macro to write to the clock or alarm registers. - macro_rules! write_datetime { - ($regw:ident, $time:ident) => { - unsafe { - $regw - .second() - .bits($time.seconds) - .minute() - .bits($time.minutes) - .hour() - .bits($time.hours) - .day() - .bits($time.day) - .month() - .bits($time.month) - .year() - .bits($time.year) - } - }; - } - - /// The RTC operating in MODE2 (Clock/Calendar) - pub struct RtcMode2; - - impl RtcMode for RtcMode2 { - type Count = Datetime; - - #[inline] - #[hal_macro_helper] - fn set_mode(rtc: &Rtc) { - // SYNC: Write - Self::sync(rtc); - // NOTE: This register and field are the same in all modes. - #[hal_cfg(any("rtc-d11", "rtc-d21"))] - rtc.mode0().ctrl().modify(|_, w| w.mode().clock()); - #[hal_cfg("rtc-d5x")] - rtc.mode0().ctrla().modify(|_, w| w.mode().clock()); - } - - #[inline] - #[hal_macro_helper] - fn set_compare(rtc: &Rtc, _number: usize, value: Self::Count) { - // SYNC: Write - Self::sync(rtc); - - #[hal_cfg(any("rtc-d11", "rtc-d21"))] - rtc.mode2().alarm(0).write(|w| write_datetime!(w, value)); - #[hal_cfg("rtc-d5x")] - if _number == 0 { - rtc.mode2().alarm0().write(|w| write_datetime!(w, value)); - } else { - rtc.mode2().alarm1().write(|w| write_datetime!(w, value)); - } - } - - #[inline] - #[hal_macro_helper] - #[cfg(feature = "rtic")] - fn get_compare(rtc: &Rtc, _number: usize) -> Self::Count { - // SYNC: Write (we just read though) - #[hal_cfg(any("rtc-d11", "rtc-d21"))] - return rtc.mode2().alarm(0).read().into(); - #[hal_cfg("rtc-d5x")] - if _number == 0 { - rtc.mode2().alarm0().read().into() - } else { - rtc.mode2().alarm1().read().into() - } - } - - #[inline] - #[hal_macro_helper] - fn count(rtc: &Rtc) -> Self::Count { - #[hal_cfg(any("rtc-d11", "rtc-d21"))] - { - // Request syncing of the COUNT register. - // SYNC: None - rtc.mode2().readreq().modify(|_, w| w.rreq().set_bit()); - } - - // SYNC: Read/Write - Self::sync(rtc); - rtc.mode2().clock().read().into() - } - - #[inline] - fn set_count(rtc: &Rtc, count: Self::Count) { - // SYNC: Read/Write - Self::sync(rtc); - rtc.mode2().clock().write(|w| write_datetime!(w, count)); - } } } From 9d8472d6d1e855d1e42345ee2e689cf8e4646ae7 Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Tue, 25 Mar 2025 07:49:06 -0700 Subject: [PATCH 04/21] fmt and cleanup --- boards/pyportal/examples/blinky_interrupt.rs | 83 +++++++++----------- boards/pyportal/examples/embassy_timer.rs | 4 +- boards/pyportal/examples/interrupt_timer.rs | 81 ------------------- 3 files changed, 37 insertions(+), 131 deletions(-) delete mode 100644 boards/pyportal/examples/interrupt_timer.rs diff --git a/boards/pyportal/examples/blinky_interrupt.rs b/boards/pyportal/examples/blinky_interrupt.rs index cf6128c4ff30..847149a05c6b 100644 --- a/boards/pyportal/examples/blinky_interrupt.rs +++ b/boards/pyportal/examples/blinky_interrupt.rs @@ -1,31 +1,30 @@ -//! Turn on and off with an LED +//! Turn on and off with an LED #![no_std] #![no_main] use core::{cell::RefCell, mem}; - -use rtt_target::rprintln; use atsamd_hal::ehal::digital::{OutputPin, StatefulOutputPin}; -use pyportal as bsp; -use critical_section::Mutex; use bsp::{ pac::{interrupt, CorePeripherals, Interrupt, Peripherals}, - pin_alias, - RedLed, + pin_alias, RedLed, }; use cortex_m::peripheral::NVIC; use cortex_m_rt::entry; +use critical_section::Mutex; +use pyportal as bsp; +use rtt_target::rprintln; use panic_semihosting as _; macro_rules! sync_wait { ($mode0:expr, $register:ident) => { while $mode0.syncbusy().read().$register().bit_is_set() {} - } + }; } -static BACKLIGHT_PIN: Mutex> = Mutex::new(RefCell::new(unsafe { mem::zeroed() })); +static BACKLIGHT_PIN: Mutex> = + Mutex::new(RefCell::new(unsafe { mem::zeroed() })); #[entry] fn main() -> ! { @@ -62,15 +61,11 @@ fn main() -> ! { // Run RTC when main chip is paused mode0.dbgctrl().write(|w| w.dbgrun().set_bit()); - - // disable the clock mode0.ctrla().write(|w| w.enable().clear_bit()); // write sync for RTC enable sync_wait!(mode0, enable); - - // trigger a reset mode0.ctrla().modify(|_, w| w.swrst().set_bit()); // write sync reset @@ -94,11 +89,9 @@ fn main() -> ! { // When CTRLA.COUNTSYNC is enabled, the first COUNT value is not correctly synchronized and thus it // is a wrong value. - // clear flag mode0.intflag().write(|w| w.cmp0().set_bit()); - // wait for count to be ready sync_wait!(mode0, count); // read the current count @@ -107,14 +100,11 @@ fn main() -> ! { let next = count + (5 * 32_768); rprintln!("count is {}, waking up at {}", count, next); - mode0.comp(0).write(|w| unsafe { w.comp().bits(next) }); // wait write - sync_wait!(mode0, comp0); - mode0.intflag().write(|w| w.cmp0().set_bit()); // enable interupt mode0.intenset().write(|w| w.cmp0().set_bit()); @@ -123,8 +113,9 @@ fn main() -> ! { // BACKLIGHT_PIN.borrow_ref_mut(cs).set_low(); // }); - - unsafe { NVIC::unmask(Interrupt::RTC); } + unsafe { + NVIC::unmask(Interrupt::RTC); + } // Enable the RTC mode0.ctrla().modify(|_, w| w.enable().set_bit()); @@ -142,31 +133,29 @@ fn RTC() { // unsafe { NVIC::mask(Interrupt::RTC) }; critical_section::with(|_cs| { - - let peripherals = unsafe { Peripherals::steal() }; - - let mode0 = peripherals.rtc.mode0(); - - // is this actually an RTC compare0 interrupt - if mode0.intflag().read().cmp0().bit_is_set() { - // clear the interrupt bit - - critical_section::with(|cs| { - BACKLIGHT_PIN.borrow_ref_mut(cs).toggle(); - }); - - - sync_wait!(mode0, count); - let count: u32 = mode0.count().read().count().bits(); - // add 5 seconds - let next = count + (2 * 32_768); - rprintln!("count is {}, waking up at {}", count, next); - - mode0.comp(0).write(|w| unsafe { w.comp().bits(next) }); - // wait write - sync_wait!(mode0, comp0); - mode0.intflag().write(|w| w.cmp0().set_bit()); - } - // unsafe { NVIC::unmask(Interrupt::RTC) }; - }) + let peripherals = unsafe { Peripherals::steal() }; + + let mode0 = peripherals.rtc.mode0(); + + // is this actually an RTC compare0 interrupt + if mode0.intflag().read().cmp0().bit_is_set() { + // clear the interrupt bit + + critical_section::with(|cs| { + BACKLIGHT_PIN.borrow_ref_mut(cs).toggle(); + }); + + sync_wait!(mode0, count); + let count: u32 = mode0.count().read().count().bits(); + // add 5 seconds + let next = count + (2 * 32_768); + rprintln!("count is {}, waking up at {}", count, next); + + mode0.comp(0).write(|w| unsafe { w.comp().bits(next) }); + // wait write + sync_wait!(mode0, comp0); + mode0.intflag().write(|w| w.cmp0().set_bit()); + } + // unsafe { NVIC::unmask(Interrupt::RTC) }; + }) } diff --git a/boards/pyportal/examples/embassy_timer.rs b/boards/pyportal/examples/embassy_timer.rs index 2d667fb680fb..de2063c60b31 100644 --- a/boards/pyportal/examples/embassy_timer.rs +++ b/boards/pyportal/examples/embassy_timer.rs @@ -7,10 +7,8 @@ use panic_halt as _; use panic_semihosting as _; use bsp::{hal, pac, pin_alias}; +use hal::ehal::digital::StatefulOutputPin; use pyportal as bsp; -use hal::{ - ehal::digital::StatefulOutputPin, -}; use embassy_time::Timer; diff --git a/boards/pyportal/examples/interrupt_timer.rs b/boards/pyportal/examples/interrupt_timer.rs deleted file mode 100644 index 248dfde04cd1..000000000000 --- a/boards/pyportal/examples/interrupt_timer.rs +++ /dev/null @@ -1,81 +0,0 @@ -#![no_std] -#![no_main] - -#[cfg(not(feature = "use_semihosting"))] -use panic_halt as _; -#[cfg(feature = "use_semihosting")] -use panic_semihosting as _; - -use bsp::{hal, pac, pin_alias}; -use pyportal as bsp; - -use hal::{ - clock::GenericClockController, - ehal::digital::StatefulOutputPin, - rtc::modes::{ - mode0::{Compare0, RtcMode0}, - RtcMode, - }, -}; -use pac::{interrupt, Interrupt, NVIC}; - -use cortex_m_rt::entry; -use cortex_m_semihosting::hprintln; - -#[entry] -fn main() -> ! { - let mut peripherals = pac::Peripherals::take().unwrap(); - - let _clocks = GenericClockController::with_internal_32kosc( - peripherals.gclk, - &mut peripherals.mclk, - &mut peripherals.osc32kctrl, - &mut peripherals.oscctrl, - &mut peripherals.nvmctrl, - ); - - // Select the 32khz source - peripherals - .osc32kctrl - .rtcctrl() - .write(|w| w.rtcsel().ulp1k()); - - let rtc = peripherals.rtc; - - rtc.mode0().dbgctrl().write(|w| w.dbgrun().set_bit()); - RtcMode0::disable(&rtc); - RtcMode0::reset(&rtc); - RtcMode0::set_mode(&rtc); - - RtcMode0::start_and_initialize(&rtc); - RtcMode0::clear_interrupt_flag::(&rtc); - RtcMode0::enable_interrupt::(&rtc); - rtc.mode0().evctrl().write(|w| w.cmpeo0().set_bit()); - while rtc.mode0().syncbusy().read(). - - unsafe { - let mut nvic: cortex_m::peripheral::NVIC = core::mem::transmute(()); - nvic.set_priority(Interrupt::RTC, 1); - NVIC::unmask(Interrupt::RTC); - } - - RtcMode0::enable(&rtc); - - let now = RtcMode0::count(&rtc); - let at = now + 2_000; - hprintln!("Currently {}, alarm set at {}", now, at); - - RtcMode0::set_compare(&rtc, 0, at); - - loop {} -} - -#[interrupt] -fn RTC() { - hprintln!("Hello"); - let peripherals = unsafe { pac::Peripherals::steal() }; - let pins = bsp::Pins::new(peripherals.port); - let mut red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); - - red_led.toggle().unwrap(); -} From 9b8bdef082f8679d9e0f47186a642ffc14b89fbf Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Tue, 25 Mar 2025 07:57:36 -0700 Subject: [PATCH 05/21] fix blinky interrupt example --- boards/pyportal/Cargo.toml | 3 +++ boards/pyportal/examples/blinky_interrupt.rs | 13 +++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/boards/pyportal/Cargo.toml b/boards/pyportal/Cargo.toml index 240754bc6a15..c2376ff8a371 100644 --- a/boards/pyportal/Cargo.toml +++ b/boards/pyportal/Cargo.toml @@ -58,6 +58,9 @@ version = "0.5" [dev-dependencies.ws2812-timer-delay] version = "~0.3" +[dev-dependencies.rtt-target] +version = "0.6" + [features] # ask the HAL to enable atsamd51j support default = ["rt", "atsamd-hal/samd51j"] diff --git a/boards/pyportal/examples/blinky_interrupt.rs b/boards/pyportal/examples/blinky_interrupt.rs index 847149a05c6b..75c8b128b54c 100644 --- a/boards/pyportal/examples/blinky_interrupt.rs +++ b/boards/pyportal/examples/blinky_interrupt.rs @@ -23,25 +23,26 @@ macro_rules! sync_wait { }; } -static BACKLIGHT_PIN: Mutex> = +static BACKLIGHT_PIN: Mutex> = Mutex::new(RefCell::new(unsafe { mem::zeroed() })); #[entry] fn main() -> ! { + rtt_target::rtt_init_print!(); let peripherals = Peripherals::take().unwrap(); - let mut core = CorePeripherals::take().unwrap(); + let _core = CorePeripherals::take().unwrap(); let pins = bsp::Pins::new(peripherals.port); - let mut red_led: bsp::TftBacklight = pin_alias!(pins.tft_backlight).into(); + let red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); critical_section::with(|cs| { let _ = BACKLIGHT_PIN.replace(cs, red_led); - BACKLIGHT_PIN.borrow_ref_mut(cs).set_low(); + BACKLIGHT_PIN.borrow_ref_mut(cs).set_low().unwrap(); }); - // rprintln!("starting up"); + rprintln!("starting up"); // enable global interrupts unsafe { @@ -142,7 +143,7 @@ fn RTC() { // clear the interrupt bit critical_section::with(|cs| { - BACKLIGHT_PIN.borrow_ref_mut(cs).toggle(); + BACKLIGHT_PIN.borrow_ref_mut(cs).toggle().unwrap(); }); sync_wait!(mode0, count); From 60ce99a645e6032dcff98dcd8bef70b908d7264d Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Tue, 25 Mar 2025 08:05:40 -0700 Subject: [PATCH 06/21] cargo fmt (with line wrapping now) --- boards/pyportal/examples/blinky_interrupt.rs | 11 +++++------ hal/src/rtc/embassy/mod.rs | 3 +-- hal/src/rtc/modes.rs | 4 +--- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/boards/pyportal/examples/blinky_interrupt.rs b/boards/pyportal/examples/blinky_interrupt.rs index 75c8b128b54c..c682d5a8232c 100644 --- a/boards/pyportal/examples/blinky_interrupt.rs +++ b/boards/pyportal/examples/blinky_interrupt.rs @@ -23,12 +23,10 @@ macro_rules! sync_wait { }; } -static BACKLIGHT_PIN: Mutex> = - Mutex::new(RefCell::new(unsafe { mem::zeroed() })); +static BACKLIGHT_PIN: Mutex> = Mutex::new(RefCell::new(unsafe { mem::zeroed() })); #[entry] fn main() -> ! { - rtt_target::rtt_init_print!(); let peripherals = Peripherals::take().unwrap(); @@ -79,7 +77,8 @@ fn main() -> ! { // Use 32 bit counter w.prescaler().div1(); // The COUNT register requires synchronization when reading. - // Disabling the synchronization will prevent reading valid values from the COUNT register. + // Disabling the synchronization will prevent reading valid values from the + // COUNT register. w.countsync().set_bit(); w.matchclr().clear_bit(); w @@ -87,8 +86,8 @@ fn main() -> ! { // write sync for countsync let init = mode0.count().read().count().bits(); sync_wait!(mode0, countsync); - // When CTRLA.COUNTSYNC is enabled, the first COUNT value is not correctly synchronized and thus it - // is a wrong value. + // When CTRLA.COUNTSYNC is enabled, the first COUNT value is not correctly + // synchronized and thus it is a wrong value. // clear flag mode0.intflag().write(|w| w.cmp0().set_bit()); diff --git a/hal/src/rtc/embassy/mod.rs b/hal/src/rtc/embassy/mod.rs index 86b773b4a21b..efda475dc0cb 100644 --- a/hal/src/rtc/embassy/mod.rs +++ b/hal/src/rtc/embassy/mod.rs @@ -1,7 +1,7 @@ use core::cell::RefCell; use core::task::Waker; -use crate::pac::{interrupt, Interrupt, Rtc, NVIC, Osc32kctrl}; +use crate::pac::{interrupt, Interrupt, Osc32kctrl, Rtc, NVIC}; use crate::rtc::modes::{ mode0::{Compare0, RtcMode0}, RtcMode, @@ -46,7 +46,6 @@ fn RTC() { RtcMode0::clear_interrupt_flag::(&rtc); } }); - } impl Driver for AtmelDriver { diff --git a/hal/src/rtc/modes.rs b/hal/src/rtc/modes.rs index e38e43081388..8f80296f0074 100644 --- a/hal/src/rtc/modes.rs +++ b/hal/src/rtc/modes.rs @@ -75,7 +75,7 @@ macro_rules! create_rtc_interrupt { macro_rules! sync_wait { ($rtc:expr, $register:ident) => { while $rtc.mode0().syncbusy().read().$register().bit_is_set() {} - } + }; } /// An abstraction of an RTC in a particular mode that provides low-level @@ -170,7 +170,6 @@ pub trait RtcMode { #[inline] #[hal_macro_helper] fn start_and_initialize(rtc: &Rtc) { - // Enable counter sync on SAMx5x, the counter cannot be read otherwise. #[hal_cfg("rtc-d5x")] { @@ -254,7 +253,6 @@ pub trait RtcMode { sync_wait!(rtc, enable) } - } /// Enables the RTC. From be38cbd3628ffe513335c87e989d7f89a36cc1cb Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Wed, 26 Mar 2025 07:43:05 -0700 Subject: [PATCH 07/21] Fmt and moving stuff --- hal/src/rtc/{embassy/mod.rs => embassy.rs} | 0 hal/src/rtc/modes.rs | 9 +++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) rename hal/src/rtc/{embassy/mod.rs => embassy.rs} (100%) diff --git a/hal/src/rtc/embassy/mod.rs b/hal/src/rtc/embassy.rs similarity index 100% rename from hal/src/rtc/embassy/mod.rs rename to hal/src/rtc/embassy.rs diff --git a/hal/src/rtc/modes.rs b/hal/src/rtc/modes.rs index 8f80296f0074..0e239b0891f8 100644 --- a/hal/src/rtc/modes.rs +++ b/hal/src/rtc/modes.rs @@ -54,7 +54,6 @@ macro_rules! create_rtc_interrupt { fn enable(rtc: &Rtc) { // SYNC: write rtc.$mode().intenset().write(|w| w.$bit().set_bit()); - sync_wait!(rtc, enable) } #[inline] @@ -72,6 +71,8 @@ macro_rules! create_rtc_interrupt { }; } +// will be unused in boards that don't use syncbusy +#[allow(unused_macros)] macro_rules! sync_wait { ($rtc:expr, $register:ident) => { while $rtc.mode0().syncbusy().read().$register().bit_is_set() {} @@ -170,6 +171,8 @@ pub trait RtcMode { #[inline] #[hal_macro_helper] fn start_and_initialize(rtc: &Rtc) { + Self::enable(rtc); + // Enable counter sync on SAMx5x, the counter cannot be read otherwise. #[hal_cfg("rtc-d5x")] { @@ -326,12 +329,14 @@ pub mod mode0 { } #[inline] + #[hal_macro_helper] fn set_compare(rtc: &Rtc, number: usize, value: Self::Count) { // SYNC: Write unsafe { rtc.mode0().comp(number).write(|w| w.comp().bits(value)); } + #[hal_cfg("rtc-d5x")] match number { 0 => sync_wait!(rtc, comp0), 1 => sync_wait!(rtc, comp1), @@ -354,8 +359,8 @@ pub mod mode0 { // SYNC: None rtc.mode0().readreq().modify(|_, w| w.rreq().set_bit()); } - // SYNC: Read/Write + #[hal_cfg("rtc-d5x")] sync_wait!(rtc, count); rtc.mode0().count().read().bits() } From 5dd855bce2d960e3c98633be338d9225b2baa9a7 Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Wed, 26 Mar 2025 07:52:55 -0700 Subject: [PATCH 08/21] comments and lints --- boards/pyportal/examples/blinky_interrupt.rs | 6 ++++-- hal/src/rtc/embassy.rs | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/boards/pyportal/examples/blinky_interrupt.rs b/boards/pyportal/examples/blinky_interrupt.rs index c682d5a8232c..a1c55f8f8d09 100644 --- a/boards/pyportal/examples/blinky_interrupt.rs +++ b/boards/pyportal/examples/blinky_interrupt.rs @@ -9,7 +9,7 @@ use bsp::{ pac::{interrupt, CorePeripherals, Interrupt, Peripherals}, pin_alias, RedLed, }; -use cortex_m::peripheral::NVIC; +use cortex_m::{asm, peripheral::NVIC}; use cortex_m_rt::entry; use critical_section::Mutex; use pyportal as bsp; @@ -125,7 +125,9 @@ fn main() -> ! { // write sync for RTC enable sync_wait!(mode0, enable); - loop {} + loop { + asm::wfi(); + } } #[interrupt] diff --git a/hal/src/rtc/embassy.rs b/hal/src/rtc/embassy.rs index efda475dc0cb..ddb5369846a3 100644 --- a/hal/src/rtc/embassy.rs +++ b/hal/src/rtc/embassy.rs @@ -1,3 +1,6 @@ +//! This is a driver for embassy-time +//! it must be initialized by calling `init()` +//! which will configure the chip's RTC use core::cell::RefCell; use core::task::Waker; From be28da068d4f8ca4c98cb8b6bbcb51dbc5d79898 Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Wed, 26 Mar 2025 19:28:37 -0700 Subject: [PATCH 09/21] Move initialising the embassy driver to a macro --- boards/pyportal/examples/embassy_timer.rs | 4 +- hal/src/lib.rs | 5 + hal/src/rtc/embassy.rs | 127 ++++++++++++++-------- 3 files changed, 92 insertions(+), 44 deletions(-) diff --git a/boards/pyportal/examples/embassy_timer.rs b/boards/pyportal/examples/embassy_timer.rs index de2063c60b31..5f1a51521f07 100644 --- a/boards/pyportal/examples/embassy_timer.rs +++ b/boards/pyportal/examples/embassy_timer.rs @@ -12,6 +12,8 @@ use pyportal as bsp; use embassy_time::Timer; +hal::embassy_time!(Driver); + #[embassy_executor::main] async fn main(_s: embassy_executor::Spawner) { let peripherals = pac::Peripherals::take().unwrap(); @@ -22,7 +24,7 @@ async fn main(_s: embassy_executor::Spawner) { // Select the 32khz source // SAFETY: not in a critical section unsafe { - hal::rtc::embassy::init(); + Driver::init(); } loop { diff --git a/hal/src/lib.rs b/hal/src/lib.rs index e20f6b5025d1..244e7be14e91 100644 --- a/hal/src/lib.rs +++ b/hal/src/lib.rs @@ -17,6 +17,11 @@ pub use embedded_io_async; #[cfg(feature = "rtic")] pub use rtic_time; +// Exposed only so the macro can initialize it +#[cfg(feature = "embassy-time")] +#[doc(hidden)] +pub use embassy_time_driver; + pub mod typelevel; mod util; diff --git a/hal/src/rtc/embassy.rs b/hal/src/rtc/embassy.rs index ddb5369846a3..e92bda5802c6 100644 --- a/hal/src/rtc/embassy.rs +++ b/hal/src/rtc/embassy.rs @@ -1,10 +1,11 @@ //! This is a driver for embassy-time -//! it must be initialized by calling `init()` +//! It must be instantiated with embassy_time!() //! which will configure the chip's RTC + use core::cell::RefCell; use core::task::Waker; -use crate::pac::{interrupt, Interrupt, Osc32kctrl, Rtc, NVIC}; +use crate::pac::{Interrupt, Osc32kctrl, Rtc, NVIC}; use crate::rtc::modes::{ mode0::{Compare0, RtcMode0}, RtcMode, @@ -13,16 +14,19 @@ use critical_section::{CriticalSection, Mutex}; use embassy_time_driver::Driver; use embassy_time_queue_utils::Queue; -embassy_time_driver::time_driver_impl!(static DRIVER: AtmelDriver = AtmelDriver{ - queue: Mutex::new(RefCell::new(Queue::new())), - -}); - -struct AtmelDriver { +/// Used internally by the embassy time driver. +/// You shouldn't need this +pub struct EmbassyBackend { queue: Mutex>, } -impl AtmelDriver { +impl EmbassyBackend { + pub const fn new() -> Self { + Self { + queue: Mutex::new(RefCell::new(Queue::new())), + } + } + fn set_alarm(&self, _cs: &CriticalSection, at: u64, rtc: &Rtc) -> bool { // Embassy uses u64::MAX as a "no upcoming interrupt" sentinel let at = match u32::try_from(at) { @@ -34,24 +38,46 @@ impl AtmelDriver { RtcMode0::set_compare(rtc, 0, at); true } -} -#[interrupt] -fn RTC() { - critical_section::with(|cs| { - let rtc = unsafe { Rtc::steal() }; - if RtcMode0::check_interrupt_flag::(&rtc) { + pub fn handle_interrupt(&self, rtc: &Rtc, cs: CriticalSection) { + if RtcMode0::check_interrupt_flag::(rtc) { // Due to synchronization delay, the RTC may be slightly behind // Assume the current time is the time the interrupt is set for - let now = RtcMode0::get_compare(&rtc, 0) as u64; - let next = DRIVER.queue.borrow_ref_mut(cs).next_expiration(now); - DRIVER.set_alarm(&cs, next, &rtc); - RtcMode0::clear_interrupt_flag::(&rtc); + let now = RtcMode0::get_compare(rtc, 0) as u64; + let next = self.queue.borrow_ref_mut(cs).next_expiration(now); + self.set_alarm(&cs, next, &rtc); + RtcMode0::clear_interrupt_flag::(rtc); } - }); + } + + /// # Safety + /// + /// This enables interrupts, which can break out of critical sections + pub unsafe fn init() { + let osc32 = Osc32kctrl::steal(); + osc32.rtcctrl().write(|w| w.rtcsel().ulp32k()); + + let rtc = Rtc::steal(); + + RtcMode0::disable(&rtc); + RtcMode0::reset(&rtc); + RtcMode0::set_mode(&rtc); + + RtcMode0::start_and_initialize(&rtc); + RtcMode0::clear_interrupt_flag::(&rtc); + RtcMode0::enable_interrupt::(&rtc); + + RtcMode0::enable(&rtc); + unsafe { + cortex_m::interrupt::enable(); + let mut nvic: cortex_m::peripheral::NVIC = core::mem::transmute(()); + nvic.set_priority(Interrupt::RTC, 1); + NVIC::unmask(Interrupt::RTC); + } + } } -impl Driver for AtmelDriver { +impl Driver for EmbassyBackend { fn now(&self) -> u64 { critical_section::with(|_cs| { let rtc = unsafe { Rtc::steal() }; @@ -72,28 +98,43 @@ impl Driver for AtmelDriver { } } -/// # Safety +/// Create an embassy-time compliant driver +/// This driver should be called outside any function +/// The driver must be started by calling init() on the created struct +/// ```invalid +/// rtc::embassy::embassy_time!(Driver); /// -/// This enables interrupts, which can break out of critical sections -pub unsafe fn init() { - let osc32 = Osc32kctrl::steal(); - osc32.rtcctrl().write(|w| w.rtcsel().ulp32k()); - - let rtc = Rtc::steal(); - - RtcMode0::disable(&rtc); - RtcMode0::reset(&rtc); - RtcMode0::set_mode(&rtc); - - RtcMode0::start_and_initialize(&rtc); - RtcMode0::clear_interrupt_flag::(&rtc); - RtcMode0::enable_interrupt::(&rtc); - - RtcMode0::enable(&rtc); - unsafe { - cortex_m::interrupt::enable(); - let mut nvic: cortex_m::peripheral::NVIC = core::mem::transmute(()); - nvic.set_priority(Interrupt::RTC, 1); - NVIC::unmask(Interrupt::RTC); +/// #[embassy_executor::main] +/// async fn main(_s: embassy_executor::Spawner) { +/// /// Safety: called outside a critical section +/// unsafe { +/// Driver::init(); +/// } +/// } +/// ``` +#[macro_export] +macro_rules! embassy_time { + ($name: ident) => { + + use crate::pac::interrupt; + use crate::hal::{embassy_time_driver, rtc::embassy::EmbassyBackend}; + + embassy_time_driver::time_driver_impl!(static DRIVER: EmbassyBackend = EmbassyBackend::new()); + + #[crate::pac::interrupt] + fn RTC() { + critical_section::with(|cs| { + let rtc = unsafe { crate::pac::Rtc::steal() }; + DRIVER.handle_interrupt(&rtc, cs) + }); + } + + pub struct $name; + + impl $name { + unsafe fn init() { + EmbassyBackend::init(); + } + } } } From f49b0cdcaee179c5e36fbdf6083fb256781dc352 Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Wed, 26 Mar 2025 19:53:15 -0700 Subject: [PATCH 10/21] Comments and tidying for rtc modes --- hal/src/rtc/modes.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/hal/src/rtc/modes.rs b/hal/src/rtc/modes.rs index 0e239b0891f8..4ef1baa41a34 100644 --- a/hal/src/rtc/modes.rs +++ b/hal/src/rtc/modes.rs @@ -52,7 +52,7 @@ macro_rules! create_rtc_interrupt { impl RtcInterrupt for $name { #[inline] fn enable(rtc: &Rtc) { - // SYNC: write + // SYNC: None rtc.$mode().intenset().write(|w| w.$bit().set_bit()); } @@ -152,14 +152,10 @@ pub trait RtcMode { rtc.mode0().ctrla().modify(|_, w| w.swrst().set_bit()); // Wait for the reset to complete - // SYNC: Write (we just read though) #[hal_cfg(any("rtc-d11", "rtc-d21"))] while rtc.mode0().ctrl().read().swrst().bit_is_set() {} #[hal_cfg("rtc-d5x")] - { - while rtc.mode0().ctrla().read().swrst().bit_is_set() {} - sync_wait!(rtc, swrst) - } + sync_wait!(rtc, swrst) } /// Starts the RTC and does any required initialization for this mode. @@ -186,8 +182,6 @@ pub trait RtcMode { }); sync_wait!(rtc, countsync); - Self::enable(rtc); - // Errata: The first read of the count is incorrect so we need to read it // then wait for it to change. Self::_wait_for_count_change(rtc); @@ -248,6 +242,7 @@ pub trait RtcMode { #[hal_macro_helper] fn disable(rtc: &Rtc) { // NOTE: This register and field are the same in all modes. + // SYNC: write #[hal_cfg(any("rtc-d11", "rtc-d21"))] rtc.mode0().ctrl().modify(|_, w| w.enable().clear_bit()); #[hal_cfg("rtc-d5x")] @@ -267,6 +262,7 @@ pub trait RtcMode { #[hal_macro_helper] fn enable(rtc: &Rtc) { // NOTE: This register and field are the same in all modes. + // SYNC: write #[hal_cfg(any("rtc-d11", "rtc-d21"))] rtc.mode0().ctrl().modify(|_, w| w.enable().set_bit()); #[hal_cfg("rtc-d5x")] @@ -322,6 +318,7 @@ pub mod mode0 { #[hal_macro_helper] fn set_mode(rtc: &Rtc) { // NOTE: This register and field are the same in all modes. + // SYNC: None #[hal_cfg(any("rtc-d11", "rtc-d21"))] rtc.mode0().ctrl().modify(|_, w| w.mode().count32()); #[hal_cfg("rtc-d5x")] From 7e1a10dd42701d8e207c77cbe0659a6d0661ac6f Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Wed, 26 Mar 2025 20:46:23 -0700 Subject: [PATCH 11/21] Add embassy-time to crates.json --- boards/pyportal/.cargo/config.toml | 4 ++-- crates.json | 37 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/boards/pyportal/.cargo/config.toml b/boards/pyportal/.cargo/config.toml index 1e4c1e4efe25..b5cda7fa75de 100644 --- a/boards/pyportal/.cargo/config.toml +++ b/boards/pyportal/.cargo/config.toml @@ -1,7 +1,7 @@ # vim:ft=toml: [target.thumbv7em-none-eabihf] -runner = "hf2 elf" -#runner = 'probe-run --chip ATSAMD51J20A' +# runner = "hf2 elf" +runner = 'probe-rs run --chip ATSAMD51J20A' [build] target = "thumbv7em-none-eabihf" diff --git a/crates.json b/crates.json index c58ab16cbdd8..d5e47ab0c75a 100644 --- a/crates.json +++ b/crates.json @@ -1,6 +1,5 @@ { "boards": { - "atsame54_xpro": { "tier": 1, "build": "cargo build --examples --all-features", @@ -267,75 +266,75 @@ }, "hal_build_variants": { "samd11c": { - "features": [ "samd11c", "dma", "rtic", "defmt", "async", "undoc-features" ], + "features": [ "samd11c", "dma", "rtic", "embassy-time", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd11d": { - "features": [ "samd11d", "dma", "rtic", "defmt", "async", "undoc-features" ], + "features": [ "samd11d", "dma", "rtic", "embassy-time", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd21e": { - "features": [ "samd21e", "usb", "dma", "rtic", "defmt", "async", "undoc-features" ], + "features": [ "samd21e", "usb", "dma", "rtic", "embassy-time", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd21el": { - "features": [ "samd21el", "dma", "rtic", "defmt", "async", "undoc-features" ], + "features": [ "samd21el", "dma", "rtic", "embassy-time", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd21g": { - "features": [ "samd21g", "usb", "dma", "rtic", "defmt", "async", "undoc-features" ], + "features": [ "samd21g", "usb", "dma", "rtic", "embassy-time", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd21gl": { - "features": [ "samd21gl", "dma", "rtic", "defmt", "async", "undoc-features" ], + "features": [ "samd21gl", "dma", "rtic", "embassy-time", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd21j": { - "features": [ "samd21j", "usb", "dma", "rtic", "defmt", "async", "undoc-features" ], + "features": [ "samd21j", "usb", "dma", "rtic", "embassy-time", "defmt", "async", "undoc-features" ], "target": "thumbv6m-none-eabi" }, "samd51g": { - "features": [ "samd51g", "usb", "dma", "sdmmc", "rtic", "defmt", "async", "undoc-features" ], + "features": [ "samd51g", "usb", "dma", "sdmmc", "rtic", "embassy-time", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "samd51j": { - "features": [ "samd51j", "usb", "dma", "sdmmc", "rtic", "defmt", "async", "undoc-features" ], + "features": [ "samd51j", "usb", "dma", "sdmmc", "rtic", "embassy-time", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "samd51n": { - "features": [ "samd51n", "usb", "dma", "sdmmc", "rtic", "defmt", "async", "undoc-features" ], + "features": [ "samd51n", "usb", "dma", "sdmmc", "rtic", "embassy-time", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "samd51p": { - "features": [ "samd51p", "usb", "dma", "sdmmc", "rtic", "defmt", "async", "undoc-features" ], + "features": [ "samd51p", "usb", "dma", "sdmmc", "rtic", "embassy-time", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "same51g": { - "features": [ "same51g", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async", "undoc-features" ], + "features": [ "same51g", "usb", "dma", "sdmmc", "rtic", "embassy-time", "can", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "same51j": { - "features": [ "same51j", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async", "undoc-features" ], + "features": [ "same51j", "usb", "dma", "sdmmc", "rtic", "embassy-time", "can", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "same51n": { - "features": [ "same51n", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async" , "undoc-features"], + "features": [ "same51n", "usb", "dma", "sdmmc", "rtic", "embassy-time", "can", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "same53j": { - "features": [ "same53j", "usb", "dma", "sdmmc", "rtic", "defmt", "async", "undoc-features" ], + "features": [ "same53j", "usb", "dma", "sdmmc", "rtic", "embassy-time", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "same53n": { - "features": [ "same53n", "usb", "dma", "sdmmc", "rtic", "defmt", "async", "undoc-features" ], + "features": [ "same53n", "usb", "dma", "sdmmc", "rtic", "embassy-time", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "same54n": { - "features": [ "same54n", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async", "undoc-features" ], + "features": [ "same54n", "usb", "dma", "sdmmc", "rtic", "embassy-time", "can", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" }, "same54p": { - "features": [ "same54p", "usb", "dma", "sdmmc", "rtic", "can", "defmt", "async", "undoc-features" ], + "features": [ "same54p", "usb", "dma", "sdmmc", "rtic", "embassy-time", "can", "defmt", "async", "undoc-features" ], "target": "thumbv7em-none-eabihf" } } From 86098455ec087bef7f69fabf505a39bc2bb701d3 Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Thu, 27 Mar 2025 18:52:47 -0700 Subject: [PATCH 12/21] Require a token of rtc configuration --- boards/pyportal/examples/embassy_timer.rs | 20 +++++++++++-- hal/src/rtc/embassy.rs | 34 ++++++++++++++++++----- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/boards/pyportal/examples/embassy_timer.rs b/boards/pyportal/examples/embassy_timer.rs index 5f1a51521f07..0aa40422bd50 100644 --- a/boards/pyportal/examples/embassy_timer.rs +++ b/boards/pyportal/examples/embassy_timer.rs @@ -7,7 +7,8 @@ use panic_halt as _; use panic_semihosting as _; use bsp::{hal, pac, pin_alias}; -use hal::ehal::digital::StatefulOutputPin; +use hal::{ehal::digital::StatefulOutputPin, + clock::v2::{rtcosc::RtcOsc, osculp32k::OscUlp32k, clock_system_at_reset}}; use pyportal as bsp; use embassy_time::Timer; @@ -16,15 +17,28 @@ hal::embassy_time!(Driver); #[embassy_executor::main] async fn main(_s: embassy_executor::Spawner) { - let peripherals = pac::Peripherals::take().unwrap(); + let mut peripherals = pac::Peripherals::take().unwrap(); let _core = pac::CorePeripherals::take().unwrap(); let pins = bsp::Pins::new(peripherals.port); let mut red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); // Select the 32khz source + let (_, clocks, tokens) = clock_system_at_reset( + peripherals.oscctrl, + peripherals.osc32kctrl, + peripherals.gclk, + peripherals.mclk, + &mut peripherals.nvmctrl, + ); + + let (osculp32k, _) = OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base); + + let (rtc, _) = RtcOsc::enable(tokens.rtcosc, osculp32k); + // SAFETY: not in a critical section + unsafe { - Driver::init(); + Driver::init(rtc); } loop { diff --git a/hal/src/rtc/embassy.rs b/hal/src/rtc/embassy.rs index e92bda5802c6..18bf89adb661 100644 --- a/hal/src/rtc/embassy.rs +++ b/hal/src/rtc/embassy.rs @@ -5,7 +5,9 @@ use core::cell::RefCell; use core::task::Waker; -use crate::pac::{Interrupt, Osc32kctrl, Rtc, NVIC}; + +use atsamd_hal_macros::hal_cfg; +use crate::pac::{Interrupt, Rtc, NVIC}; use crate::rtc::modes::{ mode0::{Compare0, RtcMode0}, RtcMode, @@ -54,9 +56,6 @@ impl EmbassyBackend { /// /// This enables interrupts, which can break out of critical sections pub unsafe fn init() { - let osc32 = Osc32kctrl::steal(); - osc32.rtcctrl().write(|w| w.rtcsel().ulp32k()); - let rtc = Rtc::steal(); RtcMode0::disable(&rtc); @@ -98,17 +97,38 @@ impl Driver for EmbassyBackend { } } + +pub trait EmbassyRtcSource {} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +mod rtc_src { + impl super::EmbassyRtcSource for crate::clock::RtcClock {} +} + + +#[hal_cfg("clock-d5x")] +mod rtc_src{ + use crate::clock::v2::{rtcosc::RtcOsc, osculp32k::OscUlp32kId, xosc32k::Xosc32kId}; + impl super::EmbassyRtcSource for RtcOsc {} + impl super::EmbassyRtcSource for RtcOsc {} +} + +#[allow(unused_imports)] +use rtc_src::*; + /// Create an embassy-time compliant driver /// This driver should be called outside any function /// The driver must be started by calling init() on the created struct +/// with an RTC token to indicate proper configuration /// ```invalid /// rtc::embassy::embassy_time!(Driver); /// /// #[embassy_executor::main] /// async fn main(_s: embassy_executor::Spawner) { +/// let rtc_token = set_up_clock!(); /// /// Safety: called outside a critical section /// unsafe { -/// Driver::init(); +/// Driver::init(rtc_token); /// } /// } /// ``` @@ -117,7 +137,7 @@ macro_rules! embassy_time { ($name: ident) => { use crate::pac::interrupt; - use crate::hal::{embassy_time_driver, rtc::embassy::EmbassyBackend}; + use crate::hal::{embassy_time_driver, rtc::embassy::{EmbassyBackend, EmbassyRtcSource}}; embassy_time_driver::time_driver_impl!(static DRIVER: EmbassyBackend = EmbassyBackend::new()); @@ -132,7 +152,7 @@ macro_rules! embassy_time { pub struct $name; impl $name { - unsafe fn init() { + unsafe fn init(_rtc: SRC) where SRC: EmbassyRtcSource { EmbassyBackend::init(); } } From 27dbdc12454dd6aa11d632e7f6be6b83b4610359 Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Thu, 27 Mar 2025 19:12:12 -0700 Subject: [PATCH 13/21] lints and fmt --- boards/pyportal/examples/embassy_timer.rs | 6 ++++-- hal/src/rtc/embassy.rs | 22 ++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/boards/pyportal/examples/embassy_timer.rs b/boards/pyportal/examples/embassy_timer.rs index 0aa40422bd50..d47fbed630e0 100644 --- a/boards/pyportal/examples/embassy_timer.rs +++ b/boards/pyportal/examples/embassy_timer.rs @@ -7,8 +7,10 @@ use panic_halt as _; use panic_semihosting as _; use bsp::{hal, pac, pin_alias}; -use hal::{ehal::digital::StatefulOutputPin, - clock::v2::{rtcosc::RtcOsc, osculp32k::OscUlp32k, clock_system_at_reset}}; +use hal::{ + clock::v2::{clock_system_at_reset, osculp32k::OscUlp32k, rtcosc::RtcOsc}, + ehal::digital::StatefulOutputPin, +}; use pyportal as bsp; use embassy_time::Timer; diff --git a/hal/src/rtc/embassy.rs b/hal/src/rtc/embassy.rs index 18bf89adb661..3b703ee18284 100644 --- a/hal/src/rtc/embassy.rs +++ b/hal/src/rtc/embassy.rs @@ -5,13 +5,12 @@ use core::cell::RefCell; use core::task::Waker; - -use atsamd_hal_macros::hal_cfg; use crate::pac::{Interrupt, Rtc, NVIC}; use crate::rtc::modes::{ mode0::{Compare0, RtcMode0}, RtcMode, }; +use atsamd_hal_macros::hal_cfg; use critical_section::{CriticalSection, Mutex}; use embassy_time_driver::Driver; use embassy_time_queue_utils::Queue; @@ -23,6 +22,7 @@ pub struct EmbassyBackend { } impl EmbassyBackend { + #[allow(clippy::new_without_default)] pub const fn new() -> Self { Self { queue: Mutex::new(RefCell::new(Queue::new())), @@ -47,7 +47,7 @@ impl EmbassyBackend { // Assume the current time is the time the interrupt is set for let now = RtcMode0::get_compare(rtc, 0) as u64; let next = self.queue.borrow_ref_mut(cs).next_expiration(now); - self.set_alarm(&cs, next, &rtc); + self.set_alarm(&cs, next, rtc); RtcMode0::clear_interrupt_flag::(rtc); } } @@ -97,7 +97,6 @@ impl Driver for EmbassyBackend { } } - pub trait EmbassyRtcSource {} #[hal_cfg(any("clock-d11", "clock-d21"))] @@ -105,10 +104,9 @@ mod rtc_src { impl super::EmbassyRtcSource for crate::clock::RtcClock {} } - #[hal_cfg("clock-d5x")] -mod rtc_src{ - use crate::clock::v2::{rtcosc::RtcOsc, osculp32k::OscUlp32kId, xosc32k::Xosc32kId}; +mod rtc_src { + use crate::clock::v2::{osculp32k::OscUlp32kId, rtcosc::RtcOsc, xosc32k::Xosc32kId}; impl super::EmbassyRtcSource for RtcOsc {} impl super::EmbassyRtcSource for RtcOsc {} } @@ -125,7 +123,7 @@ use rtc_src::*; /// /// #[embassy_executor::main] /// async fn main(_s: embassy_executor::Spawner) { -/// let rtc_token = set_up_clock!(); +/// let rtc_token = set_up_clock!(); /// /// Safety: called outside a critical section /// unsafe { /// Driver::init(rtc_token); @@ -136,15 +134,15 @@ use rtc_src::*; macro_rules! embassy_time { ($name: ident) => { - use crate::pac::interrupt; - use crate::hal::{embassy_time_driver, rtc::embassy::{EmbassyBackend, EmbassyRtcSource}}; + use $crate::pac::interrupt; + use $crate::{embassy_time_driver, rtc::embassy::{EmbassyBackend, EmbassyRtcSource}}; embassy_time_driver::time_driver_impl!(static DRIVER: EmbassyBackend = EmbassyBackend::new()); - #[crate::pac::interrupt] + #[$crate::pac::interrupt] fn RTC() { critical_section::with(|cs| { - let rtc = unsafe { crate::pac::Rtc::steal() }; + let rtc = unsafe { $crate::pac::Rtc::steal() }; DRIVER.handle_interrupt(&rtc, cs) }); } From c03bd8c0949abb6b7f484be2b44f8eff160d296b Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Thu, 27 Mar 2025 19:37:35 -0700 Subject: [PATCH 14/21] Remove sync_wait! --- hal/src/rtc/modes.rs | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/hal/src/rtc/modes.rs b/hal/src/rtc/modes.rs index 4ef1baa41a34..de671d565a58 100644 --- a/hal/src/rtc/modes.rs +++ b/hal/src/rtc/modes.rs @@ -71,14 +71,6 @@ macro_rules! create_rtc_interrupt { }; } -// will be unused in boards that don't use syncbusy -#[allow(unused_macros)] -macro_rules! sync_wait { - ($rtc:expr, $register:ident) => { - while $rtc.mode0().syncbusy().read().$register().bit_is_set() {} - }; -} - /// An abstraction of an RTC in a particular mode that provides low-level /// access and handles all register syncing issues using only associated /// functions. @@ -155,7 +147,7 @@ pub trait RtcMode { #[hal_cfg(any("rtc-d11", "rtc-d21"))] while rtc.mode0().ctrl().read().swrst().bit_is_set() {} #[hal_cfg("rtc-d5x")] - sync_wait!(rtc, swrst) + while rtc.mode0().syncbusy().read().swrst().bit_is_set() {} } /// Starts the RTC and does any required initialization for this mode. @@ -174,13 +166,14 @@ pub trait RtcMode { { // Enable counter synchronization // NOTE: This register and field are the same in all modes. - // SYNC: Write rtc.mode0().ctrla().modify(|_, w| { + // Notifications may not work with prescaler disabled w.prescaler().div1(); + // SYNC: Write w.countsync().set_bit(); w }); - sync_wait!(rtc, countsync); + Self::sync(rtc); // Errata: The first read of the count is incorrect so we need to read it // then wait for it to change. @@ -249,7 +242,7 @@ pub trait RtcMode { { rtc.mode0().ctrla().modify(|_, w| w.enable().clear_bit()); - sync_wait!(rtc, enable) + Self::sync(rtc) } } @@ -266,10 +259,8 @@ pub trait RtcMode { #[hal_cfg(any("rtc-d11", "rtc-d21"))] rtc.mode0().ctrl().modify(|_, w| w.enable().set_bit()); #[hal_cfg("rtc-d5x")] - { - rtc.mode0().ctrla().modify(|_, w| w.enable().set_bit()); - sync_wait!(rtc, enable) - } + rtc.mode0().ctrla().modify(|_, w| w.enable().set_bit()); + Self::sync(rtc); } /// Waits until the COUNT register changes. @@ -332,13 +323,7 @@ pub mod mode0 { unsafe { rtc.mode0().comp(number).write(|w| w.comp().bits(value)); } - - #[hal_cfg("rtc-d5x")] - match number { - 0 => sync_wait!(rtc, comp0), - 1 => sync_wait!(rtc, comp1), - _ => {} - } + Self::sync(rtc); } #[inline] @@ -356,9 +341,9 @@ pub mod mode0 { // SYNC: None rtc.mode0().readreq().modify(|_, w| w.rreq().set_bit()); } + // SYNC: Read/Write - #[hal_cfg("rtc-d5x")] - sync_wait!(rtc, count); + Self::sync(rtc); rtc.mode0().count().read().bits() } } From 244a38ff3a121d800051ac2ca460d769c798eba9 Mon Sep 17 00:00:00 2001 From: Dan Whitman Date: Mon, 23 Jun 2025 11:52:50 -0400 Subject: [PATCH 15/21] Completes rebase with `master`, using the correct version of `src/rtc/modes.rs` from the merged PR #845. --- hal/src/rtc/embassy.rs | 6 +- hal/src/rtc/modes.rs | 264 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 251 insertions(+), 19 deletions(-) diff --git a/hal/src/rtc/embassy.rs b/hal/src/rtc/embassy.rs index 3b703ee18284..51a5d6e42c66 100644 --- a/hal/src/rtc/embassy.rs +++ b/hal/src/rtc/embassy.rs @@ -5,10 +5,10 @@ use core::cell::RefCell; use core::task::Waker; -use crate::pac::{Interrupt, Rtc, NVIC}; +use crate::pac::{Interrupt, NVIC, Rtc}; use crate::rtc::modes::{ - mode0::{Compare0, RtcMode0}, RtcMode, + mode0::{Compare0, RtcMode0}, }; use atsamd_hal_macros::hal_cfg; use critical_section::{CriticalSection, Mutex}; @@ -56,7 +56,7 @@ impl EmbassyBackend { /// /// This enables interrupts, which can break out of critical sections pub unsafe fn init() { - let rtc = Rtc::steal(); + let rtc = unsafe { Rtc::steal() }; RtcMode0::disable(&rtc); RtcMode0::reset(&rtc); diff --git a/hal/src/rtc/modes.rs b/hal/src/rtc/modes.rs index de671d565a58..c24cceddeef4 100644 --- a/hal/src/rtc/modes.rs +++ b/hal/src/rtc/modes.rs @@ -33,10 +33,18 @@ use crate::pac; use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use pac::Rtc; +// Import prescaler divider enum +#[hal_cfg(any("rtc-d11", "rtc-d21"))] +use crate::pac::rtc::mode0::ctrl::Prescalerselect; +#[hal_cfg("rtc-d5x")] +use crate::pac::rtc::mode0::ctrla::Prescalerselect; + /// Type-level enum for RTC interrupts. pub trait RtcInterrupt { /// Enable this interrupt. fn enable(rtc: &Rtc); + /// Disable this interrupt. + fn disable(rtc: &Rtc); /// Returns whether the interrupt has been triggered. fn check_flag(rtc: &Rtc) -> bool; /// Clears the interrupt flag so the ISR will not be called again @@ -56,6 +64,12 @@ macro_rules! create_rtc_interrupt { rtc.$mode().intenset().write(|w| w.$bit().set_bit()); } + #[inline] + fn disable(rtc: &Rtc) { + // SYNC: None + rtc.$mode().intenclr().write(|w| w.$bit().set_bit()); + } + #[inline] fn check_flag(rtc: &Rtc) -> bool { // SYNC: None @@ -82,8 +96,8 @@ pub trait RtcMode { /// /// # Safety /// - /// This can be called any time but is typically only called once before - /// calling most other methods. + /// This should only be called when the RTC is disabled, and is typically + /// only called once before calling most other methods. fn set_mode(rtc: &Rtc); /// Sets a compare value. @@ -100,6 +114,7 @@ pub trait RtcMode { /// /// Should be called only after setting the RTC mode using /// [`set_mode`](RtcMode::set_mode). + #[cfg(any(feature = "rtic", feature = "embassy-time"))] fn get_compare(rtc: &Rtc, number: usize) -> Self::Count; /// Returns the current synced COUNT value. @@ -110,6 +125,14 @@ pub trait RtcMode { /// [`set_mode`](RtcMode::set_mode). fn count(rtc: &Rtc) -> Self::Count; + /// Sets the current synced COUNT value. + /// + /// # Safety + /// + /// Should be called only after setting the RTC mode using + /// [`set_mode`](RtcMode::set_mode). + fn set_count(rtc: &Rtc, count: Self::Count); + /// Returns whether register syncing is currently happening. /// /// # Safety @@ -138,16 +161,40 @@ pub trait RtcMode { // Reset RTC back to initial settings, which disables it and enters mode 0. // NOTE: This register and field are the same in all modes. // SYNC: Write + Self::sync(rtc); #[hal_cfg(any("rtc-d11", "rtc-d21"))] rtc.mode0().ctrl().modify(|_, w| w.swrst().set_bit()); #[hal_cfg("rtc-d5x")] rtc.mode0().ctrla().modify(|_, w| w.swrst().set_bit()); // Wait for the reset to complete + // SYNC: Write (we just read though) #[hal_cfg(any("rtc-d11", "rtc-d21"))] while rtc.mode0().ctrl().read().swrst().bit_is_set() {} #[hal_cfg("rtc-d5x")] - while rtc.mode0().syncbusy().read().swrst().bit_is_set() {} + // NOTE: There is also a SWRST bit in the SYNCBUSY register but the bit CTRLA register + // is the one that clears when the reset is complete. + while rtc.mode0().ctrla().read().swrst().bit_is_set() {} + } + + /// Sets the clock prescaler divider to lower the tick rate. + /// + /// # Safety + /// + /// Should be called only when the RTC is disabled. + #[inline] + #[hal_macro_helper] + fn set_prescaler(rtc: &Rtc, divider: Prescalerselect) { + // NOTE: This register and field are the same in all modes. + // SYNC: None + #[hal_cfg(any("rtc-d11", "rtc-d21"))] + rtc.mode0() + .ctrl() + .modify(|_, w| w.prescaler().variant(divider)); + #[hal_cfg("rtc-d5x")] + rtc.mode0() + .ctrla() + .modify(|_, w| w.prescaler().variant(divider)); } /// Starts the RTC and does any required initialization for this mode. @@ -166,14 +213,14 @@ pub trait RtcMode { { // Enable counter synchronization // NOTE: This register and field are the same in all modes. + // SYNC: Write + Self::sync(rtc); rtc.mode0().ctrla().modify(|_, w| { // Notifications may not work with prescaler disabled w.prescaler().div1(); - // SYNC: Write w.countsync().set_bit(); w }); - Self::sync(rtc); // Errata: The first read of the count is incorrect so we need to read it // then wait for it to change. @@ -192,6 +239,17 @@ pub trait RtcMode { I::enable(rtc); } + /// Disables an RTC interrupt. + /// + /// # Safety + /// + /// Should be called only after setting the RTC mode using + /// [`set_mode`](RtcMode::set_mode). + #[inline] + fn disable_interrupt(rtc: &Rtc) { + I::disable(rtc); + } + /// Returns whether an RTC interrupt has been triggered. /// /// # Safety @@ -235,15 +293,12 @@ pub trait RtcMode { #[hal_macro_helper] fn disable(rtc: &Rtc) { // NOTE: This register and field are the same in all modes. - // SYNC: write + // SYNC: Write + Self::sync(rtc); #[hal_cfg(any("rtc-d11", "rtc-d21"))] rtc.mode0().ctrl().modify(|_, w| w.enable().clear_bit()); #[hal_cfg("rtc-d5x")] - { - rtc.mode0().ctrla().modify(|_, w| w.enable().clear_bit()); - - Self::sync(rtc) - } + rtc.mode0().ctrla().modify(|_, w| w.enable().clear_bit()); } /// Enables the RTC. @@ -255,12 +310,13 @@ pub trait RtcMode { #[hal_macro_helper] fn enable(rtc: &Rtc) { // NOTE: This register and field are the same in all modes. - // SYNC: write + // SYNC: Write + Self::sync(rtc); + #[hal_cfg(any("rtc-d11", "rtc-d21"))] rtc.mode0().ctrl().modify(|_, w| w.enable().set_bit()); #[hal_cfg("rtc-d5x")] rtc.mode0().ctrla().modify(|_, w| w.enable().set_bit()); - Self::sync(rtc); } /// Waits until the COUNT register changes. @@ -294,14 +350,34 @@ pub mod mode0 { use super::*; create_rtc_interrupt!(mode0, Compare0, cmp0); + #[cfg(feature = "rtic")] #[hal_cfg("rtc-d5x")] create_rtc_interrupt!(mode0, Compare1, cmp1); + #[cfg(feature = "rtic")] #[hal_cfg("rtc-d5x")] create_rtc_interrupt!(mode0, Overflow, ovf); /// The RTC operating in MODE0 (32-bit COUNT) pub struct RtcMode0; + impl RtcMode0 { + /// Sets or resets the match clear bit, which clears the counter when a + /// compare value matches. + /// + /// # Safety + /// + /// This should only be called when the RTC is disabled. + #[inline] + #[hal_macro_helper] + pub fn set_match_clear(rtc: &Rtc, enable: bool) { + // SYNC: None + #[hal_cfg(any("rtc-d11", "rtc-d21"))] + rtc.mode0().ctrl().modify(|_, w| w.matchclr().bit(enable)); + #[hal_cfg("rtc-d5x")] + rtc.mode0().ctrla().modify(|_, w| w.matchclr().bit(enable)); + } + } + impl RtcMode for RtcMode0 { type Count = u32; @@ -309,7 +385,7 @@ pub mod mode0 { #[hal_macro_helper] fn set_mode(rtc: &Rtc) { // NOTE: This register and field are the same in all modes. - // SYNC: None + // SYNC: None (for these bits) #[hal_cfg(any("rtc-d11", "rtc-d21"))] rtc.mode0().ctrl().modify(|_, w| w.mode().count32()); #[hal_cfg("rtc-d5x")] @@ -317,16 +393,16 @@ pub mod mode0 { } #[inline] - #[hal_macro_helper] fn set_compare(rtc: &Rtc, number: usize, value: Self::Count) { // SYNC: Write + Self::sync(rtc); unsafe { rtc.mode0().comp(number).write(|w| w.comp().bits(value)); } - Self::sync(rtc); } #[inline] + #[cfg(any(feature = "rtic", feature = "embassy-time"))] fn get_compare(rtc: &Rtc, number: usize) -> Self::Count { // SYNC: Write (we just read though) rtc.mode0().comp(number).read().bits() @@ -346,10 +422,19 @@ pub mod mode0 { Self::sync(rtc); rtc.mode0().count().read().bits() } + + #[inline] + fn set_count(rtc: &Rtc, count: Self::Count) { + // SYNC: Read/Write + Self::sync(rtc); + unsafe { rtc.mode0().count().write(|w| w.count().bits(count)) }; + } } } /// Interface for using the RTC in MODE1 (16-bit COUNT) +#[hal_cfg(any("rtc-d11", "rtc-d21"))] +#[cfg(feature = "rtic")] pub mod mode1 { use super::*; @@ -388,6 +473,7 @@ pub mod mode1 { } #[inline] + #[cfg(any(feature = "rtic", feature = "embassy-time"))] fn get_compare(rtc: &Rtc, number: usize) -> Self::Count { // SYNC: Write (we just read though) rtc.mode1().comp(number).read().bits() @@ -407,5 +493,151 @@ pub mod mode1 { Self::sync(rtc); rtc.mode1().count().read().bits() } + + #[inline] + fn set_count(rtc: &Rtc, count: Self::Count) { + // SYNC: Read/Write + Self::sync(rtc); + unsafe { rtc.mode1().count().write(|w| w.count().bits(count)) }; + } + } +} + +/// Interface for using the RTC in MODE2 (Clock/Calendar) +pub mod mode2 { + use super::*; + + // These actually aren't needed for anything right now + //create_rtc_interrupt!(mode2, Alarm0, alarm0); + //create_rtc_interrupt!(mode2, Alarm1, alarm1); + + /// Datetime represents an RTC clock/calendar value. + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] + pub struct Datetime { + pub seconds: u8, + pub minutes: u8, + pub hours: u8, + pub day: u8, + pub month: u8, + pub year: u8, + } + + /// Macro to read from to the clock or alarm registers. + macro_rules! from_reg_datetime { + ($regr:ident) => { + impl From for Datetime { + fn from(clock: pac::rtc::mode2::$regr::R) -> Datetime { + Datetime { + seconds: clock.second().bits(), + minutes: clock.minute().bits(), + hours: clock.hour().bits(), + day: clock.day().bits(), + month: clock.month().bits(), + year: clock.year().bits(), + } + } + } + }; + } + + from_reg_datetime!(clock); + #[hal_cfg(any("rtc-d11", "rtc-d21"))] + from_reg_datetime!(alarm); + #[hal_cfg("rtc-d5x")] + from_reg_datetime!(alarm0); + #[hal_cfg("rtc-d5x")] + from_reg_datetime!(alarm1); + + /// Macro to write to the clock or alarm registers. + macro_rules! write_datetime { + ($regw:ident, $time:ident) => { + unsafe { + $regw + .second() + .bits($time.seconds) + .minute() + .bits($time.minutes) + .hour() + .bits($time.hours) + .day() + .bits($time.day) + .month() + .bits($time.month) + .year() + .bits($time.year) + } + }; + } + + /// The RTC operating in MODE2 (Clock/Calendar) + pub struct RtcMode2; + + impl RtcMode for RtcMode2 { + type Count = Datetime; + + #[inline] + #[hal_macro_helper] + fn set_mode(rtc: &Rtc) { + // SYNC: Write + Self::sync(rtc); + // NOTE: This register and field are the same in all modes. + #[hal_cfg(any("rtc-d11", "rtc-d21"))] + rtc.mode0().ctrl().modify(|_, w| w.mode().clock()); + #[hal_cfg("rtc-d5x")] + rtc.mode0().ctrla().modify(|_, w| w.mode().clock()); + } + + #[inline] + #[hal_macro_helper] + fn set_compare(rtc: &Rtc, _number: usize, value: Self::Count) { + // SYNC: Write + Self::sync(rtc); + + #[hal_cfg(any("rtc-d11", "rtc-d21"))] + rtc.mode2().alarm(0).write(|w| write_datetime!(w, value)); + #[hal_cfg("rtc-d5x")] + if _number == 0 { + rtc.mode2().alarm0().write(|w| write_datetime!(w, value)); + } else { + rtc.mode2().alarm1().write(|w| write_datetime!(w, value)); + } + } + + #[inline] + #[hal_macro_helper] + #[cfg(any(feature = "rtic", feature = "embassy-time"))] + fn get_compare(rtc: &Rtc, _number: usize) -> Self::Count { + // SYNC: Write (we just read though) + #[hal_cfg(any("rtc-d11", "rtc-d21"))] + return rtc.mode2().alarm(0).read().into(); + #[hal_cfg("rtc-d5x")] + if _number == 0 { + rtc.mode2().alarm0().read().into() + } else { + rtc.mode2().alarm1().read().into() + } + } + + #[inline] + #[hal_macro_helper] + fn count(rtc: &Rtc) -> Self::Count { + #[hal_cfg(any("rtc-d11", "rtc-d21"))] + { + // Request syncing of the COUNT register. + // SYNC: None + rtc.mode2().readreq().modify(|_, w| w.rreq().set_bit()); + } + + // SYNC: Read/Write + Self::sync(rtc); + rtc.mode2().clock().read().into() + } + + #[inline] + fn set_count(rtc: &Rtc, count: Self::Count) { + // SYNC: Read/Write + Self::sync(rtc); + rtc.mode2().clock().write(|w| write_datetime!(w, count)); + } } } From 581574051dd722042f54ea9300f93f11ecd1071e Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Wed, 20 May 2026 21:04:31 -0700 Subject: [PATCH 16/21] revert all pyportal changes for a clean HAL merge --- boards/pyportal/.cargo/config.toml | 4 +- boards/pyportal/Cargo.toml | 21 +-- boards/pyportal/examples/blinky_interrupt.rs | 163 ------------------- boards/pyportal/examples/embassy_timer.rs | 50 ------ 4 files changed, 3 insertions(+), 235 deletions(-) delete mode 100644 boards/pyportal/examples/blinky_interrupt.rs delete mode 100644 boards/pyportal/examples/embassy_timer.rs diff --git a/boards/pyportal/.cargo/config.toml b/boards/pyportal/.cargo/config.toml index b5cda7fa75de..4cf66548fe50 100644 --- a/boards/pyportal/.cargo/config.toml +++ b/boards/pyportal/.cargo/config.toml @@ -1,7 +1,7 @@ # vim:ft=toml: [target.thumbv7em-none-eabihf] -# runner = "hf2 elf" -runner = 'probe-rs run --chip ATSAMD51J20A' +runner = "hf2 elf" +# runner = 'probe-rs run --chip ATSAMD51J20A' [build] target = "thumbv7em-none-eabihf" diff --git a/boards/pyportal/Cargo.toml b/boards/pyportal/Cargo.toml index c2376ff8a371..f32f7611f0fc 100644 --- a/boards/pyportal/Cargo.toml +++ b/boards/pyportal/Cargo.toml @@ -19,8 +19,7 @@ version = "0.7.5" optional = true [dependencies.atsamd-hal] -# version = "0.21.0" -path = "../../hal" +version = "0.21.0" default-features = false [dependencies.display-interface-parallel-gpio] @@ -41,26 +40,14 @@ panic-semihosting = "0.5" smart-leds = "~0.3" usbd-serial = "0.2.2" embedded-graphics = "0.8.1" -embassy-time = "0.4" -critical-section = "1.2.0" - -[dev-dependencies.embassy-executor] -version = "0.7" -features = ["arch-cortex-m", "executor-thread", "task-arena-size-256"] [dev-dependencies.cortex-m] features = ["critical-section-single-core"] version = "0.7.5" -[dev-dependencies.cortex-m-semihosting] -version = "0.5" - [dev-dependencies.ws2812-timer-delay] version = "~0.3" -[dev-dependencies.rtt-target] -version = "0.6" - [features] # ask the HAL to enable atsamd51j support default = ["rt", "atsamd-hal/samd51j"] @@ -68,8 +55,6 @@ rt = ["cortex-m-rt", "atsamd-hal/samd51j-rt"] usb = ["atsamd-hal/usb", "usb-device"] display = ["display-interface-parallel-gpio", "ili9341"] use_semihosting = [] -rtic = ["atsamd-hal/rtic"] -embassy-time = ["atsamd-hal/embassy-time"] # for cargo flash [package.metadata] @@ -88,7 +73,3 @@ name = "neopixel_rainbow" [[example]] name = "usb_echo" required-features = ["usb"] - -[[example]] -name = "embassy_timer" -required-features = ["embassy-time"] diff --git a/boards/pyportal/examples/blinky_interrupt.rs b/boards/pyportal/examples/blinky_interrupt.rs deleted file mode 100644 index a1c55f8f8d09..000000000000 --- a/boards/pyportal/examples/blinky_interrupt.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! Turn on and off with an LED -#![no_std] -#![no_main] - -use core::{cell::RefCell, mem}; - -use atsamd_hal::ehal::digital::{OutputPin, StatefulOutputPin}; -use bsp::{ - pac::{interrupt, CorePeripherals, Interrupt, Peripherals}, - pin_alias, RedLed, -}; -use cortex_m::{asm, peripheral::NVIC}; -use cortex_m_rt::entry; -use critical_section::Mutex; -use pyportal as bsp; -use rtt_target::rprintln; - -use panic_semihosting as _; - -macro_rules! sync_wait { - ($mode0:expr, $register:ident) => { - while $mode0.syncbusy().read().$register().bit_is_set() {} - }; -} - -static BACKLIGHT_PIN: Mutex> = Mutex::new(RefCell::new(unsafe { mem::zeroed() })); - -#[entry] -fn main() -> ! { - rtt_target::rtt_init_print!(); - - let peripherals = Peripherals::take().unwrap(); - let _core = CorePeripherals::take().unwrap(); - - let pins = bsp::Pins::new(peripherals.port); - let red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); - - critical_section::with(|cs| { - let _ = BACKLIGHT_PIN.replace(cs, red_led); - BACKLIGHT_PIN.borrow_ref_mut(cs).set_low().unwrap(); - }); - - rprintln!("starting up"); - - // enable global interrupts - unsafe { - NVIC::mask(Interrupt::RTC); - // core.NVIC.set_priority(Interrupt::RTC, 8); - cortex_m::interrupt::enable(); - }; - - // use the 32k clock - peripherals - .osc32kctrl - .rtcctrl() - .write(|w| w.rtcsel().ulp32k()); - - let mode0 = peripherals.rtc.mode0(); - - // Run RTC when main chip is paused - mode0.dbgctrl().write(|w| w.dbgrun().set_bit()); - - // disable the clock - mode0.ctrla().write(|w| w.enable().clear_bit()); - // write sync for RTC enable - sync_wait!(mode0, enable); - - // trigger a reset - mode0.ctrla().modify(|_, w| w.swrst().set_bit()); - // write sync reset - sync_wait!(mode0, swrst); - - // set the mode - mode0.ctrla().modify(|_, w| w.mode().count32()); - - mode0.ctrla().modify(|_, w| { - // Use 32 bit counter - w.prescaler().div1(); - // The COUNT register requires synchronization when reading. - // Disabling the synchronization will prevent reading valid values from the - // COUNT register. - w.countsync().set_bit(); - w.matchclr().clear_bit(); - w - }); - // write sync for countsync - let init = mode0.count().read().count().bits(); - sync_wait!(mode0, countsync); - // When CTRLA.COUNTSYNC is enabled, the first COUNT value is not correctly - // synchronized and thus it is a wrong value. - - // clear flag - mode0.intflag().write(|w| w.cmp0().set_bit()); - - // wait for count to be ready - sync_wait!(mode0, count); - // read the current count - let count: u32 = mode0.count().read().count().bits(); - // add 5 seconds - let next = count + (5 * 32_768); - rprintln!("count is {}, waking up at {}", count, next); - - mode0.comp(0).write(|w| unsafe { w.comp().bits(next) }); - // wait write - - sync_wait!(mode0, comp0); - - mode0.intflag().write(|w| w.cmp0().set_bit()); - // enable interupt - mode0.intenset().write(|w| w.cmp0().set_bit()); - - // critical_section::with(|cs| { - // BACKLIGHT_PIN.borrow_ref_mut(cs).set_low(); - // }); - - unsafe { - NVIC::unmask(Interrupt::RTC); - } - - // Enable the RTC - mode0.ctrla().modify(|_, w| w.enable().set_bit()); - - // Block to wait for countsync to be correct - while mode0.count().read().count().bits() == init {} - // write sync for RTC enable - sync_wait!(mode0, enable); - - loop { - asm::wfi(); - } -} - -#[interrupt] -fn RTC() { - // unsafe { NVIC::mask(Interrupt::RTC) }; - - critical_section::with(|_cs| { - let peripherals = unsafe { Peripherals::steal() }; - - let mode0 = peripherals.rtc.mode0(); - - // is this actually an RTC compare0 interrupt - if mode0.intflag().read().cmp0().bit_is_set() { - // clear the interrupt bit - - critical_section::with(|cs| { - BACKLIGHT_PIN.borrow_ref_mut(cs).toggle().unwrap(); - }); - - sync_wait!(mode0, count); - let count: u32 = mode0.count().read().count().bits(); - // add 5 seconds - let next = count + (2 * 32_768); - rprintln!("count is {}, waking up at {}", count, next); - - mode0.comp(0).write(|w| unsafe { w.comp().bits(next) }); - // wait write - sync_wait!(mode0, comp0); - mode0.intflag().write(|w| w.cmp0().set_bit()); - } - // unsafe { NVIC::unmask(Interrupt::RTC) }; - }) -} diff --git a/boards/pyportal/examples/embassy_timer.rs b/boards/pyportal/examples/embassy_timer.rs deleted file mode 100644 index d47fbed630e0..000000000000 --- a/boards/pyportal/examples/embassy_timer.rs +++ /dev/null @@ -1,50 +0,0 @@ -#![no_std] -#![no_main] - -#[cfg(not(feature = "use_semihosting"))] -use panic_halt as _; -#[cfg(feature = "use_semihosting")] -use panic_semihosting as _; - -use bsp::{hal, pac, pin_alias}; -use hal::{ - clock::v2::{clock_system_at_reset, osculp32k::OscUlp32k, rtcosc::RtcOsc}, - ehal::digital::StatefulOutputPin, -}; -use pyportal as bsp; - -use embassy_time::Timer; - -hal::embassy_time!(Driver); - -#[embassy_executor::main] -async fn main(_s: embassy_executor::Spawner) { - let mut peripherals = pac::Peripherals::take().unwrap(); - let _core = pac::CorePeripherals::take().unwrap(); - let pins = bsp::Pins::new(peripherals.port); - let mut red_led: bsp::RedLed = pin_alias!(pins.red_led).into(); - - // Select the 32khz source - let (_, clocks, tokens) = clock_system_at_reset( - peripherals.oscctrl, - peripherals.osc32kctrl, - peripherals.gclk, - peripherals.mclk, - &mut peripherals.nvmctrl, - ); - - let (osculp32k, _) = OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base); - - let (rtc, _) = RtcOsc::enable(tokens.rtcosc, osculp32k); - - // SAFETY: not in a critical section - - unsafe { - Driver::init(rtc); - } - - loop { - red_led.toggle().unwrap(); - Timer::after_secs(1).await; - } -} From 0a359eb7b3bf036e70a36672f7a80bff8cf2fa67 Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Wed, 20 May 2026 21:57:02 -0700 Subject: [PATCH 17/21] maybe avoid race conditions? --- hal/src/rtc/embassy.rs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/hal/src/rtc/embassy.rs b/hal/src/rtc/embassy.rs index 51a5d6e42c66..43af357621aa 100644 --- a/hal/src/rtc/embassy.rs +++ b/hal/src/rtc/embassy.rs @@ -33,11 +33,21 @@ impl EmbassyBackend { // Embassy uses u64::MAX as a "no upcoming interrupt" sentinel let at = match u32::try_from(at) { Ok(at) => at, - _ if at == u64::MAX => u32::MAX, + _ if at == u64::MAX => return true, Err(_) => return false, }; + if RtcMode0::count(rtc) >= at { + // This is in the past + return false + } + RtcMode0::set_compare(rtc, 0, at); + // double check that the timestamp is still in the future + if RtcMode0::count(rtc) >= at { + // This is in the past + return false + } true } @@ -46,8 +56,12 @@ impl EmbassyBackend { // Due to synchronization delay, the RTC may be slightly behind // Assume the current time is the time the interrupt is set for let now = RtcMode0::get_compare(rtc, 0) as u64; - let next = self.queue.borrow_ref_mut(cs).next_expiration(now); - self.set_alarm(&cs, next, rtc); + loop { + let next = self.queue.borrow_ref_mut(cs).next_expiration(now); + if self.set_alarm(&cs, next, rtc) { + break + } + } RtcMode0::clear_interrupt_flag::(rtc); } } @@ -89,9 +103,12 @@ impl Driver for EmbassyBackend { let rtc = unsafe { Rtc::steal() }; let mut queue = self.queue.borrow(cs).borrow_mut(); if queue.schedule_wake(at, waker) { - let next = queue.next_expiration(self.now()); - // We can only handle one alarm at a time right now - self.set_alarm(&cs, next, &rtc); + loop { + let next = self.queue.borrow_ref_mut(cs).next_expiration(self.now()); + + // We can only handle one alarm at a time right now + self.set_alarm(&cs, next, &rtc); + } } }); } From a2b4e4bbbf68dec08e96ba4f2719fddc83d75c95 Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Tue, 26 May 2026 07:56:05 -0700 Subject: [PATCH 18/21] bump time-queue-utils version --- hal/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hal/Cargo.toml b/hal/Cargo.toml index eb113b85a130..f04fbab55122 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -72,7 +72,7 @@ rtic-time = {version = "2.0", optional = true} usb-device = {version = "0.3.2", optional = true} embassy-time-driver = { version = "0.2", optional = true, features = ["tick-hz-32_768"] } -embassy-time-queue-utils = { version = "0.1", optional = true } +embassy-time-queue-utils = { version = "0.3", optional = true } #=============================================================================== # PACs From fa63f7681fc7fd0246c695a0a412a3e56eb1a674 Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Tue, 26 May 2026 07:57:48 -0700 Subject: [PATCH 19/21] cargo fmt --- hal/src/rtc/embassy.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hal/src/rtc/embassy.rs b/hal/src/rtc/embassy.rs index 43af357621aa..f5258d89e0a5 100644 --- a/hal/src/rtc/embassy.rs +++ b/hal/src/rtc/embassy.rs @@ -39,14 +39,14 @@ impl EmbassyBackend { if RtcMode0::count(rtc) >= at { // This is in the past - return false + return false; } RtcMode0::set_compare(rtc, 0, at); // double check that the timestamp is still in the future if RtcMode0::count(rtc) >= at { // This is in the past - return false + return false; } true } @@ -59,7 +59,7 @@ impl EmbassyBackend { loop { let next = self.queue.borrow_ref_mut(cs).next_expiration(now); if self.set_alarm(&cs, next, rtc) { - break + break; } } RtcMode0::clear_interrupt_flag::(rtc); From f893b9ae8133d68c72e1bc7c611398aee1f6ab98 Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Tue, 26 May 2026 22:02:14 -0700 Subject: [PATCH 20/21] try using embassy-sync's mutex? --- hal/Cargo.toml | 2 +- hal/src/rtc/embassy.rs | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/hal/Cargo.toml b/hal/Cargo.toml index f04fbab55122..89222426ffce 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -197,7 +197,7 @@ defmt = ["dep:defmt"] dma = [] max-channels = ["dma"] rtic = ["rtic-monotonic", "rtic-time", "portable-atomic"] -embassy-time = ["embassy-time-driver", "embassy-time-queue-utils"] +embassy-time = ["embassy-sync", "embassy-time-driver", "embassy-time-queue-utils"] sdmmc = ["embedded-sdmmc"] use_rtt = ["jlink_rtt"] undoc-features = [] diff --git a/hal/src/rtc/embassy.rs b/hal/src/rtc/embassy.rs index f5258d89e0a5..767ddc38f0b6 100644 --- a/hal/src/rtc/embassy.rs +++ b/hal/src/rtc/embassy.rs @@ -11,14 +11,15 @@ use crate::rtc::modes::{ mode0::{Compare0, RtcMode0}, }; use atsamd_hal_macros::hal_cfg; -use critical_section::{CriticalSection, Mutex}; +use critical_section::CriticalSection; +use embassy_sync::blocking_mutex::{Mutex, raw::CriticalSectionRawMutex}; use embassy_time_driver::Driver; use embassy_time_queue_utils::Queue; /// Used internally by the embassy time driver. /// You shouldn't need this pub struct EmbassyBackend { - queue: Mutex>, + queue: Mutex>, } impl EmbassyBackend { @@ -57,7 +58,7 @@ impl EmbassyBackend { // Assume the current time is the time the interrupt is set for let now = RtcMode0::get_compare(rtc, 0) as u64; loop { - let next = self.queue.borrow_ref_mut(cs).next_expiration(now); + let next = self.queue.borrow(cs).borrow_mut().next_expiration(now); if self.set_alarm(&cs, next, rtc) { break; } @@ -104,7 +105,11 @@ impl Driver for EmbassyBackend { let mut queue = self.queue.borrow(cs).borrow_mut(); if queue.schedule_wake(at, waker) { loop { - let next = self.queue.borrow_ref_mut(cs).next_expiration(self.now()); + let next = self + .queue + .borrow(cs) + .borrow_mut() + .next_expiration(self.now()); // We can only handle one alarm at a time right now self.set_alarm(&cs, next, &rtc); From 3f1fd5641689d8975ebd6e812746f076a4c83939 Mon Sep 17 00:00:00 2001 From: Ellie Frost Date: Tue, 11 Aug 2026 16:23:52 -0700 Subject: [PATCH 21/21] fix re-borrow --- hal/src/rtc/embassy.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hal/src/rtc/embassy.rs b/hal/src/rtc/embassy.rs index 767ddc38f0b6..cb3bbb80a4a0 100644 --- a/hal/src/rtc/embassy.rs +++ b/hal/src/rtc/embassy.rs @@ -33,8 +33,8 @@ impl EmbassyBackend { fn set_alarm(&self, _cs: &CriticalSection, at: u64, rtc: &Rtc) -> bool { // Embassy uses u64::MAX as a "no upcoming interrupt" sentinel let at = match u32::try_from(at) { - Ok(at) => at, _ if at == u64::MAX => return true, + Ok(at) => at, Err(_) => return false, }; @@ -105,11 +105,7 @@ impl Driver for EmbassyBackend { let mut queue = self.queue.borrow(cs).borrow_mut(); if queue.schedule_wake(at, waker) { loop { - let next = self - .queue - .borrow(cs) - .borrow_mut() - .next_expiration(self.now()); + let next = queue.next_expiration(self.now()); // We can only handle one alarm at a time right now self.set_alarm(&cs, next, &rtc);