aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2025-09-22 09:00:06 +0000
committerGitHub <[email protected]>2025-09-22 09:00:06 +0000
commita25c5c4b2ac04bc7289953e4d7eb68111323df4d (patch)
tree914952e6ff6d434380b574a8c9f6b44c8cee979f
parent768182545aa600811c480a1ab298c590ce1093bc (diff)
parente9232bfad31c8a0c8567fa092a323f186bd99eb5 (diff)
Merge pull request #4686 from robamu/add-nrf-rtc-driver
add basic RTC driver for nRF
-rw-r--r--embassy-nrf/CHANGELOG.md2
-rw-r--r--embassy-nrf/src/chips/nrf51.rs5
-rw-r--r--embassy-nrf/src/chips/nrf52805.rs7
-rw-r--r--embassy-nrf/src/chips/nrf52810.rs5
-rw-r--r--embassy-nrf/src/chips/nrf52811.rs5
-rw-r--r--embassy-nrf/src/chips/nrf52820.rs5
-rw-r--r--embassy-nrf/src/chips/nrf52832.rs6
-rw-r--r--embassy-nrf/src/chips/nrf52833.rs6
-rw-r--r--embassy-nrf/src/chips/nrf52840.rs6
-rw-r--r--embassy-nrf/src/chips/nrf5340_app.rs5
-rw-r--r--embassy-nrf/src/chips/nrf5340_net.rs5
-rw-r--r--embassy-nrf/src/chips/nrf9120.rs5
-rw-r--r--embassy-nrf/src/chips/nrf9160.rs5
-rw-r--r--embassy-nrf/src/lib.rs2
-rw-r--r--embassy-nrf/src/rtc.rs265
-rw-r--r--examples/nrf52840/Cargo.toml1
-rw-r--r--examples/nrf52840/src/bin/rtc.rs57
17 files changed, 391 insertions, 1 deletions
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
10 10
11- changed: nrf54l: Disable glitch detection and enable DC/DC in init. 11- changed: nrf54l: Disable glitch detection and enable DC/DC in init.
12- changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4 12- changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4
13- changed: add persist() method for gpio and ppi
14- added: basic RTC driver
13- changed: add persist() method for gpio, gpiote, timer and ppi 15- changed: add persist() method for gpio, gpiote, timer and ppi
14- changed: impl Drop for Timer 16- changed: impl Drop for Timer
15 17
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;
8embassy_hal_internal::peripherals! { 8embassy_hal_internal::peripherals! {
9 // RTC 9 // RTC
10 RTC0, 10 RTC0,
11 #[cfg(not(feature = "time-driver-rtc1"))]
11 RTC1, 12 RTC1,
12 13
13 // WDT 14 // WDT
@@ -110,6 +111,10 @@ impl_timer!(TIMER2, TIMER2, TIMER2);
110 111
111impl_rng!(RNG, RNG, RNG); 112impl_rng!(RNG, RNG, RNG);
112 113
114impl_rtc!(RTC0, RTC0, RTC0);
115#[cfg(not(feature = "time-driver-rtc1"))]
116impl_rtc!(RTC1, RTC1, RTC1);
117
113impl_pin!(P0_00, 0, 0); 118impl_pin!(P0_00, 0, 0);
114impl_pin!(P0_01, 0, 1); 119impl_pin!(P0_01, 0, 1);
115impl_pin!(P0_02, 0, 2); 120impl_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';
12embassy_hal_internal::peripherals! { 12embassy_hal_internal::peripherals! {
13 // RTC 13 // RTC
14 RTC0, 14 RTC0,
15 #[cfg(not(feature="time-driver-rtc1"))]
15 RTC1, 16 RTC1,
16 17
17 // WDT 18 // WDT
@@ -156,6 +157,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
156impl_timer!(TIMER1, TIMER1, TIMER1); 157impl_timer!(TIMER1, TIMER1, TIMER1);
157impl_timer!(TIMER2, TIMER2, TIMER2); 158impl_timer!(TIMER2, TIMER2, TIMER2);
158 159
160impl_rtc!(RTC0, RTC0, RTC0);
161#[cfg(not(feature = "time-driver-rtc1"))]
162impl_rtc!(RTC1, RTC1, RTC1);
163
159impl_pin!(P0_00, 0, 0); 164impl_pin!(P0_00, 0, 0);
160impl_pin!(P0_01, 0, 1); 165impl_pin!(P0_01, 0, 1);
161impl_pin!(P0_02, 0, 2); 166impl_pin!(P0_02, 0, 2);
@@ -234,12 +239,12 @@ embassy_hal_internal::interrupt_mod!(
234 TIMER0, 239 TIMER0,
235 TIMER1, 240 TIMER1,
236 TIMER2, 241 TIMER2,
237 RTC0,
238 TEMP, 242 TEMP,
239 RNG, 243 RNG,
240 ECB, 244 ECB,
241 AAR_CCM, 245 AAR_CCM,
242 WDT, 246 WDT,
247 RTC0,
243 RTC1, 248 RTC1,
244 QDEC, 249 QDEC,
245 EGU0_SWI0, 250 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';
12embassy_hal_internal::peripherals! { 12embassy_hal_internal::peripherals! {
13 // RTC 13 // RTC
14 RTC0, 14 RTC0,
15 #[cfg(not(feature="time-driver-rtc1"))]
15 RTC1, 16 RTC1,
16 17
17 // WDT 18 // WDT
@@ -166,6 +167,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
166impl_timer!(TIMER1, TIMER1, TIMER1); 167impl_timer!(TIMER1, TIMER1, TIMER1);
167impl_timer!(TIMER2, TIMER2, TIMER2); 168impl_timer!(TIMER2, TIMER2, TIMER2);
168 169
170impl_rtc!(RTC0, RTC0, RTC0);
171#[cfg(not(feature = "time-driver-rtc1"))]
172impl_rtc!(RTC1, RTC1, RTC1);
173
169impl_pin!(P0_00, 0, 0); 174impl_pin!(P0_00, 0, 0);
170impl_pin!(P0_01, 0, 1); 175impl_pin!(P0_01, 0, 1);
171impl_pin!(P0_02, 0, 2); 176impl_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';
12embassy_hal_internal::peripherals! { 12embassy_hal_internal::peripherals! {
13 // RTC 13 // RTC
14 RTC0, 14 RTC0,
15 #[cfg(not(feature="time-driver-rtc1"))]
15 RTC1, 16 RTC1,
16 17
17 // WDT 18 // WDT
@@ -168,6 +169,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
168impl_timer!(TIMER1, TIMER1, TIMER1); 169impl_timer!(TIMER1, TIMER1, TIMER1);
169impl_timer!(TIMER2, TIMER2, TIMER2); 170impl_timer!(TIMER2, TIMER2, TIMER2);
170 171
172impl_rtc!(RTC0, RTC0, RTC0);
173#[cfg(not(feature = "time-driver-rtc1"))]
174impl_rtc!(RTC1, RTC1, RTC1);
175
171impl_pin!(P0_00, 0, 0); 176impl_pin!(P0_00, 0, 0);
172impl_pin!(P0_01, 0, 1); 177impl_pin!(P0_01, 0, 1);
173impl_pin!(P0_02, 0, 2); 178impl_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! {
15 15
16 // RTC 16 // RTC
17 RTC0, 17 RTC0,
18 #[cfg(not(feature="time-driver-rtc1"))]
18 RTC1, 19 RTC1,
19 20
20 // WDT 21 // WDT
@@ -164,6 +165,10 @@ impl_timer!(TIMER1, TIMER1, TIMER1);
164impl_timer!(TIMER2, TIMER2, TIMER2); 165impl_timer!(TIMER2, TIMER2, TIMER2);
165impl_timer!(TIMER3, TIMER3, TIMER3, extended); 166impl_timer!(TIMER3, TIMER3, TIMER3, extended);
166 167
168impl_rtc!(RTC0, RTC0, RTC0);
169#[cfg(not(feature = "time-driver-rtc1"))]
170impl_rtc!(RTC1, RTC1, RTC1);
171
167impl_qdec!(QDEC, QDEC, QDEC); 172impl_qdec!(QDEC, QDEC, QDEC);
168 173
169impl_rng!(RNG, RNG, RNG); 174impl_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';
16embassy_hal_internal::peripherals! { 16embassy_hal_internal::peripherals! {
17 // RTC 17 // RTC
18 RTC0, 18 RTC0,
19 #[cfg(not(feature="time-driver-rtc1"))]
19 RTC1, 20 RTC1,
20 RTC2, 21 RTC2,
21 22
@@ -182,6 +183,11 @@ impl_twim!(TWISPI1, TWIM1, TWISPI1);
182impl_twis!(TWISPI0, TWIS0, TWISPI0); 183impl_twis!(TWISPI0, TWIS0, TWISPI0);
183impl_twis!(TWISPI1, TWIS1, TWISPI1); 184impl_twis!(TWISPI1, TWIS1, TWISPI1);
184 185
186impl_rtc!(RTC0, RTC0, RTC0);
187#[cfg(not(feature = "time-driver-rtc1"))]
188impl_rtc!(RTC1, RTC1, RTC1);
189impl_rtc!(RTC2, RTC2, RTC2);
190
185impl_pwm!(PWM0, PWM0, PWM0); 191impl_pwm!(PWM0, PWM0, PWM0);
186impl_pwm!(PWM1, PWM1, PWM1); 192impl_pwm!(PWM1, PWM1, PWM1);
187impl_pwm!(PWM2, PWM2, PWM2); 193impl_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! {
15 15
16 // RTC 16 // RTC
17 RTC0, 17 RTC0,
18 #[cfg(not(feature = "time-driver-rtc1"))]
18 RTC1, 19 RTC1,
19 RTC2, 20 RTC2,
20 21
@@ -223,6 +224,11 @@ impl_timer!(TIMER2, TIMER2, TIMER2);
223impl_timer!(TIMER3, TIMER3, TIMER3, extended); 224impl_timer!(TIMER3, TIMER3, TIMER3, extended);
224impl_timer!(TIMER4, TIMER4, TIMER4, extended); 225impl_timer!(TIMER4, TIMER4, TIMER4, extended);
225 226
227impl_rtc!(RTC0, RTC0, RTC0);
228#[cfg(not(feature = "time-driver-rtc1"))]
229impl_rtc!(RTC1, RTC1, RTC1);
230impl_rtc!(RTC2, RTC2, RTC2);
231
226impl_pin!(P0_00, 0, 0); 232impl_pin!(P0_00, 0, 0);
227impl_pin!(P0_01, 0, 1); 233impl_pin!(P0_01, 0, 1);
228impl_pin!(P0_02, 0, 2); 234impl_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! {
15 15
16 // RTC 16 // RTC
17 RTC0, 17 RTC0,
18 #[cfg(not(feature = "time-driver-rtc1"))]
18 RTC1, 19 RTC1,
19 RTC2, 20 RTC2,
20 21
@@ -220,6 +221,11 @@ impl_timer!(TIMER2, TIMER2, TIMER2);
220impl_timer!(TIMER3, TIMER3, TIMER3, extended); 221impl_timer!(TIMER3, TIMER3, TIMER3, extended);
221impl_timer!(TIMER4, TIMER4, TIMER4, extended); 222impl_timer!(TIMER4, TIMER4, TIMER4, extended);
222 223
224impl_rtc!(RTC0, RTC0, RTC0);
225#[cfg(not(feature = "time-driver-rtc1"))]
226impl_rtc!(RTC1, RTC1, RTC1);
227impl_rtc!(RTC2, RTC2, RTC2);
228
223impl_qspi!(QSPI, QSPI, QSPI); 229impl_qspi!(QSPI, QSPI, QSPI);
224 230
225impl_pdm!(PDM, PDM, PDM); 231impl_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! {
168 168
169 // RTC 169 // RTC
170 RTC0, 170 RTC0,
171 #[cfg(not(feature = "time-driver-rtc1"))]
171 RTC1, 172 RTC1,
172 173
173 // WDT 174 // WDT
@@ -369,6 +370,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
369impl_timer!(TIMER1, TIMER1, TIMER1); 370impl_timer!(TIMER1, TIMER1, TIMER1);
370impl_timer!(TIMER2, TIMER2, TIMER2); 371impl_timer!(TIMER2, TIMER2, TIMER2);
371 372
373impl_rtc!(RTC0, RTC0, RTC0);
374#[cfg(not(feature = "time-driver-rtc1"))]
375impl_rtc!(RTC1, RTC1, RTC1);
376
372impl_qspi!(QSPI, QSPI, QSPI); 377impl_qspi!(QSPI, QSPI, QSPI);
373 378
374impl_pdm!(PDM0, PDM0, PDM0); 379impl_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;
59embassy_hal_internal::peripherals! { 59embassy_hal_internal::peripherals! {
60 // RTC 60 // RTC
61 RTC0, 61 RTC0,
62 #[cfg(not(feature = "time-driver-rtc1"))]
62 RTC1, 63 RTC1,
63 64
64 // WDT 65 // WDT
@@ -218,6 +219,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
218impl_timer!(TIMER1, TIMER1, TIMER1); 219impl_timer!(TIMER1, TIMER1, TIMER1);
219impl_timer!(TIMER2, TIMER2, TIMER2); 220impl_timer!(TIMER2, TIMER2, TIMER2);
220 221
222impl_rtc!(RTC0, RTC0, RTC0);
223#[cfg(not(feature = "time-driver-rtc1"))]
224impl_rtc!(RTC1, RTC1, RTC1);
225
221impl_rng!(RNG, RNG, RNG); 226impl_rng!(RNG, RNG, RNG);
222 227
223impl_pin!(P0_00, 0, 0); 228impl_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;
131embassy_hal_internal::peripherals! { 131embassy_hal_internal::peripherals! {
132 // RTC 132 // RTC
133 RTC0, 133 RTC0,
134 #[cfg(not(feature = "time-driver-rtc1"))]
134 RTC1, 135 RTC1,
135 136
136 // WDT 137 // WDT
@@ -276,6 +277,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
276impl_timer!(TIMER1, TIMER1, TIMER1); 277impl_timer!(TIMER1, TIMER1, TIMER1);
277impl_timer!(TIMER2, TIMER2, TIMER2); 278impl_timer!(TIMER2, TIMER2, TIMER2);
278 279
280impl_rtc!(RTC0, RTC0, RTC0);
281#[cfg(not(feature = "time-driver-rtc1"))]
282impl_rtc!(RTC1, RTC1, RTC1);
283
279impl_pin!(P0_00, 0, 0); 284impl_pin!(P0_00, 0, 0);
280impl_pin!(P0_01, 0, 1); 285impl_pin!(P0_01, 0, 1);
281impl_pin!(P0_02, 0, 2); 286impl_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;
131embassy_hal_internal::peripherals! { 131embassy_hal_internal::peripherals! {
132 // RTC 132 // RTC
133 RTC0, 133 RTC0,
134 #[cfg(not(feature = "time-driver-rtc1"))]
134 RTC1, 135 RTC1,
135 136
136 // WDT 137 // WDT
@@ -276,6 +277,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
276impl_timer!(TIMER1, TIMER1, TIMER1); 277impl_timer!(TIMER1, TIMER1, TIMER1);
277impl_timer!(TIMER2, TIMER2, TIMER2); 278impl_timer!(TIMER2, TIMER2, TIMER2);
278 279
280impl_rtc!(RTC0, RTC0, RTC0);
281#[cfg(not(feature = "time-driver-rtc1"))]
282impl_rtc!(RTC1, RTC1, RTC1);
283
279impl_pin!(P0_00, 0, 0); 284impl_pin!(P0_00, 0, 0);
280impl_pin!(P0_01, 0, 1); 285impl_pin!(P0_01, 0, 1);
281impl_pin!(P0_02, 0, 2); 286impl_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;
155#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] 155#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))]
156pub mod rng; 156pub mod rng;
157#[cfg(not(feature = "_nrf54l"))] // TODO 157#[cfg(not(feature = "_nrf54l"))] // TODO
158pub mod rtc;
159#[cfg(not(feature = "_nrf54l"))] // TODO
158#[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))] 160#[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))]
159pub mod saadc; 161pub mod saadc;
160#[cfg(not(feature = "_nrf54l"))] // TODO 162#[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 @@
1//! Low-level RTC driver.
2
3#![macro_use]
4
5use embassy_hal_internal::{Peri, PeripheralType};
6
7use crate::chip::interrupt::typelevel::Interrupt as _;
8use crate::pac;
9
10/// Prescaler has an invalid value which exceeds 12 bits.
11#[derive(Debug, PartialEq, Eq)]
12#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13pub struct PrescalerOutOfRangeError(u32);
14
15/// Compare value has an invalid value which exceeds 24 bits.
16#[derive(Debug, PartialEq, Eq)]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18pub struct CompareOutOfRangeError(u32);
19
20/// Interrupts/Events that can be generated by the RTCn peripheral.
21#[derive(Debug, Copy, Clone, PartialEq, Eq)]
22#[cfg_attr(feature = "defmt", derive(defmt::Format))]
23pub enum Interrupt {
24 /// Tick interrupt.
25 Tick,
26 /// Overflow interrupt.
27 Overflow,
28 /// Compare 0 interrupt.
29 Compare0,
30 /// Compare 1 interrupt.
31 Compare1,
32 /// Compare 2 interrupt.
33 Compare2,
34 /// Compare 3 interrupt. Only implemented for RTC1 and RTC2.
35 Compare3,
36}
37
38/// Compare registers available on the RTCn.
39#[derive(Debug, Copy, Clone)]
40#[cfg_attr(feature = "defmt", derive(defmt::Format))]
41pub enum CompareChannel {
42 /// Channel 0
43 _0,
44 /// Channel 1
45 _1,
46 /// Channel 2
47 _2,
48 /// Channel 3. Only implemented for RTC1 and RTC2.
49 _3,
50}
51
52pub(crate) trait SealedInstance {
53 fn regs() -> pac::rtc::Rtc;
54}
55
56/// Basic RTC instance.
57#[allow(private_bounds)]
58pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
59 /// Interrupt for this peripheral.
60 type Interrupt: crate::interrupt::typelevel::Interrupt;
61
62 /// Unsafely create a peripheral instance.
63 ///
64 /// # Safety
65 ///
66 /// Potentially allows to create multiple instances of the driver for the same peripheral
67 /// which can lead to undefined behavior.
68 unsafe fn steal() -> Peri<'static, Self>;
69}
70
71macro_rules! impl_rtc {
72 ($type:ident, $pac_type:ident, $irq:ident) => {
73 impl crate::rtc::SealedInstance for peripherals::$type {
74 #[inline]
75 fn regs() -> pac::rtc::Rtc {
76 unsafe { pac::rtc::Rtc::from_ptr(pac::$pac_type.as_ptr()) }
77 }
78 }
79
80 impl crate::rtc::Instance for peripherals::$type {
81 type Interrupt = crate::interrupt::typelevel::$irq;
82
83 unsafe fn steal() -> embassy_hal_internal::Peri<'static, Self> {
84 unsafe { peripherals::$type::steal() }
85 }
86 }
87 };
88}
89
90/// nRF RTC driver.
91pub struct Rtc<'d, T: Instance>(Peri<'d, T>);
92
93impl<'d, T: Instance> Rtc<'d, T> {
94 /// Create a new `Rtc` driver.
95 ///
96 /// fRTC \[Hz\] = 32_768 / (`prescaler` + 1 )
97 pub fn new(rtc: Peri<'d, T>, prescaler: u32) -> Result<Self, PrescalerOutOfRangeError> {
98 if prescaler >= (1 << 12) {
99 return Err(PrescalerOutOfRangeError(prescaler));
100 }
101
102 T::regs().prescaler().write(|w| w.set_prescaler(prescaler as u16));
103 Ok(Self(rtc))
104 }
105
106 /// Create a new `Rtc` driver, configuring it to run at the given frequency.
107 pub fn new_for_freq(rtc: Peri<'d, T>, freq_hz: u32) -> Result<Self, PrescalerOutOfRangeError> {
108 let prescaler = (32_768 / freq_hz).saturating_sub(1);
109 Self::new(rtc, prescaler)
110 }
111
112 /// Steal the RTC peripheral, without checking if it's already taken.
113 ///
114 /// # Safety
115 ///
116 /// Potentially allows to create multiple instances of the driver for the same peripheral
117 /// which can lead to undefined behavior.
118 pub unsafe fn steal() -> Self {
119 Self(unsafe { T::steal() })
120 }
121
122 /// Direct access to the RTC registers.
123 #[cfg(feature = "unstable-pac")]
124 #[inline]
125 pub fn regs(&mut self) -> pac::rtc::Rtc {
126 T::regs()
127 }
128
129 /// Enable the RTC.
130 #[inline]
131 pub fn enable(&mut self) {
132 T::regs().tasks_start().write_value(1);
133 }
134
135 /// Disable the RTC.
136 #[inline]
137 pub fn disable(&mut self) {
138 T::regs().tasks_stop().write_value(1);
139 }
140
141 /// Enables interrupts for the given [Interrupt] source.
142 ///
143 /// Optionally also enables the interrupt in the NVIC.
144 pub fn enable_interrupt(&mut self, int: Interrupt, enable_in_nvic: bool) {
145 let regs = T::regs();
146 match int {
147 Interrupt::Tick => regs.intenset().write(|w| w.set_tick(true)),
148 Interrupt::Overflow => regs.intenset().write(|w| w.set_ovrflw(true)),
149 Interrupt::Compare0 => regs.intenset().write(|w| w.set_compare(0, true)),
150 Interrupt::Compare1 => regs.intenset().write(|w| w.set_compare(1, true)),
151 Interrupt::Compare2 => regs.intenset().write(|w| w.set_compare(2, true)),
152 Interrupt::Compare3 => regs.intenset().write(|w| w.set_compare(3, true)),
153 }
154 if enable_in_nvic {
155 unsafe { T::Interrupt::enable() };
156 }
157 }
158
159 /// Disables interrupts for the given [Interrupt] source.
160 ///
161 /// Optionally also disables the interrupt in the NVIC.
162 pub fn disable_interrupt(&mut self, int: Interrupt, disable_in_nvic: bool) {
163 let regs = T::regs();
164 match int {
165 Interrupt::Tick => regs.intenclr().write(|w| w.set_tick(true)),
166 Interrupt::Overflow => regs.intenclr().write(|w| w.set_ovrflw(true)),
167 Interrupt::Compare0 => regs.intenclr().write(|w| w.set_compare(0, true)),
168 Interrupt::Compare1 => regs.intenclr().write(|w| w.set_compare(1, true)),
169 Interrupt::Compare2 => regs.intenclr().write(|w| w.set_compare(2, true)),
170 Interrupt::Compare3 => regs.intenclr().write(|w| w.set_compare(3, true)),
171 }
172 if disable_in_nvic {
173 T::Interrupt::disable();
174 }
175 }
176
177 /// Enable the generation of a hardware event from a given stimulus.
178 pub fn enable_event(&mut self, evt: Interrupt) {
179 let regs = T::regs();
180 match evt {
181 Interrupt::Tick => regs.evtenset().write(|w| w.set_tick(true)),
182 Interrupt::Overflow => regs.evtenset().write(|w| w.set_ovrflw(true)),
183 Interrupt::Compare0 => regs.evtenset().write(|w| w.set_compare(0, true)),
184 Interrupt::Compare1 => regs.evtenset().write(|w| w.set_compare(1, true)),
185 Interrupt::Compare2 => regs.evtenset().write(|w| w.set_compare(2, true)),
186 Interrupt::Compare3 => regs.evtenset().write(|w| w.set_compare(3, true)),
187 }
188 }
189
190 /// Disable the generation of a hardware event from a given stimulus.
191 pub fn disable_event(&mut self, evt: Interrupt) {
192 let regs = T::regs();
193 match evt {
194 Interrupt::Tick => regs.evtenclr().write(|w| w.set_tick(true)),
195 Interrupt::Overflow => regs.evtenclr().write(|w| w.set_ovrflw(true)),
196 Interrupt::Compare0 => regs.evtenclr().write(|w| w.set_compare(0, true)),
197 Interrupt::Compare1 => regs.evtenclr().write(|w| w.set_compare(1, true)),
198 Interrupt::Compare2 => regs.evtenclr().write(|w| w.set_compare(2, true)),
199 Interrupt::Compare3 => regs.evtenclr().write(|w| w.set_compare(3, true)),
200 }
201 }
202
203 /// Resets the given event.
204 pub fn reset_event(&mut self, evt: Interrupt) {
205 let regs = T::regs();
206 match evt {
207 Interrupt::Tick => regs.events_tick().write_value(0),
208 Interrupt::Overflow => regs.events_ovrflw().write_value(0),
209 Interrupt::Compare0 => regs.events_compare(0).write_value(0),
210 Interrupt::Compare1 => regs.events_compare(1).write_value(0),
211 Interrupt::Compare2 => regs.events_compare(2).write_value(0),
212 Interrupt::Compare3 => regs.events_compare(3).write_value(0),
213 }
214 }
215
216 /// Checks if the given event has been triggered.
217 pub fn is_event_triggered(&self, evt: Interrupt) -> bool {
218 let regs = T::regs();
219 let val = match evt {
220 Interrupt::Tick => regs.events_tick().read(),
221 Interrupt::Overflow => regs.events_ovrflw().read(),
222 Interrupt::Compare0 => regs.events_compare(0).read(),
223 Interrupt::Compare1 => regs.events_compare(1).read(),
224 Interrupt::Compare2 => regs.events_compare(2).read(),
225 Interrupt::Compare3 => regs.events_compare(3).read(),
226 };
227 val == 1
228 }
229
230 /// Set the compare value of a given register. The compare registers have a width
231 /// of 24 bits.
232 pub fn set_compare(&mut self, reg: CompareChannel, val: u32) -> Result<(), CompareOutOfRangeError> {
233 if val >= (1 << 24) {
234 return Err(CompareOutOfRangeError(val));
235 }
236
237 let reg = match reg {
238 CompareChannel::_0 => 0,
239 CompareChannel::_1 => 1,
240 CompareChannel::_2 => 2,
241 CompareChannel::_3 => 3,
242 };
243
244 T::regs().cc(reg).write(|w| w.set_compare(val));
245 Ok(())
246 }
247
248 /// Clear the Real Time Counter.
249 #[inline]
250 pub fn clear(&self) {
251 T::regs().tasks_clear().write_value(1);
252 }
253
254 /// Obtain the current value of the Real Time Counter, 24 bits of range.
255 #[inline]
256 pub fn read(&self) -> u32 {
257 T::regs().counter().read().counter()
258 }
259
260 /// Relase the RTC, returning the underlying peripheral instance.
261 #[inline]
262 pub fn release(self) -> Peri<'d, T> {
263 self.0
264 }
265}
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" }
35embedded-hal-bus = { version = "0.1", features = ["async"] } 35embedded-hal-bus = { version = "0.1", features = ["async"] }
36num-integer = { version = "0.1.45", default-features = false } 36num-integer = { version = "0.1.45", default-features = false }
37microfft = "0.5.0" 37microfft = "0.5.0"
38portable-atomic = "1"
38 39
39[profile.release] 40[profile.release]
40debug = 2 41debug = 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 @@
1#![no_std]
2#![no_main]
3
4use core::cell::RefCell;
5
6use embassy_executor::Spawner;
7use embassy_nrf::gpio::{Level, Output, OutputDrive};
8use embassy_nrf::interrupt;
9use embassy_nrf::rtc::Rtc;
10use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
11use embassy_sync::blocking_mutex::Mutex;
12use portable_atomic::AtomicU64;
13use {defmt_rtt as _, panic_probe as _};
14
15// 64 bit counter which will never overflow.
16static TICK_COUNTER: AtomicU64 = AtomicU64::new(0);
17static RTC: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc<'static, embassy_nrf::peripherals::RTC0>>>> =
18 Mutex::new(RefCell::new(None));
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) {
22 defmt::println!("nRF52840 RTC example");
23 let p = embassy_nrf::init(Default::default());
24 let mut led = Output::new(p.P0_13, Level::High, OutputDrive::Standard);
25 // Counter resolution is 125 ms.
26 let mut rtc = Rtc::new(p.RTC0, (1 << 12) - 1).unwrap();
27 rtc.enable_interrupt(embassy_nrf::rtc::Interrupt::Tick, true);
28 rtc.enable_event(embassy_nrf::rtc::Interrupt::Tick);
29 rtc.enable();
30 RTC.lock(|r| {
31 let mut rtc_borrow = r.borrow_mut();
32 *rtc_borrow = Some(rtc);
33 });
34
35 let mut last_counter_val = 0;
36 loop {
37 let current = TICK_COUNTER.load(core::sync::atomic::Ordering::Relaxed);
38 if current != last_counter_val {
39 led.toggle();
40 last_counter_val = current;
41 }
42 }
43}
44
45#[interrupt]
46fn RTC0() {
47 // For 64-bit, we do not need to worry about overflowing, at least not for realistic program
48 // lifetimes.
49 TICK_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
50 RTC.lock(|r| {
51 let mut rtc_borrow = r.borrow_mut();
52 rtc_borrow
53 .as_mut()
54 .unwrap()
55 .reset_event(embassy_nrf::rtc::Interrupt::Tick);
56 });
57}