aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf/CHANGELOG.md1
-rw-r--r--embassy-nrf/src/spis.rs15
-rw-r--r--embassy-rp/CHANGELOG.md3
-rw-r--r--embassy-rp/Cargo.toml3
-rw-r--r--embassy-rp/src/pio/mod.rs54
-rw-r--r--embassy-rp/src/spi.rs25
-rw-r--r--embassy-rp/src/uart/buffered.rs13
-rw-r--r--embassy-rp/src/uart/mod.rs28
-rw-r--r--embassy-stm32/CHANGELOG.md6
-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/hspi/mod.rs93
-rw-r--r--embassy-stm32/src/i2c/v2.rs11
-rw-r--r--embassy-stm32/src/low_power.rs22
-rw-r--r--embassy-stm32/src/ospi/mod.rs60
-rw-r--r--embassy-stm32/src/rcc/mod.rs111
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs17
-rw-r--r--embassy-stm32/src/timer/low_level.rs207
-rw-r--r--embassy-stm32/src/timer/mod.rs1
-rw-r--r--embassy-stm32/src/timer/ringbuffered.rs169
-rw-r--r--embassy-stm32/src/timer/simple_pwm.rs45
-rw-r--r--embassy-stm32/src/usart/mod.rs16
-rw-r--r--embassy-stm32/src/usart/ringbuffered.rs3
-rw-r--r--embassy-stm32/src/xspi/mod.rs98
-rw-r--r--embassy-time-driver/CHANGELOG.md3
-rw-r--r--embassy-time-driver/Cargo.toml2
-rw-r--r--embassy-time-driver/gen_tick.py3
-rw-r--r--embassy-time-driver/src/tick.rs3
-rw-r--r--embassy-time/CHANGELOG.md1
-rw-r--r--embassy-time/Cargo.toml2
-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/stm32f7/src/bin/pwm.rs61
-rw-r--r--examples/stm32f7/src/bin/pwm_ringbuffer.rs153
-rw-r--r--examples/stm32g4/Cargo.toml2
-rw-r--r--examples/stm32l5/Cargo.toml2
43 files changed, 955 insertions, 377 deletions
diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md
index be79bde5d..cfb040ef5 100644
--- a/embassy-nrf/CHANGELOG.md
+++ b/embassy-nrf/CHANGELOG.md
@@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
28- changed: `gpiote::InputChannel::wait()` now ensures events are seen as soon as the function is called, even if the future is not polled 28- changed: `gpiote::InputChannel::wait()` now ensures events are seen as soon as the function is called, even if the future is not polled
29- bugfix: use correct flash size for nRF54l 29- bugfix: use correct flash size for nRF54l
30- changed: add workaround for anomaly 66 on nrf52 30- changed: add workaround for anomaly 66 on nrf52
31- added: expose PPI events available on SPIS peripheral
31 32
32## 0.8.0 - 2025-09-30 33## 0.8.0 - 2025-09-30
33 34
diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs
index 96a9c0ae0..6f837c317 100644
--- a/embassy-nrf/src/spis.rs
+++ b/embassy-nrf/src/spis.rs
@@ -17,6 +17,7 @@ use crate::gpio::{self, AnyPin, OutputDrive, Pin as GpioPin, SealedPin as _, con
17use crate::interrupt::typelevel::Interrupt; 17use crate::interrupt::typelevel::Interrupt;
18use crate::pac::gpio::vals as gpiovals; 18use crate::pac::gpio::vals as gpiovals;
19use crate::pac::spis::vals; 19use crate::pac::spis::vals;
20use crate::ppi::Event;
20use crate::util::slice_in_ram_or; 21use crate::util::slice_in_ram_or;
21use crate::{interrupt, pac}; 22use crate::{interrupt, pac};
22 23
@@ -334,6 +335,20 @@ impl<'d> Spis<'d> {
334 Ok((n_rx, n_tx)) 335 Ok((n_rx, n_tx))
335 } 336 }
336 337
338 /// Returns the ACQUIRED event, for use with PPI.
339 ///
340 /// This event will fire when the semaphore is acquired.
341 pub fn event_acquired(&self) -> Event<'d> {
342 Event::from_reg(self.r.events_acquired())
343 }
344
345 /// Returns the END event, for use with PPI.
346 ///
347 /// This event will fire when the slave transaction is complete.
348 pub fn event_end(&self) -> Event<'d> {
349 Event::from_reg(self.r.events_end())
350 }
351
337 async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { 352 async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> {
338 match self.async_inner_from_ram(rx, tx).await { 353 match self.async_inner_from_ram(rx, tx).await {
339 Ok(n) => Ok(n), 354 Ok(n) => Ok(n),
diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md
index 4b0d738a7..7480b729f 100644
--- a/embassy-rp/CHANGELOG.md
+++ b/embassy-rp/CHANGELOG.md
@@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
18- Add reset_to_usb_boot for rp235x ([#4705](https://github.com/embassy-rs/embassy/pull/4705)) 18- Add reset_to_usb_boot for rp235x ([#4705](https://github.com/embassy-rs/embassy/pull/4705))
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
22- Remove atomic-polyfill with critical-section instead ([#4948](https://github.com/embassy-rs/embassy/pull/4948))
21 23
22## 0.8.0 - 2025-08-26 24## 0.8.0 - 2025-08-26
23 25
@@ -115,4 +117,3 @@ Small release fixing a few gnarly bugs, upgrading is strongly recommended.
115- rename the Channel trait to Slice and the PwmPin to PwmChannel 117- rename the Channel trait to Slice and the PwmPin to PwmChannel
116- i2c: Fix race condition that appears on fast repeated transfers. 118- i2c: Fix race condition that appears on fast repeated transfers.
117- Add a basic "read to break" function 119- Add a basic "read to break" function
118
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/spi.rs b/embassy-rp/src/spi.rs
index d9410e78d..39f128214 100644
--- a/embassy-rp/src/spi.rs
+++ b/embassy-rp/src/spi.rs
@@ -308,6 +308,11 @@ impl<'d, T: Instance> Spi<'d, T, Blocking> {
308 ) 308 )
309 } 309 }
310 310
311 /// Create an SPI driver in blocking mode supporting writes only, without SCK pin.
312 pub fn new_blocking_txonly_nosck(inner: Peri<'d, T>, mosi: Peri<'d, impl MosiPin<T> + 'd>, config: Config) -> Self {
313 Self::new_inner(inner, None, Some(mosi.into()), None, None, None, None, config)
314 }
315
311 /// Create an SPI driver in blocking mode supporting reads only. 316 /// Create an SPI driver in blocking mode supporting reads only.
312 pub fn new_blocking_rxonly( 317 pub fn new_blocking_rxonly(
313 inner: Peri<'d, T>, 318 inner: Peri<'d, T>,
@@ -371,6 +376,26 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
371 ) 376 )
372 } 377 }
373 378
379 /// Create an SPI driver in async mode supporting DMA write operations only,
380 /// without SCK pin.
381 pub fn new_txonly_nosck(
382 inner: Peri<'d, T>,
383 mosi: Peri<'d, impl MosiPin<T> + 'd>,
384 tx_dma: Peri<'d, impl Channel>,
385 config: Config,
386 ) -> Self {
387 Self::new_inner(
388 inner,
389 None,
390 Some(mosi.into()),
391 None,
392 None,
393 Some(tx_dma.into()),
394 None,
395 config,
396 )
397 }
398
374 /// Create an SPI driver in async mode supporting DMA read operations only. 399 /// Create an SPI driver in async mode supporting DMA read operations only.
375 pub fn new_rxonly( 400 pub fn new_rxonly(
376 inner: Peri<'d, T>, 401 inner: Peri<'d, T>,
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 0f19b14b3..4c38b0add 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8## Unreleased - ReleaseDate 8## Unreleased - ReleaseDate
9 9
10- fix: fix incorrect handling of split interrupts in timer driver 10- fix: fix incorrect handling of split interrupts in timer driver
11- feat: allow granular stop for regular usart
12- feat: Add continuous waveform method to SimplePWM
13- change: remove waveform timer method
11- change: low power: store stop mode for dma channels 14- change: low power: store stop mode for dma channels
12- fix: Fixed ADC4 enable() for WBA 15- fix: Fixed ADC4 enable() for WBA
13- feat: allow use of anyadcchannel for adc4 16- feat: allow use of anyadcchannel for adc4
@@ -41,7 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
41- feat: Configurable gpio speed for QSPI 44- feat: Configurable gpio speed for QSPI
42- feat: derive Clone, Copy and defmt::Format for all *SPI-related configs 45- feat: derive Clone, Copy and defmt::Format for all *SPI-related configs
43- fix: handle address and data-length errors in OSPI 46- fix: handle address and data-length errors in OSPI
44- feat: Allow OSPI DMA writes larger than 64kB using chunking 47- feat: Allow OSPI/HSPI/XSPI DMA writes larger than 64kB using chunking
45- feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times 48- feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times
46- feat: Add USB CRS sync support for STM32C071 49- feat: Add USB CRS sync support for STM32C071
47- fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map. 50- fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map.
@@ -52,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
52- feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) 55- feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668))
53- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options 56- feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options
54- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer 57- change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer
58- fix: Properly set the transfer size for OSPI/HSPI/XSPI transfers with word sizes other than 8 bits.
55- fix: stm32/adc: Calculate the ADC prescaler in a way that it allows for the max frequency to be reached 59- fix: stm32/adc: Calculate the ADC prescaler in a way that it allows for the max frequency to be reached
56- fix: Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696) 60- fix: Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696)
57- change: timer: added output compare values 61- change: timer: added output compare values
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/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs
index 69baa708e..1d3560678 100644
--- a/embassy-stm32/src/hspi/mod.rs
+++ b/embassy-stm32/src/hspi/mod.rs
@@ -391,7 +391,7 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> {
391 while T::REGS.sr().read().busy() {} 391 while T::REGS.sr().read().busy() {}
392 392
393 T::REGS.cr().modify(|w| { 393 T::REGS.cr().modify(|w| {
394 w.set_fmode(0.into()); 394 w.set_fmode(FunctionalMode::IndirectWrite.into());
395 }); 395 });
396 396
397 // Configure alternate bytes 397 // Configure alternate bytes
@@ -498,7 +498,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> {
498 w.set_dmaen(false); 498 w.set_dmaen(false);
499 }); 499 });
500 500
501 self.configure_command(&transaction, Some(buf.len()))?; 501 let transfer_size_bytes = buf.len() * W::size().bytes();
502 self.configure_command(&transaction, Some(transfer_size_bytes))?;
502 503
503 let current_address = T::REGS.ar().read().address(); 504 let current_address = T::REGS.ar().read().address();
504 let current_instruction = T::REGS.ir().read().instruction(); 505 let current_instruction = T::REGS.ir().read().instruction();
@@ -537,7 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> {
537 w.set_dmaen(false); 538 w.set_dmaen(false);
538 }); 539 });
539 540
540 self.configure_command(&transaction, Some(buf.len()))?; 541 let transfer_size_bytes = buf.len() * W::size().bytes();
542 self.configure_command(&transaction, Some(transfer_size_bytes))?;
541 543
542 T::REGS 544 T::REGS
543 .cr() 545 .cr()
@@ -767,7 +769,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> {
767 // Wait for peripheral to be free 769 // Wait for peripheral to be free
768 while T::REGS.sr().read().busy() {} 770 while T::REGS.sr().read().busy() {}
769 771
770 self.configure_command(&transaction, Some(buf.len()))?; 772 let transfer_size_bytes = buf.len() * W::size().bytes();
773 self.configure_command(&transaction, Some(transfer_size_bytes))?;
771 774
772 let current_address = T::REGS.ar().read().address(); 775 let current_address = T::REGS.ar().read().address();
773 let current_instruction = T::REGS.ir().read().instruction(); 776 let current_instruction = T::REGS.ir().read().instruction();
@@ -782,16 +785,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> {
782 T::REGS.ar().write(|v| v.set_address(current_address)); 785 T::REGS.ar().write(|v| v.set_address(current_address));
783 } 786 }
784 787
785 let transfer = unsafe { 788 for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {
786 self.dma 789 let transfer = unsafe {
787 .as_mut() 790 self.dma
788 .unwrap() 791 .as_mut()
789 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) 792 .unwrap()
790 }; 793 .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())
794 };
791 795
792 T::REGS.cr().modify(|w| w.set_dmaen(true)); 796 T::REGS.cr().modify(|w| w.set_dmaen(true));
793 797
794 transfer.blocking_wait(); 798 transfer.blocking_wait();
799 }
795 800
796 finish_dma(T::REGS); 801 finish_dma(T::REGS);
797 802
@@ -807,21 +812,24 @@ impl<'d, T: Instance> Hspi<'d, T, Async> {
807 // Wait for peripheral to be free 812 // Wait for peripheral to be free
808 while T::REGS.sr().read().busy() {} 813 while T::REGS.sr().read().busy() {}
809 814
810 self.configure_command(&transaction, Some(buf.len()))?; 815 let transfer_size_bytes = buf.len() * W::size().bytes();
816 self.configure_command(&transaction, Some(transfer_size_bytes))?;
811 T::REGS 817 T::REGS
812 .cr() 818 .cr()
813 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); 819 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into()));
814 820
815 let transfer = unsafe { 821 for chunk in buf.chunks(0xFFFF / W::size().bytes()) {
816 self.dma 822 let transfer = unsafe {
817 .as_mut() 823 self.dma
818 .unwrap() 824 .as_mut()
819 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) 825 .unwrap()
820 }; 826 .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())
827 };
821 828
822 T::REGS.cr().modify(|w| w.set_dmaen(true)); 829 T::REGS.cr().modify(|w| w.set_dmaen(true));
823 830
824 transfer.blocking_wait(); 831 transfer.blocking_wait();
832 }
825 833
826 finish_dma(T::REGS); 834 finish_dma(T::REGS);
827 835
@@ -837,7 +845,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> {
837 // Wait for peripheral to be free 845 // Wait for peripheral to be free
838 while T::REGS.sr().read().busy() {} 846 while T::REGS.sr().read().busy() {}
839 847
840 self.configure_command(&transaction, Some(buf.len()))?; 848 let transfer_size_bytes = buf.len() * W::size().bytes();
849 self.configure_command(&transaction, Some(transfer_size_bytes))?;
841 850
842 let current_address = T::REGS.ar().read().address(); 851 let current_address = T::REGS.ar().read().address();
843 let current_instruction = T::REGS.ir().read().instruction(); 852 let current_instruction = T::REGS.ir().read().instruction();
@@ -852,16 +861,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> {
852 T::REGS.ar().write(|v| v.set_address(current_address)); 861 T::REGS.ar().write(|v| v.set_address(current_address));
853 } 862 }
854 863
855 let transfer = unsafe { 864 for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {
856 self.dma 865 let transfer = unsafe {
857 .as_mut() 866 self.dma
858 .unwrap() 867 .as_mut()
859 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) 868 .unwrap()
860 }; 869 .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())
870 };
861 871
862 T::REGS.cr().modify(|w| w.set_dmaen(true)); 872 T::REGS.cr().modify(|w| w.set_dmaen(true));
863 873
864 transfer.await; 874 transfer.await;
875 }
865 876
866 finish_dma(T::REGS); 877 finish_dma(T::REGS);
867 878
@@ -877,21 +888,25 @@ impl<'d, T: Instance> Hspi<'d, T, Async> {
877 // Wait for peripheral to be free 888 // Wait for peripheral to be free
878 while T::REGS.sr().read().busy() {} 889 while T::REGS.sr().read().busy() {}
879 890
880 self.configure_command(&transaction, Some(buf.len()))?; 891 let transfer_size_bytes = buf.len() * W::size().bytes();
892 self.configure_command(&transaction, Some(transfer_size_bytes))?;
881 T::REGS 893 T::REGS
882 .cr() 894 .cr()
883 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); 895 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into()));
884 896
885 let transfer = unsafe { 897 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.
886 self.dma 898 for chunk in buf.chunks(0xFFFF / W::size().bytes()) {
887 .as_mut() 899 let transfer = unsafe {
888 .unwrap() 900 self.dma
889 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) 901 .as_mut()
890 }; 902 .unwrap()
903 .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())
904 };
891 905
892 T::REGS.cr().modify(|w| w.set_dmaen(true)); 906 T::REGS.cr().modify(|w| w.set_dmaen(true));
893 907
894 transfer.await; 908 transfer.await;
909 }
895 910
896 finish_dma(T::REGS); 911 finish_dma(T::REGS);
897 912
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 61e550ad4..b2ba94e21 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -1075,8 +1075,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1075 1075
1076 /// Write. 1076 /// Write.
1077 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { 1077 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
1078 #[cfg(all(feature = "low-power", stm32wlex))]
1079 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
1080 let timeout = self.timeout(); 1078 let timeout = self.timeout();
1081 if write.is_empty() { 1079 if write.is_empty() {
1082 self.write_internal(address.into(), write, true, timeout) 1080 self.write_internal(address.into(), write, true, timeout)
@@ -1091,8 +1089,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1091 /// 1089 ///
1092 /// The buffers are concatenated in a single write transaction. 1090 /// The buffers are concatenated in a single write transaction.
1093 pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { 1091 pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> {
1094 #[cfg(all(feature = "low-power", stm32wlex))]
1095 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
1096 let timeout = self.timeout(); 1092 let timeout = self.timeout();
1097 1093
1098 if write.is_empty() { 1094 if write.is_empty() {
@@ -1124,8 +1120,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1124 1120
1125 /// Read. 1121 /// Read.
1126 pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { 1122 pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
1127 #[cfg(all(feature = "low-power", stm32wlex))]
1128 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
1129 let timeout = self.timeout(); 1123 let timeout = self.timeout();
1130 1124
1131 if buffer.is_empty() { 1125 if buffer.is_empty() {
@@ -1138,8 +1132,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1138 1132
1139 /// Write, restart, read. 1133 /// Write, restart, read.
1140 pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { 1134 pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
1141 #[cfg(all(feature = "low-power", stm32wlex))]
1142 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
1143 let timeout = self.timeout(); 1135 let timeout = self.timeout();
1144 1136
1145 if write.is_empty() { 1137 if write.is_empty() {
@@ -1165,9 +1157,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
1165 /// 1157 ///
1166 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction 1158 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
1167 pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { 1159 pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
1168 #[cfg(all(feature = "low-power", stm32wlex))]
1169 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
1170
1171 if operations.is_empty() { 1160 if operations.is_empty() {
1172 return Err(Error::ZeroLengthTransfer); 1161 return Err(Error::ZeroLengthTransfer);
1173 } 1162 }
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/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs
index 592a8594a..2d5dbd95a 100644
--- a/embassy-stm32/src/ospi/mod.rs
+++ b/embassy-stm32/src/ospi/mod.rs
@@ -451,7 +451,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> {
451 } 451 }
452 452
453 T::REGS.cr().modify(|w| { 453 T::REGS.cr().modify(|w| {
454 w.set_fmode(0.into()); 454 w.set_fmode(vals::FunctionalMode::INDIRECT_WRITE);
455 }); 455 });
456 456
457 // Configure alternate bytes 457 // Configure alternate bytes
@@ -577,7 +577,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> {
577 w.set_dmaen(false); 577 w.set_dmaen(false);
578 }); 578 });
579 579
580 self.configure_command(&transaction, Some(buf.len()))?; 580 let transfer_size_bytes = buf.len() * W::size().bytes();
581 self.configure_command(&transaction, Some(transfer_size_bytes))?;
581 582
582 let current_address = T::REGS.ar().read().address(); 583 let current_address = T::REGS.ar().read().address();
583 let current_instruction = T::REGS.ir().read().instruction(); 584 let current_instruction = T::REGS.ir().read().instruction();
@@ -616,7 +617,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> {
616 w.set_dmaen(false); 617 w.set_dmaen(false);
617 }); 618 });
618 619
619 self.configure_command(&transaction, Some(buf.len()))?; 620 let transfer_size_bytes = buf.len() * W::size().bytes();
621 self.configure_command(&transaction, Some(transfer_size_bytes))?;
620 622
621 T::REGS 623 T::REGS
622 .cr() 624 .cr()
@@ -1153,7 +1155,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1153 // Wait for peripheral to be free 1155 // Wait for peripheral to be free
1154 while T::REGS.sr().read().busy() {} 1156 while T::REGS.sr().read().busy() {}
1155 1157
1156 self.configure_command(&transaction, Some(buf.len()))?; 1158 let transfer_size_bytes = buf.len() * W::size().bytes();
1159 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1157 1160
1158 let current_address = T::REGS.ar().read().address(); 1161 let current_address = T::REGS.ar().read().address();
1159 let current_instruction = T::REGS.ir().read().instruction(); 1162 let current_instruction = T::REGS.ir().read().instruction();
@@ -1168,16 +1171,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1168 T::REGS.ar().write(|v| v.set_address(current_address)); 1171 T::REGS.ar().write(|v| v.set_address(current_address));
1169 } 1172 }
1170 1173
1171 let transfer = unsafe { 1174 for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {
1172 self.dma 1175 let transfer = unsafe {
1173 .as_mut() 1176 self.dma
1174 .unwrap() 1177 .as_mut()
1175 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) 1178 .unwrap()
1176 }; 1179 .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())
1180 };
1177 1181
1178 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1182 T::REGS.cr().modify(|w| w.set_dmaen(true));
1179 1183
1180 transfer.blocking_wait(); 1184 transfer.blocking_wait();
1185 }
1181 1186
1182 finish_dma(T::REGS); 1187 finish_dma(T::REGS);
1183 1188
@@ -1193,13 +1198,14 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1193 // Wait for peripheral to be free 1198 // Wait for peripheral to be free
1194 while T::REGS.sr().read().busy() {} 1199 while T::REGS.sr().read().busy() {}
1195 1200
1196 self.configure_command(&transaction, Some(buf.len()))?; 1201 let transfer_size_bytes = buf.len() * W::size().bytes();
1202 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1197 T::REGS 1203 T::REGS
1198 .cr() 1204 .cr()
1199 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); 1205 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE));
1200 1206
1201 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. 1207 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.
1202 for chunk in buf.chunks(0xFFFF) { 1208 for chunk in buf.chunks(0xFFFF / W::size().bytes()) {
1203 let transfer = unsafe { 1209 let transfer = unsafe {
1204 self.dma 1210 self.dma
1205 .as_mut() 1211 .as_mut()
@@ -1226,7 +1232,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1226 // Wait for peripheral to be free 1232 // Wait for peripheral to be free
1227 while T::REGS.sr().read().busy() {} 1233 while T::REGS.sr().read().busy() {}
1228 1234
1229 self.configure_command(&transaction, Some(buf.len()))?; 1235 let transfer_size_bytes = buf.len() * W::size().bytes();
1236 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1230 1237
1231 let current_address = T::REGS.ar().read().address(); 1238 let current_address = T::REGS.ar().read().address();
1232 let current_instruction = T::REGS.ir().read().instruction(); 1239 let current_instruction = T::REGS.ir().read().instruction();
@@ -1241,16 +1248,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1241 T::REGS.ar().write(|v| v.set_address(current_address)); 1248 T::REGS.ar().write(|v| v.set_address(current_address));
1242 } 1249 }
1243 1250
1244 let transfer = unsafe { 1251 for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {
1245 self.dma 1252 let transfer = unsafe {
1246 .as_mut() 1253 self.dma
1247 .unwrap() 1254 .as_mut()
1248 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) 1255 .unwrap()
1249 }; 1256 .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())
1257 };
1250 1258
1251 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1259 T::REGS.cr().modify(|w| w.set_dmaen(true));
1252 1260
1253 transfer.await; 1261 transfer.await;
1262 }
1254 1263
1255 finish_dma(T::REGS); 1264 finish_dma(T::REGS);
1256 1265
@@ -1266,13 +1275,14 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
1266 // Wait for peripheral to be free 1275 // Wait for peripheral to be free
1267 while T::REGS.sr().read().busy() {} 1276 while T::REGS.sr().read().busy() {}
1268 1277
1269 self.configure_command(&transaction, Some(buf.len()))?; 1278 let transfer_size_bytes = buf.len() * W::size().bytes();
1279 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1270 T::REGS 1280 T::REGS
1271 .cr() 1281 .cr()
1272 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); 1282 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE));
1273 1283
1274 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. 1284 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.
1275 for chunk in buf.chunks(0xFFFF) { 1285 for chunk in buf.chunks(0xFFFF / W::size().bytes()) {
1276 let transfer = unsafe { 1286 let transfer = unsafe {
1277 self.dma 1287 self.dma
1278 .as_mut() 1288 .as_mut()
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/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index 6d4c70dff..77f19a37b 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -220,9 +220,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
220 /// 220 ///
221 /// Note: 221 /// Note:
222 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 222 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
223 #[inline(always)]
224 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 223 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) {
225 self.inner.waveform_up(dma, channel, duty).await 224 self.inner.enable_channel(channel, true);
225 self.inner.enable_update_dma(true);
226 self.inner.setup_update_dma(dma, channel, duty).await;
227 self.inner.enable_update_dma(false);
226 } 228 }
227 229
228 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. 230 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
@@ -254,7 +256,6 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
254 /// Also be aware that embassy timers use one of timers internally. It is possible to 256 /// Also be aware that embassy timers use one of timers internally. It is possible to
255 /// switch this timer by using `time-driver-timX` feature. 257 /// switch this timer by using `time-driver-timX` feature.
256 /// 258 ///
257 #[inline(always)]
258 pub async fn waveform_up_multi_channel( 259 pub async fn waveform_up_multi_channel(
259 &mut self, 260 &mut self,
260 dma: Peri<'_, impl super::UpDma<T>>, 261 dma: Peri<'_, impl super::UpDma<T>>,
@@ -262,15 +263,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
262 ending_channel: Channel, 263 ending_channel: Channel,
263 duty: &[u16], 264 duty: &[u16],
264 ) { 265 ) {
266 self.inner.enable_update_dma(true);
265 self.inner 267 self.inner
266 .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) 268 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty)
267 .await; 269 .await;
268 } 270 self.inner.enable_update_dma(false);
269
270 /// Generate a sequence of PWM waveform
271 #[inline(always)]
272 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
273 self.inner.waveform(dma, duty).await;
274 } 271 }
275} 272}
276 273
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs
index f0105ece8..aba08081f 100644
--- a/embassy-stm32/src/timer/low_level.rs
+++ b/embassy-stm32/src/timer/low_level.rs
@@ -13,9 +13,10 @@ use embassy_hal_internal::Peri;
13pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; 13pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource};
14 14
15use super::*; 15use super::*;
16use crate::dma::{Transfer, WritableRingBuffer};
16use crate::pac::timer::vals; 17use crate::pac::timer::vals;
18use crate::rcc;
17use crate::time::Hertz; 19use crate::time::Hertz;
18use crate::{dma, rcc};
19 20
20/// Input capture mode. 21/// Input capture mode.
21#[derive(Clone, Copy)] 22#[derive(Clone, Copy)]
@@ -659,29 +660,88 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
659 } 660 }
660 } 661 }
661 662
663 /// Setup a ring buffer for the channel
664 pub fn setup_ring_buffer<'a>(
665 &mut self,
666 dma: Peri<'a, impl super::UpDma<T>>,
667 channel: Channel,
668 dma_buf: &'a mut [u16],
669 ) -> WritableRingBuffer<'a, u16> {
670 #[allow(clippy::let_unit_value)] // eg. stm32f334
671 let req = dma.request();
672
673 unsafe {
674 use crate::dma::TransferOptions;
675 #[cfg(not(any(bdma, gpdma)))]
676 use crate::dma::{Burst, FifoThreshold};
677
678 let dma_transfer_option = TransferOptions {
679 #[cfg(not(any(bdma, gpdma)))]
680 fifo_threshold: Some(FifoThreshold::Full),
681 #[cfg(not(any(bdma, gpdma)))]
682 mburst: Burst::Incr8,
683 ..Default::default()
684 };
685
686 WritableRingBuffer::new(
687 dma,
688 req,
689 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
690 dma_buf,
691 dma_transfer_option,
692 )
693 }
694 }
695
662 /// Generate a sequence of PWM waveform 696 /// Generate a sequence of PWM waveform
663 /// 697 ///
664 /// Note: 698 /// Note:
665 /// you will need to provide corresponding TIMx_UP DMA channel to use this method. 699 /// you will need to provide corresponding TIMx_UP DMA channel to use this method.
666 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 700 pub fn setup_update_dma<'a>(
701 &mut self,
702 dma: Peri<'a, impl super::UpDma<T>>,
703 channel: Channel,
704 duty: &'a [u16],
705 ) -> Transfer<'a> {
667 #[allow(clippy::let_unit_value)] // eg. stm32f334 706 #[allow(clippy::let_unit_value)] // eg. stm32f334
668 let req = dma.request(); 707 let req = dma.request();
669 708
670 let original_update_dma_state = self.get_update_dma_state(); 709 unsafe {
710 #[cfg(not(any(bdma, gpdma)))]
711 use crate::dma::{Burst, FifoThreshold};
712 use crate::dma::{Transfer, TransferOptions};
671 713
672 if !original_update_dma_state { 714 let dma_transfer_option = TransferOptions {
673 self.enable_update_dma(true); 715 #[cfg(not(any(bdma, gpdma)))]
674 } 716 fifo_threshold: Some(FifoThreshold::Full),
717 #[cfg(not(any(bdma, gpdma)))]
718 mburst: Burst::Incr8,
719 ..Default::default()
720 };
675 721
676 self.waveform_helper(dma, req, channel, duty).await; 722 match self.bits() {
723 TimerBits::Bits16 => Transfer::new_write(
724 dma,
725 req,
726 duty,
727 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
728 dma_transfer_option,
729 ),
730 #[cfg(not(any(stm32l0)))]
731 TimerBits::Bits32 => {
732 #[cfg(not(any(bdma, gpdma)))]
733 panic!("unsupported timer bits");
677 734
678 // Since DMA is closed before timer update event trigger DMA is turn off, 735 #[cfg(any(bdma, gpdma))]
679 // this can almost always trigger a DMA FIFO error. 736 Transfer::new_write(
680 // 737 dma,
681 // optional TODO: 738 req,
682 // clean FEIF after disable UDE 739 duty,
683 if !original_update_dma_state { 740 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32,
684 self.enable_update_dma(false); 741 dma_transfer_option,
742 )
743 }
744 }
685 } 745 }
686 } 746 }
687 747
@@ -714,13 +774,13 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
714 /// Also be aware that embassy timers use one of timers internally. It is possible to 774 /// Also be aware that embassy timers use one of timers internally. It is possible to
715 /// switch this timer by using `time-driver-timX` feature. 775 /// switch this timer by using `time-driver-timX` feature.
716 /// 776 ///
717 pub async fn waveform_up_multi_channel( 777 pub fn setup_update_dma_burst<'a>(
718 &mut self, 778 &mut self,
719 dma: Peri<'_, impl super::UpDma<T>>, 779 dma: Peri<'a, impl super::UpDma<T>>,
720 starting_channel: Channel, 780 starting_channel: Channel,
721 ending_channel: Channel, 781 ending_channel: Channel,
722 duty: &[u16], 782 duty: &'a [u16],
723 ) { 783 ) -> Transfer<'a> {
724 let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; 784 let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32;
725 let start_ch_index = starting_channel.index(); 785 let start_ch_index = starting_channel.index();
726 let end_ch_index = ending_channel.index(); 786 let end_ch_index = ending_channel.index();
@@ -738,11 +798,6 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
738 #[allow(clippy::let_unit_value)] // eg. stm32f334 798 #[allow(clippy::let_unit_value)] // eg. stm32f334
739 let req = dma.request(); 799 let req = dma.request();
740 800
741 let original_update_dma_state = self.get_update_dma_state();
742 if !original_update_dma_state {
743 self.enable_update_dma(true);
744 }
745
746 unsafe { 801 unsafe {
747 #[cfg(not(any(bdma, gpdma)))] 802 #[cfg(not(any(bdma, gpdma)))]
748 use crate::dma::{Burst, FifoThreshold}; 803 use crate::dma::{Burst, FifoThreshold};
@@ -763,115 +818,9 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
763 self.regs_gp16().dmar().as_ptr() as *mut u16, 818 self.regs_gp16().dmar().as_ptr() as *mut u16,
764 dma_transfer_option, 819 dma_transfer_option,
765 ) 820 )
766 .await
767 };
768
769 if !original_update_dma_state {
770 self.enable_update_dma(false);
771 } 821 }
772 } 822 }
773 823
774 /// Generate a sequence of PWM waveform
775 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
776 use crate::pac::timer::vals::Ccds;
777
778 #[allow(clippy::let_unit_value)] // eg. stm32f334
779 let req = dma.request();
780
781 let cc_channel = C::CHANNEL;
782
783 let original_cc_dma_on_update = self.get_cc_dma_selection() == Ccds::ON_UPDATE;
784 let original_cc_dma_enabled = self.get_cc_dma_enable_state(cc_channel);
785
786 // redirect CC DMA request onto Update Event
787 if !original_cc_dma_on_update {
788 self.set_cc_dma_selection(Ccds::ON_UPDATE)
789 }
790
791 if !original_cc_dma_enabled {
792 self.set_cc_dma_enable_state(cc_channel, true);
793 }
794
795 self.waveform_helper(dma, req, cc_channel, duty).await;
796
797 // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off,
798 // this can almost always trigger a DMA FIFO error.
799 //
800 // optional TODO:
801 // clean FEIF after disable UDE
802 if !original_cc_dma_enabled {
803 self.set_cc_dma_enable_state(cc_channel, false);
804 }
805
806 if !original_cc_dma_on_update {
807 self.set_cc_dma_selection(Ccds::ON_COMPARE)
808 }
809 }
810
811 async fn waveform_helper(
812 &mut self,
813 dma: Peri<'_, impl dma::Channel>,
814 req: dma::Request,
815 channel: Channel,
816 duty: &[u16],
817 ) {
818 let original_duty_state = self.get_compare_value(channel);
819 let original_enable_state = self.get_channel_enable_state(channel);
820
821 if !original_enable_state {
822 self.enable_channel(channel, true);
823 }
824
825 unsafe {
826 #[cfg(not(any(bdma, gpdma)))]
827 use crate::dma::{Burst, FifoThreshold};
828 use crate::dma::{Transfer, TransferOptions};
829
830 let dma_transfer_option = TransferOptions {
831 #[cfg(not(any(bdma, gpdma)))]
832 fifo_threshold: Some(FifoThreshold::Full),
833 #[cfg(not(any(bdma, gpdma)))]
834 mburst: Burst::Incr8,
835 ..Default::default()
836 };
837
838 match self.bits() {
839 TimerBits::Bits16 => {
840 Transfer::new_write(
841 dma,
842 req,
843 duty,
844 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16,
845 dma_transfer_option,
846 )
847 .await
848 }
849 #[cfg(not(any(stm32l0)))]
850 TimerBits::Bits32 => {
851 #[cfg(not(any(bdma, gpdma)))]
852 panic!("unsupported timer bits");
853
854 #[cfg(any(bdma, gpdma))]
855 Transfer::new_write(
856 dma,
857 req,
858 duty,
859 self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32,
860 dma_transfer_option,
861 )
862 .await
863 }
864 };
865 };
866
867 // restore output compare state
868 if !original_enable_state {
869 self.enable_channel(channel, false);
870 }
871
872 self.set_compare_value(channel, original_duty_state);
873 }
874
875 /// Get capture value for a channel. 824 /// Get capture value for a channel.
876 pub fn get_capture_value(&self, channel: Channel) -> u32 { 825 pub fn get_capture_value(&self, channel: Channel) -> u32 {
877 self.get_compare_value(channel) 826 self.get_compare_value(channel)
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
index 804d1ef37..3fa363881 100644
--- a/embassy-stm32/src/timer/mod.rs
+++ b/embassy-stm32/src/timer/mod.rs
@@ -12,6 +12,7 @@ pub mod low_level;
12pub mod one_pulse; 12pub mod one_pulse;
13pub mod pwm_input; 13pub mod pwm_input;
14pub mod qei; 14pub mod qei;
15pub mod ringbuffered;
15pub mod simple_pwm; 16pub mod simple_pwm;
16 17
17use crate::interrupt; 18use crate::interrupt;
diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs
new file mode 100644
index 000000000..e8f97bf59
--- /dev/null
+++ b/embassy-stm32/src/timer/ringbuffered.rs
@@ -0,0 +1,169 @@
1//! RingBuffered PWM driver.
2
3use core::mem::ManuallyDrop;
4use core::task::Waker;
5
6use super::low_level::Timer;
7use super::{Channel, GeneralInstance4Channel};
8use crate::dma::WritableRingBuffer;
9use crate::dma::ringbuffer::Error;
10
11/// A PWM channel that uses a DMA ring buffer for continuous waveform generation.
12///
13/// This allows you to continuously update PWM duty cycles via DMA without blocking the CPU.
14/// The ring buffer enables smooth, uninterrupted waveform generation by automatically cycling
15/// through duty cycle values stored in memory.
16///
17/// You can write new duty cycle values to the ring buffer while it's running, enabling
18/// dynamic waveform generation for applications like motor control, LED dimming, or audio output.
19///
20/// # Example
21/// ```ignore
22/// let mut channel = pwm.ch1().into_ring_buffered_channel(dma_ch, &mut buffer);
23/// channel.start(); // Start DMA transfer
24/// channel.write(&[100, 200, 300]).ok(); // Update duty cycles
25/// ```
26pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> {
27 timer: ManuallyDrop<Timer<'d, T>>,
28 ring_buf: WritableRingBuffer<'d, u16>,
29 channel: Channel,
30}
31
32impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> {
33 pub(crate) fn new(
34 timer: ManuallyDrop<Timer<'d, T>>,
35 channel: Channel,
36 ring_buf: WritableRingBuffer<'d, u16>,
37 ) -> Self {
38 Self {
39 timer,
40 ring_buf,
41 channel,
42 }
43 }
44
45 /// Start the ring buffer operation.
46 ///
47 /// You must call this after creating it for it to work.
48 pub fn start(&mut self) {
49 self.ring_buf.start()
50 }
51
52 /// Clear all data in the ring buffer.
53 pub fn clear(&mut self) {
54 self.ring_buf.clear()
55 }
56
57 /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer.
58 pub fn write_immediate(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> {
59 self.ring_buf.write_immediate(buf)
60 }
61
62 /// Write elements from the ring buffer
63 /// Return a tuple of the length written and the length remaining in the buffer
64 pub fn write(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> {
65 self.ring_buf.write(buf)
66 }
67
68 /// Write an exact number of elements to the ringbuffer.
69 pub async fn write_exact(&mut self, buffer: &[u16]) -> Result<usize, Error> {
70 self.ring_buf.write_exact(buffer).await
71 }
72
73 /// Wait for any ring buffer write error.
74 pub async fn wait_write_error(&mut self) -> Result<usize, Error> {
75 self.ring_buf.wait_write_error().await
76 }
77
78 /// The current length of the ringbuffer
79 pub fn len(&mut self) -> Result<usize, Error> {
80 self.ring_buf.len()
81 }
82
83 /// The capacity of the ringbuffer
84 pub const fn capacity(&self) -> usize {
85 self.ring_buf.capacity()
86 }
87
88 /// Set a waker to be woken when at least one byte is send.
89 pub fn set_waker(&mut self, waker: &Waker) {
90 self.ring_buf.set_waker(waker)
91 }
92
93 /// Request the DMA to reset. The configuration for this channel will not be preserved.
94 ///
95 /// This doesn't immediately stop the transfer, you have to wait until is_running returns false.
96 pub fn request_reset(&mut self) {
97 self.ring_buf.request_reset()
98 }
99
100 /// Request the transfer to pause, keeping the existing configuration for this channel.
101 /// To restart the transfer, call [`start`](Self::start) again.
102 ///
103 /// This doesn't immediately stop the transfer, you have to wait until is_running returns false.
104 pub fn request_pause(&mut self) {
105 self.ring_buf.request_pause()
106 }
107
108 /// Return whether DMA is still running.
109 ///
110 /// If this returns false, it can be because either the transfer finished, or it was requested to stop early with request_stop.
111 pub fn is_running(&mut self) -> bool {
112 self.ring_buf.is_running()
113 }
114
115 /// Stop the DMA transfer and await until the buffer is empty.
116 ///
117 /// This disables the DMA transfer's circular mode so that the transfer stops when all available data has been written.
118 ///
119 /// This is designed to be used with streaming output data such as the I2S/SAI or DAC.
120 pub async fn stop(&mut self) {
121 self.ring_buf.stop().await
122 }
123
124 /// Enable the given channel.
125 pub fn enable(&mut self) {
126 self.timer.enable_channel(self.channel, true);
127 }
128
129 /// Disable the given channel.
130 pub fn disable(&mut self) {
131 self.timer.enable_channel(self.channel, false);
132 }
133
134 /// Check whether given channel is enabled
135 pub fn is_enabled(&self) -> bool {
136 self.timer.get_channel_enable_state(self.channel)
137 }
138
139 /// Get max duty value.
140 ///
141 /// This value depends on the configured frequency and the timer's clock rate from RCC.
142 pub fn max_duty_cycle(&self) -> u16 {
143 let max = self.timer.get_max_compare_value();
144 assert!(max < u16::MAX as u32);
145 max as u16 + 1
146 }
147
148 /// Set the output polarity for a given channel.
149 pub fn set_polarity(&mut self, polarity: super::low_level::OutputPolarity) {
150 self.timer.set_output_polarity(self.channel, polarity);
151 }
152
153 /// Set the output compare mode for a given channel.
154 pub fn set_output_compare_mode(&mut self, mode: super::low_level::OutputCompareMode) {
155 self.timer.set_output_compare_mode(self.channel, mode);
156 }
157}
158
159/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`].
160pub struct RingBufferedPwmChannels<'d, T: GeneralInstance4Channel> {
161 /// Channel 1
162 pub ch1: RingBufferedPwmChannel<'d, T>,
163 /// Channel 2
164 pub ch2: RingBufferedPwmChannel<'d, T>,
165 /// Channel 3
166 pub ch3: RingBufferedPwmChannel<'d, T>,
167 /// Channel 4
168 pub ch4: RingBufferedPwmChannel<'d, T>,
169}
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs
index 6c9ef17e0..484e9fd81 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -4,6 +4,7 @@ use core::marker::PhantomData;
4use core::mem::ManuallyDrop; 4use core::mem::ManuallyDrop;
5 5
6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; 6use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
7use super::ringbuffered::RingBufferedPwmChannel;
7use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; 8use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin};
8use crate::Peri; 9use crate::Peri;
9#[cfg(gpio_v2)] 10#[cfg(gpio_v2)]
@@ -158,6 +159,33 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> {
158 pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { 159 pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) {
159 self.timer.set_output_compare_mode(self.channel, mode); 160 self.timer.set_output_compare_mode(self.channel, mode);
160 } 161 }
162
163 /// Convert this PWM channel into a ring-buffered PWM channel.
164 ///
165 /// This allows continuous PWM waveform generation using a DMA ring buffer.
166 /// The ring buffer enables dynamic updates to the PWM duty cycle without blocking.
167 ///
168 /// # Arguments
169 /// * `tx_dma` - The DMA channel to use for transferring duty cycle values
170 /// * `dma_buf` - The buffer to use as a ring buffer (must be non-empty and <= 65535 elements)
171 ///
172 /// # Panics
173 /// Panics if `dma_buf` is empty or longer than 65535 elements.
174 pub fn into_ring_buffered_channel(
175 mut self,
176 tx_dma: Peri<'d, impl super::UpDma<T>>,
177 dma_buf: &'d mut [u16],
178 ) -> RingBufferedPwmChannel<'d, T> {
179 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
180
181 self.timer.enable_update_dma(true);
182
183 RingBufferedPwmChannel::new(
184 unsafe { self.timer.clone_unchecked() },
185 self.channel,
186 self.timer.setup_ring_buffer(tx_dma, self.channel, dma_buf),
187 )
188 }
161} 189}
162 190
163/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. 191/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`].
@@ -316,9 +344,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
316 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. 344 /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method.
317 /// Also be aware that embassy timers use one of timers internally. It is possible to 345 /// Also be aware that embassy timers use one of timers internally. It is possible to
318 /// switch this timer by using `time-driver-timX` feature. 346 /// switch this timer by using `time-driver-timX` feature.
319 #[inline(always)]
320 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) { 347 pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma<T>>, channel: Channel, duty: &[u16]) {
321 self.inner.waveform_up(dma, channel, duty).await; 348 self.inner.enable_channel(channel, true);
349 self.inner.enable_update_dma(true);
350 self.inner.setup_update_dma(dma, channel, duty).await;
351 self.inner.enable_update_dma(false);
322 } 352 }
323 353
324 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. 354 /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events.
@@ -350,7 +380,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
350 /// Also be aware that embassy timers use one of timers internally. It is possible to 380 /// Also be aware that embassy timers use one of timers internally. It is possible to
351 /// switch this timer by using `time-driver-timX` feature. 381 /// switch this timer by using `time-driver-timX` feature.
352 /// 382 ///
353 #[inline(always)]
354 pub async fn waveform_up_multi_channel( 383 pub async fn waveform_up_multi_channel(
355 &mut self, 384 &mut self,
356 dma: Peri<'_, impl super::UpDma<T>>, 385 dma: Peri<'_, impl super::UpDma<T>>,
@@ -358,15 +387,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
358 ending_channel: Channel, 387 ending_channel: Channel,
359 duty: &[u16], 388 duty: &[u16],
360 ) { 389 ) {
390 self.inner.enable_update_dma(true);
361 self.inner 391 self.inner
362 .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) 392 .setup_update_dma_burst(dma, starting_channel, ending_channel, duty)
363 .await; 393 .await;
364 } 394 self.inner.enable_update_dma(false);
365
366 /// Generate a sequence of PWM waveform
367 #[inline(always)]
368 pub async fn waveform<C: TimerChannel>(&mut self, dma: Peri<'_, impl super::Dma<T, C>>, duty: &[u16]) {
369 self.inner.waveform(dma, duty).await;
370 } 395 }
371} 396}
372 397
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-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs
index a80a2692b..466e1a9b4 100644
--- a/embassy-stm32/src/xspi/mod.rs
+++ b/embassy-stm32/src/xspi/mod.rs
@@ -420,9 +420,9 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> {
420 return Err(XspiError::InvalidCommand); 420 return Err(XspiError::InvalidCommand);
421 } 421 }
422 422
423 T::REGS.cr().modify(|w| { 423 T::REGS
424 w.set_fmode(0.into()); 424 .cr()
425 }); 425 .modify(|w| w.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into())));
426 426
427 // Configure alternate bytes 427 // Configure alternate bytes
428 if let Some(ab) = command.alternate_bytes { 428 if let Some(ab) = command.alternate_bytes {
@@ -538,8 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> {
538 w.set_dmaen(false); 538 w.set_dmaen(false);
539 }); 539 });
540 540
541 // self.configure_command(&transaction, Some(buf.len()))?; 541 let transfer_size_bytes = buf.len() * W::size().bytes();
542 self.configure_command(&transaction, Some(buf.len())).unwrap(); 542 self.configure_command(&transaction, Some(transfer_size_bytes))?;
543 543
544 let current_address = T::REGS.ar().read().address(); 544 let current_address = T::REGS.ar().read().address();
545 let current_instruction = T::REGS.ir().read().instruction(); 545 let current_instruction = T::REGS.ir().read().instruction();
@@ -578,7 +578,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> {
578 w.set_dmaen(false); 578 w.set_dmaen(false);
579 }); 579 });
580 580
581 self.configure_command(&transaction, Some(buf.len()))?; 581 let transfer_size_bytes = buf.len() * W::size().bytes();
582 self.configure_command(&transaction, Some(transfer_size_bytes))?;
582 583
583 T::REGS 584 T::REGS
584 .cr() 585 .cr()
@@ -1145,7 +1146,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> {
1145 // Wait for peripheral to be free 1146 // Wait for peripheral to be free
1146 while T::REGS.sr().read().busy() {} 1147 while T::REGS.sr().read().busy() {}
1147 1148
1148 self.configure_command(&transaction, Some(buf.len()))?; 1149 let transfer_size_bytes = buf.len() * W::size().bytes();
1150 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1149 1151
1150 let current_address = T::REGS.ar().read().address(); 1152 let current_address = T::REGS.ar().read().address();
1151 let current_instruction = T::REGS.ir().read().instruction(); 1153 let current_instruction = T::REGS.ir().read().instruction();
@@ -1160,16 +1162,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> {
1160 T::REGS.ar().write(|v| v.set_address(current_address)); 1162 T::REGS.ar().write(|v| v.set_address(current_address));
1161 } 1163 }
1162 1164
1163 let transfer = unsafe { 1165 for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {
1164 self.dma 1166 let transfer = unsafe {
1165 .as_mut() 1167 self.dma
1166 .unwrap() 1168 .as_mut()
1167 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) 1169 .unwrap()
1168 }; 1170 .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())
1171 };
1169 1172
1170 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1173 T::REGS.cr().modify(|w| w.set_dmaen(true));
1171 1174
1172 transfer.blocking_wait(); 1175 transfer.blocking_wait();
1176 }
1173 1177
1174 finish_dma(T::REGS); 1178 finish_dma(T::REGS);
1175 1179
@@ -1185,21 +1189,24 @@ impl<'d, T: Instance> Xspi<'d, T, Async> {
1185 // Wait for peripheral to be free 1189 // Wait for peripheral to be free
1186 while T::REGS.sr().read().busy() {} 1190 while T::REGS.sr().read().busy() {}
1187 1191
1188 self.configure_command(&transaction, Some(buf.len()))?; 1192 let transfer_size_bytes = buf.len() * W::size().bytes();
1193 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1189 T::REGS 1194 T::REGS
1190 .cr() 1195 .cr()
1191 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); 1196 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into())));
1192 1197
1193 let transfer = unsafe { 1198 for chunk in buf.chunks(0xFFFF / W::size().bytes()) {
1194 self.dma 1199 let transfer = unsafe {
1195 .as_mut() 1200 self.dma
1196 .unwrap() 1201 .as_mut()
1197 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) 1202 .unwrap()
1198 }; 1203 .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())
1204 };
1199 1205
1200 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1206 T::REGS.cr().modify(|w| w.set_dmaen(true));
1201 1207
1202 transfer.blocking_wait(); 1208 transfer.blocking_wait();
1209 }
1203 1210
1204 finish_dma(T::REGS); 1211 finish_dma(T::REGS);
1205 1212
@@ -1215,7 +1222,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> {
1215 // Wait for peripheral to be free 1222 // Wait for peripheral to be free
1216 while T::REGS.sr().read().busy() {} 1223 while T::REGS.sr().read().busy() {}
1217 1224
1218 self.configure_command(&transaction, Some(buf.len()))?; 1225 let transfer_size_bytes = buf.len() * W::size().bytes();
1226 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1219 1227
1220 let current_address = T::REGS.ar().read().address(); 1228 let current_address = T::REGS.ar().read().address();
1221 let current_instruction = T::REGS.ir().read().instruction(); 1229 let current_instruction = T::REGS.ir().read().instruction();
@@ -1230,16 +1238,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> {
1230 T::REGS.ar().write(|v| v.set_address(current_address)); 1238 T::REGS.ar().write(|v| v.set_address(current_address));
1231 } 1239 }
1232 1240
1233 let transfer = unsafe { 1241 for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) {
1234 self.dma 1242 let transfer = unsafe {
1235 .as_mut() 1243 self.dma
1236 .unwrap() 1244 .as_mut()
1237 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) 1245 .unwrap()
1238 }; 1246 .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default())
1247 };
1239 1248
1240 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1249 T::REGS.cr().modify(|w| w.set_dmaen(true));
1241 1250
1242 transfer.await; 1251 transfer.await;
1252 }
1243 1253
1244 finish_dma(T::REGS); 1254 finish_dma(T::REGS);
1245 1255
@@ -1255,21 +1265,25 @@ impl<'d, T: Instance> Xspi<'d, T, Async> {
1255 // Wait for peripheral to be free 1265 // Wait for peripheral to be free
1256 while T::REGS.sr().read().busy() {} 1266 while T::REGS.sr().read().busy() {}
1257 1267
1258 self.configure_command(&transaction, Some(buf.len()))?; 1268 let transfer_size_bytes = buf.len() * W::size().bytes();
1269 self.configure_command(&transaction, Some(transfer_size_bytes))?;
1259 T::REGS 1270 T::REGS
1260 .cr() 1271 .cr()
1261 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); 1272 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into())));
1262 1273
1263 let transfer = unsafe { 1274 // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU.
1264 self.dma 1275 for chunk in buf.chunks(0xFFFF / W::size().bytes()) {
1265 .as_mut() 1276 let transfer = unsafe {
1266 .unwrap() 1277 self.dma
1267 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) 1278 .as_mut()
1268 }; 1279 .unwrap()
1280 .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default())
1281 };
1269 1282
1270 T::REGS.cr().modify(|w| w.set_dmaen(true)); 1283 T::REGS.cr().modify(|w| w.set_dmaen(true));
1271 1284
1272 transfer.await; 1285 transfer.await;
1286 }
1273 1287
1274 finish_dma(T::REGS); 1288 finish_dma(T::REGS);
1275 1289
diff --git a/embassy-time-driver/CHANGELOG.md b/embassy-time-driver/CHANGELOG.md
index cdd432437..4951f8c3e 100644
--- a/embassy-time-driver/CHANGELOG.md
+++ b/embassy-time-driver/CHANGELOG.md
@@ -11,7 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11## 0.2.1 - 2025-08-26 11## 0.2.1 - 2025-08-26
12 12
13- Allow inlining on time driver boundary 13- Allow inlining on time driver boundary
14- add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK` 14- Add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK`
15- Add 375KHz tick rate support
15 16
16## 0.2.0 - 2025-01-02 17## 0.2.0 - 2025-01-02
17 18
diff --git a/embassy-time-driver/Cargo.toml b/embassy-time-driver/Cargo.toml
index a52e82433..cbb6168b9 100644
--- a/embassy-time-driver/Cargo.toml
+++ b/embassy-time-driver/Cargo.toml
@@ -118,6 +118,8 @@ tick-hz-256_000 = []
118tick-hz-262_144 = [] 118tick-hz-262_144 = []
119## 320.0kHz Tick Rate 119## 320.0kHz Tick Rate
120tick-hz-320_000 = [] 120tick-hz-320_000 = []
121## 375.0kHz Tick Rate
122tick-hz-375_000 = []
121## 512.0kHz Tick Rate 123## 512.0kHz Tick Rate
122tick-hz-512_000 = [] 124tick-hz-512_000 = []
123## 524.288kHz Tick Rate 125## 524.288kHz Tick Rate
diff --git a/embassy-time-driver/gen_tick.py b/embassy-time-driver/gen_tick.py
index 080434457..3cb6552df 100644
--- a/embassy-time-driver/gen_tick.py
+++ b/embassy-time-driver/gen_tick.py
@@ -1,5 +1,4 @@
1import os 1import os
2from glob import glob
3 2
4abspath = os.path.abspath(__file__) 3abspath = os.path.abspath(__file__)
5dname = os.path.dirname(abspath) 4dname = os.path.dirname(abspath)
@@ -22,6 +21,8 @@ for i in range(1, 30):
22 ticks.append(10 * i * 1_000_000) 21 ticks.append(10 * i * 1_000_000)
23for i in range(15, 50): 22for i in range(15, 50):
24 ticks.append(20 * i * 1_000_000) 23 ticks.append(20 * i * 1_000_000)
24
25ticks.append(375 * 1000)
25ticks.append(133 * 1_000_000) 26ticks.append(133 * 1_000_000)
26 27
27seen = set() 28seen = set()
diff --git a/embassy-time-driver/src/tick.rs b/embassy-time-driver/src/tick.rs
index 5059e1628..247ec9ab3 100644
--- a/embassy-time-driver/src/tick.rs
+++ b/embassy-time-driver/src/tick.rs
@@ -74,6 +74,8 @@ pub const TICK_HZ: u64 = 256_000;
74pub const TICK_HZ: u64 = 262_144; 74pub const TICK_HZ: u64 = 262_144;
75#[cfg(feature = "tick-hz-320_000")] 75#[cfg(feature = "tick-hz-320_000")]
76pub const TICK_HZ: u64 = 320_000; 76pub const TICK_HZ: u64 = 320_000;
77#[cfg(feature = "tick-hz-375_000")]
78pub const TICK_HZ: u64 = 375_000;
77#[cfg(feature = "tick-hz-512_000")] 79#[cfg(feature = "tick-hz-512_000")]
78pub const TICK_HZ: u64 = 512_000; 80pub const TICK_HZ: u64 = 512_000;
79#[cfg(feature = "tick-hz-524_288")] 81#[cfg(feature = "tick-hz-524_288")]
@@ -358,6 +360,7 @@ pub const TICK_HZ: u64 = 5_242_880_000;
358 feature = "tick-hz-256_000", 360 feature = "tick-hz-256_000",
359 feature = "tick-hz-262_144", 361 feature = "tick-hz-262_144",
360 feature = "tick-hz-320_000", 362 feature = "tick-hz-320_000",
363 feature = "tick-hz-375_000",
361 feature = "tick-hz-512_000", 364 feature = "tick-hz-512_000",
362 feature = "tick-hz-524_288", 365 feature = "tick-hz-524_288",
363 feature = "tick-hz-640_000", 366 feature = "tick-hz-640_000",
diff --git a/embassy-time/CHANGELOG.md b/embassy-time/CHANGELOG.md
index 4a50da8ef..17f8a3837 100644
--- a/embassy-time/CHANGELOG.md
+++ b/embassy-time/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 as_nanos and from_nanos where missing 11- Add as_nanos and from_nanos where missing
12- Added 375KHz tick rate support
12 13
13## 0.5.0 - 2025-08-26 14## 0.5.0 - 2025-08-26
14 15
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml
index 05614dbf5..a7ed51e78 100644
--- a/embassy-time/Cargo.toml
+++ b/embassy-time/Cargo.toml
@@ -178,6 +178,8 @@ tick-hz-256_000 = ["embassy-time-driver/tick-hz-256_000"]
178tick-hz-262_144 = ["embassy-time-driver/tick-hz-262_144"] 178tick-hz-262_144 = ["embassy-time-driver/tick-hz-262_144"]
179## 320.0kHz Tick Rate 179## 320.0kHz Tick Rate
180tick-hz-320_000 = ["embassy-time-driver/tick-hz-320_000"] 180tick-hz-320_000 = ["embassy-time-driver/tick-hz-320_000"]
181## 375.0kHz Tick Rate
182tick-hz-375_000 = ["embassy-time-driver/tick-hz-375_000"]
181## 512.0kHz Tick Rate 183## 512.0kHz Tick Rate
182tick-hz-512_000 = ["embassy-time-driver/tick-hz-512_000"] 184tick-hz-512_000 = ["embassy-time-driver/tick-hz-512_000"]
183## 524.288kHz Tick Rate 185## 524.288kHz Tick Rate
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/stm32f7/src/bin/pwm.rs b/examples/stm32f7/src/bin/pwm.rs
new file mode 100644
index 000000000..b071eb597
--- /dev/null
+++ b/examples/stm32f7/src/bin/pwm.rs
@@ -0,0 +1,61 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::Config;
7use embassy_stm32::gpio::OutputType;
8use embassy_stm32::time::{Hertz, mhz};
9use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12
13// If you are trying this and your USB device doesn't connect, the most
14// common issues are the RCC config and vbus_detection
15//
16// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure
17// for more information.
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) {
20 info!("Hello World!");
21
22 let mut config = Config::default();
23 {
24 use embassy_stm32::rcc::*;
25 config.rcc.hse = Some(Hse {
26 freq: Hertz(8_000_000),
27 mode: HseMode::Bypass,
28 });
29 config.rcc.pll_src = PllSource::HSE;
30 config.rcc.pll = Some(Pll {
31 prediv: PllPreDiv::DIV4,
32 mul: PllMul::MUL200,
33 divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 200 / 2 = 200Mhz
34 divq: Some(PllQDiv::DIV4), // 8mhz / 4 * 200 / 4 = 100Mhz
35 divr: None,
36 });
37 config.rcc.ahb_pre = AHBPrescaler::DIV1;
38 config.rcc.apb1_pre = APBPrescaler::DIV4;
39 config.rcc.apb2_pre = APBPrescaler::DIV2;
40 config.rcc.sys = Sysclk::PLL1_P;
41 }
42 let p = embassy_stm32::init(config);
43 let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull);
44 let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, mhz(1), Default::default());
45 let mut ch1 = pwm.ch1();
46 ch1.enable();
47
48 info!("PWM initialized");
49 info!("PWM max duty {}", ch1.max_duty_cycle());
50
51 loop {
52 ch1.set_duty_cycle_fully_off();
53 Timer::after_millis(300).await;
54 ch1.set_duty_cycle_fraction(1, 4);
55 Timer::after_millis(300).await;
56 ch1.set_duty_cycle_fraction(1, 2);
57 Timer::after_millis(300).await;
58 ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);
59 Timer::after_millis(300).await;
60 }
61}
diff --git a/examples/stm32f7/src/bin/pwm_ringbuffer.rs b/examples/stm32f7/src/bin/pwm_ringbuffer.rs
new file mode 100644
index 000000000..4d191ac13
--- /dev/null
+++ b/examples/stm32f7/src/bin/pwm_ringbuffer.rs
@@ -0,0 +1,153 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::Config;
7use embassy_stm32::gpio::OutputType;
8use embassy_stm32::time::mhz;
9use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12
13// If you are trying this and your USB device doesn't connect, the most
14// common issues are the RCC config and vbus_detection
15//
16// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure
17// for more information.
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) {
20 info!("PWM Ring Buffer Example");
21
22 let mut config = Config::default();
23 {
24 use embassy_stm32::rcc::*;
25 use embassy_stm32::time::Hertz;
26 config.rcc.hse = Some(Hse {
27 freq: Hertz(8_000_000),
28 mode: HseMode::Bypass,
29 });
30 config.rcc.pll_src = PllSource::HSE;
31 config.rcc.pll = Some(Pll {
32 prediv: PllPreDiv::DIV4,
33 mul: PllMul::MUL200,
34 divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 200 / 2 = 200Mhz
35 divq: Some(PllQDiv::DIV4), // 8mhz / 4 * 200 / 4 = 100Mhz
36 divr: None,
37 });
38 config.rcc.ahb_pre = AHBPrescaler::DIV1;
39 config.rcc.apb1_pre = APBPrescaler::DIV4;
40 config.rcc.apb2_pre = APBPrescaler::DIV2;
41 config.rcc.sys = Sysclk::PLL1_P;
42 }
43 let p = embassy_stm32::init(config);
44
45 // Initialize PWM on TIM1
46 let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull);
47 let ch2_pin = PwmPin::new(p.PE11, OutputType::PushPull);
48 let mut pwm = SimplePwm::new(
49 p.TIM1,
50 Some(ch1_pin),
51 Some(ch2_pin),
52 None,
53 None,
54 mhz(1),
55 Default::default(),
56 );
57
58 // Use channel 1 for static PWM at 50%
59 let mut ch1 = pwm.ch1();
60 ch1.enable();
61 ch1.set_duty_cycle_fraction(1, 2);
62 info!("Channel 1 (PE9/D6): Static 50% duty cycle");
63
64 // Get max duty from channel 1 before converting channel 2
65 let max_duty = ch1.max_duty_cycle();
66 info!("PWM max duty: {}", max_duty);
67
68 // Create a DMA ring buffer for channel 2
69 const BUFFER_SIZE: usize = 128;
70 static mut DMA_BUFFER: [u16; BUFFER_SIZE] = [0u16; BUFFER_SIZE];
71 let dma_buffer = unsafe { &mut *core::ptr::addr_of_mut!(DMA_BUFFER) };
72
73 // Pre-fill buffer with initial sine wave using lookup table approach
74 for i in 0..BUFFER_SIZE {
75 // Simple sine approximation using triangle wave
76 let phase = (i * 256) / BUFFER_SIZE;
77 let sine_approx = if phase < 128 {
78 phase as u16 * 2
79 } else {
80 (255 - phase) as u16 * 2
81 };
82 dma_buffer[i] = (sine_approx as u32 * max_duty as u32 / 256) as u16;
83 }
84
85 // Convert channel 2 to ring-buffered PWM
86 let mut ring_pwm = pwm.ch1().into_ring_buffered_channel(p.DMA2_CH5, dma_buffer);
87
88 info!("Ring buffer capacity: {}", ring_pwm.capacity());
89
90 // Pre-write some initial data to the buffer before starting
91 info!("Pre-writing initial waveform data...");
92
93 ring_pwm.write(&[0; BUFFER_SIZE]).unwrap();
94
95 // Enable the PWM channel output
96 ring_pwm.enable();
97
98 // Start the DMA ring buffer
99 ring_pwm.start();
100 info!("Channel 2 (PE11/D5): Ring buffered sine wave started");
101
102 // Give DMA time to start consuming
103 Timer::after_millis(10).await;
104
105 // Continuously update the waveform
106 let mut phase: f32 = 0.0;
107 let mut amplitude: f32 = 1.0;
108 let mut amplitude_direction = -0.05;
109
110 loop {
111 // Generate new waveform data with varying amplitude
112 let mut new_data = [0u16; 32];
113 for i in 0..new_data.len() {
114 // Triangle wave approximation for sine
115 let pos = ((i as u32 + phase as u32) * 4) % 256;
116 let sine_approx = if pos < 128 {
117 pos as u16 * 2
118 } else {
119 (255 - pos) as u16 * 2
120 };
121 let scaled = (sine_approx as u32 * (amplitude * 256.0) as u32) / (256 * 256);
122 new_data[i] = ((scaled * max_duty as u32) / 256) as u16;
123 }
124
125 // Write new data to the ring buffer
126 match ring_pwm.write_exact(&new_data).await {
127 Ok(_remaining) => {}
128 Err(e) => {
129 info!("Write error: {:?}", e);
130 }
131 }
132
133 // Update phase for animation effect
134 phase += 2.0;
135 if phase >= 64.0 {
136 phase = 0.0;
137 }
138
139 // Vary amplitude for breathing effect
140 amplitude += amplitude_direction;
141 if amplitude <= 0.2 || amplitude >= 1.0 {
142 amplitude_direction = -amplitude_direction;
143 }
144
145 // Log buffer status periodically
146 if (phase as u32) % 10 == 0 {
147 match ring_pwm.len() {
148 Ok(len) => info!("Ring buffer fill: {}/{}", len, ring_pwm.capacity()),
149 Err(_) => info!("Error reading buffer length"),
150 }
151 }
152 }
153}
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"