embassy_nrf/
rtc.rs

1//! Low-level RTC driver.
2
3#![macro_use]
4
5use core::marker::PhantomData;
6
7use embassy_hal_internal::interrupt::InterruptExt;
8use embassy_hal_internal::{Peri, PeripheralType};
9
10use crate::interrupt::typelevel::Interrupt as _;
11use crate::{interrupt, pac};
12
13/// Prescaler has an invalid value which exceeds 12 bits.
14#[derive(Debug, PartialEq, Eq)]
15#[cfg_attr(feature = "defmt", derive(defmt::Format))]
16pub struct PrescalerOutOfRangeError(u32);
17
18/// Compare value has an invalid value which exceeds 24 bits.
19#[derive(Debug, PartialEq, Eq)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21pub struct CompareOutOfRangeError(u32);
22
23/// Interrupts/Events that can be generated by the RTCn peripheral.
24#[derive(Debug, Copy, Clone, PartialEq, Eq)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub enum Interrupt {
27    /// Tick interrupt.
28    Tick,
29    /// Overflow interrupt.
30    Overflow,
31    /// Compare 0 interrupt.
32    Compare0,
33    /// Compare 1 interrupt.
34    Compare1,
35    /// Compare 2 interrupt.
36    Compare2,
37    /// Compare 3 interrupt. Only implemented for RTC1 and RTC2.
38    Compare3,
39}
40
41/// Compare registers available on the RTCn.
42#[derive(Debug, Copy, Clone)]
43#[cfg_attr(feature = "defmt", derive(defmt::Format))]
44pub enum CompareChannel {
45    /// Channel 0
46    _0,
47    /// Channel 1
48    _1,
49    /// Channel 2
50    _2,
51    /// Channel 3. Only implemented for RTC1 and RTC2.
52    _3,
53}
54
55pub(crate) trait SealedInstance {
56    fn regs() -> pac::rtc::Rtc;
57}
58
59/// Basic RTC instance.
60#[allow(private_bounds)]
61pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
62    /// Interrupt for this peripheral.
63    type Interrupt: crate::interrupt::typelevel::Interrupt;
64
65    /// Unsafely create a peripheral instance.
66    ///
67    /// # Safety
68    ///
69    /// Potentially allows to create multiple instances of the driver for the same peripheral
70    /// which can lead to undefined behavior.
71    unsafe fn steal() -> Peri<'static, Self>;
72}
73
74macro_rules! impl_rtc {
75    ($type:ident, $pac_type:ident, $irq:ident) => {
76        impl crate::rtc::SealedInstance for peripherals::$type {
77            #[inline]
78            fn regs() -> pac::rtc::Rtc {
79                unsafe { pac::rtc::Rtc::from_ptr(pac::$pac_type.as_ptr()) }
80            }
81        }
82
83        impl crate::rtc::Instance for peripherals::$type {
84            type Interrupt = crate::interrupt::typelevel::$irq;
85
86            unsafe fn steal() -> embassy_hal_internal::Peri<'static, Self> {
87                unsafe { peripherals::$type::steal() }
88            }
89        }
90    };
91}
92
93/// nRF RTC driver.
94pub struct Rtc<'d> {
95    r: pac::rtc::Rtc,
96    irq: interrupt::Interrupt,
97    _phantom: PhantomData<&'d ()>,
98}
99
100impl<'d> Rtc<'d> {
101    /// Create a new `Rtc` driver.
102    ///
103    /// fRTC \[Hz\] = 32_768 / (`prescaler` + 1 )
104    pub fn new<T: Instance>(_rtc: Peri<'d, T>, prescaler: u32) -> Result<Self, PrescalerOutOfRangeError> {
105        if prescaler >= (1 << 12) {
106            return Err(PrescalerOutOfRangeError(prescaler));
107        }
108
109        T::regs().prescaler().write(|w| w.set_prescaler(prescaler as u16));
110        Ok(Self {
111            r: T::regs(),
112            irq: T::Interrupt::IRQ,
113            _phantom: PhantomData,
114        })
115    }
116
117    /// Create a new `Rtc` driver, configuring it to run at the given frequency.
118    pub fn new_for_freq<T: Instance>(rtc: Peri<'d, T>, freq_hz: u32) -> Result<Self, PrescalerOutOfRangeError> {
119        let prescaler = (32_768 / freq_hz).saturating_sub(1);
120        Self::new(rtc, prescaler)
121    }
122
123    /// Steal the RTC peripheral, without checking if it's already taken.
124    ///
125    /// # Safety
126    ///
127    /// Potentially allows to create multiple instances of the driver for the same peripheral
128    /// which can lead to undefined behavior.
129    pub unsafe fn steal<T: Instance>() -> Self {
130        Self {
131            r: T::regs(),
132            irq: T::Interrupt::IRQ,
133            _phantom: PhantomData,
134        }
135    }
136
137    /// Direct access to the RTC registers.
138    #[cfg(feature = "unstable-pac")]
139    #[inline]
140    pub fn regs(&mut self) -> pac::rtc::Rtc {
141        self.r
142    }
143
144    /// Enable the RTC.
145    #[inline]
146    pub fn enable(&mut self) {
147        self.r.tasks_start().write_value(1);
148    }
149
150    /// Disable the RTC.
151    #[inline]
152    pub fn disable(&mut self) {
153        self.r.tasks_stop().write_value(1);
154    }
155
156    /// Enables interrupts for the given [Interrupt] source.
157    ///
158    /// Optionally also enables the interrupt in the NVIC.
159    pub fn enable_interrupt(&mut self, int: Interrupt, enable_in_nvic: bool) {
160        let regs = self.r;
161        match int {
162            Interrupt::Tick => regs.intenset().write(|w| w.set_tick(true)),
163            Interrupt::Overflow => regs.intenset().write(|w| w.set_ovrflw(true)),
164            Interrupt::Compare0 => regs.intenset().write(|w| w.set_compare(0, true)),
165            Interrupt::Compare1 => regs.intenset().write(|w| w.set_compare(1, true)),
166            Interrupt::Compare2 => regs.intenset().write(|w| w.set_compare(2, true)),
167            Interrupt::Compare3 => regs.intenset().write(|w| w.set_compare(3, true)),
168        }
169        if enable_in_nvic {
170            unsafe { self.irq.enable() };
171        }
172    }
173
174    /// Disables interrupts for the given [Interrupt] source.
175    ///
176    /// Optionally also disables the interrupt in the NVIC.
177    pub fn disable_interrupt(&mut self, int: Interrupt, disable_in_nvic: bool) {
178        let regs = self.r;
179        match int {
180            Interrupt::Tick => regs.intenclr().write(|w| w.set_tick(true)),
181            Interrupt::Overflow => regs.intenclr().write(|w| w.set_ovrflw(true)),
182            Interrupt::Compare0 => regs.intenclr().write(|w| w.set_compare(0, true)),
183            Interrupt::Compare1 => regs.intenclr().write(|w| w.set_compare(1, true)),
184            Interrupt::Compare2 => regs.intenclr().write(|w| w.set_compare(2, true)),
185            Interrupt::Compare3 => regs.intenclr().write(|w| w.set_compare(3, true)),
186        }
187        if disable_in_nvic {
188            self.irq.disable();
189        }
190    }
191
192    /// Enable the generation of a hardware event from a given stimulus.
193    pub fn enable_event(&mut self, evt: Interrupt) {
194        let regs = self.r;
195        match evt {
196            Interrupt::Tick => regs.evtenset().write(|w| w.set_tick(true)),
197            Interrupt::Overflow => regs.evtenset().write(|w| w.set_ovrflw(true)),
198            Interrupt::Compare0 => regs.evtenset().write(|w| w.set_compare(0, true)),
199            Interrupt::Compare1 => regs.evtenset().write(|w| w.set_compare(1, true)),
200            Interrupt::Compare2 => regs.evtenset().write(|w| w.set_compare(2, true)),
201            Interrupt::Compare3 => regs.evtenset().write(|w| w.set_compare(3, true)),
202        }
203    }
204
205    /// Disable the generation of a hardware event from a given stimulus.
206    pub fn disable_event(&mut self, evt: Interrupt) {
207        let regs = self.r;
208        match evt {
209            Interrupt::Tick => regs.evtenclr().write(|w| w.set_tick(true)),
210            Interrupt::Overflow => regs.evtenclr().write(|w| w.set_ovrflw(true)),
211            Interrupt::Compare0 => regs.evtenclr().write(|w| w.set_compare(0, true)),
212            Interrupt::Compare1 => regs.evtenclr().write(|w| w.set_compare(1, true)),
213            Interrupt::Compare2 => regs.evtenclr().write(|w| w.set_compare(2, true)),
214            Interrupt::Compare3 => regs.evtenclr().write(|w| w.set_compare(3, true)),
215        }
216    }
217
218    /// Resets the given event.
219    pub fn reset_event(&mut self, evt: Interrupt) {
220        let regs = self.r;
221        match evt {
222            Interrupt::Tick => regs.events_tick().write_value(0),
223            Interrupt::Overflow => regs.events_ovrflw().write_value(0),
224            Interrupt::Compare0 => regs.events_compare(0).write_value(0),
225            Interrupt::Compare1 => regs.events_compare(1).write_value(0),
226            Interrupt::Compare2 => regs.events_compare(2).write_value(0),
227            Interrupt::Compare3 => regs.events_compare(3).write_value(0),
228        }
229    }
230
231    /// Checks if the given event has been triggered.
232    pub fn is_event_triggered(&self, evt: Interrupt) -> bool {
233        let regs = self.r;
234        let val = match evt {
235            Interrupt::Tick => regs.events_tick().read(),
236            Interrupt::Overflow => regs.events_ovrflw().read(),
237            Interrupt::Compare0 => regs.events_compare(0).read(),
238            Interrupt::Compare1 => regs.events_compare(1).read(),
239            Interrupt::Compare2 => regs.events_compare(2).read(),
240            Interrupt::Compare3 => regs.events_compare(3).read(),
241        };
242        val == 1
243    }
244
245    /// Set the compare value of a given register. The compare registers have a width
246    /// of 24 bits.
247    pub fn set_compare(&mut self, reg: CompareChannel, val: u32) -> Result<(), CompareOutOfRangeError> {
248        if val >= (1 << 24) {
249            return Err(CompareOutOfRangeError(val));
250        }
251
252        let reg = match reg {
253            CompareChannel::_0 => 0,
254            CompareChannel::_1 => 1,
255            CompareChannel::_2 => 2,
256            CompareChannel::_3 => 3,
257        };
258
259        self.r.cc(reg).write(|w| w.set_compare(val));
260        Ok(())
261    }
262
263    /// Clear the Real Time Counter.
264    #[inline]
265    pub fn clear(&self) {
266        self.r.tasks_clear().write_value(1);
267    }
268
269    /// Obtain the current value of the Real Time Counter, 24 bits of range.
270    #[inline]
271    pub fn read(&self) -> u32 {
272        self.r.counter().read().counter()
273    }
274}