From 2ab1b2ac9a13ae0436f1a2c32f984c01a67ebde7 Mon Sep 17 00:00:00 2001 From: Karun Date: Fri, 26 Jan 2024 20:40:05 -0500 Subject: [PATCH 01/69] Update stm-32 build script to include ospi traits --- embassy-stm32/build.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 948ce3aff..f1ead6d46 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -891,6 +891,18 @@ fn main() { (("quadspi", "BK2_IO3"), quote!(crate::qspi::BK2D3Pin)), (("quadspi", "BK2_NCS"), quote!(crate::qspi::BK2NSSPin)), (("quadspi", "CLK"), quote!(crate::qspi::SckPin)), + (("octospi", "IO0"), quote!(crate::ospi::D0Pin)), + (("octospi", "IO1"), quote!(crate::ospi::D1Pin)), + (("octospi", "IO2"), quote!(crate::ospi::D2Pin)), + (("octospi", "IO3"), quote!(crate::ospi::D3Pin)), + (("octospi", "IO4"), quote!(crate::ospi::D4Pin)), + (("octospi", "IO5"), quote!(crate::ospi::D5Pin)), + (("octospi", "IO6"), quote!(crate::ospi::D6Pin)), + (("octospi", "IO7"), quote!(crate::ospi::D7Pin)), + (("octospi", "DQS"), quote!(crate::ospi::DQSPin)), + (("octospi", "NCS"), quote!(crate::ospi::NSSPin)), + (("octospi", "CLK"), quote!(crate::ospi::SckPin)), + (("octospi", "NCLK"), quote!(crate::ospi::NckPin)), ].into(); for p in METADATA.peripherals { @@ -1012,6 +1024,7 @@ fn main() { // SDMMCv1 uses the same channel for both directions, so just implement for RX (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), + (("octospi", "OCTOSPI1"), quote!(crate::ospi::OctoDma)), (("dac", "CH1"), quote!(crate::dac::DacDma1)), (("dac", "CH2"), quote!(crate::dac::DacDma2)), (("timer", "UP"), quote!(crate::timer::UpDma)), From 9905bbe9f7ed195932bb7d14dd363a9624aabdc7 Mon Sep 17 00:00:00 2001 From: Karun Date: Fri, 26 Jan 2024 20:40:32 -0500 Subject: [PATCH 02/69] Update peripheral crate to updated octospi pac --- embassy-stm32/Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 865970dfb..b80509098 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -67,8 +67,8 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa 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-656ecf6714fa34fdfb3b3e2f2cd034bffed3f303" } +# stm32-metapac = { version = "15" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2d51fbe7363a376606cb670cc2cec0f634251022" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -86,8 +86,8 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] 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-656ecf6714fa34fdfb3b3e2f2cd034bffed3f303", default-features = false, features = ["metadata"]} +# stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2d51fbe7363a376606cb670cc2cec0f634251022", default-features = false, features = ["metadata"]} [features] From f3609f2842d95bf1408e3bef108e0bd30f87f9d5 Mon Sep 17 00:00:00 2001 From: Karun Date: Fri, 26 Jan 2024 20:40:48 -0500 Subject: [PATCH 03/69] Add initial octopsi module --- embassy-stm32/src/lib.rs | 2 + embassy-stm32/src/ospi/mod.rs | 178 ++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 embassy-stm32/src/ospi/mod.rs diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index a465fccd8..e18b16935 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -57,6 +57,8 @@ pub mod ipcc; pub mod low_power; #[cfg(opamp)] pub mod opamp; +#[cfg(octospi)] +pub mod ospi; #[cfg(quadspi)] pub mod qspi; #[cfg(rng)] diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs new file mode 100644 index 000000000..44824e42a --- /dev/null +++ b/embassy-stm32/src/ospi/mod.rs @@ -0,0 +1,178 @@ +//! OCTOSPI Serial Peripheral Interface +//! + +#![macro_use] + +use core::ptr; + +use embassy_embedded_hal::SetConfig; +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::sealed::{AFType, Pin as _}; +use crate::gpio::{AnyPin, Pull}; +use crate::pac::octospi::{regs, vals, Octospi as Regs}; +use crate::rcc::RccPeripheral; +use crate::time::Hertz; +use crate::{peripherals, Peripheral}; + +pub struct Config; + +pub struct Ospi<'d, T: Instance, Dma> { + _peri: PeripheralRef<'d, T>, + sck: Option>, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + d4: Option>, + d5: Option>, + d6: Option>, + d7: Option>, + nss: Option>, + dqs: Option>, + dma: PeripheralRef<'d, Dma>, + config: Config, +} + +impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { + /// Create new OSPI driver for a dualspi external chip + pub fn new_dualspi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, sck, d0, d1, nss); + + sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); + d1.set_speed(crate::gpio::Speed::VeryHigh); + + Self::new_inner( + peri, + Some(d0.map_into()), + Some(d1.map_into()), + None, + None, + None, + None, + None, + None, + Some(sck.map_into()), + Some(nss.map_into()), + None, + dma, + config, + ) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + d4: Option>, + d5: Option>, + d6: Option>, + d7: Option>, + sck: Option>, + nss: Option>, + dqs: Option>, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, dma); + + T::enable_and_reset(); + T::REGS.sr().read().busy(); + + T::REGS.cr().modify(|w| { + w.set_en(true); + }); + + #[cfg(octospi_v1)] + { + T::REGS.ccr().modify(|w| { + w.set_imode(vals::PhaseMode::TWOLINES); + w.set_admode(vals::PhaseMode::TWOLINES); + w.set_abmode(vals::PhaseMode::TWOLINES); + w.set_dmode(vals::PhaseMode::TWOLINES); + }); + T::REGS.wccr().modify(|w| { + w.set_imode(vals::PhaseMode::TWOLINES); + w.set_admode(vals::PhaseMode::TWOLINES); + w.set_abmode(vals::PhaseMode::TWOLINES); + w.set_dmode(vals::PhaseMode::TWOLINES); + }); + } + + // + + // while T::REGS::sr().read().busy() {} + + Self { + _peri: peri, + sck, + d0, + d1, + d2, + d3, + d4, + d5, + d6, + d7, + nss, + dqs, + dma, + config, + } + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance { + const REGS: Regs; + } +} + +/// OSPI instance trait. +pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} + +pin_trait!(SckPin, Instance); +pin_trait!(NckPin, Instance); +pin_trait!(D0Pin, Instance); +pin_trait!(D1Pin, Instance); +pin_trait!(D2Pin, Instance); +pin_trait!(D3Pin, Instance); +pin_trait!(D4Pin, Instance); +pin_trait!(D5Pin, Instance); +pin_trait!(D6Pin, Instance); +pin_trait!(D7Pin, Instance); +pin_trait!(DQSPin, Instance); +pin_trait!(NSSPin, Instance); + +dma_trait!(OctoDma, Instance); + +foreach_peripheral!( + (octospi, $inst:ident) => { + impl sealed::Instance for peripherals::$inst { + const REGS: Regs = crate::pac::$inst; + } + + impl Instance for peripherals::$inst {} + }; +); From 9ed8d01b1102c574e23ffb4994a0b377fb762af2 Mon Sep 17 00:00:00 2001 From: Karun Date: Mon, 5 Feb 2024 21:23:03 -0500 Subject: [PATCH 04/69] Add transfer config, trait, functional initial configuration and read from memory --- embassy-stm32/src/ospi/mod.rs | 218 ++++++++++++++++++++++++++++++---- 1 file changed, 198 insertions(+), 20 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 44824e42a..547de65d9 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -3,12 +3,17 @@ #![macro_use] +pub mod enums; + +use core::ops::Add; use core::ptr; use embassy_embedded_hal::SetConfig; use embassy_futures::join::join; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embedded_hal_02::blocking::i2c::Operation; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; +use enums::*; use crate::dma::{slice_ptr_parts, word, Transfer}; use crate::gpio::sealed::{AFType, Pin as _}; @@ -18,8 +23,109 @@ use crate::rcc::RccPeripheral; use crate::time::Hertz; use crate::{peripherals, Peripheral}; +/// OPSI driver config. pub struct Config; +/// OSPI transfer configuration. +pub struct TransferConfig { + /// Instruction width (IMODE) + pub iwidth: OspiWidth, + /// Instruction Id + pub instruction: Option, + /// Number of Instruction Bytes + pub isize: AddressSize, + /// Instruction Double Transfer rate enable + pub idtr: bool, + + /// Address width (ADMODE) + pub adwidth: OspiWidth, + /// Device memory address + pub address: Option, + /// Number of Address Bytes + pub adsize: AddressSize, + /// Address Double Transfer rate enable + pub addtr: bool, + + /// Alternate bytes width (ABMODE) + pub abwidth: OspiWidth, + /// Alternate Bytes + pub alternate_bytes: Option, + /// Number of Alternate Bytes + pub absize: AddressSize, + /// Alternate Bytes Double Transfer rate enable + pub abdtr: bool, + + /// Data width (DMODE) + pub dwidth: OspiWidth, + /// Length of data + pub data_len: Option, + /// Data buffer + pub ddtr: bool, + + /// Number of dummy cycles (DCYC) + pub dummy: DummyCycles, +} + +impl Default for TransferConfig { + fn default() -> Self { + Self { + iwidth: OspiWidth::NONE, + instruction: None, + isize: AddressSize::_8Bit, + idtr: false, + + adwidth: OspiWidth::NONE, + address: None, + adsize: AddressSize::_8Bit, + addtr: false, + + abwidth: OspiWidth::NONE, + alternate_bytes: None, + absize: AddressSize::_8Bit, + abdtr: false, + + dwidth: OspiWidth::NONE, + data_len: None, + ddtr: false, + + dummy: DummyCycles::_0, + } + } +} + +pub enum OspiError { + Test, +} + +pub trait Error {} + +pub trait ErrorType { + type Error: Error; +} + +impl ErrorType for &mut T { + type Error = T::Error; +} + +/// MultiSpi interface trait +pub trait MultiSpi: ErrorType { + /// Transaction configuration for specific multispi implementation + type Config; + + /// Command function used for a configuration operation, when no user data is + /// supplied to or read from the target device. + async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error>; + + /// Read function used to read data from the target device following the supplied transaction + /// configuration. + async fn read(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error>; + + /// Write function used to send data to the target device following the supplied transaction + /// configuration. + async fn write(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error>; +} + +/// OSPI driver. pub struct Ospi<'d, T: Instance, Dma> { _peri: PeripheralRef<'d, T>, sck: Option>, @@ -37,6 +143,28 @@ pub struct Ospi<'d, T: Instance, Dma> { config: Config, } +impl Error for OspiError {} + +impl<'d, T: Instance, Dma> ErrorType for Ospi<'d, T, Dma> { + type Error = OspiError; +} + +impl<'d, T: Instance, Dma: OctoDma> MultiSpi for Ospi<'d, T, Dma> { + type Config = TransferConfig; + + async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error> { + Ok(()) + } + + async fn read(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error> { + Ok(()) + } + + async fn write(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error> { + Ok(()) + } +} + impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { /// Create new OSPI driver for a dualspi external chip pub fn new_dualspi( @@ -54,11 +182,29 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { sck.set_speed(crate::gpio::Speed::VeryHigh); nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); nss.set_speed(crate::gpio::Speed::VeryHigh); + // nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Down); + // nss.set_speed(crate::gpio::Speed::VeryHigh); d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); d0.set_speed(crate::gpio::Speed::VeryHigh); d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); d1.set_speed(crate::gpio::Speed::VeryHigh); + #[cfg(octospi_v1)] + { + T::REGS.ccr().modify(|w| { + w.set_imode(vals::PhaseMode::TWOLINES); + w.set_admode(vals::PhaseMode::TWOLINES); + w.set_abmode(vals::PhaseMode::TWOLINES); + w.set_dmode(vals::PhaseMode::TWOLINES); + }); + T::REGS.wccr().modify(|w| { + w.set_imode(vals::PhaseMode::TWOLINES); + w.set_admode(vals::PhaseMode::TWOLINES); + w.set_abmode(vals::PhaseMode::TWOLINES); + w.set_dmode(vals::PhaseMode::TWOLINES); + }); + } + Self::new_inner( peri, Some(d0.map_into()), @@ -96,31 +242,18 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { into_ref!(peri, dma); T::enable_and_reset(); - T::REGS.sr().read().busy(); + while T::REGS.sr().read().busy() {} T::REGS.cr().modify(|w| { w.set_en(true); }); - #[cfg(octospi_v1)] - { - T::REGS.ccr().modify(|w| { - w.set_imode(vals::PhaseMode::TWOLINES); - w.set_admode(vals::PhaseMode::TWOLINES); - w.set_abmode(vals::PhaseMode::TWOLINES); - w.set_dmode(vals::PhaseMode::TWOLINES); - }); - T::REGS.wccr().modify(|w| { - w.set_imode(vals::PhaseMode::TWOLINES); - w.set_admode(vals::PhaseMode::TWOLINES); - w.set_abmode(vals::PhaseMode::TWOLINES); - w.set_dmode(vals::PhaseMode::TWOLINES); - }); - } - - // - - // while T::REGS::sr().read().busy() {} + T::REGS.dcr1().modify(|w| { + w.set_devsize(23); + w.set_mtyp(vals::MemType::MACRONIX); + w.set_ckmode(false); + // w.se + }); Self { _peri: peri, @@ -139,6 +272,51 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { config, } } + + pub fn blocking_read(&mut self, transaction: TransferConfig) -> Result<(), ()> { + Ok(()) + } + + fn configure_command(&mut self, command: &TransferConfig) -> Result<(), ()> { + Ok(()) + } + + /// Poor attempt to read data from memory + pub fn receive(&mut self, buf: &mut [u8], intruction: u8, data_len: usize) -> Result<(), ()> { + T::REGS.cr().modify(|w| { + w.set_fmode(vals::FunctionalMode::INDIRECTREAD); + }); + + T::REGS.ccr().modify(|w| { + w.set_imode(vals::PhaseMode::ONELINE); + w.set_admode(vals::PhaseMode::NONE); + w.set_abmode(vals::PhaseMode::NONE); + + w.set_dmode(vals::PhaseMode::ONELINE); + }); + + T::REGS.dlr().modify(|w| { + w.set_dl((data_len - 1) as u32); + }); + + // set instruction + T::REGS.ir().modify(|w| w.set_instruction(intruction as u32)); + + // read bytes + // for idx in 0..data_len { + // while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} + // buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; + // } + // wait for finish + while !T::REGS.sr().read().tcf() {} + + let fifo_count = T::REGS.sr().read().flevel(); + for idx in 0..(fifo_count as usize) { + buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; + } + + Ok(()) + } } pub(crate) mod sealed { From a0b70672054784276d6c370b87fd4635314656ae Mon Sep 17 00:00:00 2001 From: Karun Date: Mon, 5 Feb 2024 21:24:33 -0500 Subject: [PATCH 05/69] Add user enums for transaction configuration --- embassy-stm32/src/ospi/enums.rs | 336 ++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 embassy-stm32/src/ospi/enums.rs diff --git a/embassy-stm32/src/ospi/enums.rs b/embassy-stm32/src/ospi/enums.rs new file mode 100644 index 000000000..2209d7b94 --- /dev/null +++ b/embassy-stm32/src/ospi/enums.rs @@ -0,0 +1,336 @@ +//! Enums used in Ospi configuration. + +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub(crate) enum OspiMode { + IndirectWrite, + IndirectRead, + AutoPolling, + MemoryMapped, +} + +impl Into for OspiMode { + fn into(self) -> u8 { + match self { + OspiMode::IndirectWrite => 0b00, + OspiMode::IndirectRead => 0b01, + OspiMode::AutoPolling => 0b10, + OspiMode::MemoryMapped => 0b11, + } + } +} + +/// Ospi lane width +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum OspiWidth { + /// None + NONE, + /// Single lane + SING, + /// Dual lanes + DUAL, + /// Quad lanes + QUAD, + /// Eight lanes + OCTO, +} + +impl Into for OspiWidth { + fn into(self) -> u8 { + match self { + OspiWidth::NONE => 0b00, + OspiWidth::SING => 0b01, + OspiWidth::DUAL => 0b10, + OspiWidth::QUAD => 0b11, + OspiWidth::OCTO => 0b100, + } + } +} + +/// Flash bank selection +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum FlashSelection { + /// Bank 1 + Flash1, + /// Bank 2 + Flash2, +} + +impl Into for FlashSelection { + fn into(self) -> bool { + match self { + FlashSelection::Flash1 => false, + FlashSelection::Flash2 => true, + } + } +} + +/// Ospi memory size. +#[allow(missing_docs)] +#[derive(Copy, Clone)] +pub enum MemorySize { + _1KiB, + _2KiB, + _4KiB, + _8KiB, + _16KiB, + _32KiB, + _64KiB, + _128KiB, + _256KiB, + _512KiB, + _1MiB, + _2MiB, + _4MiB, + _8MiB, + _16MiB, + _32MiB, + _64MiB, + _128MiB, + _256MiB, + _512MiB, + _1GiB, + _2GiB, + _4GiB, + Other(u8), +} + +impl Into for MemorySize { + fn into(self) -> u8 { + match self { + MemorySize::_1KiB => 9, + MemorySize::_2KiB => 10, + MemorySize::_4KiB => 11, + MemorySize::_8KiB => 12, + MemorySize::_16KiB => 13, + MemorySize::_32KiB => 14, + MemorySize::_64KiB => 15, + MemorySize::_128KiB => 16, + MemorySize::_256KiB => 17, + MemorySize::_512KiB => 18, + MemorySize::_1MiB => 19, + MemorySize::_2MiB => 20, + MemorySize::_4MiB => 21, + MemorySize::_8MiB => 22, + MemorySize::_16MiB => 23, + MemorySize::_32MiB => 24, + MemorySize::_64MiB => 25, + MemorySize::_128MiB => 26, + MemorySize::_256MiB => 27, + MemorySize::_512MiB => 28, + MemorySize::_1GiB => 29, + MemorySize::_2GiB => 30, + MemorySize::_4GiB => 31, + MemorySize::Other(val) => val, + } + } +} + +/// Ospi Address size +#[derive(Copy, Clone)] +pub enum AddressSize { + /// 8-bit address + _8Bit, + /// 16-bit address + _16Bit, + /// 24-bit address + _24bit, + /// 32-bit address + _32bit, +} + +impl Into for AddressSize { + fn into(self) -> u8 { + match self { + AddressSize::_8Bit => 0b00, + AddressSize::_16Bit => 0b01, + AddressSize::_24bit => 0b10, + AddressSize::_32bit => 0b11, + } + } +} + +/// Time the Chip Select line stays high. +#[allow(missing_docs)] +#[derive(Copy, Clone)] +pub enum ChipSelectHighTime { + _1Cycle, + _2Cycle, + _3Cycle, + _4Cycle, + _5Cycle, + _6Cycle, + _7Cycle, + _8Cycle, +} + +impl Into for ChipSelectHighTime { + fn into(self) -> u8 { + match self { + ChipSelectHighTime::_1Cycle => 0, + ChipSelectHighTime::_2Cycle => 1, + ChipSelectHighTime::_3Cycle => 2, + ChipSelectHighTime::_4Cycle => 3, + ChipSelectHighTime::_5Cycle => 4, + ChipSelectHighTime::_6Cycle => 5, + ChipSelectHighTime::_7Cycle => 6, + ChipSelectHighTime::_8Cycle => 7, + } + } +} + +/// FIFO threshold. +#[allow(missing_docs)] +#[derive(Copy, Clone)] +pub enum FIFOThresholdLevel { + _1Bytes, + _2Bytes, + _3Bytes, + _4Bytes, + _5Bytes, + _6Bytes, + _7Bytes, + _8Bytes, + _9Bytes, + _10Bytes, + _11Bytes, + _12Bytes, + _13Bytes, + _14Bytes, + _15Bytes, + _16Bytes, + _17Bytes, + _18Bytes, + _19Bytes, + _20Bytes, + _21Bytes, + _22Bytes, + _23Bytes, + _24Bytes, + _25Bytes, + _26Bytes, + _27Bytes, + _28Bytes, + _29Bytes, + _30Bytes, + _31Bytes, + _32Bytes, +} + +impl Into for FIFOThresholdLevel { + fn into(self) -> u8 { + match self { + FIFOThresholdLevel::_1Bytes => 0, + FIFOThresholdLevel::_2Bytes => 1, + FIFOThresholdLevel::_3Bytes => 2, + FIFOThresholdLevel::_4Bytes => 3, + FIFOThresholdLevel::_5Bytes => 4, + FIFOThresholdLevel::_6Bytes => 5, + FIFOThresholdLevel::_7Bytes => 6, + FIFOThresholdLevel::_8Bytes => 7, + FIFOThresholdLevel::_9Bytes => 8, + FIFOThresholdLevel::_10Bytes => 9, + FIFOThresholdLevel::_11Bytes => 10, + FIFOThresholdLevel::_12Bytes => 11, + FIFOThresholdLevel::_13Bytes => 12, + FIFOThresholdLevel::_14Bytes => 13, + FIFOThresholdLevel::_15Bytes => 14, + FIFOThresholdLevel::_16Bytes => 15, + FIFOThresholdLevel::_17Bytes => 16, + FIFOThresholdLevel::_18Bytes => 17, + FIFOThresholdLevel::_19Bytes => 18, + FIFOThresholdLevel::_20Bytes => 19, + FIFOThresholdLevel::_21Bytes => 20, + FIFOThresholdLevel::_22Bytes => 21, + FIFOThresholdLevel::_23Bytes => 22, + FIFOThresholdLevel::_24Bytes => 23, + FIFOThresholdLevel::_25Bytes => 24, + FIFOThresholdLevel::_26Bytes => 25, + FIFOThresholdLevel::_27Bytes => 26, + FIFOThresholdLevel::_28Bytes => 27, + FIFOThresholdLevel::_29Bytes => 28, + FIFOThresholdLevel::_30Bytes => 29, + FIFOThresholdLevel::_31Bytes => 30, + FIFOThresholdLevel::_32Bytes => 31, + } + } +} + +/// Dummy cycle count +#[allow(missing_docs)] +#[derive(Copy, Clone)] +pub enum DummyCycles { + _0, + _1, + _2, + _3, + _4, + _5, + _6, + _7, + _8, + _9, + _10, + _11, + _12, + _13, + _14, + _15, + _16, + _17, + _18, + _19, + _20, + _21, + _22, + _23, + _24, + _25, + _26, + _27, + _28, + _29, + _30, + _31, +} + +impl Into for DummyCycles { + fn into(self) -> u8 { + match self { + DummyCycles::_0 => 0, + DummyCycles::_1 => 1, + DummyCycles::_2 => 2, + DummyCycles::_3 => 3, + DummyCycles::_4 => 4, + DummyCycles::_5 => 5, + DummyCycles::_6 => 6, + DummyCycles::_7 => 7, + DummyCycles::_8 => 8, + DummyCycles::_9 => 9, + DummyCycles::_10 => 10, + DummyCycles::_11 => 11, + DummyCycles::_12 => 12, + DummyCycles::_13 => 13, + DummyCycles::_14 => 14, + DummyCycles::_15 => 15, + DummyCycles::_16 => 16, + DummyCycles::_17 => 17, + DummyCycles::_18 => 18, + DummyCycles::_19 => 19, + DummyCycles::_20 => 20, + DummyCycles::_21 => 21, + DummyCycles::_22 => 22, + DummyCycles::_23 => 23, + DummyCycles::_24 => 24, + DummyCycles::_25 => 25, + DummyCycles::_26 => 26, + DummyCycles::_27 => 27, + DummyCycles::_28 => 28, + DummyCycles::_29 => 29, + DummyCycles::_30 => 30, + DummyCycles::_31 => 31, + } + } +} From b86a1f07009dfac8849cdc1beeb621fea348ccd5 Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 21 Feb 2024 10:43:49 -0500 Subject: [PATCH 06/69] Add constructors Add transfer configuration Update command configuration Add peripheral width consideration Add drop impl --- embassy-stm32/src/ospi/enums.rs | 23 + embassy-stm32/src/ospi/mod.rs | 718 +++++++++++++++++++++++++++++--- 2 files changed, 673 insertions(+), 68 deletions(-) diff --git a/embassy-stm32/src/ospi/enums.rs b/embassy-stm32/src/ospi/enums.rs index 2209d7b94..e52f1f54d 100644 --- a/embassy-stm32/src/ospi/enums.rs +++ b/embassy-stm32/src/ospi/enums.rs @@ -67,6 +67,29 @@ impl Into for FlashSelection { } } +/// Wrap Size +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum WrapSize { + None, + _16Bytes, + _32Bytes, + _64Bytes, + _128Bytes, +} + +impl Into for WrapSize { + fn into(self) -> u8 { + match self { + WrapSize::None => 0x00, + WrapSize::_16Bytes => 0x02, + WrapSize::_32Bytes => 0x03, + WrapSize::_64Bytes => 0x04, + WrapSize::_128Bytes => 0x05, + } + } +} + /// Ospi memory size. #[allow(missing_docs)] #[derive(Copy, Clone)] diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 547de65d9..48b1ef789 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -11,20 +11,79 @@ use core::ptr; use embassy_embedded_hal::SetConfig; use embassy_futures::join::join; use embassy_hal_internal::{into_ref, PeripheralRef}; -use embedded_hal_02::blocking::i2c::Operation; -pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; -use enums::*; +// use embedded_hal_02::spi; +pub use enums::*; +use stm32_metapac::octospi::vals::{MemType, PhaseMode, SizeInBits}; use crate::dma::{slice_ptr_parts, word, Transfer}; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Pull}; use crate::pac::octospi::{regs, vals, Octospi as Regs}; use crate::rcc::RccPeripheral; -use crate::time::Hertz; use crate::{peripherals, Peripheral}; /// OPSI driver config. -pub struct Config; +pub struct Config { + /// Fifo threshold used by the peripheral to generate the interrupt indicating data + /// or space is available in the FIFO + pub fifo_threshold: FIFOThresholdLevel, + /// Enables dual-quad mode which allows access to two devices simultaneously to + /// increase throughput + pub dual_quad: bool, + /// Indicates the type of external device connected + pub memory_type: MemType, // Need to add an additional enum to provide this public interface + /// Defines the size of the external device connected to the OSPI corresponding + /// to the number of address bits required to access the device + pub device_size: MemorySize, + /// Sets the minimum number of clock cycles that the chip select signal must be held high + /// between commands + pub chip_select_high_time: ChipSelectHighTime, + /// Enables the free running clock + pub free_running_clock: bool, + /// Sets the clock level when the device is not selected + pub clock_mode: bool, + /// Indicates the wrap size corresponding to the external device configuration + pub wrap_size: WrapSize, + /// Specified the prescaler factor used for generating the external clock based + /// on the AHB clock + pub clock_prescaler: u8, + /// Allows the delay of 1/2 cycle the data sampling to account for external + /// signal delays + pub sample_shifting: bool, + /// Allows hold to 1/4 cycle the data + pub delay_hold_quarter_cycle: bool, + /// Enables the transaction boundary feature and defines the boundary to release + /// the chip select + pub chip_select_boundary: u8, + /// Enbales the delay block bypass so the sampling is not affected by the delay block + pub delay_block_bypass: bool, + /// Enables communication regulation feature. Chip select is released when the other + /// OctoSpi requests access to the bus + pub max_transfer: u8, + /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles + pub refresh: u32, +} +impl Default for Config { + fn default() -> Self { + Self { + fifo_threshold: FIFOThresholdLevel::_16Bytes, // 32 bytes FIFO, half capacity + dual_quad: false, + memory_type: MemType::B_STANDARD, + device_size: MemorySize::Other(0), + chip_select_high_time: ChipSelectHighTime::_5Cycle, + free_running_clock: false, + clock_mode: false, + wrap_size: WrapSize::None, + clock_prescaler: 0, + sample_shifting: false, + delay_hold_quarter_cycle: false, + chip_select_boundary: 0, // Acceptable range 0 to 31 + delay_block_bypass: true, + max_transfer: 0, + refresh: 0, + } + } +} /// OSPI transfer configuration. pub struct TransferConfig { @@ -94,7 +153,8 @@ impl Default for TransferConfig { } pub enum OspiError { - Test, + InvalidConfiguration, + InvalidCommand, } pub trait Error {} @@ -122,7 +182,7 @@ pub trait MultiSpi: ErrorType { /// Write function used to send data to the target device following the supplied transaction /// configuration. - async fn write(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error>; + async fn write(&mut self, data: &[u8], config: Self::Config) -> Result<(), Self::Error>; } /// OSPI driver. @@ -141,6 +201,7 @@ pub struct Ospi<'d, T: Instance, Dma> { dqs: Option>, dma: PeripheralRef<'d, Dma>, config: Config, + width: OspiWidth, } impl Error for OspiError {} @@ -153,21 +214,21 @@ impl<'d, T: Instance, Dma: OctoDma> MultiSpi for Ospi<'d, T, Dma> { type Config = TransferConfig; async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error> { - Ok(()) + self.command(&config).await } async fn read(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error> { - Ok(()) + self.read(data, config).await } - async fn write(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error> { - Ok(()) + async fn write(&mut self, data: &[u8], config: Self::Config) -> Result<(), Self::Error> { + self.write(data, config).await } } impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { /// Create new OSPI driver for a dualspi external chip - pub fn new_dualspi( + pub fn new_spi( peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, d0: impl Peripheral

> + 'd, @@ -182,29 +243,11 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { sck.set_speed(crate::gpio::Speed::VeryHigh); nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); nss.set_speed(crate::gpio::Speed::VeryHigh); - // nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Down); - // nss.set_speed(crate::gpio::Speed::VeryHigh); d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); d0.set_speed(crate::gpio::Speed::VeryHigh); - d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); + d1.set_as_af_pull(d1.af_num(), AFType::Input, Pull::None); d1.set_speed(crate::gpio::Speed::VeryHigh); - #[cfg(octospi_v1)] - { - T::REGS.ccr().modify(|w| { - w.set_imode(vals::PhaseMode::TWOLINES); - w.set_admode(vals::PhaseMode::TWOLINES); - w.set_abmode(vals::PhaseMode::TWOLINES); - w.set_dmode(vals::PhaseMode::TWOLINES); - }); - T::REGS.wccr().modify(|w| { - w.set_imode(vals::PhaseMode::TWOLINES); - w.set_admode(vals::PhaseMode::TWOLINES); - w.set_abmode(vals::PhaseMode::TWOLINES); - w.set_dmode(vals::PhaseMode::TWOLINES); - }); - } - Self::new_inner( peri, Some(d0.map_into()), @@ -220,6 +263,209 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { None, dma, config, + OspiWidth::SING, + ) + } + + /// Create new OSPI driver for a dualspi external chip + pub fn new_dualspi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, sck, d0, d1, nss); + + sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); + d1.set_speed(crate::gpio::Speed::VeryHigh); + + Self::new_inner( + peri, + Some(d0.map_into()), + Some(d1.map_into()), + None, + None, + None, + None, + None, + None, + Some(sck.map_into()), + Some(nss.map_into()), + None, + dma, + config, + OspiWidth::DUAL, + ) + } + + /// Create new OSPI driver for a quadspi external chip + pub fn new_quadspi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, sck, d0, d1, d2, d3, nss); + + sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); + d1.set_speed(crate::gpio::Speed::VeryHigh); + d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None); + d2.set_speed(crate::gpio::Speed::VeryHigh); + d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); + d3.set_speed(crate::gpio::Speed::VeryHigh); + + Self::new_inner( + peri, + Some(d0.map_into()), + Some(d1.map_into()), + Some(d2.map_into()), + Some(d3.map_into()), + None, + None, + None, + None, + Some(sck.map_into()), + Some(nss.map_into()), + None, + dma, + config, + OspiWidth::QUAD, + ) + } + + /// Create new OSPI driver for two quadspi external chips + pub fn new_dualquadspi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + d4: impl Peripheral

> + 'd, + d5: impl Peripheral

> + 'd, + d6: impl Peripheral

> + 'd, + d7: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, sck, d0, d1, d2, d3, d4, d5, d6, d7, nss); + + sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); + d1.set_speed(crate::gpio::Speed::VeryHigh); + d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None); + d2.set_speed(crate::gpio::Speed::VeryHigh); + d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); + d3.set_speed(crate::gpio::Speed::VeryHigh); + d4.set_as_af_pull(d4.af_num(), AFType::OutputPushPull, Pull::None); + d4.set_speed(crate::gpio::Speed::VeryHigh); + d5.set_as_af_pull(d5.af_num(), AFType::OutputPushPull, Pull::None); + d5.set_speed(crate::gpio::Speed::VeryHigh); + d6.set_as_af_pull(d6.af_num(), AFType::OutputPushPull, Pull::None); + d6.set_speed(crate::gpio::Speed::VeryHigh); + d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); + d7.set_speed(crate::gpio::Speed::VeryHigh); + + Self::new_inner( + peri, + Some(d0.map_into()), + Some(d1.map_into()), + Some(d2.map_into()), + Some(d3.map_into()), + Some(d4.map_into()), + Some(d5.map_into()), + Some(d6.map_into()), + Some(d7.map_into()), + Some(sck.map_into()), + Some(nss.map_into()), + None, + dma, + config, + OspiWidth::QUAD, + ) + } + + /// Create new OSPI driver for two quadspi external chips + pub fn new_octospi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + d4: impl Peripheral

> + 'd, + d5: impl Peripheral

> + 'd, + d6: impl Peripheral

> + 'd, + d7: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, sck, d0, d1, d2, d3, d4, d5, d6, d7, nss); + + sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); + d1.set_speed(crate::gpio::Speed::VeryHigh); + d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None); + d2.set_speed(crate::gpio::Speed::VeryHigh); + d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); + d3.set_speed(crate::gpio::Speed::VeryHigh); + d4.set_as_af_pull(d4.af_num(), AFType::OutputPushPull, Pull::None); + d4.set_speed(crate::gpio::Speed::VeryHigh); + d5.set_as_af_pull(d5.af_num(), AFType::OutputPushPull, Pull::None); + d5.set_speed(crate::gpio::Speed::VeryHigh); + d6.set_as_af_pull(d6.af_num(), AFType::OutputPushPull, Pull::None); + d6.set_speed(crate::gpio::Speed::VeryHigh); + d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); + d7.set_speed(crate::gpio::Speed::VeryHigh); + + Self::new_inner( + peri, + Some(d0.map_into()), + Some(d1.map_into()), + Some(d2.map_into()), + Some(d3.map_into()), + Some(d4.map_into()), + Some(d5.map_into()), + Some(d6.map_into()), + Some(d7.map_into()), + Some(sck.map_into()), + Some(nss.map_into()), + None, + dma, + config, + OspiWidth::OCTO, ) } @@ -238,22 +484,71 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dqs: Option>, dma: impl Peripheral

+ 'd, config: Config, + width: OspiWidth, ) -> Self { into_ref!(peri, dma); + // System configuration T::enable_and_reset(); while T::REGS.sr().read().busy() {} + // Device configuration + T::REGS.dcr1().modify(|w| { + w.set_devsize(config.device_size.into()); + w.set_mtyp(config.memory_type); + w.set_csht(config.chip_select_high_time.into()); + w.set_dlybyp(config.delay_block_bypass); + w.set_frck(false); + w.set_ckmode(config.clock_mode); + }); + + T::REGS.dcr2().modify(|w| { + w.set_wrapsize(config.wrap_size.into()); + }); + + T::REGS.dcr3().modify(|w| { + w.set_csbound(config.chip_select_boundary); + w.set_maxtran(config.max_transfer); + }); + + T::REGS.dcr4().modify(|w| { + w.set_refresh(config.refresh); + }); + + T::REGS.cr().modify(|w| { + w.set_fthres(vals::Threshold(config.fifo_threshold.into())); + }); + + // Wait for busy flag to clear + while T::REGS.sr().read().busy() {} + + T::REGS.dcr2().modify(|w| { + w.set_prescaler(config.clock_prescaler); + }); + + T::REGS.cr().modify(|w| { + w.set_dmm(config.dual_quad); + }); + + T::REGS.tcr().modify(|w| { + w.set_sshift(match config.sample_shifting { + true => vals::SampleShift::HALFCYCLE, + false => vals::SampleShift::NONE, + }); + w.set_dhqc(config.delay_hold_quarter_cycle); + }); + + // Enable peripheral T::REGS.cr().modify(|w| { w.set_en(true); }); - T::REGS.dcr1().modify(|w| { - w.set_devsize(23); - w.set_mtyp(vals::MemType::MACRONIX); - w.set_ckmode(false); - // w.se - }); + // Free running clock needs to be set after peripheral enable + if config.free_running_clock { + T::REGS.dcr1().modify(|w| { + w.set_frck(config.free_running_clock); + }); + } Self { _peri: peri, @@ -270,53 +565,341 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dqs, dma, config, + width, } } - pub fn blocking_read(&mut self, transaction: TransferConfig) -> Result<(), ()> { - Ok(()) - } + // Function to configure the peripheral for the requested command + fn configure_command(&mut self, command: &TransferConfig) -> Result<(), OspiError> { + // Check that transaction doesn't use more than hardware initialized pins + if >::into(command.iwidth) > >::into(self.width) + || >::into(command.adwidth) > >::into(self.width) + || >::into(command.abwidth) > >::into(self.width) + || >::into(command.dwidth) > >::into(self.width) + { + return Err(OspiError::InvalidCommand); + } - fn configure_command(&mut self, command: &TransferConfig) -> Result<(), ()> { - Ok(()) - } - - /// Poor attempt to read data from memory - pub fn receive(&mut self, buf: &mut [u8], intruction: u8, data_len: usize) -> Result<(), ()> { T::REGS.cr().modify(|w| { - w.set_fmode(vals::FunctionalMode::INDIRECTREAD); + w.set_fmode(0.into()); }); + // Configure alternate bytes + if let Some(ab) = command.alternate_bytes { + T::REGS.abr().write(|v| v.set_alternate(ab)); + T::REGS.ccr().modify(|w| { + w.set_abmode(PhaseMode::from_bits(command.abwidth.into())); + w.set_abdtr(command.abdtr); + w.set_absize(SizeInBits::from_bits(command.absize.into())); + }) + } + + // Configure dummy cycles + T::REGS.tcr().modify(|w| { + w.set_dcyc(command.dummy.into()); + }); + + // Configure data + if let Some(data_length) = command.data_len { + T::REGS.dlr().write(|v| { + v.set_dl((data_length - 1) as u32); + }) + } + + // Configure instruction/address/data modes T::REGS.ccr().modify(|w| { - w.set_imode(vals::PhaseMode::ONELINE); - w.set_admode(vals::PhaseMode::NONE); - w.set_abmode(vals::PhaseMode::NONE); + w.set_imode(PhaseMode::from_bits(command.iwidth.into())); + w.set_idtr(command.idtr); + w.set_isize(SizeInBits::from_bits(command.isize.into())); - w.set_dmode(vals::PhaseMode::ONELINE); + w.set_admode(PhaseMode::from_bits(command.adwidth.into())); + w.set_addtr(command.idtr); + w.set_adsize(SizeInBits::from_bits(command.adsize.into())); + + w.set_dmode(PhaseMode::from_bits(command.dwidth.into())); + w.set_ddtr(command.ddtr); }); - T::REGS.dlr().modify(|w| { - w.set_dl((data_len - 1) as u32); - }); + // Set informationrequired to initiate transaction + if let Some(instruction) = command.instruction { + if let Some(address) = command.address { + T::REGS.ir().write(|v| { + v.set_instruction(instruction); + }); - // set instruction - T::REGS.ir().modify(|w| w.set_instruction(intruction as u32)); + T::REGS.ar().write(|v| { + v.set_address(address); + }); + } else { + // Double check requirements for delay hold and sample shifting + // if let None = command.data_len { + // if self.config.delay_hold_quarter_cycle && command.idtr { + // T::REGS.ccr().modify(|w| { + // w.set_ddtr(true); + // }); + // } + // } - // read bytes - // for idx in 0..data_len { - // while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} - // buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; - // } - // wait for finish - while !T::REGS.sr().read().tcf() {} - - let fifo_count = T::REGS.sr().read().flevel(); - for idx in 0..(fifo_count as usize) { - buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; + T::REGS.ir().write(|v| { + v.set_instruction(instruction); + }); + } + } else { + if let Some(address) = command.address { + T::REGS.ar().write(|v| { + v.set_address(address); + }); + } else { + // The only single phase transaction supported is instruction only + return Err(OspiError::InvalidCommand); + } } Ok(()) } + + /// Function used to control or configure the target device without data transfer + pub async fn command(&mut self, command: &TransferConfig) -> Result<(), OspiError> { + // Prevent a transaction from being set with expected data transmission or reception + if let Some(_) = command.data_len { + return Err(OspiError::InvalidCommand); + }; + while T::REGS.sr().read().busy() {} + + // Need additional validation that command configuration doesn't have data set + self.configure_command(command)?; + + // Transaction initiated by setting final configuration, i.e the instruction register + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().write(|w| { + w.set_ctcf(true); + }); + + Ok(()) + } + + /// Blocking read with byte by byte data transfer + pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) -> Result<(), OspiError> { + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + + // Ensure DMA is not enabled for this transaction + T::REGS.cr().modify(|w| { + w.set_dmaen(false); + }); + + self.configure_command(&transaction)?; + + if let Some(len) = transaction.data_len { + let current_address = T::REGS.ar().read().address(); + let current_instruction = T::REGS.ir().read().instruction(); + + // For a indirect read transaction, the transaction begins when the instruction/address is set + T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); + if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { + T::REGS.ir().write(|v| v.set_instruction(current_instruction)); + } else { + T::REGS.ar().write(|v| v.set_address(current_address)); + } + + for idx in 0..len { + while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} + buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; + } + } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().write(|v| v.set_ctcf(true)); + + Ok(()) + } + + /// Blocking write with byte by byte data transfer + pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) -> Result<(), OspiError> { + T::REGS.cr().modify(|w| { + w.set_dmaen(false); + }); + self.configure_command(&transaction)?; + + if let Some(len) = transaction.data_len { + T::REGS + .cr() + .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); + + for idx in 0..len { + while !T::REGS.sr().read().ftf() {} + unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; + } + } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().write(|v| v.set_ctcf(true)); + + Ok(()) + } + + /// Blocking read with DMA transfer + pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) -> Result<(), OspiError> + where + Dma: OctoDma, + { + self.configure_command(&transaction)?; + + let current_address = T::REGS.ar().read().address(); + let current_instruction = T::REGS.ir().read().instruction(); + + // For a indirect read transaction, the transaction begins when the instruction/address is set + T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); + if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { + T::REGS.ir().write(|v| v.set_instruction(current_instruction)); + } else { + T::REGS.ar().write(|v| v.set_address(current_address)); + } + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_read( + &mut self.dma, + request, + T::REGS.dr().as_ptr() as *mut u8, + buf, + Default::default(), + ) + }; + + T::REGS.cr().modify(|w| w.set_dmaen(true)); + + transfer.blocking_wait(); + + finish_dma(T::REGS); + + Ok(()) + } + + /// Blocking write with DMA transfer + pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) -> Result<(), OspiError> + where + Dma: OctoDma, + { + self.configure_command(&transaction)?; + T::REGS + .cr() + .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_write( + &mut self.dma, + request, + buf, + T::REGS.dr().as_ptr() as *mut u8, + Default::default(), + ) + }; + + T::REGS.cr().modify(|w| w.set_dmaen(true)); + + transfer.blocking_wait(); + + finish_dma(T::REGS); + + Ok(()) + } + + /// Asynchronous read from external device + pub async fn read(&mut self, buf: &mut [u8], transaction: TransferConfig) -> Result<(), OspiError> + where + Dma: OctoDma, + { + self.configure_command(&transaction)?; + + let current_address = T::REGS.ar().read().address(); + let current_instruction = T::REGS.ir().read().instruction(); + + // For a indirect read transaction, the transaction begins when the instruction/address is set + T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); + if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { + T::REGS.ir().write(|v| v.set_instruction(current_instruction)); + } else { + T::REGS.ar().write(|v| v.set_address(current_address)); + } + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_read( + &mut self.dma, + request, + T::REGS.dr().as_ptr() as *mut u8, + buf, + Default::default(), + ) + }; + + T::REGS.cr().modify(|w| w.set_dmaen(true)); + + transfer.await; + + finish_dma(T::REGS); + + Ok(()) + } + + /// Asynchronous write to external device + pub async fn write(&mut self, buf: &[u8], transaction: TransferConfig) -> Result<(), OspiError> + where + Dma: OctoDma, + { + self.configure_command(&transaction)?; + T::REGS + .cr() + .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_write( + &mut self.dma, + request, + buf, + T::REGS.dr().as_ptr() as *mut u8, + Default::default(), + ) + }; + + T::REGS.cr().modify(|w| w.set_dmaen(true)); + + transfer.await; + + finish_dma(T::REGS); + + Ok(()) + } +} + +impl<'d, T: Instance, Dma> Drop for Ospi<'d, T, Dma> { + fn drop(&mut self) { + self.sck.as_ref().map(|x| x.set_as_disconnected()); + self.d0.as_ref().map(|x| x.set_as_disconnected()); + self.d1.as_ref().map(|x| x.set_as_disconnected()); + self.d2.as_ref().map(|x| x.set_as_disconnected()); + self.d3.as_ref().map(|x| x.set_as_disconnected()); + self.d4.as_ref().map(|x| x.set_as_disconnected()); + self.d5.as_ref().map(|x| x.set_as_disconnected()); + self.d6.as_ref().map(|x| x.set_as_disconnected()); + self.d7.as_ref().map(|x| x.set_as_disconnected()); + self.nss.as_ref().map(|x| x.set_as_disconnected()); + self.dqs.as_ref().map(|x| x.set_as_disconnected()); + + T::disable(); + } +} + +fn finish_dma(regs: Regs) { + while !regs.sr().read().tcf() {} + regs.fcr().write(|v| v.set_ctcf(true)); + + regs.cr().modify(|w| { + w.set_dmaen(false); + }); } pub(crate) mod sealed { @@ -342,7 +925,6 @@ pin_trait!(D6Pin, Instance); pin_trait!(D7Pin, Instance); pin_trait!(DQSPin, Instance); pin_trait!(NSSPin, Instance); - dma_trait!(OctoDma, Instance); foreach_peripheral!( From e163572bec221f3367f96ec720d45bc22b985efe Mon Sep 17 00:00:00 2001 From: Karun Date: Thu, 7 Mar 2024 12:04:26 -0500 Subject: [PATCH 07/69] Add get and set config trait implementations --- embassy-stm32/src/ospi/mod.rs | 107 +++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 48b1ef789..8fb5f06e4 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -8,7 +8,7 @@ pub mod enums; use core::ops::Add; use core::ptr; -use embassy_embedded_hal::SetConfig; +use embassy_embedded_hal::{GetConfig, SetConfig}; use embassy_futures::join::join; use embassy_hal_internal::{into_ref, PeripheralRef}; // use embedded_hal_02::spi; @@ -23,6 +23,7 @@ use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; /// OPSI driver config. +#[derive(Clone, Copy)] pub struct Config { /// Fifo threshold used by the peripheral to generate the interrupt indicating data /// or space is available in the FIFO @@ -63,6 +64,7 @@ pub struct Config { /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles pub refresh: u32, } + impl Default for Config { fn default() -> Self { Self { @@ -873,6 +875,83 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { Ok(()) } + + /// Set new bus configuration + pub fn set_config(&mut self, config: &Config) -> Result<(), ()> { + // Wait for busy flag to clear + while T::REGS.sr().read().busy() {} + + // Disable DMA channel while configuring the peripheral + T::REGS.cr().modify(|w| { + w.set_dmaen(false); + }); + + // Device configuration + T::REGS.dcr1().modify(|w| { + w.set_devsize(config.device_size.into()); + w.set_mtyp(config.memory_type); + w.set_csht(config.chip_select_high_time.into()); + w.set_dlybyp(config.delay_block_bypass); + w.set_frck(false); + w.set_ckmode(config.clock_mode); + }); + + T::REGS.dcr2().modify(|w| { + w.set_wrapsize(config.wrap_size.into()); + }); + + T::REGS.dcr3().modify(|w| { + w.set_csbound(config.chip_select_boundary); + w.set_maxtran(config.max_transfer); + }); + + T::REGS.dcr4().modify(|w| { + w.set_refresh(config.refresh); + }); + + T::REGS.cr().modify(|w| { + w.set_fthres(vals::Threshold(config.fifo_threshold.into())); + }); + + // Wait for busy flag to clear + while T::REGS.sr().read().busy() {} + + T::REGS.dcr2().modify(|w| { + w.set_prescaler(config.clock_prescaler); + }); + + T::REGS.cr().modify(|w| { + w.set_dmm(config.dual_quad); + }); + + T::REGS.tcr().modify(|w| { + w.set_sshift(match config.sample_shifting { + true => vals::SampleShift::HALFCYCLE, + false => vals::SampleShift::NONE, + }); + w.set_dhqc(config.delay_hold_quarter_cycle); + }); + + // Enable peripheral + T::REGS.cr().modify(|w| { + w.set_en(true); + }); + + // Free running clock needs to be set after peripheral enable + if config.free_running_clock { + T::REGS.dcr1().modify(|w| { + w.set_frck(config.free_running_clock); + }); + } + + self.config = *config; + Ok(()) + } + + /// Get current configuration + pub fn get_config(&self) -> Config { + self.config + } } impl<'d, T: Instance, Dma> Drop for Ospi<'d, T, Dma> { @@ -902,6 +981,17 @@ fn finish_dma(regs: Regs) { }); } +trait RegsExt { + fn dr_ptr(&self) -> *mut W; +} + +impl RegsExt for Regs { + fn dr_ptr(&self) -> *mut W { + let dr = self.dr(); + dr.as_ptr() as *mut W + } +} + pub(crate) mod sealed { use super::*; @@ -936,3 +1026,18 @@ foreach_peripheral!( impl Instance for peripherals::$inst {} }; ); + +impl<'d, T: Instance, Dma> SetConfig for Ospi<'d, T, Dma> { + type Config = Config; + type ConfigError = (); + fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { + self.set_config(config) + } +} + +impl<'d, T: Instance, Dma> GetConfig for Ospi<'d, T, Dma> { + type Config = Config; + fn get_config(&self) -> Self::Config { + self.get_config() + } +} From 3b1d87050e2a30b598e92979b6f202b67664a29c Mon Sep 17 00:00:00 2001 From: Karun Date: Thu, 7 Mar 2024 12:46:19 -0500 Subject: [PATCH 08/69] Update trait definitions Make operations generic against valid data widths --- embassy-stm32/src/ospi/enums.rs | 27 ++++++++ embassy-stm32/src/ospi/mod.rs | 114 +++++++++++++++++++++----------- 2 files changed, 102 insertions(+), 39 deletions(-) diff --git a/embassy-stm32/src/ospi/enums.rs b/embassy-stm32/src/ospi/enums.rs index e52f1f54d..4021f7ce3 100644 --- a/embassy-stm32/src/ospi/enums.rs +++ b/embassy-stm32/src/ospi/enums.rs @@ -69,6 +69,7 @@ impl Into for FlashSelection { /// Wrap Size #[allow(dead_code)] +#[allow(missing_docs)] #[derive(Copy, Clone)] pub enum WrapSize { None, @@ -90,6 +91,32 @@ impl Into for WrapSize { } } +/// Memory Type +#[allow(missing_docs)] +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum MemoryType { + Micron, + Macronix, + Standard, + MacronixRam, + HyperBusMemory, + HyperBusRegister, +} + +impl Into for MemoryType { + fn into(self) -> u8 { + match self { + MemoryType::Micron => 0x00, + MemoryType::Macronix => 0x01, + MemoryType::Standard => 0x02, + MemoryType::MacronixRam => 0x03, + MemoryType::HyperBusMemory => 0x04, + MemoryType::HyperBusRegister => 0x04, + } + } +} + /// Ospi memory size. #[allow(missing_docs)] #[derive(Copy, Clone)] diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 8fb5f06e4..3d458ba79 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -5,13 +5,9 @@ pub mod enums; -use core::ops::Add; -use core::ptr; - use embassy_embedded_hal::{GetConfig, SetConfig}; -use embassy_futures::join::join; use embassy_hal_internal::{into_ref, PeripheralRef}; -// use embedded_hal_02::spi; +use embedded_hal_1::spi::ErrorKind; pub use enums::*; use stm32_metapac::octospi::vals::{MemType, PhaseMode, SizeInBits}; @@ -32,7 +28,7 @@ pub struct Config { /// increase throughput pub dual_quad: bool, /// Indicates the type of external device connected - pub memory_type: MemType, // Need to add an additional enum to provide this public interface + pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface /// Defines the size of the external device connected to the OSPI corresponding /// to the number of address bits required to access the device pub device_size: MemorySize, @@ -70,7 +66,7 @@ impl Default for Config { Self { fifo_threshold: FIFOThresholdLevel::_16Bytes, // 32 bytes FIFO, half capacity dual_quad: false, - memory_type: MemType::B_STANDARD, + memory_type: MemoryType::Micron, device_size: MemorySize::Other(0), chip_select_high_time: ChipSelectHighTime::_5Cycle, free_running_clock: false, @@ -154,23 +150,17 @@ impl Default for TransferConfig { } } +/// Error used for Octospi implementation +#[derive(Debug)] pub enum OspiError { + /// Peripheral configuration is invalid InvalidConfiguration, + /// Operation configuration is invalid InvalidCommand, } -pub trait Error {} - -pub trait ErrorType { - type Error: Error; -} - -impl ErrorType for &mut T { - type Error = T::Error; -} - /// MultiSpi interface trait -pub trait MultiSpi: ErrorType { +pub trait MultiSpiBus: embedded_hal_1::spi::ErrorType { /// Transaction configuration for specific multispi implementation type Config; @@ -180,11 +170,27 @@ pub trait MultiSpi: ErrorType { /// Read function used to read data from the target device following the supplied transaction /// configuration. - async fn read(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error>; + async fn read(&mut self, data: &mut [Word], config: Self::Config) -> Result<(), Self::Error>; /// Write function used to send data to the target device following the supplied transaction /// configuration. - async fn write(&mut self, data: &[u8], config: Self::Config) -> Result<(), Self::Error>; + async fn write(&mut self, data: &[Word], config: Self::Config) -> Result<(), Self::Error>; +} + +impl + ?Sized, Word: Copy + 'static> MultiSpiBus for &mut T { + type Config = T::Config; + #[inline] + async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error> { + T::command(self, config).await + } + + async fn read(&mut self, data: &mut [Word], config: Self::Config) -> Result<(), Self::Error> { + T::read(self, data, config).await + } + + async fn write(&mut self, data: &[Word], config: Self::Config) -> Result<(), Self::Error> { + T::write(self, data, config).await + } } /// OSPI driver. @@ -206,24 +212,28 @@ pub struct Ospi<'d, T: Instance, Dma> { width: OspiWidth, } -impl Error for OspiError {} +impl embedded_hal_1::spi::Error for OspiError { + fn kind(&self) -> ErrorKind { + ErrorKind::Other + } +} -impl<'d, T: Instance, Dma> ErrorType for Ospi<'d, T, Dma> { +impl<'d, T: Instance, Dma> embedded_hal_1::spi::ErrorType for Ospi<'d, T, Dma> { type Error = OspiError; } -impl<'d, T: Instance, Dma: OctoDma> MultiSpi for Ospi<'d, T, Dma> { +impl<'d, T: Instance, Dma: OctoDma, W: Word> MultiSpiBus for Ospi<'d, T, Dma> { type Config = TransferConfig; async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error> { self.command(&config).await } - async fn read(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error> { + async fn read(&mut self, data: &mut [W], config: Self::Config) -> Result<(), Self::Error> { self.read(data, config).await } - async fn write(&mut self, data: &[u8], config: Self::Config) -> Result<(), Self::Error> { + async fn write(&mut self, data: &[W], config: Self::Config) -> Result<(), Self::Error> { self.write(data, config).await } } @@ -497,7 +507,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { // Device configuration T::REGS.dcr1().modify(|w| { w.set_devsize(config.device_size.into()); - w.set_mtyp(config.memory_type); + w.set_mtyp(vals::MemType::from_bits(config.memory_type.into())); w.set_csht(config.chip_select_high_time.into()); w.set_dlybyp(config.delay_block_bypass); w.set_frck(false); @@ -681,7 +691,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Blocking read with byte by byte data transfer - pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) -> Result<(), OspiError> { + pub fn blocking_read(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} @@ -706,7 +716,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { for idx in 0..len { while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} - buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; + buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() }; } } @@ -717,7 +727,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Blocking write with byte by byte data transfer - pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) -> Result<(), OspiError> { + pub fn blocking_write(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> { T::REGS.cr().modify(|w| { w.set_dmaen(false); }); @@ -730,7 +740,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { for idx in 0..len { while !T::REGS.sr().read().ftf() {} - unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; + unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) }; } } @@ -741,7 +751,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Blocking read with DMA transfer - pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) -> Result<(), OspiError> + pub fn blocking_read_dma(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> where Dma: OctoDma, { @@ -763,7 +773,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { Transfer::new_read( &mut self.dma, request, - T::REGS.dr().as_ptr() as *mut u8, + T::REGS.dr().as_ptr() as *mut W, buf, Default::default(), ) @@ -779,7 +789,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Blocking write with DMA transfer - pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) -> Result<(), OspiError> + pub fn blocking_write_dma(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> where Dma: OctoDma, { @@ -794,7 +804,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { &mut self.dma, request, buf, - T::REGS.dr().as_ptr() as *mut u8, + T::REGS.dr().as_ptr() as *mut W, Default::default(), ) }; @@ -809,7 +819,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Asynchronous read from external device - pub async fn read(&mut self, buf: &mut [u8], transaction: TransferConfig) -> Result<(), OspiError> + pub async fn read(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> where Dma: OctoDma, { @@ -831,7 +841,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { Transfer::new_read( &mut self.dma, request, - T::REGS.dr().as_ptr() as *mut u8, + T::REGS.dr().as_ptr() as *mut W, buf, Default::default(), ) @@ -847,7 +857,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Asynchronous write to external device - pub async fn write(&mut self, buf: &[u8], transaction: TransferConfig) -> Result<(), OspiError> + pub async fn write(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> where Dma: OctoDma, { @@ -862,7 +872,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { &mut self.dma, request, buf, - T::REGS.dr().as_ptr() as *mut u8, + T::REGS.dr().as_ptr() as *mut W, Default::default(), ) }; @@ -889,7 +899,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { // Device configuration T::REGS.dcr1().modify(|w| { w.set_devsize(config.device_size.into()); - w.set_mtyp(config.memory_type); + w.set_mtyp(vals::MemType::from_bits(config.memory_type.into())); w.set_csht(config.chip_select_high_time.into()); w.set_dlybyp(config.delay_block_bypass); w.set_frck(false); @@ -998,6 +1008,10 @@ pub(crate) mod sealed { pub trait Instance { const REGS: Regs; } + + pub trait Word { + const CONFIG: word_impl::Config; + } } /// OSPI instance trait. @@ -1041,3 +1055,25 @@ impl<'d, T: Instance, Dma> GetConfig for Ospi<'d, T, Dma> { self.get_config() } } + +/// Word sizes usable for OSPI. +pub trait Word: word::Word + sealed::Word {} + +macro_rules! impl_word { + ($T:ty, $config:expr) => { + impl sealed::Word for $T { + const CONFIG: Config = $config; + } + impl Word for $T {} + }; +} + +mod word_impl { + use super::*; + + pub type Config = u8; + + impl_word!(u8, 8); + impl_word!(u16, 16); + impl_word!(u32, 32); +} From fda6e3fb8ccae9b92d619fefa01d334177edb12a Mon Sep 17 00:00:00 2001 From: Karun Date: Thu, 7 Mar 2024 15:23:45 -0500 Subject: [PATCH 09/69] Resolve rustfmt issue and unused import errors --- embassy-stm32/build.rs | 1 - embassy-stm32/src/ospi/mod.rs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 3a30ba2fd..6217a3309 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1010,7 +1010,6 @@ fn main() { (("octospi", "NCLK"), quote!(crate::ospi::NckPin)), ].into(); - for p in METADATA.peripherals { if let Some(regs) = &p.registers { for pin in p.pins { diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 3d458ba79..794bdd127 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -9,12 +9,12 @@ use embassy_embedded_hal::{GetConfig, SetConfig}; use embassy_hal_internal::{into_ref, PeripheralRef}; use embedded_hal_1::spi::ErrorKind; pub use enums::*; -use stm32_metapac::octospi::vals::{MemType, PhaseMode, SizeInBits}; +use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; -use crate::dma::{slice_ptr_parts, word, Transfer}; +use crate::dma::{word, Transfer}; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Pull}; -use crate::pac::octospi::{regs, vals, Octospi as Regs}; +use crate::pac::octospi::{vals, Octospi as Regs}; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; From cf065d439efed2141aaf09454beb445e80dc7539 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Tue, 12 Mar 2024 00:54:26 +0800 Subject: [PATCH 10/69] stm32 CORDIC: ZeroOverhead q1.31 1 arg 1 res mode --- embassy-stm32/src/cordic.rs | 460 ++++++++++++++++++++++++++++++++++++ embassy-stm32/src/lib.rs | 2 + 2 files changed, 462 insertions(+) create mode 100644 embassy-stm32/src/cordic.rs diff --git a/embassy-stm32/src/cordic.rs b/embassy-stm32/src/cordic.rs new file mode 100644 index 000000000..952ee187a --- /dev/null +++ b/embassy-stm32/src/cordic.rs @@ -0,0 +1,460 @@ +//! CORDIC co-processor + +use crate::peripherals; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; + +pub use enums::*; + +mod enums { + /// CORDIC function + #[allow(missing_docs)] + #[derive(Clone, Copy)] + pub enum Function { + Cos = 0, + Sin, + Phase, + Modulus, + Arctan, + Cosh, + Sinh, + Arctanh, + Ln, + Sqrt, + } + + /// CORDIC precision + #[allow(missing_docs)] + #[derive(Clone, Copy)] + pub enum Precision { + Iters4 = 1, + Iters8, + Iters12, + Iters16, + Iters20, + Iters24, + Iters28, + Iters32, + Iters36, + Iters40, + Iters44, + Iters48, + Iters52, + Iters56, + Iters60, + } + + /// CORDIC scale + #[allow(non_camel_case_types)] + #[allow(missing_docs)] + #[derive(Clone, Copy, Default)] + pub enum Scale { + #[default] + A1_R1 = 0, + A1o2_R2, + A1o4_R4, + A1o8_R8, + A1o16_R16, + A1o32_R32, + A1o64_R64, + A1o128_R128, + } + + /// CORDIC argument/result count + #[allow(missing_docs)] + #[derive(Clone, Copy, Default)] + pub enum Count { + #[default] + One, + Two, + } + + /// CORDIC argument/result data width + #[allow(missing_docs)] + #[derive(Clone, Copy)] + pub enum Width { + Bits32, + Bits16, + } + + /// Cordic driver running mode + #[derive(Clone, Copy)] + pub enum Mode { + /// After caculation start, a read to RDATA register will block AHB until the caculation finished + ZeroOverhead, + + /// Use CORDIC interrupt to trigger a read result value + Interrupt, + + /// Use DMA to write/read value + Dma, + } +} + +/// Low-level CORDIC access. +#[cfg(feature = "unstable-pac")] +pub mod low_level { + pub use super::sealed::*; +} + +pub(crate) mod sealed { + use super::*; + use crate::pac::cordic::vals; + + /// Cordic instance + pub trait Instance { + /// Get access to CORDIC registers + fn regs() -> crate::pac::cordic::Cordic; + + /// Set Function value + fn set_func(&self, func: Function) { + Self::regs() + .csr() + .modify(|v| v.set_func(vals::Func::from_bits(func as u8))); + } + + /// Set Precision value + fn set_precision(&self, precision: Precision) { + Self::regs() + .csr() + .modify(|v| v.set_precision(vals::Precision::from_bits(precision as u8))) + } + + /// Set Scale value + fn set_scale(&self, scale: Scale) { + Self::regs() + .csr() + .modify(|v| v.set_scale(vals::Scale::from_bits(scale as u8))) + } + + /// Enable global interrupt + fn enable_irq(&self) { + Self::regs().csr().modify(|v| v.set_ien(true)) + } + + /// Disable global interrupt + fn disable_irq(&self) { + Self::regs().csr().modify(|v| v.set_ien(false)) + } + + /// Enable Read DMA + fn enable_read_dma(&self) { + Self::regs().csr().modify(|v| { + v.set_dmaren(true); + }) + } + + /// Disable Read DMA + fn disable_read_dma(&self) { + Self::regs().csr().modify(|v| { + v.set_dmaren(false); + }) + } + + /// Enable Write DMA + fn enable_write_dma(&self) { + Self::regs().csr().modify(|v| { + v.set_dmawen(true); + }) + } + + /// Disable Write DMA + fn disable_write_dma(&self) { + Self::regs().csr().modify(|v| { + v.set_dmawen(false); + }) + } + + /// Set NARGS value + fn set_argument_count(&self, n: Count) { + Self::regs().csr().modify(|v| { + v.set_nargs(match n { + Count::One => vals::Num::NUM1, + Count::Two => vals::Num::NUM2, + }) + }) + } + + /// Set NRES value + fn set_result_count(&self, n: Count) { + Self::regs().csr().modify(|v| { + v.set_nres(match n { + Count::One => vals::Num::NUM1, + Count::Two => vals::Num::NUM2, + }); + }) + } + + /// Set ARGSIZE and RESSIZE value + fn set_data_width(&self, arg: Width, res: Width) { + Self::regs().csr().modify(|v| { + v.set_argsize(match arg { + Width::Bits32 => vals::Size::BITS32, + Width::Bits16 => vals::Size::BITS16, + }); + v.set_ressize(match res { + Width::Bits32 => vals::Size::BITS32, + Width::Bits16 => vals::Size::BITS16, + }) + }) + } + + /// Read RRDY flag + fn ready_to_read(&self) -> bool { + Self::regs().csr().read().rrdy() + } + + /// Write value to WDATA + fn write_argument(&self, arg: u32) { + Self::regs().wdata().write_value(arg) + } + + /// Read value from RDATA + fn read_result(&self) -> u32 { + Self::regs().rdata().read() + } + } +} + +/// CORDIC driver +pub struct Cordic<'d, T: Instance> { + cordic: PeripheralRef<'d, T>, + config: Config, + //state: State, +} + +/// CORDIC instance trait +pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral {} + +/// CORDIC configuration +pub struct Config { + function: Function, + precision: Precision, + scale: Scale, + mode: Mode, + first_result: bool, +} + +// CORDIC running state +//struct State { +// input_buf: [u32; 8], +// buf_len: usize, +//} + +impl Config { + /// Create a config for Cordic driver + pub fn new(function: Function, precision: Precision, scale: Option, mode: Mode, first_result: bool) -> Self { + Self { + function, + precision, + scale: scale.unwrap_or_default(), + mode, + first_result, + } + } + + fn check_scale(&self) -> bool { + let scale_raw = self.scale as u8; + + match self.function { + Function::Cos | Function::Sin | Function::Phase | Function::Modulus => 0 == scale_raw, + Function::Arctan => (0..=7).contains(&scale_raw), + Function::Cosh | Function::Sinh | Function::Arctanh => 1 == scale_raw, + Function::Ln => (1..=4).contains(&scale_raw), + Function::Sqrt => (0..=2).contains(&scale_raw), + } + } +} + +impl<'d, T: Instance> Cordic<'d, T> { + /// Create a Cordic driver instance + /// + /// Note: + /// If you need a periperhal -> CORDIC -> peripehral mode, + /// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguemnts with [Self::extra_config] + pub fn new(cordic: impl Peripheral

+ 'd, config: Config) -> Self { + T::enable_and_reset(); + + into_ref!(cordic); + + if !config.check_scale() { + panic!("Scale value is not compatible with Function") + } + + let mut instance = Self { + cordic, + config, + // state: State { + // input_buf: [0u32; 8], + // buf_len: 0, + // }, + }; + + instance.reconfigure(); + + instance + } + + /// Set a new config for Cordic driver + pub fn set_config(&mut self, config: Config) { + self.config = config; + self.reconfigure(); + } + + /// Set extra config for data count and data width. + pub fn extra_config(&mut self, arg_cnt: Count, arg_width: Width, res_width: Width) { + let peri = &self.cordic; + peri.set_argument_count(arg_cnt); + peri.set_data_width(arg_width, res_width); + } + + fn reconfigure(&mut self) { + let peri = &self.cordic; + let config = &self.config; + + if peri.ready_to_read() { + warn!("At least 1 result hasn't been read, reconfigure will cause DATA LOST"); + }; + + peri.disable_irq(); + peri.disable_write_dma(); + peri.disable_read_dma(); + + // clean RRDY flag + while peri.ready_to_read() { + peri.read_result(); + } + + peri.set_func(config.function); + peri.set_precision(config.precision); + peri.set_scale(config.scale); + if config.first_result { + peri.set_result_count(Count::One) + } else { + peri.set_result_count(Count::Two) + } + + match config.mode { + Mode::ZeroOverhead => (), + Mode::Interrupt => { + peri.enable_irq(); + } + Mode::Dma => { + peri.enable_write_dma(); + peri.enable_read_dma(); + } + } + + //self.state.input_buf.fill(0u32); + } + + /// Run a CORDIC calculation + pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { + match self.config.mode { + Mode::ZeroOverhead => { + if arg2s.is_none() { + self.cordic.set_argument_count(Count::One); + + self.cordic.set_result_count(if self.config.first_result { + if output.len() < arg1s.len() { + panic!("Output buf length is not long enough") + } + Count::One + } else { + if output.len() < 2 * arg1s.len() { + panic!("Output buf length is not long enough") + } + Count::Two + }); + + let mut cnt = 0; + + for &arg in arg1s.iter() { + self.cordic.write_argument(f64_to_q1_31(arg)); + output[cnt] = q1_31_to_f64(self.cordic.read_result()); + cnt += 1; + } + + cnt + } else { + todo!() + } + } + Mode::Interrupt => todo!(), + Mode::Dma => todo!(), + } + } +} + +impl<'d, T: Instance> Drop for Cordic<'d, T> { + fn drop(&mut self) { + T::disable(); + } +} + +foreach_interrupt!( + ($inst:ident, cordic, CORDIC, GLOBAL, $irq:ident) => { + impl Instance for peripherals::$inst { + } + + impl sealed::Instance for peripherals::$inst { + fn regs() -> crate::pac::cordic::Cordic { + crate::pac::$inst + } + } + }; +); + +macro_rules! floating_fixed_convert { + ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => { + /// convert float point to fixed point format + pub fn $f_to_q(value: $float_ty) -> $unsigned_bin_typ { + const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) }; + + assert!( + (-1.0 as $float_ty) <= value, + "input value {} should be equal or greater than -1", + value + ); + + let value = if value == 1.0 as $float_ty{ + (1.0 as $float_ty) - MIN_POSITIVE + } else { + assert!( + value <= (1.0 as $float_ty) - MIN_POSITIVE, + "input value {} should be equal or less than 1-2^(-{})", + value, $offset + ); + value + }; + + (value * ((1 as $unsigned_bin_typ << $offset) as $float_ty)) as $unsigned_bin_typ + } + + #[inline(always)] + /// convert fixed point to float point format + pub fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty { + // It's needed to convert from unsigned to signed first, for correct result. + -(value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty) + } + }; +} + +floating_fixed_convert!( + f64_to_q1_31, + q1_31_to_f64, + u32, + i32, + f64, + 31, + 0x3E00_0000_0000_0000u64 // binary form of 1f64^(-31) +); + +floating_fixed_convert!( + f32_to_q1_15, + q1_15_to_f32, + u16, + i16, + f32, + 15, + 0x3800_0000u32 // binary form of 1f32^(-15) +); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 6a3d1c463..ae2e95435 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -32,6 +32,8 @@ pub mod timer; pub mod adc; #[cfg(can)] pub mod can; +#[cfg(cordic)] +pub mod cordic; #[cfg(crc)] pub mod crc; #[cfg(cryp)] From b595d942442a8b267e1311bcadedc8558183aa61 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Fri, 15 Mar 2024 15:12:51 +0800 Subject: [PATCH 11/69] stm32 CORDIC: split into multiple files --- embassy-stm32/src/cordic.rs | 460 ----------------------------- embassy-stm32/src/cordic/enums.rs | 82 +++++ embassy-stm32/src/cordic/mod.rs | 206 +++++++++++++ embassy-stm32/src/cordic/sealed.rs | 116 ++++++++ embassy-stm32/src/cordic/utils.rs | 59 ++++ 5 files changed, 463 insertions(+), 460 deletions(-) delete mode 100644 embassy-stm32/src/cordic.rs create mode 100644 embassy-stm32/src/cordic/enums.rs create mode 100644 embassy-stm32/src/cordic/mod.rs create mode 100644 embassy-stm32/src/cordic/sealed.rs create mode 100644 embassy-stm32/src/cordic/utils.rs diff --git a/embassy-stm32/src/cordic.rs b/embassy-stm32/src/cordic.rs deleted file mode 100644 index 952ee187a..000000000 --- a/embassy-stm32/src/cordic.rs +++ /dev/null @@ -1,460 +0,0 @@ -//! CORDIC co-processor - -use crate::peripherals; -use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; - -pub use enums::*; - -mod enums { - /// CORDIC function - #[allow(missing_docs)] - #[derive(Clone, Copy)] - pub enum Function { - Cos = 0, - Sin, - Phase, - Modulus, - Arctan, - Cosh, - Sinh, - Arctanh, - Ln, - Sqrt, - } - - /// CORDIC precision - #[allow(missing_docs)] - #[derive(Clone, Copy)] - pub enum Precision { - Iters4 = 1, - Iters8, - Iters12, - Iters16, - Iters20, - Iters24, - Iters28, - Iters32, - Iters36, - Iters40, - Iters44, - Iters48, - Iters52, - Iters56, - Iters60, - } - - /// CORDIC scale - #[allow(non_camel_case_types)] - #[allow(missing_docs)] - #[derive(Clone, Copy, Default)] - pub enum Scale { - #[default] - A1_R1 = 0, - A1o2_R2, - A1o4_R4, - A1o8_R8, - A1o16_R16, - A1o32_R32, - A1o64_R64, - A1o128_R128, - } - - /// CORDIC argument/result count - #[allow(missing_docs)] - #[derive(Clone, Copy, Default)] - pub enum Count { - #[default] - One, - Two, - } - - /// CORDIC argument/result data width - #[allow(missing_docs)] - #[derive(Clone, Copy)] - pub enum Width { - Bits32, - Bits16, - } - - /// Cordic driver running mode - #[derive(Clone, Copy)] - pub enum Mode { - /// After caculation start, a read to RDATA register will block AHB until the caculation finished - ZeroOverhead, - - /// Use CORDIC interrupt to trigger a read result value - Interrupt, - - /// Use DMA to write/read value - Dma, - } -} - -/// Low-level CORDIC access. -#[cfg(feature = "unstable-pac")] -pub mod low_level { - pub use super::sealed::*; -} - -pub(crate) mod sealed { - use super::*; - use crate::pac::cordic::vals; - - /// Cordic instance - pub trait Instance { - /// Get access to CORDIC registers - fn regs() -> crate::pac::cordic::Cordic; - - /// Set Function value - fn set_func(&self, func: Function) { - Self::regs() - .csr() - .modify(|v| v.set_func(vals::Func::from_bits(func as u8))); - } - - /// Set Precision value - fn set_precision(&self, precision: Precision) { - Self::regs() - .csr() - .modify(|v| v.set_precision(vals::Precision::from_bits(precision as u8))) - } - - /// Set Scale value - fn set_scale(&self, scale: Scale) { - Self::regs() - .csr() - .modify(|v| v.set_scale(vals::Scale::from_bits(scale as u8))) - } - - /// Enable global interrupt - fn enable_irq(&self) { - Self::regs().csr().modify(|v| v.set_ien(true)) - } - - /// Disable global interrupt - fn disable_irq(&self) { - Self::regs().csr().modify(|v| v.set_ien(false)) - } - - /// Enable Read DMA - fn enable_read_dma(&self) { - Self::regs().csr().modify(|v| { - v.set_dmaren(true); - }) - } - - /// Disable Read DMA - fn disable_read_dma(&self) { - Self::regs().csr().modify(|v| { - v.set_dmaren(false); - }) - } - - /// Enable Write DMA - fn enable_write_dma(&self) { - Self::regs().csr().modify(|v| { - v.set_dmawen(true); - }) - } - - /// Disable Write DMA - fn disable_write_dma(&self) { - Self::regs().csr().modify(|v| { - v.set_dmawen(false); - }) - } - - /// Set NARGS value - fn set_argument_count(&self, n: Count) { - Self::regs().csr().modify(|v| { - v.set_nargs(match n { - Count::One => vals::Num::NUM1, - Count::Two => vals::Num::NUM2, - }) - }) - } - - /// Set NRES value - fn set_result_count(&self, n: Count) { - Self::regs().csr().modify(|v| { - v.set_nres(match n { - Count::One => vals::Num::NUM1, - Count::Two => vals::Num::NUM2, - }); - }) - } - - /// Set ARGSIZE and RESSIZE value - fn set_data_width(&self, arg: Width, res: Width) { - Self::regs().csr().modify(|v| { - v.set_argsize(match arg { - Width::Bits32 => vals::Size::BITS32, - Width::Bits16 => vals::Size::BITS16, - }); - v.set_ressize(match res { - Width::Bits32 => vals::Size::BITS32, - Width::Bits16 => vals::Size::BITS16, - }) - }) - } - - /// Read RRDY flag - fn ready_to_read(&self) -> bool { - Self::regs().csr().read().rrdy() - } - - /// Write value to WDATA - fn write_argument(&self, arg: u32) { - Self::regs().wdata().write_value(arg) - } - - /// Read value from RDATA - fn read_result(&self) -> u32 { - Self::regs().rdata().read() - } - } -} - -/// CORDIC driver -pub struct Cordic<'d, T: Instance> { - cordic: PeripheralRef<'d, T>, - config: Config, - //state: State, -} - -/// CORDIC instance trait -pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral {} - -/// CORDIC configuration -pub struct Config { - function: Function, - precision: Precision, - scale: Scale, - mode: Mode, - first_result: bool, -} - -// CORDIC running state -//struct State { -// input_buf: [u32; 8], -// buf_len: usize, -//} - -impl Config { - /// Create a config for Cordic driver - pub fn new(function: Function, precision: Precision, scale: Option, mode: Mode, first_result: bool) -> Self { - Self { - function, - precision, - scale: scale.unwrap_or_default(), - mode, - first_result, - } - } - - fn check_scale(&self) -> bool { - let scale_raw = self.scale as u8; - - match self.function { - Function::Cos | Function::Sin | Function::Phase | Function::Modulus => 0 == scale_raw, - Function::Arctan => (0..=7).contains(&scale_raw), - Function::Cosh | Function::Sinh | Function::Arctanh => 1 == scale_raw, - Function::Ln => (1..=4).contains(&scale_raw), - Function::Sqrt => (0..=2).contains(&scale_raw), - } - } -} - -impl<'d, T: Instance> Cordic<'d, T> { - /// Create a Cordic driver instance - /// - /// Note: - /// If you need a periperhal -> CORDIC -> peripehral mode, - /// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguemnts with [Self::extra_config] - pub fn new(cordic: impl Peripheral

+ 'd, config: Config) -> Self { - T::enable_and_reset(); - - into_ref!(cordic); - - if !config.check_scale() { - panic!("Scale value is not compatible with Function") - } - - let mut instance = Self { - cordic, - config, - // state: State { - // input_buf: [0u32; 8], - // buf_len: 0, - // }, - }; - - instance.reconfigure(); - - instance - } - - /// Set a new config for Cordic driver - pub fn set_config(&mut self, config: Config) { - self.config = config; - self.reconfigure(); - } - - /// Set extra config for data count and data width. - pub fn extra_config(&mut self, arg_cnt: Count, arg_width: Width, res_width: Width) { - let peri = &self.cordic; - peri.set_argument_count(arg_cnt); - peri.set_data_width(arg_width, res_width); - } - - fn reconfigure(&mut self) { - let peri = &self.cordic; - let config = &self.config; - - if peri.ready_to_read() { - warn!("At least 1 result hasn't been read, reconfigure will cause DATA LOST"); - }; - - peri.disable_irq(); - peri.disable_write_dma(); - peri.disable_read_dma(); - - // clean RRDY flag - while peri.ready_to_read() { - peri.read_result(); - } - - peri.set_func(config.function); - peri.set_precision(config.precision); - peri.set_scale(config.scale); - if config.first_result { - peri.set_result_count(Count::One) - } else { - peri.set_result_count(Count::Two) - } - - match config.mode { - Mode::ZeroOverhead => (), - Mode::Interrupt => { - peri.enable_irq(); - } - Mode::Dma => { - peri.enable_write_dma(); - peri.enable_read_dma(); - } - } - - //self.state.input_buf.fill(0u32); - } - - /// Run a CORDIC calculation - pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { - match self.config.mode { - Mode::ZeroOverhead => { - if arg2s.is_none() { - self.cordic.set_argument_count(Count::One); - - self.cordic.set_result_count(if self.config.first_result { - if output.len() < arg1s.len() { - panic!("Output buf length is not long enough") - } - Count::One - } else { - if output.len() < 2 * arg1s.len() { - panic!("Output buf length is not long enough") - } - Count::Two - }); - - let mut cnt = 0; - - for &arg in arg1s.iter() { - self.cordic.write_argument(f64_to_q1_31(arg)); - output[cnt] = q1_31_to_f64(self.cordic.read_result()); - cnt += 1; - } - - cnt - } else { - todo!() - } - } - Mode::Interrupt => todo!(), - Mode::Dma => todo!(), - } - } -} - -impl<'d, T: Instance> Drop for Cordic<'d, T> { - fn drop(&mut self) { - T::disable(); - } -} - -foreach_interrupt!( - ($inst:ident, cordic, CORDIC, GLOBAL, $irq:ident) => { - impl Instance for peripherals::$inst { - } - - impl sealed::Instance for peripherals::$inst { - fn regs() -> crate::pac::cordic::Cordic { - crate::pac::$inst - } - } - }; -); - -macro_rules! floating_fixed_convert { - ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => { - /// convert float point to fixed point format - pub fn $f_to_q(value: $float_ty) -> $unsigned_bin_typ { - const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) }; - - assert!( - (-1.0 as $float_ty) <= value, - "input value {} should be equal or greater than -1", - value - ); - - let value = if value == 1.0 as $float_ty{ - (1.0 as $float_ty) - MIN_POSITIVE - } else { - assert!( - value <= (1.0 as $float_ty) - MIN_POSITIVE, - "input value {} should be equal or less than 1-2^(-{})", - value, $offset - ); - value - }; - - (value * ((1 as $unsigned_bin_typ << $offset) as $float_ty)) as $unsigned_bin_typ - } - - #[inline(always)] - /// convert fixed point to float point format - pub fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty { - // It's needed to convert from unsigned to signed first, for correct result. - -(value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty) - } - }; -} - -floating_fixed_convert!( - f64_to_q1_31, - q1_31_to_f64, - u32, - i32, - f64, - 31, - 0x3E00_0000_0000_0000u64 // binary form of 1f64^(-31) -); - -floating_fixed_convert!( - f32_to_q1_15, - q1_15_to_f32, - u16, - i16, - f32, - 15, - 0x3800_0000u32 // binary form of 1f32^(-15) -); diff --git a/embassy-stm32/src/cordic/enums.rs b/embassy-stm32/src/cordic/enums.rs new file mode 100644 index 000000000..4697a1df1 --- /dev/null +++ b/embassy-stm32/src/cordic/enums.rs @@ -0,0 +1,82 @@ +/// CORDIC function +#[allow(missing_docs)] +#[derive(Clone, Copy)] +pub enum Function { + Cos = 0, + Sin, + Phase, + Modulus, + Arctan, + Cosh, + Sinh, + Arctanh, + Ln, + Sqrt, +} + +/// CORDIC precision +#[allow(missing_docs)] +#[derive(Clone, Copy)] +pub enum Precision { + Iters4 = 1, + Iters8, + Iters12, + Iters16, + Iters20, + Iters24, + Iters28, + Iters32, + Iters36, + Iters40, + Iters44, + Iters48, + Iters52, + Iters56, + Iters60, +} + +/// CORDIC scale +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[derive(Clone, Copy, Default)] +pub enum Scale { + #[default] + A1_R1 = 0, + A1o2_R2, + A1o4_R4, + A1o8_R8, + A1o16_R16, + A1o32_R32, + A1o64_R64, + A1o128_R128, +} + +/// CORDIC argument/result count +#[allow(missing_docs)] +#[derive(Clone, Copy, Default)] +pub enum Count { + #[default] + One, + Two, +} + +/// CORDIC argument/result data width +#[allow(missing_docs)] +#[derive(Clone, Copy)] +pub enum Width { + Bits32, + Bits16, +} + +/// Cordic driver running mode +#[derive(Clone, Copy)] +pub enum Mode { + /// After caculation start, a read to RDATA register will block AHB until the caculation finished + ZeroOverhead, + + /// Use CORDIC interrupt to trigger a read result value + Interrupt, + + /// Use DMA to write/read value + Dma, +} diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs new file mode 100644 index 000000000..c0a69b757 --- /dev/null +++ b/embassy-stm32/src/cordic/mod.rs @@ -0,0 +1,206 @@ +//! CORDIC co-processor + +use crate::peripherals; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; + +mod enums; +pub use enums::*; + +pub mod utils; + +pub(crate) mod sealed; + +/// Low-level CORDIC access. +#[cfg(feature = "unstable-pac")] +pub mod low_level { + pub use super::sealed::*; +} + +/// CORDIC driver +pub struct Cordic<'d, T: Instance> { + cordic: PeripheralRef<'d, T>, + config: Config, + //state: State, +} + +/// CORDIC instance trait +pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral {} + +/// CORDIC configuration +pub struct Config { + function: Function, + precision: Precision, + scale: Scale, + mode: Mode, + first_result: bool, +} + +// CORDIC running state +//struct State { +// input_buf: [u32; 8], +// buf_len: usize, +//} + +impl Config { + /// Create a config for Cordic driver + pub fn new(function: Function, precision: Precision, scale: Option, mode: Mode, first_result: bool) -> Self { + Self { + function, + precision, + scale: scale.unwrap_or_default(), + mode, + first_result, + } + } + + fn check_scale(&self) -> bool { + let scale_raw = self.scale as u8; + + match self.function { + Function::Cos | Function::Sin | Function::Phase | Function::Modulus => 0 == scale_raw, + Function::Arctan => (0..=7).contains(&scale_raw), + Function::Cosh | Function::Sinh | Function::Arctanh => 1 == scale_raw, + Function::Ln => (1..=4).contains(&scale_raw), + Function::Sqrt => (0..=2).contains(&scale_raw), + } + } +} + +impl<'d, T: Instance> Cordic<'d, T> { + /// Create a Cordic driver instance + /// + /// Note: + /// If you need a periperhal -> CORDIC -> peripehral mode, + /// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguemnts with [Self::extra_config] + pub fn new(cordic: impl Peripheral

+ 'd, config: Config) -> Self { + T::enable_and_reset(); + + into_ref!(cordic); + + if !config.check_scale() { + panic!("Scale value is not compatible with Function") + } + + let mut instance = Self { + cordic, + config, + // state: State { + // input_buf: [0u32; 8], + // buf_len: 0, + // }, + }; + + instance.reconfigure(); + + instance + } + + /// Set a new config for Cordic driver + pub fn set_config(&mut self, config: Config) { + self.config = config; + self.reconfigure(); + } + + /// Set extra config for data count and data width. + pub fn extra_config(&mut self, arg_cnt: Count, arg_width: Width, res_width: Width) { + let peri = &self.cordic; + peri.set_argument_count(arg_cnt); + peri.set_data_width(arg_width, res_width); + } + + fn reconfigure(&mut self) { + let peri = &self.cordic; + let config = &self.config; + + if peri.ready_to_read() { + warn!("At least 1 result hasn't been read, reconfigure will cause DATA LOST"); + }; + + peri.disable_irq(); + peri.disable_write_dma(); + peri.disable_read_dma(); + + // clean RRDY flag + while peri.ready_to_read() { + peri.read_result(); + } + + peri.set_func(config.function); + peri.set_precision(config.precision); + peri.set_scale(config.scale); + if config.first_result { + peri.set_result_count(Count::One) + } else { + peri.set_result_count(Count::Two) + } + + match config.mode { + Mode::ZeroOverhead => (), + Mode::Interrupt => { + peri.enable_irq(); + } + Mode::Dma => { + peri.enable_write_dma(); + peri.enable_read_dma(); + } + } + + //self.state.input_buf.fill(0u32); + } + + /// Run a CORDIC calculation + pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { + match self.config.mode { + Mode::ZeroOverhead => { + if arg2s.is_none() { + self.cordic.set_argument_count(Count::One); + + self.cordic.set_result_count(if self.config.first_result { + if output.len() < arg1s.len() { + panic!("Output buf length is not long enough") + } + Count::One + } else { + if output.len() < 2 * arg1s.len() { + panic!("Output buf length is not long enough") + } + Count::Two + }); + + let mut cnt = 0; + + for &arg in arg1s.iter() { + self.cordic.write_argument(utils::f64_to_q1_31(arg)); + output[cnt] = utils::q1_31_to_f64(self.cordic.read_result()); + cnt += 1; + } + + cnt + } else { + todo!() + } + } + Mode::Interrupt => todo!(), + Mode::Dma => todo!(), + } + } +} + +impl<'d, T: Instance> Drop for Cordic<'d, T> { + fn drop(&mut self) { + T::disable(); + } +} + +foreach_interrupt!( + ($inst:ident, cordic, CORDIC, GLOBAL, $irq:ident) => { + impl Instance for peripherals::$inst { + } + + impl sealed::Instance for peripherals::$inst { + fn regs() -> crate::pac::cordic::Cordic { + crate::pac::$inst + } + } + }; +); diff --git a/embassy-stm32/src/cordic/sealed.rs b/embassy-stm32/src/cordic/sealed.rs new file mode 100644 index 000000000..0f00e380c --- /dev/null +++ b/embassy-stm32/src/cordic/sealed.rs @@ -0,0 +1,116 @@ +use super::*; +use crate::pac::cordic::vals; + +/// Cordic instance +pub trait Instance { + /// Get access to CORDIC registers + fn regs() -> crate::pac::cordic::Cordic; + + /// Set Function value + fn set_func(&self, func: Function) { + Self::regs() + .csr() + .modify(|v| v.set_func(vals::Func::from_bits(func as u8))); + } + + /// Set Precision value + fn set_precision(&self, precision: Precision) { + Self::regs() + .csr() + .modify(|v| v.set_precision(vals::Precision::from_bits(precision as u8))) + } + + /// Set Scale value + fn set_scale(&self, scale: Scale) { + Self::regs() + .csr() + .modify(|v| v.set_scale(vals::Scale::from_bits(scale as u8))) + } + + /// Enable global interrupt + fn enable_irq(&self) { + Self::regs().csr().modify(|v| v.set_ien(true)) + } + + /// Disable global interrupt + fn disable_irq(&self) { + Self::regs().csr().modify(|v| v.set_ien(false)) + } + + /// Enable Read DMA + fn enable_read_dma(&self) { + Self::regs().csr().modify(|v| { + v.set_dmaren(true); + }) + } + + /// Disable Read DMA + fn disable_read_dma(&self) { + Self::regs().csr().modify(|v| { + v.set_dmaren(false); + }) + } + + /// Enable Write DMA + fn enable_write_dma(&self) { + Self::regs().csr().modify(|v| { + v.set_dmawen(true); + }) + } + + /// Disable Write DMA + fn disable_write_dma(&self) { + Self::regs().csr().modify(|v| { + v.set_dmawen(false); + }) + } + + /// Set NARGS value + fn set_argument_count(&self, n: Count) { + Self::regs().csr().modify(|v| { + v.set_nargs(match n { + Count::One => vals::Num::NUM1, + Count::Two => vals::Num::NUM2, + }) + }) + } + + /// Set NRES value + fn set_result_count(&self, n: Count) { + Self::regs().csr().modify(|v| { + v.set_nres(match n { + Count::One => vals::Num::NUM1, + Count::Two => vals::Num::NUM2, + }); + }) + } + + /// Set ARGSIZE and RESSIZE value + fn set_data_width(&self, arg: Width, res: Width) { + Self::regs().csr().modify(|v| { + v.set_argsize(match arg { + Width::Bits32 => vals::Size::BITS32, + Width::Bits16 => vals::Size::BITS16, + }); + v.set_ressize(match res { + Width::Bits32 => vals::Size::BITS32, + Width::Bits16 => vals::Size::BITS16, + }) + }) + } + + /// Read RRDY flag + fn ready_to_read(&self) -> bool { + Self::regs().csr().read().rrdy() + } + + /// Write value to WDATA + fn write_argument(&self, arg: u32) { + Self::regs().wdata().write_value(arg) + } + + /// Read value from RDATA + fn read_result(&self) -> u32 { + Self::regs().rdata().read() + } +} diff --git a/embassy-stm32/src/cordic/utils.rs b/embassy-stm32/src/cordic/utils.rs new file mode 100644 index 000000000..3f055c34b --- /dev/null +++ b/embassy-stm32/src/cordic/utils.rs @@ -0,0 +1,59 @@ +//! Common match utils + +macro_rules! floating_fixed_convert { + ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => { + /// convert float point to fixed point format + pub fn $f_to_q(value: $float_ty) -> $unsigned_bin_typ { + const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) }; + + assert!( + (-1.0 as $float_ty) <= value, + "input value {} should be equal or greater than -1", + value + ); + + + let value = if value == 1.0 as $float_ty{ + // make a exception for user specifing exact 1.0 float point, + // convert 1.0 to max representable value of q1.x format + (1.0 as $float_ty) - MIN_POSITIVE + } else { + assert!( + value <= (1.0 as $float_ty) - MIN_POSITIVE, + "input value {} should be equal or less than 1-2^(-{})", + value, $offset + ); + value + }; + + (value * ((1 as $unsigned_bin_typ << $offset) as $float_ty)) as $unsigned_bin_typ + } + + #[inline(always)] + /// convert fixed point to float point format + pub fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty { + // It's needed to convert from unsigned to signed first, for correct result. + -(value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty) + } + }; +} + +floating_fixed_convert!( + f64_to_q1_31, + q1_31_to_f64, + u32, + i32, + f64, + 31, + 0x3E00_0000_0000_0000u64 // binary form of 1f64^(-31) +); + +floating_fixed_convert!( + f32_to_q1_15, + q1_15_to_f32, + u16, + i16, + f32, + 15, + 0x3800_0000u32 // binary form of 1f32^(-15) +); From a1ca9088b4e3b4644428eab80e8502a55b2cbe8f Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Fri, 15 Mar 2024 19:34:55 +0800 Subject: [PATCH 12/69] stm32 CORDIC: ZeroOverhead q1.31 mode --- embassy-stm32/src/cordic/enums.rs | 7 +- embassy-stm32/src/cordic/mod.rs | 290 ++++++++++++++++++++++++------ 2 files changed, 241 insertions(+), 56 deletions(-) diff --git a/embassy-stm32/src/cordic/enums.rs b/embassy-stm32/src/cordic/enums.rs index 4697a1df1..3e1c47f7f 100644 --- a/embassy-stm32/src/cordic/enums.rs +++ b/embassy-stm32/src/cordic/enums.rs @@ -16,14 +16,15 @@ pub enum Function { /// CORDIC precision #[allow(missing_docs)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Default)] pub enum Precision { Iters4 = 1, Iters8, Iters12, Iters16, Iters20, - Iters24, + #[default] + Iters24, // this value is recomended by Reference Manual Iters28, Iters32, Iters36, @@ -38,7 +39,7 @@ pub enum Precision { /// CORDIC scale #[allow(non_camel_case_types)] #[allow(missing_docs)] -#[derive(Clone, Copy, Default)] +#[derive(Clone, Copy, Default, PartialEq)] pub enum Scale { #[default] A1_R1 = 0, diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index c0a69b757..b15521ca6 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs @@ -10,6 +10,10 @@ pub mod utils; pub(crate) mod sealed; +// length of pre-allocated [u32] memory for CORDIC input, +// length should be multiple of 2 +const INPUT_BUF_LEN: usize = 8; + /// Low-level CORDIC access. #[cfg(feature = "unstable-pac")] pub mod low_level { @@ -20,7 +24,7 @@ pub mod low_level { pub struct Cordic<'d, T: Instance> { cordic: PeripheralRef<'d, T>, config: Config, - //state: State, + state: State, } /// CORDIC instance trait @@ -28,27 +32,33 @@ pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPer /// CORDIC configuration pub struct Config { + mode: Mode, function: Function, precision: Precision, scale: Scale, - mode: Mode, first_result: bool, } // CORDIC running state -//struct State { -// input_buf: [u32; 8], -// buf_len: usize, -//} +struct State { + input_buf: [u32; INPUT_BUF_LEN], + buf_index: usize, +} impl Config { /// Create a config for Cordic driver - pub fn new(function: Function, precision: Precision, scale: Option, mode: Mode, first_result: bool) -> Self { + pub fn new( + mode: Mode, + function: Function, + precision: Option, + scale: Option, + first_result: bool, + ) -> Self { Self { - function, - precision, - scale: scale.unwrap_or_default(), mode, + function, + precision: precision.unwrap_or_default(), + scale: scale.unwrap_or_default(), first_result, } } @@ -66,6 +76,7 @@ impl Config { } } +// common method impl<'d, T: Instance> Cordic<'d, T> { /// Create a Cordic driver instance /// @@ -84,10 +95,10 @@ impl<'d, T: Instance> Cordic<'d, T> { let mut instance = Self { cordic, config, - // state: State { - // input_buf: [0u32; 8], - // buf_len: 0, - // }, + state: State { + input_buf: [0u32; 8], + buf_index: 0, + }, }; instance.reconfigure(); @@ -128,6 +139,7 @@ impl<'d, T: Instance> Cordic<'d, T> { peri.set_func(config.function); peri.set_precision(config.precision); peri.set_scale(config.scale); + if config.first_result { peri.set_result_count(Count::One) } else { @@ -145,44 +157,8 @@ impl<'d, T: Instance> Cordic<'d, T> { } } - //self.state.input_buf.fill(0u32); - } - - /// Run a CORDIC calculation - pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { - match self.config.mode { - Mode::ZeroOverhead => { - if arg2s.is_none() { - self.cordic.set_argument_count(Count::One); - - self.cordic.set_result_count(if self.config.first_result { - if output.len() < arg1s.len() { - panic!("Output buf length is not long enough") - } - Count::One - } else { - if output.len() < 2 * arg1s.len() { - panic!("Output buf length is not long enough") - } - Count::Two - }); - - let mut cnt = 0; - - for &arg in arg1s.iter() { - self.cordic.write_argument(utils::f64_to_q1_31(arg)); - output[cnt] = utils::q1_31_to_f64(self.cordic.read_result()); - cnt += 1; - } - - cnt - } else { - todo!() - } - } - Mode::Interrupt => todo!(), - Mode::Dma => todo!(), - } + self.state.input_buf.fill(0u32); + self.state.buf_index = 0; } } @@ -192,8 +168,216 @@ impl<'d, T: Instance> Drop for Cordic<'d, T> { } } +// q1.31 related +impl<'d, T: Instance> Cordic<'d, T> { + /// Run a CORDIC calculation + pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { + let peri = &self.cordic; + let config = &self.config; + + assert!( + match config.first_result { + true => output.len() >= arg1s.len(), + false => output.len() >= 2 * arg1s.len(), + }, + "Output buf length is not long enough" + ); + + self.check_input_f64(arg1s, arg2s); + + peri.set_result_count(if config.first_result { Count::One } else { Count::Two }); + peri.set_data_width(Width::Bits32, Width::Bits32); + + let state = &mut self.state; + + let mut output_count = 0; + + let mut consumed_input_len = 0; + + match config.mode { + Mode::ZeroOverhead => { + // put double input into cordic + if arg2s.is_some() && !arg2s.unwrap().is_empty() { + let arg2s = arg2s.unwrap(); + + peri.set_argument_count(Count::Two); + + let double_value = arg1s.iter().zip(arg2s); + consumed_input_len = double_value.len(); + + for (arg1, arg2) in double_value { + // if input_buf is full, send values to cordic + if state.buf_index == INPUT_BUF_LEN - 1 { + for arg in state.input_buf.chunks(2) { + peri.write_argument(arg[0]); + peri.write_argument(arg[1]); + + output[output_count] = utils::q1_31_to_f64(peri.read_result()); + output_count += 1; + + if !config.first_result { + output[output_count] = utils::q1_31_to_f64(peri.read_result()); + output_count += 1; + } + } + + state.buf_index = 0; + } + + for &&arg in [arg1, arg2].iter() { + state.input_buf[state.buf_index] = utils::f64_to_q1_31(arg); + state.buf_index += 1; + } + } + + // put left paired args into cordic + if state.buf_index > 0 { + for arg in state.input_buf[..state.buf_index].chunks(2) { + peri.write_argument(arg[0]); + peri.write_argument(arg[1]); + + output[output_count] = utils::q1_31_to_f64(peri.read_result()); + output_count += 1; + + if !config.first_result { + output[output_count] = utils::q1_31_to_f64(peri.read_result()); + output_count += 1; + } + } + + state.buf_index = 0; + } + } + + // put single input into cordic + let input_left = &arg1s[consumed_input_len..]; + + if !input_left.is_empty() { + peri.set_argument_count(Count::One); + + for &arg in input_left.iter() { + peri.write_argument(utils::f64_to_q1_31(arg)); + + output[output_count] = utils::q1_31_to_f64(peri.read_result()); + output_count += 1; + + if !config.first_result { + output[output_count] = utils::q1_31_to_f64(peri.read_result()); + output_count += 1; + } + } + } + + output_count + } + Mode::Interrupt => todo!(), + Mode::Dma => todo!(), + } + } + + fn check_input_f64(&self, arg1s: &[f64], arg2s: Option<&[f64]>) { + let config = &self.config; + + use Function::*; + + // check SCALE value + match config.function { + Cos | Sin | Phase | Modulus => assert!(Scale::A1_R1 == config.scale, "SCALE should be 0"), + Arctan => assert!( + (0..=7).contains(&(config.scale as u8)), + "SCALE should be: 0 <= SCALE <= 7" + ), + Cosh | Sinh | Arctanh => assert!(Scale::A1o2_R2 == config.scale, "SCALE should be 1"), + + Ln => assert!( + (1..=4).contains(&(config.scale as u8)), + "SCALE should be: 1 <= SCALE <= 4" + ), + Sqrt => assert!( + (0..=2).contains(&(config.scale as u8)), + "SCALE should be: 0 <= SCALE <= 2" + ), + } + + // check ARG1 value + match config.function { + Cos | Sin | Phase | Modulus | Arctan => { + assert!( + arg1s.iter().all(|v| (-1.0..=1.0).contains(v)), + "ARG1 should be: -1 <= ARG1 <= 1" + ); + } + + Cosh | Sinh => assert!( + arg1s.iter().all(|v| (-0.559..=0.559).contains(v)), + "ARG1 should be: -0.559 <= ARG1 <= 0.559" + ), + + Arctanh => assert!( + arg1s.iter().all(|v| (-0.403..=0.403).contains(v)), + "ARG1 should be: -0.403 <= ARG1 <= 0.403" + ), + + Ln => { + match config.scale { + Scale::A1o2_R2 => assert!( + arg1s.iter().all(|v| (0.05354..0.5).contains(v)), + "When SCALE set to 1, ARG1 should be: 0.05354 <= ARG1 < 0.5" + ), + Scale::A1o4_R4 => assert!( + arg1s.iter().all(|v| (0.25..0.75).contains(v)), + "When SCALE set to 2, ARG1 should be: 0.25 <= ARG1 < 0.75" + ), + Scale::A1o8_R8 => assert!( + arg1s.iter().all(|v| (0.375..0.875).contains(v)), + "When SCALE set to 3, ARG1 should be: 0.375 <= ARG1 < 0.875" + ), + Scale::A1o16_R16 => assert!( + arg1s.iter().all(|v| (0.4375f64..0.584f64).contains(v)), + "When SCALE set to 4, ARG1 should be: 0.4375 <= ARG1 < 0.584" + ), + _ => unreachable!(), + }; + } + + Function::Sqrt => match config.scale { + Scale::A1_R1 => assert!( + arg1s.iter().all(|v| (0.027..0.75).contains(v)), + "When SCALE set to 0, ARG1 should be: 0.027 <= ARG1 < 0.75" + ), + Scale::A1o2_R2 => assert!( + arg1s.iter().all(|v| (0.375..0.875).contains(v)), + "When SCALE set to 1, ARG1 should be: 0.375 <= ARG1 < 0.875" + ), + Scale::A1o4_R4 => assert!( + arg1s.iter().all(|v| (0.4375..0.585).contains(v)), + "When SCALE set to 2, ARG1 should be: 0.4375 <= ARG1 < 0.585" + ), + _ => unreachable!(), + }, + } + + // check ARG2 value + if let Some(arg2s) = arg2s { + match config.function { + Cos | Sin => assert!( + arg2s.iter().all(|v| (0.0..=1.0).contains(v)), + "ARG2 should be: 0 <= ARG2 <= 1" + ), + + Phase | Modulus => assert!( + arg2s.iter().all(|v| (-1.0..=1.0).contains(v)), + "ARG2 should be: -1 <= ARG2 <= 1" + ), + + _ => (), + } + } + } +} + foreach_interrupt!( - ($inst:ident, cordic, CORDIC, GLOBAL, $irq:ident) => { + ($inst:ident, cordic, $block:ident, GLOBAL, $irq:ident) => { impl Instance for peripherals::$inst { } From 5d12f594303bdb76bf2356d9fc0661826e2e658e Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Sat, 16 Mar 2024 00:25:38 +0800 Subject: [PATCH 13/69] stm32 CORDIC: make use of "preload" feature --- embassy-stm32/src/cordic/mod.rs | 180 +++++++++++++++----------------- 1 file changed, 85 insertions(+), 95 deletions(-) diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index b15521ca6..997ace113 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs @@ -22,9 +22,8 @@ pub mod low_level { /// CORDIC driver pub struct Cordic<'d, T: Instance> { - cordic: PeripheralRef<'d, T>, + peri: PeripheralRef<'d, T>, config: Config, - state: State, } /// CORDIC instance trait @@ -83,23 +82,16 @@ impl<'d, T: Instance> Cordic<'d, T> { /// Note: /// If you need a periperhal -> CORDIC -> peripehral mode, /// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguemnts with [Self::extra_config] - pub fn new(cordic: impl Peripheral

+ 'd, config: Config) -> Self { + pub fn new(peri: impl Peripheral

+ 'd, config: Config) -> Self { T::enable_and_reset(); - into_ref!(cordic); + into_ref!(peri); if !config.check_scale() { panic!("Scale value is not compatible with Function") } - let mut instance = Self { - cordic, - config, - state: State { - input_buf: [0u32; 8], - buf_index: 0, - }, - }; + let mut instance = Self { peri, config }; instance.reconfigure(); @@ -114,51 +106,71 @@ impl<'d, T: Instance> Cordic<'d, T> { /// Set extra config for data count and data width. pub fn extra_config(&mut self, arg_cnt: Count, arg_width: Width, res_width: Width) { - let peri = &self.cordic; - peri.set_argument_count(arg_cnt); - peri.set_data_width(arg_width, res_width); + self.peri.set_argument_count(arg_cnt); + self.peri.set_data_width(arg_width, res_width); } fn reconfigure(&mut self) { - let peri = &self.cordic; - let config = &self.config; - - if peri.ready_to_read() { + if self.peri.ready_to_read() { warn!("At least 1 result hasn't been read, reconfigure will cause DATA LOST"); }; - peri.disable_irq(); - peri.disable_write_dma(); - peri.disable_read_dma(); + self.peri.disable_irq(); + self.peri.disable_write_dma(); + self.peri.disable_read_dma(); // clean RRDY flag - while peri.ready_to_read() { - peri.read_result(); + while self.peri.ready_to_read() { + self.peri.read_result(); } - peri.set_func(config.function); - peri.set_precision(config.precision); - peri.set_scale(config.scale); + self.peri.set_func(self.config.function); + self.peri.set_precision(self.config.precision); + self.peri.set_scale(self.config.scale); - if config.first_result { - peri.set_result_count(Count::One) + if self.config.first_result { + self.peri.set_result_count(Count::One) } else { - peri.set_result_count(Count::Two) + self.peri.set_result_count(Count::Two) } - match config.mode { + match self.config.mode { Mode::ZeroOverhead => (), Mode::Interrupt => { - peri.enable_irq(); + self.peri.enable_irq(); } Mode::Dma => { - peri.enable_write_dma(); - peri.enable_read_dma(); + self.peri.enable_write_dma(); + self.peri.enable_read_dma(); } } + } - self.state.input_buf.fill(0u32); - self.state.buf_index = 0; + fn blocking_read_f64(&mut self) -> (f64, Option) { + let res1 = utils::q1_31_to_f64(self.peri.read_result()); + + let res2 = if !self.config.first_result { + Some(utils::q1_31_to_f64(self.peri.read_result())) + } else { + None + }; + + (res1, res2) + } + + fn blocking_read_f64_to_buf(&mut self, result_buf: &mut [f64], result_index: &mut usize) { + let (res1, res2) = self.blocking_read_f64(); + result_buf[*result_index] = res1; + *result_index += 1; + + if let Some(res2) = res2 { + result_buf[*result_index] = res2; + *result_index += 1; + } + } + + fn blocking_write_f64(&mut self, arg: f64) { + self.peri.write_argument(utils::f64_to_q1_31(arg)); } } @@ -172,11 +184,8 @@ impl<'d, T: Instance> Drop for Cordic<'d, T> { impl<'d, T: Instance> Cordic<'d, T> { /// Run a CORDIC calculation pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { - let peri = &self.cordic; - let config = &self.config; - assert!( - match config.first_result { + match self.config.first_result { true => output.len() >= arg1s.len(), false => output.len() >= 2 * arg1s.len(), }, @@ -185,87 +194,68 @@ impl<'d, T: Instance> Cordic<'d, T> { self.check_input_f64(arg1s, arg2s); - peri.set_result_count(if config.first_result { Count::One } else { Count::Two }); - peri.set_data_width(Width::Bits32, Width::Bits32); + self.peri.set_result_count(if self.config.first_result { + Count::One + } else { + Count::Two + }); - let state = &mut self.state; + self.peri.set_data_width(Width::Bits32, Width::Bits32); let mut output_count = 0; let mut consumed_input_len = 0; - match config.mode { + match self.config.mode { Mode::ZeroOverhead => { // put double input into cordic if arg2s.is_some() && !arg2s.unwrap().is_empty() { let arg2s = arg2s.unwrap(); - peri.set_argument_count(Count::Two); + self.peri.set_argument_count(Count::Two); - let double_value = arg1s.iter().zip(arg2s); - consumed_input_len = double_value.len(); + // Skip 1st value from arg1s, this value will be manually "preload" to cordic, to make use of cordic preload function. + // And we preserve last value from arg2s, since it need to manually write to cordic, and read the result out. + let double_input = arg1s.iter().skip(1).zip(&arg2s[..arg2s.len() - 1]); + // Since we preload 1st value from arg1s, the consumed input length is double_input length + 1. + consumed_input_len = double_input.len() + 1; - for (arg1, arg2) in double_value { - // if input_buf is full, send values to cordic - if state.buf_index == INPUT_BUF_LEN - 1 { - for arg in state.input_buf.chunks(2) { - peri.write_argument(arg[0]); - peri.write_argument(arg[1]); + // preload first value from arg1 to cordic + self.blocking_write_f64(arg1s[0]); - output[output_count] = utils::q1_31_to_f64(peri.read_result()); - output_count += 1; + for (&arg1, &arg2) in double_input { + // Since we manually preload a value before, + // we will write arg2 (from the actual last pair) first, (at this moment, cordic start to calculating,) + // and write arg1 (from the actual next pair), then read the result, to "keep preloading" - if !config.first_result { - output[output_count] = utils::q1_31_to_f64(peri.read_result()); - output_count += 1; - } - } - - state.buf_index = 0; - } - - for &&arg in [arg1, arg2].iter() { - state.input_buf[state.buf_index] = utils::f64_to_q1_31(arg); - state.buf_index += 1; - } + self.blocking_write_f64(arg2); + self.blocking_write_f64(arg1); + self.blocking_read_f64_to_buf(output, &mut output_count); } - // put left paired args into cordic - if state.buf_index > 0 { - for arg in state.input_buf[..state.buf_index].chunks(2) { - peri.write_argument(arg[0]); - peri.write_argument(arg[1]); - - output[output_count] = utils::q1_31_to_f64(peri.read_result()); - output_count += 1; - - if !config.first_result { - output[output_count] = utils::q1_31_to_f64(peri.read_result()); - output_count += 1; - } - } - - state.buf_index = 0; - } + // write last input value from arg2s, then read out the result + self.blocking_write_f64(arg2s[arg2s.len() - 1]); + self.blocking_read_f64_to_buf(output, &mut output_count); } // put single input into cordic let input_left = &arg1s[consumed_input_len..]; if !input_left.is_empty() { - peri.set_argument_count(Count::One); + self.peri.set_argument_count(Count::One); - for &arg in input_left.iter() { - peri.write_argument(utils::f64_to_q1_31(arg)); + // "preload" value to cordic (at this moment, cordic start to calculating) + self.blocking_write_f64(input_left[0]); - output[output_count] = utils::q1_31_to_f64(peri.read_result()); - output_count += 1; - - if !config.first_result { - output[output_count] = utils::q1_31_to_f64(peri.read_result()); - output_count += 1; - } + for &arg in input_left.iter().skip(1) { + // this line write arg for next round caculation to cordic, + // and read result from last round + self.blocking_write_f64(arg); + self.blocking_read_f64_to_buf(output, &mut output_count); } + + // read the last output + self.blocking_read_f64_to_buf(output, &mut output_count); } output_count From c9f759bb21782eb0487c96a59500310d1283694c Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Sat, 16 Mar 2024 21:20:17 +0800 Subject: [PATCH 14/69] stm32 CORDIC: ZeroOverhead for q1.31 and q1.15 --- embassy-stm32/src/cordic/enums.rs | 13 - embassy-stm32/src/cordic/mod.rs | 468 ++++++++++++++++++------------ embassy-stm32/src/cordic/utils.rs | 4 +- 3 files changed, 278 insertions(+), 207 deletions(-) diff --git a/embassy-stm32/src/cordic/enums.rs b/embassy-stm32/src/cordic/enums.rs index 3e1c47f7f..37c73f549 100644 --- a/embassy-stm32/src/cordic/enums.rs +++ b/embassy-stm32/src/cordic/enums.rs @@ -68,16 +68,3 @@ pub enum Width { Bits32, Bits16, } - -/// Cordic driver running mode -#[derive(Clone, Copy)] -pub enum Mode { - /// After caculation start, a read to RDATA register will block AHB until the caculation finished - ZeroOverhead, - - /// Use CORDIC interrupt to trigger a read result value - Interrupt, - - /// Use DMA to write/read value - Dma, -} diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index 997ace113..61277d7e1 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs @@ -1,8 +1,9 @@ //! CORDIC co-processor -use crate::peripherals; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; +use crate::peripherals; + mod enums; pub use enums::*; @@ -10,10 +11,6 @@ pub mod utils; pub(crate) mod sealed; -// length of pre-allocated [u32] memory for CORDIC input, -// length should be multiple of 2 -const INPUT_BUF_LEN: usize = 8; - /// Low-level CORDIC access. #[cfg(feature = "unstable-pac")] pub mod low_level { @@ -31,30 +28,16 @@ pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPer /// CORDIC configuration pub struct Config { - mode: Mode, function: Function, precision: Precision, scale: Scale, first_result: bool, } -// CORDIC running state -struct State { - input_buf: [u32; INPUT_BUF_LEN], - buf_index: usize, -} - impl Config { /// Create a config for Cordic driver - pub fn new( - mode: Mode, - function: Function, - precision: Option, - scale: Option, - first_result: bool, - ) -> Self { + pub fn new(function: Function, precision: Option, scale: Option, first_result: bool) -> Self { Self { - mode, function, precision: precision.unwrap_or_default(), scale: scale.unwrap_or_default(), @@ -133,22 +116,123 @@ impl<'d, T: Instance> Cordic<'d, T> { } else { self.peri.set_result_count(Count::Two) } + } - match self.config.mode { - Mode::ZeroOverhead => (), - Mode::Interrupt => { - self.peri.enable_irq(); - } - Mode::Dma => { - self.peri.enable_write_dma(); - self.peri.enable_read_dma(); - } + fn blocking_read_f32(&mut self) -> (f32, Option) { + let reg_value = self.peri.read_result(); + + let res1 = utils::q1_15_to_f32((reg_value & ((1u32 << 16) - 1)) as u16); + + // We don't care about whether the function return 1 or 2 results, + // the only thing matter is whether user want 1 or 2 results. + let res2 = if !self.config.first_result { + Some(utils::q1_15_to_f32((reg_value >> 16) as u16)) + } else { + None + }; + + (res1, res2) + } +} + +impl<'d, T: Instance> Drop for Cordic<'d, T> { + fn drop(&mut self) { + T::disable(); + } +} + +// q1.31 related +impl<'d, T: Instance> Cordic<'d, T> { + /// Run a CORDIC calculation + pub fn blocking_calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { + if arg1s.is_empty() { + return 0; } + + assert!( + match self.config.first_result { + true => output.len() >= arg1s.len(), + false => output.len() >= 2 * arg1s.len(), + }, + "Output buf length is not long enough" + ); + + self.check_input_f64(arg1s, arg2s); + + self.peri.disable_irq(); + self.peri.disable_write_dma(); + self.peri.disable_read_dma(); + + self.peri.set_result_count(if self.config.first_result { + Count::One + } else { + Count::Two + }); + + self.peri.set_data_width(Width::Bits32, Width::Bits32); + + let mut output_count = 0; + + let mut consumed_input_len = 0; + + // put double input into cordic + if arg2s.is_some() && !arg2s.expect("It's infailable").is_empty() { + let arg2s = arg2s.expect("It's infailable"); + + self.peri.set_argument_count(Count::Two); + + // Skip 1st value from arg1s, this value will be manually "preload" to cordic, to make use of cordic preload function. + // And we preserve last value from arg2s, since it need to manually write to cordic, and read the result out. + let double_input = arg1s.iter().skip(1).zip(&arg2s[..arg2s.len() - 1]); + // Since we preload 1st value from arg1s, the consumed input length is double_input length + 1. + consumed_input_len = double_input.len() + 1; + + // preload first value from arg1 to cordic + self.blocking_write_f64(arg1s[0]); + + for (&arg1, &arg2) in double_input { + // Since we manually preload a value before, + // we will write arg2 (from the actual last pair) first, (at this moment, cordic start to calculating,) + // and write arg1 (from the actual next pair), then read the result, to "keep preloading" + + self.blocking_write_f64(arg2); + self.blocking_write_f64(arg1); + self.blocking_read_f64_to_buf(output, &mut output_count); + } + + // write last input value from arg2s, then read out the result + self.blocking_write_f64(arg2s[arg2s.len() - 1]); + self.blocking_read_f64_to_buf(output, &mut output_count); + } + + // put single input into cordic + let input_left = &arg1s[consumed_input_len..]; + + if !input_left.is_empty() { + self.peri.set_argument_count(Count::One); + + // "preload" value to cordic (at this moment, cordic start to calculating) + self.blocking_write_f64(input_left[0]); + + for &arg in input_left.iter().skip(1) { + // this line write arg for next round caculation to cordic, + // and read result from last round + self.blocking_write_f64(arg); + self.blocking_read_f64_to_buf(output, &mut output_count); + } + + // read the last output + self.blocking_read_f64_to_buf(output, &mut output_count); + } + + output_count } fn blocking_read_f64(&mut self) -> (f64, Option) { let res1 = utils::q1_31_to_f64(self.peri.read_result()); + // We don't care about whether the function return 1 or 2 results, + // the only thing matter is whether user want 1 or 2 results. let res2 = if !self.config.first_result { Some(utils::q1_31_to_f64(self.peri.read_result())) } else { @@ -174,16 +258,14 @@ impl<'d, T: Instance> Cordic<'d, T> { } } -impl<'d, T: Instance> Drop for Cordic<'d, T> { - fn drop(&mut self) { - T::disable(); - } -} - -// q1.31 related +// q1.15 related impl<'d, T: Instance> Cordic<'d, T> { /// Run a CORDIC calculation - pub fn calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { + pub fn blocking_calc_16bit(&mut self, arg1s: &[f32], arg2s: Option<&[f32]>, output: &mut [f32]) -> usize { + if arg1s.is_empty() { + return 0; + } + assert!( match self.config.first_result { true => output.len() >= arg1s.len(), @@ -192,180 +274,182 @@ impl<'d, T: Instance> Cordic<'d, T> { "Output buf length is not long enough" ); - self.check_input_f64(arg1s, arg2s); + self.check_input_f32(arg1s, arg2s); - self.peri.set_result_count(if self.config.first_result { - Count::One - } else { - Count::Two - }); + self.peri.disable_irq(); + self.peri.disable_write_dma(); + self.peri.disable_read_dma(); - self.peri.set_data_width(Width::Bits32, Width::Bits32); + // In q1.15 mode, 1 write/read to access 2 arguments/results + self.peri.set_argument_count(Count::One); + self.peri.set_result_count(Count::One); + + self.peri.set_data_width(Width::Bits16, Width::Bits16); let mut output_count = 0; - let mut consumed_input_len = 0; + // In q1.15 mode, we always fill 1 pair of 16bit value into WDATA register. + // If arg2s is None or empty array, we assume arg2 value always 1.0 (as reset value for ARG2). + // If arg2s has some value, and but not as long as arg1s, + // we fill the reset of arg2 values with last value from arg2s (as q1.31 version does) - match self.config.mode { - Mode::ZeroOverhead => { - // put double input into cordic - if arg2s.is_some() && !arg2s.unwrap().is_empty() { - let arg2s = arg2s.unwrap(); + let arg2_default_value = match arg2s { + Some(arg2s) if !arg2s.is_empty() => arg2s[arg2s.len() - 1], + _ => 1.0, + }; - self.peri.set_argument_count(Count::Two); + let mut args = arg1s.iter().zip( + arg2s + .unwrap_or(&[]) + .iter() + .chain(core::iter::repeat(&arg2_default_value)), + ); - // Skip 1st value from arg1s, this value will be manually "preload" to cordic, to make use of cordic preload function. - // And we preserve last value from arg2s, since it need to manually write to cordic, and read the result out. - let double_input = arg1s.iter().skip(1).zip(&arg2s[..arg2s.len() - 1]); - // Since we preload 1st value from arg1s, the consumed input length is double_input length + 1. - consumed_input_len = double_input.len() + 1; + let (&arg1, &arg2) = args + .next() + .expect("This should be infallible, since arg1s is not empty"); - // preload first value from arg1 to cordic - self.blocking_write_f64(arg1s[0]); + // preloading 1 pair of arguments + self.blocking_write_f32(arg1, arg2); - for (&arg1, &arg2) in double_input { - // Since we manually preload a value before, - // we will write arg2 (from the actual last pair) first, (at this moment, cordic start to calculating,) - // and write arg1 (from the actual next pair), then read the result, to "keep preloading" - - self.blocking_write_f64(arg2); - self.blocking_write_f64(arg1); - self.blocking_read_f64_to_buf(output, &mut output_count); - } - - // write last input value from arg2s, then read out the result - self.blocking_write_f64(arg2s[arg2s.len() - 1]); - self.blocking_read_f64_to_buf(output, &mut output_count); - } - - // put single input into cordic - let input_left = &arg1s[consumed_input_len..]; - - if !input_left.is_empty() { - self.peri.set_argument_count(Count::One); - - // "preload" value to cordic (at this moment, cordic start to calculating) - self.blocking_write_f64(input_left[0]); - - for &arg in input_left.iter().skip(1) { - // this line write arg for next round caculation to cordic, - // and read result from last round - self.blocking_write_f64(arg); - self.blocking_read_f64_to_buf(output, &mut output_count); - } - - // read the last output - self.blocking_read_f64_to_buf(output, &mut output_count); - } - - output_count - } - Mode::Interrupt => todo!(), - Mode::Dma => todo!(), + for (&arg1, &arg2) in args { + self.blocking_write_f32(arg1, arg2); + self.blocking_read_f32_to_buf(output, &mut output_count); } + + // read last pair of value from cordic + self.blocking_read_f32_to_buf(output, &mut output_count); + + output_count } - fn check_input_f64(&self, arg1s: &[f64], arg2s: Option<&[f64]>) { - let config = &self.config; + fn blocking_write_f32(&mut self, arg1: f32, arg2: f32) { + let reg_value: u32 = utils::f32_to_q1_15(arg1) as u32 + ((utils::f32_to_q1_15(arg2) as u32) << 16); + self.peri.write_argument(reg_value); + } - use Function::*; + fn blocking_read_f32_to_buf(&mut self, result_buf: &mut [f32], result_index: &mut usize) { + let (res1, res2) = self.blocking_read_f32(); + result_buf[*result_index] = res1; + *result_index += 1; - // check SCALE value - match config.function { - Cos | Sin | Phase | Modulus => assert!(Scale::A1_R1 == config.scale, "SCALE should be 0"), - Arctan => assert!( - (0..=7).contains(&(config.scale as u8)), - "SCALE should be: 0 <= SCALE <= 7" - ), - Cosh | Sinh | Arctanh => assert!(Scale::A1o2_R2 == config.scale, "SCALE should be 1"), - - Ln => assert!( - (1..=4).contains(&(config.scale as u8)), - "SCALE should be: 1 <= SCALE <= 4" - ), - Sqrt => assert!( - (0..=2).contains(&(config.scale as u8)), - "SCALE should be: 0 <= SCALE <= 2" - ), - } - - // check ARG1 value - match config.function { - Cos | Sin | Phase | Modulus | Arctan => { - assert!( - arg1s.iter().all(|v| (-1.0..=1.0).contains(v)), - "ARG1 should be: -1 <= ARG1 <= 1" - ); - } - - Cosh | Sinh => assert!( - arg1s.iter().all(|v| (-0.559..=0.559).contains(v)), - "ARG1 should be: -0.559 <= ARG1 <= 0.559" - ), - - Arctanh => assert!( - arg1s.iter().all(|v| (-0.403..=0.403).contains(v)), - "ARG1 should be: -0.403 <= ARG1 <= 0.403" - ), - - Ln => { - match config.scale { - Scale::A1o2_R2 => assert!( - arg1s.iter().all(|v| (0.05354..0.5).contains(v)), - "When SCALE set to 1, ARG1 should be: 0.05354 <= ARG1 < 0.5" - ), - Scale::A1o4_R4 => assert!( - arg1s.iter().all(|v| (0.25..0.75).contains(v)), - "When SCALE set to 2, ARG1 should be: 0.25 <= ARG1 < 0.75" - ), - Scale::A1o8_R8 => assert!( - arg1s.iter().all(|v| (0.375..0.875).contains(v)), - "When SCALE set to 3, ARG1 should be: 0.375 <= ARG1 < 0.875" - ), - Scale::A1o16_R16 => assert!( - arg1s.iter().all(|v| (0.4375f64..0.584f64).contains(v)), - "When SCALE set to 4, ARG1 should be: 0.4375 <= ARG1 < 0.584" - ), - _ => unreachable!(), - }; - } - - Function::Sqrt => match config.scale { - Scale::A1_R1 => assert!( - arg1s.iter().all(|v| (0.027..0.75).contains(v)), - "When SCALE set to 0, ARG1 should be: 0.027 <= ARG1 < 0.75" - ), - Scale::A1o2_R2 => assert!( - arg1s.iter().all(|v| (0.375..0.875).contains(v)), - "When SCALE set to 1, ARG1 should be: 0.375 <= ARG1 < 0.875" - ), - Scale::A1o4_R4 => assert!( - arg1s.iter().all(|v| (0.4375..0.585).contains(v)), - "When SCALE set to 2, ARG1 should be: 0.4375 <= ARG1 < 0.585" - ), - _ => unreachable!(), - }, - } - - // check ARG2 value - if let Some(arg2s) = arg2s { - match config.function { - Cos | Sin => assert!( - arg2s.iter().all(|v| (0.0..=1.0).contains(v)), - "ARG2 should be: 0 <= ARG2 <= 1" - ), - - Phase | Modulus => assert!( - arg2s.iter().all(|v| (-1.0..=1.0).contains(v)), - "ARG2 should be: -1 <= ARG2 <= 1" - ), - - _ => (), - } + if let Some(res2) = res2 { + result_buf[*result_index] = res2; + *result_index += 1; } } } +// check input value ARG1, ARG2, SCALE and FUNCTION are compatible with each other +macro_rules! check_input_value { + ($func_name:ident, $float_type:ty) => { + impl<'d, T: Instance> Cordic<'d, T> { + fn $func_name(&self, arg1s: &[$float_type], arg2s: Option<&[$float_type]>) { + let config = &self.config; + + use Function::*; + + // check SCALE value + match config.function { + Cos | Sin | Phase | Modulus => assert!(Scale::A1_R1 == config.scale, "SCALE should be 0"), + Arctan => assert!( + (0..=7).contains(&(config.scale as u8)), + "SCALE should be: 0 <= SCALE <= 7" + ), + Cosh | Sinh | Arctanh => assert!(Scale::A1o2_R2 == config.scale, "SCALE should be 1"), + + Ln => assert!( + (1..=4).contains(&(config.scale as u8)), + "SCALE should be: 1 <= SCALE <= 4" + ), + Sqrt => assert!( + (0..=2).contains(&(config.scale as u8)), + "SCALE should be: 0 <= SCALE <= 2" + ), + } + + // check ARG1 value + match config.function { + Cos | Sin | Phase | Modulus | Arctan => { + assert!( + arg1s.iter().all(|v| (-1.0..=1.0).contains(v)), + "ARG1 should be: -1 <= ARG1 <= 1" + ); + } + + Cosh | Sinh => assert!( + arg1s.iter().all(|v| (-0.559..=0.559).contains(v)), + "ARG1 should be: -0.559 <= ARG1 <= 0.559" + ), + + Arctanh => assert!( + arg1s.iter().all(|v| (-0.403..=0.403).contains(v)), + "ARG1 should be: -0.403 <= ARG1 <= 0.403" + ), + + Ln => { + match config.scale { + Scale::A1o2_R2 => assert!( + arg1s.iter().all(|v| (0.05354..0.5).contains(v)), + "When SCALE set to 1, ARG1 should be: 0.05354 <= ARG1 < 0.5" + ), + Scale::A1o4_R4 => assert!( + arg1s.iter().all(|v| (0.25..0.75).contains(v)), + "When SCALE set to 2, ARG1 should be: 0.25 <= ARG1 < 0.75" + ), + Scale::A1o8_R8 => assert!( + arg1s.iter().all(|v| (0.375..0.875).contains(v)), + "When SCALE set to 3, ARG1 should be: 0.375 <= ARG1 < 0.875" + ), + Scale::A1o16_R16 => assert!( + arg1s.iter().all(|v| (0.4375..0.584).contains(v)), + "When SCALE set to 4, ARG1 should be: 0.4375 <= ARG1 < 0.584" + ), + _ => unreachable!(), + }; + } + + Function::Sqrt => match config.scale { + Scale::A1_R1 => assert!( + arg1s.iter().all(|v| (0.027..0.75).contains(v)), + "When SCALE set to 0, ARG1 should be: 0.027 <= ARG1 < 0.75" + ), + Scale::A1o2_R2 => assert!( + arg1s.iter().all(|v| (0.375..0.875).contains(v)), + "When SCALE set to 1, ARG1 should be: 0.375 <= ARG1 < 0.875" + ), + Scale::A1o4_R4 => assert!( + arg1s.iter().all(|v| (0.4375..0.585).contains(v)), + "When SCALE set to 2, ARG1 should be: 0.4375 <= ARG1 < 0.585" + ), + _ => unreachable!(), + }, + } + + // check ARG2 value + if let Some(arg2s) = arg2s { + match config.function { + Cos | Sin => assert!( + arg2s.iter().all(|v| (0.0..=1.0).contains(v)), + "ARG2 should be: 0 <= ARG2 <= 1" + ), + + Phase | Modulus => assert!( + arg2s.iter().all(|v| (-1.0..=1.0).contains(v)), + "ARG2 should be: -1 <= ARG2 <= 1" + ), + + _ => (), + } + } + } + } + }; +} + +check_input_value!(check_input_f64, f64); +check_input_value!(check_input_f32, f32); + foreach_interrupt!( ($inst:ident, cordic, $block:ident, GLOBAL, $irq:ident) => { impl Instance for peripherals::$inst { diff --git a/embassy-stm32/src/cordic/utils.rs b/embassy-stm32/src/cordic/utils.rs index 3f055c34b..2f4b5c5e8 100644 --- a/embassy-stm32/src/cordic/utils.rs +++ b/embassy-stm32/src/cordic/utils.rs @@ -3,7 +3,7 @@ macro_rules! floating_fixed_convert { ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => { /// convert float point to fixed point format - pub fn $f_to_q(value: $float_ty) -> $unsigned_bin_typ { + pub(crate) fn $f_to_q(value: $float_ty) -> $unsigned_bin_typ { const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) }; assert!( @@ -31,7 +31,7 @@ macro_rules! floating_fixed_convert { #[inline(always)] /// convert fixed point to float point format - pub fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty { + pub(crate) fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty { // It's needed to convert from unsigned to signed first, for correct result. -(value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty) } From 2fa04d93ed93bed97c7575019aea32c2543e322c Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Mon, 18 Mar 2024 23:09:18 +0800 Subject: [PATCH 15/69] stm32 CORDIC: DMA for q1.31 --- embassy-stm32/build.rs | 4 +- embassy-stm32/src/cordic/mod.rs | 207 ++++++++++++++++++++++++++++++-- 2 files changed, 199 insertions(+), 12 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 15bb8ea62..e224cc5a2 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -484,7 +484,7 @@ fn main() { let expr = if let Some(mux) = self.chained_muxes.get(&v.name) { self.gen_mux(mux) } else { - self.gen_clock(&v.name) + self.gen_clock(v.name) }; match_arms.extend(quote! { crate::pac::rcc::vals::#enum_name::#variant_name => #expr, @@ -1139,6 +1139,8 @@ fn main() { (("timer", "CH2"), quote!(crate::timer::Ch2Dma)), (("timer", "CH3"), quote!(crate::timer::Ch3Dma)), (("timer", "CH4"), quote!(crate::timer::Ch4Dma)), + (("cordic", "WRITE"), quote!(crate::cordic::WriteDma)), + (("cordic", "READ"), quote!(crate::cordic::ReadDma)), ] .into(); diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index 61277d7e1..9875d73bb 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs @@ -2,7 +2,7 @@ use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; -use crate::peripherals; +use crate::{dma, peripherals}; mod enums; pub use enums::*; @@ -17,6 +17,8 @@ pub mod low_level { pub use super::sealed::*; } +const INPUT_BUF_MAX_LEN: usize = 16; + /// CORDIC driver pub struct Cordic<'d, T: Instance> { peri: PeripheralRef<'d, T>, @@ -98,7 +100,6 @@ impl<'d, T: Instance> Cordic<'d, T> { warn!("At least 1 result hasn't been read, reconfigure will cause DATA LOST"); }; - self.peri.disable_irq(); self.peri.disable_write_dma(); self.peri.disable_read_dma(); @@ -111,11 +112,8 @@ impl<'d, T: Instance> Cordic<'d, T> { self.peri.set_precision(self.config.precision); self.peri.set_scale(self.config.scale); - if self.config.first_result { - self.peri.set_result_count(Count::One) - } else { - self.peri.set_result_count(Count::Two) - } + // we don't set NRES in here, but to make sure NRES is set each time user call "calc"-ish functions, + // since each "calc"-ish functions can have different ARGSIZE and RESSIZE, thus NRES should be change accrodingly. } fn blocking_read_f32(&mut self) -> (f32, Option) { @@ -143,7 +141,7 @@ impl<'d, T: Instance> Drop for Cordic<'d, T> { // q1.31 related impl<'d, T: Instance> Cordic<'d, T> { - /// Run a CORDIC calculation + /// Run a blocking CORDIC calculation pub fn blocking_calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { if arg1s.is_empty() { return 0; @@ -159,7 +157,6 @@ impl<'d, T: Instance> Cordic<'d, T> { self.check_input_f64(arg1s, arg2s); - self.peri.disable_irq(); self.peri.disable_write_dma(); self.peri.disable_read_dma(); @@ -256,6 +253,192 @@ impl<'d, T: Instance> Cordic<'d, T> { fn blocking_write_f64(&mut self, arg: f64) { self.peri.write_argument(utils::f64_to_q1_31(arg)); } + + /// Run a async CORDIC calculation + pub async fn async_calc_32bit( + &mut self, + write_dma: impl Peripheral

>, + read_dma: impl Peripheral

>, + arg1s: &[f64], + arg2s: Option<&[f64]>, + output: &mut [f64], + ) -> usize { + if arg1s.is_empty() { + return 0; + } + + assert!( + match self.config.first_result { + true => output.len() >= arg1s.len(), + false => output.len() >= 2 * arg1s.len(), + }, + "Output buf length is not long enough" + ); + + self.check_input_f64(arg1s, arg2s); + + into_ref!(write_dma, read_dma); + + self.peri.set_result_count(if self.config.first_result { + Count::One + } else { + Count::Two + }); + + self.peri.set_data_width(Width::Bits32, Width::Bits32); + + let mut output_count = 0; + let mut consumed_input_len = 0; + let mut input_buf = [0u32; INPUT_BUF_MAX_LEN]; + let mut input_buf_len = 0; + + self.peri.enable_write_dma(); + self.peri.enable_read_dma(); + + if !arg2s.unwrap_or_default().is_empty() { + let arg2s = arg2s.expect("It's infailable"); + + self.peri.set_argument_count(Count::Two); + + let double_input = arg1s.iter().zip(arg2s); + + consumed_input_len = double_input.len(); + + for (&arg1, &arg2) in double_input { + for &arg in [arg1, arg2].iter() { + input_buf[input_buf_len] = utils::f64_to_q1_31(arg); + input_buf_len += 1; + } + + if input_buf_len == INPUT_BUF_MAX_LEN { + self.dma_calc_32bit( + &mut write_dma, + &mut read_dma, + true, + &input_buf[..input_buf_len], + output, + &mut output_count, + ) + .await; + + input_buf_len = 0; + } + } + + if input_buf_len % 2 != 0 { + panic!("input buf len should be multiple of 2 in double mode") + } + + if input_buf_len > 0 { + self.dma_calc_32bit( + &mut write_dma, + &mut read_dma, + true, + &input_buf[..input_buf_len], + output, + &mut output_count, + ) + .await; + + input_buf_len = 0; + } + } + + // single input + + if arg1s.len() > consumed_input_len { + let input_remain = &arg1s[consumed_input_len..]; + + self.peri.set_argument_count(Count::One); + + for &arg in input_remain { + input_buf[input_buf_len] = utils::f64_to_q1_31(arg); + input_buf_len += 1; + + if input_buf_len == INPUT_BUF_MAX_LEN { + self.dma_calc_32bit( + &mut write_dma, + &mut read_dma, + false, + &input_buf[..input_buf_len], + output, + &mut output_count, + ) + .await; + + input_buf_len = 0; + } + } + + if input_buf_len > 0 { + self.dma_calc_32bit( + &mut write_dma, + &mut read_dma, + false, + &input_buf[..input_buf_len], + output, + &mut output_count, + ) + .await; + + // input_buf_len = 0; + } + } + + output_count + } + + async fn dma_calc_32bit( + &mut self, + write_dma: impl Peripheral

>, + read_dma: impl Peripheral

>, + double_input: bool, + input_buf: &[u32], + output: &mut [f64], + output_start_index: &mut usize, + ) { + into_ref!(write_dma, read_dma); + + let write_req = write_dma.request(); + let read_req = read_dma.request(); + + let mut output_buf = [0u32; INPUT_BUF_MAX_LEN * 2]; // make output_buf long enough + + let mut output_buf_size = input_buf.len(); + if !self.config.first_result { + output_buf_size *= 2; + }; + if double_input { + output_buf_size /= 2; + } + + let active_output_buf = &mut output_buf[..output_buf_size]; + + unsafe { + let write_transfer = dma::Transfer::new_write( + &mut write_dma, + write_req, + input_buf, + T::regs().wdata().as_ptr() as *mut _, + Default::default(), + ); + + let read_transfer = dma::Transfer::new_read( + &mut read_dma, + read_req, + T::regs().rdata().as_ptr() as *mut _, + active_output_buf, + Default::default(), + ); + + embassy_futures::join::join(write_transfer, read_transfer).await; + } + + for &mut output_u32 in active_output_buf { + output[*output_start_index] = utils::q1_31_to_f64(output_u32); + *output_start_index += 1; + } + } } // q1.15 related @@ -276,7 +459,6 @@ impl<'d, T: Instance> Cordic<'d, T> { self.check_input_f32(arg1s, arg2s); - self.peri.disable_irq(); self.peri.disable_write_dma(); self.peri.disable_read_dma(); @@ -409,7 +591,7 @@ macro_rules! check_input_value { }; } - Function::Sqrt => match config.scale { + Sqrt => match config.scale { Scale::A1_R1 => assert!( arg1s.iter().all(|v| (0.027..0.75).contains(v)), "When SCALE set to 0, ARG1 should be: 0.027 <= ARG1 < 0.75" @@ -462,3 +644,6 @@ foreach_interrupt!( } }; ); + +dma_trait!(WriteDma, Instance); +dma_trait!(ReadDma, Instance); From 10a9cce855fbf383a8f0ea5511526777062a03c4 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Tue, 19 Mar 2024 20:09:36 +0800 Subject: [PATCH 16/69] stm32 CORDIC: DMA for q1.31 and q1.15 --- embassy-stm32/src/cordic/mod.rs | 264 ++++++++++++++++++++++-------- embassy-stm32/src/cordic/utils.rs | 13 ++ 2 files changed, 209 insertions(+), 68 deletions(-) diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index 9875d73bb..a4b98a770 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs @@ -1,5 +1,6 @@ //! CORDIC co-processor +use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use crate::{dma, peripherals}; @@ -100,9 +101,6 @@ impl<'d, T: Instance> Cordic<'d, T> { warn!("At least 1 result hasn't been read, reconfigure will cause DATA LOST"); }; - self.peri.disable_write_dma(); - self.peri.disable_read_dma(); - // clean RRDY flag while self.peri.ready_to_read() { self.peri.read_result(); @@ -115,22 +113,6 @@ impl<'d, T: Instance> Cordic<'d, T> { // we don't set NRES in here, but to make sure NRES is set each time user call "calc"-ish functions, // since each "calc"-ish functions can have different ARGSIZE and RESSIZE, thus NRES should be change accrodingly. } - - fn blocking_read_f32(&mut self) -> (f32, Option) { - let reg_value = self.peri.read_result(); - - let res1 = utils::q1_15_to_f32((reg_value & ((1u32 << 16) - 1)) as u16); - - // We don't care about whether the function return 1 or 2 results, - // the only thing matter is whether user want 1 or 2 results. - let res2 = if !self.config.first_result { - Some(utils::q1_15_to_f32((reg_value >> 16) as u16)) - } else { - None - }; - - (res1, res2) - } } impl<'d, T: Instance> Drop for Cordic<'d, T> { @@ -141,7 +123,7 @@ impl<'d, T: Instance> Drop for Cordic<'d, T> { // q1.31 related impl<'d, T: Instance> Cordic<'d, T> { - /// Run a blocking CORDIC calculation + /// Run a blocking CORDIC calculation in q1.31 format pub fn blocking_calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { if arg1s.is_empty() { return 0; @@ -157,9 +139,6 @@ impl<'d, T: Instance> Cordic<'d, T> { self.check_input_f64(arg1s, arg2s); - self.peri.disable_write_dma(); - self.peri.disable_read_dma(); - self.peri.set_result_count(if self.config.first_result { Count::One } else { @@ -172,7 +151,10 @@ impl<'d, T: Instance> Cordic<'d, T> { let mut consumed_input_len = 0; - // put double input into cordic + // + // handle 2 input args calculation + // + if arg2s.is_some() && !arg2s.expect("It's infailable").is_empty() { let arg2s = arg2s.expect("It's infailable"); @@ -202,7 +184,10 @@ impl<'d, T: Instance> Cordic<'d, T> { self.blocking_read_f64_to_buf(output, &mut output_count); } - // put single input into cordic + // + // handle 1 input arg calculation + // + let input_left = &arg1s[consumed_input_len..]; if !input_left.is_empty() { @@ -225,27 +210,14 @@ impl<'d, T: Instance> Cordic<'d, T> { output_count } - fn blocking_read_f64(&mut self) -> (f64, Option) { - let res1 = utils::q1_31_to_f64(self.peri.read_result()); + fn blocking_read_f64_to_buf(&mut self, result_buf: &mut [f64], result_index: &mut usize) { + result_buf[*result_index] = utils::q1_31_to_f64(self.peri.read_result()); + *result_index += 1; // We don't care about whether the function return 1 or 2 results, // the only thing matter is whether user want 1 or 2 results. - let res2 = if !self.config.first_result { - Some(utils::q1_31_to_f64(self.peri.read_result())) - } else { - None - }; - - (res1, res2) - } - - fn blocking_read_f64_to_buf(&mut self, result_buf: &mut [f64], result_index: &mut usize) { - let (res1, res2) = self.blocking_read_f64(); - result_buf[*result_index] = res1; - *result_index += 1; - - if let Some(res2) = res2 { - result_buf[*result_index] = res2; + if !self.config.first_result { + result_buf[*result_index] = utils::q1_31_to_f64(self.peri.read_result()); *result_index += 1; } } @@ -254,7 +226,7 @@ impl<'d, T: Instance> Cordic<'d, T> { self.peri.write_argument(utils::f64_to_q1_31(arg)); } - /// Run a async CORDIC calculation + /// Run a async CORDIC calculation in q.1.31 format pub async fn async_calc_32bit( &mut self, write_dma: impl Peripheral

>, @@ -292,8 +264,9 @@ impl<'d, T: Instance> Cordic<'d, T> { let mut input_buf = [0u32; INPUT_BUF_MAX_LEN]; let mut input_buf_len = 0; - self.peri.enable_write_dma(); - self.peri.enable_read_dma(); + // + // handle 2 input args calculation + // if !arg2s.unwrap_or_default().is_empty() { let arg2s = arg2s.expect("It's infailable"); @@ -311,7 +284,7 @@ impl<'d, T: Instance> Cordic<'d, T> { } if input_buf_len == INPUT_BUF_MAX_LEN { - self.dma_calc_32bit( + self.inner_dma_calc_32bit( &mut write_dma, &mut read_dma, true, @@ -325,12 +298,8 @@ impl<'d, T: Instance> Cordic<'d, T> { } } - if input_buf_len % 2 != 0 { - panic!("input buf len should be multiple of 2 in double mode") - } - if input_buf_len > 0 { - self.dma_calc_32bit( + self.inner_dma_calc_32bit( &mut write_dma, &mut read_dma, true, @@ -344,7 +313,9 @@ impl<'d, T: Instance> Cordic<'d, T> { } } - // single input + // + // handle 1 input arg calculation + // if arg1s.len() > consumed_input_len { let input_remain = &arg1s[consumed_input_len..]; @@ -356,7 +327,7 @@ impl<'d, T: Instance> Cordic<'d, T> { input_buf_len += 1; if input_buf_len == INPUT_BUF_MAX_LEN { - self.dma_calc_32bit( + self.inner_dma_calc_32bit( &mut write_dma, &mut read_dma, false, @@ -371,7 +342,7 @@ impl<'d, T: Instance> Cordic<'d, T> { } if input_buf_len > 0 { - self.dma_calc_32bit( + self.inner_dma_calc_32bit( &mut write_dma, &mut read_dma, false, @@ -388,32 +359,47 @@ impl<'d, T: Instance> Cordic<'d, T> { output_count } - async fn dma_calc_32bit( + // this function is highly coupled with async_calc_32bit, and is not intended to use in other place + async fn inner_dma_calc_32bit( &mut self, write_dma: impl Peripheral

>, read_dma: impl Peripheral

>, - double_input: bool, - input_buf: &[u32], - output: &mut [f64], - output_start_index: &mut usize, + double_input: bool, // gether extra info to calc output_buf size + input_buf: &[u32], // input_buf, its content should be extact values and length for calculation + output: &mut [f64], // caller uses should this as a final output array + output_start_index: &mut usize, // the index of start point of the output for this round of calculation ) { into_ref!(write_dma, read_dma); let write_req = write_dma.request(); let read_req = read_dma.request(); - let mut output_buf = [0u32; INPUT_BUF_MAX_LEN * 2]; // make output_buf long enough + // output_buf is the place to store raw value from CORDIC (via DMA). + // For buf size, we assume in this round of calculation: + // all input is 1 arg, and all calculation need 2 output, + // thus output_buf will always be long enough. + let mut output_buf = [0u32; INPUT_BUF_MAX_LEN * 2]; let mut output_buf_size = input_buf.len(); if !self.config.first_result { + // if we need 2 result for 1 input, then output_buf length should be 2x long. output_buf_size *= 2; }; if double_input { + // if input itself is 2 args for 1 calculation, then output_buf length should be /2. output_buf_size /= 2; } let active_output_buf = &mut output_buf[..output_buf_size]; + self.peri.enable_write_dma(); + self.peri.enable_read_dma(); + + let on_drop = OnDrop::new(|| { + self.peri.disable_write_dma(); + self.peri.disable_read_dma(); + }); + unsafe { let write_transfer = dma::Transfer::new_write( &mut write_dma, @@ -434,6 +420,8 @@ impl<'d, T: Instance> Cordic<'d, T> { embassy_futures::join::join(write_transfer, read_transfer).await; } + drop(on_drop); + for &mut output_u32 in active_output_buf { output[*output_start_index] = utils::q1_31_to_f64(output_u32); *output_start_index += 1; @@ -443,7 +431,7 @@ impl<'d, T: Instance> Cordic<'d, T> { // q1.15 related impl<'d, T: Instance> Cordic<'d, T> { - /// Run a CORDIC calculation + /// Run a blocking CORDIC calculation in q1.15 format pub fn blocking_calc_16bit(&mut self, arg1s: &[f32], arg2s: Option<&[f32]>, output: &mut [f32]) -> usize { if arg1s.is_empty() { return 0; @@ -459,9 +447,6 @@ impl<'d, T: Instance> Cordic<'d, T> { self.check_input_f32(arg1s, arg2s); - self.peri.disable_write_dma(); - self.peri.disable_read_dma(); - // In q1.15 mode, 1 write/read to access 2 arguments/results self.peri.set_argument_count(Count::One); self.peri.set_result_count(Count::One); @@ -506,20 +491,163 @@ impl<'d, T: Instance> Cordic<'d, T> { } fn blocking_write_f32(&mut self, arg1: f32, arg2: f32) { - let reg_value: u32 = utils::f32_to_q1_15(arg1) as u32 + ((utils::f32_to_q1_15(arg2) as u32) << 16); + let reg_value: u32 = utils::f32_args_to_u32(arg1, arg2); self.peri.write_argument(reg_value); } fn blocking_read_f32_to_buf(&mut self, result_buf: &mut [f32], result_index: &mut usize) { - let (res1, res2) = self.blocking_read_f32(); + let reg_value = self.peri.read_result(); + + let (res1, res2) = utils::u32_to_f32_res(reg_value); + result_buf[*result_index] = res1; *result_index += 1; - if let Some(res2) = res2 { + // We don't care about whether the function return 1 or 2 results, + // the only thing matter is whether user want 1 or 2 results. + if !self.config.first_result { result_buf[*result_index] = res2; *result_index += 1; } } + + /// Run a async CORDIC calculation in q1.15 format + pub async fn async_calc_16bit( + &mut self, + write_dma: impl Peripheral

>, + read_dma: impl Peripheral

>, + arg1s: &[f32], + arg2s: Option<&[f32]>, + output: &mut [f32], + ) -> usize { + if arg1s.is_empty() { + return 0; + } + + assert!( + match self.config.first_result { + true => output.len() >= arg1s.len(), + false => output.len() >= 2 * arg1s.len(), + }, + "Output buf length is not long enough" + ); + + self.check_input_f32(arg1s, arg2s); + + into_ref!(write_dma, read_dma); + + // In q1.15 mode, 1 write/read to access 2 arguments/results + self.peri.set_argument_count(Count::One); + self.peri.set_result_count(Count::One); + + self.peri.set_data_width(Width::Bits16, Width::Bits16); + + let mut output_count = 0; + let mut input_buf = [0u32; INPUT_BUF_MAX_LEN]; + let mut input_buf_len = 0; + + // In q1.15 mode, we always fill 1 pair of 16bit value into WDATA register. + // If arg2s is None or empty array, we assume arg2 value always 1.0 (as reset value for ARG2). + // If arg2s has some value, and but not as long as arg1s, + // we fill the reset of arg2 values with last value from arg2s (as q1.31 version does) + + let arg2_default_value = match arg2s { + Some(arg2s) if !arg2s.is_empty() => arg2s[arg2s.len() - 1], + _ => 1.0, + }; + + let args = arg1s.iter().zip( + arg2s + .unwrap_or(&[]) + .iter() + .chain(core::iter::repeat(&arg2_default_value)), + ); + + for (&arg1, &arg2) in args { + input_buf[input_buf_len] = utils::f32_args_to_u32(arg1, arg2); + input_buf_len += 1; + + if input_buf_len == INPUT_BUF_MAX_LEN { + self.inner_dma_calc_16bit(&mut write_dma, &mut read_dma, &input_buf, output, &mut output_count) + .await; + } + } + + if input_buf_len > 0 { + self.inner_dma_calc_16bit( + &mut write_dma, + &mut read_dma, + &input_buf[..input_buf_len], + output, + &mut output_count, + ) + .await; + } + + output_count + } + + // this function is highly coupled with async_calc_16bit, and is not intended to use in other place + async fn inner_dma_calc_16bit( + &mut self, + write_dma: impl Peripheral

>, + read_dma: impl Peripheral

>, + input_buf: &[u32], // input_buf, its content should be extact values and length for calculation + output: &mut [f32], // caller uses should this as a final output array + output_start_index: &mut usize, // the index of start point of the output for this round of calculation + ) { + into_ref!(write_dma, read_dma); + + let write_req = write_dma.request(); + let read_req = read_dma.request(); + + // output_buf is the place to store raw value from CORDIC (via DMA). + let mut output_buf = [0u32; INPUT_BUF_MAX_LEN]; + + let active_output_buf = &mut output_buf[..input_buf.len()]; + + self.peri.enable_write_dma(); + self.peri.enable_read_dma(); + + let on_drop = OnDrop::new(|| { + self.peri.disable_write_dma(); + self.peri.disable_read_dma(); + }); + + unsafe { + let write_transfer = dma::Transfer::new_write( + &mut write_dma, + write_req, + input_buf, + T::regs().wdata().as_ptr() as *mut _, + Default::default(), + ); + + let read_transfer = dma::Transfer::new_read( + &mut read_dma, + read_req, + T::regs().rdata().as_ptr() as *mut _, + active_output_buf, + Default::default(), + ); + + embassy_futures::join::join(write_transfer, read_transfer).await; + } + + drop(on_drop); + + for &mut output_u32 in active_output_buf { + let (res1, res2) = utils::u32_to_f32_res(output_u32); + + output[*output_start_index] = res1; + *output_start_index += 1; + + if !self.config.first_result { + output[*output_start_index] = res2; + *output_start_index += 1; + } + } + } } // check input value ARG1, ARG2, SCALE and FUNCTION are compatible with each other diff --git a/embassy-stm32/src/cordic/utils.rs b/embassy-stm32/src/cordic/utils.rs index 2f4b5c5e8..79bef6b97 100644 --- a/embassy-stm32/src/cordic/utils.rs +++ b/embassy-stm32/src/cordic/utils.rs @@ -57,3 +57,16 @@ floating_fixed_convert!( 15, 0x3800_0000u32 // binary form of 1f32^(-15) ); + +#[inline(always)] +pub(crate) fn f32_args_to_u32(arg1: f32, arg2: f32) -> u32 { + f32_to_q1_15(arg1) as u32 + ((f32_to_q1_15(arg2) as u32) << 16) +} + +#[inline(always)] +pub(crate) fn u32_to_f32_res(reg_value: u32) -> (f32, f32) { + let res1 = q1_15_to_f32((reg_value & ((1u32 << 16) - 1)) as u16); + let res2 = q1_15_to_f32((reg_value >> 16) as u16); + + (res1, res2) +} From 641da3602e1d7565d08180e0f5608f1ab81c309a Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Tue, 19 Mar 2024 22:19:06 +0800 Subject: [PATCH 17/69] stm32 CORDIC: error handle --- embassy-stm32/src/cordic/enums.rs | 29 +- embassy-stm32/src/cordic/errors.rs | 95 ++++++ embassy-stm32/src/cordic/mod.rs | 510 +++++++++++++++-------------- embassy-stm32/src/cordic/sealed.rs | 12 +- 4 files changed, 385 insertions(+), 261 deletions(-) create mode 100644 embassy-stm32/src/cordic/errors.rs diff --git a/embassy-stm32/src/cordic/enums.rs b/embassy-stm32/src/cordic/enums.rs index 37c73f549..4b92a6cf8 100644 --- a/embassy-stm32/src/cordic/enums.rs +++ b/embassy-stm32/src/cordic/enums.rs @@ -1,6 +1,7 @@ /// CORDIC function #[allow(missing_docs)] -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Function { Cos = 0, Sin, @@ -16,7 +17,7 @@ pub enum Function { /// CORDIC precision #[allow(missing_docs)] -#[derive(Clone, Copy, Default)] +#[derive(Debug, Clone, Copy, Default)] pub enum Precision { Iters4 = 1, Iters8, @@ -37,25 +38,25 @@ pub enum Precision { } /// CORDIC scale -#[allow(non_camel_case_types)] #[allow(missing_docs)] -#[derive(Clone, Copy, Default, PartialEq)] +#[derive(Debug, Clone, Copy, Default, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Scale { #[default] - A1_R1 = 0, - A1o2_R2, - A1o4_R4, - A1o8_R8, - A1o16_R16, - A1o32_R32, - A1o64_R64, - A1o128_R128, + Arg1Res1 = 0, + Arg1o2Res2, + Arg1o4Res4, + Arg1o8Res8, + Arg1o16Res16, + Arg1o32Res32, + Arg1o64Res64, + Arg1o128Res128, } -/// CORDIC argument/result count +/// CORDIC argument/result register access count #[allow(missing_docs)] #[derive(Clone, Copy, Default)] -pub enum Count { +pub enum AccessCount { #[default] One, Two, diff --git a/embassy-stm32/src/cordic/errors.rs b/embassy-stm32/src/cordic/errors.rs new file mode 100644 index 000000000..d0b2dc618 --- /dev/null +++ b/embassy-stm32/src/cordic/errors.rs @@ -0,0 +1,95 @@ +use super::{Function, Scale}; + +/// Error for [Cordic](super::Cordic) +#[derive(Debug)] +pub enum CordicError { + /// Config error + ConfigError(ConfigError), + /// Argument error + ArgError(ArgError), + /// Output buffer length error + OutputLengthNotEnough, +} + +#[cfg(feature = "defmt")] +impl defmt::Format for CordicError { + fn format(&self, fmt: defmt::Formatter) { + use CordicError::*; + + match self { + ConfigError(e) => defmt::write!(fmt, "{}", e), + ArgError(e) => defmt::write!(fmt, "{}", e), + OutputLengthNotEnough => defmt::write!(fmt, "Output buffer length is not long enough"), + } + } +} + +/// Error dring parsing [Cordic::Config](super::Config) +#[derive(Debug)] +pub struct ConfigError { + pub(super) func: Function, + pub(super) scale_range: [u8; 2], +} + +#[cfg(feature = "defmt")] +impl defmt::Format for ConfigError { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "For FUNCTION: {},", self.func); + + if self.scale_range[0] == self.scale_range[1] { + defmt::write!(fmt, " SCALE value should be {}", self.scale_range[0]) + } else { + defmt::write!( + fmt, + " SCALE value should be {} <= SCALE <= {}", + self.scale_range[0], + self.scale_range[1] + ) + } + } +} + +/// Error on checking input arguments +#[derive(Debug)] +pub struct ArgError { + pub(super) func: Function, + pub(super) scale: Option, + pub(super) arg_range: [f32; 2], // only for debug display, f32 is ok + pub(super) inclusive_upper_bound: bool, + pub(super) arg_type: ArgType, +} + +#[cfg(feature = "defmt")] +impl defmt::Format for ArgError { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "For FUNCTION: {},", self.func); + + if let Some(scale) = self.scale { + defmt::write!(fmt, " when SCALE is {},", scale); + } + + let arg_string = match self.arg_type { + ArgType::Arg1 => "ARG1", + ArgType::Arg2 => "ARG2", + }; + + defmt::write!(fmt, " {} should be", arg_string); + + let inclusive_string = if self.inclusive_upper_bound { "=" } else { "" }; + + defmt::write!( + fmt, + " {} <= {} <{} {}", + self.arg_range[0], + arg_string, + inclusive_string, + self.arg_range[1] + ) + } +} + +#[derive(Debug)] +pub(super) enum ArgType { + Arg1, + Arg2, +} diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index a4b98a770..5ac9addd8 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs @@ -1,4 +1,4 @@ -//! CORDIC co-processor +//! coordinate rotation digital computer (CORDIC) use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; @@ -8,6 +8,9 @@ use crate::{dma, peripherals}; mod enums; pub use enums::*; +mod errors; +pub use errors::*; + pub mod utils; pub(crate) mod sealed; @@ -30,33 +33,55 @@ pub struct Cordic<'d, T: Instance> { pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral {} /// CORDIC configuration +#[derive(Debug)] pub struct Config { function: Function, precision: Precision, scale: Scale, - first_result: bool, + res1_only: bool, } impl Config { /// Create a config for Cordic driver - pub fn new(function: Function, precision: Option, scale: Option, first_result: bool) -> Self { - Self { + pub fn new(function: Function, precision: Precision, scale: Scale, res1_only: bool) -> Result { + let config = Self { function, - precision: precision.unwrap_or_default(), - scale: scale.unwrap_or_default(), - first_result, - } + precision, + scale, + res1_only, + }; + + config.check_scale()?; + + Ok(config) } - fn check_scale(&self) -> bool { + fn check_scale(&self) -> Result<(), CordicError> { + use Function::*; + let scale_raw = self.scale as u8; - match self.function { - Function::Cos | Function::Sin | Function::Phase | Function::Modulus => 0 == scale_raw, - Function::Arctan => (0..=7).contains(&scale_raw), - Function::Cosh | Function::Sinh | Function::Arctanh => 1 == scale_raw, - Function::Ln => (1..=4).contains(&scale_raw), - Function::Sqrt => (0..=2).contains(&scale_raw), + let err_range = match self.function { + Cos | Sin | Phase | Modulus if !(0..=0).contains(&scale_raw) => Some([0, 0]), + + Arctan if !(0..=7).contains(&scale_raw) => Some([0, 7]), + + Cosh | Sinh | Arctanh if !(1..=1).contains(&scale_raw) => Some([1, 1]), + + Ln if !(1..=4).contains(&scale_raw) => Some([1, 4]), + + Sqrt if !(0..=2).contains(&scale_raw) => Some([0, 2]), + + Cos | Sin | Phase | Modulus | Arctan | Cosh | Sinh | Arctanh | Ln | Sqrt => None, + }; + + if let Some(range) = err_range { + Err(CordicError::ConfigError(ConfigError { + func: self.function, + scale_range: range, + })) + } else { + Ok(()) } } } @@ -73,10 +98,6 @@ impl<'d, T: Instance> Cordic<'d, T> { into_ref!(peri); - if !config.check_scale() { - panic!("Scale value is not compatible with Function") - } - let mut instance = Self { peri, config }; instance.reconfigure(); @@ -91,21 +112,12 @@ impl<'d, T: Instance> Cordic<'d, T> { } /// Set extra config for data count and data width. - pub fn extra_config(&mut self, arg_cnt: Count, arg_width: Width, res_width: Width) { + pub fn extra_config(&mut self, arg_cnt: AccessCount, arg_width: Width, res_width: Width) { self.peri.set_argument_count(arg_cnt); self.peri.set_data_width(arg_width, res_width); } fn reconfigure(&mut self) { - if self.peri.ready_to_read() { - warn!("At least 1 result hasn't been read, reconfigure will cause DATA LOST"); - }; - - // clean RRDY flag - while self.peri.ready_to_read() { - self.peri.read_result(); - } - self.peri.set_func(self.config.function); self.peri.set_precision(self.config.precision); self.peri.set_scale(self.config.scale); @@ -113,6 +125,47 @@ impl<'d, T: Instance> Cordic<'d, T> { // we don't set NRES in here, but to make sure NRES is set each time user call "calc"-ish functions, // since each "calc"-ish functions can have different ARGSIZE and RESSIZE, thus NRES should be change accrodingly. } + + async fn launch_a_dma_transfer( + &mut self, + write_dma: impl Peripheral

>, + read_dma: impl Peripheral

>, + input: &[u32], + output: &mut [u32], + ) { + into_ref!(write_dma, read_dma); + + let write_req = write_dma.request(); + let read_req = read_dma.request(); + + self.peri.enable_write_dma(); + self.peri.enable_read_dma(); + + let _on_drop = OnDrop::new(|| { + self.peri.disable_write_dma(); + self.peri.disable_read_dma(); + }); + + unsafe { + let write_transfer = dma::Transfer::new_write( + &mut write_dma, + write_req, + input, + T::regs().wdata().as_ptr() as *mut _, + Default::default(), + ); + + let read_transfer = dma::Transfer::new_read( + &mut read_dma, + read_req, + T::regs().rdata().as_ptr() as *mut _, + output, + Default::default(), + ); + + embassy_futures::join::join(write_transfer, read_transfer).await; + } + } } impl<'d, T: Instance> Drop for Cordic<'d, T> { @@ -124,25 +177,31 @@ impl<'d, T: Instance> Drop for Cordic<'d, T> { // q1.31 related impl<'d, T: Instance> Cordic<'d, T> { /// Run a blocking CORDIC calculation in q1.31 format - pub fn blocking_calc_32bit(&mut self, arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64]) -> usize { + pub fn blocking_calc_32bit( + &mut self, + arg1s: &[f64], + arg2s: Option<&[f64]>, + output: &mut [f64], + ) -> Result { if arg1s.is_empty() { - return 0; + return Ok(0); } - assert!( - match self.config.first_result { - true => output.len() >= arg1s.len(), - false => output.len() >= 2 * arg1s.len(), - }, - "Output buf length is not long enough" - ); + let output_length_enough = match self.config.res1_only { + true => output.len() >= arg1s.len(), + false => output.len() >= 2 * arg1s.len(), + }; - self.check_input_f64(arg1s, arg2s); + if !output_length_enough { + return Err(CordicError::OutputLengthNotEnough); + } - self.peri.set_result_count(if self.config.first_result { - Count::One + self.check_input_f64(arg1s, arg2s)?; + + self.peri.set_result_count(if self.config.res1_only { + AccessCount::One } else { - Count::Two + AccessCount::Two }); self.peri.set_data_width(Width::Bits32, Width::Bits32); @@ -155,10 +214,10 @@ impl<'d, T: Instance> Cordic<'d, T> { // handle 2 input args calculation // - if arg2s.is_some() && !arg2s.expect("It's infailable").is_empty() { - let arg2s = arg2s.expect("It's infailable"); + if arg2s.is_some() && !arg2s.unwrap().is_empty() { + let arg2s = arg2s.unwrap(); - self.peri.set_argument_count(Count::Two); + self.peri.set_argument_count(AccessCount::Two); // Skip 1st value from arg1s, this value will be manually "preload" to cordic, to make use of cordic preload function. // And we preserve last value from arg2s, since it need to manually write to cordic, and read the result out. @@ -191,7 +250,7 @@ impl<'d, T: Instance> Cordic<'d, T> { let input_left = &arg1s[consumed_input_len..]; if !input_left.is_empty() { - self.peri.set_argument_count(Count::One); + self.peri.set_argument_count(AccessCount::One); // "preload" value to cordic (at this moment, cordic start to calculating) self.blocking_write_f64(input_left[0]); @@ -207,7 +266,7 @@ impl<'d, T: Instance> Cordic<'d, T> { self.blocking_read_f64_to_buf(output, &mut output_count); } - output_count + Ok(output_count) } fn blocking_read_f64_to_buf(&mut self, result_buf: &mut [f64], result_index: &mut usize) { @@ -216,7 +275,7 @@ impl<'d, T: Instance> Cordic<'d, T> { // We don't care about whether the function return 1 or 2 results, // the only thing matter is whether user want 1 or 2 results. - if !self.config.first_result { + if !self.config.res1_only { result_buf[*result_index] = utils::q1_31_to_f64(self.peri.read_result()); *result_index += 1; } @@ -234,27 +293,28 @@ impl<'d, T: Instance> Cordic<'d, T> { arg1s: &[f64], arg2s: Option<&[f64]>, output: &mut [f64], - ) -> usize { + ) -> Result { if arg1s.is_empty() { - return 0; + return Ok(0); } - assert!( - match self.config.first_result { - true => output.len() >= arg1s.len(), - false => output.len() >= 2 * arg1s.len(), - }, - "Output buf length is not long enough" - ); + let output_length_enough = match self.config.res1_only { + true => output.len() >= arg1s.len(), + false => output.len() >= 2 * arg1s.len(), + }; - self.check_input_f64(arg1s, arg2s); + if !output_length_enough { + return Err(CordicError::OutputLengthNotEnough); + } + + self.check_input_f64(arg1s, arg2s)?; into_ref!(write_dma, read_dma); - self.peri.set_result_count(if self.config.first_result { - Count::One + self.peri.set_result_count(if self.config.res1_only { + AccessCount::One } else { - Count::Two + AccessCount::Two }); self.peri.set_data_width(Width::Bits32, Width::Bits32); @@ -269,9 +329,9 @@ impl<'d, T: Instance> Cordic<'d, T> { // if !arg2s.unwrap_or_default().is_empty() { - let arg2s = arg2s.expect("It's infailable"); + let arg2s = arg2s.unwrap(); - self.peri.set_argument_count(Count::Two); + self.peri.set_argument_count(AccessCount::Two); let double_input = arg1s.iter().zip(arg2s); @@ -320,7 +380,7 @@ impl<'d, T: Instance> Cordic<'d, T> { if arg1s.len() > consumed_input_len { let input_remain = &arg1s[consumed_input_len..]; - self.peri.set_argument_count(Count::One); + self.peri.set_argument_count(AccessCount::One); for &arg in input_remain { input_buf[input_buf_len] = utils::f64_to_q1_31(arg); @@ -356,7 +416,7 @@ impl<'d, T: Instance> Cordic<'d, T> { } } - output_count + Ok(output_count) } // this function is highly coupled with async_calc_32bit, and is not intended to use in other place @@ -369,11 +429,6 @@ impl<'d, T: Instance> Cordic<'d, T> { output: &mut [f64], // caller uses should this as a final output array output_start_index: &mut usize, // the index of start point of the output for this round of calculation ) { - into_ref!(write_dma, read_dma); - - let write_req = write_dma.request(); - let read_req = read_dma.request(); - // output_buf is the place to store raw value from CORDIC (via DMA). // For buf size, we assume in this round of calculation: // all input is 1 arg, and all calculation need 2 output, @@ -381,7 +436,7 @@ impl<'d, T: Instance> Cordic<'d, T> { let mut output_buf = [0u32; INPUT_BUF_MAX_LEN * 2]; let mut output_buf_size = input_buf.len(); - if !self.config.first_result { + if !self.config.res1_only { // if we need 2 result for 1 input, then output_buf length should be 2x long. output_buf_size *= 2; }; @@ -392,35 +447,8 @@ impl<'d, T: Instance> Cordic<'d, T> { let active_output_buf = &mut output_buf[..output_buf_size]; - self.peri.enable_write_dma(); - self.peri.enable_read_dma(); - - let on_drop = OnDrop::new(|| { - self.peri.disable_write_dma(); - self.peri.disable_read_dma(); - }); - - unsafe { - let write_transfer = dma::Transfer::new_write( - &mut write_dma, - write_req, - input_buf, - T::regs().wdata().as_ptr() as *mut _, - Default::default(), - ); - - let read_transfer = dma::Transfer::new_read( - &mut read_dma, - read_req, - T::regs().rdata().as_ptr() as *mut _, - active_output_buf, - Default::default(), - ); - - embassy_futures::join::join(write_transfer, read_transfer).await; - } - - drop(on_drop); + self.launch_a_dma_transfer(write_dma, read_dma, input_buf, active_output_buf) + .await; for &mut output_u32 in active_output_buf { output[*output_start_index] = utils::q1_31_to_f64(output_u32); @@ -432,24 +460,30 @@ impl<'d, T: Instance> Cordic<'d, T> { // q1.15 related impl<'d, T: Instance> Cordic<'d, T> { /// Run a blocking CORDIC calculation in q1.15 format - pub fn blocking_calc_16bit(&mut self, arg1s: &[f32], arg2s: Option<&[f32]>, output: &mut [f32]) -> usize { + pub fn blocking_calc_16bit( + &mut self, + arg1s: &[f32], + arg2s: Option<&[f32]>, + output: &mut [f32], + ) -> Result { if arg1s.is_empty() { - return 0; + return Ok(0); } - assert!( - match self.config.first_result { - true => output.len() >= arg1s.len(), - false => output.len() >= 2 * arg1s.len(), - }, - "Output buf length is not long enough" - ); + let output_length_enough = match self.config.res1_only { + true => output.len() >= arg1s.len(), + false => output.len() >= 2 * arg1s.len(), + }; - self.check_input_f32(arg1s, arg2s); + if !output_length_enough { + return Err(CordicError::OutputLengthNotEnough); + } + + self.check_input_f32(arg1s, arg2s)?; // In q1.15 mode, 1 write/read to access 2 arguments/results - self.peri.set_argument_count(Count::One); - self.peri.set_result_count(Count::One); + self.peri.set_argument_count(AccessCount::One); + self.peri.set_result_count(AccessCount::One); self.peri.set_data_width(Width::Bits16, Width::Bits16); @@ -472,9 +506,7 @@ impl<'d, T: Instance> Cordic<'d, T> { .chain(core::iter::repeat(&arg2_default_value)), ); - let (&arg1, &arg2) = args - .next() - .expect("This should be infallible, since arg1s is not empty"); + let (&arg1, &arg2) = args.next().unwrap(); // preloading 1 pair of arguments self.blocking_write_f32(arg1, arg2); @@ -487,7 +519,7 @@ impl<'d, T: Instance> Cordic<'d, T> { // read last pair of value from cordic self.blocking_read_f32_to_buf(output, &mut output_count); - output_count + Ok(output_count) } fn blocking_write_f32(&mut self, arg1: f32, arg2: f32) { @@ -505,7 +537,7 @@ impl<'d, T: Instance> Cordic<'d, T> { // We don't care about whether the function return 1 or 2 results, // the only thing matter is whether user want 1 or 2 results. - if !self.config.first_result { + if !self.config.res1_only { result_buf[*result_index] = res2; *result_index += 1; } @@ -519,26 +551,27 @@ impl<'d, T: Instance> Cordic<'d, T> { arg1s: &[f32], arg2s: Option<&[f32]>, output: &mut [f32], - ) -> usize { + ) -> Result { if arg1s.is_empty() { - return 0; + return Ok(0); } - assert!( - match self.config.first_result { - true => output.len() >= arg1s.len(), - false => output.len() >= 2 * arg1s.len(), - }, - "Output buf length is not long enough" - ); + let output_length_enough = match self.config.res1_only { + true => output.len() >= arg1s.len(), + false => output.len() >= 2 * arg1s.len(), + }; - self.check_input_f32(arg1s, arg2s); + if !output_length_enough { + return Err(CordicError::OutputLengthNotEnough); + } + + self.check_input_f32(arg1s, arg2s)?; into_ref!(write_dma, read_dma); // In q1.15 mode, 1 write/read to access 2 arguments/results - self.peri.set_argument_count(Count::One); - self.peri.set_result_count(Count::One); + self.peri.set_argument_count(AccessCount::One); + self.peri.set_result_count(AccessCount::One); self.peri.set_data_width(Width::Bits16, Width::Bits16); @@ -584,7 +617,7 @@ impl<'d, T: Instance> Cordic<'d, T> { .await; } - output_count + Ok(output_count) } // this function is highly coupled with async_calc_16bit, and is not intended to use in other place @@ -596,45 +629,13 @@ impl<'d, T: Instance> Cordic<'d, T> { output: &mut [f32], // caller uses should this as a final output array output_start_index: &mut usize, // the index of start point of the output for this round of calculation ) { - into_ref!(write_dma, read_dma); - - let write_req = write_dma.request(); - let read_req = read_dma.request(); - // output_buf is the place to store raw value from CORDIC (via DMA). let mut output_buf = [0u32; INPUT_BUF_MAX_LEN]; let active_output_buf = &mut output_buf[..input_buf.len()]; - self.peri.enable_write_dma(); - self.peri.enable_read_dma(); - - let on_drop = OnDrop::new(|| { - self.peri.disable_write_dma(); - self.peri.disable_read_dma(); - }); - - unsafe { - let write_transfer = dma::Transfer::new_write( - &mut write_dma, - write_req, - input_buf, - T::regs().wdata().as_ptr() as *mut _, - Default::default(), - ); - - let read_transfer = dma::Transfer::new_read( - &mut read_dma, - read_req, - T::regs().rdata().as_ptr() as *mut _, - active_output_buf, - Default::default(), - ); - - embassy_futures::join::join(write_transfer, read_transfer).await; - } - - drop(on_drop); + self.launch_a_dma_transfer(write_dma, read_dma, input_buf, active_output_buf) + .await; for &mut output_u32 in active_output_buf { let (res1, res2) = utils::u32_to_f32_res(output_u32); @@ -642,7 +643,7 @@ impl<'d, T: Instance> Cordic<'d, T> { output[*output_start_index] = res1; *output_start_index += 1; - if !self.config.first_result { + if !self.config.res1_only { output[*output_start_index] = res2; *output_start_index += 1; } @@ -654,104 +655,131 @@ impl<'d, T: Instance> Cordic<'d, T> { macro_rules! check_input_value { ($func_name:ident, $float_type:ty) => { impl<'d, T: Instance> Cordic<'d, T> { - fn $func_name(&self, arg1s: &[$float_type], arg2s: Option<&[$float_type]>) { + fn $func_name(&self, arg1s: &[$float_type], arg2s: Option<&[$float_type]>) -> Result<(), CordicError> { let config = &self.config; use Function::*; - // check SCALE value - match config.function { - Cos | Sin | Phase | Modulus => assert!(Scale::A1_R1 == config.scale, "SCALE should be 0"), - Arctan => assert!( - (0..=7).contains(&(config.scale as u8)), - "SCALE should be: 0 <= SCALE <= 7" - ), - Cosh | Sinh | Arctanh => assert!(Scale::A1o2_R2 == config.scale, "SCALE should be 1"), - - Ln => assert!( - (1..=4).contains(&(config.scale as u8)), - "SCALE should be: 1 <= SCALE <= 4" - ), - Sqrt => assert!( - (0..=2).contains(&(config.scale as u8)), - "SCALE should be: 0 <= SCALE <= 2" - ), + struct Arg1ErrInfo { + scale: Option, + range: [f32; 2], + inclusive_upper_bound: bool, } // check ARG1 value - match config.function { - Cos | Sin | Phase | Modulus | Arctan => { - assert!( - arg1s.iter().all(|v| (-1.0..=1.0).contains(v)), - "ARG1 should be: -1 <= ARG1 <= 1" - ); + let err_info = match config.function { + Cos | Sin | Phase | Modulus | Arctan if arg1s.iter().any(|v| !(-1.0..=1.0).contains(v)) => { + Some(Arg1ErrInfo { + scale: None, + range: [-1.0, 1.0], + inclusive_upper_bound: true, + }) } - Cosh | Sinh => assert!( - arg1s.iter().all(|v| (-0.559..=0.559).contains(v)), - "ARG1 should be: -0.559 <= ARG1 <= 0.559" - ), + Cosh | Sinh if arg1s.iter().any(|v| !(-0.559..=0.559).contains(v)) => Some(Arg1ErrInfo { + scale: None, + range: [-0.559, 0.559], + inclusive_upper_bound: true, + }), - Arctanh => assert!( - arg1s.iter().all(|v| (-0.403..=0.403).contains(v)), - "ARG1 should be: -0.403 <= ARG1 <= 0.403" - ), + Arctanh if arg1s.iter().any(|v| !(-0.403..=0.403).contains(v)) => Some(Arg1ErrInfo { + scale: None, + range: [-0.403, 0.403], + inclusive_upper_bound: true, + }), - Ln => { - match config.scale { - Scale::A1o2_R2 => assert!( - arg1s.iter().all(|v| (0.05354..0.5).contains(v)), - "When SCALE set to 1, ARG1 should be: 0.05354 <= ARG1 < 0.5" - ), - Scale::A1o4_R4 => assert!( - arg1s.iter().all(|v| (0.25..0.75).contains(v)), - "When SCALE set to 2, ARG1 should be: 0.25 <= ARG1 < 0.75" - ), - Scale::A1o8_R8 => assert!( - arg1s.iter().all(|v| (0.375..0.875).contains(v)), - "When SCALE set to 3, ARG1 should be: 0.375 <= ARG1 < 0.875" - ), - Scale::A1o16_R16 => assert!( - arg1s.iter().all(|v| (0.4375..0.584).contains(v)), - "When SCALE set to 4, ARG1 should be: 0.4375 <= ARG1 < 0.584" - ), - _ => unreachable!(), - }; - } + Ln => match config.scale { + Scale::Arg1o2Res2 if arg1s.iter().any(|v| !(0.0535..0.5).contains(v)) => Some(Arg1ErrInfo { + scale: Some(Scale::Arg1o2Res2), + range: [0.0535, 0.5], + inclusive_upper_bound: false, + }), + Scale::Arg1o4Res4 if arg1s.iter().any(|v| !(0.25..0.75).contains(v)) => Some(Arg1ErrInfo { + scale: Some(Scale::Arg1o4Res4), + range: [0.25, 0.75], + inclusive_upper_bound: false, + }), + Scale::Arg1o8Res8 if arg1s.iter().any(|v| !(0.375..0.875).contains(v)) => Some(Arg1ErrInfo { + scale: Some(Scale::Arg1o8Res8), + range: [0.375, 0.875], + inclusive_upper_bound: false, + }), + Scale::Arg1o16Res16 if arg1s.iter().any(|v| !(0.4375..0.584).contains(v)) => { + Some(Arg1ErrInfo { + scale: Some(Scale::Arg1o16Res16), + range: [0.4375, 0.584], + inclusive_upper_bound: false, + }) + } + + Scale::Arg1o2Res2 | Scale::Arg1o4Res4 | Scale::Arg1o8Res8 | Scale::Arg1o16Res16 => None, - Sqrt => match config.scale { - Scale::A1_R1 => assert!( - arg1s.iter().all(|v| (0.027..0.75).contains(v)), - "When SCALE set to 0, ARG1 should be: 0.027 <= ARG1 < 0.75" - ), - Scale::A1o2_R2 => assert!( - arg1s.iter().all(|v| (0.375..0.875).contains(v)), - "When SCALE set to 1, ARG1 should be: 0.375 <= ARG1 < 0.875" - ), - Scale::A1o4_R4 => assert!( - arg1s.iter().all(|v| (0.4375..0.585).contains(v)), - "When SCALE set to 2, ARG1 should be: 0.4375 <= ARG1 < 0.585" - ), _ => unreachable!(), }, + + Sqrt => match config.scale { + Scale::Arg1Res1 if arg1s.iter().any(|v| !(0.027..0.75).contains(v)) => Some(Arg1ErrInfo { + scale: Some(Scale::Arg1Res1), + range: [0.027, 0.75], + inclusive_upper_bound: false, + }), + Scale::Arg1o2Res2 if arg1s.iter().any(|v| !(0.375..0.875).contains(v)) => Some(Arg1ErrInfo { + scale: Some(Scale::Arg1o2Res2), + range: [0.375, 0.875], + inclusive_upper_bound: false, + }), + Scale::Arg1o4Res4 if arg1s.iter().any(|v| !(0.4375..0.584).contains(v)) => Some(Arg1ErrInfo { + scale: Some(Scale::Arg1o4Res4), + range: [0.4375, 0.584], + inclusive_upper_bound: false, + }), + Scale::Arg1Res1 | Scale::Arg1o2Res2 | Scale::Arg1o4Res4 => None, + _ => unreachable!(), + }, + + Cos | Sin | Phase | Modulus | Arctan | Cosh | Sinh | Arctanh => None, + }; + + if let Some(err) = err_info { + return Err(CordicError::ArgError(ArgError { + func: config.function, + scale: err.scale, + arg_range: err.range, + inclusive_upper_bound: err.inclusive_upper_bound, + arg_type: ArgType::Arg1, + })); } // check ARG2 value if let Some(arg2s) = arg2s { - match config.function { - Cos | Sin => assert!( - arg2s.iter().all(|v| (0.0..=1.0).contains(v)), - "ARG2 should be: 0 <= ARG2 <= 1" - ), + struct Arg2ErrInfo { + range: [f32; 2], + } - Phase | Modulus => assert!( - arg2s.iter().all(|v| (-1.0..=1.0).contains(v)), - "ARG2 should be: -1 <= ARG2 <= 1" - ), + let err_info = match config.function { + Cos | Sin if arg2s.iter().any(|v| !(0.0..=1.0).contains(v)) => { + Some(Arg2ErrInfo { range: [0.0, 1.0] }) + } - _ => (), + Phase | Modulus if arg2s.iter().any(|v| !(-1.0..=1.0).contains(v)) => { + Some(Arg2ErrInfo { range: [-1.0, 1.0] }) + } + + Cos | Sin | Phase | Modulus | Arctan | Cosh | Sinh | Arctanh | Ln | Sqrt => None, + }; + + if let Some(err) = err_info { + return Err(CordicError::ArgError(ArgError { + func: config.function, + scale: None, + arg_range: err.range, + inclusive_upper_bound: true, + arg_type: ArgType::Arg2, + })); } } + + Ok(()) } } }; diff --git a/embassy-stm32/src/cordic/sealed.rs b/embassy-stm32/src/cordic/sealed.rs index 0f00e380c..f9521ff7a 100644 --- a/embassy-stm32/src/cordic/sealed.rs +++ b/embassy-stm32/src/cordic/sealed.rs @@ -66,21 +66,21 @@ pub trait Instance { } /// Set NARGS value - fn set_argument_count(&self, n: Count) { + fn set_argument_count(&self, n: AccessCount) { Self::regs().csr().modify(|v| { v.set_nargs(match n { - Count::One => vals::Num::NUM1, - Count::Two => vals::Num::NUM2, + AccessCount::One => vals::Num::NUM1, + AccessCount::Two => vals::Num::NUM2, }) }) } /// Set NRES value - fn set_result_count(&self, n: Count) { + fn set_result_count(&self, n: AccessCount) { Self::regs().csr().modify(|v| { v.set_nres(match n { - Count::One => vals::Num::NUM1, - Count::Two => vals::Num::NUM2, + AccessCount::One => vals::Num::NUM1, + AccessCount::Two => vals::Num::NUM2, }); }) } From c42d9f9eaae546faae46c4d1121f1fbc393c2073 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Thu, 21 Mar 2024 13:25:40 +0800 Subject: [PATCH 18/69] stm32 CORDIC: bug fix --- embassy-stm32/src/cordic/errors.rs | 76 +++++++++++++++++++++++------- embassy-stm32/src/cordic/mod.rs | 53 ++++++++++----------- embassy-stm32/src/cordic/utils.rs | 43 +++++++++-------- 3 files changed, 109 insertions(+), 63 deletions(-) diff --git a/embassy-stm32/src/cordic/errors.rs b/embassy-stm32/src/cordic/errors.rs index d0b2dc618..2c0aca4a2 100644 --- a/embassy-stm32/src/cordic/errors.rs +++ b/embassy-stm32/src/cordic/errors.rs @@ -9,6 +9,26 @@ pub enum CordicError { ArgError(ArgError), /// Output buffer length error OutputLengthNotEnough, + /// Input value is out of range for Q1.x format + NumberOutOfRange(NumberOutOfRange), +} + +impl From for CordicError { + fn from(value: ConfigError) -> Self { + Self::ConfigError(value) + } +} + +impl From for CordicError { + fn from(value: ArgError) -> Self { + Self::ArgError(value) + } +} + +impl From for CordicError { + fn from(value: NumberOutOfRange) -> Self { + Self::NumberOutOfRange(value) + } } #[cfg(feature = "defmt")] @@ -19,6 +39,7 @@ impl defmt::Format for CordicError { match self { ConfigError(e) => defmt::write!(fmt, "{}", e), ArgError(e) => defmt::write!(fmt, "{}", e), + NumberOutOfRange(e) => defmt::write!(fmt, "{}", e), OutputLengthNotEnough => defmt::write!(fmt, "Output buffer length is not long enough"), } } @@ -68,28 +89,51 @@ impl defmt::Format for ArgError { defmt::write!(fmt, " when SCALE is {},", scale); } - let arg_string = match self.arg_type { - ArgType::Arg1 => "ARG1", - ArgType::Arg2 => "ARG2", + defmt::write!(fmt, " {} should be", self.arg_type); + + if self.inclusive_upper_bound { + defmt::write!( + fmt, + " {} <= {} <= {}", + self.arg_range[0], + self.arg_type, + self.arg_range[1] + ) + } else { + defmt::write!( + fmt, + " {} <= {} < {}", + self.arg_range[0], + self.arg_type, + self.arg_range[1] + ) }; - - defmt::write!(fmt, " {} should be", arg_string); - - let inclusive_string = if self.inclusive_upper_bound { "=" } else { "" }; - - defmt::write!( - fmt, - " {} <= {} <{} {}", - self.arg_range[0], - arg_string, - inclusive_string, - self.arg_range[1] - ) } } #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub(super) enum ArgType { Arg1, Arg2, } + +/// Input value is out of range for Q1.x format +#[allow(missing_docs)] +#[derive(Debug)] +pub enum NumberOutOfRange { + BelowLowerBound, + AboveUpperBound, +} + +#[cfg(feature = "defmt")] +impl defmt::Format for NumberOutOfRange { + fn format(&self, fmt: defmt::Formatter) { + use NumberOutOfRange::*; + + match self { + BelowLowerBound => defmt::write!(fmt, "input value should be equal or greater than -1"), + AboveUpperBound => defmt::write!(fmt, "input value should be equal or less than 1"), + } + } +} diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index 5ac9addd8..b0db3f060 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs @@ -56,7 +56,7 @@ impl Config { Ok(config) } - fn check_scale(&self) -> Result<(), CordicError> { + fn check_scale(&self) -> Result<(), ConfigError> { use Function::*; let scale_raw = self.scale as u8; @@ -76,10 +76,10 @@ impl Config { }; if let Some(range) = err_range { - Err(CordicError::ConfigError(ConfigError { + Err(ConfigError { func: self.function, scale_range: range, - })) + }) } else { Ok(()) } @@ -226,20 +226,20 @@ impl<'d, T: Instance> Cordic<'d, T> { consumed_input_len = double_input.len() + 1; // preload first value from arg1 to cordic - self.blocking_write_f64(arg1s[0]); + self.blocking_write_f64(arg1s[0])?; for (&arg1, &arg2) in double_input { // Since we manually preload a value before, // we will write arg2 (from the actual last pair) first, (at this moment, cordic start to calculating,) // and write arg1 (from the actual next pair), then read the result, to "keep preloading" - self.blocking_write_f64(arg2); - self.blocking_write_f64(arg1); + self.blocking_write_f64(arg2)?; + self.blocking_write_f64(arg1)?; self.blocking_read_f64_to_buf(output, &mut output_count); } // write last input value from arg2s, then read out the result - self.blocking_write_f64(arg2s[arg2s.len() - 1]); + self.blocking_write_f64(arg2s[arg2s.len() - 1])?; self.blocking_read_f64_to_buf(output, &mut output_count); } @@ -253,12 +253,12 @@ impl<'d, T: Instance> Cordic<'d, T> { self.peri.set_argument_count(AccessCount::One); // "preload" value to cordic (at this moment, cordic start to calculating) - self.blocking_write_f64(input_left[0]); + self.blocking_write_f64(input_left[0])?; for &arg in input_left.iter().skip(1) { // this line write arg for next round caculation to cordic, // and read result from last round - self.blocking_write_f64(arg); + self.blocking_write_f64(arg)?; self.blocking_read_f64_to_buf(output, &mut output_count); } @@ -281,8 +281,9 @@ impl<'d, T: Instance> Cordic<'d, T> { } } - fn blocking_write_f64(&mut self, arg: f64) { - self.peri.write_argument(utils::f64_to_q1_31(arg)); + fn blocking_write_f64(&mut self, arg: f64) -> Result<(), NumberOutOfRange> { + self.peri.write_argument(utils::f64_to_q1_31(arg)?); + Ok(()) } /// Run a async CORDIC calculation in q.1.31 format @@ -339,7 +340,7 @@ impl<'d, T: Instance> Cordic<'d, T> { for (&arg1, &arg2) in double_input { for &arg in [arg1, arg2].iter() { - input_buf[input_buf_len] = utils::f64_to_q1_31(arg); + input_buf[input_buf_len] = utils::f64_to_q1_31(arg)?; input_buf_len += 1; } @@ -383,7 +384,7 @@ impl<'d, T: Instance> Cordic<'d, T> { self.peri.set_argument_count(AccessCount::One); for &arg in input_remain { - input_buf[input_buf_len] = utils::f64_to_q1_31(arg); + input_buf[input_buf_len] = utils::f64_to_q1_31(arg)?; input_buf_len += 1; if input_buf_len == INPUT_BUF_MAX_LEN { @@ -509,10 +510,10 @@ impl<'d, T: Instance> Cordic<'d, T> { let (&arg1, &arg2) = args.next().unwrap(); // preloading 1 pair of arguments - self.blocking_write_f32(arg1, arg2); + self.blocking_write_f32(arg1, arg2)?; for (&arg1, &arg2) in args { - self.blocking_write_f32(arg1, arg2); + self.blocking_write_f32(arg1, arg2)?; self.blocking_read_f32_to_buf(output, &mut output_count); } @@ -522,15 +523,13 @@ impl<'d, T: Instance> Cordic<'d, T> { Ok(output_count) } - fn blocking_write_f32(&mut self, arg1: f32, arg2: f32) { - let reg_value: u32 = utils::f32_args_to_u32(arg1, arg2); - self.peri.write_argument(reg_value); + fn blocking_write_f32(&mut self, arg1: f32, arg2: f32) -> Result<(), NumberOutOfRange> { + self.peri.write_argument(utils::f32_args_to_u32(arg1, arg2)?); + Ok(()) } fn blocking_read_f32_to_buf(&mut self, result_buf: &mut [f32], result_index: &mut usize) { - let reg_value = self.peri.read_result(); - - let (res1, res2) = utils::u32_to_f32_res(reg_value); + let (res1, res2) = utils::u32_to_f32_res(self.peri.read_result()); result_buf[*result_index] = res1; *result_index += 1; @@ -597,7 +596,7 @@ impl<'d, T: Instance> Cordic<'d, T> { ); for (&arg1, &arg2) in args { - input_buf[input_buf_len] = utils::f32_args_to_u32(arg1, arg2); + input_buf[input_buf_len] = utils::f32_args_to_u32(arg1, arg2)?; input_buf_len += 1; if input_buf_len == INPUT_BUF_MAX_LEN { @@ -655,7 +654,7 @@ impl<'d, T: Instance> Cordic<'d, T> { macro_rules! check_input_value { ($func_name:ident, $float_type:ty) => { impl<'d, T: Instance> Cordic<'d, T> { - fn $func_name(&self, arg1s: &[$float_type], arg2s: Option<&[$float_type]>) -> Result<(), CordicError> { + fn $func_name(&self, arg1s: &[$float_type], arg2s: Option<&[$float_type]>) -> Result<(), ArgError> { let config = &self.config; use Function::*; @@ -741,13 +740,13 @@ macro_rules! check_input_value { }; if let Some(err) = err_info { - return Err(CordicError::ArgError(ArgError { + return Err(ArgError { func: config.function, scale: err.scale, arg_range: err.range, inclusive_upper_bound: err.inclusive_upper_bound, arg_type: ArgType::Arg1, - })); + }); } // check ARG2 value @@ -769,13 +768,13 @@ macro_rules! check_input_value { }; if let Some(err) = err_info { - return Err(CordicError::ArgError(ArgError { + return Err(ArgError { func: config.function, scale: None, arg_range: err.range, inclusive_upper_bound: true, arg_type: ArgType::Arg2, - })); + }); } } diff --git a/embassy-stm32/src/cordic/utils.rs b/embassy-stm32/src/cordic/utils.rs index 79bef6b97..3c3ed224f 100644 --- a/embassy-stm32/src/cordic/utils.rs +++ b/embassy-stm32/src/cordic/utils.rs @@ -1,39 +1,42 @@ //! Common match utils +use super::errors::NumberOutOfRange; macro_rules! floating_fixed_convert { ($f_to_q:ident, $q_to_f:ident, $unsigned_bin_typ:ty, $signed_bin_typ:ty, $float_ty:ty, $offset:literal, $min_positive:literal) => { /// convert float point to fixed point format - pub(crate) fn $f_to_q(value: $float_ty) -> $unsigned_bin_typ { + pub fn $f_to_q(value: $float_ty) -> Result<$unsigned_bin_typ, NumberOutOfRange> { const MIN_POSITIVE: $float_ty = unsafe { core::mem::transmute($min_positive) }; - assert!( - (-1.0 as $float_ty) <= value, - "input value {} should be equal or greater than -1", - value - ); + if value < -1.0 { + return Err(NumberOutOfRange::BelowLowerBound) + } + + if value > 1.0 { + return Err(NumberOutOfRange::AboveUpperBound) + } - let value = if value == 1.0 as $float_ty{ - // make a exception for user specifing exact 1.0 float point, - // convert 1.0 to max representable value of q1.x format + let value = if 1.0 - MIN_POSITIVE < value && value <= 1.0 { + // make a exception for value between (1.0^{-x} , 1.0] float point, + // convert it to max representable value of q1.x format (1.0 as $float_ty) - MIN_POSITIVE } else { - assert!( - value <= (1.0 as $float_ty) - MIN_POSITIVE, - "input value {} should be equal or less than 1-2^(-{})", - value, $offset - ); value }; - (value * ((1 as $unsigned_bin_typ << $offset) as $float_ty)) as $unsigned_bin_typ + // It's necessary to cast the float value to signed integer, before convert it to a unsigned value. + // Since value from register is actually a "signed value", a "as" cast will keep original binary format but mark it as unsgined value. + // see https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast + Ok((value * ((1 as $unsigned_bin_typ << $offset) as $float_ty)) as $signed_bin_typ as $unsigned_bin_typ) } #[inline(always)] /// convert fixed point to float point format - pub(crate) fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty { - // It's needed to convert from unsigned to signed first, for correct result. - -(value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty) + pub fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty { + // It's necessary to cast the unsigned integer to signed integer, before convert it to a float value. + // Since value from register is actually a "signed value", a "as" cast will keep original binary format but mark it as signed value. + // see https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast + (value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty) } }; } @@ -59,8 +62,8 @@ floating_fixed_convert!( ); #[inline(always)] -pub(crate) fn f32_args_to_u32(arg1: f32, arg2: f32) -> u32 { - f32_to_q1_15(arg1) as u32 + ((f32_to_q1_15(arg2) as u32) << 16) +pub(crate) fn f32_args_to_u32(arg1: f32, arg2: f32) -> Result { + Ok(f32_to_q1_15(arg1)? as u32 + ((f32_to_q1_15(arg2)? as u32) << 16)) } #[inline(always)] From 0d065ab2d658ebfad0c6e4bba562e474d6ca1012 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Thu, 21 Mar 2024 16:06:34 +0800 Subject: [PATCH 19/69] stm32 CORDIC: add HIL test --- embassy-stm32/src/cordic/errors.rs | 2 + tests/stm32/Cargo.toml | 13 ++- tests/stm32/gen_test.py | 2 +- tests/stm32/src/bin/cordic.rs | 152 +++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 tests/stm32/src/bin/cordic.rs diff --git a/embassy-stm32/src/cordic/errors.rs b/embassy-stm32/src/cordic/errors.rs index 2c0aca4a2..9020d8467 100644 --- a/embassy-stm32/src/cordic/errors.rs +++ b/embassy-stm32/src/cordic/errors.rs @@ -46,6 +46,7 @@ impl defmt::Format for CordicError { } /// Error dring parsing [Cordic::Config](super::Config) +#[allow(dead_code)] #[derive(Debug)] pub struct ConfigError { pub(super) func: Function, @@ -71,6 +72,7 @@ impl defmt::Format for ConfigError { } /// Error on checking input arguments +#[allow(dead_code)] #[derive(Debug)] pub struct ArgError { pub(super) func: Function, diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index e42470004..345c72a03 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -15,7 +15,7 @@ stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma" stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"] stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac", "ucpd"] stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan"] -stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "hash"] +stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "hash", "cordic"] stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash", "cryp"] stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash", "cryp"] stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng", "fdcan"] @@ -25,8 +25,8 @@ stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"] stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash"] stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"] stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"] -stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash"] -stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng", "hash"] +stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash", "cordic"] +stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng", "hash", "cordic"] stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"] stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng", "hash"] stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] @@ -48,6 +48,7 @@ embassy-stm32-wpan = [] not-gpdma = [] dac = [] ucpd = [] +cordic = ["dep:num-traits"] cm0 = ["portable-atomic/unsafe-assume-single-core"] @@ -83,6 +84,7 @@ chrono = { version = "^0.4", default-features = false, optional = true} sha2 = { version = "0.10.8", default-features = false } hmac = "0.12.1" aes-gcm = {version = "0.10.3", default-features = false, features = ["aes", "heapless"] } +num-traits = {version="0.2", default-features = false,features = ["libm"], optional = true} # BEGIN TESTS # Generated by gen_test.py. DO NOT EDIT. @@ -91,6 +93,11 @@ name = "can" path = "src/bin/can.rs" required-features = [ "can",] +[[bin]] +name = "cordic" +path = "src/bin/cordic.rs" +required-features = [ "rng", "cordic",] + [[bin]] name = "cryp" path = "src/bin/cryp.rs" diff --git a/tests/stm32/gen_test.py b/tests/stm32/gen_test.py index 8ff156c0e..daf714376 100644 --- a/tests/stm32/gen_test.py +++ b/tests/stm32/gen_test.py @@ -14,7 +14,7 @@ for f in sorted(glob('./src/bin/*.rs')): with open(f, 'r') as f: for line in f: if line.startswith('// required-features:'): - features = line.split(':', 2)[1].strip().split(',') + features = [feature.strip() for feature in line.split(':', 2)[1].strip().split(',')] tests[name] = features diff --git a/tests/stm32/src/bin/cordic.rs b/tests/stm32/src/bin/cordic.rs new file mode 100644 index 000000000..b580cc79b --- /dev/null +++ b/tests/stm32/src/bin/cordic.rs @@ -0,0 +1,152 @@ +// required-features: rng, cordic + +// Test Cordic driver, with Q1.31 format, Sin function, at 24 iterations (aka PRECISION = 6), using DMA transfer + +// Only test on STM32H563ZI, STM32U585AI and STM32U5a5JI. +// STM32G491RE is not tested, since it memory.x has less memory size than it actually has, +// and the test seems use much memory than memory.x suggest. +// see https://github.com/embassy-rs/stm32-data/issues/301#issuecomment-1925412561 + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::{bind_interrupts, cordic, peripherals, rng}; +use num_traits::Float; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + +/* input value control, can be changed */ + +const ARG1_LENGTH: usize = 9; +const ARG2_LENGTH: usize = 4; // this might not be the exact length of ARG2, since ARG2 need to be inside [0, 1] + +const INPUT_Q1_31_LENGHT: usize = ARG1_LENGTH + ARG2_LENGTH; +const INPUT_U8_LENGTH: usize = 4 * INPUT_Q1_31_LENGHT; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let dp = embassy_stm32::init(Default::default()); + + // + // use RNG generate random Q1.31 value + // + // we don't generate floating-point value, since not all binary value are valid floating-point value, + // and Q1.31 only accept a fixed range of value. + + let mut rng = rng::Rng::new(dp.RNG, Irqs); + + let mut input_buf_u8 = [0u8; INPUT_U8_LENGTH]; + unwrap!(rng.async_fill_bytes(&mut input_buf_u8).await); + + // convert every [u8; 4] to a u32, for a Q1.31 value + let input_q1_31 = unsafe { core::mem::transmute::<[u8; INPUT_U8_LENGTH], [u32; INPUT_Q1_31_LENGHT]>(input_buf_u8) }; + + let mut input_f64_buf = [0f64; INPUT_Q1_31_LENGHT]; + + let mut cordic_output_f64_buf = [0f64; ARG1_LENGTH * 2]; + + // convert Q1.31 value back to f64, for software calculation verify + for (val_u32, val_f64) in input_q1_31.iter().zip(input_f64_buf.iter_mut()) { + *val_f64 = cordic::utils::q1_31_to_f64(*val_u32); + } + + let mut arg2_f64_buf = [0f64; ARG2_LENGTH]; + let mut arg2_f64_len = 0; + + // check if ARG2 is in range [0, 1] (limited by CORDIC peripheral with Sin mode) + for &arg2 in &input_f64_buf[ARG1_LENGTH..] { + if arg2 >= 0.0 { + arg2_f64_buf[arg2_f64_len] = arg2; + arg2_f64_len += 1; + } + } + + // the actal value feed to CORDIC + let arg1_f64_ls = &input_f64_buf[..ARG1_LENGTH]; + let arg2_f64_ls = &arg2_f64_buf[..arg2_f64_len]; + + let mut cordic = cordic::Cordic::new( + dp.CORDIC, + unwrap!(cordic::Config::new( + cordic::Function::Sin, + Default::default(), + Default::default(), + false, + )), + ); + + //#[cfg(feature = "stm32g491re")] + //let (mut write_dma, mut read_dma) = (dp.DMA1_CH4, dp.DMA1_CH5); + + #[cfg(any(feature = "stm32h563zi", feature = "stm32u585ai", feature = "stm32u5a5zj"))] + let (mut write_dma, mut read_dma) = (dp.GPDMA1_CH4, dp.GPDMA1_CH5); + + let cordic_start_point = embassy_time::Instant::now(); + + let cnt = unwrap!( + cordic + .async_calc_32bit( + &mut write_dma, + &mut read_dma, + arg1_f64_ls, + Some(arg2_f64_ls), + &mut cordic_output_f64_buf, + ) + .await + ); + + let cordic_end_point = embassy_time::Instant::now(); + + // since we get 2 output for 1 calculation, the output length should be ARG1_LENGTH * 2 + defmt::assert!(cnt == ARG1_LENGTH * 2); + + let mut software_output_f64_buf = [0f64; ARG1_LENGTH * 2]; + + // for software calc, if there is no ARG2 value, insert a 1.0 as value (the reset value for ARG2 in CORDIC) + let arg2_f64_ls = if arg2_f64_len == 0 { &[1.0] } else { arg2_f64_ls }; + + let software_inputs = arg1_f64_ls + .iter() + .zip( + arg2_f64_ls + .iter() + .chain(core::iter::repeat(&arg2_f64_ls[arg2_f64_ls.len() - 1])), + ) + .zip(software_output_f64_buf.chunks_mut(2)); + + let software_start_point = embassy_time::Instant::now(); + + for ((arg1, arg2), res) in software_inputs { + let (raw_res1, raw_res2) = (arg1 * core::f64::consts::PI).sin_cos(); + + (res[0], res[1]) = (raw_res1 * arg2, raw_res2 * arg2); + } + + let software_end_point = embassy_time::Instant::now(); + + for (cordic_res, software_res) in cordic_output_f64_buf[..cnt] + .chunks(2) + .zip(software_output_f64_buf.chunks(2)) + { + for (cord_res, soft_res) in cordic_res.iter().zip(software_res.iter()) { + defmt::assert!((cord_res - soft_res).abs() <= 2.0.powi(-19)); + } + } + + // This comparsion is just for fun. Since it not a equal compare: + // software use 64-bit floating point, but CORDIC use 32-bit fixed point. + trace!( + "calculate count: {}, Cordic time: {} us, software time: {} us", + ARG1_LENGTH, + (cordic_end_point - cordic_start_point).as_micros(), + (software_end_point - software_start_point).as_micros() + ); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From fac4f9aa2f67aa6c7f522a10a4c546434a39883e Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Fri, 22 Mar 2024 00:24:53 +0800 Subject: [PATCH 20/69] stm32 CORDIC: typo fix --- embassy-stm32/src/cordic/enums.rs | 2 +- embassy-stm32/src/cordic/errors.rs | 2 +- embassy-stm32/src/cordic/mod.rs | 18 +++++++++--------- embassy-stm32/src/cordic/utils.rs | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/embassy-stm32/src/cordic/enums.rs b/embassy-stm32/src/cordic/enums.rs index 4b92a6cf8..e8695fac7 100644 --- a/embassy-stm32/src/cordic/enums.rs +++ b/embassy-stm32/src/cordic/enums.rs @@ -25,7 +25,7 @@ pub enum Precision { Iters16, Iters20, #[default] - Iters24, // this value is recomended by Reference Manual + Iters24, // this value is recommended by Reference Manual Iters28, Iters32, Iters36, diff --git a/embassy-stm32/src/cordic/errors.rs b/embassy-stm32/src/cordic/errors.rs index 9020d8467..653014290 100644 --- a/embassy-stm32/src/cordic/errors.rs +++ b/embassy-stm32/src/cordic/errors.rs @@ -45,7 +45,7 @@ impl defmt::Format for CordicError { } } -/// Error dring parsing [Cordic::Config](super::Config) +/// Error during parsing [Cordic::Config](super::Config) #[allow(dead_code)] #[derive(Debug)] pub struct ConfigError { diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index b0db3f060..f12efe2eb 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs @@ -91,8 +91,8 @@ impl<'d, T: Instance> Cordic<'d, T> { /// Create a Cordic driver instance /// /// Note: - /// If you need a periperhal -> CORDIC -> peripehral mode, - /// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguemnts with [Self::extra_config] + /// If you need a peripheral -> CORDIC -> peripheral mode, + /// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguments with [Self::extra_config] pub fn new(peri: impl Peripheral

+ 'd, config: Config) -> Self { T::enable_and_reset(); @@ -123,7 +123,7 @@ impl<'d, T: Instance> Cordic<'d, T> { self.peri.set_scale(self.config.scale); // we don't set NRES in here, but to make sure NRES is set each time user call "calc"-ish functions, - // since each "calc"-ish functions can have different ARGSIZE and RESSIZE, thus NRES should be change accrodingly. + // since each "calc"-ish functions can have different ARGSIZE and RESSIZE, thus NRES should be change accordingly. } async fn launch_a_dma_transfer( @@ -256,7 +256,7 @@ impl<'d, T: Instance> Cordic<'d, T> { self.blocking_write_f64(input_left[0])?; for &arg in input_left.iter().skip(1) { - // this line write arg for next round caculation to cordic, + // this line write arg for next round calculation to cordic, // and read result from last round self.blocking_write_f64(arg)?; self.blocking_read_f64_to_buf(output, &mut output_count); @@ -426,8 +426,8 @@ impl<'d, T: Instance> Cordic<'d, T> { write_dma: impl Peripheral

>, read_dma: impl Peripheral

>, double_input: bool, // gether extra info to calc output_buf size - input_buf: &[u32], // input_buf, its content should be extact values and length for calculation - output: &mut [f64], // caller uses should this as a final output array + input_buf: &[u32], // input_buf, its content should be exact length for calculation + output: &mut [f64], // caller should uses this buf as a final output array output_start_index: &mut usize, // the index of start point of the output for this round of calculation ) { // output_buf is the place to store raw value from CORDIC (via DMA). @@ -581,7 +581,7 @@ impl<'d, T: Instance> Cordic<'d, T> { // In q1.15 mode, we always fill 1 pair of 16bit value into WDATA register. // If arg2s is None or empty array, we assume arg2 value always 1.0 (as reset value for ARG2). // If arg2s has some value, and but not as long as arg1s, - // we fill the reset of arg2 values with last value from arg2s (as q1.31 version does) + // we fill the reset of arg2 values with last value from arg2s (as CORDIC behavior on q1.31 format) let arg2_default_value = match arg2s { Some(arg2s) if !arg2s.is_empty() => arg2s[arg2s.len() - 1], @@ -624,8 +624,8 @@ impl<'d, T: Instance> Cordic<'d, T> { &mut self, write_dma: impl Peripheral

>, read_dma: impl Peripheral

>, - input_buf: &[u32], // input_buf, its content should be extact values and length for calculation - output: &mut [f32], // caller uses should this as a final output array + input_buf: &[u32], // input_buf, its content should be exact length for calculation + output: &mut [f32], // caller should uses this buf as a final output array output_start_index: &mut usize, // the index of start point of the output for this round of calculation ) { // output_buf is the place to store raw value from CORDIC (via DMA). diff --git a/embassy-stm32/src/cordic/utils.rs b/embassy-stm32/src/cordic/utils.rs index 3c3ed224f..41821d6e2 100644 --- a/embassy-stm32/src/cordic/utils.rs +++ b/embassy-stm32/src/cordic/utils.rs @@ -25,7 +25,7 @@ macro_rules! floating_fixed_convert { }; // It's necessary to cast the float value to signed integer, before convert it to a unsigned value. - // Since value from register is actually a "signed value", a "as" cast will keep original binary format but mark it as unsgined value. + // Since value from register is actually a "signed value", a "as" cast will keep original binary format but mark it as a unsigned value for register writing. // see https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast Ok((value * ((1 as $unsigned_bin_typ << $offset) as $float_ty)) as $signed_bin_typ as $unsigned_bin_typ) } @@ -34,7 +34,7 @@ macro_rules! floating_fixed_convert { /// convert fixed point to float point format pub fn $q_to_f(value: $unsigned_bin_typ) -> $float_ty { // It's necessary to cast the unsigned integer to signed integer, before convert it to a float value. - // Since value from register is actually a "signed value", a "as" cast will keep original binary format but mark it as signed value. + // Since value from register is actually a "signed value", a "as" cast will keep original binary format but mark it as a signed value. // see https://doc.rust-lang.org/reference/expressions/operator-expr.html#numeric-cast (value as $signed_bin_typ as $float_ty) / ((1 as $unsigned_bin_typ << $offset) as $float_ty) } From 441aa4c8cede2b63cc55b51db6eb89b1e35671f9 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Fri, 22 Mar 2024 00:39:43 +0800 Subject: [PATCH 21/69] stm32 CORDIC: make HIL run --- tests/stm32/src/bin/cordic.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tests/stm32/src/bin/cordic.rs b/tests/stm32/src/bin/cordic.rs index b580cc79b..cd2e9d6f7 100644 --- a/tests/stm32/src/bin/cordic.rs +++ b/tests/stm32/src/bin/cordic.rs @@ -4,13 +4,15 @@ // Only test on STM32H563ZI, STM32U585AI and STM32U5a5JI. // STM32G491RE is not tested, since it memory.x has less memory size than it actually has, -// and the test seems use much memory than memory.x suggest. +// and the test seems use more memory than memory.x suggest. // see https://github.com/embassy-rs/stm32-data/issues/301#issuecomment-1925412561 #![no_std] #![no_main] -use defmt::*; +#[path = "../common.rs"] +mod common; +use common::*; use embassy_executor::Spawner; use embassy_stm32::{bind_interrupts, cordic, peripherals, rng}; use num_traits::Float; @@ -25,12 +27,12 @@ bind_interrupts!(struct Irqs { const ARG1_LENGTH: usize = 9; const ARG2_LENGTH: usize = 4; // this might not be the exact length of ARG2, since ARG2 need to be inside [0, 1] -const INPUT_Q1_31_LENGHT: usize = ARG1_LENGTH + ARG2_LENGTH; -const INPUT_U8_LENGTH: usize = 4 * INPUT_Q1_31_LENGHT; +const INPUT_Q1_31_LENGTH: usize = ARG1_LENGTH + ARG2_LENGTH; +const INPUT_U8_LENGTH: usize = 4 * INPUT_Q1_31_LENGTH; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let dp = embassy_stm32::init(Default::default()); + let dp = embassy_stm32::init(config()); // // use RNG generate random Q1.31 value @@ -41,12 +43,12 @@ async fn main(_spawner: Spawner) { let mut rng = rng::Rng::new(dp.RNG, Irqs); let mut input_buf_u8 = [0u8; INPUT_U8_LENGTH]; - unwrap!(rng.async_fill_bytes(&mut input_buf_u8).await); + defmt::unwrap!(rng.async_fill_bytes(&mut input_buf_u8).await); // convert every [u8; 4] to a u32, for a Q1.31 value - let input_q1_31 = unsafe { core::mem::transmute::<[u8; INPUT_U8_LENGTH], [u32; INPUT_Q1_31_LENGHT]>(input_buf_u8) }; + let input_q1_31 = unsafe { core::mem::transmute::<[u8; INPUT_U8_LENGTH], [u32; INPUT_Q1_31_LENGTH]>(input_buf_u8) }; - let mut input_f64_buf = [0f64; INPUT_Q1_31_LENGHT]; + let mut input_f64_buf = [0f64; INPUT_Q1_31_LENGTH]; let mut cordic_output_f64_buf = [0f64; ARG1_LENGTH * 2]; @@ -66,13 +68,13 @@ async fn main(_spawner: Spawner) { } } - // the actal value feed to CORDIC + // the actual value feed to CORDIC let arg1_f64_ls = &input_f64_buf[..ARG1_LENGTH]; let arg2_f64_ls = &arg2_f64_buf[..arg2_f64_len]; let mut cordic = cordic::Cordic::new( dp.CORDIC, - unwrap!(cordic::Config::new( + defmt::unwrap!(cordic::Config::new( cordic::Function::Sin, Default::default(), Default::default(), @@ -138,9 +140,9 @@ async fn main(_spawner: Spawner) { } } - // This comparsion is just for fun. Since it not a equal compare: + // This comparison is just for fun. Since it not a equal compare: // software use 64-bit floating point, but CORDIC use 32-bit fixed point. - trace!( + defmt::trace!( "calculate count: {}, Cordic time: {} us, software time: {} us", ARG1_LENGTH, (cordic_end_point - cordic_start_point).as_micros(), From 83069e7b49bd181236e6a68005ad6119d39b39c3 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Fri, 22 Mar 2024 00:58:03 +0800 Subject: [PATCH 22/69] stm32 CORDIC: add example --- examples/stm32h5/src/bin/cordic.rs | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 examples/stm32h5/src/bin/cordic.rs diff --git a/examples/stm32h5/src/bin/cordic.rs b/examples/stm32h5/src/bin/cordic.rs new file mode 100644 index 000000000..d49f75b8f --- /dev/null +++ b/examples/stm32h5/src/bin/cordic.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::cordic; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut dp = embassy_stm32::init(Default::default()); + + let mut cordic = cordic::Cordic::new( + &mut dp.CORDIC, + unwrap!(cordic::Config::new( + cordic::Function::Sin, + Default::default(), + Default::default(), + false, + )), + ); + + let mut output = [0f64; 16]; + + let arg1 = [1.0, 0.0, -1.0]; // for trigonometric function, the ARG1 value [-pi, pi] should be map to [-1, 1] + let arg2 = [0.5, 1.0]; + + let cnt = unwrap!( + cordic + .async_calc_32bit(&mut dp.GPDMA1_CH0, &mut dp.GPDMA1_CH1, &arg1, Some(&arg2), &mut output,) + .await + ); + + println!("async calc 32bit: {}", output[..cnt]); +} From 0abcccee966af0b12e62fc7fae8499fa03194823 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Fri, 22 Mar 2024 17:29:10 +0800 Subject: [PATCH 23/69] stm32 CORDIC: re-design API --- embassy-stm32/src/cordic/errors.rs | 67 +-- embassy-stm32/src/cordic/mod.rs | 781 +++++++++++------------------ embassy-stm32/src/cordic/utils.rs | 15 +- examples/stm32h5/src/bin/cordic.rs | 59 ++- tests/stm32/src/bin/cordic.rs | 110 ++-- 5 files changed, 434 insertions(+), 598 deletions(-) diff --git a/embassy-stm32/src/cordic/errors.rs b/embassy-stm32/src/cordic/errors.rs index 653014290..3c70fc9e7 100644 --- a/embassy-stm32/src/cordic/errors.rs +++ b/embassy-stm32/src/cordic/errors.rs @@ -5,12 +5,14 @@ use super::{Function, Scale}; pub enum CordicError { /// Config error ConfigError(ConfigError), - /// Argument error - ArgError(ArgError), - /// Output buffer length error - OutputLengthNotEnough, + /// Argument length is incorrect + ArgumentLengthIncorrect, + /// Result buffer length error + ResultLengthNotEnough, /// Input value is out of range for Q1.x format NumberOutOfRange(NumberOutOfRange), + /// Argument error + ArgError(ArgError), } impl From for CordicError { @@ -19,18 +21,18 @@ impl From for CordicError { } } -impl From for CordicError { - fn from(value: ArgError) -> Self { - Self::ArgError(value) - } -} - impl From for CordicError { fn from(value: NumberOutOfRange) -> Self { Self::NumberOutOfRange(value) } } +impl From for CordicError { + fn from(value: ArgError) -> Self { + Self::ArgError(value) + } +} + #[cfg(feature = "defmt")] impl defmt::Format for CordicError { fn format(&self, fmt: defmt::Formatter) { @@ -38,9 +40,10 @@ impl defmt::Format for CordicError { match self { ConfigError(e) => defmt::write!(fmt, "{}", e), - ArgError(e) => defmt::write!(fmt, "{}", e), + ResultLengthNotEnough => defmt::write!(fmt, "Output buffer length is not long enough"), + ArgumentLengthIncorrect => defmt::write!(fmt, "Argument length incorrect"), NumberOutOfRange(e) => defmt::write!(fmt, "{}", e), - OutputLengthNotEnough => defmt::write!(fmt, "Output buffer length is not long enough"), + ArgError(e) => defmt::write!(fmt, "{}", e), } } } @@ -71,6 +74,26 @@ impl defmt::Format for ConfigError { } } +/// Input value is out of range for Q1.x format +#[allow(missing_docs)] +#[derive(Debug)] +pub enum NumberOutOfRange { + BelowLowerBound, + AboveUpperBound, +} + +#[cfg(feature = "defmt")] +impl defmt::Format for NumberOutOfRange { + fn format(&self, fmt: defmt::Formatter) { + use NumberOutOfRange::*; + + match self { + BelowLowerBound => defmt::write!(fmt, "input value should be equal or greater than -1"), + AboveUpperBound => defmt::write!(fmt, "input value should be equal or less than 1"), + } + } +} + /// Error on checking input arguments #[allow(dead_code)] #[derive(Debug)] @@ -119,23 +142,3 @@ pub(super) enum ArgType { Arg1, Arg2, } - -/// Input value is out of range for Q1.x format -#[allow(missing_docs)] -#[derive(Debug)] -pub enum NumberOutOfRange { - BelowLowerBound, - AboveUpperBound, -} - -#[cfg(feature = "defmt")] -impl defmt::Format for NumberOutOfRange { - fn format(&self, fmt: defmt::Formatter) { - use NumberOutOfRange::*; - - match self { - BelowLowerBound => defmt::write!(fmt, "input value should be equal or greater than -1"), - AboveUpperBound => defmt::write!(fmt, "input value should be equal or less than 1"), - } - } -} diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index f12efe2eb..2479e1b27 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs @@ -21,8 +21,6 @@ pub mod low_level { pub use super::sealed::*; } -const INPUT_BUF_MAX_LEN: usize = 16; - /// CORDIC driver pub struct Cordic<'d, T: Instance> { peri: PeripheralRef<'d, T>, @@ -38,17 +36,15 @@ pub struct Config { function: Function, precision: Precision, scale: Scale, - res1_only: bool, } impl Config { /// Create a config for Cordic driver - pub fn new(function: Function, precision: Precision, scale: Scale, res1_only: bool) -> Result { + pub fn new(function: Function, precision: Precision, scale: Scale) -> Result { let config = Self { function, precision, scale, - res1_only, }; config.check_scale()?; @@ -117,7 +113,32 @@ impl<'d, T: Instance> Cordic<'d, T> { self.peri.set_data_width(arg_width, res_width); } - fn reconfigure(&mut self) { + fn clean_rrdy_flag(&mut self) { + while self.peri.ready_to_read() { + self.peri.read_result(); + } + } + + /// Disable IRQ and DMA, clean RRDY, and set ARG2 to +1 (0x7FFFFFFF) + pub fn reconfigure(&mut self) { + // reset ARG2 to +1 + { + self.peri.disable_irq(); + self.peri.disable_read_dma(); + self.peri.disable_write_dma(); + self.clean_rrdy_flag(); + + self.peri.set_func(Function::Cos); + self.peri.set_precision(Precision::Iters4); + self.peri.set_scale(Scale::Arg1Res1); + self.peri.set_argument_count(AccessCount::Two); + self.peri.set_data_width(Width::Bits32, Width::Bits32); + self.peri.write_argument(0x0u32); + self.peri.write_argument(0x7FFFFFFFu32); + + self.clean_rrdy_flag(); + } + self.peri.set_func(self.config.function); self.peri.set_precision(self.config.precision); self.peri.set_scale(self.config.scale); @@ -125,16 +146,154 @@ impl<'d, T: Instance> Cordic<'d, T> { // we don't set NRES in here, but to make sure NRES is set each time user call "calc"-ish functions, // since each "calc"-ish functions can have different ARGSIZE and RESSIZE, thus NRES should be change accordingly. } +} - async fn launch_a_dma_transfer( +impl<'d, T: Instance> Drop for Cordic<'d, T> { + fn drop(&mut self) { + T::disable(); + } +} + +// q1.31 related +impl<'d, T: Instance> Cordic<'d, T> { + /// Run a blocking CORDIC calculation in q1.31 format + /// + /// Notice: + /// If you set `arg1_only` to `true`, please be sure ARG2 value has been set to desired value before. + /// This function won't set ARG2 to +1 before or after each round of calculation. + /// If you want to make sure ARG2 is set to +1, consider run [.reconfigure()](Self::reconfigure). + pub fn blocking_calc_32bit( + &mut self, + arg: &[u32], + res: &mut [u32], + arg1_only: bool, + res1_only: bool, + ) -> Result { + if arg.is_empty() { + return Ok(0); + } + + let res_cnt = Self::check_arg_res_length_32bit(arg.len(), res.len(), arg1_only, res1_only)?; + + self.peri + .set_argument_count(if arg1_only { AccessCount::One } else { AccessCount::Two }); + + self.peri + .set_result_count(if res1_only { AccessCount::One } else { AccessCount::Two }); + + self.peri.set_data_width(Width::Bits32, Width::Bits32); + + let mut cnt = 0; + + match arg1_only { + true => { + // To use cordic preload function, the first value is special. + // It is loaded to CORDIC WDATA register out side of loop + let first_value = arg[0]; + + // preload 1st value to CORDIC, to start the CORDIC calc + self.peri.write_argument(first_value); + + for &arg1 in &arg[1..] { + // preload arg1 (for next calc) + self.peri.write_argument(arg1); + + // then read current result out + res[cnt] = self.peri.read_result(); + cnt += 1; + if !res1_only { + res[cnt] = self.peri.read_result(); + cnt += 1; + } + } + + // read the last result + res[cnt] = self.peri.read_result(); + cnt += 1; + if !res1_only { + res[cnt] = self.peri.read_result(); + // cnt += 1; + } + } + false => { + // To use cordic preload function, the first and last value is special. + // They are load to CORDIC WDATA register out side of loop + let first_value = arg[0]; + let last_value = arg[arg.len() - 1]; + + let paired_args = &arg[1..arg.len() - 1]; + + // preload 1st value to CORDIC + self.peri.write_argument(first_value); + + for args in paired_args.chunks(2) { + let arg2 = args[0]; + let arg1 = args[1]; + + // load arg2 (for current calc) first, to start the CORDIC calc + self.peri.write_argument(arg2); + + // preload arg1 (for next calc) + self.peri.write_argument(arg1); + + // then read current result out + res[cnt] = self.peri.read_result(); + cnt += 1; + if !res1_only { + res[cnt] = self.peri.read_result(); + cnt += 1; + } + } + + // load last value to CORDIC, and finish the calculation + self.peri.write_argument(last_value); + res[cnt] = self.peri.read_result(); + cnt += 1; + if !res1_only { + res[cnt] = self.peri.read_result(); + // cnt += 1; + } + } + } + + // at this point cnt should be equal to res_cnt + + Ok(res_cnt) + } + + /// Run a async CORDIC calculation in q.1.31 format + /// + /// Notice: + /// If you set `arg1_only` to `true`, please be sure ARG2 value has been set to desired value before. + /// This function won't set ARG2 to +1 before or after each round of calculation. + /// If you want to make sure ARG2 is set to +1, consider run [.reconfigure()](Self::reconfigure). + pub async fn async_calc_32bit( &mut self, write_dma: impl Peripheral

>, read_dma: impl Peripheral

>, - input: &[u32], - output: &mut [u32], - ) { + arg: &[u32], + res: &mut [u32], + arg1_only: bool, + res1_only: bool, + ) -> Result { + if arg.is_empty() { + return Ok(0); + } + + let res_cnt = Self::check_arg_res_length_32bit(arg.len(), res.len(), arg1_only, res1_only)?; + + let active_res_buf = &mut res[..res_cnt]; + into_ref!(write_dma, read_dma); + self.peri + .set_argument_count(if arg1_only { AccessCount::One } else { AccessCount::Two }); + + self.peri + .set_result_count(if res1_only { AccessCount::One } else { AccessCount::Two }); + + self.peri.set_data_width(Width::Bits32, Width::Bits32); + let write_req = write_dma.request(); let read_req = read_dma.request(); @@ -150,7 +309,7 @@ impl<'d, T: Instance> Cordic<'d, T> { let write_transfer = dma::Transfer::new_write( &mut write_dma, write_req, - input, + arg, T::regs().wdata().as_ptr() as *mut _, Default::default(), ); @@ -159,328 +318,60 @@ impl<'d, T: Instance> Cordic<'d, T> { &mut read_dma, read_req, T::regs().rdata().as_ptr() as *mut _, - output, + active_res_buf, Default::default(), ); embassy_futures::join::join(write_transfer, read_transfer).await; } - } -} -impl<'d, T: Instance> Drop for Cordic<'d, T> { - fn drop(&mut self) { - T::disable(); + Ok(res_cnt) } -} -// q1.31 related -impl<'d, T: Instance> Cordic<'d, T> { - /// Run a blocking CORDIC calculation in q1.31 format - pub fn blocking_calc_32bit( - &mut self, - arg1s: &[f64], - arg2s: Option<&[f64]>, - output: &mut [f64], + fn check_arg_res_length_32bit( + arg_len: usize, + res_len: usize, + arg1_only: bool, + res1_only: bool, ) -> Result { - if arg1s.is_empty() { - return Ok(0); + if !arg1_only && arg_len % 2 != 0 { + return Err(CordicError::ArgumentLengthIncorrect); } - let output_length_enough = match self.config.res1_only { - true => output.len() >= arg1s.len(), - false => output.len() >= 2 * arg1s.len(), - }; + let mut minimal_res_length = arg_len; - if !output_length_enough { - return Err(CordicError::OutputLengthNotEnough); + if !res1_only { + minimal_res_length *= 2; } - self.check_input_f64(arg1s, arg2s)?; - - self.peri.set_result_count(if self.config.res1_only { - AccessCount::One - } else { - AccessCount::Two - }); - - self.peri.set_data_width(Width::Bits32, Width::Bits32); - - let mut output_count = 0; - - let mut consumed_input_len = 0; - - // - // handle 2 input args calculation - // - - if arg2s.is_some() && !arg2s.unwrap().is_empty() { - let arg2s = arg2s.unwrap(); - - self.peri.set_argument_count(AccessCount::Two); - - // Skip 1st value from arg1s, this value will be manually "preload" to cordic, to make use of cordic preload function. - // And we preserve last value from arg2s, since it need to manually write to cordic, and read the result out. - let double_input = arg1s.iter().skip(1).zip(&arg2s[..arg2s.len() - 1]); - // Since we preload 1st value from arg1s, the consumed input length is double_input length + 1. - consumed_input_len = double_input.len() + 1; - - // preload first value from arg1 to cordic - self.blocking_write_f64(arg1s[0])?; - - for (&arg1, &arg2) in double_input { - // Since we manually preload a value before, - // we will write arg2 (from the actual last pair) first, (at this moment, cordic start to calculating,) - // and write arg1 (from the actual next pair), then read the result, to "keep preloading" - - self.blocking_write_f64(arg2)?; - self.blocking_write_f64(arg1)?; - self.blocking_read_f64_to_buf(output, &mut output_count); - } - - // write last input value from arg2s, then read out the result - self.blocking_write_f64(arg2s[arg2s.len() - 1])?; - self.blocking_read_f64_to_buf(output, &mut output_count); + if !arg1_only { + minimal_res_length /= 2 } - // - // handle 1 input arg calculation - // - - let input_left = &arg1s[consumed_input_len..]; - - if !input_left.is_empty() { - self.peri.set_argument_count(AccessCount::One); - - // "preload" value to cordic (at this moment, cordic start to calculating) - self.blocking_write_f64(input_left[0])?; - - for &arg in input_left.iter().skip(1) { - // this line write arg for next round calculation to cordic, - // and read result from last round - self.blocking_write_f64(arg)?; - self.blocking_read_f64_to_buf(output, &mut output_count); - } - - // read the last output - self.blocking_read_f64_to_buf(output, &mut output_count); + if minimal_res_length > res_len { + return Err(CordicError::ResultLengthNotEnough); } - Ok(output_count) - } - - fn blocking_read_f64_to_buf(&mut self, result_buf: &mut [f64], result_index: &mut usize) { - result_buf[*result_index] = utils::q1_31_to_f64(self.peri.read_result()); - *result_index += 1; - - // We don't care about whether the function return 1 or 2 results, - // the only thing matter is whether user want 1 or 2 results. - if !self.config.res1_only { - result_buf[*result_index] = utils::q1_31_to_f64(self.peri.read_result()); - *result_index += 1; - } - } - - fn blocking_write_f64(&mut self, arg: f64) -> Result<(), NumberOutOfRange> { - self.peri.write_argument(utils::f64_to_q1_31(arg)?); - Ok(()) - } - - /// Run a async CORDIC calculation in q.1.31 format - pub async fn async_calc_32bit( - &mut self, - write_dma: impl Peripheral

>, - read_dma: impl Peripheral

>, - arg1s: &[f64], - arg2s: Option<&[f64]>, - output: &mut [f64], - ) -> Result { - if arg1s.is_empty() { - return Ok(0); - } - - let output_length_enough = match self.config.res1_only { - true => output.len() >= arg1s.len(), - false => output.len() >= 2 * arg1s.len(), - }; - - if !output_length_enough { - return Err(CordicError::OutputLengthNotEnough); - } - - self.check_input_f64(arg1s, arg2s)?; - - into_ref!(write_dma, read_dma); - - self.peri.set_result_count(if self.config.res1_only { - AccessCount::One - } else { - AccessCount::Two - }); - - self.peri.set_data_width(Width::Bits32, Width::Bits32); - - let mut output_count = 0; - let mut consumed_input_len = 0; - let mut input_buf = [0u32; INPUT_BUF_MAX_LEN]; - let mut input_buf_len = 0; - - // - // handle 2 input args calculation - // - - if !arg2s.unwrap_or_default().is_empty() { - let arg2s = arg2s.unwrap(); - - self.peri.set_argument_count(AccessCount::Two); - - let double_input = arg1s.iter().zip(arg2s); - - consumed_input_len = double_input.len(); - - for (&arg1, &arg2) in double_input { - for &arg in [arg1, arg2].iter() { - input_buf[input_buf_len] = utils::f64_to_q1_31(arg)?; - input_buf_len += 1; - } - - if input_buf_len == INPUT_BUF_MAX_LEN { - self.inner_dma_calc_32bit( - &mut write_dma, - &mut read_dma, - true, - &input_buf[..input_buf_len], - output, - &mut output_count, - ) - .await; - - input_buf_len = 0; - } - } - - if input_buf_len > 0 { - self.inner_dma_calc_32bit( - &mut write_dma, - &mut read_dma, - true, - &input_buf[..input_buf_len], - output, - &mut output_count, - ) - .await; - - input_buf_len = 0; - } - } - - // - // handle 1 input arg calculation - // - - if arg1s.len() > consumed_input_len { - let input_remain = &arg1s[consumed_input_len..]; - - self.peri.set_argument_count(AccessCount::One); - - for &arg in input_remain { - input_buf[input_buf_len] = utils::f64_to_q1_31(arg)?; - input_buf_len += 1; - - if input_buf_len == INPUT_BUF_MAX_LEN { - self.inner_dma_calc_32bit( - &mut write_dma, - &mut read_dma, - false, - &input_buf[..input_buf_len], - output, - &mut output_count, - ) - .await; - - input_buf_len = 0; - } - } - - if input_buf_len > 0 { - self.inner_dma_calc_32bit( - &mut write_dma, - &mut read_dma, - false, - &input_buf[..input_buf_len], - output, - &mut output_count, - ) - .await; - - // input_buf_len = 0; - } - } - - Ok(output_count) - } - - // this function is highly coupled with async_calc_32bit, and is not intended to use in other place - async fn inner_dma_calc_32bit( - &mut self, - write_dma: impl Peripheral

>, - read_dma: impl Peripheral

>, - double_input: bool, // gether extra info to calc output_buf size - input_buf: &[u32], // input_buf, its content should be exact length for calculation - output: &mut [f64], // caller should uses this buf as a final output array - output_start_index: &mut usize, // the index of start point of the output for this round of calculation - ) { - // output_buf is the place to store raw value from CORDIC (via DMA). - // For buf size, we assume in this round of calculation: - // all input is 1 arg, and all calculation need 2 output, - // thus output_buf will always be long enough. - let mut output_buf = [0u32; INPUT_BUF_MAX_LEN * 2]; - - let mut output_buf_size = input_buf.len(); - if !self.config.res1_only { - // if we need 2 result for 1 input, then output_buf length should be 2x long. - output_buf_size *= 2; - }; - if double_input { - // if input itself is 2 args for 1 calculation, then output_buf length should be /2. - output_buf_size /= 2; - } - - let active_output_buf = &mut output_buf[..output_buf_size]; - - self.launch_a_dma_transfer(write_dma, read_dma, input_buf, active_output_buf) - .await; - - for &mut output_u32 in active_output_buf { - output[*output_start_index] = utils::q1_31_to_f64(output_u32); - *output_start_index += 1; - } + Ok(minimal_res_length) } } // q1.15 related impl<'d, T: Instance> Cordic<'d, T> { - /// Run a blocking CORDIC calculation in q1.15 format - pub fn blocking_calc_16bit( - &mut self, - arg1s: &[f32], - arg2s: Option<&[f32]>, - output: &mut [f32], - ) -> Result { - if arg1s.is_empty() { + /// Run a blocking CORDIC calculation in q1.15 format + /// + /// Notice:: + /// User will take respond to merge two u16 arguments into one u32 data, and/or split one u32 data into two u16 results. + pub fn blocking_calc_16bit(&mut self, arg: &[u32], res: &mut [u32]) -> Result { + if arg.is_empty() { return Ok(0); } - let output_length_enough = match self.config.res1_only { - true => output.len() >= arg1s.len(), - false => output.len() >= 2 * arg1s.len(), - }; - - if !output_length_enough { - return Err(CordicError::OutputLengthNotEnough); + if arg.len() > res.len() { + return Err(CordicError::ResultLengthNotEnough); } - self.check_input_f32(arg1s, arg2s)?; + let res_cnt = arg.len(); // In q1.15 mode, 1 write/read to access 2 arguments/results self.peri.set_argument_count(AccessCount::One); @@ -488,83 +379,53 @@ impl<'d, T: Instance> Cordic<'d, T> { self.peri.set_data_width(Width::Bits16, Width::Bits16); - let mut output_count = 0; + // To use cordic preload function, the first value is special. + // It is loaded to CORDIC WDATA register out side of loop + let first_value = arg[0]; - // In q1.15 mode, we always fill 1 pair of 16bit value into WDATA register. - // If arg2s is None or empty array, we assume arg2 value always 1.0 (as reset value for ARG2). - // If arg2s has some value, and but not as long as arg1s, - // we fill the reset of arg2 values with last value from arg2s (as q1.31 version does) + // preload 1st value to CORDIC, to start the CORDIC calc + self.peri.write_argument(first_value); - let arg2_default_value = match arg2s { - Some(arg2s) if !arg2s.is_empty() => arg2s[arg2s.len() - 1], - _ => 1.0, - }; + let mut cnt = 0; - let mut args = arg1s.iter().zip( - arg2s - .unwrap_or(&[]) - .iter() - .chain(core::iter::repeat(&arg2_default_value)), - ); + for &arg_val in &arg[1..] { + // preload arg_val (for next calc) + self.peri.write_argument(arg_val); - let (&arg1, &arg2) = args.next().unwrap(); - - // preloading 1 pair of arguments - self.blocking_write_f32(arg1, arg2)?; - - for (&arg1, &arg2) in args { - self.blocking_write_f32(arg1, arg2)?; - self.blocking_read_f32_to_buf(output, &mut output_count); + // then read current result out + res[cnt] = self.peri.read_result(); + cnt += 1; } - // read last pair of value from cordic - self.blocking_read_f32_to_buf(output, &mut output_count); + // read last result out + res[cnt] = self.peri.read_result(); + // cnt += 1; - Ok(output_count) + Ok(res_cnt) } - fn blocking_write_f32(&mut self, arg1: f32, arg2: f32) -> Result<(), NumberOutOfRange> { - self.peri.write_argument(utils::f32_args_to_u32(arg1, arg2)?); - Ok(()) - } - - fn blocking_read_f32_to_buf(&mut self, result_buf: &mut [f32], result_index: &mut usize) { - let (res1, res2) = utils::u32_to_f32_res(self.peri.read_result()); - - result_buf[*result_index] = res1; - *result_index += 1; - - // We don't care about whether the function return 1 or 2 results, - // the only thing matter is whether user want 1 or 2 results. - if !self.config.res1_only { - result_buf[*result_index] = res2; - *result_index += 1; - } - } - - /// Run a async CORDIC calculation in q1.15 format + /// Run a async CORDIC calculation in q1.15 format + /// + /// Notice:: + /// User will take respond to merge two u16 arguments into one u32 data, and/or split one u32 data into two u16 results. pub async fn async_calc_16bit( &mut self, write_dma: impl Peripheral

>, read_dma: impl Peripheral

>, - arg1s: &[f32], - arg2s: Option<&[f32]>, - output: &mut [f32], + arg: &[u32], + res: &mut [u32], ) -> Result { - if arg1s.is_empty() { + if arg.is_empty() { return Ok(0); } - let output_length_enough = match self.config.res1_only { - true => output.len() >= arg1s.len(), - false => output.len() >= 2 * arg1s.len(), - }; - - if !output_length_enough { - return Err(CordicError::OutputLengthNotEnough); + if arg.len() > res.len() { + return Err(CordicError::ResultLengthNotEnough); } - self.check_input_f32(arg1s, arg2s)?; + let res_cnt = arg.len(); + + let active_res_buf = &mut res[..res_cnt]; into_ref!(write_dma, read_dma); @@ -574,142 +435,96 @@ impl<'d, T: Instance> Cordic<'d, T> { self.peri.set_data_width(Width::Bits16, Width::Bits16); - let mut output_count = 0; - let mut input_buf = [0u32; INPUT_BUF_MAX_LEN]; - let mut input_buf_len = 0; + let write_req = write_dma.request(); + let read_req = read_dma.request(); - // In q1.15 mode, we always fill 1 pair of 16bit value into WDATA register. - // If arg2s is None or empty array, we assume arg2 value always 1.0 (as reset value for ARG2). - // If arg2s has some value, and but not as long as arg1s, - // we fill the reset of arg2 values with last value from arg2s (as CORDIC behavior on q1.31 format) + self.peri.enable_write_dma(); + self.peri.enable_read_dma(); - let arg2_default_value = match arg2s { - Some(arg2s) if !arg2s.is_empty() => arg2s[arg2s.len() - 1], - _ => 1.0, - }; + let _on_drop = OnDrop::new(|| { + self.peri.disable_write_dma(); + self.peri.disable_read_dma(); + }); - let args = arg1s.iter().zip( - arg2s - .unwrap_or(&[]) - .iter() - .chain(core::iter::repeat(&arg2_default_value)), - ); - - for (&arg1, &arg2) in args { - input_buf[input_buf_len] = utils::f32_args_to_u32(arg1, arg2)?; - input_buf_len += 1; - - if input_buf_len == INPUT_BUF_MAX_LEN { - self.inner_dma_calc_16bit(&mut write_dma, &mut read_dma, &input_buf, output, &mut output_count) - .await; - } - } - - if input_buf_len > 0 { - self.inner_dma_calc_16bit( + unsafe { + let write_transfer = dma::Transfer::new_write( &mut write_dma, + write_req, + arg, + T::regs().wdata().as_ptr() as *mut _, + Default::default(), + ); + + let read_transfer = dma::Transfer::new_read( &mut read_dma, - &input_buf[..input_buf_len], - output, - &mut output_count, - ) - .await; + read_req, + T::regs().rdata().as_ptr() as *mut _, + active_res_buf, + Default::default(), + ); + + embassy_futures::join::join(write_transfer, read_transfer).await; } - Ok(output_count) - } - - // this function is highly coupled with async_calc_16bit, and is not intended to use in other place - async fn inner_dma_calc_16bit( - &mut self, - write_dma: impl Peripheral

>, - read_dma: impl Peripheral

>, - input_buf: &[u32], // input_buf, its content should be exact length for calculation - output: &mut [f32], // caller should uses this buf as a final output array - output_start_index: &mut usize, // the index of start point of the output for this round of calculation - ) { - // output_buf is the place to store raw value from CORDIC (via DMA). - let mut output_buf = [0u32; INPUT_BUF_MAX_LEN]; - - let active_output_buf = &mut output_buf[..input_buf.len()]; - - self.launch_a_dma_transfer(write_dma, read_dma, input_buf, active_output_buf) - .await; - - for &mut output_u32 in active_output_buf { - let (res1, res2) = utils::u32_to_f32_res(output_u32); - - output[*output_start_index] = res1; - *output_start_index += 1; - - if !self.config.res1_only { - output[*output_start_index] = res2; - *output_start_index += 1; - } - } + Ok(res_cnt) } } -// check input value ARG1, ARG2, SCALE and FUNCTION are compatible with each other -macro_rules! check_input_value { - ($func_name:ident, $float_type:ty) => { +macro_rules! check_arg_value { + ($func_arg1_name:ident, $func_arg2_name:ident, $float_type:ty) => { impl<'d, T: Instance> Cordic<'d, T> { - fn $func_name(&self, arg1s: &[$float_type], arg2s: Option<&[$float_type]>) -> Result<(), ArgError> { + /// check input value ARG1, SCALE and FUNCTION are compatible with each other + pub fn $func_arg1_name(&self, arg: $float_type) -> Result<(), ArgError> { let config = &self.config; use Function::*; struct Arg1ErrInfo { scale: Option, - range: [f32; 2], + range: [f32; 2], // f32 is ok, it only used in error display inclusive_upper_bound: bool, } - // check ARG1 value let err_info = match config.function { - Cos | Sin | Phase | Modulus | Arctan if arg1s.iter().any(|v| !(-1.0..=1.0).contains(v)) => { - Some(Arg1ErrInfo { - scale: None, - range: [-1.0, 1.0], - inclusive_upper_bound: true, - }) - } + Cos | Sin | Phase | Modulus | Arctan if !(-1.0..=1.0).contains(arg) => Some(Arg1ErrInfo { + scale: None, + range: [-1.0, 1.0], + inclusive_upper_bound: true, + }), - Cosh | Sinh if arg1s.iter().any(|v| !(-0.559..=0.559).contains(v)) => Some(Arg1ErrInfo { + Cosh | Sinh if !(-0.559..=0.559).contains(arg) => Some(Arg1ErrInfo { scale: None, range: [-0.559, 0.559], inclusive_upper_bound: true, }), - Arctanh if arg1s.iter().any(|v| !(-0.403..=0.403).contains(v)) => Some(Arg1ErrInfo { + Arctanh if !(-0.403..=0.403).contains(arg) => Some(Arg1ErrInfo { scale: None, range: [-0.403, 0.403], inclusive_upper_bound: true, }), Ln => match config.scale { - Scale::Arg1o2Res2 if arg1s.iter().any(|v| !(0.0535..0.5).contains(v)) => Some(Arg1ErrInfo { + Scale::Arg1o2Res2 if !(0.0535..0.5).contains(arg) => Some(Arg1ErrInfo { scale: Some(Scale::Arg1o2Res2), range: [0.0535, 0.5], inclusive_upper_bound: false, }), - Scale::Arg1o4Res4 if arg1s.iter().any(|v| !(0.25..0.75).contains(v)) => Some(Arg1ErrInfo { + Scale::Arg1o4Res4 if !(0.25..0.75).contains(arg) => Some(Arg1ErrInfo { scale: Some(Scale::Arg1o4Res4), range: [0.25, 0.75], inclusive_upper_bound: false, }), - Scale::Arg1o8Res8 if arg1s.iter().any(|v| !(0.375..0.875).contains(v)) => Some(Arg1ErrInfo { + Scale::Arg1o8Res8 if !(0.375..0.875).contains(arg) => Some(Arg1ErrInfo { scale: Some(Scale::Arg1o8Res8), range: [0.375, 0.875], inclusive_upper_bound: false, }), - Scale::Arg1o16Res16 if arg1s.iter().any(|v| !(0.4375..0.584).contains(v)) => { - Some(Arg1ErrInfo { - scale: Some(Scale::Arg1o16Res16), - range: [0.4375, 0.584], - inclusive_upper_bound: false, - }) - } + Scale::Arg1o16Res16 if !(0.4375..0.584).contains(arg) => Some(Arg1ErrInfo { + scale: Some(Scale::Arg1o16Res16), + range: [0.4375, 0.584], + inclusive_upper_bound: false, + }), Scale::Arg1o2Res2 | Scale::Arg1o4Res4 | Scale::Arg1o8Res8 | Scale::Arg1o16Res16 => None, @@ -717,17 +532,17 @@ macro_rules! check_input_value { }, Sqrt => match config.scale { - Scale::Arg1Res1 if arg1s.iter().any(|v| !(0.027..0.75).contains(v)) => Some(Arg1ErrInfo { + Scale::Arg1Res1 if !(0.027..0.75).contains(arg) => Some(Arg1ErrInfo { scale: Some(Scale::Arg1Res1), range: [0.027, 0.75], inclusive_upper_bound: false, }), - Scale::Arg1o2Res2 if arg1s.iter().any(|v| !(0.375..0.875).contains(v)) => Some(Arg1ErrInfo { + Scale::Arg1o2Res2 if !(0.375..0.875).contains(arg) => Some(Arg1ErrInfo { scale: Some(Scale::Arg1o2Res2), range: [0.375, 0.875], inclusive_upper_bound: false, }), - Scale::Arg1o4Res4 if arg1s.iter().any(|v| !(0.4375..0.584).contains(v)) => Some(Arg1ErrInfo { + Scale::Arg1o4Res4 if !(0.4375..0.584).contains(arg) => Some(Arg1ErrInfo { scale: Some(Scale::Arg1o4Res4), range: [0.4375, 0.584], inclusive_upper_bound: false, @@ -749,33 +564,35 @@ macro_rules! check_input_value { }); } - // check ARG2 value - if let Some(arg2s) = arg2s { - struct Arg2ErrInfo { - range: [f32; 2], - } + Ok(()) + } - let err_info = match config.function { - Cos | Sin if arg2s.iter().any(|v| !(0.0..=1.0).contains(v)) => { - Some(Arg2ErrInfo { range: [0.0, 1.0] }) - } + /// check input value ARG2 and FUNCTION are compatible with each other + pub fn $func_arg2_name(&self, arg: $float_type) -> Result<(), ArgError> { + let config = &self.config; - Phase | Modulus if arg2s.iter().any(|v| !(-1.0..=1.0).contains(v)) => { - Some(Arg2ErrInfo { range: [-1.0, 1.0] }) - } + use Function::*; - Cos | Sin | Phase | Modulus | Arctan | Cosh | Sinh | Arctanh | Ln | Sqrt => None, - }; + struct Arg2ErrInfo { + range: [f32; 2], // f32 is ok, it only used in error display + } - if let Some(err) = err_info { - return Err(ArgError { - func: config.function, - scale: None, - arg_range: err.range, - inclusive_upper_bound: true, - arg_type: ArgType::Arg2, - }); - } + let err_info = match config.function { + Cos | Sin if !(0.0..=1.0).contains(arg) => Some(Arg2ErrInfo { range: [0.0, 1.0] }), + + Phase | Modulus if !(-1.0..=1.0).contains(arg) => Some(Arg2ErrInfo { range: [-1.0, 1.0] }), + + Cos | Sin | Phase | Modulus | Arctan | Cosh | Sinh | Arctanh | Ln | Sqrt => None, + }; + + if let Some(err) = err_info { + return Err(ArgError { + func: config.function, + scale: None, + arg_range: err.range, + inclusive_upper_bound: true, + arg_type: ArgType::Arg2, + }); } Ok(()) @@ -784,8 +601,8 @@ macro_rules! check_input_value { }; } -check_input_value!(check_input_f64, f64); -check_input_value!(check_input_f32, f32); +check_arg_value!(check_f64_arg1, check_f64_arg2, &f64); +check_arg_value!(check_f32_arg1, check_f32_arg2, &f32); foreach_interrupt!( ($inst:ident, cordic, $block:ident, GLOBAL, $irq:ident) => { diff --git a/embassy-stm32/src/cordic/utils.rs b/embassy-stm32/src/cordic/utils.rs index 41821d6e2..008f50270 100644 --- a/embassy-stm32/src/cordic/utils.rs +++ b/embassy-stm32/src/cordic/utils.rs @@ -1,4 +1,4 @@ -//! Common match utils +//! Common math utils use super::errors::NumberOutOfRange; macro_rules! floating_fixed_convert { @@ -60,16 +60,3 @@ floating_fixed_convert!( 15, 0x3800_0000u32 // binary form of 1f32^(-15) ); - -#[inline(always)] -pub(crate) fn f32_args_to_u32(arg1: f32, arg2: f32) -> Result { - Ok(f32_to_q1_15(arg1)? as u32 + ((f32_to_q1_15(arg2)? as u32) << 16)) -} - -#[inline(always)] -pub(crate) fn u32_to_f32_res(reg_value: u32) -> (f32, f32) { - let res1 = q1_15_to_f32((reg_value & ((1u32 << 16) - 1)) as u16); - let res2 = q1_15_to_f32((reg_value >> 16) as u16); - - (res1, res2) -} diff --git a/examples/stm32h5/src/bin/cordic.rs b/examples/stm32h5/src/bin/cordic.rs index d49f75b8f..73e873574 100644 --- a/examples/stm32h5/src/bin/cordic.rs +++ b/examples/stm32h5/src/bin/cordic.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::cordic; +use embassy_stm32::cordic::{self, utils}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -16,20 +16,63 @@ async fn main(_spawner: Spawner) { cordic::Function::Sin, Default::default(), Default::default(), - false, )), ); - let mut output = [0f64; 16]; + // for output buf, the length is not that strict, larger than minimal required is ok. + let mut output_f64 = [0f64; 19]; + let mut output_u32 = [0u32; 21]; - let arg1 = [1.0, 0.0, -1.0]; // for trigonometric function, the ARG1 value [-pi, pi] should be map to [-1, 1] - let arg2 = [0.5, 1.0]; + // tips: + // CORDIC peripheral has some strict on input value, you can also use ".check_argX_fXX()" methods + // to make sure your input values are compatible with current CORDIC setup. + let arg1 = [-1.0, -0.5, 0.0, 0.5, 1.0]; // for trigonometric function, the ARG1 value [-pi, pi] should be map to [-1, 1] + let arg2 = [0.5]; // and for Sin function, ARG2 should be in [0, 1] - let cnt = unwrap!( + let mut input_buf = [0u32; 9]; + + // convert input from floating point to fixed point + input_buf[0] = unwrap!(utils::f64_to_q1_31(arg1[0])); + input_buf[1] = unwrap!(utils::f64_to_q1_31(arg2[0])); + + // If input length is small, blocking mode can be used to minimize overhead. + let cnt0 = unwrap!(cordic.blocking_calc_32bit( + &input_buf[..2], // input length is strict, since driver use its length to detect calculation count + &mut output_u32, + false, + false + )); + + // convert result from fixed point into floating point + for (&u32_val, f64_val) in output_u32[..cnt0].iter().zip(output_f64.iter_mut()) { + *f64_val = utils::q1_31_to_f64(u32_val); + } + + // convert input from floating point to fixed point + // + // first value from arg1 is used, so truncate to arg1[1..] + for (&f64_val, u32_val) in arg1[1..].iter().zip(input_buf.iter_mut()) { + *u32_val = unwrap!(utils::f64_to_q1_31(f64_val)); + } + + // If calculation is a little longer, async mode can make use of DMA, and let core do some other stuff. + let cnt1 = unwrap!( cordic - .async_calc_32bit(&mut dp.GPDMA1_CH0, &mut dp.GPDMA1_CH1, &arg1, Some(&arg2), &mut output,) + .async_calc_32bit( + &mut dp.GPDMA1_CH0, + &mut dp.GPDMA1_CH1, + &input_buf[..arg1.len() - 1], // limit input buf to its actual length + &mut output_u32, + true, + false + ) .await ); - println!("async calc 32bit: {}", output[..cnt]); + // convert result from fixed point into floating point + for (&u32_val, f64_val) in output_u32[..cnt1].iter().zip(output_f64[cnt0..cnt0 + cnt1].iter_mut()) { + *f64_val = utils::q1_31_to_f64(u32_val); + } + + println!("result: {}", output_f64[..cnt0 + cnt1]); } diff --git a/tests/stm32/src/bin/cordic.rs b/tests/stm32/src/bin/cordic.rs index cd2e9d6f7..669fd96ab 100644 --- a/tests/stm32/src/bin/cordic.rs +++ b/tests/stm32/src/bin/cordic.rs @@ -14,6 +14,7 @@ mod common; use common::*; use embassy_executor::Spawner; +use embassy_stm32::cordic::utils; use embassy_stm32::{bind_interrupts, cordic, peripherals, rng}; use num_traits::Float; use {defmt_rtt as _, panic_probe as _}; @@ -24,11 +25,12 @@ bind_interrupts!(struct Irqs { /* input value control, can be changed */ -const ARG1_LENGTH: usize = 9; -const ARG2_LENGTH: usize = 4; // this might not be the exact length of ARG2, since ARG2 need to be inside [0, 1] +const INPUT_U32_COUNT: usize = 9; +const INPUT_U8_COUNT: usize = 4 * INPUT_U32_COUNT; -const INPUT_Q1_31_LENGTH: usize = ARG1_LENGTH + ARG2_LENGTH; -const INPUT_U8_LENGTH: usize = 4 * INPUT_Q1_31_LENGTH; +// Assume first calculation needs 2 arguments, the reset needs 1 argument. +// And all calculation generate 2 results. +const OUTPUT_LENGTH: usize = (INPUT_U32_COUNT - 1) * 2; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -42,43 +44,28 @@ async fn main(_spawner: Spawner) { let mut rng = rng::Rng::new(dp.RNG, Irqs); - let mut input_buf_u8 = [0u8; INPUT_U8_LENGTH]; + let mut input_buf_u8 = [0u8; INPUT_U8_COUNT]; defmt::unwrap!(rng.async_fill_bytes(&mut input_buf_u8).await); // convert every [u8; 4] to a u32, for a Q1.31 value - let input_q1_31 = unsafe { core::mem::transmute::<[u8; INPUT_U8_LENGTH], [u32; INPUT_Q1_31_LENGTH]>(input_buf_u8) }; + let mut input_q1_31 = unsafe { core::mem::transmute::<[u8; INPUT_U8_COUNT], [u32; INPUT_U32_COUNT]>(input_buf_u8) }; - let mut input_f64_buf = [0f64; INPUT_Q1_31_LENGTH]; + // ARG2 for Sin function should be inside [0, 1], set MSB to 0 of a Q1.31 value, will make sure it's no less than 0. + input_q1_31[1] &= !(1u32 << 31); - let mut cordic_output_f64_buf = [0f64; ARG1_LENGTH * 2]; + // + // CORDIC calculation + // - // convert Q1.31 value back to f64, for software calculation verify - for (val_u32, val_f64) in input_q1_31.iter().zip(input_f64_buf.iter_mut()) { - *val_f64 = cordic::utils::q1_31_to_f64(*val_u32); - } - - let mut arg2_f64_buf = [0f64; ARG2_LENGTH]; - let mut arg2_f64_len = 0; - - // check if ARG2 is in range [0, 1] (limited by CORDIC peripheral with Sin mode) - for &arg2 in &input_f64_buf[ARG1_LENGTH..] { - if arg2 >= 0.0 { - arg2_f64_buf[arg2_f64_len] = arg2; - arg2_f64_len += 1; - } - } - - // the actual value feed to CORDIC - let arg1_f64_ls = &input_f64_buf[..ARG1_LENGTH]; - let arg2_f64_ls = &arg2_f64_buf[..arg2_f64_len]; + let mut output_q1_31 = [0u32; OUTPUT_LENGTH]; + // setup Cordic driver let mut cordic = cordic::Cordic::new( dp.CORDIC, defmt::unwrap!(cordic::Config::new( cordic::Function::Sin, Default::default(), Default::default(), - false, )), ); @@ -88,67 +75,66 @@ async fn main(_spawner: Spawner) { #[cfg(any(feature = "stm32h563zi", feature = "stm32u585ai", feature = "stm32u5a5zj"))] let (mut write_dma, mut read_dma) = (dp.GPDMA1_CH4, dp.GPDMA1_CH5); - let cordic_start_point = embassy_time::Instant::now(); + // calculate first result using blocking mode + let cnt0 = defmt::unwrap!(cordic.blocking_calc_32bit(&input_q1_31[..2], &mut output_q1_31, false, false)); - let cnt = unwrap!( + // calculate rest results using async mode + let cnt1 = defmt::unwrap!( cordic .async_calc_32bit( &mut write_dma, &mut read_dma, - arg1_f64_ls, - Some(arg2_f64_ls), - &mut cordic_output_f64_buf, + &input_q1_31[2..], + &mut output_q1_31[cnt0..], + true, + false, ) .await ); - let cordic_end_point = embassy_time::Instant::now(); + // all output value length should be the same as our output buffer size + defmt::assert_eq!(cnt0 + cnt1, output_q1_31.len()); - // since we get 2 output for 1 calculation, the output length should be ARG1_LENGTH * 2 - defmt::assert!(cnt == ARG1_LENGTH * 2); + let mut cordic_result_f64 = [0.0f64; OUTPUT_LENGTH]; - let mut software_output_f64_buf = [0f64; ARG1_LENGTH * 2]; + for (f64_val, u32_val) in cordic_result_f64.iter_mut().zip(output_q1_31) { + *f64_val = utils::q1_31_to_f64(u32_val); + } - // for software calc, if there is no ARG2 value, insert a 1.0 as value (the reset value for ARG2 in CORDIC) - let arg2_f64_ls = if arg2_f64_len == 0 { &[1.0] } else { arg2_f64_ls }; + // + // software calculation + // - let software_inputs = arg1_f64_ls + let mut software_result_f64 = [0.0f64; OUTPUT_LENGTH]; + + let arg2 = utils::q1_31_to_f64(input_q1_31[1]); + + for (&arg1, res) in input_q1_31 .iter() - .zip( - arg2_f64_ls - .iter() - .chain(core::iter::repeat(&arg2_f64_ls[arg2_f64_ls.len() - 1])), - ) - .zip(software_output_f64_buf.chunks_mut(2)); + .enumerate() + .filter_map(|(idx, val)| if idx != 1 { Some(val) } else { None }) + .zip(software_result_f64.chunks_mut(2)) + { + let arg1 = utils::q1_31_to_f64(arg1); - let software_start_point = embassy_time::Instant::now(); - - for ((arg1, arg2), res) in software_inputs { let (raw_res1, raw_res2) = (arg1 * core::f64::consts::PI).sin_cos(); - (res[0], res[1]) = (raw_res1 * arg2, raw_res2 * arg2); } - let software_end_point = embassy_time::Instant::now(); + // + // check result are the same + // - for (cordic_res, software_res) in cordic_output_f64_buf[..cnt] + for (cordic_res, software_res) in cordic_result_f64[..cnt0 + cnt1] .chunks(2) - .zip(software_output_f64_buf.chunks(2)) + .zip(software_result_f64.chunks(2)) { for (cord_res, soft_res) in cordic_res.iter().zip(software_res.iter()) { + // 2.0.powi(-19) is the max residual error for Sin function, in q1.31 format, with 24 iterations (aka PRECISION = 6) defmt::assert!((cord_res - soft_res).abs() <= 2.0.powi(-19)); } } - // This comparison is just for fun. Since it not a equal compare: - // software use 64-bit floating point, but CORDIC use 32-bit fixed point. - defmt::trace!( - "calculate count: {}, Cordic time: {} us, software time: {} us", - ARG1_LENGTH, - (cordic_end_point - cordic_start_point).as_micros(), - (software_end_point - software_start_point).as_micros() - ); - info!("Test OK"); cortex_m::asm::bkpt(); } From 8fa1d06a6a7bf3d00ac87319ac71953237535c43 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Sat, 23 Mar 2024 09:04:09 +0800 Subject: [PATCH 24/69] stm32 CORDIC: use private_bounds for sealed traits. --- embassy-stm32/src/cordic/mod.rs | 16 ++++++---------- embassy-stm32/src/cordic/sealed.rs | 2 +- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index 2479e1b27..6bbc48f2b 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs @@ -11,16 +11,11 @@ pub use enums::*; mod errors; pub use errors::*; +mod sealed; +use self::sealed::SealedInstance; + pub mod utils; -pub(crate) mod sealed; - -/// Low-level CORDIC access. -#[cfg(feature = "unstable-pac")] -pub mod low_level { - pub use super::sealed::*; -} - /// CORDIC driver pub struct Cordic<'d, T: Instance> { peri: PeripheralRef<'d, T>, @@ -28,7 +23,8 @@ pub struct Cordic<'d, T: Instance> { } /// CORDIC instance trait -pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance + Peripheral

+ crate::rcc::RccPeripheral {} /// CORDIC configuration #[derive(Debug)] @@ -609,7 +605,7 @@ foreach_interrupt!( impl Instance for peripherals::$inst { } - impl sealed::Instance for peripherals::$inst { + impl SealedInstance for peripherals::$inst { fn regs() -> crate::pac::cordic::Cordic { crate::pac::$inst } diff --git a/embassy-stm32/src/cordic/sealed.rs b/embassy-stm32/src/cordic/sealed.rs index f9521ff7a..8f0bd1830 100644 --- a/embassy-stm32/src/cordic/sealed.rs +++ b/embassy-stm32/src/cordic/sealed.rs @@ -2,7 +2,7 @@ use super::*; use crate::pac::cordic::vals; /// Cordic instance -pub trait Instance { +pub(super) trait SealedInstance { /// Get access to CORDIC registers fn regs() -> crate::pac::cordic::Cordic; From 79eabc95aa3b627dcfec92491979b433e25a09ba Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Sat, 23 Mar 2024 09:53:19 +0800 Subject: [PATCH 25/69] stm32 CORDIC: add g491re back to cordic test --- tests/stm32/Cargo.toml | 2 +- tests/stm32/src/bin/cordic.rs | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 345c72a03..b19af98a0 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -14,7 +14,7 @@ stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma", "dac", "sdmmc"] stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"] stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac", "ucpd"] -stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan"] +stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan", "cordic"] stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "hash", "cordic"] stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash", "cryp"] stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash", "cryp"] diff --git a/tests/stm32/src/bin/cordic.rs b/tests/stm32/src/bin/cordic.rs index 669fd96ab..400e10207 100644 --- a/tests/stm32/src/bin/cordic.rs +++ b/tests/stm32/src/bin/cordic.rs @@ -2,11 +2,6 @@ // Test Cordic driver, with Q1.31 format, Sin function, at 24 iterations (aka PRECISION = 6), using DMA transfer -// Only test on STM32H563ZI, STM32U585AI and STM32U5a5JI. -// STM32G491RE is not tested, since it memory.x has less memory size than it actually has, -// and the test seems use more memory than memory.x suggest. -// see https://github.com/embassy-rs/stm32-data/issues/301#issuecomment-1925412561 - #![no_std] #![no_main] @@ -69,11 +64,11 @@ async fn main(_spawner: Spawner) { )), ); - //#[cfg(feature = "stm32g491re")] - //let (mut write_dma, mut read_dma) = (dp.DMA1_CH4, dp.DMA1_CH5); + #[cfg(feature = "stm32g491re")] + let (mut write_dma, mut read_dma) = (dp.DMA1_CH4, dp.DMA1_CH5); #[cfg(any(feature = "stm32h563zi", feature = "stm32u585ai", feature = "stm32u5a5zj"))] - let (mut write_dma, mut read_dma) = (dp.GPDMA1_CH4, dp.GPDMA1_CH5); + let (mut write_dma, mut read_dma) = (dp.GPDMA1_CH0, dp.GPDMA1_CH1); // calculate first result using blocking mode let cnt0 = defmt::unwrap!(cordic.blocking_calc_32bit(&input_q1_31[..2], &mut output_q1_31, false, false)); From 6b2e15e318c05a66f17575cde4987353a52108a4 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Tue, 26 Mar 2024 14:43:09 +0800 Subject: [PATCH 26/69] stm32 CORDIC: exclude stm32u5a --- embassy-stm32/build.rs | 9 +++++++-- embassy-stm32/src/lib.rs | 5 +++-- tests/stm32/Cargo.toml | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index e224cc5a2..057c4cee2 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1139,13 +1139,18 @@ fn main() { (("timer", "CH2"), quote!(crate::timer::Ch2Dma)), (("timer", "CH3"), quote!(crate::timer::Ch3Dma)), (("timer", "CH4"), quote!(crate::timer::Ch4Dma)), - (("cordic", "WRITE"), quote!(crate::cordic::WriteDma)), - (("cordic", "READ"), quote!(crate::cordic::ReadDma)), + (("cordic", "WRITE"), quote!(crate::cordic::WriteDma)), // FIXME: stm32u5a crash on Cordic driver + (("cordic", "READ"), quote!(crate::cordic::ReadDma)), // FIXME: stm32u5a crash on Cordic driver ] .into(); for p in METADATA.peripherals { if let Some(regs) = &p.registers { + // FIXME: stm32u5a crash on Cordic driver + if chip_name.starts_with("stm32u5a") && regs.kind == "cordic" { + continue; + } + let mut dupe = HashSet::new(); for ch in p.dma_channels { // Some chips have multiple request numbers for the same (peri, signal, channel) combos. diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index ae2e95435..dd4aef51e 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -32,7 +32,8 @@ pub mod timer; pub mod adc; #[cfg(can)] pub mod can; -#[cfg(cordic)] +// FIXME: Cordic driver cause stm32u5a5zj crash +#[cfg(all(cordic, not(any(stm32u5a5, stm32u5a9))))] pub mod cordic; #[cfg(crc)] pub mod crc; @@ -236,7 +237,7 @@ pub fn init(config: Config) -> Peripherals { #[cfg(dbgmcu)] crate::pac::DBGMCU.cr().modify(|cr| { - #[cfg(any(dbgmcu_h5))] + #[cfg(dbgmcu_h5)] { cr.set_stop(config.enable_debug_during_sleep); cr.set_standby(config.enable_debug_during_sleep); diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index b19af98a0..e09083111 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -26,7 +26,7 @@ stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash" stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"] stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"] stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash", "cordic"] -stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng", "hash", "cordic"] +stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"] stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng", "hash"] stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] From 82046c46913dd9a45ec035805eb504d54edfefd5 Mon Sep 17 00:00:00 2001 From: schmettow Date: Tue, 26 Mar 2024 22:26:44 +0100 Subject: [PATCH 27/69] Added YLab project to "in the wild" YLab uses Embassy --- docs/modules/ROOT/pages/embassy_in_the_wild.adoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/modules/ROOT/pages/embassy_in_the_wild.adoc b/docs/modules/ROOT/pages/embassy_in_the_wild.adoc index 85ad7f4a2..75208a883 100644 --- a/docs/modules/ROOT/pages/embassy_in_the_wild.adoc +++ b/docs/modules/ROOT/pages/embassy_in_the_wild.adoc @@ -9,3 +9,11 @@ Here are known examples of real-world projects which make use of Embassy. Feel f * The link:https://github.com/lora-rs/lora-rs[lora-rs] project includes link:https://github.com/lora-rs/lora-rs/tree/main/examples/stm32l0/src/bin[various standalone examples] for NRF52840, RP2040, STM32L0 and STM32WL ** link:https://github.com/matoushybl/air-force-one[Air force one: A simple air quality monitoring system] *** Targets nRF52 and uses nrf-softdevice + +* link:https://github.com/schmettow/ylab-edge-go[YLab Edge Go] and link:https://github.com/schmettow/ylab-edge-pro[YLab Edge Pro] projects develop +firmware (RP2040, STM32) for capturing physiological data in behavioural science research. Included so far are: +** biopotentials (analog ports) +** motion capture (6-axis accelerometers) +** air quality (CO2, Temp, Humidity) +** comes with an app for capturing and visualizing data [link:https://github.com/schmettow/ystudio[Ystudio]] + From 746ded94b13d037c5be16071d2d9d195b979c0c7 Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 20 Mar 2024 19:52:36 +0100 Subject: [PATCH 28/69] Fix minor typos --- embassy-stm32/src/i2c/v1.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 9f29ed5e0..563bbfdaf 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -58,10 +58,10 @@ pub unsafe fn on_interrupt() { /// - `ST` = start condition /// - `SR` = repeated start condition /// - `SP` = stop condition +/// - `ACK`/`NACK` = last byte in read operation #[derive(Copy, Clone)] enum FrameOptions { - /// `[ST/SR]+[NACK]+[SP]` First frame (of this type) in operation and last frame overall in this - /// transaction. + /// `[ST/SR]+[NACK]+[SP]` First frame (of this type) in transaction and also last frame overall. FirstAndLastFrame, /// `[ST/SR]+[NACK]` First frame of this type in transaction, last frame in a read operation but /// not the last frame overall. From 0885c102d3c332591b2d913113cde08fca8fe41b Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 20 Mar 2024 20:11:59 +0100 Subject: [PATCH 29/69] Refactor async I2C transfers to use frame options --- embassy-stm32/src/i2c/v1.rs | 341 +++++++++++++++++++----------------- 1 file changed, 183 insertions(+), 158 deletions(-) diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 563bbfdaf..2751d443f 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -459,7 +459,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }); } - async fn write_with_stop(&mut self, address: u8, write: &[u8], send_stop: bool) -> Result<(), Error> + async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { @@ -487,74 +487,76 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }) }); - Self::enable_interrupts(); - - // Send a START condition - T::regs().cr1().modify(|reg| { - reg.set_start(true); - }); - let state = T::state(); - // Wait until START condition was generated - poll_fn(|cx| { - state.waker.register(cx.waker()); + if frame.send_start() { + // Send a START condition + Self::enable_interrupts(); + T::regs().cr1().modify(|reg| { + reg.set_start(true); + }); - match Self::check_and_clear_error_flags() { - Err(e) => Poll::Ready(Err(e)), - Ok(sr1) => { - if sr1.start() { - Poll::Ready(Ok(())) - } else { - Poll::Pending + // Wait until START condition was generated + poll_fn(|cx| { + state.waker.register(cx.waker()); + + match Self::check_and_clear_error_flags() { + Err(e) => Poll::Ready(Err(e)), + Ok(sr1) => { + if sr1.start() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } } } - } - }) - .await?; + }) + .await?; - // Also wait until signalled we're master and everything is waiting for us - Self::enable_interrupts(); - poll_fn(|cx| { - state.waker.register(cx.waker()); + // Also wait until signalled we're master and everything is waiting for us + Self::enable_interrupts(); + poll_fn(|cx| { + state.waker.register(cx.waker()); - match Self::check_and_clear_error_flags() { - Err(e) => Poll::Ready(Err(e)), - Ok(_) => { - let sr2 = T::regs().sr2().read(); - if !sr2.msl() && !sr2.busy() { - Poll::Pending - } else { - Poll::Ready(Ok(())) + match Self::check_and_clear_error_flags() { + Err(e) => Poll::Ready(Err(e)), + Ok(_) => { + let sr2 = T::regs().sr2().read(); + if !sr2.msl() && !sr2.busy() { + Poll::Pending + } else { + Poll::Ready(Ok(())) + } } } - } - }) - .await?; + }) + .await?; - // Set up current address, we're trying to talk to - Self::enable_interrupts(); - T::regs().dr().write(|reg| reg.set_dr(address << 1)); + // Set up current address, we're trying to talk to + Self::enable_interrupts(); + T::regs().dr().write(|reg| reg.set_dr(address << 1)); - poll_fn(|cx| { - state.waker.register(cx.waker()); - match Self::check_and_clear_error_flags() { - Err(e) => Poll::Ready(Err(e)), - Ok(sr1) => { - if sr1.addr() { - // Clear the ADDR condition by reading SR2. - T::regs().sr2().read(); - Poll::Ready(Ok(())) - } else { - // If we need to go around, then re-enable the interrupts, otherwise nothing - // can wake us up and we'll hang. - Self::enable_interrupts(); - Poll::Pending + poll_fn(|cx| { + state.waker.register(cx.waker()); + match Self::check_and_clear_error_flags() { + Err(e) => Poll::Ready(Err(e)), + Ok(sr1) => { + if sr1.addr() { + // Clear the ADDR condition by reading SR2. + T::regs().sr2().read(); + Poll::Ready(Ok(())) + } else { + // If we need to go around, then re-enable the interrupts, otherwise nothing + // can wake us up and we'll hang. + Self::enable_interrupts(); + Poll::Pending + } } } - } - }) - .await?; + }) + .await?; + } + Self::enable_interrupts(); let poll_error = poll_fn(|cx| { state.waker.register(cx.waker()); @@ -591,7 +593,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if sr1.btf() { - if send_stop { + if frame.send_stop() { T::regs().cr1().modify(|w| { w.set_stop(true); }); @@ -606,6 +608,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }) .await?; + if frame.send_stop() { + // Wait for STOP condition to transmit. + Self::enable_interrupts(); + poll_fn(|cx| { + T::state().waker.register(cx.waker()); + // TODO: error interrupts are enabled here, should we additional check for and return errors? + if T::regs().cr1().read().stop() { + Poll::Pending + } else { + Poll::Ready(Ok(())) + } + }) + .await?; + } + drop(on_drop); // Fallthrough is success @@ -617,26 +634,24 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { where TXDMA: crate::i2c::TxDma, { - self.write_with_stop(address, write, true).await?; - - // Wait for STOP condition to transmit. - Self::enable_interrupts(); - poll_fn(|cx| { - T::state().waker.register(cx.waker()); - // TODO: error interrupts are enabled here, should we additional check for and return errors? - if T::regs().cr1().read().stop() { - Poll::Pending - } else { - Poll::Ready(Ok(())) - } - }) - .await?; + self.write_frame(address, write, FrameOptions::FirstAndLastFrame) + .await?; Ok(()) } /// Read. pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> + where + RXDMA: crate::i2c::RxDma, + { + 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, { @@ -667,98 +682,99 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }) }); - Self::enable_interrupts(); + if frame.send_start() { + // Send a START condition and set ACK bit + Self::enable_interrupts(); + T::regs().cr1().modify(|reg| { + reg.set_start(true); + reg.set_ack(true); + }); - // Send a START condition and set ACK bit - T::regs().cr1().modify(|reg| { - reg.set_start(true); - reg.set_ack(true); - }); + // Wait until START condition was generated + poll_fn(|cx| { + state.waker.register(cx.waker()); - // Wait until START condition was generated - poll_fn(|cx| { - state.waker.register(cx.waker()); - - match Self::check_and_clear_error_flags() { - Err(e) => Poll::Ready(Err(e)), - Ok(sr1) => { - if sr1.start() { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - } - } - }) - .await?; - - // Also wait until signalled we're master and everything is waiting for us - Self::enable_interrupts(); - poll_fn(|cx| { - state.waker.register(cx.waker()); - - // blocking read didn’t have a check_and_clear call here, but blocking write did so - // I’m adding it here in case that was an oversight. - match Self::check_and_clear_error_flags() { - Err(e) => Poll::Ready(Err(e)), - Ok(_) => { - let sr2 = T::regs().sr2().read(); - if !sr2.msl() && !sr2.busy() { - Poll::Pending - } else { - Poll::Ready(Ok(())) - } - } - } - }) - .await?; - - // Set up current address, we're trying to talk to - T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1)); - - // Wait for the address to be acknowledged - - Self::enable_interrupts(); - poll_fn(|cx| { - state.waker.register(cx.waker()); - - match Self::check_and_clear_error_flags() { - Err(e) => Poll::Ready(Err(e)), - Ok(sr1) => { - if sr1.addr() { - // 18.3.8: When a single byte must be received: the NACK must be programmed during EV6 - // event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag. - if buffer_len == 1 { - T::regs().cr1().modify(|w| { - w.set_ack(false); - }); + match Self::check_and_clear_error_flags() { + Err(e) => Poll::Ready(Err(e)), + Ok(sr1) => { + if sr1.start() { + Poll::Ready(Ok(())) + } else { + Poll::Pending } - Poll::Ready(Ok(())) - } else { - Poll::Pending } } - } - }) - .await?; + }) + .await?; - // Clear ADDR condition by reading SR2 - T::regs().sr2().read(); + // Also wait until signalled we're master and everything is waiting for us + Self::enable_interrupts(); + poll_fn(|cx| { + state.waker.register(cx.waker()); + + // blocking read didn’t have a check_and_clear call here, but blocking write did so + // I’m adding it here in case that was an oversight. + match Self::check_and_clear_error_flags() { + Err(e) => Poll::Ready(Err(e)), + Ok(_) => { + let sr2 = T::regs().sr2().read(); + if !sr2.msl() && !sr2.busy() { + Poll::Pending + } else { + Poll::Ready(Ok(())) + } + } + } + }) + .await?; + + // Set up current address, we're trying to talk to + T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1)); + + // Wait for the address to be acknowledged + + Self::enable_interrupts(); + poll_fn(|cx| { + state.waker.register(cx.waker()); + + match Self::check_and_clear_error_flags() { + Err(e) => Poll::Ready(Err(e)), + Ok(sr1) => { + if sr1.addr() { + // 18.3.8: When a single byte must be received: the NACK must be programmed during EV6 + // event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag. + if buffer_len == 1 && frame.send_nack() { + T::regs().cr1().modify(|w| { + w.set_ack(false); + }); + } + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + } + } + }) + .await?; + + // Clear ADDR condition by reading SR2 + T::regs().sr2().read(); + } // 18.3.8: When a single byte must be received: [snip] Then the // user can program the STOP condition either after clearing ADDR flag, or in the // DMA Transfer Complete interrupt routine. - if buffer_len == 1 { + if buffer_len == 1 && frame.send_stop() { T::regs().cr1().modify(|w| { w.set_stop(true); }); - } else { + } else if buffer_len != 1 && frame.send_nack() { // If, in the I2C_CR2 register, the LAST bit is set, I2C // automatically sends a NACK after the next byte following EOT_1. The user can // generate a Stop condition in the DMA Transfer Complete interrupt routine if enabled. T::regs().cr2().modify(|w| { w.set_last(true); - }) + }); } // Wait for bytes to be received, or an error to occur. @@ -777,18 +793,27 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { _ => Ok(()), }?; - // Wait for the STOP to be sent (STOP bit cleared). - Self::enable_interrupts(); - poll_fn(|cx| { - state.waker.register(cx.waker()); - // TODO: error interrupts are enabled here, should we additional check for and return errors? - if T::regs().cr1().read().stop() { - Poll::Pending - } else { - Poll::Ready(Ok(())) + if frame.send_stop() { + if buffer_len != 1 { + T::regs().cr1().modify(|w| { + w.set_stop(true); + }); } - }) - .await?; + + // Wait for the STOP to be sent (STOP bit cleared). + Self::enable_interrupts(); + poll_fn(|cx| { + state.waker.register(cx.waker()); + // TODO: error interrupts are enabled here, should we additional check for and return errors? + if T::regs().cr1().read().stop() { + Poll::Pending + } else { + Poll::Ready(Ok(())) + } + }) + .await?; + } + drop(on_drop); // Fallthrough is success @@ -801,8 +826,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { RXDMA: crate::i2c::RxDma, TXDMA: crate::i2c::TxDma, { - self.write_with_stop(address, write, false).await?; - self.read(address, read).await + self.write_frame(address, write, FrameOptions::FirstFrame).await?; + self.read_frame(address, read, FrameOptions::FirstAndLastFrame).await } } From 9c00a40e73f49aa0d46a47259fe8adc8c3f248b8 Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Thu, 21 Mar 2024 00:25:39 +0100 Subject: [PATCH 30/69] Extract frame options generation into iterator to reuse in async --- embassy-stm32/src/i2c/v1.rs | 102 ++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 44 deletions(-) diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 2751d443f..dd2cea6b8 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -5,7 +5,7 @@ //! All other devices (as of 2023-12-28) use [`v2`](super::v2) instead. use core::future::poll_fn; -use core::task::Poll; +use core::{iter, task::Poll}; use embassy_embedded_hal::SetConfig; use embassy_futures::select::{select, Either}; @@ -103,6 +103,62 @@ impl FrameOptions { } } +/// Iterates over operations in transaction. +/// +/// Returns necessary frame options for each operation to uphold the [transaction contract] and have +/// the right start/stop/(N)ACK conditions on the wire. +/// +/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction +fn operation_frames<'a, 'b: 'a>( + operations: &'a mut [Operation<'b>], +) -> impl IntoIterator, FrameOptions)> { + let mut operations = operations.iter_mut().peekable(); + + let mut next_first_frame = true; + + iter::from_fn(move || { + let Some(op) = operations.next() else { + return None; + }; + + // Is `op` first frame of its type? + let first_frame = next_first_frame; + let next_op = operations.peek(); + + // Get appropriate frame options as combination of the following properties: + // + // - For each first operation of its type, generate a (repeated) start condition. + // - For the last operation overall in the entire transaction, generate a stop condition. + // - For read operations, check the next operation: if it is also a read operation, we merge + // these and send ACK for all bytes in the current operation; send NACK only for the final + // read operation's last byte (before write or end of entire transaction) to indicate last + // byte read and release the bus for transmission of the bus master's next byte (or stop). + // + // We check the third property unconditionally, i.e. even for write opeartions. This is okay + // because the resulting frame options are identical for write operations. + let frame = match (first_frame, next_op) { + (true, None) => FrameOptions::FirstAndLastFrame, + (true, Some(Operation::Read(_))) => FrameOptions::FirstAndNextFrame, + (true, Some(Operation::Write(_))) => FrameOptions::FirstFrame, + // + (false, None) => FrameOptions::LastFrame, + (false, Some(Operation::Read(_))) => FrameOptions::NextFrame, + (false, Some(Operation::Write(_))) => FrameOptions::LastFrameNoStop, + }; + + // Pre-calculate if `next_op` is the first operation of its type. We do this here and not at + // the beginning of the loop because we hand out `op` as iterator value and cannot access it + // anymore in the next iteration. + next_first_frame = match (&op, next_op) { + (_, None) => false, + (Operation::Read(_), Some(Operation::Write(_))) | (Operation::Write(_), Some(Operation::Read(_))) => true, + (Operation::Read(_), Some(Operation::Read(_))) | (Operation::Write(_), Some(Operation::Write(_))) => false, + }; + + Some((op, frame)) + }) +} + impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { T::regs().cr1().modify(|reg| { @@ -397,53 +453,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let timeout = self.timeout(); - let mut operations = operations.iter_mut(); - - let mut prev_op: Option<&mut Operation<'_>> = None; - let mut next_op = operations.next(); - - while let Some(op) = next_op { - next_op = operations.next(); - - // Check if this is the first frame of this type. This is the case for the first overall - // frame in the transaction and whenever the type of operation changes. - let first_frame = - match (prev_op.as_ref(), &op) { - (None, _) => true, - (Some(Operation::Read(_)), Operation::Write(_)) - | (Some(Operation::Write(_)), Operation::Read(_)) => true, - (Some(Operation::Read(_)), Operation::Read(_)) - | (Some(Operation::Write(_)), Operation::Write(_)) => false, - }; - - let frame = match (first_frame, next_op.as_ref()) { - // If this is the first frame of this type, we generate a (repeated) start condition - // but have to consider the next operation: if it is the last, we generate the final - // stop condition. Otherwise, we branch on the operation: with read operations, only - // the last byte overall (before a write operation or the end of the transaction) is - // to be NACK'd, i.e. if another read operation follows, we must ACK this last byte. - (true, None) => FrameOptions::FirstAndLastFrame, - // Make sure to keep sending ACK for last byte in read operation when it is followed - // by another consecutive read operation. If the current operation is write, this is - // identical to `FirstFrame`. - (true, Some(Operation::Read(_))) => FrameOptions::FirstAndNextFrame, - // Otherwise, send NACK for last byte (in read operation). (For write, this does not - // matter and could also be `FirstAndNextFrame`.) - (true, Some(Operation::Write(_))) => FrameOptions::FirstFrame, - - // If this is not the first frame of its type, we do not generate a (repeated) start - // condition. Otherwise, we branch the same way as above. - (false, None) => FrameOptions::LastFrame, - (false, Some(Operation::Read(_))) => FrameOptions::NextFrame, - (false, Some(Operation::Write(_))) => FrameOptions::LastFrameNoStop, - }; - + for (op, frame) in operation_frames(operations) { match op { Operation::Read(read) => self.blocking_read_timeout(addr, read, timeout, frame)?, Operation::Write(write) => self.write_bytes(addr, write, timeout, frame)?, } - - prev_op = Some(op); } Ok(()) From accec7a84076ff26f9bdad388809b96537cf31da Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Thu, 21 Mar 2024 00:30:53 +0100 Subject: [PATCH 31/69] Implement asynchronous transaction for I2C v1 --- embassy-stm32/src/i2c/mod.rs | 4 +- embassy-stm32/src/i2c/v1.rs | 214 ++++++++++++++++++++--------------- 2 files changed, 123 insertions(+), 95 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index f1b11cc44..6700f0f7d 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -332,8 +332,6 @@ impl<'d, T: Instance, TXDMA: TxDma, RXDMA: RxDma> embedded_hal_async::i2c: address: u8, operations: &mut [embedded_hal_1::i2c::Operation<'_>], ) -> Result<(), Self::Error> { - let _ = address; - let _ = operations; - todo!() + self.transaction(address, operations).await } } diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index dd2cea6b8..a740ab834 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -111,12 +111,21 @@ impl FrameOptions { /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction fn operation_frames<'a, 'b: 'a>( operations: &'a mut [Operation<'b>], -) -> impl IntoIterator, FrameOptions)> { +) -> Result, FrameOptions)>, Error> { + // Check empty read buffer before starting transaction. Otherwise, we would risk halting with an + // error in the middle of the transaction. + if operations.iter().any(|op| match op { + Operation::Read(read) => read.is_empty(), + Operation::Write(_) => false, + }) { + return Err(Error::Overrun); + } + let mut operations = operations.iter_mut().peekable(); let mut next_first_frame = true; - iter::from_fn(move || { + Ok(iter::from_fn(move || { let Some(op) = operations.next() else { return None; }; @@ -156,7 +165,7 @@ fn operation_frames<'a, 'b: 'a>( }; Some((op, frame)) - }) + })) } impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { @@ -442,18 +451,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { - // Check empty read buffer before starting transaction. Otherwise, we would not generate the - // stop condition below. - if operations.iter().any(|op| match op { - Operation::Read(read) => read.is_empty(), - Operation::Write(_) => false, - }) { - return Err(Error::Overrun); - } - let timeout = self.timeout(); - for (op, frame) in operation_frames(operations) { + for (op, frame) in operation_frames(operations)? { match op { Operation::Read(read) => self.blocking_read_timeout(addr, read, timeout, frame)?, Operation::Write(write) => self.write_bytes(addr, write, timeout, frame)?, @@ -480,9 +480,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let dma_transfer = unsafe { let regs = T::regs(); regs.cr2().modify(|w| { + // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for reception. + w.set_itbufen(false); // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register. w.set_dmaen(true); - w.set_itbufen(false); + // Sending NACK is not necessary (nor possible) for write transfer. + w.set_last(false); }); // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event. let dst = regs.dr().as_ptr() as *mut u8; @@ -520,6 +523,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if sr1.start() { Poll::Ready(Ok(())) } else { + // If we need to go around, then re-enable the interrupts, otherwise nothing + // can wake us up and we'll hang. + Self::enable_interrupts(); Poll::Pending } } @@ -537,6 +543,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(_) => { let sr2 = T::regs().sr2().read(); if !sr2.msl() && !sr2.busy() { + // If we need to go around, then re-enable the interrupts, otherwise nothing + // can wake us up and we'll hang. + Self::enable_interrupts(); Poll::Pending } else { Poll::Ready(Ok(())) @@ -550,14 +559,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Self::enable_interrupts(); T::regs().dr().write(|reg| reg.set_dr(address << 1)); + // Wait for the address to be acknowledged poll_fn(|cx| { state.waker.register(cx.waker()); + match Self::check_and_clear_error_flags() { Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if sr1.addr() { - // Clear the ADDR condition by reading SR2. - T::regs().sr2().read(); Poll::Ready(Ok(())) } else { // If we need to go around, then re-enable the interrupts, otherwise nothing @@ -569,8 +578,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } }) .await?; + + // Clear condition by reading SR2 + T::regs().sr2().read(); } + // Wait for bytes to be sent, or an error to occur. Self::enable_interrupts(); let poll_error = poll_fn(|cx| { state.waker.register(cx.waker()); @@ -579,7 +592,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Unclear why the Err turbofish is necessary here? The compiler didn’t require it in the other // identical poll_fn check_and_clear matches. Err(e) => Poll::Ready(Err::(e)), - Ok(_) => Poll::Pending, + Ok(_) => { + // If we need to go around, then re-enable the interrupts, otherwise nothing + // can wake us up and we'll hang. + Self::enable_interrupts(); + Poll::Pending + } } }); @@ -589,52 +607,38 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { _ => Ok(()), }?; - // The I2C transfer itself will take longer than the DMA transfer, so wait for that to finish too. - - // 18.3.8 “Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA - // requests then wait for a BTF event before programming the Stop condition.” - - // TODO: If this has to be done “in the interrupt routine after the EOT interrupt”, where to put it? T::regs().cr2().modify(|w| { w.set_dmaen(false); }); - Self::enable_interrupts(); - poll_fn(|cx| { - state.waker.register(cx.waker()); - - match Self::check_and_clear_error_flags() { - Err(e) => Poll::Ready(Err(e)), - Ok(sr1) => { - if sr1.btf() { - if frame.send_stop() { - T::regs().cr1().modify(|w| { - w.set_stop(true); - }); - } - - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - } - } - }) - .await?; - if frame.send_stop() { - // Wait for STOP condition to transmit. + // The I2C transfer itself will take longer than the DMA transfer, so wait for that to finish too. + + // 18.3.8 “Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA + // requests then wait for a BTF event before programming the Stop condition.” Self::enable_interrupts(); poll_fn(|cx| { - T::state().waker.register(cx.waker()); - // TODO: error interrupts are enabled here, should we additional check for and return errors? - if T::regs().cr1().read().stop() { - Poll::Pending - } else { - Poll::Ready(Ok(())) + state.waker.register(cx.waker()); + + match Self::check_and_clear_error_flags() { + Err(e) => Poll::Ready(Err(e)), + Ok(sr1) => { + if sr1.btf() { + Poll::Ready(Ok(())) + } else { + // If we need to go around, then re-enable the interrupts, otherwise nothing + // can wake us up and we'll hang. + Self::enable_interrupts(); + Poll::Pending + } + } } }) .await?; + + T::regs().cr1().modify(|w| { + w.set_stop(true); + }); } drop(on_drop); @@ -669,15 +673,19 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { where RXDMA: crate::i2c::RxDma, { - let state = T::state(); let buffer_len = buffer.len(); let dma_transfer = unsafe { let regs = T::regs(); regs.cr2().modify(|w| { - // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register. + // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for reception. w.set_itbufen(false); + // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register. w.set_dmaen(true); + // If, in the I2C_CR2 register, the LAST bit is set, I2C + // automatically sends a NACK after the next byte following EOT_1. The user can + // generate a Stop condition in the DMA Transfer Complete interrupt routine if enabled. + w.set_last(frame.send_nack() && buffer_len != 1); }); // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event. let src = regs.dr().as_ptr() as *mut u8; @@ -696,6 +704,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }) }); + let state = T::state(); + if frame.send_start() { // Send a START condition and set ACK bit Self::enable_interrupts(); @@ -714,6 +724,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if sr1.start() { Poll::Ready(Ok(())) } else { + // If we need to go around, then re-enable the interrupts, otherwise nothing + // can wake us up and we'll hang. + Self::enable_interrupts(); Poll::Pending } } @@ -733,6 +746,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Ok(_) => { let sr2 = T::regs().sr2().read(); if !sr2.msl() && !sr2.busy() { + // If we need to go around, then re-enable the interrupts, otherwise nothing + // can wake us up and we'll hang. + Self::enable_interrupts(); Poll::Pending } else { Poll::Ready(Ok(())) @@ -743,11 +759,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { .await?; // Set up current address, we're trying to talk to + Self::enable_interrupts(); T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1)); // Wait for the address to be acknowledged - - Self::enable_interrupts(); poll_fn(|cx| { state.waker.register(cx.waker()); @@ -755,15 +770,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { Err(e) => Poll::Ready(Err(e)), Ok(sr1) => { if sr1.addr() { - // 18.3.8: When a single byte must be received: the NACK must be programmed during EV6 - // event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag. - if buffer_len == 1 && frame.send_nack() { - T::regs().cr1().modify(|w| { - w.set_ack(false); - }); - } Poll::Ready(Ok(())) } else { + // If we need to go around, then re-enable the interrupts, otherwise nothing + // can wake us up and we'll hang. + Self::enable_interrupts(); Poll::Pending } } @@ -771,24 +782,29 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }) .await?; - // Clear ADDR condition by reading SR2 + // 18.3.8: When a single byte must be received: the NACK must be programmed during EV6 + // event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag. + if frame.send_nack() && buffer_len == 1 { + T::regs().cr1().modify(|w| { + w.set_ack(false); + }); + } + + // Clear condition by reading SR2 T::regs().sr2().read(); + } else if frame.send_nack() && buffer_len == 1 { + T::regs().cr1().modify(|w| { + w.set_ack(false); + }); } // 18.3.8: When a single byte must be received: [snip] Then the // user can program the STOP condition either after clearing ADDR flag, or in the // DMA Transfer Complete interrupt routine. - if buffer_len == 1 && frame.send_stop() { + if frame.send_stop() && buffer_len == 1 { T::regs().cr1().modify(|w| { w.set_stop(true); }); - } else if buffer_len != 1 && frame.send_nack() { - // If, in the I2C_CR2 register, the LAST bit is set, I2C - // automatically sends a NACK after the next byte following EOT_1. The user can - // generate a Stop condition in the DMA Transfer Complete interrupt routine if enabled. - T::regs().cr2().modify(|w| { - w.set_last(true); - }); } // Wait for bytes to be received, or an error to occur. @@ -798,7 +814,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { match Self::check_and_clear_error_flags() { Err(e) => Poll::Ready(Err::(e)), - _ => Poll::Pending, + _ => { + // If we need to go around, then re-enable the interrupts, otherwise nothing + // can wake us up and we'll hang. + Self::enable_interrupts(); + Poll::Pending + } } }); @@ -807,25 +828,14 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { _ => Ok(()), }?; - if frame.send_stop() { - if buffer_len != 1 { - T::regs().cr1().modify(|w| { - w.set_stop(true); - }); - } + T::regs().cr2().modify(|w| { + w.set_dmaen(false); + }); - // Wait for the STOP to be sent (STOP bit cleared). - Self::enable_interrupts(); - poll_fn(|cx| { - state.waker.register(cx.waker()); - // TODO: error interrupts are enabled here, should we additional check for and return errors? - if T::regs().cr1().read().stop() { - Poll::Pending - } else { - Poll::Ready(Ok(())) - } - }) - .await?; + if frame.send_stop() && buffer_len != 1 { + T::regs().cr1().modify(|w| { + w.set_stop(true); + }); } drop(on_drop); @@ -843,6 +853,26 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { self.write_frame(address, write, FrameOptions::FirstFrame).await?; self.read_frame(address, read, FrameOptions::FirstAndLastFrame).await } + + /// 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, + { + for (op, frame) in operation_frames(operations)? { + match op { + Operation::Read(read) => self.read_frame(addr, read, frame).await?, + Operation::Write(write) => self.write_frame(addr, write, frame).await?, + } + } + + Ok(()) + } } impl<'d, T: Instance, TXDMA, RXDMA> Drop for I2c<'d, T, TXDMA, RXDMA> { From c1175bf7d850d6e9091853e6e9980b11407b5a21 Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 27 Mar 2024 00:04:35 +0100 Subject: [PATCH 32/69] It is not necessary to wait for STOP to be fully generated --- embassy-stm32/src/i2c/v1.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index a740ab834..1e2205389 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -296,10 +296,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if frame.send_stop() { // Send a STOP condition T::regs().cr1().modify(|reg| reg.set_stop(true)); - // Wait for STOP condition to transmit. - while T::regs().cr1().read().stop() { - timeout.check()?; - } } // Fallthrough is success @@ -405,13 +401,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Receive last byte *last = self.recv_byte(timeout)?; - if frame.send_stop() { - // Wait for the STOP to be sent. - while T::regs().cr1().read().stop() { - timeout.check()?; - } - } - // Fallthrough is success Ok(()) } From 2e2986c67b3a7a6a87695a359d630864c9eb6194 Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 27 Mar 2024 00:17:12 +0100 Subject: [PATCH 33/69] It is not necessary to wait for SB and MSL sequentially --- embassy-stm32/src/i2c/v1.rs | 72 ++++++++----------------------------- 1 file changed, 14 insertions(+), 58 deletions(-) diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 1e2205389..1e0eea33c 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -264,14 +264,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { timeout.check()?; } - // Also wait until signalled we're master and everything is waiting for us - while { - Self::check_and_clear_error_flags()?; - - let sr2 = T::regs().sr2().read(); - !sr2.msl() && !sr2.busy() - } { - timeout.check()?; + // Check if we were the ones to generate START + if T::regs().cr1().read().start() || !T::regs().sr2().read().msl() { + return Err(Error::Arbitration); } // Set up current address, we're trying to talk to @@ -362,12 +357,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { timeout.check()?; } - // Also wait until signalled we're master and everything is waiting for us - while { - let sr2 = T::regs().sr2().read(); - !sr2.msl() && !sr2.busy() - } { - timeout.check()?; + // Check if we were the ones to generate START + if T::regs().cr1().read().start() || !T::regs().sr2().read().msl() { + return Err(Error::Arbitration); } // Set up current address, we're trying to talk to @@ -522,27 +514,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }) .await?; - // Also wait until signalled we're master and everything is waiting for us - Self::enable_interrupts(); - poll_fn(|cx| { - state.waker.register(cx.waker()); - - match Self::check_and_clear_error_flags() { - Err(e) => Poll::Ready(Err(e)), - Ok(_) => { - let sr2 = T::regs().sr2().read(); - if !sr2.msl() && !sr2.busy() { - // If we need to go around, then re-enable the interrupts, otherwise nothing - // can wake us up and we'll hang. - Self::enable_interrupts(); - Poll::Pending - } else { - Poll::Ready(Ok(())) - } - } - } - }) - .await?; + // Check if we were the ones to generate START + if T::regs().cr1().read().start() || !T::regs().sr2().read().msl() { + return Err(Error::Arbitration); + } // Set up current address, we're trying to talk to Self::enable_interrupts(); @@ -723,29 +698,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { }) .await?; - // Also wait until signalled we're master and everything is waiting for us - Self::enable_interrupts(); - poll_fn(|cx| { - state.waker.register(cx.waker()); - - // blocking read didn’t have a check_and_clear call here, but blocking write did so - // I’m adding it here in case that was an oversight. - match Self::check_and_clear_error_flags() { - Err(e) => Poll::Ready(Err(e)), - Ok(_) => { - let sr2 = T::regs().sr2().read(); - if !sr2.msl() && !sr2.busy() { - // If we need to go around, then re-enable the interrupts, otherwise nothing - // can wake us up and we'll hang. - Self::enable_interrupts(); - Poll::Pending - } else { - Poll::Ready(Ok(())) - } - } - } - }) - .await?; + // Check if we were the ones to generate START + if T::regs().cr1().read().start() || !T::regs().sr2().read().msl() { + return Err(Error::Arbitration); + } // Set up current address, we're trying to talk to Self::enable_interrupts(); From b299266cd240d73bf6ec36d6a0710523ce5eb139 Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 27 Mar 2024 00:20:29 +0100 Subject: [PATCH 34/69] It is not necessary to enable interrupts before registering waker --- embassy-stm32/src/i2c/v1.rs | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 1e0eea33c..7c0bb31d6 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -489,7 +489,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if frame.send_start() { // Send a START condition - Self::enable_interrupts(); T::regs().cr1().modify(|reg| { reg.set_start(true); }); @@ -504,8 +503,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if sr1.start() { Poll::Ready(Ok(())) } else { - // If we need to go around, then re-enable the interrupts, otherwise nothing - // can wake us up and we'll hang. + // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(); Poll::Pending } @@ -520,7 +518,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } // Set up current address, we're trying to talk to - Self::enable_interrupts(); T::regs().dr().write(|reg| reg.set_dr(address << 1)); // Wait for the address to be acknowledged @@ -533,8 +530,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if sr1.addr() { Poll::Ready(Ok(())) } else { - // If we need to go around, then re-enable the interrupts, otherwise nothing - // can wake us up and we'll hang. + // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(); Poll::Pending } @@ -548,7 +544,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } // Wait for bytes to be sent, or an error to occur. - Self::enable_interrupts(); let poll_error = poll_fn(|cx| { state.waker.register(cx.waker()); @@ -557,8 +552,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // identical poll_fn check_and_clear matches. Err(e) => Poll::Ready(Err::(e)), Ok(_) => { - // If we need to go around, then re-enable the interrupts, otherwise nothing - // can wake us up and we'll hang. + // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(); Poll::Pending } @@ -580,7 +574,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // 18.3.8 “Master transmitter: In the interrupt routine after the EOT interrupt, disable DMA // requests then wait for a BTF event before programming the Stop condition.” - Self::enable_interrupts(); poll_fn(|cx| { state.waker.register(cx.waker()); @@ -590,8 +583,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if sr1.btf() { Poll::Ready(Ok(())) } else { - // If we need to go around, then re-enable the interrupts, otherwise nothing - // can wake us up and we'll hang. + // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(); Poll::Pending } @@ -672,7 +664,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if frame.send_start() { // Send a START condition and set ACK bit - Self::enable_interrupts(); T::regs().cr1().modify(|reg| { reg.set_start(true); reg.set_ack(true); @@ -688,8 +679,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if sr1.start() { Poll::Ready(Ok(())) } else { - // If we need to go around, then re-enable the interrupts, otherwise nothing - // can wake us up and we'll hang. + // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(); Poll::Pending } @@ -704,7 +694,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } // Set up current address, we're trying to talk to - Self::enable_interrupts(); T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1)); // Wait for the address to be acknowledged @@ -717,8 +706,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { if sr1.addr() { Poll::Ready(Ok(())) } else { - // If we need to go around, then re-enable the interrupts, otherwise nothing - // can wake us up and we'll hang. + // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(); Poll::Pending } @@ -753,15 +741,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } // Wait for bytes to be received, or an error to occur. - Self::enable_interrupts(); let poll_error = poll_fn(|cx| { state.waker.register(cx.waker()); match Self::check_and_clear_error_flags() { Err(e) => Poll::Ready(Err::(e)), _ => { - // If we need to go around, then re-enable the interrupts, otherwise nothing - // can wake us up and we'll hang. + // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(); Poll::Pending } From 7e44db099ca6a475af9f22fd0ebb85845553a570 Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 27 Mar 2024 00:33:04 +0100 Subject: [PATCH 35/69] Move FrameOptions and related function to module itself --- embassy-stm32/src/i2c/mod.rs | 130 +++++++++++++++++++++++++++++++++++ embassy-stm32/src/i2c/v1.rs | 129 +--------------------------------- 2 files changed, 131 insertions(+), 128 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 6700f0f7d..85381047d 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -6,6 +6,7 @@ mod _version; use core::future::Future; +use core::iter; use core::marker::PhantomData; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; @@ -335,3 +336,132 @@ impl<'d, T: Instance, TXDMA: TxDma, RXDMA: RxDma> embedded_hal_async::i2c: self.transaction(address, operations).await } } + +/// Frame type in I2C transaction. +/// +/// This tells each method what kind of framing to use, to generate a (repeated) start condition (ST +/// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an +/// ACK or NACK after the last byte received. +/// +/// For write operations, the following options are identical because they differ only in the (N)ACK +/// treatment relevant for read operations: +/// +/// - `FirstFrame` and `FirstAndNextFrame` +/// - `NextFrame` and `LastFrameNoStop` +/// +/// Abbreviations used below: +/// +/// - `ST` = start condition +/// - `SR` = repeated start condition +/// - `SP` = stop condition +/// - `ACK`/`NACK` = last byte in read operation +#[derive(Copy, Clone)] +enum FrameOptions { + /// `[ST/SR]+[NACK]+[SP]` First frame (of this type) in transaction and also last frame overall. + FirstAndLastFrame, + /// `[ST/SR]+[NACK]` First frame of this type in transaction, last frame in a read operation but + /// not the last frame overall. + FirstFrame, + /// `[ST/SR]+[ACK]` First frame of this type in transaction, neither last frame overall nor last + /// frame in a read operation. + FirstAndNextFrame, + /// `[ACK]` Middle frame in a read operation (neither first nor last). + NextFrame, + /// `[NACK]+[SP]` Last frame overall in this transaction but not the first frame. + LastFrame, + /// `[NACK]` Last frame in a read operation but not last frame overall in this transaction. + LastFrameNoStop, +} + +impl FrameOptions { + /// Sends start or repeated start condition before transfer. + fn send_start(self) -> bool { + match self { + Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true, + Self::NextFrame | Self::LastFrame | Self::LastFrameNoStop => false, + } + } + + /// Sends stop condition after transfer. + fn send_stop(self) -> bool { + match self { + Self::FirstAndLastFrame | Self::LastFrame => true, + Self::FirstFrame | Self::FirstAndNextFrame | Self::NextFrame | Self::LastFrameNoStop => false, + } + } + + /// Sends NACK after last byte received, indicating end of read operation. + fn send_nack(self) -> bool { + match self { + Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true, + Self::FirstAndNextFrame | Self::NextFrame => false, + } + } +} + +/// Iterates over operations in transaction. +/// +/// Returns necessary frame options for each operation to uphold the [transaction contract] and have +/// the right start/stop/(N)ACK conditions on the wire. +/// +/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction +fn operation_frames<'a, 'b: 'a>( + operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], +) -> Result, FrameOptions)>, Error> { + use embedded_hal_1::i2c::Operation; + + // Check empty read buffer before starting transaction. Otherwise, we would risk halting with an + // error in the middle of the transaction. + if operations.iter().any(|op| match op { + Operation::Read(read) => read.is_empty(), + Operation::Write(_) => false, + }) { + return Err(Error::Overrun); + } + + let mut operations = operations.iter_mut().peekable(); + + let mut next_first_frame = true; + + Ok(iter::from_fn(move || { + let Some(op) = operations.next() else { + return None; + }; + + // Is `op` first frame of its type? + let first_frame = next_first_frame; + let next_op = operations.peek(); + + // Get appropriate frame options as combination of the following properties: + // + // - For each first operation of its type, generate a (repeated) start condition. + // - For the last operation overall in the entire transaction, generate a stop condition. + // - For read operations, check the next operation: if it is also a read operation, we merge + // these and send ACK for all bytes in the current operation; send NACK only for the final + // read operation's last byte (before write or end of entire transaction) to indicate last + // byte read and release the bus for transmission of the bus master's next byte (or stop). + // + // We check the third property unconditionally, i.e. even for write opeartions. This is okay + // because the resulting frame options are identical for write operations. + let frame = match (first_frame, next_op) { + (true, None) => FrameOptions::FirstAndLastFrame, + (true, Some(Operation::Read(_))) => FrameOptions::FirstAndNextFrame, + (true, Some(Operation::Write(_))) => FrameOptions::FirstFrame, + // + (false, None) => FrameOptions::LastFrame, + (false, Some(Operation::Read(_))) => FrameOptions::NextFrame, + (false, Some(Operation::Write(_))) => FrameOptions::LastFrameNoStop, + }; + + // Pre-calculate if `next_op` is the first operation of its type. We do this here and not at + // the beginning of the loop because we hand out `op` as iterator value and cannot access it + // anymore in the next iteration. + next_first_frame = match (&op, next_op) { + (_, None) => false, + (Operation::Read(_), Some(Operation::Write(_))) | (Operation::Write(_), Some(Operation::Read(_))) => true, + (Operation::Read(_), Some(Operation::Read(_))) | (Operation::Write(_), Some(Operation::Write(_))) => false, + }; + + Some((op, frame)) + })) +} diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 7c0bb31d6..be7f91a9a 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -5,7 +5,7 @@ //! All other devices (as of 2023-12-28) use [`v2`](super::v2) instead. use core::future::poll_fn; -use core::{iter, task::Poll}; +use core::task::Poll; use embassy_embedded_hal::SetConfig; use embassy_futures::select::{select, Either}; @@ -41,133 +41,6 @@ pub unsafe fn on_interrupt() { }); } -/// Frame type in I2C transaction. -/// -/// This tells each method what kind of framing to use, to generate a (repeated) start condition (ST -/// or SR), and/or a stop condition (SP). For read operations, this also controls whether to send an -/// ACK or NACK after the last byte received. -/// -/// For write operations, the following options are identical because they differ only in the (N)ACK -/// treatment relevant for read operations: -/// -/// - `FirstFrame` and `FirstAndNextFrame` -/// - `NextFrame` and `LastFrameNoStop` -/// -/// Abbreviations used below: -/// -/// - `ST` = start condition -/// - `SR` = repeated start condition -/// - `SP` = stop condition -/// - `ACK`/`NACK` = last byte in read operation -#[derive(Copy, Clone)] -enum FrameOptions { - /// `[ST/SR]+[NACK]+[SP]` First frame (of this type) in transaction and also last frame overall. - FirstAndLastFrame, - /// `[ST/SR]+[NACK]` First frame of this type in transaction, last frame in a read operation but - /// not the last frame overall. - FirstFrame, - /// `[ST/SR]+[ACK]` First frame of this type in transaction, neither last frame overall nor last - /// frame in a read operation. - FirstAndNextFrame, - /// `[ACK]` Middle frame in a read operation (neither first nor last). - NextFrame, - /// `[NACK]+[SP]` Last frame overall in this transaction but not the first frame. - LastFrame, - /// `[NACK]` Last frame in a read operation but not last frame overall in this transaction. - LastFrameNoStop, -} - -impl FrameOptions { - /// Sends start or repeated start condition before transfer. - fn send_start(self) -> bool { - match self { - Self::FirstAndLastFrame | Self::FirstFrame | Self::FirstAndNextFrame => true, - Self::NextFrame | Self::LastFrame | Self::LastFrameNoStop => false, - } - } - - /// Sends stop condition after transfer. - fn send_stop(self) -> bool { - match self { - Self::FirstAndLastFrame | Self::LastFrame => true, - Self::FirstFrame | Self::FirstAndNextFrame | Self::NextFrame | Self::LastFrameNoStop => false, - } - } - - /// Sends NACK after last byte received, indicating end of read operation. - fn send_nack(self) -> bool { - match self { - Self::FirstAndLastFrame | Self::FirstFrame | Self::LastFrame | Self::LastFrameNoStop => true, - Self::FirstAndNextFrame | Self::NextFrame => false, - } - } -} - -/// Iterates over operations in transaction. -/// -/// Returns necessary frame options for each operation to uphold the [transaction contract] and have -/// the right start/stop/(N)ACK conditions on the wire. -/// -/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction -fn operation_frames<'a, 'b: 'a>( - operations: &'a mut [Operation<'b>], -) -> Result, FrameOptions)>, Error> { - // Check empty read buffer before starting transaction. Otherwise, we would risk halting with an - // error in the middle of the transaction. - if operations.iter().any(|op| match op { - Operation::Read(read) => read.is_empty(), - Operation::Write(_) => false, - }) { - return Err(Error::Overrun); - } - - let mut operations = operations.iter_mut().peekable(); - - let mut next_first_frame = true; - - Ok(iter::from_fn(move || { - let Some(op) = operations.next() else { - return None; - }; - - // Is `op` first frame of its type? - let first_frame = next_first_frame; - let next_op = operations.peek(); - - // Get appropriate frame options as combination of the following properties: - // - // - For each first operation of its type, generate a (repeated) start condition. - // - For the last operation overall in the entire transaction, generate a stop condition. - // - For read operations, check the next operation: if it is also a read operation, we merge - // these and send ACK for all bytes in the current operation; send NACK only for the final - // read operation's last byte (before write or end of entire transaction) to indicate last - // byte read and release the bus for transmission of the bus master's next byte (or stop). - // - // We check the third property unconditionally, i.e. even for write opeartions. This is okay - // because the resulting frame options are identical for write operations. - let frame = match (first_frame, next_op) { - (true, None) => FrameOptions::FirstAndLastFrame, - (true, Some(Operation::Read(_))) => FrameOptions::FirstAndNextFrame, - (true, Some(Operation::Write(_))) => FrameOptions::FirstFrame, - // - (false, None) => FrameOptions::LastFrame, - (false, Some(Operation::Read(_))) => FrameOptions::NextFrame, - (false, Some(Operation::Write(_))) => FrameOptions::LastFrameNoStop, - }; - - // Pre-calculate if `next_op` is the first operation of its type. We do this here and not at - // the beginning of the loop because we hand out `op` as iterator value and cannot access it - // anymore in the next iteration. - next_first_frame = match (&op, next_op) { - (_, None) => false, - (Operation::Read(_), Some(Operation::Write(_))) | (Operation::Write(_), Some(Operation::Read(_))) => true, - (Operation::Read(_), Some(Operation::Read(_))) | (Operation::Write(_), Some(Operation::Write(_))) => false, - }; - - Some((op, frame)) - })) -} - impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { pub(crate) fn init(&mut self, freq: Hertz, _config: Config) { T::regs().cr1().modify(|reg| { From 54d7d495135a2d4a17457d7fdb5d9306f598acc2 Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 27 Mar 2024 01:07:42 +0100 Subject: [PATCH 36/69] Refactor DMA implementation of I2C v1, clarify flow of code --- embassy-stm32/src/i2c/v1.rs | 131 ++++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 58 deletions(-) diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index be7f91a9a..5c57a9ccc 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -142,7 +142,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Arbitration); } - // Set up current address, we're trying to talk to + // Set up current address we're trying to talk to T::regs().dr().write(|reg| reg.set_dr(addr << 1)); // Wait until address was sent @@ -235,7 +235,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Arbitration); } - // Set up current address, we're trying to talk to + // Set up current address we're trying to talk to T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)); // Wait until address was sent @@ -331,27 +331,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { where TXDMA: crate::i2c::TxDma, { - let dma_transfer = unsafe { - let regs = T::regs(); - regs.cr2().modify(|w| { - // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for reception. - w.set_itbufen(false); - // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register. - w.set_dmaen(true); - // Sending NACK is not necessary (nor possible) for write transfer. - w.set_last(false); - }); - // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event. - let dst = 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()) - }; + T::regs().cr2().modify(|w| { + // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for + // reception. + w.set_itbufen(false); + // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 + // register. + w.set_dmaen(true); + // Sending NACK is not necessary (nor possible) for write transfer. + w.set_last(false); + }); + // Sentinel to disable transfer when an error occurs or future is canceled. + // TODO: Generate STOP condition on cancel? let on_drop = OnDrop::new(|| { - let regs = T::regs(); - regs.cr2().modify(|w| { + T::regs().cr2().modify(|w| { w.set_dmaen(false); w.set_iterren(false); w.set_itevten(false); @@ -390,7 +384,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Arbitration); } - // Set up current address, we're trying to talk to + // Set up current address we're trying to talk to T::regs().dr().write(|reg| reg.set_dr(address << 1)); // Wait for the address to be acknowledged @@ -416,14 +410,22 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { T::regs().sr2().read(); } + let dma_transfer = unsafe { + // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to + // 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()) + }; + // Wait for bytes to be sent, or an error to occur. let poll_error = poll_fn(|cx| { state.waker.register(cx.waker()); match Self::check_and_clear_error_flags() { - // Unclear why the Err turbofish is necessary here? The compiler didn’t require it in the other - // identical poll_fn check_and_clear matches. - Err(e) => Poll::Ready(Err::(e)), + Err(e) => Poll::Ready(Err::<(), Error>(e)), Ok(_) => { // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(); @@ -502,31 +504,30 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { where RXDMA: crate::i2c::RxDma, { - let buffer_len = buffer.len(); + if buffer.is_empty() { + return Err(Error::Overrun); + } - let dma_transfer = unsafe { - let regs = T::regs(); - regs.cr2().modify(|w| { - // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for reception. - w.set_itbufen(false); - // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 register. - w.set_dmaen(true); - // If, in the I2C_CR2 register, the LAST bit is set, I2C - // automatically sends a NACK after the next byte following EOT_1. The user can - // generate a Stop condition in the DMA Transfer Complete interrupt routine if enabled. - w.set_last(frame.send_nack() && buffer_len != 1); - }); - // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved to this address from the memory after each TxE event. - let src = regs.dr().as_ptr() as *mut u8; + // Some branches below depend on whether the buffer contains only a single byte. + let single_byte = buffer.len() == 1; - let ch = &mut self.rx_dma; - let request = ch.request(); - Transfer::new_read(ch, request, src, buffer, Default::default()) - }; + T::regs().cr2().modify(|w| { + // Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for + // reception. + w.set_itbufen(false); + // DMA mode can be enabled for transmission by setting the DMAEN bit in the I2C_CR2 + // register. + w.set_dmaen(true); + // If, in the I2C_CR2 register, the LAST bit is set, I2C automatically sends a NACK + // after the next byte following EOT_1. The user can generate a Stop condition in + // the DMA Transfer Complete interrupt routine if enabled. + w.set_last(frame.send_nack() && !single_byte); + }); + // Sentinel to disable transfer when an error occurs or future is canceled. + // TODO: Generate STOP condition on cancel? let on_drop = OnDrop::new(|| { - let regs = T::regs(); - regs.cr2().modify(|w| { + T::regs().cr2().modify(|w| { w.set_dmaen(false); w.set_iterren(false); w.set_itevten(false); @@ -566,7 +567,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { return Err(Error::Arbitration); } - // Set up current address, we're trying to talk to + // Set up current address we're trying to talk to T::regs().dr().write(|reg| reg.set_dr((address << 1) + 1)); // Wait for the address to be acknowledged @@ -590,7 +591,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // 18.3.8: When a single byte must be received: the NACK must be programmed during EV6 // event, i.e. program ACK=0 when ADDR=1, before clearing ADDR flag. - if frame.send_nack() && buffer_len == 1 { + if frame.send_nack() && single_byte { T::regs().cr1().modify(|w| { w.set_ack(false); }); @@ -598,27 +599,41 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Clear condition by reading SR2 T::regs().sr2().read(); - } else if frame.send_nack() && buffer_len == 1 { - T::regs().cr1().modify(|w| { - w.set_ack(false); - }); + } else { + // Before starting reception of single byte (but without START condition, i.e. in case + // of continued frame), program NACK to emit at end of this byte. + if frame.send_nack() && single_byte { + T::regs().cr1().modify(|w| { + w.set_ack(false); + }); + } } - // 18.3.8: When a single byte must be received: [snip] Then the - // user can program the STOP condition either after clearing ADDR flag, or in the - // DMA Transfer Complete interrupt routine. - if frame.send_stop() && buffer_len == 1 { + // 18.3.8: When a single byte must be received: [snip] Then the user can program the STOP + // condition either after clearing ADDR flag, or in the DMA Transfer Complete interrupt + // routine. + if frame.send_stop() && single_byte { T::regs().cr1().modify(|w| { w.set_stop(true); }); } + let dma_transfer = unsafe { + // Set the I2C_DR register address in the DMA_SxPAR register. The data will be moved + // 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()) + }; + // Wait for bytes to be received, or an error to occur. let poll_error = poll_fn(|cx| { state.waker.register(cx.waker()); match Self::check_and_clear_error_flags() { - Err(e) => Poll::Ready(Err::(e)), + Err(e) => Poll::Ready(Err::<(), Error>(e)), _ => { // When pending, (re-)enable interrupts to wake us up. Self::enable_interrupts(); @@ -636,7 +651,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { w.set_dmaen(false); }); - if frame.send_stop() && buffer_len != 1 { + if frame.send_stop() && !single_byte { T::regs().cr1().modify(|w| { w.set_stop(true); }); From 0cfb65abc225cafa89562eb80d8e61389465d7ef Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 27 Mar 2024 01:30:27 +0100 Subject: [PATCH 37/69] Add transaction stub to I2C v2 --- embassy-stm32/src/i2c/v2.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 8baf2849d..da3b0ee30 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -557,6 +557,21 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { 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 From 13636556d969314843a04907d22e2921b1be388d Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 27 Mar 2024 01:41:13 +0100 Subject: [PATCH 38/69] Mark shared data structure as dead_code for I2C v2 branch --- embassy-stm32/src/i2c/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 85381047d..9a91e2f25 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -356,6 +356,7 @@ impl<'d, T: Instance, TXDMA: TxDma, RXDMA: RxDma> embedded_hal_async::i2c: /// - `SP` = stop condition /// - `ACK`/`NACK` = last byte in read operation #[derive(Copy, Clone)] +#[allow(dead_code)] enum FrameOptions { /// `[ST/SR]+[NACK]+[SP]` First frame (of this type) in transaction and also last frame overall. FirstAndLastFrame, @@ -373,6 +374,7 @@ enum FrameOptions { LastFrameNoStop, } +#[allow(dead_code)] impl FrameOptions { /// Sends start or repeated start condition before transfer. fn send_start(self) -> bool { @@ -405,6 +407,7 @@ impl FrameOptions { /// the right start/stop/(N)ACK conditions on the wire. /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction +#[allow(dead_code)] fn operation_frames<'a, 'b: 'a>( operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], ) -> Result, FrameOptions)>, Error> { From b52e9a60eb929facd21eab2d524781e127f3e04b Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 27 Mar 2024 10:39:33 +0100 Subject: [PATCH 39/69] Add missing check for empty buffer in asynchronous read_write() --- embassy-stm32/src/i2c/mod.rs | 4 ++++ embassy-stm32/src/i2c/v1.rs | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 9a91e2f25..d4d4aec5d 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -415,6 +415,10 @@ fn operation_frames<'a, 'b: 'a>( // Check empty read buffer before starting transaction. Otherwise, we would risk halting with an // error in the middle of the transaction. + // + // In principle, we could allow empty read frames within consecutive read operations, as long as + // at least one byte remains in the final (merged) read operation, but that makes the logic more + // complicated and error-prone. if operations.iter().any(|op| match op { Operation::Read(read) => read.is_empty(), Operation::Write(_) => false, diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 5c57a9ccc..d45c48b24 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -669,6 +669,12 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { RXDMA: crate::i2c::RxDma, TXDMA: crate::i2c::TxDma, { + // Check empty read buffer before starting transaction. Otherwise, we would not generate the + // stop condition below. + if read.is_empty() { + return Err(Error::Overrun); + } + self.write_frame(address, write, FrameOptions::FirstFrame).await?; self.read_frame(address, read, FrameOptions::FirstAndLastFrame).await } From bb5fcce0a095431de063aedd6eb12161d94805db Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 27 Mar 2024 10:42:38 +0100 Subject: [PATCH 40/69] Use named imports within function to make code easier to read --- embassy-stm32/src/i2c/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index d4d4aec5d..a46061d54 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -411,7 +411,7 @@ impl FrameOptions { fn operation_frames<'a, 'b: 'a>( operations: &'a mut [embedded_hal_1::i2c::Operation<'b>], ) -> Result, FrameOptions)>, Error> { - use embedded_hal_1::i2c::Operation; + use embedded_hal_1::i2c::Operation::{Read, Write}; // Check empty read buffer before starting transaction. Otherwise, we would risk halting with an // error in the middle of the transaction. @@ -420,8 +420,8 @@ fn operation_frames<'a, 'b: 'a>( // at least one byte remains in the final (merged) read operation, but that makes the logic more // complicated and error-prone. if operations.iter().any(|op| match op { - Operation::Read(read) => read.is_empty(), - Operation::Write(_) => false, + Read(read) => read.is_empty(), + Write(_) => false, }) { return Err(Error::Overrun); } @@ -452,12 +452,12 @@ fn operation_frames<'a, 'b: 'a>( // because the resulting frame options are identical for write operations. let frame = match (first_frame, next_op) { (true, None) => FrameOptions::FirstAndLastFrame, - (true, Some(Operation::Read(_))) => FrameOptions::FirstAndNextFrame, - (true, Some(Operation::Write(_))) => FrameOptions::FirstFrame, + (true, Some(Read(_))) => FrameOptions::FirstAndNextFrame, + (true, Some(Write(_))) => FrameOptions::FirstFrame, // (false, None) => FrameOptions::LastFrame, - (false, Some(Operation::Read(_))) => FrameOptions::NextFrame, - (false, Some(Operation::Write(_))) => FrameOptions::LastFrameNoStop, + (false, Some(Read(_))) => FrameOptions::NextFrame, + (false, Some(Write(_))) => FrameOptions::LastFrameNoStop, }; // Pre-calculate if `next_op` is the first operation of its type. We do this here and not at @@ -465,8 +465,8 @@ fn operation_frames<'a, 'b: 'a>( // anymore in the next iteration. next_first_frame = match (&op, next_op) { (_, None) => false, - (Operation::Read(_), Some(Operation::Write(_))) | (Operation::Write(_), Some(Operation::Read(_))) => true, - (Operation::Read(_), Some(Operation::Read(_))) | (Operation::Write(_), Some(Operation::Write(_))) => false, + (Read(_), Some(Write(_))) | (Write(_), Some(Read(_))) => true, + (Read(_), Some(Read(_))) | (Write(_), Some(Write(_))) => false, }; Some((op, frame)) From f10ee07a905e6627537cbb00d72d6dd35108a949 Mon Sep 17 00:00:00 2001 From: schmettow Date: Thu, 28 Mar 2024 14:35:24 +0100 Subject: [PATCH 41/69] Update embassy_in_the_wild.adoc corrected link to Ystudio project --- docs/modules/ROOT/pages/embassy_in_the_wild.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/embassy_in_the_wild.adoc b/docs/modules/ROOT/pages/embassy_in_the_wild.adoc index 75208a883..4b650449f 100644 --- a/docs/modules/ROOT/pages/embassy_in_the_wild.adoc +++ b/docs/modules/ROOT/pages/embassy_in_the_wild.adoc @@ -15,5 +15,5 @@ firmware (RP2040, STM32) for capturing physiological data in behavioural science ** biopotentials (analog ports) ** motion capture (6-axis accelerometers) ** air quality (CO2, Temp, Humidity) -** comes with an app for capturing and visualizing data [link:https://github.com/schmettow/ystudio[Ystudio]] +** comes with an app for capturing and visualizing data [link:https://github.com/schmettow/ystudio-zero[Ystudio]] From 47b492ba055910e4f5f1d32193d07a7689d3398e Mon Sep 17 00:00:00 2001 From: Karun Date: Tue, 2 Apr 2024 12:01:33 -0400 Subject: [PATCH 42/69] retrigger checks From d62615b5360a92fc3c039dfd9669b3e7771f1c79 Mon Sep 17 00:00:00 2001 From: Karun Date: Tue, 2 Apr 2024 15:48:39 -0400 Subject: [PATCH 43/69] Update metapac to use PR #442 with octospi rcc updates --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 89c17cb3d..6d1c2d25c 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-e7f91751fbbf856e0cb30e50ae6db79f0409b085" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ac187e40aa97da86f7d3cf22abad918f42f01739" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" From 2caea89b6ab9859470ccf6c7d7414c01251bbecd Mon Sep 17 00:00:00 2001 From: Karun Date: Tue, 2 Apr 2024 15:50:57 -0400 Subject: [PATCH 44/69] Update build dependency as well --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 6d1c2d25c..158c630b9 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -95,7 +95,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-e7f91751fbbf856e0cb30e50ae6db79f0409b085", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ac187e40aa97da86f7d3cf22abad918f42f01739", default-features = false, features = ["metadata"]} [features] From 166c95be6c2917e5133fa361ee88d845a6f4ef73 Mon Sep 17 00:00:00 2001 From: Karun Date: Tue, 2 Apr 2024 16:14:10 -0400 Subject: [PATCH 45/69] Update to use private supertrait, following PR#2730 --- embassy-stm32/src/ospi/mod.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 794bdd127..f85f07fc4 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -12,8 +12,7 @@ pub use enums::*; use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; use crate::dma::{word, Transfer}; -use crate::gpio::sealed::{AFType, Pin as _}; -use crate::gpio::{AnyPin, Pull}; +use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _}; use crate::pac::octospi::{vals, Octospi as Regs}; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -1002,20 +1001,17 @@ impl RegsExt for Regs { } } -pub(crate) mod sealed { - use super::*; +pub(crate) trait SealedInstance { + const REGS: Regs; +} - pub trait Instance { - const REGS: Regs; - } - - pub trait Word { - const CONFIG: word_impl::Config; - } +trait SealedWord { + const CONFIG: word_impl::Config; } /// OSPI instance trait. -pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral {} pin_trait!(SckPin, Instance); pin_trait!(NckPin, Instance); @@ -1033,7 +1029,7 @@ dma_trait!(OctoDma, Instance); foreach_peripheral!( (octospi, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { + impl SealedInstance for peripherals::$inst { const REGS: Regs = crate::pac::$inst; } @@ -1057,11 +1053,12 @@ impl<'d, T: Instance, Dma> GetConfig for Ospi<'d, T, Dma> { } /// Word sizes usable for OSPI. -pub trait Word: word::Word + sealed::Word {} +#[allow(private_bounds)] +pub trait Word: word::Word + SealedWord {} macro_rules! impl_word { ($T:ty, $config:expr) => { - impl sealed::Word for $T { + impl SealedWord for $T { const CONFIG: Config = $config; } impl Word for $T {} From 66a7b629097cca89895e8557cf31aba50f5a1982 Mon Sep 17 00:00:00 2001 From: Karun Date: Tue, 2 Apr 2024 16:24:31 -0400 Subject: [PATCH 46/69] Add octospi version dependency for max transfer support --- embassy-stm32/src/ospi/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index f85f07fc4..84d1ac284 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -519,7 +519,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { T::REGS.dcr3().modify(|w| { w.set_csbound(config.chip_select_boundary); - w.set_maxtran(config.max_transfer); + #[cfg(octospi_v1)] + { + w.set_maxtran(config.max_transfer); + } }); T::REGS.dcr4().modify(|w| { @@ -911,7 +914,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { T::REGS.dcr3().modify(|w| { w.set_csbound(config.chip_select_boundary); - w.set_maxtran(config.max_transfer); + #[cfg(octospi_v1)] + { + w.set_maxtran(config.max_transfer); + } }); T::REGS.dcr4().modify(|w| { From a031b3b79ec5ebf8f0ec1f7df605944acf7d5720 Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 3 Apr 2024 13:42:38 -0400 Subject: [PATCH 47/69] Update metapac --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index d00e7aa55..89b24f0eb 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-ac187e40aa97da86f7d3cf22abad918f42f01739" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b7ec569a5510c324693f0515ac8ea20b12917a9" } 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-ac187e40aa97da86f7d3cf22abad918f42f01739", 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"]} [features] default = ["rt"] From 630fd90d26207521d39f1bf76d1f19b862e4393d Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 3 Apr 2024 14:01:40 -0400 Subject: [PATCH 48/69] Address PR comments --- embassy-stm32/src/ospi/mod.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 84d1ac284..59c73c24d 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -889,7 +889,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Set new bus configuration - pub fn set_config(&mut self, config: &Config) -> Result<(), ()> { + pub fn set_config(&mut self, config: &Config) { // Wait for busy flag to clear while T::REGS.sr().read().busy() {} @@ -960,7 +960,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } self.config = *config; - Ok(()) } /// Get current configuration @@ -1012,7 +1011,7 @@ pub(crate) trait SealedInstance { } trait SealedWord { - const CONFIG: word_impl::Config; + const CONFIG: u8; } /// OSPI instance trait. @@ -1047,7 +1046,8 @@ impl<'d, T: Instance, Dma> SetConfig for Ospi<'d, T, Dma> { type Config = Config; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { - self.set_config(config) + self.set_config(config); + Ok(()) } } @@ -1065,18 +1065,12 @@ pub trait Word: word::Word + SealedWord {} macro_rules! impl_word { ($T:ty, $config:expr) => { impl SealedWord for $T { - const CONFIG: Config = $config; + const CONFIG: u8 = $config; } impl Word for $T {} }; } -mod word_impl { - use super::*; - - pub type Config = u8; - - impl_word!(u8, 8); - impl_word!(u16, 16); - impl_word!(u32, 32); -} +impl_word!(u8, 8); +impl_word!(u16, 16); +impl_word!(u32, 32); From b3bbf42b8bf4d31dab66bf75d80389279b0502f2 Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 3 Apr 2024 15:58:20 -0400 Subject: [PATCH 49/69] Remove data length from transfer config Remove non hal traits Fix function comments --- embassy-stm32/src/ospi/mod.rs | 91 ++++++----------------------------- 1 file changed, 16 insertions(+), 75 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 59c73c24d..bcdffc266 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -113,8 +113,6 @@ pub struct TransferConfig { /// Data width (DMODE) pub dwidth: OspiWidth, - /// Length of data - pub data_len: Option, /// Data buffer pub ddtr: bool, @@ -141,7 +139,6 @@ impl Default for TransferConfig { abdtr: false, dwidth: OspiWidth::NONE, - data_len: None, ddtr: false, dummy: DummyCycles::_0, @@ -158,40 +155,6 @@ pub enum OspiError { InvalidCommand, } -/// MultiSpi interface trait -pub trait MultiSpiBus: embedded_hal_1::spi::ErrorType { - /// Transaction configuration for specific multispi implementation - type Config; - - /// Command function used for a configuration operation, when no user data is - /// supplied to or read from the target device. - async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error>; - - /// Read function used to read data from the target device following the supplied transaction - /// configuration. - async fn read(&mut self, data: &mut [Word], config: Self::Config) -> Result<(), Self::Error>; - - /// Write function used to send data to the target device following the supplied transaction - /// configuration. - async fn write(&mut self, data: &[Word], config: Self::Config) -> Result<(), Self::Error>; -} - -impl + ?Sized, Word: Copy + 'static> MultiSpiBus for &mut T { - type Config = T::Config; - #[inline] - async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error> { - T::command(self, config).await - } - - async fn read(&mut self, data: &mut [Word], config: Self::Config) -> Result<(), Self::Error> { - T::read(self, data, config).await - } - - async fn write(&mut self, data: &[Word], config: Self::Config) -> Result<(), Self::Error> { - T::write(self, data, config).await - } -} - /// OSPI driver. pub struct Ospi<'d, T: Instance, Dma> { _peri: PeripheralRef<'d, T>, @@ -211,35 +174,9 @@ pub struct Ospi<'d, T: Instance, Dma> { width: OspiWidth, } -impl embedded_hal_1::spi::Error for OspiError { - fn kind(&self) -> ErrorKind { - ErrorKind::Other - } -} - -impl<'d, T: Instance, Dma> embedded_hal_1::spi::ErrorType for Ospi<'d, T, Dma> { - type Error = OspiError; -} - -impl<'d, T: Instance, Dma: OctoDma, W: Word> MultiSpiBus for Ospi<'d, T, Dma> { - type Config = TransferConfig; - - async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error> { - self.command(&config).await - } - - async fn read(&mut self, data: &mut [W], config: Self::Config) -> Result<(), Self::Error> { - self.read(data, config).await - } - - async fn write(&mut self, data: &[W], config: Self::Config) -> Result<(), Self::Error> { - self.write(data, config).await - } -} - impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { - /// Create new OSPI driver for a dualspi external chip - pub fn new_spi( + /// Create new OSPI driver for a single spi external chip + pub fn new_singlespi( peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, d0: impl Peripheral

> + 'd, @@ -422,7 +359,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { ) } - /// Create new OSPI driver for two quadspi external chips + /// Create new OSPI driver for octospi external chips pub fn new_octospi( peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, @@ -584,7 +521,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } // Function to configure the peripheral for the requested command - fn configure_command(&mut self, command: &TransferConfig) -> Result<(), OspiError> { + fn configure_command(&mut self, command: &TransferConfig, data_len: Option) -> Result<(), OspiError> { // Check that transaction doesn't use more than hardware initialized pins if >::into(command.iwidth) > >::into(self.width) || >::into(command.adwidth) > >::into(self.width) @@ -614,10 +551,14 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { }); // Configure data - if let Some(data_length) = command.data_len { + if let Some(data_length) = data_len { T::REGS.dlr().write(|v| { v.set_dl((data_length - 1) as u32); }) + } else { + T::REGS.dlr().write(|v| { + v.set_dl((0) as u32); + }) } // Configure instruction/address/data modes @@ -681,7 +622,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { while T::REGS.sr().read().busy() {} // Need additional validation that command configuration doesn't have data set - self.configure_command(command)?; + self.configure_command(command, None)?; // Transaction initiated by setting final configuration, i.e the instruction register while !T::REGS.sr().read().tcf() {} @@ -702,7 +643,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { w.set_dmaen(false); }); - self.configure_command(&transaction)?; + self.configure_command(&transaction, Some(buf.len()))?; if let Some(len) = transaction.data_len { let current_address = T::REGS.ar().read().address(); @@ -733,7 +674,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { T::REGS.cr().modify(|w| { w.set_dmaen(false); }); - self.configure_command(&transaction)?; + self.configure_command(&transaction, Some(buf.len()))?; if let Some(len) = transaction.data_len { T::REGS @@ -757,7 +698,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { - self.configure_command(&transaction)?; + self.configure_command(&transaction, Some(buf.len()))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -795,7 +736,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { - self.configure_command(&transaction)?; + self.configure_command(&transaction, Some(buf.len()))?; T::REGS .cr() .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); @@ -825,7 +766,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { - self.configure_command(&transaction)?; + self.configure_command(&transaction, Some(buf.len()))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -863,7 +804,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { - self.configure_command(&transaction)?; + self.configure_command(&transaction, Some(buf.len()))?; T::REGS .cr() .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); From 80aeea93fd9d34b95441b6900e658e86b5b18b03 Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 3 Apr 2024 16:05:23 -0400 Subject: [PATCH 50/69] Configure dual-quad setting by constructor --- embassy-stm32/src/ospi/mod.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index bcdffc266..7020cf9ad 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -23,9 +23,6 @@ pub struct Config { /// Fifo threshold used by the peripheral to generate the interrupt indicating data /// or space is available in the FIFO pub fifo_threshold: FIFOThresholdLevel, - /// Enables dual-quad mode which allows access to two devices simultaneously to - /// increase throughput - pub dual_quad: bool, /// Indicates the type of external device connected pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface /// Defines the size of the external device connected to the OSPI corresponding @@ -64,7 +61,6 @@ impl Default for Config { fn default() -> Self { Self { fifo_threshold: FIFOThresholdLevel::_16Bytes, // 32 bytes FIFO, half capacity - dual_quad: false, memory_type: MemoryType::Micron, device_size: MemorySize::Other(0), chip_select_high_time: ChipSelectHighTime::_5Cycle, @@ -196,6 +192,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d1.set_as_af_pull(d1.af_num(), AFType::Input, Pull::None); d1.set_speed(crate::gpio::Speed::VeryHigh); + T::REGS.cr().modify(|w| { + w.set_dmm(false); + }); + Self::new_inner( peri, Some(d0.map_into()), @@ -236,6 +236,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); d1.set_speed(crate::gpio::Speed::VeryHigh); + T::REGS.cr().modify(|w| { + w.set_dmm(false); + }); + Self::new_inner( peri, Some(d0.map_into()), @@ -282,6 +286,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); d3.set_speed(crate::gpio::Speed::VeryHigh); + T::REGS.cr().modify(|w| { + w.set_dmm(false); + }); + Self::new_inner( peri, Some(d0.map_into()), @@ -340,6 +348,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); d7.set_speed(crate::gpio::Speed::VeryHigh); + T::REGS.cr().modify(|w| { + w.set_dmm(true); + }); + Self::new_inner( peri, Some(d0.map_into()), @@ -398,6 +410,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); d7.set_speed(crate::gpio::Speed::VeryHigh); + T::REGS.cr().modify(|w| { + w.set_dmm(false); + }); + Self::new_inner( peri, Some(d0.map_into()), @@ -477,10 +493,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { w.set_prescaler(config.clock_prescaler); }); - T::REGS.cr().modify(|w| { - w.set_dmm(config.dual_quad); - }); - T::REGS.tcr().modify(|w| { w.set_sshift(match config.sample_shifting { true => vals::SampleShift::HALFCYCLE, From 4ea7dfce176cf588fe0bc37efbca44e229bd6e0e Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 3 Apr 2024 16:36:02 -0400 Subject: [PATCH 51/69] Fix build errors Add empty checks/peripheral busy waits --- embassy-stm32/src/ospi/mod.rs | 93 +++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 7020cf9ad..4a89cd810 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -7,7 +7,6 @@ pub mod enums; use embassy_embedded_hal::{GetConfig, SetConfig}; use embassy_hal_internal::{into_ref, PeripheralRef}; -use embedded_hal_1::spi::ErrorKind; pub use enums::*; use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; @@ -144,11 +143,14 @@ impl Default for TransferConfig { /// Error used for Octospi implementation #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum OspiError { /// Peripheral configuration is invalid InvalidConfiguration, /// Operation configuration is invalid InvalidCommand, + /// Size zero buffer passed to instruction + EmptyBuffer, } /// OSPI driver. @@ -627,10 +629,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { /// Function used to control or configure the target device without data transfer pub async fn command(&mut self, command: &TransferConfig) -> Result<(), OspiError> { - // Prevent a transaction from being set with expected data transmission or reception - if let Some(_) = command.data_len { - return Err(OspiError::InvalidCommand); - }; + // Wait for peripheral to be free while T::REGS.sr().read().busy() {} // Need additional validation that command configuration doesn't have data set @@ -647,6 +646,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { /// Blocking read with byte by byte data transfer pub fn blocking_read(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + // Wait for peripheral to be free while T::REGS.sr().read().busy() {} @@ -657,22 +660,20 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { self.configure_command(&transaction, Some(buf.len()))?; - if let Some(len) = transaction.data_len { - let current_address = T::REGS.ar().read().address(); - let current_instruction = T::REGS.ir().read().instruction(); + let current_address = T::REGS.ar().read().address(); + let current_instruction = T::REGS.ir().read().instruction(); - // For a indirect read transaction, the transaction begins when the instruction/address is set - T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); - if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { - T::REGS.ir().write(|v| v.set_instruction(current_instruction)); - } else { - T::REGS.ar().write(|v| v.set_address(current_address)); - } + // For a indirect read transaction, the transaction begins when the instruction/address is set + T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); + if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { + T::REGS.ir().write(|v| v.set_instruction(current_instruction)); + } else { + T::REGS.ar().write(|v| v.set_address(current_address)); + } - for idx in 0..len { - while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} - buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() }; - } + for idx in 0..buf.len() { + while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} + buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() }; } while !T::REGS.sr().read().tcf() {} @@ -683,20 +684,26 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { /// Blocking write with byte by byte data transfer pub fn blocking_write(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + T::REGS.cr().modify(|w| { w.set_dmaen(false); }); + self.configure_command(&transaction, Some(buf.len()))?; - if let Some(len) = transaction.data_len { - T::REGS - .cr() - .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); + T::REGS + .cr() + .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); - for idx in 0..len { - while !T::REGS.sr().read().ftf() {} - unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) }; - } + for idx in 0..buf.len() { + while !T::REGS.sr().read().ftf() {} + unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) }; } while !T::REGS.sr().read().tcf() {} @@ -710,6 +717,13 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + self.configure_command(&transaction, Some(buf.len()))?; let current_address = T::REGS.ar().read().address(); @@ -748,6 +762,13 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + self.configure_command(&transaction, Some(buf.len()))?; T::REGS .cr() @@ -778,6 +799,13 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + self.configure_command(&transaction, Some(buf.len()))?; let current_address = T::REGS.ar().read().address(); @@ -816,6 +844,13 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + self.configure_command(&transaction, Some(buf.len()))?; T::REGS .cr() @@ -888,10 +923,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { w.set_prescaler(config.clock_prescaler); }); - T::REGS.cr().modify(|w| { - w.set_dmm(config.dual_quad); - }); - T::REGS.tcr().modify(|w| { w.set_sshift(match config.sample_shifting { true => vals::SampleShift::HALFCYCLE, From 330a3b04882a072406b9c3b4ef35d4c91c994e01 Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 3 Apr 2024 16:42:16 -0400 Subject: [PATCH 52/69] Fix passing of dual quad param to inner constructor --- embassy-stm32/src/ospi/mod.rs | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 4a89cd810..398c3298f 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -194,10 +194,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d1.set_as_af_pull(d1.af_num(), AFType::Input, Pull::None); d1.set_speed(crate::gpio::Speed::VeryHigh); - T::REGS.cr().modify(|w| { - w.set_dmm(false); - }); - Self::new_inner( peri, Some(d0.map_into()), @@ -214,6 +210,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dma, config, OspiWidth::SING, + false, ) } @@ -238,10 +235,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); d1.set_speed(crate::gpio::Speed::VeryHigh); - T::REGS.cr().modify(|w| { - w.set_dmm(false); - }); - Self::new_inner( peri, Some(d0.map_into()), @@ -258,6 +251,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dma, config, OspiWidth::DUAL, + false, ) } @@ -288,10 +282,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); d3.set_speed(crate::gpio::Speed::VeryHigh); - T::REGS.cr().modify(|w| { - w.set_dmm(false); - }); - Self::new_inner( peri, Some(d0.map_into()), @@ -308,6 +298,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dma, config, OspiWidth::QUAD, + false, ) } @@ -350,10 +341,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); d7.set_speed(crate::gpio::Speed::VeryHigh); - T::REGS.cr().modify(|w| { - w.set_dmm(true); - }); - Self::new_inner( peri, Some(d0.map_into()), @@ -370,6 +357,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dma, config, OspiWidth::QUAD, + true, ) } @@ -412,10 +400,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); d7.set_speed(crate::gpio::Speed::VeryHigh); - T::REGS.cr().modify(|w| { - w.set_dmm(false); - }); - Self::new_inner( peri, Some(d0.map_into()), @@ -432,6 +416,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dma, config, OspiWidth::OCTO, + false, ) } @@ -451,6 +436,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dma: impl Peripheral

+ 'd, config: Config, width: OspiWidth, + dual_quad: bool, ) -> Self { into_ref!(peri, dma); @@ -495,6 +481,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { w.set_prescaler(config.clock_prescaler); }); + T::REGS.cr().modify(|w| { + w.set_dmm(dual_quad); + }); + T::REGS.tcr().modify(|w| { w.set_sshift(match config.sample_shifting { true => vals::SampleShift::HALFCYCLE, From ab85eb4b60cd49ebcd43d2305f42327685f5e5a6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 5 Apr 2024 00:20:22 +0200 Subject: [PATCH 53/69] nrf: remove mod sealed. --- embassy-nrf/src/buffered_uarte.rs | 25 ++++----- embassy-nrf/src/gpio.rs | 82 ++++++++++++++--------------- embassy-nrf/src/gpiote.rs | 14 +++-- embassy-nrf/src/i2s.rs | 56 +++++++++----------- embassy-nrf/src/pdm.rs | 43 +++++++-------- embassy-nrf/src/ppi/mod.rs | 22 ++++---- embassy-nrf/src/pwm.rs | 16 +++--- embassy-nrf/src/qdec.rs | 43 +++++++-------- embassy-nrf/src/qspi.rs | 40 +++++++------- embassy-nrf/src/radio/mod.rs | 38 +++++++------- embassy-nrf/src/rng.rs | 87 ++++++++++++++----------------- embassy-nrf/src/saadc.rs | 16 +++--- embassy-nrf/src/spim.rs | 62 ++++++++++------------ embassy-nrf/src/spis.rs | 43 +++++++-------- embassy-nrf/src/timer.rs | 22 +++----- embassy-nrf/src/twim.rs | 37 ++++++------- embassy-nrf/src/twis.rs | 37 ++++++------- embassy-nrf/src/uarte.rs | 59 +++++++++------------ embassy-nrf/src/usb/mod.rs | 13 ++--- 19 files changed, 339 insertions(+), 416 deletions(-) diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index b04c96e09..385d4015e 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -20,8 +20,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; // Re-export SVD variants to allow user to directly set values pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; -use crate::gpio::sealed::Pin; -use crate::gpio::{AnyPin, Pin as GpioPin, PselBits}; +use crate::gpio::{AnyPin, Pin as GpioPin, PselBits, SealedPin}; use crate::interrupt::typelevel::Interrupt; use crate::ppi::{ self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, @@ -30,19 +29,15 @@ use crate::timer::{Instance as TimerInstance, Timer}; use crate::uarte::{configure, drop_tx_rx, Config, Instance as UarteInstance}; use crate::{interrupt, pac, Peripheral}; -mod sealed { - use super::*; +pub(crate) struct State { + tx_buf: RingBuffer, + tx_count: AtomicUsize, - pub struct State { - pub tx_buf: RingBuffer, - pub tx_count: AtomicUsize, - - pub rx_buf: RingBuffer, - pub rx_started: AtomicBool, - pub rx_started_count: AtomicU8, - pub rx_ended_count: AtomicU8, - pub rx_ppi_ch: AtomicU8, - } + rx_buf: RingBuffer, + rx_started: AtomicBool, + rx_started_count: AtomicU8, + rx_ended_count: AtomicU8, + rx_ppi_ch: AtomicU8, } /// UART error. @@ -53,8 +48,6 @@ pub enum Error { // No errors for now } -pub(crate) use sealed::State; - impl State { pub(crate) const fn new() -> Self { Self { diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index f2353f21d..7b272dca0 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -7,7 +7,6 @@ use core::hint::unreachable_unchecked; use cfg_if::cfg_if; use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; -use self::sealed::Pin as _; #[cfg(feature = "nrf51")] use crate::pac::gpio; #[cfg(feature = "nrf51")] @@ -361,59 +360,56 @@ impl<'d> Drop for Flex<'d> { } } -pub(crate) mod sealed { - use super::*; +pub(crate) trait SealedPin { + fn pin_port(&self) -> u8; - pub trait Pin { - fn pin_port(&self) -> u8; - - #[inline] - fn _pin(&self) -> u8 { - cfg_if! { - if #[cfg(feature = "_gpio-p1")] { - self.pin_port() % 32 - } else { - self.pin_port() - } + #[inline] + fn _pin(&self) -> u8 { + cfg_if! { + if #[cfg(feature = "_gpio-p1")] { + self.pin_port() % 32 + } else { + self.pin_port() } } + } - #[inline] - fn block(&self) -> &gpio::RegisterBlock { - unsafe { - match self.pin_port() / 32 { - #[cfg(feature = "nrf51")] - 0 => &*pac::GPIO::ptr(), - #[cfg(not(feature = "nrf51"))] - 0 => &*pac::P0::ptr(), - #[cfg(feature = "_gpio-p1")] - 1 => &*pac::P1::ptr(), - _ => unreachable_unchecked(), - } + #[inline] + fn block(&self) -> &gpio::RegisterBlock { + unsafe { + match self.pin_port() / 32 { + #[cfg(feature = "nrf51")] + 0 => &*pac::GPIO::ptr(), + #[cfg(not(feature = "nrf51"))] + 0 => &*pac::P0::ptr(), + #[cfg(feature = "_gpio-p1")] + 1 => &*pac::P1::ptr(), + _ => unreachable_unchecked(), } } + } - #[inline] - fn conf(&self) -> &gpio::PIN_CNF { - &self.block().pin_cnf[self._pin() as usize] - } + #[inline] + fn conf(&self) -> &gpio::PIN_CNF { + &self.block().pin_cnf[self._pin() as usize] + } - /// Set the output as high. - #[inline] - fn set_high(&self) { - unsafe { self.block().outset.write(|w| w.bits(1u32 << self._pin())) } - } + /// Set the output as high. + #[inline] + fn set_high(&self) { + unsafe { self.block().outset.write(|w| w.bits(1u32 << self._pin())) } + } - /// Set the output as low. - #[inline] - fn set_low(&self) { - unsafe { self.block().outclr.write(|w| w.bits(1u32 << self._pin())) } - } + /// Set the output as low. + #[inline] + fn set_low(&self) { + unsafe { self.block().outclr.write(|w| w.bits(1u32 << self._pin())) } } } /// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an [AnyPin]. -pub trait Pin: Peripheral

+ Into + sealed::Pin + Sized + 'static { +#[allow(private_bounds)] +pub trait Pin: Peripheral

+ Into + SealedPin + Sized + 'static { /// Number of the pin within the port (0..31) #[inline] fn pin(&self) -> u8 { @@ -464,7 +460,7 @@ impl AnyPin { impl_peripheral!(AnyPin); impl Pin for AnyPin {} -impl sealed::Pin for AnyPin { +impl SealedPin for AnyPin { #[inline] fn pin_port(&self) -> u8 { self.pin_port @@ -502,7 +498,7 @@ pub(crate) fn deconfigure_pin(psel_bits: u32) { macro_rules! impl_pin { ($type:ident, $port_num:expr, $pin_num:expr) => { impl crate::gpio::Pin for peripherals::$type {} - impl crate::gpio::sealed::Pin for peripherals::$type { + impl crate::gpio::SealedPin for peripherals::$type { #[inline] fn pin_port(&self) -> u8 { $port_num * 32 + $pin_num diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 4a28279a9..d7f075722 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -7,8 +7,7 @@ use core::task::{Context, Poll}; use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use crate::gpio::sealed::Pin as _; -use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin}; +use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin, SealedPin as _}; use crate::interrupt::InterruptExt; use crate::ppi::{Event, Task}; use crate::{interrupt, pac, peripherals}; @@ -446,14 +445,13 @@ impl<'d> Flex<'d> { // ======================= -mod sealed { - pub trait Channel {} -} +trait SealedChannel {} /// GPIOTE channel trait. /// /// Implemented by all GPIOTE channels. -pub trait Channel: sealed::Channel + Into + Sized + 'static { +#[allow(private_bounds)] +pub trait Channel: SealedChannel + Into + Sized + 'static { /// Get the channel number. fn number(&self) -> usize; @@ -478,7 +476,7 @@ pub struct AnyChannel { number: u8, } impl_peripheral!(AnyChannel); -impl sealed::Channel for AnyChannel {} +impl SealedChannel for AnyChannel {} impl Channel for AnyChannel { fn number(&self) -> usize { self.number as usize @@ -487,7 +485,7 @@ impl Channel for AnyChannel { macro_rules! impl_channel { ($type:ident, $number:expr) => { - impl sealed::Channel for peripherals::$type {} + impl SealedChannel for peripherals::$type {} impl Channel for peripherals::$type { fn number(&self) -> usize { $number as usize diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 907acdf4c..966271ed9 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -6,11 +6,12 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::mem::size_of; use core::ops::{Deref, DerefMut}; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::typelevel::Interrupt; @@ -1140,50 +1141,45 @@ impl MultiBuffering { } } -pub(crate) mod sealed { - use core::sync::atomic::AtomicBool; +/// Peripheral static state +pub(crate) struct State { + started: AtomicBool, + rx_waker: AtomicWaker, + tx_waker: AtomicWaker, + stop_waker: AtomicWaker, +} - use embassy_sync::waitqueue::AtomicWaker; - - /// Peripheral static state - pub struct State { - pub started: AtomicBool, - pub rx_waker: AtomicWaker, - pub tx_waker: AtomicWaker, - pub stop_waker: AtomicWaker, - } - - impl State { - pub const fn new() -> Self { - Self { - started: AtomicBool::new(false), - rx_waker: AtomicWaker::new(), - tx_waker: AtomicWaker::new(), - stop_waker: AtomicWaker::new(), - } +impl State { + pub(crate) const fn new() -> Self { + Self { + started: AtomicBool::new(false), + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), + stop_waker: AtomicWaker::new(), } } - - pub trait Instance { - fn regs() -> &'static crate::pac::i2s::RegisterBlock; - fn state() -> &'static State; - } +} + +pub(crate) trait SealedInstance { + fn regs() -> &'static crate::pac::i2s::RegisterBlock; + fn state() -> &'static State; } /// I2S peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_i2s { ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::i2s::sealed::Instance for peripherals::$type { + impl crate::i2s::SealedInstance for peripherals::$type { fn regs() -> &'static crate::pac::i2s::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } - fn state() -> &'static crate::i2s::sealed::State { - static STATE: crate::i2s::sealed::State = crate::i2s::sealed::State::new(); + fn state() -> &'static crate::i2s::State { + static STATE: crate::i2s::State = crate::i2s::State::new(); &STATE } } diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 754d38310..ef2662c85 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -9,11 +9,11 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; use fixed::types::I7F1; use crate::chip::EASY_DMA_SIZE; -use crate::gpio::sealed::Pin; -use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin}; use crate::interrupt::typelevel::Interrupt; use crate::pac::pdm::mode::{EDGE_A, OPERATION_A}; pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency; @@ -451,42 +451,39 @@ impl<'d, T: Instance> Drop for Pdm<'d, T> { } } -pub(crate) mod sealed { - use embassy_sync::waitqueue::AtomicWaker; +/// Peripheral static state +pub(crate) struct State { + waker: AtomicWaker, +} - /// Peripheral static state - pub struct State { - pub waker: AtomicWaker, - } - - impl State { - pub const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } +impl State { + pub(crate) const fn new() -> Self { + Self { + waker: AtomicWaker::new(), } } - - pub trait Instance { - fn regs() -> &'static crate::pac::pdm::RegisterBlock; - fn state() -> &'static State; - } +} + +pub(crate) trait SealedInstance { + fn regs() -> &'static crate::pac::pdm::RegisterBlock; + fn state() -> &'static State; } /// PDM peripheral instance -pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { /// Interrupt for this peripheral type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_pdm { ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::pdm::sealed::Instance for peripherals::$type { + impl crate::pdm::SealedInstance for peripherals::$type { fn regs() -> &'static crate::pac::pdm::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } - fn state() -> &'static crate::pdm::sealed::State { - static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new(); + fn state() -> &'static crate::pdm::State { + static STATE: crate::pdm::State = crate::pdm::State::new(); &STATE } } diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index f5764b8b7..13f7dcc83 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -210,13 +210,12 @@ unsafe impl Send for Event<'_> {} // ====================== // traits -pub(crate) mod sealed { - pub trait Channel {} - pub trait Group {} -} +pub(crate) trait SealedChannel {} +pub(crate) trait SealedGroup {} /// Interface for PPI channels. -pub trait Channel: sealed::Channel + Peripheral

+ Sized + 'static { +#[allow(private_bounds)] +pub trait Channel: SealedChannel + Peripheral

+ Sized + 'static { /// Returns the number of the channel fn number(&self) -> usize; } @@ -234,7 +233,8 @@ pub trait StaticChannel: Channel + Into { } /// Interface for a group of PPI channels. -pub trait Group: sealed::Group + Peripheral

+ Into + Sized + 'static { +#[allow(private_bounds)] +pub trait Group: SealedGroup + Peripheral

+ Into + Sized + 'static { /// Returns the number of the group. fn number(&self) -> usize; /// Convert into a type erased group. @@ -254,7 +254,7 @@ pub struct AnyStaticChannel { pub(crate) number: u8, } impl_peripheral!(AnyStaticChannel); -impl sealed::Channel for AnyStaticChannel {} +impl SealedChannel for AnyStaticChannel {} impl Channel for AnyStaticChannel { fn number(&self) -> usize { self.number as usize @@ -272,7 +272,7 @@ pub struct AnyConfigurableChannel { pub(crate) number: u8, } impl_peripheral!(AnyConfigurableChannel); -impl sealed::Channel for AnyConfigurableChannel {} +impl SealedChannel for AnyConfigurableChannel {} impl Channel for AnyConfigurableChannel { fn number(&self) -> usize { self.number as usize @@ -287,7 +287,7 @@ impl ConfigurableChannel for AnyConfigurableChannel { #[cfg(not(feature = "nrf51"))] macro_rules! impl_ppi_channel { ($type:ident, $number:expr) => { - impl crate::ppi::sealed::Channel for peripherals::$type {} + impl crate::ppi::SealedChannel for peripherals::$type {} impl crate::ppi::Channel for peripherals::$type { fn number(&self) -> usize { $number @@ -338,7 +338,7 @@ pub struct AnyGroup { number: u8, } impl_peripheral!(AnyGroup); -impl sealed::Group for AnyGroup {} +impl SealedGroup for AnyGroup {} impl Group for AnyGroup { fn number(&self) -> usize { self.number as usize @@ -347,7 +347,7 @@ impl Group for AnyGroup { macro_rules! impl_group { ($type:ident, $number:expr) => { - impl sealed::Group for peripherals::$type {} + impl SealedGroup for peripherals::$type {} impl Group for peripherals::$type { fn number(&self) -> usize { $number diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 833370d4b..1318d3f94 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -6,8 +6,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use embassy_hal_internal::{into_ref, PeripheralRef}; -use crate::gpio::sealed::Pin as _; -use crate::gpio::{AnyPin, Pin as GpioPin, PselBits}; +use crate::gpio::{AnyPin, Pin as GpioPin, PselBits, SealedPin as _}; use crate::ppi::{Event, Task}; use crate::util::slice_in_ram_or; use crate::{interrupt, pac, Peripheral}; @@ -847,23 +846,20 @@ impl<'a, T: Instance> Drop for SimplePwm<'a, T> { } } -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - fn regs() -> &'static pac::pwm0::RegisterBlock; - } +pub(crate) trait SealedInstance { + fn regs() -> &'static pac::pwm0::RegisterBlock; } /// PWM peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_pwm { ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::pwm::sealed::Instance for peripherals::$type { + impl crate::pwm::SealedInstance for peripherals::$type { fn regs() -> &'static pac::pwm0::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 9455ec925..7409c9b1e 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -7,9 +7,9 @@ use core::marker::PhantomData; use core::task::Poll; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; -use crate::gpio::sealed::Pin as _; -use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin as _}; use crate::interrupt::typelevel::Interrupt; use crate::{interrupt, Peripheral}; @@ -245,42 +245,39 @@ pub enum LedPolarity { ActiveLow, } -pub(crate) mod sealed { - use embassy_sync::waitqueue::AtomicWaker; +/// Peripheral static state +pub(crate) struct State { + waker: AtomicWaker, +} - /// Peripheral static state - pub struct State { - pub waker: AtomicWaker, - } - - impl State { - pub const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } +impl State { + pub(crate) const fn new() -> Self { + Self { + waker: AtomicWaker::new(), } } - - pub trait Instance { - fn regs() -> &'static crate::pac::qdec::RegisterBlock; - fn state() -> &'static State; - } +} + +pub(crate) trait SealedInstance { + fn regs() -> &'static crate::pac::qdec::RegisterBlock; + fn state() -> &'static State; } /// qdec peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_qdec { ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::qdec::sealed::Instance for peripherals::$type { + impl crate::qdec::SealedInstance for peripherals::$type { fn regs() -> &'static crate::pac::qdec::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } - fn state() -> &'static crate::qdec::sealed::State { - static STATE: crate::qdec::sealed::State = crate::qdec::sealed::State::new(); + fn state() -> &'static crate::qdec::State { + static STATE: crate::qdec::State = crate::qdec::State::new(); &STATE } } diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 4134a4c87..060fe72cd 100755 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -9,6 +9,7 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use crate::gpio::{self, Pin as GpioPin}; @@ -652,42 +653,39 @@ mod _eh1 { impl<'d, T: Instance> embedded_storage_async::nor_flash::MultiwriteNorFlash for Qspi<'d, T> {} } -pub(crate) mod sealed { - use embassy_sync::waitqueue::AtomicWaker; +/// Peripheral static state +pub(crate) struct State { + waker: AtomicWaker, +} - /// Peripheral static state - pub struct State { - pub waker: AtomicWaker, - } - - impl State { - pub const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } +impl State { + pub(crate) const fn new() -> Self { + Self { + waker: AtomicWaker::new(), } } - - pub trait Instance { - fn regs() -> &'static crate::pac::qspi::RegisterBlock; - fn state() -> &'static State; - } +} + +pub(crate) trait SealedInstance { + fn regs() -> &'static crate::pac::qspi::RegisterBlock; + fn state() -> &'static State; } /// QSPI peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_qspi { ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::qspi::sealed::Instance for peripherals::$type { + impl crate::qspi::SealedInstance for peripherals::$type { fn regs() -> &'static crate::pac::qspi::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } - fn state() -> &'static crate::qspi::sealed::State { - static STATE: crate::qspi::sealed::State = crate::qspi::sealed::State::new(); + fn state() -> &'static crate::qspi::State { + static STATE: crate::qspi::State = crate::qspi::State::new(); &STATE } } diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 4c0cc3280..8edca1df2 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs @@ -19,6 +19,7 @@ pub mod ieee802154; use core::marker::PhantomData; +use embassy_sync::waitqueue::AtomicWaker; use pac::radio::state::STATE_A as RadioState; pub use pac::radio::txpower::TXPOWER_A as TxPower; @@ -56,36 +57,32 @@ impl interrupt::typelevel::Handler for InterruptHandl } } -pub(crate) mod sealed { - use embassy_sync::waitqueue::AtomicWaker; - - pub struct State { - /// end packet transmission or reception - pub event_waker: AtomicWaker, - } - impl State { - pub const fn new() -> Self { - Self { - event_waker: AtomicWaker::new(), - } +pub(crate) struct State { + /// end packet transmission or reception + event_waker: AtomicWaker, +} +impl State { + pub(crate) const fn new() -> Self { + Self { + event_waker: AtomicWaker::new(), } } +} - pub trait Instance { - fn regs() -> &'static crate::pac::radio::RegisterBlock; - fn state() -> &'static State; - } +pub(crate) trait SealedInstance { + fn regs() -> &'static crate::pac::radio::RegisterBlock; + fn state() -> &'static State; } macro_rules! impl_radio { ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::radio::sealed::Instance for peripherals::$type { + impl crate::radio::SealedInstance for peripherals::$type { fn regs() -> &'static pac::radio::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } - fn state() -> &'static crate::radio::sealed::State { - static STATE: crate::radio::sealed::State = crate::radio::sealed::State::new(); + fn state() -> &'static crate::radio::State { + static STATE: crate::radio::State = crate::radio::State::new(); &STATE } } @@ -96,7 +93,8 @@ macro_rules! impl_radio { } /// Radio peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index 1c463fb7c..ff61e08f3 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -2,13 +2,16 @@ #![macro_use] +use core::cell::{RefCell, RefMut}; use core::future::poll_fn; use core::marker::PhantomData; use core::ptr; use core::task::Poll; +use critical_section::{CriticalSection, Mutex}; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::WakerRegistration; use crate::interrupt::typelevel::Interrupt; use crate::{interrupt, Peripheral}; @@ -205,73 +208,61 @@ impl<'d, T: Instance> rand_core::RngCore for Rng<'d, T> { impl<'d, T: Instance> rand_core::CryptoRng for Rng<'d, T> {} -pub(crate) mod sealed { - use core::cell::{Ref, RefCell, RefMut}; +/// Peripheral static state +pub(crate) struct State { + inner: Mutex>, +} - use critical_section::{CriticalSection, Mutex}; - use embassy_sync::waitqueue::WakerRegistration; +struct InnerState { + ptr: *mut u8, + end: *mut u8, + waker: WakerRegistration, +} - use super::*; +unsafe impl Send for InnerState {} - /// Peripheral static state - pub struct State { - inner: Mutex>, - } - - pub struct InnerState { - pub ptr: *mut u8, - pub end: *mut u8, - pub waker: WakerRegistration, - } - - unsafe impl Send for InnerState {} - - impl State { - pub const fn new() -> Self { - Self { - inner: Mutex::new(RefCell::new(InnerState::new())), - } - } - - pub fn borrow<'cs>(&'cs self, cs: CriticalSection<'cs>) -> Ref<'cs, InnerState> { - self.inner.borrow(cs).borrow() - } - - pub fn borrow_mut<'cs>(&'cs self, cs: CriticalSection<'cs>) -> RefMut<'cs, InnerState> { - self.inner.borrow(cs).borrow_mut() +impl State { + pub(crate) const fn new() -> Self { + Self { + inner: Mutex::new(RefCell::new(InnerState::new())), } } - impl InnerState { - pub const fn new() -> Self { - Self { - ptr: ptr::null_mut(), - end: ptr::null_mut(), - waker: WakerRegistration::new(), - } - } - } - - pub trait Instance { - fn regs() -> &'static crate::pac::rng::RegisterBlock; - fn state() -> &'static State; + fn borrow_mut<'cs>(&'cs self, cs: CriticalSection<'cs>) -> RefMut<'cs, InnerState> { + self.inner.borrow(cs).borrow_mut() } } +impl InnerState { + const fn new() -> Self { + Self { + ptr: ptr::null_mut(), + end: ptr::null_mut(), + waker: WakerRegistration::new(), + } + } +} + +pub(crate) trait SealedInstance { + fn regs() -> &'static crate::pac::rng::RegisterBlock; + fn state() -> &'static State; +} + /// RNG peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_rng { ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::rng::sealed::Instance for peripherals::$type { + impl crate::rng::SealedInstance for peripherals::$type { fn regs() -> &'static crate::pac::rng::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } - fn state() -> &'static crate::rng::sealed::State { - static STATE: crate::rng::sealed::State = crate::rng::sealed::State::new(); + fn state() -> &'static crate::rng::State { + static STATE: crate::rng::State = crate::rng::State::new(); &STATE } } diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 662b05614..17c65fa3e 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -16,7 +16,6 @@ pub(crate) use saadc::ch::pselp::PSELP_A as InputChannel; use saadc::oversample::OVERSAMPLE_A; use saadc::resolution::VAL_A; -use self::sealed::Input as _; use crate::interrupt::InterruptExt; use crate::ppi::{ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; @@ -662,16 +661,13 @@ pub enum Resolution { _14BIT = 3, } -pub(crate) mod sealed { - use super::*; - - pub trait Input { - fn channel(&self) -> InputChannel; - } +pub(crate) trait SealedInput { + fn channel(&self) -> InputChannel; } /// An input that can be used as either or negative end of a ADC differential in the SAADC periperhal. -pub trait Input: sealed::Input + Into + Peripheral

+ Sized + 'static { +#[allow(private_bounds)] +pub trait Input: SealedInput + Into + Peripheral

+ Sized + 'static { /// Convert this SAADC input to a type-erased `AnyInput`. /// /// This allows using several inputs in situations that might require @@ -693,7 +689,7 @@ pub struct AnyInput { impl_peripheral!(AnyInput); -impl sealed::Input for AnyInput { +impl SealedInput for AnyInput { fn channel(&self) -> InputChannel { self.channel } @@ -706,7 +702,7 @@ macro_rules! impl_saadc_input { impl_saadc_input!(@local, crate::peripherals::$pin, $ch); }; (@local, $pin:ty, $ch:ident) => { - impl crate::saadc::sealed::Input for $pin { + impl crate::saadc::SealedInput for $pin { fn channel(&self) -> crate::saadc::InputChannel { crate::saadc::InputChannel::$ch } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index c45d45e68..373f22642 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -4,18 +4,20 @@ use core::future::poll_fn; use core::marker::PhantomData; +#[cfg(feature = "_nrf52832_anomaly_109")] +use core::sync::atomic::AtomicU8; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; pub use pac::spim0::config::ORDER_A as BitOrder; pub use pac::spim0::frequency::FREQUENCY_A as Frequency; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; -use crate::gpio::sealed::Pin as _; -use crate::gpio::{self, convert_drive, AnyPin, OutputDrive, Pin as GpioPin, PselBits}; +use crate::gpio::{self, convert_drive, AnyPin, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _}; use crate::interrupt::typelevel::Interrupt; use crate::util::{slice_in_ram_or, slice_ptr_len, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{interrupt, pac, Peripheral}; @@ -487,54 +489,46 @@ impl<'d, T: Instance> Drop for Spim<'d, T> { } } -pub(crate) mod sealed { +pub(crate) struct State { + waker: AtomicWaker, #[cfg(feature = "_nrf52832_anomaly_109")] - use core::sync::atomic::AtomicU8; + rx: AtomicU8, + #[cfg(feature = "_nrf52832_anomaly_109")] + tx: AtomicU8, +} - use embassy_sync::waitqueue::AtomicWaker; - - use super::*; - - pub struct State { - pub waker: AtomicWaker, - #[cfg(feature = "_nrf52832_anomaly_109")] - pub rx: AtomicU8, - #[cfg(feature = "_nrf52832_anomaly_109")] - pub tx: AtomicU8, - } - - impl State { - pub const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - #[cfg(feature = "_nrf52832_anomaly_109")] - rx: AtomicU8::new(0), - #[cfg(feature = "_nrf52832_anomaly_109")] - tx: AtomicU8::new(0), - } +impl State { + pub(crate) const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + #[cfg(feature = "_nrf52832_anomaly_109")] + rx: AtomicU8::new(0), + #[cfg(feature = "_nrf52832_anomaly_109")] + tx: AtomicU8::new(0), } } - - pub trait Instance { - fn regs() -> &'static pac::spim0::RegisterBlock; - fn state() -> &'static State; - } +} + +pub(crate) trait SealedInstance { + fn regs() -> &'static pac::spim0::RegisterBlock; + fn state() -> &'static State; } /// SPIM peripheral instance -pub trait Instance: Peripheral

+ sealed::Instance + 'static { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_spim { ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::spim::sealed::Instance for peripherals::$type { + impl crate::spim::SealedInstance for peripherals::$type { fn regs() -> &'static pac::spim0::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } - fn state() -> &'static crate::spim::sealed::State { - static STATE: crate::spim::sealed::State = crate::spim::sealed::State::new(); + fn state() -> &'static crate::spim::State { + static STATE: crate::spim::State = crate::spim::State::new(); &STATE } } diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 772ca40cc..47bbeaf77 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -8,12 +8,12 @@ use core::task::Poll; use embassy_embedded_hal::SetConfig; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; pub use pac::spis0::config::ORDER_A as BitOrder; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; -use crate::gpio::sealed::Pin as _; -use crate::gpio::{self, AnyPin, Pin as GpioPin}; +use crate::gpio::{self, AnyPin, Pin as GpioPin, SealedPin as _}; use crate::interrupt::typelevel::Interrupt; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::{interrupt, pac, Peripheral}; @@ -456,43 +456,38 @@ impl<'d, T: Instance> Drop for Spis<'d, T> { } } -pub(crate) mod sealed { - use embassy_sync::waitqueue::AtomicWaker; +pub(crate) struct State { + waker: AtomicWaker, +} - use super::*; - - pub struct State { - pub waker: AtomicWaker, - } - - impl State { - pub const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } +impl State { + pub(crate) const fn new() -> Self { + Self { + waker: AtomicWaker::new(), } } - - pub trait Instance { - fn regs() -> &'static pac::spis0::RegisterBlock; - fn state() -> &'static State; - } +} + +pub(crate) trait SealedInstance { + fn regs() -> &'static pac::spis0::RegisterBlock; + fn state() -> &'static State; } /// SPIS peripheral instance -pub trait Instance: Peripheral

+ sealed::Instance + 'static { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_spis { ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::spis::sealed::Instance for peripherals::$type { + impl crate::spis::SealedInstance for peripherals::$type { fn regs() -> &'static pac::spis0::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } - fn state() -> &'static crate::spis::sealed::State { - static STATE: crate::spis::sealed::State = crate::spis::sealed::State::new(); + fn state() -> &'static crate::spis::State { + static STATE: crate::spis::State = crate::spis::State::new(); &STATE } } diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 2970ad3f2..ac5328ded 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -11,30 +11,25 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::ppi::{Event, Task}; use crate::{pac, Peripheral}; -pub(crate) mod sealed { - - use super::*; - - pub trait Instance { - /// The number of CC registers this instance has. - const CCS: usize; - fn regs() -> &'static pac::timer0::RegisterBlock; - } - pub trait ExtendedInstance {} +pub(crate) trait SealedInstance { + /// The number of CC registers this instance has. + const CCS: usize; + fn regs() -> &'static pac::timer0::RegisterBlock; } /// Basic Timer instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: crate::interrupt::typelevel::Interrupt; } /// Extended timer instance. -pub trait ExtendedInstance: Instance + sealed::ExtendedInstance {} +pub trait ExtendedInstance: Instance {} macro_rules! impl_timer { ($type:ident, $pac_type:ident, $irq:ident, $ccs:literal) => { - impl crate::timer::sealed::Instance for peripherals::$type { + impl crate::timer::SealedInstance for peripherals::$type { const CCS: usize = $ccs; fn regs() -> &'static pac::timer0::RegisterBlock { unsafe { &*(pac::$pac_type::ptr() as *const pac::timer0::RegisterBlock) } @@ -49,7 +44,6 @@ macro_rules! impl_timer { }; ($type:ident, $pac_type:ident, $irq:ident, extended) => { impl_timer!($type, $pac_type, $irq, 6); - impl crate::timer::sealed::ExtendedInstance for peripherals::$type {} impl crate::timer::ExtendedInstance for peripherals::$type {} }; } diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 24810a08c..c64743ecc 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -727,41 +727,38 @@ impl<'a, T: Instance> Drop for Twim<'a, T> { } } -pub(crate) mod sealed { - use super::*; +pub(crate) struct State { + end_waker: AtomicWaker, +} - pub struct State { - pub end_waker: AtomicWaker, - } - - impl State { - pub const fn new() -> Self { - Self { - end_waker: AtomicWaker::new(), - } +impl State { + pub(crate) const fn new() -> Self { + Self { + end_waker: AtomicWaker::new(), } } - - pub trait Instance { - fn regs() -> &'static pac::twim0::RegisterBlock; - fn state() -> &'static State; - } +} + +pub(crate) trait SealedInstance { + fn regs() -> &'static pac::twim0::RegisterBlock; + fn state() -> &'static State; } /// TWIM peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_twim { ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::twim::sealed::Instance for peripherals::$type { + impl crate::twim::SealedInstance for peripherals::$type { fn regs() -> &'static pac::twim0::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } - fn state() -> &'static crate::twim::sealed::State { - static STATE: crate::twim::sealed::State = crate::twim::sealed::State::new(); + fn state() -> &'static crate::twim::State { + static STATE: crate::twim::State = crate::twim::State::new(); &STATE } } diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index 415150447..f3eab008f 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -754,41 +754,38 @@ impl<'a, T: Instance> Drop for Twis<'a, T> { } } -pub(crate) mod sealed { - use super::*; +pub(crate) struct State { + waker: AtomicWaker, +} - pub struct State { - pub waker: AtomicWaker, - } - - impl State { - pub const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } +impl State { + pub(crate) const fn new() -> Self { + Self { + waker: AtomicWaker::new(), } } - - pub trait Instance { - fn regs() -> &'static pac::twis0::RegisterBlock; - fn state() -> &'static State; - } +} + +pub(crate) trait SealedInstance { + fn regs() -> &'static pac::twis0::RegisterBlock; + fn state() -> &'static State; } /// TWIS peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_twis { ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::twis::sealed::Instance for peripherals::$type { + impl crate::twis::SealedInstance for peripherals::$type { fn regs() -> &'static pac::twis0::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } - fn state() -> &'static crate::twis::sealed::State { - static STATE: crate::twis::sealed::State = crate::twis::sealed::State::new(); + fn state() -> &'static crate::twis::State { + static STATE: crate::twis::State = crate::twis::State::new(); &STATE } } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index cbd5dccbc..fa0a773a8 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -15,18 +15,18 @@ use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{compiler_fence, AtomicU8, Ordering}; use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; use pac::uarte0::RegisterBlock; // Re-export SVD variants to allow user to directly set values. pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; -use crate::gpio::sealed::Pin as _; -use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; +use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits, SealedPin as _}; use crate::interrupt::typelevel::Interrupt; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; @@ -939,7 +939,7 @@ pub(crate) fn apply_workaround_for_enable_anomaly(r: &crate::pac::uarte0::Regist } } -pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) { +pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &State) { if s.tx_rx_refcount.fetch_sub(1, Ordering::Relaxed) == 1 { // Finally we can disable, and we do so for the peripheral // i.e. not just rx concerns. @@ -954,49 +954,42 @@ pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) { } } -pub(crate) mod sealed { - use core::sync::atomic::AtomicU8; - - use embassy_sync::waitqueue::AtomicWaker; - - use super::*; - - pub struct State { - pub rx_waker: AtomicWaker, - pub tx_waker: AtomicWaker, - pub tx_rx_refcount: AtomicU8, - } - impl State { - pub const fn new() -> Self { - Self { - rx_waker: AtomicWaker::new(), - tx_waker: AtomicWaker::new(), - tx_rx_refcount: AtomicU8::new(0), - } +pub(crate) struct State { + pub(crate) rx_waker: AtomicWaker, + pub(crate) tx_waker: AtomicWaker, + pub(crate) tx_rx_refcount: AtomicU8, +} +impl State { + pub(crate) const fn new() -> Self { + Self { + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), + tx_rx_refcount: AtomicU8::new(0), } } - - pub trait Instance { - fn regs() -> &'static pac::uarte0::RegisterBlock; - fn state() -> &'static State; - fn buffered_state() -> &'static crate::buffered_uarte::State; - } +} + +pub(crate) trait SealedInstance { + fn regs() -> &'static pac::uarte0::RegisterBlock; + fn state() -> &'static State; + fn buffered_state() -> &'static crate::buffered_uarte::State; } /// UARTE peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_uarte { ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::uarte::sealed::Instance for peripherals::$type { + impl crate::uarte::SealedInstance for peripherals::$type { fn regs() -> &'static pac::uarte0::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } - fn state() -> &'static crate::uarte::sealed::State { - static STATE: crate::uarte::sealed::State = crate::uarte::sealed::State::new(); + fn state() -> &'static crate::uarte::State { + static STATE: crate::uarte::State = crate::uarte::State::new(); &STATE } fn buffered_state() -> &'static crate::buffered_uarte::State { diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs index e26b49db3..09cf87e97 100644 --- a/embassy-nrf/src/usb/mod.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -793,23 +793,20 @@ impl Allocator { } } -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - fn regs() -> &'static pac::usbd::RegisterBlock; - } +pub(crate) trait SealedInstance { + fn regs() -> &'static pac::usbd::RegisterBlock; } /// USB peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } macro_rules! impl_usb { ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::usb::sealed::Instance for peripherals::$type { + impl crate::usb::SealedInstance for peripherals::$type { fn regs() -> &'static pac::usbd::RegisterBlock { unsafe { &*pac::$pac_type::ptr() } } From a84b33995eacc32e0e13d70293fa9bd7b2bd75f8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 5 Apr 2024 00:35:25 +0200 Subject: [PATCH 54/69] rp: remove mod sealed. --- embassy-rp/src/adc.rs | 24 ++++---- embassy-rp/src/clocks.rs | 11 ++-- embassy-rp/src/dma.rs | 23 ++++--- embassy-rp/src/flash.rs | 16 ++--- embassy-rp/src/gpio.rs | 96 ++++++++++++++---------------- embassy-rp/src/i2c.rs | 51 +++++++--------- embassy-rp/src/pio/mod.rs | 64 ++++++++++---------- embassy-rp/src/pwm.rs | 12 ++-- embassy-rp/src/rtc/mod.rs | 11 ++-- embassy-rp/src/spi.rs | 27 ++++----- embassy-rp/src/uart/mod.rs | 55 ++++++++--------- embassy-rp/src/usb.rs | 13 ++-- examples/rp/src/bin/pio_stepper.rs | 2 +- 13 files changed, 189 insertions(+), 216 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 4c01fe195..101c5b71f 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -8,8 +8,7 @@ use core::task::Poll; use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use crate::gpio::sealed::Pin as GpioPin; -use crate::gpio::{self, AnyPin, Pull}; +use crate::gpio::{self, AnyPin, Pull, SealedPin as GpioPin}; use crate::interrupt::typelevel::Binding; use crate::interrupt::InterruptExt; use crate::peripherals::{ADC, ADC_TEMP_SENSOR}; @@ -334,29 +333,28 @@ impl interrupt::typelevel::Handler for Inter } } -mod sealed { - pub trait AdcSample: crate::dma::Word {} - - pub trait AdcChannel {} -} +trait SealedAdcSample: crate::dma::Word {} +trait SealedAdcChannel {} /// ADC sample. -pub trait AdcSample: sealed::AdcSample {} +#[allow(private_bounds)] +pub trait AdcSample: SealedAdcSample {} -impl sealed::AdcSample for u16 {} +impl SealedAdcSample for u16 {} impl AdcSample for u16 {} -impl sealed::AdcSample for u8 {} +impl SealedAdcSample for u8 {} impl AdcSample for u8 {} /// ADC channel. -pub trait AdcChannel: sealed::AdcChannel {} +#[allow(private_bounds)] +pub trait AdcChannel: SealedAdcChannel {} /// ADC pin. pub trait AdcPin: AdcChannel + gpio::Pin {} macro_rules! impl_pin { ($pin:ident, $channel:expr) => { - impl sealed::AdcChannel for peripherals::$pin {} + impl SealedAdcChannel for peripherals::$pin {} impl AdcChannel for peripherals::$pin {} impl AdcPin for peripherals::$pin {} }; @@ -367,5 +365,5 @@ impl_pin!(PIN_27, 1); impl_pin!(PIN_28, 2); impl_pin!(PIN_29, 3); -impl sealed::AdcChannel for peripherals::ADC_TEMP_SENSOR {} +impl SealedAdcChannel for peripherals::ADC_TEMP_SENSOR {} impl AdcChannel for peripherals::ADC_TEMP_SENSOR {} diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index b7f6aeac9..bedb79464 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -6,8 +6,7 @@ use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; use embassy_hal_internal::{into_ref, PeripheralRef}; use pac::clocks::vals::*; -use crate::gpio::sealed::Pin; -use crate::gpio::AnyPin; +use crate::gpio::{AnyPin, SealedPin}; use crate::pac::common::{Reg, RW}; use crate::{pac, reset, Peripheral}; @@ -788,14 +787,14 @@ impl_gpinpin!(PIN_20, 20, 0); impl_gpinpin!(PIN_22, 22, 1); /// General purpose clock input driver. -pub struct Gpin<'d, T: Pin> { +pub struct Gpin<'d, T: GpinPin> { gpin: PeripheralRef<'d, AnyPin>, _phantom: PhantomData, } -impl<'d, T: Pin> Gpin<'d, T> { +impl<'d, T: GpinPin> Gpin<'d, T> { /// Create new gpin driver. - pub fn new(gpin: impl Peripheral

+ 'd) -> Gpin<'d, P> { + pub fn new(gpin: impl Peripheral

+ 'd) -> Self { into_ref!(gpin); gpin.gpio().ctrl().write(|w| w.set_funcsel(0x08)); @@ -811,7 +810,7 @@ impl<'d, T: Pin> Gpin<'d, T> { // } } -impl<'d, T: Pin> Drop for Gpin<'d, T> { +impl<'d, T: GpinPin> Drop for Gpin<'d, T> { fn drop(&mut self) { self.gpin .gpio() diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 44aabce6b..e6374a86c 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -208,14 +208,12 @@ pub(crate) const CHANNEL_COUNT: usize = 12; const NEW_AW: AtomicWaker = AtomicWaker::new(); static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; -mod sealed { - pub trait Channel {} - - pub trait Word {} -} +trait SealedChannel {} +trait SealedWord {} /// DMA channel interface. -pub trait Channel: Peripheral

+ sealed::Channel + Into + Sized + 'static { +#[allow(private_bounds)] +pub trait Channel: Peripheral

+ SealedChannel + Into + Sized + 'static { /// Channel number. fn number(&self) -> u8; @@ -231,26 +229,27 @@ pub trait Channel: Peripheral

+ sealed::Channel + Into + S } /// DMA word. -pub trait Word: sealed::Word { +#[allow(private_bounds)] +pub trait Word: SealedWord { /// Word size. fn size() -> vals::DataSize; } -impl sealed::Word for u8 {} +impl SealedWord for u8 {} impl Word for u8 { fn size() -> vals::DataSize { vals::DataSize::SIZE_BYTE } } -impl sealed::Word for u16 {} +impl SealedWord for u16 {} impl Word for u16 { fn size() -> vals::DataSize { vals::DataSize::SIZE_HALFWORD } } -impl sealed::Word for u32 {} +impl SealedWord for u32 {} impl Word for u32 { fn size() -> vals::DataSize { vals::DataSize::SIZE_WORD @@ -264,7 +263,7 @@ pub struct AnyChannel { impl_peripheral!(AnyChannel); -impl sealed::Channel for AnyChannel {} +impl SealedChannel for AnyChannel {} impl Channel for AnyChannel { fn number(&self) -> u8 { self.number @@ -273,7 +272,7 @@ impl Channel for AnyChannel { macro_rules! channel { ($name:ident, $num:expr) => { - impl sealed::Channel for peripherals::$name {} + impl SealedChannel for peripherals::$name {} impl Channel for peripherals::$name { fn number(&self) -> u8 { $num diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 422b77400..45b385cb4 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -903,22 +903,22 @@ pub(crate) unsafe fn in_ram(operation: impl FnOnce()) -> Result<(), Error> { Ok(()) } -mod sealed { - pub trait Instance {} - pub trait Mode {} -} +trait SealedInstance {} +trait SealedMode {} /// Flash instance. -pub trait Instance: sealed::Instance {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance {} /// Flash mode. -pub trait Mode: sealed::Mode {} +#[allow(private_bounds)] +pub trait Mode: SealedMode {} -impl sealed::Instance for FLASH {} +impl SealedInstance for FLASH {} impl Instance for FLASH {} macro_rules! impl_mode { ($name:ident) => { - impl sealed::Mode for $name {} + impl SealedMode for $name {} impl Mode for $name {} }; } diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index a84c00a2c..ea87fd9da 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -8,7 +8,6 @@ use core::task::{Context, Poll}; use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use self::sealed::Pin as _; use crate::interrupt::InterruptExt; use crate::pac::common::{Reg, RW}; use crate::pac::SIO; @@ -802,68 +801,65 @@ impl<'w> Drop for DormantWake<'w> { } } -pub(crate) mod sealed { - use super::*; +pub(crate) trait SealedPin: Sized { + fn pin_bank(&self) -> u8; - pub trait Pin: Sized { - fn pin_bank(&self) -> u8; + #[inline] + fn _pin(&self) -> u8 { + self.pin_bank() & 0x1f + } - #[inline] - fn _pin(&self) -> u8 { - self.pin_bank() & 0x1f + #[inline] + fn _bank(&self) -> Bank { + match self.pin_bank() >> 5 { + #[cfg(feature = "qspi-as-gpio")] + 1 => Bank::Qspi, + _ => Bank::Bank0, } + } - #[inline] - fn _bank(&self) -> Bank { - match self.pin_bank() >> 5 { - #[cfg(feature = "qspi-as-gpio")] - 1 => Bank::Qspi, - _ => Bank::Bank0, - } + fn io(&self) -> pac::io::Io { + match self._bank() { + Bank::Bank0 => crate::pac::IO_BANK0, + #[cfg(feature = "qspi-as-gpio")] + Bank::Qspi => crate::pac::IO_QSPI, } + } - fn io(&self) -> pac::io::Io { - match self._bank() { - Bank::Bank0 => crate::pac::IO_BANK0, - #[cfg(feature = "qspi-as-gpio")] - Bank::Qspi => crate::pac::IO_QSPI, - } - } + fn gpio(&self) -> pac::io::Gpio { + self.io().gpio(self._pin() as _) + } - fn gpio(&self) -> pac::io::Gpio { - self.io().gpio(self._pin() as _) - } + fn pad_ctrl(&self) -> Reg { + let block = match self._bank() { + Bank::Bank0 => crate::pac::PADS_BANK0, + #[cfg(feature = "qspi-as-gpio")] + Bank::Qspi => crate::pac::PADS_QSPI, + }; + block.gpio(self._pin() as _) + } - fn pad_ctrl(&self) -> Reg { - let block = match self._bank() { - Bank::Bank0 => crate::pac::PADS_BANK0, - #[cfg(feature = "qspi-as-gpio")] - Bank::Qspi => crate::pac::PADS_QSPI, - }; - block.gpio(self._pin() as _) - } + fn sio_out(&self) -> pac::sio::Gpio { + SIO.gpio_out(self._bank() as _) + } - fn sio_out(&self) -> pac::sio::Gpio { - SIO.gpio_out(self._bank() as _) - } + fn sio_oe(&self) -> pac::sio::Gpio { + SIO.gpio_oe(self._bank() as _) + } - fn sio_oe(&self) -> pac::sio::Gpio { - SIO.gpio_oe(self._bank() as _) - } + fn sio_in(&self) -> Reg { + SIO.gpio_in(self._bank() as _) + } - fn sio_in(&self) -> Reg { - SIO.gpio_in(self._bank() as _) - } - - fn int_proc(&self) -> pac::io::Int { - let proc = SIO.cpuid().read(); - self.io().int_proc(proc as _) - } + fn int_proc(&self) -> pac::io::Int { + let proc = SIO.cpuid().read(); + self.io().int_proc(proc as _) } } /// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an [AnyPin]. -pub trait Pin: Peripheral

+ Into + sealed::Pin + Sized + 'static { +#[allow(private_bounds)] +pub trait Pin: Peripheral

+ Into + SealedPin + Sized + 'static { /// Degrade to a generic pin struct fn degrade(self) -> AnyPin { AnyPin { @@ -903,7 +899,7 @@ impl AnyPin { impl_peripheral!(AnyPin); impl Pin for AnyPin {} -impl sealed::Pin for AnyPin { +impl SealedPin for AnyPin { fn pin_bank(&self) -> u8 { self.pin_bank } @@ -914,7 +910,7 @@ impl sealed::Pin for AnyPin { macro_rules! impl_pin { ($name:ident, $bank:expr, $pin_num:expr) => { impl Pin for peripherals::$name {} - impl sealed::Pin for peripherals::$name { + impl SealedPin for peripherals::$name { #[inline] fn pin_bank(&self) -> u8 { ($bank as u8) * 32 + $pin_num diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 26a819b25..256875b4a 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -784,34 +784,24 @@ pub fn i2c_reserved_addr(addr: u16) -> bool { ((addr & 0x78) == 0 || (addr & 0x78) == 0x78) && addr != 0 } -mod sealed { - use embassy_sync::waitqueue::AtomicWaker; +pub(crate) trait SealedInstance { + const TX_DREQ: u8; + const RX_DREQ: u8; - use crate::interrupt; - - pub trait Instance { - const TX_DREQ: u8; - const RX_DREQ: u8; - - type Interrupt: interrupt::typelevel::Interrupt; - - fn regs() -> crate::pac::i2c::I2c; - fn reset() -> crate::pac::resets::regs::Peripherals; - fn waker() -> &'static AtomicWaker; - } - - pub trait Mode {} - - pub trait SdaPin {} - pub trait SclPin {} + fn regs() -> crate::pac::i2c::I2c; + fn reset() -> crate::pac::resets::regs::Peripherals; + fn waker() -> &'static AtomicWaker; } +trait SealedMode {} + /// Driver mode. -pub trait Mode: sealed::Mode {} +#[allow(private_bounds)] +pub trait Mode: SealedMode {} macro_rules! impl_mode { ($name:ident) => { - impl sealed::Mode for $name {} + impl SealedMode for $name {} impl Mode for $name {} }; } @@ -825,16 +815,18 @@ impl_mode!(Blocking); impl_mode!(Async); /// I2C instance. -pub trait Instance: sealed::Instance {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance { + /// Interrupt for this peripheral. + type Interrupt: interrupt::typelevel::Interrupt; +} macro_rules! impl_instance { ($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => { - impl sealed::Instance for peripherals::$type { + impl SealedInstance for peripherals::$type { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; - type Interrupt = crate::interrupt::typelevel::$irq; - #[inline] fn regs() -> pac::i2c::I2c { pac::$type @@ -854,7 +846,9 @@ macro_rules! impl_instance { &WAKER } } - impl Instance for peripherals::$type {} + impl Instance for peripherals::$type { + type Interrupt = crate::interrupt::typelevel::$irq; + } }; } @@ -862,13 +856,12 @@ impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33); impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35); /// SDA pin. -pub trait SdaPin: sealed::SdaPin + crate::gpio::Pin {} +pub trait SdaPin: crate::gpio::Pin {} /// SCL pin. -pub trait SclPin: sealed::SclPin + crate::gpio::Pin {} +pub trait SclPin: crate::gpio::Pin {} macro_rules! impl_pin { ($pin:ident, $instance:ident, $function:ident) => { - impl sealed::$function for peripherals::$pin {} impl $function for peripherals::$pin {} }; } diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index 7eca700ba..2e5c57a26 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -15,8 +15,7 @@ use pac::pio::vals::SmExecctrlStatusSel; use pio::{Program, SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; -use crate::gpio::sealed::Pin as SealedPin; -use crate::gpio::{self, AnyPin, Drive, Level, Pull, SlewRate}; +use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate}; use crate::interrupt::typelevel::{Binding, Handler, Interrupt}; use crate::pac::dma::vals::TreqSel; use crate::relocate::RelocatedProgram; @@ -695,6 +694,12 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } } + /// Set the clock divider for this state machine. + pub fn set_clock_divider(&mut self, clock_divider: FixedU32) { + let sm = Self::this_sm(); + sm.clkdiv().write(|w| w.0 = clock_divider.to_bits() << 8); + } + #[inline(always)] fn this_sm() -> crate::pac::pio::StateMachine { PIO::PIO.sm(SM) @@ -1148,49 +1153,47 @@ fn on_pio_drop() { } } -mod sealed { - use super::*; +trait SealedInstance { + const PIO_NO: u8; + const PIO: &'static crate::pac::pio::Pio; + const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; - pub trait PioPin {} + #[inline] + fn wakers() -> &'static Wakers { + const NEW_AW: AtomicWaker = AtomicWaker::new(); + static WAKERS: Wakers = Wakers([NEW_AW; 12]); - pub trait Instance { - const PIO_NO: u8; - const PIO: &'static crate::pac::pio::Pio; - const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; - type Interrupt: crate::interrupt::typelevel::Interrupt; + &WAKERS + } - #[inline] - fn wakers() -> &'static Wakers { - const NEW_AW: AtomicWaker = AtomicWaker::new(); - static WAKERS: Wakers = Wakers([NEW_AW; 12]); + #[inline] + fn state() -> &'static State { + static STATE: State = State { + users: AtomicU8::new(0), + used_pins: AtomicU32::new(0), + }; - &WAKERS - } - - #[inline] - fn state() -> &'static State { - static STATE: State = State { - users: AtomicU8::new(0), - used_pins: AtomicU32::new(0), - }; - - &STATE - } + &STATE } } /// PIO instance. -pub trait Instance: sealed::Instance + Sized + Unpin {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance + Sized + Unpin { + /// Interrupt for this peripheral. + type Interrupt: crate::interrupt::typelevel::Interrupt; +} macro_rules! impl_pio { ($name:ident, $pio:expr, $pac:ident, $funcsel:ident, $irq:ident) => { - impl sealed::Instance for peripherals::$name { + impl SealedInstance for peripherals::$name { const PIO_NO: u8 = $pio; const PIO: &'static pac::pio::Pio = &pac::$pac; const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel; + } + impl Instance for peripherals::$name { type Interrupt = crate::interrupt::typelevel::$irq; } - impl Instance for peripherals::$name {} }; } @@ -1198,12 +1201,11 @@ impl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0); impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0); /// PIO pin. -pub trait PioPin: sealed::PioPin + gpio::Pin {} +pub trait PioPin: gpio::Pin {} macro_rules! impl_pio_pin { ($( $pin:ident, )*) => { $( - impl sealed::PioPin for peripherals::$pin {} impl PioPin for peripherals::$pin {} )* }; diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 5aab3ff4f..7613e4e58 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -6,8 +6,7 @@ use fixed::FixedU16; use pac::pwm::regs::{ChDiv, Intr}; use pac::pwm::vals::Divmode; -use crate::gpio::sealed::Pin as _; -use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin as _}; use crate::{pac, peripherals, RegExt}; /// The configuration of a PWM slice. @@ -300,12 +299,11 @@ impl<'d, T: Slice> Drop for Pwm<'d, T> { } } -mod sealed { - pub trait Slice {} -} +trait SealedSlice {} /// PWM Slice. -pub trait Slice: Peripheral

+ sealed::Slice + Sized + 'static { +#[allow(private_bounds)] +pub trait Slice: Peripheral

+ SealedSlice + Sized + 'static { /// Slice number. fn number(&self) -> u8; @@ -317,7 +315,7 @@ pub trait Slice: Peripheral

+ sealed::Slice + Sized + 'static { macro_rules! slice { ($name:ident, $num:expr) => { - impl sealed::Slice for peripherals::$name {} + impl SealedSlice for peripherals::$name {} impl Slice for peripherals::$name { fn number(&self) -> u8 { $num diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index c8691bdc2..2ce7ac645 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -188,16 +188,15 @@ pub enum RtcError { NotRunning, } -mod sealed { - pub trait Instance { - fn regs(&self) -> crate::pac::rtc::Rtc; - } +trait SealedInstance { + fn regs(&self) -> crate::pac::rtc::Rtc; } /// RTC peripheral instance. -pub trait Instance: sealed::Instance {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance {} -impl sealed::Instance for crate::peripherals::RTC { +impl SealedInstance for crate::peripherals::RTC { fn regs(&self) -> crate::pac::rtc::Rtc { crate::pac::RTC } diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index a2a22ffe5..ef4c644ae 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -7,8 +7,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Phase, Polarity}; use crate::dma::{AnyChannel, Channel}; -use crate::gpio::sealed::Pin as _; -use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin as _}; use crate::{pac, peripherals, Peripheral}; /// SPI errors. @@ -443,28 +442,26 @@ impl<'d, T: Instance> Spi<'d, T, Async> { } } -mod sealed { - use super::*; +trait SealedMode {} - pub trait Mode {} +trait SealedInstance { + const TX_DREQ: u8; + const RX_DREQ: u8; - pub trait Instance { - const TX_DREQ: u8; - const RX_DREQ: u8; - - fn regs(&self) -> pac::spi::Spi; - } + fn regs(&self) -> pac::spi::Spi; } /// Mode. -pub trait Mode: sealed::Mode {} +#[allow(private_bounds)] +pub trait Mode: SealedMode {} /// SPI instance trait. -pub trait Instance: sealed::Instance {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance {} macro_rules! impl_instance { ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { - impl sealed::Instance for peripherals::$type { + impl SealedInstance for peripherals::$type { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; @@ -527,7 +524,7 @@ impl_pin!(PIN_29, SPI1, CsPin); macro_rules! impl_mode { ($name:ident) => { - impl sealed::Mode for $name {} + impl SealedMode for $name {} impl Mode for $name {} }; } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 65dcf4eb4..ee2dcb27d 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -12,8 +12,7 @@ use pac::uart::regs::Uartris; use crate::clocks::clk_peri_freq; use crate::dma::{AnyChannel, Channel}; -use crate::gpio::sealed::Pin; -use crate::gpio::AnyPin; +use crate::gpio::{AnyPin, SealedPin}; use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::pac::io::vals::{Inover, Outover}; use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; @@ -1107,35 +1106,26 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> } } -mod sealed { - use super::*; +trait SealedMode {} - pub trait Mode {} +trait SealedInstance { + const TX_DREQ: u8; + const RX_DREQ: u8; - pub trait Instance { - const TX_DREQ: u8; - const RX_DREQ: u8; + fn regs() -> pac::uart::Uart; - type Interrupt: interrupt::typelevel::Interrupt; + fn buffered_state() -> &'static buffered::State; - fn regs() -> pac::uart::Uart; - - fn buffered_state() -> &'static buffered::State; - - fn dma_state() -> &'static DmaState; - } - pub trait TxPin {} - pub trait RxPin {} - pub trait CtsPin {} - pub trait RtsPin {} + fn dma_state() -> &'static DmaState; } /// UART mode. -pub trait Mode: sealed::Mode {} +#[allow(private_bounds)] +pub trait Mode: SealedMode {} macro_rules! impl_mode { ($name:ident) => { - impl sealed::Mode for $name {} + impl SealedMode for $name {} impl Mode for $name {} }; } @@ -1149,16 +1139,18 @@ impl_mode!(Blocking); impl_mode!(Async); /// UART instance. -pub trait Instance: sealed::Instance {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance { + /// Interrupt for this instance. + type Interrupt: interrupt::typelevel::Interrupt; +} macro_rules! impl_instance { ($inst:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { - impl sealed::Instance for peripherals::$inst { + impl SealedInstance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; - type Interrupt = crate::interrupt::typelevel::$irq; - fn regs() -> pac::uart::Uart { pac::$inst } @@ -1176,7 +1168,9 @@ macro_rules! impl_instance { &STATE } } - impl Instance for peripherals::$inst {} + impl Instance for peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + } }; } @@ -1184,17 +1178,16 @@ impl_instance!(UART0, UART0_IRQ, 20, 21); impl_instance!(UART1, UART1_IRQ, 22, 23); /// Trait for TX pins. -pub trait TxPin: sealed::TxPin + crate::gpio::Pin {} +pub trait TxPin: crate::gpio::Pin {} /// Trait for RX pins. -pub trait RxPin: sealed::RxPin + crate::gpio::Pin {} +pub trait RxPin: crate::gpio::Pin {} /// Trait for Clear To Send (CTS) pins. -pub trait CtsPin: sealed::CtsPin + crate::gpio::Pin {} +pub trait CtsPin: crate::gpio::Pin {} /// Trait for Request To Send (RTS) pins. -pub trait RtsPin: sealed::RtsPin + crate::gpio::Pin {} +pub trait RtsPin: crate::gpio::Pin {} macro_rules! impl_pin { ($pin:ident, $instance:ident, $function:ident) => { - impl sealed::$function for peripherals::$pin {} impl $function for peripherals::$pin {} }; } diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index d68dee4a3..37d37d6d9 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -14,20 +14,19 @@ use embassy_usb_driver::{ use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; -pub(crate) mod sealed { - pub trait Instance { - fn regs() -> crate::pac::usb::Usb; - fn dpram() -> crate::pac::usb_dpram::UsbDpram; - } +trait SealedInstance { + fn regs() -> crate::pac::usb::Usb; + fn dpram() -> crate::pac::usb_dpram::UsbDpram; } /// USB peripheral instance. -pub trait Instance: sealed::Instance + 'static { +#[allow(private_bounds)] +pub trait Instance: SealedInstance + 'static { /// Interrupt for this peripheral. type Interrupt: interrupt::typelevel::Interrupt; } -impl crate::usb::sealed::Instance for peripherals::USB { +impl crate::usb::SealedInstance for peripherals::USB { fn regs() -> pac::usb::Usb { pac::USBCTRL_REGS } diff --git a/examples/rp/src/bin/pio_stepper.rs b/examples/rp/src/bin/pio_stepper.rs index ab9ecf623..4952f4fbd 100644 --- a/examples/rp/src/bin/pio_stepper.rs +++ b/examples/rp/src/bin/pio_stepper.rs @@ -69,7 +69,7 @@ impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> { let clock_divider: FixedU32 = (125_000_000 / (freq * 136)).to_fixed(); assert!(clock_divider <= 65536, "clkdiv must be <= 65536"); assert!(clock_divider >= 1, "clkdiv must be >= 1"); - T::PIO.sm(SM).clkdiv().write(|w| w.0 = clock_divider.to_bits() << 8); + self.sm.set_clock_divider(clock_divider); self.sm.clkdiv_restart(); } From d597815c9aed3c2d6b8729db1b78fd0e843181f6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 5 Apr 2024 00:48:19 +0200 Subject: [PATCH 55/69] stm32: remove last few mod sealed's. --- embassy-stm32/src/cordic/mod.rs | 118 +++++++++++++++++++++++++++- embassy-stm32/src/cordic/sealed.rs | 116 --------------------------- embassy-stm32/src/usart/buffered.rs | 36 ++++----- 3 files changed, 131 insertions(+), 139 deletions(-) delete mode 100644 embassy-stm32/src/cordic/sealed.rs diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index 6bbc48f2b..9ac10e714 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs @@ -3,6 +3,7 @@ use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; +use crate::pac::cordic::vals; use crate::{dma, peripherals}; mod enums; @@ -11,9 +12,6 @@ pub use enums::*; mod errors; pub use errors::*; -mod sealed; -use self::sealed::SealedInstance; - pub mod utils; /// CORDIC driver @@ -22,6 +20,120 @@ pub struct Cordic<'d, T: Instance> { config: Config, } +/// Cordic instance +trait SealedInstance { + /// Get access to CORDIC registers + fn regs() -> crate::pac::cordic::Cordic; + + /// Set Function value + fn set_func(&self, func: Function) { + Self::regs() + .csr() + .modify(|v| v.set_func(vals::Func::from_bits(func as u8))); + } + + /// Set Precision value + fn set_precision(&self, precision: Precision) { + Self::regs() + .csr() + .modify(|v| v.set_precision(vals::Precision::from_bits(precision as u8))) + } + + /// Set Scale value + fn set_scale(&self, scale: Scale) { + Self::regs() + .csr() + .modify(|v| v.set_scale(vals::Scale::from_bits(scale as u8))) + } + + /// Enable global interrupt + fn enable_irq(&self) { + Self::regs().csr().modify(|v| v.set_ien(true)) + } + + /// Disable global interrupt + fn disable_irq(&self) { + Self::regs().csr().modify(|v| v.set_ien(false)) + } + + /// Enable Read DMA + fn enable_read_dma(&self) { + Self::regs().csr().modify(|v| { + v.set_dmaren(true); + }) + } + + /// Disable Read DMA + fn disable_read_dma(&self) { + Self::regs().csr().modify(|v| { + v.set_dmaren(false); + }) + } + + /// Enable Write DMA + fn enable_write_dma(&self) { + Self::regs().csr().modify(|v| { + v.set_dmawen(true); + }) + } + + /// Disable Write DMA + fn disable_write_dma(&self) { + Self::regs().csr().modify(|v| { + v.set_dmawen(false); + }) + } + + /// Set NARGS value + fn set_argument_count(&self, n: AccessCount) { + Self::regs().csr().modify(|v| { + v.set_nargs(match n { + AccessCount::One => vals::Num::NUM1, + AccessCount::Two => vals::Num::NUM2, + }) + }) + } + + /// Set NRES value + fn set_result_count(&self, n: AccessCount) { + Self::regs().csr().modify(|v| { + v.set_nres(match n { + AccessCount::One => vals::Num::NUM1, + AccessCount::Two => vals::Num::NUM2, + }); + }) + } + + /// Set ARGSIZE and RESSIZE value + fn set_data_width(&self, arg: Width, res: Width) { + Self::regs().csr().modify(|v| { + v.set_argsize(match arg { + Width::Bits32 => vals::Size::BITS32, + Width::Bits16 => vals::Size::BITS16, + }); + v.set_ressize(match res { + Width::Bits32 => vals::Size::BITS32, + Width::Bits16 => vals::Size::BITS16, + }) + }) + } + + /// Read RRDY flag + fn ready_to_read(&self) -> bool { + Self::regs().csr().read().rrdy() + } + + /// Write value to WDATA + fn write_argument(&self, arg: u32) { + Self::regs().wdata().write_value(arg) + } + + /// Read value from RDATA + fn read_result(&self) -> u32 { + Self::regs().rdata().read() + } +} + /// CORDIC instance trait #[allow(private_bounds)] pub trait Instance: SealedInstance + Peripheral

+ crate::rcc::RccPeripheral {} diff --git a/embassy-stm32/src/cordic/sealed.rs b/embassy-stm32/src/cordic/sealed.rs deleted file mode 100644 index 8f0bd1830..000000000 --- a/embassy-stm32/src/cordic/sealed.rs +++ /dev/null @@ -1,116 +0,0 @@ -use super::*; -use crate::pac::cordic::vals; - -/// Cordic instance -pub(super) trait SealedInstance { - /// Get access to CORDIC registers - fn regs() -> crate::pac::cordic::Cordic; - - /// Set Function value - fn set_func(&self, func: Function) { - Self::regs() - .csr() - .modify(|v| v.set_func(vals::Func::from_bits(func as u8))); - } - - /// Set Precision value - fn set_precision(&self, precision: Precision) { - Self::regs() - .csr() - .modify(|v| v.set_precision(vals::Precision::from_bits(precision as u8))) - } - - /// Set Scale value - fn set_scale(&self, scale: Scale) { - Self::regs() - .csr() - .modify(|v| v.set_scale(vals::Scale::from_bits(scale as u8))) - } - - /// Enable global interrupt - fn enable_irq(&self) { - Self::regs().csr().modify(|v| v.set_ien(true)) - } - - /// Disable global interrupt - fn disable_irq(&self) { - Self::regs().csr().modify(|v| v.set_ien(false)) - } - - /// Enable Read DMA - fn enable_read_dma(&self) { - Self::regs().csr().modify(|v| { - v.set_dmaren(true); - }) - } - - /// Disable Read DMA - fn disable_read_dma(&self) { - Self::regs().csr().modify(|v| { - v.set_dmaren(false); - }) - } - - /// Enable Write DMA - fn enable_write_dma(&self) { - Self::regs().csr().modify(|v| { - v.set_dmawen(true); - }) - } - - /// Disable Write DMA - fn disable_write_dma(&self) { - Self::regs().csr().modify(|v| { - v.set_dmawen(false); - }) - } - - /// Set NARGS value - fn set_argument_count(&self, n: AccessCount) { - Self::regs().csr().modify(|v| { - v.set_nargs(match n { - AccessCount::One => vals::Num::NUM1, - AccessCount::Two => vals::Num::NUM2, - }) - }) - } - - /// Set NRES value - fn set_result_count(&self, n: AccessCount) { - Self::regs().csr().modify(|v| { - v.set_nres(match n { - AccessCount::One => vals::Num::NUM1, - AccessCount::Two => vals::Num::NUM2, - }); - }) - } - - /// Set ARGSIZE and RESSIZE value - fn set_data_width(&self, arg: Width, res: Width) { - Self::regs().csr().modify(|v| { - v.set_argsize(match arg { - Width::Bits32 => vals::Size::BITS32, - Width::Bits16 => vals::Size::BITS16, - }); - v.set_ressize(match res { - Width::Bits32 => vals::Size::BITS32, - Width::Bits16 => vals::Size::BITS16, - }) - }) - } - - /// Read RRDY flag - fn ready_to_read(&self) -> bool { - Self::regs().csr().read().rrdy() - } - - /// Write value to WDATA - fn write_argument(&self, arg: u32) { - Self::regs().wdata().write_value(arg) - } - - /// Read value from RDATA - fn read_result(&self) -> u32 { - Self::regs().rdata().read() - } -} diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 51862e185..949ac1b13 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -105,27 +105,23 @@ impl interrupt::typelevel::Handler for Interrupt } } -pub(crate) use sealed::State; -pub(crate) mod sealed { - use super::*; - pub struct State { - pub(crate) rx_waker: AtomicWaker, - pub(crate) rx_buf: RingBuffer, - pub(crate) tx_waker: AtomicWaker, - pub(crate) tx_buf: RingBuffer, - pub(crate) tx_done: AtomicBool, - } +pub(crate) struct State { + pub(crate) rx_waker: AtomicWaker, + pub(crate) rx_buf: RingBuffer, + pub(crate) tx_waker: AtomicWaker, + pub(crate) tx_buf: RingBuffer, + pub(crate) tx_done: AtomicBool, +} - impl State { - /// Create new state - pub const fn new() -> Self { - Self { - rx_buf: RingBuffer::new(), - tx_buf: RingBuffer::new(), - rx_waker: AtomicWaker::new(), - tx_waker: AtomicWaker::new(), - tx_done: AtomicBool::new(true), - } +impl State { + /// Create new state + pub(crate) const fn new() -> Self { + Self { + rx_buf: RingBuffer::new(), + tx_buf: RingBuffer::new(), + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), + tx_done: AtomicBool::new(true), } } } From 348a46b110b63e7ba1ca8ca558331c00963d0800 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Mon, 25 Mar 2024 19:25:38 +0800 Subject: [PATCH 56/69] move `enable_outputs` to private trait ... ... to avoid API leaking. --- embassy-stm32/src/timer/mod.rs | 43 +++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 2ba6b3f11..5c03aa1cb 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -69,17 +69,26 @@ pub trait GeneralInstance1Channel: CoreInstance {} /// General-purpose 16-bit timer with 2 channels instance. pub trait GeneralInstance2Channel: GeneralInstance1Channel {} -/// General-purpose 16-bit timer with 4 channels instance. -pub trait GeneralInstance4Channel: BasicInstance + GeneralInstance2Channel { +// This trait add *extra* methods to GeneralInstance4Channel, +// that GeneralInstance4Channel doesn't use, but the "AdvancedInstance"s need. +// And it's a private trait, so it's content won't leak to outer namespace. +// +// If you want to add a new method to it, please leave a detail comment to explain it. +trait General4ChBlankSealed { // SimplePwm<'d, T> is implemented for T: GeneralInstance4Channel // Advanced timers implement this trait, but the output needs to be // enabled explicitly. // To support general-purpose and advanced timers, this function is added // here defaulting to noop and overwritten for advanced timers. - /// Enable timer outputs. + // + // Enable timer outputs. fn enable_outputs(&self) {} } +/// General-purpose 16-bit timer with 4 channels instance. +#[allow(private_bounds)] +pub trait GeneralInstance4Channel: BasicInstance + GeneralInstance2Channel + General4ChBlankSealed {} + /// General-purpose 32-bit timer with 4 channels instance. pub trait GeneralInstance32bit4Channel: GeneralInstance4Channel {} @@ -138,6 +147,21 @@ macro_rules! impl_core_timer { }; } +// This macro only apply to "AdvancedInstance(s)", +// not "GeneralInstance4Channel" itself. +#[allow(unused)] +macro_rules! impl_general_4ch_blank_sealed { + ($inst:ident) => { + impl General4ChBlankSealed for crate::peripherals::$inst { + fn enable_outputs(&self) { + unsafe { crate::pac::timer::Tim1chCmp::from_ptr(Self::regs()) } + .bdtr() + .modify(|w| w.set_moe(true)); + } + } + }; +} + foreach_interrupt! { ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { impl_core_timer!($inst, TimerBits::Bits16); @@ -152,6 +176,7 @@ foreach_interrupt! { impl GeneralInstance1Channel for crate::peripherals::$inst {} impl GeneralInstance2Channel for crate::peripherals::$inst {} impl GeneralInstance4Channel for crate::peripherals::$inst {} + impl General4ChBlankSealed for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => { @@ -161,6 +186,7 @@ foreach_interrupt! { impl GeneralInstance1Channel for crate::peripherals::$inst {} impl GeneralInstance2Channel for crate::peripherals::$inst {} impl GeneralInstance4Channel for crate::peripherals::$inst {} + impl General4ChBlankSealed for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { @@ -170,6 +196,7 @@ foreach_interrupt! { impl GeneralInstance1Channel for crate::peripherals::$inst {} impl GeneralInstance2Channel for crate::peripherals::$inst {} impl GeneralInstance4Channel for crate::peripherals::$inst {} + impl General4ChBlankSealed for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { @@ -180,6 +207,7 @@ foreach_interrupt! { impl GeneralInstance2Channel for crate::peripherals::$inst {} impl GeneralInstance4Channel for crate::peripherals::$inst {} impl GeneralInstance32bit4Channel for crate::peripherals::$inst {} + impl General4ChBlankSealed for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => { @@ -188,7 +216,8 @@ foreach_interrupt! { impl BasicInstance for crate::peripherals::$inst {} impl GeneralInstance1Channel for crate::peripherals::$inst {} impl GeneralInstance2Channel for crate::peripherals::$inst {} - impl GeneralInstance4Channel for crate::peripherals::$inst { fn enable_outputs(&self) { set_moe::() }} + impl GeneralInstance4Channel for crate::peripherals::$inst {} + impl_general_4ch_blank_sealed!($inst); impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; } impl AdvancedInstance2Channel for crate::peripherals::$inst {} impl AdvancedInstance4Channel for crate::peripherals::$inst {} @@ -200,7 +229,8 @@ foreach_interrupt! { impl BasicInstance for crate::peripherals::$inst {} impl GeneralInstance1Channel for crate::peripherals::$inst {} impl GeneralInstance2Channel for crate::peripherals::$inst {} - impl GeneralInstance4Channel for crate::peripherals::$inst { fn enable_outputs(&self) { set_moe::() }} + impl GeneralInstance4Channel for crate::peripherals::$inst {} + impl_general_4ch_blank_sealed!($inst); impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; } impl AdvancedInstance2Channel for crate::peripherals::$inst {} impl AdvancedInstance4Channel for crate::peripherals::$inst {} @@ -212,7 +242,8 @@ foreach_interrupt! { impl BasicInstance for crate::peripherals::$inst {} impl GeneralInstance1Channel for crate::peripherals::$inst {} impl GeneralInstance2Channel for crate::peripherals::$inst {} - impl GeneralInstance4Channel for crate::peripherals::$inst { fn enable_outputs(&self) { set_moe::() }} + impl GeneralInstance4Channel for crate::peripherals::$inst {} + impl_general_4ch_blank_sealed!($inst); impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; } impl AdvancedInstance2Channel for crate::peripherals::$inst {} impl AdvancedInstance4Channel for crate::peripherals::$inst {} From 78b9cb98d4afc9c2be67176b174255ba78170525 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Mon, 25 Mar 2024 19:29:59 +0800 Subject: [PATCH 57/69] "separate CC interrupt" is for AdvCh4 only --- embassy-stm32/src/time_driver.rs | 8 +++---- embassy-stm32/src/timer/mod.rs | 39 ++++++++++++++++---------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index cc8161276..a3fe9b275 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -15,8 +15,6 @@ use crate::pac::timer::vals; use crate::rcc::SealedRccPeripheral; #[cfg(feature = "low-power")] use crate::rtc::Rtc; -#[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))] -use crate::timer::AdvancedInstance1Channel; use crate::timer::CoreInstance; use crate::{interrupt, peripherals}; @@ -263,6 +261,7 @@ pub(crate) struct RtcDriver { rtc: Mutex>>, } +#[allow(clippy::declare_interior_mutable_const)] const ALARM_STATE_NEW: AlarmState = AlarmState::new(); embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { @@ -312,9 +311,10 @@ impl RtcDriver { #[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))] { - ::CaptureCompareInterrupt::unpend(); + use crate::timer::AdvancedInstance4Channel; + ::CaptureCompareInterrupt::unpend(); unsafe { - ::CaptureCompareInterrupt::enable(); + ::CaptureCompareInterrupt::enable(); } } diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 5c03aa1cb..b3d4fa284 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -93,16 +93,16 @@ pub trait GeneralInstance4Channel: BasicInstance + GeneralInstance2Channel + Gen pub trait GeneralInstance32bit4Channel: GeneralInstance4Channel {} /// Advanced 16-bit timer with 1 channel instance. -pub trait AdvancedInstance1Channel: BasicNoCr2Instance + GeneralInstance1Channel { - /// Capture compare interrupt for this timer. - type CaptureCompareInterrupt: interrupt::typelevel::Interrupt; -} +pub trait AdvancedInstance1Channel: BasicNoCr2Instance + GeneralInstance1Channel {} /// Advanced 16-bit timer with 2 channels instance. pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + AdvancedInstance1Channel {} /// Advanced 16-bit timer with 4 channels instance. -pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {} +pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel { + /// Capture compare interrupt for this timer. + type CaptureCompareInterrupt: interrupt::typelevel::Interrupt; +} pin_trait!(Channel1Pin, GeneralInstance4Channel); pin_trait!(Channel2Pin, GeneralInstance4Channel); @@ -162,6 +162,15 @@ macro_rules! impl_general_4ch_blank_sealed { }; } +#[allow(unused)] +macro_rules! impl_adv_4ch { + ($inst:ident) => { + impl AdvancedInstance4Channel for crate::peripherals::$inst { + type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; + } + }; +} + foreach_interrupt! { ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { impl_core_timer!($inst, TimerBits::Bits16); @@ -218,9 +227,9 @@ foreach_interrupt! { impl GeneralInstance2Channel for crate::peripherals::$inst {} impl GeneralInstance4Channel for crate::peripherals::$inst {} impl_general_4ch_blank_sealed!($inst); - impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; } + impl AdvancedInstance1Channel for crate::peripherals::$inst {} impl AdvancedInstance2Channel for crate::peripherals::$inst {} - impl AdvancedInstance4Channel for crate::peripherals::$inst {} + impl_adv_4ch!($inst); }; ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => { @@ -231,9 +240,9 @@ foreach_interrupt! { impl GeneralInstance2Channel for crate::peripherals::$inst {} impl GeneralInstance4Channel for crate::peripherals::$inst {} impl_general_4ch_blank_sealed!($inst); - impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; } + impl AdvancedInstance1Channel for crate::peripherals::$inst {} impl AdvancedInstance2Channel for crate::peripherals::$inst {} - impl AdvancedInstance4Channel for crate::peripherals::$inst {} + impl_adv_4ch!($inst); }; ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { @@ -244,16 +253,8 @@ foreach_interrupt! { impl GeneralInstance2Channel for crate::peripherals::$inst {} impl GeneralInstance4Channel for crate::peripherals::$inst {} impl_general_4ch_blank_sealed!($inst); - impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; } + impl AdvancedInstance1Channel for crate::peripherals::$inst {} impl AdvancedInstance2Channel for crate::peripherals::$inst {} - impl AdvancedInstance4Channel for crate::peripherals::$inst {} + impl_adv_4ch!($inst); }; } - -#[cfg(not(stm32l0))] -#[allow(unused)] -fn set_moe() { - unsafe { crate::pac::timer::Tim1chCmp::from_ptr(T::regs()) } - .bdtr() - .modify(|w| w.set_moe(true)); -} From 5dc3738bc2b4e1d8f9f77588ecab7956493b59de Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Tue, 26 Mar 2024 01:05:38 +0800 Subject: [PATCH 58/69] add missing interrupt for timer --- embassy-stm32/src/time_driver.rs | 10 +-- embassy-stm32/src/timer/mod.rs | 105 +++++++++++++++++++------------ 2 files changed, 71 insertions(+), 44 deletions(-) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index a3fe9b275..a2e75020c 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -306,15 +306,15 @@ impl RtcDriver { w.set_ccie(0, true); }); - ::Interrupt::unpend(); - unsafe { ::Interrupt::enable() }; + ::UpdateInterrupt::unpend(); + unsafe { ::UpdateInterrupt::enable() }; #[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))] { - use crate::timer::AdvancedInstance4Channel; - ::CaptureCompareInterrupt::unpend(); + use crate::timer::GeneralInstance1Channel; + ::CaptureCompareInterrupt::unpend(); unsafe { - ::CaptureCompareInterrupt::enable(); + ::CaptureCompareInterrupt::enable(); } } diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index b3d4fa284..346127005 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -47,8 +47,8 @@ pub enum TimerBits { /// Core timer instance. pub trait CoreInstance: RccPeripheral + 'static { - /// Interrupt for this timer. - type Interrupt: interrupt::typelevel::Interrupt; + /// Update Interrupt for this timer. + type UpdateInterrupt: interrupt::typelevel::Interrupt; /// Amount of bits this timer has. const BITS: TimerBits; @@ -64,10 +64,16 @@ pub trait BasicNoCr2Instance: CoreInstance {} pub trait BasicInstance: BasicNoCr2Instance {} /// General-purpose 16-bit timer with 1 channel instance. -pub trait GeneralInstance1Channel: CoreInstance {} +pub trait GeneralInstance1Channel: CoreInstance { + /// Capture compare interrupt for this timer. + type CaptureCompareInterrupt: interrupt::typelevel::Interrupt; +} /// General-purpose 16-bit timer with 2 channels instance. -pub trait GeneralInstance2Channel: GeneralInstance1Channel {} +pub trait GeneralInstance2Channel: GeneralInstance1Channel { + /// Trigger event interrupt for this timer. + type TriggerInterrupt: interrupt::typelevel::Interrupt; +} // This trait add *extra* methods to GeneralInstance4Channel, // that GeneralInstance4Channel doesn't use, but the "AdvancedInstance"s need. @@ -93,16 +99,18 @@ pub trait GeneralInstance4Channel: BasicInstance + GeneralInstance2Channel + Gen pub trait GeneralInstance32bit4Channel: GeneralInstance4Channel {} /// Advanced 16-bit timer with 1 channel instance. -pub trait AdvancedInstance1Channel: BasicNoCr2Instance + GeneralInstance1Channel {} +pub trait AdvancedInstance1Channel: BasicNoCr2Instance + GeneralInstance1Channel { + /// Communication interrupt for this timer. + type CommunicationInterrupt: interrupt::typelevel::Interrupt; + /// Break input interrupt for this timer. + type BreakInputInterrupt: interrupt::typelevel::Interrupt; +} /// Advanced 16-bit timer with 2 channels instance. pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + AdvancedInstance1Channel {} /// Advanced 16-bit timer with 4 channels instance. -pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel { - /// Capture compare interrupt for this timer. - type CaptureCompareInterrupt: interrupt::typelevel::Interrupt; -} +pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {} pin_trait!(Channel1Pin, GeneralInstance4Channel); pin_trait!(Channel2Pin, GeneralInstance4Channel); @@ -136,7 +144,7 @@ dma_trait!(Ch4Dma, GeneralInstance4Channel); macro_rules! impl_core_timer { ($inst:ident, $bits:expr) => { impl CoreInstance for crate::peripherals::$inst { - type Interrupt = crate::_generated::peripheral_interrupts::$inst::UP; + type UpdateInterrupt = crate::_generated::peripheral_interrupts::$inst::UP; const BITS: TimerBits = $bits; @@ -147,6 +155,34 @@ macro_rules! impl_core_timer { }; } +#[allow(unused)] +macro_rules! impl_general_1ch { + ($inst:ident) => { + impl GeneralInstance1Channel for crate::peripherals::$inst { + type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; + } + }; +} + +#[allow(unused)] +macro_rules! impl_general_2ch { + ($inst:ident) => { + impl GeneralInstance2Channel for crate::peripherals::$inst { + type TriggerInterrupt = crate::_generated::peripheral_interrupts::$inst::TRG; + } + }; +} + +#[allow(unused)] +macro_rules! impl_advanced_1ch { + ($inst:ident) => { + impl AdvancedInstance1Channel for crate::peripherals::$inst { + type CommunicationInterrupt = crate::_generated::peripheral_interrupts::$inst::COM; + type BreakInputInterrupt = crate::_generated::peripheral_interrupts::$inst::BRK; + } + }; +} + // This macro only apply to "AdvancedInstance(s)", // not "GeneralInstance4Channel" itself. #[allow(unused)] @@ -162,15 +198,6 @@ macro_rules! impl_general_4ch_blank_sealed { }; } -#[allow(unused)] -macro_rules! impl_adv_4ch { - ($inst:ident) => { - impl AdvancedInstance4Channel for crate::peripherals::$inst { - type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; - } - }; -} - foreach_interrupt! { ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { impl_core_timer!($inst, TimerBits::Bits16); @@ -182,8 +209,8 @@ foreach_interrupt! { impl_core_timer!($inst, TimerBits::Bits16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl GeneralInstance1Channel for crate::peripherals::$inst {} - impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl_general_1ch!($inst); + impl_general_2ch!($inst); impl GeneralInstance4Channel for crate::peripherals::$inst {} impl General4ChBlankSealed for crate::peripherals::$inst {} }; @@ -192,8 +219,8 @@ foreach_interrupt! { impl_core_timer!($inst, TimerBits::Bits16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl GeneralInstance1Channel for crate::peripherals::$inst {} - impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl_general_1ch!($inst); + impl_general_2ch!($inst); impl GeneralInstance4Channel for crate::peripherals::$inst {} impl General4ChBlankSealed for crate::peripherals::$inst {} }; @@ -202,8 +229,8 @@ foreach_interrupt! { impl_core_timer!($inst, TimerBits::Bits16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl GeneralInstance1Channel for crate::peripherals::$inst {} - impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl_general_1ch!($inst); + impl_general_2ch!($inst); impl GeneralInstance4Channel for crate::peripherals::$inst {} impl General4ChBlankSealed for crate::peripherals::$inst {} }; @@ -212,8 +239,8 @@ foreach_interrupt! { impl_core_timer!($inst, TimerBits::Bits32); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl GeneralInstance1Channel for crate::peripherals::$inst {} - impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl_general_1ch!($inst); + impl_general_2ch!($inst); impl GeneralInstance4Channel for crate::peripherals::$inst {} impl GeneralInstance32bit4Channel for crate::peripherals::$inst {} impl General4ChBlankSealed for crate::peripherals::$inst {} @@ -223,38 +250,38 @@ foreach_interrupt! { impl_core_timer!($inst, TimerBits::Bits16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl GeneralInstance1Channel for crate::peripherals::$inst {} - impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl_general_1ch!($inst); + impl_general_2ch!($inst); impl GeneralInstance4Channel for crate::peripherals::$inst {} impl_general_4ch_blank_sealed!($inst); - impl AdvancedInstance1Channel for crate::peripherals::$inst {} + impl_advanced_1ch!($inst); impl AdvancedInstance2Channel for crate::peripherals::$inst {} - impl_adv_4ch!($inst); + impl AdvancedInstance4Channel for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => { impl_core_timer!($inst, TimerBits::Bits16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl GeneralInstance1Channel for crate::peripherals::$inst {} - impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl_general_1ch!($inst); + impl_general_2ch!($inst); impl GeneralInstance4Channel for crate::peripherals::$inst {} impl_general_4ch_blank_sealed!($inst); - impl AdvancedInstance1Channel for crate::peripherals::$inst {} + impl_advanced_1ch!($inst); impl AdvancedInstance2Channel for crate::peripherals::$inst {} - impl_adv_4ch!($inst); + impl AdvancedInstance4Channel for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { impl_core_timer!($inst, TimerBits::Bits16); impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl GeneralInstance1Channel for crate::peripherals::$inst {} - impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl_general_1ch!($inst); + impl_general_2ch!($inst); impl GeneralInstance4Channel for crate::peripherals::$inst {} impl_general_4ch_blank_sealed!($inst); - impl AdvancedInstance1Channel for crate::peripherals::$inst {} + impl_advanced_1ch!($inst); impl AdvancedInstance2Channel for crate::peripherals::$inst {} - impl_adv_4ch!($inst); + impl AdvancedInstance4Channel for crate::peripherals::$inst {} }; } From 3d7d3e028601ca05e58d82b8efc72b40855def86 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 5 Apr 2024 00:56:21 +0200 Subject: [PATCH 59/69] stm32/time-driver: always use CC interrupt. This avoids cfg's, because it works both for timers that have a a dedicated CC interrupt line, and timers where all interrupts go to a single interrupt line. --- embassy-stm32/src/time_driver.rs | 43 +++++++++++++------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index a2e75020c..e592fbf7d 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -15,7 +15,7 @@ use crate::pac::timer::vals; use crate::rcc::SealedRccPeripheral; #[cfg(feature = "low-power")] use crate::rtc::Rtc; -use crate::timer::CoreInstance; +use crate::timer::{CoreInstance, GeneralInstance1Channel}; use crate::{interrupt, peripherals}; // NOTE regarding ALARM_COUNT: @@ -67,7 +67,7 @@ type T = peripherals::TIM23; type T = peripherals::TIM24; foreach_interrupt! { - (TIM1, timer, $block:ident, UP, $irq:ident) => { + (TIM1, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim1)] #[cfg(feature = "rt")] #[interrupt] @@ -83,7 +83,7 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM2, timer, $block:ident, UP, $irq:ident) => { + (TIM2, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim2)] #[cfg(feature = "rt")] #[interrupt] @@ -91,7 +91,7 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM3, timer, $block:ident, UP, $irq:ident) => { + (TIM3, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim3)] #[cfg(feature = "rt")] #[interrupt] @@ -99,7 +99,7 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM4, timer, $block:ident, UP, $irq:ident) => { + (TIM4, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim4)] #[cfg(feature = "rt")] #[interrupt] @@ -107,7 +107,7 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM5, timer, $block:ident, UP, $irq:ident) => { + (TIM5, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim5)] #[cfg(feature = "rt")] #[interrupt] @@ -115,7 +115,7 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM8, timer, $block:ident, UP, $irq:ident) => { + (TIM8, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim8)] #[cfg(feature = "rt")] #[interrupt] @@ -131,7 +131,7 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM9, timer, $block:ident, UP, $irq:ident) => { + (TIM9, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim9)] #[cfg(feature = "rt")] #[interrupt] @@ -139,7 +139,7 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM12, timer, $block:ident, UP, $irq:ident) => { + (TIM12, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim12)] #[cfg(feature = "rt")] #[interrupt] @@ -147,7 +147,7 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM15, timer, $block:ident, UP, $irq:ident) => { + (TIM15, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim15)] #[cfg(feature = "rt")] #[interrupt] @@ -155,7 +155,7 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM20, timer, $block:ident, UP, $irq:ident) => { + (TIM20, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim20)] #[cfg(feature = "rt")] #[interrupt] @@ -171,7 +171,7 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM21, timer, $block:ident, UP, $irq:ident) => { + (TIM21, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim21)] #[cfg(feature = "rt")] #[interrupt] @@ -179,7 +179,7 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM22, timer, $block:ident, UP, $irq:ident) => { + (TIM22, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim22)] #[cfg(feature = "rt")] #[interrupt] @@ -187,7 +187,7 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM23, timer, $block:ident, UP, $irq:ident) => { + (TIM23, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim23)] #[cfg(feature = "rt")] #[interrupt] @@ -195,7 +195,7 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM24, timer, $block:ident, UP, $irq:ident) => { + (TIM24, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim24)] #[cfg(feature = "rt")] #[interrupt] @@ -306,17 +306,8 @@ impl RtcDriver { w.set_ccie(0, true); }); - ::UpdateInterrupt::unpend(); - unsafe { ::UpdateInterrupt::enable() }; - - #[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))] - { - use crate::timer::GeneralInstance1Channel; - ::CaptureCompareInterrupt::unpend(); - unsafe { - ::CaptureCompareInterrupt::enable(); - } - } + ::CaptureCompareInterrupt::unpend(); + unsafe { ::CaptureCompareInterrupt::enable() }; r.cr1().modify(|w| w.set_cen(true)); } From 143b288333a96c3caf6a0f372edca13ebea10af1 Mon Sep 17 00:00:00 2001 From: pawel00100 Date: Wed, 24 Jan 2024 23:24:26 +0100 Subject: [PATCH 60/69] Add parameter for enabling pull-up and pull-down in RP PWM input mode --- embassy-rp/src/pwm.rs | 35 +++++++++++++++++++----- examples/rp/src/bin/pwm_input.rs | 3 ++- tests/rp/src/bin/pwm.rs | 46 +++++++++++++++++++++++++++++--- 3 files changed, 73 insertions(+), 11 deletions(-) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 7613e4e58..a1f400cfb 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -6,7 +6,7 @@ use fixed::FixedU16; use pac::pwm::regs::{ChDiv, Intr}; use pac::pwm::vals::Divmode; -use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin as _}; +use crate::gpio::{AnyPin, Pin as GpioPin, Pull, SealedPin as _}; use crate::{pac, peripherals, RegExt}; /// The configuration of a PWM slice. @@ -92,6 +92,7 @@ impl<'d, T: Slice> Pwm<'d, T> { inner: impl Peripheral

+ 'd, a: Option>, b: Option>, + b_pull: Pull, config: Config, divmode: Divmode, ) -> Self { @@ -110,6 +111,10 @@ impl<'d, T: Slice> Pwm<'d, T> { } if let Some(pin) = &b { pin.gpio().ctrl().write(|w| w.set_funcsel(4)); + pin.pad_ctrl().modify(|w| { + w.set_pue(b_pull == Pull::Up); + w.set_pde(b_pull == Pull::Down); + }); } Self { inner, @@ -121,7 +126,7 @@ impl<'d, T: Slice> Pwm<'d, T> { /// Create PWM driver without any configured pins. #[inline] pub fn new_free(inner: impl Peripheral

+ 'd, config: Config) -> Self { - Self::new_inner(inner, None, None, config, Divmode::DIV) + Self::new_inner(inner, None, None, Pull::None, config, Divmode::DIV) } /// Create PWM driver with a single 'a' as output. @@ -132,7 +137,7 @@ impl<'d, T: Slice> Pwm<'d, T> { config: Config, ) -> Self { into_ref!(a); - Self::new_inner(inner, Some(a.map_into()), None, config, Divmode::DIV) + Self::new_inner(inner, Some(a.map_into()), None, Pull::None, config, Divmode::DIV) } /// Create PWM driver with a single 'b' pin as output. @@ -143,7 +148,7 @@ impl<'d, T: Slice> Pwm<'d, T> { config: Config, ) -> Self { into_ref!(b); - Self::new_inner(inner, None, Some(b.map_into()), config, Divmode::DIV) + Self::new_inner(inner, None, Some(b.map_into()), Pull::None, config, Divmode::DIV) } /// Create PWM driver with a 'a' and 'b' pins as output. @@ -155,7 +160,14 @@ impl<'d, T: Slice> Pwm<'d, T> { config: Config, ) -> Self { into_ref!(a, b); - Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, Divmode::DIV) + Self::new_inner( + inner, + Some(a.map_into()), + Some(b.map_into()), + Pull::None, + config, + Divmode::DIV, + ) } /// Create PWM driver with a single 'b' as input pin. @@ -163,11 +175,12 @@ impl<'d, T: Slice> Pwm<'d, T> { pub fn new_input( inner: impl Peripheral

+ 'd, b: impl Peripheral

> + 'd, + b_pull: Pull, mode: InputMode, config: Config, ) -> Self { into_ref!(b); - Self::new_inner(inner, None, Some(b.map_into()), config, mode.into()) + Self::new_inner(inner, None, Some(b.map_into()), b_pull, config, mode.into()) } /// Create PWM driver with a 'a' and 'b' pins in the desired input mode. @@ -176,11 +189,19 @@ impl<'d, T: Slice> Pwm<'d, T> { inner: impl Peripheral

+ 'd, a: impl Peripheral

> + 'd, b: impl Peripheral

> + 'd, + b_pull: Pull, mode: InputMode, config: Config, ) -> Self { into_ref!(a, b); - Self::new_inner(inner, Some(a.map_into()), Some(b.map_into()), config, mode.into()) + Self::new_inner( + inner, + Some(a.map_into()), + Some(b.map_into()), + b_pull, + config, + mode.into(), + ) } /// Set the PWM config. diff --git a/examples/rp/src/bin/pwm_input.rs b/examples/rp/src/bin/pwm_input.rs index 0652dc42b..bf454a936 100644 --- a/examples/rp/src/bin/pwm_input.rs +++ b/examples/rp/src/bin/pwm_input.rs @@ -5,6 +5,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_rp::gpio::Pull; use embassy_rp::pwm::{Config, InputMode, Pwm}; use embassy_time::{Duration, Ticker}; use {defmt_rtt as _, panic_probe as _}; @@ -14,7 +15,7 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let cfg: Config = Default::default(); - let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, InputMode::RisingEdge, cfg); + let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, Pull::None, InputMode::RisingEdge, cfg); let mut ticker = Ticker::every(Duration::from_secs(1)); loop { diff --git a/tests/rp/src/bin/pwm.rs b/tests/rp/src/bin/pwm.rs index 4b02e5bab..c05197000 100644 --- a/tests/rp/src/bin/pwm.rs +++ b/tests/rp/src/bin/pwm.rs @@ -94,7 +94,7 @@ async fn main(_spawner: Spawner) { // Test level-gated { let mut pin2 = Output::new(&mut p11, Level::Low); - let pwm = Pwm::new_input(&mut p.PWM_SLICE3, &mut p7, InputMode::Level, cfg.clone()); + let pwm = Pwm::new_input(&mut p.PWM_SLICE3, &mut p7, Pull::None, InputMode::Level, cfg.clone()); assert_eq!(pwm.counter(), 0); Timer::after_millis(5).await; assert_eq!(pwm.counter(), 0); @@ -110,7 +110,13 @@ async fn main(_spawner: Spawner) { // Test rising-gated { let mut pin2 = Output::new(&mut p11, Level::Low); - let pwm = Pwm::new_input(&mut p.PWM_SLICE3, &mut p7, InputMode::RisingEdge, cfg.clone()); + let pwm = Pwm::new_input( + &mut p.PWM_SLICE3, + &mut p7, + Pull::None, + InputMode::RisingEdge, + cfg.clone(), + ); assert_eq!(pwm.counter(), 0); Timer::after_millis(5).await; assert_eq!(pwm.counter(), 0); @@ -125,7 +131,13 @@ async fn main(_spawner: Spawner) { // Test falling-gated { let mut pin2 = Output::new(&mut p11, Level::High); - let pwm = Pwm::new_input(&mut p.PWM_SLICE3, &mut p7, InputMode::FallingEdge, cfg.clone()); + let pwm = Pwm::new_input( + &mut p.PWM_SLICE3, + &mut p7, + Pull::None, + InputMode::FallingEdge, + cfg.clone(), + ); assert_eq!(pwm.counter(), 0); Timer::after_millis(5).await; assert_eq!(pwm.counter(), 0); @@ -137,6 +149,34 @@ async fn main(_spawner: Spawner) { assert_eq!(pwm.counter(), 1); } + // pull-down + { + let pin2 = Input::new(&mut p11, Pull::None); + Pwm::new_input( + &mut p.PWM_SLICE3, + &mut p7, + Pull::Down, + InputMode::FallingEdge, + cfg.clone(), + ); + Timer::after_millis(1).await; + assert!(pin2.is_low()); + } + + // pull-up + { + let pin2 = Input::new(&mut p11, Pull::None); + Pwm::new_input( + &mut p.PWM_SLICE3, + &mut p7, + Pull::Up, + InputMode::FallingEdge, + cfg.clone(), + ); + Timer::after_millis(1).await; + assert!(pin2.is_high()); + } + info!("Test OK"); cortex_m::asm::bkpt(); } From c2b8ddaa83d251395a974094b2afc4550204249f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 5 Apr 2024 01:41:47 +0200 Subject: [PATCH 61/69] stm32/qspi: autodeduce transfer len from buffer len. mirrors change made in #2672. --- embassy-stm32/src/qspi/mod.rs | 53 +++++++++++++++-------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 3c054e666..0a4b4f074 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -27,8 +27,6 @@ pub struct TransferConfig { pub address: Option, /// Number of dummy cycles (DCYC) pub dummy: DummyCycles, - /// Length of data - pub data_len: Option, } impl Default for TransferConfig { @@ -40,7 +38,6 @@ impl Default for TransferConfig { instruction: 0, address: None, dummy: DummyCycles::_0, - data_len: None, } } } @@ -231,7 +228,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { pub fn command(&mut self, transaction: TransferConfig) { #[cfg(not(stm32h7))] T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); + self.setup_transaction(QspiMode::IndirectWrite, &transaction, None); while !T::REGS.sr().read().tcf() {} T::REGS.fcr().modify(|v| v.set_ctcf(true)); @@ -241,21 +238,19 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) { #[cfg(not(stm32h7))] T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); + self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); - if let Some(len) = transaction.data_len { - let current_ar = T::REGS.ar().read().address(); - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectRead.into()); - }); - T::REGS.ar().write(|v| { - v.set_address(current_ar); - }); + let current_ar = T::REGS.ar().read().address(); + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectRead.into()); + }); + T::REGS.ar().write(|v| { + v.set_address(current_ar); + }); - for idx in 0..len { - while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} - buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; - } + for b in buf { + while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} + *b = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; } while !T::REGS.sr().read().tcf() {} @@ -268,17 +263,15 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { #[cfg(not(stm32h7))] T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); + self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); - if let Some(len) = transaction.data_len { - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectWrite.into()); - }); + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectWrite.into()); + }); - for idx in 0..len { - while !T::REGS.sr().read().ftf() {} - unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; - } + for &b in buf { + while !T::REGS.sr().read().ftf() {} + unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(b) }; } while !T::REGS.sr().read().tcf() {} @@ -290,7 +283,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { where Dma: QuadDma, { - self.setup_transaction(QspiMode::IndirectWrite, &transaction); + self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); T::REGS.ccr().modify(|v| { v.set_fmode(QspiMode::IndirectRead.into()); @@ -323,7 +316,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { where Dma: QuadDma, { - self.setup_transaction(QspiMode::IndirectWrite, &transaction); + self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); T::REGS.ccr().modify(|v| { v.set_fmode(QspiMode::IndirectWrite.into()); @@ -347,7 +340,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { transfer.blocking_wait(); } - fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) { + fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig, data_len: Option) { T::REGS.fcr().modify(|v| { v.set_csmf(true); v.set_ctcf(true); @@ -357,7 +350,7 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { while T::REGS.sr().read().busy() {} - if let Some(len) = transaction.data_len { + if let Some(len) = data_len { T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1)); } From c953b9045b40fd3ece546ef94714fa2f108e0c25 Mon Sep 17 00:00:00 2001 From: Torin Cooper-Bennun Date: Fri, 5 Apr 2024 13:00:33 +0100 Subject: [PATCH 62/69] stm32: adc: v3: [h5] set OR.OP0 to 1 when ADCx_INP0 is selected, per RM --- embassy-stm32/src/adc/v3.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 8c9b47197..e25630be2 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -222,6 +222,13 @@ impl<'d, T: Instance> Adc<'d, T> { // spin } + // RM0492, RM0481, etc. + // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." + #[cfg(adc_h5)] + if pin.channel() == 0 { + T::regs().or().modify(|reg| reg.set_op0(true)); + } + // Configure channel Self::set_channel_sample_time(pin.channel(), self.sample_time); @@ -244,6 +251,13 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cr().modify(|reg| reg.set_addis(true)); + // RM0492, RM0481, etc. + // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." + #[cfg(adc_h5)] + if pin.channel() == 0 { + T::regs().or().modify(|reg| reg.set_op0(false)); + } + val } From 2ad82c2adf123d3e555724f77149ce9ab9c2bd8c Mon Sep 17 00:00:00 2001 From: Dillon McEwan Date: Fri, 5 Apr 2024 10:07:15 -0700 Subject: [PATCH 63/69] Fix 'clocok' typo in RCC docs --- embassy-stm32/src/rcc/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index d53d02203..c328344aa 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -111,7 +111,7 @@ mod util { } } -/// Get the kernel clocok frequency of the peripheral `T`. +/// Get the kernel clock frequency of the peripheral `T`. /// /// # Panics /// From 99ea564f1c5cc3dcce908cb853f528a52cca8ae8 Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Fri, 5 Apr 2024 22:11:01 +0200 Subject: [PATCH 64/69] Add VBUS detection blackpill comment --- examples/stm32f4/src/bin/usb_hid_keyboard.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index a799b4e72..d6e0be5ea 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs @@ -49,6 +49,7 @@ async fn main(_spawner: Spawner) { // Create the driver, from the HAL. let mut ep_out_buffer = [0u8; 256]; let mut config = embassy_stm32::usb::Config::default(); + // If the board you’re using doesn’t have the VBUS pin wired up correctly for detecting the USB bus voltage (e.g. on the f4 blackpill board), set this to false config.vbus_detection = true; let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); From cf0097162cb33d77d90ce5cb50f8fb5c24972748 Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Sun, 7 Apr 2024 15:19:56 +0200 Subject: [PATCH 65/69] [embassy-stm32] added comments explaining multiprio interrupts --- examples/stm32f0/src/bin/multiprio.rs | 4 ++++ examples/stm32f3/src/bin/multiprio.rs | 4 ++++ examples/stm32f4/src/bin/multiprio.rs | 4 ++++ examples/stm32h7/src/bin/multiprio.rs | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/examples/stm32f0/src/bin/multiprio.rs b/examples/stm32f0/src/bin/multiprio.rs index e49951726..e36c0d6c2 100644 --- a/examples/stm32f0/src/bin/multiprio.rs +++ b/examples/stm32f0/src/bin/multiprio.rs @@ -126,6 +126,10 @@ fn main() -> ! { // Initialize and create handle for devicer peripherals let _p = embassy_stm32::init(Default::default()); + // STM32s don’t have software-defined interrupts, so just use any free interrupt vectors which aren’t used + // by the rest of your application. In this case we’re using UART1 and UART2, but there’s nothing special + // about them. Any otherwise unused interrupt vector would work exactly the same. + // High-priority executor: USART1, priority level 6 interrupt::USART1.set_priority(Priority::P6); let spawner = EXECUTOR_HIGH.start(interrupt::USART1); diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs index 328447210..3a3059db1 100644 --- a/examples/stm32f3/src/bin/multiprio.rs +++ b/examples/stm32f3/src/bin/multiprio.rs @@ -127,6 +127,10 @@ fn main() -> ! { let _p = embassy_stm32::init(Default::default()); + // STM32s don’t have software-defined interrupts, so just use any free interrupt vectors which aren’t used + // by the rest of your application. In this case we’re using UART4 and UART5, but there’s nothing special + // about them. Any otherwise unused interrupt vector would work exactly the same. + // High-priority executor: UART4, priority level 6 interrupt::UART4.set_priority(Priority::P6); let spawner = EXECUTOR_HIGH.start(interrupt::UART4); diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs index 328447210..5a55cd291 100644 --- a/examples/stm32f4/src/bin/multiprio.rs +++ b/examples/stm32f4/src/bin/multiprio.rs @@ -127,6 +127,10 @@ fn main() -> ! { let _p = embassy_stm32::init(Default::default()); + // STM32s don’t have software-defined interrupts, so just use any free interrupt vectors which aren’t used + // by the rest of your application. In this case we’re using UART6 and UART7, but there’s nothing special + // about them. Any otherwise unused interrupt vector would work exactly the same. + // High-priority executor: UART4, priority level 6 interrupt::UART4.set_priority(Priority::P6); let spawner = EXECUTOR_HIGH.start(interrupt::UART4); diff --git a/examples/stm32h7/src/bin/multiprio.rs b/examples/stm32h7/src/bin/multiprio.rs index 73f8dd092..f9a00f806 100644 --- a/examples/stm32h7/src/bin/multiprio.rs +++ b/examples/stm32h7/src/bin/multiprio.rs @@ -127,6 +127,10 @@ fn main() -> ! { let _p = embassy_stm32::init(Default::default()); + // STM32s don’t have software-defined interrupts, so just use any free interrupt vectors which aren’t used + // by the rest of your application. In this case we’re using UART6 and UART7, but there’s nothing special + // about them. Any otherwise unused interrupt vector would work exactly the same. + // High-priority executor: UART4, priority level 6 interrupt::UART4.set_priority(Priority::P6); let spawner = EXECUTOR_HIGH.start(interrupt::UART4); From 56d34eefaa2862ebaf883e5388f909f7989aba1f Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Sun, 7 Apr 2024 15:33:51 +0200 Subject: [PATCH 66/69] Apply suggestions from code review Co-authored-by: becothas <41289937+becothas@users.noreply.github.com> --- examples/stm32f4/src/bin/multiprio.rs | 2 +- examples/stm32h7/src/bin/multiprio.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs index 5a55cd291..3a3059db1 100644 --- a/examples/stm32f4/src/bin/multiprio.rs +++ b/examples/stm32f4/src/bin/multiprio.rs @@ -128,7 +128,7 @@ fn main() -> ! { let _p = embassy_stm32::init(Default::default()); // STM32s don’t have software-defined interrupts, so just use any free interrupt vectors which aren’t used - // by the rest of your application. In this case we’re using UART6 and UART7, but there’s nothing special + // by the rest of your application. In this case we’re using UART4 and UART5, but there’s nothing special // about them. Any otherwise unused interrupt vector would work exactly the same. // High-priority executor: UART4, priority level 6 diff --git a/examples/stm32h7/src/bin/multiprio.rs b/examples/stm32h7/src/bin/multiprio.rs index f9a00f806..ded9d390e 100644 --- a/examples/stm32h7/src/bin/multiprio.rs +++ b/examples/stm32h7/src/bin/multiprio.rs @@ -128,7 +128,7 @@ fn main() -> ! { let _p = embassy_stm32::init(Default::default()); // STM32s don’t have software-defined interrupts, so just use any free interrupt vectors which aren’t used - // by the rest of your application. In this case we’re using UART6 and UART7, but there’s nothing special + // by the rest of your application. In this case we’re using UART4 and UART5, but there’s nothing special // about them. Any otherwise unused interrupt vector would work exactly the same. // High-priority executor: UART4, priority level 6 From effc08dde34c39da3f6bb4363d517de4f5c1ddd4 Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Sun, 7 Apr 2024 16:22:42 +0200 Subject: [PATCH 67/69] =?UTF-8?q?Incorporated=20adam=E2=80=99s=20suggestio?= =?UTF-8?q?n=20into=20all=20multiprio=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/stm32f0/src/bin/multiprio.rs | 7 ++++--- examples/stm32f3/src/bin/multiprio.rs | 7 ++++--- examples/stm32f4/src/bin/multiprio.rs | 7 ++++--- examples/stm32h7/src/bin/multiprio.rs | 7 ++++--- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/examples/stm32f0/src/bin/multiprio.rs b/examples/stm32f0/src/bin/multiprio.rs index e36c0d6c2..1c3f3991a 100644 --- a/examples/stm32f0/src/bin/multiprio.rs +++ b/examples/stm32f0/src/bin/multiprio.rs @@ -126,9 +126,10 @@ fn main() -> ! { // Initialize and create handle for devicer peripherals let _p = embassy_stm32::init(Default::default()); - // STM32s don’t have software-defined interrupts, so just use any free interrupt vectors which aren’t used - // by the rest of your application. In this case we’re using UART1 and UART2, but there’s nothing special - // about them. Any otherwise unused interrupt vector would work exactly the same. + // STM32s don’t have any interrupts exclusively for software use, but they can all be triggered by software as well as + // by the peripheral, so we can just use any free interrupt vectors which aren’t used by the rest of your application. + // In this case we’re using UART1 and UART2, but there’s nothing special about them. Any otherwise unused interrupt + // vector would work exactly the same. // High-priority executor: USART1, priority level 6 interrupt::USART1.set_priority(Priority::P6); diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs index 3a3059db1..87830b416 100644 --- a/examples/stm32f3/src/bin/multiprio.rs +++ b/examples/stm32f3/src/bin/multiprio.rs @@ -127,9 +127,10 @@ fn main() -> ! { let _p = embassy_stm32::init(Default::default()); - // STM32s don’t have software-defined interrupts, so just use any free interrupt vectors which aren’t used - // by the rest of your application. In this case we’re using UART4 and UART5, but there’s nothing special - // about them. Any otherwise unused interrupt vector would work exactly the same. + // STM32s don’t have any interrupts exclusively for software use, but they can all be triggered by software as well as + // by the peripheral, so we can just use any free interrupt vectors which aren’t used by the rest of your application. + // In this case we’re using UART4 and UART5, but there’s nothing special about them. Any otherwise unused interrupt + // vector would work exactly the same. // High-priority executor: UART4, priority level 6 interrupt::UART4.set_priority(Priority::P6); diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs index 3a3059db1..87830b416 100644 --- a/examples/stm32f4/src/bin/multiprio.rs +++ b/examples/stm32f4/src/bin/multiprio.rs @@ -127,9 +127,10 @@ fn main() -> ! { let _p = embassy_stm32::init(Default::default()); - // STM32s don’t have software-defined interrupts, so just use any free interrupt vectors which aren’t used - // by the rest of your application. In this case we’re using UART4 and UART5, but there’s nothing special - // about them. Any otherwise unused interrupt vector would work exactly the same. + // STM32s don’t have any interrupts exclusively for software use, but they can all be triggered by software as well as + // by the peripheral, so we can just use any free interrupt vectors which aren’t used by the rest of your application. + // In this case we’re using UART4 and UART5, but there’s nothing special about them. Any otherwise unused interrupt + // vector would work exactly the same. // High-priority executor: UART4, priority level 6 interrupt::UART4.set_priority(Priority::P6); diff --git a/examples/stm32h7/src/bin/multiprio.rs b/examples/stm32h7/src/bin/multiprio.rs index ded9d390e..fcbb6c653 100644 --- a/examples/stm32h7/src/bin/multiprio.rs +++ b/examples/stm32h7/src/bin/multiprio.rs @@ -127,9 +127,10 @@ fn main() -> ! { let _p = embassy_stm32::init(Default::default()); - // STM32s don’t have software-defined interrupts, so just use any free interrupt vectors which aren’t used - // by the rest of your application. In this case we’re using UART4 and UART5, but there’s nothing special - // about them. Any otherwise unused interrupt vector would work exactly the same. + // STM32s don’t have any interrupts exclusively for software use, but they can all be triggered by software as well as + // by the peripheral, so we can just use any free interrupt vectors which aren’t used by the rest of your application. + // In this case we’re using UART4 and UART5, but there’s nothing special about them. Any otherwise unused interrupt + // vector would work exactly the same. // High-priority executor: UART4, priority level 6 interrupt::UART4.set_priority(Priority::P6); From fa05256f0563716ffa6e865b73679f4d545bcff9 Mon Sep 17 00:00:00 2001 From: Oliver Rockstedt Date: Mon, 8 Apr 2024 00:39:58 +0200 Subject: [PATCH 68/69] embassy-sync: Add len, is_empty and is_full functions to Channel. --- embassy-sync/CHANGELOG.md | 5 ++++- embassy-sync/src/channel.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index e7db97ef7..3f6b39d8b 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md @@ -5,6 +5,10 @@ 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 + +- Add `len`, `is_empty` and `is_full` functions to `Channel`. + ## 0.5.0 - 2023-12-04 - Add a PriorityChannel. @@ -35,7 +39,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Remove unnecessary uses of `atomic-polyfill` - Add `#[must_use]` to all futures. - ## 0.1.0 - 2022-08-26 - First release diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index 48f4dafd6..18be462cb 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -449,6 +449,18 @@ impl ChannelState { Poll::Pending } } + + fn len(&self) -> usize { + self.queue.len() + } + + fn is_empty(&self) -> bool { + self.queue.is_empty() + } + + fn is_full(&self) -> bool { + self.queue.is_full() + } } /// A bounded channel for communicating between asynchronous tasks @@ -572,6 +584,21 @@ where pub fn try_receive(&self) -> Result { self.lock(|c| c.try_receive()) } + + /// Returns the number of elements currently in the channel. + pub fn len(&self) -> usize { + self.lock(|c| c.len()) + } + + /// Returns whether the channel is empty. + pub fn is_empty(&self) -> bool { + self.lock(|c| c.is_empty()) + } + + /// Returns whether the channel is full. + pub fn is_full(&self) -> bool { + self.lock(|c| c.is_full()) + } } /// Implements the DynamicChannel to allow creating types that are unaware of the queue size with the From 03a87add156258ebb14d96e7d0b6b8597015e69a Mon Sep 17 00:00:00 2001 From: Adam Simpkins Date: Sun, 7 Apr 2024 14:37:52 -0700 Subject: [PATCH 69/69] USB: fix comments about the config.max_packet_size_0 field Fix the comment about the default value: this defaults to 64 rather than 8 bytes. It seems like the max packet size for endpoint 0 should normally be selected automatically, rather than being part of the config. At best it seems like this setting should just be a hint that gets used if when the bus is operating at full speed. The contents of the device descriptor should ideally be updated with the correct max packet size after bus enumeration completes. In practice always using 64 is probably fine if low speed environments never need to be supported. (Super speed requires a max packet size of 512 bytes, which I didn't list in the comments here.) --- embassy-usb/src/builder.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index c06107396..387b780de 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -38,11 +38,12 @@ pub struct Config<'a> { /// Maximum packet size in bytes for the control endpoint 0. /// - /// Valid values are 8, 16, 32 and 64. There's generally no need to change this from the default - /// value of 8 bytes unless a class uses control transfers for sending large amounts of data, in - /// which case using a larger packet size may be more efficient. + /// Valid values depend on the speed at which the bus is enumerated. + /// - low speed: 8 + /// - full speed: 8, 16, 32, or 64 + /// - high speed: 64 /// - /// Default: 8 bytes + /// Default: 64 bytes pub max_packet_size_0: u8, /// Manufacturer name string descriptor.