aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-net-nrf91/Cargo.toml2
-rw-r--r--embassy-nrf/CHANGELOG.md2
-rw-r--r--embassy-nrf/Cargo.toml18
-rw-r--r--embassy-nrf/src/chips/nrf54l15_app.rs27
-rw-r--r--embassy-nrf/src/lib.rs1
-rw-r--r--embassy-nrf/src/saadc.rs10
-rw-r--r--embassy-nrf/src/time_driver.rs152
-rw-r--r--examples/nrf54l15/Cargo.toml2
-rw-r--r--examples/nrf54l15/src/bin/rtc.rs56
-rw-r--r--examples/nrf54l15/src/bin/timer.rs30
10 files changed, 180 insertions, 120 deletions
diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml
index 75b7aeeb2..8cf11f1fb 100644
--- a/embassy-net-nrf91/Cargo.toml
+++ b/embassy-net-nrf91/Cargo.toml
@@ -18,7 +18,7 @@ log = ["dep:log"]
18defmt = { version = "1.0.1", optional = true } 18defmt = { version = "1.0.1", optional = true }
19log = { version = "0.4.14", optional = true } 19log = { version = "0.4.14", optional = true }
20 20
21nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "58198c23bce72edc10b4e1656d1b54441fc74e7c" } 21nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "176dc4afe1dd8df78f3cbda4479ab5151aa32252" }
22cortex-m = "0.7.7" 22cortex-m = "0.7.7"
23 23
24embassy-time = { version = "0.5.0", path = "../embassy-time" } 24embassy-time = { version = "0.5.0", path = "../embassy-time" }
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md
index cfb040ef5..5657ddcfb 100644
--- a/embassy-nrf/CHANGELOG.md
+++ b/embassy-nrf/CHANGELOG.md
@@ -8,7 +8,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- added: Add basic RTC support for nRF54L
12- changed: apply trimming values from FICR.TRIMCNF on nrf53/54l 11- changed: apply trimming values from FICR.TRIMCNF on nrf53/54l
13- changed: do not panic on BufferedUarte overrun 12- changed: do not panic on BufferedUarte overrun
14- added: allow direct access to the input pin of `gpiote::InputChannel` 13- added: allow direct access to the input pin of `gpiote::InputChannel`
@@ -29,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
29- bugfix: use correct flash size for nRF54l 28- bugfix: use correct flash size for nRF54l
30- changed: add workaround for anomaly 66 on nrf52 29- changed: add workaround for anomaly 66 on nrf52
31- added: expose PPI events available on SPIS peripheral 30- added: expose PPI events available on SPIS peripheral
31- added: add basic GRTC time driver support for nRF54L
32 32
33## 0.8.0 - 2025-09-30 33## 0.8.0 - 2025-09-30
34 34
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index 08f4b280b..f73772682 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -23,8 +23,8 @@ build = [
23 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf5340-app-s", "time", "time-driver-rtc1"]}, 23 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf5340-app-s", "time", "time-driver-rtc1"]},
24 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf5340-app-ns", "time", "time-driver-rtc1"]}, 24 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf5340-app-ns", "time", "time-driver-rtc1"]},
25 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf5340-net", "time", "time-driver-rtc1"]}, 25 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf5340-net", "time", "time-driver-rtc1"]},
26 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l15-app-s", "time", "time-driver-rtc1"]}, 26 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l15-app-s", "time", "time-driver-grtc"]},
27 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l15-app-ns", "time", "time-driver-rtc1"]}, 27 {target = "thumbv8m.main-none-eabihf", features = ["gpiote", "nrf54l15-app-ns", "time", "time-driver-grtc"]},
28 {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time"]}, 28 {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time"]},
29 {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time-driver-rtc1"]}, 29 {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time-driver-rtc1"]},
30 {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time", "time-driver-rtc1"]}, 30 {target = "thumbv7em-none-eabi", features = ["gpiote", "nrf52840", "time", "time-driver-rtc1"]},
@@ -80,9 +80,10 @@ unstable-pac = []
80gpiote = [] 80gpiote = []
81 81
82## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz 82## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz
83## 83time-driver-rtc1 = ["_time-driver", "embassy-time-driver?/tick-hz-32_768"]
84## Note: For nRF54L, it's actually RTC30 84
85time-driver-rtc1 = ["_time-driver"] 85## Use GRTC (CC n=1, GRTC_1 irq) as the time driver for `embassy-time`, with a tick rate of 1 MHz
86time-driver-grtc = ["_time-driver", "embassy-time-driver?/tick-hz-1_000_000"]
86 87
87## Enable embassy-net 802.15.4 driver 88## Enable embassy-net 802.15.4 driver
88net-driver = ["_net-driver"] 89net-driver = ["_net-driver"]
@@ -152,7 +153,7 @@ _nrf5340-net = ["_nrf5340", "nrf-pac/nrf5340-net"]
152_nrf5340 = ["_gpio-p1", "_dppi"] 153_nrf5340 = ["_gpio-p1", "_dppi"]
153_nrf54l15-app = ["_nrf54l15", "nrf-pac/nrf54l15-app"] 154_nrf54l15-app = ["_nrf54l15", "nrf-pac/nrf54l15-app"]
154_nrf54l15 = ["_nrf54l", "_gpio-p1", "_gpio-p2"] 155_nrf54l15 = ["_nrf54l", "_gpio-p1", "_gpio-p2"]
155_nrf54l = ["_dppi"] 156_nrf54l = ["_dppi", "_grtc"]
156 157
157_nrf9160 = ["nrf-pac/nrf9160", "_dppi", "_spi-v1"] 158_nrf9160 = ["nrf-pac/nrf9160", "_dppi", "_spi-v1"]
158_nrf9120 = ["nrf-pac/nrf9120", "_dppi", "_spi-v1"] 159_nrf9120 = ["nrf-pac/nrf9120", "_dppi", "_spi-v1"]
@@ -160,7 +161,7 @@ _nrf52 = ["_ppi"]
160_nrf51 = ["_ppi", "_spi-v1"] 161_nrf51 = ["_ppi", "_spi-v1"]
161_nrf91 = [] 162_nrf91 = []
162 163
163_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"] 164_time-driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils", "embassy-embedded-hal/time"]
164 165
165_net-driver = ["dep:embassy-net-driver-channel","dep:embassy-futures"] 166_net-driver = ["dep:embassy-net-driver-channel","dep:embassy-futures"]
166 167
@@ -173,6 +174,7 @@ _dppi = []
173_gpio-p1 = [] 174_gpio-p1 = []
174_gpio-p2 = [] 175_gpio-p2 = []
175_spi-v1 = [] 176_spi-v1 = []
177_grtc = []
176 178
177# Errata workarounds 179# Errata workarounds
178_nrf52832_anomaly_109 = [] 180_nrf52832_anomaly_109 = []
@@ -200,7 +202,7 @@ embedded-io-async = { version = "0.6.1" }
200rand-core-06 = { package = "rand_core", version = "0.6" } 202rand-core-06 = { package = "rand_core", version = "0.6" }
201rand-core-09 = { package = "rand_core", version = "0.9" } 203rand-core-09 = { package = "rand_core", version = "0.9" }
202 204
203nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "58198c23bce72edc10b4e1656d1b54441fc74e7c" } 205nrf-pac = { version = "0.1.0", git = "https://github.com/embassy-rs/nrf-pac.git", rev = "176dc4afe1dd8df78f3cbda4479ab5151aa32252" }
204 206
205defmt = { version = "1.0.1", optional = true } 207defmt = { version = "1.0.1", optional = true }
206bitflags = "2.4.2" 208bitflags = "2.4.2"
diff --git a/embassy-nrf/src/chips/nrf54l15_app.rs b/embassy-nrf/src/chips/nrf54l15_app.rs
index 8846717db..8e6595248 100644
--- a/embassy-nrf/src/chips/nrf54l15_app.rs
+++ b/embassy-nrf/src/chips/nrf54l15_app.rs
@@ -26,7 +26,6 @@ pub mod pac {
26 PPIB10_NS as PPIB10, 26 PPIB10_NS as PPIB10,
27 PPIB11_NS as PPIB11, 27 PPIB11_NS as PPIB11,
28 TIMER10_NS as TIMER10, 28 TIMER10_NS as TIMER10,
29 RTC10_NS as RTC10,
30 EGU10_NS as EGU10, 29 EGU10_NS as EGU10,
31 RADIO_NS as RADIO, 30 RADIO_NS as RADIO,
32 DPPIC20_NS as DPPIC20, 31 DPPIC20_NS as DPPIC20,
@@ -76,7 +75,6 @@ pub mod pac {
76 TWIM30_NS as TWIM30, 75 TWIM30_NS as TWIM30,
77 TWIS30_NS as TWIS30, 76 TWIS30_NS as TWIS30,
78 UARTE30_NS as UARTE30, 77 UARTE30_NS as UARTE30,
79 RTC30_NS as RTC30,
80 COMP_NS as COMP, 78 COMP_NS as COMP,
81 LPCOMP_NS as LPCOMP, 79 LPCOMP_NS as LPCOMP,
82 WDT31_NS as WDT31, 80 WDT31_NS as WDT31,
@@ -127,7 +125,6 @@ pub mod pac {
127 PPIB10_S as PPIB10, 125 PPIB10_S as PPIB10,
128 PPIB11_S as PPIB11, 126 PPIB11_S as PPIB11,
129 TIMER10_S as TIMER10, 127 TIMER10_S as TIMER10,
130 RTC10_S as RTC10,
131 EGU10_S as EGU10, 128 EGU10_S as EGU10,
132 RADIO_S as RADIO, 129 RADIO_S as RADIO,
133 SPU20_S as SPU20, 130 SPU20_S as SPU20,
@@ -180,7 +177,6 @@ pub mod pac {
180 TWIM30_S as TWIM30, 177 TWIM30_S as TWIM30,
181 TWIS30_S as TWIS30, 178 TWIS30_S as TWIS30,
182 UARTE30_S as UARTE30, 179 UARTE30_S as UARTE30,
183 RTC30_S as RTC30,
184 COMP_S as COMP, 180 COMP_S as COMP,
185 LPCOMP_S as LPCOMP, 181 LPCOMP_S as LPCOMP,
186 WDT30_S as WDT30, 182 WDT30_S as WDT30,
@@ -417,11 +413,19 @@ embassy_hal_internal::peripherals! {
417 P2_10, 413 P2_10,
418 414
419 // GRTC 415 // GRTC
420 GRTC, 416 GRTC_CH0,
421 417 #[cfg(not(feature = "time-driver-grtc"))]
422 // RTC 418 GRTC_CH1,
423 RTC10, 419 GRTC_CH2,
424 RTC30, 420 GRTC_CH3,
421 GRTC_CH4,
422 GRTC_CH5,
423 GRTC_CH6,
424 GRTC_CH7,
425 GRTC_CH8,
426 GRTC_CH9,
427 GRTC_CH10,
428 GRTC_CH11,
425 429
426 // PWM 430 // PWM
427 PWM20, 431 PWM20,
@@ -548,9 +552,6 @@ cfg_if::cfg_if! {
548 } 552 }
549} 553}
550 554
551impl_rtc!(RTC10, RTC10, RTC10);
552impl_rtc!(RTC30, RTC30, RTC30);
553
554#[cfg(feature = "_ns")] 555#[cfg(feature = "_ns")]
555impl_wdt!(WDT, WDT31, WDT31, 0); 556impl_wdt!(WDT, WDT31, WDT31, 0);
556#[cfg(feature = "_s")] 557#[cfg(feature = "_s")]
@@ -703,7 +704,6 @@ embassy_hal_internal::interrupt_mod!(
703 TIMER00, 704 TIMER00,
704 SPU10, 705 SPU10,
705 TIMER10, 706 TIMER10,
706 RTC10,
707 EGU10, 707 EGU10,
708 RADIO_0, 708 RADIO_0,
709 RADIO_1, 709 RADIO_1,
@@ -737,7 +737,6 @@ embassy_hal_internal::interrupt_mod!(
737 GRTC_3, 737 GRTC_3,
738 SPU30, 738 SPU30,
739 SERIAL30, 739 SERIAL30,
740 RTC30,
741 COMP_LPCOMP, 740 COMP_LPCOMP,
742 WDT30, 741 WDT30,
743 WDT31, 742 WDT31,
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 28d2119ae..e8762b00d 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -153,6 +153,7 @@ pub mod reset;
153#[cfg(not(feature = "_nrf54l"))] 153#[cfg(not(feature = "_nrf54l"))]
154#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] 154#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))]
155pub mod rng; 155pub mod rng;
156#[cfg(not(feature = "_nrf54l"))]
156pub mod rtc; 157pub mod rtc;
157#[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))] 158#[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))]
158pub mod saadc; 159pub mod saadc;
diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs
index ca8cbd73e..ab0e2b86a 100644
--- a/embassy-nrf/src/saadc.rs
+++ b/embassy-nrf/src/saadc.rs
@@ -771,7 +771,7 @@ pub(crate) trait SealedInput {
771 fn port(&self) -> u8; 771 fn port(&self) -> u8;
772 772
773 #[cfg(feature = "_nrf54l")] 773 #[cfg(feature = "_nrf54l")]
774 fn internal(&self) -> vals::Internal; 774 fn internal(&self) -> vals::PselpInternal;
775 775
776 #[cfg(feature = "_nrf54l")] 776 #[cfg(feature = "_nrf54l")]
777 fn connect(&self) -> vals::PselpConnect; 777 fn connect(&self) -> vals::PselpConnect;
@@ -832,7 +832,7 @@ pub struct AnyInput<'a> {
832pub struct AnyInput<'a> { 832pub struct AnyInput<'a> {
833 pin: u8, 833 pin: u8,
834 port: u8, 834 port: u8,
835 internal: vals::Internal, 835 internal: vals::PselpInternal,
836 connect: vals::PselpConnect, 836 connect: vals::PselpConnect,
837 _phantom: PhantomData<&'a ()>, 837 _phantom: PhantomData<&'a ()>,
838} 838}
@@ -881,7 +881,7 @@ impl SealedInput for AnyInput<'_> {
881 } 881 }
882 882
883 #[cfg(feature = "_nrf54l")] 883 #[cfg(feature = "_nrf54l")]
884 fn internal(&self) -> vals::Internal { 884 fn internal(&self) -> vals::PselpInternal {
885 self.internal 885 self.internal
886 } 886 }
887 887
@@ -923,8 +923,8 @@ macro_rules! impl_saadc_input {
923 $port 923 $port
924 } 924 }
925 925
926 fn internal(&self) -> crate::pac::saadc::vals::Internal { 926 fn internal(&self) -> crate::pac::saadc::vals::PselpInternal {
927 crate::pac::saadc::vals::Internal::$internal 927 crate::pac::saadc::vals::PselpInternal::$internal
928 } 928 }
929 929
930 fn connect(&self) -> crate::pac::saadc::vals::PselpConnect { 930 fn connect(&self) -> crate::pac::saadc::vals::PselpConnect {
diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs
index b723e2334..35f65bd64 100644
--- a/embassy-nrf/src/time_driver.rs
+++ b/embassy-nrf/src/time_driver.rs
@@ -1,4 +1,5 @@
1use core::cell::{Cell, RefCell}; 1use core::cell::{Cell, RefCell};
2#[cfg(not(feature = "_grtc"))]
2use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; 3use core::sync::atomic::{AtomicU32, Ordering, compiler_fence};
3 4
4use critical_section::CriticalSection; 5use critical_section::CriticalSection;
@@ -8,21 +9,27 @@ use embassy_time_driver::Driver;
8use embassy_time_queue_utils::Queue; 9use embassy_time_queue_utils::Queue;
9 10
10use crate::interrupt::InterruptExt; 11use crate::interrupt::InterruptExt;
12#[cfg(feature = "_grtc")]
13use crate::pac::grtc::vals::Busy;
11use crate::{interrupt, pac}; 14use crate::{interrupt, pac};
12 15
13#[cfg(feature = "_nrf54l")] 16#[cfg(feature = "_grtc")]
14fn rtc() -> pac::rtc::Rtc { 17fn rtc() -> pac::grtc::Grtc {
15 pac::RTC30 18 pac::GRTC
16} 19}
17#[cfg(not(feature = "_nrf54l"))] 20
21#[cfg(not(feature = "_grtc"))]
18fn rtc() -> pac::rtc::Rtc { 22fn rtc() -> pac::rtc::Rtc {
19 pac::RTC1 23 pac::RTC1
20} 24}
21 25
22/// Calculate the timestamp from the period count and the tick count. 26/// Calculate the timestamp from the period count and the tick count.
23/// 27///
24/// The RTC counter is 24 bit. Ticking at 32768hz, it overflows every ~8 minutes. This is 28/// For nRF54 devices and newer, the GRTC counter is 52 bits, so the time driver uses the
25/// too short. We must make it "never" overflow. 29/// syscounter and ignores the periods handling, since it overflows every 142 years.
30///
31/// For most other devices, the RTC counter is 24 bit. Ticking at 32768hz, it overflows every ~8 minutes.
32/// This is too short. We must make it "never" overflow.
26/// 33///
27/// The obvious way would be to count overflow periods. Every time the counter overflows, 34/// The obvious way would be to count overflow periods. Every time the counter overflows,
28/// increase a `periods` variable. `now()` simply does `periods << 24 + counter`. So, the logic 35/// increase a `periods` variable. `now()` simply does `periods << 24 + counter`. So, the logic
@@ -64,14 +71,39 @@ fn rtc() -> pac::rtc::Rtc {
64/// `period` is a 32bit integer, so It overflows on 2^32 * 2^23 / 32768 seconds of uptime, which is 34865 71/// `period` is a 32bit integer, so It overflows on 2^32 * 2^23 / 32768 seconds of uptime, which is 34865
65/// years. For comparison, flash memory like the one containing your firmware is usually rated to retain 72/// years. For comparison, flash memory like the one containing your firmware is usually rated to retain
66/// data for only 10-20 years. 34865 years is long enough! 73/// data for only 10-20 years. 34865 years is long enough!
74#[cfg(not(feature = "_grtc"))]
67fn calc_now(period: u32, counter: u32) -> u64 { 75fn calc_now(period: u32, counter: u32) -> u64 {
68 ((period as u64) << 23) + ((counter ^ ((period & 1) << 23)) as u64) 76 ((period as u64) << 23) + ((counter ^ ((period & 1) << 23)) as u64)
69} 77}
70 78
79#[cfg(feature = "_grtc")]
80fn syscounter() -> u64 {
81 let r = rtc();
82 r.syscounter(0).active().write(|w| w.set_active(true));
83 loop {
84 let countl: u32 = r.syscounter(0).syscounterl().read();
85 let counth = r.syscounter(0).syscounterh().read();
86
87 if counth.busy() == Busy::READY && !counth.overflow() {
88 let counth: u32 = counth.value();
89 let count = countl as u64 | ((counth as u64) << 32);
90 r.syscounter(0).active().write(|w| w.set_active(false));
91 return count;
92 }
93 // If overflow or not ready, loop will re-read both registers
94 }
95}
96
97#[cfg(not(feature = "_grtc"))]
71fn compare_n(n: usize) -> u32 { 98fn compare_n(n: usize) -> u32 {
72 1 << (n + 16) 99 1 << (n + 16)
73} 100}
74 101
102#[cfg(feature = "_grtc")]
103fn compare_n(n: usize) -> u32 {
104 1 << n // GRTC uses bits 0-11 for COMPARE[0-11]
105}
106
75#[cfg(test)] 107#[cfg(test)]
76mod test { 108mod test {
77 use super::*; 109 use super::*;
@@ -108,6 +140,7 @@ impl AlarmState {
108 140
109struct RtcDriver { 141struct RtcDriver {
110 /// Number of 2^23 periods elapsed since boot. 142 /// Number of 2^23 periods elapsed since boot.
143 #[cfg(not(feature = "_grtc"))]
111 period: AtomicU32, 144 period: AtomicU32,
112 /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. 145 /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
113 alarms: Mutex<AlarmState>, 146 alarms: Mutex<AlarmState>,
@@ -115,6 +148,7 @@ struct RtcDriver {
115} 148}
116 149
117embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { 150embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
151 #[cfg(not(feature = "_grtc"))]
118 period: AtomicU32::new(0), 152 period: AtomicU32::new(0),
119 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), 153 alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
120 queue: Mutex::new(RefCell::new(Queue::new())), 154 queue: Mutex::new(RefCell::new(Queue::new())),
@@ -123,25 +157,43 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
123impl RtcDriver { 157impl RtcDriver {
124 fn init(&'static self, irq_prio: crate::interrupt::Priority) { 158 fn init(&'static self, irq_prio: crate::interrupt::Priority) {
125 let r = rtc(); 159 let r = rtc();
126 r.cc(3).write(|w| w.set_compare(0x800000)); 160 // Chips without GRTC needs to deal with overflow
161 #[cfg(not(feature = "_grtc"))]
162 {
163 r.cc(3).write(|w| w.set_compare(0x800000));
127 164
128 r.intenset().write(|w| { 165 r.intenset().write(|w| {
129 w.set_ovrflw(true); 166 w.set_ovrflw(true);
130 w.set_compare(3, true); 167 w.set_compare(3, true);
131 }); 168 });
169 }
132 170
171 #[cfg(feature = "_grtc")]
172 {
173 r.mode().write(|w| {
174 w.set_syscounteren(true);
175 });
176 }
133 r.tasks_clear().write_value(1); 177 r.tasks_clear().write_value(1);
134 r.tasks_start().write_value(1); 178 r.tasks_start().write_value(1);
135 179
136 // Wait for clear 180 // Wait for clear
181 #[cfg(not(feature = "_grtc"))]
137 while r.counter().read().0 != 0 {} 182 while r.counter().read().0 != 0 {}
138 183
139 #[cfg(feature = "_nrf54l")] 184 #[cfg(feature = "_grtc")]
185 loop {
186 if r.status().lftimer().read().ready() {
187 break;
188 }
189 }
190
191 #[cfg(feature = "_grtc")]
140 { 192 {
141 interrupt::RTC30.set_priority(irq_prio); 193 interrupt::GRTC_1.set_priority(irq_prio);
142 unsafe { interrupt::RTC30.enable() }; 194 unsafe { interrupt::GRTC_1.enable() };
143 } 195 }
144 #[cfg(not(feature = "_nrf54l"))] 196 #[cfg(not(feature = "_grtc"))]
145 { 197 {
146 interrupt::RTC1.set_priority(irq_prio); 198 interrupt::RTC1.set_priority(irq_prio);
147 unsafe { interrupt::RTC1.enable() }; 199 unsafe { interrupt::RTC1.enable() };
@@ -150,11 +202,14 @@ impl RtcDriver {
150 202
151 fn on_interrupt(&self) { 203 fn on_interrupt(&self) {
152 let r = rtc(); 204 let r = rtc();
205
206 #[cfg(not(feature = "_grtc"))]
153 if r.events_ovrflw().read() == 1 { 207 if r.events_ovrflw().read() == 1 {
154 r.events_ovrflw().write_value(0); 208 r.events_ovrflw().write_value(0);
155 self.next_period(); 209 self.next_period();
156 } 210 }
157 211
212 #[cfg(not(feature = "_grtc"))]
158 if r.events_compare(3).read() == 1 { 213 if r.events_compare(3).read() == 1 {
159 r.events_compare(3).write_value(0); 214 r.events_compare(3).write_value(0);
160 self.next_period(); 215 self.next_period();
@@ -169,6 +224,7 @@ impl RtcDriver {
169 } 224 }
170 } 225 }
171 226
227 #[cfg(not(feature = "_grtc"))]
172 fn next_period(&self) { 228 fn next_period(&self) {
173 critical_section::with(|cs| { 229 critical_section::with(|cs| {
174 let r = rtc(); 230 let r = rtc();
@@ -190,7 +246,10 @@ impl RtcDriver {
190 fn trigger_alarm(&self, cs: CriticalSection) { 246 fn trigger_alarm(&self, cs: CriticalSection) {
191 let n = 0; 247 let n = 0;
192 let r = rtc(); 248 let r = rtc();
249 #[cfg(not(feature = "_grtc"))]
193 r.intenclr().write(|w| w.0 = compare_n(n)); 250 r.intenclr().write(|w| w.0 = compare_n(n));
251 #[cfg(feature = "_grtc")]
252 r.intenclr(1).write(|w| w.0 = compare_n(n));
194 253
195 let alarm = &self.alarms.borrow(cs); 254 let alarm = &self.alarms.borrow(cs);
196 alarm.timestamp.set(u64::MAX); 255 alarm.timestamp.set(u64::MAX);
@@ -214,7 +273,10 @@ impl RtcDriver {
214 if timestamp <= t { 273 if timestamp <= t {
215 // If alarm timestamp has passed the alarm will not fire. 274 // If alarm timestamp has passed the alarm will not fire.
216 // Disarm the alarm and return `false` to indicate that. 275 // Disarm the alarm and return `false` to indicate that.
276 #[cfg(not(feature = "_grtc"))]
217 r.intenclr().write(|w| w.0 = compare_n(n)); 277 r.intenclr().write(|w| w.0 = compare_n(n));
278 #[cfg(feature = "_grtc")]
279 r.intenclr(1).write(|w| w.0 = compare_n(n));
218 280
219 alarm.timestamp.set(u64::MAX); 281 alarm.timestamp.set(u64::MAX);
220 282
@@ -226,7 +288,7 @@ impl RtcDriver {
226 // Write the CC value regardless of whether we're going to enable it now or not. 288 // Write the CC value regardless of whether we're going to enable it now or not.
227 // This way, when we enable it later, the right value is already set. 289 // This way, when we enable it later, the right value is already set.
228 290
229 // nrf52 docs say: 291 // nrf52 docs say :
230 // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event. 292 // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event.
231 // To workaround this, we never write a timestamp smaller than N+3. 293 // To workaround this, we never write a timestamp smaller than N+3.
232 // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc. 294 // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc.
@@ -238,22 +300,39 @@ impl RtcDriver {
238 // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed 300 // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
239 // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time, 301 // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
240 // and we don't do that here. 302 // and we don't do that here.
241 let safe_timestamp = timestamp.max(t + 3); 303 #[cfg(not(feature = "_grtc"))]
242 r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF)); 304 {
243 305 let safe_timestamp = timestamp.max(t + 3);
244 let diff = timestamp - t; 306 r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF));
245 if diff < 0xc00000 { 307 let diff = timestamp - t;
246 r.intenset().write(|w| w.0 = compare_n(n)); 308 if diff < 0xc00000 {
247 309 r.intenset().write(|w| w.0 = compare_n(n));
248 // If we have not passed the timestamp, we can be sure the alarm will be invoked. Otherwise, 310
249 // we need to retry setting the alarm. 311 // If we have not passed the timestamp, we can be sure the alarm will be invoked. Otherwise,
250 if self.now() + 2 <= timestamp { 312 // we need to retry setting the alarm.
313 if self.now() + 2 <= timestamp {
314 return true;
315 }
316 } else {
317 // If it's too far in the future, don't setup the compare channel yet.
318 // It will be setup later by `next_period`.
319 r.intenclr().write(|w| w.0 = compare_n(n));
251 return true; 320 return true;
252 } 321 }
253 } else { 322 }
254 // If it's too far in the future, don't setup the compare channel yet. 323
255 // It will be setup later by `next_period`. 324 // The nRF54 datasheet states that 'The EVENTS_COMPARE[n] event is generated immediately if the
256 r.intenclr().write(|w| w.0 = compare_n(n)); 325 // configured compare value at CC[n] is less than the current SYSCOUNTER value.'. This means we
326 // can write the expected timestamp and be sure the alarm is triggered.
327 #[cfg(feature = "_grtc")]
328 {
329 let ccl = timestamp as u32;
330 let cch = (timestamp >> 32) as u32 & 0xFFFFF; // 20 bits for CCH
331
332 r.cc(n).ccl().write_value(ccl);
333 r.cc(n).cch().write(|w| w.set_cch(cch));
334 r.intenset(1).write(|w| w.0 = compare_n(n));
335
257 return true; 336 return true;
258 } 337 }
259 } 338 }
@@ -261,6 +340,7 @@ impl RtcDriver {
261} 340}
262 341
263impl Driver for RtcDriver { 342impl Driver for RtcDriver {
343 #[cfg(not(feature = "_grtc"))]
264 fn now(&self) -> u64 { 344 fn now(&self) -> u64 {
265 // `period` MUST be read before `counter`, see comment at the top for details. 345 // `period` MUST be read before `counter`, see comment at the top for details.
266 let period = self.period.load(Ordering::Relaxed); 346 let period = self.period.load(Ordering::Relaxed);
@@ -269,10 +349,14 @@ impl Driver for RtcDriver {
269 calc_now(period, counter) 349 calc_now(period, counter)
270 } 350 }
271 351
352 #[cfg(feature = "_grtc")]
353 fn now(&self) -> u64 {
354 syscounter()
355 }
356
272 fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { 357 fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
273 critical_section::with(|cs| { 358 critical_section::with(|cs| {
274 let mut queue = self.queue.borrow(cs).borrow_mut(); 359 let mut queue = self.queue.borrow(cs).borrow_mut();
275
276 if queue.schedule_wake(at, waker) { 360 if queue.schedule_wake(at, waker) {
277 let mut next = queue.next_expiration(self.now()); 361 let mut next = queue.next_expiration(self.now());
278 while !self.set_alarm(cs, next) { 362 while !self.set_alarm(cs, next) {
@@ -283,14 +367,14 @@ impl Driver for RtcDriver {
283 } 367 }
284} 368}
285 369
286#[cfg(feature = "_nrf54l")] 370#[cfg(feature = "_grtc")]
287#[cfg(feature = "rt")] 371#[cfg(feature = "rt")]
288#[interrupt] 372#[interrupt]
289fn RTC30() { 373fn GRTC_1() {
290 DRIVER.on_interrupt() 374 DRIVER.on_interrupt()
291} 375}
292 376
293#[cfg(not(feature = "_nrf54l"))] 377#[cfg(not(feature = "_grtc"))]
294#[cfg(feature = "rt")] 378#[cfg(feature = "rt")]
295#[interrupt] 379#[interrupt]
296fn RTC1() { 380fn RTC1() {
diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml
index 4ef77279f..f34df0f26 100644
--- a/examples/nrf54l15/Cargo.toml
+++ b/examples/nrf54l15/Cargo.toml
@@ -10,7 +10,7 @@ embassy-futures = { version = "0.1.2", path = "../../embassy-futures" }
10embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 10embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
11embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 11embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } 12embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
13embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } 13embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-grtc", "gpiote", "unstable-pac", "time"] }
14embedded-io = { version = "0.6.0", features = ["defmt-03"] } 14embedded-io = { version = "0.6.0", features = ["defmt-03"] }
15embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } 15embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
16 16
diff --git a/examples/nrf54l15/src/bin/rtc.rs b/examples/nrf54l15/src/bin/rtc.rs
deleted file mode 100644
index a45aaca52..000000000
--- a/examples/nrf54l15/src/bin/rtc.rs
+++ /dev/null
@@ -1,56 +0,0 @@
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::Mutex;
11use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
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>>>> = Mutex::new(RefCell::new(None));
18
19#[embassy_executor::main]
20async fn main(_spawner: Spawner) {
21 defmt::println!("nRF54L15 RTC example");
22 let p = embassy_nrf::init(Default::default());
23 let mut led = Output::new(p.P2_09, Level::High, OutputDrive::Standard);
24 // Counter resolution is 125 ms.
25 let mut rtc = Rtc::new(p.RTC10, (1 << 12) - 1).unwrap();
26 rtc.enable_interrupt(embassy_nrf::rtc::Interrupt::Tick, true);
27 rtc.enable_event(embassy_nrf::rtc::Interrupt::Tick);
28 rtc.enable();
29 RTC.lock(|r| {
30 let mut rtc_borrow = r.borrow_mut();
31 *rtc_borrow = Some(rtc);
32 });
33
34 let mut last_counter_val = 0;
35 loop {
36 let current = TICK_COUNTER.load(core::sync::atomic::Ordering::Relaxed);
37 if current != last_counter_val {
38 led.toggle();
39 last_counter_val = current;
40 }
41 }
42}
43
44#[interrupt]
45fn RTC10() {
46 // For 64-bit, we do not need to worry about overflowing, at least not for realistic program
47 // lifetimes.
48 TICK_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
49 RTC.lock(|r| {
50 let mut rtc_borrow = r.borrow_mut();
51 rtc_borrow
52 .as_mut()
53 .unwrap()
54 .reset_event(embassy_nrf::rtc::Interrupt::Tick);
55 });
56}
diff --git a/examples/nrf54l15/src/bin/timer.rs b/examples/nrf54l15/src/bin/timer.rs
new file mode 100644
index 000000000..68acc91c1
--- /dev/null
+++ b/examples/nrf54l15/src/bin/timer.rs
@@ -0,0 +1,30 @@
1#![no_std]
2#![no_main]
3
4use defmt::{info, unwrap};
5use embassy_executor::Spawner;
6use embassy_time::Timer;
7use {defmt_rtt as _, panic_probe as _};
8
9#[embassy_executor::task]
10async fn run1() {
11 loop {
12 info!("BIG INFREQUENT TICK");
13 Timer::after_secs(10).await;
14 }
15}
16
17#[embassy_executor::task]
18async fn run2() {
19 loop {
20 info!("tick");
21 Timer::after_secs(1).await;
22 }
23}
24
25#[embassy_executor::main]
26async fn main(spawner: Spawner) {
27 let _p = embassy_nrf::init(Default::default());
28 spawner.spawn(unwrap!(run1()));
29 spawner.spawn(unwrap!(run2()));
30}