diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 0cc1d56bb..0fedf9360 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - changed: nrf54l: Disable glitch detection and enable DC/DC in init. - changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4 +- changed: add persist() method for gpio and ppi +- added: basic RTC driver - changed: add persist() method for gpio, gpiote, timer and ppi - changed: impl Drop for Timer diff --git a/embassy-nrf/src/chips/nrf51.rs b/embassy-nrf/src/chips/nrf51.rs index fd13ae5c4..3976e8ff0 100644 --- a/embassy-nrf/src/chips/nrf51.rs +++ b/embassy-nrf/src/chips/nrf51.rs @@ -8,6 +8,7 @@ pub const FLASH_SIZE: usize = 128 * 1024; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, // WDT @@ -110,6 +111,10 @@ impl_timer!(TIMER2, TIMER2, TIMER2); impl_rng!(RNG, RNG, RNG); +impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] +impl_rtc!(RTC1, RTC1, RTC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 7e72df8fc..63ba6999a 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -12,6 +12,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'B'; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature="time-driver-rtc1"))] RTC1, // WDT @@ -156,6 +157,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] +impl_rtc!(RTC1, RTC1, RTC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); @@ -234,12 +239,12 @@ embassy_hal_internal::interrupt_mod!( TIMER0, TIMER1, TIMER2, - RTC0, TEMP, RNG, ECB, AAR_CCM, WDT, + RTC0, RTC1, QDEC, EGU0_SWI0, diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index e388e44e8..7f744f9fb 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -12,6 +12,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'E'; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature="time-driver-rtc1"))] RTC1, // WDT @@ -166,6 +167,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] +impl_rtc!(RTC1, RTC1, RTC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 96b8df30b..908167e31 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -12,6 +12,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'B'; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature="time-driver-rtc1"))] RTC1, // WDT @@ -168,6 +169,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] +impl_rtc!(RTC1, RTC1, RTC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index ad461b153..22360575b 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -15,6 +15,7 @@ embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature="time-driver-rtc1"))] RTC1, // WDT @@ -164,6 +165,10 @@ impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER3, TIMER3, TIMER3, extended); +impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] +impl_rtc!(RTC1, RTC1, RTC1); + impl_qdec!(QDEC, QDEC, QDEC); impl_rng!(RNG, RNG, RNG); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index cf2abf23a..1598df3fe 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -16,6 +16,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'G'; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature="time-driver-rtc1"))] RTC1, RTC2, @@ -182,6 +183,11 @@ impl_twim!(TWISPI1, TWIM1, TWISPI1); impl_twis!(TWISPI0, TWIS0, TWISPI0); impl_twis!(TWISPI1, TWIS1, TWISPI1); +impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] +impl_rtc!(RTC1, RTC1, RTC1); +impl_rtc!(RTC2, RTC2, RTC2); + impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index e46eb1d2b..6931fb064 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -15,6 +15,7 @@ embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, RTC2, @@ -223,6 +224,11 @@ impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_timer!(TIMER4, TIMER4, TIMER4, extended); +impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] +impl_rtc!(RTC1, RTC1, RTC1); +impl_rtc!(RTC2, RTC2, RTC2); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 88747843d..5fa521aae 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -15,6 +15,7 @@ embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, RTC2, @@ -220,6 +221,11 @@ impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_timer!(TIMER4, TIMER4, TIMER4, extended); +impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] +impl_rtc!(RTC1, RTC1, RTC1); +impl_rtc!(RTC2, RTC2, RTC2); + impl_qspi!(QSPI, QSPI, QSPI); impl_pdm!(PDM, PDM, PDM); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index c0290b7a4..730c9842d 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -168,6 +168,7 @@ embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, // WDT @@ -369,6 +370,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] +impl_rtc!(RTC1, RTC1, RTC1); + impl_qspi!(QSPI, QSPI, QSPI); impl_pdm!(PDM0, PDM0, PDM0); diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index d4c3e5353..413afc5c5 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -59,6 +59,7 @@ pub const FLASH_SIZE: usize = 256 * 1024; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, // WDT @@ -218,6 +219,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] +impl_rtc!(RTC1, RTC1, RTC1); + impl_rng!(RNG, RNG, RNG); impl_pin!(P0_00, 0, 0); diff --git a/embassy-nrf/src/chips/nrf9120.rs b/embassy-nrf/src/chips/nrf9120.rs index e8ddbf86f..5aee19d97 100644 --- a/embassy-nrf/src/chips/nrf9120.rs +++ b/embassy-nrf/src/chips/nrf9120.rs @@ -131,6 +131,7 @@ pub const FLASH_SIZE: usize = 1024 * 1024; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, // WDT @@ -276,6 +277,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] +impl_rtc!(RTC1, RTC1, RTC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 5d04a72e5..64aec217c 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -131,6 +131,7 @@ pub const FLASH_SIZE: usize = 1024 * 1024; embassy_hal_internal::peripherals! { // RTC RTC0, + #[cfg(not(feature = "time-driver-rtc1"))] RTC1, // WDT @@ -276,6 +277,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_rtc!(RTC0, RTC0, RTC0); +#[cfg(not(feature = "time-driver-rtc1"))] +impl_rtc!(RTC1, RTC1, RTC1); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 897e660b8..e0847a312 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -155,6 +155,8 @@ pub mod reset; #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] pub mod rng; #[cfg(not(feature = "_nrf54l"))] // TODO +pub mod rtc; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod saadc; #[cfg(not(feature = "_nrf54l"))] // TODO diff --git a/embassy-nrf/src/rtc.rs b/embassy-nrf/src/rtc.rs new file mode 100644 index 000000000..1a90d1e24 --- /dev/null +++ b/embassy-nrf/src/rtc.rs @@ -0,0 +1,265 @@ +//! Low-level RTC driver. + +#![macro_use] + +use embassy_hal_internal::{Peri, PeripheralType}; + +use crate::chip::interrupt::typelevel::Interrupt as _; +use crate::pac; + +/// Prescaler has an invalid value which exceeds 12 bits. +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PrescalerOutOfRangeError(u32); + +/// Compare value has an invalid value which exceeds 24 bits. +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct CompareOutOfRangeError(u32); + +/// Interrupts/Events that can be generated by the RTCn peripheral. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Interrupt { + /// Tick interrupt. + Tick, + /// Overflow interrupt. + Overflow, + /// Compare 0 interrupt. + Compare0, + /// Compare 1 interrupt. + Compare1, + /// Compare 2 interrupt. + Compare2, + /// Compare 3 interrupt. Only implemented for RTC1 and RTC2. + Compare3, +} + +/// Compare registers available on the RTCn. +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum CompareChannel { + /// Channel 0 + _0, + /// Channel 1 + _1, + /// Channel 2 + _2, + /// Channel 3. Only implemented for RTC1 and RTC2. + _3, +} + +pub(crate) trait SealedInstance { + fn regs() -> pac::rtc::Rtc; +} + +/// Basic RTC instance. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: crate::interrupt::typelevel::Interrupt; + + /// Unsafely create a peripheral instance. + /// + /// # Safety + /// + /// Potentially allows to create multiple instances of the driver for the same peripheral + /// which can lead to undefined behavior. + unsafe fn steal() -> Peri<'static, Self>; +} + +macro_rules! impl_rtc { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::rtc::SealedInstance for peripherals::$type { + #[inline] + fn regs() -> pac::rtc::Rtc { + unsafe { pac::rtc::Rtc::from_ptr(pac::$pac_type.as_ptr()) } + } + } + + impl crate::rtc::Instance for peripherals::$type { + type Interrupt = crate::interrupt::typelevel::$irq; + + unsafe fn steal() -> embassy_hal_internal::Peri<'static, Self> { + unsafe { peripherals::$type::steal() } + } + } + }; +} + +/// nRF RTC driver. +pub struct Rtc<'d, T: Instance>(Peri<'d, T>); + +impl<'d, T: Instance> Rtc<'d, T> { + /// Create a new `Rtc` driver. + /// + /// fRTC \[Hz\] = 32_768 / (`prescaler` + 1 ) + pub fn new(rtc: Peri<'d, T>, prescaler: u32) -> Result { + if prescaler >= (1 << 12) { + return Err(PrescalerOutOfRangeError(prescaler)); + } + + T::regs().prescaler().write(|w| w.set_prescaler(prescaler as u16)); + Ok(Self(rtc)) + } + + /// Create a new `Rtc` driver, configuring it to run at the given frequency. + pub fn new_for_freq(rtc: Peri<'d, T>, freq_hz: u32) -> Result { + let prescaler = (32_768 / freq_hz).saturating_sub(1); + Self::new(rtc, prescaler) + } + + /// Steal the RTC peripheral, without checking if it's already taken. + /// + /// # Safety + /// + /// Potentially allows to create multiple instances of the driver for the same peripheral + /// which can lead to undefined behavior. + pub unsafe fn steal() -> Self { + Self(unsafe { T::steal() }) + } + + /// Direct access to the RTC registers. + #[cfg(feature = "unstable-pac")] + #[inline] + pub fn regs(&mut self) -> pac::rtc::Rtc { + T::regs() + } + + /// Enable the RTC. + #[inline] + pub fn enable(&mut self) { + T::regs().tasks_start().write_value(1); + } + + /// Disable the RTC. + #[inline] + pub fn disable(&mut self) { + T::regs().tasks_stop().write_value(1); + } + + /// Enables interrupts for the given [Interrupt] source. + /// + /// Optionally also enables the interrupt in the NVIC. + pub fn enable_interrupt(&mut self, int: Interrupt, enable_in_nvic: bool) { + let regs = T::regs(); + match int { + Interrupt::Tick => regs.intenset().write(|w| w.set_tick(true)), + Interrupt::Overflow => regs.intenset().write(|w| w.set_ovrflw(true)), + Interrupt::Compare0 => regs.intenset().write(|w| w.set_compare(0, true)), + Interrupt::Compare1 => regs.intenset().write(|w| w.set_compare(1, true)), + Interrupt::Compare2 => regs.intenset().write(|w| w.set_compare(2, true)), + Interrupt::Compare3 => regs.intenset().write(|w| w.set_compare(3, true)), + } + if enable_in_nvic { + unsafe { T::Interrupt::enable() }; + } + } + + /// Disables interrupts for the given [Interrupt] source. + /// + /// Optionally also disables the interrupt in the NVIC. + pub fn disable_interrupt(&mut self, int: Interrupt, disable_in_nvic: bool) { + let regs = T::regs(); + match int { + Interrupt::Tick => regs.intenclr().write(|w| w.set_tick(true)), + Interrupt::Overflow => regs.intenclr().write(|w| w.set_ovrflw(true)), + Interrupt::Compare0 => regs.intenclr().write(|w| w.set_compare(0, true)), + Interrupt::Compare1 => regs.intenclr().write(|w| w.set_compare(1, true)), + Interrupt::Compare2 => regs.intenclr().write(|w| w.set_compare(2, true)), + Interrupt::Compare3 => regs.intenclr().write(|w| w.set_compare(3, true)), + } + if disable_in_nvic { + T::Interrupt::disable(); + } + } + + /// Enable the generation of a hardware event from a given stimulus. + pub fn enable_event(&mut self, evt: Interrupt) { + let regs = T::regs(); + match evt { + Interrupt::Tick => regs.evtenset().write(|w| w.set_tick(true)), + Interrupt::Overflow => regs.evtenset().write(|w| w.set_ovrflw(true)), + Interrupt::Compare0 => regs.evtenset().write(|w| w.set_compare(0, true)), + Interrupt::Compare1 => regs.evtenset().write(|w| w.set_compare(1, true)), + Interrupt::Compare2 => regs.evtenset().write(|w| w.set_compare(2, true)), + Interrupt::Compare3 => regs.evtenset().write(|w| w.set_compare(3, true)), + } + } + + /// Disable the generation of a hardware event from a given stimulus. + pub fn disable_event(&mut self, evt: Interrupt) { + let regs = T::regs(); + match evt { + Interrupt::Tick => regs.evtenclr().write(|w| w.set_tick(true)), + Interrupt::Overflow => regs.evtenclr().write(|w| w.set_ovrflw(true)), + Interrupt::Compare0 => regs.evtenclr().write(|w| w.set_compare(0, true)), + Interrupt::Compare1 => regs.evtenclr().write(|w| w.set_compare(1, true)), + Interrupt::Compare2 => regs.evtenclr().write(|w| w.set_compare(2, true)), + Interrupt::Compare3 => regs.evtenclr().write(|w| w.set_compare(3, true)), + } + } + + /// Resets the given event. + pub fn reset_event(&mut self, evt: Interrupt) { + let regs = T::regs(); + match evt { + Interrupt::Tick => regs.events_tick().write_value(0), + Interrupt::Overflow => regs.events_ovrflw().write_value(0), + Interrupt::Compare0 => regs.events_compare(0).write_value(0), + Interrupt::Compare1 => regs.events_compare(1).write_value(0), + Interrupt::Compare2 => regs.events_compare(2).write_value(0), + Interrupt::Compare3 => regs.events_compare(3).write_value(0), + } + } + + /// Checks if the given event has been triggered. + pub fn is_event_triggered(&self, evt: Interrupt) -> bool { + let regs = T::regs(); + let val = match evt { + Interrupt::Tick => regs.events_tick().read(), + Interrupt::Overflow => regs.events_ovrflw().read(), + Interrupt::Compare0 => regs.events_compare(0).read(), + Interrupt::Compare1 => regs.events_compare(1).read(), + Interrupt::Compare2 => regs.events_compare(2).read(), + Interrupt::Compare3 => regs.events_compare(3).read(), + }; + val == 1 + } + + /// Set the compare value of a given register. The compare registers have a width + /// of 24 bits. + pub fn set_compare(&mut self, reg: CompareChannel, val: u32) -> Result<(), CompareOutOfRangeError> { + if val >= (1 << 24) { + return Err(CompareOutOfRangeError(val)); + } + + let reg = match reg { + CompareChannel::_0 => 0, + CompareChannel::_1 => 1, + CompareChannel::_2 => 2, + CompareChannel::_3 => 3, + }; + + T::regs().cc(reg).write(|w| w.set_compare(val)); + Ok(()) + } + + /// Clear the Real Time Counter. + #[inline] + pub fn clear(&self) { + T::regs().tasks_clear().write_value(1); + } + + /// Obtain the current value of the Real Time Counter, 24 bits of range. + #[inline] + pub fn read(&self) -> u32 { + T::regs().counter().read().counter() + } + + /// Relase the RTC, returning the underlying peripheral instance. + #[inline] + pub fn release(self) -> Peri<'d, T> { + self.0 + } +} diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 452e83b7e..ca3c6f863 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -35,6 +35,7 @@ embedded-hal-async = { version = "1.0" } embedded-hal-bus = { version = "0.1", features = ["async"] } num-integer = { version = "0.1.45", default-features = false } microfft = "0.5.0" +portable-atomic = "1" [profile.release] debug = 2 diff --git a/examples/nrf52840/src/bin/rtc.rs b/examples/nrf52840/src/bin/rtc.rs new file mode 100644 index 000000000..a3df7da14 --- /dev/null +++ b/examples/nrf52840/src/bin/rtc.rs @@ -0,0 +1,57 @@ +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::interrupt; +use embassy_nrf::rtc::Rtc; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use portable_atomic::AtomicU64; +use {defmt_rtt as _, panic_probe as _}; + +// 64 bit counter which will never overflow. +static TICK_COUNTER: AtomicU64 = AtomicU64::new(0); +static RTC: Mutex>>> = + Mutex::new(RefCell::new(None)); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + defmt::println!("nRF52840 RTC example"); + let p = embassy_nrf::init(Default::default()); + let mut led = Output::new(p.P0_13, Level::High, OutputDrive::Standard); + // Counter resolution is 125 ms. + let mut rtc = Rtc::new(p.RTC0, (1 << 12) - 1).unwrap(); + rtc.enable_interrupt(embassy_nrf::rtc::Interrupt::Tick, true); + rtc.enable_event(embassy_nrf::rtc::Interrupt::Tick); + rtc.enable(); + RTC.lock(|r| { + let mut rtc_borrow = r.borrow_mut(); + *rtc_borrow = Some(rtc); + }); + + let mut last_counter_val = 0; + loop { + let current = TICK_COUNTER.load(core::sync::atomic::Ordering::Relaxed); + if current != last_counter_val { + led.toggle(); + last_counter_val = current; + } + } +} + +#[interrupt] +fn RTC0() { + // For 64-bit, we do not need to worry about overflowing, at least not for realistic program + // lifetimes. + TICK_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed); + RTC.lock(|r| { + let mut rtc_borrow = r.borrow_mut(); + rtc_borrow + .as_mut() + .unwrap() + .reset_event(embassy_nrf::rtc::Interrupt::Tick); + }); +}