diff --git a/.github/ci/test-nightly.sh b/.github/ci/test-nightly.sh index d6e5dc574..1724ffe89 100755 --- a/.github/ci/test-nightly.sh +++ b/.github/ci/test-nightly.sh @@ -11,3 +11,4 @@ mv rust-toolchain-nightly.toml rust-toolchain.toml MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml --features nightly +MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-sync/Cargo.toml diff --git a/.github/ci/test.sh b/.github/ci/test.sh index 6cb3a4bff..41da644fc 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh @@ -8,6 +8,7 @@ export RUSTUP_HOME=/ci/cache/rustup export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target +cargo test --manifest-path ./embassy-futures/Cargo.toml cargo test --manifest-path ./embassy-sync/Cargo.toml cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml cargo test --manifest-path ./embassy-hal-internal/Cargo.toml diff --git a/.vscode/settings.json b/.vscode/settings.json index 0c195a13b..220d25914 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,10 +15,11 @@ "rust-analyzer.cargo.target": "thumbv7em-none-eabi", //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ - "stm32f103c8", + "stm32f446re", "time-driver-any", "unstable-pac", "exti", + "rt", ], "rust-analyzer.linkedProjects": [ // Uncomment ONE line for the chip you want to work on. diff --git a/ci.sh b/ci.sh index 1393b9d47..514c6769b 100755 --- a/ci.sh +++ b/ci.sh @@ -253,6 +253,9 @@ rm out/tests/stm32f207zg/eth # doesn't work, gives "noise error", no idea why. usart_dma does pass. rm out/tests/stm32u5a5zj/usart +# flaky, perhaps bad wire +rm out/tests/stm32l152re/usart_rx_ringbuffered + if [[ -z "${TELEPROBE_TOKEN-}" ]]; then echo No teleprobe token found, skipping running HIL tests exit diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs index 135b8c245..a3808f56f 100644 --- a/cyw43/src/control.rs +++ b/cyw43/src/control.rs @@ -124,8 +124,7 @@ impl<'a> Control<'a> { self.set_iovar_u32("apsta", 1).await; // read MAC addr. - let mut mac_addr = [0; 6]; - assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); + let mac_addr = self.address().await; debug!("mac addr: {:02x}", Bytes(&mac_addr)); let country = countries::WORLD_WIDE_XX; @@ -574,6 +573,13 @@ impl<'a> Control<'a> { self.ioctl(IoctlType::Set, IOCTL_CMD_DISASSOC, 0, &mut []).await; info!("Disassociated") } + + /// Gets the MAC address of the device + pub async fn address(&mut self) -> [u8; 6] { + let mut mac_addr = [0; 6]; + assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); + mac_addr + } } /// WiFi network scanner. diff --git a/embassy-futures/src/yield_now.rs b/embassy-futures/src/yield_now.rs index bb3c67d17..4d4e535f2 100644 --- a/embassy-futures/src/yield_now.rs +++ b/embassy-futures/src/yield_now.rs @@ -9,10 +9,16 @@ use core::task::{Context, Poll}; /// hold, while still allowing other tasks to run concurrently (not monopolizing /// the executor thread). /// -/// ```rust,no_run +/// ```rust +/// # use embassy_futures::{block_on, yield_now}; +/// # async fn test_fn() { +/// # let mut iter_count: u32 = 0; +/// # let mut some_condition = || { iter_count += 1; iter_count > 10 }; /// while !some_condition() { /// yield_now().await; /// } +/// # } +/// # block_on(test_fn()); /// ``` /// /// The downside is this will spin in a busy loop, using 100% of the CPU, while diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md new file mode 100644 index 000000000..773a1a108 --- /dev/null +++ b/embassy-nrf/CHANGELOG.md @@ -0,0 +1,40 @@ +# Changelog for embassy-nrf + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +- Drop `sealed` mod +- nrf52840: Add dcdc voltage parameter to configure REG0 regulator +- radio: Add support for IEEE 802.15.4 and BLE via radio peripheral +- spim: Reduce trace-level messages ("Copying SPIM tx buffer..") +- uart: Add support for rx- or tx-only BufferedUart +- uart: Implement splitting Rx/Tx +- spi: Allow specifying OutputDrive for SPI spins +- pdm: Fix gain register value derivation +- spim: Implement chunked DMA transfers +- spi: Add bounds checks for EasyDMA buffer size +- uarte: Add support for handling RX errors +- nrf51: Implement support for nrf51 chip +- pwm: Expose `duty` method +- pwm: Fix infinite loop +- spi: Add support for configuring bit order for bus +- pwm: Expose `pwm::PWM_CLK_HZ` and add `is_enabled` method +- gpio: Drop GPIO Pin generics (API break) + +## 0.1.0 - 2024-01-12 + +- First release with support for following NRF chips: + - nrf52805 + - nrf52810 + - nrf52811 + - nrf52820 + - nrf52832 + - nrf52833 + - nrf52840 + - nrf5340 + - nrf9160 + diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 459d2e370..1f9a51678 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -115,6 +115,9 @@ low-power-debug-with-sleep = [] ## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) memory-x = ["stm32-metapac/memory-x"] +## Use secure registers when TrustZone is enabled +trustzone-secure = [] + ## Re-export stm32-metapac at `embassy_stm32::pac`. ## This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. ## If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 0f87dd8ac..67fceda56 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -662,6 +662,7 @@ fn main() { #(pub use crate::pac::rcc::vals::#enum_names as #enum_names; )* #[derive(Clone, Copy)] + #[non_exhaustive] pub struct ClockMux { #( #struct_fields, )* } diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 7e3681469..8766d0a60 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -16,6 +16,9 @@ mod dmamux; #[cfg(dmamux)] pub use dmamux::*; +mod util; +pub(crate) use util::*; + pub(crate) mod ringbuffer; pub mod word; diff --git a/embassy-stm32/src/dma/util.rs b/embassy-stm32/src/dma/util.rs new file mode 100644 index 000000000..962ea2501 --- /dev/null +++ b/embassy-stm32/src/dma/util.rs @@ -0,0 +1,60 @@ +use embassy_hal_internal::PeripheralRef; + +use super::word::Word; +use super::{AnyChannel, Request, Transfer, TransferOptions}; + +/// Convenience wrapper, contains a channel and a request number. +/// +/// Commonly used in peripheral drivers that own DMA channels. +pub(crate) struct ChannelAndRequest<'d> { + pub channel: PeripheralRef<'d, AnyChannel>, + pub request: Request, +} + +impl<'d> ChannelAndRequest<'d> { + pub unsafe fn read<'a, W: Word>( + &'a mut self, + peri_addr: *mut W, + buf: &'a mut [W], + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_read(&mut self.channel, self.request, peri_addr, buf, options) + } + + pub unsafe fn read_raw<'a, W: Word>( + &'a mut self, + peri_addr: *mut W, + buf: *mut [W], + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_read_raw(&mut self.channel, self.request, peri_addr, buf, options) + } + + pub unsafe fn write<'a, W: Word>( + &'a mut self, + buf: &'a [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_write(&mut self.channel, self.request, buf, peri_addr, options) + } + + pub unsafe fn write_raw<'a, W: Word>( + &'a mut self, + buf: *const [W], + peri_addr: *mut W, + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_write_raw(&mut self.channel, self.request, buf, peri_addr, options) + } + + pub unsafe fn write_repeated<'a, W: Word>( + &'a mut self, + repeated: &'a W, + count: usize, + peri_addr: *mut W, + options: TransferOptions, + ) -> Transfer<'a> { + Transfer::new_write_repeated(&mut self.channel, self.request, repeated, count, peri_addr, options) + } +} diff --git a/embassy-stm32/src/flash/h50.rs b/embassy-stm32/src/flash/h50.rs index 5b15be261..56ea7a421 100644 --- a/embassy-stm32/src/flash/h50.rs +++ b/embassy-stm32/src/flash/h50.rs @@ -61,9 +61,12 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E interrupt::free(|_| { pac::FLASH.nscr().modify(|w| { - w.set_bksel(match sector.bank { - FlashBank::Bank1 => Bksel::BANK1, - FlashBank::Bank2 => Bksel::BANK2, + // BKSEL ignores SWAP_BANK, so we must take it into account here + w.set_bksel(match (sector.bank, banks_swapped()) { + (FlashBank::Bank1, false) => Bksel::BANK1, + (FlashBank::Bank2, true) => Bksel::BANK1, + (FlashBank::Bank2, false) => Bksel::BANK2, + (FlashBank::Bank1, true) => Bksel::BANK2, }); w.set_snb(sector.index_in_bank); w.set_ser(true); @@ -111,6 +114,47 @@ pub(crate) unsafe fn clear_all_err() { }) } +/// Get the current SWAP_BANK option. +/// +/// This value is only loaded on system or power-on reset. `perform_bank_swap()` +/// will not reflect here. +pub fn banks_swapped() -> bool { + pac::FLASH.optcr().read().swap_bank() +} + +/// Logical, persistent swap of flash banks 1 and 2. +/// +/// This allows the application to write a new firmware blob into bank 2, then +/// swap the banks and perform a reset, loading the new firmware. +/// +/// Swap does not take effect until system or power-on reset. +/// +/// PLEASE READ THE REFERENCE MANUAL - there are nuances to this feature. For +/// instance, erase commands and interrupt enables which take a flash bank as a +/// parameter ignore the swap! +pub fn perform_bank_swap() { + while busy() {} + + unsafe { + clear_all_err(); + } + + // unlock OPTLOCK + pac::FLASH.optkeyr().write(|w| *w = 0x0819_2A3B); + pac::FLASH.optkeyr().write(|w| *w = 0x4C5D_6E7F); + while pac::FLASH.optcr().read().optlock() {} + + // toggle SWAP_BANK option + pac::FLASH.optsr_prg().modify(|w| w.set_swap_bank(!banks_swapped())); + + // load option bytes + pac::FLASH.optcr().modify(|w| w.set_optstrt(true)); + while pac::FLASH.optcr().read().optstrt() {} + + // re-lock OPTLOCK + pac::FLASH.optcr().modify(|w| w.set_optlock(true)); +} + fn sr_busy(sr: Nssr) -> bool { // Note: RM0492 sometimes incorrectly refers to WBNE as NSWBNE sr.bsy() || sr.dbne() || sr.wbne() diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index 580c490da..ddd4d73ff 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs @@ -14,26 +14,43 @@ pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { } pub(crate) unsafe fn lock() { + #[cfg(feature = "trustzone-secure")] pac::FLASH.seccr().modify(|w| w.set_lock(true)); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH.nscr().modify(|w| w.set_lock(true)); } pub(crate) unsafe fn unlock() { + #[cfg(feature = "trustzone-secure")] if pac::FLASH.seccr().read().lock() { pac::FLASH.seckeyr().write_value(0x4567_0123); pac::FLASH.seckeyr().write_value(0xCDEF_89AB); } + #[cfg(not(feature = "trustzone-secure"))] + if pac::FLASH.nscr().read().lock() { + pac::FLASH.nskeyr().write_value(0x4567_0123); + pac::FLASH.nskeyr().write_value(0xCDEF_89AB); + } } pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); + #[cfg(feature = "trustzone-secure")] pac::FLASH.seccr().write(|w| { w.set_pg(pac::flash::vals::SeccrPg::B_0X1); }); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH.nscr().write(|w| { + w.set_pg(pac::flash::vals::NscrPg::B_0X1); + }); } pub(crate) unsafe fn disable_blocking_write() { + #[cfg(feature = "trustzone-secure")] pac::FLASH.seccr().write(|w| w.set_pg(pac::flash::vals::SeccrPg::B_0X0)); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH.nscr().write(|w| w.set_pg(pac::flash::vals::NscrPg::B_0X0)); } pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { @@ -50,19 +67,35 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) } pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + #[cfg(feature = "trustzone-secure")] pac::FLASH.seccr().modify(|w| { w.set_per(pac::flash::vals::SeccrPer::B_0X1); w.set_pnb(sector.index_in_bank) }); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH.nscr().modify(|w| { + w.set_per(pac::flash::vals::NscrPer::B_0X1); + w.set_pnb(sector.index_in_bank) + }); + #[cfg(feature = "trustzone-secure")] pac::FLASH.seccr().modify(|w| { w.set_strt(true); }); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH.nscr().modify(|w| { + w.set_strt(true); + }); let ret: Result<(), Error> = blocking_wait_ready(); + #[cfg(feature = "trustzone-secure")] pac::FLASH .seccr() .modify(|w| w.set_per(pac::flash::vals::SeccrPer::B_0X0)); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH + .nscr() + .modify(|w| w.set_per(pac::flash::vals::NscrPer::B_0X0)); clear_all_err(); ret } @@ -70,12 +103,18 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E pub(crate) unsafe fn clear_all_err() { // read and write back the same value. // This clears all "write 1 to clear" bits. + #[cfg(feature = "trustzone-secure")] pac::FLASH.secsr().modify(|_| {}); + #[cfg(not(feature = "trustzone-secure"))] + pac::FLASH.nssr().modify(|_| {}); } unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { + #[cfg(feature = "trustzone-secure")] let sr = pac::FLASH.secsr().read(); + #[cfg(not(feature = "trustzone-secure"))] + let sr = pac::FLASH.nssr().read(); if !sr.bsy() { if sr.pgserr() { diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index a46061d54..ccbea9831 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -14,9 +14,10 @@ use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; -use crate::dma::NoDma; +use crate::dma::ChannelAndRequest; use crate::gpio::{AFType, Pull}; use crate::interrupt::typelevel::Interrupt; +use crate::mode::{Async, Blocking, Mode}; use crate::time::Hertz; use crate::{interrupt, peripherals}; @@ -71,17 +72,16 @@ impl Default for Config { } /// I2C driver. -pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> { +pub struct I2c<'d, T: Instance, M: Mode> { _peri: PeripheralRef<'d, T>, - #[allow(dead_code)] - tx_dma: PeripheralRef<'d, TXDMA>, - #[allow(dead_code)] - rx_dma: PeripheralRef<'d, RXDMA>, + tx_dma: Option>, + rx_dma: Option>, #[cfg(feature = "time")] timeout: Duration, + _phantom: PhantomData, } -impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance> I2c<'d, T, Async> { /// Create a new I2C driver. pub fn new( peri: impl Peripheral

+ 'd, @@ -90,12 +90,40 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { _irq: impl interrupt::typelevel::Binding> + interrupt::typelevel::Binding> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, freq: Hertz, config: Config, ) -> Self { - into_ref!(peri, scl, sda, tx_dma, rx_dma); + Self::new_inner(peri, scl, sda, new_dma!(tx_dma), new_dma!(rx_dma), freq, config) + } +} + +impl<'d, T: Instance> I2c<'d, T, Blocking> { + /// Create a new blocking I2C driver. + pub fn new_blocking( + peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + freq: Hertz, + config: Config, + ) -> Self { + Self::new_inner(peri, scl, sda, None, None, freq, config) + } +} + +impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { + /// Create a new I2C driver. + fn new_inner( + peri: impl Peripheral

+ 'd, + scl: impl Peripheral

> + 'd, + sda: impl Peripheral

> + 'd, + tx_dma: Option>, + rx_dma: Option>, + freq: Hertz, + config: Config, + ) -> Self { + into_ref!(peri, scl, sda); T::enable_and_reset(); @@ -125,6 +153,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { rx_dma, #[cfg(feature = "time")] timeout: config.timeout, + _phantom: PhantomData, }; this.init(freq, config); @@ -249,7 +278,7 @@ foreach_peripheral!( }; ); -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { +impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> { type Error = Error; fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -257,7 +286,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Read for I2c<'d, T> { } } -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { +impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> { type Error = Error; fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { @@ -265,7 +294,7 @@ impl<'d, T: Instance> embedded_hal_02::blocking::i2c::Write for I2c<'d, T> { } } -impl<'d, T: Instance> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T> { +impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> { type Error = Error; fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { @@ -289,11 +318,11 @@ impl embedded_hal_1::i2c::Error for Error { } } -impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> { type Error = Error; } -impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> { +impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> { fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, read) } @@ -315,7 +344,7 @@ impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> { } } -impl<'d, T: Instance, TXDMA: TxDma, RXDMA: RxDma> embedded_hal_async::i2c::I2c for I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance> embedded_hal_async::i2c::I2c for I2c<'d, T, Async> { async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.read(address, read).await } diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index d45c48b24..13a473344 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -13,7 +13,7 @@ use embassy_hal_internal::drop::OnDrop; use embedded_hal_1::i2c::Operation; use super::*; -use crate::dma::Transfer; +use crate::mode::Mode as PeriMode; use crate::pac::i2c; // /!\ /!\ @@ -41,7 +41,7 @@ pub unsafe fn on_interrupt() { }); } -impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance, M: PeriMode> I2c<'d, T, M> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { T::regs().cr1().modify(|reg| { reg.set_pe(false); @@ -326,11 +326,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_itevten(true); }); } +} - async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { +impl<'d, T: Instance> I2c<'d, T, Async> { + async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> { T::regs().cr2().modify(|w| { // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for // reception. @@ -415,9 +414,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // this address from the memory after each TxE event. let dst = T::regs().dr().as_ptr() as *mut u8; - let ch = &mut self.tx_dma; - let request = ch.request(); - Transfer::new_write(ch, request, write, dst, Default::default()) + self.tx_dma.as_mut().unwrap().write(write, dst, Default::default()) }; // Wait for bytes to be sent, or an error to occur. @@ -479,10 +476,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } /// Write. - pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { + pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { self.write_frame(address, write, FrameOptions::FirstAndLastFrame) .await?; @@ -490,20 +484,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } /// Read. - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - { + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { self.read_frame(address, buffer, FrameOptions::FirstAndLastFrame) .await?; Ok(()) } - async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - { + async fn read_frame(&mut self, address: u8, buffer: &mut [u8], frame: FrameOptions) -> Result<(), Error> { if buffer.is_empty() { return Err(Error::Overrun); } @@ -623,9 +611,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // from this address from the memory after each RxE event. let src = T::regs().dr().as_ptr() as *mut u8; - let ch = &mut self.rx_dma; - let request = ch.request(); - Transfer::new_read(ch, request, src, buffer, Default::default()) + self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) }; // Wait for bytes to be received, or an error to occur. @@ -664,11 +650,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } /// Write, restart, read. - pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - TXDMA: crate::i2c::TxDma, - { + pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { // Check empty read buffer before starting transaction. Otherwise, we would not generate the // stop condition below. if read.is_empty() { @@ -684,11 +666,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { /// Consecutive operations of same type are merged. See [transaction contract] for details. /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction - pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - TXDMA: crate::i2c::TxDma, - { + pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { for (op, frame) in operation_frames(operations)? { match op { Operation::Read(read) => self.read_frame(addr, read, frame).await?, @@ -700,7 +678,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } -impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance, M: PeriMode> Drop for I2c<'d, T, M> { fn drop(&mut self) { T::disable(); } @@ -806,7 +784,7 @@ impl Timings { } } -impl<'d, T: Instance> SetConfig for I2c<'d, T> { +impl<'d, T: Instance, M: PeriMode> SetConfig for I2c<'d, T, M> { type Config = Hertz; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index da3b0ee30..12df98534 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -7,7 +7,6 @@ use embassy_hal_internal::drop::OnDrop; use embedded_hal_1::i2c::Operation; use super::*; -use crate::dma::Transfer; use crate::pac::i2c; pub(crate) unsafe fn on_interrupt() { @@ -24,7 +23,7 @@ pub(crate) unsafe fn on_interrupt() { }); } -impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { T::regs().cr1().modify(|reg| { reg.set_pe(false); @@ -302,276 +301,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { result } - async fn write_dma_internal( - &mut self, - address: u8, - write: &[u8], - first_slice: bool, - last_slice: bool, - timeout: Timeout, - ) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - let total_len = write.len(); - - let dma_transfer = unsafe { - let regs = T::regs(); - regs.cr1().modify(|w| { - w.set_txdmaen(true); - if first_slice { - w.set_tcie(true); - } - }); - let dst = regs.txdr().as_ptr() as *mut u8; - - let ch = &mut self.tx_dma; - let request = ch.request(); - Transfer::new_write(ch, request, write, dst, Default::default()) - }; - - let state = T::state(); - let mut remaining_len = total_len; - - let on_drop = OnDrop::new(|| { - let regs = T::regs(); - regs.cr1().modify(|w| { - if last_slice { - w.set_txdmaen(false); - } - w.set_tcie(false); - }) - }); - - poll_fn(|cx| { - state.waker.register(cx.waker()); - - let isr = T::regs().isr().read(); - if remaining_len == total_len { - if first_slice { - Self::master_write( - address, - total_len.min(255), - Stop::Software, - (total_len > 255) || !last_slice, - timeout, - )?; - } else { - Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, timeout)?; - T::regs().cr1().modify(|w| w.set_tcie(true)); - } - } else if !(isr.tcr() || isr.tc()) { - // poll_fn was woken without an interrupt present - return Poll::Pending; - } else if remaining_len == 0 { - return Poll::Ready(Ok(())); - } else { - let last_piece = (remaining_len <= 255) && last_slice; - - if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) { - return Poll::Ready(Err(e)); - } - T::regs().cr1().modify(|w| w.set_tcie(true)); - } - - remaining_len = remaining_len.saturating_sub(255); - Poll::Pending - }) - .await?; - - dma_transfer.await; - - if last_slice { - // This should be done already - self.wait_tc(timeout)?; - self.master_stop(); - } - - drop(on_drop); - - Ok(()) - } - - async fn read_dma_internal( - &mut self, - address: u8, - buffer: &mut [u8], - restart: bool, - timeout: Timeout, - ) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - { - let total_len = buffer.len(); - - let dma_transfer = unsafe { - let regs = T::regs(); - regs.cr1().modify(|w| { - w.set_rxdmaen(true); - w.set_tcie(true); - }); - let src = regs.rxdr().as_ptr() as *mut u8; - - let ch = &mut self.rx_dma; - let request = ch.request(); - Transfer::new_read(ch, request, src, buffer, Default::default()) - }; - - let state = T::state(); - let mut remaining_len = total_len; - - let on_drop = OnDrop::new(|| { - let regs = T::regs(); - regs.cr1().modify(|w| { - w.set_rxdmaen(false); - w.set_tcie(false); - }) - }); - - poll_fn(|cx| { - state.waker.register(cx.waker()); - - let isr = T::regs().isr().read(); - if remaining_len == total_len { - Self::master_read( - address, - total_len.min(255), - Stop::Software, - total_len > 255, - restart, - timeout, - )?; - } else if !(isr.tcr() || isr.tc()) { - // poll_fn was woken without an interrupt present - return Poll::Pending; - } else if remaining_len == 0 { - return Poll::Ready(Ok(())); - } else { - let last_piece = remaining_len <= 255; - - if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) { - return Poll::Ready(Err(e)); - } - T::regs().cr1().modify(|w| w.set_tcie(true)); - } - - remaining_len = remaining_len.saturating_sub(255); - Poll::Pending - }) - .await?; - - dma_transfer.await; - - // This should be done already - self.wait_tc(timeout)?; - self.master_stop(); - - drop(on_drop); - - Ok(()) - } - - // ========================= - // Async public API - - /// Write. - pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - let timeout = self.timeout(); - if write.is_empty() { - self.write_internal(address, write, true, timeout) - } else { - timeout - .with(self.write_dma_internal(address, write, true, true, timeout)) - .await - } - } - - /// Write multiple buffers. - /// - /// The buffers are concatenated in a single write transaction. - pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> - where - TXDMA: crate::i2c::TxDma, - { - let timeout = self.timeout(); - - if write.is_empty() { - return Err(Error::ZeroLengthTransfer); - } - let mut iter = write.iter(); - - let mut first = true; - let mut current = iter.next(); - while let Some(c) = current { - let next = iter.next(); - let is_last = next.is_none(); - - let fut = self.write_dma_internal(address, c, first, is_last, timeout); - timeout.with(fut).await?; - first = false; - current = next; - } - Ok(()) - } - - /// Read. - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - { - let timeout = self.timeout(); - - if buffer.is_empty() { - self.read_internal(address, buffer, false, timeout) - } else { - let fut = self.read_dma_internal(address, buffer, false, timeout); - timeout.with(fut).await - } - } - - /// Write, restart, read. - pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> - where - TXDMA: super::TxDma, - RXDMA: super::RxDma, - { - let timeout = self.timeout(); - - if write.is_empty() { - self.write_internal(address, write, false, timeout)?; - } else { - let fut = self.write_dma_internal(address, write, true, true, timeout); - timeout.with(fut).await?; - } - - if read.is_empty() { - self.read_internal(address, read, true, timeout)?; - } else { - let fut = self.read_dma_internal(address, read, true, timeout); - timeout.with(fut).await?; - } - - Ok(()) - } - - /// Transaction with operations. - /// - /// Consecutive operations of same type are merged. See [transaction contract] for details. - /// - /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction - pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> - where - RXDMA: crate::i2c::RxDma, - TXDMA: crate::i2c::TxDma, - { - let _ = addr; - let _ = operations; - todo!() - } - // ========================= // Blocking public API @@ -684,7 +413,252 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } -impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> { +impl<'d, T: Instance> I2c<'d, T, Async> { + async fn write_dma_internal( + &mut self, + address: u8, + write: &[u8], + first_slice: bool, + last_slice: bool, + timeout: Timeout, + ) -> Result<(), Error> { + let total_len = write.len(); + + let dma_transfer = unsafe { + let regs = T::regs(); + regs.cr1().modify(|w| { + w.set_txdmaen(true); + if first_slice { + w.set_tcie(true); + } + }); + let dst = regs.txdr().as_ptr() as *mut u8; + + self.tx_dma.as_mut().unwrap().write(write, dst, Default::default()) + }; + + let state = T::state(); + let mut remaining_len = total_len; + + let on_drop = OnDrop::new(|| { + let regs = T::regs(); + regs.cr1().modify(|w| { + if last_slice { + w.set_txdmaen(false); + } + w.set_tcie(false); + }) + }); + + poll_fn(|cx| { + state.waker.register(cx.waker()); + + let isr = T::regs().isr().read(); + if remaining_len == total_len { + if first_slice { + Self::master_write( + address, + total_len.min(255), + Stop::Software, + (total_len > 255) || !last_slice, + timeout, + )?; + } else { + Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, timeout)?; + T::regs().cr1().modify(|w| w.set_tcie(true)); + } + } else if !(isr.tcr() || isr.tc()) { + // poll_fn was woken without an interrupt present + return Poll::Pending; + } else if remaining_len == 0 { + return Poll::Ready(Ok(())); + } else { + let last_piece = (remaining_len <= 255) && last_slice; + + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) { + return Poll::Ready(Err(e)); + } + T::regs().cr1().modify(|w| w.set_tcie(true)); + } + + remaining_len = remaining_len.saturating_sub(255); + Poll::Pending + }) + .await?; + + dma_transfer.await; + + if last_slice { + // This should be done already + self.wait_tc(timeout)?; + self.master_stop(); + } + + drop(on_drop); + + Ok(()) + } + + async fn read_dma_internal( + &mut self, + address: u8, + buffer: &mut [u8], + restart: bool, + timeout: Timeout, + ) -> Result<(), Error> { + let total_len = buffer.len(); + + let dma_transfer = unsafe { + let regs = T::regs(); + regs.cr1().modify(|w| { + w.set_rxdmaen(true); + w.set_tcie(true); + }); + let src = regs.rxdr().as_ptr() as *mut u8; + + self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) + }; + + let state = T::state(); + let mut remaining_len = total_len; + + let on_drop = OnDrop::new(|| { + let regs = T::regs(); + regs.cr1().modify(|w| { + w.set_rxdmaen(false); + w.set_tcie(false); + }) + }); + + poll_fn(|cx| { + state.waker.register(cx.waker()); + + let isr = T::regs().isr().read(); + if remaining_len == total_len { + Self::master_read( + address, + total_len.min(255), + Stop::Software, + total_len > 255, + restart, + timeout, + )?; + } else if !(isr.tcr() || isr.tc()) { + // poll_fn was woken without an interrupt present + return Poll::Pending; + } else if remaining_len == 0 { + return Poll::Ready(Ok(())); + } else { + let last_piece = remaining_len <= 255; + + if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, timeout) { + return Poll::Ready(Err(e)); + } + T::regs().cr1().modify(|w| w.set_tcie(true)); + } + + remaining_len = remaining_len.saturating_sub(255); + Poll::Pending + }) + .await?; + + dma_transfer.await; + + // This should be done already + self.wait_tc(timeout)?; + self.master_stop(); + + drop(on_drop); + + Ok(()) + } + + // ========================= + // Async public API + + /// Write. + pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + let timeout = self.timeout(); + if write.is_empty() { + self.write_internal(address, write, true, timeout) + } else { + timeout + .with(self.write_dma_internal(address, write, true, true, timeout)) + .await + } + } + + /// Write multiple buffers. + /// + /// The buffers are concatenated in a single write transaction. + pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { + let timeout = self.timeout(); + + if write.is_empty() { + return Err(Error::ZeroLengthTransfer); + } + let mut iter = write.iter(); + + let mut first = true; + let mut current = iter.next(); + while let Some(c) = current { + let next = iter.next(); + let is_last = next.is_none(); + + let fut = self.write_dma_internal(address, c, first, is_last, timeout); + timeout.with(fut).await?; + first = false; + current = next; + } + Ok(()) + } + + /// Read. + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + let timeout = self.timeout(); + + if buffer.is_empty() { + self.read_internal(address, buffer, false, timeout) + } else { + let fut = self.read_dma_internal(address, buffer, false, timeout); + timeout.with(fut).await + } + } + + /// Write, restart, read. + pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + let timeout = self.timeout(); + + if write.is_empty() { + self.write_internal(address, write, false, timeout)?; + } else { + let fut = self.write_dma_internal(address, write, true, true, timeout); + timeout.with(fut).await?; + } + + if read.is_empty() { + self.read_internal(address, read, true, timeout)?; + } else { + let fut = self.read_dma_internal(address, read, true, timeout); + timeout.with(fut).await?; + } + + Ok(()) + } + + /// Transaction with operations. + /// + /// Consecutive operations of same type are merged. See [transaction contract] for details. + /// + /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction + pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { + let _ = addr; + let _ = operations; + todo!() + } +} + +impl<'d, T: Instance, M: Mode> Drop for I2c<'d, T, M> { fn drop(&mut self) { T::disable(); } @@ -814,7 +788,7 @@ impl Timings { } } -impl<'d, T: Instance> SetConfig for I2c<'d, T> { +impl<'d, T: Instance, M: Mode> SetConfig for I2c<'d, T, M> { type Config = Hertz; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index c5a606b21..9b80dc1d0 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -2,6 +2,7 @@ use embassy_hal_internal::into_ref; use crate::gpio::{AFType, AnyPin, SealedPin}; +use crate::mode::Async; use crate::pac::spi::vals; use crate::spi::{Config as SpiConfig, *}; use crate::time::Hertz; @@ -152,15 +153,15 @@ impl Default for Config { } /// I2S driver. -pub struct I2S<'d, T: Instance, Tx, Rx> { - _peri: Spi<'d, T, Tx, Rx>, +pub struct I2S<'d, T: Instance> { + _peri: Spi<'d, T, Async>, sd: Option>, ws: Option>, ck: Option>, mck: Option>, } -impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { +impl<'d, T: Instance> I2S<'d, T> { /// Note: Full-Duplex modes are not supported at this time pub fn new( peri: impl Peripheral

+ 'd, @@ -168,8 +169,8 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { ws: impl Peripheral

> + 'd, ck: impl Peripheral

> + 'd, mck: impl Peripheral

> + 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, + txdma: impl Peripheral

> + 'd, + rxdma: impl Peripheral

> + 'd, freq: Hertz, config: Config, ) -> Self { @@ -265,24 +266,17 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { } /// Write audio data. - pub async fn write(&mut self, data: &[W]) -> Result<(), Error> - where - Tx: TxDma, - { + pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { self._peri.write(data).await } /// Read audio data. - pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> - where - Tx: TxDma, - Rx: RxDma, - { + pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { self._peri.read(data).await } } -impl<'d, T: Instance, Tx, Rx> Drop for I2S<'d, T, Tx, Rx> { +impl<'d, T: Instance> Drop for I2S<'d, T> { fn drop(&mut self) { self.sd.as_ref().map(|x| x.set_as_disconnected()); self.ws.as_ref().map(|x| x.set_as_disconnected()); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index ea17f8477..7d2b49ff4 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -15,8 +15,31 @@ mod fmt; include!(concat!(env!("OUT_DIR"), "/_macros.rs")); // Utilities +mod macros; pub mod time; -mod traits; +/// Operating modes for peripherals. +pub mod mode { + trait SealedMode {} + + /// Operating mode for a peripheral. + #[allow(private_bounds)] + pub trait Mode: SealedMode {} + + macro_rules! impl_mode { + ($name:ident) => { + impl SealedMode for $name {} + impl Mode for $name {} + }; + } + + /// Blocking mode. + pub struct Blocking; + /// Async mode. + pub struct Async; + + impl_mode!(Blocking); + impl_mode!(Async); +} // Always-present hardware pub mod dma; diff --git a/embassy-stm32/src/traits.rs b/embassy-stm32/src/macros.rs similarity index 77% rename from embassy-stm32/src/traits.rs rename to embassy-stm32/src/macros.rs index 13f695821..14137bc37 100644 --- a/embassy-stm32/src/traits.rs +++ b/embassy-stm32/src/macros.rs @@ -69,3 +69,29 @@ macro_rules! dma_trait_impl { } }; } + +macro_rules! new_dma { + ($name:ident) => {{ + let dma = $name.into_ref(); + let request = dma.request(); + Some(crate::dma::ChannelAndRequest { + channel: dma.map_into(), + request, + }) + }}; +} + +macro_rules! new_pin { + ($name:ident, $aftype:expr) => {{ + new_pin!($name, $aftype, crate::gpio::Speed::Medium, crate::gpio::Pull::None) + }}; + ($name:ident, $aftype:expr, $speed:expr) => { + new_pin!($name, $aftype, $speed, crate::gpio::Pull::None) + }; + ($name:ident, $aftype:expr, $speed:expr, $pull:expr) => {{ + let pin = $name.into_ref(); + pin.set_as_af_pull(pin.af_num(), $aftype, $pull); + pin.set_speed($speed); + Some(pin.map_into()) + }}; +} diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 00f21ed68..a4e497fe7 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -119,3 +119,21 @@ mod util { pub fn frequency() -> Hertz { T::frequency() } + +/// Enables and resets peripheral `T`. +/// +/// # Safety +/// +/// Peripheral must not be in use. +pub unsafe fn enable_and_reset() { + T::enable_and_reset(); +} + +/// Disables peripheral `T`. +/// +/// # Safety +/// +/// Peripheral must not be in use. +pub unsafe fn disable() { + T::disable(); +} diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 340cfde03..c39ef1913 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -1,6 +1,7 @@ //! Serial Peripheral Interface (SPI) #![macro_use] +use core::marker::PhantomData; use core::ptr; use embassy_embedded_hal::SetConfig; @@ -8,8 +9,9 @@ use embassy_futures::join::join; use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; -use crate::dma::{slice_ptr_parts, word, Transfer}; -use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _}; +use crate::dma::{slice_ptr_parts, word, ChannelAndRequest}; +use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _, Speed}; +use crate::mode::{Async, Blocking, Mode as PeriMode}; use crate::pac::spi::{regs, vals, Spi as Regs}; use crate::rcc::RccPeripheral; use crate::time::Hertz; @@ -81,163 +83,37 @@ impl Config { BitOrder::MsbFirst => vals::Lsbfirst::MSBFIRST, } } -} + fn sck_pull_mode(&self) -> Pull { + match self.mode.polarity { + Polarity::IdleLow => Pull::Down, + Polarity::IdleHigh => Pull::Up, + } + } +} /// SPI driver. -pub struct Spi<'d, T: Instance, Tx, Rx> { +pub struct Spi<'d, T: Instance, M: PeriMode> { _peri: PeripheralRef<'d, T>, sck: Option>, mosi: Option>, miso: Option>, - txdma: PeripheralRef<'d, Tx>, - rxdma: PeripheralRef<'d, Rx>, + tx_dma: Option>, + rx_dma: Option>, + _phantom: PhantomData, current_word_size: word_impl::Config, } -impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { - /// Create a new SPI driver. - pub fn new( - peri: impl Peripheral

+ 'd, - sck: impl Peripheral

> + 'd, - mosi: impl Peripheral

> + 'd, - miso: impl Peripheral

> + 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, sck, mosi, miso); - - let sck_pull_mode = match config.mode.polarity { - Polarity::IdleLow => Pull::Down, - Polarity::IdleHigh => Pull::Up, - }; - - sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, sck_pull_mode); - sck.set_speed(crate::gpio::Speed::VeryHigh); - mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); - mosi.set_speed(crate::gpio::Speed::VeryHigh); - miso.set_as_af(miso.af_num(), AFType::Input); - miso.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(sck.map_into()), - Some(mosi.map_into()), - Some(miso.map_into()), - txdma, - rxdma, - config, - ) - } - - /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI). - pub fn new_rxonly( - peri: impl Peripheral

+ 'd, - sck: impl Peripheral

> + 'd, - miso: impl Peripheral

> + 'd, - txdma: impl Peripheral

+ 'd, // TODO remove - rxdma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(sck, miso); - sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - sck.set_speed(crate::gpio::Speed::VeryHigh); - miso.set_as_af(miso.af_num(), AFType::Input); - miso.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(sck.map_into()), - None, - Some(miso.map_into()), - txdma, - rxdma, - config, - ) - } - - /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO). - pub fn new_txonly( - peri: impl Peripheral

+ 'd, - sck: impl Peripheral

> + 'd, - mosi: impl Peripheral

> + 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, // TODO remove - config: Config, - ) -> Self { - into_ref!(sck, mosi); - sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - sck.set_speed(crate::gpio::Speed::VeryHigh); - mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull); - mosi.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(sck.map_into()), - Some(mosi.map_into()), - None, - txdma, - rxdma, - config, - ) - } - - /// Create a new SPI driver, in TX-only mode, without SCK pin. - /// - /// This can be useful for bit-banging non-SPI protocols. - pub fn new_txonly_nosck( - peri: impl Peripheral

+ 'd, - mosi: impl Peripheral

> + 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, // TODO: remove - config: Config, - ) -> Self { - into_ref!(mosi); - mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down); - mosi.set_speed(crate::gpio::Speed::Medium); - - Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, config) - } - - #[cfg(stm32wl)] - /// Useful for on chip peripherals like SUBGHZ which are hardwired. - pub fn new_subghz( - peri: impl Peripheral

+ 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, - ) -> Self { - // see RM0453 rev 1 section 7.2.13 page 291 - // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. - // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz. - let pclk3_freq = ::frequency().0; - let freq = Hertz(core::cmp::min(pclk3_freq / 2, 16_000_000)); - let mut config = Config::default(); - config.mode = MODE_0; - config.bit_order = BitOrder::MsbFirst; - config.frequency = freq; - Self::new_inner(peri, None, None, None, txdma, rxdma, config) - } - - #[allow(dead_code)] - pub(crate) fn new_internal( - peri: impl Peripheral

+ 'd, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - Self::new_inner(peri, None, None, None, txdma, rxdma, config) - } - +impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { fn new_inner( peri: impl Peripheral

+ 'd, sck: Option>, mosi: Option>, miso: Option>, - txdma: impl Peripheral

+ 'd, - rxdma: impl Peripheral

+ 'd, + tx_dma: Option>, + rx_dma: Option>, config: Config, ) -> Self { - into_ref!(peri, txdma, rxdma); + into_ref!(peri); let pclk = T::frequency(); let freq = config.frequency; @@ -333,9 +209,10 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { sck, mosi, miso, - txdma, - rxdma, + tx_dma, + rx_dma, current_word_size: ::CONFIG, + _phantom: PhantomData, } } @@ -462,169 +339,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { self.current_word_size = word_size; } - /// SPI write, using DMA. - pub async fn write(&mut self, data: &[W]) -> Result<(), Error> - where - Tx: TxDma, - { - if data.is_empty() { - return Ok(()); - } - - self.set_word_size(W::CONFIG); - T::REGS.cr1().modify(|w| { - w.set_spe(false); - }); - - let tx_request = self.txdma.request(); - let tx_dst = T::REGS.tx_ptr(); - let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) }; - - set_txdmaen(T::REGS, true); - T::REGS.cr1().modify(|w| { - w.set_spe(true); - }); - #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cr1().modify(|w| { - w.set_cstart(true); - }); - - tx_f.await; - - finish_dma(T::REGS); - - Ok(()) - } - - /// SPI read, using DMA. - pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> - where - Tx: TxDma, - Rx: RxDma, - { - if data.is_empty() { - return Ok(()); - } - - self.set_word_size(W::CONFIG); - T::REGS.cr1().modify(|w| { - w.set_spe(false); - }); - - // SPIv3 clears rxfifo on SPE=0 - #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] - flush_rx_fifo(T::REGS); - - set_rxdmaen(T::REGS, true); - - let clock_byte_count = data.len(); - - let rx_request = self.rxdma.request(); - let rx_src = T::REGS.rx_ptr(); - let rx_f = unsafe { Transfer::new_read(&mut self.rxdma, rx_request, rx_src, data, Default::default()) }; - - let tx_request = self.txdma.request(); - let tx_dst = T::REGS.tx_ptr(); - let clock_byte = 0x00u8; - let tx_f = unsafe { - Transfer::new_write_repeated( - &mut self.txdma, - tx_request, - &clock_byte, - clock_byte_count, - tx_dst, - Default::default(), - ) - }; - - set_txdmaen(T::REGS, true); - T::REGS.cr1().modify(|w| { - w.set_spe(true); - }); - #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cr1().modify(|w| { - w.set_cstart(true); - }); - - join(tx_f, rx_f).await; - - finish_dma(T::REGS); - - Ok(()) - } - - async fn transfer_inner(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> - where - Tx: TxDma, - Rx: RxDma, - { - let (_, rx_len) = slice_ptr_parts(read); - let (_, tx_len) = slice_ptr_parts(write); - assert_eq!(rx_len, tx_len); - if rx_len == 0 { - return Ok(()); - } - - self.set_word_size(W::CONFIG); - T::REGS.cr1().modify(|w| { - w.set_spe(false); - }); - - // SPIv3 clears rxfifo on SPE=0 - #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] - flush_rx_fifo(T::REGS); - - set_rxdmaen(T::REGS, true); - - let rx_request = self.rxdma.request(); - let rx_src = T::REGS.rx_ptr(); - let rx_f = unsafe { Transfer::new_read_raw(&mut self.rxdma, rx_request, rx_src, read, Default::default()) }; - - let tx_request = self.txdma.request(); - let tx_dst = T::REGS.tx_ptr(); - let tx_f = unsafe { Transfer::new_write_raw(&mut self.txdma, tx_request, write, tx_dst, Default::default()) }; - - set_txdmaen(T::REGS, true); - T::REGS.cr1().modify(|w| { - w.set_spe(true); - }); - #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cr1().modify(|w| { - w.set_cstart(true); - }); - - join(tx_f, rx_f).await; - - finish_dma(T::REGS); - - Ok(()) - } - - /// Bidirectional transfer, using DMA. - /// - /// This transfers both buffers at the same time, so it is NOT equivalent to `write` followed by `read`. - /// - /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. - /// If `write` is shorter it is padded with zero bytes. - pub async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> - where - Tx: TxDma, - Rx: RxDma, - { - self.transfer_inner(read, write).await - } - - /// In-place bidirectional transfer, using DMA. - /// - /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. - pub async fn transfer_in_place(&mut self, data: &mut [W]) -> Result<(), Error> - where - Tx: TxDma, - Rx: RxDma, - { - self.transfer_inner(data, data).await - } - /// Blocking write. pub fn blocking_write(&mut self, words: &[W]) -> Result<(), Error> { T::REGS.cr1().modify(|w| w.set_spe(true)); @@ -682,7 +396,334 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { } } -impl<'d, T: Instance, Tx, Rx> Drop for Spi<'d, T, Tx, Rx> { +impl<'d, T: Instance> Spi<'d, T, Blocking> { + /// Create a new blocking SPI driver. + pub fn new_blocking( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + mosi: impl Peripheral

> + 'd, + miso: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), + new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(miso, AFType::Input, Speed::VeryHigh), + None, + None, + config, + ) + } + + /// Create a new blocking SPI driver, in RX-only mode (only MISO pin, no MOSI). + pub fn new_blocking_rxonly( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + miso: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), + None, + new_pin!(miso, AFType::Input, Speed::VeryHigh), + None, + None, + config, + ) + } + + /// Create a new blocking SPI driver, in TX-only mode (only MOSI pin, no MISO). + pub fn new_blocking_txonly( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + mosi: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), + new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), + None, + None, + None, + config, + ) + } + + /// Create a new SPI driver, in TX-only mode, without SCK pin. + /// + /// This can be useful for bit-banging non-SPI protocols. + pub fn new_blocking_txonly_nosck( + peri: impl Peripheral

+ 'd, + mosi: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + None, + new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), + None, + None, + None, + config, + ) + } +} + +impl<'d, T: Instance> Spi<'d, T, Async> { + /// Create a new SPI driver. + pub fn new( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + mosi: impl Peripheral

> + 'd, + miso: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), + new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(miso, AFType::Input, Speed::VeryHigh), + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) + } + + /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI). + pub fn new_rxonly( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + miso: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), + None, + new_pin!(miso, AFType::Input, Speed::VeryHigh), + None, + new_dma!(rx_dma), + config, + ) + } + + /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO). + pub fn new_txonly( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + mosi: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), + new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), + None, + new_dma!(tx_dma), + None, + config, + ) + } + + /// Create a new SPI driver, in TX-only mode, without SCK pin. + /// + /// This can be useful for bit-banging non-SPI protocols. + pub fn new_txonly_nosck( + peri: impl Peripheral

+ 'd, + mosi: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + None, + new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), + None, + new_dma!(tx_dma), + None, + config, + ) + } + + #[cfg(stm32wl)] + /// Useful for on chip peripherals like SUBGHZ which are hardwired. + pub fn new_subghz( + peri: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + ) -> Self { + // see RM0453 rev 1 section 7.2.13 page 291 + // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. + // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz. + let pclk3_freq = ::frequency().0; + let freq = Hertz(core::cmp::min(pclk3_freq / 2, 16_000_000)); + let mut config = Config::default(); + config.mode = MODE_0; + config.bit_order = BitOrder::MsbFirst; + config.frequency = freq; + + Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) + } + + #[allow(dead_code)] + pub(crate) fn new_internal( + peri: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) + } + + /// SPI write, using DMA. + pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { + if data.is_empty() { + return Ok(()); + } + + self.set_word_size(W::CONFIG); + T::REGS.cr1().modify(|w| { + w.set_spe(false); + }); + + let tx_dst = T::REGS.tx_ptr(); + let tx_f = unsafe { self.tx_dma.as_mut().unwrap().write(data, tx_dst, Default::default()) }; + + set_txdmaen(T::REGS, true); + T::REGS.cr1().modify(|w| { + w.set_spe(true); + }); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + T::REGS.cr1().modify(|w| { + w.set_cstart(true); + }); + + tx_f.await; + + finish_dma(T::REGS); + + Ok(()) + } + + /// SPI read, using DMA. + pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { + if data.is_empty() { + return Ok(()); + } + + self.set_word_size(W::CONFIG); + T::REGS.cr1().modify(|w| { + w.set_spe(false); + }); + + // SPIv3 clears rxfifo on SPE=0 + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + flush_rx_fifo(T::REGS); + + set_rxdmaen(T::REGS, true); + + let clock_byte_count = data.len(); + + let rx_src = T::REGS.rx_ptr(); + let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read(rx_src, data, Default::default()) }; + + let tx_dst = T::REGS.tx_ptr(); + let clock_byte = 0x00u8; + let tx_f = unsafe { + self.tx_dma + .as_mut() + .unwrap() + .write_repeated(&clock_byte, clock_byte_count, tx_dst, Default::default()) + }; + + set_txdmaen(T::REGS, true); + T::REGS.cr1().modify(|w| { + w.set_spe(true); + }); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + T::REGS.cr1().modify(|w| { + w.set_cstart(true); + }); + + join(tx_f, rx_f).await; + + finish_dma(T::REGS); + + Ok(()) + } + + async fn transfer_inner(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> { + let (_, rx_len) = slice_ptr_parts(read); + let (_, tx_len) = slice_ptr_parts(write); + assert_eq!(rx_len, tx_len); + if rx_len == 0 { + return Ok(()); + } + + self.set_word_size(W::CONFIG); + T::REGS.cr1().modify(|w| { + w.set_spe(false); + }); + + // SPIv3 clears rxfifo on SPE=0 + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + flush_rx_fifo(T::REGS); + + set_rxdmaen(T::REGS, true); + + let rx_src = T::REGS.rx_ptr(); + let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) }; + + let tx_dst = T::REGS.tx_ptr(); + let tx_f = unsafe { + self.tx_dma + .as_mut() + .unwrap() + .write_raw(write, tx_dst, Default::default()) + }; + + set_txdmaen(T::REGS, true); + T::REGS.cr1().modify(|w| { + w.set_spe(true); + }); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + T::REGS.cr1().modify(|w| { + w.set_cstart(true); + }); + + join(tx_f, rx_f).await; + + finish_dma(T::REGS); + + Ok(()) + } + + /// Bidirectional transfer, using DMA. + /// + /// This transfers both buffers at the same time, so it is NOT equivalent to `write` followed by `read`. + /// + /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. + /// If `write` is shorter it is padded with zero bytes. + pub async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { + self.transfer_inner(read, write).await + } + + /// In-place bidirectional transfer, using DMA. + /// + /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. + pub async fn transfer_in_place(&mut self, data: &mut [W]) -> Result<(), Error> { + self.transfer_inner(data, data).await + } +} + +impl<'d, T: Instance, M: PeriMode> Drop for Spi<'d, T, M> { fn drop(&mut self) { self.sck.as_ref().map(|x| x.set_as_disconnected()); self.mosi.as_ref().map(|x| x.set_as_disconnected()); @@ -819,7 +860,7 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { fn flush_rx_fifo(regs: Regs) { #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] while regs.sr().read().rxne() { - #[cfg(spi_v1)] + #[cfg(not(spi_v2))] let _ = regs.dr().read(); #[cfg(spi_v2)] let _ = regs.dr16().read(); @@ -900,7 +941,7 @@ fn transfer_word(regs: Regs, tx_word: W) -> Result { // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 macro_rules! impl_blocking { ($w:ident) => { - impl<'d, T: Instance, Tx, Rx> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, T, Tx, Rx> { + impl<'d, T: Instance, M: PeriMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, T, M> { type Error = Error; fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { @@ -908,7 +949,7 @@ macro_rules! impl_blocking { } } - impl<'d, T: Instance, Tx, Rx> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, T, Tx, Rx> { + impl<'d, T: Instance, M: PeriMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, T, M> { type Error = Error; fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { @@ -922,11 +963,11 @@ macro_rules! impl_blocking { impl_blocking!(u8); impl_blocking!(u16); -impl<'d, T: Instance, Tx, Rx> embedded_hal_1::spi::ErrorType for Spi<'d, T, Tx, Rx> { +impl<'d, T: Instance, M: PeriMode> embedded_hal_1::spi::ErrorType for Spi<'d, T, M> { type Error = Error; } -impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBus for Spi<'d, T, Tx, Rx> { +impl<'d, T: Instance, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus for Spi<'d, T, M> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } @@ -959,7 +1000,7 @@ impl embedded_hal_1::spi::Error for Error { } } -impl<'d, T: Instance, Tx: TxDma, Rx: RxDma, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, T, Tx, Rx> { +impl<'d, T: Instance, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, T, Async> { async fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } @@ -1094,7 +1135,7 @@ foreach_peripheral!( }; ); -impl<'d, T: Instance, Tx, Rx> SetConfig for Spi<'d, T, Tx, Rx> { +impl<'d, T: Instance, M: PeriMode> SetConfig for Spi<'d, T, M> { type Config = Config; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 7c0523a25..e7fdf4da6 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -13,9 +13,10 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use futures::future::{select, Either}; -use crate::dma::{NoDma, Transfer}; -use crate::gpio::AFType; +use crate::dma::ChannelAndRequest; +use crate::gpio::{AFType, AnyPin, SealedPin}; use crate::interrupt::typelevel::Interrupt; +use crate::mode::{Async, Blocking, Mode}; #[allow(unused_imports)] #[cfg(not(any(usart_v1, usart_v2)))] use crate::pac::usart::regs::Isr as Sr; @@ -162,6 +163,26 @@ pub struct Config { /// Set this to true to invert RX pin signal values (VDD =0/mark, Gnd = 1/idle). #[cfg(any(usart_v3, usart_v4))] pub invert_rx: bool, + + // private: set by new_half_duplex, not by the user. + half_duplex: bool, +} + +impl Config { + fn tx_af(&self) -> AFType { + #[cfg(any(usart_v3, usart_v4))] + if self.swap_rx_tx { + return AFType::Input; + }; + AFType::OutputPushPull + } + fn rx_af(&self) -> AFType { + #[cfg(any(usart_v3, usart_v4))] + if self.swap_rx_tx { + return AFType::OutputPushPull; + }; + AFType::Input + } } impl Default for Config { @@ -181,6 +202,7 @@ impl Default for Config { invert_tx: false, #[cfg(any(usart_v3, usart_v4))] invert_rx: false, + half_duplex: false, } } } @@ -217,12 +239,12 @@ enum ReadCompletionEvent { /// /// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`] /// as alternatives that do provide the necessary guarantees for `embedded_io::Read`. -pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { - tx: UartTx<'d, T, TxDma>, - rx: UartRx<'d, T, RxDma>, +pub struct Uart<'d, T: BasicInstance, M: Mode> { + tx: UartTx<'d, T, M>, + rx: UartRx<'d, T, M>, } -impl<'d, T: BasicInstance, TxDma, RxDma> SetConfig for Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance, M: Mode> SetConfig for Uart<'d, T, M> { type Config = Config; type ConfigError = ConfigError; @@ -236,12 +258,15 @@ impl<'d, T: BasicInstance, TxDma, RxDma> SetConfig for Uart<'d, T, TxDma, RxDma> /// /// Can be obtained from [`Uart::split`], or can be constructed independently, /// if you do not need the receiving half of the driver. -pub struct UartTx<'d, T: BasicInstance, TxDma = NoDma> { - phantom: PhantomData<&'d mut T>, - tx_dma: PeripheralRef<'d, TxDma>, +pub struct UartTx<'d, T: BasicInstance, M: Mode> { + _phantom: PhantomData<(T, M)>, + tx: Option>, + cts: Option>, + de: Option>, + tx_dma: Option>, } -impl<'d, T: BasicInstance, TxDma> SetConfig for UartTx<'d, T, TxDma> { +impl<'d, T: BasicInstance, M: Mode> SetConfig for UartTx<'d, T, M> { type Config = Config; type ConfigError = ConfigError; @@ -279,15 +304,17 @@ impl<'d, T: BasicInstance, TxDma> SetConfig for UartTx<'d, T, TxDma> { /// store data received between calls. /// /// Also see [this github comment](https://github.com/embassy-rs/embassy/pull/2185#issuecomment-1810047043). -pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> { - _peri: PeripheralRef<'d, T>, - rx_dma: PeripheralRef<'d, RxDma>, +pub struct UartRx<'d, T: BasicInstance, M: Mode> { + _phantom: PhantomData<(T, M)>, + rx: Option>, + rts: Option>, + rx_dma: Option>, detect_previous_overrun: bool, #[cfg(any(usart_v1, usart_v2))] buffered_sr: stm32_metapac::usart::regs::Sr, } -impl<'d, T: BasicInstance, RxDma> SetConfig for UartRx<'d, T, RxDma> { +impl<'d, T: BasicInstance, M: Mode> SetConfig for UartRx<'d, T, M> { type Config = Config; type ConfigError = ConfigError; @@ -296,17 +323,21 @@ impl<'d, T: BasicInstance, RxDma> SetConfig for UartRx<'d, T, RxDma> { } } -impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { +impl<'d, T: BasicInstance> UartTx<'d, T, Async> { /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. pub fn new( peri: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - T::enable_and_reset(); - - Self::new_inner(peri, tx, tx_dma, config) + Self::new_inner( + peri, + new_pin!(tx, AFType::OutputPushPull), + None, + new_dma!(tx_dma), + config, + ) } /// Create a new tx-only UART with a clear-to-send pin @@ -314,40 +345,86 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { peri: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - into_ref!(cts); - - T::enable_and_reset(); - - cts.set_as_af(cts.af_num(), AFType::Input); - T::regs().cr3().write(|w| { - w.set_ctse(true); - }); - Self::new_inner(peri, tx, tx_dma, config) + Self::new_inner( + peri, + new_pin!(tx, AFType::OutputPushPull), + new_pin!(cts, AFType::Input), + new_dma!(tx_dma), + config, + ) } - fn new_inner( - _peri: impl Peripheral

+ 'd, + /// Initiate an asynchronous UART write + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + let ch = self.tx_dma.as_mut().unwrap(); + T::regs().cr3().modify(|reg| { + reg.set_dmat(true); + }); + // If we don't assign future to a variable, the data register pointer + // is held across an await and makes the future non-Send. + let transfer = unsafe { ch.write(buffer, tdr(T::regs()), Default::default()) }; + transfer.await; + Ok(()) + } +} + +impl<'d, T: BasicInstance> UartTx<'d, T, Blocking> { + /// Create a new blocking tx-only UART with no hardware flow control. + /// + /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. + pub fn new_blocking( + peri: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, config: Config, ) -> Result { - into_ref!(_peri, tx, tx_dma); + Self::new_inner(peri, new_pin!(tx, AFType::OutputPushPull), None, None, config) + } + + /// Create a new blocking tx-only UART with a clear-to-send pin + pub fn new_blocking_with_cts( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(tx, AFType::OutputPushPull), + new_pin!(cts, AFType::Input), + None, + config, + ) + } +} + +impl<'d, T: BasicInstance, M: Mode> UartTx<'d, T, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, + tx: Option>, + cts: Option>, + tx_dma: Option>, + config: Config, + ) -> Result { + T::enable_and_reset(); let r = T::regs(); - - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - + r.cr3().modify(|w| { + w.set_ctse(cts.is_some()); + }); configure(r, &config, T::frequency(), T::KIND, false, true)?; // create state once! let _s = T::state(); Ok(Self { + tx, + cts, + de: None, tx_dma, - phantom: PhantomData, + _phantom: PhantomData, }) } @@ -356,23 +433,6 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { reconfigure::(config) } - /// Initiate an asynchronous UART write - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> - where - TxDma: crate::usart::TxDma, - { - let ch = &mut self.tx_dma; - let request = ch.request(); - T::regs().cr3().modify(|reg| { - reg.set_dmat(true); - }); - // If we don't assign future to a variable, the data register pointer - // is held across an await and makes the future non-Send. - let transfer = unsafe { Transfer::new_write(ch, request, buffer, tdr(T::regs()), Default::default()) }; - transfer.await; - Ok(()) - } - /// Perform a blocking UART write pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { let r = T::regs(); @@ -391,18 +451,18 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { } } -impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { +impl<'d, T: BasicInstance> UartRx<'d, T, Async> { + /// Create a new rx-only UART with no hardware flow control. + /// /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. pub fn new( peri: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, rx: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - T::enable_and_reset(); - - Self::new_inner(peri, rx, rx_dma, config) + Self::new_inner(peri, new_pin!(rx, AFType::Input), None, new_dma!(rx_dma), config) } /// Create a new rx-only UART with a request-to-send pin @@ -411,143 +471,27 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { _irq: impl interrupt::typelevel::Binding> + 'd, rx: impl Peripheral

> + 'd, rts: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

+ 'd, + rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - into_ref!(rts); - - T::enable_and_reset(); - - rts.set_as_af(rts.af_num(), AFType::OutputPushPull); - T::regs().cr3().write(|w| { - w.set_rtse(true); - }); - - Self::new_inner(peri, rx, rx_dma, config) - } - - fn new_inner( - peri: impl Peripheral

+ 'd, - rx: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

+ 'd, - config: Config, - ) -> Result { - into_ref!(peri, rx, rx_dma); - - let r = T::regs(); - - rx.set_as_af(rx.af_num(), AFType::Input); - - configure(r, &config, T::frequency(), T::KIND, true, false)?; - - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - // create state once! - let _s = T::state(); - - Ok(Self { - _peri: peri, - rx_dma, - detect_previous_overrun: config.detect_previous_overrun, - #[cfg(any(usart_v1, usart_v2))] - buffered_sr: stm32_metapac::usart::regs::Sr(0), - }) - } - - /// Reconfigure the driver - pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { - reconfigure::(config) - } - - #[cfg(any(usart_v1, usart_v2))] - fn check_rx_flags(&mut self) -> Result { - let r = T::regs(); - loop { - // Handle all buffered error flags. - if self.buffered_sr.pe() { - self.buffered_sr.set_pe(false); - return Err(Error::Parity); - } else if self.buffered_sr.fe() { - self.buffered_sr.set_fe(false); - return Err(Error::Framing); - } else if self.buffered_sr.ne() { - self.buffered_sr.set_ne(false); - return Err(Error::Noise); - } else if self.buffered_sr.ore() { - self.buffered_sr.set_ore(false); - return Err(Error::Overrun); - } else if self.buffered_sr.rxne() { - self.buffered_sr.set_rxne(false); - return Ok(true); - } else { - // No error flags from previous iterations were set: Check the actual status register - let sr = r.sr().read(); - if !sr.rxne() { - return Ok(false); - } - - // Buffer the status register and let the loop handle the error flags. - self.buffered_sr = sr; - } - } - } - - #[cfg(any(usart_v3, usart_v4))] - fn check_rx_flags(&mut self) -> Result { - let r = T::regs(); - let sr = r.isr().read(); - if sr.pe() { - r.icr().write(|w| w.set_pe(true)); - return Err(Error::Parity); - } else if sr.fe() { - r.icr().write(|w| w.set_fe(true)); - return Err(Error::Framing); - } else if sr.ne() { - r.icr().write(|w| w.set_ne(true)); - return Err(Error::Noise); - } else if sr.ore() { - r.icr().write(|w| w.set_ore(true)); - return Err(Error::Overrun); - } - Ok(sr.rxne()) + Self::new_inner( + peri, + new_pin!(rx, AFType::Input), + new_pin!(rts, AFType::OutputPushPull), + new_dma!(rx_dma), + config, + ) } /// Initiate an asynchronous UART read - pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> - where - RxDma: crate::usart::RxDma, - { + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.inner_read(buffer, false).await?; Ok(()) } - /// Read a single u8 if there is one available, otherwise return WouldBlock - pub fn nb_read(&mut self) -> Result> { - let r = T::regs(); - if self.check_rx_flags()? { - Ok(unsafe { rdr(r).read_volatile() }) - } else { - Err(nb::Error::WouldBlock) - } - } - - /// Perform a blocking read into `buffer` - pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let r = T::regs(); - for b in buffer { - while !self.check_rx_flags()? {} - unsafe { *b = rdr(r).read_volatile() } - } - Ok(()) - } - /// Initiate an asynchronous read with idle line detection enabled - pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result - where - RxDma: crate::usart::RxDma, - { + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { self.inner_read(buffer, true).await } @@ -555,10 +499,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { &mut self, buffer: &mut [u8], enable_idle_line_detection: bool, - ) -> Result - where - RxDma: crate::usart::RxDma, - { + ) -> Result { let r = T::regs(); // make sure USART state is restored to neutral state when this future is dropped @@ -581,15 +522,14 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { }); }); - let ch = &mut self.rx_dma; - let request = ch.request(); + let ch = self.rx_dma.as_mut().unwrap(); let buffer_len = buffer.len(); // Start USART DMA // will not do anything yet because DMAR is not yet set // future which will complete when DMA Read request completes - let transfer = unsafe { Transfer::new_read(ch, request, rdr(T::regs()), buffer, Default::default()) }; + let transfer = unsafe { ch.read(rdr(T::regs()), buffer, Default::default()) }; // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer if !self.detect_previous_overrun { @@ -732,10 +672,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { r } - async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result - where - RxDma: crate::usart::RxDma, - { + async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result { if buffer.is_empty() { return Ok(0); } else if buffer.len() > 0xFFFF { @@ -755,34 +692,186 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { } } -impl<'d, T: BasicInstance, TxDma> Drop for UartTx<'d, T, TxDma> { +impl<'d, T: BasicInstance> UartRx<'d, T, Blocking> { + /// Create a new rx-only UART with no hardware flow control. + /// + /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. + pub fn new_blocking( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner(peri, new_pin!(rx, AFType::Input), None, None, config) + } + + /// Create a new rx-only UART with a request-to-send pin + pub fn new_blocking_with_rts( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, AFType::Input), + new_pin!(rts, AFType::OutputPushPull), + None, + config, + ) + } +} + +impl<'d, T: BasicInstance, M: Mode> UartRx<'d, T, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, + rx: Option>, + rts: Option>, + rx_dma: Option>, + config: Config, + ) -> Result { + T::enable_and_reset(); + + let r = T::regs(); + r.cr3().write(|w| { + w.set_rtse(rts.is_some()); + }); + configure(r, &config, T::frequency(), T::KIND, true, false)?; + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + // create state once! + let _s = T::state(); + + Ok(Self { + _phantom: PhantomData, + rx, + rts, + rx_dma, + detect_previous_overrun: config.detect_previous_overrun, + #[cfg(any(usart_v1, usart_v2))] + buffered_sr: stm32_metapac::usart::regs::Sr(0), + }) + } + + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + reconfigure::(config) + } + + #[cfg(any(usart_v1, usart_v2))] + fn check_rx_flags(&mut self) -> Result { + let r = T::regs(); + loop { + // Handle all buffered error flags. + if self.buffered_sr.pe() { + self.buffered_sr.set_pe(false); + return Err(Error::Parity); + } else if self.buffered_sr.fe() { + self.buffered_sr.set_fe(false); + return Err(Error::Framing); + } else if self.buffered_sr.ne() { + self.buffered_sr.set_ne(false); + return Err(Error::Noise); + } else if self.buffered_sr.ore() { + self.buffered_sr.set_ore(false); + return Err(Error::Overrun); + } else if self.buffered_sr.rxne() { + self.buffered_sr.set_rxne(false); + return Ok(true); + } else { + // No error flags from previous iterations were set: Check the actual status register + let sr = r.sr().read(); + if !sr.rxne() { + return Ok(false); + } + + // Buffer the status register and let the loop handle the error flags. + self.buffered_sr = sr; + } + } + } + + #[cfg(any(usart_v3, usart_v4))] + fn check_rx_flags(&mut self) -> Result { + let r = T::regs(); + let sr = r.isr().read(); + if sr.pe() { + r.icr().write(|w| w.set_pe(true)); + return Err(Error::Parity); + } else if sr.fe() { + r.icr().write(|w| w.set_fe(true)); + return Err(Error::Framing); + } else if sr.ne() { + r.icr().write(|w| w.set_ne(true)); + return Err(Error::Noise); + } else if sr.ore() { + r.icr().write(|w| w.set_ore(true)); + return Err(Error::Overrun); + } + Ok(sr.rxne()) + } + + /// Read a single u8 if there is one available, otherwise return WouldBlock + pub(crate) fn nb_read(&mut self) -> Result> { + let r = T::regs(); + if self.check_rx_flags()? { + Ok(unsafe { rdr(r).read_volatile() }) + } else { + Err(nb::Error::WouldBlock) + } + } + + /// Perform a blocking read into `buffer` + pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let r = T::regs(); + for b in buffer { + while !self.check_rx_flags()? {} + unsafe { *b = rdr(r).read_volatile() } + } + Ok(()) + } +} + +impl<'d, T: BasicInstance, M: Mode> Drop for UartTx<'d, T, M> { fn drop(&mut self) { + self.tx.as_ref().map(|x| x.set_as_disconnected()); + self.cts.as_ref().map(|x| x.set_as_disconnected()); + self.de.as_ref().map(|x| x.set_as_disconnected()); T::disable(); } } -impl<'d, T: BasicInstance, TxDma> Drop for UartRx<'d, T, TxDma> { +impl<'d, T: BasicInstance, M: Mode> Drop for UartRx<'d, T, M> { fn drop(&mut self) { + self.rx.as_ref().map(|x| x.set_as_disconnected()); + self.rts.as_ref().map(|x| x.set_as_disconnected()); T::disable(); } } -impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance> Uart<'d, T, Async> { /// Create a new bidirectional UART pub fn new( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, _irq: impl interrupt::typelevel::Binding> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - // UartRx and UartTx have one refcount ea. - T::enable_and_reset(); - T::enable_and_reset(); - - Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config) + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + None, + None, + None, + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) } /// Create a new bidirectional UART with request-to-send and clear-to-send pins @@ -793,23 +882,21 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { _irq: impl interrupt::typelevel::Binding> + 'd, rts: impl Peripheral

> + 'd, cts: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - into_ref!(cts, rts); - - // UartRx and UartTx have one refcount ea. - T::enable_and_reset(); - T::enable_and_reset(); - - rts.set_as_af(rts.af_num(), AFType::OutputPushPull); - cts.set_as_af(cts.af_num(), AFType::Input); - T::regs().cr3().write(|w| { - w.set_rtse(true); - w.set_ctse(true); - }); - Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config) + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + new_pin!(rts, AFType::OutputPushPull), + new_pin!(cts, AFType::Input), + None, + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) } #[cfg(not(any(usart_v1, usart_v2)))] @@ -820,21 +907,21 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { tx: impl Peripheral

> + 'd, _irq: impl interrupt::typelevel::Binding> + 'd, de: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Result { - into_ref!(de); - - // UartRx and UartTx have one refcount ea. - T::enable_and_reset(); - T::enable_and_reset(); - - de.set_as_af(de.af_num(), AFType::OutputPushPull); - T::regs().cr3().write(|w| { - w.set_dem(true); - }); - Self::new_inner_configure(peri, rx, tx, tx_dma, rx_dma, config) + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + None, + None, + new_pin!(de, AFType::OutputPushPull), + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) } /// Create a single-wire half-duplex Uart transceiver on a single Tx pin. @@ -852,22 +939,24 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { peri: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, _irq: impl interrupt::typelevel::Binding> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, mut config: Config, ) -> Result { - // UartRx and UartTx have one refcount ea. - T::enable_and_reset(); - T::enable_and_reset(); - config.swap_rx_tx = false; + config.half_duplex = true; - into_ref!(peri, tx, tx_dma, rx_dma); - - T::regs().cr3().write(|w| w.set_hdsel(true)); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - - Self::new_inner(peri, tx_dma, rx_dma, config) + Self::new_inner( + peri, + None, + new_pin!(tx, AFType::OutputPushPull), + None, + None, + None, + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) } /// Create a single-wire half-duplex Uart transceiver on a single Rx pin. @@ -885,62 +974,196 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, _irq: impl interrupt::typelevel::Binding> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, mut config: Config, ) -> Result { - // UartRx and UartTx have one refcount ea. - T::enable_and_reset(); - T::enable_and_reset(); - config.swap_rx_tx = true; + config.half_duplex = true; - into_ref!(peri, rx, tx_dma, rx_dma); - - T::regs().cr3().write(|w| w.set_hdsel(true)); - rx.set_as_af(rx.af_num(), AFType::OutputPushPull); - - Self::new_inner(peri, tx_dma, rx_dma, config) + Self::new_inner( + peri, + None, + None, + new_pin!(rx, AFType::OutputPushPull), + None, + None, + new_dma!(tx_dma), + new_dma!(rx_dma), + config, + ) } - fn new_inner_configure( + /// Perform an asynchronous write + pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { + self.tx.write(buffer).await + } + + /// Perform an asynchronous read into `buffer` + pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + self.rx.read(buffer).await + } + + /// Perform an an asynchronous read with idle line detection enabled + pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { + self.rx.read_until_idle(buffer).await + } +} + +impl<'d, T: BasicInstance> Uart<'d, T, Blocking> { + /// Create a new blocking bidirectional UART. + pub fn new_blocking( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, - tx_dma: impl Peripheral

+ 'd, - rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Result { - into_ref!(peri, rx, tx, tx_dma, rx_dma); - - // Some chips do not have swap_rx_tx bit - cfg_if::cfg_if! { - if #[cfg(any(usart_v3, usart_v4))] { - if config.swap_rx_tx { - let (rx, tx) = (tx, rx); - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - } else { - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - } - } else { - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - } - } - - Self::new_inner(peri, tx_dma, rx_dma, config) + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + None, + None, + None, + None, + None, + config, + ) } - fn new_inner( - peri: PeripheralRef<'d, T>, - tx_dma: PeripheralRef<'d, TxDma>, - rx_dma: PeripheralRef<'d, RxDma>, + /// Create a new bidirectional UART with request-to-send and clear-to-send pins + pub fn new_blocking_with_rtscts( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + cts: impl Peripheral

> + 'd, config: Config, ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + new_pin!(rts, AFType::OutputPushPull), + new_pin!(cts, AFType::Input), + None, + None, + None, + config, + ) + } + + #[cfg(not(any(usart_v1, usart_v2)))] + /// Create a new bidirectional UART with a driver-enable pin + pub fn new_blocking_with_de( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + de: impl Peripheral

> + 'd, + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, config.rx_af()), + new_pin!(tx, config.tx_af()), + None, + None, + new_pin!(de, AFType::OutputPushPull), + None, + None, + config, + ) + } + + /// Create a single-wire half-duplex Uart transceiver on a single Tx pin. + /// + /// See [`new_half_duplex_on_rx`][`Self::new_half_duplex_on_rx`] if you would prefer to use an Rx pin. + /// There is no functional difference between these methods, as both allow bidirectional communication. + /// + /// The pin is always released when no data is transmitted. Thus, it acts as a standard + /// I/O in idle or in reception. + /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict + /// on the line must be managed by software (for instance by using a centralized arbiter). + #[cfg(not(any(usart_v1, usart_v2)))] + #[doc(alias("HDSEL"))] + pub fn new_blocking_half_duplex( + peri: impl Peripheral

+ 'd, + tx: impl Peripheral

> + 'd, + mut config: Config, + ) -> Result { + config.swap_rx_tx = false; + config.half_duplex = true; + + Self::new_inner( + peri, + None, + new_pin!(tx, AFType::OutputPushPull), + None, + None, + None, + None, + None, + config, + ) + } + + /// Create a single-wire half-duplex Uart transceiver on a single Rx pin. + /// + /// See [`new_half_duplex`][`Self::new_half_duplex`] if you would prefer to use an Tx pin. + /// There is no functional difference between these methods, as both allow bidirectional communication. + /// + /// The pin is always released when no data is transmitted. Thus, it acts as a standard + /// I/O in idle or in reception. + /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict + /// on the line must be managed by software (for instance by using a centralized arbiter). + #[cfg(not(any(usart_v1, usart_v2)))] + #[doc(alias("HDSEL"))] + pub fn new_blocking_half_duplex_on_rx( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + mut config: Config, + ) -> Result { + config.swap_rx_tx = true; + config.half_duplex = true; + + Self::new_inner( + peri, + None, + None, + new_pin!(rx, AFType::OutputPushPull), + None, + None, + None, + None, + config, + ) + } +} + +impl<'d, T: BasicInstance, M: Mode> Uart<'d, T, M> { + fn new_inner( + _peri: impl Peripheral

+ 'd, + rx: Option>, + tx: Option>, + rts: Option>, + cts: Option>, + de: Option>, + tx_dma: Option>, + rx_dma: Option>, + config: Config, + ) -> Result { + // UartRx and UartTx have one refcount each. + T::enable_and_reset(); + T::enable_and_reset(); + let r = T::regs(); + r.cr3().write(|w| { + w.set_rtse(rts.is_some()); + w.set_ctse(cts.is_some()); + #[cfg(not(any(usart_v1, usart_v2)))] + w.set_dem(de.is_some()); + }); configure(r, &config, T::frequency(), T::KIND, true, true)?; T::Interrupt::unpend(); @@ -951,11 +1174,16 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { Ok(Self { tx: UartTx { + _phantom: PhantomData, + tx, + cts, + de, tx_dma, - phantom: PhantomData, }, rx: UartRx { - _peri: peri, + _phantom: PhantomData, + rx, + rts, rx_dma, detect_previous_overrun: config.detect_previous_overrun, #[cfg(any(usart_v1, usart_v2))] @@ -964,14 +1192,6 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { }) } - /// Initiate an asynchronous write - pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> - where - TxDma: crate::usart::TxDma, - { - self.tx.write(buffer).await - } - /// Perform a blocking write pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> { self.tx.blocking_write(buffer) @@ -982,16 +1202,8 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { self.tx.blocking_flush() } - /// Initiate an asynchronous read into `buffer` - pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> - where - RxDma: crate::usart::RxDma, - { - self.rx.read(buffer).await - } - /// Read a single `u8` or return `WouldBlock` - pub fn nb_read(&mut self) -> Result> { + pub(crate) fn nb_read(&mut self) -> Result> { self.rx.nb_read() } @@ -1000,18 +1212,10 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { self.rx.blocking_read(buffer) } - /// Initiate an an asynchronous read with idle line detection enabled - pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result - where - RxDma: crate::usart::RxDma, - { - self.rx.read_until_idle(buffer).await - } - /// Split the Uart into a transmitter and receiver, which is /// particularly useful when having two tasks correlating to /// transmitting and receiving. - pub fn split(self) -> (UartTx<'d, T, TxDma>, UartRx<'d, T, RxDma>) { + pub fn split(self) -> (UartTx<'d, T, M>, UartRx<'d, T, M>) { (self.tx, self.rx) } } @@ -1153,6 +1357,8 @@ fn configure( #[cfg(not(usart_v1))] r.cr3().modify(|w| { w.set_onebit(config.assume_noise_free); + #[cfg(any(usart_v3, usart_v4))] + w.set_hdsel(config.half_duplex); }); r.cr1().write(|w| { @@ -1185,14 +1391,14 @@ fn configure( Ok(()) } -impl<'d, T: BasicInstance, RxDma> embedded_hal_02::serial::Read for UartRx<'d, T, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_02::serial::Read for UartRx<'d, T, M> { type Error = Error; fn read(&mut self) -> Result> { self.nb_read() } } -impl<'d, T: BasicInstance, TxDma> embedded_hal_02::blocking::serial::Write for UartTx<'d, T, TxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_02::blocking::serial::Write for UartTx<'d, T, M> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) @@ -1202,14 +1408,14 @@ impl<'d, T: BasicInstance, TxDma> embedded_hal_02::blocking::serial::Write f } } -impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_02::serial::Read for Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_02::serial::Read for Uart<'d, T, M> { type Error = Error; fn read(&mut self) -> Result> { self.nb_read() } } -impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_02::blocking::serial::Write for Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_02::blocking::serial::Write for Uart<'d, T, M> { type Error = Error; fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { self.blocking_write(buffer) @@ -1231,25 +1437,25 @@ impl embedded_hal_nb::serial::Error for Error { } } -impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::ErrorType for Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, T, M> { type Error = Error; } -impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, TxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, M> { type Error = Error; } -impl<'d, T: BasicInstance, RxDma> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, M> { type Error = Error; } -impl<'d, T: BasicInstance, RxDma> embedded_hal_nb::serial::Read for UartRx<'d, T, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M> { fn read(&mut self) -> nb::Result { self.nb_read() } } -impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::Write for UartTx<'d, T, TxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } @@ -1259,13 +1465,13 @@ impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::Write for UartTx<'d, } } -impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Read for Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, T, M> { fn read(&mut self) -> Result> { self.nb_read() } } -impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Write for Uart<'d, T, TxDma, RxDma> { +impl<'d, T: BasicInstance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) } @@ -1281,21 +1487,21 @@ impl embedded_io::Error for Error { } } -impl embedded_io::ErrorType for Uart<'_, T, TxDma, RxDma> +impl embedded_io::ErrorType for Uart<'_, T, M> where T: BasicInstance, { type Error = Error; } -impl embedded_io::ErrorType for UartTx<'_, T, TxDma> +impl embedded_io::ErrorType for UartTx<'_, T, M> where T: BasicInstance, { type Error = Error; } -impl embedded_io::Write for Uart<'_, T, TxDma, RxDma> +impl embedded_io::Write for Uart<'_, T, M> where T: BasicInstance, { @@ -1309,7 +1515,7 @@ where } } -impl embedded_io::Write for UartTx<'_, T, TxDma> +impl embedded_io::Write for UartTx<'_, T, M> where T: BasicInstance, { @@ -1323,10 +1529,9 @@ where } } -impl embedded_io_async::Write for Uart<'_, T, TxDma, RxDma> +impl embedded_io_async::Write for Uart<'_, T, Async> where T: BasicInstance, - TxDma: self::TxDma, { async fn write(&mut self, buf: &[u8]) -> Result { self.write(buf).await?; @@ -1338,10 +1543,9 @@ where } } -impl embedded_io_async::Write for UartTx<'_, T, TxDma> +impl embedded_io_async::Write for UartTx<'_, T, Async> where T: BasicInstance, - TxDma: self::TxDma, { async fn write(&mut self, buf: &[u8]) -> Result { self.write(buf).await?; diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index b852f0176..8eb18fe5b 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -1,21 +1,22 @@ use core::future::poll_fn; +use core::marker::PhantomData; use core::mem; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_internal::PeripheralRef; use futures::future::{select, Either}; use super::{clear_interrupt_flags, rdr, reconfigure, sr, BasicInstance, Config, ConfigError, Error, UartRx}; use crate::dma::ReadableRingBuffer; +use crate::mode::Async; use crate::usart::{Regs, Sr}; /// Rx-only Ring-buffered UART Driver /// /// Created with [UartRx::into_ring_buffered] pub struct RingBufferedUartRx<'d, T: BasicInstance> { - _peri: PeripheralRef<'d, T>, + _phantom: PhantomData, ring_buf: ReadableRingBuffer<'d, u8>, } @@ -28,26 +29,29 @@ impl<'d, T: BasicInstance> SetConfig for RingBufferedUartRx<'d, T> { } } -impl<'d, T: BasicInstance, RxDma: super::RxDma> UartRx<'d, T, RxDma> { +impl<'d, T: BasicInstance> UartRx<'d, T, Async> { /// Turn the `UartRx` into a buffered uart which can continously receive in the background /// without the possibility of losing bytes. The `dma_buf` is a buffer registered to the /// DMA controller, and must be large enough to prevent overflows. - pub fn into_ring_buffered(self, dma_buf: &'d mut [u8]) -> RingBufferedUartRx<'d, T> { + pub fn into_ring_buffered(mut self, dma_buf: &'d mut [u8]) -> RingBufferedUartRx<'d, T> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); - let request = self.rx_dma.request(); let opts = Default::default(); // Safety: we forget the struct before this function returns. - let rx_dma = unsafe { self.rx_dma.clone_unchecked() }; - let _peri = unsafe { self._peri.clone_unchecked() }; + let rx_dma = self.rx_dma.as_mut().unwrap(); + let request = rx_dma.request; + let rx_dma = unsafe { rx_dma.channel.clone_unchecked() }; let ring_buf = unsafe { ReadableRingBuffer::new(rx_dma, request, rdr(T::regs()), dma_buf, opts) }; // Don't disable the clock mem::forget(self); - RingBufferedUartRx { _peri, ring_buf } + RingBufferedUartRx { + _phantom: PhantomData, + ring_buf, + } } } diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 72459d660..b48a408c4 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -3,6 +3,7 @@ //! This module provides a mutex that can be used to synchronize data between asynchronous tasks. use core::cell::{RefCell, UnsafeCell}; use core::future::poll_fn; +use core::mem; use core::ops::{Deref, DerefMut}; use core::task::Poll; @@ -134,6 +135,7 @@ where /// successfully locked the mutex, and grants access to the contents. /// /// Dropping it unlocks the mutex. +#[clippy::has_significant_drop] pub struct MutexGuard<'a, M, T> where M: RawMutex, @@ -142,6 +144,25 @@ where mutex: &'a Mutex, } +impl<'a, M, T> MutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + /// Returns a locked view over a portion of the locked data. + pub fn map(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { + let mutex = this.mutex; + let value = fun(unsafe { &mut *this.mutex.inner.get() }); + // Don't run the `drop` method for MutexGuard. The ownership of the underlying + // locked state is being moved to the returned MappedMutexGuard. + mem::forget(this); + MappedMutexGuard { + state: &mutex.state, + value, + } + } +} + impl<'a, M, T> Drop for MutexGuard<'a, M, T> where M: RawMutex, @@ -180,3 +201,115 @@ where unsafe { &mut *(self.mutex.inner.get()) } } } + +/// A handle to a held `Mutex` that has had a function applied to it via [`MutexGuard::map`] or +/// [`MappedMutexGuard::map`]. +/// +/// This can be used to hold a subfield of the protected data. +#[clippy::has_significant_drop] +pub struct MappedMutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + state: &'a BlockingMutex>, + value: *mut T, +} + +impl<'a, M, T> MappedMutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + /// Returns a locked view over a portion of the locked data. + pub fn map(this: Self, fun: impl FnOnce(&mut T) -> &mut U) -> MappedMutexGuard<'a, M, U> { + let state = this.state; + let value = fun(unsafe { &mut *this.value }); + // Don't run the `drop` method for MutexGuard. The ownership of the underlying + // locked state is being moved to the returned MappedMutexGuard. + mem::forget(this); + MappedMutexGuard { state, value } + } +} + +impl<'a, M, T> Deref for MappedMutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + type Target = T; + fn deref(&self) -> &Self::Target { + // Safety: the MutexGuard represents exclusive access to the contents + // of the mutex, so it's OK to get it. + unsafe { &*self.value } + } +} + +impl<'a, M, T> DerefMut for MappedMutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + // Safety: the MutexGuard represents exclusive access to the contents + // of the mutex, so it's OK to get it. + unsafe { &mut *self.value } + } +} + +impl<'a, M, T> Drop for MappedMutexGuard<'a, M, T> +where + M: RawMutex, + T: ?Sized, +{ + fn drop(&mut self) { + self.state.lock(|s| { + let mut s = unwrap!(s.try_borrow_mut()); + s.locked = false; + s.waker.wake(); + }) + } +} + +unsafe impl Send for MappedMutexGuard<'_, M, T> +where + M: RawMutex + Sync, + T: Send + ?Sized, +{ +} + +unsafe impl Sync for MappedMutexGuard<'_, M, T> +where + M: RawMutex + Sync, + T: Sync + ?Sized, +{ +} + +#[cfg(test)] +mod tests { + use crate::blocking_mutex::raw::NoopRawMutex; + use crate::mutex::{Mutex, MutexGuard}; + + #[futures_test::test] + async fn mapped_guard_releases_lock_when_dropped() { + let mutex: Mutex = Mutex::new([0, 1]); + + { + let guard = mutex.lock().await; + assert_eq!(*guard, [0, 1]); + let mut mapped = MutexGuard::map(guard, |this| &mut this[1]); + assert_eq!(*mapped, 1); + *mapped = 2; + } + + { + let guard = mutex.lock().await; + assert_eq!(*guard, [0, 2]); + let mut mapped = MutexGuard::map(guard, |this| &mut this[1]); + assert_eq!(*mapped, 2); + *mapped = 3; + } + + assert_eq!(*mutex.lock().await, [0, 3]); + } +} diff --git a/examples/stm32f3/src/bin/usart_dma.rs b/examples/stm32f3/src/bin/usart_dma.rs index 5234e53b9..573a49f19 100644 --- a/examples/stm32f3/src/bin/usart_dma.rs +++ b/examples/stm32f3/src/bin/usart_dma.rs @@ -5,7 +5,6 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; @@ -21,7 +20,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, Irqs, p.DMA1_CH4, NoDma, config).unwrap(); + let mut usart = Uart::new(p.USART1, p.PE1, p.PE0, Irqs, p.DMA1_CH4, p.DMA1_CH5, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f4/src/bin/i2c.rs b/examples/stm32f4/src/bin/i2c.rs index 4b5da774d..4a96357a4 100644 --- a/examples/stm32f4/src/bin/i2c.rs +++ b/examples/stm32f4/src/bin/i2c.rs @@ -3,35 +3,19 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::{Error, I2c}; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, i2c, peripherals}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; -bind_interrupts!(struct Irqs { - I2C2_EV => i2c::EventInterruptHandler; - I2C2_ER => i2c::ErrorInterruptHandler; -}); - #[embassy_executor::main] async fn main(_spawner: Spawner) { info!("Hello world!"); let p = embassy_stm32::init(Default::default()); - let mut i2c = I2c::new( - p.I2C2, - p.PB10, - p.PB11, - Irqs, - NoDma, - NoDma, - Hertz(100_000), - Default::default(), - ); + let mut i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default()); let mut data = [0u8; 1]; diff --git a/examples/stm32f4/src/bin/spi.rs b/examples/stm32f4/src/bin/spi.rs index dc9141c62..970d819fc 100644 --- a/examples/stm32f4/src/bin/spi.rs +++ b/examples/stm32f4/src/bin/spi.rs @@ -3,7 +3,6 @@ use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; @@ -18,7 +17,7 @@ fn main() -> ! { let mut spi_config = Config::default(); spi_config.frequency = Hertz(1_000_000); - let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); + let mut spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32f4/src/bin/usart.rs b/examples/stm32f4/src/bin/usart.rs index 40d9d70f1..991bf6673 100644 --- a/examples/stm32f4/src/bin/usart.rs +++ b/examples/stm32f4/src/bin/usart.rs @@ -3,7 +3,6 @@ use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use {defmt_rtt as _, panic_probe as _}; @@ -19,7 +18,7 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, NoDma, NoDma, config).unwrap(); + let mut usart = Uart::new_blocking(p.USART3, p.PD9, p.PD8, config).unwrap(); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32f4/src/bin/usart_dma.rs b/examples/stm32f4/src/bin/usart_dma.rs index dd6de599c..aaf8d6c4f 100644 --- a/examples/stm32f4/src/bin/usart_dma.rs +++ b/examples/stm32f4/src/bin/usart_dma.rs @@ -5,7 +5,6 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; @@ -21,7 +20,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, p.DMA1_CH3, NoDma, config).unwrap(); + let mut usart = Uart::new(p.USART3, p.PD9, p.PD8, Irqs, p.DMA1_CH3, p.DMA1_CH1, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32f4/src/bin/ws2812_spi.rs b/examples/stm32f4/src/bin/ws2812_spi.rs index 56ccb67b8..e00d14327 100644 --- a/examples/stm32f4/src/bin/ws2812_spi.rs +++ b/examples/stm32f4/src/bin/ws2812_spi.rs @@ -13,8 +13,8 @@ #![no_std] #![no_main] +use embassy_stm32::spi; use embassy_stm32::time::khz; -use embassy_stm32::{dma, spi}; use embassy_time::{Duration, Ticker, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -78,7 +78,7 @@ async fn main(_spawner: embassy_executor::Spawner) { spi_config.frequency = khz(12_800); // Since we only output waveform, then the Rx and Sck and RxDma it is not considered - let mut ws2812_spi = spi::Spi::new_txonly_nosck(dp.SPI1, dp.PB5, dp.DMA2_CH3, dma::NoDma, spi_config); + let mut ws2812_spi = spi::Spi::new_txonly_nosck(dp.SPI1, dp.PB5, dp.DMA2_CH3, spi_config); // flip color at 2 Hz let mut ticker = Ticker::every(Duration::from_millis(500)); diff --git a/examples/stm32f7/src/bin/usart_dma.rs b/examples/stm32f7/src/bin/usart_dma.rs index fb604b34f..47456adf2 100644 --- a/examples/stm32f7/src/bin/usart_dma.rs +++ b/examples/stm32f7/src/bin/usart_dma.rs @@ -5,7 +5,6 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; @@ -19,7 +18,7 @@ bind_interrupts!(struct Irqs { async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, Irqs, p.DMA1_CH1, NoDma, config).unwrap(); + let mut usart = Uart::new(p.UART7, p.PA8, p.PA15, Irqs, p.DMA1_CH1, p.DMA1_CH3, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32g0/src/bin/spi_neopixel.rs b/examples/stm32g0/src/bin/spi_neopixel.rs index c5ea51721..2deee271d 100644 --- a/examples/stm32g0/src/bin/spi_neopixel.rs +++ b/examples/stm32g0/src/bin/spi_neopixel.rs @@ -4,7 +4,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::dma::word::U5; -use embassy_stm32::dma::NoDma; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; use embassy_time::Timer; @@ -77,7 +76,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.frequency = Hertz(4_000_000); - let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, config); + let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, config); let mut neopixels = Ws2812::new(); diff --git a/examples/stm32h5/src/bin/usart.rs b/examples/stm32h5/src/bin/usart.rs index f9cbad6af..cc49c2fdb 100644 --- a/examples/stm32h5/src/bin/usart.rs +++ b/examples/stm32h5/src/bin/usart.rs @@ -4,22 +4,16 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; -use embassy_stm32::{bind_interrupts, peripherals, usart}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -bind_interrupts!(struct Irqs { - UART7 => usart::InterruptHandler; -}); - #[embassy_executor::task] async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, NoDma, NoDma, config).unwrap(); + let mut usart = Uart::new_blocking(p.UART7, p.PF6, p.PF7, config).unwrap(); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32h5/src/bin/usart_dma.rs b/examples/stm32h5/src/bin/usart_dma.rs index caae0dd18..c644e84bd 100644 --- a/examples/stm32h5/src/bin/usart_dma.rs +++ b/examples/stm32h5/src/bin/usart_dma.rs @@ -6,7 +6,6 @@ use core::fmt::Write; use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; @@ -22,7 +21,7 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.GPDMA1_CH0, NoDma, config).unwrap(); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.GPDMA1_CH0, p.GPDMA1_CH1, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h5/src/bin/usart_split.rs b/examples/stm32h5/src/bin/usart_split.rs index 92047de8d..77b4caa9e 100644 --- a/examples/stm32h5/src/bin/usart_split.rs +++ b/examples/stm32h5/src/bin/usart_split.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; -use embassy_stm32::peripherals::{GPDMA1_CH1, UART7}; +use embassy_stm32::mode::Async; +use embassy_stm32::peripherals::UART7; use embassy_stm32::usart::{Config, Uart, UartRx}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; @@ -15,18 +15,6 @@ bind_interrupts!(struct Irqs { UART7 => usart::InterruptHandler; }); -#[embassy_executor::task] -async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { - unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); - info!("wrote Hello, starting echo"); - - let mut buf = [0u8; 1]; - loop { - unwrap!(usart.blocking_read(&mut buf)); - unwrap!(usart.blocking_write(&buf)); - } -} - static CHANNEL: Channel = Channel::new(); #[embassy_executor::main] @@ -50,7 +38,7 @@ async fn main(spawner: Spawner) -> ! { } #[embassy_executor::task] -async fn reader(mut rx: UartRx<'static, UART7, GPDMA1_CH1>) { +async fn reader(mut rx: UartRx<'static, UART7, Async>) { let mut buf = [0; 8]; loop { info!("reading..."); diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs index aed27723a..aaebdc346 100644 --- a/examples/stm32h7/src/bin/spi.rs +++ b/examples/stm32h7/src/bin/spi.rs @@ -7,7 +7,7 @@ use core::str::from_utf8; use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; -use embassy_stm32::dma::NoDma; +use embassy_stm32::mode::Blocking; use embassy_stm32::peripherals::SPI3; use embassy_stm32::time::mhz; use embassy_stm32::{spi, Config}; @@ -16,7 +16,7 @@ use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::task] -async fn main_task(mut spi: spi::Spi<'static, SPI3, NoDma, NoDma>) { +async fn main_task(mut spi: spi::Spi<'static, SPI3, Blocking>) { for n in 0u32.. { let mut write: String<128> = String::new(); core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); @@ -62,7 +62,7 @@ fn main() -> ! { let mut spi_config = spi::Config::default(); spi_config.frequency = mhz(1); - let spi = spi::Spi::new(p.SPI3, p.PB3, p.PB5, p.PB4, NoDma, NoDma, spi_config); + let spi = spi::Spi::new_blocking(p.SPI3, p.PB3, p.PB5, p.PB4, spi_config); let executor = EXECUTOR.init(Executor::new()); diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs index 54d4d7656..3d3c724eb 100644 --- a/examples/stm32h7/src/bin/spi_dma.rs +++ b/examples/stm32h7/src/bin/spi_dma.rs @@ -7,15 +7,15 @@ use core::str::from_utf8; use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; -use embassy_stm32::peripherals::{DMA1_CH3, DMA1_CH4, SPI3}; +use embassy_stm32::mode::Async; use embassy_stm32::time::mhz; -use embassy_stm32::{spi, Config}; +use embassy_stm32::{peripherals, spi, Config}; use heapless::String; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::task] -async fn main_task(mut spi: spi::Spi<'static, SPI3, DMA1_CH3, DMA1_CH4>) { +async fn main_task(mut spi: spi::Spi<'static, peripherals::SPI3, Async>) { for n in 0u32.. { let mut write: String<128> = String::new(); let mut read = [0; 128]; diff --git a/examples/stm32h7/src/bin/usart.rs b/examples/stm32h7/src/bin/usart.rs index f9cbad6af..cc49c2fdb 100644 --- a/examples/stm32h7/src/bin/usart.rs +++ b/examples/stm32h7/src/bin/usart.rs @@ -4,22 +4,16 @@ use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; -use embassy_stm32::{bind_interrupts, peripherals, usart}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; -bind_interrupts!(struct Irqs { - UART7 => usart::InterruptHandler; -}); - #[embassy_executor::task] async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, NoDma, NoDma, config).unwrap(); + let mut usart = Uart::new_blocking(p.UART7, p.PF6, p.PF7, config).unwrap(); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32h7/src/bin/usart_dma.rs b/examples/stm32h7/src/bin/usart_dma.rs index ae1f3a2e9..6f340d40a 100644 --- a/examples/stm32h7/src/bin/usart_dma.rs +++ b/examples/stm32h7/src/bin/usart_dma.rs @@ -6,7 +6,6 @@ use core::fmt::Write; use cortex_m_rt::entry; use defmt::*; use embassy_executor::Executor; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; @@ -22,7 +21,7 @@ async fn main_task() { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.DMA1_CH0, NoDma, config).unwrap(); + let mut usart = Uart::new(p.UART7, p.PF6, p.PF7, Irqs, p.DMA1_CH0, p.DMA1_CH1, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32h7/src/bin/usart_split.rs b/examples/stm32h7/src/bin/usart_split.rs index b98c40877..4ad8e77ce 100644 --- a/examples/stm32h7/src/bin/usart_split.rs +++ b/examples/stm32h7/src/bin/usart_split.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; -use embassy_stm32::peripherals::{DMA1_CH1, UART7}; +use embassy_stm32::mode::Async; +use embassy_stm32::peripherals::UART7; use embassy_stm32::usart::{Config, Uart, UartRx}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; @@ -15,18 +15,6 @@ bind_interrupts!(struct Irqs { UART7 => usart::InterruptHandler; }); -#[embassy_executor::task] -async fn writer(mut usart: Uart<'static, UART7, NoDma, NoDma>) { - unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); - info!("wrote Hello, starting echo"); - - let mut buf = [0u8; 1]; - loop { - unwrap!(usart.blocking_read(&mut buf)); - unwrap!(usart.blocking_write(&buf)); - } -} - static CHANNEL: Channel = Channel::new(); #[embassy_executor::main] @@ -50,7 +38,7 @@ async fn main(spawner: Spawner) -> ! { } #[embassy_executor::task] -async fn reader(mut rx: UartRx<'static, UART7, DMA1_CH1>) { +async fn reader(mut rx: UartRx<'static, UART7, Async>) { let mut buf = [0; 8]; loop { info!("reading..."); diff --git a/examples/stm32l0/src/bin/spi.rs b/examples/stm32l0/src/bin/spi.rs index f23a537b8..8e0cfdedb 100644 --- a/examples/stm32l0/src/bin/spi.rs +++ b/examples/stm32l0/src/bin/spi.rs @@ -3,7 +3,6 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; @@ -17,7 +16,7 @@ async fn main(_spawner: Spawner) { let mut spi_config = Config::default(); spi_config.frequency = Hertz(1_000_000); - let mut spi = Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, NoDma, NoDma, spi_config); + let mut spi = Spi::new_blocking(p.SPI1, p.PB3, p.PA7, p.PA6, spi_config); let mut cs = Output::new(p.PA15, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l1/src/bin/spi.rs b/examples/stm32l1/src/bin/spi.rs index 8be686c5a..eabf1bac2 100644 --- a/examples/stm32l1/src/bin/spi.rs +++ b/examples/stm32l1/src/bin/spi.rs @@ -3,7 +3,6 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; @@ -17,7 +16,7 @@ async fn main(_spawner: Spawner) { let mut spi_config = Config::default(); spi_config.frequency = Hertz(1_000_000); - let mut spi = Spi::new(p.SPI1, p.PA5, p.PA7, p.PA6, NoDma, NoDma, spi_config); + let mut spi = Spi::new_blocking(p.SPI1, p.PA5, p.PA7, p.PA6, spi_config); let mut cs = Output::new(p.PA4, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/src/bin/i2c.rs b/examples/stm32l4/src/bin/i2c.rs index f553deb82..2861bc091 100644 --- a/examples/stm32l4/src/bin/i2c.rs +++ b/examples/stm32l4/src/bin/i2c.rs @@ -3,33 +3,17 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::I2c; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, i2c, peripherals}; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; -bind_interrupts!(struct Irqs { - I2C2_EV => i2c::EventInterruptHandler; - I2C2_ER => i2c::ErrorInterruptHandler; -}); - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut i2c = I2c::new( - p.I2C2, - p.PB10, - p.PB11, - Irqs, - NoDma, - NoDma, - Hertz(100_000), - Default::default(), - ); + let mut i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default()); let mut data = [0u8; 1]; unwrap!(i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data)); diff --git a/examples/stm32l4/src/bin/i2c_blocking_async.rs b/examples/stm32l4/src/bin/i2c_blocking_async.rs index 1b8652bcc..a014b23e0 100644 --- a/examples/stm32l4/src/bin/i2c_blocking_async.rs +++ b/examples/stm32l4/src/bin/i2c_blocking_async.rs @@ -4,34 +4,18 @@ use defmt::*; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::I2c; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, i2c, peripherals}; use embedded_hal_async::i2c::I2c as I2cTrait; use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; -bind_interrupts!(struct Irqs { - I2C2_EV => i2c::EventInterruptHandler; - I2C2_ER => i2c::ErrorInterruptHandler; -}); - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let i2c = I2c::new( - p.I2C2, - p.PB10, - p.PB11, - Irqs, - NoDma, - NoDma, - Hertz(100_000), - Default::default(), - ); + let i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default()); let mut i2c = BlockingAsync::new(i2c); let mut data = [0u8; 1]; diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index 77aa929ab..694629ede 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -23,18 +23,23 @@ use embassy_futures::select::{select, Either}; use embassy_futures::yield_now; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources, StaticConfigV4}; +use embassy_net_adin1110::{Device, Runner, ADIN1110}; +use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; +use embassy_stm32::i2c::{self, Config as I2C_Config, I2c}; +use embassy_stm32::mode::Async; +use embassy_stm32::rng::{self, Rng}; +use embassy_stm32::spi::{Config as SPI_Config, Spi}; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, exti, pac, peripherals}; use embassy_time::{Delay, Duration, Ticker, Timer}; use embedded_hal_async::i2c::I2c as I2cBus; +use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io::Write as bWrite; use embedded_io_async::Write; -use hal::gpio::{Input, Level, Output, Speed}; -use hal::i2c::{self, I2c}; -use hal::rng::{self, Rng}; -use hal::{bind_interrupts, exti, pac, peripherals}; use heapless::Vec; +use panic_probe as _; use rand::RngCore; use static_cell::StaticCell; -use {embassy_stm32 as hal, panic_probe as _}; bind_interrupts!(struct Irqs { I2C3_EV => i2c::EventInterruptHandler; @@ -42,13 +47,6 @@ bind_interrupts!(struct Irqs { RNG => rng::InterruptHandler; }); -use embassy_net_adin1110::{Device, Runner, ADIN1110}; -use embedded_hal_bus::spi::ExclusiveDevice; -use hal::gpio::Pull; -use hal::i2c::Config as I2C_Config; -use hal::spi::{Config as SPI_Config, Spi}; -use hal::time::Hertz; - // Basic settings // MAC-address used by the adin1110 const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]; @@ -57,12 +55,12 @@ const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address([192, 168, 1, 5]), 24); // Listen port for the webserver const HTTP_LISTEN_PORT: u16 = 80; -pub type SpeSpi = Spi<'static, peripherals::SPI2, peripherals::DMA1_CH1, peripherals::DMA1_CH2>; +pub type SpeSpi = Spi<'static, peripherals::SPI2, Async>; pub type SpeSpiCs = ExclusiveDevice, Delay>; pub type SpeInt = exti::ExtiInput<'static>; pub type SpeRst = Output<'static>; pub type Adin1110T = ADIN1110; -pub type TempSensI2c = I2c<'static, peripherals::I2C3, peripherals::DMA1_CH6, peripherals::DMA1_CH7>; +pub type TempSensI2c = I2c<'static, peripherals::I2C3, Async>; static TEMP: AtomicI32 = AtomicI32::new(0); diff --git a/examples/stm32l4/src/bin/spi.rs b/examples/stm32l4/src/bin/spi.rs index 6653e4516..5693a3765 100644 --- a/examples/stm32l4/src/bin/spi.rs +++ b/examples/stm32l4/src/bin/spi.rs @@ -2,7 +2,6 @@ #![no_main] use defmt::*; -use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; @@ -17,7 +16,7 @@ fn main() -> ! { let mut spi_config = Config::default(); spi_config.frequency = Hertz(1_000_000); - let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); + let mut spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/src/bin/spi_blocking_async.rs b/examples/stm32l4/src/bin/spi_blocking_async.rs index 68dbb70ad..1f1089101 100644 --- a/examples/stm32l4/src/bin/spi_blocking_async.rs +++ b/examples/stm32l4/src/bin/spi_blocking_async.rs @@ -4,7 +4,6 @@ use defmt::*; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::spi::{Config, Spi}; use embassy_stm32::time::Hertz; @@ -19,7 +18,7 @@ async fn main(_spawner: Spawner) { let mut spi_config = Config::default(); spi_config.frequency = Hertz(1_000_000); - let spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); + let spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config); let mut spi = BlockingAsync::new(spi); diff --git a/examples/stm32l4/src/bin/usart.rs b/examples/stm32l4/src/bin/usart.rs index 7bab23950..d9b388026 100644 --- a/examples/stm32l4/src/bin/usart.rs +++ b/examples/stm32l4/src/bin/usart.rs @@ -2,7 +2,6 @@ #![no_main] use defmt::*; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use {defmt_rtt as _, panic_probe as _}; @@ -18,7 +17,7 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); let config = Config::default(); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, NoDma, NoDma, config).unwrap(); + let mut usart = Uart::new_blocking(p.UART4, p.PA1, p.PA0, config).unwrap(); unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); info!("wrote Hello, starting echo"); diff --git a/examples/stm32l4/src/bin/usart_dma.rs b/examples/stm32l4/src/bin/usart_dma.rs index 031888f70..b4f7a1643 100644 --- a/examples/stm32l4/src/bin/usart_dma.rs +++ b/examples/stm32l4/src/bin/usart_dma.rs @@ -5,7 +5,6 @@ use core::fmt::Write; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, Uart}; use embassy_stm32::{bind_interrupts, peripherals, usart}; use heapless::String; @@ -21,7 +20,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, p.DMA1_CH3, NoDma, config).unwrap(); + let mut usart = Uart::new(p.UART4, p.PA1, p.PA0, Irqs, p.DMA1_CH3, p.DMA1_CH4, config).unwrap(); for n in 0u32.. { let mut s: String<128> = String::new(); diff --git a/examples/stm32u0/.cargo/config.toml b/examples/stm32u0/.cargo/config.toml index 28b8af96e..688347084 100644 --- a/examples/stm32u0/.cargo/config.toml +++ b/examples/stm32u0/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list` -runner = "probe-rs run --chip STM32u083rctx" +# replace stm32u083rctx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip stm32u083rctx" [build] target = "thumbv6m-none-eabi" diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 8219cb038..64fd837a2 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -# Change stm32c031c6 to your chip name, if necessary. +# Change stm32u083rc to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 03294339d..01320b88d 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -24,5 +24,9 @@ heapless = { version = "0.8", default-features = false } micromath = "2.0.0" +[features] +## Use secure registers when TrustZone is enabled +trustzone-secure = ["embassy-stm32/trustzone-secure"] + [profile.release] debug = 2 diff --git a/examples/stm32u5/src/bin/i2c.rs b/examples/stm32u5/src/bin/i2c.rs index e376c6bc8..19a78eac9 100644 --- a/examples/stm32u5/src/bin/i2c.rs +++ b/examples/stm32u5/src/bin/i2c.rs @@ -3,33 +3,17 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::i2c::I2c; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, i2c, peripherals}; use {defmt_rtt as _, panic_probe as _}; const HTS221_ADDRESS: u8 = 0x5F; const WHOAMI: u8 = 0x0F; -bind_interrupts!(struct Irqs { - I2C2_EV => i2c::EventInterruptHandler; - I2C2_ER => i2c::ErrorInterruptHandler; -}); - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut i2c = I2c::new( - p.I2C2, - p.PH4, - p.PH5, - Irqs, - NoDma, - NoDma, - Hertz(100_000), - Default::default(), - ); + let mut i2c = I2c::new_blocking(p.I2C2, p.PH4, p.PH5, Hertz(100_000), Default::default()); let mut data = [0u8; 1]; unwrap!(i2c.blocking_write_read(HTS221_ADDRESS, &[WHOAMI], &mut data)); diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml index 98696fd2b..ac160b995 100644 --- a/rust-toolchain-nightly.toml +++ b/rust-toolchain-nightly.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2024-03-20" +channel = "nightly-2024-04-14" components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] targets = [ "thumbv7em-none-eabi", diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index b0bdd477f..59cb0cfd3 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -6,7 +6,6 @@ mod common; use common::*; use defmt::assert_eq; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::spi::{self, Spi}; use embassy_stm32::time::Hertz; @@ -23,11 +22,11 @@ async fn main(_spawner: Spawner) { let mut spi_config = spi::Config::default(); spi_config.frequency = Hertz(1_000_000); - let mut spi = Spi::new( + let mut spi = Spi::new_blocking( spi, sck, // Arduino D13 mosi, // Arduino D11 miso, // Arduino D12 - NoDma, NoDma, spi_config, + spi_config, ); let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE]; diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 9b20eb784..a6e34674d 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -6,7 +6,6 @@ mod common; use common::*; use defmt::{assert, assert_eq, unreachable}; use embassy_executor::Spawner; -use embassy_stm32::dma::NoDma; use embassy_stm32::usart::{Config, ConfigError, Error, Uart}; use embassy_time::{block_for, Duration, Instant}; @@ -20,11 +19,10 @@ async fn main(_spawner: Spawner) { let mut usart = peri!(p, UART); let mut rx = peri!(p, UART_RX); let mut tx = peri!(p, UART_TX); - let irq = irqs!(UART); { let config = Config::default(); - let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, irq, NoDma, NoDma, config).unwrap(); + let mut usart = Uart::new_blocking(&mut usart, &mut rx, &mut tx, config).unwrap(); // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. @@ -40,7 +38,7 @@ async fn main(_spawner: Spawner) { // Test error handling with with an overflow error { let config = Config::default(); - let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, irq, NoDma, NoDma, config).unwrap(); + let mut usart = Uart::new_blocking(&mut usart, &mut rx, &mut tx, config).unwrap(); // Send enough bytes to fill the RX FIFOs off all USART versions. let data = [0; 64]; @@ -70,7 +68,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.baudrate = baudrate; - let mut usart = match Uart::new(&mut usart, &mut rx, &mut tx, irq, NoDma, NoDma, config) { + let mut usart = match Uart::new_blocking(&mut usart, &mut rx, &mut tx, config) { Ok(x) => x, Err(ConfigError::BaudrateTooHigh) => { info!("baudrate too high"); diff --git a/tests/stm32/src/bin/usart_rx_ringbuffered.rs b/tests/stm32/src/bin/usart_rx_ringbuffered.rs index 0c110421d..908452eaf 100644 --- a/tests/stm32/src/bin/usart_rx_ringbuffered.rs +++ b/tests/stm32/src/bin/usart_rx_ringbuffered.rs @@ -8,6 +8,7 @@ mod common; use common::*; use defmt::{assert_eq, panic}; use embassy_executor::Spawner; +use embassy_stm32::mode::Async; use embassy_stm32::usart::{Config, DataBits, Parity, RingBufferedUartRx, StopBits, Uart, UartTx}; use embassy_time::Timer; use rand_chacha::ChaCha8Rng; @@ -51,7 +52,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn transmit_task(mut tx: UartTx<'static, peris::UART, peris::UART_TX_DMA>) { +async fn transmit_task(mut tx: UartTx<'static, peris::UART, Async>) { // workaround https://github.com/embassy-rs/embassy/issues/1426 Timer::after_millis(100).await;