From fcb270bcede54d975ff89c05691131d2a898638f Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Wed, 10 Apr 2024 11:00:30 +0300 Subject: [PATCH 01/23] nrf: Add basic CHANGELOG summarizing changes in embassy-nrf --- embassy-nrf/CHANGELOG.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 embassy-nrf/CHANGELOG.md 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 + From 64b806db0bb6a3ae507330ddd3dc8c815e3b1563 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Fri, 12 Apr 2024 17:59:25 +0300 Subject: [PATCH 02/23] Expose RCC enable and disable methods --- embassy-stm32/src/rcc/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index c328344aa..5497bba07 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(); +} From ffc61f78b0226b1898eb7aca66a759b76e0407af Mon Sep 17 00:00:00 2001 From: Michael Zill Date: Tue, 9 Apr 2024 06:56:15 +0200 Subject: [PATCH 03/23] stm32/spi,crc: update for new PAC --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/crc/v1.rs | 12 ++++++++++++ embassy-stm32/src/crc/v2v3.rs | 16 ++++++++-------- embassy-stm32/src/spi/mod.rs | 17 ++++++++++++----- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 89b24f0eb..93792ecf8 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -70,7 +70,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b7ec569a5510c324693f0515ac8ea20b12917a9" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0c4baf478324e19741c7a9795ab0aa8217c3691c" } vcell = "0.1.3" nb = "1.0.0" @@ -96,7 +96,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b7ec569a5510c324693f0515ac8ea20b12917a9", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0c4baf478324e19741c7a9795ab0aa8217c3691c", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs index f8909d438..e8e0270af 100644 --- a/embassy-stm32/src/crc/v1.rs +++ b/embassy-stm32/src/crc/v1.rs @@ -32,6 +32,9 @@ impl<'d> Crc<'d> { /// Feeds a word to the peripheral and returns the current CRC value pub fn feed_word(&mut self, word: u32) -> u32 { // write a single byte to the device, and return the result + #[cfg(not(crc_v1))] + PAC_CRC.dr32().write_value(word); + #[cfg(crc_v1)] PAC_CRC.dr().write_value(word); self.read() } @@ -39,6 +42,9 @@ impl<'d> Crc<'d> { /// Feed a slice of words to the peripheral and return the result. pub fn feed_words(&mut self, words: &[u32]) -> u32 { for word in words { + #[cfg(not(crc_v1))] + PAC_CRC.dr32().write_value(*word); + #[cfg(crc_v1)] PAC_CRC.dr().write_value(*word); } @@ -46,6 +52,12 @@ impl<'d> Crc<'d> { } /// Read the CRC result value. + #[cfg(not(crc_v1))] + pub fn read(&self) -> u32 { + PAC_CRC.dr32().read() + } + /// Read the CRC result value. + #[cfg(crc_v1)] pub fn read(&self) -> u32 { PAC_CRC.dr().read() } diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs index 46f5ea1be..13fb6778c 100644 --- a/embassy-stm32/src/crc/v2v3.rs +++ b/embassy-stm32/src/crc/v2v3.rs @@ -136,7 +136,7 @@ impl<'d> Crc<'d> { /// Feeds a byte into the CRC peripheral. Returns the computed checksum. pub fn feed_byte(&mut self, byte: u8) -> u32 { PAC_CRC.dr8().write_value(byte); - PAC_CRC.dr().read() + PAC_CRC.dr32().read() } /// Feeds an slice of bytes into the CRC peripheral. Returns the computed checksum. @@ -144,30 +144,30 @@ impl<'d> Crc<'d> { for byte in bytes { PAC_CRC.dr8().write_value(*byte); } - PAC_CRC.dr().read() + PAC_CRC.dr32().read() } /// Feeds a halfword into the CRC peripheral. Returns the computed checksum. pub fn feed_halfword(&mut self, halfword: u16) -> u32 { PAC_CRC.dr16().write_value(halfword); - PAC_CRC.dr().read() + PAC_CRC.dr32().read() } /// Feeds an slice of halfwords into the CRC peripheral. Returns the computed checksum. pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 { for halfword in halfwords { PAC_CRC.dr16().write_value(*halfword); } - PAC_CRC.dr().read() + PAC_CRC.dr32().read() } /// Feeds a words into the CRC peripheral. Returns the computed checksum. pub fn feed_word(&mut self, word: u32) -> u32 { - PAC_CRC.dr().write_value(word as u32); - PAC_CRC.dr().read() + PAC_CRC.dr32().write_value(word as u32); + PAC_CRC.dr32().read() } /// Feeds an slice of words into the CRC peripheral. Returns the computed checksum. pub fn feed_words(&mut self, words: &[u32]) -> u32 { for word in words { - PAC_CRC.dr().write_value(*word as u32); + PAC_CRC.dr32().write_value(*word as u32); } - PAC_CRC.dr().read() + PAC_CRC.dr32().read() } } diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 0b38c4288..450975f18 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -735,18 +735,22 @@ trait RegsExt { impl RegsExt for Regs { fn tx_ptr(&self) -> *mut W { - #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + #[cfg(any(spi_v1, spi_f1))] let dr = self.dr(); + #[cfg(spi_v2)] + let dr = self.dr16(); #[cfg(any(spi_v3, spi_v4, spi_v5))] - let dr = self.txdr(); + let dr = self.txdr32(); dr.as_ptr() as *mut W } fn rx_ptr(&self) -> *mut W { - #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + #[cfg(any(spi_v1, spi_f1))] let dr = self.dr(); + #[cfg(spi_v2)] + let dr = self.dr16(); #[cfg(any(spi_v3, spi_v4, spi_v5))] - let dr = self.rxdr(); + let dr = self.rxdr32(); dr.as_ptr() as *mut W } } @@ -815,11 +819,14 @@ 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(not(spi_v2))] let _ = regs.dr().read(); + #[cfg(spi_v2)] + let _ = regs.dr16().read(); } #[cfg(any(spi_v3, spi_v4, spi_v5))] while regs.sr().read().rxp() { - let _ = regs.rxdr().read(); + let _ = regs.rxdr32().read(); } } From 1f3b690f76b6732e1ff152091040cec833da25cf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 13 Apr 2024 02:55:15 +0200 Subject: [PATCH 04/23] stm32/flash: remove otp flash regions (removed in newer metapacs). --- embassy-stm32/build.rs | 2 -- embassy-stm32/src/flash/f4.rs | 5 +---- embassy-stm32/src/flash/h50.rs | 6 ++---- embassy-stm32/src/flash/mod.rs | 2 -- 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 38b6c480c..0f87dd8ac 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -272,8 +272,6 @@ fn main() { "Bank1" } else if region.name.starts_with("BANK_2") { "Bank2" - } else if region.name == "OTP" { - "Otp" } else { continue; } diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 00e61f2d2..90f13ff29 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -16,7 +16,7 @@ mod alt_regions { use embassy_hal_internal::PeripheralRef; use stm32_metapac::FLASH_SIZE; - use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; + use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3}; use crate::flash::{asynch, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion}; use crate::peripherals::FLASH; @@ -62,7 +62,6 @@ mod alt_regions { pub bank2_region1: AltBank2Region1<'d, MODE>, pub bank2_region2: AltBank2Region2<'d, MODE>, pub bank2_region3: AltBank2Region3<'d, MODE>, - pub otp_region: OTPRegion<'d, MODE>, } impl<'d> Flash<'d> { @@ -79,7 +78,6 @@ mod alt_regions { bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), - otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), } } @@ -96,7 +94,6 @@ mod alt_regions { bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData), bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData), bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData), - otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData), } } } diff --git a/embassy-stm32/src/flash/h50.rs b/embassy-stm32/src/flash/h50.rs index db05bef5d..5b15be261 100644 --- a/embassy-stm32/src/flash/h50.rs +++ b/embassy-stm32/src/flash/h50.rs @@ -55,7 +55,6 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) } pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { - assert!(sector.bank != FlashBank::Otp); assert!(sector.index_in_bank < 8); while busy() {} @@ -63,9 +62,8 @@ 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::B_0X0, - FlashBank::Bank2 => Bksel::B_0X1, - _ => unreachable!(), + FlashBank::Bank1 => Bksel::BANK1, + FlashBank::Bank2 => Bksel::BANK2, }); w.set_snb(sector.index_in_bank); w.set_ser(true); diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 1d8031e82..9d7861816 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -89,8 +89,6 @@ pub enum FlashBank { Bank1 = 0, /// Bank 2 Bank2 = 1, - /// OTP region - Otp, } #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] From 4079a8acf8d0d33dc1ca815475492cbf33407464 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 13 Apr 2024 03:30:30 +0200 Subject: [PATCH 05/23] stm32/adc: update g4 for new pac. --- embassy-stm32/src/adc/g4.rs | 304 ++++++++++++++++++++++++++++++++ embassy-stm32/src/adc/mod.rs | 29 ++- examples/stm32g4/src/bin/adc.rs | 2 +- 3 files changed, 332 insertions(+), 3 deletions(-) create mode 100644 embassy-stm32/src/adc/g4.rs diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs new file mode 100644 index 000000000..f6741f019 --- /dev/null +++ b/embassy-stm32/src/adc/g4.rs @@ -0,0 +1,304 @@ +#[allow(unused)] +use pac::adc::vals::{Adcaldif, Difsel, Exten}; +use pac::adccommon::vals::Presc; + +use super::{blocking_delay_us, Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; +use crate::time::Hertz; +use crate::{pac, Peripheral}; + +/// Default VREF voltage used for sample conversion to millivolts. +pub const VREF_DEFAULT_MV: u32 = 3300; +/// VREF voltage used for factory calibration of VREFINTCAL register. +pub const VREF_CALIB_MV: u32 = 3300; + +/// Max single ADC operation clock frequency +#[cfg(stm32g4)] +const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); +#[cfg(stm32h7)] +const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); + +#[cfg(stm32g4)] +const VREF_CHANNEL: u8 = 18; +#[cfg(stm32g4)] +const TEMP_CHANNEL: u8 = 16; + +#[cfg(stm32h7)] +const VREF_CHANNEL: u8 = 19; +#[cfg(stm32h7)] +const TEMP_CHANNEL: u8 = 18; + +// TODO this should be 14 for H7a/b/35 +const VBAT_CHANNEL: u8 = 17; + +// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs +/// Internal voltage reference channel. +pub struct VrefInt; +impl InternalChannel for VrefInt {} +impl super::SealedInternalChannel for VrefInt { + fn channel(&self) -> u8 { + VREF_CHANNEL + } +} + +/// Internal temperature channel. +pub struct Temperature; +impl InternalChannel for Temperature {} +impl super::SealedInternalChannel for Temperature { + fn channel(&self) -> u8 { + TEMP_CHANNEL + } +} + +/// Internal battery voltage channel. +pub struct Vbat; +impl InternalChannel for Vbat {} +impl super::SealedInternalChannel for Vbat { + fn channel(&self) -> u8 { + VBAT_CHANNEL + } +} + +// NOTE (unused): The prescaler enum closely copies the hardware capabilities, +// but high prescaling doesn't make a lot of sense in the current implementation and is ommited. +#[allow(unused)] +enum Prescaler { + NotDivided, + DividedBy2, + DividedBy4, + DividedBy6, + DividedBy8, + DividedBy10, + DividedBy12, + DividedBy16, + DividedBy32, + DividedBy64, + DividedBy128, + DividedBy256, +} + +impl Prescaler { + fn from_ker_ck(frequency: Hertz) -> Self { + let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + match raw_prescaler { + 0 => Self::NotDivided, + 1 => Self::DividedBy2, + 2..=3 => Self::DividedBy4, + 4..=5 => Self::DividedBy6, + 6..=7 => Self::DividedBy8, + 8..=9 => Self::DividedBy10, + 10..=11 => Self::DividedBy12, + _ => unimplemented!(), + } + } + + fn divisor(&self) -> u32 { + match self { + Prescaler::NotDivided => 1, + Prescaler::DividedBy2 => 2, + Prescaler::DividedBy4 => 4, + Prescaler::DividedBy6 => 6, + Prescaler::DividedBy8 => 8, + Prescaler::DividedBy10 => 10, + Prescaler::DividedBy12 => 12, + Prescaler::DividedBy16 => 16, + Prescaler::DividedBy32 => 32, + Prescaler::DividedBy64 => 64, + Prescaler::DividedBy128 => 128, + Prescaler::DividedBy256 => 256, + } + } + + fn presc(&self) -> Presc { + match self { + Prescaler::NotDivided => Presc::DIV1, + Prescaler::DividedBy2 => Presc::DIV2, + Prescaler::DividedBy4 => Presc::DIV4, + Prescaler::DividedBy6 => Presc::DIV6, + Prescaler::DividedBy8 => Presc::DIV8, + Prescaler::DividedBy10 => Presc::DIV10, + Prescaler::DividedBy12 => Presc::DIV12, + Prescaler::DividedBy16 => Presc::DIV16, + Prescaler::DividedBy32 => Presc::DIV32, + Prescaler::DividedBy64 => Presc::DIV64, + Prescaler::DividedBy128 => Presc::DIV128, + Prescaler::DividedBy256 => Presc::DIV256, + } + } +} + +impl<'d, T: Instance> Adc<'d, T> { + /// Create a new ADC driver. + pub fn new(adc: impl Peripheral

+ 'd) -> Self { + embassy_hal_internal::into_ref!(adc); + T::enable_and_reset(); + + let prescaler = Prescaler::from_ker_ck(T::frequency()); + + T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); + + let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + info!("ADC frequency set to {} Hz", frequency.0); + + if frequency > MAX_ADC_CLK_FREQ { + panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); + } + + let mut s = Self { + adc, + sample_time: SampleTime::from_bits(0), + }; + s.power_up(); + s.configure_differential_inputs(); + + s.calibrate(); + blocking_delay_us(1); + + s.enable(); + s.configure(); + + s + } + + fn power_up(&mut self) { + T::regs().cr().modify(|reg| { + reg.set_deeppwd(false); + reg.set_advregen(true); + }); + + blocking_delay_us(10); + } + + fn configure_differential_inputs(&mut self) { + T::regs().difsel().modify(|w| { + for n in 0..20 { + w.set_difsel(n, Difsel::SINGLEENDED); + } + }); + } + + fn calibrate(&mut self) { + T::regs().cr().modify(|w| { + w.set_adcaldif(Adcaldif::SINGLEENDED); + }); + + T::regs().cr().modify(|w| w.set_adcal(true)); + + while T::regs().cr().read().adcal() {} + } + + fn enable(&mut self) { + T::regs().isr().write(|w| w.set_adrdy(true)); + T::regs().cr().modify(|w| w.set_aden(true)); + while !T::regs().isr().read().adrdy() {} + T::regs().isr().write(|w| w.set_adrdy(true)); + } + + fn configure(&mut self) { + // single conversion mode, software trigger + T::regs().cfgr().modify(|w| { + w.set_cont(false); + w.set_exten(Exten::DISABLED); + }); + } + + /// Enable reading the voltage reference internal channel. + pub fn enable_vrefint(&self) -> VrefInt { + T::common_regs().ccr().modify(|reg| { + reg.set_vrefen(true); + }); + + VrefInt {} + } + + /// Enable reading the temperature internal channel. + pub fn enable_temperature(&self) -> Temperature { + T::common_regs().ccr().modify(|reg| { + reg.set_vsenseen(true); + }); + + Temperature {} + } + + /// Enable reading the vbat internal channel. + pub fn enable_vbat(&self) -> Vbat { + T::common_regs().ccr().modify(|reg| { + reg.set_vbaten(true); + }); + + Vbat {} + } + + /// Set the ADC sample time. + pub fn set_sample_time(&mut self, sample_time: SampleTime) { + self.sample_time = sample_time; + } + + /// Set the ADC resolution. + pub fn set_resolution(&mut self, resolution: Resolution) { + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + } + + /// Perform a single conversion. + fn convert(&mut self) -> u16 { + T::regs().isr().modify(|reg| { + reg.set_eos(true); + reg.set_eoc(true); + }); + + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + + while !T::regs().isr().read().eos() { + // spin + } + + T::regs().dr().read().0 as u16 + } + + /// Read an ADC pin. + pub fn read

(&mut self, pin: &mut P) -> u16 + where + P: AdcPin, + P: crate::gpio::Pin, + { + pin.set_as_analog(); + + self.read_channel(pin.channel()) + } + + /// Read an ADC internal channel. + pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { + self.read_channel(channel.channel()) + } + + fn read_channel(&mut self, channel: u8) -> u16 { + // Configure channel + Self::set_channel_sample_time(channel, self.sample_time); + + #[cfg(stm32h7)] + { + T::regs().cfgr2().modify(|w| w.set_lshift(0)); + T::regs() + .pcsel() + .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); + } + + T::regs().sqr1().write(|reg| { + reg.set_sq(0, channel); + reg.set_l(0); + }); + + self.convert() + } + + fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + let sample_time = sample_time.into(); + if ch <= 9 { + T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); + } else { + T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); + } + } +} diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 24dd7cc3c..12c5751bd 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -12,6 +12,7 @@ #[cfg_attr(adc_v2, path = "v2.rs")] #[cfg_attr(any(adc_v3, adc_g0, adc_h5), path = "v3.rs")] #[cfg_attr(adc_v4, path = "v4.rs")] +#[cfg_attr(adc_g4, path = "g4.rs")] mod _version; #[allow(unused)] @@ -79,13 +80,37 @@ pub(crate) fn blocking_delay_us(us: u32) { } /// ADC instance. -#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5)))] +#[cfg(not(any( + adc_f1, + adc_v1, + adc_l0, + adc_v2, + adc_v3, + adc_v4, + adc_g4, + adc_f3, + adc_f3_v1_1, + adc_g0, + adc_h5 +)))] #[allow(private_bounds)] pub trait Instance: SealedInstance + crate::Peripheral

{ type Interrupt: crate::interrupt::typelevel::Interrupt; } /// ADC instance. -#[cfg(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5))] +#[cfg(any( + adc_f1, + adc_v1, + adc_l0, + adc_v2, + adc_v3, + adc_v4, + adc_g4, + adc_f3, + adc_f3_v1_1, + adc_g0, + adc_h5 +))] #[allow(private_bounds)] pub trait Instance: SealedInstance + crate::Peripheral

+ crate::rcc::RccPeripheral { type Interrupt: crate::interrupt::typelevel::Interrupt; diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index 68b54e406..3de38cbd6 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs @@ -29,7 +29,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut adc = Adc::new(p.ADC2); - adc.set_sample_time(SampleTime::CYCLES32_5); + adc.set_sample_time(SampleTime::CYCLES24_5); loop { let measured = adc.read(&mut p.PA7); From 65c085ce910f50903bc5c41ca82eda989810f855 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 13 Apr 2024 02:13:41 +0200 Subject: [PATCH 06/23] Add stm32u0 support. --- ci.sh | 3 ++ embassy-stm32/Cargo.toml | 38 +++++++++++++++++++++++-- embassy-stm32/src/exti.rs | 16 +++++------ embassy-stm32/src/rcc/l.rs | 28 +++++++++++++++--- embassy-stm32/src/rcc/mco.rs | 2 +- embassy-stm32/src/rcc/mod.rs | 2 +- examples/stm32u0/.cargo/config.toml | 9 ++++++ examples/stm32u0/Cargo.toml | 25 ++++++++++++++++ examples/stm32u0/build.rs | 5 ++++ examples/stm32u0/src/bin/blinky.rs | 26 +++++++++++++++++ examples/stm32u0/src/bin/button.rs | 24 ++++++++++++++++ examples/stm32u0/src/bin/button_exti.rs | 25 ++++++++++++++++ 12 files changed, 187 insertions(+), 16 deletions(-) create mode 100644 examples/stm32u0/.cargo/config.toml create mode 100644 examples/stm32u0/Cargo.toml create mode 100644 examples/stm32u0/build.rs create mode 100644 examples/stm32u0/src/bin/blinky.rs create mode 100644 examples/stm32u0/src/bin/button.rs create mode 100644 examples/stm32u0/src/bin/button_exti.rs diff --git a/ci.sh b/ci.sh index d17f4e13e..1393b9d47 100755 --- a/ci.sh +++ b/ci.sh @@ -149,6 +149,9 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \ --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\ --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \ --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \ diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 93792ecf8..459d2e370 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -30,6 +30,7 @@ flavors = [ { regex_feature = "stm32l1.*", target = "thumbv7m-none-eabi" }, { regex_feature = "stm32l4.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32l5.*", target = "thumbv8m.main-none-eabihf", features = ["low-power"] }, + { regex_feature = "stm32u0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32u5.*", target = "thumbv8m.main-none-eabihf" }, { regex_feature = "stm32wb.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32wba.*", target = "thumbv8m.main-none-eabihf" }, @@ -70,7 +71,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0c4baf478324e19741c7a9795ab0aa8217c3691c" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d674277b78ca7400ecfeeb1b5af4e460a65c1a61" } vcell = "0.1.3" nb = "1.0.0" @@ -96,7 +97,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0c4baf478324e19741c7a9795ab0aa8217c3691c", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d674277b78ca7400ecfeeb1b5af4e460a65c1a61", default-features = false, features = ["metadata"]} [features] default = ["rt"] @@ -1419,6 +1420,38 @@ stm32l562qe = [ "stm32-metapac/stm32l562qe" ] stm32l562re = [ "stm32-metapac/stm32l562re" ] stm32l562ve = [ "stm32-metapac/stm32l562ve" ] stm32l562ze = [ "stm32-metapac/stm32l562ze" ] +stm32u031c6 = [ "stm32-metapac/stm32u031c6" ] +stm32u031c8 = [ "stm32-metapac/stm32u031c8" ] +stm32u031f4 = [ "stm32-metapac/stm32u031f4" ] +stm32u031f6 = [ "stm32-metapac/stm32u031f6" ] +stm32u031f8 = [ "stm32-metapac/stm32u031f8" ] +stm32u031g6 = [ "stm32-metapac/stm32u031g6" ] +stm32u031g8 = [ "stm32-metapac/stm32u031g8" ] +stm32u031k4 = [ "stm32-metapac/stm32u031k4" ] +stm32u031k6 = [ "stm32-metapac/stm32u031k6" ] +stm32u031k8 = [ "stm32-metapac/stm32u031k8" ] +stm32u031r6 = [ "stm32-metapac/stm32u031r6" ] +stm32u031r8 = [ "stm32-metapac/stm32u031r8" ] +stm32u073c8 = [ "stm32-metapac/stm32u073c8" ] +stm32u073cb = [ "stm32-metapac/stm32u073cb" ] +stm32u073cc = [ "stm32-metapac/stm32u073cc" ] +stm32u073h8 = [ "stm32-metapac/stm32u073h8" ] +stm32u073hb = [ "stm32-metapac/stm32u073hb" ] +stm32u073hc = [ "stm32-metapac/stm32u073hc" ] +stm32u073k8 = [ "stm32-metapac/stm32u073k8" ] +stm32u073kb = [ "stm32-metapac/stm32u073kb" ] +stm32u073kc = [ "stm32-metapac/stm32u073kc" ] +stm32u073m8 = [ "stm32-metapac/stm32u073m8" ] +stm32u073mb = [ "stm32-metapac/stm32u073mb" ] +stm32u073mc = [ "stm32-metapac/stm32u073mc" ] +stm32u073r8 = [ "stm32-metapac/stm32u073r8" ] +stm32u073rb = [ "stm32-metapac/stm32u073rb" ] +stm32u073rc = [ "stm32-metapac/stm32u073rc" ] +stm32u083cc = [ "stm32-metapac/stm32u083cc" ] +stm32u083hc = [ "stm32-metapac/stm32u083hc" ] +stm32u083kc = [ "stm32-metapac/stm32u083kc" ] +stm32u083mc = [ "stm32-metapac/stm32u083mc" ] +stm32u083rc = [ "stm32-metapac/stm32u083rc" ] stm32u535cb = [ "stm32-metapac/stm32u535cb" ] stm32u535cc = [ "stm32-metapac/stm32u535cc" ] stm32u535ce = [ "stm32-metapac/stm32u535ce" ] @@ -1474,6 +1507,7 @@ stm32u599vj = [ "stm32-metapac/stm32u599vj" ] stm32u599zi = [ "stm32-metapac/stm32u599zi" ] stm32u599zj = [ "stm32-metapac/stm32u599zj" ] stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ] +stm32u5a5qi = [ "stm32-metapac/stm32u5a5qi" ] stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ] stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ] stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ] diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 8d5dae436..224d51b84 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -27,11 +27,11 @@ fn cpu_regs() -> pac::exti::Exti { EXTI } -#[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))] +#[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))] fn exticr_regs() -> pac::syscfg::Syscfg { pac::SYSCFG } -#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] +#[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] fn exticr_regs() -> pac::exti::Exti { EXTI } @@ -44,9 +44,9 @@ unsafe fn on_irq() { #[cfg(feature = "low-power")] crate::low_power::on_wakeup_irq(); - #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] + #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] let bits = EXTI.pr(0).read().0; - #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] + #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0; // We don't handle or change any EXTI lines above 16. @@ -61,9 +61,9 @@ unsafe fn on_irq() { } // Clear pending - #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] + #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] EXTI.pr(0).write_value(Lines(bits)); - #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] + #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] { EXTI.rpr(0).write_value(Lines(bits)); EXTI.fpr(0).write_value(Lines(bits)); @@ -241,9 +241,9 @@ impl<'a> ExtiInputFuture<'a> { EXTI.ftsr(0).modify(|w| w.set_line(pin, falling)); // clear pending bit - #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))] + #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] EXTI.pr(0).write(|w| w.set_line(pin, true)); - #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))] + #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] { EXTI.rpr(0).write(|w| w.set_line(pin, true)); EXTI.fpr(0).write(|w| w.set_line(pin, true)); diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index 9079ddd41..d7235ac7f 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs @@ -49,6 +49,7 @@ pub struct Config { pub sys: Sysclk, pub ahb_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, + #[cfg(not(stm32u0))] pub apb2_pre: APBPrescaler, #[cfg(any(stm32wl5x, stm32wb))] pub core2_ahb_pre: AHBPrescaler, @@ -75,6 +76,7 @@ impl Default for Config { sys: Sysclk::MSI, ahb_pre: AHBPrescaler::DIV1, apb1_pre: APBPrescaler::DIV1, + #[cfg(not(stm32u0))] apb2_pre: APBPrescaler::DIV1, #[cfg(any(stm32wl5x, stm32wb))] core2_ahb_pre: AHBPrescaler::DIV1, @@ -130,7 +132,7 @@ pub const WPAN_DEFAULT: Config = Config { }; fn msi_enable(range: MSIRange) { - #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))] + #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] RCC.cr().modify(|w| { #[cfg(not(stm32wb))] w.set_msirgsel(crate::pac::rcc::vals::Msirgsel::CR); @@ -240,7 +242,7 @@ pub(crate) unsafe fn init(config: Config) { let pll_input = PllInput { hse, hsi, - #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))] + #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] msi, }; let pll = init_pll(PllInstance::Pll, config.pll, &pll_input); @@ -254,6 +256,10 @@ pub(crate) unsafe fn init(config: Config) { Sysclk::HSI => hsi.unwrap(), Sysclk::MSI => msi.unwrap(), Sysclk::PLL1_R => pll.r.unwrap(), + #[cfg(stm32u0)] + Sysclk::LSI | Sysclk::LSE => todo!(), + #[cfg(stm32u0)] + Sysclk::_RESERVED_6 | Sysclk::_RESERVED_7 => unreachable!(), }; #[cfg(rcc_l4plus)] @@ -263,6 +269,7 @@ pub(crate) unsafe fn init(config: Config) { let hclk1 = sys_clk / config.ahb_pre; let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre); + #[cfg(not(stm32u0))] let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre); #[cfg(any(stm32l4, stm32l5, stm32wlex))] let hclk2 = hclk1; @@ -315,6 +322,13 @@ pub(crate) unsafe fn init(config: Config) { ..=64_000_000 => 3, _ => 4, }; + #[cfg(stm32u0)] + let latency = match hclk1.0 { + // VOS RANGE1, others TODO. + ..=24_000_000 => 0, + ..=48_000_000 => 1, + _ => 2, + }; #[cfg(stm32l1)] FLASH.acr().write(|w| w.set_acc64(true)); @@ -326,7 +340,11 @@ pub(crate) unsafe fn init(config: Config) { RCC.cfgr().modify(|w| { w.set_sw(config.sys); w.set_hpre(config.ahb_pre); + #[cfg(stm32u0)] + w.set_ppre(config.apb1_pre); + #[cfg(not(stm32u0))] w.set_ppre1(config.apb1_pre); + #[cfg(not(stm32u0))] w.set_ppre2(config.apb2_pre); }); while RCC.cfgr().read().sws() != config.sys {} @@ -353,8 +371,10 @@ pub(crate) unsafe fn init(config: Config) { #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))] hclk3: Some(hclk3), pclk1: Some(pclk1), + #[cfg(not(stm32u0))] pclk2: Some(pclk2), pclk1_tim: Some(pclk1_tim), + #[cfg(not(stm32u0))] pclk2_tim: Some(pclk2_tim), #[cfg(stm32wl)] pclk3: Some(hclk3), @@ -408,7 +428,7 @@ fn msirange_to_hertz(range: MSIRange) -> Hertz { Hertz(32_768 * (1 << (range as u8 + 1))) } -#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))] +#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] fn msirange_to_hertz(range: MSIRange) -> Hertz { match range { MSIRange::RANGE100K => Hertz(100_000), @@ -521,7 +541,7 @@ mod pll { } } -#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))] +#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] mod pll { use super::{pll_enable, PllInstance}; pub use crate::pac::rcc::vals::{ diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index d8604e07e..4b22a099d 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs @@ -52,7 +52,7 @@ macro_rules! impl_peri { }; } -#[cfg(any(rcc_c0, rcc_g0))] +#[cfg(any(rcc_c0, rcc_g0, rcc_u0))] #[allow(unused_imports)] use self::{McoSource as Mco1Source, McoSource as Mco2Source}; diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 5497bba07..a4e497fe7 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -25,7 +25,7 @@ pub use hsi48::*; #[cfg_attr(stm32g0, path = "g0.rs")] #[cfg_attr(stm32g4, path = "g4.rs")] #[cfg_attr(any(stm32h5, stm32h7), path = "h.rs")] -#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl), path = "l.rs")] +#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")] #[cfg_attr(stm32u5, path = "u5.rs")] #[cfg_attr(stm32wba, path = "wba.rs")] mod _version; diff --git a/examples/stm32u0/.cargo/config.toml b/examples/stm32u0/.cargo/config.toml new file mode 100644 index 000000000..688347084 --- /dev/null +++ b/examples/stm32u0/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace stm32u083rctx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip stm32u083rctx" + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml new file mode 100644 index 000000000..495be3e75 --- /dev/null +++ b/examples/stm32u0/Cargo.toml @@ -0,0 +1,25 @@ +[package] +edition = "2021" +name = "embassy-stm32u0-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +# 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"] } +embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" +embedded-hal = "0.2.6" +panic-probe = { version = "0.3", features = ["print-defmt"] } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +heapless = { version = "0.8", default-features = false } + +[profile.release] +debug = 2 diff --git a/examples/stm32u0/build.rs b/examples/stm32u0/build.rs new file mode 100644 index 000000000..8cd32d7ed --- /dev/null +++ b/examples/stm32u0/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/stm32u0/src/bin/blinky.rs b/examples/stm32u0/src/bin/blinky.rs new file mode 100644 index 000000000..90e479aae --- /dev/null +++ b/examples/stm32u0/src/bin/blinky.rs @@ -0,0 +1,26 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut led = Output::new(p.PA5, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after_millis(300).await; + + info!("low"); + led.set_low(); + Timer::after_millis(300).await; + } +} diff --git a/examples/stm32u0/src/bin/button.rs b/examples/stm32u0/src/bin/button.rs new file mode 100644 index 000000000..8017f0274 --- /dev/null +++ b/examples/stm32u0/src/bin/button.rs @@ -0,0 +1,24 @@ +#![no_std] +#![no_main] + +use cortex_m_rt::entry; +use defmt::*; +use embassy_stm32::gpio::{Input, Pull}; +use {defmt_rtt as _, panic_probe as _}; + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let button = Input::new(p.PC13, Pull::Up); + + loop { + if button.is_high() { + info!("high"); + } else { + info!("low"); + } + } +} diff --git a/examples/stm32u0/src/bin/button_exti.rs b/examples/stm32u0/src/bin/button_exti.rs new file mode 100644 index 000000000..34a08bbc6 --- /dev/null +++ b/examples/stm32u0/src/bin/button_exti.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::exti::ExtiInput; +use embassy_stm32::gpio::Pull; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); + + info!("Press the USER button..."); + + loop { + button.wait_for_falling_edge().await; + info!("Pressed!"); + button.wait_for_rising_edge().await; + info!("Released!"); + } +} From 5178c24cf4b0fcf4ac25f76b5456cf96e745a2be Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Tue, 9 Apr 2024 11:00:44 -0700 Subject: [PATCH 07/23] Fix embassy-futures test failure Running `cargo test` in embassy-futures was failing. The `no_run` tag on this doc example caused it to still try and compile this example, just not run it, and compilation failed. This updates the example so that it can successfully compile and run. --- embassy-futures/src/yield_now.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 From e147f29b5ccbe94fb0703a4cb37ce6a403c321f3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 14 Apr 2024 22:21:19 +0200 Subject: [PATCH 08/23] ci: test embassy-futures. --- .github/ci/test.sh | 1 + 1 file changed, 1 insertion(+) 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 From 1741209ba242e43ba4f2974c685ca2fab7dca766 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 14 Apr 2024 23:59:38 +0200 Subject: [PATCH 09/23] update nightly. Fixes docs build. --- rust-toolchain-nightly.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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", From 86706bdc1438cdccf017d073f9b8f8ff2a0322fe Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 14 Apr 2024 19:35:59 -0300 Subject: [PATCH 10/23] Add map method --- embassy-sync/src/mutex.rs | 133 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) 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]); + } +} From a25cae46c5f01821911f7d989e7d80ff5d8a5376 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 Apr 2024 00:52:42 +0200 Subject: [PATCH 11/23] ci: test embassy-sync on miri. --- .github/ci/test-nightly.sh | 1 + 1 file changed, 1 insertion(+) 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 From fcaa7de87e48463b50b9842288b25ae648dc351f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 Apr 2024 01:12:28 +0200 Subject: [PATCH 12/23] stm32/rcc: make ClockMux non_exhasutive. --- embassy-stm32/build.rs | 1 + 1 file changed, 1 insertion(+) 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, )* } From f66b63017c6ab317af633564a2d8dafd79f71807 Mon Sep 17 00:00:00 2001 From: Torin Cooper-Bennun Date: Thu, 11 Apr 2024 15:45:04 +0100 Subject: [PATCH 13/23] stm32: flash: h50: implement bank swapping --- embassy-stm32/src/flash/h50.rs | 50 ++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) 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() From 6ffecb1250bbbdf7f1336479d8f7b7aa1fd00285 Mon Sep 17 00:00:00 2001 From: Jack Hogan Date: Mon, 15 Apr 2024 14:11:28 -0400 Subject: [PATCH 14/23] Added MAC address getter to cyw43 controller --- cyw43/src/control.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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. From 40e7ea47bac9afc70696a66010b28928535ca075 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 Apr 2024 20:20:48 +0200 Subject: [PATCH 15/23] temporarily disable stm32l152re/usart_rx_ringbuffered test. --- ci.sh | 3 +++ 1 file changed, 3 insertions(+) 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 From 2fa0bb7d6eaf73dcb589f831b97559f5b81bb9c1 Mon Sep 17 00:00:00 2001 From: Warren Campbell Date: Mon, 15 Apr 2024 14:54:01 -0400 Subject: [PATCH 16/23] Use non-secure registers for u5 flash --- embassy-stm32/src/flash/u5.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index 580c490da..4a2168b14 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs @@ -14,26 +14,26 @@ pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { } pub(crate) unsafe fn lock() { - pac::FLASH.seccr().modify(|w| w.set_lock(true)); + pac::FLASH.nscr().modify(|w| w.set_lock(true)); } pub(crate) unsafe fn unlock() { - if pac::FLASH.seccr().read().lock() { - pac::FLASH.seckeyr().write_value(0x4567_0123); - pac::FLASH.seckeyr().write_value(0xCDEF_89AB); + 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); - pac::FLASH.seccr().write(|w| { - w.set_pg(pac::flash::vals::SeccrPg::B_0X1); + pac::FLASH.nscr().write(|w| { + w.set_pg(pac::flash::vals::NscrPg::B_0X1); }); } pub(crate) unsafe fn disable_blocking_write() { - pac::FLASH.seccr().write(|w| w.set_pg(pac::flash::vals::SeccrPg::B_0X0)); + 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 +50,19 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) } pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { - pac::FLASH.seccr().modify(|w| { - w.set_per(pac::flash::vals::SeccrPer::B_0X1); + pac::FLASH.nscr().modify(|w| { + w.set_per(pac::flash::vals::NscrPer::B_0X1); w.set_pnb(sector.index_in_bank) }); - pac::FLASH.seccr().modify(|w| { + pac::FLASH.nscr().modify(|w| { w.set_strt(true); }); let ret: Result<(), Error> = blocking_wait_ready(); pac::FLASH - .seccr() - .modify(|w| w.set_per(pac::flash::vals::SeccrPer::B_0X0)); + .nscr() + .modify(|w| w.set_per(pac::flash::vals::NscrPer::B_0X0)); clear_all_err(); ret } @@ -70,12 +70,12 @@ 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. - pac::FLASH.secsr().modify(|_| {}); + pac::FLASH.nssr().modify(|_| {}); } unsafe fn blocking_wait_ready() -> Result<(), Error> { loop { - let sr = pac::FLASH.secsr().read(); + let sr = pac::FLASH.nssr().read(); if !sr.bsy() { if sr.pgserr() { From be087e5d43326178878878bebae96f3b51a3b184 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 Apr 2024 21:19:17 +0200 Subject: [PATCH 17/23] stm32/spi: remove DMA generic params. --- .vscode/settings.json | 3 +- embassy-stm32/src/i2s.rs | 24 +- embassy-stm32/src/lib.rs | 23 + embassy-stm32/src/spi/mod.rs | 671 ++++++++++-------- embassy-stm32/src/traits.rs | 23 + examples/stm32f4/src/bin/spi.rs | 3 +- examples/stm32f4/src/bin/ws2812_spi.rs | 4 +- examples/stm32g0/src/bin/spi_neopixel.rs | 3 +- examples/stm32h7/src/bin/spi.rs | 6 +- examples/stm32h7/src/bin/spi_dma.rs | 6 +- examples/stm32l0/src/bin/spi.rs | 3 +- examples/stm32l1/src/bin/spi.rs | 3 +- .../src/bin/spe_adin1110_http_server.rs | 24 +- examples/stm32l4/src/bin/spi.rs | 3 +- .../stm32l4/src/bin/spi_blocking_async.rs | 3 +- tests/stm32/src/bin/spi.rs | 5 +- 16 files changed, 442 insertions(+), 365 deletions(-) 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/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..1f0f85936 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -17,6 +17,29 @@ include!(concat!(env!("OUT_DIR"), "/_macros.rs")); // Utilities 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/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 450975f18..a4465e289 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, AnyChannel, Request, Transfer}; +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>, + txdma: Option<(PeripheralRef<'d, AnyChannel>, Request)>, + rxdma: Option<(PeripheralRef<'d, AnyChannel>, Request)>, + _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, + txdma: Option<(PeripheralRef<'d, AnyChannel>, Request)>, + rxdma: Option<(PeripheralRef<'d, AnyChannel>, Request)>, config: Config, ) -> Self { - into_ref!(peri, txdma, rxdma); + into_ref!(peri); let pclk = T::frequency(); let freq = config.frequency; @@ -336,6 +212,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { txdma, rxdma, 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,338 @@ 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, + txdma: impl Peripheral

> + 'd, + rxdma: 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!(txdma), + new_dma!(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, + rxdma: 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!(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, + 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!(txdma), + 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, + txdma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + None, + new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), + None, + new_dma!(txdma), + None, + 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, new_dma!(txdma), new_dma!(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, new_dma!(txdma), new_dma!(rxdma), 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 (txdma, tx_request) = self.txdma.as_mut().unwrap(); + let tx_dst = T::REGS.tx_ptr(); + let tx_f = unsafe { Transfer::new_write(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> { + 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 (rxdma, rx_request) = self.rxdma.as_mut().unwrap(); + let rx_src = T::REGS.rx_ptr(); + let rx_f = unsafe { Transfer::new_read(rxdma, *rx_request, rx_src, data, Default::default()) }; + + let (txdma, tx_request) = self.txdma.as_mut().unwrap(); + let tx_dst = T::REGS.tx_ptr(); + let clock_byte = 0x00u8; + let tx_f = unsafe { + Transfer::new_write_repeated( + 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> { + 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 (rxdma, rx_request) = self.rxdma.as_mut().unwrap(); + let rx_src = T::REGS.rx_ptr(); + let rx_f = unsafe { Transfer::new_read_raw(rxdma, *rx_request, rx_src, read, Default::default()) }; + + let (txdma, tx_request) = self.txdma.as_mut().unwrap(); + let tx_dst = T::REGS.tx_ptr(); + let tx_f = unsafe { Transfer::new_write_raw(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> { + 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()); @@ -900,7 +945,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 +953,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 +967,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 +1004,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 +1139,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/traits.rs b/embassy-stm32/src/traits.rs index 13f695821..539302c49 100644 --- a/embassy-stm32/src/traits.rs +++ b/embassy-stm32/src/traits.rs @@ -69,3 +69,26 @@ macro_rules! dma_trait_impl { } }; } + +macro_rules! new_dma { + ($name:ident) => {{ + let dma = $name.into_ref(); + let req = dma.request(); + Some((dma.map_into(), req)) + }}; +} + +macro_rules! new_pin { + ($name:ident, $aftype:expr, $speed:expr) => {{ + let pin = $name.into_ref(); + pin.set_as_af(pin.af_num(), $aftype); + pin.set_speed($speed); + Some(pin.map_into()) + }}; + ($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/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/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/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/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/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/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index 77aa929ab..a99d08924 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,7 +55,7 @@ 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>; 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/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]; From 02da66aec8432f71b8f3121a972044191df2d7e8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 Apr 2024 21:47:37 +0200 Subject: [PATCH 18/23] stm32/dma: add ChannelAndRequest helper. --- embassy-stm32/src/dma/mod.rs | 3 ++ embassy-stm32/src/dma/util.rs | 60 +++++++++++++++++++++++++++++++++++ embassy-stm32/src/spi/mod.rs | 40 +++++++++++------------ embassy-stm32/src/traits.rs | 7 ++-- 4 files changed, 86 insertions(+), 24 deletions(-) create mode 100644 embassy-stm32/src/dma/util.rs 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/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index a4465e289..303b85346 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -9,7 +9,7 @@ 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, AnyChannel, Request, Transfer}; +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}; @@ -97,8 +97,8 @@ pub struct Spi<'d, T: Instance, M: PeriMode> { sck: Option>, mosi: Option>, miso: Option>, - txdma: Option<(PeripheralRef<'d, AnyChannel>, Request)>, - rxdma: Option<(PeripheralRef<'d, AnyChannel>, Request)>, + txdma: Option>, + rxdma: Option>, _phantom: PhantomData, current_word_size: word_impl::Config, } @@ -109,8 +109,8 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { sck: Option>, mosi: Option>, miso: Option>, - txdma: Option<(PeripheralRef<'d, AnyChannel>, Request)>, - rxdma: Option<(PeripheralRef<'d, AnyChannel>, Request)>, + txdma: Option>, + rxdma: Option>, config: Config, ) -> Self { into_ref!(peri); @@ -593,9 +593,8 @@ impl<'d, T: Instance> Spi<'d, T, Async> { w.set_spe(false); }); - let (txdma, tx_request) = self.txdma.as_mut().unwrap(); let tx_dst = T::REGS.tx_ptr(); - let tx_f = unsafe { Transfer::new_write(txdma, *tx_request, data, tx_dst, Default::default()) }; + let tx_f = unsafe { self.txdma.as_mut().unwrap().write(data, tx_dst, Default::default()) }; set_txdmaen(T::REGS, true); T::REGS.cr1().modify(|w| { @@ -632,22 +631,16 @@ impl<'d, T: Instance> Spi<'d, T, Async> { let clock_byte_count = data.len(); - let (rxdma, rx_request) = self.rxdma.as_mut().unwrap(); let rx_src = T::REGS.rx_ptr(); - let rx_f = unsafe { Transfer::new_read(rxdma, *rx_request, rx_src, data, Default::default()) }; + let rx_f = unsafe { self.rxdma.as_mut().unwrap().read(rx_src, data, Default::default()) }; - let (txdma, tx_request) = self.txdma.as_mut().unwrap(); let tx_dst = T::REGS.tx_ptr(); let clock_byte = 0x00u8; let tx_f = unsafe { - Transfer::new_write_repeated( - txdma, - *tx_request, - &clock_byte, - clock_byte_count, - tx_dst, - Default::default(), - ) + self.txdma + .as_mut() + .unwrap() + .write_repeated(&clock_byte, clock_byte_count, tx_dst, Default::default()) }; set_txdmaen(T::REGS, true); @@ -685,13 +678,16 @@ impl<'d, T: Instance> Spi<'d, T, Async> { set_rxdmaen(T::REGS, true); - let (rxdma, rx_request) = self.rxdma.as_mut().unwrap(); let rx_src = T::REGS.rx_ptr(); - let rx_f = unsafe { Transfer::new_read_raw(rxdma, *rx_request, rx_src, read, Default::default()) }; + let rx_f = unsafe { self.rxdma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) }; - let (txdma, tx_request) = self.txdma.as_mut().unwrap(); let tx_dst = T::REGS.tx_ptr(); - let tx_f = unsafe { Transfer::new_write_raw(txdma, *tx_request, write, tx_dst, Default::default()) }; + let tx_f = unsafe { + self.txdma + .as_mut() + .unwrap() + .write_raw(write, tx_dst, Default::default()) + }; set_txdmaen(T::REGS, true); T::REGS.cr1().modify(|w| { diff --git a/embassy-stm32/src/traits.rs b/embassy-stm32/src/traits.rs index 539302c49..f603f661f 100644 --- a/embassy-stm32/src/traits.rs +++ b/embassy-stm32/src/traits.rs @@ -73,8 +73,11 @@ macro_rules! dma_trait_impl { macro_rules! new_dma { ($name:ident) => {{ let dma = $name.into_ref(); - let req = dma.request(); - Some((dma.map_into(), req)) + let request = dma.request(); + Some(crate::dma::ChannelAndRequest { + channel: dma.map_into(), + request, + }) }}; } From 09a284e9592cc426b5a3c3b72b3f02c0c4844742 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 Apr 2024 21:48:10 +0200 Subject: [PATCH 19/23] stm32: rename mod traits to macros. --- embassy-stm32/src/lib.rs | 2 +- embassy-stm32/src/{traits.rs => macros.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename embassy-stm32/src/{traits.rs => macros.rs} (100%) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 1f0f85936..7d2b49ff4 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -15,8 +15,8 @@ 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 {} diff --git a/embassy-stm32/src/traits.rs b/embassy-stm32/src/macros.rs similarity index 100% rename from embassy-stm32/src/traits.rs rename to embassy-stm32/src/macros.rs From 2eab099b85a7e0ba10d1f558dce89112cd577c68 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 Apr 2024 21:56:08 +0200 Subject: [PATCH 20/23] stm32/spi: rename rxdma, txdma -> rx_dma, tx_dma. --- embassy-stm32/src/spi/mod.rs | 54 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 303b85346..c39ef1913 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -97,8 +97,8 @@ pub struct Spi<'d, T: Instance, M: PeriMode> { sck: Option>, mosi: Option>, miso: Option>, - txdma: Option>, - rxdma: Option>, + tx_dma: Option>, + rx_dma: Option>, _phantom: PhantomData, current_word_size: word_impl::Config, } @@ -109,8 +109,8 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { sck: Option>, mosi: Option>, miso: Option>, - txdma: Option>, - rxdma: Option>, + tx_dma: Option>, + rx_dma: Option>, config: Config, ) -> Self { into_ref!(peri); @@ -209,8 +209,8 @@ impl<'d, T: Instance, M: PeriMode> Spi<'d, T, M> { sck, mosi, miso, - txdma, - rxdma, + tx_dma, + rx_dma, current_word_size: ::CONFIG, _phantom: PhantomData, } @@ -479,8 +479,8 @@ impl<'d, T: Instance> Spi<'d, T, Async> { sck: impl Peripheral

> + 'd, mosi: impl Peripheral

> + 'd, miso: impl Peripheral

> + 'd, - txdma: impl Peripheral

> + 'd, - rxdma: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Self { Self::new_inner( @@ -488,8 +488,8 @@ impl<'d, T: Instance> Spi<'d, T, Async> { 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!(txdma), - new_dma!(rxdma), + new_dma!(tx_dma), + new_dma!(rx_dma), config, ) } @@ -499,7 +499,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, miso: impl Peripheral

> + 'd, - rxdma: impl Peripheral

> + 'd, + rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Self { Self::new_inner( @@ -508,7 +508,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { None, new_pin!(miso, AFType::Input, Speed::VeryHigh), None, - new_dma!(rxdma), + new_dma!(rx_dma), config, ) } @@ -518,7 +518,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, mosi: impl Peripheral

> + 'd, - txdma: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

> + 'd, config: Config, ) -> Self { Self::new_inner( @@ -526,7 +526,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), None, - new_dma!(txdma), + new_dma!(tx_dma), None, config, ) @@ -538,7 +538,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { pub fn new_txonly_nosck( peri: impl Peripheral

+ 'd, mosi: impl Peripheral

> + 'd, - txdma: impl Peripheral

> + 'd, + tx_dma: impl Peripheral

> + 'd, config: Config, ) -> Self { Self::new_inner( @@ -546,7 +546,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { None, new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh), None, - new_dma!(txdma), + new_dma!(tx_dma), None, config, ) @@ -556,8 +556,8 @@ impl<'d, T: Instance> Spi<'d, T, Async> { /// 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, + 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. @@ -569,17 +569,17 @@ impl<'d, T: Instance> Spi<'d, T, Async> { config.bit_order = BitOrder::MsbFirst; config.frequency = freq; - Self::new_inner(peri, None, None, None, new_dma!(txdma), new_dma!(rxdma), config) + 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, - txdma: impl Peripheral

> + 'd, - rxdma: 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!(txdma), new_dma!(rxdma), config) + Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) } /// SPI write, using DMA. @@ -594,7 +594,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> { }); let tx_dst = T::REGS.tx_ptr(); - let tx_f = unsafe { self.txdma.as_mut().unwrap().write(data, tx_dst, Default::default()) }; + 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| { @@ -632,12 +632,12 @@ impl<'d, T: Instance> Spi<'d, T, Async> { let clock_byte_count = data.len(); let rx_src = T::REGS.rx_ptr(); - let rx_f = unsafe { self.rxdma.as_mut().unwrap().read(rx_src, data, Default::default()) }; + 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.txdma + self.tx_dma .as_mut() .unwrap() .write_repeated(&clock_byte, clock_byte_count, tx_dst, Default::default()) @@ -679,11 +679,11 @@ impl<'d, T: Instance> Spi<'d, T, Async> { set_rxdmaen(T::REGS, true); let rx_src = T::REGS.rx_ptr(); - let rx_f = unsafe { self.rxdma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) }; + 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.txdma + self.tx_dma .as_mut() .unwrap() .write_raw(write, tx_dst, Default::default()) From 913bb19a34b88e996972c6666679b2b99ae01ce0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 Apr 2024 23:40:12 +0200 Subject: [PATCH 21/23] stm32/i2c: remove DMA generic params. --- embassy-stm32/src/i2c/mod.rs | 61 +- embassy-stm32/src/i2c/v1.rs | 50 +- embassy-stm32/src/i2c/v2.rs | 522 +++++++++--------- examples/stm32f4/src/bin/i2c.rs | 18 +- examples/stm32l4/src/bin/i2c.rs | 18 +- .../stm32l4/src/bin/i2c_blocking_async.rs | 18 +- .../src/bin/spe_adin1110_http_server.rs | 2 +- examples/stm32u5/src/bin/i2c.rs | 18 +- 8 files changed, 312 insertions(+), 395 deletions(-) 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/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/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 a99d08924..694629ede 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -60,7 +60,7 @@ 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/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)); From c5119c6318f72a69f1bdfb91e7987dff954d6c1e Mon Sep 17 00:00:00 2001 From: Warren Campbell Date: Mon, 15 Apr 2024 17:32:44 -0400 Subject: [PATCH 22/23] Add support for using secure registers --- embassy-stm32/Cargo.toml | 3 +++ embassy-stm32/src/flash/u5.rs | 39 +++++++++++++++++++++++++++++++++++ examples/stm32u5/Cargo.toml | 4 ++++ 3 files changed, 46 insertions(+) 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/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index 4a2168b14..ddd4d73ff 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs @@ -14,10 +14,19 @@ 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); @@ -27,12 +36,20 @@ pub(crate) unsafe fn unlock() { 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)); } @@ -50,16 +67,32 @@ 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)); @@ -70,11 +103,17 @@ 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() { 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 From d6b1233f16407e3501efcd1885460645a75ed57b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 16 Apr 2024 02:00:43 +0200 Subject: [PATCH 23/23] stm32/usart: remove DMA generic params. --- embassy-stm32/src/macros.rs | 10 +- embassy-stm32/src/usart/mod.rs | 868 ++++++++++++------- embassy-stm32/src/usart/ringbuffered.rs | 20 +- examples/stm32f3/src/bin/usart_dma.rs | 3 +- examples/stm32f4/src/bin/usart.rs | 3 +- examples/stm32f4/src/bin/usart_dma.rs | 3 +- examples/stm32f7/src/bin/usart_dma.rs | 3 +- examples/stm32h5/src/bin/usart.rs | 8 +- examples/stm32h5/src/bin/usart_dma.rs | 3 +- examples/stm32h5/src/bin/usart_split.rs | 18 +- examples/stm32h7/src/bin/usart.rs | 8 +- examples/stm32h7/src/bin/usart_dma.rs | 3 +- examples/stm32h7/src/bin/usart_split.rs | 18 +- examples/stm32l4/src/bin/usart.rs | 3 +- examples/stm32l4/src/bin/usart_dma.rs | 3 +- tests/stm32/src/bin/usart.rs | 8 +- tests/stm32/src/bin/usart_rx_ringbuffered.rs | 3 +- 17 files changed, 574 insertions(+), 411 deletions(-) diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs index f603f661f..14137bc37 100644 --- a/embassy-stm32/src/macros.rs +++ b/embassy-stm32/src/macros.rs @@ -82,12 +82,12 @@ macro_rules! new_dma { } macro_rules! new_pin { - ($name:ident, $aftype:expr, $speed:expr) => {{ - let pin = $name.into_ref(); - pin.set_as_af(pin.af_num(), $aftype); - pin.set_speed($speed); - Some(pin.map_into()) + ($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); 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/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/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/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/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/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/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/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;