From 4d6b3c57b1a0b99409cae743a102408f23ca828b Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 20 Jul 2023 16:08:59 +0200 Subject: [PATCH 01/41] rp: fix multicore stack guard setup the region field of the register is four bits wide followed by the valid bit that causes the rnr update we rely on for the rasr write. 0x08 is just a bit short to reach the valid bit, and since rp2040 has only 8 regions it (at best) doesn't do anything at all. --- embassy-rp/src/multicore.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 468e8470a..89a2680ad 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -74,7 +74,7 @@ fn install_stack_guard(stack_bottom: *mut usize) { let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7)); unsafe { core.MPU.ctrl.write(5); // enable mpu with background default map - core.MPU.rbar.write((addr & !0xff) | 0x8); + core.MPU.rbar.write((addr & !0xff) | (1 << 4)); // set address and update RNR core.MPU.rasr.write( 1 // enable region | (0x7 << 1) // size 2^(7 + 1) = 256 From e9445ec72d8713aa7705e0a727c13c259d529e71 Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 20 Jul 2023 16:57:54 +0200 Subject: [PATCH 02/41] rp: allow for MPU-based stack guards on core 0 as well using these will require some linker script intervention. setting the core0 stack needs linker intervention anyway (to provide _stack_start), having it also provide _stack_end for the guard to use is not that much of a stretch. --- embassy-rp/src/lib.rs | 68 +++++++++++++++++++++++++++++++++++++ embassy-rp/src/multicore.rs | 33 ++++-------------- 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 4f205a16e..50f028d4c 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -219,6 +219,74 @@ select_bootloader! { default => BOOT_LOADER_W25Q080 } +/// Installs a stack guard for the CORE0 stack in MPU region 0. +/// Will fail if the MPU is already confgigured. This function requires +/// a `_stack_end` symbol to be defined by the linker script, and expexcts +/// `_stack_end` to be located at the lowest address (largest depth) of +/// the stack. +/// +/// This method can *only* set up stack guards on the currently +/// executing core. Stack guards for CORE1 are set up automatically, +/// only CORE0 should ever use this. +/// +/// # Usage +/// +/// ```no_run +/// #![feature(type_alias_impl_trait)] +/// use embassy_rp::install_core0_stack_guard; +/// use embassy_executor::{Executor, Spawner}; +/// +/// #[embassy_executor::main] +/// async fn main(_spawner: Spawner) { +/// // set up by the linker as follows: +/// // +/// // MEMORY { +/// // STACK0: ORIGIN = 0x20040000, LENGTH = 4K +/// // } +/// // +/// // _stack_end = ORIGIN(STACK0); +/// // _stack_start = _stack_end + LENGTH(STACK0); +/// // +/// install_core0_stack_guard().expect("MPU already configured"); +/// let p = embassy_rp::init(Default::default()); +/// +/// // ... +/// } +/// ``` +pub fn install_core0_stack_guard() -> Result<(), ()> { + extern "C" { + static mut _stack_end: usize; + } + unsafe { install_stack_guard(&mut _stack_end as *mut usize) } +} + +#[inline(always)] +fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { + let core = unsafe { cortex_m::Peripherals::steal() }; + + // Fail if MPU is already configured + if core.MPU.ctrl.read() != 0 { + return Err(()); + } + + // The minimum we can protect is 32 bytes on a 32 byte boundary, so round up which will + // just shorten the valid stack range a tad. + let addr = (stack_bottom as u32 + 31) & !31; + // Mask is 1 bit per 32 bytes of the 256 byte range... clear the bit for the segment we want + let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7)); + unsafe { + core.MPU.ctrl.write(5); // enable mpu with background default map + core.MPU.rbar.write((addr & !0xff) | (1 << 4)); // set address and update RNR + core.MPU.rasr.write( + 1 // enable region + | (0x7 << 1) // size 2^(7 + 1) = 256 + | (subregion_select << 8) + | 0x10000000, // XN = disable instruction fetch; no other bits means no permissions + ); + } + Ok(()) +} + pub mod config { use crate::clocks::ClockConfig; diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 89a2680ad..915761801 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -52,41 +52,20 @@ use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; use crate::interrupt::InterruptExt; use crate::peripherals::CORE1; -use crate::{gpio, interrupt, pac}; +use crate::{gpio, install_stack_guard, interrupt, pac}; const PAUSE_TOKEN: u32 = 0xDEADBEEF; const RESUME_TOKEN: u32 = !0xDEADBEEF; static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); #[inline(always)] -fn install_stack_guard(stack_bottom: *mut usize) { - let core = unsafe { cortex_m::Peripherals::steal() }; - - // Trap if MPU is already configured - if core.MPU.ctrl.read() != 0 { +fn core1_setup(stack_bottom: *mut usize) { + if let Err(_) = install_stack_guard(stack_bottom) { + // currently only happens if the MPU was already set up, which + // would indicate that the core is already in use from outside + // embassy, somehow. trap if so since we can't deal with that. cortex_m::asm::udf(); } - - // The minimum we can protect is 32 bytes on a 32 byte boundary, so round up which will - // just shorten the valid stack range a tad. - let addr = (stack_bottom as u32 + 31) & !31; - // Mask is 1 bit per 32 bytes of the 256 byte range... clear the bit for the segment we want - let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7)); - unsafe { - core.MPU.ctrl.write(5); // enable mpu with background default map - core.MPU.rbar.write((addr & !0xff) | (1 << 4)); // set address and update RNR - core.MPU.rasr.write( - 1 // enable region - | (0x7 << 1) // size 2^(7 + 1) = 256 - | (subregion_select << 8) - | 0x10000000, // XN = disable instruction fetch; no other bits means no permissions - ); - } -} - -#[inline(always)] -fn core1_setup(stack_bottom: *mut usize) { - install_stack_guard(stack_bottom); unsafe { gpio::init(); } From c83552eadcf7546acf8bf4de47d7d9243ffe56d0 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Sat, 22 Jul 2023 12:45:18 +0100 Subject: [PATCH 03/41] stm32: fix DAC examples The DAC driver defaults to enabling the channel trigger, but leaves it at the default value of TIM6 TRGO, then performs a software trigger after writing the new output value. We could change the trigger selection to software trigger, but for this example it's simpler to just disable the trigger. --- examples/stm32f4/src/bin/dac.rs | 2 +- examples/stm32h7/src/bin/dac.rs | 2 +- examples/stm32l4/src/bin/dac.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/stm32f4/src/bin/dac.rs b/examples/stm32f4/src/bin/dac.rs index 3a6216712..aaedcfecc 100644 --- a/examples/stm32f4/src/bin/dac.rs +++ b/examples/stm32f4/src/bin/dac.rs @@ -14,11 +14,11 @@ async fn main(_spawner: Spawner) -> ! { info!("Hello World, dude!"); let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4); + unwrap!(dac.set_trigger_enable(false)); loop { for v in 0..=255 { unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); - dac.trigger(); } } } diff --git a/examples/stm32h7/src/bin/dac.rs b/examples/stm32h7/src/bin/dac.rs index 586b4154b..ee078286b 100644 --- a/examples/stm32h7/src/bin/dac.rs +++ b/examples/stm32h7/src/bin/dac.rs @@ -21,11 +21,11 @@ fn main() -> ! { let p = embassy_stm32::init(config); let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); + unwrap!(dac.set_trigger_enable(false)); loop { for v in 0..=255 { unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); - dac.trigger(); } } } diff --git a/examples/stm32l4/src/bin/dac.rs b/examples/stm32l4/src/bin/dac.rs index ade43eb35..0193a248e 100644 --- a/examples/stm32l4/src/bin/dac.rs +++ b/examples/stm32l4/src/bin/dac.rs @@ -13,11 +13,11 @@ fn main() -> ! { info!("Hello World!"); let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); + unwrap!(dac.set_trigger_enable(false)); loop { for v in 0..=255 { unwrap!(dac.set(Value::Bit8(to_sine_wave(v)))); - dac.trigger(); } } } From e5b4641f9ee1b181a16028b84131083e0247d4d5 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Sat, 22 Jul 2023 12:47:17 +0100 Subject: [PATCH 04/41] stm32/dac: set pin mode to analog (ref #334) --- embassy-stm32/src/dac/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 31a2d8863..3dee242ef 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -213,8 +213,9 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { pub fn new( peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - _pin: impl Peripheral

> + 'd, + pin: impl Peripheral

> + crate::gpio::sealed::Pin + 'd, ) -> Self { + pin.set_as_analog(); into_ref!(peri, dma); T::enable(); T::reset(); @@ -324,8 +325,9 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { pub fn new( _peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - _pin: impl Peripheral

> + 'd, + pin: impl Peripheral

> + crate::gpio::sealed::Pin + 'd, ) -> Self { + pin.set_as_analog(); into_ref!(_peri, dma); T::enable(); T::reset(); @@ -439,9 +441,11 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { peri: impl Peripheral

+ 'd, dma_ch1: impl Peripheral

+ 'd, dma_ch2: impl Peripheral

+ 'd, - _pin_ch1: impl Peripheral

> + 'd, - _pin_ch2: impl Peripheral

> + 'd, + pin_ch1: impl Peripheral

> + crate::gpio::sealed::Pin + 'd, + pin_ch2: impl Peripheral

> + crate::gpio::sealed::Pin + 'd, ) -> Self { + pin_ch1.set_as_analog(); + pin_ch2.set_as_analog(); into_ref!(peri, dma_ch1, dma_ch2); T::enable(); T::reset(); From 224fbc8125de73e08c0fe03df6d3980001f77c7f Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Sat, 22 Jul 2023 13:17:40 +0100 Subject: [PATCH 05/41] stm32: remove duplicate features from stm32f4 examples Cargo.toml --- examples/stm32f4/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index c1c821364..a7e60b7bb 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } From 80ce6d1fb7eeb4617c2d10468e136a5013d71ac3 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sat, 22 Jul 2023 19:25:02 +0200 Subject: [PATCH 06/41] update DAC triggers to incorporate v3 --- embassy-stm32/Cargo.toml | 4 +-- embassy-stm32/src/dac/mod.rs | 57 +++++++++++++++++++++++++++++------- embassy-stm32/src/dma/dma.rs | 19 ++++++++++-- 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ec934e8be..0fb6fdb56 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -57,7 +57,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "12" +stm32-metapac = "13" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -74,7 +74,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "12", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "13", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 3dee242ef..6712585cf 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -38,11 +38,30 @@ impl Channel { #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// Trigger sources for CH1 pub enum Ch1Trigger { - Tim6, - Tim3, - Tim7, - Tim15, + #[cfg(dac_v3)] + Tim1, Tim2, + #[cfg(not(dac_v2))] + Tim3, + #[cfg(dac_v3)] + Tim4, + #[cfg(dac_v3)] + Tim5, + Tim6, + Tim7, + #[cfg(dac_v3)] + Tim8, + Tim15, + #[cfg(dac_v3)] + Hrtim1Dactrg1, + #[cfg(dac_v3)] + Hrtim1Dactrg2, + #[cfg(dac_v3)] + Lptim1, + #[cfg(dac_v3)] + Lptim2, + #[cfg(dac_v3)] + Lptim3, Exti9, Software, } @@ -50,11 +69,30 @@ pub enum Ch1Trigger { impl Ch1Trigger { fn tsel(&self) -> dac::vals::Tsel1 { match self { - Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO, - Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO, - Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO, - Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO, + #[cfg(dac_v3)] + Ch1Trigger::Tim1 => dac::vals::Tsel1::TIM1_TRGO, Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO, + #[cfg(dac_v2)] + Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO, + #[cfg(dac_v3)] + Ch1Trigger::Tim4 => dac::vals::Tsel1::TIM4_TRGO, + #[cfg(dac_v3)] + Ch1Trigger::Tim5 => dac::vals::Tsel1::TIM5_TRGO, + Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO, + Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO, + #[cfg(dac_v3)] + Ch1Trigger::Tim8 => dac::vals::Tsel1::TIM8_TRGO, + Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO, + #[cfg(dac_v3)] + Ch1Trigger::Hrtim1Dactrg1 => dac::vals::Tsel1::HRTIM1_DACTRG1, + #[cfg(dac_v3)] + Ch1Trigger::Hrtim1Dactrg2 => dac::vals::Tsel1::HRTIM1_DACTRG2, + #[cfg(dac_v3)] + Ch1Trigger::Lptim1 => dac::vals::Tsel1::LPTIM1_OUT, + #[cfg(dac_v3)] + Ch1Trigger::Lptim2 => dac::vals::Tsel1::LPTIM2_OUT, + #[cfg(dac_v3)] + Ch1Trigger::Lptim3 => dac::vals::Tsel1::LPTIM3_OUT, Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9, Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE, } @@ -363,7 +401,6 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. /// /// **Important:** Channel 2 has to be configured for the DAC instance! - #[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though) pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: DmaCh2, @@ -467,7 +504,7 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { dac_ch1.enable_channel().unwrap(); dac_ch1.set_trigger_enable(true).unwrap(); - #[cfg(dac_v2)] + #[cfg(any(dac_v2, dac_v3))] dac_ch2.set_channel_mode(0).unwrap(); dac_ch2.enable_channel().unwrap(); dac_ch2.set_trigger_enable(true).unwrap(); diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 58d438af8..f14084599 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -28,6 +28,12 @@ pub struct TransferOptions { pub flow_ctrl: FlowControl, /// FIFO threshold for DMA FIFO mode. If none, direct mode is used. pub fifo_threshold: Option, + /// Enable circular DMA + pub circular: bool, + /// Enable half transfer interrupt + pub half_transfer_ir: bool, + /// Enable transfer complete interrupt + pub complete_transfer_ir: bool, } impl Default for TransferOptions { @@ -37,6 +43,9 @@ impl Default for TransferOptions { mburst: Burst::Single, flow_ctrl: FlowControl::Dma, fifo_threshold: None, + circular: false, + half_transfer_ir: false, + complete_transfer_ir: true, } } } @@ -365,7 +374,13 @@ impl<'a, C: Channel> Transfer<'a, C> { }); w.set_pinc(vals::Inc::FIXED); w.set_teie(true); - w.set_tcie(true); + w.set_tcie(options.complete_transfer_ir); + if options.circular { + w.set_circ(vals::Circ::ENABLED); + debug!("Setting circular mode"); + } else { + w.set_circ(vals::Circ::DISABLED); + } #[cfg(dma_v1)] w.set_trbuff(true); @@ -646,7 +661,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { w.set_minc(vals::Inc::INCREMENTED); w.set_pinc(vals::Inc::FIXED); w.set_teie(true); - w.set_htie(true); + w.set_htie(options.half_transfer_ir); w.set_tcie(true); w.set_circ(vals::Circ::ENABLED); #[cfg(dma_v1)] From a56b3e9a44dbad2480e9937fa9e26e320951fc12 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sat, 22 Jul 2023 19:47:36 +0200 Subject: [PATCH 07/41] update feature gates for v3 --- embassy-stm32/src/dac/mod.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index bf15eaac5..979748bb4 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -164,7 +164,7 @@ pub trait DacChannel { } /// Set mode register of the given channel - #[cfg(dac_v2)] + #[cfg(any(dac_v2, dac_v3))] fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> { T::regs().mcr().modify(|reg| { reg.set_mode(Self::CHANNEL.index(), val); @@ -262,7 +262,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { // Configure each activated channel. All results can be `unwrap`ed since they // will only error if the channel is not configured (i.e. ch1, ch2 are false) - #[cfg(dac_v2)] + #[cfg(any(dac_v2, dac_v3))] dac.set_channel_mode(0).unwrap(); dac.enable_channel().unwrap(); dac.set_trigger_enable(true).unwrap(); @@ -288,7 +288,6 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled. /// /// **Important:** Channel 1 has to be configured for the DAC instance! - #[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though) pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error> where Tx: DmaCh1, @@ -377,7 +376,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { // Configure each activated channel. All results can be `unwrap`ed since they // will only error if the channel is not configured (i.e. ch1, ch2 are false) - #[cfg(dac_v2)] + #[cfg(any(dac_v2, dac_v3))] dac.set_channel_mode(0).unwrap(); dac.enable_channel().unwrap(); dac.set_trigger_enable(true).unwrap(); @@ -499,7 +498,7 @@ impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> { // Configure each activated channel. All results can be `unwrap`ed since they // will only error if the channel is not configured (i.e. ch1, ch2 are false) - #[cfg(dac_v2)] + #[cfg(any(dac_v2, dac_v3))] dac_ch1.set_channel_mode(0).unwrap(); dac_ch1.enable_channel().unwrap(); dac_ch1.set_trigger_enable(true).unwrap(); From 8e230bf6ece95470fa3be6476a4e01dbddb716ca Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Sat, 22 Jul 2023 21:36:03 +0200 Subject: [PATCH 08/41] add missing TransferOptions fields for DMA --- embassy-stm32/src/sdmmc/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 698292bff..434c56a48 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -225,6 +225,9 @@ const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOp mburst: crate::dma::Burst::Incr4, flow_ctrl: crate::dma::FlowControl::Peripheral, fifo_threshold: Some(crate::dma::FifoThreshold::Full), + circular: false, + half_transfer_ir: false, + complete_transfer_ir: true, }; #[cfg(all(sdmmc_v1, not(dma)))] const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions { From fbe30b2453a4bdbacd091aa1804b416bcb4e0706 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Sat, 22 Jul 2023 21:58:29 +0100 Subject: [PATCH 09/41] Add notes about setting chip name correctly for examples. --- README.md | 6 ++++++ examples/stm32c0/Cargo.toml | 3 ++- examples/stm32f0/Cargo.toml | 3 ++- examples/stm32f1/Cargo.toml | 3 ++- examples/stm32f2/Cargo.toml | 3 ++- examples/stm32f3/Cargo.toml | 3 ++- examples/stm32f4/Cargo.toml | 3 ++- examples/stm32f7/Cargo.toml | 3 ++- examples/stm32g0/Cargo.toml | 3 ++- examples/stm32g4/Cargo.toml | 3 ++- examples/stm32h5/Cargo.toml | 3 ++- examples/stm32h7/Cargo.toml | 3 ++- examples/stm32l0/Cargo.toml | 3 ++- examples/stm32l4/Cargo.toml | 3 ++- examples/stm32l5/Cargo.toml | 3 ++- examples/stm32u5/Cargo.toml | 3 ++- examples/stm32wb/Cargo.toml | 7 ++++--- examples/stm32wl/Cargo.toml | 3 ++- 18 files changed, 42 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index b05e55aa5..28407b8bc 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,12 @@ cargo install probe-rs --features cli cd examples/nrf52840 ``` +- Ensure `Cargo.toml` sets the right feature for the name of the chip you are programming. + If this name is incorrect, the example may fail to run or immediately crash + after being programmed. + +- Ensure `.cargo/config.toml` contains the name of the chip you are programming. + - Run the example For example: diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 26837abef..e74c5357b 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32c031c6 to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index b7b5eaa99..620a139ae 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -7,6 +7,8 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# Change stm32f091rc to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" defmt = "0.3" @@ -15,5 +17,4 @@ panic-probe = "0.3" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 29cad5b67..8450c541f 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32f103c8 to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 652210c7f..147e2ecbf 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32f207zg to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 489d0ff4c..6ac5d57e9 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32f303ze to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index c1c821364..e1ef40f92 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32f429zi to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 84d7b79c5..bbc99fee0 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32f767zi to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io = { version = "0.4.0", features = ["async"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index c88282d91..4a14568ac 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32g071rb to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 18bd03c39..935997a74 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32g491re to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 227bc28b4..aebc795c1 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32h563zi to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } embedded-io = { version = "0.4.0", features = ["async"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 768702fa9..2d82c0d0d 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32h743bi to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } embedded-io = { version = "0.4.0", features = ["async"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 747cec7bf..e6a5a4c14 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -10,10 +10,11 @@ nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstab "embassy-lora", "lora-phy", "lorawan-device", "lorawan", "embedded-io/async"] [dependencies] +# Change stm32l072cz to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index c55558518..41c9869bf 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -5,11 +5,12 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32l4s5vi to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits", "chrono"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits", "chrono"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 54911482e..5d66c59c3 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32l552ze to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 835e32940..a43a55909 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32u585ai to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 7c0b83e65..48e340264 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -5,11 +5,12 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32wb55rg to your chip name in both dependencies, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } +embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } -embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "udp", "medium-ieee802154", "nightly"], optional=true } defmt = "0.3" @@ -50,4 +51,4 @@ required-features = ["ble"] [[bin]] name = "gatt_server" -required-features = ["ble"] \ No newline at end of file +required-features = ["ble"] diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index e2c66f456..6e6f269aa 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] +# Change stm32wl55jc-cm4 to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } lora-phy = { version = "1" } From fbcc587eca63b038e0e878a58b0b21ba54bc21d8 Mon Sep 17 00:00:00 2001 From: xoviat <49173759+xoviat@users.noreply.github.com> Date: Sat, 22 Jul 2023 17:06:04 -0500 Subject: [PATCH 10/41] update readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 28407b8bc..c4c01dfbc 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ The embassy-net network stac - **Bluetooth** - The nrf-softdevice crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers. +The embassy-stm32-wpan crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers. - **LoRa** - embassy-lora supports LoRa networking. @@ -125,6 +126,8 @@ For example: cargo run --release --bin blinky ``` +For more help getting started, see [Getting Started][1] and [Running the Examples][2]. + ## Developing Embassy with Rust Analyzer based editors The [Rust Analyzer](https://rust-analyzer.github.io/) is used by [Visual Studio Code](https://code.visualstudio.com/) @@ -157,3 +160,5 @@ This work is licensed under either of at your option. +[1]: https://github.com/embassy-rs/embassy/wiki/Getting-Started +[2]: https://github.com/embassy-rs/embassy/wiki/Running-the-Examples From 4883fdd1549e8d43290b39d72c3b311d300010ea Mon Sep 17 00:00:00 2001 From: Alex Ferro Date: Sat, 22 Jul 2023 17:17:01 -0600 Subject: [PATCH 11/41] Add a STM32/DMARingBuffer::read_exact helper This provides a helper function with an async implementation, that will only return (or error) when it was able to read that many bytes, sleeping until ready. Additionally, corrected the documentation for Ringbuffer functions to use "elements" instead of "bytes" as the types were already generic over the word/element size. --- embassy-stm32/src/dma/bdma.rs | 44 +++++++++++++++++++++++++++-- embassy-stm32/src/dma/dma.rs | 44 +++++++++++++++++++++++++++-- embassy-stm32/src/dma/ringbuffer.rs | 18 ++++++------ 3 files changed, 91 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 5a87888b7..68c78123d 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -466,15 +466,53 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); } - /// Read bytes from the ring buffer + /// Read elements from the ring buffer /// Return a tuple of the length read and the length remaining in the buffer - /// If not all of the bytes were read, then there will be some bytes in the buffer remaining - /// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read + /// If not all of the elements were read, then there will be some elements in the buffer remaining + /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) } + /// Read an exact number of elements from the ringbuffer. + /// + /// Returns the remaining number of elements available for immediate reading. + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + /// + /// Async/Wake Behavior: + /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, + /// and when it wraps around. This means that when called with a buffer of length 'M', when this + /// ring buffer was created with a buffer of size 'N': + /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. + /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. + pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result { + use core::future::poll_fn; + use core::sync::atomic::compiler_fence; + + let mut read_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + self.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + match self.read(&mut buffer[read_data..buffer_len]) { + Ok((len, remaining)) => { + read_data += len; + if read_data == buffer_len { + Poll::Ready(Ok(remaining)) + } else { + Poll::Pending + } + } + Err(e) => Poll::Ready(Err(e)), + } + }) + .await + } + /// The capacity of the ringbuffer pub fn cap(&self) -> usize { self.ringbuf.cap() diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 58d438af8..9459fb763 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -696,15 +696,53 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); } - /// Read bytes from the ring buffer + /// Read elements from the ring buffer /// Return a tuple of the length read and the length remaining in the buffer - /// If not all of the bytes were read, then there will be some bytes in the buffer remaining - /// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read + /// If not all of the elements were read, then there will be some elements in the buffer remaining + /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) } + /// Read an exact number of elements from the ringbuffer. + /// + /// Returns the remaining number of elements available for immediate reading. + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + /// + /// Async/Wake Behavior: + /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, + /// and when it wraps around. This means that when called with a buffer of length 'M', when this + /// ring buffer was created with a buffer of size 'N': + /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. + /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. + pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result { + use core::future::poll_fn; + use core::sync::atomic::compiler_fence; + + let mut read_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + self.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + match self.read(&mut buffer[read_data..buffer_len]) { + Ok((len, remaining)) => { + read_data += len; + if read_data == buffer_len { + Poll::Ready(Ok(remaining)) + } else { + Poll::Pending + } + } + Err(e) => Poll::Ready(Err(e)), + } + }) + .await + } + // The capacity of the ringbuffer pub fn cap(&self) -> usize { self.ringbuf.cap() diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index a2bde986f..190793974 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -72,10 +72,10 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { self.cap() - remaining_transfers } - /// Read bytes from the ring buffer + /// Read elements from the ring buffer /// Return a tuple of the length read and the length remaining in the buffer - /// If not all of the bytes were read, then there will be some bytes in the buffer remaining - /// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read + /// If not all of the elements were read, then there will be some elements in the buffer remaining + /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, mut dma: impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { /* @@ -95,11 +95,11 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { */ let end = self.pos(dma.get_remaining_transfers()); if self.start == end && dma.get_complete_count() == 0 { - // No bytes are available in the buffer + // No elements are available in the buffer Ok((0, self.cap())) } else if self.start < end { // The available, unread portion in the ring buffer DOES NOT wrap - // Copy out the bytes from the dma buffer + // Copy out the elements from the dma buffer let len = self.copy_to(buf, self.start..end); compiler_fence(Ordering::SeqCst); @@ -128,7 +128,7 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { // The DMA writer has wrapped since we last read and is currently // writing (or the next byte added will be) in the beginning of the ring buffer. - // The provided read buffer is not large enough to include all bytes from the tail of the dma buffer. + // The provided read buffer is not large enough to include all elements from the tail of the dma buffer. // Copy out from the dma buffer let len = self.copy_to(buf, self.start..self.cap()); @@ -154,8 +154,8 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { // The DMA writer has wrapped since we last read and is currently // writing (or the next byte added will be) in the beginning of the ring buffer. - // The provided read buffer is large enough to include all bytes from the tail of the dma buffer, - // so the next read will not have any unread tail bytes in the ring buffer. + // The provided read buffer is large enough to include all elements from the tail of the dma buffer, + // so the next read will not have any unread tail elements in the ring buffer. // Copy out from the dma buffer let tail = self.copy_to(buf, self.start..self.cap()); @@ -180,7 +180,7 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { } /// Copy from the dma buffer at `data_range` into `buf` fn copy_to(&mut self, buf: &mut [W], data_range: Range) -> usize { - // Limit the number of bytes that can be copied + // Limit the number of elements that can be copied let length = usize::min(data_range.len(), buf.len()); // Copy from dma buffer into read buffer From bd60f003e0ef367e1678d549973ba9b4ae753652 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 23 Jul 2023 17:01:34 -0500 Subject: [PATCH 12/41] stm32/rcc: move rcc logic from ipcc --- embassy-stm32/src/ipcc.rs | 56 +----- embassy-stm32/src/rcc/mod.rs | 8 + embassy-stm32/src/rcc/wb.rs | 340 +++++++++++++++++++++++++++++------ 3 files changed, 294 insertions(+), 110 deletions(-) diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index a24cba9f0..e100ca5cc 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -265,63 +265,9 @@ pub(crate) mod sealed { } fn _configure_pwr() { - // TODO: move this to RCC - - let pwr = crate::pac::PWR; + // TODO: move the rest of this to rcc let rcc = crate::pac::RCC; - rcc.cfgr().modify(|w| w.set_stopwuck(true)); - - pwr.cr1().modify(|w| w.set_dbp(true)); - pwr.cr1().modify(|w| w.set_dbp(true)); - - // configure LSE - rcc.bdcr().modify(|w| w.set_lseon(true)); - - // select system clock source = PLL - // set PLL coefficients - // m: 2, - // n: 12, - // r: 3, - // q: 4, - // p: 3, - let src_bits = 0b11; - let pllp = (3 - 1) & 0b11111; - let pllq = (4 - 1) & 0b111; - let pllr = (3 - 1) & 0b111; - let plln = 12 & 0b1111111; - let pllm = (2 - 1) & 0b111; - rcc.pllcfgr().modify(|w| { - w.set_pllsrc(src_bits); - w.set_pllm(pllm); - w.set_plln(plln); - w.set_pllr(pllr); - w.set_pllp(pllp); - w.set_pllpen(true); - w.set_pllq(pllq); - w.set_pllqen(true); - }); - // enable PLL - rcc.cr().modify(|w| w.set_pllon(true)); - rcc.cr().write(|w| w.set_hsion(false)); - // while !rcc.cr().read().pllrdy() {} - - // configure SYSCLK mux to use PLL clocl - rcc.cfgr().modify(|w| w.set_sw(0b11)); - - // configure CPU1 & CPU2 dividers - rcc.cfgr().modify(|w| w.set_hpre(0)); // not divided - rcc.extcfgr().modify(|w| { - w.set_c2hpre(0b1000); // div2 - w.set_shdhpre(0); // not divided - }); - - // apply APB1 / APB2 values - rcc.cfgr().modify(|w| { - w.set_ppre1(0b000); // not divided - w.set_ppre2(0b000); // not divided - }); - // TODO: required // set RF wake-up clock = LSE rcc.csr().modify(|w| w.set_rfwkpsel(0b01)); diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 886fc0b93..4ae65d3e6 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -78,6 +78,14 @@ pub struct Clocks { /// The existence of this value indicates that the clock configuration can no longer be changed static mut CLOCK_FREQS: MaybeUninit = MaybeUninit::uninit(); +#[cfg(stm32wb)] +/// RCC initialization function +pub(crate) unsafe fn init(config: Config) { + set_freqs(compute_clocks(&config)); + + configure_clocks(&config); +} + /// Sets the clock frequencies /// /// Safety: Sets a mutable global. diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index e6123821a..41f602c02 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -1,6 +1,5 @@ -use crate::pac::RCC; -use crate::rcc::{set_freqs, Clocks}; -use crate::time::Hertz; +use crate::rcc::Clocks; +use crate::time::{khz, mhz, Hertz}; /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, /// and with the addition of the init function to configure a system clock. @@ -13,11 +12,94 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000); /// LSI speed pub const LSI_FREQ: Hertz = Hertz(32_000); -/// System clock mux source #[derive(Clone, Copy)] -pub enum ClockSrc { - HSE(Hertz), - HSI16, +pub enum HsePrescaler { + NotDivided, + Div2, +} + +impl From for bool { + fn from(value: HsePrescaler) -> Self { + match value { + HsePrescaler::NotDivided => false, + HsePrescaler::Div2 => true, + } + } +} + +pub struct Hse { + pub prediv: HsePrescaler, + + pub frequency: Hertz, +} + +/// System clock mux source +#[derive(Clone, Copy, PartialEq)] +pub enum Sysclk { + /// MSI selected as sysclk + MSI, + /// HSI selected as sysclk + HSI, + /// HSE selected as sysclk + HSE, + /// PLL selected as sysclk + Pll, +} + +impl From for u8 { + fn from(value: Sysclk) -> Self { + match value { + Sysclk::MSI => 0b00, + Sysclk::HSI => 0b01, + Sysclk::HSE => 0b10, + Sysclk::Pll => 0b11, + } + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum PllSource { + Hsi, + Msi, + Hse, +} + +impl From for u8 { + fn from(value: PllSource) -> Self { + match value { + PllSource::Msi => 0b01, + PllSource::Hsi => 0b10, + PllSource::Hse => 0b11, + } + } +} + +pub enum Pll48Source { + PllSai, + Pll, + Msi, + Hsi48, +} + +pub struct PllMux { + /// Source clock selection. + pub source: PllSource, + + /// PLL pre-divider (DIVM). Must be between 1 and 63. + pub prediv: u8, +} + +pub struct Pll { + /// PLL multiplication factor. Must be between 4 and 512. + pub mul: u16, + + /// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128. + /// On PLL1, it must be even (in particular, it cannot be 1.) + pub divp: Option, + /// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128. + pub divq: Option, + /// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128. + pub divr: Option, } /// AHB prescaler @@ -84,8 +166,18 @@ impl Into for AHBPrescaler { /// Clocks configutation pub struct Config { - pub mux: ClockSrc, - pub ahb_pre: AHBPrescaler, + pub hse: Option, + pub lse: Option, + pub sys: Sysclk, + pub mux: Option, + pub pll48: Option, + + pub pll: Option, + pub pllsai: Option, + + pub ahb1_pre: AHBPrescaler, + pub ahb2_pre: AHBPrescaler, + pub ahb3_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, } @@ -94,76 +186,214 @@ impl Default for Config { #[inline] fn default() -> Config { Config { - mux: ClockSrc::HSI16, - ahb_pre: AHBPrescaler::NotDivided, + hse: Some(Hse { + frequency: mhz(32), + prediv: HsePrescaler::NotDivided, + }), + lse: Some(khz(32)), + sys: Sysclk::HSI, + mux: Some(PllMux { + source: PllSource::Hse, + prediv: 2, + }), + pll48: None, + + pll: Some(Pll { + mul: 12, + divp: Some(3), + divq: Some(4), + divr: Some(3), + }), + pllsai: None, + + ahb1_pre: AHBPrescaler::NotDivided, + ahb2_pre: AHBPrescaler::Div2, + ahb3_pre: AHBPrescaler::NotDivided, apb1_pre: APBPrescaler::NotDivided, apb2_pre: APBPrescaler::NotDivided, } } } -pub(crate) unsafe fn init(config: Config) { - let (sys_clk, sw) = match config.mux { - ClockSrc::HSI16 => { - // Enable HSI16 - RCC.cr().write(|w| w.set_hsion(true)); - while !RCC.cr().read().hsirdy() {} - - (HSI_FREQ.0, 0x01) - } - ClockSrc::HSE(freq) => { - // Enable HSE - RCC.cr().write(|w| w.set_hseon(true)); - while !RCC.cr().read().hserdy() {} - - (freq.0, 0x02) - } - }; - - RCC.cfgr().modify(|w| { - w.set_sw(sw.into()); - w.set_hpre(config.ahb_pre.into()); - w.set_ppre1(config.apb1_pre.into()); - w.set_ppre2(config.apb2_pre.into()); +pub(crate) fn compute_clocks(config: &Config) -> Clocks { + let hse_clk = config.hse.as_ref().map(|hse| match hse.prediv { + HsePrescaler::NotDivided => hse.frequency, + HsePrescaler::Div2 => hse.frequency / 2u32, }); - let ahb_freq: u32 = match config.ahb_pre { + let mux_clk = config.mux.as_ref().map(|pll_mux| { + (match pll_mux.source { + PllSource::Hse => hse_clk.unwrap(), + PllSource::Hsi => HSI_FREQ, + _ => unreachable!(), + } / pll_mux.prediv) + }); + + let (pll_r, _pll_q, _pll_p) = match &config.pll { + Some(pll) => { + let pll_vco = mux_clk.unwrap() * pll.mul as u32; + + ( + pll.divr.map(|divr| pll_vco / divr), + pll.divq.map(|divq| pll_vco / divq), + pll.divp.map(|divp| pll_vco / divp), + ) + } + None => (None, None, None), + }; + + let sys_clk = match config.sys { + Sysclk::HSE => hse_clk.unwrap(), + Sysclk::HSI => HSI_FREQ, + Sysclk::Pll => pll_r.unwrap(), + _ => unreachable!(), + }; + + let ahb1_clk = match config.ahb1_pre { AHBPrescaler::NotDivided => sys_clk, pre => { let pre: u8 = pre.into(); - let pre = 1 << (pre as u32 - 7); + let pre = 1u32 << (pre as u32 - 7); sys_clk / pre } }; - let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { - APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + let ahb2_clk = match config.ahb2_pre { + AHBPrescaler::NotDivided => sys_clk, pre => { let pre: u8 = pre.into(); - let pre: u8 = 1 << (pre - 3); - let freq = ahb_freq / pre as u32; - (freq, freq * 2) + let pre = 1u32 << (pre as u32 - 7); + sys_clk / pre } }; - let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { - APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + let ahb3_clk = match config.ahb3_pre { + AHBPrescaler::NotDivided => sys_clk, pre => { let pre: u8 = pre.into(); - let pre: u8 = 1 << (pre - 3); - let freq = ahb_freq / pre as u32; - (freq, freq * 2) + let pre = 1u32 << (pre as u32 - 7); + sys_clk / pre } }; - set_freqs(Clocks { - sys: Hertz(sys_clk), - ahb1: Hertz(ahb_freq), - ahb2: Hertz(ahb_freq), - ahb3: Hertz(ahb_freq), - apb1: Hertz(apb1_freq), - apb2: Hertz(apb2_freq), - apb1_tim: Hertz(apb1_tim_freq), - apb2_tim: Hertz(apb2_tim_freq), + let (apb1_clk, apb1_tim_clk) = match config.apb1_pre { + APBPrescaler::NotDivided => (ahb1_clk, ahb1_clk), + pre => { + let pre: u8 = pre.into(); + let pre: u8 = 1 << (pre - 3); + let freq = ahb1_clk / pre as u32; + (freq, freq * 2u32) + } + }; + + let (apb2_clk, apb2_tim_clk) = match config.apb2_pre { + APBPrescaler::NotDivided => (ahb1_clk, ahb1_clk), + pre => { + let pre: u8 = pre.into(); + let pre: u8 = 1 << (pre - 3); + let freq = ahb1_clk / pre as u32; + (freq, freq * 2u32) + } + }; + + Clocks { + sys: sys_clk, + ahb1: ahb1_clk, + ahb2: ahb2_clk, + ahb3: ahb3_clk, + apb1: apb1_clk, + apb2: apb2_clk, + apb1_tim: apb1_tim_clk, + apb2_tim: apb2_tim_clk, + } +} + +pub(crate) fn configure_clocks(config: &Config) { + let pwr = crate::pac::PWR; + let rcc = crate::pac::RCC; + + let needs_hsi = if let Some(pll_mux) = &config.mux { + pll_mux.source == PllSource::Hsi + } else { + false + }; + + if needs_hsi || config.sys == Sysclk::HSI { + rcc.cr().modify(|w| { + w.set_hsion(true); + }); + + while !rcc.cr().read().hsirdy() {} + } + + match &config.lse { + Some(_) => { + rcc.cfgr().modify(|w| w.set_stopwuck(true)); + + pwr.cr1().modify(|w| w.set_dbp(true)); + pwr.cr1().modify(|w| w.set_dbp(true)); + + rcc.bdcr().modify(|w| w.set_lseon(true)); + } + _ => {} + } + + match &config.hse { + Some(hse) => { + rcc.cr().modify(|w| { + w.set_hsepre(hse.prediv.into()); + w.set_hseon(true); + }); + + while !rcc.cr().read().hserdy() {} + } + _ => {} + } + + match &config.mux { + Some(pll_mux) => { + rcc.pllcfgr().modify(|w| { + w.set_pllm(pll_mux.prediv); + w.set_pllsrc(pll_mux.source.into()); + }); + } + _ => {} + }; + + match &config.pll { + Some(pll) => { + rcc.pllcfgr().modify(|w| { + w.set_plln((pll.mul - 1) as u8); + pll.divp.map(|divp| { + w.set_pllpen(true); + w.set_pllp((divp - 1) as u8) + }); + pll.divq.map(|divq| { + w.set_pllqen(true); + w.set_pllq((divq - 1) as u8) + }); + pll.divr.map(|divr| w.set_pllr((divr - 1) as u8)); + }); + + rcc.cr().modify(|w| w.set_pllon(true)); + + while !rcc.cr().read().pllrdy() {} + } + _ => {} + } + + rcc.cfgr().modify(|w| { + w.set_sw(config.sys.into()); + w.set_hpre(config.ahb1_pre.into()); + w.set_ppre1(config.apb1_pre.into()); + w.set_ppre2(config.apb2_pre.into()); + + w.set_ppre1(config.apb1_pre.into()); + w.set_ppre2(config.apb2_pre.into()); + }); + + rcc.extcfgr().modify(|w| { + w.set_c2hpre(config.ahb2_pre.into()); + w.set_shdhpre(config.ahb3_pre.into()); }); } From 2a0fe7304595497b3458b350574ed788a5a07876 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 24 Jul 2023 17:46:03 +0300 Subject: [PATCH 13/41] stm32/can: bxcan async enable --- embassy-stm32/src/can/bxcan.rs | 12 ++++++++++++ examples/stm32f4/src/bin/can.rs | 7 +++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 5a0153464..8b8244d4f 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -161,6 +161,18 @@ impl<'d, T: Instance> Can<'d, T> { .leave_disabled(); } + /// Enables the peripheral and synchronizes with the bus. + /// + /// This will wait for 11 consecutive recessive bits (bus idle state). + /// Contrary to enable method from bxcan library, this will not freeze the executor while waiting. + pub async fn enable(&mut self) { + while self.borrow_mut().enable_non_blocking().is_err() { + // SCE interrupt is only generated for entering sleep mode, but not leaving. + // Yield to allow other tasks to execute while can bus is initializing. + embassy_futures::yield_now().await; + } + } + /// Queues the message to be sent but exerts backpressure pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { poll_fn(|cx| { diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs index 08bed88db..f84f74d30 100644 --- a/examples/stm32f4/src/bin/can.rs +++ b/examples/stm32f4/src/bin/can.rs @@ -40,10 +40,13 @@ async fn main(_spawner: Spawner) { can.as_mut() .modify_config() - .set_bit_timing(0x001c0003) // http://www.bittiming.can-wiki.info/ .set_loopback(true) // Receive own frames .set_silent(true) - .enable(); + .leave_disabled(); + + can.set_bitrate(1_000_000); + + can.enable().await; let mut i: u8 = 0; loop { From a60d92cfbbacc909ba781802cad04fe00e849026 Mon Sep 17 00:00:00 2001 From: Michael van Niekerk Date: Mon, 24 Jul 2023 22:20:00 +0200 Subject: [PATCH 14/41] Tx and Rx setup --- examples/rp/.idea/.gitignore | 8 + examples/rp/.idea/modules.xml | 8 + examples/rp/.idea/rp.iml | 12 ++ examples/rp/.idea/vcs.xml | 6 + examples/rp/src/bin/pio_uart.rs | 262 ++++++++++++++++++++++++++++++++ 5 files changed, 296 insertions(+) create mode 100644 examples/rp/.idea/.gitignore create mode 100644 examples/rp/.idea/modules.xml create mode 100644 examples/rp/.idea/rp.iml create mode 100644 examples/rp/.idea/vcs.xml create mode 100644 examples/rp/src/bin/pio_uart.rs diff --git a/examples/rp/.idea/.gitignore b/examples/rp/.idea/.gitignore new file mode 100644 index 000000000..13566b81b --- /dev/null +++ b/examples/rp/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/examples/rp/.idea/modules.xml b/examples/rp/.idea/modules.xml new file mode 100644 index 000000000..06ff4b23d --- /dev/null +++ b/examples/rp/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/examples/rp/.idea/rp.iml b/examples/rp/.idea/rp.iml new file mode 100644 index 000000000..9b4cf845b --- /dev/null +++ b/examples/rp/.idea/rp.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/rp/.idea/vcs.xml b/examples/rp/.idea/vcs.xml new file mode 100644 index 000000000..b2bdec2d7 --- /dev/null +++ b/examples/rp/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs new file mode 100644 index 000000000..14d05f4db --- /dev/null +++ b/examples/rp/src/bin/pio_uart.rs @@ -0,0 +1,262 @@ +//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. +//! +//! This creates a USB serial port that echos. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, panic}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::{PIO0, USB}; +use embassy_rp::usb::{Driver, Instance, InterruptHandler}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::{Builder, Config}; +use embassy_rp::pio::{InterruptHandler as PioInterruptHandler}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct UsbIrqs { + USBCTRL_IRQ => InterruptHandler; +}); + +bind_interrupts!(struct PioIrqs { + PIO0_IRQ_0 => PioInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello there!"); + + let p = embassy_rp::init(Default::default()); + + // Create the driver, from the HAL. + let driver = Driver::new(p.USB, UsbIrqs); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("PIO UART example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} + +mod uart { + use embassy_rp::peripherals::PIO0; + use embassy_rp::pio::{Common, Pio, PioPin, StateMachine}; + use embassy_rp::Peripheral; + + use crate::PioIrqs; + + pub struct PioUart<'a> { + baud: u64, + pio: Common<'a, PIO0>, + sm0: StateMachine<'a, PIO0, 0>, + sm1: StateMachine<'a, PIO0, 1>, + } + + impl<'a> PioUart<'a> { + pub async fn new( + baud: u64, + pio: impl Peripheral

+ 'a, + tx_pin: impl PioPin, + rx_pin: impl PioPin, + ) -> PioUart<'a> { + let Pio { + mut common, + mut sm0, + mut sm1, + .. + } = Pio::new(pio, PioIrqs); + + crate::uart_tx::setup_uart_tx_on_sm0(&mut common, &mut sm0, tx_pin, baud); + crate::uart_rx::setup_uart_rx_on_sm1(&mut common, &mut sm1, rx_pin, baud); + + PioUart { + baud, + pio: common, + sm0, + sm1, + } + } + } +} + +mod uart_tx { + use embassy_rp::gpio::Level; + use embassy_rp::peripherals::PIO0; + use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; + use embassy_rp::relocate::RelocatedProgram; + use fixed::traits::ToFixed; + use fixed_macro::types::U56F8; + + pub fn setup_uart_tx_on_sm0<'a>( + common: &mut Common<'a, PIO0>, + sm_tx: &mut StateMachine<'a, PIO0, 0>, + tx_pin: impl PioPin, + baud: u64, + ) { + let prg = pio_proc::pio_asm!( + r#" + ;.program uart_tx + .side_set 1 opt + + ; An 8n1 UART transmit program. + ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin. + + pull side 1 [7] ; Assert stop bit, or stall with line in idle state + set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks + bitloop: ; This loop will run 8 times (8n1 UART) + out pins, 1 ; Shift 1 bit from OSR to the first OUT pin + jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. + "# + ); + let tx_pin = common.make_pio_pin(tx_pin); + sm_tx.set_pins(Level::High, &[&tx_pin]); + sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); + + let relocated = RelocatedProgram::new(&prg.program); + let mut cfg = Config::default(); + + cfg.use_program(&common.load_program(&relocated), &[&tx_pin]); + cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); + cfg.shift_out.auto_fill = false; + cfg.shift_out.direction = ShiftDirection::Right; + cfg.fifo_join = FifoJoin::TxOnly; + cfg.set_out_pins(&[&tx_pin]); + cfg.set_set_pins(&[&tx_pin]); + sm_tx.set_config(&cfg); + sm_tx.set_enable(true) + } +} + +mod uart_rx { + use embassy_rp::gpio::Level; + use embassy_rp::peripherals::PIO0; + use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; + use embassy_rp::relocate::RelocatedProgram; + use fixed::traits::ToFixed; + use fixed_macro::types::U56F8; + + pub fn setup_uart_rx_on_sm1<'a>( + common: &mut Common<'a, PIO0>, + sm_rx: &mut StateMachine<'a, PIO0, 1>, + rx_pin: impl PioPin, + baud: u64, + ) { + let prg = pio_proc::pio_asm!( + r#" + ;.program uart_rx + + ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and + ; break conditions more gracefully. + ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX. + + start: + wait 0 pin 0 ; Stall until start bit is asserted + set x, 7 [10] ; Preload bit counter, then delay until halfway through + bitloop: ; the first data bit (12 cycles incl wait, set). + in pins, 1 ; Shift data bit into ISR + jmp x-- bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles + jmp pin good_stop ; Check stop bit (should be high) + + irq 4 rel ; Either a framing error or a break. Set a sticky flag, + wait 1 pin 0 ; and wait for line to return to idle state. + jmp start ; Don't push data if we didn't see good framing. + + good_stop: ; No delay before returning to start; a little slack is + push ; important in case the TX clock is slightly too fast. + "# + ); + + let rx_pin = common.make_pio_pin(rx_pin); + sm_rx.set_pins(Level::High, &[&rx_pin]); + sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]); + + let relocated = RelocatedProgram::new(&prg.program); + let mut cfg = Config::default(); + + cfg.use_program(&common.load_program(&relocated), &[&rx_pin]); + cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); + cfg.shift_out.auto_fill = false; + cfg.shift_out.direction = ShiftDirection::Right; + cfg.fifo_join = FifoJoin::RxOnly; + cfg.set_in_pins(&[&rx_pin]); + cfg.set_jmp_pin(&rx_pin); + // cfg.set_set_pins(&[&rx_pin]); + sm_rx.set_config(&cfg); + sm_rx.set_enable(true) + } +} From 1425dda0a76da5da44de4c2fb7d04fc5a02cfc8a Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 24 Jul 2023 17:19:45 -0500 Subject: [PATCH 15/41] stm32/rcc: fix minor issues --- embassy-stm32/src/rcc/wb.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index 41f602c02..91a11a95d 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -191,7 +191,7 @@ impl Default for Config { prediv: HsePrescaler::NotDivided, }), lse: Some(khz(32)), - sys: Sysclk::HSI, + sys: Sysclk::Pll, mux: Some(PllMux { source: PllSource::Hse, prediv: 2, @@ -363,7 +363,7 @@ pub(crate) fn configure_clocks(config: &Config) { match &config.pll { Some(pll) => { rcc.pllcfgr().modify(|w| { - w.set_plln((pll.mul - 1) as u8); + w.set_plln(pll.mul as u8); pll.divp.map(|divp| { w.set_pllpen(true); w.set_pllp((divp - 1) as u8) @@ -372,7 +372,10 @@ pub(crate) fn configure_clocks(config: &Config) { w.set_pllqen(true); w.set_pllq((divq - 1) as u8) }); - pll.divr.map(|divr| w.set_pllr((divr - 1) as u8)); + pll.divr.map(|divr| { + // w.set_pllren(true); + w.set_pllr((divr - 1) as u8); + }); }); rcc.cr().modify(|w| w.set_pllon(true)); @@ -387,9 +390,6 @@ pub(crate) fn configure_clocks(config: &Config) { w.set_hpre(config.ahb1_pre.into()); w.set_ppre1(config.apb1_pre.into()); w.set_ppre2(config.apb2_pre.into()); - - w.set_ppre1(config.apb1_pre.into()); - w.set_ppre2(config.apb2_pre.into()); }); rcc.extcfgr().modify(|w| { From 3c41784de87a8903ac72cea19da784c0152896fe Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 24 Jul 2023 18:08:23 -0500 Subject: [PATCH 16/41] stm32/usart: fix error msg for lptim --- embassy-stm32/src/usart/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index ea8e525ea..3b9226fdf 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -857,7 +857,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", oversampling, config.baudrate, - pclk_freq.0 / div + (pclk_freq.0 * mul as u32) / div ); r.cr2().write(|w| { From 270d1d59a0b6ab2f180376bb193c2495f14632b8 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 24 Jul 2023 18:25:15 -0500 Subject: [PATCH 17/41] stm32/rcc: use wpan default only for wpan --- embassy-stm32/src/rcc/wb.rs | 52 +++++++++++++++++++++------------ tests/stm32/src/bin/wpan_ble.rs | 6 +++- tests/stm32/src/bin/wpan_mac.rs | 6 +++- 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index 91a11a95d..4322b950a 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -182,32 +182,48 @@ pub struct Config { pub apb2_pre: APBPrescaler, } +pub const WPAN_DEFAULT: Config = Config { + hse: Some(Hse { + frequency: mhz(32), + prediv: HsePrescaler::NotDivided, + }), + lse: Some(khz(32)), + sys: Sysclk::Pll, + mux: Some(PllMux { + source: PllSource::Hse, + prediv: 2, + }), + pll48: None, + + pll: Some(Pll { + mul: 12, + divp: Some(3), + divq: Some(4), + divr: Some(3), + }), + pllsai: None, + + ahb1_pre: AHBPrescaler::NotDivided, + ahb2_pre: AHBPrescaler::Div2, + ahb3_pre: AHBPrescaler::NotDivided, + apb1_pre: APBPrescaler::NotDivided, + apb2_pre: APBPrescaler::NotDivided, +}; + impl Default for Config { #[inline] fn default() -> Config { Config { - hse: Some(Hse { - frequency: mhz(32), - prediv: HsePrescaler::NotDivided, - }), - lse: Some(khz(32)), - sys: Sysclk::Pll, - mux: Some(PllMux { - source: PllSource::Hse, - prediv: 2, - }), + hse: None, + lse: None, + sys: Sysclk::HSI, + mux: None, pll48: None, - - pll: Some(Pll { - mul: 12, - divp: Some(3), - divq: Some(4), - divr: Some(3), - }), + pll: None, pllsai: None, ahb1_pre: AHBPrescaler::NotDivided, - ahb2_pre: AHBPrescaler::Div2, + ahb2_pre: AHBPrescaler::NotDivided, ahb3_pre: AHBPrescaler::NotDivided, apb1_pre: APBPrescaler::NotDivided, apb2_pre: APBPrescaler::NotDivided, diff --git a/tests/stm32/src/bin/wpan_ble.rs b/tests/stm32/src/bin/wpan_ble.rs index 3ad8aca4e..452da77a4 100644 --- a/tests/stm32/src/bin/wpan_ble.rs +++ b/tests/stm32/src/bin/wpan_ble.rs @@ -12,6 +12,7 @@ use common::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::hci::host::uart::UartHci; use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; use embassy_stm32_wpan::hci::types::AdvertisingType; @@ -40,7 +41,10 @@ async fn run_mm_queue(memory_manager: mm::MemoryManager) { #[embassy_executor::main] async fn main(spawner: Spawner) { - let p = embassy_stm32::init(config()); + let mut config = config(); + config.rcc = WPAN_DEFAULT; + + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs index b04a19ee9..7eab2fd38 100644 --- a/tests/stm32/src/bin/wpan_mac.rs +++ b/tests/stm32/src/bin/wpan_mac.rs @@ -10,6 +10,7 @@ use common::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::mac::commands::{AssociateRequest, GetRequest, ResetRequest, SetRequest}; use embassy_stm32_wpan::mac::event::MacEvent; use embassy_stm32_wpan::mac::typedefs::{ @@ -31,7 +32,10 @@ async fn run_mm_queue(memory_manager: mm::MemoryManager) { #[embassy_executor::main] async fn main(spawner: Spawner) { - let p = embassy_stm32::init(config()); + let mut config = config(); + config.rcc = WPAN_DEFAULT; + + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); From 858ddf6777d6df0e8c02921d29cd6a8095a7cdad Mon Sep 17 00:00:00 2001 From: Piotr Esden-Tempski Date: Wed, 26 Jul 2023 18:32:40 -0700 Subject: [PATCH 18/41] Added debug=2 in release profile to all examples. This makes rtt output work right when using `cargo run` in release mode. Debug was already enabled for release builds in some of the examples but not all. --- examples/nrf-rtos-trace/Cargo.toml | 3 +++ examples/nrf52840-rtic/Cargo.toml | 3 +++ examples/nrf52840/Cargo.toml | 3 +++ examples/nrf5340/Cargo.toml | 3 +++ examples/rp/Cargo.toml | 3 ++- examples/std/Cargo.toml | 3 +++ examples/stm32c0/Cargo.toml | 3 +++ examples/stm32f0/Cargo.toml | 3 +++ examples/stm32f1/Cargo.toml | 3 +++ examples/stm32f2/Cargo.toml | 3 +++ examples/stm32f3/Cargo.toml | 3 +++ examples/stm32f7/Cargo.toml | 3 +++ examples/stm32g0/Cargo.toml | 3 +++ examples/stm32g4/Cargo.toml | 3 +++ examples/stm32l0/Cargo.toml | 3 +++ examples/stm32l1/Cargo.toml | 3 +++ examples/stm32l4/Cargo.toml | 3 +++ examples/stm32l5/Cargo.toml | 3 +++ examples/stm32u5/Cargo.toml | 3 +++ examples/stm32wb/Cargo.toml | 3 +++ examples/stm32wl/Cargo.toml | 3 +++ examples/wasm/Cargo.toml | 3 +++ 22 files changed, 65 insertions(+), 1 deletion(-) diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 30b67b7b2..068474e7a 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -34,3 +34,6 @@ log = { version = "0.4.17", optional = true } [[bin]] name = "rtos_trace" required-features = ["nightly"] + +[profile.release] +debug = 2 diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index ded3b7db8..715f1ecfe 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -19,3 +19,6 @@ cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-sing cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } + +[profile.release] +debug = 2 diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 9b41ec5ab..5d6bf54e8 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -59,3 +59,6 @@ microfft = "0.5.0" [patch.crates-io] lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } + +[profile.release] +debug = 2 diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index f1d45f336..b0e51dcf4 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -53,3 +53,6 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } + +[profile.release] +debug = 2 diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index c812cb3ee..f2fe5da4e 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -53,7 +53,8 @@ pio = "0.2.1" rand = { version = "0.8.5", default-features = false } [profile.release] -debug = true +debug = 2 [patch.crates-io] lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } + diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 92933ab50..42adede10 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -23,3 +23,6 @@ clap = { version = "3.0.0-beta.5", features = ["derive"] } rand_core = { version = "0.6.3", features = ["std"] } heapless = { version = "0.7.5", default-features = false } static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index e74c5357b..8534921ab 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -20,3 +20,6 @@ embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } + +[profile.release] +debug = 2 diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 620a139ae..46b6db45c 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -18,3 +18,6 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 8450c541f..5d32992cd 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -26,3 +26,6 @@ nb = "1.0.0" [profile.dev] opt-level = "s" + +[profile.release] +debug = 2 diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 147e2ecbf..9857fb631 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -21,3 +21,6 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } nb = "1.0.0" + +[profile.release] +debug = 2 diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 6ac5d57e9..bd594d16a 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -25,3 +25,6 @@ heapless = { version = "0.7.5", default-features = false } nb = "1.0.0" embedded-storage = "0.3.0" static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index bbc99fee0..a6964c7bc 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -28,3 +28,6 @@ rand_core = "0.6.3" critical-section = "1.1" embedded-storage = "0.3.0" static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index 4a14568ac..b4dfe3c6b 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -20,3 +20,6 @@ embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } + +[profile.release] +debug = 2 diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 935997a74..ce8838605 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -22,3 +22,6 @@ embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } + +[profile.release] +debug = 2 diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index e6a5a4c14..f2ebae775 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -36,3 +36,6 @@ static_cell = "1.1" [patch.crates-io] lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } + +[profile.release] +debug = 2 diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index dcca1cc3d..329d44cac 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -20,3 +20,6 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } embedded-storage = "0.3.0" + +[profile.release] +debug = 2 diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 41c9869bf..0f770e2f0 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -27,3 +27,6 @@ heapless = { version = "0.7.5", default-features = false } chrono = { version = "^0.4", default-features = false } micromath = "2.0.0" + +[profile.release] +debug = 2 diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 5d66c59c3..1afd00398 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -27,3 +27,6 @@ heapless = { version = "0.7.5", default-features = false } rand_core = { version = "0.6.3", default-features = false } embedded-io = { version = "0.4.0", features = ["async"] } static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index a43a55909..db251eafe 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -23,3 +23,6 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } micromath = "2.0.0" + +[profile.release] +debug = 2 diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 48e340264..1a5aff352 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -52,3 +52,6 @@ required-features = ["ble"] [[bin]] name = "gatt_server" required-features = ["ble"] + +[profile.release] +debug = 2 diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 6e6f269aa..3e99b1018 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -30,3 +30,6 @@ chrono = { version = "^0.4", default-features = false } [patch.crates-io] lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } + +[profile.release] +debug = 2 diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 3679e3857..2791cc341 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -17,3 +17,6 @@ wasm-bindgen = "0.2" web-sys = { version = "0.3", features = ["Document", "Element", "HtmlElement", "Node", "Window" ] } log = "0.4.11" critical-section = { version = "1.1", features = ["std"] } + +[profile.release] +debug = 2 From c54ae73d4999fdf6243adeedcde1467493c8935a Mon Sep 17 00:00:00 2001 From: ceekdee Date: Wed, 26 Jul 2023 21:51:09 -0500 Subject: [PATCH 19/41] Use lora-phy v1.2.1; modify embassy-lora dependencies. --- embassy-lora/Cargo.toml | 13 +++---------- examples/nrf52840/Cargo.toml | 3 --- examples/rp/Cargo.toml | 3 --- examples/stm32l0/Cargo.toml | 3 --- examples/stm32wl/Cargo.toml | 3 --- 5 files changed, 3 insertions(+), 22 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index e4524af5b..c4857ace0 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -12,7 +12,7 @@ target = "thumbv7em-none-eabi" [features] stm32wl = ["dep:embassy-stm32"] -time = [] +time = ["dep:embassy-time", "dep:lorawan-device"] defmt = ["dep:defmt", "lorawan-device/defmt"] [dependencies] @@ -20,18 +20,11 @@ defmt = ["dep:defmt", "lorawan-device/defmt"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy-time = { version = "0.1.2", path = "../embassy-time" } +embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } embedded-hal-async = { version = "=0.2.0-alpha.2" } -embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } -futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } embedded-hal = { version = "0.2", features = ["unproven"] } -bit_field = { version = "0.10" } lora-phy = { version = "1" } -lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] } - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } +lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 9b41ec5ab..f39f9323f 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -56,6 +56,3 @@ serde = { version = "1.0.136", default-features = false } embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } num-integer = { version = "0.1.45", default-features = false } microfft = "0.5.0" - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index c812cb3ee..8a675443a 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -54,6 +54,3 @@ rand = { version = "0.8.5", default-features = false } [profile.release] debug = true - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index e6a5a4c14..e794cf1eb 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -33,6 +33,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } embedded-hal = "0.2.6" static_cell = "1.1" - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 6e6f269aa..b3f57af53 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -27,6 +27,3 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } chrono = { version = "^0.4", default-features = false } - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } From 13acca624f4995e06cbe1606bee10015ecd31d06 Mon Sep 17 00:00:00 2001 From: ceekdee Date: Wed, 26 Jul 2023 22:23:02 -0500 Subject: [PATCH 20/41] Correct embassy-lora time feature --- embassy-lora/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index c4857ace0..a77ed003e 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -12,7 +12,7 @@ target = "thumbv7em-none-eabi" [features] stm32wl = ["dep:embassy-stm32"] -time = ["dep:embassy-time", "dep:lorawan-device"] +time = ["embassy-time", "lorawan-device"] defmt = ["dep:defmt", "lorawan-device/defmt"] [dependencies] From a6543cef16e74f8e935c92f0b4f3ef12b8f72f75 Mon Sep 17 00:00:00 2001 From: goueslati Date: Thu, 27 Jul 2023 15:00:01 +0100 Subject: [PATCH 21/41] wpan: update stm32wb-hci --- embassy-stm32-wpan/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 6cd122200..2c6089c56 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -26,7 +26,7 @@ aligned = "0.4.1" bit_field = "0.10.2" stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } -stm32wb-hci = { version = "0.1.3", optional = true } +stm32wb-hci = { version = "0.1.4", optional = true } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } bitflags = { version = "2.3.3", optional = true } From 93864610ce5d9127eb415f8020ec43c26fb20804 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Thu, 27 Jul 2023 19:04:43 +0200 Subject: [PATCH 22/41] Add DAC HIL test with ADC --- tests/stm32/Cargo.toml | 13 ++++-- tests/stm32/src/bin/dac.rs | 81 ++++++++++++++++++++++++++++++++++++++ tests/stm32/src/common.rs | 1 + 3 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 tests/stm32/src/bin/dac.rs diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 3007cd1e6..17320649e 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -7,11 +7,11 @@ autobins = false [features] stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill -stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "can", "not-gpdma"] # Nucleo "sdmmc" -stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo +stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "can", "not-gpdma", "dac-adc-pin"] # Nucleo "sdmmc" +stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma", "dac-adc-pin"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo -stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo +stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma", "dac-adc-pin"] # Nucleo stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble", "mac" ] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board @@ -23,6 +23,7 @@ ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"] mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] embassy-stm32-wpan = [] not-gpdma = [] +dac-adc-pin = [] [dependencies] teleprobe-meta = "1" @@ -42,6 +43,7 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } embedded-hal-async = { version = "=0.2.0-alpha.2" } +micromath = "2.0.0" panic-probe = { version = "0.3.0", features = ["print-defmt"] } rand_core = { version = "0.6", default-features = false } rand_chacha = { version = "0.3", default-features = false } @@ -55,6 +57,11 @@ name = "can" path = "src/bin/can.rs" required-features = [ "can",] +[[bin]] +name = "dac" +path = "src/bin/dac.rs" +required-features = [ "dac-adc-pin",] + [[bin]] name = "gpio" path = "src/bin/gpio.rs" diff --git a/tests/stm32/src/bin/dac.rs b/tests/stm32/src/bin/dac.rs new file mode 100644 index 000000000..67a7d5b59 --- /dev/null +++ b/tests/stm32/src/bin/dac.rs @@ -0,0 +1,81 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +// required-features: dac-adc-pin + +#[path = "../common.rs"] +mod common; +use common::*; +use defmt::assert; +use embassy_executor::Spawner; +use embassy_stm32::adc::Adc; +use embassy_stm32::dac::{DacCh1, DacChannel, Value}; +use embassy_stm32::dma::NoDma; +use embassy_time::{Delay, Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Initialize the board and obtain a Peripherals instance + let p: embassy_stm32::Peripherals = embassy_stm32::init(config()); + + #[cfg(feature = "stm32f429zi")] + let dac_peripheral = p.DAC; + + #[cfg(any(feature = "stm32h755zi", feature = "stm32g071rb"))] + let dac_peripheral = p.DAC1; + + let mut dac: DacCh1<'_, _, NoDma> = DacCh1::new(dac_peripheral, NoDma, p.PA4); + unwrap!(dac.set_trigger_enable(false)); + + let mut adc = Adc::new(p.ADC1, &mut Delay); + + #[cfg(feature = "stm32h755zi")] + let normalization_factor = 256; + #[cfg(any(feature = "stm32f429zi", feature = "stm32g071rb"))] + let normalization_factor: i32 = 16; + + unwrap!(dac.set(Value::Bit8(0))); + // Now wait a little to obtain a stable value + Timer::after(Duration::from_millis(30)).await; + let offset = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4); + + for v in 0..=255 { + // First set the DAC output value + let dac_output_val = to_sine_wave(v); + unwrap!(dac.set(Value::Bit8(dac_output_val))); + + // Now wait a little to obtain a stable value + Timer::after(Duration::from_millis(30)).await; + + // Need to steal the peripherals here because PA4 is obviously in use already + let measured = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4); + // Calibrate and normalize the measurement to get close to the dac_output_val + let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16; + + info!("value / measured: {} / {}", dac_output_val, measured_normalized); + + // The deviations are quite enormous but that does not matter since this is only a quick test + assert!((dac_output_val as i16 - measured_normalized).abs() < 15); + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +use core::f32::consts::PI; + +use micromath::F32Ext; + +fn to_sine_wave(v: u8) -> u8 { + if v >= 128 { + // top half + let r = PI * ((v - 128) as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } else { + // bottom half + let r = PI + PI * (v as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } +} diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 3d2a9b8ef..ca5cb43ac 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -33,6 +33,7 @@ pub fn config() -> Config { { config.rcc.sys_ck = Some(Hertz(400_000_000)); config.rcc.pll1.q_ck = Some(Hertz(100_000_000)); + config.rcc.adc_clock_source = embassy_stm32::rcc::AdcClockSource::PerCk; } #[cfg(feature = "stm32u585ai")] From e947aa01533b7fd41133678ed8444a16c9c341e0 Mon Sep 17 00:00:00 2001 From: Michael van Niekerk Date: Fri, 28 Jul 2023 11:37:38 +0200 Subject: [PATCH 23/41] Comments --- examples/rp/Cargo.toml | 1 + examples/rp/src/bin/pio_uart.rs | 345 ++++++++++++++++++++++++-------- 2 files changed, 258 insertions(+), 88 deletions(-) diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index c812cb3ee..2a018ad04 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } +embassy-hal-common = { version = "0.1.0", path = "../../embassy-hal-common", features = ["defmt"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index 14d05f4db..eeb213e1b 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -1,23 +1,35 @@ -//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. +//! This example shows how to use the PIO module in the RP2040 chip to implement a duplex UART. +//! The PIO module is a very powerful peripheral that can be used to implement many different +//! protocols. It is a very flexible state machine that can be programmed to do almost anything. //! -//! This creates a USB serial port that echos. +//! This example opens up a USB device that implements a CDC ACM serial port. It then uses the +//! PIO module to implement a UART that is connected to the USB serial port. This allows you to +//! communicate with a device connected to the RP2040 over USB serial. #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#![feature(async_fn_in_trait)] -use defmt::{info, panic}; +use defmt::{info, panic, trace}; use embassy_executor::Spawner; -use embassy_futures::join::join; +use embassy_futures::join::{join, join3}; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::{PIO0, USB}; +use embassy_rp::pio::InterruptHandler as PioInterruptHandler; use embassy_rp::usb::{Driver, Instance, InterruptHandler}; -use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::Channel; +use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; -use embassy_rp::pio::{InterruptHandler as PioInterruptHandler}; +use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; +use crate::uart::PioUart; +use crate::uart_rx::PioUartRx; +use crate::uart_tx::PioUartTx; + bind_interrupts!(struct UsbIrqs { USBCTRL_IRQ => InterruptHandler; }); @@ -69,7 +81,7 @@ async fn main(_spawner: Spawner) { ); // Create classes on the builder. - let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + let class = CdcAcmClass::new(&mut builder, &mut state, 64); // Build the builder. let mut usb = builder.build(); @@ -77,19 +89,50 @@ async fn main(_spawner: Spawner) { // Run the USB device. let usb_fut = usb.run(); - // Do stuff with the class! - let echo_fut = async { + // PIO UART setup + let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5).await; + let (mut uart_tx, mut uart_rx) = uart.split(); + + // Channels setup + static USB_CHANNEL_TX: Channel = Channel::::new(); + let mut usb_channel_tx_send = USB_CHANNEL_TX.sender(); + let mut usb_channel_tx_recv = USB_CHANNEL_TX.receiver(); + + static UART_CHANNEL_TX: Channel = Channel::::new(); + let mut uart_channel_tx_send = UART_CHANNEL_TX.sender(); + let mut uart_channel_tx_recv = UART_CHANNEL_TX.receiver(); + + let (mut usb_tx, mut usb_rx) = class.split(); + + // Read + write from USB + let usb_future = async { loop { - class.wait_connection().await; + info!("Wait for USB connection"); + usb_rx.wait_connection().await; info!("Connected"); - let _ = echo(&mut class).await; + let _ = join( + usb_read(&mut usb_rx, &mut uart_channel_tx_send), + usb_write(&mut usb_tx, &mut usb_channel_tx_recv), + ) + .await; info!("Disconnected"); } }; + // Read + write from UART + let uart_future = async { + loop { + let _ = join( + uart_read(&mut uart_rx, &mut usb_channel_tx_send), + uart_write(&mut uart_tx, &mut uart_channel_tx_recv), + ) + .await; + } + }; + // Run everything concurrently. // If we had made everything `'static` above instead, we could do this using separate tasks instead. - join(usb_fut, echo_fut).await; + join3(usb_fut, usb_future, uart_future).await; } struct Disconnected {} @@ -103,28 +146,79 @@ impl From for Disconnected { } } -async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { +/// Read from the USB and write it to the UART TX send channel +async fn usb_read<'d, T: Instance + 'd>( + usb_rx: &mut Receiver<'d, Driver<'d, T>>, + uart_tx_send: &mut embassy_sync::channel::Sender<'d, ThreadModeRawMutex, u8, 20>, +) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { - let n = class.read_packet(&mut buf).await?; + let n = usb_rx.read_packet(&mut buf).await?; let data = &buf[..n]; - info!("data: {:x}", data); - class.write_packet(data).await?; + trace!("USB IN: {:x}", data); + for byte in data { + uart_tx_send.send(*byte).await; + } + } +} + +/// Read from the USB TX receive channel and write it to the USB +async fn usb_write<'d, T: Instance + 'd>( + usb_tx: &mut Sender<'d, Driver<'d, T>>, + usb_tx_recv: &mut embassy_sync::channel::Receiver<'d, ThreadModeRawMutex, u8, 20>, +) -> Result<(), Disconnected> { + loop { + let n = usb_tx_recv.recv().await; + let data = [n]; + trace!("USB OUT: {:x}", data); + usb_tx.write_packet(&data).await?; + } +} + +/// Read from the UART and write it to the USB TX send channel +async fn uart_read<'a>( + uart_rx: &mut PioUartRx<'a>, + usb_tx_send: &mut embassy_sync::channel::Sender<'a, ThreadModeRawMutex, u8, 20>, +) -> Result<(), Disconnected> { + let mut buf = [0; 1]; + loop { + let n = uart_rx.read(&mut buf).await.expect("UART read error"); + if n == 0 { + continue; + } + trace!("UART IN: {:x}", buf); + usb_tx_send.send(buf[0]).await; + } +} + +/// Read from the UART TX receive channel and write it to the UART +async fn uart_write<'a>( + uart_tx: &mut PioUartTx<'a>, + uart_rx_recv: &mut embassy_sync::channel::Receiver<'a, ThreadModeRawMutex, u8, 20>, +) -> Result<(), Disconnected> { + loop { + let n = uart_rx_recv.recv().await; + let data = [n]; + trace!("UART OUT: {:x}", data); + let _ = uart_tx.write(&data).await; } } mod uart { - use embassy_rp::peripherals::PIO0; - use embassy_rp::pio::{Common, Pio, PioPin, StateMachine}; - use embassy_rp::Peripheral; + use core::fmt::Debug; + use embassy_rp::peripherals::PIO0; + use embassy_rp::pio::{Pio, PioPin}; + use embassy_rp::Peripheral; + use embedded_io::ErrorKind; + + use crate::uart_rx::PioUartRx; + use crate::uart_tx::PioUartTx; use crate::PioIrqs; pub struct PioUart<'a> { - baud: u64, - pio: Common<'a, PIO0>, - sm0: StateMachine<'a, PIO0, 0>, - sm1: StateMachine<'a, PIO0, 1>, + tx: PioUartTx<'a>, + rx: PioUartRx<'a>, } impl<'a> PioUart<'a> { @@ -135,21 +229,25 @@ mod uart { rx_pin: impl PioPin, ) -> PioUart<'a> { let Pio { - mut common, - mut sm0, - mut sm1, - .. + mut common, sm0, sm1, .. } = Pio::new(pio, PioIrqs); - crate::uart_tx::setup_uart_tx_on_sm0(&mut common, &mut sm0, tx_pin, baud); - crate::uart_rx::setup_uart_rx_on_sm1(&mut common, &mut sm1, rx_pin, baud); + let (tx, origin) = PioUartTx::new(&mut common, sm0, tx_pin, baud, None); + let (rx, _) = PioUartRx::new(&mut common, sm1, rx_pin, baud, Some(origin)); - PioUart { - baud, - pio: common, - sm0, - sm1, - } + PioUart { tx, rx } + } + + pub fn split(self) -> (PioUartTx<'a>, PioUartRx<'a>) { + (self.tx, self.rx) + } + } + #[derive(defmt::Format, Debug)] + pub struct PioUartError {} + + impl embedded_io::Error for PioUartError { + fn kind(&self) -> ErrorKind { + ErrorKind::Other } } } @@ -159,18 +257,27 @@ mod uart_tx { use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; + use embedded_io::asynch::Write; + use embedded_io::Io; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; - pub fn setup_uart_tx_on_sm0<'a>( - common: &mut Common<'a, PIO0>, - sm_tx: &mut StateMachine<'a, PIO0, 0>, - tx_pin: impl PioPin, - baud: u64, - ) { - let prg = pio_proc::pio_asm!( - r#" - ;.program uart_tx + use crate::uart::PioUartError; + + pub struct PioUartTx<'a> { + sm_tx: StateMachine<'a, PIO0, 0>, + } + + impl<'a> PioUartTx<'a> { + pub fn new( + common: &mut Common<'a, PIO0>, + mut sm_tx: StateMachine<'a, PIO0, 0>, + tx_pin: impl PioPin, + baud: u64, + origin: Option, + ) -> (Self, u8) { + let mut prg = pio_proc::pio_asm!( + r#" .side_set 1 opt ; An 8n1 UART transmit program. @@ -182,23 +289,55 @@ mod uart_tx { out pins, 1 ; Shift 1 bit from OSR to the first OUT pin jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. "# - ); - let tx_pin = common.make_pio_pin(tx_pin); - sm_tx.set_pins(Level::High, &[&tx_pin]); - sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); + ); + prg.program.origin = origin; + let tx_pin = common.make_pio_pin(tx_pin); + sm_tx.set_pins(Level::High, &[&tx_pin]); + sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); - let relocated = RelocatedProgram::new(&prg.program); - let mut cfg = Config::default(); + let relocated = RelocatedProgram::new(&prg.program); - cfg.use_program(&common.load_program(&relocated), &[&tx_pin]); - cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); - cfg.shift_out.auto_fill = false; - cfg.shift_out.direction = ShiftDirection::Right; - cfg.fifo_join = FifoJoin::TxOnly; - cfg.set_out_pins(&[&tx_pin]); - cfg.set_set_pins(&[&tx_pin]); - sm_tx.set_config(&cfg); - sm_tx.set_enable(true) + let mut cfg = Config::default(); + + cfg.set_out_pins(&[&tx_pin]); + cfg.use_program(&common.load_program(&relocated), &[&tx_pin]); + cfg.shift_out.auto_fill = false; + cfg.shift_out.direction = ShiftDirection::Right; + cfg.fifo_join = FifoJoin::TxOnly; + cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); + sm_tx.set_config(&cfg); + sm_tx.set_enable(true); + + // The 4 state machines of the PIO each have their own program counter that starts taking + // instructions at an offset (origin) of the 32 instruction "space" the PIO device has. + // It is up to the programmer to sort out where to place these instructions. + // From the pio_asm! macro you get a ProgramWithDefines which has a field .program.origin + // which takes an Option. + // + // When you load more than one RelocatedProgram into the PIO, + // you load your first program at origin = 0. + // The RelocatedProgram has .code().count() which returns a usize, + // for which you can then use as your next program's origin. + let offset = relocated.code().count() as u8 + origin.unwrap_or_default(); + (Self { sm_tx }, offset) + } + + pub async fn write_u8(&mut self, data: u8) { + self.sm_tx.tx().wait_push(data as u32).await; + } + } + + impl Io for PioUartTx<'_> { + type Error = PioUartError; + } + + impl Write for PioUartTx<'_> { + async fn write(&mut self, buf: &[u8]) -> Result { + for byte in buf { + self.write_u8(*byte).await; + } + Ok(buf.len()) + } } } @@ -207,19 +346,27 @@ mod uart_rx { use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; + use embedded_io::asynch::Read; + use embedded_io::Io; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; - pub fn setup_uart_rx_on_sm1<'a>( - common: &mut Common<'a, PIO0>, - sm_rx: &mut StateMachine<'a, PIO0, 1>, - rx_pin: impl PioPin, - baud: u64, - ) { - let prg = pio_proc::pio_asm!( - r#" - ;.program uart_rx + use crate::uart::PioUartError; + pub struct PioUartRx<'a> { + sm_rx: StateMachine<'a, PIO0, 1>, + } + + impl<'a> PioUartRx<'a> { + pub fn new( + common: &mut Common<'a, PIO0>, + mut sm_rx: StateMachine<'a, PIO0, 1>, + rx_pin: impl PioPin, + baud: u64, + origin: Option, + ) -> (Self, u8) { + let mut prg = pio_proc::pio_asm!( + r#" ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and ; break conditions more gracefully. ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX. @@ -227,36 +374,58 @@ mod uart_rx { start: wait 0 pin 0 ; Stall until start bit is asserted set x, 7 [10] ; Preload bit counter, then delay until halfway through - bitloop: ; the first data bit (12 cycles incl wait, set). + rx_bitloop: ; the first data bit (12 cycles incl wait, set). in pins, 1 ; Shift data bit into ISR - jmp x-- bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles - jmp pin good_stop ; Check stop bit (should be high) + jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles + jmp pin good_rx_stop ; Check stop bit (should be high) irq 4 rel ; Either a framing error or a break. Set a sticky flag, wait 1 pin 0 ; and wait for line to return to idle state. jmp start ; Don't push data if we didn't see good framing. - good_stop: ; No delay before returning to start; a little slack is + good_rx_stop: ; No delay before returning to start; a little slack is push ; important in case the TX clock is slightly too fast. "# - ); + ); + prg.program.origin = origin; + let relocated = RelocatedProgram::new(&prg.program); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[]); - let rx_pin = common.make_pio_pin(rx_pin); - sm_rx.set_pins(Level::High, &[&rx_pin]); - sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]); + let rx_pin = common.make_pio_pin(rx_pin); + sm_rx.set_pins(Level::High, &[&rx_pin]); + cfg.set_in_pins(&[&rx_pin]); + cfg.set_jmp_pin(&rx_pin); + sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]); - let relocated = RelocatedProgram::new(&prg.program); - let mut cfg = Config::default(); + cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); + cfg.shift_out.auto_fill = false; + cfg.shift_out.direction = ShiftDirection::Right; + cfg.fifo_join = FifoJoin::RxOnly; + sm_rx.set_config(&cfg); + sm_rx.set_enable(true); - cfg.use_program(&common.load_program(&relocated), &[&rx_pin]); - cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); - cfg.shift_out.auto_fill = false; - cfg.shift_out.direction = ShiftDirection::Right; - cfg.fifo_join = FifoJoin::RxOnly; - cfg.set_in_pins(&[&rx_pin]); - cfg.set_jmp_pin(&rx_pin); - // cfg.set_set_pins(&[&rx_pin]); - sm_rx.set_config(&cfg); - sm_rx.set_enable(true) + let offset = relocated.code().count() as u8 + origin.unwrap_or_default(); + (Self { sm_rx }, offset) + } + + pub async fn read_u8(&mut self) -> u8 { + self.sm_rx.rx().wait_pull().await as u8 + } + } + + impl Io for PioUartRx<'_> { + type Error = PioUartError; + } + + impl Read for PioUartRx<'_> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + let mut i = 0; + while i < buf.len() { + buf[i] = self.read_u8().await; + i += 1; + } + Ok(i) + } } } From 0f1ff77fcc3c085f9969bac4963d784c022e6044 Mon Sep 17 00:00:00 2001 From: Michael van Niekerk Date: Fri, 28 Jul 2023 11:38:08 +0200 Subject: [PATCH 24/41] Comments --- examples/rp/.idea/.gitignore | 8 -------- examples/rp/.idea/modules.xml | 8 -------- examples/rp/.idea/rp.iml | 12 ------------ examples/rp/.idea/vcs.xml | 6 ------ 4 files changed, 34 deletions(-) delete mode 100644 examples/rp/.idea/.gitignore delete mode 100644 examples/rp/.idea/modules.xml delete mode 100644 examples/rp/.idea/rp.iml delete mode 100644 examples/rp/.idea/vcs.xml diff --git a/examples/rp/.idea/.gitignore b/examples/rp/.idea/.gitignore deleted file mode 100644 index 13566b81b..000000000 --- a/examples/rp/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/examples/rp/.idea/modules.xml b/examples/rp/.idea/modules.xml deleted file mode 100644 index 06ff4b23d..000000000 --- a/examples/rp/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/examples/rp/.idea/rp.iml b/examples/rp/.idea/rp.iml deleted file mode 100644 index 9b4cf845b..000000000 --- a/examples/rp/.idea/rp.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/examples/rp/.idea/vcs.xml b/examples/rp/.idea/vcs.xml deleted file mode 100644 index b2bdec2d7..000000000 --- a/examples/rp/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 91338adc159dd026ba56dcb4e991ed9f60053bb0 Mon Sep 17 00:00:00 2001 From: Michael van Niekerk Date: Fri, 28 Jul 2023 11:56:59 +0200 Subject: [PATCH 25/41] Don't include embedded-hal-common --- examples/rp/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 2a018ad04..c812cb3ee 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -7,7 +7,6 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } -embassy-hal-common = { version = "0.1.0", path = "../../embassy-hal-common", features = ["defmt"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } From 146c744223056561c6be61dda791993d939d0ae0 Mon Sep 17 00:00:00 2001 From: Michael van Niekerk Date: Fri, 28 Jul 2023 12:56:31 +0200 Subject: [PATCH 26/41] Fixes as per PR --- examples/rp/src/bin/pio_uart.rs | 114 ++++++++++++++------------------ 1 file changed, 48 insertions(+), 66 deletions(-) diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index eeb213e1b..c978f8f06 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -19,7 +19,7 @@ use embassy_rp::peripherals::{PIO0, USB}; use embassy_rp::pio::InterruptHandler as PioInterruptHandler; use embassy_rp::usb::{Driver, Instance, InterruptHandler}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; -use embassy_sync::channel::Channel; +use embassy_sync::pipe::Pipe; use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; @@ -30,11 +30,8 @@ use crate::uart::PioUart; use crate::uart_rx::PioUartRx; use crate::uart_tx::PioUartTx; -bind_interrupts!(struct UsbIrqs { +bind_interrupts!(struct Irqs { USBCTRL_IRQ => InterruptHandler; -}); - -bind_interrupts!(struct PioIrqs { PIO0_IRQ_0 => PioInterruptHandler; }); @@ -45,7 +42,7 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); // Create the driver, from the HAL. - let driver = Driver::new(p.USB, UsbIrqs); + let driver = Driver::new(p.USB, Irqs); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -90,17 +87,17 @@ async fn main(_spawner: Spawner) { let usb_fut = usb.run(); // PIO UART setup - let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5).await; + let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5); let (mut uart_tx, mut uart_rx) = uart.split(); - // Channels setup - static USB_CHANNEL_TX: Channel = Channel::::new(); - let mut usb_channel_tx_send = USB_CHANNEL_TX.sender(); - let mut usb_channel_tx_recv = USB_CHANNEL_TX.receiver(); + // Pipe setup + static USB_PIPE: Pipe = Pipe::new(); + let mut usb_pipe_writer = USB_PIPE.writer(); + let mut usb_pipe_reader = USB_PIPE.reader(); - static UART_CHANNEL_TX: Channel = Channel::::new(); - let mut uart_channel_tx_send = UART_CHANNEL_TX.sender(); - let mut uart_channel_tx_recv = UART_CHANNEL_TX.receiver(); + static UART_PIPE: Pipe = Pipe::new(); + let mut uart_pipe_writer = UART_PIPE.writer(); + let mut uart_pipe_reader = UART_PIPE.reader(); let (mut usb_tx, mut usb_rx) = class.split(); @@ -111,8 +108,8 @@ async fn main(_spawner: Spawner) { usb_rx.wait_connection().await; info!("Connected"); let _ = join( - usb_read(&mut usb_rx, &mut uart_channel_tx_send), - usb_write(&mut usb_tx, &mut usb_channel_tx_recv), + usb_read(&mut usb_rx, &mut uart_pipe_writer), + usb_write(&mut usb_tx, &mut usb_pipe_reader), ) .await; info!("Disconnected"); @@ -120,15 +117,10 @@ async fn main(_spawner: Spawner) { }; // Read + write from UART - let uart_future = async { - loop { - let _ = join( - uart_read(&mut uart_rx, &mut usb_channel_tx_send), - uart_write(&mut uart_tx, &mut uart_channel_tx_recv), - ) - .await; - } - }; + let uart_future = join( + uart_read(&mut uart_rx, &mut usb_pipe_writer), + uart_write(&mut uart_tx, &mut uart_pipe_reader), + ); // Run everything concurrently. // If we had made everything `'static` above instead, we could do this using separate tasks instead. @@ -146,75 +138,73 @@ impl From for Disconnected { } } -/// Read from the USB and write it to the UART TX send channel +/// Read from the USB and write it to the UART TX pipe async fn usb_read<'d, T: Instance + 'd>( usb_rx: &mut Receiver<'d, Driver<'d, T>>, - uart_tx_send: &mut embassy_sync::channel::Sender<'d, ThreadModeRawMutex, u8, 20>, + uart_pipe_writer: &mut embassy_sync::pipe::Writer<'static, ThreadModeRawMutex, 20>, ) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { let n = usb_rx.read_packet(&mut buf).await?; let data = &buf[..n]; trace!("USB IN: {:x}", data); - for byte in data { - uart_tx_send.send(*byte).await; - } + uart_pipe_writer.write(data).await; } } -/// Read from the USB TX receive channel and write it to the USB +/// Read from the USB TX pipe and write it to the USB async fn usb_write<'d, T: Instance + 'd>( usb_tx: &mut Sender<'d, Driver<'d, T>>, - usb_tx_recv: &mut embassy_sync::channel::Receiver<'d, ThreadModeRawMutex, u8, 20>, + usb_pipe_reader: &mut embassy_sync::pipe::Reader<'d, ThreadModeRawMutex, 20>, ) -> Result<(), Disconnected> { + let mut buf = [0; 64]; loop { - let n = usb_tx_recv.recv().await; - let data = [n]; + let n = usb_pipe_reader.read(&mut buf).await; + let data = &buf[..n]; trace!("USB OUT: {:x}", data); usb_tx.write_packet(&data).await?; } } -/// Read from the UART and write it to the USB TX send channel +/// Read from the UART and write it to the USB TX pipe async fn uart_read<'a>( uart_rx: &mut PioUartRx<'a>, - usb_tx_send: &mut embassy_sync::channel::Sender<'a, ThreadModeRawMutex, u8, 20>, -) -> Result<(), Disconnected> { - let mut buf = [0; 1]; + usb_pipe_writer: &mut embassy_sync::pipe::Writer<'static, ThreadModeRawMutex, 20>, +) -> ! { + let mut buf = [0; 64]; loop { let n = uart_rx.read(&mut buf).await.expect("UART read error"); if n == 0 { continue; } + let data = &buf[..n]; trace!("UART IN: {:x}", buf); - usb_tx_send.send(buf[0]).await; + usb_pipe_writer.write(data).await; } } -/// Read from the UART TX receive channel and write it to the UART +/// Read from the UART TX pipe and write it to the UART async fn uart_write<'a>( uart_tx: &mut PioUartTx<'a>, - uart_rx_recv: &mut embassy_sync::channel::Receiver<'a, ThreadModeRawMutex, u8, 20>, -) -> Result<(), Disconnected> { + uart_pipe_reader: &mut embassy_sync::pipe::Reader<'a, ThreadModeRawMutex, 20>, +) -> ! { + let mut buf = [0; 64]; loop { - let n = uart_rx_recv.recv().await; - let data = [n]; + let n = uart_pipe_reader.read(&mut buf).await; + let data = &buf[..n]; trace!("UART OUT: {:x}", data); let _ = uart_tx.write(&data).await; } } mod uart { - use core::fmt::Debug; - use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Pio, PioPin}; use embassy_rp::Peripheral; - use embedded_io::ErrorKind; use crate::uart_rx::PioUartRx; use crate::uart_tx::PioUartTx; - use crate::PioIrqs; + use crate::Irqs; pub struct PioUart<'a> { tx: PioUartTx<'a>, @@ -222,7 +212,7 @@ mod uart { } impl<'a> PioUart<'a> { - pub async fn new( + pub fn new( baud: u64, pio: impl Peripheral

+ 'a, tx_pin: impl PioPin, @@ -230,7 +220,7 @@ mod uart { ) -> PioUart<'a> { let Pio { mut common, sm0, sm1, .. - } = Pio::new(pio, PioIrqs); + } = Pio::new(pio, Irqs); let (tx, origin) = PioUartTx::new(&mut common, sm0, tx_pin, baud, None); let (rx, _) = PioUartRx::new(&mut common, sm1, rx_pin, baud, Some(origin)); @@ -242,17 +232,11 @@ mod uart { (self.tx, self.rx) } } - #[derive(defmt::Format, Debug)] - pub struct PioUartError {} - - impl embedded_io::Error for PioUartError { - fn kind(&self) -> ErrorKind { - ErrorKind::Other - } - } } mod uart_tx { + use core::convert::Infallible; + use embassy_rp::gpio::Level; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; @@ -262,8 +246,6 @@ mod uart_tx { use fixed::traits::ToFixed; use fixed_macro::types::U56F8; - use crate::uart::PioUartError; - pub struct PioUartTx<'a> { sm_tx: StateMachine<'a, PIO0, 0>, } @@ -328,11 +310,11 @@ mod uart_tx { } impl Io for PioUartTx<'_> { - type Error = PioUartError; + type Error = Infallible; } impl Write for PioUartTx<'_> { - async fn write(&mut self, buf: &[u8]) -> Result { + async fn write(&mut self, buf: &[u8]) -> Result { for byte in buf { self.write_u8(*byte).await; } @@ -342,6 +324,8 @@ mod uart_tx { } mod uart_rx { + use core::convert::Infallible; + use embassy_rp::gpio::Level; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; @@ -351,8 +335,6 @@ mod uart_rx { use fixed::traits::ToFixed; use fixed_macro::types::U56F8; - use crate::uart::PioUartError; - pub struct PioUartRx<'a> { sm_rx: StateMachine<'a, PIO0, 1>, } @@ -415,11 +397,11 @@ mod uart_rx { } impl Io for PioUartRx<'_> { - type Error = PioUartError; + type Error = Infallible; } impl Read for PioUartRx<'_> { - async fn read(&mut self, buf: &mut [u8]) -> Result { + async fn read(&mut self, buf: &mut [u8]) -> Result { let mut i = 0; while i < buf.len() { buf[i] = self.read_u8().await; From 38b5d1ee2b1319a6f84c8894f05c650bb3630ece Mon Sep 17 00:00:00 2001 From: chemicstry Date: Fri, 28 Jul 2023 14:22:24 +0300 Subject: [PATCH 27/41] stm32/can: implement more convenience methods --- embassy-stm32/src/can/bxcan.rs | 160 ++++++++++++++++++++++++--------- 1 file changed, 119 insertions(+), 41 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 8b8244d4f..d4ec23816 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -77,6 +77,7 @@ pub struct Can<'d, T: Instance> { } #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum BusError { Stuff, Form, @@ -90,6 +91,22 @@ pub enum BusError { BusWarning, } +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TryReadError { + /// Bus error + BusError(BusError), + /// Receive buffer is empty + Empty, +} + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TryWriteError { + /// All transmit mailboxes are full + Full, +} + impl<'d, T: Instance> Can<'d, T> { /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. /// You must call [Can::enable_non_blocking] to use the peripheral. @@ -175,56 +192,46 @@ impl<'d, T: Instance> Can<'d, T> { /// Queues the message to be sent but exerts backpressure pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { - poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); - if let Ok(status) = self.can.borrow_mut().transmit(frame) { - return Poll::Ready(status); - } - - Poll::Pending - }) - .await + CanTx { can: &self.can }.write(frame).await } - pub async fn flush(&self, mb: bxcan::Mailbox) { - poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); - if T::regs().tsr().read().tme(mb.index()) { - return Poll::Ready(()); - } + /// Attempts to transmit a frame without blocking. + /// + /// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full. + pub fn try_write(&mut self, frame: &Frame) -> Result { + CanTx { can: &self.can }.try_write(frame) + } - Poll::Pending - }) - .await; + /// Waits for a specific transmit mailbox to become empty + pub async fn flush(&self, mb: bxcan::Mailbox) { + CanTx { can: &self.can }.flush(mb).await + } + + /// Waits until any of the transmit mailboxes become empty + pub async fn flush_any(&self) { + CanTx { can: &self.can }.flush_any().await + } + + /// Waits until all of the transmit mailboxes become empty + pub async fn flush_all(&self) { + CanTx { can: &self.can }.flush_all().await } /// Returns a tuple of the time the message was received and the message frame pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> { - poll_fn(|cx| { - T::state().err_waker.register(cx.waker()); - if let Poll::Ready((time, frame)) = T::state().rx_queue.recv().poll_unpin(cx) { - return Poll::Ready(Ok((time, frame))); - } else if let Some(err) = self.curr_error() { - return Poll::Ready(Err(err)); - } - - Poll::Pending - }) - .await + CanRx { can: &self.can }.read().await } - fn curr_error(&self) -> Option { - let err = { T::regs().esr().read() }; - if err.boff() { - return Some(BusError::BusOff); - } else if err.epvf() { - return Some(BusError::BusPassive); - } else if err.ewgf() { - return Some(BusError::BusWarning); - } else if let Some(err) = err.lec().into_bus_err() { - return Some(err); - } - None + /// Attempts to read a can frame without blocking. + /// + /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. + pub fn try_read(&mut self) -> Result<(u16, bxcan::Frame), TryReadError> { + CanRx { can: &self.can }.try_read() + } + + /// Waits while receive queue is empty. + pub async fn wait_not_empty(&mut self) { + CanRx { can: &self.can }.wait_not_empty().await } unsafe fn receive_fifo(fifo: RxFifo) { @@ -386,6 +393,14 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { .await } + /// Attempts to transmit a frame without blocking. + /// + /// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full. + pub fn try_write(&mut self, frame: &Frame) -> Result { + self.can.borrow_mut().transmit(frame).map_err(|_| TryWriteError::Full) + } + + /// Waits for a specific transmit mailbox to become empty pub async fn flush(&self, mb: bxcan::Mailbox) { poll_fn(|cx| { T::state().tx_waker.register(cx.waker()); @@ -397,6 +412,42 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { }) .await; } + + /// Waits until any of the transmit mailboxes become empty + pub async fn flush_any(&self) { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + + let tsr = T::regs().tsr().read(); + if tsr.tme(bxcan::Mailbox::Mailbox0.index()) + || tsr.tme(bxcan::Mailbox::Mailbox1.index()) + || tsr.tme(bxcan::Mailbox::Mailbox2.index()) + { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + } + + /// Waits until all of the transmit mailboxes become empty + pub async fn flush_all(&self) { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + + let tsr = T::regs().tsr().read(); + if tsr.tme(bxcan::Mailbox::Mailbox0.index()) + && tsr.tme(bxcan::Mailbox::Mailbox1.index()) + && tsr.tme(bxcan::Mailbox::Mailbox2.index()) + { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + } } #[allow(dead_code)] @@ -419,6 +470,33 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { .await } + /// Attempts to read a CAN frame without blocking. + /// + /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. + pub fn try_read(&mut self) -> Result<(u16, bxcan::Frame), TryReadError> { + if let Ok(envelope) = T::state().rx_queue.try_recv() { + return Ok(envelope); + } + + if let Some(err) = self.curr_error() { + return Err(TryReadError::BusError(err)); + } + + Err(TryReadError::Empty) + } + + /// Waits while receive queue is empty. + pub async fn wait_not_empty(&mut self) { + poll_fn(|cx| { + if T::state().rx_queue.poll_ready_to_receive(cx) { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await + } + fn curr_error(&self) -> Option { let err = { T::regs().esr().read() }; if err.boff() { From 036e6ae30c9e772ef8ef20439f121e108b9106f0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 Jul 2023 13:23:22 +0200 Subject: [PATCH 28/41] Rename embassy-hal-common to embassy-hal-internal, document it's for internal use only. (#1700) --- .github/ci/test.sh | 2 +- .../Cargo.toml | 2 +- embassy-hal-internal/README.md | 16 ++++++++++++++++ .../build.rs | 0 .../src/atomic_ring_buffer.rs | 0 .../src/drop.rs | 0 .../src/fmt.rs | 0 .../src/interrupt.rs | 0 .../src/lib.rs | 1 + .../src/macros.rs | 0 .../src/peripheral.rs | 0 .../src/ratio.rs | 0 .../src/ring_buffer.rs | 0 embassy-lora/Cargo.toml | 1 + embassy-nrf/Cargo.toml | 2 +- embassy-nrf/src/buffered_uarte.rs | 4 ++-- embassy-nrf/src/chips/nrf52805.rs | 4 ++-- embassy-nrf/src/chips/nrf52810.rs | 4 ++-- embassy-nrf/src/chips/nrf52811.rs | 4 ++-- embassy-nrf/src/chips/nrf52820.rs | 4 ++-- embassy-nrf/src/chips/nrf52832.rs | 4 ++-- embassy-nrf/src/chips/nrf52833.rs | 4 ++-- embassy-nrf/src/chips/nrf52840.rs | 4 ++-- embassy-nrf/src/chips/nrf5340_app.rs | 4 ++-- embassy-nrf/src/chips/nrf5340_net.rs | 4 ++-- embassy-nrf/src/chips/nrf9160.rs | 4 ++-- embassy-nrf/src/gpio.rs | 2 +- embassy-nrf/src/gpiote.rs | 2 +- embassy-nrf/src/i2s.rs | 4 ++-- embassy-nrf/src/lib.rs | 4 ++-- embassy-nrf/src/nvmc.rs | 2 +- embassy-nrf/src/pdm.rs | 4 ++-- embassy-nrf/src/ppi/dppi.rs | 2 +- embassy-nrf/src/ppi/mod.rs | 2 +- embassy-nrf/src/ppi/ppi.rs | 2 +- embassy-nrf/src/pwm.rs | 2 +- embassy-nrf/src/qdec.rs | 2 +- embassy-nrf/src/qspi.rs | 4 ++-- embassy-nrf/src/rng.rs | 4 ++-- embassy-nrf/src/saadc.rs | 4 ++-- embassy-nrf/src/spim.rs | 2 +- embassy-nrf/src/spis.rs | 2 +- embassy-nrf/src/temp.rs | 4 ++-- embassy-nrf/src/timer.rs | 2 +- embassy-nrf/src/twim.rs | 2 +- embassy-nrf/src/twis.rs | 2 +- embassy-nrf/src/uarte.rs | 4 ++-- embassy-nrf/src/usb/mod.rs | 2 +- embassy-rp/Cargo.toml | 4 ++-- embassy-rp/src/adc.rs | 2 +- embassy-rp/src/clocks.rs | 2 +- embassy-rp/src/dma.rs | 2 +- embassy-rp/src/flash.rs | 2 +- embassy-rp/src/gpio.rs | 2 +- embassy-rp/src/i2c.rs | 2 +- embassy-rp/src/lib.rs | 8 ++++---- embassy-rp/src/pio.rs | 2 +- embassy-rp/src/pwm.rs | 2 +- embassy-rp/src/rtc/mod.rs | 2 +- embassy-rp/src/spi.rs | 2 +- embassy-rp/src/uart/buffered.rs | 2 +- embassy-rp/src/uart/mod.rs | 2 +- embassy-stm32-wpan/Cargo.toml | 4 ++-- embassy-stm32-wpan/src/lib.rs | 2 +- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/build.rs | 10 +++++----- embassy-stm32/src/adc/f1.rs | 2 +- embassy-stm32/src/adc/v1.rs | 2 +- embassy-stm32/src/adc/v2.rs | 2 +- embassy-stm32/src/adc/v3.rs | 2 +- embassy-stm32/src/adc/v4.rs | 2 +- embassy-stm32/src/can/bxcan.rs | 2 +- embassy-stm32/src/can/fdcan.rs | 2 +- embassy-stm32/src/crc/v1.rs | 2 +- embassy-stm32/src/crc/v2v3.rs | 2 +- embassy-stm32/src/dac/mod.rs | 2 +- embassy-stm32/src/dcmi.rs | 2 +- embassy-stm32/src/dma/bdma.rs | 2 +- embassy-stm32/src/dma/dma.rs | 2 +- embassy-stm32/src/dma/gpdma.rs | 2 +- embassy-stm32/src/dma/mod.rs | 2 +- embassy-stm32/src/eth/v1/mod.rs | 2 +- embassy-stm32/src/eth/v2/mod.rs | 2 +- embassy-stm32/src/exti.rs | 2 +- embassy-stm32/src/flash/asynch.rs | 4 ++-- embassy-stm32/src/flash/common.rs | 4 ++-- embassy-stm32/src/flash/f4.rs | 2 +- embassy-stm32/src/fmc.rs | 2 +- embassy-stm32/src/gpio.rs | 2 +- embassy-stm32/src/i2c/v1.rs | 2 +- embassy-stm32/src/i2c/v2.rs | 4 ++-- embassy-stm32/src/i2s.rs | 2 +- embassy-stm32/src/lib.rs | 4 ++-- embassy-stm32/src/pwm/complementary_pwm.rs | 2 +- embassy-stm32/src/pwm/simple_pwm.rs | 2 +- embassy-stm32/src/qspi/mod.rs | 2 +- embassy-stm32/src/rcc/f4.rs | 2 +- embassy-stm32/src/rcc/h7.rs | 2 +- embassy-stm32/src/rcc/l4.rs | 2 +- embassy-stm32/src/rng.rs | 2 +- embassy-stm32/src/rtc/mod.rs | 2 +- embassy-stm32/src/sdmmc/mod.rs | 4 ++-- embassy-stm32/src/spi/mod.rs | 2 +- embassy-stm32/src/usart/buffered.rs | 2 +- embassy-stm32/src/usart/mod.rs | 4 ++-- embassy-stm32/src/usart/ringbuffered.rs | 2 +- embassy-stm32/src/usb/usb.rs | 2 +- embassy-stm32/src/usb_otg/usb.rs | 2 +- embassy-stm32/src/wdg/mod.rs | 2 +- examples/stm32g4/Cargo.toml | 1 - 110 files changed, 150 insertions(+), 133 deletions(-) rename {embassy-hal-common => embassy-hal-internal}/Cargo.toml (95%) create mode 100644 embassy-hal-internal/README.md rename {embassy-hal-common => embassy-hal-internal}/build.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/atomic_ring_buffer.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/drop.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/fmt.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/interrupt.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/lib.rs (89%) rename {embassy-hal-common => embassy-hal-internal}/src/macros.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/peripheral.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/ratio.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/ring_buffer.rs (100%) diff --git a/.github/ci/test.sh b/.github/ci/test.sh index d014e4bd7..2892bcf8d 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh @@ -13,7 +13,7 @@ hashtime save /ci/cache/filetime.json cargo test --manifest-path ./embassy-sync/Cargo.toml cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml -cargo test --manifest-path ./embassy-hal-common/Cargo.toml +cargo test --manifest-path ./embassy-hal-internal/Cargo.toml cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue cargo test --manifest-path ./embassy-boot/boot/Cargo.toml diff --git a/embassy-hal-common/Cargo.toml b/embassy-hal-internal/Cargo.toml similarity index 95% rename from embassy-hal-common/Cargo.toml rename to embassy-hal-internal/Cargo.toml index 18c758d7b..42e03199c 100644 --- a/embassy-hal-common/Cargo.toml +++ b/embassy-hal-internal/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "embassy-hal-common" +name = "embassy-hal-internal" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/embassy-hal-internal/README.md b/embassy-hal-internal/README.md new file mode 100644 index 000000000..d6539701b --- /dev/null +++ b/embassy-hal-internal/README.md @@ -0,0 +1,16 @@ +# embassy-macros + +An [Embassy](https://embassy.dev) project. + +Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY. Embassy HALs (`embassy-nrf`, `embassy-stm32`, `embassy-rp`) already reexport +everything you need to use them effectively. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/embassy-hal-common/build.rs b/embassy-hal-internal/build.rs similarity index 100% rename from embassy-hal-common/build.rs rename to embassy-hal-internal/build.rs diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-internal/src/atomic_ring_buffer.rs similarity index 100% rename from embassy-hal-common/src/atomic_ring_buffer.rs rename to embassy-hal-internal/src/atomic_ring_buffer.rs diff --git a/embassy-hal-common/src/drop.rs b/embassy-hal-internal/src/drop.rs similarity index 100% rename from embassy-hal-common/src/drop.rs rename to embassy-hal-internal/src/drop.rs diff --git a/embassy-hal-common/src/fmt.rs b/embassy-hal-internal/src/fmt.rs similarity index 100% rename from embassy-hal-common/src/fmt.rs rename to embassy-hal-internal/src/fmt.rs diff --git a/embassy-hal-common/src/interrupt.rs b/embassy-hal-internal/src/interrupt.rs similarity index 100% rename from embassy-hal-common/src/interrupt.rs rename to embassy-hal-internal/src/interrupt.rs diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-internal/src/lib.rs similarity index 89% rename from embassy-hal-common/src/lib.rs rename to embassy-hal-internal/src/lib.rs index 235964aa4..3640ea184 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-internal/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![allow(clippy::new_without_default)] +#![doc = include_str!("../README.md")] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-hal-common/src/macros.rs b/embassy-hal-internal/src/macros.rs similarity index 100% rename from embassy-hal-common/src/macros.rs rename to embassy-hal-internal/src/macros.rs diff --git a/embassy-hal-common/src/peripheral.rs b/embassy-hal-internal/src/peripheral.rs similarity index 100% rename from embassy-hal-common/src/peripheral.rs rename to embassy-hal-internal/src/peripheral.rs diff --git a/embassy-hal-common/src/ratio.rs b/embassy-hal-internal/src/ratio.rs similarity index 100% rename from embassy-hal-common/src/ratio.rs rename to embassy-hal-internal/src/ratio.rs diff --git a/embassy-hal-common/src/ring_buffer.rs b/embassy-hal-internal/src/ring_buffer.rs similarity index 100% rename from embassy-hal-common/src/ring_buffer.rs rename to embassy-hal-internal/src/ring_buffer.rs diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index a77ed003e..402ad2d70 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -26,5 +26,6 @@ embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features embedded-hal-async = { version = "=0.2.0-alpha.2" } embedded-hal = { version = "0.2", features = ["unproven"] } +futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } lora-phy = { version = "1" } lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 57dd22f1c..d10cd2c34 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -93,7 +93,7 @@ _gpio-p1 = [] [dependencies] embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-3"] } +embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 9bc1c1e7a..5a0a3c7c0 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -15,8 +15,8 @@ use core::slice; use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; use core::task::Poll; -use embassy_hal_common::atomic_ring_buffer::RingBuffer; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; // 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}; diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 8776000c8..70e4b4863 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024; pub const RESET_PIN: u32 = 21; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -208,7 +208,7 @@ impl_ppi_channel!(PPI_CH31, 31 => static); impl_saadc_input!(P0_04, ANALOG_INPUT2); impl_saadc_input!(P0_05, ANALOG_INPUT3); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 5519e8953..7416d3912 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024; pub const RESET_PIN: u32 = 21; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -234,7 +234,7 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index d5367c59a..588010685 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024; pub const RESET_PIN: u32 = 21; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -236,7 +236,7 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 785170447..0ecddaf31 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 256 * 1024; pub const RESET_PIN: u32 = 18; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // USB USBD, @@ -224,7 +224,7 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index b77564a5c..ae39628d2 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -12,7 +12,7 @@ pub const FLASH_SIZE: usize = 512 * 1024; pub const RESET_PIN: u32 = 21; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -263,7 +263,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index bff7f4ebb..b8830b338 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 512 * 1024; pub const RESET_PIN: u32 = 18; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // USB USBD, @@ -306,7 +306,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 9b0050823..a490cb079 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 1024 * 1024; pub const RESET_PIN: u32 = 18; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // USB USBD, @@ -311,7 +311,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 410ae921c..afc2c4a7e 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -218,7 +218,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; pub const FLASH_SIZE: usize = 1024 * 1024; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // USB USBD, @@ -506,7 +506,7 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5); impl_saadc_input!(P0_19, ANALOG_INPUT6); impl_saadc_input!(P0_20, ANALOG_INPUT7); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( FPU, CACHE, SPU, diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 6ac783085..dee666a61 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -109,7 +109,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; pub const FLASH_SIZE: usize = 256 * 1024; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -342,7 +342,7 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); impl_ppi_channel!(PPI_CH30, 30 => configurable); impl_ppi_channel!(PPI_CH31, 31 => configurable); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( CLOCK_POWER, RADIO, RNG, diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 67ea032ff..495285ba3 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -169,7 +169,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; pub const FLASH_SIZE: usize = 1024 * 1024; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -368,7 +368,7 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5); impl_saadc_input!(P0_19, ANALOG_INPUT6); impl_saadc_input!(P0_20, ANALOG_INPUT7); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( SPU, CLOCK_POWER, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0, diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 895ab9340..ea2b76096 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -5,7 +5,7 @@ use core::convert::Infallible; use core::hint::unreachable_unchecked; use cfg_if::cfg_if; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use self::sealed::Pin as _; use crate::pac::p0 as gpio; diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 6550f2abd..7488bc085 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -4,7 +4,7 @@ use core::convert::Infallible; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; -use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as _; diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index fea38c4c0..907acdf4c 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -9,8 +9,8 @@ use core::ops::{Deref, DerefMut}; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::typelevel::Interrupt; diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index d23759f9d..355a00497 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -98,7 +98,7 @@ mod chip; /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. -// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`. +// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { @@ -127,7 +127,7 @@ pub use chip::pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use chip::pac; pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE}; -pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; pub use crate::chip::interrupt; pub use crate::pac::NVIC_PRIO_BITS; diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index 91a5a272f..de840b886 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs @@ -2,7 +2,7 @@ use core::{ptr, slice}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{ ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, }; diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 217884d1c..01f41e9f9 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -6,8 +6,8 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use fixed::types::I7F1; use futures::future::poll_fn; diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index 40ccb2f09..0bc7f821e 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use super::{Channel, ConfigurableChannel, Event, Ppi, Task}; use crate::{pac, Peripheral}; diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index ff6593bd5..5b4a64388 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -18,7 +18,7 @@ use core::marker::PhantomData; use core::ptr::NonNull; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use crate::{peripherals, Peripheral}; diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index 1fe898625..3e9e9fc81 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task}; use crate::{pac, Peripheral}; diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index c8c81fa01..2f0397632 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -4,7 +4,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin, PselBits}; diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 8bac87d37..2aa50a2ba 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -6,7 +6,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index baefc7967..36ee33f6d 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -7,8 +7,8 @@ use core::marker::PhantomData; use core::ptr; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use crate::gpio::{self, Pin as GpioPin}; diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index 923b8b467..e2803f0d3 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -8,8 +8,8 @@ use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::typelevel::Interrupt; diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 23292924c..662b05614 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -6,8 +6,8 @@ use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::{saadc, SAADC}; use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A}; diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index b7dc332e9..4673a0175 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -8,7 +8,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; pub use pac::spim0::frequency::FREQUENCY_A as Frequency; diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index aa438415a..212825121 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -7,7 +7,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::{into_ref, PeripheralRef}; +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::chip::FORCE_COPY_BUFFER_SIZE; diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index 491e92c04..cec46d8d0 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -3,8 +3,8 @@ use core::future::poll_fn; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::I30F2; diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 04748238d..3dbfdac42 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -6,7 +6,7 @@ #![macro_use] -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::ppi::{Event, Task}; use crate::{pac, Peripheral}; diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 2ad0d19b1..fdea480e3 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -9,7 +9,7 @@ use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index a115d5616..c6c020557 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -8,7 +8,7 @@ use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 48d57fea4..e79df3561 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -18,8 +18,8 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; 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}; diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs index 76cf40ac7..e26b49db3 100644 --- a/embassy-nrf/src/usb/mod.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -11,7 +11,7 @@ use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; use core::task::Poll; use cortex_m::peripheral::NVIC; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver as driver; use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported}; diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 8f3ed885d..b53c7a01a 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -16,7 +16,7 @@ flavors = [ default = [ "rt" ] rt = [ "rp-pac/rt" ] -defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-common/defmt"] +defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-internal/defmt"] # critical section that is safe for multicore use critical-section-impl = ["critical-section/restore-state-u8"] @@ -58,7 +58,7 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"] embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-time = { version = "0.1.2", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-2"] } +embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } atomic-polyfill = "1.0.1" diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 95780c068..4fba31169 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as GpioPin; diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index acb21dce5..976d06de7 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use pac::clocks::vals::*; use crate::gpio::sealed::Pin; diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 1a458778c..c8f741804 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -4,7 +4,7 @@ use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; -use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::dma::vals::DataSize; diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 96d2d4541..0ed6808eb 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::Peripheral; +use embassy_hal_internal::Peripheral; use embedded_storage::nor_flash::{ check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index a3d330cdc..2807eb678 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -3,7 +3,7 @@ use core::future::Future; use core::pin::Pin as FuturePin; use core::task::{Context, Poll}; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::InterruptExt; diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 9b85b2345..536ad747d 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -2,7 +2,7 @@ use core::future; use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::i2c; diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 50f028d4c..ebec9fec6 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -36,7 +36,7 @@ pub mod pio_instr_util; pub mod relocate; // Reexports -pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] pub use rp_pac as pac; #[cfg(not(feature = "unstable-pac"))] @@ -45,7 +45,7 @@ pub(crate) use rp_pac as pac; #[cfg(feature = "rt")] pub use crate::pac::NVIC_PRIO_BITS; -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( TIMER_IRQ_0, TIMER_IRQ_1, TIMER_IRQ_2, @@ -85,7 +85,7 @@ embassy_hal_common::interrupt_mod!( /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. -// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`. +// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { @@ -107,7 +107,7 @@ macro_rules! bind_interrupts { }; } -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { PIN_0, PIN_1, PIN_2, diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 72a2f44ed..464988b27 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -5,7 +5,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, AtomicU8}; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::extra::U8; use fixed::FixedU32; diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 20bb88446..c0ddb2a90 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -1,6 +1,6 @@ //! Pulse Width Modulation (PWM) -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use fixed::traits::ToFixed; use fixed::FixedU16; use pac::pwm::regs::{ChDiv, Intr}; diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index 1b33fdf8d..60ca8627b 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -1,6 +1,6 @@ mod filter; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; pub use self::filter::DateTimeFilter; diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index af101cf4a..544b542e8 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; use embassy_futures::join::join; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Phase, Polarity}; use crate::dma::{AnyChannel, Channel}; diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 30eeb5476..9d96db12c 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -3,7 +3,7 @@ use core::slice; use core::task::Poll; use atomic_polyfill::{AtomicU8, Ordering}; -use embassy_hal_common::atomic_ring_buffer::RingBuffer; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{Duration, Timer}; diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 7b94bce5e..69c6ac2f1 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -4,7 +4,7 @@ use core::task::Poll; use atomic_polyfill::{AtomicU16, Ordering}; use embassy_futures::select::{select, Either}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{Duration, Timer}; use pac::uart::regs::Uartris; diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 2c6089c56..96c474845 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -15,7 +15,7 @@ embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } +embassy-hal-internal = { version = "0.1.0", path = "../embassy-hal-internal" } embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver", optional=true } @@ -31,7 +31,7 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa bitflags = { version = "2.3.3", optional = true } [features] -defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "stm32wb-hci?/defmt"] +defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "stm32wb-hci?/defmt"] ble = ["dep:stm32wb-hci"] mac = ["dep:bitflags", "dep:embassy-net-driver" ] diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 6836d7a8b..5ecce2cc2 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -8,7 +8,7 @@ pub mod fmt; use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, Ordering}; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::interrupt; use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32::peripherals::IPCC; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 0fb6fdb56..6f34c7416 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -34,7 +34,7 @@ flavors = [ embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-4"] } +embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } @@ -80,7 +80,7 @@ stm32-metapac = { version = "13", default-features = false, features = ["metadat default = ["rt"] rt = ["stm32-metapac/rt"] -defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] memory-x = ["stm32-metapac/memory-x"] exti = [] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 995ad1443..0e9606ec3 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -138,7 +138,7 @@ fn main() { let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect(); g.extend(quote! { - embassy_hal_common::peripherals_definition!(#(#singleton_tokens),*); + embassy_hal_internal::peripherals_definition!(#(#singleton_tokens),*); }); let singleton_tokens: Vec<_> = singletons @@ -148,7 +148,7 @@ fn main() { .collect(); g.extend(quote! { - embassy_hal_common::peripherals_struct!(#(#singleton_tokens),*); + embassy_hal_internal::peripherals_struct!(#(#singleton_tokens),*); }); // ======== @@ -160,7 +160,7 @@ fn main() { } g.extend(quote! { - embassy_hal_common::interrupt_mod!( + embassy_hal_internal::interrupt_mod!( #( #irqs, )* @@ -211,7 +211,7 @@ fn main() { let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); flash_regions.extend(quote! { #[cfg(flash)] - pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData); + pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_internal::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData); }); } @@ -243,7 +243,7 @@ fn main() { #[cfg(flash)] impl<'d, MODE> FlashLayout<'d, MODE> { - pub(crate) fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { + pub(crate) fn new(p: embassy_hal_internal::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { #(#inits),*, _mode: core::marker::PhantomData, diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 2322204d5..e577ec289 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, SampleTime}; diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index d9af0c55e..e8245884e 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 091c1d447..9a7acea53 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use super::InternalChannel; diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 3a6e58cf6..821cc7f6a 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index c51c6840f..64d0f0c75 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -226,7 +226,7 @@ impl Prescaler { impl<'d, T: Instance> Adc<'d, T> { pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - embassy_hal_common::into_ref!(adc); + embassy_hal_internal::into_ref!(adc); T::enable(); T::reset(); diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 8b8244d4f..55d34201f 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -6,7 +6,7 @@ use core::task::Poll; pub use bxcan; use bxcan::{Data, ExtendedId, Frame, Id, StandardId}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use futures::FutureExt; use crate::gpio::sealed::AFType; diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index c31a7fc63..f77788db3 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -1,5 +1,5 @@ pub use bxcan; -use embassy_hal_common::PeripheralRef; +use embassy_hal_internal::PeripheralRef; use crate::peripherals; diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs index 3946a2d47..154f2eb91 100644 --- a/embassy-stm32/src/crc/v1.rs +++ b/embassy-stm32/src/crc/v1.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac::CRC as PAC_CRC; use crate::peripherals::CRC; diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs index f337055a7..de0c08755 100644 --- a/embassy-stm32/src/crc/v2v3.rs +++ b/embassy-stm32/src/crc/v2v3.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac::crc::vals; use crate::pac::CRC as PAC_CRC; diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 979748bb4..a2040b857 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -3,7 +3,7 @@ //! Provide access to the STM32 digital-to-analog converter (DAC). use core::marker::PhantomData; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac::dac; use crate::rcc::RccPeripheral; diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index 78b026cb6..7497f4aaa 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -2,7 +2,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::dma::Transfer; diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 68c78123d..d956047d5 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -6,7 +6,7 @@ use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll, Waker}; use atomic_polyfill::AtomicUsize; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index c27ec7bd9..219ef2eb0 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -4,7 +4,7 @@ use core::pin::Pin; use core::sync::atomic::{fence, AtomicUsize, Ordering}; use core::task::{Context, Poll, Waker}; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index b7bcf7795..97cc200d7 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -5,7 +5,7 @@ use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll}; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use super::word::{Word, WordSize}; diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 0858587bd..4f1a58ae2 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -26,7 +26,7 @@ pub mod word; use core::mem; -use embassy_hal_common::impl_peripheral; +use embassy_hal_internal::impl_peripheral; #[cfg(dmamux)] pub use self::dmamux::*; diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 2a6ea35ff..a1e0240c8 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -6,7 +6,7 @@ mod tx_desc; use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; pub(crate) use self::rx_desc::{RDes, RDesRing}; diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index bb681c42b..ada495fdb 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -3,7 +3,7 @@ mod descriptors; use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; use super::*; diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 3ff92c9e6..925cf39be 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::pin::Pin; use core::task::{Context, Poll}; -use embassy_hal_common::impl_peripheral; +use embassy_hal_internal::impl_peripheral; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::{AnyPin, Input, Pin as GpioPin}; diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index f175349cd..e966e2a77 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -1,8 +1,8 @@ use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::into_ref; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::into_ref; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::Mutex; diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 2a374733d..16c511295 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,8 +1,8 @@ use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use stm32_metapac::FLASH_BASE; use super::{ diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 4cb39e033..728f6d604 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -14,7 +14,7 @@ use crate::pac; mod alt_regions { use core::marker::PhantomData; - use embassy_hal_common::PeripheralRef; + use embassy_hal_internal::PeripheralRef; use stm32_metapac::FLASH_SIZE; use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index 60d7a00ee..177e66a91 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use crate::gpio::sealed::AFType; use crate::gpio::{Pull, Speed}; diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index af3a8eaca..cda597145 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -1,7 +1,7 @@ #![macro_use] use core::convert::Infallible; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use crate::pac::gpio::{self, vals}; use crate::{pac, peripherals, Peripheral}; diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index aa485cd86..e5254a8cd 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::dma::NoDma; use crate::gpio::sealed::AFType; diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 208d1527d..eaf980a4d 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -4,8 +4,8 @@ use core::marker::PhantomData; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::dma::{NoDma, Transfer}; diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index 62dda69b4..1ccad7328 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::AnyPin; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 45a7b5476..ebd0e7cd5 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -79,7 +79,7 @@ pub use crate::_generated::interrupt; /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. -// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`. +// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { @@ -103,7 +103,7 @@ macro_rules! bind_interrupts { // Reexports pub use _generated::{peripherals, Peripherals}; -pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] pub use stm32_metapac as pac; #[cfg(not(feature = "unstable-pac"))] diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs index 4d64d005c..64bb32c39 100644 --- a/embassy-stm32/src/pwm/complementary_pwm.rs +++ b/embassy-stm32/src/pwm/complementary_pwm.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use stm32_metapac::timer::vals::Ckd; use super::simple_pwm::*; diff --git a/embassy-stm32/src/pwm/simple_pwm.rs b/embassy-stm32/src/pwm/simple_pwm.rs index 995f59c23..514796930 100644 --- a/embassy-stm32/src/pwm/simple_pwm.rs +++ b/embassy-stm32/src/pwm/simple_pwm.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use super::*; #[allow(unused_imports)] diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 31b676088..32382fb28 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -2,7 +2,7 @@ pub mod enums; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use enums::*; use crate::dma::Transfer; diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index b84470440..7aa9f0fd2 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use stm32_metapac::rcc::vals::{Mco1, Mco2, Mcopre}; use super::sealed::RccPeripheral; diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs index 7e5cd0d1a..bbc0e0831 100644 --- a/embassy-stm32/src/rcc/h7.rs +++ b/embassy-stm32/src/rcc/h7.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; pub use pll::PllConfig; use stm32_metapac::rcc::vals::{Mco1, Mco2}; diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index 8a9b4adbf..dc5f55d0c 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use stm32_metapac::rcc::regs::Cfgr; use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel}; diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index b2faec53d..27415c2d7 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -3,7 +3,7 @@ use core::future::poll_fn; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use rand_core::{CryptoRng, RngCore}; diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 12a2ac795..323be3187 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -15,7 +15,7 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; #[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")] mod _version; pub use _version::*; -use embassy_hal_common::Peripheral; +use embassy_hal_internal::Peripheral; /// Errors that can occur on methods on [RtcClock] #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 434c56a48..6b532363c 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -6,8 +6,8 @@ use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR}; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index d5f63f84e..bdf3c85b0 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -4,7 +4,7 @@ use core::ptr; use embassy_embedded_hal::SetConfig; use embassy_futures::join::join; -use embassy_hal_common::{into_ref, PeripheralRef}; +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}; diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 433ad299c..ca117da82 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -2,7 +2,7 @@ use core::future::poll_fn; use core::slice; use core::task::Poll; -use embassy_hal_common::atomic_ring_buffer::RingBuffer; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use super::*; diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 3b9226fdf..d99034bca 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -5,8 +5,8 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use futures::future::{select, Either}; use crate::dma::{NoDma, Transfer}; diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index c74d7e092..80261d048 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -2,7 +2,7 @@ use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::PeripheralRef; +use embassy_hal_internal::PeripheralRef; use futures::future::{select, Either}; use super::{clear_interrupt_flags, rdr, sr, BasicInstance, Error, UartRx}; diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index ecdd1d0b8..cef196355 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -5,7 +5,7 @@ use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Poll; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver as driver; use embassy_usb_driver::{ diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index fd0e22adf..348f0f79d 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, AtomicU16, Ordering}; use core::task::Poll; -use embassy_hal_common::{into_ref, Peripheral}; +use embassy_hal_internal::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{ self, Bus as _, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index b03e81d6e..eafd03364 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::{into_ref, Peripheral}; +use embassy_hal_internal::{into_ref, Peripheral}; use stm32_metapac::iwdg::vals::{Key, Pr}; use crate::rcc::LSI_FREQ; diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index ce8838605..cf3e2ce9b 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -10,7 +10,6 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" From d5f9d17b7c12c73cf16aa7414ce819bbd06efc2e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 Jul 2023 13:38:26 +0200 Subject: [PATCH 29/41] Make pipes local vars. --- examples/rp/src/bin/pio_uart.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index c978f8f06..ca1c7f394 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -18,7 +18,7 @@ use embassy_rp::bind_interrupts; use embassy_rp::peripherals::{PIO0, USB}; use embassy_rp::pio::InterruptHandler as PioInterruptHandler; use embassy_rp::usb::{Driver, Instance, InterruptHandler}; -use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::pipe::Pipe; use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; use embassy_usb::driver::EndpointError; @@ -91,13 +91,13 @@ async fn main(_spawner: Spawner) { let (mut uart_tx, mut uart_rx) = uart.split(); // Pipe setup - static USB_PIPE: Pipe = Pipe::new(); - let mut usb_pipe_writer = USB_PIPE.writer(); - let mut usb_pipe_reader = USB_PIPE.reader(); + let usb_pipe: Pipe = Pipe::new(); + let mut usb_pipe_writer = usb_pipe.writer(); + let mut usb_pipe_reader = usb_pipe.reader(); - static UART_PIPE: Pipe = Pipe::new(); - let mut uart_pipe_writer = UART_PIPE.writer(); - let mut uart_pipe_reader = UART_PIPE.reader(); + let uart_pipe: Pipe = Pipe::new(); + let mut uart_pipe_writer = uart_pipe.writer(); + let mut uart_pipe_reader = uart_pipe.reader(); let (mut usb_tx, mut usb_rx) = class.split(); @@ -141,7 +141,7 @@ impl From for Disconnected { /// Read from the USB and write it to the UART TX pipe async fn usb_read<'d, T: Instance + 'd>( usb_rx: &mut Receiver<'d, Driver<'d, T>>, - uart_pipe_writer: &mut embassy_sync::pipe::Writer<'static, ThreadModeRawMutex, 20>, + uart_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, ) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { @@ -155,7 +155,7 @@ async fn usb_read<'d, T: Instance + 'd>( /// Read from the USB TX pipe and write it to the USB async fn usb_write<'d, T: Instance + 'd>( usb_tx: &mut Sender<'d, Driver<'d, T>>, - usb_pipe_reader: &mut embassy_sync::pipe::Reader<'d, ThreadModeRawMutex, 20>, + usb_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, ) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { @@ -167,9 +167,9 @@ async fn usb_write<'d, T: Instance + 'd>( } /// Read from the UART and write it to the USB TX pipe -async fn uart_read<'a>( - uart_rx: &mut PioUartRx<'a>, - usb_pipe_writer: &mut embassy_sync::pipe::Writer<'static, ThreadModeRawMutex, 20>, +async fn uart_read( + uart_rx: &mut PioUartRx<'_>, + usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, ) -> ! { let mut buf = [0; 64]; loop { @@ -184,9 +184,9 @@ async fn uart_read<'a>( } /// Read from the UART TX pipe and write it to the UART -async fn uart_write<'a>( - uart_tx: &mut PioUartTx<'a>, - uart_pipe_reader: &mut embassy_sync::pipe::Reader<'a, ThreadModeRawMutex, 20>, +async fn uart_write( + uart_tx: &mut PioUartTx<'_>, + uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, ) -> ! { let mut buf = [0; 64]; loop { From f81ee103bf946bc42355474d5e125d1e5ab08da8 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Fri, 28 Jul 2023 14:52:03 +0200 Subject: [PATCH 30/41] Allow ethernet and 802.15.4 to coexist Co-authored-by: Thibaut Vandervelden --- embassy-net/src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 81c751a2c..3f9150168 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -32,12 +32,14 @@ pub use smoltcp::iface::MulticastError; use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4::{self, RetryConfig}; +#[cfg(feature = "medium-ethernet")] +pub use smoltcp::wire::EthernetAddress; +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] +pub use smoltcp::wire::HardwareAddress; #[cfg(feature = "udp")] pub use smoltcp::wire::IpListenEndpoint; -#[cfg(feature = "medium-ethernet")] -pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; #[cfg(feature = "medium-ieee802154")] -pub use smoltcp::wire::{HardwareAddress, Ieee802154Address}; +pub use smoltcp::wire::{Ieee802154Address, Ieee802154Frame}; pub use smoltcp::wire::{IpAddress, IpCidr, IpEndpoint}; #[cfg(feature = "proto-ipv4")] pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; @@ -583,7 +585,7 @@ impl SocketStack { impl Inner { #[cfg(feature = "proto-ipv4")] fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) { - #[cfg(feature = "medium-ethernet")] + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] let medium = self.device.capabilities().medium; debug!("Acquired IP configuration:"); From 3690af9bea5968653780d296146a91c63994d89d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 Jul 2023 15:29:27 +0200 Subject: [PATCH 31/41] stm32/timer: merge pwm module into timer. (#1703) The traits there are applicable to timer use cases other than PWM. It doesn't make sense to keep them separated. --- embassy-stm32/build.rs | 30 +- embassy-stm32/src/lib.rs | 1 - embassy-stm32/src/pwm/mod.rs | 269 ----------------- .../src/{pwm => timer}/complementary_pwm.rs | 0 embassy-stm32/src/timer/mod.rs | 271 ++++++++++++++++-- .../src/{pwm => timer}/simple_pwm.rs | 0 examples/stm32f4/src/bin/pwm.rs | 4 +- examples/stm32f4/src/bin/pwm_complementary.rs | 6 +- examples/stm32g4/src/bin/pwm.rs | 4 +- .../stm32h7/src/bin/low_level_timer_api.rs | 2 +- examples/stm32h7/src/bin/pwm.rs | 4 +- 11 files changed, 271 insertions(+), 320 deletions(-) delete mode 100644 embassy-stm32/src/pwm/mod.rs rename embassy-stm32/src/{pwm => timer}/complementary_pwm.rs (100%) rename embassy-stm32/src/{pwm => timer}/simple_pwm.rs (100%) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 0e9606ec3..409a943d2 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -572,21 +572,21 @@ fn main() { (("fmc", "Clk"), quote!(crate::fmc::ClkPin)), (("fmc", "BA0"), quote!(crate::fmc::BA0Pin)), (("fmc", "BA1"), quote!(crate::fmc::BA1Pin)), - (("timer", "CH1"), quote!(crate::pwm::Channel1Pin)), - (("timer", "CH1N"), quote!(crate::pwm::Channel1ComplementaryPin)), - (("timer", "CH2"), quote!(crate::pwm::Channel2Pin)), - (("timer", "CH2N"), quote!(crate::pwm::Channel2ComplementaryPin)), - (("timer", "CH3"), quote!(crate::pwm::Channel3Pin)), - (("timer", "CH3N"), quote!(crate::pwm::Channel3ComplementaryPin)), - (("timer", "CH4"), quote!(crate::pwm::Channel4Pin)), - (("timer", "CH4N"), quote!(crate::pwm::Channel4ComplementaryPin)), - (("timer", "ETR"), quote!(crate::pwm::ExternalTriggerPin)), - (("timer", "BKIN"), quote!(crate::pwm::BreakInputPin)), - (("timer", "BKIN_COMP1"), quote!(crate::pwm::BreakInputComparator1Pin)), - (("timer", "BKIN_COMP2"), quote!(crate::pwm::BreakInputComparator2Pin)), - (("timer", "BKIN2"), quote!(crate::pwm::BreakInput2Pin)), - (("timer", "BKIN2_COMP1"), quote!(crate::pwm::BreakInput2Comparator1Pin)), - (("timer", "BKIN2_COMP2"), quote!(crate::pwm::BreakInput2Comparator2Pin)), + (("timer", "CH1"), quote!(crate::timer::Channel1Pin)), + (("timer", "CH1N"), quote!(crate::timer::Channel1ComplementaryPin)), + (("timer", "CH2"), quote!(crate::timer::Channel2Pin)), + (("timer", "CH2N"), quote!(crate::timer::Channel2ComplementaryPin)), + (("timer", "CH3"), quote!(crate::timer::Channel3Pin)), + (("timer", "CH3N"), quote!(crate::timer::Channel3ComplementaryPin)), + (("timer", "CH4"), quote!(crate::timer::Channel4Pin)), + (("timer", "CH4N"), quote!(crate::timer::Channel4ComplementaryPin)), + (("timer", "ETR"), quote!(crate::timer::ExternalTriggerPin)), + (("timer", "BKIN"), quote!(crate::timer::BreakInputPin)), + (("timer", "BKIN_COMP1"), quote!(crate::timer::BreakInputComparator1Pin)), + (("timer", "BKIN_COMP2"), quote!(crate::timer::BreakInputComparator2Pin)), + (("timer", "BKIN2"), quote!(crate::timer::BreakInput2Pin)), + (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInput2Comparator1Pin)), + (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInput2Comparator2Pin)), (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index ebd0e7cd5..bb2ef2fc0 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -43,7 +43,6 @@ pub mod flash; pub mod i2s; #[cfg(stm32wb)] pub mod ipcc; -pub mod pwm; #[cfg(quadspi)] pub mod qspi; #[cfg(rng)] diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs deleted file mode 100644 index 5aba2663e..000000000 --- a/embassy-stm32/src/pwm/mod.rs +++ /dev/null @@ -1,269 +0,0 @@ -pub mod complementary_pwm; -pub mod simple_pwm; - -use stm32_metapac::timer::vals::Ckd; - -#[cfg(feature = "unstable-pac")] -pub mod low_level { - pub use super::sealed::*; -} - -#[derive(Clone, Copy)] -pub enum Channel { - Ch1, - Ch2, - Ch3, - Ch4, -} - -impl Channel { - pub fn raw(&self) -> usize { - match self { - Channel::Ch1 => 0, - Channel::Ch2 => 1, - Channel::Ch3 => 2, - Channel::Ch4 => 3, - } - } -} - -#[derive(Clone, Copy)] -pub enum OutputCompareMode { - Frozen, - ActiveOnMatch, - InactiveOnMatch, - Toggle, - ForceInactive, - ForceActive, - PwmMode1, - PwmMode2, -} - -impl From for stm32_metapac::timer::vals::Ocm { - fn from(mode: OutputCompareMode) -> Self { - match mode { - OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, - OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH, - OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH, - OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, - OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE, - OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE, - OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1, - OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2, - } - } -} - -pub(crate) mod sealed { - use super::*; - - pub trait CaptureCompare16bitInstance: crate::timer::sealed::GeneralPurpose16bitInstance { - /// Global output enable. Does not do anything on non-advanced timers. - fn enable_outputs(&mut self, enable: bool); - - fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); - - fn enable_channel(&mut self, channel: Channel, enable: bool); - - fn set_compare_value(&mut self, channel: Channel, value: u16); - - fn get_max_compare_value(&self) -> u16; - } - - pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { - fn set_dead_time_clock_division(&mut self, value: Ckd); - - fn set_dead_time_value(&mut self, value: u8); - - fn enable_complementary_channel(&mut self, channel: Channel, enable: bool); - } - - pub trait CaptureCompare32bitInstance: crate::timer::sealed::GeneralPurpose32bitInstance { - fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); - - fn enable_channel(&mut self, channel: Channel, enable: bool); - - fn set_compare_value(&mut self, channel: Channel, value: u32); - - fn get_max_compare_value(&self) -> u32; - } -} - -pub trait CaptureCompare16bitInstance: - sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static -{ -} - -pub trait ComplementaryCaptureCompare16bitInstance: - sealed::ComplementaryCaptureCompare16bitInstance + crate::timer::AdvancedControlInstance + 'static -{ -} - -pub trait CaptureCompare32bitInstance: - sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + crate::timer::GeneralPurpose32bitInstance + 'static -{ -} - -#[allow(unused)] -macro_rules! impl_compare_capable_16bit { - ($inst:ident) => { - impl crate::pwm::sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { - fn enable_outputs(&mut self, _enable: bool) {} - - fn set_output_compare_mode(&mut self, channel: crate::pwm::Channel, mode: OutputCompareMode) { - use crate::timer::sealed::GeneralPurpose16bitInstance; - let r = Self::regs_gp16(); - let raw_channel: usize = channel.raw(); - r.ccmr_output(raw_channel / 2) - .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); - } - - fn enable_channel(&mut self, channel: Channel, enable: bool) { - use crate::timer::sealed::GeneralPurpose16bitInstance; - Self::regs_gp16() - .ccer() - .modify(|w| w.set_cce(channel.raw(), enable)); - } - - fn set_compare_value(&mut self, channel: Channel, value: u16) { - use crate::timer::sealed::GeneralPurpose16bitInstance; - Self::regs_gp16().ccr(channel.raw()).modify(|w| w.set_ccr(value)); - } - - fn get_max_compare_value(&self) -> u16 { - use crate::timer::sealed::GeneralPurpose16bitInstance; - Self::regs_gp16().arr().read().arr() - } - } - }; -} - -foreach_interrupt! { - ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { - impl_compare_capable_16bit!($inst); - - impl CaptureCompare16bitInstance for crate::peripherals::$inst { - - } - }; - - ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { - impl_compare_capable_16bit!($inst); - impl crate::pwm::sealed::CaptureCompare32bitInstance for crate::peripherals::$inst { - fn set_output_compare_mode( - &mut self, - channel: crate::pwm::Channel, - mode: OutputCompareMode, - ) { - use crate::timer::sealed::GeneralPurpose32bitInstance; - let raw_channel = channel.raw(); - Self::regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into())); - } - - fn enable_channel(&mut self, channel: Channel, enable: bool) { - use crate::timer::sealed::GeneralPurpose32bitInstance; - Self::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), enable)); - } - - fn set_compare_value(&mut self, channel: Channel, value: u32) { - use crate::timer::sealed::GeneralPurpose32bitInstance; - Self::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value)); - } - - fn get_max_compare_value(&self) -> u32 { - use crate::timer::sealed::GeneralPurpose32bitInstance; - Self::regs_gp32().arr().read().arr() as u32 - } - } - impl CaptureCompare16bitInstance for crate::peripherals::$inst { - - } - impl CaptureCompare32bitInstance for crate::peripherals::$inst { - - } - }; - - ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { - impl crate::pwm::sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { - fn enable_outputs(&mut self, enable: bool) { - use crate::timer::sealed::AdvancedControlInstance; - let r = Self::regs_advanced(); - r.bdtr().modify(|w| w.set_moe(enable)); - } - - fn set_output_compare_mode( - &mut self, - channel: crate::pwm::Channel, - mode: OutputCompareMode, - ) { - use crate::timer::sealed::AdvancedControlInstance; - let r = Self::regs_advanced(); - let raw_channel: usize = channel.raw(); - r.ccmr_output(raw_channel / 2) - .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); - } - - fn enable_channel(&mut self, channel: Channel, enable: bool) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced() - .ccer() - .modify(|w| w.set_cce(channel.raw(), enable)); - } - - fn set_compare_value(&mut self, channel: Channel, value: u16) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced() - .ccr(channel.raw()) - .modify(|w| w.set_ccr(value)); - } - - fn get_max_compare_value(&self) -> u16 { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced().arr().read().arr() - } - } - - impl CaptureCompare16bitInstance for crate::peripherals::$inst { - - } - - impl crate::pwm::sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { - fn set_dead_time_clock_division(&mut self, value: Ckd) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); - } - - fn set_dead_time_value(&mut self, value: u8) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); - } - - fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced() - .ccer() - .modify(|w| w.set_ccne(channel.raw(), enable)); - } - } - - impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { - - } - }; -} - -pin_trait!(Channel1Pin, CaptureCompare16bitInstance); -pin_trait!(Channel1ComplementaryPin, CaptureCompare16bitInstance); -pin_trait!(Channel2Pin, CaptureCompare16bitInstance); -pin_trait!(Channel2ComplementaryPin, CaptureCompare16bitInstance); -pin_trait!(Channel3Pin, CaptureCompare16bitInstance); -pin_trait!(Channel3ComplementaryPin, CaptureCompare16bitInstance); -pin_trait!(Channel4Pin, CaptureCompare16bitInstance); -pin_trait!(Channel4ComplementaryPin, CaptureCompare16bitInstance); -pin_trait!(ExternalTriggerPin, CaptureCompare16bitInstance); -pin_trait!(BreakInputPin, CaptureCompare16bitInstance); -pin_trait!(BreakInputComparator1Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInputComparator2Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs similarity index 100% rename from embassy-stm32/src/pwm/complementary_pwm.rs rename to embassy-stm32/src/timer/complementary_pwm.rs diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 09b7a3776..6c2d6d827 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,3 +1,6 @@ +pub mod complementary_pwm; +pub mod simple_pwm; + use stm32_metapac::timer::vals; use crate::interrupt; @@ -43,15 +46,123 @@ pub(crate) mod sealed { pub trait AdvancedControlInstance: GeneralPurpose16bitInstance { fn regs_advanced() -> crate::pac::timer::TimAdv; } + + pub trait CaptureCompare16bitInstance: GeneralPurpose16bitInstance { + /// Global output enable. Does not do anything on non-advanced timers. + fn enable_outputs(&mut self, enable: bool); + + fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + + fn enable_channel(&mut self, channel: Channel, enable: bool); + + fn set_compare_value(&mut self, channel: Channel, value: u16); + + fn get_max_compare_value(&self) -> u16; + } + + pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { + fn set_dead_time_clock_division(&mut self, value: vals::Ckd); + + fn set_dead_time_value(&mut self, value: u8); + + fn enable_complementary_channel(&mut self, channel: Channel, enable: bool); + } + + pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance { + fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + + fn enable_channel(&mut self, channel: Channel, enable: bool); + + fn set_compare_value(&mut self, channel: Channel, value: u32); + + fn get_max_compare_value(&self) -> u32; + } } +#[derive(Clone, Copy)] +pub enum Channel { + Ch1, + Ch2, + Ch3, + Ch4, +} + +impl Channel { + pub fn raw(&self) -> usize { + match self { + Channel::Ch1 => 0, + Channel::Ch2 => 1, + Channel::Ch3 => 2, + Channel::Ch4 => 3, + } + } +} + +#[derive(Clone, Copy)] +pub enum OutputCompareMode { + Frozen, + ActiveOnMatch, + InactiveOnMatch, + Toggle, + ForceInactive, + ForceActive, + PwmMode1, + PwmMode2, +} + +impl From for stm32_metapac::timer::vals::Ocm { + fn from(mode: OutputCompareMode) -> Self { + match mode { + OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, + OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH, + OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH, + OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, + OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE, + OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE, + OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1, + OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2, + } + } +} + +pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} + pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'static {} pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {} -pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} +pub trait CaptureCompare16bitInstance: + sealed::CaptureCompare16bitInstance + GeneralPurpose16bitInstance + 'static +{ +} + +pub trait ComplementaryCaptureCompare16bitInstance: + sealed::ComplementaryCaptureCompare16bitInstance + AdvancedControlInstance + 'static +{ +} + +pub trait CaptureCompare32bitInstance: + sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + GeneralPurpose32bitInstance + 'static +{ +} + +pin_trait!(Channel1Pin, CaptureCompare16bitInstance); +pin_trait!(Channel1ComplementaryPin, CaptureCompare16bitInstance); +pin_trait!(Channel2Pin, CaptureCompare16bitInstance); +pin_trait!(Channel2ComplementaryPin, CaptureCompare16bitInstance); +pin_trait!(Channel3Pin, CaptureCompare16bitInstance); +pin_trait!(Channel3ComplementaryPin, CaptureCompare16bitInstance); +pin_trait!(Channel4Pin, CaptureCompare16bitInstance); +pin_trait!(Channel4ComplementaryPin, CaptureCompare16bitInstance); +pin_trait!(ExternalTriggerPin, CaptureCompare16bitInstance); +pin_trait!(BreakInputPin, CaptureCompare16bitInstance); +pin_trait!(BreakInputComparator1Pin, CaptureCompare16bitInstance); +pin_trait!(BreakInputComparator2Pin, CaptureCompare16bitInstance); +pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance); +pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance); +pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); #[allow(unused)] macro_rules! impl_basic_16bit_timer { @@ -140,33 +251,94 @@ macro_rules! impl_32bit_timer { }; } +#[allow(unused)] +macro_rules! impl_compare_capable_16bit { + ($inst:ident) => { + impl sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { + fn enable_outputs(&mut self, _enable: bool) {} + + fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) { + use sealed::GeneralPurpose16bitInstance; + let r = Self::regs_gp16(); + let raw_channel: usize = channel.raw(); + r.ccmr_output(raw_channel / 2) + .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } + + fn enable_channel(&mut self, channel: Channel, enable: bool) { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16() + .ccer() + .modify(|w| w.set_cce(channel.raw(), enable)); + } + + fn set_compare_value(&mut self, channel: Channel, value: u16) { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16().ccr(channel.raw()).modify(|w| w.set_ccr(value)); + } + + fn get_max_compare_value(&self) -> u16 { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16().arr().read().arr() + } + } + }; +} + foreach_interrupt! { ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { impl_basic_16bit_timer!($inst, $irq); - - impl Basic16bitInstance for crate::peripherals::$inst { - } + impl Basic16bitInstance for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { impl_basic_16bit_timer!($inst, $irq); - - impl Basic16bitInstance for crate::peripherals::$inst { - } + impl_compare_capable_16bit!($inst); + impl Basic16bitInstance for crate::peripherals::$inst {} + impl GeneralPurpose16bitInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { fn regs_gp16() -> crate::pac::timer::TimGp16 { crate::pac::$inst } } - - impl GeneralPurpose16bitInstance for crate::peripherals::$inst { - } }; ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { impl_basic_16bit_timer!($inst, $irq); + impl_32bit_timer!($inst); + impl_compare_capable_16bit!($inst); + impl Basic16bitInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} + impl CaptureCompare32bitInstance for crate::peripherals::$inst {} + impl GeneralPurpose16bitInstance for crate::peripherals::$inst {} + impl GeneralPurpose32bitInstance for crate::peripherals::$inst {} - impl Basic16bitInstance for crate::peripherals::$inst { + impl sealed::CaptureCompare32bitInstance for crate::peripherals::$inst { + fn set_output_compare_mode( + &mut self, + channel: Channel, + mode: OutputCompareMode, + ) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + let raw_channel = channel.raw(); + Self::regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } + + fn enable_channel(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), enable)); + } + + fn set_compare_value(&mut self, channel: Channel, value: u32) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value)); + } + + fn get_max_compare_value(&self) -> u32 { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32().arr().read().arr() as u32 + } } impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { @@ -174,21 +346,16 @@ foreach_interrupt! { unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) } } } - - impl GeneralPurpose16bitInstance for crate::peripherals::$inst { - } - - impl_32bit_timer!($inst); - - impl GeneralPurpose32bitInstance for crate::peripherals::$inst { - } }; ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { impl_basic_16bit_timer!($inst, $irq); - impl Basic16bitInstance for crate::peripherals::$inst { - } + impl Basic16bitInstance for crate::peripherals::$inst {} + impl GeneralPurpose16bitInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} + impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {} + impl AdvancedControlInstance for crate::peripherals::$inst {} impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { fn regs_gp16() -> crate::pac::timer::TimGp16 { @@ -196,16 +363,70 @@ foreach_interrupt! { } } - impl GeneralPurpose16bitInstance for crate::peripherals::$inst { - } - impl sealed::AdvancedControlInstance for crate::peripherals::$inst { fn regs_advanced() -> crate::pac::timer::TimAdv { crate::pac::$inst } } - impl AdvancedControlInstance for crate::peripherals::$inst { + impl sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { + fn enable_outputs(&mut self, enable: bool) { + use crate::timer::sealed::AdvancedControlInstance; + let r = Self::regs_advanced(); + r.bdtr().modify(|w| w.set_moe(enable)); + } + + fn set_output_compare_mode( + &mut self, + channel: Channel, + mode: OutputCompareMode, + ) { + use crate::timer::sealed::AdvancedControlInstance; + let r = Self::regs_advanced(); + let raw_channel: usize = channel.raw(); + r.ccmr_output(raw_channel / 2) + .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } + + fn enable_channel(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_cce(channel.raw(), enable)); + } + + fn set_compare_value(&mut self, channel: Channel, value: u16) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccr(channel.raw()) + .modify(|w| w.set_ccr(value)); + } + + fn get_max_compare_value(&self) -> u16 { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().arr().read().arr() + } } + + impl sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { + fn set_dead_time_clock_division(&mut self, value: vals::Ckd) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); + } + + fn set_dead_time_value(&mut self, value: u8) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); + } + + fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_ccne(channel.raw(), enable)); + } + } + + }; } diff --git a/embassy-stm32/src/pwm/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs similarity index 100% rename from embassy-stm32/src/pwm/simple_pwm.rs rename to embassy-stm32/src/timer/simple_pwm.rs diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 7c5902052..4f130c26b 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs @@ -4,9 +4,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::pwm::Channel; use embassy_stm32::time::khz; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::timer::Channel; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index a8a68ed6e..8cc2a4117 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -4,10 +4,10 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; -use embassy_stm32::pwm::simple_pwm::PwmPin; -use embassy_stm32::pwm::Channel; use embassy_stm32::time::khz; +use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; +use embassy_stm32::timer::simple_pwm::PwmPin; +use embassy_stm32::timer::Channel; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index 8f7842ed7..b5a9b9952 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -4,9 +4,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::pwm::Channel; use embassy_stm32::time::khz; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::timer::Channel; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index d360df085..45b0872b5 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -6,8 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::low_level::AFType; use embassy_stm32::gpio::Speed; -use embassy_stm32::pwm::*; use embassy_stm32::time::{khz, mhz, Hertz}; +use embassy_stm32::timer::*; use embassy_stm32::{into_ref, Config, Peripheral, PeripheralRef}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index c5c0dd290..adf2ea9ce 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -4,9 +4,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::pwm::Channel; use embassy_stm32::time::{khz, mhz}; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::timer::Channel; use embassy_stm32::Config; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; From 973b152375d8ace2c247c602d39aea7b01fb636e Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Fri, 28 Jul 2023 15:41:45 +0200 Subject: [PATCH 32/41] CI: ethernet and ieee802.15.4 should be able to co-exist --- ci.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci.sh b/ci.sh index 376cc8f44..19628b50e 100755 --- a/ci.sh +++ b/ci.sh @@ -27,6 +27,8 @@ cargo batch \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits,nightly \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,nightly \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits,nightly \ From b57ba84da5f287d7c2d4899c485b2732ff2745a2 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Fri, 28 Jul 2023 16:34:20 +0200 Subject: [PATCH 33/41] add dac-dma example for h7, remove memory.x --- examples/stm32h7/Cargo.toml | 2 +- examples/stm32h7/memory.x | 5 - examples/stm32h7/src/bin/dac_dma.rs | 140 ++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 6 deletions(-) delete mode 100644 examples/stm32h7/memory.x create mode 100644 examples/stm32h7/src/bin/dac_dma.rs diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 2d82c0d0d..3c1232e67 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32h743bi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "memory-x", "unstable-pac", "unstable-traits"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } diff --git a/examples/stm32h7/memory.x b/examples/stm32h7/memory.x deleted file mode 100644 index 026b14b9b..000000000 --- a/examples/stm32h7/memory.x +++ /dev/null @@ -1,5 +0,0 @@ -MEMORY -{ - FLASH : ORIGIN = 0x8000000, LENGTH = 1024K - RAM : ORIGIN = 0x24000000, LENGTH = 384K -} diff --git a/examples/stm32h7/src/bin/dac_dma.rs b/examples/stm32h7/src/bin/dac_dma.rs new file mode 100644 index 000000000..a9cb5d1ed --- /dev/null +++ b/examples/stm32h7/src/bin/dac_dma.rs @@ -0,0 +1,140 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::dac::{DacChannel, ValueArray}; +use embassy_stm32::pac::timer::vals::{Mms, Opm}; +use embassy_stm32::peripherals::{TIM6, TIM7}; +use embassy_stm32::rcc::low_level::RccPeripheral; +use embassy_stm32::time::{mhz, Hertz}; +use embassy_stm32::timer::low_level::Basic16bitInstance; +use micromath::F32Ext; +use {defmt_rtt as _, panic_probe as _}; + +pub type Dac1Type = + embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>; + +pub type Dac2Type = + embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.sys_ck = Some(mhz(400)); + config.rcc.hclk = Some(mhz(100)); + config.rcc.pll1.q_ck = Some(mhz(100)); + + // Initialize the board and obtain a Peripherals instance + let p: embassy_stm32::Peripherals = embassy_stm32::init(config); + + // Obtain two independent channels (p.DAC1 can only be consumed once, though!) + let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); + + spawner.spawn(dac_task1(dac_ch1)).ok(); + spawner.spawn(dac_task2(dac_ch2)).ok(); +} + +#[embassy_executor::task] +async fn dac_task1(mut dac: Dac1Type) { + let data: &[u8; 256] = &calculate_array::<256>(); + + info!("TIM6 frequency is {}", TIM6::frequency()); + const FREQUENCY: Hertz = Hertz::hz(200); + + // Compute the reload value such that we obtain the FREQUENCY for the sine + let reload: u32 = (TIM6::frequency().0 / FREQUENCY.0) / data.len() as u32; + + // Depends on your clock and on the specific chip used, you may need higher or lower values here + if reload < 10 { + error!("Reload value {} below threshold!", reload); + } + + dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap(); + dac.enable_channel().unwrap(); + + TIM6::enable(); + TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM6::regs().cr1().modify(|w| { + w.set_opm(Opm::DISABLED); + w.set_cen(true); + }); + + debug!( + "TIM6 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", + TIM6::frequency(), + FREQUENCY, + reload, + reload as u16, + data.len() + ); + + // Loop technically not necessary if DMA circular mode is enabled + loop { + info!("Loop DAC1"); + if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { + error!("Could not write to dac: {}", e); + } + } +} + +#[embassy_executor::task] +async fn dac_task2(mut dac: Dac2Type) { + let data: &[u8; 256] = &calculate_array::<256>(); + + info!("TIM7 frequency is {}", TIM7::frequency()); + + const FREQUENCY: Hertz = Hertz::hz(600); + let reload: u32 = (TIM7::frequency().0 / FREQUENCY.0) / data.len() as u32; + + if reload < 10 { + error!("Reload value {} below threshold!", reload); + } + + TIM7::enable(); + TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM7::regs().cr1().modify(|w| { + w.set_opm(Opm::DISABLED); + w.set_cen(true); + }); + + dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap(); + + debug!( + "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", + TIM7::frequency(), + FREQUENCY, + reload, + reload as u16, + data.len() + ); + + if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { + error!("Could not write to dac: {}", e); + } +} + +fn to_sine_wave(v: u8) -> u8 { + if v >= 128 { + // top half + let r = 3.14 * ((v - 128) as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } else { + // bottom half + let r = 3.14 + 3.14 * (v as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } +} + +fn calculate_array() -> [u8; N] { + let mut res = [0; N]; + let mut i = 0; + while i < N { + res[i] = to_sine_wave(i as u8); + i += 1; + } + res +} From 937a63ce28beee87ae78756ecf8377f465b8cf9d Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Fri, 28 Jul 2023 16:38:02 +0200 Subject: [PATCH 34/41] remove memory.x files for other stm32 examples --- examples/stm32f7/Cargo.toml | 2 +- examples/stm32f7/build.rs | 40 +---------------------------- examples/stm32f7/memory.x | 12 --------- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h5/memory.x | 5 ---- examples/stm32h7/.cargo/config.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l4/build.rs | 30 ---------------------- examples/stm32l4/memory.x | 7 ----- 9 files changed, 5 insertions(+), 97 deletions(-) delete mode 100644 examples/stm32f7/memory.x delete mode 100644 examples/stm32h5/memory.x delete mode 100644 examples/stm32l4/memory.x diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index a6964c7bc..a379cbbe3 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f767zi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32f7/build.rs b/examples/stm32f7/build.rs index 2b5d412a9..8cd32d7ed 100644 --- a/examples/stm32f7/build.rs +++ b/examples/stm32f7/build.rs @@ -1,43 +1,5 @@ -//! adapted from https://github.com/stm32-rs/stm32f7xx-hal/blob/master/build.rs -use std::fs::File; -use std::io::prelude::*; -use std::path::PathBuf; -use std::{env, io}; - -#[derive(Debug)] -enum Error { - Env(env::VarError), - Io(io::Error), -} - -impl From for Error { - fn from(error: env::VarError) -> Self { - Self::Env(error) - } -} - -impl From for Error { - fn from(error: io::Error) -> Self { - Self::Io(error) - } -} - -fn main() -> Result<(), Error> { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=memory.x"); - - let out_dir = env::var("OUT_DIR")?; - let out_dir = PathBuf::from(out_dir); - - let memory_x = include_bytes!("memory.x").as_ref(); - File::create(out_dir.join("memory.x"))?.write_all(memory_x)?; - - // Tell Cargo where to find the file. - println!("cargo:rustc-link-search={}", out_dir.display()); - +fn main() { println!("cargo:rustc-link-arg-bins=--nmagic"); println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); - - Ok(()) } diff --git a/examples/stm32f7/memory.x b/examples/stm32f7/memory.x deleted file mode 100644 index 899f7a4b8..000000000 --- a/examples/stm32f7/memory.x +++ /dev/null @@ -1,12 +0,0 @@ -/* For STM32F765,767,768,769,777,778,779 devices */ -MEMORY -{ - /* NOTE K = KiBi = 1024 bytes */ - FLASH : ORIGIN = 0x08000000, LENGTH = 2M - RAM : ORIGIN = 0x20000000, LENGTH = 368K + 16K -} - -/* This is where the call stack will be allocated. */ -/* The stack is of the full descending type. */ -/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ -_stack_start = ORIGIN(RAM) + LENGTH(RAM); diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index aebc795c1..51d3bad1f 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32h563zi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } diff --git a/examples/stm32h5/memory.x b/examples/stm32h5/memory.x deleted file mode 100644 index 456061509..000000000 --- a/examples/stm32h5/memory.x +++ /dev/null @@ -1,5 +0,0 @@ -MEMORY -{ - FLASH : ORIGIN = 0x08000000, LENGTH = 0x200000 - RAM : ORIGIN = 0x20000000, LENGTH = 0x50000 -} diff --git a/examples/stm32h7/.cargo/config.toml b/examples/stm32h7/.cargo/config.toml index 5f680dbce..4160bf855 100644 --- a/examples/stm32h7/.cargo/config.toml +++ b/examples/stm32h7/.cargo/config.toml @@ -1,5 +1,5 @@ [target.thumbv7em-none-eabihf] -runner = 'probe-rs run --chip STM32H743ZITx' +runner = 'probe-rs run --chip STM32H7A3ZITxQ' [build] target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 0f770e2f0..3b27d8e81 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32l4s5vi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits", "chrono"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "memory-x", "time-driver-any", "exti", "unstable-traits", "chrono"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32l4/build.rs b/examples/stm32l4/build.rs index 30691aa97..8cd32d7ed 100644 --- a/examples/stm32l4/build.rs +++ b/examples/stm32l4/build.rs @@ -1,34 +1,4 @@ -//! This build script copies the `memory.x` file from the crate root into -//! a directory where the linker can always find it at build time. -//! For many projects this is optional, as the linker always searches the -//! project root directory -- wherever `Cargo.toml` is. However, if you -//! are using a workspace or have a more complicated build setup, this -//! build script becomes required. Additionally, by requesting that -//! Cargo re-run the build script whenever `memory.x` is changed, -//! updating `memory.x` ensures a rebuild of the application with the -//! new memory settings. - -use std::env; -use std::fs::File; -use std::io::Write; -use std::path::PathBuf; - fn main() { - // Put `memory.x` in our output directory and ensure it's - // on the linker search path. - let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); - File::create(out.join("memory.x")) - .unwrap() - .write_all(include_bytes!("memory.x")) - .unwrap(); - println!("cargo:rustc-link-search={}", out.display()); - - // By default, Cargo will re-run a build script whenever - // any file in the project changes. By specifying `memory.x` - // here, we ensure the build script is only re-run when - // `memory.x` is changed. - println!("cargo:rerun-if-changed=memory.x"); - println!("cargo:rustc-link-arg-bins=--nmagic"); println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); diff --git a/examples/stm32l4/memory.x b/examples/stm32l4/memory.x deleted file mode 100644 index eb87d1b54..000000000 --- a/examples/stm32l4/memory.x +++ /dev/null @@ -1,7 +0,0 @@ -MEMORY -{ - /* NOTE 1 K = 1 KiBi = 1024 bytes */ - /* These values correspond to the STM32L4S5 */ - FLASH : ORIGIN = 0x08000000, LENGTH = 1024K - RAM : ORIGIN = 0x20000000, LENGTH = 128K -} From 6dd2fc59418c9d96f049f184694ddaf4845a4425 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Fri, 28 Jul 2023 16:59:13 +0200 Subject: [PATCH 35/41] add document-features --- embassy-stm32/Cargo.toml | 54 +++++++++++++++++++++++++++++----------- embassy-stm32/src/lib.rs | 3 +++ 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 6f34c7416..ba279f795 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -67,6 +67,7 @@ cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } chrono = { version = "^0.4", default-features = false, optional = true} bit_field = "0.10.2" +document-features = "0.2.7" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -78,40 +79,63 @@ stm32-metapac = { version = "13", default-features = false, features = ["metadat [features] default = ["rt"] + +## Enable `stm32-metapac`'s `rt` feature rt = ["stm32-metapac/rt"] +## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] -memory-x = ["stm32-metapac/memory-x"] + exti = [] -# Enables additional driver features that depend on embassy-time +## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) +memory-x = ["stm32-metapac/memory-x"] + +## Enable nightly-only features +nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] + +## Re-export stm32-metapac at `embassy_stm32::pac`. +## This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. +## If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. +## There are no plans to make this stable. +unstable-pac = [] + +## Implement embedded-hal 1.0 alpha traits. +## Implement embedded-hal-async traits if `nightly` is set as well. +unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] + +#! ## Time + +## Enables additional driver features that depend on embassy-time time = ["dep:embassy-time"] # Features starting with `_` are for internal use only. They're not intended # to be enabled by other crates, and are not covered by semver guarantees. _time-driver = ["time"] + +## Use any time driver time-driver-any = ["_time-driver"] +## Use TIM2 as time driver time-driver-tim2 = ["_time-driver"] +## Use TIM3 as time driver time-driver-tim3 = ["_time-driver"] +## Use TIM4 as time driver time-driver-tim4 = ["_time-driver"] +## Use TIM5 as time driver time-driver-tim5 = ["_time-driver"] +## Use TIM12 as time driver time-driver-tim12 = ["_time-driver"] +## Use TIM15 as time driver time-driver-tim15 = ["_time-driver"] -# Enable nightly-only features -nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] -# Reexport stm32-metapac at `embassy_stm32::pac`. -# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. -# If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. -# There are no plans to make this stable. -unstable-pac = [] +#! ## Chip-selection features +#! Select your chip by specifying the model as a feature, e.g. `stm32c011d6`. +#! Check the `Cargo.toml` for the latest list of supported chips. +#! +#! **Important:** Do not forget to adapt the target chip in your toolchain, +#! e.g. in `.cargo/config.toml`. -# Implement embedded-hal 1.0 alpha traits. -# Implement embedded-hal-async traits if `nightly` is set as well. -unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] - -# Chip-selection features stm32c011d6 = [ "stm32-metapac/stm32c011d6" ] stm32c011f4 = [ "stm32-metapac/stm32c011f4" ] stm32c011f6 = [ "stm32-metapac/stm32c011f6" ] @@ -1445,4 +1469,4 @@ stm32wle5cb = [ "stm32-metapac/stm32wle5cb" ] stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ] stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ] stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ] -stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] \ No newline at end of file +stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index bb2ef2fc0..9e67596b0 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,6 +1,9 @@ #![cfg_attr(not(test), no_std)] #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] +//! ## Feature flags +#![doc = document_features::document_features!(feature_label = r#"{feature}"#)] + // This must go FIRST so that all the other modules see its macros. pub mod fmt; include!(concat!(env!("OUT_DIR"), "/_macros.rs")); From cbc8871a0bb40eb5fec82e7c27ed4c0127844c3c Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 28 Jul 2023 18:45:57 +0200 Subject: [PATCH 36/41] rp: relocate programs implicitly during load this removed the RelocatedProgram construction step from pio uses. there's not all that much to be said for the extra step because the origin can be set on the input program itself, and the remaining information exposed by RelocatedProgram can be exposed from LoadedProgram instead (even though it's already available on the pio_asm programs, albeit perhaps less convenient). we do lose access to the relocated instruction iterator, but we also cannot think of anything this iterator would actually be useful for outside of program loading. --- cyw43-pio/src/lib.rs | 8 +- embassy-rp/src/lib.rs | 2 +- embassy-rp/src/pio.rs | 60 ++++++++++++-- embassy-rp/src/relocate.rs | 5 -- examples/rp/src/bin/pio_async.rs | 10 +-- examples/rp/src/bin/pio_dma.rs | 4 +- examples/rp/src/bin/pio_hd44780.rs | 7 +- examples/rp/src/bin/pio_uart.rs | 41 +++------- examples/rp/src/bin/pio_ws2812.rs | 4 +- tests/rp/src/bin/pio_irq.rs | 4 +- tests/rp/src/bin/pio_multi_load.rs | 126 +++++++++++++++++++++++++++++ 11 files changed, 200 insertions(+), 71 deletions(-) create mode 100644 tests/rp/src/bin/pio_multi_load.rs diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs index dca30c74d..830a5b44a 100644 --- a/cyw43-pio/src/lib.rs +++ b/cyw43-pio/src/lib.rs @@ -8,7 +8,6 @@ use cyw43::SpiBusCyw43; use embassy_rp::dma::Channel; use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate}; use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef}; use fixed::FixedU32; use pio_proc::pio_asm; @@ -88,8 +87,6 @@ where ".wrap" ); - let relocated = RelocatedProgram::new(&program.program); - let mut pin_io: embassy_rp::pio::Pin = common.make_pio_pin(dio); pin_io.set_pull(Pull::None); pin_io.set_schmitt(true); @@ -102,7 +99,8 @@ where pin_clk.set_slew_rate(SlewRate::Fast); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[&pin_clk]); + let loaded_program = common.load_program(&program.program); + cfg.use_program(&loaded_program, &[&pin_clk]); cfg.set_out_pins(&[&pin_io]); cfg.set_in_pins(&[&pin_io]); cfg.set_set_pins(&[&pin_io]); @@ -142,7 +140,7 @@ where sm, irq, dma: dma.into_ref(), - wrap_target: relocated.wrap().target, + wrap_target: loaded_program.wrap.target, } } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index ebec9fec6..45156458d 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -33,7 +33,7 @@ pub mod watchdog; // TODO: move `pio_instr_util` and `relocate` to inside `pio` pub mod pio; pub mod pio_instr_util; -pub mod relocate; +pub(crate) mod relocate; // Reexports pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 464988b27..3de398af7 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -11,7 +11,7 @@ use fixed::types::extra::U8; use fixed::FixedU32; use pac::io::vals::Gpio0ctrlFuncsel; use pac::pio::vals::SmExecctrlStatusSel; -use pio::{SideSet, Wrap}; +use pio::{Program, SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; @@ -734,23 +734,67 @@ pub struct InstanceMemory<'d, PIO: Instance> { pub struct LoadedProgram<'d, PIO: Instance> { pub used_memory: InstanceMemory<'d, PIO>, - origin: u8, - wrap: Wrap, - side_set: SideSet, + pub origin: u8, + pub wrap: Wrap, + pub side_set: SideSet, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum LoadError { + /// Insufficient consecutive free instruction space to load program. + InsufficientSpace, + /// Loading the program would overwrite an instruction address already + /// used by another program. + AddressInUse(usize), } impl<'d, PIO: Instance> Common<'d, PIO> { - pub fn load_program(&mut self, prog: &RelocatedProgram) -> LoadedProgram<'d, PIO> { + /// Load a PIO program. This will automatically relocate the program to + /// an available chunk of free instruction memory if the program origin + /// was not explicitly specified, otherwise it will attempt to load the + /// program only at its origin. + pub fn load_program(&mut self, prog: &Program) -> LoadedProgram<'d, PIO> { match self.try_load_program(prog) { Ok(r) => r, - Err(at) => panic!("Trying to write already used PIO instruction memory at {}", at), + Err(e) => panic!("Failed to load PIO program: {:?}", e), } } + /// Load a PIO program. This will automatically relocate the program to + /// an available chunk of free instruction memory if the program origin + /// was not explicitly specified, otherwise it will attempt to load the + /// program only at its origin. pub fn try_load_program( &mut self, - prog: &RelocatedProgram, + prog: &Program, + ) -> Result, LoadError> { + match prog.origin { + Some(origin) => self + .try_load_program_at(prog, origin) + .map_err(|a| LoadError::AddressInUse(a)), + None => { + // naively search for free space, allowing wraparound since + // PIO does support that. with only 32 instruction slots it + // doesn't make much sense to do anything more fancy. + let mut origin = 0; + while origin < 32 { + match self.try_load_program_at(prog, origin as _) { + Ok(r) => return Ok(r), + Err(a) => origin = a + 1, + } + } + Err(LoadError::InsufficientSpace) + } + } + } + + fn try_load_program_at( + &mut self, + prog: &Program, + origin: u8, ) -> Result, usize> { + let prog = RelocatedProgram::new_with_origin(prog, origin); let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?; Ok(LoadedProgram { used_memory, @@ -760,7 +804,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> { }) } - pub fn try_write_instr(&mut self, start: usize, instrs: I) -> Result, usize> + fn try_write_instr(&mut self, start: usize, instrs: I) -> Result, usize> where I: Iterator, { diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs index 9cb279ccd..b35b4ed72 100644 --- a/embassy-rp/src/relocate.rs +++ b/embassy-rp/src/relocate.rs @@ -41,11 +41,6 @@ pub struct RelocatedProgram<'a, const PROGRAM_SIZE: usize> { } impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { - pub fn new(program: &Program) -> RelocatedProgram { - let origin = program.origin.unwrap_or(0); - RelocatedProgram { program, origin } - } - pub fn new_with_origin(program: &Program, origin: u8) -> RelocatedProgram { RelocatedProgram { program, origin } } diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index c001d6440..a6d6144be 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -8,7 +8,6 @@ use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::relocate::RelocatedProgram; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; @@ -29,9 +28,8 @@ fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, ".wrap", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.use_program(&pio.load_program(&prg.program), &[]); let out_pin = pio.make_pio_pin(pin); cfg.set_out_pins(&[&out_pin]); cfg.set_set_pins(&[&out_pin]); @@ -65,9 +63,8 @@ fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, ".wrap", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.use_program(&pio.load_program(&prg.program), &[]); cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); cfg.shift_in.auto_fill = true; cfg.shift_in.direction = ShiftDirection::Right; @@ -96,9 +93,8 @@ fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, "irq 3 [15]", ".wrap", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.use_program(&pio.load_program(&prg.program), &[]); cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); sm.set_config(&cfg); } diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 9ab72e1f3..86e5017ac 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -8,7 +8,6 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; -use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{bind_interrupts, Peripheral}; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; @@ -46,9 +45,8 @@ async fn main(_spawner: Spawner) { ".wrap", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[]); + cfg.use_program(&common.load_program(&prg.program), &[]); cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed(); cfg.shift_in = ShiftConfig { auto_fill: true, diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 8aedd24b6..d80c5c24b 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -14,7 +14,6 @@ use embassy_rp::pio::{ Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; use embassy_rp::pwm::{self, Pwm}; -use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Instant, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -127,9 +126,8 @@ impl<'l> HD44780<'l> { sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[&e]); + cfg.use_program(&common.load_program(&prg.program), &[&e]); cfg.clock_divider = 125u8.into(); cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); cfg.shift_out = ShiftConfig { @@ -201,9 +199,8 @@ impl<'l> HD44780<'l> { "# ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[&e]); + cfg.use_program(&common.load_program(&prg.program), &[&e]); cfg.clock_divider = 8u8.into(); // ~64ns/insn cfg.set_jmp_pin(&db7); cfg.set_set_pins(&[&rs, &rw]); diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index ca1c7f394..5fddbe292 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -222,8 +222,8 @@ mod uart { mut common, sm0, sm1, .. } = Pio::new(pio, Irqs); - let (tx, origin) = PioUartTx::new(&mut common, sm0, tx_pin, baud, None); - let (rx, _) = PioUartRx::new(&mut common, sm1, rx_pin, baud, Some(origin)); + let tx = PioUartTx::new(&mut common, sm0, tx_pin, baud); + let rx = PioUartRx::new(&mut common, sm1, rx_pin, baud); PioUart { tx, rx } } @@ -240,7 +240,6 @@ mod uart_tx { use embassy_rp::gpio::Level; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; - use embassy_rp::relocate::RelocatedProgram; use embedded_io::asynch::Write; use embedded_io::Io; use fixed::traits::ToFixed; @@ -256,9 +255,8 @@ mod uart_tx { mut sm_tx: StateMachine<'a, PIO0, 0>, tx_pin: impl PioPin, baud: u64, - origin: Option, - ) -> (Self, u8) { - let mut prg = pio_proc::pio_asm!( + ) -> Self { + let prg = pio_proc::pio_asm!( r#" .side_set 1 opt @@ -272,17 +270,14 @@ mod uart_tx { jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. "# ); - prg.program.origin = origin; let tx_pin = common.make_pio_pin(tx_pin); sm_tx.set_pins(Level::High, &[&tx_pin]); sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); - let relocated = RelocatedProgram::new(&prg.program); - let mut cfg = Config::default(); cfg.set_out_pins(&[&tx_pin]); - cfg.use_program(&common.load_program(&relocated), &[&tx_pin]); + cfg.use_program(&common.load_program(&prg.program), &[&tx_pin]); cfg.shift_out.auto_fill = false; cfg.shift_out.direction = ShiftDirection::Right; cfg.fifo_join = FifoJoin::TxOnly; @@ -290,18 +285,7 @@ mod uart_tx { sm_tx.set_config(&cfg); sm_tx.set_enable(true); - // The 4 state machines of the PIO each have their own program counter that starts taking - // instructions at an offset (origin) of the 32 instruction "space" the PIO device has. - // It is up to the programmer to sort out where to place these instructions. - // From the pio_asm! macro you get a ProgramWithDefines which has a field .program.origin - // which takes an Option. - // - // When you load more than one RelocatedProgram into the PIO, - // you load your first program at origin = 0. - // The RelocatedProgram has .code().count() which returns a usize, - // for which you can then use as your next program's origin. - let offset = relocated.code().count() as u8 + origin.unwrap_or_default(); - (Self { sm_tx }, offset) + Self { sm_tx } } pub async fn write_u8(&mut self, data: u8) { @@ -329,7 +313,6 @@ mod uart_rx { use embassy_rp::gpio::Level; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; - use embassy_rp::relocate::RelocatedProgram; use embedded_io::asynch::Read; use embedded_io::Io; use fixed::traits::ToFixed; @@ -345,9 +328,8 @@ mod uart_rx { mut sm_rx: StateMachine<'a, PIO0, 1>, rx_pin: impl PioPin, baud: u64, - origin: Option, - ) -> (Self, u8) { - let mut prg = pio_proc::pio_asm!( + ) -> Self { + let prg = pio_proc::pio_asm!( r#" ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and ; break conditions more gracefully. @@ -369,10 +351,8 @@ mod uart_rx { push ; important in case the TX clock is slightly too fast. "# ); - prg.program.origin = origin; - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[]); + cfg.use_program(&common.load_program(&prg.program), &[]); let rx_pin = common.make_pio_pin(rx_pin); sm_rx.set_pins(Level::High, &[&rx_pin]); @@ -387,8 +367,7 @@ mod uart_rx { sm_rx.set_config(&cfg); sm_rx.set_enable(true); - let offset = relocated.code().count() as u8 + origin.unwrap_or_default(); - (Self { sm_rx }, offset) + Self { sm_rx } } pub async fn read_u8(&mut self) -> u8 { diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs index 3de2bd48d..bc87016ec 100644 --- a/examples/rp/src/bin/pio_ws2812.rs +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -12,7 +12,6 @@ use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{ Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; -use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Timer}; use fixed::types::U24F8; @@ -73,8 +72,7 @@ impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> { cfg.set_out_pins(&[&out_pin]); cfg.set_set_pins(&[&out_pin]); - let relocated = RelocatedProgram::new(&prg); - cfg.use_program(&pio.load_program(&relocated), &[&out_pin]); + cfg.use_program(&pio.load_program(&prg), &[&out_pin]); // Clock config, measured in kHz to avoid overflows // TODO CLOCK_FREQ should come from embassy_rp diff --git a/tests/rp/src/bin/pio_irq.rs b/tests/rp/src/bin/pio_irq.rs index 45004424a..bdea63eaa 100644 --- a/tests/rp/src/bin/pio_irq.rs +++ b/tests/rp/src/bin/pio_irq.rs @@ -9,7 +9,6 @@ use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Config, InterruptHandler, Pio}; -use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -35,9 +34,8 @@ async fn main(_spawner: Spawner) { "irq wait 1", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[]); + cfg.use_program(&common.load_program(&prg.program), &[]); sm.set_config(&cfg); sm.set_enable(true); diff --git a/tests/rp/src/bin/pio_multi_load.rs b/tests/rp/src/bin/pio_multi_load.rs new file mode 100644 index 000000000..356f16795 --- /dev/null +++ b/tests/rp/src/bin/pio_multi_load.rs @@ -0,0 +1,126 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; + +use defmt::info; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{Config, InterruptHandler, LoadError, Pio}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let pio = p.PIO0; + let Pio { + mut common, + mut sm0, + mut sm1, + mut sm2, + irq_flags, + .. + } = Pio::new(pio, Irqs); + + // load with explicit origin works + let prg1 = pio_proc::pio_asm!( + ".origin 4" + "nop", + "nop", + "nop", + "nop", + "nop", + "nop", + "nop", + "irq 0", + "nop", + "nop", + ); + let loaded1 = common.load_program(&prg1.program); + assert_eq!(loaded1.origin, 4); + assert_eq!(loaded1.wrap.source, 13); + assert_eq!(loaded1.wrap.target, 4); + + // load without origin chooses a free space + let prg2 = pio_proc::pio_asm!("nop", "nop", "nop", "nop", "nop", "nop", "nop", "irq 1", "nop", "nop",); + let loaded2 = common.load_program(&prg2.program); + assert_eq!(loaded2.origin, 14); + assert_eq!(loaded2.wrap.source, 23); + assert_eq!(loaded2.wrap.target, 14); + + // wrapping around the end of program space automatically works + let prg3 = + pio_proc::pio_asm!("nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "irq 2",); + let loaded3 = common.load_program(&prg3.program); + assert_eq!(loaded3.origin, 24); + assert_eq!(loaded3.wrap.source, 3); + assert_eq!(loaded3.wrap.target, 24); + + // check that the programs actually work + { + let mut cfg = Config::default(); + cfg.use_program(&loaded1, &[]); + sm0.set_config(&cfg); + sm0.set_enable(true); + while !irq_flags.check(0) {} + sm0.set_enable(false); + } + { + let mut cfg = Config::default(); + cfg.use_program(&loaded2, &[]); + sm1.set_config(&cfg); + sm1.set_enable(true); + while !irq_flags.check(1) {} + sm1.set_enable(false); + } + { + let mut cfg = Config::default(); + cfg.use_program(&loaded3, &[]); + sm2.set_config(&cfg); + sm2.set_enable(true); + while !irq_flags.check(2) {} + sm2.set_enable(false); + } + + // instruction memory is full now. all loads should fail. + { + let prg = pio_proc::pio_asm!(".origin 0", "nop"); + match common.try_load_program(&prg.program) { + Err(LoadError::AddressInUse(0)) => (), + _ => panic!("program loaded when it shouldn't"), + }; + + let prg = pio_proc::pio_asm!("nop"); + match common.try_load_program(&prg.program) { + Err(LoadError::InsufficientSpace) => (), + _ => panic!("program loaded when it shouldn't"), + }; + } + + // freeing some memory should allow further loads though. + unsafe { + common.free_instr(loaded3.used_memory); + } + { + let prg = pio_proc::pio_asm!(".origin 0", "nop"); + match common.try_load_program(&prg.program) { + Ok(_) => (), + _ => panic!("program didn't loaded when it shouldn"), + }; + + let prg = pio_proc::pio_asm!("nop"); + match common.try_load_program(&prg.program) { + Ok(_) => (), + _ => panic!("program didn't loaded when it shouldn"), + }; + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From b344c843c4ccebcacf27f68db6d335aed2615302 Mon Sep 17 00:00:00 2001 From: Brandon Ros Date: Fri, 28 Jul 2023 17:25:07 -0400 Subject: [PATCH 37/41] sync latest cyw43-firmware --- cyw43-firmware/43439A0.bin | Bin 224190 -> 230321 bytes cyw43-firmware/43439A0_clm.bin | Bin 4752 -> 4752 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/cyw43-firmware/43439A0.bin b/cyw43-firmware/43439A0.bin index b46b3beff05de7dfcbf41d53a12bf3a74381881b..017375277d77d10e198deb1c668e2e6bb074d869 100755 GIT binary patch delta 78990 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

Cch^r!c<&_I;&@39H8>@7<$fnK0 z*5cG+wg%r}E8QZDYV}1Spg9kcz5+>_6q}Q@RLDJtmxrf#qNo^>HeDFMQesA7%_&XJ z^GuyZ!^XVbXgL;=Lh;IXNQ%5c+Jr|3HUwcO$mRDg=WER+Zc^G^;7Sy!#*k7uBH*NK z;AnSev%FUC z3ZWyf)mHlxPIP)}E&00Yfi%Js1*jDw$3alI(z{7Ff^;KDpNb24Hd_ofYn_o&lQYLP zJLP$!5^I5_8kkKjnNb17MfxrfzWi@ciL1$ z_79t{hr0Xk&QneNDE}gKpRv`)w-#R?QmOt`qpRr(xKh#f+%Bg#QmR%v*GA%U*J{_S zFh0#aed8Z12??kTS=2^eIpkB}cqur{;$SUZ$-@{3AxmAaqKr_@fEx+Z9$C;swuDP^c)UvedaW zNvGiHaJGFejrSed$PYZoh6H<m|c*N1mW<(<=lO57ZM3VU;ebwg0Q8C!KG@|>RxD@lau6@X38MC zbc)gqDka(y^A>--i#QJ&?0T!3)nx(gH&{wQ?0g8BxaHCT)+=hJQh3Woe8Nb$#o9I* zZ-W^iqG)vf&MNG*qj#m6T+78M)v)l$Ty`4Two)Bk`ylpDj5kf+dSH< zZIyVgiC1Hly1k5TpNF?|zh_^}qh`x=Wx{V)=NYZjm9*cue&KXw()nHUR=XZcs}jHb zYT8X_-)81BpN4v;DwawK!lhK@)8AMEFnuf4Kp30A9g(j4U1xpxsp!}uuduNhfk{`? zg8E^d+eE@|g4w?Hq&>q=oSI%ym({c9=hI%ZM#E0h*M0C3VW;)5S`XvPaPPq3&CYnA zgBM^+%(pbro~k_co6$}&Pw@h{oWC9BVPVqBjs+QMcXy}Sx89O#ws#c}&+`g5lpK;x z6Tr;i9ji&%&}F%T-f39`-*}@nWX0=?9>>$92V7GEwEp+F;`8=rNc(Fz&gz^;?)-eO z(HYb9Kq^{^2b0Ws=2D#02%ol1#qKpO%dM(krLMpscf}+;4K!gLIf^nyi&d_xvC3DS zCd6-;l5s1oQhl_6c`cyE=;K_lW;0bAfE|s+4;76n)%FUT zwg4;rGPZ01?ViVTD0h|j4Vw3x`gNK1uTl*!aq+iLGr4863be{+Xpc#CYh0lczr*<{ zpe1#vhs*>h+@2>#0gr_5EkYgCj4$-Z&XYsH+Wr-4AK-X|nj%q03qnna`8es9@d(Hh zx}tWk+ne;I%P2C?FrDl6B)#j3_!p*q-8;ZU{R`t=w*#17|H8<1Y~e!M8O>I!vJDGy zID%$_BHuzf0RPUY?2m=e+rI+iFU&hf_`U=4%sC>)W|fW3p~>)Fpt9F<=orftb-20) za{Uode|bRZBfzAH9ES)~3s=-wmHFh-0q7w8RLx36FSk!?^-JhF^0<*GzQyfpar-va ze(4%5e7nLo*zFtU_WcvSX;K%oA-7S0I+>uTos>}QsG=p46?Z7j8qOce(eaY&#LT?! zVK5%nk?i-!w?rJ08u_|@AV9+|{t|O%j*AeY#r%LS!rtg&dXom}g zy>YbD=zL8mwtJ)Bwi5kx@7-whQ=T|K5E#L>ETTOk0>A}we#Plj+$RK8DiN8SdN?-t zTzFe$w-(XRj_oiQ5-Lm&mqpI#nN3~5wNTb&G1!m5MuLM4&hHs&W2XuStjOM zOp~RT*{~%v%!hkw3?ZMvyQ*yI5}F`A!#;wi6v2L6LW8?^!j(!h4k-AY$>DB-zHTZP z9MUOZ0tAVtSi7aPlTUCn*S64<9IDjsYT3Z0GMkc$t_!{2{#9SJqACzD{VC=i{@2**1R=BaX(!vuDm;NFvo=8`3ImfFh_4z)jCKPaQo0aw%tamp8q z{s<+7#TV863iCTmBmAmWKc{yXmq)I{8^}<`##3L1X^7ty^<#xNI>WHMct^b8`MvDv z!?cr>!?qr#&q^a%;1SwG8pjfk;EvFtvYkh0S6RBizB*3drh%=P{R9G~u^T7oX-^ZO zGpG{Omz91>r%5K}eUgs&2RZd55*W*lous4NZKOfHx)Ui-MDTrw1)idhQa}H)iKpm1 zSz6Cdou+?EoytBwL$}gS&r!&m2#utLm7fr5rn(trfJD^xg)HMMkY`~peTAKLCsy(m z-6q{*$!F<7&&yis_sTrJrjsP;Z8qZ@Iv%O+`-TpXr2XvAZ$VIaS?YI`O41sJS==O* zuw~!VV(H5=46`UHaE(NVb|f7Nq*XKYv|oWp(C6*4DL+zw8dyT?UvwaZ%H=4vyjk-s zVFeZRN!(;{RnXJY*s|k4(bZIXyDae+daqSrjda{*Hf+em2i6jIY6J$CrCp&zB;>u( zLBBzqC$7@rRC=XsXB8bIOZ!;O4SKNC2O_ZFIC)nkRSc)3)egKeu~#5FTwo*j!GY~)_3T~(!sK$cW5e4*XvKzQ68-CP*Y%uRA8g6d`8JVK()uKbT#>k1m>~q zKS6Z~tNN4LrM$Ayf6gW}K z74@9jU&@SkA(G_Y_vjOnRKs3tq@x4Bd|2>VssAcCZsbju1INlTXz>b_-$&8B!nJ~q zEt}UuGm+4iALS89uDv8zQfXdUoh-jE1Afaxo-YL+6oK2WmG;=Y$Oavix;RPTq_Qrp zWYF+&SwAm%hC!OkUh|Rhd~{ihk35Pu^(a5Nht$9p`^g)nrqzP=%H6uMAZMn}1T`m{4j((&}uo`j-UWvvs->N?4NJPlI*jIGLh ztarE^*p`ycqHml;k$iI4_;5L=YmU4TkoQeNb9*X2zEiZ%cjIcb`7)$ z{odzEQqtu|;yad<7#|_N^N0%SWmUc4Frd<aA~Dal(i$6HY}qTw^@*Ume3Z|}NA?rcV>nGFK0$1yMII3tA_~DvjY6Q* z_7NkolVrbIzE&)(?kCN!S>K76*mYzMh@CQHwDYBY^xV|vIPVJ~xD5BXzhG7FB7JGL85ZsAe& ze(SU#quH3TR4#?=Y(9uxyuhS6t`Mf2`r@A8xjlegV93`73?nFF)AW=R9 z`2a2U0`Zy6vzLBF<5Z>wmb}Ly%65pP1H*}9Mj>&!Q~3nlKqWq(P)g} z-!CHg>9qU~kmBKol+qD|qQ79`C46{%LF^y?%|DJAyGXx1WWkJLefbyc2So4sIVqX3 zei7SA7LfPIhR?`?;y%ormF1U|0$&3)jG*<5D`6^w-OT3y5j+J|KU4!1eiHv;#{c^6{VVAstC-yQe`oc_n zUq-x_%`xlPi?On))2o~aQu^ux;-$~J5Z~RjXvQbRm$7eSk^64u8z=Yke2*6L=%p+z z4&;7F977l>;oYTO8;s?JLtd+k`Gh?kCy$Q&j+9Jk$2+9GvM?&O`OAV z<{Nl4EQVx|+PYBw(uiP;(l&(FqiTH)`iM0OLkMT5;nfL`WTVFXKFO)bUekBnH z(Q#;f*+&m*n=?p$5u#cB#!*_ipZJ-GedsHsT{l4+ro43pA0h}XoY6x3jQAi3X2(ha zt%~eT{4B&C@rDS(3FJY_@DT)}U9_H-he?*R9aQEK<7wdtkQPrR9zPxv8{X=3Pi%7m z{WHxyfT*+=O+QvDa7gK`-7Ptu>cE2d4W)(;ew2TJhjB%(8 z5RH(c+b2E-_a`1R8ocNl8Iz($Ps9ZnMcPw4YsecUp zsRe%2h5COe)Y^9}j3SEhCiqdvQw6~@CEaIY)-9S*gcwxETWcl@PLN0BWs+ON zXHTBI1FNR96VGtQn!0fuIj1i9D9JuEd(z@Lfaa2j{aRx4$$~R;rEyuJKeqPK>f*ds z7uhs_1nS~E^^nk#3;EjHs(cKRZt3F0H2cMxy4Xb`p4SPgt1Ke{_B^Q&t%X-BVXv5z zCi=xIT22^LA2`Xz<-{I|8eO4f@tpL6t!?D1)9G5?6wmSwh%6rXfja=Cb+RZ8xk_4X zGa!N+iG0y^ub^8U!-wRFEY?bf4?A#1!e03|@q2+5=A*S=5!uH+uT=u35>5<|oS0Y0 ztr6>|p-c;A6xqhTBCT=orl6~5Aki%v^_vjnYXs4#*!&>E!Wl&q_U)s3l-}=S;>e3+ zB;vYOCpU>bY9#S{o!r7F($_tHKrs68Zc-BY2Ja9h7yy{_SMTjk{7|J)Yl$Ckw4%&d z_(kQ3J`?#`-8j5p?x0TRNowHHkyGSX)T>6YBVJ8XP$XvY9a2ann>Il9nz)zuvl-(w zrpNDNjhq^kN~A#1>jLa3{~i7*iTH|m9TlYtu=BCTGg-rX)KOwDd7StiBKGK+fY^}% zvT4lc)WC<%Ntz#t_Bx4TV5vVw7DiI%QajdD5QilY{p(?xTHTams5?Cuf5E%w#BY!$ zubQt|k4I!v-uGI$e?Ym{XU6`3a<4{d$b!hX>SD_^;;)P5+V``T^xyc*CtfN?O0mBR zWb4+{z)1oW^^DZIv%5rE*S(0Y%X^aOrG8a(^%qNs-*0>|s(l`I(XRhMxn#0XUVuY@HHl>OT||M6VgWn4X*KTsasshM|$iTfoqcLs?# zkH*?WI%ose--n$WC{L3@S?@=&i|E2$eH2HE;q1Vpa*wQW2V<>?81Or-^?O#=$?Sq&-Y!#^2JWF~~vU zIpo>*jwJpQc-qr8jn~qi@c*VgZT|!`lSqvhMtwqR!^z(vwdEf$DOf1|p8%J)rf%{M zeAaq;op_K1@4x;}AUe-btC=kH6=~WBzYu#Kuc1t>hF0KnlladC&uGR+OjP)IM0Nz^ z&R(N%h3J<4E5yv!{(?I?64moW9Z|24NHgk!Pp?{{{*jloJ)egphTTMMMQ2kdUC_?i6#7nlg)mm$>rmJ~V7VvygG>x>? z80VLW7q)qP+&MXEQrt#LF>Cp+;j<;Lgn|#zJvM6H_#%dMZ$Nx7r9UMGQ~IV?Ff`*p zOCIWSeR>&R4cF9-e-&@Zq7)lZw0|<(DCLWRBXaWJqRmnj(pm@XwVb3Lm;?P8mUOM{ zBAg^{yXf5Nc3Q+c=^Gr9{ei?Jzv&|h>j>g$sT?7LfR+7sVd_V|yGRM~CtfE|!aU-2 zN-OW)2N6RfI{^7`IhpY{@&ACO4FOxUm+XmphNN^B<$eCNmiAdJc*ltUQOJ!zVTqWm z{g1-$SKb7C@%KOS|C#yy%ft-me^|`?afcty{2#tnH~uIlWl_Itc>SK$di;*Z(7B5; zHl5OX^$tE_>M&^;XB1`Z{}QR?5dSZ==H*TQ0^RflpBG7_HZS^rDX5R^%*#pDvn+b3 z+@t@GbPX6Ve}`5F6`Yuv>UCOx#lYWza(3~^f-zBZepJ%6aUL>BchMI zj2`!L!x9JB>rEs&lXKb9hS}gjH`Z-Br=^K$9F4U}`;6BZfKM6lYg!iIRr>jk%P|`=*%w=|0c0}@_H9lC~~kz0eM%9h4FVG zmC|Q?+Zl7~ha}=QN80?GKnt&IwriN|pSPKSpMSY7;$w_cu`d31p(u&?kT+{d#C|@A zcvsu(E7rvTUoT5+)wVwOP!d*n_2DZz)Q8F`0v z$U~x-fCEBM7g(!9_+SEShtczL-DHim!}Ief1qeobq#g3#v@s-mlYlOi^O^YQz&ZzX zk&D4m26O?N%Hz=jjNyQOSjsx^C2-AN@yUmj1k4;ZX2&>o6~+|86mZXeG++A^@EEux zrhPcC&mry?O;=;s09wucqG^%#Dbzw1@*wcjKAhzb=qtE5dvCn$gag9_Qx-4~rJ!I2wYImjXVc_}JU2iKB#<-`k22#kj*8sJX~d7yVyBV$ zdxx4~lnYx;Da8kzC{Igpq5)%gr+mPP2BZRd$d?9;#oYL>T(!g=*AkmVqW?dsP5n=5 z)BclMI@>W)j)>9t*#xl%LY1%PRX$q`0x%(k=)R~BM`U*r@wF&l`<;wg1s?h z0rjlyNV#KP0dIfmy8wL>`_F}7-%<80T7?v9eottAGvGHLej4#FYs5oL|Nqv+3;&`0 zMK|rzI$PVGW9Ml}Z*f;0+cOf|&^MT4B&1bYXt1oAzlwE;#J|IQN67)wJ1l$@M)y9g zrB7?Qe?WEEfCcQ+62t4x*mMN!6RROUoOKE)`DfGdWnH``PYHmW@t>VYygRWY|K3mp zl>Miw54(%vq2+XvefGE<$?lGlL-Ib-^7+!;3|fG*?uK?FMKg%K4Q~xC!gqWLf3R(9 z|B>{PkVI;J75}NEpEwjbb{Z|<9kNOba#^eRUvZ)3LCy2cjn)S*{)hNWZsIl4YEeuB zD@c>WHQp(pR!i^?Ey0`rr1wvszWtvR?))e9THXsI*^FdNdszpUZJYegASSptOti(4nI&XX&#lb+ zUf$$pfp*-lSVBD%{TI=eqr{RywAJE(X)BdT0IerT^W<~>YbVr5Ui{Kf$gtIsF|szO zb+Ccuc)G}DMjC9pkvY>)fv6NNOxq846Ju$w@s8Kw4IW*?V>CJl#Th|ekbDLEqB*VDTCdinEyA? zcDJlR4YLt%CKUh6F6UJu)mubRX6A7PC1~D6)UvrN;F0QuN7Cv8dXcj4P*CyU=d=3L zCZeG%hVZ1x>$Fu?@3rYr&$1BbA@u-P`C*916mkFLJ-_oX_qd8#4d846j03Pmt$}OT zTEy6{YTgjDhV_D7ORHMVwR(urmDCQG#A_QkBRuT0<490m$-Liz$B|cwc^t`^=!p>d zQ10(s5Ho->VhkxMh@D2#%qx7s38l6TIE=84k~MKxkV9-H+WWkhyYvb>vyw)J57Csw z*tg&;`7_8RWL)!>U@h6wMow;^ngl+Z)>^9(7O;ly4nMI!1_ll zbWapQhy2U$BRs@_j2e4Z(`O=d{9m7K4Y3M!WVu!L#cKNOXgv%+6h?_2#=_F+@i6KvlpW%)P=v?< zEJcRjQW?M$yVQvs+s)##u=oL%$(shYH;WcIPQzfk2bznCab4|qy{)RN@EqI*(v&bf zjp4S#4KoZES)YCfbH&F@ccYTpMvUz=d7*SG)t^Uhdc1K@)a_5+fQ{ zyHeZx7>zh8nP?AU_U2z9=6j?I@;7VL*U{5k0+$tu*&?Qs$3-vXg1WB@oS}*l$8DOO z0Tbb$^Q%N{;w-g(m5u1<*)MKd>iABHB*omqxuW%pq+Xe==+%nBkf@Zf#bbdzy z*Nu>M?WMC1*~x6WY=$+IGMo*o1)EPGkxwhW-Y^IhIO?U&@uV-;)vq@SYSsw$WDcD; z!#YuF-Y21vz)9)XeOQW@s_~FAzF()L{Q7vmIf)hK@Xz1b{v4c&sjMRh58sLGZVn7T zE#Ol)F$#Thw=-ce@`R9nvj)zKHxifzzCrpJ_ab9Gtzf&+#1vw_pr%pX{lu&ECpWvAUn90R)imh)iz=qU7puaLH{iM1O(7?}#c7au6-ghYF4W6W;208#Uim@%YuN&>O=iFXuhk4hJA z=RN+aXzdeY=52N(AN=;Q(FF*_K@>Kl08a6b&b$Izrzkdds}NoM{z);N%p66izt*zG zB04?#h{y+{jtG)bM?kWPB=P2DlC+0)6w)c|&m#Im_%`Uhq<+IsLbg%c*^ld}Y4gd+ z@|82nhfPk5FtJITedQ^Y%-cYsYLL2m|4rJ{Iky;0c5d|2n2;$Sw#Smtf;q%e5$7dq zVwNq8ow{h61;4jVNbGN+<5=4!dax?Kyuxd^OSWy_xV1dRFlQz^XSy85v~~7`B)hA` zSdcT_SdhLT#aO@ITjWegbQ!5dJ4#ZFjGg5MK50vcwd7Ioi3NxRrDW3SYgEg delta 72844 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 Dmcqh| diff --git a/cyw43-firmware/43439A0_clm.bin b/cyw43-firmware/43439A0_clm.bin index 6e3ba786b2b496ef3d347d8f8150cb433f684692..1fedd753ac255eb8feb4b81a8214d4070a8e414c 100755 GIT binary patch delta 67 zcmbQBIzd&%$;aQxhJk^hTJ^08BamhSVh{j{1qd-P@W~3i+^8~>Nx)dwz(m);NWsY1 P%GlV-z+&@Rro;RIogxiR delta 67 zcmbQBIzd&%$;aQxhJk@WTlJ|CBamhSVh{j{1qd-P+-_tQ->5Q^Nx)Fo&`{UNQo+c` P%EZXZ*l_b%ro;RInTQQo From cffb819e61a9cc87cad9a5351669e7dd28eeeba4 Mon Sep 17 00:00:00 2001 From: Brandon Ros Date: Fri, 28 Jul 2023 17:34:07 -0400 Subject: [PATCH 38/41] changelog --- cyw43-firmware/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cyw43-firmware/README.md b/cyw43-firmware/README.md index 7381fdc56..db3d9c9cf 100644 --- a/cyw43-firmware/README.md +++ b/cyw43-firmware/README.md @@ -2,4 +2,8 @@ Firmware obtained from https://github.com/Infineon/wifi-host-driver/tree/master/WiFi_Host_Driver/resources/firmware/COMPONENT_43439 -Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt) \ No newline at end of file +Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt) + +## Changelog + +* 2023-07-28: synced with `ad3bad0` - Update 43439 fw from 7.95.55 ot 7.95.62 From 29acc465017ae0b09ffb9d6364c9052e8b5f3937 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 Jul 2023 23:47:07 +0200 Subject: [PATCH 39/41] core::fmt devours your RAM and flash and explodes your stack. (#1708) --- embassy-net-esp-hosted/src/control.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index 79f8cde7b..37f220da0 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -1,5 +1,4 @@ use ch::driver::LinkState; -use defmt::Debug2Format; use embassy_net_driver_channel as ch; use heapless::String; @@ -57,7 +56,6 @@ impl<'a> Control<'a> { let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; - debug!("======= {:?}", Debug2Format(&resp)); assert_eq!(resp.resp, 0); self.state_ch.set_link_state(LinkState::Up); } From d39404cddaa188790040fab00fe317205d4e5ab2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 Jul 2023 23:49:37 +0200 Subject: [PATCH 40/41] fix flaky test wifi_esp_hosted_perf --- tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index 398ab9d27..e2adfe0be 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -118,9 +118,9 @@ const WIFI_NETWORK: &str = "EmbassyTest"; const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; const TEST_DURATION: usize = 10; -const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 150; -const TEST_EXPECTED_UPLOAD_KBPS: usize = 150; -const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 150; +const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 50; +const TEST_EXPECTED_UPLOAD_KBPS: usize = 50; +const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 50; const RX_BUFFER_SIZE: usize = 4096; const TX_BUFFER_SIZE: usize = 4096; const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); From fd47445d75eb3546b5581dbbcd9926611489b743 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 Jul 2023 23:58:47 +0200 Subject: [PATCH 41/41] cyw43: Update firmware in HIL test. --- tests/rp/src/bin/cyw43-perf.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index bc127e2e5..fffdabc9b 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -48,9 +48,9 @@ async fn main(spawner: Spawner) { } // cyw43 firmware needs to be flashed manually: - // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x101c0000 + // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x101b0000 // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x101f8000 - let fw = unsafe { core::slice::from_raw_parts(0x101c0000 as *const u8, 224190) }; + let fw = unsafe { core::slice::from_raw_parts(0x101b0000 as *const u8, 230321) }; let clm = unsafe { core::slice::from_raw_parts(0x101f8000 as *const u8, 4752) }; let pwr = Output::new(p.PIN_23, Level::Low);