aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElias Hanelt <[email protected]>2025-11-26 10:11:20 -0800
committerElias Hanelt <[email protected]>2025-11-26 10:13:59 -0800
commit006a0873d8efb9620841759f2c15450dc9ae044e (patch)
treed2b4282599893fd2f988772e51af7637453d9190
parent88ce8314c5cd653ac245cc5dbf367f6814f24be9 (diff)
parent9879f8de1e5c1af80218d61837ed71d7c19c3fe8 (diff)
Merge remote-tracking branch 'origin/main' into i2c-slave/fix-early-term
-rwxr-xr-xci.sh1
-rw-r--r--embassy-rp/CHANGELOG.md2
-rw-r--r--embassy-rp/Cargo.toml3
-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.md2
-rw-r--r--embassy-stm32/build.rs49
-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/spi/mod.rs46
-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/nrf52840/Cargo.toml2
-rw-r--r--examples/nrf5340/Cargo.toml2
-rw-r--r--examples/rp/Cargo.toml2
-rw-r--r--examples/rp235x/Cargo.toml2
-rw-r--r--examples/stm32f4/Cargo.toml2
-rw-r--r--examples/stm32g4/Cargo.toml2
-rw-r--r--examples/stm32l5/Cargo.toml2
27 files changed, 295 insertions, 284 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/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md
index fa8609bbf..7480b729f 100644
--- a/embassy-rp/CHANGELOG.md
+++ b/embassy-rp/CHANGELOG.md
@@ -19,6 +19,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)) 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))
20- Add PIO::Ws2812 color order support 20- Add PIO::Ws2812 color order support
21- Add TX-only, no SCK SPI support 21- Add TX-only, no SCK SPI support
22- Remove atomic-polyfill with critical-section instead ([#4948](https://github.com/embassy-rs/embassy/pull/4948))
22 23
23## 0.8.0 - 2025-08-26 24## 0.8.0 - 2025-08-26
24 25
@@ -116,4 +117,3 @@ Small release fixing a few gnarly bugs, upgrading is strongly recommended.
116- rename the Channel trait to Slice and the PwmPin to PwmChannel 117- rename the Channel trait to Slice and the PwmPin to PwmChannel
117- i2c: Fix race condition that appears on fast repeated transfers. 118- i2c: Fix race condition that appears on fast repeated transfers.
118- Add a basic "read to break" function 119- Add a basic "read to break" function
119
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index 9ad4b47a3..421f0b0f6 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -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..4c38b0add 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
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/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/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index 89553f129..abb80ed26 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -54,16 +54,6 @@ pub enum BitOrder {
54 MsbFirst, 54 MsbFirst,
55} 55}
56 56
57/// SPI Direction.
58#[derive(Debug, PartialEq, Eq, Clone, Copy)]
59#[cfg_attr(feature = "defmt", derive(defmt::Format))]
60pub enum Direction {
61 /// Transmit
62 Transmit,
63 /// Receive
64 Receive,
65}
66
67/// SPI configuration. 57/// SPI configuration.
68#[non_exhaustive] 58#[non_exhaustive]
69#[derive(Copy, Clone)] 59#[derive(Copy, Clone)]
@@ -358,19 +348,6 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
358 Ok(()) 348 Ok(())
359 } 349 }
360 350
361 /// Set SPI direction
362 pub fn set_direction(&mut self, dir: Option<Direction>) {
363 let (bidimode, bidioe) = match dir {
364 Some(Direction::Transmit) => (vals::Bidimode::BIDIRECTIONAL, vals::Bidioe::TRANSMIT),
365 Some(Direction::Receive) => (vals::Bidimode::BIDIRECTIONAL, vals::Bidioe::RECEIVE),
366 None => (vals::Bidimode::UNIDIRECTIONAL, vals::Bidioe::TRANSMIT),
367 };
368 self.info.regs.cr1().modify(|w| {
369 w.set_bidimode(bidimode);
370 w.set_bidioe(bidioe);
371 });
372 }
373
374 /// Get current SPI configuration. 351 /// Get current SPI configuration.
375 pub fn get_current_config(&self) -> Config { 352 pub fn get_current_config(&self) -> Config {
376 #[cfg(any(spi_v1, spi_v2, spi_v3))] 353 #[cfg(any(spi_v1, spi_v2, spi_v3))]
@@ -731,29 +708,6 @@ impl<'d> Spi<'d, Async, Master> {
731 ) 708 )
732 } 709 }
733 710
734 /// Create a new SPI driver, in bidirectional mode
735 pub fn new_bidi<T: Instance, #[cfg(afio)] A>(
736 peri: Peri<'d, T>,
737 sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,
738 sdio: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
739 tx_dma: Peri<'d, impl TxDma<T>>,
740 rx_dma: Peri<'d, impl RxDma<T>>,
741 config: Config,
742 ) -> Self {
743 let mut this = Self::new_inner(
744 peri,
745 new_pin!(sck, config.sck_af()),
746 new_pin!(sdio, AfType::output(OutputType::PushPull, config.gpio_speed)),
747 None,
748 None,
749 new_dma!(tx_dma),
750 new_dma!(rx_dma),
751 config,
752 );
753 this.set_direction(Some(Direction::Transmit));
754 this
755 }
756
757 /// Create a new SPI driver, in TX-only mode, without SCK pin. 711 /// Create a new SPI driver, in TX-only mode, without SCK pin.
758 /// 712 ///
759 /// This can be useful for bit-banging non-SPI protocols. 713 /// This can be useful for bit-banging non-SPI protocols.
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/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/rp/Cargo.toml b/examples/rp/Cargo.toml
index 640addb28..9d7d99259 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -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..ad396275b 100644
--- a/examples/rp235x/Cargo.toml
+++ b/examples/rp235x/Cargo.toml
@@ -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"