Merge pull request #4686 from robamu/add-nrf-rtc-driver

add basic RTC driver for nRF
This commit is contained in:
Ulf Lilleengen 2025-09-22 09:00:06 +00:00 committed by GitHub
commit a25c5c4b2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 391 additions and 1 deletions

View File

@ -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: nrf54l: Disable glitch detection and enable DC/DC in init.
- changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4 - 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: add persist() method for gpio, gpiote, timer and ppi
- changed: impl Drop for Timer - changed: impl Drop for Timer

View File

@ -8,6 +8,7 @@ pub const FLASH_SIZE: usize = 128 * 1024;
embassy_hal_internal::peripherals! { embassy_hal_internal::peripherals! {
// RTC // RTC
RTC0, RTC0,
#[cfg(not(feature = "time-driver-rtc1"))]
RTC1, RTC1,
// WDT // WDT
@ -110,6 +111,10 @@ impl_timer!(TIMER2, TIMER2, TIMER2);
impl_rng!(RNG, RNG, RNG); 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_00, 0, 0);
impl_pin!(P0_01, 0, 1); impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2); impl_pin!(P0_02, 0, 2);

View File

@ -12,6 +12,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'B';
embassy_hal_internal::peripherals! { embassy_hal_internal::peripherals! {
// RTC // RTC
RTC0, RTC0,
#[cfg(not(feature="time-driver-rtc1"))]
RTC1, RTC1,
// WDT // WDT
@ -156,6 +157,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); 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_00, 0, 0);
impl_pin!(P0_01, 0, 1); impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2); impl_pin!(P0_02, 0, 2);
@ -234,12 +239,12 @@ embassy_hal_internal::interrupt_mod!(
TIMER0, TIMER0,
TIMER1, TIMER1,
TIMER2, TIMER2,
RTC0,
TEMP, TEMP,
RNG, RNG,
ECB, ECB,
AAR_CCM, AAR_CCM,
WDT, WDT,
RTC0,
RTC1, RTC1,
QDEC, QDEC,
EGU0_SWI0, EGU0_SWI0,

View File

@ -12,6 +12,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'E';
embassy_hal_internal::peripherals! { embassy_hal_internal::peripherals! {
// RTC // RTC
RTC0, RTC0,
#[cfg(not(feature="time-driver-rtc1"))]
RTC1, RTC1,
// WDT // WDT
@ -166,6 +167,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); 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_00, 0, 0);
impl_pin!(P0_01, 0, 1); impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2); impl_pin!(P0_02, 0, 2);

View File

@ -12,6 +12,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'B';
embassy_hal_internal::peripherals! { embassy_hal_internal::peripherals! {
// RTC // RTC
RTC0, RTC0,
#[cfg(not(feature="time-driver-rtc1"))]
RTC1, RTC1,
// WDT // WDT
@ -168,6 +169,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); 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_00, 0, 0);
impl_pin!(P0_01, 0, 1); impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2); impl_pin!(P0_02, 0, 2);

View File

@ -15,6 +15,7 @@ embassy_hal_internal::peripherals! {
// RTC // RTC
RTC0, RTC0,
#[cfg(not(feature="time-driver-rtc1"))]
RTC1, RTC1,
// WDT // WDT
@ -164,6 +165,10 @@ impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); impl_timer!(TIMER2, TIMER2, TIMER2);
impl_timer!(TIMER3, TIMER3, TIMER3, extended); 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_qdec!(QDEC, QDEC, QDEC);
impl_rng!(RNG, RNG, RNG); impl_rng!(RNG, RNG, RNG);

View File

@ -16,6 +16,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'G';
embassy_hal_internal::peripherals! { embassy_hal_internal::peripherals! {
// RTC // RTC
RTC0, RTC0,
#[cfg(not(feature="time-driver-rtc1"))]
RTC1, RTC1,
RTC2, RTC2,
@ -182,6 +183,11 @@ impl_twim!(TWISPI1, TWIM1, TWISPI1);
impl_twis!(TWISPI0, TWIS0, TWISPI0); impl_twis!(TWISPI0, TWIS0, TWISPI0);
impl_twis!(TWISPI1, TWIS1, TWISPI1); 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!(PWM0, PWM0, PWM0);
impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM1, PWM1, PWM1);
impl_pwm!(PWM2, PWM2, PWM2); impl_pwm!(PWM2, PWM2, PWM2);

View File

@ -15,6 +15,7 @@ embassy_hal_internal::peripherals! {
// RTC // RTC
RTC0, RTC0,
#[cfg(not(feature = "time-driver-rtc1"))]
RTC1, RTC1,
RTC2, RTC2,
@ -223,6 +224,11 @@ impl_timer!(TIMER2, TIMER2, TIMER2);
impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_timer!(TIMER3, TIMER3, TIMER3, extended);
impl_timer!(TIMER4, TIMER4, TIMER4, 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_00, 0, 0);
impl_pin!(P0_01, 0, 1); impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2); impl_pin!(P0_02, 0, 2);

View File

@ -15,6 +15,7 @@ embassy_hal_internal::peripherals! {
// RTC // RTC
RTC0, RTC0,
#[cfg(not(feature = "time-driver-rtc1"))]
RTC1, RTC1,
RTC2, RTC2,
@ -220,6 +221,11 @@ impl_timer!(TIMER2, TIMER2, TIMER2);
impl_timer!(TIMER3, TIMER3, TIMER3, extended); impl_timer!(TIMER3, TIMER3, TIMER3, extended);
impl_timer!(TIMER4, TIMER4, TIMER4, 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_qspi!(QSPI, QSPI, QSPI);
impl_pdm!(PDM, PDM, PDM); impl_pdm!(PDM, PDM, PDM);

View File

@ -168,6 +168,7 @@ embassy_hal_internal::peripherals! {
// RTC // RTC
RTC0, RTC0,
#[cfg(not(feature = "time-driver-rtc1"))]
RTC1, RTC1,
// WDT // WDT
@ -369,6 +370,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); 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_qspi!(QSPI, QSPI, QSPI);
impl_pdm!(PDM0, PDM0, PDM0); impl_pdm!(PDM0, PDM0, PDM0);

View File

@ -59,6 +59,7 @@ pub const FLASH_SIZE: usize = 256 * 1024;
embassy_hal_internal::peripherals! { embassy_hal_internal::peripherals! {
// RTC // RTC
RTC0, RTC0,
#[cfg(not(feature = "time-driver-rtc1"))]
RTC1, RTC1,
// WDT // WDT
@ -218,6 +219,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); 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_rng!(RNG, RNG, RNG);
impl_pin!(P0_00, 0, 0); impl_pin!(P0_00, 0, 0);

View File

@ -131,6 +131,7 @@ pub const FLASH_SIZE: usize = 1024 * 1024;
embassy_hal_internal::peripherals! { embassy_hal_internal::peripherals! {
// RTC // RTC
RTC0, RTC0,
#[cfg(not(feature = "time-driver-rtc1"))]
RTC1, RTC1,
// WDT // WDT
@ -276,6 +277,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); 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_00, 0, 0);
impl_pin!(P0_01, 0, 1); impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2); impl_pin!(P0_02, 0, 2);

View File

@ -131,6 +131,7 @@ pub const FLASH_SIZE: usize = 1024 * 1024;
embassy_hal_internal::peripherals! { embassy_hal_internal::peripherals! {
// RTC // RTC
RTC0, RTC0,
#[cfg(not(feature = "time-driver-rtc1"))]
RTC1, RTC1,
// WDT // WDT
@ -276,6 +277,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2); 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_00, 0, 0);
impl_pin!(P0_01, 0, 1); impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2); impl_pin!(P0_02, 0, 2);

View File

@ -155,6 +155,8 @@ pub mod reset;
#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))]
pub mod rng; pub mod rng;
#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf54l"))] // TODO
pub mod rtc;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))] #[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))]
pub mod saadc; pub mod saadc;
#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf54l"))] // TODO

265
embassy-nrf/src/rtc.rs Normal file
View File

@ -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<Self, PrescalerOutOfRangeError> {
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<Self, PrescalerOutOfRangeError> {
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
}
}

View File

@ -35,6 +35,7 @@ embedded-hal-async = { version = "1.0" }
embedded-hal-bus = { version = "0.1", features = ["async"] } embedded-hal-bus = { version = "0.1", features = ["async"] }
num-integer = { version = "0.1.45", default-features = false } num-integer = { version = "0.1.45", default-features = false }
microfft = "0.5.0" microfft = "0.5.0"
portable-atomic = "1"
[profile.release] [profile.release]
debug = 2 debug = 2

View File

@ -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<CriticalSectionRawMutex, RefCell<Option<Rtc<'static, embassy_nrf::peripherals::RTC0>>>> =
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);
});
}