//! Low-level RTC driver. #![macro_use] use core::marker::PhantomData; use embassy_hal_internal::interrupt::InterruptExt; use embassy_hal_internal::{Peri, PeripheralType}; use crate::interrupt::typelevel::Interrupt as _; use crate::{interrupt, 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> { r: pac::rtc::Rtc, irq: interrupt::Interrupt, _phantom: PhantomData<&'d ()>, } impl<'d> Rtc<'d> { /// Create a new `Rtc` driver. /// /// fRTC \[Hz\] = 32_768 / (`prescaler` + 1 ) pub fn new(_rtc: Peri<'d, T>, prescaler: u32) -> Result { if prescaler >= (1 << 12) { return Err(PrescalerOutOfRangeError(prescaler)); } T::regs().prescaler().write(|w| w.set_prescaler(prescaler as u16)); Ok(Self { r: T::regs(), irq: T::Interrupt::IRQ, _phantom: PhantomData, }) } /// Create a new `Rtc` driver, configuring it to run at the given frequency. pub fn new_for_freq(rtc: Peri<'d, T>, freq_hz: u32) -> Result { 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 { r: T::regs(), irq: T::Interrupt::IRQ, _phantom: PhantomData, } } /// Direct access to the RTC registers. #[cfg(feature = "unstable-pac")] #[inline] pub fn regs(&mut self) -> pac::rtc::Rtc { self.r } /// Enable the RTC. #[inline] pub fn enable(&mut self) { self.r.tasks_start().write_value(1); } /// Disable the RTC. #[inline] pub fn disable(&mut self) { self.r.tasks_stop().write_value(1); } /// Enables interrupts for the given [Interrupt] source. /// /// Optionally also enables the interrupt in the NVIC. pub fn enable_interrupt(&mut self, int: Interrupt, enable_in_nvic: bool) { let regs = self.r; 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 { self.irq.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 = self.r; 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 { self.irq.disable(); } } /// Enable the generation of a hardware event from a given stimulus. pub fn enable_event(&mut self, evt: Interrupt) { let regs = self.r; match evt { Interrupt::Tick => regs.evtenset().write(|w| w.set_tick(true)), Interrupt::Overflow => regs.evtenset().write(|w| w.set_ovrflw(true)), 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 = self.r; 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 = self.r; 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 = self.r; 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, }; self.r.cc(reg).write(|w| w.set_compare(val)); Ok(()) } /// Clear the Real Time Counter. #[inline] pub fn clear(&self) { self.r.tasks_clear().write_value(1); } /// Obtain the current value of the Real Time Counter, 24 bits of range. #[inline] pub fn read(&self) -> u32 { self.r.counter().read().counter() } }