diff options
| author | xoviat <[email protected]> | 2025-11-26 08:44:03 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-11-26 08:44:03 -0600 |
| commit | 1045738fa3e2f2f6b2968d4b35a4b618e6235d2e (patch) | |
| tree | 6cf0abaca5f946cac1598f34d8f98a922f6c10e6 | |
| parent | 3ba8bb866a19a09f25e0b21419a068fd765a9033 (diff) | |
| parent | 9fa4f7309895bab81eb0e398d8f457ee528aad69 (diff) | |
Merge branch 'main' into time
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 | |||
| 17 | use crate::interrupt::typelevel::Interrupt; | 17 | use crate::interrupt::typelevel::Interrupt; |
| 18 | use crate::pac::gpio::vals as gpiovals; | 18 | use crate::pac::gpio::vals as gpiovals; |
| 19 | use crate::pac::spis::vals; | 19 | use crate::pac::spis::vals; |
| 20 | use crate::ppi::Event; | ||
| 20 | use crate::util::slice_in_ram_or; | 21 | use crate::util::slice_in_ram_or; |
| 21 | use crate::{interrupt, pac}; | 22 | use 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" ] | |||
| 47 | defmt = ["dep:defmt", "embassy-usb-driver/defmt", "embassy-hal-internal/defmt"] | 47 | defmt = ["dep:defmt", "embassy-usb-driver/defmt", "embassy-hal-internal/defmt"] |
| 48 | ## Enable log support | 48 | ## Enable log support |
| 49 | log = ["dep:log"] | 49 | log = ["dep:log"] |
| 50 | ## Enable chrono support | 50 | ## Enable chrono support |
| 51 | chrono = ["dep:chrono"] | 51 | chrono = ["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" } | |||
| 159 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } | 159 | embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } |
| 160 | embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } | 160 | embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } |
| 161 | embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } | 161 | embassy-usb-driver = { version = "0.2.0", path = "../embassy-usb-driver" } |
| 162 | atomic-polyfill = "1.0.1" | ||
| 163 | defmt = { version = "1.0.1", optional = true } | 162 | defmt = { version = "1.0.1", optional = true } |
| 164 | log = { version = "0.4.14", optional = true } | 163 | log = { version = "0.4.14", optional = true } |
| 165 | nb = "1.1.0" | 164 | nb = "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 @@ | |||
| 2 | use core::future::Future; | 2 | use core::future::Future; |
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | use core::pin::Pin as FuturePin; | 4 | use core::pin::Pin as FuturePin; |
| 5 | use core::sync::atomic::{Ordering, compiler_fence}; | 5 | use core::sync::atomic::{AtomicU8, AtomicU32, Ordering, compiler_fence}; |
| 6 | use core::task::{Context, Poll}; | 6 | use core::task::{Context, Poll}; |
| 7 | 7 | ||
| 8 | use atomic_polyfill::{AtomicU8, AtomicU64}; | ||
| 9 | use embassy_hal_internal::{Peri, PeripheralType}; | 8 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 11 | use fixed::FixedU32; | 10 | use 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 | ||
| 1412 | struct AtomicU64 { | ||
| 1413 | upper_32: AtomicU32, | ||
| 1414 | lower_32: AtomicU32, | ||
| 1415 | } | ||
| 1416 | |||
| 1417 | impl 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 | ||
| 1419 | fn on_pio_drop<PIO: Instance>() { | 1460 | fn 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. |
| 2 | use core::future::Future; | 2 | use core::future::Future; |
| 3 | use core::slice; | 3 | use core::slice; |
| 4 | use core::sync::atomic::{AtomicU8, Ordering}; | ||
| 4 | 5 | ||
| 5 | use atomic_polyfill::AtomicU8; | ||
| 6 | use embassy_hal_internal::atomic_ring_buffer::RingBuffer; | 6 | use embassy_hal_internal::atomic_ring_buffer::RingBuffer; |
| 7 | 7 | ||
| 8 | use super::*; | 8 | use 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. |
| 2 | use core::future::poll_fn; | 2 | use core::future::poll_fn; |
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | use core::sync::atomic::{AtomicU16, Ordering}; | ||
| 4 | use core::task::Poll; | 5 | use core::task::Poll; |
| 5 | 6 | ||
| 6 | use atomic_polyfill::{AtomicU16, Ordering}; | ||
| 7 | use embassy_futures::select::{Either, select}; | 7 | use embassy_futures::select::{Either, select}; |
| 8 | use embassy_hal_internal::{Peri, PeripheralType}; | 8 | use embassy_hal_internal::{Peri, PeripheralType}; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; | 9 | use 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 | ||
| 9 | use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; | 9 | use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; |
| 10 | use super::word::{Word, WordSize}; | 10 | use super::word::{Word, WordSize}; |
| 11 | use super::{AnyChannel, BusyChannel, Channel, Dir, Request, STATE}; | 11 | use super::{AnyChannel, Channel, Dir, Request, STATE}; |
| 12 | use crate::interrupt::typelevel::Interrupt; | 12 | use crate::interrupt::typelevel::Interrupt; |
| 13 | use crate::rcc::BusyPeripheral; | ||
| 13 | use crate::{interrupt, pac}; | 14 | use crate::{interrupt, pac}; |
| 14 | 15 | ||
| 15 | pub(crate) struct ChannelInfo { | 16 | pub(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"] |
| 604 | pub struct Transfer<'a> { | 605 | pub struct Transfer<'a> { |
| 605 | channel: BusyChannel<'a>, | 606 | channel: BusyPeripheral<Peri<'a, AnyChannel>>, |
| 606 | } | 607 | } |
| 607 | 608 | ||
| 608 | impl<'a> Transfer<'a> { | 609 | impl<'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. |
| 820 | pub struct ReadableRingBuffer<'a, W: Word> { | 821 | pub 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. |
| 976 | pub struct WritableRingBuffer<'a, W: Word> { | 977 | pub 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 | ||
| 12 | use super::word::{Word, WordSize}; | 12 | use super::word::{Word, WordSize}; |
| 13 | use super::{AnyChannel, Channel, Dir, Request, STATE}; | 13 | use super::{AnyChannel, Channel, Dir, Request, STATE}; |
| 14 | use crate::dma::BusyChannel; | ||
| 15 | use crate::interrupt::typelevel::Interrupt; | 14 | use crate::interrupt::typelevel::Interrupt; |
| 16 | use crate::pac; | 15 | use crate::pac; |
| 17 | use crate::pac::gpdma::vals; | 16 | use crate::pac::gpdma::vals; |
| 17 | use crate::rcc::BusyPeripheral; | ||
| 18 | 18 | ||
| 19 | pub mod linked_list; | 19 | pub mod linked_list; |
| 20 | pub mod ringbuffered; | 20 | pub 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"] |
| 411 | pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { | 411 | pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { |
| 412 | channel: BusyChannel<'a>, | 412 | channel: BusyPeripheral<Peri<'a, AnyChannel>>, |
| 413 | } | 413 | } |
| 414 | 414 | ||
| 415 | impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { | 415 | impl<'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"] |
| 510 | pub struct Transfer<'a> { | 510 | pub struct Transfer<'a> { |
| 511 | channel: BusyChannel<'a>, | 511 | channel: BusyPeripheral<Peri<'a, AnyChannel>>, |
| 512 | } | 512 | } |
| 513 | 513 | ||
| 514 | impl<'a> Transfer<'a> { | 514 | impl<'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}; | |||
| 12 | use crate::dma::gpdma::linked_list::{RunMode, Table}; | 12 | use crate::dma::gpdma::linked_list::{RunMode, Table}; |
| 13 | use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; | 13 | use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; |
| 14 | use crate::dma::word::Word; | 14 | use crate::dma::word::Word; |
| 15 | use crate::dma::{BusyChannel, Channel, Dir, Request}; | 15 | use crate::dma::{Channel, Dir, Request}; |
| 16 | use crate::rcc::BusyPeripheral; | ||
| 16 | 17 | ||
| 17 | struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); | 18 | struct 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. |
| 51 | pub struct ReadableRingBuffer<'a, W: Word> { | 52 | pub 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. |
| 191 | pub struct WritableRingBuffer<'a, W: Word> { | 192 | pub 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))] |
| 5 | mod dma_bdma; | 5 | mod dma_bdma; |
| 6 | use core::ops; | ||
| 7 | 6 | ||
| 8 | #[cfg(any(bdma, dma))] | 7 | #[cfg(any(bdma, dma))] |
| 9 | pub use dma_bdma::*; | 8 | pub use dma_bdma::*; |
| 10 | 9 | ||
| 11 | #[cfg(gpdma)] | 10 | #[cfg(gpdma)] |
| 12 | pub(crate) mod gpdma; | 11 | pub(crate) mod gpdma; |
| 13 | use embassy_hal_internal::Peri; | ||
| 14 | #[cfg(gpdma)] | 12 | #[cfg(gpdma)] |
| 15 | pub use gpdma::ringbuffered::*; | 13 | pub use gpdma::ringbuffered::*; |
| 16 | #[cfg(gpdma)] | 14 | #[cfg(gpdma)] |
| @@ -27,9 +25,10 @@ pub(crate) use util::*; | |||
| 27 | pub(crate) mod ringbuffer; | 25 | pub(crate) mod ringbuffer; |
| 28 | pub mod word; | 26 | pub mod word; |
| 29 | 27 | ||
| 30 | use embassy_hal_internal::{PeripheralType, impl_peripheral}; | 28 | use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; |
| 31 | 29 | ||
| 32 | use crate::interrupt; | 30 | use crate::interrupt; |
| 31 | use 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)))] |
| 49 | pub type Request = (); | 48 | pub type Request = (); |
| 50 | 49 | ||
| 50 | impl<'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 | |||
| 51 | pub(crate) trait SealedChannel { | 57 | pub(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 | ||
| 106 | pub(crate) struct BusyChannel<'a> { | ||
| 107 | channel: Peri<'a, AnyChannel>, | ||
| 108 | } | ||
| 109 | |||
| 110 | impl<'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 | |||
| 121 | impl<'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 | |||
| 130 | impl<'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 | |||
| 138 | impl<'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. |
| 145 | pub struct AnyChannel { | 113 | pub 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 | ||
| 52 | use crate::interrupt; | 52 | use crate::interrupt; |
| 53 | pub use crate::rcc::StopMode; | 53 | pub use crate::rcc::StopMode; |
| 54 | use crate::rcc::{RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2, decrement_stop_refcount, increment_stop_refcount}; | 54 | use crate::rcc::{BusyPeripheral, RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2}; |
| 55 | use crate::time_driver::get_driver; | 55 | use crate::time_driver::get_driver; |
| 56 | 56 | ||
| 57 | const THREAD_PENDER: usize = usize::MAX; | 57 | const THREAD_PENDER: usize = usize::MAX; |
| @@ -59,7 +59,9 @@ const THREAD_PENDER: usize = usize::MAX; | |||
| 59 | static mut EXECUTOR_TAKEN: bool = false; | 59 | static 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 |
| 62 | pub struct DeviceBusy(StopMode); | 62 | pub struct DeviceBusy { |
| 63 | _stop_mode: BusyPeripheral<StopMode>, | ||
| 64 | } | ||
| 63 | 65 | ||
| 64 | impl DeviceBusy { | 66 | impl 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 | |||
| 85 | impl 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 | ||
| 6 | use core::mem::MaybeUninit; | 6 | use core::mem::MaybeUninit; |
| 7 | use core::ops; | ||
| 7 | 8 | ||
| 8 | mod bd; | 9 | mod bd; |
| 9 | pub use bd::*; | 10 | pub 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")] |
| 115 | pub(crate) fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { | 116 | fn 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")] |
| 128 | pub(crate) fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { | 129 | fn 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")] | ||
| 187 | type BusyRccPeripheral = BusyPeripheral<StopMode>; | ||
| 188 | |||
| 189 | #[cfg(not(feature = "low-power"))] | ||
| 190 | type BusyRccPeripheral = (); | ||
| 191 | |||
| 185 | impl RccInfo { | 192 | impl 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 | ||
| 377 | pub(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")] | ||
| 384 | impl<'a> StoppablePeripheral for StopMode { | ||
| 385 | fn stop_mode(&self) -> StopMode { | ||
| 386 | *self | ||
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | pub(crate) struct BusyPeripheral<T: StoppablePeripheral> { | ||
| 391 | peripheral: T, | ||
| 392 | } | ||
| 393 | |||
| 394 | impl<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 | |||
| 403 | impl<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 | |||
| 410 | impl<T: StoppablePeripheral> ops::Deref for BusyPeripheral<T> { | ||
| 411 | type Target = T; | ||
| 412 | |||
| 413 | fn deref(&self) -> &Self::Target { | ||
| 414 | &self.peripheral | ||
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | impl<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)] |
| 330 | mod util { | 425 | mod 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; | |||
| 13 | pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; | 13 | pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; |
| 14 | 14 | ||
| 15 | use super::*; | 15 | use super::*; |
| 16 | use crate::dma::{Transfer, WritableRingBuffer}; | ||
| 16 | use crate::pac::timer::vals; | 17 | use crate::pac::timer::vals; |
| 18 | use crate::rcc; | ||
| 17 | use crate::time::Hertz; | 19 | use crate::time::Hertz; |
| 18 | use 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; | |||
| 12 | pub mod one_pulse; | 12 | pub mod one_pulse; |
| 13 | pub mod pwm_input; | 13 | pub mod pwm_input; |
| 14 | pub mod qei; | 14 | pub mod qei; |
| 15 | pub mod ringbuffered; | ||
| 15 | pub mod simple_pwm; | 16 | pub mod simple_pwm; |
| 16 | 17 | ||
| 17 | use crate::interrupt; | 18 | use 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 | |||
| 3 | use core::mem::ManuallyDrop; | ||
| 4 | use core::task::Waker; | ||
| 5 | |||
| 6 | use super::low_level::Timer; | ||
| 7 | use super::{Channel, GeneralInstance4Channel}; | ||
| 8 | use crate::dma::WritableRingBuffer; | ||
| 9 | use 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 | /// ``` | ||
| 26 | pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { | ||
| 27 | timer: ManuallyDrop<Timer<'d, T>>, | ||
| 28 | ring_buf: WritableRingBuffer<'d, u16>, | ||
| 29 | channel: Channel, | ||
| 30 | } | ||
| 31 | |||
| 32 | impl<'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`]. | ||
| 160 | pub 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; | |||
| 4 | use core::mem::ManuallyDrop; | 4 | use core::mem::ManuallyDrop; |
| 5 | 5 | ||
| 6 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; | 6 | use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; |
| 7 | use super::ringbuffered::RingBufferedPwmChannel; | ||
| 7 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; | 8 | use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; |
| 8 | use crate::Peri; | 9 | use 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 | ||
| 325 | impl Drop for RingBufferedUartRx<'_> { | 327 | impl 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 = [] | |||
| 118 | tick-hz-262_144 = [] | 118 | tick-hz-262_144 = [] |
| 119 | ## 320.0kHz Tick Rate | 119 | ## 320.0kHz Tick Rate |
| 120 | tick-hz-320_000 = [] | 120 | tick-hz-320_000 = [] |
| 121 | ## 375.0kHz Tick Rate | ||
| 122 | tick-hz-375_000 = [] | ||
| 121 | ## 512.0kHz Tick Rate | 123 | ## 512.0kHz Tick Rate |
| 122 | tick-hz-512_000 = [] | 124 | tick-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 @@ | |||
| 1 | import os | 1 | import os |
| 2 | from glob import glob | ||
| 3 | 2 | ||
| 4 | abspath = os.path.abspath(__file__) | 3 | abspath = os.path.abspath(__file__) |
| 5 | dname = os.path.dirname(abspath) | 4 | dname = 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) |
| 23 | for i in range(15, 50): | 22 | for i in range(15, 50): |
| 24 | ticks.append(20 * i * 1_000_000) | 23 | ticks.append(20 * i * 1_000_000) |
| 24 | |||
| 25 | ticks.append(375 * 1000) | ||
| 25 | ticks.append(133 * 1_000_000) | 26 | ticks.append(133 * 1_000_000) |
| 26 | 27 | ||
| 27 | seen = set() | 28 | seen = 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; | |||
| 74 | pub const TICK_HZ: u64 = 262_144; | 74 | pub const TICK_HZ: u64 = 262_144; |
| 75 | #[cfg(feature = "tick-hz-320_000")] | 75 | #[cfg(feature = "tick-hz-320_000")] |
| 76 | pub const TICK_HZ: u64 = 320_000; | 76 | pub const TICK_HZ: u64 = 320_000; |
| 77 | #[cfg(feature = "tick-hz-375_000")] | ||
| 78 | pub const TICK_HZ: u64 = 375_000; | ||
| 77 | #[cfg(feature = "tick-hz-512_000")] | 79 | #[cfg(feature = "tick-hz-512_000")] |
| 78 | pub const TICK_HZ: u64 = 512_000; | 80 | pub 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"] | |||
| 178 | tick-hz-262_144 = ["embassy-time-driver/tick-hz-262_144"] | 178 | tick-hz-262_144 = ["embassy-time-driver/tick-hz-262_144"] |
| 179 | ## 320.0kHz Tick Rate | 179 | ## 320.0kHz Tick Rate |
| 180 | tick-hz-320_000 = ["embassy-time-driver/tick-hz-320_000"] | 180 | tick-hz-320_000 = ["embassy-time-driver/tick-hz-320_000"] |
| 181 | ## 375.0kHz Tick Rate | ||
| 182 | tick-hz-375_000 = ["embassy-time-driver/tick-hz-375_000"] | ||
| 181 | ## 512.0kHz Tick Rate | 183 | ## 512.0kHz Tick Rate |
| 182 | tick-hz-512_000 = ["embassy-time-driver/tick-hz-512_000"] | 184 | tick-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" | |||
| 69 | embedded-io-async = "0.6.1" | 69 | embedded-io-async = "0.6.1" |
| 70 | 70 | ||
| 71 | # for HID | 71 | # for HID |
| 72 | usbd-hid = { version = "0.8.1", optional = true } | 72 | usbd-hid = { version = "0.9.0", optional = true } |
| 73 | ssmarshal = { version = "1.0", default-features = false, optional = true } | 73 | ssmarshal = { 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" | |||
| 28 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | 28 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 29 | rand = { version = "0.9.0", default-features = false } | 29 | rand = { version = "0.9.0", default-features = false } |
| 30 | embedded-storage = "0.3.1" | 30 | embedded-storage = "0.3.1" |
| 31 | usbd-hid = "0.8.1" | 31 | usbd-hid = "0.9.0" |
| 32 | serde = { version = "1.0.136", default-features = false } | 32 | serde = { version = "1.0.136", default-features = false } |
| 33 | embedded-hal = { version = "1.0" } | 33 | embedded-hal = { version = "1.0" } |
| 34 | embedded-hal-async = { version = "1.0" } | 34 | embedded-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 | |||
| 23 | cortex-m-rt = "0.7.0" | 23 | cortex-m-rt = "0.7.0" |
| 24 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | 24 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } |
| 25 | embedded-storage = "0.3.1" | 25 | embedded-storage = "0.3.1" |
| 26 | usbd-hid = "0.8.1" | 26 | usbd-hid = "0.9.0" |
| 27 | serde = { version = "1.0.136", default-features = false } | 27 | serde = { 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" | |||
| 45 | byte-slice-cast = { version = "1.2.0", default-features = false } | 45 | byte-slice-cast = { version = "1.2.0", default-features = false } |
| 46 | smart-leds = "0.4.0" | 46 | smart-leds = "0.4.0" |
| 47 | heapless = "0.8" | 47 | heapless = "0.8" |
| 48 | usbd-hid = "0.8.1" | 48 | usbd-hid = "0.9.0" |
| 49 | 49 | ||
| 50 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 50 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 51 | embedded-hal-async = "1.0" | 51 | embedded-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" | |||
| 46 | byte-slice-cast = { version = "1.2.0", default-features = false } | 46 | byte-slice-cast = { version = "1.2.0", default-features = false } |
| 47 | smart-leds = "0.3.0" | 47 | smart-leds = "0.3.0" |
| 48 | heapless = "0.8" | 48 | heapless = "0.8" |
| 49 | usbd-hid = "0.8.1" | 49 | usbd-hid = "0.9.0" |
| 50 | 50 | ||
| 51 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 51 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 52 | embedded-hal-async = "1.0" | 52 | embedded-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" | |||
| 32 | nb = "1.0.0" | 32 | nb = "1.0.0" |
| 33 | embedded-storage = "0.3.1" | 33 | embedded-storage = "0.3.1" |
| 34 | micromath = "2.0.0" | 34 | micromath = "2.0.0" |
| 35 | usbd-hid = "0.8.1" | 35 | usbd-hid = "0.9.0" |
| 36 | static_cell = "2" | 36 | static_cell = "2" |
| 37 | chrono = { version = "^0.4", default-features = false} | 37 | chrono = { 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 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::Config; | ||
| 7 | use embassy_stm32::gpio::OutputType; | ||
| 8 | use embassy_stm32::time::{Hertz, mhz}; | ||
| 9 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | ||
| 10 | use embassy_time::Timer; | ||
| 11 | use {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] | ||
| 19 | async 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 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::Config; | ||
| 7 | use embassy_stm32::gpio::OutputType; | ||
| 8 | use embassy_stm32::time::mhz; | ||
| 9 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | ||
| 10 | use embassy_time::Timer; | ||
| 11 | use {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] | ||
| 19 | async 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 | |||
| 13 | embassy-time = { path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 13 | embassy-time = { path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
| 14 | embassy-usb = { path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-futures = { path = "../../embassy-futures" } | 15 | embassy-futures = { path = "../../embassy-futures" } |
| 16 | usbd-hid = "0.8.1" | 16 | usbd-hid = "0.9.0" |
| 17 | 17 | ||
| 18 | defmt = "1.0.1" | 18 | defmt = "1.0.1" |
| 19 | defmt-rtt = "1.0.0" | 19 | defmt-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 | |||
| 14 | embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } | 14 | embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } |
| 15 | embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } | 15 | embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } |
| 16 | embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } | 16 | embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } |
| 17 | usbd-hid = "0.8.1" | 17 | usbd-hid = "0.9.0" |
| 18 | 18 | ||
| 19 | defmt = "1.0.1" | 19 | defmt = "1.0.1" |
| 20 | defmt-rtt = "1.0.0" | 20 | defmt-rtt = "1.0.0" |
