diff options
| author | Ulf Lilleengen <[email protected]> | 2025-09-22 09:00:06 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-09-22 09:00:06 +0000 |
| commit | a25c5c4b2ac04bc7289953e4d7eb68111323df4d (patch) | |
| tree | 914952e6ff6d434380b574a8c9f6b44c8cee979f | |
| parent | 768182545aa600811c480a1ab298c590ce1093bc (diff) | |
| parent | e9232bfad31c8a0c8567fa092a323f186bd99eb5 (diff) | |
Merge pull request #4686 from robamu/add-nrf-rtc-driver
add basic RTC driver for nRF
| -rw-r--r-- | embassy-nrf/CHANGELOG.md | 2 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf51.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52805.rs | 7 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52810.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52811.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52820.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52832.rs | 6 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52833.rs | 6 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf52840.rs | 6 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf5340_app.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf5340_net.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf9120.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/chips/nrf9160.rs | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/lib.rs | 2 | ||||
| -rw-r--r-- | embassy-nrf/src/rtc.rs | 265 | ||||
| -rw-r--r-- | examples/nrf52840/Cargo.toml | 1 | ||||
| -rw-r--r-- | examples/nrf52840/src/bin/rtc.rs | 57 |
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; | |||
| 8 | embassy_hal_internal::peripherals! { | 8 | embassy_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 | ||
| 111 | impl_rng!(RNG, RNG, RNG); | 112 | impl_rng!(RNG, RNG, RNG); |
| 112 | 113 | ||
| 114 | impl_rtc!(RTC0, RTC0, RTC0); | ||
| 115 | #[cfg(not(feature = "time-driver-rtc1"))] | ||
| 116 | impl_rtc!(RTC1, RTC1, RTC1); | ||
| 117 | |||
| 113 | impl_pin!(P0_00, 0, 0); | 118 | impl_pin!(P0_00, 0, 0); |
| 114 | impl_pin!(P0_01, 0, 1); | 119 | impl_pin!(P0_01, 0, 1); |
| 115 | impl_pin!(P0_02, 0, 2); | 120 | 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'; | |||
| 12 | embassy_hal_internal::peripherals! { | 12 | embassy_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); | |||
| 156 | impl_timer!(TIMER1, TIMER1, TIMER1); | 157 | impl_timer!(TIMER1, TIMER1, TIMER1); |
| 157 | impl_timer!(TIMER2, TIMER2, TIMER2); | 158 | impl_timer!(TIMER2, TIMER2, TIMER2); |
| 158 | 159 | ||
| 160 | impl_rtc!(RTC0, RTC0, RTC0); | ||
| 161 | #[cfg(not(feature = "time-driver-rtc1"))] | ||
| 162 | impl_rtc!(RTC1, RTC1, RTC1); | ||
| 163 | |||
| 159 | impl_pin!(P0_00, 0, 0); | 164 | impl_pin!(P0_00, 0, 0); |
| 160 | impl_pin!(P0_01, 0, 1); | 165 | impl_pin!(P0_01, 0, 1); |
| 161 | impl_pin!(P0_02, 0, 2); | 166 | impl_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'; | |||
| 12 | embassy_hal_internal::peripherals! { | 12 | embassy_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); | |||
| 166 | impl_timer!(TIMER1, TIMER1, TIMER1); | 167 | impl_timer!(TIMER1, TIMER1, TIMER1); |
| 167 | impl_timer!(TIMER2, TIMER2, TIMER2); | 168 | impl_timer!(TIMER2, TIMER2, TIMER2); |
| 168 | 169 | ||
| 170 | impl_rtc!(RTC0, RTC0, RTC0); | ||
| 171 | #[cfg(not(feature = "time-driver-rtc1"))] | ||
| 172 | impl_rtc!(RTC1, RTC1, RTC1); | ||
| 173 | |||
| 169 | impl_pin!(P0_00, 0, 0); | 174 | impl_pin!(P0_00, 0, 0); |
| 170 | impl_pin!(P0_01, 0, 1); | 175 | impl_pin!(P0_01, 0, 1); |
| 171 | impl_pin!(P0_02, 0, 2); | 176 | 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'; | |||
| 12 | embassy_hal_internal::peripherals! { | 12 | embassy_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); | |||
| 168 | impl_timer!(TIMER1, TIMER1, TIMER1); | 169 | impl_timer!(TIMER1, TIMER1, TIMER1); |
| 169 | impl_timer!(TIMER2, TIMER2, TIMER2); | 170 | impl_timer!(TIMER2, TIMER2, TIMER2); |
| 170 | 171 | ||
| 172 | impl_rtc!(RTC0, RTC0, RTC0); | ||
| 173 | #[cfg(not(feature = "time-driver-rtc1"))] | ||
| 174 | impl_rtc!(RTC1, RTC1, RTC1); | ||
| 175 | |||
| 171 | impl_pin!(P0_00, 0, 0); | 176 | impl_pin!(P0_00, 0, 0); |
| 172 | impl_pin!(P0_01, 0, 1); | 177 | impl_pin!(P0_01, 0, 1); |
| 173 | impl_pin!(P0_02, 0, 2); | 178 | 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! { | |||
| 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); | |||
| 164 | impl_timer!(TIMER2, TIMER2, TIMER2); | 165 | impl_timer!(TIMER2, TIMER2, TIMER2); |
| 165 | impl_timer!(TIMER3, TIMER3, TIMER3, extended); | 166 | impl_timer!(TIMER3, TIMER3, TIMER3, extended); |
| 166 | 167 | ||
| 168 | impl_rtc!(RTC0, RTC0, RTC0); | ||
| 169 | #[cfg(not(feature = "time-driver-rtc1"))] | ||
| 170 | impl_rtc!(RTC1, RTC1, RTC1); | ||
| 171 | |||
| 167 | impl_qdec!(QDEC, QDEC, QDEC); | 172 | impl_qdec!(QDEC, QDEC, QDEC); |
| 168 | 173 | ||
| 169 | impl_rng!(RNG, RNG, RNG); | 174 | 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'; | |||
| 16 | embassy_hal_internal::peripherals! { | 16 | embassy_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); | |||
| 182 | impl_twis!(TWISPI0, TWIS0, TWISPI0); | 183 | impl_twis!(TWISPI0, TWIS0, TWISPI0); |
| 183 | impl_twis!(TWISPI1, TWIS1, TWISPI1); | 184 | impl_twis!(TWISPI1, TWIS1, TWISPI1); |
| 184 | 185 | ||
| 186 | impl_rtc!(RTC0, RTC0, RTC0); | ||
| 187 | #[cfg(not(feature = "time-driver-rtc1"))] | ||
| 188 | impl_rtc!(RTC1, RTC1, RTC1); | ||
| 189 | impl_rtc!(RTC2, RTC2, RTC2); | ||
| 190 | |||
| 185 | impl_pwm!(PWM0, PWM0, PWM0); | 191 | impl_pwm!(PWM0, PWM0, PWM0); |
| 186 | impl_pwm!(PWM1, PWM1, PWM1); | 192 | impl_pwm!(PWM1, PWM1, PWM1); |
| 187 | impl_pwm!(PWM2, PWM2, PWM2); | 193 | 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! { | |||
| 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); | |||
| 223 | impl_timer!(TIMER3, TIMER3, TIMER3, extended); | 224 | impl_timer!(TIMER3, TIMER3, TIMER3, extended); |
| 224 | impl_timer!(TIMER4, TIMER4, TIMER4, extended); | 225 | impl_timer!(TIMER4, TIMER4, TIMER4, extended); |
| 225 | 226 | ||
| 227 | impl_rtc!(RTC0, RTC0, RTC0); | ||
| 228 | #[cfg(not(feature = "time-driver-rtc1"))] | ||
| 229 | impl_rtc!(RTC1, RTC1, RTC1); | ||
| 230 | impl_rtc!(RTC2, RTC2, RTC2); | ||
| 231 | |||
| 226 | impl_pin!(P0_00, 0, 0); | 232 | impl_pin!(P0_00, 0, 0); |
| 227 | impl_pin!(P0_01, 0, 1); | 233 | impl_pin!(P0_01, 0, 1); |
| 228 | impl_pin!(P0_02, 0, 2); | 234 | 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! { | |||
| 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); | |||
| 220 | impl_timer!(TIMER3, TIMER3, TIMER3, extended); | 221 | impl_timer!(TIMER3, TIMER3, TIMER3, extended); |
| 221 | impl_timer!(TIMER4, TIMER4, TIMER4, extended); | 222 | impl_timer!(TIMER4, TIMER4, TIMER4, extended); |
| 222 | 223 | ||
| 224 | impl_rtc!(RTC0, RTC0, RTC0); | ||
| 225 | #[cfg(not(feature = "time-driver-rtc1"))] | ||
| 226 | impl_rtc!(RTC1, RTC1, RTC1); | ||
| 227 | impl_rtc!(RTC2, RTC2, RTC2); | ||
| 228 | |||
| 223 | impl_qspi!(QSPI, QSPI, QSPI); | 229 | impl_qspi!(QSPI, QSPI, QSPI); |
| 224 | 230 | ||
| 225 | impl_pdm!(PDM, PDM, PDM); | 231 | 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! { | |||
| 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); | |||
| 369 | impl_timer!(TIMER1, TIMER1, TIMER1); | 370 | impl_timer!(TIMER1, TIMER1, TIMER1); |
| 370 | impl_timer!(TIMER2, TIMER2, TIMER2); | 371 | impl_timer!(TIMER2, TIMER2, TIMER2); |
| 371 | 372 | ||
| 373 | impl_rtc!(RTC0, RTC0, RTC0); | ||
| 374 | #[cfg(not(feature = "time-driver-rtc1"))] | ||
| 375 | impl_rtc!(RTC1, RTC1, RTC1); | ||
| 376 | |||
| 372 | impl_qspi!(QSPI, QSPI, QSPI); | 377 | impl_qspi!(QSPI, QSPI, QSPI); |
| 373 | 378 | ||
| 374 | impl_pdm!(PDM0, PDM0, PDM0); | 379 | 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; | |||
| 59 | embassy_hal_internal::peripherals! { | 59 | embassy_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); | |||
| 218 | impl_timer!(TIMER1, TIMER1, TIMER1); | 219 | impl_timer!(TIMER1, TIMER1, TIMER1); |
| 219 | impl_timer!(TIMER2, TIMER2, TIMER2); | 220 | impl_timer!(TIMER2, TIMER2, TIMER2); |
| 220 | 221 | ||
| 222 | impl_rtc!(RTC0, RTC0, RTC0); | ||
| 223 | #[cfg(not(feature = "time-driver-rtc1"))] | ||
| 224 | impl_rtc!(RTC1, RTC1, RTC1); | ||
| 225 | |||
| 221 | impl_rng!(RNG, RNG, RNG); | 226 | impl_rng!(RNG, RNG, RNG); |
| 222 | 227 | ||
| 223 | impl_pin!(P0_00, 0, 0); | 228 | 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; | |||
| 131 | embassy_hal_internal::peripherals! { | 131 | embassy_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); | |||
| 276 | impl_timer!(TIMER1, TIMER1, TIMER1); | 277 | impl_timer!(TIMER1, TIMER1, TIMER1); |
| 277 | impl_timer!(TIMER2, TIMER2, TIMER2); | 278 | impl_timer!(TIMER2, TIMER2, TIMER2); |
| 278 | 279 | ||
| 280 | impl_rtc!(RTC0, RTC0, RTC0); | ||
| 281 | #[cfg(not(feature = "time-driver-rtc1"))] | ||
| 282 | impl_rtc!(RTC1, RTC1, RTC1); | ||
| 283 | |||
| 279 | impl_pin!(P0_00, 0, 0); | 284 | impl_pin!(P0_00, 0, 0); |
| 280 | impl_pin!(P0_01, 0, 1); | 285 | impl_pin!(P0_01, 0, 1); |
| 281 | impl_pin!(P0_02, 0, 2); | 286 | 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; | |||
| 131 | embassy_hal_internal::peripherals! { | 131 | embassy_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); | |||
| 276 | impl_timer!(TIMER1, TIMER1, TIMER1); | 277 | impl_timer!(TIMER1, TIMER1, TIMER1); |
| 277 | impl_timer!(TIMER2, TIMER2, TIMER2); | 278 | impl_timer!(TIMER2, TIMER2, TIMER2); |
| 278 | 279 | ||
| 280 | impl_rtc!(RTC0, RTC0, RTC0); | ||
| 281 | #[cfg(not(feature = "time-driver-rtc1"))] | ||
| 282 | impl_rtc!(RTC1, RTC1, RTC1); | ||
| 283 | |||
| 279 | impl_pin!(P0_00, 0, 0); | 284 | impl_pin!(P0_00, 0, 0); |
| 280 | impl_pin!(P0_01, 0, 1); | 285 | impl_pin!(P0_01, 0, 1); |
| 281 | impl_pin!(P0_02, 0, 2); | 286 | 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; | |||
| 155 | #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] | 155 | #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] |
| 156 | pub mod rng; | 156 | pub mod rng; |
| 157 | #[cfg(not(feature = "_nrf54l"))] // TODO | 157 | #[cfg(not(feature = "_nrf54l"))] // TODO |
| 158 | pub 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")))] |
| 159 | pub mod saadc; | 161 | pub 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 | |||
| 5 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 6 | |||
| 7 | use crate::chip::interrupt::typelevel::Interrupt as _; | ||
| 8 | use 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))] | ||
| 13 | pub 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))] | ||
| 18 | pub 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))] | ||
| 23 | pub 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))] | ||
| 41 | pub 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 | |||
| 52 | pub(crate) trait SealedInstance { | ||
| 53 | fn regs() -> pac::rtc::Rtc; | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Basic RTC instance. | ||
| 57 | #[allow(private_bounds)] | ||
| 58 | pub 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 | |||
| 71 | macro_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. | ||
| 91 | pub struct Rtc<'d, T: Instance>(Peri<'d, T>); | ||
| 92 | |||
| 93 | impl<'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" } | |||
| 35 | embedded-hal-bus = { version = "0.1", features = ["async"] } | 35 | embedded-hal-bus = { version = "0.1", features = ["async"] } |
| 36 | num-integer = { version = "0.1.45", default-features = false } | 36 | num-integer = { version = "0.1.45", default-features = false } |
| 37 | microfft = "0.5.0" | 37 | microfft = "0.5.0" |
| 38 | portable-atomic = "1" | ||
| 38 | 39 | ||
| 39 | [profile.release] | 40 | [profile.release] |
| 40 | debug = 2 | 41 | 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 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use core::cell::RefCell; | ||
| 5 | |||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | ||
| 8 | use embassy_nrf::interrupt; | ||
| 9 | use embassy_nrf::rtc::Rtc; | ||
| 10 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 11 | use embassy_sync::blocking_mutex::Mutex; | ||
| 12 | use portable_atomic::AtomicU64; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | // 64 bit counter which will never overflow. | ||
| 16 | static TICK_COUNTER: AtomicU64 = AtomicU64::new(0); | ||
| 17 | static RTC: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc<'static, embassy_nrf::peripherals::RTC0>>>> = | ||
| 18 | Mutex::new(RefCell::new(None)); | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async 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] | ||
| 46 | fn 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 | } | ||
