aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElias Hanelt <[email protected]>2025-12-01 14:43:51 -0800
committerElias Hanelt <[email protected]>2025-12-01 14:43:51 -0800
commit7772ab265c9776b3df3f7d4aeb397b6076e1f5a8 (patch)
tree40af598203172aa76fc66bdefdd767d93952e0d0
parent88ce8314c5cd653ac245cc5dbf367f6814f24be9 (diff)
parent217b683427687e8f3a27f02852e6f5bd2405ace3 (diff)
Merge remote-tracking branch 'origin/main' into feature/spi-bidi
-rwxr-xr-xci.sh1
-rw-r--r--cyw43-pio/CHANGELOG.md2
-rw-r--r--cyw43-pio/Cargo.toml6
-rw-r--r--cyw43/CHANGELOG.md2
-rw-r--r--cyw43/Cargo.toml2
-rw-r--r--embassy-boot-rp/CHANGELOG.md2
-rw-r--r--embassy-boot-rp/Cargo.toml4
-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--embassy-rp/CHANGELOG.md4
-rw-r--r--embassy-rp/Cargo.toml5
-rw-r--r--embassy-rp/src/pio/mod.rs54
-rw-r--r--embassy-rp/src/uart/buffered.rs13
-rw-r--r--embassy-rp/src/uart/mod.rs28
-rw-r--r--embassy-stm32/CHANGELOG.md3
-rw-r--r--embassy-stm32/build.rs49
-rw-r--r--embassy-stm32/src/can/enums.rs37
-rw-r--r--embassy-stm32/src/can/util.rs33
-rw-r--r--embassy-stm32/src/dma/dma_bdma.rs15
-rw-r--r--embassy-stm32/src/dma/gpdma/mod.rs10
-rw-r--r--embassy-stm32/src/dma/gpdma/ringbuffered.rs11
-rw-r--r--embassy-stm32/src/dma/mod.rs50
-rw-r--r--embassy-stm32/src/low_power.rs22
-rw-r--r--embassy-stm32/src/rcc/mod.rs111
-rw-r--r--embassy-stm32/src/time_driver.rs126
-rw-r--r--embassy-stm32/src/usart/mod.rs16
-rw-r--r--embassy-stm32/src/usart/ringbuffered.rs3
-rw-r--r--embassy-usb/CHANGELOG.md1
-rw-r--r--embassy-usb/Cargo.toml2
-rw-r--r--examples/boot/application/rp/Cargo.toml4
-rw-r--r--examples/nrf52840/Cargo.toml2
-rw-r--r--examples/nrf5340/Cargo.toml2
-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
-rw-r--r--examples/rp/Cargo.toml8
-rw-r--r--examples/rp235x/Cargo.toml8
-rw-r--r--examples/stm32f4/Cargo.toml2
-rw-r--r--examples/stm32g4/Cargo.toml2
-rw-r--r--examples/stm32l5/Cargo.toml2
-rw-r--r--tests/rp/Cargo.toml2
46 files changed, 558 insertions, 386 deletions
diff --git a/ci.sh b/ci.sh
index b4ed0dc18..cee761500 100755
--- a/ci.sh
+++ b/ci.sh
@@ -65,6 +65,7 @@ rm out/tests/pimoroni-pico-plus-2/pwm
65# flaky 65# flaky
66rm out/tests/rpi-pico/pwm 66rm out/tests/rpi-pico/pwm
67rm out/tests/rpi-pico/cyw43-perf 67rm out/tests/rpi-pico/cyw43-perf
68rm out/tests/rpi-pico/uart_buffered
68 69
69# tests are implemented but the HIL test farm doesn't actually have these boards, yet 70# tests are implemented but the HIL test farm doesn't actually have these boards, yet
70rm -rf out/tests/stm32c071rb 71rm -rf out/tests/stm32c071rb
diff --git a/cyw43-pio/CHANGELOG.md b/cyw43-pio/CHANGELOG.md
index c2d18919c..80343e02e 100644
--- a/cyw43-pio/CHANGELOG.md
+++ b/cyw43-pio/CHANGELOG.md
@@ -8,6 +8,8 @@ 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## 0.9.0 - 2025-11-27
12
11- Select pio program based on core clock speed #4792 13- Select pio program based on core clock speed #4792
12 14
13## 0.8.0 - 2025-08-28 15## 0.8.0 - 2025-08-28
diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml
index a10a091e9..3001fa92b 100644
--- a/cyw43-pio/Cargo.toml
+++ b/cyw43-pio/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "cyw43-pio" 2name = "cyw43-pio"
3version = "0.8.0" 3version = "0.9.0"
4edition = "2024" 4edition = "2024"
5description = "RP2040 PIO SPI implementation for cyw43" 5description = "RP2040 PIO SPI implementation for cyw43"
6keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] 6keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"]
@@ -10,8 +10,8 @@ repository = "https://github.com/embassy-rs/embassy"
10documentation = "https://docs.embassy.dev/cyw43-pio" 10documentation = "https://docs.embassy.dev/cyw43-pio"
11 11
12[dependencies] 12[dependencies]
13cyw43 = { version = "0.5.0", path = "../cyw43" } 13cyw43 = { version = "0.6.0", path = "../cyw43" }
14embassy-rp = { version = "0.8.0", path = "../embassy-rp" } 14embassy-rp = { version = "0.9.0", path = "../embassy-rp" }
15fixed = "1.23.1" 15fixed = "1.23.1"
16defmt = { version = "1.0.1", optional = true } 16defmt = { version = "1.0.1", optional = true }
17 17
diff --git a/cyw43/CHANGELOG.md b/cyw43/CHANGELOG.md
index 1045fd30b..9fe341357 100644
--- a/cyw43/CHANGELOG.md
+++ b/cyw43/CHANGELOG.md
@@ -8,6 +8,8 @@ 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## 0.6.0 - 2025-11-27
12
11- Updated documentation for Control::join() #4678 13- Updated documentation for Control::join() #4678
12- Bump bt-hci to 0.6.0. 14- Bump bt-hci to 0.6.0.
13- Add error handling to HCI transport implementation. 15- Add error handling to HCI transport implementation.
diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml
index 314427611..6d7647697 100644
--- a/cyw43/Cargo.toml
+++ b/cyw43/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "cyw43" 2name = "cyw43"
3version = "0.5.0" 3version = "0.6.0"
4edition = "2024" 4edition = "2024"
5description = "Rust driver for the CYW43439 WiFi chip, used in the Raspberry Pi Pico W." 5description = "Rust driver for the CYW43439 WiFi chip, used in the Raspberry Pi Pico W."
6keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] 6keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"]
diff --git a/embassy-boot-rp/CHANGELOG.md b/embassy-boot-rp/CHANGELOG.md
index 8cc1e73c0..068075b5c 100644
--- a/embassy-boot-rp/CHANGELOG.md
+++ b/embassy-boot-rp/CHANGELOG.md
@@ -8,6 +8,8 @@ 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## 0.9.0 - 2025-11-27
12
11## 0.8.0 - 2025-08-26 13## 0.8.0 - 2025-08-26
12 14
13## 0.1.1 - 2025-08-15 15## 0.1.1 - 2025-08-15
diff --git a/embassy-boot-rp/Cargo.toml b/embassy-boot-rp/Cargo.toml
index e80c79374..2f63dea4c 100644
--- a/embassy-boot-rp/Cargo.toml
+++ b/embassy-boot-rp/Cargo.toml
@@ -1,7 +1,7 @@
1[package] 1[package]
2edition = "2024" 2edition = "2024"
3name = "embassy-boot-rp" 3name = "embassy-boot-rp"
4version = "0.8.0" 4version = "0.9.0"
5description = "Bootloader lib for RP2040 chips" 5description = "Bootloader lib for RP2040 chips"
6license = "MIT OR Apache-2.0" 6license = "MIT OR Apache-2.0"
7repository = "https://github.com/embassy-rs/embassy" 7repository = "https://github.com/embassy-rs/embassy"
@@ -31,7 +31,7 @@ defmt = { version = "1.0.1", optional = true }
31log = { version = "0.4", optional = true } 31log = { version = "0.4", optional = true }
32 32
33embassy-sync = { version = "0.7.2", path = "../embassy-sync" } 33embassy-sync = { version = "0.7.2", path = "../embassy-sync" }
34embassy-rp = { version = "0.8.0", path = "../embassy-rp", default-features = false } 34embassy-rp = { version = "0.9.0", path = "../embassy-rp", default-features = false }
35embassy-boot = { version = "0.6.1", path = "../embassy-boot" } 35embassy-boot = { version = "0.6.1", path = "../embassy-boot" }
36embassy-time = { version = "0.5.0", path = "../embassy-time" } 36embassy-time = { version = "0.5.0", path = "../embassy-time" }
37 37
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/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md
index fa8609bbf..773301b0f 100644
--- a/embassy-rp/CHANGELOG.md
+++ b/embassy-rp/CHANGELOG.md
@@ -8,6 +8,8 @@ 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## 0.9.0 - 2025-11-27
12
11- Add documentation for pio `get_x` about autopush. 13- Add documentation for pio `get_x` about autopush.
12- Fix several minor typos in documentation 14- Fix several minor typos in documentation
13- Add PIO SPI 15- Add PIO SPI
@@ -19,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
19- Add fix #4822 in PIO onewire. Change to disable the state machine before setting y register ([#4824](https://github.com/embassy-rs/embassy/pull/4824)) 21- Add fix #4822 in PIO onewire. Change to disable the state machine before setting y register ([#4824](https://github.com/embassy-rs/embassy/pull/4824))
20- Add PIO::Ws2812 color order support 22- Add PIO::Ws2812 color order support
21- Add TX-only, no SCK SPI support 23- Add TX-only, no SCK SPI support
24- Remove atomic-polyfill with critical-section instead ([#4948](https://github.com/embassy-rs/embassy/pull/4948))
22 25
23## 0.8.0 - 2025-08-26 26## 0.8.0 - 2025-08-26
24 27
@@ -116,4 +119,3 @@ Small release fixing a few gnarly bugs, upgrading is strongly recommended.
116- rename the Channel trait to Slice and the PwmPin to PwmChannel 119- rename the Channel trait to Slice and the PwmPin to PwmChannel
117- i2c: Fix race condition that appears on fast repeated transfers. 120- i2c: Fix race condition that appears on fast repeated transfers.
118- Add a basic "read to break" function 121- Add a basic "read to break" function
119
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index 9ad4b47a3..8e4bb927f 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2name = "embassy-rp" 2name = "embassy-rp"
3version = "0.8.0" 3version = "0.9.0"
4edition = "2024" 4edition = "2024"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6description = "Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 or RP235x microcontroller" 6description = "Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 or RP235x microcontroller"
@@ -47,7 +47,7 @@ rt = [ "rp-pac/rt" ]
47defmt = ["dep:defmt", "embassy-usb-driver/defmt", "embassy-hal-internal/defmt"] 47defmt = ["dep:defmt", "embassy-usb-driver/defmt", "embassy-hal-internal/defmt"]
48## Enable log support 48## Enable log support
49log = ["dep:log"] 49log = ["dep:log"]
50## Enable chrono support 50## Enable chrono support
51chrono = ["dep:chrono"] 51chrono = ["dep:chrono"]
52 52
53## Configure the [`critical-section`](https://docs.rs/critical-section) crate to use an implementation that is safe for multicore use on rp2040. 53## Configure the [`critical-section`](https://docs.rs/critical-section) crate to use an implementation that is safe for multicore use on rp2040.
@@ -159,7 +159,6 @@ embassy-futures = { version = "0.1.2", path = "../embassy-futures" }
159embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } 159embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
160embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } 160embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" }
161embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } 161embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" }
162atomic-polyfill = "1.0.1"
163defmt = { version = "1.0.1", optional = true } 162defmt = { version = "1.0.1", optional = true }
164log = { version = "0.4.14", optional = true } 163log = { version = "0.4.14", optional = true }
165nb = "1.1.0" 164nb = "1.1.0"
diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs
index 92b2c603e..1c370fdfc 100644
--- a/embassy-rp/src/pio/mod.rs
+++ b/embassy-rp/src/pio/mod.rs
@@ -2,10 +2,9 @@
2use core::future::Future; 2use core::future::Future;
3use core::marker::PhantomData; 3use core::marker::PhantomData;
4use core::pin::Pin as FuturePin; 4use core::pin::Pin as FuturePin;
5use core::sync::atomic::{Ordering, compiler_fence}; 5use core::sync::atomic::{AtomicU8, AtomicU32, Ordering, compiler_fence};
6use core::task::{Context, Poll}; 6use core::task::{Context, Poll};
7 7
8use atomic_polyfill::{AtomicU8, AtomicU64};
9use embassy_hal_internal::{Peri, PeripheralType}; 8use embassy_hal_internal::{Peri, PeripheralType};
10use embassy_sync::waitqueue::AtomicWaker; 9use embassy_sync::waitqueue::AtomicWaker;
11use fixed::FixedU32; 10use fixed::FixedU32;
@@ -1232,7 +1231,13 @@ impl<'d, PIO: Instance> Common<'d, PIO> {
1232 w.set_pde(false); 1231 w.set_pde(false);
1233 }); 1232 });
1234 // we can be relaxed about this because we're &mut here and nothing is cached 1233 // we can be relaxed about this because we're &mut here and nothing is cached
1235 PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); 1234 critical_section::with(|_| {
1235 let val = PIO::state().used_pins.load(Ordering::Relaxed);
1236 PIO::state()
1237 .used_pins
1238 .store(val | 1 << pin.pin_bank(), Ordering::Relaxed);
1239 });
1240
1236 Pin { 1241 Pin {
1237 pin: pin.into(), 1242 pin: pin.into(),
1238 pio: PhantomData::default(), 1243 pio: PhantomData::default(),
@@ -1404,6 +1409,42 @@ impl<'d, PIO: Instance> Pio<'d, PIO> {
1404 } 1409 }
1405} 1410}
1406 1411
1412struct AtomicU64 {
1413 upper_32: AtomicU32,
1414 lower_32: AtomicU32,
1415}
1416
1417impl AtomicU64 {
1418 const fn new(val: u64) -> Self {
1419 let upper_32 = (val >> 32) as u32;
1420 let lower_32 = val as u32;
1421
1422 Self {
1423 upper_32: AtomicU32::new(upper_32),
1424 lower_32: AtomicU32::new(lower_32),
1425 }
1426 }
1427
1428 fn load(&self, order: Ordering) -> u64 {
1429 let (upper, lower) = critical_section::with(|_| (self.upper_32.load(order), self.lower_32.load(order)));
1430
1431 let upper = (upper as u64) << 32;
1432 let lower = lower as u64;
1433
1434 upper | lower
1435 }
1436
1437 fn store(&self, val: u64, order: Ordering) {
1438 let upper_32 = (val >> 32) as u32;
1439 let lower_32 = val as u32;
1440
1441 critical_section::with(|_| {
1442 self.upper_32.store(upper_32, order);
1443 self.lower_32.store(lower_32, order);
1444 });
1445 }
1446}
1447
1407/// Representation of the PIO state keeping a record of which pins are assigned to 1448/// Representation of the PIO state keeping a record of which pins are assigned to
1408/// each PIO. 1449/// each PIO.
1409// make_pio_pin notionally takes ownership of the pin it is given, but the wrapped pin 1450// make_pio_pin notionally takes ownership of the pin it is given, but the wrapped pin
@@ -1418,7 +1459,12 @@ pub struct State {
1418 1459
1419fn on_pio_drop<PIO: Instance>() { 1460fn on_pio_drop<PIO: Instance>() {
1420 let state = PIO::state(); 1461 let state = PIO::state();
1421 if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { 1462 let users_state = critical_section::with(|_| {
1463 let val = state.users.load(Ordering::Acquire) - 1;
1464 state.users.store(val, Ordering::Release);
1465 val
1466 });
1467 if users_state == 1 {
1422 let used_pins = state.used_pins.load(Ordering::Relaxed); 1468 let used_pins = state.used_pins.load(Ordering::Relaxed);
1423 let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _; 1469 let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _;
1424 for i in 0..crate::gpio::BANK0_PIN_COUNT { 1470 for i in 0..crate::gpio::BANK0_PIN_COUNT {
diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs
index 02649ad81..fdb8ce776 100644
--- a/embassy-rp/src/uart/buffered.rs
+++ b/embassy-rp/src/uart/buffered.rs
@@ -1,8 +1,8 @@
1//! Buffered UART driver. 1//! Buffered UART driver.
2use core::future::Future; 2use core::future::Future;
3use core::slice; 3use core::slice;
4use core::sync::atomic::{AtomicU8, Ordering};
4 5
5use atomic_polyfill::AtomicU8;
6use embassy_hal_internal::atomic_ring_buffer::RingBuffer; 6use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
7 7
8use super::*; 8use super::*;
@@ -241,7 +241,11 @@ impl BufferedUartRx {
241 } 241 }
242 242
243 fn get_rx_error(state: &State) -> Option<Error> { 243 fn get_rx_error(state: &State) -> Option<Error> {
244 let errs = state.rx_error.swap(0, Ordering::Relaxed); 244 let errs = critical_section::with(|_| {
245 let val = state.rx_error.load(Ordering::Relaxed);
246 state.rx_error.store(0, Ordering::Relaxed);
247 val
248 });
245 if errs & RXE_OVERRUN != 0 { 249 if errs & RXE_OVERRUN != 0 {
246 Some(Error::Overrun) 250 Some(Error::Overrun)
247 } else if errs & RXE_BREAK != 0 { 251 } else if errs & RXE_BREAK != 0 {
@@ -555,7 +559,10 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for BufferedInterr
555 } 559 }
556 let dr = r.uartdr().read(); 560 let dr = r.uartdr().read();
557 if (dr.0 >> 8) != 0 { 561 if (dr.0 >> 8) != 0 {
558 s.rx_error.fetch_or((dr.0 >> 8) as u8, Ordering::Relaxed); 562 critical_section::with(|_| {
563 let val = s.rx_error.load(Ordering::Relaxed);
564 s.rx_error.store(val | ((dr.0 >> 8) as u8), Ordering::Relaxed);
565 });
559 error = true; 566 error = true;
560 // only fill the buffer with valid characters. the current character is fine 567 // only fill the buffer with valid characters. the current character is fine
561 // if the error is an overrun, but if we add it to the buffer we'll report 568 // if the error is an overrun, but if we add it to the buffer we'll report
diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs
index 8be87a5d2..b7b569dd5 100644
--- a/embassy-rp/src/uart/mod.rs
+++ b/embassy-rp/src/uart/mod.rs
@@ -1,9 +1,9 @@
1//! UART driver. 1//! UART driver.
2use core::future::poll_fn; 2use core::future::poll_fn;
3use core::marker::PhantomData; 3use core::marker::PhantomData;
4use core::sync::atomic::{AtomicU16, Ordering};
4use core::task::Poll; 5use core::task::Poll;
5 6
6use atomic_polyfill::{AtomicU16, Ordering};
7use embassy_futures::select::{Either, select}; 7use embassy_futures::select::{Either, select};
8use embassy_hal_internal::{Peri, PeripheralType}; 8use embassy_hal_internal::{Peri, PeripheralType};
9use embassy_sync::waitqueue::AtomicWaker; 9use embassy_sync::waitqueue::AtomicWaker;
@@ -456,7 +456,12 @@ impl<'d> UartRx<'d, Async> {
456 transfer, 456 transfer,
457 poll_fn(|cx| { 457 poll_fn(|cx| {
458 self.dma_state.rx_err_waker.register(cx.waker()); 458 self.dma_state.rx_err_waker.register(cx.waker());
459 match self.dma_state.rx_errs.swap(0, Ordering::Relaxed) { 459 let rx_errs = critical_section::with(|_| {
460 let val = self.dma_state.rx_errs.load(Ordering::Relaxed);
461 self.dma_state.rx_errs.store(0, Ordering::Relaxed);
462 val
463 });
464 match rx_errs {
460 0 => Poll::Pending, 465 0 => Poll::Pending,
461 e => Poll::Ready(Uartris(e as u32)), 466 e => Poll::Ready(Uartris(e as u32)),
462 } 467 }
@@ -468,7 +473,11 @@ impl<'d> UartRx<'d, Async> {
468 Either::First(()) => { 473 Either::First(()) => {
469 // We're here because the DMA finished, BUT if an error occurred on the LAST 474 // We're here because the DMA finished, BUT if an error occurred on the LAST
470 // byte, then we may still need to grab the error state! 475 // byte, then we may still need to grab the error state!
471 Uartris(self.dma_state.rx_errs.swap(0, Ordering::Relaxed) as u32) 476 Uartris(critical_section::with(|_| {
477 let val = self.dma_state.rx_errs.load(Ordering::Relaxed);
478 self.dma_state.rx_errs.store(0, Ordering::Relaxed);
479 val
480 }) as u32)
472 } 481 }
473 Either::Second(e) => { 482 Either::Second(e) => {
474 // We're here because we errored, which means this is the error that 483 // We're here because we errored, which means this is the error that
@@ -616,7 +625,12 @@ impl<'d> UartRx<'d, Async> {
616 transfer, 625 transfer,
617 poll_fn(|cx| { 626 poll_fn(|cx| {
618 self.dma_state.rx_err_waker.register(cx.waker()); 627 self.dma_state.rx_err_waker.register(cx.waker());
619 match self.dma_state.rx_errs.swap(0, Ordering::Relaxed) { 628 let rx_errs = critical_section::with(|_| {
629 let val = self.dma_state.rx_errs.load(Ordering::Relaxed);
630 self.dma_state.rx_errs.store(0, Ordering::Relaxed);
631 val
632 });
633 match rx_errs {
620 0 => Poll::Pending, 634 0 => Poll::Pending,
621 e => Poll::Ready(Uartris(e as u32)), 635 e => Poll::Ready(Uartris(e as u32)),
622 } 636 }
@@ -629,7 +643,11 @@ impl<'d> UartRx<'d, Async> {
629 Either::First(()) => { 643 Either::First(()) => {
630 // We're here because the DMA finished, BUT if an error occurred on the LAST 644 // We're here because the DMA finished, BUT if an error occurred on the LAST
631 // byte, then we may still need to grab the error state! 645 // byte, then we may still need to grab the error state!
632 Uartris(self.dma_state.rx_errs.swap(0, Ordering::Relaxed) as u32) 646 Uartris(critical_section::with(|_| {
647 let val = self.dma_state.rx_errs.load(Ordering::Relaxed);
648 self.dma_state.rx_errs.store(0, Ordering::Relaxed);
649 val
650 }) as u32)
633 } 651 }
634 Either::Second(e) => { 652 Either::Second(e) => {
635 // We're here because we errored, which means this is the error that 653 // We're here because we errored, which means this is the error that
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index d3e5ba48d..2b273482c 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7 7
8## Unreleased - ReleaseDate 8## Unreleased - ReleaseDate
9 9
10- fix: fix incorrect handling of split interrupts in timer driver
11- feat: allow granular stop for regular usart
10- feat: Add continuous waveform method to SimplePWM 12- feat: Add continuous waveform method to SimplePWM
11- change: remove waveform timer method 13- change: remove waveform timer method
12- change: low power: store stop mode for dma channels 14- change: low power: store stop mode for dma channels
@@ -82,6 +84,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
82- removal: ExtiInput no longer accepts AnyPin/AnyChannel; AnyChannel removed entirely 84- removal: ExtiInput no longer accepts AnyPin/AnyChannel; AnyChannel removed entirely
83- fix: build script ensures EXTI2_TSC is listed as the IRQ of EXTI2 even if the PAC doesn't 85- fix: build script ensures EXTI2_TSC is listed as the IRQ of EXTI2 even if the PAC doesn't
84- feat: stm32/lcd: added implementation 86- feat: stm32/lcd: added implementation
87- change: add error messages to can timing calculations ([#4961](https://github.com/embassy-rs/embassy/pull/4961))
85 88
86## 0.4.0 - 2025-08-26 89## 0.4.0 - 2025-08-26
87 90
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 109571e8f..46d6290e7 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -9,7 +9,7 @@ use proc_macro2::{Ident, TokenStream};
9use quote::{format_ident, quote}; 9use quote::{format_ident, quote};
10use stm32_metapac::metadata::ir::BitOffset; 10use stm32_metapac::metadata::ir::BitOffset;
11use stm32_metapac::metadata::{ 11use stm32_metapac::metadata::{
12 ALL_CHIPS, ALL_PERIPHERAL_VERSIONS, METADATA, MemoryRegion, MemoryRegionKind, PeripheralRccKernelClock, 12 ALL_CHIPS, ALL_PERIPHERAL_VERSIONS, METADATA, MemoryRegion, MemoryRegionKind, Peripheral, PeripheralRccKernelClock,
13 PeripheralRccRegister, PeripheralRegisters, StopMode, 13 PeripheralRccRegister, PeripheralRegisters, StopMode,
14}; 14};
15 15
@@ -133,6 +133,9 @@ fn main() {
133 cfgs.enable("backup_sram") 133 cfgs.enable("backup_sram")
134 } 134 }
135 135
136 // compile a map of peripherals
137 let peripheral_map: BTreeMap<&str, &Peripheral> = METADATA.peripherals.iter().map(|p| (p.name, p)).collect();
138
136 // generate one singleton per peripheral (with many exceptions...) 139 // generate one singleton per peripheral (with many exceptions...)
137 for p in METADATA.peripherals { 140 for p in METADATA.peripherals {
138 if let Some(r) = &p.registers { 141 if let Some(r) = &p.registers {
@@ -319,9 +322,33 @@ fn main() {
319 _ => panic!("unknown time_driver {:?}", time_driver), 322 _ => panic!("unknown time_driver {:?}", time_driver),
320 }; 323 };
321 324
322 if !time_driver_singleton.is_empty() { 325 let time_driver_irq_decl = if !time_driver_singleton.is_empty() {
323 cfgs.enable(format!("time_driver_{}", time_driver_singleton.to_lowercase())); 326 cfgs.enable(format!("time_driver_{}", time_driver_singleton.to_lowercase()));
324 } 327
328 let p = peripheral_map.get(time_driver_singleton).unwrap();
329 let irqs: BTreeSet<_> = p
330 .interrupts
331 .iter()
332 .filter(|i| i.signal == "CC" || i.signal == "UP")
333 .map(|i| i.interrupt.to_ascii_uppercase())
334 .collect();
335
336 irqs.iter()
337 .map(|i| {
338 let irq = format_ident!("{}", i);
339 quote! {
340 #[cfg(feature = "rt")]
341 #[interrupt]
342 fn #irq() {
343 crate::time_driver::get_driver().on_interrupt();
344 }
345 }
346 })
347 .collect()
348 } else {
349 TokenStream::new()
350 };
351
325 for tim in [ 352 for tim in [
326 "tim1", "tim2", "tim3", "tim4", "tim5", "tim8", "tim9", "tim12", "tim15", "tim20", "tim21", "tim22", "tim23", 353 "tim1", "tim2", "tim3", "tim4", "tim5", "tim8", "tim9", "tim12", "tim15", "tim20", "tim21", "tim22", "tim23",
327 "tim24", 354 "tim24",
@@ -371,6 +398,8 @@ fn main() {
371 ); 398 );
372 }); 399 });
373 400
401 g.extend(time_driver_irq_decl);
402
374 // ======== 403 // ========
375 // Generate FLASH regions 404 // Generate FLASH regions
376 cfgs.declare("flash"); 405 cfgs.declare("flash");
@@ -1862,7 +1891,7 @@ fn main() {
1862 flash_regions_table.push(row); 1891 flash_regions_table.push(row);
1863 } 1892 }
1864 1893
1865 let gpio_base = METADATA.peripherals.iter().find(|p| p.name == "GPIOA").unwrap().address as u32; 1894 let gpio_base = peripheral_map.get("GPIOA").unwrap().address as u32;
1866 let gpio_stride = 0x400; 1895 let gpio_stride = 0x400;
1867 1896
1868 for pin in METADATA.pins { 1897 for pin in METADATA.pins {
@@ -1980,11 +2009,11 @@ fn main() {
1980 continue; 2009 continue;
1981 } 2010 }
1982 2011
1983 let stop_mode = METADATA 2012 let dma_peri = peripheral_map.get(ch.dma).unwrap();
1984 .peripherals 2013 let stop_mode = dma_peri
1985 .iter() 2014 .rcc
1986 .find(|p| p.name == ch.dma) 2015 .as_ref()
1987 .map(|p| p.rcc.as_ref().map(|rcc| rcc.stop_mode.clone()).unwrap_or_default()) 2016 .map(|rcc| rcc.stop_mode.clone())
1988 .unwrap_or_default(); 2017 .unwrap_or_default();
1989 2018
1990 let stop_mode = match stop_mode { 2019 let stop_mode = match stop_mode {
@@ -2009,8 +2038,6 @@ fn main() {
2009 2038
2010 let dma = format_ident!("{}", ch.dma); 2039 let dma = format_ident!("{}", ch.dma);
2011 let ch_num = ch.channel as usize; 2040 let ch_num = ch.channel as usize;
2012
2013 let dma_peri = METADATA.peripherals.iter().find(|p| p.name == ch.dma).unwrap();
2014 let bi = dma_peri.registers.as_ref().unwrap(); 2041 let bi = dma_peri.registers.as_ref().unwrap();
2015 2042
2016 let dma_info = match bi.kind { 2043 let dma_info = match bi.kind {
diff --git a/embassy-stm32/src/can/enums.rs b/embassy-stm32/src/can/enums.rs
index 6d91020fc..c5900cadc 100644
--- a/embassy-stm32/src/can/enums.rs
+++ b/embassy-stm32/src/can/enums.rs
@@ -82,3 +82,40 @@ pub enum RefCountOp {
82 /// Notify sender destroyed 82 /// Notify sender destroyed
83 NotifySenderDestroyed, 83 NotifySenderDestroyed,
84} 84}
85
86/// Error returned when calculating the can timing fails
87#[derive(Debug)]
88#[cfg_attr(feature = "defmt", derive(defmt::Format))]
89pub enum TimingCalcError {
90 /// Bitrate is lower than 1000
91 BitrateTooLow {
92 /// The set bitrate
93 bitrate: u32,
94 },
95 /// No solution possible
96 NoSolution {
97 /// The sum of BS1 and BS2
98 bs1_bs2_sum: u8,
99 },
100 /// Prescaler is not 1 < prescaler < 1024
101 InvalidPrescaler {
102 /// The calculated prescaler value
103 prescaler: u32,
104 },
105 /// BS1 or BS2 are not in the range 0 < BSx < BSx_MAX
106 BSNotInRange {
107 /// The value of BS1
108 bs1: u8,
109 /// The value of BS2
110 bs2: u8,
111 },
112 /// Final bitrate doesn't match the requested bitrate
113 NoMatch {
114 /// The requested bitrate
115 requested: u32,
116 /// The calculated bitrate
117 final_calculated: u32,
118 },
119 /// core::num::NonZeroUxx::new error
120 CoreNumNew,
121}
diff --git a/embassy-stm32/src/can/util.rs b/embassy-stm32/src/can/util.rs
index 6d7f0c16a..beca4c34e 100644
--- a/embassy-stm32/src/can/util.rs
+++ b/embassy-stm32/src/can/util.rs
@@ -2,6 +2,8 @@
2 2
3use core::num::{NonZeroU8, NonZeroU16}; 3use core::num::{NonZeroU8, NonZeroU16};
4 4
5use crate::can::enums::TimingCalcError;
6
5/// Shared struct to represent bit timings used by calc_can_timings. 7/// Shared struct to represent bit timings used by calc_can_timings.
6#[derive(Clone, Copy, Debug)] 8#[derive(Clone, Copy, Debug)]
7pub struct NominalBitTiming { 9pub struct NominalBitTiming {
@@ -17,7 +19,10 @@ pub struct NominalBitTiming {
17} 19}
18 20
19/// Calculate nominal CAN bit timing based on CAN bitrate and periphial clock frequency 21/// Calculate nominal CAN bit timing based on CAN bitrate and periphial clock frequency
20pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> Option<NominalBitTiming> { 22pub fn calc_can_timings(
23 periph_clock: crate::time::Hertz,
24 can_bitrate: u32,
25) -> Result<NominalBitTiming, TimingCalcError> {
21 const BS1_MAX: u8 = 16; 26 const BS1_MAX: u8 = 16;
22 const BS2_MAX: u8 = 8; 27 const BS2_MAX: u8 = 8;
23 const MAX_SAMPLE_POINT_PERMILL: u16 = 900; 28 const MAX_SAMPLE_POINT_PERMILL: u16 = 900;
@@ -25,7 +30,7 @@ pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> O
25 let periph_clock = periph_clock.0; 30 let periph_clock = periph_clock.0;
26 31
27 if can_bitrate < 1000 { 32 if can_bitrate < 1000 {
28 return None; 33 return Err(TimingCalcError::BitrateTooLow { bitrate: can_bitrate });
29 } 34 }
30 35
31 // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG 36 // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG
@@ -53,14 +58,14 @@ pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> O
53 let mut bs1_bs2_sum = max_quanta_per_bit - 1; 58 let mut bs1_bs2_sum = max_quanta_per_bit - 1;
54 while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 { 59 while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 {
55 if bs1_bs2_sum <= 2 { 60 if bs1_bs2_sum <= 2 {
56 return None; // No solution 61 return Err(TimingCalcError::NoSolution { bs1_bs2_sum }); // No solution
57 } 62 }
58 bs1_bs2_sum -= 1; 63 bs1_bs2_sum -= 1;
59 } 64 }
60 65
61 let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32; 66 let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32;
62 if (prescaler < 1) || (prescaler > 1024) { 67 if (prescaler < 1) || (prescaler > 1024) {
63 return None; // No solution 68 return Err(TimingCalcError::InvalidPrescaler { prescaler }); // No solution
64 } 69 }
65 70
66 // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. 71 // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum.
@@ -93,22 +98,26 @@ pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> O
93 98
94 // Check is BS1 and BS2 are in range 99 // Check is BS1 and BS2 are in range
95 if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) { 100 if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) {
96 return None; 101 return Err(TimingCalcError::BSNotInRange { bs1, bs2 });
97 } 102 }
98 103
104 let calculated = periph_clock / (prescaler * (1 + bs1 + bs2) as u32);
99 // Check if final bitrate matches the requested 105 // Check if final bitrate matches the requested
100 if can_bitrate != (periph_clock / (prescaler * (1 + bs1 + bs2) as u32)) { 106 if can_bitrate != calculated {
101 return None; 107 return Err(TimingCalcError::NoMatch {
108 requested: can_bitrate,
109 final_calculated: calculated,
110 });
102 } 111 }
103 112
104 // One is recommended by DS-015, CANOpen, and DeviceNet 113 // One is recommended by DS-015, CANOpen, and DeviceNet
105 let sync_jump_width = core::num::NonZeroU8::new(1)?; 114 let sync_jump_width = core::num::NonZeroU8::new(1).ok_or(TimingCalcError::CoreNumNew)?;
106 115
107 let seg1 = core::num::NonZeroU8::new(bs1)?; 116 let seg1 = core::num::NonZeroU8::new(bs1).ok_or(TimingCalcError::CoreNumNew)?;
108 let seg2 = core::num::NonZeroU8::new(bs2)?; 117 let seg2 = core::num::NonZeroU8::new(bs2).ok_or(TimingCalcError::CoreNumNew)?;
109 let nz_prescaler = core::num::NonZeroU16::new(prescaler as u16)?; 118 let nz_prescaler = core::num::NonZeroU16::new(prescaler as u16).ok_or(TimingCalcError::CoreNumNew)?;
110 119
111 Some(NominalBitTiming { 120 Ok(NominalBitTiming {
112 sync_jump_width, 121 sync_jump_width,
113 prescaler: nz_prescaler, 122 prescaler: nz_prescaler,
114 seg1, 123 seg1,
diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs
index b46ae2813..adc084474 100644
--- a/embassy-stm32/src/dma/dma_bdma.rs
+++ b/embassy-stm32/src/dma/dma_bdma.rs
@@ -8,8 +8,9 @@ use embassy_sync::waitqueue::AtomicWaker;
8 8
9use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; 9use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer};
10use super::word::{Word, WordSize}; 10use super::word::{Word, WordSize};
11use super::{AnyChannel, BusyChannel, Channel, Dir, Request, STATE}; 11use super::{AnyChannel, Channel, Dir, Request, STATE};
12use crate::interrupt::typelevel::Interrupt; 12use crate::interrupt::typelevel::Interrupt;
13use crate::rcc::BusyPeripheral;
13use crate::{interrupt, pac}; 14use crate::{interrupt, pac};
14 15
15pub(crate) struct ChannelInfo { 16pub(crate) struct ChannelInfo {
@@ -602,7 +603,7 @@ impl AnyChannel {
602/// DMA transfer. 603/// DMA transfer.
603#[must_use = "futures do nothing unless you `.await` or poll them"] 604#[must_use = "futures do nothing unless you `.await` or poll them"]
604pub struct Transfer<'a> { 605pub struct Transfer<'a> {
605 channel: BusyChannel<'a>, 606 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
606} 607}
607 608
608impl<'a> Transfer<'a> { 609impl<'a> Transfer<'a> {
@@ -714,7 +715,7 @@ impl<'a> Transfer<'a> {
714 ); 715 );
715 channel.start(); 716 channel.start();
716 Self { 717 Self {
717 channel: BusyChannel::new(channel), 718 channel: BusyPeripheral::new(channel),
718 } 719 }
719 } 720 }
720 721
@@ -818,7 +819,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> {
818 819
819/// Ringbuffer for receiving data using DMA circular mode. 820/// Ringbuffer for receiving data using DMA circular mode.
820pub struct ReadableRingBuffer<'a, W: Word> { 821pub struct ReadableRingBuffer<'a, W: Word> {
821 channel: BusyChannel<'a>, 822 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
822 ringbuf: ReadableDmaRingBuffer<'a, W>, 823 ringbuf: ReadableDmaRingBuffer<'a, W>,
823} 824}
824 825
@@ -855,7 +856,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
855 ); 856 );
856 857
857 Self { 858 Self {
858 channel: BusyChannel::new(channel), 859 channel: BusyPeripheral::new(channel),
859 ringbuf: ReadableDmaRingBuffer::new(buffer), 860 ringbuf: ReadableDmaRingBuffer::new(buffer),
860 } 861 }
861 } 862 }
@@ -974,7 +975,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> {
974 975
975/// Ringbuffer for writing data using DMA circular mode. 976/// Ringbuffer for writing data using DMA circular mode.
976pub struct WritableRingBuffer<'a, W: Word> { 977pub struct WritableRingBuffer<'a, W: Word> {
977 channel: BusyChannel<'a>, 978 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
978 ringbuf: WritableDmaRingBuffer<'a, W>, 979 ringbuf: WritableDmaRingBuffer<'a, W>,
979} 980}
980 981
@@ -1011,7 +1012,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> {
1011 ); 1012 );
1012 1013
1013 Self { 1014 Self {
1014 channel: BusyChannel::new(channel), 1015 channel: BusyPeripheral::new(channel),
1015 ringbuf: WritableDmaRingBuffer::new(buffer), 1016 ringbuf: WritableDmaRingBuffer::new(buffer),
1016 } 1017 }
1017 } 1018 }
diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs
index 383c74a78..bfd0570f8 100644
--- a/embassy-stm32/src/dma/gpdma/mod.rs
+++ b/embassy-stm32/src/dma/gpdma/mod.rs
@@ -11,10 +11,10 @@ use linked_list::Table;
11 11
12use super::word::{Word, WordSize}; 12use super::word::{Word, WordSize};
13use super::{AnyChannel, Channel, Dir, Request, STATE}; 13use super::{AnyChannel, Channel, Dir, Request, STATE};
14use crate::dma::BusyChannel;
15use crate::interrupt::typelevel::Interrupt; 14use crate::interrupt::typelevel::Interrupt;
16use crate::pac; 15use crate::pac;
17use crate::pac::gpdma::vals; 16use crate::pac::gpdma::vals;
17use crate::rcc::BusyPeripheral;
18 18
19pub mod linked_list; 19pub mod linked_list;
20pub mod ringbuffered; 20pub mod ringbuffered;
@@ -409,7 +409,7 @@ impl AnyChannel {
409/// Linked-list DMA transfer. 409/// Linked-list DMA transfer.
410#[must_use = "futures do nothing unless you `.await` or poll them"] 410#[must_use = "futures do nothing unless you `.await` or poll them"]
411pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { 411pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> {
412 channel: BusyChannel<'a>, 412 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
413} 413}
414 414
415impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { 415impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> {
@@ -431,7 +431,7 @@ impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> {
431 channel.start(); 431 channel.start();
432 432
433 Self { 433 Self {
434 channel: BusyChannel::new(channel), 434 channel: BusyPeripheral::new(channel),
435 } 435 }
436 } 436 }
437 437
@@ -508,7 +508,7 @@ impl<'a, const ITEM_COUNT: usize> Future for LinkedListTransfer<'a, ITEM_COUNT>
508/// DMA transfer. 508/// DMA transfer.
509#[must_use = "futures do nothing unless you `.await` or poll them"] 509#[must_use = "futures do nothing unless you `.await` or poll them"]
510pub struct Transfer<'a> { 510pub struct Transfer<'a> {
511 channel: BusyChannel<'a>, 511 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
512} 512}
513 513
514impl<'a> Transfer<'a> { 514impl<'a> Transfer<'a> {
@@ -629,7 +629,7 @@ impl<'a> Transfer<'a> {
629 channel.start(); 629 channel.start();
630 630
631 Self { 631 Self {
632 channel: BusyChannel::new(channel), 632 channel: BusyPeripheral::new(channel),
633 } 633 }
634 } 634 }
635 635
diff --git a/embassy-stm32/src/dma/gpdma/ringbuffered.rs b/embassy-stm32/src/dma/gpdma/ringbuffered.rs
index 54e4d5f71..c150d0b95 100644
--- a/embassy-stm32/src/dma/gpdma/ringbuffered.rs
+++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs
@@ -12,7 +12,8 @@ use super::{AnyChannel, STATE, TransferOptions};
12use crate::dma::gpdma::linked_list::{RunMode, Table}; 12use crate::dma::gpdma::linked_list::{RunMode, Table};
13use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; 13use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer};
14use crate::dma::word::Word; 14use crate::dma::word::Word;
15use crate::dma::{BusyChannel, Channel, Dir, Request}; 15use crate::dma::{Channel, Dir, Request};
16use crate::rcc::BusyPeripheral;
16 17
17struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); 18struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>);
18 19
@@ -49,7 +50,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> {
49 50
50/// Ringbuffer for receiving data using GPDMA linked-list mode. 51/// Ringbuffer for receiving data using GPDMA linked-list mode.
51pub struct ReadableRingBuffer<'a, W: Word> { 52pub struct ReadableRingBuffer<'a, W: Word> {
52 channel: BusyChannel<'a>, 53 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
53 ringbuf: ReadableDmaRingBuffer<'a, W>, 54 ringbuf: ReadableDmaRingBuffer<'a, W>,
54 table: Table<2>, 55 table: Table<2>,
55 options: TransferOptions, 56 options: TransferOptions,
@@ -70,7 +71,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> {
70 let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::PeripheralToMemory); 71 let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::PeripheralToMemory);
71 72
72 Self { 73 Self {
73 channel: BusyChannel::new(channel), 74 channel: BusyPeripheral::new(channel),
74 ringbuf: ReadableDmaRingBuffer::new(buffer), 75 ringbuf: ReadableDmaRingBuffer::new(buffer),
75 table, 76 table,
76 options, 77 options,
@@ -189,7 +190,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> {
189 190
190/// Ringbuffer for writing data using GPDMA linked-list mode. 191/// Ringbuffer for writing data using GPDMA linked-list mode.
191pub struct WritableRingBuffer<'a, W: Word> { 192pub struct WritableRingBuffer<'a, W: Word> {
192 channel: BusyChannel<'a>, 193 channel: BusyPeripheral<Peri<'a, AnyChannel>>,
193 ringbuf: WritableDmaRingBuffer<'a, W>, 194 ringbuf: WritableDmaRingBuffer<'a, W>,
194 table: Table<2>, 195 table: Table<2>,
195 options: TransferOptions, 196 options: TransferOptions,
@@ -210,7 +211,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> {
210 let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::MemoryToPeripheral); 211 let table = Table::<2>::new_ping_pong::<W>(request, peri_addr, buffer, Dir::MemoryToPeripheral);
211 212
212 Self { 213 Self {
213 channel: BusyChannel::new(channel), 214 channel: BusyPeripheral::new(channel),
214 ringbuf: WritableDmaRingBuffer::new(buffer), 215 ringbuf: WritableDmaRingBuffer::new(buffer),
215 table, 216 table,
216 options, 217 options,
diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs
index 4becc2d87..efb324fa6 100644
--- a/embassy-stm32/src/dma/mod.rs
+++ b/embassy-stm32/src/dma/mod.rs
@@ -3,14 +3,12 @@
3 3
4#[cfg(any(bdma, dma))] 4#[cfg(any(bdma, dma))]
5mod dma_bdma; 5mod dma_bdma;
6use core::ops;
7 6
8#[cfg(any(bdma, dma))] 7#[cfg(any(bdma, dma))]
9pub use dma_bdma::*; 8pub use dma_bdma::*;
10 9
11#[cfg(gpdma)] 10#[cfg(gpdma)]
12pub(crate) mod gpdma; 11pub(crate) mod gpdma;
13use embassy_hal_internal::Peri;
14#[cfg(gpdma)] 12#[cfg(gpdma)]
15pub use gpdma::ringbuffered::*; 13pub use gpdma::ringbuffered::*;
16#[cfg(gpdma)] 14#[cfg(gpdma)]
@@ -27,9 +25,10 @@ pub(crate) use util::*;
27pub(crate) mod ringbuffer; 25pub(crate) mod ringbuffer;
28pub mod word; 26pub mod word;
29 27
30use embassy_hal_internal::{PeripheralType, impl_peripheral}; 28use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral};
31 29
32use crate::interrupt; 30use crate::interrupt;
31use crate::rcc::StoppablePeripheral;
33 32
34/// The direction of a DMA transfer. 33/// The direction of a DMA transfer.
35#[derive(Debug, Copy, Clone, PartialEq, Eq)] 34#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -48,6 +47,13 @@ pub type Request = u8;
48#[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))] 47#[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))]
49pub type Request = (); 48pub type Request = ();
50 49
50impl<'a> StoppablePeripheral for Peri<'a, AnyChannel> {
51 #[cfg(feature = "low-power")]
52 fn stop_mode(&self) -> crate::rcc::StopMode {
53 self.stop_mode
54 }
55}
56
51pub(crate) trait SealedChannel { 57pub(crate) trait SealedChannel {
52 #[cfg(not(stm32n6))] 58 #[cfg(not(stm32n6))]
53 fn id(&self) -> u8; 59 fn id(&self) -> u8;
@@ -103,44 +109,6 @@ macro_rules! dma_channel_impl {
103 }; 109 };
104} 110}
105 111
106pub(crate) struct BusyChannel<'a> {
107 channel: Peri<'a, AnyChannel>,
108}
109
110impl<'a> BusyChannel<'a> {
111 pub fn new(channel: Peri<'a, AnyChannel>) -> Self {
112 #[cfg(feature = "low-power")]
113 critical_section::with(|cs| {
114 crate::rcc::increment_stop_refcount(cs, channel.stop_mode);
115 });
116
117 Self { channel }
118 }
119}
120
121impl<'a> Drop for BusyChannel<'a> {
122 fn drop(&mut self) {
123 #[cfg(feature = "low-power")]
124 critical_section::with(|cs| {
125 crate::rcc::decrement_stop_refcount(cs, self.stop_mode);
126 });
127 }
128}
129
130impl<'a> ops::Deref for BusyChannel<'a> {
131 type Target = Peri<'a, AnyChannel>;
132
133 fn deref(&self) -> &Self::Target {
134 &self.channel
135 }
136}
137
138impl<'a> ops::DerefMut for BusyChannel<'a> {
139 fn deref_mut(&mut self) -> &mut Self::Target {
140 &mut self.channel
141 }
142}
143
144/// Type-erased DMA channel. 112/// Type-erased DMA channel.
145pub struct AnyChannel { 113pub struct AnyChannel {
146 pub(crate) id: u8, 114 pub(crate) id: u8,
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index bd8290da0..cdf3323fb 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -51,7 +51,7 @@ use embassy_executor::*;
51 51
52use crate::interrupt; 52use crate::interrupt;
53pub use crate::rcc::StopMode; 53pub use crate::rcc::StopMode;
54use crate::rcc::{RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2, decrement_stop_refcount, increment_stop_refcount}; 54use crate::rcc::{BusyPeripheral, RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2};
55use crate::time_driver::get_driver; 55use crate::time_driver::get_driver;
56 56
57const THREAD_PENDER: usize = usize::MAX; 57const THREAD_PENDER: usize = usize::MAX;
@@ -59,7 +59,9 @@ const THREAD_PENDER: usize = usize::MAX;
59static mut EXECUTOR_TAKEN: bool = false; 59static mut EXECUTOR_TAKEN: bool = false;
60 60
61/// Prevent the device from going into the stop mode if held 61/// Prevent the device from going into the stop mode if held
62pub struct DeviceBusy(StopMode); 62pub struct DeviceBusy {
63 _stop_mode: BusyPeripheral<StopMode>,
64}
63 65
64impl DeviceBusy { 66impl DeviceBusy {
65 /// Create a new DeviceBusy with stop1. 67 /// Create a new DeviceBusy with stop1.
@@ -74,19 +76,9 @@ impl DeviceBusy {
74 76
75 /// Create a new DeviceBusy. 77 /// Create a new DeviceBusy.
76 pub fn new(stop_mode: StopMode) -> Self { 78 pub fn new(stop_mode: StopMode) -> Self {
77 critical_section::with(|cs| { 79 Self {
78 increment_stop_refcount(cs, stop_mode); 80 _stop_mode: BusyPeripheral::new(stop_mode),
79 }); 81 }
80
81 Self(stop_mode)
82 }
83}
84
85impl Drop for DeviceBusy {
86 fn drop(&mut self) {
87 critical_section::with(|cs| {
88 decrement_stop_refcount(cs, self.0);
89 });
90 } 82 }
91} 83}
92 84
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index 85434fa83..1dd634cfe 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -4,6 +4,7 @@
4#![allow(missing_docs)] // TODO 4#![allow(missing_docs)] // TODO
5 5
6use core::mem::MaybeUninit; 6use core::mem::MaybeUninit;
7use core::ops;
7 8
8mod bd; 9mod bd;
9pub use bd::*; 10pub use bd::*;
@@ -112,7 +113,7 @@ pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clo
112} 113}
113 114
114#[cfg(feature = "low-power")] 115#[cfg(feature = "low-power")]
115pub(crate) fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { 116fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) {
116 match stop_mode { 117 match stop_mode {
117 StopMode::Standby => {} 118 StopMode::Standby => {}
118 StopMode::Stop2 => unsafe { 119 StopMode::Stop2 => unsafe {
@@ -125,7 +126,7 @@ pub(crate) fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode)
125} 126}
126 127
127#[cfg(feature = "low-power")] 128#[cfg(feature = "low-power")]
128pub(crate) fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { 129fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) {
129 match stop_mode { 130 match stop_mode {
130 StopMode::Standby => {} 131 StopMode::Standby => {}
131 StopMode::Stop2 => unsafe { 132 StopMode::Stop2 => unsafe {
@@ -182,6 +183,12 @@ pub enum StopMode {
182 Standby, 183 Standby,
183} 184}
184 185
186#[cfg(feature = "low-power")]
187type BusyRccPeripheral = BusyPeripheral<StopMode>;
188
189#[cfg(not(feature = "low-power"))]
190type BusyRccPeripheral = ();
191
185impl RccInfo { 192impl RccInfo {
186 /// Safety: 193 /// Safety:
187 /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit 194 /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit
@@ -234,9 +241,6 @@ impl RccInfo {
234 } 241 }
235 } 242 }
236 243
237 #[cfg(feature = "low-power")]
238 increment_stop_refcount(_cs, self.stop_mode);
239
240 // set the xxxRST bit 244 // set the xxxRST bit
241 let reset_ptr = self.reset_ptr(); 245 let reset_ptr = self.reset_ptr();
242 if let Some(reset_ptr) = reset_ptr { 246 if let Some(reset_ptr) = reset_ptr {
@@ -292,9 +296,6 @@ impl RccInfo {
292 } 296 }
293 } 297 }
294 298
295 #[cfg(feature = "low-power")]
296 decrement_stop_refcount(_cs, self.stop_mode);
297
298 // clear the xxxEN bit 299 // clear the xxxEN bit
299 let enable_ptr = self.enable_ptr(); 300 let enable_ptr = self.enable_ptr();
300 unsafe { 301 unsafe {
@@ -303,16 +304,63 @@ impl RccInfo {
303 } 304 }
304 } 305 }
305 306
307 #[allow(dead_code)]
308 pub(crate) fn increment_stop_refcount_with_cs(&self, _cs: CriticalSection) {
309 #[cfg(feature = "low-power")]
310 increment_stop_refcount(_cs, self.stop_mode);
311 }
312
313 #[allow(dead_code)]
314 pub(crate) fn increment_stop_refcount(&self) {
315 #[cfg(feature = "low-power")]
316 critical_section::with(|cs| self.increment_stop_refcount_with_cs(cs))
317 }
318
319 #[allow(dead_code)]
320 pub(crate) fn decrement_stop_refcount_with_cs(&self, _cs: CriticalSection) {
321 #[cfg(feature = "low-power")]
322 decrement_stop_refcount(_cs, self.stop_mode);
323 }
324
325 #[allow(dead_code)]
326 pub(crate) fn decrement_stop_refcount(&self) {
327 #[cfg(feature = "low-power")]
328 critical_section::with(|cs| self.decrement_stop_refcount_with_cs(cs))
329 }
330
306 // TODO: should this be `unsafe`? 331 // TODO: should this be `unsafe`?
307 pub(crate) fn enable_and_reset(&self) { 332 pub(crate) fn enable_and_reset(&self) {
333 critical_section::with(|cs| {
334 self.enable_and_reset_with_cs(cs);
335 self.increment_stop_refcount_with_cs(cs);
336 })
337 }
338
339 #[allow(dead_code)]
340 pub(crate) fn enable_and_reset_without_stop(&self) {
308 critical_section::with(|cs| self.enable_and_reset_with_cs(cs)) 341 critical_section::with(|cs| self.enable_and_reset_with_cs(cs))
309 } 342 }
310 343
311 // TODO: should this be `unsafe`? 344 // TODO: should this be `unsafe`?
312 pub(crate) fn disable(&self) { 345 pub(crate) fn disable(&self) {
346 critical_section::with(|cs| {
347 self.disable_with_cs(cs);
348 self.decrement_stop_refcount_with_cs(cs);
349 })
350 }
351
352 // TODO: should this be `unsafe`?
353 #[allow(dead_code)]
354 pub(crate) fn disable_without_stop(&self) {
313 critical_section::with(|cs| self.disable_with_cs(cs)) 355 critical_section::with(|cs| self.disable_with_cs(cs))
314 } 356 }
315 357
358 #[allow(dead_code)]
359 pub(crate) fn block_stop(&self) -> BusyRccPeripheral {
360 #[cfg(feature = "low-power")]
361 BusyPeripheral::new(self.stop_mode)
362 }
363
316 fn reset_ptr(&self) -> Option<*mut u32> { 364 fn reset_ptr(&self) -> Option<*mut u32> {
317 if self.reset_offset_or_0xff != 0xff { 365 if self.reset_offset_or_0xff != 0xff {
318 Some(unsafe { (RCC.as_ptr() as *mut u32).add(self.reset_offset_or_0xff as _) }) 366 Some(unsafe { (RCC.as_ptr() as *mut u32).add(self.reset_offset_or_0xff as _) })
@@ -326,6 +374,53 @@ impl RccInfo {
326 } 374 }
327} 375}
328 376
377pub(crate) trait StoppablePeripheral {
378 #[cfg(feature = "low-power")]
379 #[allow(dead_code)]
380 fn stop_mode(&self) -> StopMode;
381}
382
383#[cfg(feature = "low-power")]
384impl<'a> StoppablePeripheral for StopMode {
385 fn stop_mode(&self) -> StopMode {
386 *self
387 }
388}
389
390pub(crate) struct BusyPeripheral<T: StoppablePeripheral> {
391 peripheral: T,
392}
393
394impl<T: StoppablePeripheral> BusyPeripheral<T> {
395 pub fn new(peripheral: T) -> Self {
396 #[cfg(feature = "low-power")]
397 critical_section::with(|cs| increment_stop_refcount(cs, peripheral.stop_mode()));
398
399 Self { peripheral }
400 }
401}
402
403impl<T: StoppablePeripheral> Drop for BusyPeripheral<T> {
404 fn drop(&mut self) {
405 #[cfg(feature = "low-power")]
406 critical_section::with(|cs| decrement_stop_refcount(cs, self.peripheral.stop_mode()));
407 }
408}
409
410impl<T: StoppablePeripheral> ops::Deref for BusyPeripheral<T> {
411 type Target = T;
412
413 fn deref(&self) -> &Self::Target {
414 &self.peripheral
415 }
416}
417
418impl<T: StoppablePeripheral> ops::DerefMut for BusyPeripheral<T> {
419 fn deref_mut(&mut self) -> &mut Self::Target {
420 &mut self.peripheral
421 }
422}
423
329#[allow(unused)] 424#[allow(unused)]
330mod util { 425mod util {
331 use crate::time::Hertz; 426 use crate::time::Hertz;
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index 0b75aef92..cfcf5f3fd 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -14,11 +14,11 @@ use stm32_metapac::timer::{TimGp16, regs};
14 14
15use crate::interrupt::typelevel::Interrupt; 15use crate::interrupt::typelevel::Interrupt;
16use crate::pac::timer::vals; 16use crate::pac::timer::vals;
17use crate::peripherals;
17use crate::rcc::{self, SealedRccPeripheral}; 18use crate::rcc::{self, SealedRccPeripheral};
18#[cfg(feature = "low-power")] 19#[cfg(feature = "low-power")]
19use crate::rtc::Rtc; 20use crate::rtc::Rtc;
20use crate::timer::{CoreInstance, GeneralInstance1Channel}; 21use crate::timer::{CoreInstance, GeneralInstance1Channel};
21use crate::{interrupt, peripherals};
22 22
23// NOTE regarding ALARM_COUNT: 23// NOTE regarding ALARM_COUNT:
24// 24//
@@ -56,121 +56,6 @@ type T = peripherals::TIM23;
56#[cfg(time_driver_tim24)] 56#[cfg(time_driver_tim24)]
57type T = peripherals::TIM24; 57type T = peripherals::TIM24;
58 58
59foreach_interrupt! {
60 (TIM1, timer, $block:ident, CC, $irq:ident) => {
61 #[cfg(time_driver_tim1)]
62 #[cfg(feature = "rt")]
63 #[interrupt]
64 fn $irq() {
65 DRIVER.on_interrupt()
66 }
67 };
68 (TIM2, timer, $block:ident, CC, $irq:ident) => {
69 #[cfg(time_driver_tim2)]
70 #[cfg(feature = "rt")]
71 #[interrupt]
72 fn $irq() {
73 DRIVER.on_interrupt()
74 }
75 };
76 (TIM3, timer, $block:ident, CC, $irq:ident) => {
77 #[cfg(time_driver_tim3)]
78 #[cfg(feature = "rt")]
79 #[interrupt]
80 fn $irq() {
81 DRIVER.on_interrupt()
82 }
83 };
84 (TIM4, timer, $block:ident, CC, $irq:ident) => {
85 #[cfg(time_driver_tim4)]
86 #[cfg(feature = "rt")]
87 #[interrupt]
88 fn $irq() {
89 DRIVER.on_interrupt()
90 }
91 };
92 (TIM5, timer, $block:ident, CC, $irq:ident) => {
93 #[cfg(time_driver_tim5)]
94 #[cfg(feature = "rt")]
95 #[interrupt]
96 fn $irq() {
97 DRIVER.on_interrupt()
98 }
99 };
100 (TIM8, timer, $block:ident, CC, $irq:ident) => {
101 #[cfg(time_driver_tim8)]
102 #[cfg(feature = "rt")]
103 #[interrupt]
104 fn $irq() {
105 DRIVER.on_interrupt()
106 }
107 };
108 (TIM9, timer, $block:ident, CC, $irq:ident) => {
109 #[cfg(time_driver_tim9)]
110 #[cfg(feature = "rt")]
111 #[interrupt]
112 fn $irq() {
113 DRIVER.on_interrupt()
114 }
115 };
116 (TIM12, timer, $block:ident, CC, $irq:ident) => {
117 #[cfg(time_driver_tim12)]
118 #[cfg(feature = "rt")]
119 #[interrupt]
120 fn $irq() {
121 DRIVER.on_interrupt()
122 }
123 };
124 (TIM15, timer, $block:ident, CC, $irq:ident) => {
125 #[cfg(time_driver_tim15)]
126 #[cfg(feature = "rt")]
127 #[interrupt]
128 fn $irq() {
129 DRIVER.on_interrupt()
130 }
131 };
132 (TIM20, timer, $block:ident, CC, $irq:ident) => {
133 #[cfg(time_driver_tim20)]
134 #[cfg(feature = "rt")]
135 #[interrupt]
136 fn $irq() {
137 DRIVER.on_interrupt()
138 }
139 };
140 (TIM21, timer, $block:ident, CC, $irq:ident) => {
141 #[cfg(time_driver_tim21)]
142 #[cfg(feature = "rt")]
143 #[interrupt]
144 fn $irq() {
145 DRIVER.on_interrupt()
146 }
147 };
148 (TIM22, timer, $block:ident, CC, $irq:ident) => {
149 #[cfg(time_driver_tim22)]
150 #[cfg(feature = "rt")]
151 #[interrupt]
152 fn $irq() {
153 DRIVER.on_interrupt()
154 }
155 };
156 (TIM23, timer, $block:ident, CC, $irq:ident) => {
157 #[cfg(time_driver_tim23)]
158 #[cfg(feature = "rt")]
159 #[interrupt]
160 fn $irq() {
161 DRIVER.on_interrupt()
162 }
163 };
164 (TIM24, timer, $block:ident, CC, $irq:ident) => {
165 #[cfg(time_driver_tim24)]
166 #[cfg(feature = "rt")]
167 #[interrupt]
168 fn $irq() {
169 DRIVER.on_interrupt()
170 }
171 };
172}
173
174fn regs_gp16() -> TimGp16 { 59fn regs_gp16() -> TimGp16 {
175 unsafe { TimGp16::from_ptr(T::regs()) } 60 unsafe { TimGp16::from_ptr(T::regs()) }
176} 61}
@@ -282,7 +167,11 @@ impl RtcDriver {
282 r.cnt().write(|w| w.set_cnt(self.saved_count.load(Ordering::SeqCst))); 167 r.cnt().write(|w| w.set_cnt(self.saved_count.load(Ordering::SeqCst)));
283 168
284 <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend(); 169 <T as GeneralInstance1Channel>::CaptureCompareInterrupt::unpend();
285 unsafe { <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable() }; 170 <T as CoreInstance>::UpdateInterrupt::unpend();
171 unsafe {
172 <T as GeneralInstance1Channel>::CaptureCompareInterrupt::enable();
173 <T as CoreInstance>::UpdateInterrupt::enable();
174 }
286 } 175 }
287 176
288 fn init(&'static self, cs: CriticalSection) { 177 fn init(&'static self, cs: CriticalSection) {
@@ -290,7 +179,7 @@ impl RtcDriver {
290 regs_gp16().cr1().modify(|w| w.set_cen(true)); 179 regs_gp16().cr1().modify(|w| w.set_cen(true));
291 } 180 }
292 181
293 fn on_interrupt(&self) { 182 pub(crate) fn on_interrupt(&self) {
294 let r = regs_gp16(); 183 let r = regs_gp16();
295 184
296 critical_section::with(|cs| { 185 critical_section::with(|cs| {
@@ -508,7 +397,6 @@ impl Driver for RtcDriver {
508 } 397 }
509} 398}
510 399
511#[cfg(feature = "low-power")]
512pub(crate) const fn get_driver() -> &'static RtcDriver { 400pub(crate) const fn get_driver() -> &'static RtcDriver {
513 &DRIVER 401 &DRIVER
514} 402}
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index 0e7da634d..8047d6005 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -491,6 +491,8 @@ impl<'d> UartTx<'d, Async> {
491 491
492 /// Initiate an asynchronous UART write 492 /// Initiate an asynchronous UART write
493 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { 493 pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
494 let _ = self.info.rcc.block_stop();
495
494 let r = self.info.regs; 496 let r = self.info.regs;
495 497
496 half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback)); 498 half_duplex_set_rx_tx_before_write(&r, self.duplex == Duplex::Half(HalfDuplexReadback::Readback));
@@ -508,6 +510,8 @@ impl<'d> UartTx<'d, Async> {
508 510
509 /// Wait until transmission complete 511 /// Wait until transmission complete
510 pub async fn flush(&mut self) -> Result<(), Error> { 512 pub async fn flush(&mut self) -> Result<(), Error> {
513 let _ = self.info.rcc.block_stop();
514
511 flush(&self.info, &self.state).await 515 flush(&self.info, &self.state).await
512 } 516 }
513} 517}
@@ -569,7 +573,7 @@ impl<'d, M: Mode> UartTx<'d, M> {
569 let state = self.state; 573 let state = self.state;
570 state.tx_rx_refcount.store(1, Ordering::Relaxed); 574 state.tx_rx_refcount.store(1, Ordering::Relaxed);
571 575
572 info.rcc.enable_and_reset(); 576 info.rcc.enable_and_reset_without_stop();
573 577
574 info.regs.cr3().modify(|w| { 578 info.regs.cr3().modify(|w| {
575 w.set_ctse(self.cts.is_some()); 579 w.set_ctse(self.cts.is_some());
@@ -726,6 +730,8 @@ impl<'d> UartRx<'d, Async> {
726 730
727 /// Initiate an asynchronous UART read 731 /// Initiate an asynchronous UART read
728 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { 732 pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
733 let _ = self.info.rcc.block_stop();
734
729 self.inner_read(buffer, false).await?; 735 self.inner_read(buffer, false).await?;
730 736
731 Ok(()) 737 Ok(())
@@ -733,6 +739,8 @@ impl<'d> UartRx<'d, Async> {
733 739
734 /// Initiate an asynchronous read with idle line detection enabled 740 /// Initiate an asynchronous read with idle line detection enabled
735 pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { 741 pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
742 let _ = self.info.rcc.block_stop();
743
736 self.inner_read(buffer, true).await 744 self.inner_read(buffer, true).await
737 } 745 }
738 746
@@ -1004,7 +1012,7 @@ impl<'d, M: Mode> UartRx<'d, M> {
1004 .eager_reads 1012 .eager_reads
1005 .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); 1013 .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);
1006 1014
1007 info.rcc.enable_and_reset(); 1015 info.rcc.enable_and_reset_without_stop();
1008 1016
1009 info.regs.cr3().write(|w| { 1017 info.regs.cr3().write(|w| {
1010 w.set_rtse(self.rts.is_some()); 1018 w.set_rtse(self.rts.is_some());
@@ -1143,7 +1151,7 @@ fn drop_tx_rx(info: &Info, state: &State) {
1143 refcount == 1 1151 refcount == 1
1144 }); 1152 });
1145 if is_last_drop { 1153 if is_last_drop {
1146 info.rcc.disable(); 1154 info.rcc.disable_without_stop();
1147 } 1155 }
1148} 1156}
1149 1157
@@ -1506,7 +1514,7 @@ impl<'d, M: Mode> Uart<'d, M> {
1506 .eager_reads 1514 .eager_reads
1507 .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed); 1515 .store(config.eager_reads.unwrap_or(0), Ordering::Relaxed);
1508 1516
1509 info.rcc.enable_and_reset(); 1517 info.rcc.enable_and_reset_without_stop();
1510 1518
1511 info.regs.cr3().write(|w| { 1519 info.regs.cr3().write(|w| {
1512 w.set_rtse(self.rx.rts.is_some()); 1520 w.set_rtse(self.rx.rts.is_some());
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs
index bac570d27..cc5224b69 100644
--- a/embassy-stm32/src/usart/ringbuffered.rs
+++ b/embassy-stm32/src/usart/ringbuffered.rs
@@ -117,6 +117,8 @@ impl<'d> UartRx<'d, Async> {
117 let rx = unsafe { self.rx.as_ref().map(|x| x.clone_unchecked()) }; 117 let rx = unsafe { self.rx.as_ref().map(|x| x.clone_unchecked()) };
118 let rts = unsafe { self.rts.as_ref().map(|x| x.clone_unchecked()) }; 118 let rts = unsafe { self.rts.as_ref().map(|x| x.clone_unchecked()) };
119 119
120 info.rcc.increment_stop_refcount();
121
120 // Don't disable the clock 122 // Don't disable the clock
121 mem::forget(self); 123 mem::forget(self);
122 124
@@ -324,6 +326,7 @@ impl<'d> RingBufferedUartRx<'d> {
324 326
325impl Drop for RingBufferedUartRx<'_> { 327impl Drop for RingBufferedUartRx<'_> {
326 fn drop(&mut self) { 328 fn drop(&mut self) {
329 self.info.rcc.decrement_stop_refcount();
327 self.stop_uart(); 330 self.stop_uart();
328 self.rx.as_ref().map(|x| x.set_as_disconnected()); 331 self.rx.as_ref().map(|x| x.set_as_disconnected());
329 self.rts.as_ref().map(|x| x.set_as_disconnected()); 332 self.rts.as_ref().map(|x| x.set_as_disconnected());
diff --git a/embassy-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md
index cfb1bf021..3dd71ffbc 100644
--- a/embassy-usb/CHANGELOG.md
+++ b/embassy-usb/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- Add support for USB HID Boot Protocol Mode 11- Add support for USB HID Boot Protocol Mode
12- Bump usbd-hid from 0.8.1 to 0.9.0
12 13
13## 0.5.1 - 2025-08-26 14## 0.5.1 - 2025-08-26
14 15
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml
index aeb7392f1..3d1e005e4 100644
--- a/embassy-usb/Cargo.toml
+++ b/embassy-usb/Cargo.toml
@@ -69,5 +69,5 @@ heapless = "0.8"
69embedded-io-async = "0.6.1" 69embedded-io-async = "0.6.1"
70 70
71# for HID 71# for HID
72usbd-hid = { version = "0.8.1", optional = true } 72usbd-hid = { version = "0.9.0", optional = true }
73ssmarshal = { version = "1.0", default-features = false, optional = true } 73ssmarshal = { version = "1.0", default-features = false, optional = true }
diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml
index 70a2c28c3..292d1abec 100644
--- a/examples/boot/application/rp/Cargo.toml
+++ b/examples/boot/application/rp/Cargo.toml
@@ -9,8 +9,8 @@ publish = false
9embassy-sync = { version = "0.7.2", path = "../../../../embassy-sync" } 9embassy-sync = { version = "0.7.2", path = "../../../../embassy-sync" }
10embassy-executor = { version = "0.9.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] } 10embassy-executor = { version = "0.9.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread"] }
11embassy-time = { version = "0.5.0", path = "../../../../embassy-time", features = [] } 11embassy-time = { version = "0.5.0", path = "../../../../embassy-time", features = [] }
12embassy-rp = { version = "0.8.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } 12embassy-rp = { version = "0.9.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] }
13embassy-boot-rp = { version = "0.8.0", path = "../../../../embassy-boot-rp", features = [] } 13embassy-boot-rp = { version = "0.9.0", path = "../../../../embassy-boot-rp", features = [] }
14embassy-embedded-hal = { version = "0.5.0", path = "../../../../embassy-embedded-hal" } 14embassy-embedded-hal = { version = "0.5.0", path = "../../../../embassy-embedded-hal" }
15 15
16defmt = "1.0.1" 16defmt = "1.0.1"
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index a026d6352..1fe3d2419 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -28,7 +28,7 @@ cortex-m-rt = "0.7.0"
28panic-probe = { version = "1.0.0", features = ["print-defmt"] } 28panic-probe = { version = "1.0.0", features = ["print-defmt"] }
29rand = { version = "0.9.0", default-features = false } 29rand = { version = "0.9.0", default-features = false }
30embedded-storage = "0.3.1" 30embedded-storage = "0.3.1"
31usbd-hid = "0.8.1" 31usbd-hid = "0.9.0"
32serde = { version = "1.0.136", default-features = false } 32serde = { version = "1.0.136", default-features = false }
33embedded-hal = { version = "1.0" } 33embedded-hal = { version = "1.0" }
34embedded-hal-async = { version = "1.0" } 34embedded-hal-async = { version = "1.0" }
diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml
index 4dcbdd715..97efe58e8 100644
--- a/examples/nrf5340/Cargo.toml
+++ b/examples/nrf5340/Cargo.toml
@@ -23,7 +23,7 @@ cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-sing
23cortex-m-rt = "0.7.0" 23cortex-m-rt = "0.7.0"
24panic-probe = { version = "1.0.0", features = ["print-defmt"] } 24panic-probe = { version = "1.0.0", features = ["print-defmt"] }
25embedded-storage = "0.3.1" 25embedded-storage = "0.3.1"
26usbd-hid = "0.8.1" 26usbd-hid = "0.9.0"
27serde = { version = "1.0.136", default-features = false } 27serde = { version = "1.0.136", default-features = false }
28 28
29[profile.release] 29[profile.release]
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}
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 640addb28..e247f6f7a 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -11,14 +11,14 @@ embassy-embedded-hal = { version = "0.5.0", path = "../../embassy-embedded-hal",
11embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } 11embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
12embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 12embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
13embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 13embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
14embassy-rp = { version = "0.8.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } 14embassy-rp = { version = "0.9.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] }
15embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } 15embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] }
16embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] } 16embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] }
17embassy-net-wiznet = { version = "0.2.1", path = "../../embassy-net-wiznet", features = ["defmt"] } 17embassy-net-wiznet = { version = "0.2.1", path = "../../embassy-net-wiznet", features = ["defmt"] }
18embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } 18embassy-futures = { version = "0.1.2", path = "../../embassy-futures" }
19embassy-usb-logger = { version = "0.5.1", path = "../../embassy-usb-logger" } 19embassy-usb-logger = { version = "0.5.1", path = "../../embassy-usb-logger" }
20cyw43 = { version = "0.5.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } 20cyw43 = { version = "0.6.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] }
21cyw43-pio = { version = "0.8.0", path = "../../cyw43-pio", features = ["defmt"] } 21cyw43-pio = { version = "0.9.0", path = "../../cyw43-pio", features = ["defmt"] }
22 22
23defmt = "1.0.1" 23defmt = "1.0.1"
24defmt-rtt = "1.0.0" 24defmt-rtt = "1.0.0"
@@ -45,7 +45,7 @@ display-interface = "0.5.0"
45byte-slice-cast = { version = "1.2.0", default-features = false } 45byte-slice-cast = { version = "1.2.0", default-features = false }
46smart-leds = "0.4.0" 46smart-leds = "0.4.0"
47heapless = "0.8" 47heapless = "0.8"
48usbd-hid = "0.8.1" 48usbd-hid = "0.9.0"
49 49
50embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 50embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
51embedded-hal-async = "1.0" 51embedded-hal-async = "1.0"
diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml
index 39a4f421a..16dfb5b77 100644
--- a/examples/rp235x/Cargo.toml
+++ b/examples/rp235x/Cargo.toml
@@ -11,14 +11,14 @@ embassy-embedded-hal = { version = "0.5.0", path = "../../embassy-embedded-hal",
11embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } 11embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
12embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } 12embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
13embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 13embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
14embassy-rp = { version = "0.8.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } 14embassy-rp = { version = "0.9.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] }
15embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } 15embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] }
16embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } 16embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "icmp", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] }
17embassy-net-wiznet = { version = "0.2.1", path = "../../embassy-net-wiznet", features = ["defmt"] } 17embassy-net-wiznet = { version = "0.2.1", path = "../../embassy-net-wiznet", features = ["defmt"] }
18embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } 18embassy-futures = { version = "0.1.2", path = "../../embassy-futures" }
19embassy-usb-logger = { version = "0.5.1", path = "../../embassy-usb-logger" } 19embassy-usb-logger = { version = "0.5.1", path = "../../embassy-usb-logger" }
20cyw43 = { version = "0.5.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } 20cyw43 = { version = "0.6.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] }
21cyw43-pio = { version = "0.8.0", path = "../../cyw43-pio", features = ["defmt"] } 21cyw43-pio = { version = "0.9.0", path = "../../cyw43-pio", features = ["defmt"] }
22 22
23defmt = "1.0.1" 23defmt = "1.0.1"
24defmt-rtt = "1.0.0" 24defmt-rtt = "1.0.0"
@@ -46,7 +46,7 @@ display-interface = "0.5.0"
46byte-slice-cast = { version = "1.2.0", default-features = false } 46byte-slice-cast = { version = "1.2.0", default-features = false }
47smart-leds = "0.3.0" 47smart-leds = "0.3.0"
48heapless = "0.8" 48heapless = "0.8"
49usbd-hid = "0.8.1" 49usbd-hid = "0.9.0"
50 50
51embedded-hal-1 = { package = "embedded-hal", version = "1.0" } 51embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
52embedded-hal-async = "1.0" 52embedded-hal-async = "1.0"
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml
index d06b7505c..b4555045a 100644
--- a/examples/stm32f4/Cargo.toml
+++ b/examples/stm32f4/Cargo.toml
@@ -32,7 +32,7 @@ critical-section = "1.1"
32nb = "1.0.0" 32nb = "1.0.0"
33embedded-storage = "0.3.1" 33embedded-storage = "0.3.1"
34micromath = "2.0.0" 34micromath = "2.0.0"
35usbd-hid = "0.8.1" 35usbd-hid = "0.9.0"
36static_cell = "2" 36static_cell = "2"
37chrono = { version = "^0.4", default-features = false} 37chrono = { version = "^0.4", default-features = false}
38 38
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml
index 8bbeb594c..d1c19582b 100644
--- a/examples/stm32g4/Cargo.toml
+++ b/examples/stm32g4/Cargo.toml
@@ -13,7 +13,7 @@ embassy-executor = { path = "../../embassy-executor", features = ["arch-cortex-m
13embassy-time = { path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 13embassy-time = { path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
14embassy-usb = { path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { path = "../../embassy-usb", features = ["defmt"] }
15embassy-futures = { path = "../../embassy-futures" } 15embassy-futures = { path = "../../embassy-futures" }
16usbd-hid = "0.8.1" 16usbd-hid = "0.9.0"
17 17
18defmt = "1.0.1" 18defmt = "1.0.1"
19defmt-rtt = "1.0.0" 19defmt-rtt = "1.0.0"
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml
index b6158c854..586b00836 100644
--- a/examples/stm32l5/Cargo.toml
+++ b/examples/stm32l5/Cargo.toml
@@ -14,7 +14,7 @@ embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["de
14embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } 15embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
16embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } 16embassy-futures = { version = "0.1.2", path = "../../embassy-futures" }
17usbd-hid = "0.8.1" 17usbd-hid = "0.9.0"
18 18
19defmt = "1.0.1" 19defmt = "1.0.1"
20defmt-rtt = "1.0.0" 20defmt-rtt = "1.0.0"
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml
index 640e58f11..4dfe6904e 100644
--- a/tests/rp/Cargo.toml
+++ b/tests/rp/Cargo.toml
@@ -16,7 +16,7 @@ teleprobe-meta = "1.1"
16embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } 16embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] }
17embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 17embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
18embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", ] } 18embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", ] }
19embassy-rp = { version = "0.8.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } 19embassy-rp = { version = "0.9.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] }
20embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } 20embassy-futures = { version = "0.1.2", path = "../../embassy-futures" }
21embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } 21embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] }
22embassy-net-wiznet = { version = "0.2.1", path = "../../embassy-net-wiznet", features = ["defmt"] } 22embassy-net-wiznet = { version = "0.2.1", path = "../../embassy-net-wiznet", features = ["defmt"] }