From a46f33b2144df0b913b50bb8c78256e20bce84c8 Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Sat, 20 Aug 2022 16:37:51 -0400 Subject: [PATCH 001/298] Initial PDM driver --- embassy-nrf/src/chips/nrf52810.rs | 3 + embassy-nrf/src/chips/nrf52811.rs | 3 + embassy-nrf/src/chips/nrf52832.rs | 3 + embassy-nrf/src/chips/nrf52833.rs | 3 + embassy-nrf/src/chips/nrf52840.rs | 3 + embassy-nrf/src/lib.rs | 8 ++ embassy-nrf/src/pdm.rs | 185 ++++++++++++++++++++++++++++++ examples/nrf/Cargo.toml | 1 + examples/nrf/src/bin/pdm.rs | 34 ++++++ 9 files changed, 243 insertions(+) create mode 100644 embassy-nrf/src/pdm.rs create mode 100644 examples/nrf/src/bin/pdm.rs diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index faa52d8f..3e500098 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -128,6 +128,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // PDM + PDM, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index bbdf1cbe..25c7c0d9 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -128,6 +128,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // PDM + PDM, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 18b8eda6..2c6276d4 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -138,6 +138,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // PDM + PDM, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 39a0f93f..3b33907d 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -158,6 +158,9 @@ embassy_hal_common::peripherals! { // QDEC QDEC, + + // PDM + PDM, } #[cfg(feature = "nightly")] diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index e3d8f34a..ae59f8b2 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -161,6 +161,9 @@ embassy_hal_common::peripherals! { // TEMP TEMP, + + // PDM + PDM, } #[cfg(feature = "nightly")] diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index f3b3ca0c..20589195 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -103,6 +103,14 @@ pub mod uarte; pub mod usb; #[cfg(not(feature = "_nrf5340"))] pub mod wdt; +#[cfg(any( + feature = "nrf52810", + feature = "nrf52811", + feature = "nrf52832", + feature = "nrf52833", + feature = "nrf52840", +))] +pub mod pdm; // This mod MUST go last, so that it sees all the `impl_foo!` macros #[cfg_attr(feature = "nrf52805", path = "chips/nrf52805.rs")] diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs new file mode 100644 index 00000000..629eab99 --- /dev/null +++ b/embassy-nrf/src/pdm.rs @@ -0,0 +1,185 @@ +#![macro_use] + +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_util::waitqueue::AtomicWaker; +use futures::future::poll_fn; +use pac::{pdm, PDM}; +use pdm::mode::{EDGE_A, OPERATION_A}; +use fixed::types::I7F1; + +use crate::interrupt::InterruptExt; +use crate::gpio::Pin as GpioPin; +use crate::{interrupt, pac, peripherals, Peripheral}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error {} + +/// One-shot and continuous PDM. +pub struct Pdm<'d> { + _p: PeripheralRef<'d, peripherals::PDM>, +} + +static WAKER: AtomicWaker = AtomicWaker::new(); + +/// Used to configure the PDM peripheral. +/// +/// See the `Default` impl for suitable default values. +#[non_exhaustive] +pub struct Config { + /// Clock + /// Clock ratio + /// Channels + pub channels: Channels, + /// Edge to sample on + pub left_edge: Edge, + /// Gain left in dB + pub gain_left: I7F1, + /// Gain right in dB + pub gain_right: I7F1, +} + +impl Default for Config { + /// Default configuration for single channel sampling. + fn default() -> Self { + Self { + channels: Channels::Stereo, + left_edge: Edge::FallingEdge, + gain_left: I7F1::ZERO, + gain_right: I7F1::ZERO, + } + } +} + +/// The state of a continuously running sampler. While it reflects +/// the progress of a sampler, it also signals what should be done +/// next. For example, if the sampler has stopped then the Pdm implementation +/// can then tear down its infrastructure. +#[derive(PartialEq)] +pub enum SamplerState { + Sampled, + Stopped, +} + +impl<'d> Pdm<'d> { + pub fn new( + pdm: impl Peripheral

+ 'd, + irq: impl Peripheral

+ 'd, + data: impl Peripheral

+ 'd, + clock: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(pdm, irq, data, clock); + + let r = unsafe { &*PDM::ptr() }; + + let Config { channels, left_edge, gain_left, gain_right } = config; + + // Configure channels + r.enable.write(|w| w.enable().enabled()); + // TODO: Clock control + r.mode.write(|w| { + w.operation().variant(channels.into()); + w.edge().variant(left_edge.into()); + w + }); + + r.psel.din.write(|w| unsafe { w.bits(data.psel_bits()) }); + r.psel.clk.write(|w| unsafe { w.bits(clock.psel_bits()) }); + + // Disable all events interrupts + r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); + + irq.set_handler(Self::on_interrupt); + irq.unpend(); + irq.enable(); + + Self { _p: pdm } + } + + fn on_interrupt(_ctx: *mut ()) { + let r = Self::regs(); + + if r.events_end.read().bits() != 0 { + r.intenclr.write(|w| w.end().clear()); + WAKER.wake(); + } + + if r.events_started.read().bits() != 0 { + r.intenclr.write(|w| w.started().clear()); + WAKER.wake(); + } + } + + fn regs() -> &'static pdm::RegisterBlock { + unsafe { &*PDM::ptr() } + } + + /// One shot sampling. If the PDM is configured for multiple channels, the samples will be interleaved. + pub async fn sample(&mut self, buf: &mut [i16; N]) { + let r = Self::regs(); + + // Set up the DMA + r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(buf.as_mut_ptr() as u32) }); + r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) }); + + // Reset and enable the end event + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); + + // Don't reorder the start event before the previous writes. Hopefully self + // wouldn't happen anyway. + compiler_fence(Ordering::SeqCst); + + r.tasks_start.write(|w| { w.tasks_start().set_bit() }); + + // Wait for 'end' event. + poll_fn(|cx| { + let r = Self::regs(); + + WAKER.register(cx.waker()); + + if r.events_end.read().bits() != 0 { + r.events_end.reset(); + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum Edge { + FallingEdge, + RisingEdge, +} + +impl From for EDGE_A { + fn from(edge: Edge) -> Self { + match edge { + Edge::FallingEdge => EDGE_A::LEFTFALLING, + Edge::RisingEdge => EDGE_A::LEFTRISING, + } + } +} + +#[derive(Clone, Copy, PartialEq)] +pub enum Channels { + Stereo, + Mono, +} + +impl From for OPERATION_A { + fn from(ch: Channels) -> Self { + match ch { + Channels::Stereo => OPERATION_A::STEREO, + Channels::Mono => OPERATION_A::MONO, + } + } +} \ No newline at end of file diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 2fcc3122..673bcfc6 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -27,6 +27,7 @@ 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"] } rand = { version = "0.8.4", default-features = false } +fixed = "1.10.0" embedded-storage = "0.3.0" usbd-hid = "0.5.2" serde = { version = "1.0.136", default-features = false } diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf/src/bin/pdm.rs new file mode 100644 index 00000000..d5e90e27 --- /dev/null +++ b/examples/nrf/src/bin/pdm.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::interrupt; +use embassy_nrf::pdm::{Config, Channels, Pdm}; +use embassy_time::{Duration, Timer}; +use fixed::types::I7F1; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_p: Spawner) { + let mut p = embassy_nrf::init(Default::default()); + let mut config = Config::default(); + // Pins are correct for the onboard microphone on the Feather nRF52840 Sense. + config.channels = Channels::Mono; + config.gain_left = I7F1::from_bits(5); // 2.5 dB + let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), &mut p.P0_00, &mut p.P0_01, config); + + loop { + let mut buf = [0; 128]; + pdm.sample(&mut buf).await; + info!( + "{} samples, min {=i16}, max {=i16}, mean {=i16}", + buf.len(), + buf.iter().min().unwrap(), + buf.iter().max().unwrap(), + (buf.iter().map(|v| i32::from(*v)).sum::() / buf.len() as i32) as i16, + ); + Timer::after(Duration::from_millis(100)).await; + } +} From 530f192acceb5a10c416e1823dc27a749e68b7dc Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Sat, 20 Aug 2022 17:08:29 -0400 Subject: [PATCH 002/298] Set gain at runtime --- embassy-nrf/src/pdm.rs | 21 +++++++++++++++++++++ examples/nrf/Cargo.toml | 1 + examples/nrf/src/bin/pdm.rs | 29 +++++++++++++++++++---------- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 629eab99..b3cc8760 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -88,6 +88,8 @@ impl<'d> Pdm<'d> { w }); + Self::_set_gain(r, gain_left, gain_right); + r.psel.din.write(|w| unsafe { w.bits(data.psel_bits()) }); r.psel.clk.write(|w| unsafe { w.bits(clock.psel_bits()) }); @@ -115,6 +117,18 @@ impl<'d> Pdm<'d> { } } + fn _set_gain(r: &pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) { + let gain_left = gain_left.saturating_add(I7F1::from_bits(40)).saturating_to_num::().clamp(0, 0x50); + let gain_right = gain_right.saturating_add(I7F1::from_bits(40)).saturating_to_num::().clamp(0, 0x50); + + r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) }); + r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) }); + } + + pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) { + Self::_set_gain(Self::regs(), gain_left, gain_right) + } + fn regs() -> &'static pdm::RegisterBlock { unsafe { &*PDM::ptr() } } @@ -154,6 +168,13 @@ impl<'d> Pdm<'d> { } } +impl<'d> Drop for Pdm<'d> { + fn drop(&mut self) { + let r = Self::regs(); + r.enable.write(|w| w.enable().disabled()); + } +} + #[derive(Clone, Copy, PartialEq)] pub enum Edge { FallingEdge, diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index 673bcfc6..d0567ba8 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -31,3 +31,4 @@ fixed = "1.10.0" embedded-storage = "0.3.0" usbd-hid = "0.5.2" serde = { version = "1.0.136", default-features = false } +num-integer = { version = "0.1.45", default-features = false } diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf/src/bin/pdm.rs index d5e90e27..a73d01fb 100644 --- a/examples/nrf/src/bin/pdm.rs +++ b/examples/nrf/src/bin/pdm.rs @@ -8,6 +8,7 @@ use embassy_nrf::interrupt; use embassy_nrf::pdm::{Config, Channels, Pdm}; use embassy_time::{Duration, Timer}; use fixed::types::I7F1; +use num_integer::Roots; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -20,15 +21,23 @@ async fn main(_p: Spawner) { let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), &mut p.P0_00, &mut p.P0_01, config); loop { - let mut buf = [0; 128]; - pdm.sample(&mut buf).await; - info!( - "{} samples, min {=i16}, max {=i16}, mean {=i16}", - buf.len(), - buf.iter().min().unwrap(), - buf.iter().max().unwrap(), - (buf.iter().map(|v| i32::from(*v)).sum::() / buf.len() as i32) as i16, - ); - Timer::after(Duration::from_millis(100)).await; + for gain in [I7F1::from_num(-20), I7F1::from_num(0), I7F1::from_num(20)] { + pdm.set_gain(gain, gain); + info!("Gain = {} dB", defmt::Debug2Format(&gain)); + for _ in 0..10 { + let mut buf = [0; 128]; + pdm.sample(&mut buf).await; + info!( + "{} samples, min {=i16}, max {=i16}, RMS {=i16}", + buf.len(), + buf.iter().min().unwrap(), + buf.iter().max().unwrap(), + ( + buf.iter().map(|v| i32::from(*v).pow(2)).fold(0i32, |a,b| a.saturating_add(b)) + / buf.len() as i32).sqrt() as i16, + ); + Timer::after(Duration::from_millis(100)).await; + } + } } } From 0963b5f92c9588ab00f556a6c521fad059eac72e Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Sat, 20 Aug 2022 17:58:54 -0400 Subject: [PATCH 003/298] Add continuous PDM sampling with example --- embassy-nrf/src/pdm.rs | 118 ++++++++++++++++++++++++- examples/nrf/src/bin/pdm.rs | 3 +- examples/nrf/src/bin/pdm_continuous.rs | 50 +++++++++++ 3 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 examples/nrf/src/bin/pdm_continuous.rs diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index b3cc8760..db4c74af 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -115,6 +115,11 @@ impl<'d> Pdm<'d> { r.intenclr.write(|w| w.started().clear()); WAKER.wake(); } + + if r.events_stopped.read().bits() != 0 { + r.intenclr.write(|w| w.stopped().clear()); + WAKER.wake(); + } } fn _set_gain(r: &pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) { @@ -141,15 +146,20 @@ impl<'d> Pdm<'d> { r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(buf.as_mut_ptr() as u32) }); r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) }); - // Reset and enable the end event + // Reset and enable the events r.events_end.reset(); - r.intenset.write(|w| w.end().set()); + r.events_stopped.reset(); + r.intenset.write(|w| { + w.end().set(); + w.stopped().set(); + w + }); // Don't reorder the start event before the previous writes. Hopefully self // wouldn't happen anyway. compiler_fence(Ordering::SeqCst); - r.tasks_start.write(|w| { w.tasks_start().set_bit() }); + r.tasks_start.write(|w| w.tasks_start().set_bit()); // Wait for 'end' event. poll_fn(|cx| { @@ -158,7 +168,109 @@ impl<'d> Pdm<'d> { WAKER.register(cx.waker()); if r.events_end.read().bits() != 0 { + // END means the whole buffer has been received. r.events_end.reset(); + // Note that the beginning of the buffer might be overwritten before the task fully stops :( + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + } + + if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + } + + /// Continuous sampling with double buffers. + /// + /// A TIMER and two PPI peripherals are passed in so that precise sampling + /// can be attained. The sampling interval is expressed by selecting a + /// timer clock frequency to use along with a counter threshold to be reached. + /// For example, 1KHz can be achieved using a frequency of 1MHz and a counter + /// threshold of 1000. + /// + /// A sampler closure is provided that receives the buffer of samples, noting + /// that the size of this buffer can be less than the original buffer's size. + /// A command is return from the closure that indicates whether the sampling + /// should continue or stop. + /// + /// NOTE: The time spent within the callback supplied should not exceed the time + /// taken to acquire the samples into a single buffer. You should measure the + /// time taken by the callback and set the sample buffer size accordingly. + /// Exceeding this time can lead to samples becoming dropped. + pub async fn run_task_sampler( + &mut self, + bufs: &mut [[i16; N]; 2], + mut sampler: S, + ) where + S: FnMut(&[i16; N]) -> SamplerState, + { + let r = Self::regs(); + + r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) }); + r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) }); + + // Reset and enable the events + r.events_end.reset(); + r.events_started.reset(); + r.events_stopped.reset(); + r.intenset.write(|w| { + w.end().set(); + w.started().set(); + w.stopped().set(); + w + }); + + // Don't reorder the start event before the previous writes. Hopefully self + // wouldn't happen anyway. + compiler_fence(Ordering::SeqCst); + + r.tasks_start.write(|w| { w.tasks_start().set_bit() }); + + let mut current_buffer = 0; + + let mut done = false; + + // Wait for events and complete when the sampler indicates it has had enough. + poll_fn(|cx| { + let r = Self::regs(); + + WAKER.register(cx.waker()); + + if r.events_end.read().bits() != 0 { + compiler_fence(Ordering::SeqCst); + + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); + + if !done { // Discard the last buffer after the user requested a stop. + if sampler(&bufs[current_buffer]) == SamplerState::Sampled { + let next_buffer = 1 - current_buffer; + current_buffer = next_buffer; + } else { + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + done = true; + }; + }; + } + + if r.events_started.read().bits() != 0 { + r.events_started.reset(); + r.intenset.write(|w| w.started().set()); + + let next_buffer = 1 - current_buffer; + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(bufs[next_buffer].as_mut_ptr() as u32) }); + } + + if r.events_stopped.read().bits() != 0 { + r.events_stopped.reset(); + r.intenset.write(|w| w.stopped().set()); + return Poll::Ready(()); } diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf/src/bin/pdm.rs index a73d01fb..85a59a52 100644 --- a/examples/nrf/src/bin/pdm.rs +++ b/examples/nrf/src/bin/pdm.rs @@ -25,7 +25,7 @@ async fn main(_p: Spawner) { pdm.set_gain(gain, gain); info!("Gain = {} dB", defmt::Debug2Format(&gain)); for _ in 0..10 { - let mut buf = [0; 128]; + let mut buf = [0; 1500]; pdm.sample(&mut buf).await; info!( "{} samples, min {=i16}, max {=i16}, RMS {=i16}", @@ -36,6 +36,7 @@ async fn main(_p: Spawner) { buf.iter().map(|v| i32::from(*v).pow(2)).fold(0i32, |a,b| a.saturating_add(b)) / buf.len() as i32).sqrt() as i16, ); + info!("samples = {}", &buf); Timer::after(Duration::from_millis(100)).await; } } diff --git a/examples/nrf/src/bin/pdm_continuous.rs b/examples/nrf/src/bin/pdm_continuous.rs new file mode 100644 index 00000000..e7d1806b --- /dev/null +++ b/examples/nrf/src/bin/pdm_continuous.rs @@ -0,0 +1,50 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::interrupt; +use embassy_nrf::pdm::{Config, Channels, Pdm, SamplerState}; +use embassy_nrf::timer::Frequency; +use fixed::types::I7F1; +use num_integer::Roots; +use {defmt_rtt as _, panic_probe as _}; + +// Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer + +#[embassy_executor::main] +async fn main(_p: Spawner) { + let mut p = embassy_nrf::init(Default::default()); + let mut config = Config::default(); + // Pins are correct for the onboard microphone on the Feather nRF52840 Sense. + config.channels = Channels::Mono; + config.gain_left = I7F1::from_bits(5); // 2.5 dB + let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), &mut p.P0_00, &mut p.P0_01, config); + + let mut bufs = [[0; 500]; 2]; + + pdm + .run_task_sampler( + &mut bufs, + move |buf| { + // NOTE: It is important that the time spent within this callback + // does not exceed the time taken to acquire the 1500 samples we + // have in this example, which would be 10us + 2us per + // sample * 1500 = 18ms. You need to measure the time taken here + // and set the sample buffer size accordingly. Exceeding this + // time can lead to the peripheral re-writing the other buffer. + info!( + "{} samples, min {=i16}, max {=i16}, RMS {=i16}", + buf.len(), + buf.iter().min().unwrap(), + buf.iter().max().unwrap(), + ( + buf.iter().map(|v| i32::from(*v).pow(2)).fold(0i32, |a,b| a.saturating_add(b)) + / buf.len() as i32).sqrt() as i16, + ); + SamplerState::Sampled + }, + ) + .await; +} From 3d26573c6b36c670a170279db49e7adc5e37466b Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Sun, 21 Aug 2022 01:44:04 -0400 Subject: [PATCH 004/298] Discard the first N samples due to transients --- embassy-nrf/src/pdm.rs | 22 +++++++++++++++++++--- examples/nrf/src/bin/pdm.rs | 8 +++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index db4c74af..c8375b1e 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -139,7 +139,8 @@ impl<'d> Pdm<'d> { } /// One shot sampling. If the PDM is configured for multiple channels, the samples will be interleaved. - pub async fn sample(&mut self, buf: &mut [i16; N]) { + /// The first samples from the PDM peripheral and microphone usually contain garbage data, so the discard parameter sets the number of complete buffers to discard before returning. + pub async fn sample(&mut self, mut discard: usize, buf: &mut [i16; N]) { let r = Self::regs(); // Set up the DMA @@ -148,9 +149,11 @@ impl<'d> Pdm<'d> { // Reset and enable the events r.events_end.reset(); + r.events_started.reset(); r.events_stopped.reset(); r.intenset.write(|w| { w.end().set(); + w.started().set(); w.stopped().set(); w }); @@ -168,13 +171,26 @@ impl<'d> Pdm<'d> { WAKER.register(cx.waker()); if r.events_end.read().bits() != 0 { + compiler_fence(Ordering::SeqCst); // END means the whole buffer has been received. r.events_end.reset(); - // Note that the beginning of the buffer might be overwritten before the task fully stops :( - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + r.intenset.write(|w| w.end().set()); + + if discard > 0 { + discard -= 1; + } else { + // Note that the beginning of the buffer might be overwritten before the task fully stops :( + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + } + } + + if r.events_started.read().bits() != 0 { + compiler_fence(Ordering::SeqCst); + r.events_started.reset(); } if r.events_stopped.read().bits() != 0 { + compiler_fence(Ordering::SeqCst); r.events_stopped.reset(); return Poll::Ready(()); } diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf/src/bin/pdm.rs index 85a59a52..605eca59 100644 --- a/examples/nrf/src/bin/pdm.rs +++ b/examples/nrf/src/bin/pdm.rs @@ -26,14 +26,16 @@ async fn main(_p: Spawner) { info!("Gain = {} dB", defmt::Debug2Format(&gain)); for _ in 0..10 { let mut buf = [0; 1500]; - pdm.sample(&mut buf).await; + pdm.sample(5, &mut buf).await; + let mean = (buf.iter().map(|v| i32::from(*v)).sum::() / buf.len() as i32) as i16; info!( - "{} samples, min {=i16}, max {=i16}, RMS {=i16}", + "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}", buf.len(), buf.iter().min().unwrap(), buf.iter().max().unwrap(), + mean, ( - buf.iter().map(|v| i32::from(*v).pow(2)).fold(0i32, |a,b| a.saturating_add(b)) + buf.iter().map(|v| i32::from(*v - mean).pow(2)).fold(0i32, |a,b| a.saturating_add(b)) / buf.len() as i32).sqrt() as i16, ); info!("samples = {}", &buf); From 029713eca0e6c4a17283891de77c69f27bbf1a54 Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Sun, 21 Aug 2022 02:04:11 -0400 Subject: [PATCH 005/298] Stop PDM sampling when future is dropped --- embassy-nrf/src/pdm.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index c8375b1e..6a070e7b 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -4,6 +4,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_common::drop::OnDrop; use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use pac::{pdm, PDM}; @@ -149,11 +150,9 @@ impl<'d> Pdm<'d> { // Reset and enable the events r.events_end.reset(); - r.events_started.reset(); r.events_stopped.reset(); r.intenset.write(|w| { w.end().set(); - w.started().set(); w.stopped().set(); w }); @@ -164,6 +163,12 @@ impl<'d> Pdm<'d> { r.tasks_start.write(|w| w.tasks_start().set_bit()); + let ondrop = OnDrop::new(|| { + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + // N.B. It would be better if this were async, but Drop only support sync code. + while r.events_stopped.read().bits() != 0 {} + }); + // Wait for 'end' event. poll_fn(|cx| { let r = Self::regs(); @@ -183,21 +188,14 @@ impl<'d> Pdm<'d> { r.tasks_stop.write(|w| w.tasks_stop().set_bit()); } } - - if r.events_started.read().bits() != 0 { - compiler_fence(Ordering::SeqCst); - r.events_started.reset(); - } - if r.events_stopped.read().bits() != 0 { - compiler_fence(Ordering::SeqCst); - r.events_stopped.reset(); return Poll::Ready(()); } Poll::Pending }) .await; + ondrop.defuse(); } /// Continuous sampling with double buffers. @@ -250,6 +248,12 @@ impl<'d> Pdm<'d> { let mut done = false; + let ondrop = OnDrop::new(|| { + r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + // N.B. It would be better if this were async, but Drop only support sync code. + while r.events_stopped.read().bits() != 0 {} + }); + // Wait for events and complete when the sampler indicates it has had enough. poll_fn(|cx| { let r = Self::regs(); @@ -284,15 +288,13 @@ impl<'d> Pdm<'d> { } if r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); - r.intenset.write(|w| w.stopped().set()); - return Poll::Ready(()); } Poll::Pending }) .await; + ondrop.defuse(); } } From ed97e61dbecc636c3cc9f67778d4b7eb48cff893 Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Sun, 21 Aug 2022 02:16:26 -0400 Subject: [PATCH 006/298] PDM clock frequency control --- embassy-nrf/src/pdm.rs | 13 ++++++++++--- examples/nrf/src/bin/pdm_continuous.rs | 13 ++++++++----- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 6a070e7b..b58a0e97 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -9,6 +9,8 @@ use embassy_util::waitqueue::AtomicWaker; use futures::future::poll_fn; use pac::{pdm, PDM}; use pdm::mode::{EDGE_A, OPERATION_A}; +pub use pdm::pdmclkctrl::FREQ_A as Frequency; +pub use pdm::ratio::RATIO_A as Ratio; use fixed::types::I7F1; use crate::interrupt::InterruptExt; @@ -32,8 +34,10 @@ static WAKER: AtomicWaker = AtomicWaker::new(); /// See the `Default` impl for suitable default values. #[non_exhaustive] pub struct Config { - /// Clock + /// Clock frequency + pub frequency: Frequency, /// Clock ratio + pub ratio: Ratio, /// Channels pub channels: Channels, /// Edge to sample on @@ -48,6 +52,8 @@ impl Default for Config { /// Default configuration for single channel sampling. fn default() -> Self { Self { + frequency: Frequency::DEFAULT, + ratio: Ratio::RATIO80, channels: Channels::Stereo, left_edge: Edge::FallingEdge, gain_left: I7F1::ZERO, @@ -78,11 +84,12 @@ impl<'d> Pdm<'d> { let r = unsafe { &*PDM::ptr() }; - let Config { channels, left_edge, gain_left, gain_right } = config; + let Config { frequency, ratio, channels, left_edge, gain_left, gain_right } = config; // Configure channels r.enable.write(|w| w.enable().enabled()); - // TODO: Clock control + r.pdmclkctrl.write(|w| w.freq().variant(frequency)); + r.ratio.write(|w| w.ratio().variant(ratio)); r.mode.write(|w| { w.operation().variant(channels.into()); w.edge().variant(left_edge.into()); diff --git a/examples/nrf/src/bin/pdm_continuous.rs b/examples/nrf/src/bin/pdm_continuous.rs index e7d1806b..33ba1e27 100644 --- a/examples/nrf/src/bin/pdm_continuous.rs +++ b/examples/nrf/src/bin/pdm_continuous.rs @@ -5,8 +5,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_nrf::interrupt; -use embassy_nrf::pdm::{Config, Channels, Pdm, SamplerState}; -use embassy_nrf::timer::Frequency; +use embassy_nrf::pdm::{Config, Channels, Pdm, SamplerState, Frequency, Ratio}; use fixed::types::I7F1; use num_integer::Roots; use {defmt_rtt as _, panic_probe as _}; @@ -18,11 +17,13 @@ async fn main(_p: Spawner) { let mut p = embassy_nrf::init(Default::default()); let mut config = Config::default(); // Pins are correct for the onboard microphone on the Feather nRF52840 Sense. + config.frequency = Frequency::_1280K; // 16 kHz sample rate + config.ratio = Ratio::RATIO80; config.channels = Channels::Mono; config.gain_left = I7F1::from_bits(5); // 2.5 dB let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), &mut p.P0_00, &mut p.P0_01, config); - let mut bufs = [[0; 500]; 2]; + let mut bufs = [[0; 1024]; 2]; pdm .run_task_sampler( @@ -34,13 +35,15 @@ async fn main(_p: Spawner) { // sample * 1500 = 18ms. You need to measure the time taken here // and set the sample buffer size accordingly. Exceeding this // time can lead to the peripheral re-writing the other buffer. + let mean = (buf.iter().map(|v| i32::from(*v)).sum::() / buf.len() as i32) as i16; info!( - "{} samples, min {=i16}, max {=i16}, RMS {=i16}", + "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}", buf.len(), buf.iter().min().unwrap(), buf.iter().max().unwrap(), + mean, ( - buf.iter().map(|v| i32::from(*v).pow(2)).fold(0i32, |a,b| a.saturating_add(b)) + buf.iter().map(|v| i32::from(*v - mean).pow(2)).fold(0i32, |a,b| a.saturating_add(b)) / buf.len() as i32).sqrt() as i16, ); SamplerState::Sampled From 64154fec8cd7497992ef0d93f319b98215b8a84e Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Sun, 21 Aug 2022 02:43:13 -0400 Subject: [PATCH 007/298] Demonstrate FFT in example --- examples/nrf/Cargo.toml | 1 + examples/nrf/src/bin/pdm_continuous.rs | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index d0567ba8..876dcf73 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -32,3 +32,4 @@ embedded-storage = "0.3.0" usbd-hid = "0.5.2" serde = { version = "1.0.136", default-features = false } num-integer = { version = "0.1.45", default-features = false } +microfft = "0.5.0" diff --git a/examples/nrf/src/bin/pdm_continuous.rs b/examples/nrf/src/bin/pdm_continuous.rs index 33ba1e27..e78bc40d 100644 --- a/examples/nrf/src/bin/pdm_continuous.rs +++ b/examples/nrf/src/bin/pdm_continuous.rs @@ -3,11 +3,13 @@ #![feature(type_alias_impl_trait)] use defmt::info; +use core::cmp::Ordering; use embassy_executor::Spawner; use embassy_nrf::interrupt; use embassy_nrf::pdm::{Config, Channels, Pdm, SamplerState, Frequency, Ratio}; use fixed::types::I7F1; use num_integer::Roots; +use microfft::real::rfft_1024; use {defmt_rtt as _, panic_probe as _}; // Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer @@ -36,8 +38,10 @@ async fn main(_p: Spawner) { // and set the sample buffer size accordingly. Exceeding this // time can lead to the peripheral re-writing the other buffer. let mean = (buf.iter().map(|v| i32::from(*v)).sum::() / buf.len() as i32) as i16; + let (peak_freq_index, peak_mag) = fft_peak_freq(&buf); + let peak_freq = peak_freq_index * 16000 / buf.len(); info!( - "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}", + "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}, peak {} @ {} Hz", buf.len(), buf.iter().min().unwrap(), buf.iter().max().unwrap(), @@ -45,9 +49,27 @@ async fn main(_p: Spawner) { ( buf.iter().map(|v| i32::from(*v - mean).pow(2)).fold(0i32, |a,b| a.saturating_add(b)) / buf.len() as i32).sqrt() as i16, + peak_mag, peak_freq, ); SamplerState::Sampled }, ) .await; } + +fn fft_peak_freq(input: &[i16; 1024]) -> (usize, u32) { + let mut f = [0f32; 1024]; + for i in 0..input.len() { + f[i] = (input[i] as f32) / 32768.0; + } + // N.B. rfft_1024 does the FFT in-place so result is actually also a reference to f. + let result = rfft_1024(&mut f); + result[0].im = 0.0; + + result + .iter() + .map(|c| ((c.norm_sqr()*32768.0) as u32).sqrt()) + .enumerate() + .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Equal)) + .unwrap() +} \ No newline at end of file From 14eae9ca06f63a69ccc29d5fd9e1dec3848a3e98 Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Sun, 21 Aug 2022 12:40:51 -0400 Subject: [PATCH 008/298] Optimize pdm_continuous example --- examples/nrf/src/bin/pdm_continuous.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/nrf/src/bin/pdm_continuous.rs b/examples/nrf/src/bin/pdm_continuous.rs index e78bc40d..284a68af 100644 --- a/examples/nrf/src/bin/pdm_continuous.rs +++ b/examples/nrf/src/bin/pdm_continuous.rs @@ -68,8 +68,9 @@ fn fft_peak_freq(input: &[i16; 1024]) -> (usize, u32) { result .iter() - .map(|c| ((c.norm_sqr()*32768.0) as u32).sqrt()) + .map(|c| c.norm_sqr()) .enumerate() .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Equal)) + .map(|(i, v)| (i, ((v*32768.0) as u32).sqrt())) .unwrap() -} \ No newline at end of file +} From d896f80405aa8963877049ed999e4aba25d6e2bb Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Tue, 23 Aug 2022 23:02:48 -0400 Subject: [PATCH 009/298] util -> sync rename --- embassy-nrf/src/pdm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index b58a0e97..c2c54fba 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -5,7 +5,7 @@ use core::task::Poll; use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_hal_common::drop::OnDrop; -use embassy_util::waitqueue::AtomicWaker; +use embassy_sync::waitqueue::AtomicWaker; use futures::future::poll_fn; use pac::{pdm, PDM}; use pdm::mode::{EDGE_A, OPERATION_A}; From cdb3fb059f32a5669856390cfe379da71092d545 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 25 Jun 2023 15:33:57 -0500 Subject: [PATCH 010/298] stm32/hrtim: first draft --- embassy-stm32/build.rs | 10 ++ embassy-stm32/src/pwm/advanced_pwm.rs | 147 ++++++++++++++++++++++++++ embassy-stm32/src/pwm/mod.rs | 60 +++++++++++ embassy-stm32/src/timer/mod.rs | 39 +++++++ 4 files changed, 256 insertions(+) create mode 100644 embassy-stm32/src/pwm/advanced_pwm.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 40103d32..4856047c 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -589,6 +589,16 @@ fn main() { (("timer", "BKIN2"), quote!(crate::pwm::BreakInput2Pin)), (("timer", "BKIN2_COMP1"), quote!(crate::pwm::BreakInput2Comparator1Pin)), (("timer", "BKIN2_COMP2"), quote!(crate::pwm::BreakInput2Comparator2Pin)), + (("hrtim", "CHA1"), quote!(crate::pwm::ChannelAPin)), + (("hrtim", "CHA2"), quote!(crate::pwm::ChannelAComplementaryPin)), + (("hrtim", "CHB1"), quote!(crate::pwm::ChannelBPin)), + (("hrtim", "CHB2"), quote!(crate::pwm::ChannelBComplementaryPin)), + (("hrtim", "CHC1"), quote!(crate::pwm::ChannelCPin)), + (("hrtim", "CHC2"), quote!(crate::pwm::ChannelCComplementaryPin)), + (("hrtim", "CHD1"), quote!(crate::pwm::ChannelDPin)), + (("hrtim", "CHD2"), quote!(crate::pwm::ChannelDComplementaryPin)), + (("hrtim", "CHE1"), quote!(crate::pwm::ChannelEPin)), + (("hrtim", "CHE2"), quote!(crate::pwm::ChannelEComplementaryPin)), (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs new file mode 100644 index 00000000..39d60c50 --- /dev/null +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -0,0 +1,147 @@ +use core::marker::PhantomData; + +use embassy_hal_common::{into_ref, PeripheralRef}; + +use super::simple_pwm::*; +use super::*; +#[allow(unused_imports)] +use crate::gpio::sealed::{AFType, Pin}; +use crate::gpio::AnyPin; +use crate::time::Hertz; +use crate::Peripheral; + +// Re-implement the channels for hrtim +pub struct ChA; +pub struct ChB; +pub struct ChC; +pub struct ChD; +pub struct ChE; + +pub struct PwmPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, +} + +pub struct ComplementaryPwmPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, +} + +macro_rules! advanced_channel_impl { + ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => { + impl<'d, Perip: AdvancedCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + PwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + + impl<'d, Perip: AdvancedCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + ComplementaryPwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + }; +} + +advanced_channel_impl!(new_cha, ChA, ChannelAPin, ChannelAComplementaryPin); +advanced_channel_impl!(new_chb, ChB, ChannelBPin, ChannelBComplementaryPin); +advanced_channel_impl!(new_chc, ChC, ChannelCPin, ChannelCComplementaryPin); +advanced_channel_impl!(new_chd, ChD, ChannelDPin, ChannelDComplementaryPin); +advanced_channel_impl!(new_che, ChE, ChannelEPin, ChannelEComplementaryPin); + +pub struct AdvancedPwm<'d, T> { + inner: PeripheralRef<'d, T>, +} + +impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { + pub fn new( + tim: impl Peripheral

+ 'd, + _ch1: Option>, + _ch1n: Option>, + _ch2: Option>, + _ch2n: Option>, + _ch3: Option>, + _ch3n: Option>, + _ch4: Option>, + _ch4n: Option>, + freq: Hertz, + ) -> Self { + Self::new_inner(tim, freq) + } + + fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz) -> Self { + into_ref!(tim); + + T::enable(); + ::reset(); + + let mut this = Self { inner: tim }; + // + // this.inner.set_frequency(freq); + // this.inner.start(); + // + // this.inner.enable_outputs(true); + // + // this.inner + // .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); + // this.inner + // .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); + // this.inner + // .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); + // this.inner + // .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); + this + } + + pub fn enable(&mut self, channel: AdvancedChannel) { + // self.inner.enable_channel(channel, true); + // self.inner.enable_complementary_channel(channel, true); + } + + pub fn disable(&mut self, channel: AdvancedChannel) { + // self.inner.enable_complementary_channel(channel, false); + // self.inner.enable_channel(channel, false); + } + + pub fn set_freq(&mut self, freq: Hertz) { + // self.inner.set_frequency(freq); + } + + pub fn get_max_duty(&self) -> u16 { + todo!() + // self.inner.get_max_compare_value() + } + + pub fn set_duty(&mut self, channel: AdvancedChannel, duty: u16) { + // assert!(duty < self.get_max_duty()); + // self.inner.set_compare_value(channel, duty) + } + + /// Set the dead time as a proportion of max_duty + pub fn set_dead_time(&mut self, value: u16) { + // let (ckd, value) = compute_dead_time_value(value); + // + // self.inner.set_dead_time_clock_division(ckd); + // self.inner.set_dead_time_value(value); + } +} diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 5aba2663..4c203134 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -1,3 +1,5 @@ +#[cfg(hrtim_v1)] +pub mod advanced_pwm; pub mod complementary_pwm; pub mod simple_pwm; @@ -27,6 +29,29 @@ impl Channel { } } +#[cfg(hrtim_v1)] +#[derive(Clone, Copy)] +pub enum AdvancedChannel { + ChA, + ChB, + ChC, + ChD, + ChE, +} + +#[cfg(hrtim_v1)] +impl AdvancedChannel { + pub fn raw(&self) -> usize { + match self { + AdvancedChannel::ChA => 0, + AdvancedChannel::ChB => 1, + AdvancedChannel::ChC => 2, + AdvancedChannel::ChD => 3, + AdvancedChannel::ChE => 4, + } + } +} + #[derive(Clone, Copy)] pub enum OutputCompareMode { Frozen, @@ -57,6 +82,14 @@ impl From for stm32_metapac::timer::vals::Ocm { pub(crate) mod sealed { use super::*; + pub trait AdvancedCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { + fn enable_outputs(&mut self, enable: bool); + + fn set_output_compare_mode(&mut self, channel: AdvancedChannel, mode: OutputCompareMode); + + fn enable_channel(&mut self, channel: AdvancedChannel, enable: bool); + } + 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); @@ -89,6 +122,8 @@ pub(crate) mod sealed { } } +pub trait AdvancedCaptureCompare16bitInstance: sealed::AdvancedCaptureCompare16bitInstance + 'static {} + pub trait CaptureCompare16bitInstance: sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static { @@ -250,6 +285,20 @@ foreach_interrupt! { } }; + + ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { + impl crate::pwm::sealed::AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { + fn enable_outputs(&mut self, enable: bool) { todo!() } + + fn set_output_compare_mode(&mut self, channel: AdvancedChannel, mode: OutputCompareMode) { todo!() } + + fn enable_channel(&mut self, channel: AdvancedChannel, enable: bool) { todo!() } + } + + impl AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { + + } + }; } pin_trait!(Channel1Pin, CaptureCompare16bitInstance); @@ -267,3 +316,14 @@ pin_trait!(BreakInputComparator2Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); + +pin_trait!(ChannelAPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelAComplementaryPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelBPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelBComplementaryPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelCPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelCComplementaryPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelDPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelDComplementaryPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelEPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelEComplementaryPin, AdvancedCaptureCompare16bitInstance); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 09b7a377..34ad5db1 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -43,6 +43,21 @@ pub(crate) mod sealed { pub trait AdvancedControlInstance: GeneralPurpose16bitInstance { fn regs_advanced() -> crate::pac::timer::TimAdv; } + + #[cfg(hrtim_v1)] + pub trait HighResolutionControlInstance: RccPeripheral { + type Interrupt: interrupt::typelevel::Interrupt; + + fn regs_highres() -> crate::pac::hrtim::Hrtim; + + fn start(&mut self); + + fn stop(&mut self); + + fn reset(&mut self); + + fn set_frequency(&mut self, frequency: Hertz); + } } pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} @@ -51,6 +66,9 @@ pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'st pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {} +#[cfg(hrtim_v1)] +pub trait HighResolutionControlInstance: sealed::HighResolutionControlInstance + 'static {} + pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} #[allow(unused)] @@ -208,4 +226,25 @@ foreach_interrupt! { impl AdvancedControlInstance for crate::peripherals::$inst { } }; + + ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { + impl sealed::HighResolutionControlInstance for crate::peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + + fn regs_highres() -> crate::pac::hrtim::Hrtim { + crate::pac::$inst + } + + fn start(&mut self) { todo!() } + + fn stop(&mut self) { todo!() } + + fn reset(&mut self) { todo!() } + + fn set_frequency(&mut self, frequency: Hertz) { todo!() } + } + + impl HighResolutionControlInstance for crate::peripherals::$inst { + } + }; } From 71513ccb3905965aef2ae29a841dfdf4dfffe69a Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 26 Jun 2023 19:59:10 -0500 Subject: [PATCH 011/298] stm32/hrtim: impl. draft frequency computation --- embassy-stm32/src/pwm/mod.rs | 32 +++++--- embassy-stm32/src/timer/mod.rs | 138 ++++++++++++++++++++++++++++++++- 2 files changed, 155 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 4c203134..1838bfdf 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -29,7 +29,6 @@ impl Channel { } } -#[cfg(hrtim_v1)] #[derive(Clone, Copy)] pub enum AdvancedChannel { ChA, @@ -39,7 +38,6 @@ pub enum AdvancedChannel { ChE, } -#[cfg(hrtim_v1)] impl AdvancedChannel { pub fn raw(&self) -> usize { match self { @@ -82,6 +80,7 @@ impl From for stm32_metapac::timer::vals::Ocm { pub(crate) mod sealed { use super::*; + #[cfg(hrtim_v1)] pub trait AdvancedCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { fn enable_outputs(&mut self, enable: bool); @@ -122,6 +121,7 @@ pub(crate) mod sealed { } } +#[cfg(hrtim_v1)] pub trait AdvancedCaptureCompare16bitInstance: sealed::AdvancedCaptureCompare16bitInstance + 'static {} pub trait CaptureCompare16bitInstance: @@ -317,13 +317,21 @@ pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); -pin_trait!(ChannelAPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelAComplementaryPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelBPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelBComplementaryPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelCPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelCComplementaryPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelDPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelDComplementaryPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelEPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelEComplementaryPin, AdvancedCaptureCompare16bitInstance); +#[cfg(hrtim_v1)] +mod hrtim_pins { + use super::*; + + pin_trait!(ChannelAPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelAComplementaryPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelBPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelBComplementaryPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelCPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelCComplementaryPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelDPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelDComplementaryPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelEPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelEComplementaryPin, AdvancedCaptureCompare16bitInstance); +} + +#[cfg(hrtim_v1)] +pub use hrtim_pins::*; diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 34ad5db1..03b9b3cf 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,3 +1,6 @@ +#[cfg(hrtim_v1)] +use core::ops; + use stm32_metapac::timer::vals; use crate::interrupt; @@ -50,13 +53,64 @@ pub(crate) mod sealed { fn regs_highres() -> crate::pac::hrtim::Hrtim; + fn set_master_frequency(&mut self, frequency: Hertz); + + fn set_channel_frequency(&mut self, channel: usize, frequency: Hertz); + fn start(&mut self); fn stop(&mut self); fn reset(&mut self); + } +} - fn set_frequency(&mut self, frequency: Hertz); +#[cfg(hrtim_v1)] +#[derive(Clone, Copy)] +pub(crate) enum HighResolutionControlPrescaler { + Div1, + Div2, + Div4, + Div8, + Div16, + Div32, + Div64, + Div128, +} + +#[cfg(hrtim_v1)] +impl ops::Div for Hertz { + type Output = Hertz; + + fn div(self, rhs: HighResolutionControlPrescaler) -> Self::Output { + let divisor = match rhs { + HighResolutionControlPrescaler::Div1 => 1, + HighResolutionControlPrescaler::Div2 => 2, + HighResolutionControlPrescaler::Div4 => 4, + HighResolutionControlPrescaler::Div8 => 8, + HighResolutionControlPrescaler::Div16 => 16, + HighResolutionControlPrescaler::Div32 => 32, + HighResolutionControlPrescaler::Div64 => 64, + HighResolutionControlPrescaler::Div128 => 128, + }; + + Hertz(self.0 / divisor) + } +} + +#[cfg(hrtim_v1)] +impl From for u8 { + fn from(val: HighResolutionControlPrescaler) -> Self { + match val { + HighResolutionControlPrescaler::Div1 => 0b000, + HighResolutionControlPrescaler::Div2 => 0b001, + HighResolutionControlPrescaler::Div4 => 0b010, + HighResolutionControlPrescaler::Div8 => 0b011, + HighResolutionControlPrescaler::Div16 => 0b100, + HighResolutionControlPrescaler::Div32 => 0b101, + HighResolutionControlPrescaler::Div64 => 0b110, + HighResolutionControlPrescaler::Div128 => 0b111, + } } } @@ -235,13 +289,91 @@ foreach_interrupt! { crate::pac::$inst } + fn set_master_frequency(&mut self, frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + + // TODO: fix frequency source + let f = frequency.0; + let timer_f = Self::frequency().0; + // Ratio taken from RM0364 Table 81 + let base_f = Hertz(timer_f * (70_300 / 144_000_000)); + + /* + Find the smallest prescaler that allows us to acheive our frequency + */ + let psc = [ + HighResolutionControlPrescaler::Div1, + HighResolutionControlPrescaler::Div2, + HighResolutionControlPrescaler::Div4, + HighResolutionControlPrescaler::Div8, + HighResolutionControlPrescaler::Div16, + HighResolutionControlPrescaler::Div32, + HighResolutionControlPrescaler::Div64, + HighResolutionControlPrescaler::Div128, + ] + .iter() + .skip_while(|psc| frequency < base_f / **psc) + .next() + .unwrap(); + + let psc_timer_f = Hertz(timer_f) / *psc; + let per: u16 = (psc_timer_f / f).0 as u16; + + let regs = Self::regs_highres(); + + regs.mcr().modify(|w| w.set_ckpsc(((*psc).into()))); + regs.mper().modify(|w| w.set_mper(per)); + + // regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); + // regs.egr().write(|r| r.set_ug(true)); + // regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); + } + + fn set_channel_frequency(&mut self, channel: usize, frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + + // TODO: fix frequency source + let f = frequency.0; + let timer_f = Self::frequency().0; + // Ratio taken from RM0364 Table 81 + let base_f = Hertz(timer_f * (70_300 / 144_000_000)); + + /* + Find the smallest prescaler that allows us to acheive our frequency + */ + let psc = [ + HighResolutionControlPrescaler::Div1, + HighResolutionControlPrescaler::Div2, + HighResolutionControlPrescaler::Div4, + HighResolutionControlPrescaler::Div8, + HighResolutionControlPrescaler::Div16, + HighResolutionControlPrescaler::Div32, + HighResolutionControlPrescaler::Div64, + HighResolutionControlPrescaler::Div128, + ] + .iter() + .skip_while(|psc| frequency < base_f / **psc) + .next() + .unwrap(); + + let psc_timer_f = Hertz(timer_f) / *psc; + let per: u16 = (psc_timer_f / f).0 as u16; + + let regs = Self::regs_highres(); + + regs.tim(channel).cr().modify(|w| w.set_ckpsc(((*psc).into()))); + regs.tim(channel).per().modify(|w| w.set_per(per)); + + // regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); + // regs.egr().write(|r| r.set_ug(true)); + // regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); + } + fn start(&mut self) { todo!() } fn stop(&mut self) { todo!() } fn reset(&mut self) { todo!() } - - fn set_frequency(&mut self, frequency: Hertz) { todo!() } } impl HighResolutionControlInstance for crate::peripherals::$inst { From b9eb3dfad7b601a7819377c2c94369ca74efc824 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 26 Jun 2023 20:23:26 -0500 Subject: [PATCH 012/298] stm32/hrtim: add api concept --- embassy-stm32/src/pwm/advanced_pwm.rs | 131 +++++++++++++++++++------- 1 file changed, 98 insertions(+), 33 deletions(-) diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index 39d60c50..a0895224 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -11,11 +11,30 @@ use crate::time::Hertz; use crate::Peripheral; // Re-implement the channels for hrtim -pub struct ChA; -pub struct ChB; -pub struct ChC; -pub struct ChD; -pub struct ChE; +pub struct Master { + phantom: PhantomData, +} +pub struct ChA { + phantom: PhantomData, +} +pub struct ChB { + phantom: PhantomData, +} +pub struct ChC { + phantom: PhantomData, +} +pub struct ChD { + phantom: PhantomData, +} +pub struct ChE { + phantom: PhantomData, +} + +mod sealed { + pub trait AdvancedChannel {} +} + +pub trait AdvancedChannel: sealed::AdvancedChannel {} pub struct PwmPin<'d, Perip, Channel> { _pin: PeripheralRef<'d, AnyPin>, @@ -60,6 +79,9 @@ macro_rules! advanced_channel_impl { } } } + + impl sealed::AdvancedChannel for $channel {} + impl AdvancedChannel for $channel {} }; } @@ -69,8 +91,15 @@ advanced_channel_impl!(new_chc, ChC, ChannelCPin, ChannelCComplementaryPin); advanced_channel_impl!(new_chd, ChD, ChannelDPin, ChannelDComplementaryPin); advanced_channel_impl!(new_che, ChE, ChannelEPin, ChannelEComplementaryPin); +/// Struct used to divide a high resolution timer into multiple channels pub struct AdvancedPwm<'d, T> { inner: PeripheralRef<'d, T>, + pub master: Master, + pub ch_a: ChA, + pub ch_b: ChB, + pub ch_c: ChC, + pub ch_d: ChD, + pub ch_e: ChE, } impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { @@ -84,18 +113,25 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { _ch3n: Option>, _ch4: Option>, _ch4n: Option>, - freq: Hertz, ) -> Self { - Self::new_inner(tim, freq) + Self::new_inner(tim) } - fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz) -> Self { + fn new_inner(tim: impl Peripheral

+ 'd) -> Self { into_ref!(tim); T::enable(); ::reset(); - let mut this = Self { inner: tim }; + Self { + inner: tim, + master: Master { phantom: PhantomData }, + ch_a: ChA { phantom: PhantomData }, + ch_b: ChB { phantom: PhantomData }, + ch_c: ChC { phantom: PhantomData }, + ch_d: ChD { phantom: PhantomData }, + ch_e: ChE { phantom: PhantomData }, + } // // this.inner.set_frequency(freq); // this.inner.start(); @@ -110,32 +146,31 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { // .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); // this.inner // .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); - this } - pub fn enable(&mut self, channel: AdvancedChannel) { - // self.inner.enable_channel(channel, true); - // self.inner.enable_complementary_channel(channel, true); - } - - pub fn disable(&mut self, channel: AdvancedChannel) { - // self.inner.enable_complementary_channel(channel, false); - // self.inner.enable_channel(channel, false); - } - - pub fn set_freq(&mut self, freq: Hertz) { - // self.inner.set_frequency(freq); - } - - pub fn get_max_duty(&self) -> u16 { - todo!() - // self.inner.get_max_compare_value() - } - - pub fn set_duty(&mut self, channel: AdvancedChannel, duty: u16) { - // assert!(duty < self.get_max_duty()); - // self.inner.set_compare_value(channel, duty) - } + // pub fn enable(&mut self, channel: AdvancedChannel) { + // // self.inner.enable_channel(channel, true); + // // self.inner.enable_complementary_channel(channel, true); + // } + // + // pub fn disable(&mut self, channel: AdvancedChannel) { + // // self.inner.enable_complementary_channel(channel, false); + // // self.inner.enable_channel(channel, false); + // } + // + // pub fn set_freq(&mut self, freq: Hertz) { + // // self.inner.set_frequency(freq); + // } + // + // pub fn get_max_duty(&self) -> u16 { + // todo!() + // // self.inner.get_max_compare_value() + // } + // + // pub fn set_duty(&mut self, channel: AdvancedChannel, duty: u16) { + // // assert!(duty < self.get_max_duty()); + // // self.inner.set_compare_value(channel, duty) + // } /// Set the dead time as a proportion of max_duty pub fn set_dead_time(&mut self, value: u16) { @@ -145,3 +180,33 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { // self.inner.set_dead_time_value(value); } } + +// Represents a fixed-frequency bridge converter +pub struct BridgeConverter { + pub ch: T, +} + +impl BridgeConverter { + pub fn new(channel: T, frequency: Hertz) -> Self { + Self { ch: channel } + } + + pub fn set_duty(&mut self, primary: u16, secondary: u16) { + todo!() + } +} + +// Represents a variable-frequency resonant converter +pub struct ResonantConverter { + pub ch: T, +} + +impl ResonantConverter { + pub fn new(channel: T, min_frequency: Hertz) -> Self { + Self { ch: channel } + } + + pub fn set_frequency(&mut self, frequency: Hertz) { + todo!() + } +} From 348019e37f0ff5716d80199e33244c0a1a59b360 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 27 Jun 2023 18:24:32 -0500 Subject: [PATCH 013/298] stm32/hrtim: impl channel alloc type system --- embassy-stm32/src/pwm/advanced_pwm.rs | 167 ++++++++++++-------------- embassy-stm32/src/pwm/mod.rs | 144 +++++++++++++++++----- embassy-stm32/src/timer/mod.rs | 149 +---------------------- 3 files changed, 194 insertions(+), 266 deletions(-) diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index a0895224..c7b5f748 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -2,7 +2,6 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; -use super::simple_pwm::*; use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; @@ -11,30 +10,34 @@ use crate::time::Hertz; use crate::Peripheral; // Re-implement the channels for hrtim -pub struct Master { - phantom: PhantomData, +pub struct Master { + phantom: PhantomData, } -pub struct ChA { - phantom: PhantomData, +pub struct ChA { + phantom: PhantomData, } -pub struct ChB { - phantom: PhantomData, +pub struct ChB { + phantom: PhantomData, } -pub struct ChC { - phantom: PhantomData, +pub struct ChC { + phantom: PhantomData, } -pub struct ChD { - phantom: PhantomData, +pub struct ChD { + phantom: PhantomData, } -pub struct ChE { - phantom: PhantomData, +pub struct ChE { + phantom: PhantomData, } mod sealed { - pub trait AdvancedChannel {} + use crate::pwm::AdvancedCaptureCompare16bitInstance; + + pub trait AdvancedChannel {} } -pub trait AdvancedChannel: sealed::AdvancedChannel {} +pub trait AdvancedChannel: sealed::AdvancedChannel { + fn raw() -> usize; +} pub struct PwmPin<'d, Perip, Channel> { _pin: PeripheralRef<'d, AnyPin>, @@ -47,8 +50,8 @@ pub struct ComplementaryPwmPin<'d, Perip, Channel> { } macro_rules! advanced_channel_impl { - ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => { - impl<'d, Perip: AdvancedCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { + ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { + impl<'d, Perip: AdvancedCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -64,7 +67,7 @@ macro_rules! advanced_channel_impl { } } - impl<'d, Perip: AdvancedCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { + impl<'d, Perip: AdvancedCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -80,39 +83,45 @@ macro_rules! advanced_channel_impl { } } - impl sealed::AdvancedChannel for $channel {} - impl AdvancedChannel for $channel {} + impl sealed::AdvancedChannel for $channel {} + impl AdvancedChannel for $channel { + fn raw() -> usize { + $ch_num + } + } }; } -advanced_channel_impl!(new_cha, ChA, ChannelAPin, ChannelAComplementaryPin); -advanced_channel_impl!(new_chb, ChB, ChannelBPin, ChannelBComplementaryPin); -advanced_channel_impl!(new_chc, ChC, ChannelCPin, ChannelCComplementaryPin); -advanced_channel_impl!(new_chd, ChD, ChannelDPin, ChannelDComplementaryPin); -advanced_channel_impl!(new_che, ChE, ChannelEPin, ChannelEComplementaryPin); +advanced_channel_impl!(new_cha, ChA, 0, ChannelAPin, ChannelAComplementaryPin); +advanced_channel_impl!(new_chb, ChB, 1, ChannelBPin, ChannelBComplementaryPin); +advanced_channel_impl!(new_chc, ChC, 2, ChannelCPin, ChannelCComplementaryPin); +advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin); +advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin); /// Struct used to divide a high resolution timer into multiple channels -pub struct AdvancedPwm<'d, T> { - inner: PeripheralRef<'d, T>, - pub master: Master, - pub ch_a: ChA, - pub ch_b: ChB, - pub ch_c: ChC, - pub ch_d: ChD, - pub ch_e: ChE, +pub struct AdvancedPwm<'d, T: AdvancedCaptureCompare16bitInstance> { + _inner: PeripheralRef<'d, T>, + pub master: Master, + pub ch_a: ChA, + pub ch_b: ChB, + pub ch_c: ChC, + pub ch_d: ChD, + pub ch_e: ChE, } -impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { +impl<'d, T: AdvancedCaptureCompare16bitInstance> AdvancedPwm<'d, T> { pub fn new( tim: impl Peripheral

+ 'd, - _ch1: Option>, - _ch1n: Option>, - _ch2: Option>, - _ch2n: Option>, - _ch3: Option>, - _ch3n: Option>, - _ch4: Option>, - _ch4n: Option>, + _cha: Option>>, + _chan: Option>>, + _chb: Option>>, + _chbn: Option>>, + _chc: Option>>, + _chcn: Option>>, + _chd: Option>>, + _chdn: Option>>, + _che: Option>>, + _chen: Option>>, ) -> Self { Self::new_inner(tim) } @@ -124,7 +133,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { ::reset(); Self { - inner: tim, + _inner: tim, master: Master { phantom: PhantomData }, ch_a: ChA { phantom: PhantomData }, ch_b: ChB { phantom: PhantomData }, @@ -132,48 +141,11 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { ch_d: ChD { phantom: PhantomData }, ch_e: ChE { phantom: PhantomData }, } - // - // this.inner.set_frequency(freq); - // this.inner.start(); - // - // this.inner.enable_outputs(true); - // - // this.inner - // .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); - // this.inner - // .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); - // this.inner - // .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); - // this.inner - // .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); } - // pub fn enable(&mut self, channel: AdvancedChannel) { - // // self.inner.enable_channel(channel, true); - // // self.inner.enable_complementary_channel(channel, true); - // } - // - // pub fn disable(&mut self, channel: AdvancedChannel) { - // // self.inner.enable_complementary_channel(channel, false); - // // self.inner.enable_channel(channel, false); - // } - // - // pub fn set_freq(&mut self, freq: Hertz) { - // // self.inner.set_frequency(freq); - // } - // - // pub fn get_max_duty(&self) -> u16 { - // todo!() - // // self.inner.get_max_compare_value() - // } - // - // pub fn set_duty(&mut self, channel: AdvancedChannel, duty: u16) { - // // assert!(duty < self.get_max_duty()); - // // self.inner.set_compare_value(channel, duty) - // } - /// Set the dead time as a proportion of max_duty - pub fn set_dead_time(&mut self, value: u16) { + pub fn set_dead_time(&mut self, _value: u16) { + todo!() // let (ckd, value) = compute_dead_time_value(value); // // self.inner.set_dead_time_clock_division(ckd); @@ -182,28 +154,39 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { } // Represents a fixed-frequency bridge converter -pub struct BridgeConverter { - pub ch: T, +pub struct BridgeConverter> { + phantom: PhantomData, + pub ch: C, } -impl BridgeConverter { - pub fn new(channel: T, frequency: Hertz) -> Self { - Self { ch: channel } +impl> BridgeConverter { + pub fn new(channel: C, frequency: Hertz) -> Self { + Self { + phantom: PhantomData, + ch: channel, + } } pub fn set_duty(&mut self, primary: u16, secondary: u16) { + let _ = T::regs(); + let _ = C::raw(); + todo!() } } // Represents a variable-frequency resonant converter -pub struct ResonantConverter { - pub ch: T, +pub struct ResonantConverter> { + phantom: PhantomData, + pub ch: C, } -impl ResonantConverter { - pub fn new(channel: T, min_frequency: Hertz) -> Self { - Self { ch: channel } +impl> ResonantConverter { + pub fn new(channel: C, min_frequency: Hertz) -> Self { + Self { + phantom: PhantomData, + ch: channel, + } } pub fn set_frequency(&mut self, frequency: Hertz) { diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 1838bfdf..67009e78 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -3,8 +3,14 @@ pub mod advanced_pwm; pub mod complementary_pwm; pub mod simple_pwm; +#[cfg(hrtim_v1)] +use core::ops; + use stm32_metapac::timer::vals::Ckd; +#[cfg(hrtim_v1)] +use crate::time::Hertz; + #[cfg(feature = "unstable-pac")] pub mod low_level { pub use super::sealed::*; @@ -29,27 +35,6 @@ impl Channel { } } -#[derive(Clone, Copy)] -pub enum AdvancedChannel { - ChA, - ChB, - ChC, - ChD, - ChE, -} - -impl AdvancedChannel { - pub fn raw(&self) -> usize { - match self { - AdvancedChannel::ChA => 0, - AdvancedChannel::ChB => 1, - AdvancedChannel::ChC => 2, - AdvancedChannel::ChD => 3, - AdvancedChannel::ChE => 4, - } - } -} - #[derive(Clone, Copy)] pub enum OutputCompareMode { Frozen, @@ -77,16 +62,87 @@ impl From for stm32_metapac::timer::vals::Ocm { } } +#[cfg(hrtim_v1)] +#[derive(Clone, Copy)] +pub(crate) enum HighResolutionControlPrescaler { + Div1, + Div2, + Div4, + Div8, + Div16, + Div32, + Div64, + Div128, +} + +#[cfg(hrtim_v1)] +impl ops::Div for Hertz { + type Output = Hertz; + + fn div(self, rhs: HighResolutionControlPrescaler) -> Self::Output { + let divisor = match rhs { + HighResolutionControlPrescaler::Div1 => 1, + HighResolutionControlPrescaler::Div2 => 2, + HighResolutionControlPrescaler::Div4 => 4, + HighResolutionControlPrescaler::Div8 => 8, + HighResolutionControlPrescaler::Div16 => 16, + HighResolutionControlPrescaler::Div32 => 32, + HighResolutionControlPrescaler::Div64 => 64, + HighResolutionControlPrescaler::Div128 => 128, + }; + + Hertz(self.0 / divisor) + } +} + +#[cfg(hrtim_v1)] +impl From for u8 { + fn from(val: HighResolutionControlPrescaler) -> Self { + match val { + HighResolutionControlPrescaler::Div1 => 0b000, + HighResolutionControlPrescaler::Div2 => 0b001, + HighResolutionControlPrescaler::Div4 => 0b010, + HighResolutionControlPrescaler::Div8 => 0b011, + HighResolutionControlPrescaler::Div16 => 0b100, + HighResolutionControlPrescaler::Div32 => 0b101, + HighResolutionControlPrescaler::Div64 => 0b110, + HighResolutionControlPrescaler::Div128 => 0b111, + } + } +} + +#[cfg(hrtim_v1)] +impl HighResolutionControlPrescaler { + pub fn compute_min(base_f: Hertz, frequency: Hertz) -> Self { + *[ + HighResolutionControlPrescaler::Div1, + HighResolutionControlPrescaler::Div2, + HighResolutionControlPrescaler::Div4, + HighResolutionControlPrescaler::Div8, + HighResolutionControlPrescaler::Div16, + HighResolutionControlPrescaler::Div32, + HighResolutionControlPrescaler::Div64, + HighResolutionControlPrescaler::Div128, + ] + .iter() + .skip_while(|psc| frequency <= base_f / **psc) + .next() + .unwrap() + } +} + pub(crate) mod sealed { use super::*; #[cfg(hrtim_v1)] pub trait AdvancedCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { - fn enable_outputs(&mut self, enable: bool); + fn set_master_frequency(frequency: Hertz); - fn set_output_compare_mode(&mut self, channel: AdvancedChannel, mode: OutputCompareMode); + fn set_channel_frequency(channnel: usize, frequency: Hertz); - fn enable_channel(&mut self, channel: AdvancedChannel, enable: bool); + // fn enable_outputs(enable: bool); + // + // fn enable_channel(&mut self, channel: usize, enable: bool); } pub trait CaptureCompare16bitInstance: crate::timer::sealed::GeneralPurpose16bitInstance { @@ -288,11 +344,45 @@ foreach_interrupt! { ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { impl crate::pwm::sealed::AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { - fn enable_outputs(&mut self, enable: bool) { todo!() } + fn set_master_frequency(frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + use crate::timer::sealed::HighResolutionControlInstance; - fn set_output_compare_mode(&mut self, channel: AdvancedChannel, mode: OutputCompareMode) { todo!() } + let f = frequency.0; + // TODO: fix frequency source + // let timer_f = Self::frequency().0; + let timer_f = Hertz(144_000_000).0; + let base_f = Hertz((32 * timer_f as u64 / u16::MAX as u64) as u32); + let psc = HighResolutionControlPrescaler::compute_min(base_f, frequency); - fn enable_channel(&mut self, channel: AdvancedChannel, enable: bool) { todo!() } + let psc_timer_f = Hertz(timer_f) / psc; + let per: u16 = (psc_timer_f / f).0 as u16; + + let regs = Self::regs(); + + regs.mcr().modify(|w| w.set_ckpsc(psc.into())); + regs.mper().modify(|w| w.set_mper(per)); + } + + fn set_channel_frequency(channel: usize, frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + use crate::timer::sealed::HighResolutionControlInstance; + + let f = frequency.0; + // TODO: fix frequency source + // let timer_f = Self::frequency().0; + let timer_f = Hertz(144_000_000).0; + let base_f = Hertz((32 * timer_f as u64 / u16::MAX as u64) as u32); + let psc = HighResolutionControlPrescaler::compute_min(base_f, frequency); + + let psc_timer_f = Hertz(timer_f) / psc; + let per: u16 = (psc_timer_f / f).0 as u16; + + let regs = Self::regs(); + + regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into())); + regs.tim(channel).per().modify(|w| w.set_per(per)); + } } impl AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 03b9b3cf..89cb1666 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -51,66 +51,7 @@ pub(crate) mod sealed { pub trait HighResolutionControlInstance: RccPeripheral { type Interrupt: interrupt::typelevel::Interrupt; - fn regs_highres() -> crate::pac::hrtim::Hrtim; - - fn set_master_frequency(&mut self, frequency: Hertz); - - fn set_channel_frequency(&mut self, channel: usize, frequency: Hertz); - - fn start(&mut self); - - fn stop(&mut self); - - fn reset(&mut self); - } -} - -#[cfg(hrtim_v1)] -#[derive(Clone, Copy)] -pub(crate) enum HighResolutionControlPrescaler { - Div1, - Div2, - Div4, - Div8, - Div16, - Div32, - Div64, - Div128, -} - -#[cfg(hrtim_v1)] -impl ops::Div for Hertz { - type Output = Hertz; - - fn div(self, rhs: HighResolutionControlPrescaler) -> Self::Output { - let divisor = match rhs { - HighResolutionControlPrescaler::Div1 => 1, - HighResolutionControlPrescaler::Div2 => 2, - HighResolutionControlPrescaler::Div4 => 4, - HighResolutionControlPrescaler::Div8 => 8, - HighResolutionControlPrescaler::Div16 => 16, - HighResolutionControlPrescaler::Div32 => 32, - HighResolutionControlPrescaler::Div64 => 64, - HighResolutionControlPrescaler::Div128 => 128, - }; - - Hertz(self.0 / divisor) - } -} - -#[cfg(hrtim_v1)] -impl From for u8 { - fn from(val: HighResolutionControlPrescaler) -> Self { - match val { - HighResolutionControlPrescaler::Div1 => 0b000, - HighResolutionControlPrescaler::Div2 => 0b001, - HighResolutionControlPrescaler::Div4 => 0b010, - HighResolutionControlPrescaler::Div8 => 0b011, - HighResolutionControlPrescaler::Div16 => 0b100, - HighResolutionControlPrescaler::Div32 => 0b101, - HighResolutionControlPrescaler::Div64 => 0b110, - HighResolutionControlPrescaler::Div128 => 0b111, - } + fn regs() -> crate::pac::hrtim::Hrtim; } } @@ -285,95 +226,9 @@ foreach_interrupt! { impl sealed::HighResolutionControlInstance for crate::peripherals::$inst { type Interrupt = crate::interrupt::typelevel::$irq; - fn regs_highres() -> crate::pac::hrtim::Hrtim { + fn regs() -> crate::pac::hrtim::Hrtim { crate::pac::$inst } - - fn set_master_frequency(&mut self, frequency: Hertz) { - use crate::rcc::sealed::RccPeripheral; - - // TODO: fix frequency source - let f = frequency.0; - let timer_f = Self::frequency().0; - // Ratio taken from RM0364 Table 81 - let base_f = Hertz(timer_f * (70_300 / 144_000_000)); - - /* - Find the smallest prescaler that allows us to acheive our frequency - */ - let psc = [ - HighResolutionControlPrescaler::Div1, - HighResolutionControlPrescaler::Div2, - HighResolutionControlPrescaler::Div4, - HighResolutionControlPrescaler::Div8, - HighResolutionControlPrescaler::Div16, - HighResolutionControlPrescaler::Div32, - HighResolutionControlPrescaler::Div64, - HighResolutionControlPrescaler::Div128, - ] - .iter() - .skip_while(|psc| frequency < base_f / **psc) - .next() - .unwrap(); - - let psc_timer_f = Hertz(timer_f) / *psc; - let per: u16 = (psc_timer_f / f).0 as u16; - - let regs = Self::regs_highres(); - - regs.mcr().modify(|w| w.set_ckpsc(((*psc).into()))); - regs.mper().modify(|w| w.set_mper(per)); - - // regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); - // regs.egr().write(|r| r.set_ug(true)); - // regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); - } - - fn set_channel_frequency(&mut self, channel: usize, frequency: Hertz) { - use crate::rcc::sealed::RccPeripheral; - - // TODO: fix frequency source - let f = frequency.0; - let timer_f = Self::frequency().0; - // Ratio taken from RM0364 Table 81 - let base_f = Hertz(timer_f * (70_300 / 144_000_000)); - - /* - Find the smallest prescaler that allows us to acheive our frequency - */ - let psc = [ - HighResolutionControlPrescaler::Div1, - HighResolutionControlPrescaler::Div2, - HighResolutionControlPrescaler::Div4, - HighResolutionControlPrescaler::Div8, - HighResolutionControlPrescaler::Div16, - HighResolutionControlPrescaler::Div32, - HighResolutionControlPrescaler::Div64, - HighResolutionControlPrescaler::Div128, - ] - .iter() - .skip_while(|psc| frequency < base_f / **psc) - .next() - .unwrap(); - - let psc_timer_f = Hertz(timer_f) / *psc; - let per: u16 = (psc_timer_f / f).0 as u16; - - let regs = Self::regs_highres(); - - regs.tim(channel).cr().modify(|w| w.set_ckpsc(((*psc).into()))); - regs.tim(channel).per().modify(|w| w.set_per(per)); - - // regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); - // regs.egr().write(|r| r.set_ug(true)); - // regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); - } - - fn start(&mut self) { todo!() } - - fn stop(&mut self) { todo!() } - - fn reset(&mut self) { todo!() } } impl HighResolutionControlInstance for crate::peripherals::$inst { From 3252eaa060d8efb79f99511ac40a26be9cf287f9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 27 Jun 2023 20:30:58 -0500 Subject: [PATCH 014/298] stm32/hrtim: add example impl. --- .vscode/settings.json | 1 + ci.sh | 1 + embassy-stm32/src/pwm/advanced_pwm.rs | 2 +- examples/stm32f334/.cargo/config.toml | 9 ++++ examples/stm32f334/Cargo.toml | 26 +++++++++++ examples/stm32f334/build.rs | 5 +++ examples/stm32f334/src/bin/hello.rs | 23 ++++++++++ examples/stm32f334/src/bin/pwm.rs | 63 +++++++++++++++++++++++++++ 8 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 examples/stm32f334/.cargo/config.toml create mode 100644 examples/stm32f334/Cargo.toml create mode 100644 examples/stm32f334/build.rs create mode 100644 examples/stm32f334/src/bin/hello.rs create mode 100644 examples/stm32f334/src/bin/pwm.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 9ef7fe1c..fdfafafa 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -25,6 +25,7 @@ // "examples/stm32f1/Cargo.toml", // "examples/stm32f2/Cargo.toml", // "examples/stm32f3/Cargo.toml", + // "examples/stm32f334/Cargo.toml", // "examples/stm32f4/Cargo.toml", // "examples/stm32f7/Cargo.toml", // "examples/stm32g0/Cargo.toml", diff --git a/ci.sh b/ci.sh index a03efb85..ec6304f1 100755 --- a/ci.sh +++ b/ci.sh @@ -119,6 +119,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \ --- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \ --- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \ + --- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f334 \ --- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \ --- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \ --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \ diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index c7b5f748..bb0e06ad 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -52,7 +52,7 @@ pub struct ComplementaryPwmPin<'d, Perip, Channel> { macro_rules! advanced_channel_impl { ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { impl<'d, Perip: AdvancedCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { pin.set_low(); diff --git a/examples/stm32f334/.cargo/config.toml b/examples/stm32f334/.cargo/config.toml new file mode 100644 index 00000000..7f3fda52 --- /dev/null +++ b/examples/stm32f334/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32F303ZETx" + +[build] +target = "thumbv7em-none-eabihf" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml new file mode 100644 index 00000000..6410891a --- /dev/null +++ b/examples/stm32f334/Cargo.toml @@ -0,0 +1,26 @@ +[package] +edition = "2021" +name = "embassy-stm32f3-examples" +version = "0.1.0" +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"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f334r8", "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" } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" +embedded-hal = "0.2.6" +panic-probe = { version = "0.3", features = ["print-defmt"] } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +heapless = { version = "0.7.5", default-features = false } +nb = "1.0.0" +embedded-storage = "0.3.0" +static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32f334/build.rs b/examples/stm32f334/build.rs new file mode 100644 index 00000000..8cd32d7e --- /dev/null +++ b/examples/stm32f334/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/stm32f334/src/bin/hello.rs b/examples/stm32f334/src/bin/hello.rs new file mode 100644 index 00000000..65773210 --- /dev/null +++ b/examples/stm32f334/src/bin/hello.rs @@ -0,0 +1,23 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_stm32::time::Hertz; +use embassy_stm32::Config; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let mut config = Config::default(); + config.rcc.hse = Some(Hertz(8_000_000)); + config.rcc.sysclk = Some(Hertz(16_000_000)); + let _p = embassy_stm32::init(config); + + loop { + info!("Hello World!"); + Timer::after(Duration::from_secs(1)).await; + } +} diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs new file mode 100644 index 00000000..cc2ea861 --- /dev/null +++ b/examples/stm32f334/src/bin/pwm.rs @@ -0,0 +1,63 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::pwm::advanced_pwm::*; +use embassy_stm32::pwm::Channel; +use embassy_stm32::time::khz; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let ch1 = PwmPin::new_cha(p.PA8); + let ch1n = ComplementaryPwmPin::new_cha(p.PA9); + let mut pwm = AdvancedPwm::new( + p.HRTIM1, + Some(ch1), + Some(ch1n), + None, + None, + None, + None, + None, + None, + None, + None, + ); + + pwm.set_dead_time(0); + + let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(100)); + + buck_converter.set_duty(0, u16::MAX); + + // note: if the pins are not passed into the advanced pwm struct, they will not be output + let mut boost_converter = BridgeConverter::new(pwm.ch_b, khz(100)); + + boost_converter.set_duty(0, 0); + + // let max = pwm.get_max_duty(); + // pwm.set_dead_time(max / 1024); + // + // pwm.enable(Channel::Ch1); + // + // info!("PWM initialized"); + // info!("PWM max duty {}", max); + // + // loop { + // pwm.set_duty(Channel::Ch1, 0); + // Timer::after(Duration::from_millis(300)).await; + // pwm.set_duty(Channel::Ch1, max / 4); + // Timer::after(Duration::from_millis(300)).await; + // pwm.set_duty(Channel::Ch1, max / 2); + // Timer::after(Duration::from_millis(300)).await; + // pwm.set_duty(Channel::Ch1, max - 1); + // Timer::after(Duration::from_millis(300)).await; + // } +} From 8c4997c5fcd8c25d68865b9e7537e0add50eac24 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 29 Jun 2023 21:05:41 -0500 Subject: [PATCH 015/298] stm32/hrtim: impl. bridge, dead-time part. res. --- embassy-stm32/src/pwm/advanced_pwm.rs | 193 +++++++++++++++++++++----- embassy-stm32/src/pwm/mod.rs | 117 +++++++++++----- examples/stm32f334/src/bin/pwm.rs | 9 +- 3 files changed, 241 insertions(+), 78 deletions(-) diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index bb0e06ad..228899c0 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -9,33 +9,44 @@ use crate::gpio::AnyPin; use crate::time::Hertz; use crate::Peripheral; -// Re-implement the channels for hrtim -pub struct Master { +pub enum Source { + Master, + ChA, + ChB, + ChC, + ChD, + ChE, +} + +pub struct BurstController { phantom: PhantomData, } -pub struct ChA { +pub struct Master { phantom: PhantomData, } -pub struct ChB { +pub struct ChA { phantom: PhantomData, } -pub struct ChC { +pub struct ChB { phantom: PhantomData, } -pub struct ChD { +pub struct ChC { phantom: PhantomData, } -pub struct ChE { +pub struct ChD { + phantom: PhantomData, +} +pub struct ChE { phantom: PhantomData, } mod sealed { - use crate::pwm::AdvancedCaptureCompare16bitInstance; + use crate::pwm::HighResolutionCaptureCompare16bitInstance; - pub trait AdvancedChannel {} + pub trait AdvancedChannel {} } -pub trait AdvancedChannel: sealed::AdvancedChannel { +pub trait AdvancedChannel: sealed::AdvancedChannel { fn raw() -> usize; } @@ -51,7 +62,7 @@ pub struct ComplementaryPwmPin<'d, Perip, Channel> { macro_rules! advanced_channel_impl { ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { - impl<'d, Perip: AdvancedCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { + impl<'d, Perip: HighResolutionCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -67,7 +78,7 @@ macro_rules! advanced_channel_impl { } } - impl<'d, Perip: AdvancedCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { + impl<'d, Perip: HighResolutionCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -83,8 +94,8 @@ macro_rules! advanced_channel_impl { } } - impl sealed::AdvancedChannel for $channel {} - impl AdvancedChannel for $channel { + impl sealed::AdvancedChannel for $channel {} + impl AdvancedChannel for $channel { fn raw() -> usize { $ch_num } @@ -99,9 +110,10 @@ advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin); advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin); /// Struct used to divide a high resolution timer into multiple channels -pub struct AdvancedPwm<'d, T: AdvancedCaptureCompare16bitInstance> { +pub struct AdvancedPwm<'d, T: HighResolutionCaptureCompare16bitInstance> { _inner: PeripheralRef<'d, T>, pub master: Master, + pub burst_controller: BurstController, pub ch_a: ChA, pub ch_b: ChB, pub ch_c: ChC, @@ -109,7 +121,7 @@ pub struct AdvancedPwm<'d, T: AdvancedCaptureCompare16bitInstance> { pub ch_e: ChE, } -impl<'d, T: AdvancedCaptureCompare16bitInstance> AdvancedPwm<'d, T> { +impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> { pub fn new( tim: impl Peripheral

+ 'd, _cha: Option>>, @@ -135,6 +147,7 @@ impl<'d, T: AdvancedCaptureCompare16bitInstance> AdvancedPwm<'d, T> { Self { _inner: tim, master: Master { phantom: PhantomData }, + burst_controller: BurstController { phantom: PhantomData }, ch_a: ChA { phantom: PhantomData }, ch_b: ChB { phantom: PhantomData }, ch_c: ChC { phantom: PhantomData }, @@ -142,54 +155,162 @@ impl<'d, T: AdvancedCaptureCompare16bitInstance> AdvancedPwm<'d, T> { ch_e: ChE { phantom: PhantomData }, } } +} - /// Set the dead time as a proportion of max_duty - pub fn set_dead_time(&mut self, _value: u16) { - todo!() - // let (ckd, value) = compute_dead_time_value(value); - // - // self.inner.set_dead_time_clock_division(ckd); - // self.inner.set_dead_time_value(value); +impl BurstController { + pub fn set_source(&mut self, source: Source) { + let regs = T::regs(); } } -// Represents a fixed-frequency bridge converter -pub struct BridgeConverter> { +/// Represents a fixed-frequency bridge converter +/// +/// Our implementation of the bridge converter uses a single channel and two compare registers, +/// allowing implementation of a synchronous buck or boost converter in continuous or discontinuous +/// conduction mode. +/// +/// It is important to remember that in synchronous topologies, energy can flow in reverse during +/// light loading conditions, and that the low-side switch must be active for a short time to drive +/// a bootstrapped high-side switch. +pub struct BridgeConverter> { phantom: PhantomData, pub ch: C, } -impl> BridgeConverter { +impl> BridgeConverter { pub fn new(channel: C, frequency: Hertz) -> Self { + use crate::pac::hrtim::vals::{Activeeffect, Cont, Inactiveeffect}; + + T::set_channel_frequency(C::raw(), frequency); + + // Always enable preload + T::regs().tim(C::raw()).cr().modify(|w| { + w.set_preen(true); + + // TODO: fix metapac + w.set_cont(Cont(1)); + }); + + // Set output 1 to active on a period event + T::regs() + .tim(C::raw()) + .setr(0) + .modify(|w| w.set_per(Activeeffect::SETACTIVE)); + + // Set output 1 to inactive on a compare 1 event + T::regs() + .tim(C::raw()) + .rstr(0) + .modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE)); + + // Set output 2 to active on a compare 1 event + T::regs() + .tim(C::raw()) + .setr(1) + .modify(|w| w.set_cmp(0, Activeeffect::SETACTIVE)); + + // Set output 2 to inactive on a compare 2 event + T::regs() + .tim(C::raw()) + .rstr(1) + .modify(|w| w.set_cmp(1, Inactiveeffect::SETINACTIVE)); + Self { phantom: PhantomData, ch: channel, } } - pub fn set_duty(&mut self, primary: u16, secondary: u16) { - let _ = T::regs(); - let _ = C::raw(); + pub fn start(&mut self) { + T::regs().mcr().modify(|w| w.set_tcen(C::raw(), true)); + } - todo!() + pub fn stop(&mut self) { + T::regs().mcr().modify(|w| w.set_tcen(C::raw(), false)); + } + + /// Set the dead time as a proportion of the maximum compare value + pub fn set_dead_time(&mut self, value: u16) { + T::set_channel_dead_time(C::raw(), value); + } + + /// Get the maximum compare value of a duty cycle + pub fn get_max_compare_value(&mut self) -> u16 { + T::regs().tim(C::raw()).per().read().per() + } + + /// The primary duty is the period in which the primary switch is active + /// + /// In the case of a buck converter, this is the high-side switch + /// In the case of a boost converter, this is the low-side switch + pub fn set_primary_duty(&mut self, primary: u16) { + T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(primary)); + } + + /// The primary duty is the period in any switch is active + /// + /// If less than or equal to the primary duty, the secondary switch will never be active + pub fn set_secondary_duty(&mut self, secondary: u16) { + T::regs().tim(C::raw()).cmp(1).modify(|w| w.set_cmp(secondary)); } } -// Represents a variable-frequency resonant converter -pub struct ResonantConverter> { +/// Represents a variable-frequency resonant converter +/// +/// This implementation of a resonsant converter is appropriate for a half or full bridge, +/// but does not include secondary rectification, which is appropriate for applications +/// with a low-voltage on the secondary side. +pub struct ResonantConverter> { phantom: PhantomData, + min_period: u16, + max_period: u16, pub ch: C, } -impl> ResonantConverter { - pub fn new(channel: C, min_frequency: Hertz) -> Self { +impl> ResonantConverter { + pub fn new(channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { + use crate::pac::hrtim::vals::Cont; + + T::set_channel_frequency(C::raw(), min_frequency); + + // Always enable preload + T::regs().tim(C::raw()).cr().modify(|w| { + w.set_preen(true); + + // TODO: fix metapac + w.set_cont(Cont(1)); + w.set_half(true); + }); + + // TODO: compute min period value + Self { + min_period: 0, + max_period: T::regs().tim(C::raw()).per().read().per(), phantom: PhantomData, ch: channel, } } - pub fn set_frequency(&mut self, frequency: Hertz) { - todo!() + /// Set the dead time as a proportion of the maximum compare value + pub fn set_dead_time(&mut self, value: u16) { + T::set_channel_dead_time(C::raw(), value); + } + + pub fn set_period(&mut self, period: u16) { + assert!(period < self.max_period); + assert!(period > self.min_period); + + T::regs().tim(C::raw()).per().modify(|w| w.set_per(period)); + } + + /// Get the minimum compare value of a duty cycle + pub fn get_min_period(&mut self) -> u16 { + self.min_period + } + + /// Get the maximum compare value of a duty cycle + pub fn get_max_period(&mut self) -> u16 { + self.max_period } } diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 67009e78..2586dc15 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -76,11 +76,9 @@ pub(crate) enum HighResolutionControlPrescaler { } #[cfg(hrtim_v1)] -impl ops::Div for Hertz { - type Output = Hertz; - - fn div(self, rhs: HighResolutionControlPrescaler) -> Self::Output { - let divisor = match rhs { +impl From for u32 { + fn from(val: HighResolutionControlPrescaler) -> Self { + match val { HighResolutionControlPrescaler::Div1 => 1, HighResolutionControlPrescaler::Div2 => 2, HighResolutionControlPrescaler::Div4 => 4, @@ -89,9 +87,7 @@ impl ops::Div for Hertz { HighResolutionControlPrescaler::Div32 => 32, HighResolutionControlPrescaler::Div64 => 64, HighResolutionControlPrescaler::Div128 => 128, - }; - - Hertz(self.0 / divisor) + } } } @@ -111,9 +107,26 @@ impl From for u8 { } } +#[cfg(hrtim_v1)] +impl From for HighResolutionControlPrescaler { + fn from(val: u8) -> Self { + match val { + 0b000 => HighResolutionControlPrescaler::Div1, + 0b001 => HighResolutionControlPrescaler::Div2, + 0b010 => HighResolutionControlPrescaler::Div4, + 0b011 => HighResolutionControlPrescaler::Div8, + 0b100 => HighResolutionControlPrescaler::Div16, + 0b101 => HighResolutionControlPrescaler::Div32, + 0b110 => HighResolutionControlPrescaler::Div64, + 0b111 => HighResolutionControlPrescaler::Div128, + _ => unreachable!(), + } + } +} + #[cfg(hrtim_v1)] impl HighResolutionControlPrescaler { - pub fn compute_min(base_f: Hertz, frequency: Hertz) -> Self { + pub fn compute_min(val: u32) -> Self { *[ HighResolutionControlPrescaler::Div1, HighResolutionControlPrescaler::Div2, @@ -125,7 +138,7 @@ impl HighResolutionControlPrescaler { HighResolutionControlPrescaler::Div128, ] .iter() - .skip_while(|psc| frequency <= base_f / **psc) + .skip_while(|psc| >::into(**psc) <= val) .next() .unwrap() } @@ -135,11 +148,14 @@ pub(crate) mod sealed { use super::*; #[cfg(hrtim_v1)] - pub trait AdvancedCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { + pub trait HighResolutionCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { fn set_master_frequency(frequency: Hertz); fn set_channel_frequency(channnel: usize, frequency: Hertz); + /// Set the dead time as a proportion of max_duty + fn set_channel_dead_time(channnel: usize, dead_time: u16); + // fn enable_outputs(enable: bool); // // fn enable_channel(&mut self, channel: usize, enable: bool); @@ -178,7 +194,10 @@ pub(crate) mod sealed { } #[cfg(hrtim_v1)] -pub trait AdvancedCaptureCompare16bitInstance: sealed::AdvancedCaptureCompare16bitInstance + 'static {} +pub trait HighResolutionCaptureCompare16bitInstance: + sealed::HighResolutionCaptureCompare16bitInstance + 'static +{ +} pub trait CaptureCompare16bitInstance: sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static @@ -343,20 +362,19 @@ foreach_interrupt! { }; ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { - impl crate::pwm::sealed::AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { + impl crate::pwm::sealed::HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { fn set_master_frequency(frequency: Hertz) { use crate::rcc::sealed::RccPeripheral; use crate::timer::sealed::HighResolutionControlInstance; let f = frequency.0; - // TODO: fix frequency source - // let timer_f = Self::frequency().0; - let timer_f = Hertz(144_000_000).0; - let base_f = Hertz((32 * timer_f as u64 / u16::MAX as u64) as u32); - let psc = HighResolutionControlPrescaler::compute_min(base_f, frequency); + let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = HighResolutionControlPrescaler::compute_min(psc_min); - let psc_timer_f = Hertz(timer_f) / psc; - let per: u16 = (psc_timer_f / f).0 as u16; + let psc_val: u32 = psc.into(); + let timer_f = timer_f / psc_val; + let per: u16 = (timer_f / f) as u16; let regs = Self::regs(); @@ -369,23 +387,46 @@ foreach_interrupt! { use crate::timer::sealed::HighResolutionControlInstance; let f = frequency.0; - // TODO: fix frequency source - // let timer_f = Self::frequency().0; - let timer_f = Hertz(144_000_000).0; - let base_f = Hertz((32 * timer_f as u64 / u16::MAX as u64) as u32); - let psc = HighResolutionControlPrescaler::compute_min(base_f, frequency); + let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = HighResolutionControlPrescaler::compute_min(psc_min); - let psc_timer_f = Hertz(timer_f) / psc; - let per: u16 = (psc_timer_f / f).0 as u16; + let psc_val: u32 = psc.into(); + let timer_f = timer_f / psc_val; + let per: u16 = (timer_f / f) as u16; let regs = Self::regs(); regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into())); regs.tim(channel).per().modify(|w| w.set_per(per)); } + + fn set_channel_dead_time(channel: usize, dead_time: u16) { + use crate::rcc::sealed::RccPeripheral; + use crate::timer::sealed::HighResolutionControlInstance; + + let regs = Self::regs(); + + let channel_psc: HighResolutionControlPrescaler = regs.tim(channel).cr().read().ckpsc().into(); + let psc_val: u32 = channel_psc.into(); + + + // The dead-time base clock runs 4 times slower than the hrtim base clock + // u9::MAX = 511 + let psc_min = (psc_val * dead_time as u32) / (4 * 511); + let psc = HighResolutionControlPrescaler::compute_min(psc_min); + let dt_psc_val: u32 = psc.into(); + let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); + + regs.tim(channel).dt().modify(|w| { + w.set_dtprsc(psc.into()); + w.set_dtf(dt_val as u16); + w.set_dtr(dt_val as u16); + }); + } } - impl AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { + impl HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { } }; @@ -411,16 +452,16 @@ pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); mod hrtim_pins { use super::*; - pin_trait!(ChannelAPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelAComplementaryPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelBPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelBComplementaryPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelCPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelCComplementaryPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelDPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelDComplementaryPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelEPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelEComplementaryPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelAPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelAComplementaryPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelBPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelBComplementaryPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelCPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelCComplementaryPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelDPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelDComplementaryPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelEPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelEComplementaryPin, HighResolutionCaptureCompare16bitInstance); } #[cfg(hrtim_v1)] diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index cc2ea861..20a62137 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs @@ -31,16 +31,17 @@ async fn main(_spawner: Spawner) { None, ); - pwm.set_dead_time(0); - let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(100)); - buck_converter.set_duty(0, u16::MAX); + buck_converter.set_primary_duty(0); + buck_converter.set_secondary_duty(0); + buck_converter.set_dead_time(0); // note: if the pins are not passed into the advanced pwm struct, they will not be output let mut boost_converter = BridgeConverter::new(pwm.ch_b, khz(100)); - boost_converter.set_duty(0, 0); + boost_converter.set_primary_duty(0); + boost_converter.set_secondary_duty(0); // let max = pwm.get_max_duty(); // pwm.set_dead_time(max / 1024); From c07854fed8f6ba38d418ef63853769a9af109bff Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 30 Jun 2023 18:20:33 -0500 Subject: [PATCH 016/298] stm32/hrtim: minor fixes --- embassy-stm32/src/pwm/advanced_pwm.rs | 49 +++++++++++++++++++++------ 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index 228899c0..7e595421 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -43,12 +43,12 @@ pub struct ChE { mod sealed { use crate::pwm::HighResolutionCaptureCompare16bitInstance; - pub trait AdvancedChannel {} + pub trait AdvancedChannel { + fn raw() -> usize; + } } -pub trait AdvancedChannel: sealed::AdvancedChannel { - fn raw() -> usize; -} +pub trait AdvancedChannel: sealed::AdvancedChannel {} pub struct PwmPin<'d, Perip, Channel> { _pin: PeripheralRef<'d, AnyPin>, @@ -94,12 +94,12 @@ macro_rules! advanced_channel_impl { } } - impl sealed::AdvancedChannel for $channel {} - impl AdvancedChannel for $channel { + impl sealed::AdvancedChannel for $channel { fn raw() -> usize { $ch_num } } + impl AdvancedChannel for $channel {} }; } @@ -158,8 +158,8 @@ impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> { } impl BurstController { - pub fn set_source(&mut self, source: Source) { - let regs = T::regs(); + pub fn set_source(&mut self, _source: Source) { + todo!("burst mode control registers not implemented") } } @@ -229,6 +229,32 @@ impl> Bridge T::regs().mcr().modify(|w| w.set_tcen(C::raw(), false)); } + pub fn enable_burst_mode(&mut self) { + use crate::pac::hrtim::vals::{Idlem, Idles}; + + // TODO: fix metapac + T::regs().tim(C::raw()).outr().modify(|w| { + w.set_idlem(0, Idlem(1)); + w.set_idlem(1, Idlem(1)); + + w.set_idles(0, Idles(1)); + w.set_idles(1, Idles(1)); + }) + } + + pub fn disable_burst_mode(&mut self) { + use crate::pac::hrtim::vals::{Idlem, Idles}; + + // TODO: fix metapac + T::regs().tim(C::raw()).outr().modify(|w| { + w.set_idlem(0, Idlem(0)); + w.set_idlem(1, Idlem(0)); + + w.set_idles(0, Idles(0)); + w.set_idles(1, Idles(0)); + }) + } + /// Set the dead time as a proportion of the maximum compare value pub fn set_dead_time(&mut self, value: u16) { T::set_channel_dead_time(C::raw(), value); @@ -282,11 +308,12 @@ impl> Resona w.set_half(true); }); - // TODO: compute min period value + let max_period = T::regs().tim(C::raw()).per().read().per(); + let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16; Self { - min_period: 0, - max_period: T::regs().tim(C::raw()).per().read().per(), + min_period: min_period, + max_period: max_period, phantom: PhantomData, ch: channel, } From 6e13f5b387d8f0f948b5874bc300925b015947d9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 30 Jun 2023 18:33:22 -0500 Subject: [PATCH 017/298] rustfmt --- embassy-stm32/src/pwm/mod.rs | 4 ---- embassy-stm32/src/rcc/f3.rs | 1 + embassy-stm32/src/timer/mod.rs | 3 --- examples/stm32f334/src/bin/pwm.rs | 4 +--- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 2586dc15..d09e38d0 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -3,9 +3,6 @@ pub mod advanced_pwm; pub mod complementary_pwm; pub mod simple_pwm; -#[cfg(hrtim_v1)] -use core::ops; - use stm32_metapac::timer::vals::Ckd; #[cfg(hrtim_v1)] @@ -402,7 +399,6 @@ foreach_interrupt! { } fn set_channel_dead_time(channel: usize, dead_time: u16) { - use crate::rcc::sealed::RccPeripheral; use crate::timer::sealed::HighResolutionControlInstance; let regs = Self::regs(); diff --git a/embassy-stm32/src/rcc/f3.rs b/embassy-stm32/src/rcc/f3.rs index 2deee80d..321270a7 100644 --- a/embassy-stm32/src/rcc/f3.rs +++ b/embassy-stm32/src/rcc/f3.rs @@ -264,6 +264,7 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) { } #[inline] +#[allow(unused_variables)] fn get_usb_pre(config: &Config, sysclk: u32, pclk1: u32, pll_config: &Option) -> Usbpre { cfg_if::cfg_if! { // Some chips do not have USB diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 89cb1666..a92f854e 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,6 +1,3 @@ -#[cfg(hrtim_v1)] -use core::ops; - use stm32_metapac::timer::vals; use crate::interrupt; diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index 20a62137..1b5d509e 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs @@ -5,9 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::pwm::advanced_pwm::*; -use embassy_stm32::pwm::Channel; use embassy_stm32::time::khz; -use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -17,7 +15,7 @@ async fn main(_spawner: Spawner) { let ch1 = PwmPin::new_cha(p.PA8); let ch1n = ComplementaryPwmPin::new_cha(p.PA9); - let mut pwm = AdvancedPwm::new( + let pwm = AdvancedPwm::new( p.HRTIM1, Some(ch1), Some(ch1n), From 21a86531955facbdba5583b5218e20f1ca6dc23e Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 1 Jul 2023 17:32:16 -0500 Subject: [PATCH 018/298] hrtim: minor cleanup --- embassy-stm32/Cargo.toml | 2 +- embassy-stm32/src/pwm/advanced_pwm.rs | 28 ++++++++++++++------------- examples/stm32f334/.cargo/config.toml | 2 +- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 3d9ee826..551c0969 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 = "10" +stm32-metapac = "12" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index 7e595421..65f4e7ca 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -187,8 +187,12 @@ impl> Bridge T::regs().tim(C::raw()).cr().modify(|w| { w.set_preen(true); - // TODO: fix metapac - w.set_cont(Cont(1)); + w.set_cont(Cont::CONTINUOUS); + }); + + T::regs().oenr().modify(|w| { + w.set_t1oen(C::raw(), true); + w.set_t2oen(C::raw(), true); }); // Set output 1 to active on a period event @@ -234,24 +238,23 @@ impl> Bridge // TODO: fix metapac T::regs().tim(C::raw()).outr().modify(|w| { - w.set_idlem(0, Idlem(1)); - w.set_idlem(1, Idlem(1)); + w.set_idlem(0, Idlem::SETIDLE); + w.set_idlem(1, Idlem::SETIDLE); - w.set_idles(0, Idles(1)); - w.set_idles(1, Idles(1)); + w.set_idles(0, Idles::INACTIVE); + w.set_idles(1, Idles::INACTIVE); }) } pub fn disable_burst_mode(&mut self) { use crate::pac::hrtim::vals::{Idlem, Idles}; - // TODO: fix metapac T::regs().tim(C::raw()).outr().modify(|w| { - w.set_idlem(0, Idlem(0)); - w.set_idlem(1, Idlem(0)); + w.set_idlem(0, Idlem::NOEFFECT); + w.set_idlem(1, Idlem::NOEFFECT); - w.set_idles(0, Idles(0)); - w.set_idles(1, Idles(0)); + w.set_idles(0, Idles::INACTIVE); + w.set_idles(1, Idles::INACTIVE); }) } @@ -303,8 +306,7 @@ impl> Resona T::regs().tim(C::raw()).cr().modify(|w| { w.set_preen(true); - // TODO: fix metapac - w.set_cont(Cont(1)); + w.set_cont(Cont::CONTINUOUS); w.set_half(true); }); diff --git a/examples/stm32f334/.cargo/config.toml b/examples/stm32f334/.cargo/config.toml index 7f3fda52..caf947be 100644 --- a/examples/stm32f334/.cargo/config.toml +++ b/examples/stm32f334/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32F303ZETx" +runner = "probe-run --chip STM32F334R8" [build] target = "thumbv7em-none-eabihf" From aceba1c03fc45179d4e910fad254a31191cf0c44 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 1 Jul 2023 21:47:44 -0500 Subject: [PATCH 019/298] hrtim: fix example and auto adjust psc. --- embassy-stm32/src/pwm/advanced_pwm.rs | 87 ++++++++++++++++++++------- embassy-stm32/src/pwm/mod.rs | 37 ++++++++++-- examples/stm32f334/src/bin/button.rs | 27 +++++++++ examples/stm32f334/src/bin/pwm.rs | 65 +++++++++++--------- 4 files changed, 160 insertions(+), 56 deletions(-) create mode 100644 examples/stm32f334/src/bin/button.rs diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index 65f4e7ca..fa34b2e1 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -144,6 +144,18 @@ impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> { T::enable(); ::reset(); + // // Enable and and stabilize the DLL + // T::regs().dllcr().modify(|w| { + // // w.set_calen(true); + // // w.set_calrte(11); + // w.set_cal(true); + // }); + // + // debug!("wait for dll calibration"); + // while !T::regs().isr().read().dllrdy() {} + // + // debug!("dll calibration complete"); + Self { _inner: tim, master: Master { phantom: PhantomData }, @@ -173,12 +185,14 @@ impl BurstController { /// light loading conditions, and that the low-side switch must be active for a short time to drive /// a bootstrapped high-side switch. pub struct BridgeConverter> { - phantom: PhantomData, - pub ch: C, + timer: PhantomData, + channel: PhantomData, + dead_time: u16, + primary_duty: u16, } impl> BridgeConverter { - pub fn new(channel: C, frequency: Hertz) -> Self { + pub fn new(_channel: C, frequency: Hertz) -> Self { use crate::pac::hrtim::vals::{Activeeffect, Cont, Inactiveeffect}; T::set_channel_frequency(C::raw(), frequency); @@ -186,15 +200,21 @@ impl> Bridge // Always enable preload T::regs().tim(C::raw()).cr().modify(|w| { w.set_preen(true); + w.set_repu(true); w.set_cont(Cont::CONTINUOUS); }); + // Enable timer outputs T::regs().oenr().modify(|w| { w.set_t1oen(C::raw(), true); w.set_t2oen(C::raw(), true); }); + // The dead-time generation unit cannot be used because it forces the other output + // to be completely complementary to the first output, which restricts certain waveforms + // Therefore, software-implemented dead time must be used when setting the duty cycles + // Set output 1 to active on a period event T::regs() .tim(C::raw()) @@ -207,21 +227,23 @@ impl> Bridge .rstr(0) .modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE)); - // Set output 2 to active on a compare 1 event + // Set output 2 to active on a compare 2 event T::regs() .tim(C::raw()) .setr(1) - .modify(|w| w.set_cmp(0, Activeeffect::SETACTIVE)); + .modify(|w| w.set_cmp(1, Activeeffect::SETACTIVE)); - // Set output 2 to inactive on a compare 2 event + // Set output 2 to inactive on a compare 3 event T::regs() .tim(C::raw()) .rstr(1) - .modify(|w| w.set_cmp(1, Inactiveeffect::SETINACTIVE)); + .modify(|w| w.set_cmp(2, Inactiveeffect::SETINACTIVE)); Self { - phantom: PhantomData, - ch: channel, + timer: PhantomData, + channel: PhantomData, + dead_time: 0, + primary_duty: 0, } } @@ -236,7 +258,6 @@ impl> Bridge pub fn enable_burst_mode(&mut self) { use crate::pac::hrtim::vals::{Idlem, Idles}; - // TODO: fix metapac T::regs().tim(C::raw()).outr().modify(|w| { w.set_idlem(0, Idlem::SETIDLE); w.set_idlem(1, Idlem::SETIDLE); @@ -258,9 +279,18 @@ impl> Bridge }) } + fn update_primary_duty_or_dead_time(&mut self) { + T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(self.primary_duty)); + T::regs() + .tim(C::raw()) + .cmp(1) + .modify(|w| w.set_cmp(self.primary_duty + self.dead_time)); + } + /// Set the dead time as a proportion of the maximum compare value - pub fn set_dead_time(&mut self, value: u16) { - T::set_channel_dead_time(C::raw(), value); + pub fn set_dead_time(&mut self, dead_time: u16) { + self.dead_time = dead_time; + self.update_primary_duty_or_dead_time(); } /// Get the maximum compare value of a duty cycle @@ -272,15 +302,17 @@ impl> Bridge /// /// In the case of a buck converter, this is the high-side switch /// In the case of a boost converter, this is the low-side switch - pub fn set_primary_duty(&mut self, primary: u16) { - T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(primary)); + pub fn set_primary_duty(&mut self, primary_duty: u16) { + self.primary_duty = primary_duty; + self.update_primary_duty_or_dead_time(); } - /// The primary duty is the period in any switch is active + /// The secondary duty is the period in any switch is active /// /// If less than or equal to the primary duty, the secondary switch will never be active - pub fn set_secondary_duty(&mut self, secondary: u16) { - T::regs().tim(C::raw()).cmp(1).modify(|w| w.set_cmp(secondary)); + /// If a fully complementary output is desired, the secondary duty can be set to the max compare + pub fn set_secondary_duty(&mut self, secondary_duty: u16) { + T::regs().tim(C::raw()).cmp(2).modify(|w| w.set_cmp(secondary_duty)); } } @@ -290,14 +322,14 @@ impl> Bridge /// but does not include secondary rectification, which is appropriate for applications /// with a low-voltage on the secondary side. pub struct ResonantConverter> { - phantom: PhantomData, + timer: PhantomData, + channel: PhantomData, min_period: u16, max_period: u16, - pub ch: C, } impl> ResonantConverter { - pub fn new(channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { + pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { use crate::pac::hrtim::vals::Cont; T::set_channel_frequency(C::raw(), min_frequency); @@ -305,19 +337,30 @@ impl> Resona // Always enable preload T::regs().tim(C::raw()).cr().modify(|w| { w.set_preen(true); + w.set_repu(true); w.set_cont(Cont::CONTINUOUS); w.set_half(true); }); + // Enable timer outputs + T::regs().oenr().modify(|w| { + w.set_t1oen(C::raw(), true); + w.set_t2oen(C::raw(), true); + }); + + // Dead-time generator can be used in this case because the primary fets + // of a resonant converter are always complementary + T::regs().tim(C::raw()).outr().modify(|w| w.set_dten(true)); + let max_period = T::regs().tim(C::raw()).per().read().per(); let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16; Self { + timer: PhantomData, + channel: PhantomData, min_period: min_period, max_period: max_period, - phantom: PhantomData, - ch: channel, } } diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index d09e38d0..429a290e 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -123,7 +123,7 @@ impl From for HighResolutionControlPrescaler { #[cfg(hrtim_v1)] impl HighResolutionControlPrescaler { - pub fn compute_min(val: u32) -> Self { + pub fn compute_min_high_res(val: u32) -> Self { *[ HighResolutionControlPrescaler::Div1, HighResolutionControlPrescaler::Div2, @@ -139,6 +139,18 @@ impl HighResolutionControlPrescaler { .next() .unwrap() } + + pub fn compute_min_low_res(val: u32) -> Self { + *[ + HighResolutionControlPrescaler::Div32, + HighResolutionControlPrescaler::Div64, + HighResolutionControlPrescaler::Div128, + ] + .iter() + .skip_while(|psc| >::into(**psc) <= val) + .next() + .unwrap() + } } pub(crate) mod sealed { @@ -367,10 +379,14 @@ foreach_interrupt! { let f = frequency.0; let timer_f = Self::frequency().0; let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); - let psc = HighResolutionControlPrescaler::compute_min(psc_min); + let psc = if Self::regs().isr().read().dllrdy() { + HighResolutionControlPrescaler::compute_min_high_res(psc_min) + } else { + HighResolutionControlPrescaler::compute_min_low_res(psc_min) + }; let psc_val: u32 = psc.into(); - let timer_f = timer_f / psc_val; + let timer_f = 32 * (timer_f / psc_val); let per: u16 = (timer_f / f) as u16; let regs = Self::regs(); @@ -386,10 +402,14 @@ foreach_interrupt! { let f = frequency.0; let timer_f = Self::frequency().0; let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); - let psc = HighResolutionControlPrescaler::compute_min(psc_min); + let psc = if Self::regs().isr().read().dllrdy() { + HighResolutionControlPrescaler::compute_min_high_res(psc_min) + } else { + HighResolutionControlPrescaler::compute_min_low_res(psc_min) + }; let psc_val: u32 = psc.into(); - let timer_f = timer_f / psc_val; + let timer_f = 32 * (timer_f / psc_val); let per: u16 = (timer_f / f) as u16; let regs = Self::regs(); @@ -410,7 +430,12 @@ foreach_interrupt! { // The dead-time base clock runs 4 times slower than the hrtim base clock // u9::MAX = 511 let psc_min = (psc_val * dead_time as u32) / (4 * 511); - let psc = HighResolutionControlPrescaler::compute_min(psc_min); + let psc = if Self::regs().isr().read().dllrdy() { + HighResolutionControlPrescaler::compute_min_high_res(psc_min) + } else { + HighResolutionControlPrescaler::compute_min_low_res(psc_min) + }; + let dt_psc_val: u32 = psc.into(); let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); diff --git a/examples/stm32f334/src/bin/button.rs b/examples/stm32f334/src/bin/button.rs new file mode 100644 index 00000000..599c0f27 --- /dev/null +++ b/examples/stm32f334/src/bin/button.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let mut out1 = Output::new(p.PA8, Level::Low, Speed::High); + + out1.set_high(); + Timer::after(Duration::from_millis(500)).await; + out1.set_low(); + + Timer::after(Duration::from_millis(500)).await; + info!("end program"); + + cortex_m::asm::bkpt(); +} diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index 1b5d509e..36411974 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs @@ -5,12 +5,20 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::pwm::advanced_pwm::*; -use embassy_stm32::time::khz; +use embassy_stm32::time::{khz, mhz}; +use embassy_stm32::Config; +use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = embassy_stm32::init(Default::default()); + let mut config: Config = Default::default(); + config.rcc.sysclk = Some(mhz(64)); + config.rcc.hclk = Some(mhz(64)); + config.rcc.pclk1 = Some(mhz(32)); + config.rcc.pclk2 = Some(mhz(64)); + + let p = embassy_stm32::init(config); info!("Hello World!"); let ch1 = PwmPin::new_cha(p.PA8); @@ -29,34 +37,35 @@ async fn main(_spawner: Spawner) { None, ); - let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(100)); + info!("pwm constructed"); - buck_converter.set_primary_duty(0); - buck_converter.set_secondary_duty(0); - buck_converter.set_dead_time(0); + let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(5)); - // note: if the pins are not passed into the advanced pwm struct, they will not be output - let mut boost_converter = BridgeConverter::new(pwm.ch_b, khz(100)); - - boost_converter.set_primary_duty(0); - boost_converter.set_secondary_duty(0); - - // let max = pwm.get_max_duty(); - // pwm.set_dead_time(max / 1024); + // embassy_stm32::pac::HRTIM1 + // .tim(0) + // .setr(0) + // .modify(|w| w.set_sst(Activeeffect::SETACTIVE)); // - // pwm.enable(Channel::Ch1); + // Timer::after(Duration::from_millis(500)).await; // - // info!("PWM initialized"); - // info!("PWM max duty {}", max); - // - // loop { - // pwm.set_duty(Channel::Ch1, 0); - // Timer::after(Duration::from_millis(300)).await; - // pwm.set_duty(Channel::Ch1, max / 4); - // Timer::after(Duration::from_millis(300)).await; - // pwm.set_duty(Channel::Ch1, max / 2); - // Timer::after(Duration::from_millis(300)).await; - // pwm.set_duty(Channel::Ch1, max - 1); - // Timer::after(Duration::from_millis(300)).await; - // } + // embassy_stm32::pac::HRTIM1 + // .tim(0) + // .rstr(0) + // .modify(|w| w.set_srt(Inactiveeffect::SETINACTIVE)); + + let max_duty = buck_converter.get_max_compare_value(); + + info!("max compare value: {}", max_duty); + + buck_converter.set_dead_time(max_duty / 20); + buck_converter.set_primary_duty(max_duty / 2); + buck_converter.set_secondary_duty(3 * max_duty / 4); + + buck_converter.start(); + + Timer::after(Duration::from_millis(500)).await; + + info!("end program"); + + cortex_m::asm::bkpt(); } From 2e6b813225450828ebeb9908048675db0e843330 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 2 Jul 2023 09:17:12 -0500 Subject: [PATCH 020/298] hrtim: add guardrails on bridge sec. duty --- embassy-stm32/src/pwm/advanced_pwm.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index fa34b2e1..07d8708d 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -177,7 +177,7 @@ impl BurstController { /// Represents a fixed-frequency bridge converter /// -/// Our implementation of the bridge converter uses a single channel and two compare registers, +/// Our implementation of the bridge converter uses a single channel and three compare registers, /// allowing implementation of a synchronous buck or boost converter in continuous or discontinuous /// conduction mode. /// @@ -189,6 +189,8 @@ pub struct BridgeConverter, dead_time: u16, primary_duty: u16, + min_secondary_duty: u16, + max_secondary_duty: u16, } impl> BridgeConverter { @@ -244,6 +246,8 @@ impl> Bridge channel: PhantomData, dead_time: 0, primary_duty: 0, + min_secondary_duty: 0, + max_secondary_duty: 0, } } @@ -280,16 +284,19 @@ impl> Bridge } fn update_primary_duty_or_dead_time(&mut self) { + self.min_secondary_duty = self.primary_duty + self.dead_time; + T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(self.primary_duty)); T::regs() .tim(C::raw()) .cmp(1) - .modify(|w| w.set_cmp(self.primary_duty + self.dead_time)); + .modify(|w| w.set_cmp(self.min_secondary_duty)); } /// Set the dead time as a proportion of the maximum compare value pub fn set_dead_time(&mut self, dead_time: u16) { self.dead_time = dead_time; + self.max_secondary_duty = self.get_max_compare_value() - dead_time; self.update_primary_duty_or_dead_time(); } @@ -309,9 +316,17 @@ impl> Bridge /// The secondary duty is the period in any switch is active /// - /// If less than or equal to the primary duty, the secondary switch will never be active + /// If less than or equal to the primary duty, the secondary switch will be active for one tick /// If a fully complementary output is desired, the secondary duty can be set to the max compare pub fn set_secondary_duty(&mut self, secondary_duty: u16) { + let secondary_duty = if secondary_duty > self.max_secondary_duty { + self.max_secondary_duty + } else if secondary_duty <= self.min_secondary_duty { + self.min_secondary_duty + 1 + } else { + secondary_duty + }; + T::regs().tim(C::raw()).cmp(2).modify(|w| w.set_cmp(secondary_duty)); } } From 0c49e6747c02e03fc6517969105cfdf20239fc64 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 2 Jul 2023 22:00:50 -0500 Subject: [PATCH 021/298] wip --- embassy-stm32/src/pwm/advanced_pwm.rs | 31 +++++++++++---------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index 07d8708d..9e40c5bf 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -195,7 +195,7 @@ pub struct BridgeConverter> BridgeConverter { pub fn new(_channel: C, frequency: Hertz) -> Self { - use crate::pac::hrtim::vals::{Activeeffect, Cont, Inactiveeffect}; + use crate::pac::hrtim::vals::{Activeeffect, Inactiveeffect}; T::set_channel_frequency(C::raw(), frequency); @@ -203,8 +203,7 @@ impl> Bridge T::regs().tim(C::raw()).cr().modify(|w| { w.set_preen(true); w.set_repu(true); - - w.set_cont(Cont::CONTINUOUS); + w.set_cont(true); }); // Enable timer outputs @@ -260,26 +259,22 @@ impl> Bridge } pub fn enable_burst_mode(&mut self) { - use crate::pac::hrtim::vals::{Idlem, Idles}; - T::regs().tim(C::raw()).outr().modify(|w| { - w.set_idlem(0, Idlem::SETIDLE); - w.set_idlem(1, Idlem::SETIDLE); + // Enable Burst Mode + w.set_idlem(0, true); + w.set_idlem(1, true); - w.set_idles(0, Idles::INACTIVE); - w.set_idles(1, Idles::INACTIVE); + // Set output to active during the burst + w.set_idles(0, true); + w.set_idles(1, true); }) } pub fn disable_burst_mode(&mut self) { - use crate::pac::hrtim::vals::{Idlem, Idles}; - T::regs().tim(C::raw()).outr().modify(|w| { - w.set_idlem(0, Idlem::NOEFFECT); - w.set_idlem(1, Idlem::NOEFFECT); - - w.set_idles(0, Idles::INACTIVE); - w.set_idles(1, Idles::INACTIVE); + // Disable Burst Mode + w.set_idlem(0, false); + w.set_idlem(1, false); }) } @@ -345,8 +340,6 @@ pub struct ResonantConverter> ResonantConverter { pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { - use crate::pac::hrtim::vals::Cont; - T::set_channel_frequency(C::raw(), min_frequency); // Always enable preload @@ -354,7 +347,7 @@ impl> Resona w.set_preen(true); w.set_repu(true); - w.set_cont(Cont::CONTINUOUS); + w.set_cont(true); w.set_half(true); }); From 0b63af33135784c1410dc8667cfefbaa538a1f04 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Jul 2023 19:02:04 -0500 Subject: [PATCH 022/298] wpan: prepare net impl. --- embassy-net-driver/src/lib.rs | 3 +++ embassy-net/Cargo.toml | 1 + embassy-net/src/device.rs | 2 ++ embassy-net/src/lib.rs | 13 ++++++++++++- embassy-stm32-wpan/Cargo.toml | 3 ++- examples/stm32wb/Cargo.toml | 3 ++- 6 files changed, 22 insertions(+), 3 deletions(-) diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs index 4149bf4a..09def20c 100644 --- a/embassy-net-driver/src/lib.rs +++ b/embassy-net-driver/src/lib.rs @@ -164,6 +164,9 @@ pub enum Medium { /// /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode. Ip, + + /// IEEE 802_15_4 medium + Ieee802154, } impl Default for Medium { diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 6dc429dd..9b6a11c1 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -37,6 +37,7 @@ proto-ipv4 = ["smoltcp/proto-ipv4"] proto-ipv6 = ["smoltcp/proto-ipv6"] medium-ethernet = ["smoltcp/medium-ethernet"] medium-ip = ["smoltcp/medium-ip"] +medium-ieee802154 = ["smoltcp/medium-ieee802154"] igmp = ["smoltcp/proto-igmp"] [dependencies] diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 4513c86d..d29ab897 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -51,6 +51,8 @@ where Medium::Ethernet => phy::Medium::Ethernet, #[cfg(feature = "medium-ip")] Medium::Ip => phy::Medium::Ip, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => phy::Medium::Ieee802154, #[allow(unreachable_patterns)] _ => panic!( "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.", diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 0d0a986f..ad98d7f6 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -24,9 +24,11 @@ use embassy_net_driver::{Driver, LinkState, Medium}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; +#[allow(unused_imports)] use heapless::Vec; #[cfg(feature = "igmp")] pub use smoltcp::iface::MulticastError; +#[allow(unused_imports)] use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4::{self, RetryConfig}; @@ -34,6 +36,8 @@ use smoltcp::socket::dhcpv4::{self, RetryConfig}; 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::{IpAddress, IpCidr, IpEndpoint}; #[cfg(feature = "proto-ipv4")] pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; @@ -232,7 +236,7 @@ impl Stack { resources: &'static mut StackResources, random_seed: u64, ) -> Self { - #[cfg(feature = "medium-ethernet")] + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] let medium = device.capabilities().medium; let hardware_addr = match medium { @@ -240,6 +244,8 @@ impl Stack { Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())), #[cfg(feature = "medium-ip")] Medium::Ip => HardwareAddress::Ip, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::Absent), #[allow(unreachable_patterns)] _ => panic!( "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.", @@ -262,6 +268,7 @@ impl Stack { let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; + #[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))] let mut socket = SocketStack { sockets, iface, @@ -269,6 +276,7 @@ impl Stack { next_local_port, }; + #[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))] let mut inner = Inner { device, link_up: false, @@ -287,6 +295,9 @@ impl Stack { dns_waker: WakerRegistration::new(), }; + #[cfg(feature = "medium-ieee802154")] + let _ = config; + #[cfg(feature = "proto-ipv4")] match config.ipv4 { ConfigV4::Static(config) => { diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 868bffe7..082d00f1 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -17,6 +17,7 @@ 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-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } +embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel", optional=true } defmt = { version = "0.3", optional = true } cortex-m = "0.7.6" @@ -32,7 +33,7 @@ bitflags = { version = "2.3.3", optional = true } defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "stm32wb-hci?/defmt"] ble = ["dep:stm32wb-hci"] -mac = ["dep:bitflags"] +mac = ["dep:bitflags", "dep:embassy-net-driver-channel"] stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ] diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index becf2d3f..8585b99f 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -10,6 +10,7 @@ embassy-executor = { version = "0.2.0", path = "../../embassy-executor", feature 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" defmt-rtt = "0.4" @@ -24,7 +25,7 @@ heapless = { version = "0.7.5", default-features = false } [features] default = ["ble", "mac"] -mac = ["embassy-stm32-wpan/mac"] +mac = ["embassy-stm32-wpan/mac", "dep:embassy-net"] ble = ["embassy-stm32-wpan/ble"] [[bin]] From cd592cb0550146158ea6f9d90ba8afe9e1b06a92 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Jul 2023 19:15:01 -0500 Subject: [PATCH 023/298] wpan: add files from cyw43 --- embassy-stm32-wpan/src/lib.rs | 3 +- embassy-stm32-wpan/src/mac/control.rs | 454 ++++++++++++++++++++++++ embassy-stm32-wpan/src/mac/driver.rs | 102 ++++++ embassy-stm32-wpan/src/mac/mod.rs | 92 +++++ embassy-stm32-wpan/src/mac/runner.rs | 489 ++++++++++++++++++++++++++ 5 files changed, 1139 insertions(+), 1 deletion(-) create mode 100644 embassy-stm32-wpan/src/mac/control.rs create mode 100644 embassy-stm32-wpan/src/mac/driver.rs create mode 100644 embassy-stm32-wpan/src/mac/runner.rs diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 57f0dc4f..6836d7a8 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] -#![cfg_attr(feature = "ble", feature(async_fn_in_trait))] +#![cfg_attr(any(feature = "ble", feature = "mac"), feature(async_fn_in_trait))] +#![cfg_attr(feature = "mac", feature(type_alias_impl_trait, concat_bytes))] // This must go FIRST so that all the other modules see its macros. pub mod fmt; diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs new file mode 100644 index 00000000..c67614dd --- /dev/null +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -0,0 +1,454 @@ +use core::cmp::{max, min}; + +use ch::driver::LinkState; +use embassy_net_driver_channel as ch; +use embassy_time::{Duration, Timer}; + +pub use crate::bus::SpiBusCyw43; +use crate::consts::*; +use crate::events::{Event, EventSubscriber, Events}; +use crate::fmt::Bytes; +use crate::ioctl::{IoctlState, IoctlType}; +use crate::structs::*; +use crate::{countries, events, PowerManagementMode}; + +#[derive(Debug)] +pub struct Error { + pub status: u32, +} + +pub struct Control<'a> { + state_ch: ch::StateRunner<'a>, + events: &'a Events, + ioctl_state: &'a IoctlState, +} + +impl<'a> Control<'a> { + pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self { + Self { + state_ch, + events: event_sub, + ioctl_state, + } + } + + pub async fn init(&mut self, clm: &[u8]) { + const CHUNK_SIZE: usize = 1024; + + debug!("Downloading CLM..."); + + let mut offs = 0; + for chunk in clm.chunks(CHUNK_SIZE) { + let mut flag = DOWNLOAD_FLAG_HANDLER_VER; + if offs == 0 { + flag |= DOWNLOAD_FLAG_BEGIN; + } + offs += chunk.len(); + if offs == clm.len() { + flag |= DOWNLOAD_FLAG_END; + } + + let header = DownloadHeader { + flag, + dload_type: DOWNLOAD_TYPE_CLM, + len: chunk.len() as _, + crc: 0, + }; + let mut buf = [0; 8 + 12 + CHUNK_SIZE]; + buf[0..8].copy_from_slice(b"clmload\x00"); + buf[8..20].copy_from_slice(&header.to_bytes()); + buf[20..][..chunk.len()].copy_from_slice(&chunk); + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()]) + .await; + } + + // check clmload ok + assert_eq!(self.get_iovar_u32("clmload_status").await, 0); + + debug!("Configuring misc stuff..."); + + // Disable tx gloming which transfers multiple packets in one request. + // 'glom' is short for "conglomerate" which means "gather together into + // a compact mass". + self.set_iovar_u32("bus:txglom", 0).await; + self.set_iovar_u32("apsta", 1).await; + + // read MAC addr. + let mut mac_addr = [0; 6]; + assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); + debug!("mac addr: {:02x}", Bytes(&mac_addr)); + + let country = countries::WORLD_WIDE_XX; + let country_info = CountryInfo { + country_abbrev: [country.code[0], country.code[1], 0, 0], + country_code: [country.code[0], country.code[1], 0, 0], + rev: if country.rev == 0 { -1 } else { country.rev as _ }, + }; + self.set_iovar("country", &country_info.to_bytes()).await; + + // set country takes some time, next ioctls fail if we don't wait. + Timer::after(Duration::from_millis(100)).await; + + // Set antenna to chip antenna + self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await; + + self.set_iovar_u32("bus:txglom", 0).await; + Timer::after(Duration::from_millis(100)).await; + //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...?? + //Timer::after(Duration::from_millis(100)).await; + self.set_iovar_u32("ampdu_ba_wsize", 8).await; + Timer::after(Duration::from_millis(100)).await; + self.set_iovar_u32("ampdu_mpdu", 4).await; + Timer::after(Duration::from_millis(100)).await; + //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes + + //Timer::after(Duration::from_millis(100)).await; + + // evts + let mut evts = EventMask { + iface: 0, + events: [0xFF; 24], + }; + + // Disable spammy uninteresting events. + evts.unset(Event::RADIO); + evts.unset(Event::IF); + evts.unset(Event::PROBREQ_MSG); + evts.unset(Event::PROBREQ_MSG_RX); + evts.unset(Event::PROBRESP_MSG); + evts.unset(Event::PROBRESP_MSG); + evts.unset(Event::ROAM); + + self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; + + Timer::after(Duration::from_millis(100)).await; + + // set wifi up + self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; + + Timer::after(Duration::from_millis(100)).await; + + self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto + self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any + + Timer::after(Duration::from_millis(100)).await; + + self.state_ch.set_ethernet_address(mac_addr); + + debug!("INIT DONE"); + } + + pub async fn set_power_management(&mut self, mode: PowerManagementMode) { + // power save mode + let mode_num = mode.mode(); + if mode_num == 2 { + self.set_iovar_u32("pm2_sleep_ret", mode.sleep_ret_ms() as u32).await; + self.set_iovar_u32("bcn_li_bcn", mode.beacon_period() as u32).await; + self.set_iovar_u32("bcn_li_dtim", mode.dtim_period() as u32).await; + self.set_iovar_u32("assoc_listen", mode.assoc() as u32).await; + } + self.ioctl_set_u32(86, 0, mode_num).await; + } + + pub async fn join_open(&mut self, ssid: &str) -> Result<(), Error> { + self.set_iovar_u32("ampdu_ba_wsize", 8).await; + + self.ioctl_set_u32(134, 0, 0).await; // wsec = open + self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await; + self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 + self.ioctl_set_u32(22, 0, 0).await; // set_auth = open (0) + + let mut i = SsidInfo { + len: ssid.len() as _, + ssid: [0; 32], + }; + i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); + + self.wait_for_join(i).await + } + + pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> { + self.set_iovar_u32("ampdu_ba_wsize", 8).await; + + self.ioctl_set_u32(134, 0, 4).await; // wsec = wpa2 + self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await; + self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await; + self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await; + + Timer::after(Duration::from_millis(100)).await; + + let mut pfi = PassphraseInfo { + len: passphrase.len() as _, + flags: 1, + passphrase: [0; 64], + }; + pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes()); + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) + .await; // WLC_SET_WSEC_PMK + + self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 + self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open) + self.ioctl_set_u32(165, 0, 0x80).await; // set_wpa_auth + + let mut i = SsidInfo { + len: ssid.len() as _, + ssid: [0; 32], + }; + i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); + + self.wait_for_join(i).await + } + + async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> { + self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]); + let mut subscriber = self.events.queue.subscriber().unwrap(); + // the actual join operation starts here + // we make sure to enable events before so we don't miss any + + // set_ssid + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) + .await; + + // to complete the join, we wait for a SET_SSID event + // we also save the AUTH status for the user, it may be interesting + let mut auth_status = 0; + let status = loop { + let msg = subscriber.next_message_pure().await; + if msg.header.event_type == Event::AUTH && msg.header.status != EStatus::SUCCESS { + auth_status = msg.header.status; + } else if msg.header.event_type == Event::SET_SSID { + // join operation ends with SET_SSID event + break msg.header.status; + } + }; + + self.events.mask.disable_all(); + if status == EStatus::SUCCESS { + // successful join + self.state_ch.set_link_state(LinkState::Up); + debug!("JOINED"); + Ok(()) + } else { + warn!("JOIN failed with status={} auth={}", status, auth_status); + Err(Error { status }) + } + } + + pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) { + assert!(gpio_n < 3); + self.set_iovar_u32x2("gpioout", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 }) + .await + } + + pub async fn start_ap_open(&mut self, ssid: &str, channel: u8) { + self.start_ap(ssid, "", Security::OPEN, channel).await; + } + + pub async fn start_ap_wpa2(&mut self, ssid: &str, passphrase: &str, channel: u8) { + self.start_ap(ssid, passphrase, Security::WPA2_AES_PSK, channel).await; + } + + async fn start_ap(&mut self, ssid: &str, passphrase: &str, security: Security, channel: u8) { + if security != Security::OPEN + && (passphrase.as_bytes().len() < MIN_PSK_LEN || passphrase.as_bytes().len() > MAX_PSK_LEN) + { + panic!("Passphrase is too short or too long"); + } + + // Temporarily set wifi down + self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await; + + // Turn off APSTA mode + self.set_iovar_u32("apsta", 0).await; + + // Set wifi up again + self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; + + // Turn on AP mode + self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await; + + // Set SSID + let mut i = SsidInfoWithIndex { + index: 0, + ssid_info: SsidInfo { + len: ssid.as_bytes().len() as _, + ssid: [0; 32], + }, + }; + i.ssid_info.ssid[..ssid.as_bytes().len()].copy_from_slice(ssid.as_bytes()); + self.set_iovar("bsscfg:ssid", &i.to_bytes()).await; + + // Set channel number + self.ioctl_set_u32(IOCTL_CMD_SET_CHANNEL, 0, channel as u32).await; + + // Set security + self.set_iovar_u32x2("bsscfg:wsec", 0, (security as u32) & 0xFF).await; + + if security != Security::OPEN { + self.set_iovar_u32x2("bsscfg:wpa_auth", 0, 0x0084).await; // wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK + + Timer::after(Duration::from_millis(100)).await; + + // Set passphrase + let mut pfi = PassphraseInfo { + len: passphrase.as_bytes().len() as _, + flags: 1, // WSEC_PASSPHRASE + passphrase: [0; 64], + }; + pfi.passphrase[..passphrase.as_bytes().len()].copy_from_slice(passphrase.as_bytes()); + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) + .await; + } + + // Change mutlicast rate from 1 Mbps to 11 Mbps + self.set_iovar_u32("2g_mrate", 11000000 / 500000).await; + + // Start AP + self.set_iovar_u32x2("bss", 0, 1).await; // bss = BSS_UP + } + + async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { + let mut buf = [0; 8]; + buf[0..4].copy_from_slice(&val1.to_le_bytes()); + buf[4..8].copy_from_slice(&val2.to_le_bytes()); + self.set_iovar(name, &buf).await + } + + async fn set_iovar_u32(&mut self, name: &str, val: u32) { + self.set_iovar(name, &val.to_le_bytes()).await + } + + async fn get_iovar_u32(&mut self, name: &str) -> u32 { + let mut buf = [0; 4]; + let len = self.get_iovar(name, &mut buf).await; + assert_eq!(len, 4); + u32::from_le_bytes(buf) + } + + async fn set_iovar(&mut self, name: &str, val: &[u8]) { + self.set_iovar_v::<64>(name, val).await + } + + async fn set_iovar_v(&mut self, name: &str, val: &[u8]) { + debug!("set {} = {:02x}", name, Bytes(val)); + + let mut buf = [0; BUFSIZE]; + buf[..name.len()].copy_from_slice(name.as_bytes()); + buf[name.len()] = 0; + buf[name.len() + 1..][..val.len()].copy_from_slice(val); + + let total_len = name.len() + 1 + val.len(); + self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len]) + .await; + } + + // TODO this is not really working, it always returns all zeros. + async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize { + debug!("get {}", name); + + let mut buf = [0; 64]; + buf[..name.len()].copy_from_slice(name.as_bytes()); + buf[name.len()] = 0; + + let total_len = max(name.len() + 1, res.len()); + let res_len = self + .ioctl(IoctlType::Get, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len]) + .await; + + let out_len = min(res.len(), res_len); + res[..out_len].copy_from_slice(&buf[..out_len]); + out_len + } + + async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { + let mut buf = val.to_le_bytes(); + self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await; + } + + async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { + struct CancelOnDrop<'a>(&'a IoctlState); + + impl CancelOnDrop<'_> { + fn defuse(self) { + core::mem::forget(self); + } + } + + impl Drop for CancelOnDrop<'_> { + fn drop(&mut self) { + self.0.cancel_ioctl(); + } + } + + let ioctl = CancelOnDrop(self.ioctl_state); + let resp_len = ioctl.0.do_ioctl(kind, cmd, iface, buf).await; + ioctl.defuse(); + + resp_len + } + + /// Start a wifi scan + /// + /// Returns a `Stream` of networks found by the device + /// + /// # Note + /// Device events are currently implemented using a bounded queue. + /// To not miss any events, you should make sure to always await the stream. + pub async fn scan(&mut self) -> Scanner<'_> { + const SCANTYPE_PASSIVE: u8 = 1; + + let scan_params = ScanParams { + version: 1, + action: 1, + sync_id: 1, + ssid_len: 0, + ssid: [0; 32], + bssid: [0xff; 6], + bss_type: 2, + scan_type: SCANTYPE_PASSIVE, + nprobes: !0, + active_time: !0, + passive_time: !0, + home_time: !0, + channel_num: 0, + channel_list: [0; 1], + }; + + self.events.mask.enable(&[Event::ESCAN_RESULT]); + let subscriber = self.events.queue.subscriber().unwrap(); + self.set_iovar_v::<256>("escan", &scan_params.to_bytes()).await; + + Scanner { + subscriber, + events: &self.events, + } + } +} + +pub struct Scanner<'a> { + subscriber: EventSubscriber<'a>, + events: &'a Events, +} + +impl Scanner<'_> { + /// wait for the next found network + pub async fn next(&mut self) -> Option { + let event = self.subscriber.next_message_pure().await; + if event.header.status != EStatus::PARTIAL { + self.events.mask.disable_all(); + return None; + } + + if let events::Payload::BssInfo(bss) = event.payload { + Some(bss) + } else { + None + } + } +} + +impl Drop for Scanner<'_> { + fn drop(&mut self) { + self.events.mask.disable_all(); + } +} diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs new file mode 100644 index 00000000..3171d61f --- /dev/null +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -0,0 +1,102 @@ +#![no_std] +#![no_main] +#![allow(incomplete_features)] +#![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)] +#![deny(unused_must_use)] + +use core::slice; + +use embassy_net_driver_channel as ch; +use embedded_hal_1::digital::OutputPin; +use events::Events; +use ioctl::IoctlState; + +use crate::bus::Bus; +pub use crate::bus::SpiBusCyw43; +pub use crate::control::{Control, Error as ControlError}; +pub use crate::runner::Runner; +pub use crate::structs::BssInfo; + +const MTU: usize = 1514; + +pub struct State { + ioctl_state: IoctlState, + ch: ch::State, + events: Events, +} + +impl State { + pub fn new() -> Self { + Self { + ioctl_state: IoctlState::new(), + ch: ch::State::new(), + events: Events::new(), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PowerManagementMode { + /// Custom, officially unsupported mode. Use at your own risk. + /// All power-saving features set to their max at only a marginal decrease in power consumption + /// as oppposed to `Aggressive`. + SuperSave, + + /// Aggressive power saving mode. + Aggressive, + + /// The default mode. + PowerSave, + + /// Performance is prefered over power consumption but still some power is conserved as opposed to + /// `None`. + Performance, + + /// Unlike all the other PM modes, this lowers the power consumption at all times at the cost of + /// a much lower throughput. + ThroughputThrottling, + + /// No power management is configured. This consumes the most power. + None, +} + +impl Default for PowerManagementMode { + fn default() -> Self { + Self::PowerSave + } +} + +impl PowerManagementMode { + // TODO +} + +pub type NetDriver<'a> = ch::Device<'a, MTU>; + +pub async fn new<'a, PWR, SPI>( + state: &'a mut State, + pwr: PWR, + spi: SPI, + firmware: &[u8], +) -> (NetDriver<'a>, Control<'a>, Runner<'a, PWR, SPI>) +where + PWR: OutputPin, + SPI: SpiBusCyw43, +{ + let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); + let state_ch = ch_runner.state_runner(); + + let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events); + + runner.init(firmware).await; + + ( + device, + Control::new(state_ch, &state.events, &state.ioctl_state), + runner, + ) +} + +fn slice8_mut(x: &mut [u32]) -> &mut [u8] { + let len = x.len() * 4; + unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } +} diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index 1af8fe6b..e27f0ba7 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -1,9 +1,101 @@ pub mod commands; mod consts; +pub mod control; pub mod event; mod helpers; pub mod indications; mod macros; mod opcodes; pub mod responses; +pub mod runner; pub mod typedefs; + +use core::slice; + +use embassy_net_driver_channel as ch; + +use crate::bus::Bus; +pub use crate::bus::SpiBusCyw43; +pub use crate::mac::control::{Control, Error as ControlError}; +pub use crate::runner::Runner; +pub use crate::structs::BssInfo; +use crate::sub::mac::Mac; + +const MTU: usize = 1514; + +pub struct State { + ioctl_state: IoctlState, + ch: ch::State, + events: Events, +} + +impl State { + pub fn new() -> Self { + Self { + ioctl_state: IoctlState::new(), + ch: ch::State::new(), + events: Events::new(), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PowerManagementMode { + /// Custom, officially unsupported mode. Use at your own risk. + /// All power-saving features set to their max at only a marginal decrease in power consumption + /// as oppposed to `Aggressive`. + SuperSave, + + /// Aggressive power saving mode. + Aggressive, + + /// The default mode. + PowerSave, + + /// Performance is prefered over power consumption but still some power is conserved as opposed to + /// `None`. + Performance, + + /// Unlike all the other PM modes, this lowers the power consumption at all times at the cost of + /// a much lower throughput. + ThroughputThrottling, + + /// No power management is configured. This consumes the most power. + None, +} + +impl Default for PowerManagementMode { + fn default() -> Self { + Self::PowerSave + } +} + +impl PowerManagementMode { + // TODO +} + +pub type NetDriver<'a> = ch::Device<'a, MTU>; + +pub async fn new<'a, PWR, SPI>( + state: &'a mut State, + mac_subsystem: Mac, + firmware: &[u8], +) -> (NetDriver<'a>, Control<'a>, Runner<'a, PWR, SPI>) { + let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); + let state_ch = ch_runner.state_runner(); + + let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events); + + runner.init(firmware).await; + + ( + device, + Control::new(state_ch, &state.events, &state.ioctl_state), + runner, + ) +} + +fn slice8_mut(x: &mut [u32]) -> &mut [u8] { + let len = x.len() * 4; + unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } +} diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs new file mode 100644 index 00000000..7f0bbd67 --- /dev/null +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -0,0 +1,489 @@ +use embassy_futures::select::{select3, Either3}; +use embassy_net_driver_channel as ch; +use embassy_sync::pubsub::PubSubBehavior; + +use crate::sub::mac::Mac; + +#[cfg(feature = "firmware-logs")] +struct LogState { + addr: u32, + last_idx: usize, + buf: [u8; 256], + buf_count: usize, +} + +#[cfg(feature = "firmware-logs")] +impl Default for LogState { + fn default() -> Self { + Self { + addr: Default::default(), + last_idx: Default::default(), + buf: [0; 256], + buf_count: Default::default(), + } + } +} + +pub struct Runner<'a, PWR, SPI> { + ch: ch::Runner<'a, MTU>, + mac: Mac, + + ioctl_state: &'a IoctlState, + ioctl_id: u16, + sdpcm_seq: u8, + sdpcm_seq_max: u8, + + events: &'a Events, + + #[cfg(feature = "firmware-logs")] + log: LogState, +} + +impl<'a, PWR, SPI> Runner<'a, PWR, SPI> +where + PWR: OutputPin, + SPI: SpiBusCyw43, +{ + pub(crate) fn new( + ch: ch::Runner<'a, MTU>, + bus: Bus, + ioctl_state: &'a IoctlState, + events: &'a Events, + ) -> Self { + Self { + ch, + bus, + ioctl_state, + ioctl_id: 0, + sdpcm_seq: 0, + sdpcm_seq_max: 1, + events, + #[cfg(feature = "firmware-logs")] + log: LogState::default(), + } + } + + pub(crate) async fn init(&mut self, firmware: &[u8]) { + self.bus.init().await; + + #[cfg(feature = "firmware-logs")] + self.log_init().await; + + debug!("wifi init done"); + } + + #[cfg(feature = "firmware-logs")] + async fn log_init(&mut self) { + // Initialize shared memory for logging. + + let addr = CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size; + let shared_addr = self.bus.bp_read32(addr).await; + debug!("shared_addr {:08x}", shared_addr); + + let mut shared = [0; SharedMemData::SIZE]; + self.bus.bp_read(shared_addr, &mut shared).await; + let shared = SharedMemData::from_bytes(&shared); + + self.log.addr = shared.console_addr + 8; + } + + #[cfg(feature = "firmware-logs")] + async fn log_read(&mut self) { + // Read log struct + let mut log = [0; SharedMemLog::SIZE]; + self.bus.bp_read(self.log.addr, &mut log).await; + let log = SharedMemLog::from_bytes(&log); + + let idx = log.idx as usize; + + // If pointer hasn't moved, no need to do anything. + if idx == self.log.last_idx { + return; + } + + // Read entire buf for now. We could read only what we need, but then we + // run into annoying alignment issues in `bp_read`. + let mut buf = [0; 0x400]; + self.bus.bp_read(log.buf, &mut buf).await; + + while self.log.last_idx != idx as usize { + let b = buf[self.log.last_idx]; + if b == b'\r' || b == b'\n' { + if self.log.buf_count != 0 { + let s = unsafe { core::str::from_utf8_unchecked(&self.log.buf[..self.log.buf_count]) }; + debug!("LOGS: {}", s); + self.log.buf_count = 0; + } + } else if self.log.buf_count < self.log.buf.len() { + self.log.buf[self.log.buf_count] = b; + self.log.buf_count += 1; + } + + self.log.last_idx += 1; + if self.log.last_idx == 0x400 { + self.log.last_idx = 0; + } + } + } + + pub async fn run(mut self) -> ! { + let mut buf = [0; 512]; + loop { + #[cfg(feature = "firmware-logs")] + self.log_read().await; + + if self.has_credit() { + let ioctl = self.ioctl_state.wait_pending(); + let tx = self.ch.tx_buf(); + let ev = self.bus.wait_for_event(); + + match select3(ioctl, tx, ev).await { + Either3::First(PendingIoctl { + buf: iobuf, + kind, + cmd, + iface, + }) => { + self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }).await; + self.check_status(&mut buf).await; + } + Either3::Second(packet) => { + trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); + + let mut buf = [0; 512]; + let buf8 = slice8_mut(&mut buf); + + // There MUST be 2 bytes of padding between the SDPCM and BDC headers. + // And ONLY for data packets! + // No idea why, but the firmware will append two zero bytes to the tx'd packets + // otherwise. If the packet is exactly 1514 bytes (the max MTU), this makes it + // be oversized and get dropped. + // WHD adds it here https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/include/whd_sdpcm.h#L90 + // and adds it to the header size her https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/whd_sdpcm.c#L597 + // ¯\_(ツ)_/¯ + const PADDING_SIZE: usize = 2; + let total_len = SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE + packet.len(); + + let seq = self.sdpcm_seq; + self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); + + let sdpcm_header = SdpcmHeader { + len: total_len as u16, // TODO does this len need to be rounded up to u32? + len_inv: !total_len as u16, + sequence: seq, + channel_and_flags: CHANNEL_TYPE_DATA, + next_length: 0, + header_length: (SdpcmHeader::SIZE + PADDING_SIZE) as _, + wireless_flow_control: 0, + bus_data_credit: 0, + reserved: [0, 0], + }; + + let bdc_header = BdcHeader { + flags: BDC_VERSION << BDC_VERSION_SHIFT, + priority: 0, + flags2: 0, + data_offset: 0, + }; + trace!("tx {:?}", sdpcm_header); + trace!(" {:?}", bdc_header); + + buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); + buf8[SdpcmHeader::SIZE + PADDING_SIZE..][..BdcHeader::SIZE] + .copy_from_slice(&bdc_header.to_bytes()); + buf8[SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE..][..packet.len()] + .copy_from_slice(packet); + + let total_len = (total_len + 3) & !3; // round up to 4byte + + trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); + + self.bus.wlan_write(&buf[..(total_len / 4)]).await; + self.ch.tx_done(); + self.check_status(&mut buf).await; + } + Either3::Third(()) => { + self.handle_irq(&mut buf).await; + } + } + } else { + warn!("TX stalled"); + self.bus.wait_for_event().await; + self.handle_irq(&mut buf).await; + } + } + } + + /// Wait for IRQ on F2 packet available + async fn handle_irq(&mut self, buf: &mut [u32; 512]) { + // Receive stuff + let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; + trace!("irq{}", FormatInterrupt(irq)); + + if irq & IRQ_F2_PACKET_AVAILABLE != 0 { + self.check_status(buf).await; + } + + if irq & IRQ_DATA_UNAVAILABLE != 0 { + // TODO what should we do here? + warn!("IRQ DATA_UNAVAILABLE, clearing..."); + self.bus.write16(FUNC_BUS, REG_BUS_INTERRUPT, 1).await; + } + } + + /// Handle F2 events while status register is set + async fn check_status(&mut self, buf: &mut [u32; 512]) { + loop { + let status = self.bus.status(); + trace!("check status{}", FormatStatus(status)); + + if status & STATUS_F2_PKT_AVAILABLE != 0 { + let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; + self.bus.wlan_read(buf, len).await; + trace!("rx {:02x}", Bytes(&slice8_mut(buf)[..(len as usize).min(48)])); + self.rx(&mut slice8_mut(buf)[..len as usize]); + } else { + break; + } + } + } + + fn rx(&mut self, packet: &mut [u8]) { + let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { + return; + }; + + self.update_credit(&sdpcm_header); + + let channel = sdpcm_header.channel_and_flags & 0x0f; + + match channel { + CHANNEL_TYPE_CONTROL => { + let Some((cdc_header, response)) = CdcHeader::parse(payload) else { + return; + }; + trace!(" {:?}", cdc_header); + + if cdc_header.id == self.ioctl_id { + if cdc_header.status != 0 { + // TODO: propagate error instead + panic!("IOCTL error {}", cdc_header.status as i32); + } + + self.ioctl_state.ioctl_done(response); + } + } + CHANNEL_TYPE_EVENT => { + let Some((_, bdc_packet)) = BdcHeader::parse(payload) else { + warn!("BDC event, incomplete header"); + return; + }; + + let Some((event_packet, evt_data)) = EventPacket::parse(bdc_packet) else { + warn!("BDC event, incomplete data"); + return; + }; + + const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h + if event_packet.eth.ether_type != ETH_P_LINK_CTL { + warn!( + "unexpected ethernet type 0x{:04x}, expected Broadcom ether type 0x{:04x}", + event_packet.eth.ether_type, ETH_P_LINK_CTL + ); + return; + } + const BROADCOM_OUI: &[u8] = &[0x00, 0x10, 0x18]; + if event_packet.hdr.oui != BROADCOM_OUI { + warn!( + "unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}", + Bytes(&event_packet.hdr.oui), + Bytes(BROADCOM_OUI) + ); + return; + } + const BCMILCP_SUBTYPE_VENDOR_LONG: u16 = 32769; + if event_packet.hdr.subtype != BCMILCP_SUBTYPE_VENDOR_LONG { + warn!("unexpected subtype {}", event_packet.hdr.subtype); + return; + } + + const BCMILCP_BCM_SUBTYPE_EVENT: u16 = 1; + if event_packet.hdr.user_subtype != BCMILCP_BCM_SUBTYPE_EVENT { + warn!("unexpected user_subtype {}", event_packet.hdr.subtype); + return; + } + + let evt_type = events::Event::from(event_packet.msg.event_type as u8); + debug!( + "=== EVENT {:?}: {:?} {:02x}", + evt_type, + event_packet.msg, + Bytes(evt_data) + ); + + if self.events.mask.is_enabled(evt_type) { + let status = event_packet.msg.status; + let event_payload = match evt_type { + Event::ESCAN_RESULT if status == EStatus::PARTIAL => { + let Some((_, bss_info)) = ScanResults::parse(evt_data) else { + return; + }; + let Some(bss_info) = BssInfo::parse(bss_info) else { + return; + }; + events::Payload::BssInfo(*bss_info) + } + Event::ESCAN_RESULT => events::Payload::None, + _ => events::Payload::None, + }; + + // this intentionally uses the non-blocking publish immediate + // publish() is a deadlock risk in the current design as awaiting here prevents ioctls + // The `Runner` always yields when accessing the device, so consumers always have a chance to receive the event + // (if they are actively awaiting the queue) + self.events.queue.publish_immediate(events::Message::new( + Status { + event_type: evt_type, + status, + }, + event_payload, + )); + } + } + CHANNEL_TYPE_DATA => { + let Some((_, packet)) = BdcHeader::parse(payload) else { + return; + }; + trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); + + match self.ch.try_rx_buf() { + Some(buf) => { + buf[..packet.len()].copy_from_slice(packet); + self.ch.rx_done(packet.len()) + } + None => warn!("failed to push rxd packet to the channel."), + } + } + _ => {} + } + } + + fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) { + if sdpcm_header.channel_and_flags & 0xf < 3 { + let mut sdpcm_seq_max = sdpcm_header.bus_data_credit; + if sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) > 0x40 { + sdpcm_seq_max = self.sdpcm_seq + 2; + } + self.sdpcm_seq_max = sdpcm_seq_max; + } + } + + fn has_credit(&self) -> bool { + self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 + } + + async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { + let mut buf = [0; 512]; + let buf8 = slice8_mut(&mut buf); + + let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); + + let sdpcm_seq = self.sdpcm_seq; + self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); + self.ioctl_id = self.ioctl_id.wrapping_add(1); + + let sdpcm_header = SdpcmHeader { + len: total_len as u16, // TODO does this len need to be rounded up to u32? + len_inv: !total_len as u16, + sequence: sdpcm_seq, + channel_and_flags: CHANNEL_TYPE_CONTROL, + next_length: 0, + header_length: SdpcmHeader::SIZE as _, + wireless_flow_control: 0, + bus_data_credit: 0, + reserved: [0, 0], + }; + + let cdc_header = CdcHeader { + cmd: cmd, + len: data.len() as _, + flags: kind as u16 | (iface as u16) << 12, + id: self.ioctl_id, + status: 0, + }; + trace!("tx {:?}", sdpcm_header); + trace!(" {:?}", cdc_header); + + buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); + buf8[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); + buf8[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); + + let total_len = (total_len + 3) & !3; // round up to 4byte + + trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); + + self.bus.wlan_write(&buf[..total_len / 4]).await; + } + + async fn core_disable(&mut self, core: Core) { + let base = core.base_addr(); + + // Dummy read? + let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; + + // Check it isn't already reset + let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; + if r & AI_RESETCTRL_BIT_RESET != 0 { + return; + } + + self.bus.bp_write8(base + AI_IOCTRL_OFFSET, 0).await; + let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; + + block_for(Duration::from_millis(1)); + + self.bus + .bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET) + .await; + let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; + } + + async fn core_reset(&mut self, core: Core) { + self.core_disable(core).await; + + let base = core.base_addr(); + self.bus + .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) + .await; + let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; + + self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await; + + Timer::after(Duration::from_millis(1)).await; + + self.bus + .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN) + .await; + let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; + + Timer::after(Duration::from_millis(1)).await; + } + + async fn core_is_up(&mut self, core: Core) -> bool { + let base = core.base_addr(); + + let io = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; + if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN { + debug!("core_is_up: returning false due to bad ioctrl {:02x}", io); + return false; + } + + let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; + if r & (AI_RESETCTRL_BIT_RESET) != 0 { + debug!("core_is_up: returning false due to bad resetctrl {:02x}", r); + return false; + } + + true + } +} From 582006c75cdd7e9d819bcacc0f6724c05f161bee Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 09:32:54 -0500 Subject: [PATCH 024/298] wpan/mac: further cleanup --- embassy-stm32-wpan/src/mac/event.rs | 119 ++++++++++++++++++++ embassy-stm32-wpan/src/mac/ioctl.rs | 124 +++++++++++++++++++++ embassy-stm32-wpan/src/mac/mod.rs | 14 +-- embassy-stm32-wpan/src/mac/runner.rs | 161 ++------------------------- 4 files changed, 257 insertions(+), 161 deletions(-) create mode 100644 embassy-stm32-wpan/src/mac/ioctl.rs diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index dfce21fe..ea326a33 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -1,3 +1,8 @@ +use core::cell::RefCell; + +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::pubsub::{PubSubChannel, Subscriber}; + use super::helpers::to_u16; use super::indications::{ AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication, @@ -25,6 +30,7 @@ pub trait ParseableMacEvent { Self: Sized; } +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MacEvent { MlmeAssociateCnf(AssociateConfirm), @@ -92,3 +98,116 @@ impl TryFrom<&[u8]> for MacEvent { } } } + +// TODO this PubSub can probably be replaced with shared memory to make it a bit more efficient. +pub type EventQueue = PubSubChannel; +pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, Message, 2, 1, 1>; + +pub struct Events { + pub queue: EventQueue, + pub mask: SharedEventMask, +} + +impl Events { + pub fn new() -> Self { + Self { + queue: EventQueue::new(), + mask: SharedEventMask::default(), + } + } +} + +#[derive(Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Status { + pub event_type: MacEvent, + pub status: u32, +} + +#[derive(Clone, Copy)] +pub enum Payload { + None, + // BssInfo(BssInfo), +} + +#[derive(Clone)] + +pub struct Message { + pub header: Status, + pub payload: Payload, +} + +impl Message { + pub fn new(status: Status, payload: Payload) -> Self { + Self { + header: status, + payload, + } + } +} + +#[derive(Default)] +struct EventMask { + mask: [u32; Self::WORD_COUNT], +} + +impl EventMask { + const WORD_COUNT: usize = ((Event::LAST as u32 + (u32::BITS - 1)) / u32::BITS) as usize; + + fn enable(&mut self, event: MacEvent) { + let n = event as u32; + let word = n / u32::BITS; + let bit = n % u32::BITS; + + self.mask[word as usize] |= 1 << bit; + } + + fn disable(&mut self, event: MacEvent) { + let n = event as u32; + let word = n / u32::BITS; + let bit = n % u32::BITS; + + self.mask[word as usize] &= !(1 << bit); + } + + fn is_enabled(&self, event: MacEvent) -> bool { + let n = event as u32; + let word = n / u32::BITS; + let bit = n % u32::BITS; + + self.mask[word as usize] & (1 << bit) > 0 + } +} + +#[derive(Default)] + +pub struct SharedEventMask { + mask: RefCell, +} + +impl SharedEventMask { + pub fn enable(&self, events: &[MacEvent]) { + let mut mask = self.mask.borrow_mut(); + for event in events { + mask.enable(*event); + } + } + + #[allow(dead_code)] + pub fn disable(&self, events: &[MacEvent]) { + let mut mask = self.mask.borrow_mut(); + for event in events { + mask.disable(*event); + } + } + + pub fn disable_all(&self) { + let mut mask = self.mask.borrow_mut(); + mask.mask = Default::default(); + } + + pub fn is_enabled(&self, event: MacEvent) -> bool { + let mask = self.mask.borrow(); + mask.is_enabled(event) + } +} diff --git a/embassy-stm32-wpan/src/mac/ioctl.rs b/embassy-stm32-wpan/src/mac/ioctl.rs new file mode 100644 index 00000000..0fe55cd6 --- /dev/null +++ b/embassy-stm32-wpan/src/mac/ioctl.rs @@ -0,0 +1,124 @@ +use core::cell::{Cell, RefCell}; +use core::future::poll_fn; +use core::task::{Poll, Waker}; + +use embassy_sync::waitqueue::WakerRegistration; + +#[derive(Clone, Copy)] +pub enum IoctlType { + Get = 0, + Set = 2, +} + +#[derive(Clone, Copy)] +pub struct PendingIoctl { + pub buf: *mut [u8], + pub kind: IoctlType, + pub cmd: u32, + pub iface: u32, +} + +#[derive(Clone, Copy)] +enum IoctlStateInner { + Pending(PendingIoctl), + Sent { buf: *mut [u8] }, + Done { resp_len: usize }, +} + +struct Wakers { + control: WakerRegistration, + runner: WakerRegistration, +} + +impl Default for Wakers { + fn default() -> Self { + Self { + control: WakerRegistration::new(), + runner: WakerRegistration::new(), + } + } +} + +pub struct IoctlState { + state: Cell, + wakers: RefCell, +} + +impl IoctlState { + pub fn new() -> Self { + Self { + state: Cell::new(IoctlStateInner::Done { resp_len: 0 }), + wakers: Default::default(), + } + } + + fn wake_control(&self) { + self.wakers.borrow_mut().control.wake(); + } + + fn register_control(&self, waker: &Waker) { + self.wakers.borrow_mut().control.register(waker); + } + + fn wake_runner(&self) { + self.wakers.borrow_mut().runner.wake(); + } + + fn register_runner(&self, waker: &Waker) { + self.wakers.borrow_mut().runner.register(waker); + } + + pub async fn wait_complete(&self) -> usize { + poll_fn(|cx| { + if let IoctlStateInner::Done { resp_len } = self.state.get() { + Poll::Ready(resp_len) + } else { + self.register_control(cx.waker()); + Poll::Pending + } + }) + .await + } + + pub async fn wait_pending(&self) -> PendingIoctl { + let pending = poll_fn(|cx| { + if let IoctlStateInner::Pending(pending) = self.state.get() { + Poll::Ready(pending) + } else { + self.register_runner(cx.waker()); + Poll::Pending + } + }) + .await; + + self.state.set(IoctlStateInner::Sent { buf: pending.buf }); + pending + } + + pub fn cancel_ioctl(&self) { + self.state.set(IoctlStateInner::Done { resp_len: 0 }); + } + + pub async fn do_ioctl(&self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { + self.state + .set(IoctlStateInner::Pending(PendingIoctl { buf, kind, cmd, iface })); + self.wake_runner(); + self.wait_complete().await + } + + pub fn ioctl_done(&self, response: &[u8]) { + if let IoctlStateInner::Sent { buf } = self.state.get() { + // trace!("IOCTL Response: {:02x}", Bytes(response)); + + // TODO fix this + (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); + + self.state.set(IoctlStateInner::Done { + resp_len: response.len(), + }); + self.wake_control(); + } else { + warn!("IOCTL Response but no pending Ioctl"); + } + } +} diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index e27f0ba7..67dc429e 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -4,6 +4,7 @@ pub mod control; pub mod event; mod helpers; pub mod indications; +mod ioctl; mod macros; mod opcodes; pub mod responses; @@ -14,11 +15,10 @@ use core::slice; use embassy_net_driver_channel as ch; -use crate::bus::Bus; -pub use crate::bus::SpiBusCyw43; pub use crate::mac::control::{Control, Error as ControlError}; -pub use crate::runner::Runner; -pub use crate::structs::BssInfo; +use crate::mac::event::Events; +use crate::mac::ioctl::IoctlState; +pub use crate::mac::runner::Runner; use crate::sub::mac::Mac; const MTU: usize = 1514; @@ -76,15 +76,15 @@ impl PowerManagementMode { pub type NetDriver<'a> = ch::Device<'a, MTU>; -pub async fn new<'a, PWR, SPI>( +pub async fn new<'a>( state: &'a mut State, mac_subsystem: Mac, firmware: &[u8], -) -> (NetDriver<'a>, Control<'a>, Runner<'a, PWR, SPI>) { +) -> (NetDriver<'a>, Control<'a>, Runner<'a>) { let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); let state_ch = ch_runner.state_runner(); - let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events); + let mut runner = Runner::new(ch_runner, mac_subsystem, &state.ioctl_state, &state.events); runner.init(firmware).await; diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index 7f0bbd67..fbb7cb74 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -2,29 +2,12 @@ use embassy_futures::select::{select3, Either3}; use embassy_net_driver_channel as ch; use embassy_sync::pubsub::PubSubBehavior; +use crate::mac::event::Events; +use crate::mac::ioctl::{IoctlState, PendingIoctl}; +use crate::mac::MTU; use crate::sub::mac::Mac; -#[cfg(feature = "firmware-logs")] -struct LogState { - addr: u32, - last_idx: usize, - buf: [u8; 256], - buf_count: usize, -} - -#[cfg(feature = "firmware-logs")] -impl Default for LogState { - fn default() -> Self { - Self { - addr: Default::default(), - last_idx: Default::default(), - buf: [0; 256], - buf_count: Default::default(), - } - } -} - -pub struct Runner<'a, PWR, SPI> { +pub struct Runner<'a> { ch: ch::Runner<'a, MTU>, mac: Mac, @@ -34,32 +17,18 @@ pub struct Runner<'a, PWR, SPI> { sdpcm_seq_max: u8, events: &'a Events, - - #[cfg(feature = "firmware-logs")] - log: LogState, } -impl<'a, PWR, SPI> Runner<'a, PWR, SPI> -where - PWR: OutputPin, - SPI: SpiBusCyw43, -{ - pub(crate) fn new( - ch: ch::Runner<'a, MTU>, - bus: Bus, - ioctl_state: &'a IoctlState, - events: &'a Events, - ) -> Self { +impl<'a> Runner<'a> { + pub(crate) fn new(ch: ch::Runner<'a, MTU>, mac: Mac, ioctl_state: &'a IoctlState, events: &'a Events) -> Self { Self { ch, - bus, + mac, ioctl_state, ioctl_id: 0, sdpcm_seq: 0, sdpcm_seq_max: 1, events, - #[cfg(feature = "firmware-logs")] - log: LogState::default(), } } @@ -72,60 +41,6 @@ where debug!("wifi init done"); } - #[cfg(feature = "firmware-logs")] - async fn log_init(&mut self) { - // Initialize shared memory for logging. - - let addr = CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size; - let shared_addr = self.bus.bp_read32(addr).await; - debug!("shared_addr {:08x}", shared_addr); - - let mut shared = [0; SharedMemData::SIZE]; - self.bus.bp_read(shared_addr, &mut shared).await; - let shared = SharedMemData::from_bytes(&shared); - - self.log.addr = shared.console_addr + 8; - } - - #[cfg(feature = "firmware-logs")] - async fn log_read(&mut self) { - // Read log struct - let mut log = [0; SharedMemLog::SIZE]; - self.bus.bp_read(self.log.addr, &mut log).await; - let log = SharedMemLog::from_bytes(&log); - - let idx = log.idx as usize; - - // If pointer hasn't moved, no need to do anything. - if idx == self.log.last_idx { - return; - } - - // Read entire buf for now. We could read only what we need, but then we - // run into annoying alignment issues in `bp_read`. - let mut buf = [0; 0x400]; - self.bus.bp_read(log.buf, &mut buf).await; - - while self.log.last_idx != idx as usize { - let b = buf[self.log.last_idx]; - if b == b'\r' || b == b'\n' { - if self.log.buf_count != 0 { - let s = unsafe { core::str::from_utf8_unchecked(&self.log.buf[..self.log.buf_count]) }; - debug!("LOGS: {}", s); - self.log.buf_count = 0; - } - } else if self.log.buf_count < self.log.buf.len() { - self.log.buf[self.log.buf_count] = b; - self.log.buf_count += 1; - } - - self.log.last_idx += 1; - if self.log.last_idx == 0x400 { - self.log.last_idx = 0; - } - } - } - pub async fn run(mut self) -> ! { let mut buf = [0; 512]; loop { @@ -424,66 +339,4 @@ where self.bus.wlan_write(&buf[..total_len / 4]).await; } - - async fn core_disable(&mut self, core: Core) { - let base = core.base_addr(); - - // Dummy read? - let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; - - // Check it isn't already reset - let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; - if r & AI_RESETCTRL_BIT_RESET != 0 { - return; - } - - self.bus.bp_write8(base + AI_IOCTRL_OFFSET, 0).await; - let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; - - block_for(Duration::from_millis(1)); - - self.bus - .bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET) - .await; - let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; - } - - async fn core_reset(&mut self, core: Core) { - self.core_disable(core).await; - - let base = core.base_addr(); - self.bus - .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) - .await; - let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; - - self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await; - - Timer::after(Duration::from_millis(1)).await; - - self.bus - .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN) - .await; - let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; - - Timer::after(Duration::from_millis(1)).await; - } - - async fn core_is_up(&mut self, core: Core) -> bool { - let base = core.base_addr(); - - let io = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; - if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN { - debug!("core_is_up: returning false due to bad ioctrl {:02x}", io); - return false; - } - - let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; - if r & (AI_RESETCTRL_BIT_RESET) != 0 { - debug!("core_is_up: returning false due to bad resetctrl {:02x}", r); - return false; - } - - true - } } From e95a7dc555f367534d3b8bc7a9b6f2d361b0d951 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 12:41:57 -0500 Subject: [PATCH 025/298] wpan/mac: use slice view to avoid copy --- embassy-stm32-wpan/src/mac/commands.rs | 83 ++++++++++++++------------ embassy-stm32-wpan/src/mac/typedefs.rs | 2 + embassy-stm32-wpan/src/sub/mac.rs | 9 +-- examples/stm32wb/src/bin/mac_ffd.rs | 5 +- examples/stm32wb/src/bin/mac_rfd.rs | 35 ++++++----- tests/stm32/src/bin/wpan_mac.rs | 6 +- 6 files changed, 77 insertions(+), 63 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/mac/commands.rs index 8cfa0a05..8acae24b 100644 --- a/embassy-stm32-wpan/src/mac/commands.rs +++ b/embassy-stm32-wpan/src/mac/commands.rs @@ -1,15 +1,16 @@ +use core::{mem, slice}; + use super::opcodes::OpcodeM4ToM0; use super::typedefs::{ AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus, PanId, PibId, ScanType, SecurityLevel, }; -pub trait MacCommand { +pub trait MacCommand: Sized { const OPCODE: OpcodeM4ToM0; - const SIZE: usize; - fn copy_into_slice(&self, buf: &mut [u8]) { - unsafe { core::ptr::copy(self as *const _ as *const u8, buf as *mut _ as *mut u8, Self::SIZE) }; + fn payload<'a>(&'a self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::()) } } } @@ -41,7 +42,6 @@ pub struct AssociateRequest { impl MacCommand for AssociateRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateReq; - const SIZE: usize = 25; } /// MLME DISASSOCIATE Request sed to request a disassociation @@ -70,20 +70,22 @@ pub struct DisassociateRequest { impl MacCommand for DisassociateRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDisassociateReq; - const SIZE: usize = 24; } /// MLME GET Request used to request a PIB value #[repr(C)] +#[derive(Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GetRequest { /// the name of the PIB attribute to read pub pib_attribute: PibId, + + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 3], } impl MacCommand for GetRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq; - const SIZE: usize = 4; } /// MLME GTS Request used to request and maintain GTSs @@ -104,19 +106,20 @@ pub struct GtsRequest { impl MacCommand for GtsRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq; - const SIZE: usize = 12; } #[repr(C)] +#[derive(Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetRequest { /// MAC PIB attributes are set to their default values or not during reset pub set_default_pib: bool, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 3], } impl MacCommand for ResetRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeResetReq; - const SIZE: usize = 4; } /// MLME RX ENABLE Request used to request that the receiver is either enabled @@ -129,6 +132,8 @@ pub struct RxEnableRequest { /// configure the transceiver to RX with ranging for a value of /// RANGING_ON or to not enable ranging for RANGING_OFF pub ranging_rx_control: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], /// number of symbols measured before the receiver is to be enabled or disabled pub rx_on_time: [u8; 4], /// number of symbols for which the receiver is to be enabled @@ -137,19 +142,6 @@ pub struct RxEnableRequest { impl MacCommand for RxEnableRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeRxEnableReq; - const SIZE: usize = 12; - - fn copy_into_slice(&self, buf: &mut [u8]) { - buf[0] = self.defer_permit as u8; - buf[1] = self.ranging_rx_control as u8; - - // stuffing to keep 32bit alignment - buf[2] = 0; - buf[3] = 0; - - buf[4..8].copy_from_slice(&self.rx_on_time); - buf[8..12].copy_from_slice(&self.rx_on_duration); - } } /// MLME SCAN Request used to initiate a channel scan over a given list of channels @@ -172,11 +164,12 @@ pub struct ScanRequest { pub key_id_mode: KeyIdMode, /// index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], } impl MacCommand for ScanRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeScanReq; - const SIZE: usize = 20; } /// MLME SET Request used to attempt to write the given value to the indicated PIB attribute @@ -191,13 +184,12 @@ pub struct SetRequest { impl MacCommand for SetRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSetReq; - const SIZE: usize = 8; } /// MLME START Request used by the FFDs to intiate a new PAN or to begin using a new superframe /// configuration -#[derive(Default)] #[repr(C)] +#[derive(Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct StartRequest { /// PAN indentifier to used by the device @@ -236,7 +228,6 @@ pub struct StartRequest { impl MacCommand for StartRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeStartReq; - const SIZE: usize = 35; } /// MLME SYNC Request used to synchronize with the coordinator by acquiring and, if @@ -253,11 +244,12 @@ pub struct SyncRequest { /// /// `false` if the MLME is to synchronize with only the next beacon pub track_beacon: bool, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 1], } impl MacCommand for SyncRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSyncReq; - const SIZE: usize = 4; } /// MLME POLL Request propmts the device to request data from the coordinator @@ -278,11 +270,12 @@ pub struct PollRequest { pub key_source: [u8; 8], /// PAN identifier of the coordinator pub coord_pan_id: PanId, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], } impl MacCommand for PollRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmePollReq; - const SIZE: usize = 24; } /// MLME DPS Request allows the next higher layer to request that the PHY utilize a @@ -297,33 +290,38 @@ pub struct DpsRequest { /// the number of symbols for which the transmitter and receiver will utilize the /// respective DPS indices dps_index_duration: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 1], } impl MacCommand for DpsRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDpsReq; - const SIZE: usize = 4; } /// MLME SOUNDING request primitive which is used by the next higher layer to request that /// the PHY respond with channel sounding information #[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SoundingRequest; +pub struct SoundingRequest { + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 4], +} impl MacCommand for SoundingRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSoundingReq; - const SIZE: usize = 4; } /// MLME CALIBRATE request primitive which used to obtain the results of a ranging /// calibration request from an RDEV #[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct CalibrateRequest; +pub struct CalibrateRequest { + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 4], +} impl MacCommand for CalibrateRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeCalibrateReq; - const SIZE: usize = 4; } /// MCPS DATA Request used for MAC data related requests from the application @@ -370,6 +368,15 @@ pub struct DataRequest { pub datrate: u8, } +impl DataRequest { + pub fn set_buffer<'a>(&'a mut self, buf: &'a [u8]) -> &mut Self { + self.msdu_ptr = &buf as *const _ as *const u8; + self.msdu_length = buf.len() as u8; + + self + } +} + impl Default for DataRequest { fn default() -> Self { Self { @@ -397,7 +404,6 @@ impl Default for DataRequest { impl MacCommand for DataRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsDataReq; - const SIZE: usize = 40; } /// for MCPS PURGE Request used to purge an MSDU from the transaction queue @@ -407,11 +413,12 @@ pub struct PurgeRequest { /// the handle associated with the MSDU to be purged from the transaction /// queue pub msdu_handle: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 3], } impl MacCommand for PurgeRequest { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsPurgeReq; - const SIZE: usize = 4; } /// MLME ASSOCIATE Response used to initiate a response to an MLME-ASSOCIATE.indication @@ -434,11 +441,12 @@ pub struct AssociateResponse { pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], } impl MacCommand for AssociateResponse { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateRes; - const SIZE: usize = 24; } /// MLME ORPHAN Response used to respond to the MLME ORPHAN Indication @@ -459,9 +467,10 @@ pub struct OrphanResponse { pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + pub a_stuffing: [u8; 2], } impl MacCommand for OrphanResponse { const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeOrphanRes; - const SIZE: usize = 24; } diff --git a/embassy-stm32-wpan/src/mac/typedefs.rs b/embassy-stm32-wpan/src/mac/typedefs.rs index 30c7731b..98c67c86 100644 --- a/embassy-stm32-wpan/src/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/mac/typedefs.rs @@ -37,9 +37,11 @@ numeric_enum! { numeric_enum! { #[repr(u8)] /// this enum contains all the MAC PIB Ids + #[derive(Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum PibId { // PHY + #[default] CurrentChannel = 0x00, ChannelsSupported = 0x01, TransmitPower = 0x02, diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index 4893cb47..d30ed2f1 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -85,12 +85,7 @@ impl Mac { where T: MacCommand, { - let mut payload = [0u8; MAX_PACKET_SIZE]; - cmd.copy_into_slice(&mut payload); - - let response = self - .tl_write_and_get_response(T::OPCODE as u16, &payload[..T::SIZE]) - .await; + let response = self.tl_write_and_get_response(T::OPCODE as u16, cmd.payload()).await; if response == 0x00 { Ok(()) @@ -107,8 +102,6 @@ impl Mac { } } -const MAX_PACKET_SIZE: usize = 255; - impl evt::MemoryManager for Mac { /// SAFETY: passing a pointer to something other than a managed event packet is UB unsafe fn drop_event_packet(_: *mut EvtPacket) { diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index e4d81997..f8c8ba28 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -67,7 +67,10 @@ async fn main(spawner: Spawner) { info!("resetting"); mbox.mac_subsystem - .send_command(&ResetRequest { set_default_pib: true }) + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) .await .unwrap(); let evt = mbox.mac_subsystem.read().await; diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index b2dac72c..b0eb9106 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -69,7 +69,10 @@ async fn main(spawner: Spawner) { info!("resetting"); mbox.mac_subsystem - .send_command(&ResetRequest { set_default_pib: true }) + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) .await .unwrap(); let evt = mbox.mac_subsystem.read().await; @@ -91,6 +94,7 @@ async fn main(spawner: Spawner) { mbox.mac_subsystem .send_command(&GetRequest { pib_attribute: PibId::ExtendedAddress, + ..Default::default() }) .await .unwrap(); @@ -141,23 +145,22 @@ async fn main(spawner: Spawner) { info!("{:#x}", evt); info!("sending data"); - let mut data_buffer = [0u8; 256]; let data = b"Hello from embassy!"; - data_buffer[..data.len()].copy_from_slice(data); mbox.mac_subsystem - .send_command(&DataRequest { - src_addr_mode: AddressMode::Short, - dst_addr_mode: AddressMode::Short, - dst_pan_id: PanId([0x1A, 0xAA]), - dst_address: MacAddress::BROADCAST, - msdu_handle: 0x02, - ack_tx: 0x00, - gts_tx: false, - msdu_ptr: &data_buffer as *const _ as *const u8, - msdu_length: data.len() as u8, - security_level: SecurityLevel::Unsecure, - ..Default::default() - }) + .send_command( + DataRequest { + src_addr_mode: AddressMode::Short, + dst_addr_mode: AddressMode::Short, + dst_pan_id: PanId([0x1A, 0xAA]), + dst_address: MacAddress::BROADCAST, + msdu_handle: 0x02, + ack_tx: 0x00, + gts_tx: false, + security_level: SecurityLevel::Unsecure, + ..Default::default() + } + .set_buffer(data), + ) .await .unwrap(); let evt = mbox.mac_subsystem.read().await; diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs index cfa0aca3..2fc15dc9 100644 --- a/tests/stm32/src/bin/wpan_mac.rs +++ b/tests/stm32/src/bin/wpan_mac.rs @@ -49,7 +49,10 @@ async fn main(spawner: Spawner) { info!("resetting"); mbox.mac_subsystem - .send_command(&ResetRequest { set_default_pib: true }) + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) .await .unwrap(); let evt = mbox.mac_subsystem.read().await; @@ -71,6 +74,7 @@ async fn main(spawner: Spawner) { mbox.mac_subsystem .send_command(&GetRequest { pib_attribute: PibId::ExtendedAddress, + ..Default::default() }) .await .unwrap(); From 7c465465c1a97234c3fbeb18154bfd7f79ab07f2 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 13:59:15 -0500 Subject: [PATCH 026/298] wpan: use builtin conversion methods --- embassy-stm32-wpan/src/mac/event.rs | 4 ++-- embassy-stm32-wpan/src/mac/helpers.rs | 7 ------- embassy-stm32-wpan/src/mac/indications.rs | 13 ++++++------- embassy-stm32-wpan/src/mac/mod.rs | 1 - embassy-stm32-wpan/src/mac/responses.rs | 15 +++++++-------- 5 files changed, 15 insertions(+), 25 deletions(-) delete mode 100644 embassy-stm32-wpan/src/mac/helpers.rs diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index dfce21fe..c2bdc7e1 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -1,4 +1,3 @@ -use super::helpers::to_u16; use super::indications::{ AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication, DpsIndication, GtsIndication, OrphanIndication, PollIndication, SyncLossIndication, @@ -58,7 +57,8 @@ impl TryFrom<&[u8]> for MacEvent { type Error = (); fn try_from(value: &[u8]) -> Result { - let opcode = to_u16(&value[0..2]); + let opcode = u16::from_le_bytes(value[0..2].try_into().unwrap()); + let opcode = OpcodeM0ToM4::try_from(opcode)?; let buf = &value[2..]; diff --git a/embassy-stm32-wpan/src/mac/helpers.rs b/embassy-stm32-wpan/src/mac/helpers.rs deleted file mode 100644 index 5a5bf8a8..00000000 --- a/embassy-stm32-wpan/src/mac/helpers.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub fn to_u16(buf: &[u8]) -> u16 { - ((buf[1] as u16) << 8) | buf[0] as u16 -} - -pub fn to_u32(buf: &[u8]) -> u32 { - ((buf[0] as u32) << 0) + ((buf[1] as u32) << 8) + ((buf[2] as u32) << 16) + ((buf[3] as u32) << 24) -} diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index 6df4aa23..436b9ac9 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -1,6 +1,5 @@ use super::consts::MAX_PENDING_ADDRESS; use super::event::ParseableMacEvent; -use super::helpers::to_u32; use super::typedefs::{ AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor, PanId, SecurityLevel, @@ -114,7 +113,7 @@ impl ParseableMacEvent for BeaconNotifyIndication { ]; Ok(Self { - sdu_ptr: to_u32(&buf[0..4]) as *const u8, + sdu_ptr: u32::from_le_bytes(buf[0..4].try_into().unwrap()) as *const u8, pan_descriptor: PanDescriptor::try_from(&buf[4..26])?, addr_list, bsn: buf[82], @@ -405,7 +404,7 @@ impl ParseableMacEvent for DataIndication { }; Ok(Self { - msdu_ptr: to_u32(&buf[0..4]) as *const u8, + msdu_ptr: u32::from_le_bytes(buf[0..4].try_into().unwrap()) as *const u8, src_addr_mode, src_pan_id: PanId([buf[5], buf[6]]), src_address, @@ -424,10 +423,10 @@ impl ParseableMacEvent for DataIndication { uwn_preamble_symbol_repetitions: buf[45], datrate: buf[46], ranging_received: buf[47], - ranging_counter_start: to_u32(&buf[48..52]), - ranging_counter_stop: to_u32(&buf[52..56]), - ranging_tracking_interval: to_u32(&buf[56..60]), - ranging_offset: to_u32(&buf[60..64]), + ranging_counter_start: u32::from_le_bytes(buf[48..52].try_into().unwrap()), + ranging_counter_stop: u32::from_le_bytes(buf[52..56].try_into().unwrap()), + ranging_tracking_interval: u32::from_le_bytes(buf[56..60].try_into().unwrap()), + ranging_offset: u32::from_le_bytes(buf[60..64].try_into().unwrap()), ranging_fom: buf[65], rssi: buf[66], }) diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index 1af8fe6b..8d5edad6 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -1,7 +1,6 @@ pub mod commands; mod consts; pub mod event; -mod helpers; pub mod indications; mod macros; mod opcodes; diff --git a/embassy-stm32-wpan/src/mac/responses.rs b/embassy-stm32-wpan/src/mac/responses.rs index 2f6f5bf5..d29257f8 100644 --- a/embassy-stm32-wpan/src/mac/responses.rs +++ b/embassy-stm32-wpan/src/mac/responses.rs @@ -1,6 +1,5 @@ use super::consts::{MAX_ED_SCAN_RESULTS_SUPPORTED, MAX_PAN_DESC_SUPPORTED, MAX_SOUNDING_LIST_SUPPORTED}; use super::event::ParseableMacEvent; -use super::helpers::to_u32; use super::typedefs::{ AddressMode, AssociationStatus, KeyIdMode, MacAddress, MacStatus, PanDescriptor, PanId, PibId, ScanType, SecurityLevel, @@ -100,7 +99,7 @@ impl ParseableMacEvent for GetConfirm { fn try_parse(buf: &[u8]) -> Result { Self::validate(buf)?; - let address = to_u32(&buf[0..4]); + let address = u32::from_le_bytes(buf[0..4].try_into().unwrap()); Ok(Self { pib_attribute_value_ptr: address as *const u8, @@ -357,8 +356,8 @@ impl ParseableMacEvent for CalibrateConfirm { Ok(Self { status: MacStatus::try_from(buf[0])?, // 3 byte stuffing - cal_tx_rmaker_offset: to_u32(&buf[4..8]), - cal_rx_rmaker_offset: to_u32(&buf[8..12]), + cal_tx_rmaker_offset: u32::from_le_bytes(buf[4..8].try_into().unwrap()), + cal_rx_rmaker_offset: u32::from_le_bytes(buf[8..12].try_into().unwrap()), }) } } @@ -400,10 +399,10 @@ impl ParseableMacEvent for DataConfirm { time_stamp: [buf[1], buf[2], buf[3], buf[4]], ranging_received: buf[5], status: MacStatus::try_from(buf[6])?, - ranging_counter_start: to_u32(&buf[7..11]), - ranging_counter_stop: to_u32(&buf[11..15]), - ranging_tracking_interval: to_u32(&buf[15..19]), - ranging_offset: to_u32(&buf[19..23]), + ranging_counter_start: u32::from_le_bytes(buf[7..11].try_into().unwrap()), + ranging_counter_stop: u32::from_le_bytes(buf[11..15].try_into().unwrap()), + ranging_tracking_interval: u32::from_le_bytes(buf[15..19].try_into().unwrap()), + ranging_offset: u32::from_le_bytes(buf[19..23].try_into().unwrap()), ranging_fom: buf[24], }) } From 28b419d65ede6ff29c79dbcaa27145f1c3458a57 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 15:09:30 -0500 Subject: [PATCH 027/298] wpan/mac: use lifetimes to control events --- embassy-stm32-wpan/src/mac/event.rs | 169 ++++++++------ embassy-stm32-wpan/src/mac/indications.rs | 269 ++------------------- embassy-stm32-wpan/src/mac/responses.rs | 271 ++++------------------ embassy-stm32-wpan/src/sub/mac.rs | 9 +- examples/stm32wb/src/bin/mac_ffd.rs | 46 ++-- examples/stm32wb/src/bin/mac_rfd.rs | 55 +++-- tests/stm32/src/bin/wpan_mac.rs | 32 ++- 7 files changed, 243 insertions(+), 608 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index c2bdc7e1..67f207d5 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -1,3 +1,5 @@ +use core::mem; + use super::indications::{ AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication, DpsIndication, GtsIndication, OrphanIndication, PollIndication, SyncLossIndication, @@ -6,89 +8,110 @@ use super::responses::{ AssociateConfirm, CalibrateConfirm, DataConfirm, DisassociateConfirm, DpsConfirm, GetConfirm, GtsConfirm, PollConfirm, PurgeConfirm, ResetConfirm, RxEnableConfirm, ScanConfirm, SetConfirm, SoundingConfirm, StartConfirm, }; +use crate::evt::EvtBox; use crate::mac::opcodes::OpcodeM0ToM4; +use crate::sub::mac::Mac; -pub trait ParseableMacEvent { - const SIZE: usize; - - fn validate(buf: &[u8]) -> Result<(), ()> { - if buf.len() < Self::SIZE { - return Err(()); +pub(crate) trait ParseableMacEvent: Sized { + fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> { + if buf.len() < mem::size_of::() { + Err(()) + } else { + Ok(unsafe { &*(buf as *const _ as *const Self) }) } + } +} - Ok(()) +pub struct Event { + event_box: EvtBox, +} + +impl Event { + pub(crate) fn new(event_box: EvtBox) -> Self { + Self { event_box } } - fn try_parse(buf: &[u8]) -> Result - where - Self: Sized; -} - -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum MacEvent { - MlmeAssociateCnf(AssociateConfirm), - MlmeDisassociateCnf(DisassociateConfirm), - MlmeGetCnf(GetConfirm), - MlmeGtsCnf(GtsConfirm), - MlmeResetCnf(ResetConfirm), - MlmeRxEnableCnf(RxEnableConfirm), - MlmeScanCnf(ScanConfirm), - MlmeSetCnf(SetConfirm), - MlmeStartCnf(StartConfirm), - MlmePollCnf(PollConfirm), - MlmeDpsCnf(DpsConfirm), - MlmeSoundingCnf(SoundingConfirm), - MlmeCalibrateCnf(CalibrateConfirm), - McpsDataCnf(DataConfirm), - McpsPurgeCnf(PurgeConfirm), - MlmeAssociateInd(AssociateIndication), - MlmeDisassociateInd(DisassociateIndication), - MlmeBeaconNotifyInd(BeaconNotifyIndication), - MlmeCommStatusInd(CommStatusIndication), - MlmeGtsInd(GtsIndication), - MlmeOrphanInd(OrphanIndication), - MlmeSyncLossInd(SyncLossIndication), - MlmeDpsInd(DpsIndication), - McpsDataInd(DataIndication), - MlmePollInd(PollIndication), -} - -impl TryFrom<&[u8]> for MacEvent { - type Error = (); - - fn try_from(value: &[u8]) -> Result { - let opcode = u16::from_le_bytes(value[0..2].try_into().unwrap()); + pub fn mac_event<'a>(&'a self) -> Result, ()> { + let payload = self.event_box.payload(); + let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap()); let opcode = OpcodeM0ToM4::try_from(opcode)?; - let buf = &value[2..]; - match opcode { - OpcodeM0ToM4::MlmeAssociateCnf => Ok(Self::MlmeAssociateCnf(AssociateConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeDisassociateCnf => Ok(Self::MlmeDisassociateCnf(DisassociateConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeGetCnf => Ok(Self::MlmeGetCnf(GetConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeGtsCnf => Ok(Self::MlmeGtsCnf(GtsConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeResetCnf => Ok(Self::MlmeResetCnf(ResetConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeRxEnableCnf => Ok(Self::MlmeRxEnableCnf(RxEnableConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeScanCnf => Ok(Self::MlmeScanCnf(ScanConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeSetCnf => Ok(Self::MlmeSetCnf(SetConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeStartCnf => Ok(Self::MlmeStartCnf(StartConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmePollCnf => Ok(Self::MlmePollCnf(PollConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeDpsCnf => Ok(Self::MlmeDpsCnf(DpsConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeSoundingCnf => Ok(Self::MlmeSoundingCnf(SoundingConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeCalibrateCnf => Ok(Self::MlmeCalibrateCnf(CalibrateConfirm::try_parse(buf)?)), - OpcodeM0ToM4::McpsDataCnf => Ok(Self::McpsDataCnf(DataConfirm::try_parse(buf)?)), - OpcodeM0ToM4::McpsPurgeCnf => Ok(Self::McpsPurgeCnf(PurgeConfirm::try_parse(buf)?)), - OpcodeM0ToM4::MlmeAssociateInd => Ok(Self::MlmeAssociateInd(AssociateIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeDisassociateInd => Ok(Self::MlmeDisassociateInd(DisassociateIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeBeaconNotifyInd => Ok(Self::MlmeBeaconNotifyInd(BeaconNotifyIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeCommStatusInd => Ok(Self::MlmeCommStatusInd(CommStatusIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeGtsInd => Ok(Self::MlmeGtsInd(GtsIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeOrphanInd => Ok(Self::MlmeOrphanInd(OrphanIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeSyncLossInd => Ok(Self::MlmeSyncLossInd(SyncLossIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmeDpsInd => Ok(Self::MlmeDpsInd(DpsIndication::try_parse(buf)?)), - OpcodeM0ToM4::McpsDataInd => Ok(Self::McpsDataInd(DataIndication::try_parse(buf)?)), - OpcodeM0ToM4::MlmePollInd => Ok(Self::MlmePollInd(PollIndication::try_parse(buf)?)), + OpcodeM0ToM4::MlmeAssociateCnf => Ok(MacEvent::MlmeAssociateCnf(AssociateConfirm::from_buffer( + &payload[2..], + )?)), + OpcodeM0ToM4::MlmeDisassociateCnf => Ok(MacEvent::MlmeDisassociateCnf(DisassociateConfirm::from_buffer( + &payload[2..], + )?)), + OpcodeM0ToM4::MlmeGetCnf => Ok(MacEvent::MlmeGetCnf(GetConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeGtsCnf => Ok(MacEvent::MlmeGtsCnf(GtsConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeResetCnf => Ok(MacEvent::MlmeResetCnf(ResetConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeRxEnableCnf => { + Ok(MacEvent::MlmeRxEnableCnf(RxEnableConfirm::from_buffer(&payload[2..])?)) + } + OpcodeM0ToM4::MlmeScanCnf => Ok(MacEvent::MlmeScanCnf(ScanConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeSetCnf => Ok(MacEvent::MlmeSetCnf(SetConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeStartCnf => Ok(MacEvent::MlmeStartCnf(StartConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmePollCnf => Ok(MacEvent::MlmePollCnf(PollConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeDpsCnf => Ok(MacEvent::MlmeDpsCnf(DpsConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeSoundingCnf => { + Ok(MacEvent::MlmeSoundingCnf(SoundingConfirm::from_buffer(&payload[2..])?)) + } + OpcodeM0ToM4::MlmeCalibrateCnf => Ok(MacEvent::MlmeCalibrateCnf(CalibrateConfirm::from_buffer( + &payload[2..], + )?)), + OpcodeM0ToM4::McpsDataCnf => Ok(MacEvent::McpsDataCnf(DataConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::McpsPurgeCnf => Ok(MacEvent::McpsPurgeCnf(PurgeConfirm::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeAssociateInd => Ok(MacEvent::MlmeAssociateInd(AssociateIndication::from_buffer( + &payload[2..], + )?)), + OpcodeM0ToM4::MlmeDisassociateInd => Ok(MacEvent::MlmeDisassociateInd( + DisassociateIndication::from_buffer(&payload[2..])?, + )), + OpcodeM0ToM4::MlmeBeaconNotifyInd => Ok(MacEvent::MlmeBeaconNotifyInd( + BeaconNotifyIndication::from_buffer(&payload[2..])?, + )), + OpcodeM0ToM4::MlmeCommStatusInd => Ok(MacEvent::MlmeCommStatusInd(CommStatusIndication::from_buffer( + &payload[2..], + )?)), + OpcodeM0ToM4::MlmeGtsInd => Ok(MacEvent::MlmeGtsInd(GtsIndication::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeOrphanInd => Ok(MacEvent::MlmeOrphanInd(OrphanIndication::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeSyncLossInd => Ok(MacEvent::MlmeSyncLossInd(SyncLossIndication::from_buffer( + &payload[2..], + )?)), + OpcodeM0ToM4::MlmeDpsInd => Ok(MacEvent::MlmeDpsInd(DpsIndication::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::McpsDataInd => Ok(MacEvent::McpsDataInd(DataIndication::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmePollInd => Ok(MacEvent::MlmePollInd(PollIndication::from_buffer(&payload[2..])?)), } } } + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MacEvent<'a> { + MlmeAssociateCnf(&'a AssociateConfirm), + MlmeDisassociateCnf(&'a DisassociateConfirm), + MlmeGetCnf(&'a GetConfirm), + MlmeGtsCnf(&'a GtsConfirm), + MlmeResetCnf(&'a ResetConfirm), + MlmeRxEnableCnf(&'a RxEnableConfirm), + MlmeScanCnf(&'a ScanConfirm), + MlmeSetCnf(&'a SetConfirm), + MlmeStartCnf(&'a StartConfirm), + MlmePollCnf(&'a PollConfirm), + MlmeDpsCnf(&'a DpsConfirm), + MlmeSoundingCnf(&'a SoundingConfirm), + MlmeCalibrateCnf(&'a CalibrateConfirm), + McpsDataCnf(&'a DataConfirm), + McpsPurgeCnf(&'a PurgeConfirm), + MlmeAssociateInd(&'a AssociateIndication), + MlmeDisassociateInd(&'a DisassociateIndication), + MlmeBeaconNotifyInd(&'a BeaconNotifyIndication), + MlmeCommStatusInd(&'a CommStatusIndication), + MlmeGtsInd(&'a GtsIndication), + MlmeOrphanInd(&'a OrphanIndication), + MlmeSyncLossInd(&'a SyncLossIndication), + MlmeDpsInd(&'a DpsIndication), + McpsDataInd(&'a DataIndication), + MlmePollInd(&'a PollIndication), +} diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index 436b9ac9..c7e9be84 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -23,22 +23,7 @@ pub struct AssociateIndication { pub key_source: [u8; 8], } -impl ParseableMacEvent for AssociateIndication { - const SIZE: usize = 20; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - device_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], - capability_information: Capabilities::from_bits(buf[8]).ok_or(())?, - security_level: SecurityLevel::try_from(buf[9])?, - key_id_mode: KeyIdMode::try_from(buf[10])?, - key_index: buf[11], - key_source: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], - }) - } -} +impl ParseableMacEvent for AssociateIndication {} /// MLME DISASSOCIATE indication which will be used to send /// disassociation indication to the application. @@ -58,22 +43,7 @@ pub struct DisassociateIndication { pub key_source: [u8; 8], } -impl ParseableMacEvent for DisassociateIndication { - const SIZE: usize = 20; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - device_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], - disassociation_reason: DisassociationReason::try_from(buf[8])?, - security_level: SecurityLevel::try_from(buf[9])?, - key_id_mode: KeyIdMode::try_from(buf[10])?, - key_index: buf[11], - key_source: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], - }) - } -} +impl ParseableMacEvent for DisassociateIndication {} /// MLME BEACON NOTIIFY Indication which is used to send parameters contained /// within a beacon frame received by the MAC to the application @@ -94,34 +64,7 @@ pub struct BeaconNotifyIndication { pub sdu_length: u8, } -impl ParseableMacEvent for BeaconNotifyIndication { - const SIZE: usize = 88; - - fn try_parse(buf: &[u8]) -> Result { - // TODO: this is unchecked - - Self::validate(buf)?; - - let addr_list = [ - MacAddress::try_from(&buf[26..34])?, - MacAddress::try_from(&buf[34..42])?, - MacAddress::try_from(&buf[42..50])?, - MacAddress::try_from(&buf[50..58])?, - MacAddress::try_from(&buf[58..66])?, - MacAddress::try_from(&buf[66..74])?, - MacAddress::try_from(&buf[74..82])?, - ]; - - Ok(Self { - sdu_ptr: u32::from_le_bytes(buf[0..4].try_into().unwrap()) as *const u8, - pan_descriptor: PanDescriptor::try_from(&buf[4..26])?, - addr_list, - bsn: buf[82], - pend_addr_spec: buf[83], - sdu_length: buf[83], - }) - } -} +impl ParseableMacEvent for BeaconNotifyIndication {} /// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -149,51 +92,7 @@ pub struct CommStatusIndication { pub key_source: [u8; 8], } -impl ParseableMacEvent for CommStatusIndication { - const SIZE: usize = 32; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let src_addr_mode = AddressMode::try_from(buf[2])?; - let dst_addr_mode = AddressMode::try_from(buf[3])?; - - let src_address = match src_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[4], buf[5]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], - }, - }; - - let dst_address = match dst_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[12], buf[13]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19]], - }, - }; - - Ok(Self { - pan_id: PanId([buf[0], buf[1]]), - src_addr_mode, - dst_addr_mode, - src_address, - dst_address, - status: MacStatus::try_from(buf[20])?, - security_level: SecurityLevel::try_from(buf[21])?, - key_id_mode: KeyIdMode::try_from(buf[22])?, - key_index: buf[23], - key_source: [buf[24], buf[25], buf[26], buf[27], buf[28], buf[29], buf[30], buf[31]], - }) - } -} +impl ParseableMacEvent for CommStatusIndication {} /// MLME GTS Indication indicates that a GTS has been allocated or that a /// previously allocated GTS has been deallocated @@ -209,27 +108,13 @@ pub struct GtsIndication { pub key_id_mode: KeyIdMode, /// Index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], /// Originator of the key to be used pub key_source: [u8; 8], } -impl ParseableMacEvent for GtsIndication { - const SIZE: usize = 16; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - device_address: [buf[0], buf[1]], - gts_characteristics: buf[2], - security_level: SecurityLevel::try_from(buf[3])?, - key_id_mode: KeyIdMode::try_from(buf[4])?, - key_index: buf[5], - // 2 byte stuffing - key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], - }) - } -} +impl ParseableMacEvent for GtsIndication {} /// MLME ORPHAN Indication which is used by the coordinator to notify the /// application of the presence of an orphaned device @@ -245,24 +130,11 @@ pub struct OrphanIndication { pub key_id_mode: KeyIdMode, /// Index of the key used by the originator of the received frame pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 1], } -impl ParseableMacEvent for OrphanIndication { - const SIZE: usize = 20; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - orphan_address: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]], - key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], - security_level: SecurityLevel::try_from(buf[16])?, - key_id_mode: KeyIdMode::try_from(buf[17])?, - key_index: buf[18], - // 1 byte stuffing - }) - } -} +impl ParseableMacEvent for OrphanIndication {} /// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss /// of synchronization with the coordinator @@ -286,42 +158,20 @@ pub struct SyncLossIndication { pub key_source: [u8; 8], } -impl ParseableMacEvent for SyncLossIndication { - const SIZE: usize = 16; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - pan_id: PanId([buf[0], buf[1]]), - loss_reason: buf[2], - channel_number: MacChannel::try_from(buf[3])?, - channel_page: buf[4], - security_level: SecurityLevel::try_from(buf[5])?, - key_id_mode: KeyIdMode::try_from(buf[6])?, - key_index: buf[7], - key_source: [buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]], - }) - } -} +impl ParseableMacEvent for SyncLossIndication {} /// MLME DPS Indication which indicates the expiration of the DPSIndexDuration /// and the resetting of the DPS values in the PHY #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DpsIndication; - -impl ParseableMacEvent for DpsIndication { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self) - } +pub struct DpsIndication { + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 4], } +impl ParseableMacEvent for DpsIndication {} + #[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(C, align(8))] +#[repr(C)] pub struct DataIndication { /// Pointer to the set of octets forming the MSDU being indicated pub msdu_ptr: *const u8, @@ -373,65 +223,7 @@ pub struct DataIndication { pub rssi: u8, } -impl ParseableMacEvent for DataIndication { - const SIZE: usize = 68; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let src_addr_mode = AddressMode::try_from(buf[4])?; - let src_address = match src_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[7], buf[8]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14]], - }, - }; - - let dst_addr_mode = AddressMode::try_from(buf[15])?; - let dst_address = match dst_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[18], buf[19]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[18], buf[19], buf[20], buf[21], buf[22], buf[23], buf[24], buf[25]], - }, - }; - - Ok(Self { - msdu_ptr: u32::from_le_bytes(buf[0..4].try_into().unwrap()) as *const u8, - src_addr_mode, - src_pan_id: PanId([buf[5], buf[6]]), - src_address, - dst_addr_mode, - dst_pan_id: PanId([buf[16], buf[17]]), - dst_address, - msdu_length: buf[26], - mpdu_link_quality: buf[27], - dsn: buf[28], - time_stamp: [buf[29], buf[30], buf[31], buf[32]], - security_level: SecurityLevel::try_from(buf[33]).unwrap_or(SecurityLevel::Unsecure), // TODO: this is totaly wrong, but I'm too smol brain to fix it - key_id_mode: KeyIdMode::try_from(buf[34]).unwrap_or(KeyIdMode::Implicite), // TODO: this is totaly wrong, but I'm too smol brain to fix it - key_source: [buf[35], buf[36], buf[37], buf[38], buf[39], buf[40], buf[41], buf[42]], - key_index: buf[43], - uwbprf: buf[44], - uwn_preamble_symbol_repetitions: buf[45], - datrate: buf[46], - ranging_received: buf[47], - ranging_counter_start: u32::from_le_bytes(buf[48..52].try_into().unwrap()), - ranging_counter_stop: u32::from_le_bytes(buf[52..56].try_into().unwrap()), - ranging_tracking_interval: u32::from_le_bytes(buf[56..60].try_into().unwrap()), - ranging_offset: u32::from_le_bytes(buf[60..64].try_into().unwrap()), - ranging_fom: buf[65], - rssi: buf[66], - }) - } -} +impl ParseableMacEvent for DataIndication {} /// MLME POLL Indication which will be used for indicating the Data Request /// reception to upper layer as defined in Zigbee r22 - D.8.2 @@ -443,27 +235,4 @@ pub struct PollIndication { pub request_address: MacAddress, } -impl ParseableMacEvent for PollIndication { - const SIZE: usize = 9; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let addr_mode = AddressMode::try_from(buf[0])?; - let request_address = match addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[1], buf[2]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]], - }, - }; - - Ok(Self { - addr_mode, - request_address, - }) - } -} +impl ParseableMacEvent for PollIndication {} diff --git a/embassy-stm32-wpan/src/mac/responses.rs b/embassy-stm32-wpan/src/mac/responses.rs index d29257f8..e0376a7f 100644 --- a/embassy-stm32-wpan/src/mac/responses.rs +++ b/embassy-stm32-wpan/src/mac/responses.rs @@ -21,24 +21,11 @@ pub struct AssociateConfirm { pub key_id_mode: KeyIdMode, /// the index of the key to be used pub key_index: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], } -impl ParseableMacEvent for AssociateConfirm { - const SIZE: usize = 16; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - assoc_short_address: [buf[0], buf[1]], - status: AssociationStatus::try_from(buf[2])?, - security_level: SecurityLevel::try_from(buf[3])?, - key_source: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], - key_id_mode: KeyIdMode::try_from(buf[12])?, - key_index: buf[13], - }) - } -} +impl ParseableMacEvent for AssociateConfirm {} /// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application. #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -53,32 +40,7 @@ pub struct DisassociateConfirm { pub device_address: MacAddress, } -impl ParseableMacEvent for DisassociateConfirm { - const SIZE: usize = 12; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let device_addr_mode = AddressMode::try_from(buf[1])?; - let device_address = match device_addr_mode { - AddressMode::NoAddress => MacAddress { short: [0, 0] }, - AddressMode::Reserved => MacAddress { short: [0, 0] }, - AddressMode::Short => MacAddress { - short: [buf[4], buf[5]], - }, - AddressMode::Extended => MacAddress { - extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]], - }, - }; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - device_addr_mode, - device_pan_id: PanId([buf[2], buf[3]]), - device_address, - }) - } -} +impl ParseableMacEvent for DisassociateConfirm {} /// MLME GET Confirm which requests information about a given PIB attribute #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -91,24 +53,11 @@ pub struct GetConfirm { pub pib_attribute: PibId, /// The lenght of the PIB attribute Value return pub pib_attribute_value_len: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 1], } -impl ParseableMacEvent for GetConfirm { - const SIZE: usize = 8; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let address = u32::from_le_bytes(buf[0..4].try_into().unwrap()); - - Ok(Self { - pib_attribute_value_ptr: address as *const u8, - status: MacStatus::try_from(buf[4])?, - pib_attribute: PibId::try_from(buf[5])?, - pib_attribute_value_len: buf[6], - }) - } -} +impl ParseableMacEvent for GetConfirm {} /// MLME GTS Confirm which eports the results of a request to allocate a new GTS /// or to deallocate an existing GTS @@ -118,39 +67,22 @@ pub struct GtsConfirm { pub gts_characteristics: u8, /// The status of the GTS reques pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], } -impl ParseableMacEvent for GtsConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - gts_characteristics: buf[0], - status: MacStatus::try_from(buf[1])?, - }) - } -} +impl ParseableMacEvent for GtsConfirm {} /// MLME RESET Confirm which is used to report the results of the reset operation #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetConfirm { /// The result of the reset operation status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for ResetConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for ResetConfirm {} /// MLME RX ENABLE Confirm which is used to report the results of the attempt /// to enable or disable the receiver @@ -158,19 +90,11 @@ impl ParseableMacEvent for ResetConfirm { pub struct RxEnableConfirm { /// Result of the request to enable or disable the receiver status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for RxEnableConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for RxEnableConfirm {} /// MLME SCAN Confirm which is used to report the result of the channel scan request #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -195,42 +119,7 @@ pub struct ScanConfirm { pub uwb_energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED], } -impl ParseableMacEvent for ScanConfirm { - const SIZE: usize = 185; - - fn try_parse(buf: &[u8]) -> Result { - // TODO: this is unchecked - - Self::validate(buf)?; - - let mut energy_detect_list = [0; MAX_ED_SCAN_RESULTS_SUPPORTED]; - energy_detect_list.copy_from_slice(&buf[8..24]); - - let pan_descriptor_list = [ - PanDescriptor::try_from(&buf[24..46])?, - PanDescriptor::try_from(&buf[46..68])?, - PanDescriptor::try_from(&buf[68..90])?, - PanDescriptor::try_from(&buf[90..102])?, - PanDescriptor::try_from(&buf[102..124])?, - PanDescriptor::try_from(&buf[124..146])?, - ]; - - let mut uwb_energy_detect_list = [0; MAX_ED_SCAN_RESULTS_SUPPORTED]; - uwb_energy_detect_list.copy_from_slice(&buf[147..163]); - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - scan_type: ScanType::try_from(buf[1])?, - channel_page: buf[2], - unscanned_channels: [buf[3], buf[4], buf[5], buf[6]], - result_list_size: buf[7], - energy_detect_list, - pan_descriptor_list, - detected_category: buf[146], - uwb_energy_detect_list, - }) - } -} +impl ParseableMacEvent for ScanConfirm {} /// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -239,20 +128,11 @@ pub struct SetConfirm { pub status: MacStatus, /// The name of the PIB attribute that was written pub pin_attribute: PibId, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], } -impl ParseableMacEvent for SetConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - pin_attribute: PibId::try_from(buf[1])?, - }) - } -} +impl ParseableMacEvent for SetConfirm {} /// MLME START Confirm which is used to report the results of the attempt to /// start using a new superframe configuration @@ -260,57 +140,33 @@ impl ParseableMacEvent for SetConfirm { pub struct StartConfirm { /// Result of the attempt to start using an updated superframe configuration pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for StartConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for StartConfirm {} /// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollConfirm { /// The status of the data request pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for PollConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for PollConfirm {} /// MLME DPS Confirm which reports the results of the attempt to enable or disable the DPS #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DpsConfirm { /// The status of the DPS request pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for DpsConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - }) - } -} +impl ParseableMacEvent for DpsConfirm {} /// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide /// channel sounding information @@ -318,20 +174,11 @@ impl ParseableMacEvent for DpsConfirm { pub struct SoundingConfirm { /// Results of the sounding measurement sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED], + + status: u8, } -impl ParseableMacEvent for SoundingConfirm { - const SIZE: usize = 1; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - let mut sounding_list = [0u8; MAX_SOUNDING_LIST_SUPPORTED]; - sounding_list[..buf.len()].copy_from_slice(buf); - - Ok(Self { sounding_list }) - } -} +impl ParseableMacEvent for SoundingConfirm {} /// MLME CALIBRATE Confirm which reports the result of a request to the PHY /// to provide internal propagation path information @@ -339,6 +186,8 @@ impl ParseableMacEvent for SoundingConfirm { pub struct CalibrateConfirm { /// The status of the attempt to return sounding data pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], /// A count of the propagation time from the ranging counter /// to the transmit antenna pub cal_tx_rmaker_offset: u32, @@ -347,20 +196,7 @@ pub struct CalibrateConfirm { pub cal_rx_rmaker_offset: u32, } -impl ParseableMacEvent for CalibrateConfirm { - const SIZE: usize = 12; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - status: MacStatus::try_from(buf[0])?, - // 3 byte stuffing - cal_tx_rmaker_offset: u32::from_le_bytes(buf[4..8].try_into().unwrap()), - cal_rx_rmaker_offset: u32::from_le_bytes(buf[8..12].try_into().unwrap()), - }) - } -} +impl ParseableMacEvent for CalibrateConfirm {} /// MCPS DATA Confirm which will be used for reporting the results of /// MAC data related requests from the application @@ -386,27 +222,11 @@ pub struct DataConfirm { pub ranging_offset: u32, /// The FoM characterizing the ranging measurement pub ranging_fom: u8, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 3], } -impl ParseableMacEvent for DataConfirm { - const SIZE: usize = 28; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - msdu_handle: buf[0], - time_stamp: [buf[1], buf[2], buf[3], buf[4]], - ranging_received: buf[5], - status: MacStatus::try_from(buf[6])?, - ranging_counter_start: u32::from_le_bytes(buf[7..11].try_into().unwrap()), - ranging_counter_stop: u32::from_le_bytes(buf[11..15].try_into().unwrap()), - ranging_tracking_interval: u32::from_le_bytes(buf[15..19].try_into().unwrap()), - ranging_offset: u32::from_le_bytes(buf[19..23].try_into().unwrap()), - ranging_fom: buf[24], - }) - } -} +impl ParseableMacEvent for DataConfirm {} /// MCPS PURGE Confirm which will be used by the MAC to notify the application of /// the status of its request to purge an MSDU from the transaction queue @@ -416,17 +236,8 @@ pub struct PurgeConfirm { pub msdu_handle: u8, /// The status of the request pub status: MacStatus, + /// byte stuffing to keep 32 bit alignment + a_stuffing: [u8; 2], } -impl ParseableMacEvent for PurgeConfirm { - const SIZE: usize = 4; - - fn try_parse(buf: &[u8]) -> Result { - Self::validate(buf)?; - - Ok(Self { - msdu_handle: buf[0], - status: MacStatus::try_from(buf[1])?, - }) - } -} +impl ParseableMacEvent for PurgeConfirm {} diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index d30ed2f1..d9bf4c90 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -12,7 +12,7 @@ use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{EvtBox, EvtPacket}; use crate::mac::commands::MacCommand; -use crate::mac::event::MacEvent; +use crate::mac::event::Event; use crate::mac::typedefs::MacError; use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; use crate::{channels, evt}; @@ -94,11 +94,8 @@ impl Mac { } } - pub async fn read(&self) -> Result { - let evt_box = self.tl_read().await; - let payload = evt_box.payload(); - - MacEvent::try_from(payload) + pub async fn read(&self) -> Event { + Event::new(self.tl_read().await) } } diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index f8c8ba28..86413ea0 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -73,8 +73,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("setting extended address"); let extended_address: u64 = 0xACDE480000000001; @@ -85,8 +87,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("setting short address"); let short_address: u16 = 0x1122; @@ -97,8 +101,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("setting association permit"); let association_permit: bool = true; @@ -109,8 +115,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("setting TX power"); let transmit_power: i8 = 2; @@ -121,8 +129,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("starting FFD device"); mbox.mac_subsystem @@ -137,8 +147,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("setting RX on when idle"); let rx_on_while_idle: bool = true; @@ -149,14 +161,16 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } loop { let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt); + defmt::info!("{:#x}", evt.mac_event()); - if let Ok(evt) = evt { + if let Ok(evt) = evt.mac_event() { match evt { MacEvent::MlmeAssociateInd(association) => mbox .mac_subsystem diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index b0eb9106..7cb401d8 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -75,8 +75,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("setting extended address"); let extended_address: u64 = 0xACDE480000000002; @@ -87,8 +89,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } info!("getting extended address"); mbox.mac_subsystem @@ -98,14 +102,17 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); - if let Ok(MacEvent::MlmeGetCnf(evt)) = evt { - if evt.pib_attribute_value_len == 8 { - let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); - info!("value {:#x}", value) + if let Ok(MacEvent::MlmeGetCnf(evt)) = evt.mac_event() { + if evt.pib_attribute_value_len == 8 { + let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + + info!("value {:#x}", value) + } } } @@ -124,13 +131,15 @@ async fn main(spawner: Spawner) { }; info!("{}", a); mbox.mac_subsystem.send_command(&a).await.unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + let short_addr = { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); - let short_addr = if let Ok(MacEvent::MlmeAssociateCnf(conf)) = evt { - conf.assoc_short_address - } else { - defmt::panic!() + if let Ok(MacEvent::MlmeAssociateCnf(conf)) = evt.mac_event() { + conf.assoc_short_address + } else { + defmt::panic!() + } }; info!("setting short address"); @@ -141,8 +150,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); + } info!("sending data"); let data = b"Hello from embassy!"; @@ -163,11 +174,13 @@ async fn main(spawner: Spawner) { ) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); + } loop { let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + info!("{:#x}", evt.mac_event()); } } diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs index 2fc15dc9..d64a5ef8 100644 --- a/tests/stm32/src/bin/wpan_mac.rs +++ b/tests/stm32/src/bin/wpan_mac.rs @@ -55,8 +55,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); + } info!("setting extended address"); let extended_address: u64 = 0xACDE480000000002; @@ -67,8 +69,10 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); + } info!("getting extended address"); mbox.mac_subsystem @@ -78,14 +82,16 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); - if let Ok(MacEvent::MlmeGetCnf(evt)) = evt { - if evt.pib_attribute_value_len == 8 { - let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + if let Ok(MacEvent::MlmeGetCnf(evt)) = evt.mac_event() { + if evt.pib_attribute_value_len == 8 { + let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; - info!("value {:#x}", value) + info!("value {:#x}", value) + } } } @@ -104,8 +110,10 @@ async fn main(spawner: Spawner) { }; info!("{}", a); mbox.mac_subsystem.send_command(&a).await.unwrap(); - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt); + { + let evt = mbox.mac_subsystem.read().await; + info!("{:#x}", evt.mac_event()); + } info!("Test OK"); cortex_m::asm::bkpt(); From a0515ca7ac9f4aebeadede7c6a2fa312b60b5d55 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 16:16:56 -0500 Subject: [PATCH 028/298] wpan: add repr(c) to mac responses --- embassy-stm32-wpan/src/mac/indications.rs | 11 ++++++++++- embassy-stm32-wpan/src/mac/responses.rs | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index c7e9be84..66819dc9 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -7,6 +7,7 @@ use super::typedefs::{ /// MLME ASSOCIATE Indication which will be used by the MAC /// to indicate the reception of an association request command +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateIndication { /// Extended address of the device requesting association @@ -27,6 +28,7 @@ impl ParseableMacEvent for AssociateIndication {} /// MLME DISASSOCIATE indication which will be used to send /// disassociation indication to the application. +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DisassociateIndication { /// Extended address of the device requesting association @@ -47,6 +49,7 @@ impl ParseableMacEvent for DisassociateIndication {} /// MLME BEACON NOTIIFY Indication which is used to send parameters contained /// within a beacon frame received by the MAC to the application +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct BeaconNotifyIndication { /// he set of octets comprising the beacon payload to be transferred @@ -67,6 +70,7 @@ pub struct BeaconNotifyIndication { impl ParseableMacEvent for BeaconNotifyIndication {} /// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CommStatusIndication { /// The 16-bit PAN identifier of the device from which the frame @@ -96,6 +100,7 @@ impl ParseableMacEvent for CommStatusIndication {} /// MLME GTS Indication indicates that a GTS has been allocated or that a /// previously allocated GTS has been deallocated +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsIndication { /// The short address of the device that has been allocated or deallocated a GTS @@ -118,6 +123,7 @@ impl ParseableMacEvent for GtsIndication {} /// MLME ORPHAN Indication which is used by the coordinator to notify the /// application of the presence of an orphaned device +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OrphanIndication { /// Extended address of the orphaned device @@ -138,6 +144,7 @@ impl ParseableMacEvent for OrphanIndication {} /// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss /// of synchronization with the coordinator +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SyncLossIndication { /// The PAN identifier with which the device lost synchronization or to which it was realigned @@ -162,6 +169,7 @@ impl ParseableMacEvent for SyncLossIndication {} /// MLME DPS Indication which indicates the expiration of the DPSIndexDuration /// and the resetting of the DPS values in the PHY +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DpsIndication { /// byte stuffing to keep 32 bit alignment @@ -170,8 +178,8 @@ pub struct DpsIndication { impl ParseableMacEvent for DpsIndication {} -#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataIndication { /// Pointer to the set of octets forming the MSDU being indicated pub msdu_ptr: *const u8, @@ -227,6 +235,7 @@ impl ParseableMacEvent for DataIndication {} /// MLME POLL Indication which will be used for indicating the Data Request /// reception to upper layer as defined in Zigbee r22 - D.8.2 +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollIndication { /// addressing mode used diff --git a/embassy-stm32-wpan/src/mac/responses.rs b/embassy-stm32-wpan/src/mac/responses.rs index e0376a7f..5d203084 100644 --- a/embassy-stm32-wpan/src/mac/responses.rs +++ b/embassy-stm32-wpan/src/mac/responses.rs @@ -7,6 +7,7 @@ use super::typedefs::{ /// MLME ASSOCIATE Confirm used to inform of the initiating device whether /// its request to associate was successful or unsuccessful +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateConfirm { /// short address allocated by the coordinator on successful association @@ -28,6 +29,7 @@ pub struct AssociateConfirm { impl ParseableMacEvent for AssociateConfirm {} /// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application. +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DisassociateConfirm { /// status of the disassociation attempt @@ -43,6 +45,7 @@ pub struct DisassociateConfirm { impl ParseableMacEvent for DisassociateConfirm {} /// MLME GET Confirm which requests information about a given PIB attribute +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GetConfirm { /// The pointer to the value of the PIB attribute attempted to read @@ -61,6 +64,7 @@ impl ParseableMacEvent for GetConfirm {} /// MLME GTS Confirm which eports the results of a request to allocate a new GTS /// or to deallocate an existing GTS +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsConfirm { /// The characteristics of the GTS @@ -74,6 +78,7 @@ pub struct GtsConfirm { impl ParseableMacEvent for GtsConfirm {} /// MLME RESET Confirm which is used to report the results of the reset operation +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetConfirm { /// The result of the reset operation @@ -86,6 +91,7 @@ impl ParseableMacEvent for ResetConfirm {} /// MLME RX ENABLE Confirm which is used to report the results of the attempt /// to enable or disable the receiver +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RxEnableConfirm { /// Result of the request to enable or disable the receiver @@ -97,6 +103,7 @@ pub struct RxEnableConfirm { impl ParseableMacEvent for RxEnableConfirm {} /// MLME SCAN Confirm which is used to report the result of the channel scan request +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ScanConfirm { /// Status of the scan request @@ -122,6 +129,7 @@ pub struct ScanConfirm { impl ParseableMacEvent for ScanConfirm {} /// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SetConfirm { /// The result of the set operation @@ -136,6 +144,7 @@ impl ParseableMacEvent for SetConfirm {} /// MLME START Confirm which is used to report the results of the attempt to /// start using a new superframe configuration +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct StartConfirm { /// Result of the attempt to start using an updated superframe configuration @@ -147,6 +156,7 @@ pub struct StartConfirm { impl ParseableMacEvent for StartConfirm {} /// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollConfirm { /// The status of the data request @@ -158,6 +168,7 @@ pub struct PollConfirm { impl ParseableMacEvent for PollConfirm {} /// MLME DPS Confirm which reports the results of the attempt to enable or disable the DPS +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DpsConfirm { /// The status of the DPS request @@ -170,6 +181,7 @@ impl ParseableMacEvent for DpsConfirm {} /// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide /// channel sounding information +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SoundingConfirm { /// Results of the sounding measurement @@ -182,6 +194,7 @@ impl ParseableMacEvent for SoundingConfirm {} /// MLME CALIBRATE Confirm which reports the result of a request to the PHY /// to provide internal propagation path information +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CalibrateConfirm { /// The status of the attempt to return sounding data @@ -200,6 +213,7 @@ impl ParseableMacEvent for CalibrateConfirm {} /// MCPS DATA Confirm which will be used for reporting the results of /// MAC data related requests from the application +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataConfirm { /// The handle associated with the MSDU being confirmed @@ -230,6 +244,7 @@ impl ParseableMacEvent for DataConfirm {} /// MCPS PURGE Confirm which will be used by the MAC to notify the application of /// the status of its request to purge an MSDU from the transaction queue +#[repr(C)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PurgeConfirm { /// Handle associated with the MSDU requested to be purged from the transaction queue From 34217ea797c6bbea6219bb2bc2b611a99212e14b Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 17:28:34 -0500 Subject: [PATCH 029/298] wpan: add slice data view --- embassy-stm32-wpan/src/mac/event.rs | 73 ++++++++++------------- embassy-stm32-wpan/src/mac/indications.rs | 8 +++ examples/stm32wb/src/bin/mac_ffd.rs | 22 ++++--- 3 files changed, 52 insertions(+), 51 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index 67f207d5..a2bb7922 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -36,53 +36,40 @@ impl Event { let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap()); let opcode = OpcodeM0ToM4::try_from(opcode)?; + let buf = &payload[2..]; match opcode { - OpcodeM0ToM4::MlmeAssociateCnf => Ok(MacEvent::MlmeAssociateCnf(AssociateConfirm::from_buffer( - &payload[2..], - )?)), - OpcodeM0ToM4::MlmeDisassociateCnf => Ok(MacEvent::MlmeDisassociateCnf(DisassociateConfirm::from_buffer( - &payload[2..], - )?)), - OpcodeM0ToM4::MlmeGetCnf => Ok(MacEvent::MlmeGetCnf(GetConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeGtsCnf => Ok(MacEvent::MlmeGtsCnf(GtsConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeResetCnf => Ok(MacEvent::MlmeResetCnf(ResetConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeRxEnableCnf => { - Ok(MacEvent::MlmeRxEnableCnf(RxEnableConfirm::from_buffer(&payload[2..])?)) + OpcodeM0ToM4::MlmeAssociateCnf => Ok(MacEvent::MlmeAssociateCnf(AssociateConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeDisassociateCnf => { + Ok(MacEvent::MlmeDisassociateCnf(DisassociateConfirm::from_buffer(buf)?)) } - OpcodeM0ToM4::MlmeScanCnf => Ok(MacEvent::MlmeScanCnf(ScanConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeSetCnf => Ok(MacEvent::MlmeSetCnf(SetConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeStartCnf => Ok(MacEvent::MlmeStartCnf(StartConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmePollCnf => Ok(MacEvent::MlmePollCnf(PollConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeDpsCnf => Ok(MacEvent::MlmeDpsCnf(DpsConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeSoundingCnf => { - Ok(MacEvent::MlmeSoundingCnf(SoundingConfirm::from_buffer(&payload[2..])?)) + OpcodeM0ToM4::MlmeGetCnf => Ok(MacEvent::MlmeGetCnf(GetConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeGtsCnf => Ok(MacEvent::MlmeGtsCnf(GtsConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeResetCnf => Ok(MacEvent::MlmeResetCnf(ResetConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeRxEnableCnf => Ok(MacEvent::MlmeRxEnableCnf(RxEnableConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeScanCnf => Ok(MacEvent::MlmeScanCnf(ScanConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeSetCnf => Ok(MacEvent::MlmeSetCnf(SetConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeStartCnf => Ok(MacEvent::MlmeStartCnf(StartConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmePollCnf => Ok(MacEvent::MlmePollCnf(PollConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeDpsCnf => Ok(MacEvent::MlmeDpsCnf(DpsConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeSoundingCnf => Ok(MacEvent::MlmeSoundingCnf(SoundingConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeCalibrateCnf => Ok(MacEvent::MlmeCalibrateCnf(CalibrateConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::McpsDataCnf => Ok(MacEvent::McpsDataCnf(DataConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::McpsPurgeCnf => Ok(MacEvent::McpsPurgeCnf(PurgeConfirm::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeAssociateInd => Ok(MacEvent::MlmeAssociateInd(AssociateIndication::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeDisassociateInd => { + Ok(MacEvent::MlmeDisassociateInd(DisassociateIndication::from_buffer(buf)?)) } - OpcodeM0ToM4::MlmeCalibrateCnf => Ok(MacEvent::MlmeCalibrateCnf(CalibrateConfirm::from_buffer( - &payload[2..], - )?)), - OpcodeM0ToM4::McpsDataCnf => Ok(MacEvent::McpsDataCnf(DataConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::McpsPurgeCnf => Ok(MacEvent::McpsPurgeCnf(PurgeConfirm::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeAssociateInd => Ok(MacEvent::MlmeAssociateInd(AssociateIndication::from_buffer( - &payload[2..], - )?)), - OpcodeM0ToM4::MlmeDisassociateInd => Ok(MacEvent::MlmeDisassociateInd( - DisassociateIndication::from_buffer(&payload[2..])?, - )), - OpcodeM0ToM4::MlmeBeaconNotifyInd => Ok(MacEvent::MlmeBeaconNotifyInd( - BeaconNotifyIndication::from_buffer(&payload[2..])?, - )), - OpcodeM0ToM4::MlmeCommStatusInd => Ok(MacEvent::MlmeCommStatusInd(CommStatusIndication::from_buffer( - &payload[2..], - )?)), - OpcodeM0ToM4::MlmeGtsInd => Ok(MacEvent::MlmeGtsInd(GtsIndication::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeOrphanInd => Ok(MacEvent::MlmeOrphanInd(OrphanIndication::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmeSyncLossInd => Ok(MacEvent::MlmeSyncLossInd(SyncLossIndication::from_buffer( - &payload[2..], - )?)), - OpcodeM0ToM4::MlmeDpsInd => Ok(MacEvent::MlmeDpsInd(DpsIndication::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::McpsDataInd => Ok(MacEvent::McpsDataInd(DataIndication::from_buffer(&payload[2..])?)), - OpcodeM0ToM4::MlmePollInd => Ok(MacEvent::MlmePollInd(PollIndication::from_buffer(&payload[2..])?)), + OpcodeM0ToM4::MlmeBeaconNotifyInd => { + Ok(MacEvent::MlmeBeaconNotifyInd(BeaconNotifyIndication::from_buffer(buf)?)) + } + OpcodeM0ToM4::MlmeCommStatusInd => Ok(MacEvent::MlmeCommStatusInd(CommStatusIndication::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeGtsInd => Ok(MacEvent::MlmeGtsInd(GtsIndication::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeOrphanInd => Ok(MacEvent::MlmeOrphanInd(OrphanIndication::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeSyncLossInd => Ok(MacEvent::MlmeSyncLossInd(SyncLossIndication::from_buffer(buf)?)), + OpcodeM0ToM4::MlmeDpsInd => Ok(MacEvent::MlmeDpsInd(DpsIndication::from_buffer(buf)?)), + OpcodeM0ToM4::McpsDataInd => Ok(MacEvent::McpsDataInd(DataIndication::from_buffer(buf)?)), + OpcodeM0ToM4::MlmePollInd => Ok(MacEvent::MlmePollInd(PollIndication::from_buffer(buf)?)), } } } diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index 66819dc9..cf795dfa 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -1,3 +1,5 @@ +use core::slice; + use super::consts::MAX_PENDING_ADDRESS; use super::event::ParseableMacEvent; use super::typedefs::{ @@ -233,6 +235,12 @@ pub struct DataIndication { impl ParseableMacEvent for DataIndication {} +impl DataIndication { + pub fn payload<'a>(&'a self) -> &'a [u8] { + unsafe { slice::from_raw_parts(self.msdu_ptr as *const _ as *const u8, self.msdu_length as usize) } + } +} + /// MLME POLL Indication which will be used for indicating the Data Request /// reception to upper layer as defined in Zigbee r22 - D.8.2 #[repr(C)] diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 86413ea0..bc71e29a 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -168,9 +168,10 @@ async fn main(spawner: Spawner) { loop { let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); - if let Ok(evt) = evt.mac_event() { + defmt::info!("parsed mac event"); + defmt::info!("{:#x}", evt); + match evt { MacEvent::MlmeAssociateInd(association) => mbox .mac_subsystem @@ -184,17 +185,22 @@ async fn main(spawner: Spawner) { .await .unwrap(), MacEvent::McpsDataInd(data_ind) => { - let data_addr = data_ind.msdu_ptr; - let mut data = [0u8; 256]; - unsafe { data_addr.copy_to(&mut data as *mut _, data_ind.msdu_length as usize) } - info!("{}", data[..data_ind.msdu_length as usize]); + let payload = data_ind.payload(); + let ref_payload = b"Hello from embassy!"; + info!("{}", payload); - if &data[..data_ind.msdu_length as usize] == b"Hello from embassy!" { + if payload == ref_payload { info!("success"); + } else { + info!("ref payload: {}", ref_payload); } } - _ => {} + _ => { + defmt::info!("other mac event"); + } } + } else { + defmt::info!("failed to parse mac event"); } } } From fe1e7c4d7660163457226316ffbf30f6f3c8ddc5 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 18:07:05 -0500 Subject: [PATCH 030/298] wpan: fix datarequest --- embassy-stm32-wpan/src/mac/commands.rs | 2 +- embassy-stm32-wpan/src/mac/indications.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/mac/commands.rs index 8acae24b..8f6dcbbb 100644 --- a/embassy-stm32-wpan/src/mac/commands.rs +++ b/embassy-stm32-wpan/src/mac/commands.rs @@ -370,7 +370,7 @@ pub struct DataRequest { impl DataRequest { pub fn set_buffer<'a>(&'a mut self, buf: &'a [u8]) -> &mut Self { - self.msdu_ptr = &buf as *const _ as *const u8; + self.msdu_ptr = buf as *const _ as *const u8; self.msdu_length = buf.len() as u8; self diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index cf795dfa..98826e66 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -237,7 +237,7 @@ impl ParseableMacEvent for DataIndication {} impl DataIndication { pub fn payload<'a>(&'a self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.msdu_ptr as *const _ as *const u8, self.msdu_length as usize) } + unsafe { slice::from_raw_parts(self.msdu_ptr, self.msdu_length as usize) } } } From 7b34f5e866958f2ff28d7deb6888666690fe2837 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Jul 2023 18:54:11 -0500 Subject: [PATCH 031/298] wpan: make dataind fields private --- embassy-stm32-wpan/src/mac/indications.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index 98826e66..5445fb4a 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -206,9 +206,9 @@ pub struct DataIndication { /// The time, in symbols, at which the data were received pub time_stamp: [u8; 4], /// The security level purportedly used by the received data frame - pub security_level: SecurityLevel, + security_level: SecurityLevel, /// Mode used to identify the key used by originator of received frame - pub key_id_mode: KeyIdMode, + key_id_mode: KeyIdMode, /// The originator of the key pub key_source: [u8; 8], /// The index of the key From e4ad1aa542ad28bed23532b3a0bee0d062454516 Mon Sep 17 00:00:00 2001 From: Alex Ferro Date: Sun, 16 Jul 2023 19:59:35 -0600 Subject: [PATCH 032/298] Embassy-rp I2C: Fix 1664 Change embassy-rp i2c.rs impl of embedded_hal_async::i2c::I2c::transaction to only do the call to setup() for address once per call to transactions. Calling setup multiple times results in I2C transactions being skipped on the bus, even across calls to transaction() or devices. --- embassy-rp/src/i2c.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 791c6455..9b85b234 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -716,6 +716,9 @@ mod nightly { async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { let addr: u16 = address.into(); + if operations.len() > 0 { + Self::setup(addr)?; + } let mut iterator = operations.iter_mut(); while let Some(op) = iterator.next() { @@ -723,11 +726,9 @@ mod nightly { match op { Operation::Read(buffer) => { - Self::setup(addr)?; self.read_async_internal(buffer, false, last).await?; } Operation::Write(buffer) => { - Self::setup(addr)?; self.write_async_internal(buffer.into_iter().cloned(), last).await?; } } From 55ac480cb000b466571feee712d4ff075a1c64f8 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 17 Jul 2023 16:24:09 -0500 Subject: [PATCH 033/298] stm32/eth: fix cfg(not(time)) --- embassy-stm32/src/eth/generic_smi.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/eth/generic_smi.rs b/embassy-stm32/src/eth/generic_smi.rs index 90631b17..2ed46ca2 100644 --- a/embassy-stm32/src/eth/generic_smi.rs +++ b/embassy-stm32/src/eth/generic_smi.rs @@ -45,20 +45,19 @@ use self::phy_consts::*; pub struct GenericSMI { #[cfg(feature = "time")] poll_interval: Duration, + #[cfg(not(feature = "time"))] + _private: (), } impl GenericSMI { - #[cfg(feature = "time")] pub fn new() -> Self { Self { + #[cfg(feature = "time")] poll_interval: Duration::from_millis(500), + #[cfg(not(feature = "time"))] + _private: (), } } - - #[cfg(not(feature = "time"))] - pub fn new() -> Self { - Self {} - } } unsafe impl PHY for GenericSMI { @@ -102,6 +101,7 @@ unsafe impl PHY for GenericSMI { /// Public functions for the PHY impl GenericSMI { + #[cfg(feature = "time")] pub fn set_poll_interval(&mut self, poll_interval: Duration) { self.poll_interval = poll_interval } From 8f23b6faa6f04f83ece119e94335f892d516f6b3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 17 Jul 2023 19:26:58 -0500 Subject: [PATCH 034/298] wpan: refactor control, driver --- embassy-stm32-wpan/src/mac/control.rs | 446 +------------------------- embassy-stm32-wpan/src/mac/driver.rs | 160 ++++----- embassy-stm32-wpan/src/mac/event.rs | 120 ------- embassy-stm32-wpan/src/mac/ioctl.rs | 124 ------- embassy-stm32-wpan/src/mac/mod.rs | 83 +---- embassy-stm32-wpan/src/mac/runner.rs | 329 +------------------ 6 files changed, 105 insertions(+), 1157 deletions(-) delete mode 100644 embassy-stm32-wpan/src/mac/ioctl.rs diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs index c67614dd..6e45e595 100644 --- a/embassy-stm32-wpan/src/mac/control.rs +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -1,16 +1,4 @@ -use core::cmp::{max, min}; - -use ch::driver::LinkState; -use embassy_net_driver_channel as ch; -use embassy_time::{Duration, Timer}; - -pub use crate::bus::SpiBusCyw43; -use crate::consts::*; -use crate::events::{Event, EventSubscriber, Events}; -use crate::fmt::Bytes; -use crate::ioctl::{IoctlState, IoctlType}; -use crate::structs::*; -use crate::{countries, events, PowerManagementMode}; +use crate::mac::runner::Runner; #[derive(Debug)] pub struct Error { @@ -18,437 +6,15 @@ pub struct Error { } pub struct Control<'a> { - state_ch: ch::StateRunner<'a>, - events: &'a Events, - ioctl_state: &'a IoctlState, + runner: &'a Runner, } impl<'a> Control<'a> { - pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self { - Self { - state_ch, - events: event_sub, - ioctl_state, - } - } - - pub async fn init(&mut self, clm: &[u8]) { - const CHUNK_SIZE: usize = 1024; - - debug!("Downloading CLM..."); - - let mut offs = 0; - for chunk in clm.chunks(CHUNK_SIZE) { - let mut flag = DOWNLOAD_FLAG_HANDLER_VER; - if offs == 0 { - flag |= DOWNLOAD_FLAG_BEGIN; - } - offs += chunk.len(); - if offs == clm.len() { - flag |= DOWNLOAD_FLAG_END; - } - - let header = DownloadHeader { - flag, - dload_type: DOWNLOAD_TYPE_CLM, - len: chunk.len() as _, - crc: 0, - }; - let mut buf = [0; 8 + 12 + CHUNK_SIZE]; - buf[0..8].copy_from_slice(b"clmload\x00"); - buf[8..20].copy_from_slice(&header.to_bytes()); - buf[20..][..chunk.len()].copy_from_slice(&chunk); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()]) - .await; + pub(crate) fn new(runner: &'a Runner) -> Self { + Self { runner: runner } } - // check clmload ok - assert_eq!(self.get_iovar_u32("clmload_status").await, 0); - - debug!("Configuring misc stuff..."); - - // Disable tx gloming which transfers multiple packets in one request. - // 'glom' is short for "conglomerate" which means "gather together into - // a compact mass". - self.set_iovar_u32("bus:txglom", 0).await; - self.set_iovar_u32("apsta", 1).await; - - // read MAC addr. - let mut mac_addr = [0; 6]; - assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); - debug!("mac addr: {:02x}", Bytes(&mac_addr)); - - let country = countries::WORLD_WIDE_XX; - let country_info = CountryInfo { - country_abbrev: [country.code[0], country.code[1], 0, 0], - country_code: [country.code[0], country.code[1], 0, 0], - rev: if country.rev == 0 { -1 } else { country.rev as _ }, - }; - self.set_iovar("country", &country_info.to_bytes()).await; - - // set country takes some time, next ioctls fail if we don't wait. - Timer::after(Duration::from_millis(100)).await; - - // Set antenna to chip antenna - self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await; - - self.set_iovar_u32("bus:txglom", 0).await; - Timer::after(Duration::from_millis(100)).await; - //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...?? - //Timer::after(Duration::from_millis(100)).await; - self.set_iovar_u32("ampdu_ba_wsize", 8).await; - Timer::after(Duration::from_millis(100)).await; - self.set_iovar_u32("ampdu_mpdu", 4).await; - Timer::after(Duration::from_millis(100)).await; - //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes - - //Timer::after(Duration::from_millis(100)).await; - - // evts - let mut evts = EventMask { - iface: 0, - events: [0xFF; 24], - }; - - // Disable spammy uninteresting events. - evts.unset(Event::RADIO); - evts.unset(Event::IF); - evts.unset(Event::PROBREQ_MSG); - evts.unset(Event::PROBREQ_MSG_RX); - evts.unset(Event::PROBRESP_MSG); - evts.unset(Event::PROBRESP_MSG); - evts.unset(Event::ROAM); - - self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; - - Timer::after(Duration::from_millis(100)).await; - - // set wifi up - self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; - - Timer::after(Duration::from_millis(100)).await; - - self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto - self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any - - Timer::after(Duration::from_millis(100)).await; - - self.state_ch.set_ethernet_address(mac_addr); - - debug!("INIT DONE"); - } - - pub async fn set_power_management(&mut self, mode: PowerManagementMode) { - // power save mode - let mode_num = mode.mode(); - if mode_num == 2 { - self.set_iovar_u32("pm2_sleep_ret", mode.sleep_ret_ms() as u32).await; - self.set_iovar_u32("bcn_li_bcn", mode.beacon_period() as u32).await; - self.set_iovar_u32("bcn_li_dtim", mode.dtim_period() as u32).await; - self.set_iovar_u32("assoc_listen", mode.assoc() as u32).await; - } - self.ioctl_set_u32(86, 0, mode_num).await; - } - - pub async fn join_open(&mut self, ssid: &str) -> Result<(), Error> { - self.set_iovar_u32("ampdu_ba_wsize", 8).await; - - self.ioctl_set_u32(134, 0, 0).await; // wsec = open - self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await; - self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 - self.ioctl_set_u32(22, 0, 0).await; // set_auth = open (0) - - let mut i = SsidInfo { - len: ssid.len() as _, - ssid: [0; 32], - }; - i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - - self.wait_for_join(i).await - } - - pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> { - self.set_iovar_u32("ampdu_ba_wsize", 8).await; - - self.ioctl_set_u32(134, 0, 4).await; // wsec = wpa2 - self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await; - self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await; - self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await; - - Timer::after(Duration::from_millis(100)).await; - - let mut pfi = PassphraseInfo { - len: passphrase.len() as _, - flags: 1, - passphrase: [0; 64], - }; - pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes()); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) - .await; // WLC_SET_WSEC_PMK - - self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 - self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open) - self.ioctl_set_u32(165, 0, 0x80).await; // set_wpa_auth - - let mut i = SsidInfo { - len: ssid.len() as _, - ssid: [0; 32], - }; - i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); - - self.wait_for_join(i).await - } - - async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> { - self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]); - let mut subscriber = self.events.queue.subscriber().unwrap(); - // the actual join operation starts here - // we make sure to enable events before so we don't miss any - - // set_ssid - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) - .await; - - // to complete the join, we wait for a SET_SSID event - // we also save the AUTH status for the user, it may be interesting - let mut auth_status = 0; - let status = loop { - let msg = subscriber.next_message_pure().await; - if msg.header.event_type == Event::AUTH && msg.header.status != EStatus::SUCCESS { - auth_status = msg.header.status; - } else if msg.header.event_type == Event::SET_SSID { - // join operation ends with SET_SSID event - break msg.header.status; - } - }; - - self.events.mask.disable_all(); - if status == EStatus::SUCCESS { - // successful join - self.state_ch.set_link_state(LinkState::Up); - debug!("JOINED"); - Ok(()) - } else { - warn!("JOIN failed with status={} auth={}", status, auth_status); - Err(Error { status }) - } - } - - pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) { - assert!(gpio_n < 3); - self.set_iovar_u32x2("gpioout", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 }) - .await - } - - pub async fn start_ap_open(&mut self, ssid: &str, channel: u8) { - self.start_ap(ssid, "", Security::OPEN, channel).await; - } - - pub async fn start_ap_wpa2(&mut self, ssid: &str, passphrase: &str, channel: u8) { - self.start_ap(ssid, passphrase, Security::WPA2_AES_PSK, channel).await; - } - - async fn start_ap(&mut self, ssid: &str, passphrase: &str, security: Security, channel: u8) { - if security != Security::OPEN - && (passphrase.as_bytes().len() < MIN_PSK_LEN || passphrase.as_bytes().len() > MAX_PSK_LEN) - { - panic!("Passphrase is too short or too long"); - } - - // Temporarily set wifi down - self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await; - - // Turn off APSTA mode - self.set_iovar_u32("apsta", 0).await; - - // Set wifi up again - self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; - - // Turn on AP mode - self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await; - - // Set SSID - let mut i = SsidInfoWithIndex { - index: 0, - ssid_info: SsidInfo { - len: ssid.as_bytes().len() as _, - ssid: [0; 32], - }, - }; - i.ssid_info.ssid[..ssid.as_bytes().len()].copy_from_slice(ssid.as_bytes()); - self.set_iovar("bsscfg:ssid", &i.to_bytes()).await; - - // Set channel number - self.ioctl_set_u32(IOCTL_CMD_SET_CHANNEL, 0, channel as u32).await; - - // Set security - self.set_iovar_u32x2("bsscfg:wsec", 0, (security as u32) & 0xFF).await; - - if security != Security::OPEN { - self.set_iovar_u32x2("bsscfg:wpa_auth", 0, 0x0084).await; // wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK - - Timer::after(Duration::from_millis(100)).await; - - // Set passphrase - let mut pfi = PassphraseInfo { - len: passphrase.as_bytes().len() as _, - flags: 1, // WSEC_PASSPHRASE - passphrase: [0; 64], - }; - pfi.passphrase[..passphrase.as_bytes().len()].copy_from_slice(passphrase.as_bytes()); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) - .await; - } - - // Change mutlicast rate from 1 Mbps to 11 Mbps - self.set_iovar_u32("2g_mrate", 11000000 / 500000).await; - - // Start AP - self.set_iovar_u32x2("bss", 0, 1).await; // bss = BSS_UP - } - - async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { - let mut buf = [0; 8]; - buf[0..4].copy_from_slice(&val1.to_le_bytes()); - buf[4..8].copy_from_slice(&val2.to_le_bytes()); - self.set_iovar(name, &buf).await - } - - async fn set_iovar_u32(&mut self, name: &str, val: u32) { - self.set_iovar(name, &val.to_le_bytes()).await - } - - async fn get_iovar_u32(&mut self, name: &str) -> u32 { - let mut buf = [0; 4]; - let len = self.get_iovar(name, &mut buf).await; - assert_eq!(len, 4); - u32::from_le_bytes(buf) - } - - async fn set_iovar(&mut self, name: &str, val: &[u8]) { - self.set_iovar_v::<64>(name, val).await - } - - async fn set_iovar_v(&mut self, name: &str, val: &[u8]) { - debug!("set {} = {:02x}", name, Bytes(val)); - - let mut buf = [0; BUFSIZE]; - buf[..name.len()].copy_from_slice(name.as_bytes()); - buf[name.len()] = 0; - buf[name.len() + 1..][..val.len()].copy_from_slice(val); - - let total_len = name.len() + 1 + val.len(); - self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len]) - .await; - } - - // TODO this is not really working, it always returns all zeros. - async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize { - debug!("get {}", name); - - let mut buf = [0; 64]; - buf[..name.len()].copy_from_slice(name.as_bytes()); - buf[name.len()] = 0; - - let total_len = max(name.len() + 1, res.len()); - let res_len = self - .ioctl(IoctlType::Get, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len]) - .await; - - let out_len = min(res.len(), res_len); - res[..out_len].copy_from_slice(&buf[..out_len]); - out_len - } - - async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { - let mut buf = val.to_le_bytes(); - self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await; - } - - async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { - struct CancelOnDrop<'a>(&'a IoctlState); - - impl CancelOnDrop<'_> { - fn defuse(self) { - core::mem::forget(self); - } - } - - impl Drop for CancelOnDrop<'_> { - fn drop(&mut self) { - self.0.cancel_ioctl(); - } - } - - let ioctl = CancelOnDrop(self.ioctl_state); - let resp_len = ioctl.0.do_ioctl(kind, cmd, iface, buf).await; - ioctl.defuse(); - - resp_len - } - - /// Start a wifi scan - /// - /// Returns a `Stream` of networks found by the device - /// - /// # Note - /// Device events are currently implemented using a bounded queue. - /// To not miss any events, you should make sure to always await the stream. - pub async fn scan(&mut self) -> Scanner<'_> { - const SCANTYPE_PASSIVE: u8 = 1; - - let scan_params = ScanParams { - version: 1, - action: 1, - sync_id: 1, - ssid_len: 0, - ssid: [0; 32], - bssid: [0xff; 6], - bss_type: 2, - scan_type: SCANTYPE_PASSIVE, - nprobes: !0, - active_time: !0, - passive_time: !0, - home_time: !0, - channel_num: 0, - channel_list: [0; 1], - }; - - self.events.mask.enable(&[Event::ESCAN_RESULT]); - let subscriber = self.events.queue.subscriber().unwrap(); - self.set_iovar_v::<256>("escan", &scan_params.to_bytes()).await; - - Scanner { - subscriber, - events: &self.events, - } - } -} - -pub struct Scanner<'a> { - subscriber: EventSubscriber<'a>, - events: &'a Events, -} - -impl Scanner<'_> { - /// wait for the next found network - pub async fn next(&mut self) -> Option { - let event = self.subscriber.next_message_pure().await; - if event.header.status != EStatus::PARTIAL { - self.events.mask.disable_all(); - return None; - } - - if let events::Payload::BssInfo(bss) = event.payload { - Some(bss) - } else { - None - } - } -} - -impl Drop for Scanner<'_> { - fn drop(&mut self) { - self.events.mask.disable_all(); + pub async fn init(&mut self) { + // TODO } } diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 3171d61f..00072749 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -1,102 +1,110 @@ -#![no_std] -#![no_main] #![allow(incomplete_features)] -#![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)] #![deny(unused_must_use)] -use core::slice; +use core::task::Context; -use embassy_net_driver_channel as ch; -use embedded_hal_1::digital::OutputPin; -use events::Events; -use ioctl::IoctlState; +use embassy_net_driver::{Capabilities, LinkState, Medium}; -use crate::bus::Bus; -pub use crate::bus::SpiBusCyw43; -pub use crate::control::{Control, Error as ControlError}; -pub use crate::runner::Runner; -pub use crate::structs::BssInfo; +use crate::mac::runner::Runner; +use crate::mac::MTU; -const MTU: usize = 1514; - -pub struct State { - ioctl_state: IoctlState, - ch: ch::State, - events: Events, +pub struct Driver<'d> { + runner: &'d Runner, } -impl State { - pub fn new() -> Self { - Self { - ioctl_state: IoctlState::new(), - ch: ch::State::new(), - events: Events::new(), - } +impl<'d> Driver<'d> { + pub(crate) fn new(runner: &'d Runner) -> Self { + Self { runner: runner } } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PowerManagementMode { - /// Custom, officially unsupported mode. Use at your own risk. - /// All power-saving features set to their max at only a marginal decrease in power consumption - /// as oppposed to `Aggressive`. - SuperSave, +impl<'d> embassy_net_driver::Driver for Driver<'d> { + // type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; + // type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; + type RxToken<'a> = RxToken where Self: 'a; + type TxToken<'a> = TxToken where Self: 'a; - /// Aggressive power saving mode. - Aggressive, + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + // WAKER.register(cx.waker()); + // if self.rx.available().is_some() && self.tx.available().is_some() { + // Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) + // } else { + // None + // } - /// The default mode. - PowerSave, + None + } - /// Performance is prefered over power consumption but still some power is conserved as opposed to - /// `None`. - Performance, + fn transmit(&mut self, cx: &mut Context) -> Option> { + // WAKER.register(cx.waker()); + // / if self.tx.available().is_some() { + // / Some(TxToken { tx: &mut self.tx }) + // / } else { + // / None + // / } - /// Unlike all the other PM modes, this lowers the power consumption at all times at the cost of - /// a much lower throughput. - ThroughputThrottling, + None + } - /// No power management is configured. This consumes the most power. - None, -} + fn capabilities(&self) -> Capabilities { + let mut caps = Capabilities::default(); + caps.max_transmission_unit = MTU; + // caps.max_burst_size = Some(self.tx.len()); -impl Default for PowerManagementMode { - fn default() -> Self { - Self::PowerSave + caps.medium = Medium::Ieee802154; + caps + } + + fn link_state(&mut self, cx: &mut Context) -> LinkState { + // if self.phy.poll_link(&mut self.station_management, cx) { + // LinkState::Up + // } else { + // LinkState::Down + // } + + LinkState::Down + } + + fn ethernet_address(&self) -> [u8; 6] { + // self.mac_addr + + [0; 6] } } -impl PowerManagementMode { - // TODO +pub struct RxToken { + // rx: &'a mut RDesRing<'d>, } -pub type NetDriver<'a> = ch::Device<'a, MTU>; +impl embassy_net_driver::RxToken for RxToken { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + // let pkt = unwrap!(self.rx.available()); -pub async fn new<'a, PWR, SPI>( - state: &'a mut State, - pwr: PWR, - spi: SPI, - firmware: &[u8], -) -> (NetDriver<'a>, Control<'a>, Runner<'a, PWR, SPI>) -where - PWR: OutputPin, - SPI: SpiBusCyw43, -{ - let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); - let state_ch = ch_runner.state_runner(); - - let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events); - - runner.init(firmware).await; - - ( - device, - Control::new(state_ch, &state.events, &state.ioctl_state), - runner, - ) + let pkt = &[]; + let r = f(&mut pkt[0..]); + // self.rx.pop_packet(); + r + } } -fn slice8_mut(x: &mut [u32]) -> &mut [u8] { - let len = x.len() * 4; - unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } +pub struct TxToken { + // tx: &'a mut TDesRing<'d>, +} + +impl embassy_net_driver::TxToken for TxToken { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + // let pkt = unwrap!(self.tx.available()); + let pkt = &[]; + let r = f(&mut pkt[..len]); + // self.tx.transmit(len); + r + } } diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index 661c06ac..a2bb7922 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -1,9 +1,3 @@ -use core::cell::RefCell; - -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::pubsub::{PubSubChannel, Subscriber}; - -use super::helpers::to_u16; use core::mem; use super::indications::{ @@ -80,7 +74,6 @@ impl Event { } } -#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum MacEvent<'a> { MlmeAssociateCnf(&'a AssociateConfirm), @@ -109,116 +102,3 @@ pub enum MacEvent<'a> { McpsDataInd(&'a DataIndication), MlmePollInd(&'a PollIndication), } - -// TODO this PubSub can probably be replaced with shared memory to make it a bit more efficient. -pub type EventQueue = PubSubChannel; -pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, Message, 2, 1, 1>; - -pub struct Events { - pub queue: EventQueue, - pub mask: SharedEventMask, -} - -impl Events { - pub fn new() -> Self { - Self { - queue: EventQueue::new(), - mask: SharedEventMask::default(), - } - } -} - -#[derive(Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Status { - pub event_type: MacEvent, - pub status: u32, -} - -#[derive(Clone, Copy)] -pub enum Payload { - None, - // BssInfo(BssInfo), -} - -#[derive(Clone)] - -pub struct Message { - pub header: Status, - pub payload: Payload, -} - -impl Message { - pub fn new(status: Status, payload: Payload) -> Self { - Self { - header: status, - payload, - } - } -} - -#[derive(Default)] -struct EventMask { - mask: [u32; Self::WORD_COUNT], -} - -impl EventMask { - const WORD_COUNT: usize = ((Event::LAST as u32 + (u32::BITS - 1)) / u32::BITS) as usize; - - fn enable(&mut self, event: MacEvent) { - let n = event as u32; - let word = n / u32::BITS; - let bit = n % u32::BITS; - - self.mask[word as usize] |= 1 << bit; - } - - fn disable(&mut self, event: MacEvent) { - let n = event as u32; - let word = n / u32::BITS; - let bit = n % u32::BITS; - - self.mask[word as usize] &= !(1 << bit); - } - - fn is_enabled(&self, event: MacEvent) -> bool { - let n = event as u32; - let word = n / u32::BITS; - let bit = n % u32::BITS; - - self.mask[word as usize] & (1 << bit) > 0 - } -} - -#[derive(Default)] - -pub struct SharedEventMask { - mask: RefCell, -} - -impl SharedEventMask { - pub fn enable(&self, events: &[MacEvent]) { - let mut mask = self.mask.borrow_mut(); - for event in events { - mask.enable(*event); - } - } - - #[allow(dead_code)] - pub fn disable(&self, events: &[MacEvent]) { - let mut mask = self.mask.borrow_mut(); - for event in events { - mask.disable(*event); - } - } - - pub fn disable_all(&self) { - let mut mask = self.mask.borrow_mut(); - mask.mask = Default::default(); - } - - pub fn is_enabled(&self, event: MacEvent) -> bool { - let mask = self.mask.borrow(); - mask.is_enabled(event) - } -} diff --git a/embassy-stm32-wpan/src/mac/ioctl.rs b/embassy-stm32-wpan/src/mac/ioctl.rs deleted file mode 100644 index 0fe55cd6..00000000 --- a/embassy-stm32-wpan/src/mac/ioctl.rs +++ /dev/null @@ -1,124 +0,0 @@ -use core::cell::{Cell, RefCell}; -use core::future::poll_fn; -use core::task::{Poll, Waker}; - -use embassy_sync::waitqueue::WakerRegistration; - -#[derive(Clone, Copy)] -pub enum IoctlType { - Get = 0, - Set = 2, -} - -#[derive(Clone, Copy)] -pub struct PendingIoctl { - pub buf: *mut [u8], - pub kind: IoctlType, - pub cmd: u32, - pub iface: u32, -} - -#[derive(Clone, Copy)] -enum IoctlStateInner { - Pending(PendingIoctl), - Sent { buf: *mut [u8] }, - Done { resp_len: usize }, -} - -struct Wakers { - control: WakerRegistration, - runner: WakerRegistration, -} - -impl Default for Wakers { - fn default() -> Self { - Self { - control: WakerRegistration::new(), - runner: WakerRegistration::new(), - } - } -} - -pub struct IoctlState { - state: Cell, - wakers: RefCell, -} - -impl IoctlState { - pub fn new() -> Self { - Self { - state: Cell::new(IoctlStateInner::Done { resp_len: 0 }), - wakers: Default::default(), - } - } - - fn wake_control(&self) { - self.wakers.borrow_mut().control.wake(); - } - - fn register_control(&self, waker: &Waker) { - self.wakers.borrow_mut().control.register(waker); - } - - fn wake_runner(&self) { - self.wakers.borrow_mut().runner.wake(); - } - - fn register_runner(&self, waker: &Waker) { - self.wakers.borrow_mut().runner.register(waker); - } - - pub async fn wait_complete(&self) -> usize { - poll_fn(|cx| { - if let IoctlStateInner::Done { resp_len } = self.state.get() { - Poll::Ready(resp_len) - } else { - self.register_control(cx.waker()); - Poll::Pending - } - }) - .await - } - - pub async fn wait_pending(&self) -> PendingIoctl { - let pending = poll_fn(|cx| { - if let IoctlStateInner::Pending(pending) = self.state.get() { - Poll::Ready(pending) - } else { - self.register_runner(cx.waker()); - Poll::Pending - } - }) - .await; - - self.state.set(IoctlStateInner::Sent { buf: pending.buf }); - pending - } - - pub fn cancel_ioctl(&self) { - self.state.set(IoctlStateInner::Done { resp_len: 0 }); - } - - pub async fn do_ioctl(&self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { - self.state - .set(IoctlStateInner::Pending(PendingIoctl { buf, kind, cmd, iface })); - self.wake_runner(); - self.wait_complete().await - } - - pub fn ioctl_done(&self, response: &[u8]) { - if let IoctlStateInner::Sent { buf } = self.state.get() { - // trace!("IOCTL Response: {:02x}", Bytes(response)); - - // TODO fix this - (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); - - self.state.set(IoctlStateInner::Done { - resp_len: response.len(), - }); - self.wake_control(); - } else { - warn!("IOCTL Response but no pending Ioctl"); - } - } -} diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index df03e423..e024aeae 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -1,9 +1,9 @@ pub mod commands; mod consts; pub mod control; +mod driver; pub mod event; pub mod indications; -mod ioctl; mod macros; mod opcodes; pub mod responses; @@ -12,86 +12,19 @@ pub mod typedefs; use core::slice; -use embassy_net_driver_channel as ch; - pub use crate::mac::control::{Control, Error as ControlError}; -use crate::mac::event::Events; -use crate::mac::ioctl::IoctlState; +use crate::mac::driver::Driver; pub use crate::mac::runner::Runner; use crate::sub::mac::Mac; -const MTU: usize = 1514; +const MTU: usize = 127; -pub struct State { - ioctl_state: IoctlState, - ch: ch::State, - events: Events, -} +pub async fn new<'a>(mac: Mac) -> (Runner, Control<'a>, Driver<'a>) { + let runner = Runner::new(mac); + let control = Control::new(&runner); + let driver = Driver::new(&runner); -impl State { - pub fn new() -> Self { - Self { - ioctl_state: IoctlState::new(), - ch: ch::State::new(), - events: Events::new(), - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PowerManagementMode { - /// Custom, officially unsupported mode. Use at your own risk. - /// All power-saving features set to their max at only a marginal decrease in power consumption - /// as oppposed to `Aggressive`. - SuperSave, - - /// Aggressive power saving mode. - Aggressive, - - /// The default mode. - PowerSave, - - /// Performance is prefered over power consumption but still some power is conserved as opposed to - /// `None`. - Performance, - - /// Unlike all the other PM modes, this lowers the power consumption at all times at the cost of - /// a much lower throughput. - ThroughputThrottling, - - /// No power management is configured. This consumes the most power. - None, -} - -impl Default for PowerManagementMode { - fn default() -> Self { - Self::PowerSave - } -} - -impl PowerManagementMode { - // TODO -} - -pub type NetDriver<'a> = ch::Device<'a, MTU>; - -pub async fn new<'a>( - state: &'a mut State, - mac_subsystem: Mac, - firmware: &[u8], -) -> (NetDriver<'a>, Control<'a>, Runner<'a>) { - let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); - let state_ch = ch_runner.state_runner(); - - let mut runner = Runner::new(ch_runner, mac_subsystem, &state.ioctl_state, &state.events); - - runner.init(firmware).await; - - ( - device, - Control::new(state_ch, &state.events, &state.ioctl_state), - runner, - ) + (runner, control, driver) } fn slice8_mut(x: &mut [u32]) -> &mut [u8] { diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index fbb7cb74..e97c9c8e 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -1,342 +1,27 @@ use embassy_futures::select::{select3, Either3}; -use embassy_net_driver_channel as ch; -use embassy_sync::pubsub::PubSubBehavior; -use crate::mac::event::Events; -use crate::mac::ioctl::{IoctlState, PendingIoctl}; use crate::mac::MTU; use crate::sub::mac::Mac; -pub struct Runner<'a> { - ch: ch::Runner<'a, MTU>, +pub struct Runner { mac: Mac, - - ioctl_state: &'a IoctlState, - ioctl_id: u16, - sdpcm_seq: u8, - sdpcm_seq_max: u8, - - events: &'a Events, + // TODO: tx_ring + // TODO: rx_buf } -impl<'a> Runner<'a> { - pub(crate) fn new(ch: ch::Runner<'a, MTU>, mac: Mac, ioctl_state: &'a IoctlState, events: &'a Events) -> Self { - Self { - ch, - mac, - ioctl_state, - ioctl_id: 0, - sdpcm_seq: 0, - sdpcm_seq_max: 1, - events, - } +impl Runner { + pub(crate) fn new(mac: Mac) -> Self { + Self { mac } } pub(crate) async fn init(&mut self, firmware: &[u8]) { - self.bus.init().await; - - #[cfg(feature = "firmware-logs")] - self.log_init().await; - debug!("wifi init done"); } pub async fn run(mut self) -> ! { let mut buf = [0; 512]; loop { - #[cfg(feature = "firmware-logs")] - self.log_read().await; - - if self.has_credit() { - let ioctl = self.ioctl_state.wait_pending(); - let tx = self.ch.tx_buf(); - let ev = self.bus.wait_for_event(); - - match select3(ioctl, tx, ev).await { - Either3::First(PendingIoctl { - buf: iobuf, - kind, - cmd, - iface, - }) => { - self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }).await; - self.check_status(&mut buf).await; - } - Either3::Second(packet) => { - trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); - - let mut buf = [0; 512]; - let buf8 = slice8_mut(&mut buf); - - // There MUST be 2 bytes of padding between the SDPCM and BDC headers. - // And ONLY for data packets! - // No idea why, but the firmware will append two zero bytes to the tx'd packets - // otherwise. If the packet is exactly 1514 bytes (the max MTU), this makes it - // be oversized and get dropped. - // WHD adds it here https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/include/whd_sdpcm.h#L90 - // and adds it to the header size her https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/whd_sdpcm.c#L597 - // ¯\_(ツ)_/¯ - const PADDING_SIZE: usize = 2; - let total_len = SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE + packet.len(); - - let seq = self.sdpcm_seq; - self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); - - let sdpcm_header = SdpcmHeader { - len: total_len as u16, // TODO does this len need to be rounded up to u32? - len_inv: !total_len as u16, - sequence: seq, - channel_and_flags: CHANNEL_TYPE_DATA, - next_length: 0, - header_length: (SdpcmHeader::SIZE + PADDING_SIZE) as _, - wireless_flow_control: 0, - bus_data_credit: 0, - reserved: [0, 0], - }; - - let bdc_header = BdcHeader { - flags: BDC_VERSION << BDC_VERSION_SHIFT, - priority: 0, - flags2: 0, - data_offset: 0, - }; - trace!("tx {:?}", sdpcm_header); - trace!(" {:?}", bdc_header); - - buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); - buf8[SdpcmHeader::SIZE + PADDING_SIZE..][..BdcHeader::SIZE] - .copy_from_slice(&bdc_header.to_bytes()); - buf8[SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE..][..packet.len()] - .copy_from_slice(packet); - - let total_len = (total_len + 3) & !3; // round up to 4byte - - trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); - - self.bus.wlan_write(&buf[..(total_len / 4)]).await; - self.ch.tx_done(); - self.check_status(&mut buf).await; - } - Either3::Third(()) => { - self.handle_irq(&mut buf).await; - } - } - } else { - warn!("TX stalled"); - self.bus.wait_for_event().await; - self.handle_irq(&mut buf).await; - } + // TODO } } - - /// Wait for IRQ on F2 packet available - async fn handle_irq(&mut self, buf: &mut [u32; 512]) { - // Receive stuff - let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; - trace!("irq{}", FormatInterrupt(irq)); - - if irq & IRQ_F2_PACKET_AVAILABLE != 0 { - self.check_status(buf).await; - } - - if irq & IRQ_DATA_UNAVAILABLE != 0 { - // TODO what should we do here? - warn!("IRQ DATA_UNAVAILABLE, clearing..."); - self.bus.write16(FUNC_BUS, REG_BUS_INTERRUPT, 1).await; - } - } - - /// Handle F2 events while status register is set - async fn check_status(&mut self, buf: &mut [u32; 512]) { - loop { - let status = self.bus.status(); - trace!("check status{}", FormatStatus(status)); - - if status & STATUS_F2_PKT_AVAILABLE != 0 { - let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; - self.bus.wlan_read(buf, len).await; - trace!("rx {:02x}", Bytes(&slice8_mut(buf)[..(len as usize).min(48)])); - self.rx(&mut slice8_mut(buf)[..len as usize]); - } else { - break; - } - } - } - - fn rx(&mut self, packet: &mut [u8]) { - let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { - return; - }; - - self.update_credit(&sdpcm_header); - - let channel = sdpcm_header.channel_and_flags & 0x0f; - - match channel { - CHANNEL_TYPE_CONTROL => { - let Some((cdc_header, response)) = CdcHeader::parse(payload) else { - return; - }; - trace!(" {:?}", cdc_header); - - if cdc_header.id == self.ioctl_id { - if cdc_header.status != 0 { - // TODO: propagate error instead - panic!("IOCTL error {}", cdc_header.status as i32); - } - - self.ioctl_state.ioctl_done(response); - } - } - CHANNEL_TYPE_EVENT => { - let Some((_, bdc_packet)) = BdcHeader::parse(payload) else { - warn!("BDC event, incomplete header"); - return; - }; - - let Some((event_packet, evt_data)) = EventPacket::parse(bdc_packet) else { - warn!("BDC event, incomplete data"); - return; - }; - - const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h - if event_packet.eth.ether_type != ETH_P_LINK_CTL { - warn!( - "unexpected ethernet type 0x{:04x}, expected Broadcom ether type 0x{:04x}", - event_packet.eth.ether_type, ETH_P_LINK_CTL - ); - return; - } - const BROADCOM_OUI: &[u8] = &[0x00, 0x10, 0x18]; - if event_packet.hdr.oui != BROADCOM_OUI { - warn!( - "unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}", - Bytes(&event_packet.hdr.oui), - Bytes(BROADCOM_OUI) - ); - return; - } - const BCMILCP_SUBTYPE_VENDOR_LONG: u16 = 32769; - if event_packet.hdr.subtype != BCMILCP_SUBTYPE_VENDOR_LONG { - warn!("unexpected subtype {}", event_packet.hdr.subtype); - return; - } - - const BCMILCP_BCM_SUBTYPE_EVENT: u16 = 1; - if event_packet.hdr.user_subtype != BCMILCP_BCM_SUBTYPE_EVENT { - warn!("unexpected user_subtype {}", event_packet.hdr.subtype); - return; - } - - let evt_type = events::Event::from(event_packet.msg.event_type as u8); - debug!( - "=== EVENT {:?}: {:?} {:02x}", - evt_type, - event_packet.msg, - Bytes(evt_data) - ); - - if self.events.mask.is_enabled(evt_type) { - let status = event_packet.msg.status; - let event_payload = match evt_type { - Event::ESCAN_RESULT if status == EStatus::PARTIAL => { - let Some((_, bss_info)) = ScanResults::parse(evt_data) else { - return; - }; - let Some(bss_info) = BssInfo::parse(bss_info) else { - return; - }; - events::Payload::BssInfo(*bss_info) - } - Event::ESCAN_RESULT => events::Payload::None, - _ => events::Payload::None, - }; - - // this intentionally uses the non-blocking publish immediate - // publish() is a deadlock risk in the current design as awaiting here prevents ioctls - // The `Runner` always yields when accessing the device, so consumers always have a chance to receive the event - // (if they are actively awaiting the queue) - self.events.queue.publish_immediate(events::Message::new( - Status { - event_type: evt_type, - status, - }, - event_payload, - )); - } - } - CHANNEL_TYPE_DATA => { - let Some((_, packet)) = BdcHeader::parse(payload) else { - return; - }; - trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); - - match self.ch.try_rx_buf() { - Some(buf) => { - buf[..packet.len()].copy_from_slice(packet); - self.ch.rx_done(packet.len()) - } - None => warn!("failed to push rxd packet to the channel."), - } - } - _ => {} - } - } - - fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) { - if sdpcm_header.channel_and_flags & 0xf < 3 { - let mut sdpcm_seq_max = sdpcm_header.bus_data_credit; - if sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) > 0x40 { - sdpcm_seq_max = self.sdpcm_seq + 2; - } - self.sdpcm_seq_max = sdpcm_seq_max; - } - } - - fn has_credit(&self) -> bool { - self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 - } - - async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { - let mut buf = [0; 512]; - let buf8 = slice8_mut(&mut buf); - - let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); - - let sdpcm_seq = self.sdpcm_seq; - self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); - self.ioctl_id = self.ioctl_id.wrapping_add(1); - - let sdpcm_header = SdpcmHeader { - len: total_len as u16, // TODO does this len need to be rounded up to u32? - len_inv: !total_len as u16, - sequence: sdpcm_seq, - channel_and_flags: CHANNEL_TYPE_CONTROL, - next_length: 0, - header_length: SdpcmHeader::SIZE as _, - wireless_flow_control: 0, - bus_data_credit: 0, - reserved: [0, 0], - }; - - let cdc_header = CdcHeader { - cmd: cmd, - len: data.len() as _, - flags: kind as u16 | (iface as u16) << 12, - id: self.ioctl_id, - status: 0, - }; - trace!("tx {:?}", sdpcm_header); - trace!(" {:?}", cdc_header); - - buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); - buf8[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); - buf8[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); - - let total_len = (total_len + 3) & !3; // round up to 4byte - - trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); - - self.bus.wlan_write(&buf[..total_len / 4]).await; - } } From d040871f7a078db94846305463c30a461f821d7f Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 17 Jul 2023 20:14:06 -0500 Subject: [PATCH 035/298] wpan: fix comp errors and impl. some of runner --- embassy-stm32-wpan/src/mac/driver.rs | 8 +- embassy-stm32-wpan/src/mac/mod.rs | 9 +- embassy-stm32-wpan/src/mac/runner.rs | 75 ++++++++-- examples/stm32wb/src/bin/mac_ffd_net.rs | 180 ++++++++++++++++++++++++ 4 files changed, 255 insertions(+), 17 deletions(-) create mode 100644 examples/stm32wb/src/bin/mac_ffd_net.rs diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 00072749..118f6908 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -25,6 +25,8 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { type TxToken<'a> = TxToken where Self: 'a; fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + self.runner.rx_waker.register(cx.waker()); + // WAKER.register(cx.waker()); // if self.rx.available().is_some() && self.tx.available().is_some() { // Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) @@ -36,6 +38,8 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { } fn transmit(&mut self, cx: &mut Context) -> Option> { + self.runner.tx_waker.register(cx.waker()); + // WAKER.register(cx.waker()); // / if self.tx.available().is_some() { // / Some(TxToken { tx: &mut self.tx }) @@ -84,7 +88,7 @@ impl embassy_net_driver::RxToken for RxToken { // NOTE(unwrap): we checked the queue wasn't full when creating the token. // let pkt = unwrap!(self.rx.available()); - let pkt = &[]; + let pkt = &mut []; let r = f(&mut pkt[0..]); // self.rx.pop_packet(); r @@ -102,7 +106,7 @@ impl embassy_net_driver::TxToken for TxToken { { // NOTE(unwrap): we checked the queue wasn't full when creating the token. // let pkt = unwrap!(self.tx.available()); - let pkt = &[]; + let pkt = &mut []; let r = f(&mut pkt[..len]); // self.tx.transmit(len); r diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index e024aeae..2f9d1c81 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -15,16 +15,11 @@ use core::slice; pub use crate::mac::control::{Control, Error as ControlError}; use crate::mac::driver::Driver; pub use crate::mac::runner::Runner; -use crate::sub::mac::Mac; const MTU: usize = 127; -pub async fn new<'a>(mac: Mac) -> (Runner, Control<'a>, Driver<'a>) { - let runner = Runner::new(mac); - let control = Control::new(&runner); - let driver = Driver::new(&runner); - - (runner, control, driver) +pub async fn new<'a>(runner: &'a Runner) -> (Control<'a>, Driver<'a>) { + (Control::new(runner), Driver::new(runner)) } fn slice8_mut(x: &mut [u32]) -> &mut [u8] { diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index e97c9c8e..d545d6c9 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -1,27 +1,86 @@ use embassy_futures::select::{select3, Either3}; +use embassy_sync::waitqueue::AtomicWaker; +use crate::mac::event::{Event, MacEvent}; use crate::mac::MTU; use crate::sub::mac::Mac; +pub(crate) struct TxRing { + // stores n packets of up to mtu size + ring: [[u8; MTU]; 5], + pending: bool, + // start: u8, + // end: u8, +} + +impl TxRing { + pub(crate) fn new() -> Self { + Self { + ring: [[0; MTU]; 5], + pending: false, + } + } + + // wait for a free packet to become available + pub fn is_packet_free(&self) -> bool { + !self.pending + } + + // get the next available free packet + pub fn get_free_packet<'a>(&'a mut self) -> &'a mut [u8] { + self.pending = true; + + &mut self.ring[0] + } + + pub fn get_packet_to_transmit<'a>(&'a mut self) -> Option<&'a [u8]> { + if self.pending { + self.pending = false; + + Some(&self.ring[0]) + } else { + None + } + } +} + pub struct Runner { - mac: Mac, - // TODO: tx_ring - // TODO: rx_buf + mac_subsystem: Mac, + pub(crate) rx_ring: Option, + pub(crate) tx_ring: TxRing, + pub(crate) rx_waker: AtomicWaker, + pub(crate) tx_waker: AtomicWaker, } impl Runner { - pub(crate) fn new(mac: Mac) -> Self { - Self { mac } + pub fn new(mac: Mac) -> Self { + Self { + mac_subsystem: mac, + rx_ring: None, + tx_ring: TxRing::new(), + rx_waker: AtomicWaker::new(), + tx_waker: AtomicWaker::new(), + } } pub(crate) async fn init(&mut self, firmware: &[u8]) { debug!("wifi init done"); } - pub async fn run(mut self) -> ! { - let mut buf = [0; 512]; + pub async fn run(&self) -> ! { loop { - // TODO + let event = self.mac_subsystem.read().await; + if let Ok(evt) = event.mac_event() { + match evt { + MacEvent::McpsDataInd(data_ind) => { + // TODO: store mac_event in rx_ring + self.rx_waker.wake(); + } + _ => {} + } + } + + // TODO: select tx event } } } diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs new file mode 100644 index 00000000..b1cf051b --- /dev/null +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -0,0 +1,180 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::mac::commands::{AssociateResponse, ResetRequest, SetRequest, StartRequest}; +use embassy_stm32_wpan::mac::typedefs::{MacChannel, MacStatus, PanId, PibId, SecurityLevel}; +use embassy_stm32_wpan::mac::{self, Runner}; +use embassy_stm32_wpan::sub::mm; +use embassy_stm32_wpan::TlMbox; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; +}); + +#[embassy_executor::task] +async fn run_mm_queue(memory_manager: mm::MemoryManager) { + memory_manager.run_queue().await; +} + +#[embassy_executor::task] +async fn run_mac(runner: &'static Runner) { + runner.run().await; +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Open STM32CubeProgrammer + - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the + stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Select "Start Wireless Stack". + - Disconnect from the device. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let mbox = TlMbox::init(p.IPCC, Irqs, config); + + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + + core::mem::drop(sys_event); + + let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + info!("initialized mac: {}", result); + + info!("resetting"); + mbox.mac_subsystem + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) + .await + .unwrap(); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } + + info!("setting extended address"); + let extended_address: u64 = 0xACDE480000000001; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &extended_address as *const _ as *const u8, + pib_attribute: PibId::ExtendedAddress, + }) + .await + .unwrap(); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } + + info!("setting short address"); + let short_address: u16 = 0x1122; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &short_address as *const _ as *const u8, + pib_attribute: PibId::ShortAddress, + }) + .await + .unwrap(); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } + + info!("setting association permit"); + let association_permit: bool = true; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &association_permit as *const _ as *const u8, + pib_attribute: PibId::AssociationPermit, + }) + .await + .unwrap(); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } + + info!("setting TX power"); + let transmit_power: i8 = 2; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &transmit_power as *const _ as *const u8, + pib_attribute: PibId::TransmitPower, + }) + .await + .unwrap(); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } + + info!("starting FFD device"); + mbox.mac_subsystem + .send_command(&StartRequest { + pan_id: PanId([0x1A, 0xAA]), + channel_number: MacChannel::Channel16, + beacon_order: 0x0F, + superframe_order: 0x0F, + pan_coordinator: true, + battery_life_extension: false, + ..Default::default() + }) + .await + .unwrap(); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } + + info!("setting RX on when idle"); + let rx_on_while_idle: bool = true; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, + pib_attribute: PibId::RxOnWhenIdle, + }) + .await + .unwrap(); + { + let evt = mbox.mac_subsystem.read().await; + defmt::info!("{:#x}", evt.mac_event()); + } + + let runner = make_static!(Runner::new(mbox.mac_subsystem)); + + spawner.spawn(run_mac(runner)).unwrap(); + + let (driver, control) = mac::new(runner).await; +} From f581831b86f1f3e46a1cc7a8c4d1a2baf3379e6a Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Tue, 18 Jul 2023 10:39:29 +0200 Subject: [PATCH 036/298] Make dual-stack work in embassy-net --- embassy-net/src/lib.rs | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 0d0a986f..f43e3797 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -531,11 +531,14 @@ impl Inner { debug!(" IP address: {}", config.address); s.iface.update_ip_addrs(|addrs| { - if addrs.is_empty() { - addrs.push(IpCidr::Ipv4(config.address)).unwrap(); - } else { - addrs[0] = IpCidr::Ipv4(config.address); + if let Some((index, _)) = addrs + .iter() + .enumerate() + .find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_))) + { + addrs.remove(index); } + addrs.push(IpCidr::Ipv4(config.address)).unwrap(); }); #[cfg(feature = "medium-ethernet")] @@ -570,11 +573,14 @@ impl Inner { debug!(" IP address: {}", config.address); s.iface.update_ip_addrs(|addrs| { - if addrs.is_empty() { - addrs.push(IpCidr::Ipv6(config.address)).unwrap(); - } else { - addrs[0] = IpCidr::Ipv6(config.address); + if let Some((index, _)) = addrs + .iter() + .enumerate() + .find(|(_, &addr)| matches!(addr, IpCidr::Ipv6(_))) + { + addrs.remove(index); } + addrs.push(IpCidr::Ipv6(config.address)).unwrap(); }); #[cfg(feature = "medium-ethernet")] @@ -643,12 +649,19 @@ impl Inner { } #[allow(unused)] // used only with dhcp - fn unapply_config(&mut self, s: &mut SocketStack) { + fn unapply_config_v4(&mut self, s: &mut SocketStack) { #[cfg(feature = "medium-ethernet")] let medium = self.device.capabilities().medium; - debug!("Lost IP configuration"); - s.iface.update_ip_addrs(|ip_addrs| ip_addrs.clear()); + s.iface.update_ip_addrs(|ip_addrs| { + if let Some((index, _)) = ip_addrs + .iter() + .enumerate() + .find(|(index, &addr)| matches!(addr, IpCidr::Ipv4(_))) + { + ip_addrs.remove(index); + } + }); #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { #[cfg(feature = "proto-ipv4")] @@ -695,7 +708,7 @@ impl Inner { if self.link_up { match socket.poll() { None => {} - Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), + Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s), Some(dhcpv4::Event::Configured(config)) => { let config = StaticConfigV4 { address: config.address, @@ -707,7 +720,7 @@ impl Inner { } } else if old_link_up { socket.reset(); - self.unapply_config(s); + self.unapply_config_v4(s); } } //if old_link_up || self.link_up { From 6bf4717b0a17cf4ca3439f0b2726b7fb6f5f3b13 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Tue, 18 Jul 2023 10:57:05 +0200 Subject: [PATCH 037/298] cfg-gate `unapply_config_v4` --- embassy-net/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index f43e3797..ae595d0d 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -648,16 +648,17 @@ impl Inner { socket.set_retry_config(config.retry_config); } - #[allow(unused)] // used only with dhcp + #[cfg(feature = "dhcpv4")] fn unapply_config_v4(&mut self, s: &mut SocketStack) { #[cfg(feature = "medium-ethernet")] let medium = self.device.capabilities().medium; debug!("Lost IP configuration"); s.iface.update_ip_addrs(|ip_addrs| { + #[cfg(feature = "proto-ipv4")] if let Some((index, _)) = ip_addrs .iter() .enumerate() - .find(|(index, &addr)| matches!(addr, IpCidr::Ipv4(_))) + .find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_))) { ip_addrs.remove(index); } From fc901f98564134368db3a92fb0644c5afe292f10 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 18 Jul 2023 13:54:41 +0200 Subject: [PATCH 038/298] ci: add check for no CRLF line endings. --- .github/ci/crlf.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100755 .github/ci/crlf.sh diff --git a/.github/ci/crlf.sh b/.github/ci/crlf.sh new file mode 100755 index 00000000..45751040 --- /dev/null +++ b/.github/ci/crlf.sh @@ -0,0 +1,17 @@ +#!/bin/bash +## on push branch~=gh-readonly-queue/main/.* +## on pull_request + +set -euo pipefail + +FILES_WITH_CRLF=$(find ! -path "./.git/*" -not -type d | xargs file -N | (grep " CRLF " || true)) + +if [ -z "$FILES_WITH_CRLF" ]; then + echo -e "No files with CRLF endings found." + exit 0 +else + NR_FILES=$(echo "$FILES_WITH_CRLF" | wc -l) + echo -e "ERROR: Found ${NR_FILES} files with CRLF endings." + echo "$FILES_WITH_CRLF" + exit "$NR_FILES" +fi \ No newline at end of file From 48957dce8767fb573527626b4a4a060fa3f1b98a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 18 Jul 2023 14:14:25 +0200 Subject: [PATCH 039/298] Add gitattributes to control crlf conversion. --- .gitattributes | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..4db9edae --- /dev/null +++ b/.gitattributes @@ -0,0 +1,41 @@ +* text=auto + +*.adoc text +*.html text +*.in text +*.json text +*.md text +*.proto text +*.py text +*.rs text +*.service text +*.sh text +*.toml text +*.txt text +*.x text +*.yml text + +*.raw binary +*.bin binary +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.mov binary +*.mp4 binary +*.mp3 binary +*.flv binary +*.fla binary +*.swf binary +*.gz binary +*.zip binary +*.7z binary +*.ttf binary +*.eot binary +*.woff binary +*.pyc binary +*.pdf binary +*.ez binary +*.bz2 binary +*.swp binary \ No newline at end of file From 10f59667879bc476004c78bc0d3d6a0c4f560e55 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 18 Jul 2023 14:17:44 +0200 Subject: [PATCH 040/298] Convert files to LF endings. --- embassy-nrf/src/pdm.rs | 588 +++++++++++++++--------------- embassy-stm32/src/qspi/mod.rs | 664 +++++++++++++++++----------------- 2 files changed, 626 insertions(+), 626 deletions(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 0e30f700..efa1fbcc 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,294 +1,294 @@ -//! Pulse Density Modulation (PDM) mirophone driver. - -#![macro_use] - -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 futures::future::poll_fn; - -use crate::chip::EASY_DMA_SIZE; -use crate::gpio::sealed::Pin; -use crate::gpio::{AnyPin, Pin as GpioPin}; -use crate::interrupt::typelevel::Interrupt; -use crate::{interrupt, Peripheral}; - -/// Interrupt handler. -pub struct InterruptHandler { - _phantom: PhantomData, -} - -impl interrupt::typelevel::Handler for InterruptHandler { - unsafe fn on_interrupt() { - T::regs().intenclr.write(|w| w.end().clear()); - T::state().waker.wake(); - } -} - -/// PDM microphone interface -pub struct Pdm<'d, T: Instance> { - _peri: PeripheralRef<'d, T>, -} - -/// PDM error. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -pub enum Error { - /// Buffer is too long. - BufferTooLong, - /// Buffer is empty - BufferZeroLength, - /// PDM is not running - NotRunning, -} - -static DUMMY_BUFFER: [i16; 1] = [0; 1]; - -impl<'d, T: Instance> Pdm<'d, T> { - /// Create PDM driver - pub fn new( - pdm: impl Peripheral

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

+ 'd, - din: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(pdm, clk, din); - Self::new_inner(pdm, clk.map_into(), din.map_into(), config) - } - - fn new_inner( - pdm: PeripheralRef<'d, T>, - clk: PeripheralRef<'d, AnyPin>, - din: PeripheralRef<'d, AnyPin>, - config: Config, - ) -> Self { - into_ref!(pdm); - - let r = T::regs(); - - // setup gpio pins - din.conf().write(|w| w.input().set_bit()); - r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) }); - clk.set_low(); - clk.conf().write(|w| w.dir().output()); - r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) }); - - // configure - // use default for - // - gain right - // - gain left - // - clk - // - ratio - r.mode.write(|w| { - w.edge().bit(config.edge == Edge::LeftRising); - w.operation().bit(config.operation_mode == OperationMode::Mono); - w - }); - r.gainl.write(|w| w.gainl().default_gain()); - r.gainr.write(|w| w.gainr().default_gain()); - - // IRQ - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - r.enable.write(|w| w.enable().set_bit()); - - Self { _peri: pdm } - } - - /// Start sampling microphon data into a dummy buffer - /// Usefull to start the microphon and keep it active between recording samples - pub async fn start(&mut self) { - let r = T::regs(); - - // start dummy sampling because microphon needs some setup time - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); - r.sample - .maxcnt - .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); - - r.tasks_start.write(|w| unsafe { w.bits(1) }); - } - - /// Stop sampling microphon data inta a dummy buffer - pub async fn stop(&mut self) { - let r = T::regs(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - r.events_started.reset(); - } - - /// Sample data into the given buffer. - pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { - if buffer.len() == 0 { - return Err(Error::BufferZeroLength); - } - if buffer.len() > EASY_DMA_SIZE { - return Err(Error::BufferTooLong); - } - - let r = T::regs(); - - if r.events_started.read().bits() == 0 { - return Err(Error::NotRunning); - } - - let drop = OnDrop::new(move || { - r.intenclr.write(|w| w.end().clear()); - r.events_stopped.reset(); - - // reset to dummy buffer - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); - r.sample - .maxcnt - .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); - - while r.events_stopped.read().bits() == 0 {} - }); - - // setup user buffer - let ptr = buffer.as_ptr(); - let len = buffer.len(); - r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) }); - r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) }); - - // wait till the current sample is finished and the user buffer sample is started - Self::wait_for_sample().await; - - // reset the buffer back to the dummy buffer - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); - r.sample - .maxcnt - .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); - - // wait till the user buffer is sampled - Self::wait_for_sample().await; - - drop.defuse(); - - Ok(()) - } - - async fn wait_for_sample() { - let r = T::regs(); - - r.events_end.reset(); - r.intenset.write(|w| w.end().set()); - - compiler_fence(Ordering::SeqCst); - - poll_fn(|cx| { - T::state().waker.register(cx.waker()); - if r.events_end.read().bits() != 0 { - return Poll::Ready(()); - } - Poll::Pending - }) - .await; - - compiler_fence(Ordering::SeqCst); - } -} - -/// PDM microphone driver Config -pub struct Config { - /// Use stero or mono operation - pub operation_mode: OperationMode, - /// On which edge the left channel should be samples - pub edge: Edge, -} - -impl Default for Config { - fn default() -> Self { - Self { - operation_mode: OperationMode::Mono, - edge: Edge::LeftFalling, - } - } -} - -/// PDM operation mode. -#[derive(PartialEq)] -pub enum OperationMode { - /// Mono (1 channel) - Mono, - /// Stereo (2 channels) - Stereo, -} - -/// PDM edge polarity -#[derive(PartialEq)] -pub enum Edge { - /// Left edge is rising - LeftRising, - /// Left edge is falling - LeftFalling, -} - -impl<'d, T: Instance> Drop for Pdm<'d, T> { - fn drop(&mut self) { - let r = T::regs(); - - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - - r.enable.write(|w| w.enable().disabled()); - - r.psel.din.reset(); - r.psel.clk.reset(); - } -} - -pub(crate) mod sealed { - use embassy_sync::waitqueue::AtomicWaker; - - /// Peripheral static state - pub struct State { - pub waker: AtomicWaker, - } - - impl State { - pub const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } - } - } - - pub trait Instance { - fn regs() -> &'static crate::pac::pdm::RegisterBlock; - fn state() -> &'static State; - } -} - -/// PDM peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { - /// Interrupt for this peripheral. - type Interrupt: interrupt::typelevel::Interrupt; -} - -macro_rules! impl_pdm { - ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::pdm::sealed::Instance for peripherals::$type { - fn regs() -> &'static crate::pac::pdm::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } - } - fn state() -> &'static crate::pdm::sealed::State { - static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new(); - &STATE - } - } - impl crate::pdm::Instance for peripherals::$type { - type Interrupt = crate::interrupt::typelevel::$irq; - } - }; -} +//! Pulse Density Modulation (PDM) mirophone driver. + +#![macro_use] + +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 futures::future::poll_fn; + +use crate::chip::EASY_DMA_SIZE; +use crate::gpio::sealed::Pin; +use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::interrupt::typelevel::Interrupt; +use crate::{interrupt, Peripheral}; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + T::regs().intenclr.write(|w| w.end().clear()); + T::state().waker.wake(); + } +} + +/// PDM microphone interface +pub struct Pdm<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, +} + +/// PDM error. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// Buffer is too long. + BufferTooLong, + /// Buffer is empty + BufferZeroLength, + /// PDM is not running + NotRunning, +} + +static DUMMY_BUFFER: [i16; 1] = [0; 1]; + +impl<'d, T: Instance> Pdm<'d, T> { + /// Create PDM driver + pub fn new( + pdm: impl Peripheral

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

+ 'd, + din: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(pdm, clk, din); + Self::new_inner(pdm, clk.map_into(), din.map_into(), config) + } + + fn new_inner( + pdm: PeripheralRef<'d, T>, + clk: PeripheralRef<'d, AnyPin>, + din: PeripheralRef<'d, AnyPin>, + config: Config, + ) -> Self { + into_ref!(pdm); + + let r = T::regs(); + + // setup gpio pins + din.conf().write(|w| w.input().set_bit()); + r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) }); + clk.set_low(); + clk.conf().write(|w| w.dir().output()); + r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) }); + + // configure + // use default for + // - gain right + // - gain left + // - clk + // - ratio + r.mode.write(|w| { + w.edge().bit(config.edge == Edge::LeftRising); + w.operation().bit(config.operation_mode == OperationMode::Mono); + w + }); + r.gainl.write(|w| w.gainl().default_gain()); + r.gainr.write(|w| w.gainr().default_gain()); + + // IRQ + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + r.enable.write(|w| w.enable().set_bit()); + + Self { _peri: pdm } + } + + /// Start sampling microphon data into a dummy buffer + /// Usefull to start the microphon and keep it active between recording samples + pub async fn start(&mut self) { + let r = T::regs(); + + // start dummy sampling because microphon needs some setup time + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + r.tasks_start.write(|w| unsafe { w.bits(1) }); + } + + /// Stop sampling microphon data inta a dummy buffer + pub async fn stop(&mut self) { + let r = T::regs(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.events_started.reset(); + } + + /// Sample data into the given buffer. + pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { + if buffer.len() == 0 { + return Err(Error::BufferZeroLength); + } + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + + let r = T::regs(); + + if r.events_started.read().bits() == 0 { + return Err(Error::NotRunning); + } + + let drop = OnDrop::new(move || { + r.intenclr.write(|w| w.end().clear()); + r.events_stopped.reset(); + + // reset to dummy buffer + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + while r.events_stopped.read().bits() == 0 {} + }); + + // setup user buffer + let ptr = buffer.as_ptr(); + let len = buffer.len(); + r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) }); + r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) }); + + // wait till the current sample is finished and the user buffer sample is started + Self::wait_for_sample().await; + + // reset the buffer back to the dummy buffer + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + // wait till the user buffer is sampled + Self::wait_for_sample().await; + + drop.defuse(); + + Ok(()) + } + + async fn wait_for_sample() { + let r = T::regs(); + + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); + + compiler_fence(Ordering::SeqCst); + + poll_fn(|cx| { + T::state().waker.register(cx.waker()); + if r.events_end.read().bits() != 0 { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + + compiler_fence(Ordering::SeqCst); + } +} + +/// PDM microphone driver Config +pub struct Config { + /// Use stero or mono operation + pub operation_mode: OperationMode, + /// On which edge the left channel should be samples + pub edge: Edge, +} + +impl Default for Config { + fn default() -> Self { + Self { + operation_mode: OperationMode::Mono, + edge: Edge::LeftFalling, + } + } +} + +/// PDM operation mode. +#[derive(PartialEq)] +pub enum OperationMode { + /// Mono (1 channel) + Mono, + /// Stereo (2 channels) + Stereo, +} + +/// PDM edge polarity +#[derive(PartialEq)] +pub enum Edge { + /// Left edge is rising + LeftRising, + /// Left edge is falling + LeftFalling, +} + +impl<'d, T: Instance> Drop for Pdm<'d, T> { + fn drop(&mut self) { + let r = T::regs(); + + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + + r.enable.write(|w| w.enable().disabled()); + + r.psel.din.reset(); + r.psel.clk.reset(); + } +} + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + /// Peripheral static state + pub struct State { + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::pdm::RegisterBlock; + fn state() -> &'static State; + } +} + +/// PDM peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: interrupt::typelevel::Interrupt; +} + +macro_rules! impl_pdm { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::pdm::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::pdm::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::pdm::sealed::State { + static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new(); + &STATE + } + } + impl crate::pdm::Instance for peripherals::$type { + type Interrupt = crate::interrupt::typelevel::$irq; + } + }; +} diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index e9db934b..31b67608 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -1,332 +1,332 @@ -#![macro_use] - -pub mod enums; - -use embassy_hal_common::{into_ref, PeripheralRef}; -use enums::*; - -use crate::dma::Transfer; -use crate::gpio::sealed::AFType; -use crate::gpio::AnyPin; -use crate::pac::quadspi::Quadspi as Regs; -use crate::rcc::RccPeripheral; -use crate::{peripherals, Peripheral}; - -pub struct TransferConfig { - /// Instraction width (IMODE) - pub iwidth: QspiWidth, - /// Address width (ADMODE) - pub awidth: QspiWidth, - /// Data width (DMODE) - pub dwidth: QspiWidth, - /// Instruction Id - pub instruction: u8, - /// Flash memory address - pub address: Option, - /// Number of dummy cycles (DCYC) - pub dummy: DummyCycles, - /// Length of data - pub data_len: Option, -} - -impl Default for TransferConfig { - fn default() -> Self { - Self { - iwidth: QspiWidth::NONE, - awidth: QspiWidth::NONE, - dwidth: QspiWidth::NONE, - instruction: 0, - address: None, - dummy: DummyCycles::_0, - data_len: None, - } - } -} - -pub struct Config { - /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. - /// If you need other value the whose predefined use `Other` variant. - pub memory_size: MemorySize, - /// Address size (8/16/24/32-bit) - pub address_size: AddressSize, - /// Scalar factor for generating CLK [0-255] - pub prescaler: u8, - /// Number of bytes to trigger FIFO threshold flag. - pub fifo_threshold: FIFOThresholdLevel, - /// Minimum number of cycles that chip select must be high between issued commands - pub cs_high_time: ChipSelectHightTime, -} - -impl Default for Config { - fn default() -> Self { - Self { - memory_size: MemorySize::Other(0), - address_size: AddressSize::_24bit, - prescaler: 128, - fifo_threshold: FIFOThresholdLevel::_17Bytes, - cs_high_time: ChipSelectHightTime::_5Cycle, - } - } -} - -#[allow(dead_code)] -pub struct Qspi<'d, T: Instance, Dma> { - _peri: PeripheralRef<'d, T>, - sck: Option>, - d0: Option>, - d1: Option>, - d2: Option>, - d3: Option>, - nss: Option>, - dma: PeripheralRef<'d, Dma>, - config: Config, -} - -impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { - pub fn new( - peri: impl Peripheral

+ 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - sck: impl Peripheral

> + 'd, - nss: impl Peripheral

> + 'd, - dma: impl Peripheral

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

+ 'd, - d0: Option>, - d1: Option>, - d2: Option>, - d3: Option>, - sck: Option>, - nss: Option>, - dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, dma); - - T::enable(); - T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into())); - - while T::REGS.sr().read().busy() {} - - T::REGS.cr().write(|w| { - w.set_prescaler(config.prescaler); - w.set_en(true); - }); - T::REGS.dcr().write(|w| { - w.set_fsize(config.memory_size.into()); - w.set_csht(config.cs_high_time.into()); - w.set_ckmode(false); - }); - - Self { - _peri: peri, - sck, - d0, - d1, - d2, - d3, - nss, - dma, - config, - } - } - - pub fn command(&mut self, transaction: TransferConfig) { - T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - while !T::REGS.sr().read().tcf() {} - T::REGS.fcr().modify(|v| v.set_ctcf(true)); - } - - pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) { - T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - if let Some(len) = transaction.data_len { - let current_ar = T::REGS.ar().read().address(); - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectRead.into()); - }); - T::REGS.ar().write(|v| { - v.set_address(current_ar); - }); - - for idx in 0..len { - while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} - buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; - } - } - - while !T::REGS.sr().read().tcf() {} - T::REGS.fcr().modify(|v| v.set_ctcf(true)); - } - - pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) { - T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - if let Some(len) = transaction.data_len { - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectWrite.into()); - }); - - for idx in 0..len { - while !T::REGS.sr().read().ftf() {} - unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; - } - } - - while !T::REGS.sr().read().tcf() {} - T::REGS.fcr().modify(|v| v.set_ctcf(true)); - } - - pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) - where - Dma: QuadDma, - { - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectRead.into()); - }); - let current_ar = T::REGS.ar().read().address(); - T::REGS.ar().write(|v| { - v.set_address(current_ar); - }); - - let request = self.dma.request(); - let transfer = unsafe { - Transfer::new_read( - &mut self.dma, - request, - T::REGS.dr().as_ptr() as *mut u8, - buf, - Default::default(), - ) - }; - - T::REGS.cr().modify(|v| v.set_dmaen(true)); - - transfer.blocking_wait(); - } - - pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) - where - Dma: QuadDma, - { - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectWrite.into()); - }); - - let request = self.dma.request(); - let transfer = unsafe { - Transfer::new_write( - &mut self.dma, - request, - buf, - T::REGS.dr().as_ptr() as *mut u8, - Default::default(), - ) - }; - - T::REGS.cr().modify(|v| v.set_dmaen(true)); - - transfer.blocking_wait(); - } - - fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) { - T::REGS.fcr().modify(|v| { - v.set_csmf(true); - v.set_ctcf(true); - v.set_ctef(true); - v.set_ctof(true); - }); - - while T::REGS.sr().read().busy() {} - - if let Some(len) = transaction.data_len { - T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1)); - } - - T::REGS.ccr().write(|v| { - v.set_fmode(fmode.into()); - v.set_imode(transaction.iwidth.into()); - v.set_instruction(transaction.instruction); - v.set_admode(transaction.awidth.into()); - v.set_adsize(self.config.address_size.into()); - v.set_dmode(transaction.dwidth.into()); - v.set_abmode(QspiWidth::NONE.into()); - v.set_dcyc(transaction.dummy.into()); - }); - - if let Some(addr) = transaction.address { - T::REGS.ar().write(|v| { - v.set_address(addr); - }); - } - } -} - -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - const REGS: Regs; - } -} - -pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} - -pin_trait!(SckPin, Instance); -pin_trait!(D0Pin, Instance); -pin_trait!(D1Pin, Instance); -pin_trait!(D2Pin, Instance); -pin_trait!(D3Pin, Instance); -pin_trait!(NSSPin, Instance); - -dma_trait!(QuadDma, Instance); - -foreach_peripheral!( - (quadspi, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { - const REGS: Regs = crate::pac::$inst; - } - - impl Instance for peripherals::$inst {} - }; -); +#![macro_use] + +pub mod enums; + +use embassy_hal_common::{into_ref, PeripheralRef}; +use enums::*; + +use crate::dma::Transfer; +use crate::gpio::sealed::AFType; +use crate::gpio::AnyPin; +use crate::pac::quadspi::Quadspi as Regs; +use crate::rcc::RccPeripheral; +use crate::{peripherals, Peripheral}; + +pub struct TransferConfig { + /// Instraction width (IMODE) + pub iwidth: QspiWidth, + /// Address width (ADMODE) + pub awidth: QspiWidth, + /// Data width (DMODE) + pub dwidth: QspiWidth, + /// Instruction Id + pub instruction: u8, + /// Flash memory address + pub address: Option, + /// Number of dummy cycles (DCYC) + pub dummy: DummyCycles, + /// Length of data + pub data_len: Option, +} + +impl Default for TransferConfig { + fn default() -> Self { + Self { + iwidth: QspiWidth::NONE, + awidth: QspiWidth::NONE, + dwidth: QspiWidth::NONE, + instruction: 0, + address: None, + dummy: DummyCycles::_0, + data_len: None, + } + } +} + +pub struct Config { + /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. + /// If you need other value the whose predefined use `Other` variant. + pub memory_size: MemorySize, + /// Address size (8/16/24/32-bit) + pub address_size: AddressSize, + /// Scalar factor for generating CLK [0-255] + pub prescaler: u8, + /// Number of bytes to trigger FIFO threshold flag. + pub fifo_threshold: FIFOThresholdLevel, + /// Minimum number of cycles that chip select must be high between issued commands + pub cs_high_time: ChipSelectHightTime, +} + +impl Default for Config { + fn default() -> Self { + Self { + memory_size: MemorySize::Other(0), + address_size: AddressSize::_24bit, + prescaler: 128, + fifo_threshold: FIFOThresholdLevel::_17Bytes, + cs_high_time: ChipSelectHightTime::_5Cycle, + } + } +} + +#[allow(dead_code)] +pub struct Qspi<'d, T: Instance, Dma> { + _peri: PeripheralRef<'d, T>, + sck: Option>, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + nss: Option>, + dma: PeripheralRef<'d, Dma>, + config: Config, +} + +impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { + pub fn new( + peri: impl Peripheral

+ 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + sck: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

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

+ 'd, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + sck: Option>, + nss: Option>, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, dma); + + T::enable(); + T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into())); + + while T::REGS.sr().read().busy() {} + + T::REGS.cr().write(|w| { + w.set_prescaler(config.prescaler); + w.set_en(true); + }); + T::REGS.dcr().write(|w| { + w.set_fsize(config.memory_size.into()); + w.set_csht(config.cs_high_time.into()); + w.set_ckmode(false); + }); + + Self { + _peri: peri, + sck, + d0, + d1, + d2, + d3, + nss, + dma, + config, + } + } + + pub fn command(&mut self, transaction: TransferConfig) { + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); + } + + pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) { + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + if let Some(len) = transaction.data_len { + let current_ar = T::REGS.ar().read().address(); + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectRead.into()); + }); + T::REGS.ar().write(|v| { + v.set_address(current_ar); + }); + + for idx in 0..len { + while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} + buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; + } + } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); + } + + pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) { + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + if let Some(len) = transaction.data_len { + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectWrite.into()); + }); + + for idx in 0..len { + while !T::REGS.sr().read().ftf() {} + unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; + } + } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); + } + + pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) + where + Dma: QuadDma, + { + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectRead.into()); + }); + let current_ar = T::REGS.ar().read().address(); + T::REGS.ar().write(|v| { + v.set_address(current_ar); + }); + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_read( + &mut self.dma, + request, + T::REGS.dr().as_ptr() as *mut u8, + buf, + Default::default(), + ) + }; + + T::REGS.cr().modify(|v| v.set_dmaen(true)); + + transfer.blocking_wait(); + } + + pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) + where + Dma: QuadDma, + { + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectWrite.into()); + }); + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_write( + &mut self.dma, + request, + buf, + T::REGS.dr().as_ptr() as *mut u8, + Default::default(), + ) + }; + + T::REGS.cr().modify(|v| v.set_dmaen(true)); + + transfer.blocking_wait(); + } + + fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) { + T::REGS.fcr().modify(|v| { + v.set_csmf(true); + v.set_ctcf(true); + v.set_ctef(true); + v.set_ctof(true); + }); + + while T::REGS.sr().read().busy() {} + + if let Some(len) = transaction.data_len { + T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1)); + } + + T::REGS.ccr().write(|v| { + v.set_fmode(fmode.into()); + v.set_imode(transaction.iwidth.into()); + v.set_instruction(transaction.instruction); + v.set_admode(transaction.awidth.into()); + v.set_adsize(self.config.address_size.into()); + v.set_dmode(transaction.dwidth.into()); + v.set_abmode(QspiWidth::NONE.into()); + v.set_dcyc(transaction.dummy.into()); + }); + + if let Some(addr) = transaction.address { + T::REGS.ar().write(|v| { + v.set_address(addr); + }); + } + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance { + const REGS: Regs; + } +} + +pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} + +pin_trait!(SckPin, Instance); +pin_trait!(D0Pin, Instance); +pin_trait!(D1Pin, Instance); +pin_trait!(D2Pin, Instance); +pin_trait!(D3Pin, Instance); +pin_trait!(NSSPin, Instance); + +dma_trait!(QuadDma, Instance); + +foreach_peripheral!( + (quadspi, $inst:ident) => { + impl sealed::Instance for peripherals::$inst { + const REGS: Regs = crate::pac::$inst; + } + + impl Instance for peripherals::$inst {} + }; +); From 98576c17b6c29972074867499332365f7f3e89db Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Tue, 18 Jul 2023 18:35:20 +0300 Subject: [PATCH 041/298] Fix multicast support (#1670) --- embassy-net/src/lib.rs | 62 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index ae595d0d..4fbafe75 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -479,30 +479,78 @@ impl Stack { } #[cfg(feature = "igmp")] -impl Stack { +impl Stack { /// Join a multicast group. - pub fn join_multicast_group(&self, addr: T) -> Result + pub async fn join_multicast_group(&self, addr: T) -> Result + where + T: Into, + { + let addr = addr.into(); + + poll_fn(move |cx| self.poll_join_multicast_group(addr, cx)).await + } + + /// Join a multicast group. + /// + /// When the send queue is full, this method will return `Poll::Pending` + /// and register the current task to be notified when the queue has space available. + pub fn poll_join_multicast_group(&self, addr: T, cx: &mut Context<'_>) -> Poll> where T: Into, { let addr = addr.into(); self.with_mut(|s, i| { - s.iface - .join_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now())) + let mut smoldev = DriverAdapter { + cx: Some(cx), + inner: &mut i.device, + }; + + match s + .iface + .join_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now())) + { + Ok(announce_sent) => Poll::Ready(Ok(announce_sent)), + Err(MulticastError::Exhausted) => Poll::Pending, + Err(other) => Poll::Ready(Err(other)), + } }) } /// Leave a multicast group. - pub fn leave_multicast_group(&self, addr: T) -> Result + pub async fn leave_multicast_group(&self, addr: T) -> Result + where + T: Into, + { + let addr = addr.into(); + + poll_fn(move |cx| self.poll_leave_multicast_group(addr, cx)).await + } + + /// Leave a multicast group. + /// + /// When the send queue is full, this method will return `Poll::Pending` + /// and register the current task to be notified when the queue has space available. + pub fn poll_leave_multicast_group(&self, addr: T, cx: &mut Context<'_>) -> Poll> where T: Into, { let addr = addr.into(); self.with_mut(|s, i| { - s.iface - .leave_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now())) + let mut smoldev = DriverAdapter { + cx: Some(cx), + inner: &mut i.device, + }; + + match s + .iface + .leave_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now())) + { + Ok(leave_sent) => Poll::Ready(Ok(leave_sent)), + Err(MulticastError::Exhausted) => Poll::Pending, + Err(other) => Poll::Ready(Err(other)), + } }) } From a1d3bc30fa44795e6e64ba0db8607f9ea65c55a2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 18 Jul 2023 18:15:35 +0200 Subject: [PATCH 042/298] net-esp-hosted: build docs. --- .github/ci/doc.sh | 1 + embassy-net-esp-hosted/Cargo.toml | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 9e9c78a4..06c6fa00 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -37,6 +37,7 @@ docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/g docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup +docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static export KUBECONFIG=/ci/secrets/kubeconfig.yml diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index 26f5b40b..0053c49a 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml @@ -18,3 +18,9 @@ embedded-hal-async = { version = "=0.2.0-alpha.2" } noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } #noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } heapless = "0.7.16" + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/" +target = "thumbv7em-none-eabi" +features = ["defmt"] \ No newline at end of file From c333d855fca0743de1d58ef14e3c2f6389f73145 Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Tue, 18 Jul 2023 17:13:43 -0400 Subject: [PATCH 043/298] Remove merge error --- embassy-nrf/src/pdm.rs | 2 +- examples/nrf/Cargo.toml | 37 ------------------------------------- 2 files changed, 1 insertion(+), 38 deletions(-) delete mode 100644 examples/nrf/Cargo.toml diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 1fc717fd..f2675bb7 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -463,4 +463,4 @@ macro_rules! impl_pdm { type Interrupt = crate::interrupt::typelevel::$irq; } }; -} \ No newline at end of file +} diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml deleted file mode 100644 index 8309dda0..00000000 --- a/examples/nrf/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -edition = "2021" -name = "embassy-nrf-examples" -version = "0.1.0" - -[features] -default = ["nightly"] -nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid", "embassy-usb-ncm", "embedded-io/async", "embassy-net"] - -[dependencies] -embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } -embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } -embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } -embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } -embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"], optional = true } -embedded-io = "0.3.0" - -defmt = "0.3" -defmt-rtt = "0.3" - -static_cell = "1.0" -cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } -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"] } -rand = { version = "0.8.4", default-features = false } -fixed = "1.10.0" -embedded-storage = "0.3.0" -usbd-hid = "0.5.2" -serde = { version = "1.0.136", default-features = false } -num-integer = { version = "0.1.45", default-features = false } -microfft = "0.5.0" From 2c01f277c27bc6ca4f3d41ac57aa1ea24868cfc1 Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Tue, 18 Jul 2023 17:17:04 -0400 Subject: [PATCH 044/298] cargo fmt --- embassy-nrf/src/pdm.rs | 26 ++++++--- examples/nrf52840/src/bin/pdm.rs | 8 ++- examples/nrf52840/src/bin/pdm_continuous.rs | 65 +++++++++++---------- 3 files changed, 56 insertions(+), 43 deletions(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index f2675bb7..48668c7f 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -8,17 +8,17 @@ use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use futures::future::poll_fn; use fixed::types::I7F1; +use futures::future::poll_fn; use crate::chip::EASY_DMA_SIZE; use crate::gpio::sealed::Pin; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::typelevel::Interrupt; -use crate::{interrupt, Peripheral}; use crate::pac::pdm::mode::{EDGE_A, OPERATION_A}; pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency; pub use crate::pac::pdm::ratio::RATIO_A as Ratio; +use crate::{interrupt, Peripheral}; /// Interrupt handler. pub struct InterruptHandler { @@ -133,8 +133,14 @@ impl<'d, T: Instance> Pdm<'d, T> { } fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) { - let gain_left = gain_left.saturating_add(I7F1::from_bits(40)).saturating_to_num::().clamp(0, 0x50); - let gain_right = gain_right.saturating_add(I7F1::from_bits(40)).saturating_to_num::().clamp(0, 0x50); + let gain_left = gain_left + .saturating_add(I7F1::from_bits(40)) + .saturating_to_num::() + .clamp(0, 0x50); + let gain_right = gain_right + .saturating_add(I7F1::from_bits(40)) + .saturating_to_num::() + .clamp(0, 0x50); r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) }); r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) }); @@ -258,7 +264,8 @@ impl<'d, T: Instance> Pdm<'d, T> { &mut self, bufs: &mut [[i16; N]; 2], mut sampler: S, - ) -> Result<(), Error> where + ) -> Result<(), Error> + where S: FnMut(&[i16; N]) -> SamplerState, { let r = T::regs(); @@ -267,7 +274,9 @@ impl<'d, T: Instance> Pdm<'d, T> { return Err(Error::AlreadyRunning); } - r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) }); + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) }); r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) }); // Reset and enable the events @@ -285,7 +294,7 @@ impl<'d, T: Instance> Pdm<'d, T> { // wouldn't happen anyway. compiler_fence(Ordering::SeqCst); - r.tasks_start.write(|w| { w.tasks_start().set_bit() }); + r.tasks_start.write(|w| w.tasks_start().set_bit()); let mut current_buffer = 0; @@ -309,7 +318,8 @@ impl<'d, T: Instance> Pdm<'d, T> { r.events_end.reset(); r.intenset.write(|w| w.end().set()); - if !done { // Discard the last buffer after the user requested a stop. + if !done { + // Discard the last buffer after the user requested a stop. if sampler(&bufs[current_buffer]) == SamplerState::Sampled { let next_buffer = 1 - current_buffer; current_buffer = next_buffer; diff --git a/examples/nrf52840/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs index 47fe6773..444b9137 100644 --- a/examples/nrf52840/src/bin/pdm.rs +++ b/examples/nrf52840/src/bin/pdm.rs @@ -41,9 +41,11 @@ async fn main(_p: Spawner) { buf.iter().min().unwrap(), buf.iter().max().unwrap(), mean, - ( - buf.iter().map(|v| i32::from(*v - mean).pow(2)).fold(0i32, |a,b| a.saturating_add(b)) - / buf.len() as i32).sqrt() as i16, + (buf.iter() + .map(|v| i32::from(*v - mean).pow(2)) + .fold(0i32, |a, b| a.saturating_add(b)) + / buf.len() as i32) + .sqrt() as i16, ); info!("samples: {:?}", &buf); diff --git a/examples/nrf52840/src/bin/pdm_continuous.rs b/examples/nrf52840/src/bin/pdm_continuous.rs index 9eaf3071..7d853147 100644 --- a/examples/nrf52840/src/bin/pdm_continuous.rs +++ b/examples/nrf52840/src/bin/pdm_continuous.rs @@ -2,14 +2,15 @@ #![no_main] #![feature(type_alias_impl_trait)] -use defmt::info; use core::cmp::Ordering; + +use defmt::info; use embassy_executor::Spawner; +use embassy_nrf::pdm::{self, Config, Frequency, OperationMode, Pdm, Ratio, SamplerState}; use embassy_nrf::{bind_interrupts, peripherals}; -use embassy_nrf::pdm::{self, Config, OperationMode, Pdm, SamplerState, Frequency, Ratio}; use fixed::types::I7F1; -use num_integer::Roots; use microfft::real::rfft_1024; +use num_integer::Roots; use {defmt_rtt as _, panic_probe as _}; // Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer @@ -31,34 +32,34 @@ async fn main(_p: Spawner) { let mut bufs = [[0; 1024]; 2]; - pdm - .run_task_sampler( - &mut bufs, - move |buf| { - // NOTE: It is important that the time spent within this callback - // does not exceed the time taken to acquire the 1500 samples we - // have in this example, which would be 10us + 2us per - // sample * 1500 = 18ms. You need to measure the time taken here - // and set the sample buffer size accordingly. Exceeding this - // time can lead to the peripheral re-writing the other buffer. - let mean = (buf.iter().map(|v| i32::from(*v)).sum::() / buf.len() as i32) as i16; - let (peak_freq_index, peak_mag) = fft_peak_freq(&buf); - let peak_freq = peak_freq_index * 16000 / buf.len(); - info!( - "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}, peak {} @ {} Hz", - buf.len(), - buf.iter().min().unwrap(), - buf.iter().max().unwrap(), - mean, - ( - buf.iter().map(|v| i32::from(*v - mean).pow(2)).fold(0i32, |a,b| a.saturating_add(b)) - / buf.len() as i32).sqrt() as i16, - peak_mag, peak_freq, - ); - SamplerState::Sampled - }, - ) - .await.unwrap(); + pdm.run_task_sampler(&mut bufs, move |buf| { + // NOTE: It is important that the time spent within this callback + // does not exceed the time taken to acquire the 1500 samples we + // have in this example, which would be 10us + 2us per + // sample * 1500 = 18ms. You need to measure the time taken here + // and set the sample buffer size accordingly. Exceeding this + // time can lead to the peripheral re-writing the other buffer. + let mean = (buf.iter().map(|v| i32::from(*v)).sum::() / buf.len() as i32) as i16; + let (peak_freq_index, peak_mag) = fft_peak_freq(&buf); + let peak_freq = peak_freq_index * 16000 / buf.len(); + info!( + "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}, peak {} @ {} Hz", + buf.len(), + buf.iter().min().unwrap(), + buf.iter().max().unwrap(), + mean, + (buf.iter() + .map(|v| i32::from(*v - mean).pow(2)) + .fold(0i32, |a, b| a.saturating_add(b)) + / buf.len() as i32) + .sqrt() as i16, + peak_mag, + peak_freq, + ); + SamplerState::Sampled + }) + .await + .unwrap(); } fn fft_peak_freq(input: &[i16; 1024]) -> (usize, u32) { @@ -75,6 +76,6 @@ fn fft_peak_freq(input: &[i16; 1024]) -> (usize, u32) { .map(|c| c.norm_sqr()) .enumerate() .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Equal)) - .map(|(i, v)| (i, ((v*32768.0) as u32).sqrt())) + .map(|(i, v)| (i, ((v * 32768.0) as u32).sqrt())) .unwrap() } From be7fbe50d7afe78988509be485d461494d77381a Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Tue, 18 Jul 2023 18:31:32 -0400 Subject: [PATCH 045/298] Update pdm driver to build with all the PACs --- embassy-nrf/src/pdm.rs | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 48668c7f..4cee566c 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -17,9 +17,16 @@ use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::typelevel::Interrupt; use crate::pac::pdm::mode::{EDGE_A, OPERATION_A}; pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency; -pub use crate::pac::pdm::ratio::RATIO_A as Ratio; use crate::{interrupt, Peripheral}; +#[cfg(any( + feature = "nrf52840", + feature = "nrf52833", + feature = "_nrf5340-app", + feature = "_nrf9160", +))] +pub use crate::pac::pdm::ratio::RATIO_A as Ratio; + /// Interrupt handler. pub struct InterruptHandler { _phantom: PhantomData, @@ -111,6 +118,12 @@ impl<'d, T: Instance> Pdm<'d, T> { // configure r.pdmclkctrl.write(|w| w.freq().variant(config.frequency)); + #[cfg(any( + feature = "nrf52840", + feature = "nrf52833", + feature = "_nrf5340-app", + feature = "_nrf9160", + ))] r.ratio.write(|w| w.ratio().variant(config.ratio)); r.mode.write(|w| { w.operation().variant(config.operation_mode.into()); @@ -294,14 +307,14 @@ impl<'d, T: Instance> Pdm<'d, T> { // wouldn't happen anyway. compiler_fence(Ordering::SeqCst); - r.tasks_start.write(|w| w.tasks_start().set_bit()); + r.tasks_start.write(|w| unsafe { w.bits(1) }); let mut current_buffer = 0; let mut done = false; let drop = OnDrop::new(|| { - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); // N.B. It would be better if this were async, but Drop only support sync code. while r.events_stopped.read().bits() != 0 {} }); @@ -324,7 +337,7 @@ impl<'d, T: Instance> Pdm<'d, T> { let next_buffer = 1 - current_buffer; current_buffer = next_buffer; } else { - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); done = true; }; }; @@ -361,6 +374,12 @@ pub struct Config { /// Clock frequency pub frequency: Frequency, /// Clock ratio + #[cfg(any( + feature = "nrf52840", + feature = "nrf52833", + feature = "_nrf5340-app", + feature = "_nrf9160", + ))] pub ratio: Ratio, /// Gain left in dB pub gain_left: I7F1, @@ -374,6 +393,12 @@ impl Default for Config { operation_mode: OperationMode::Mono, edge: Edge::LeftFalling, frequency: Frequency::DEFAULT, + #[cfg(any( + feature = "nrf52840", + feature = "nrf52833", + feature = "_nrf5340-app", + feature = "_nrf9160", + ))] ratio: Ratio::RATIO80, gain_left: I7F1::ZERO, gain_right: I7F1::ZERO, From 7555a1e3025a45a145734026c0f841d7c6c2625f Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Tue, 18 Jul 2023 18:32:19 -0400 Subject: [PATCH 046/298] cargo fmt --- embassy-nrf/src/pdm.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 4cee566c..217884d1 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -17,8 +17,6 @@ use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::typelevel::Interrupt; use crate::pac::pdm::mode::{EDGE_A, OPERATION_A}; pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency; -use crate::{interrupt, Peripheral}; - #[cfg(any( feature = "nrf52840", feature = "nrf52833", @@ -26,6 +24,7 @@ use crate::{interrupt, Peripheral}; feature = "_nrf9160", ))] pub use crate::pac::pdm::ratio::RATIO_A as Ratio; +use crate::{interrupt, Peripheral}; /// Interrupt handler. pub struct InterruptHandler { From 890d113b855dc11be75a9716401c7703f4ce48e1 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 18 Jul 2023 18:28:12 -0500 Subject: [PATCH 047/298] wpan: fully implement initial draft concept --- embassy-stm32-wpan/src/mac/control.rs | 8 +-- embassy-stm32-wpan/src/mac/driver.rs | 75 ++++++++++++++----------- embassy-stm32-wpan/src/mac/mod.rs | 2 +- embassy-stm32-wpan/src/mac/runner.rs | 21 +++---- embassy-sync/src/channel.rs | 22 ++++++++ examples/stm32wb/src/bin/mac_ffd_net.rs | 2 +- 6 files changed, 80 insertions(+), 50 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs index 6e45e595..2f8a7d07 100644 --- a/embassy-stm32-wpan/src/mac/control.rs +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -6,13 +6,13 @@ pub struct Error { } pub struct Control<'a> { - runner: &'a Runner, + runner: &'a Runner<'a>, } impl<'a> Control<'a> { - pub(crate) fn new(runner: &'a Runner) -> Self { - Self { runner: runner } - } + pub(crate) fn new(runner: &'a Runner<'a>) -> Self { + Self { runner: runner } + } pub async fn init(&mut self) { // TODO diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 118f6908..8ebfb2b7 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -4,16 +4,20 @@ use core::task::Context; use embassy_net_driver::{Capabilities, LinkState, Medium}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::channel::Channel; +use super::event::MacEvent; +use crate::mac::event::Event; use crate::mac::runner::Runner; use crate::mac::MTU; pub struct Driver<'d> { - runner: &'d Runner, + runner: &'d Runner<'d>, } impl<'d> Driver<'d> { - pub(crate) fn new(runner: &'d Runner) -> Self { + pub(crate) fn new(runner: &'d Runner<'d>) -> Self { Self { runner: runner } } } @@ -21,33 +25,32 @@ impl<'d> Driver<'d> { impl<'d> embassy_net_driver::Driver for Driver<'d> { // type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; // type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; - type RxToken<'a> = RxToken where Self: 'a; - type TxToken<'a> = TxToken where Self: 'a; + type RxToken<'a> = RxToken<'d> where Self: 'a; + type TxToken<'a> = TxToken<'d> where Self: 'a; fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - self.runner.rx_waker.register(cx.waker()); - - // WAKER.register(cx.waker()); - // if self.rx.available().is_some() && self.tx.available().is_some() { - // Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) - // } else { - // None - // } - - None + if self.runner.rx_channel.poll_ready_to_receive(cx) && self.runner.tx_channel.poll_ready_to_receive(cx) { + Some(( + RxToken { + rx: &self.runner.rx_channel, + }, + TxToken { + tx: &self.runner.tx_channel, + }, + )) + } else { + None + } } fn transmit(&mut self, cx: &mut Context) -> Option> { - self.runner.tx_waker.register(cx.waker()); - - // WAKER.register(cx.waker()); - // / if self.tx.available().is_some() { - // / Some(TxToken { tx: &mut self.tx }) - // / } else { - // / None - // / } - - None + if self.runner.tx_channel.poll_ready_to_receive(cx) { + Some(TxToken { + tx: &self.runner.tx_channel, + }) + } else { + None + } } fn capabilities(&self) -> Capabilities { @@ -76,30 +79,38 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { } } -pub struct RxToken { - // rx: &'a mut RDesRing<'d>, +pub struct RxToken<'d> { + rx: &'d Channel, } -impl embassy_net_driver::RxToken for RxToken { +impl<'d> embassy_net_driver::RxToken for RxToken<'d> { fn consume(self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - // let pkt = unwrap!(self.rx.available()); + // Only valid data events should be put into the queue + + let event = self.rx.try_recv().unwrap(); + let mac_event = event.mac_event().unwrap(); + let data_event = match mac_event { + MacEvent::McpsDataInd(data_event) => data_event, + _ => unreachable!(), + }; let pkt = &mut []; let r = f(&mut pkt[0..]); - // self.rx.pop_packet(); + + // let r = f(&mut data_event.payload()); r } } -pub struct TxToken { +pub struct TxToken<'d> { + tx: &'d Channel, // tx: &'a mut TDesRing<'d>, } -impl embassy_net_driver::TxToken for TxToken { +impl<'d> embassy_net_driver::TxToken for TxToken<'d> { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index 2f9d1c81..3dcda17a 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -18,7 +18,7 @@ pub use crate::mac::runner::Runner; const MTU: usize = 127; -pub async fn new<'a>(runner: &'a Runner) -> (Control<'a>, Driver<'a>) { +pub async fn new<'a>(runner: &'a Runner<'a>) -> (Control<'a>, Driver<'a>) { (Control::new(runner), Driver::new(runner)) } diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index d545d6c9..911ff60b 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -1,4 +1,6 @@ use embassy_futures::select::{select3, Either3}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::channel::Channel; use embassy_sync::waitqueue::AtomicWaker; use crate::mac::event::{Event, MacEvent}; @@ -44,22 +46,18 @@ impl TxRing { } } -pub struct Runner { +pub struct Runner<'a> { mac_subsystem: Mac, - pub(crate) rx_ring: Option, - pub(crate) tx_ring: TxRing, - pub(crate) rx_waker: AtomicWaker, - pub(crate) tx_waker: AtomicWaker, + pub(crate) rx_channel: Channel, + pub(crate) tx_channel: Channel, } -impl Runner { +impl<'a> Runner<'a> { pub fn new(mac: Mac) -> Self { Self { mac_subsystem: mac, - rx_ring: None, - tx_ring: TxRing::new(), - rx_waker: AtomicWaker::new(), - tx_waker: AtomicWaker::new(), + rx_channel: Channel::new(), + tx_channel: Channel::new(), } } @@ -73,8 +71,7 @@ impl Runner { if let Ok(evt) = event.mac_event() { match evt { MacEvent::McpsDataInd(data_ind) => { - // TODO: store mac_event in rx_ring - self.rx_waker.wake(); + self.rx_channel.try_send(event); } _ => {} } diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index 77352874..f421af39 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -335,6 +335,12 @@ impl ChannelState { } } + fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> bool { + self.receiver_waker.register(cx.waker()); + + !self.queue.is_empty() + } + fn try_send(&mut self, message: T) -> Result<(), TrySendError> { self.try_send_with_context(message, None) } @@ -353,6 +359,12 @@ impl ChannelState { } } } + + fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> bool { + self.senders_waker.register(cx.waker()); + + !self.queue.is_full() + } } /// A bounded channel for communicating between asynchronous tasks @@ -401,6 +413,16 @@ where self.lock(|c| c.try_send_with_context(m, cx)) } + /// Allows a poll_fn to poll until the channel is ready to receive + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + self.lock(|c| c.poll_ready_to_receive(cx)) + } + + /// Allows a poll_fn to poll until the channel is ready to send + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + self.lock(|c| c.poll_ready_to_send(cx)) + } + /// Get a sender for this channel. pub fn sender(&self) -> Sender<'_, M, T, N> { Sender { channel: self } diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs index b1cf051b..6072f418 100644 --- a/examples/stm32wb/src/bin/mac_ffd_net.rs +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -25,7 +25,7 @@ async fn run_mm_queue(memory_manager: mm::MemoryManager) { } #[embassy_executor::task] -async fn run_mac(runner: &'static Runner) { +async fn run_mac(runner: &'static Runner<'static>) { runner.run().await; } From ca1d4179a792d4a33b4f2b97b33002759fd28a21 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 18 Jul 2023 20:52:03 -0500 Subject: [PATCH 048/298] wpan: implement initial event loop --- embassy-stm32-wpan/Cargo.toml | 4 +- embassy-stm32-wpan/src/mac/driver.rs | 22 +++-- embassy-stm32-wpan/src/mac/runner.rs | 113 ++++++++++++------------ examples/stm32wb/Cargo.toml | 6 +- examples/stm32wb/src/bin/mac_ffd_net.rs | 10 ++- 5 files changed, 85 insertions(+), 70 deletions(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 082d00f1..ab58714d 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -18,6 +18,7 @@ embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel", optional=true } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver", optional=true } defmt = { version = "0.3", optional = true } cortex-m = "0.7.6" @@ -27,13 +28,14 @@ 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 } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 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"] ble = ["dep:stm32wb-hci"] -mac = ["dep:bitflags", "dep:embassy-net-driver-channel"] +mac = ["dep:bitflags", "dep:embassy-net-driver-channel", "dep:embassy-net-driver"] stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ] diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 8ebfb2b7..a41b7509 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -29,13 +29,14 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { type TxToken<'a> = TxToken<'d> where Self: 'a; fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - if self.runner.rx_channel.poll_ready_to_receive(cx) && self.runner.tx_channel.poll_ready_to_receive(cx) { + if self.runner.rx_channel.poll_ready_to_receive(cx) && self.runner.tx_buf_channel.poll_ready_to_receive(cx) { Some(( RxToken { rx: &self.runner.rx_channel, }, TxToken { tx: &self.runner.tx_channel, + tx_buf: &self.runner.tx_buf_channel, }, )) } else { @@ -44,9 +45,10 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { } fn transmit(&mut self, cx: &mut Context) -> Option> { - if self.runner.tx_channel.poll_ready_to_receive(cx) { + if self.runner.tx_buf_channel.poll_ready_to_receive(cx) { Some(TxToken { tx: &self.runner.tx_channel, + tx_buf: &self.runner.tx_buf_channel, }) } else { None @@ -106,8 +108,8 @@ impl<'d> embassy_net_driver::RxToken for RxToken<'d> { } pub struct TxToken<'d> { - tx: &'d Channel, - // tx: &'a mut TDesRing<'d>, + tx: &'d Channel, + tx_buf: &'d Channel, } impl<'d> embassy_net_driver::TxToken for TxToken<'d> { @@ -115,11 +117,13 @@ impl<'d> embassy_net_driver::TxToken for TxToken<'d> { where F: FnOnce(&mut [u8]) -> R, { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - // let pkt = unwrap!(self.tx.available()); - let pkt = &mut []; - let r = f(&mut pkt[..len]); - // self.tx.transmit(len); + // Only valid tx buffers should be put into the queue + let buf = self.tx_buf.try_recv().unwrap(); + let r = f(&mut buf[..len]); + + // The tx channel should always be of equal capacity to the tx_buf channel + self.tx.try_send(buf).unwrap(); + r } } diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index 911ff60b..9edcff9b 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -1,83 +1,80 @@ -use embassy_futures::select::{select3, Either3}; +use embassy_futures::join; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; -use embassy_sync::waitqueue::AtomicWaker; +use crate::mac::commands::DataRequest; use crate::mac::event::{Event, MacEvent}; +use crate::mac::typedefs::{AddressMode, MacAddress, PanId, SecurityLevel}; use crate::mac::MTU; use crate::sub::mac::Mac; -pub(crate) struct TxRing { - // stores n packets of up to mtu size - ring: [[u8; MTU]; 5], - pending: bool, - // start: u8, - // end: u8, -} - -impl TxRing { - pub(crate) fn new() -> Self { - Self { - ring: [[0; MTU]; 5], - pending: false, - } - } - - // wait for a free packet to become available - pub fn is_packet_free(&self) -> bool { - !self.pending - } - - // get the next available free packet - pub fn get_free_packet<'a>(&'a mut self) -> &'a mut [u8] { - self.pending = true; - - &mut self.ring[0] - } - - pub fn get_packet_to_transmit<'a>(&'a mut self) -> Option<&'a [u8]> { - if self.pending { - self.pending = false; - - Some(&self.ring[0]) - } else { - None - } - } -} - pub struct Runner<'a> { mac_subsystem: Mac, pub(crate) rx_channel: Channel, - pub(crate) tx_channel: Channel, + pub(crate) tx_channel: Channel, + pub(crate) tx_buf_channel: Channel, } impl<'a> Runner<'a> { - pub fn new(mac: Mac) -> Self { - Self { + pub fn new(mac: Mac, tx_buf_queue: [&'a mut [u8; MTU]; 5]) -> Self { + let this = Self { mac_subsystem: mac, rx_channel: Channel::new(), tx_channel: Channel::new(), - } - } + tx_buf_channel: Channel::new(), + }; - pub(crate) async fn init(&mut self, firmware: &[u8]) { - debug!("wifi init done"); + for buf in tx_buf_queue { + this.tx_buf_channel.try_send(buf).unwrap(); + } + + this } pub async fn run(&self) -> ! { - loop { - let event = self.mac_subsystem.read().await; - if let Ok(evt) = event.mac_event() { - match evt { - MacEvent::McpsDataInd(data_ind) => { - self.rx_channel.try_send(event); + join::join( + async { + loop { + let event = self.mac_subsystem.read().await; + if let Ok(evt) = event.mac_event() { + match evt { + MacEvent::McpsDataInd(_) => { + self.rx_channel.send(event).await; + } + _ => {} + } } - _ => {} } - } + }, + async { + loop { + let buf = self.tx_channel.recv().await; - // TODO: select tx event - } + self.mac_subsystem + .send_command( + DataRequest { + src_addr_mode: AddressMode::Short, + dst_addr_mode: AddressMode::Short, + dst_pan_id: PanId([0x1A, 0xAA]), + dst_address: MacAddress::BROADCAST, + msdu_handle: 0x02, + ack_tx: 0x00, + gts_tx: false, + security_level: SecurityLevel::Unsecure, + ..Default::default() + } + .set_buffer(&buf), + ) + .await + .unwrap(); + + // The tx channel should always be of equal capacity to the tx_buf channel + self.tx_buf_channel.try_send(buf).unwrap(); + } + }, + ) + .await; + + loop {} } } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 8585b99f..7c0b83e6 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -21,7 +21,7 @@ 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 } - +static_cell = { version = "1.1", features = ["nightly"]} [features] default = ["ble", "mac"] @@ -40,6 +40,10 @@ required-features = ["mac"] name = "mac_ffd" required-features = ["mac"] +[[bin]] +name = "mac_ffd_net" +required-features = ["mac"] + [[bin]] name = "eddystone_beacon" required-features = ["ble"] diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs index 6072f418..7b8c4b9d 100644 --- a/examples/stm32wb/src/bin/mac_ffd_net.rs +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -172,7 +172,15 @@ async fn main(spawner: Spawner) { defmt::info!("{:#x}", evt.mac_event()); } - let runner = make_static!(Runner::new(mbox.mac_subsystem)); + let tx_queue = [ + make_static!([0u8; 127]), + make_static!([0u8; 127]), + make_static!([0u8; 127]), + make_static!([0u8; 127]), + make_static!([0u8; 127]), + ]; + + let runner = make_static!(Runner::new(mbox.mac_subsystem, tx_queue)); spawner.spawn(run_mac(runner)).unwrap(); From 3df2c71e6c27eedacef1ddd822537c8762fe0578 Mon Sep 17 00:00:00 2001 From: Alessandro Pezzato Date: Tue, 18 Jul 2023 22:24:52 +0200 Subject: [PATCH 049/298] stm32/uart: add swap_rx_tx --- embassy-stm32/src/usart/mod.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index c97efbf0..1ba182e2 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -116,6 +116,10 @@ pub struct Config { /// but will effectively disable noise detection. #[cfg(not(usart_v1))] pub assume_noise_free: bool, + + /// Set this to true to swap the RX and TX pins. + #[cfg(any(usart_v3, usart_v4))] + pub swap_rx_tx: bool, } impl Default for Config { @@ -129,6 +133,8 @@ impl Default for Config { detect_previous_overrun: false, #[cfg(not(usart_v1))] assume_noise_free: false, + #[cfg(any(usart_v3, usart_v4))] + swap_rx_tx: false, } } } @@ -688,8 +694,20 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { let r = T::regs(); - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + #[allow(unused_variables)] + let swap_rx_tx = false; + + #[cfg(any(usart_v3, usart_v4))] + let swap_rx_tx = config.swap_rx_tx; + + if swap_rx_tx { + let (rx, tx) = (tx, rx); + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } else { + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } configure(r, &config, T::frequency(), T::KIND, true, true); @@ -847,6 +865,9 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: StopBits::STOP1P5 => vals::Stop::STOP1P5, StopBits::STOP2 => vals::Stop::STOP2, }); + + #[cfg(any(usart_v3, usart_v4))] + w.set_swap(config.swap_rx_tx); }); r.cr1().write(|w| { // enable uart From 36ff688fab8bb0a17f001cdf62f24884e3ac1f33 Mon Sep 17 00:00:00 2001 From: Alessandro Pezzato Date: Wed, 19 Jul 2023 10:50:40 +0200 Subject: [PATCH 050/298] stm32/uart: optimize swap_rx_tx --- embassy-stm32/src/usart/mod.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 1ba182e2..ea8e525e 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -694,19 +694,21 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { let r = T::regs(); - #[allow(unused_variables)] - let swap_rx_tx = false; - - #[cfg(any(usart_v3, usart_v4))] - let swap_rx_tx = config.swap_rx_tx; - - if swap_rx_tx { - let (rx, tx) = (tx, rx); - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - } else { - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + // Some chips do not have swap_rx_tx bit + cfg_if::cfg_if! { + if #[cfg(any(usart_v3, usart_v4))] { + if config.swap_rx_tx { + let (rx, tx) = (tx, rx); + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } else { + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } + } else { + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } } configure(r, &config, T::frequency(), T::KIND, true, true); From 28254842db645bc961d19fa2287610b7b1d3447c Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 19 Jul 2023 17:49:08 -0500 Subject: [PATCH 051/298] - optimize event to parse opcode only once - optimze channels - return mut ref for smoltcp rx --- embassy-stm32-wpan/Cargo.toml | 3 +- embassy-stm32-wpan/src/mac/control.rs | 1 + embassy-stm32-wpan/src/mac/driver.rs | 23 ++-- embassy-stm32-wpan/src/mac/event.rs | 123 +++++++++++++++------- embassy-stm32-wpan/src/mac/indications.rs | 4 +- embassy-stm32-wpan/src/mac/mod.rs | 7 -- embassy-stm32-wpan/src/mac/runner.rs | 19 ++-- embassy-stm32-wpan/src/sub/mac.rs | 2 +- examples/stm32wb/src/bin/mac_ffd.rs | 34 +++--- examples/stm32wb/src/bin/mac_ffd_net.rs | 28 ++--- examples/stm32wb/src/bin/mac_rfd.rs | 34 +++--- tests/stm32/src/bin/wpan_mac.rs | 33 +++--- 12 files changed, 178 insertions(+), 133 deletions(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index ab58714d..6cd12220 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -17,7 +17,6 @@ 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-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } -embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel", optional=true } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver", optional=true } defmt = { version = "0.3", optional = true } @@ -35,7 +34,7 @@ bitflags = { version = "2.3.3", optional = true } defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "stm32wb-hci?/defmt"] ble = ["dep:stm32wb-hci"] -mac = ["dep:bitflags", "dep:embassy-net-driver-channel", "dep:embassy-net-driver"] +mac = ["dep:bitflags", "dep:embassy-net-driver" ] stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ] diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs index 2f8a7d07..c45f6407 100644 --- a/embassy-stm32-wpan/src/mac/control.rs +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -6,6 +6,7 @@ pub struct Error { } pub struct Control<'a> { + #[allow(dead_code)] runner: &'a Runner<'a>, } diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index a41b7509..3017808f 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -7,8 +7,7 @@ use embassy_net_driver::{Capabilities, LinkState, Medium}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; -use super::event::MacEvent; -use crate::mac::event::Event; +use crate::mac::event::{Event, MacEvent}; use crate::mac::runner::Runner; use crate::mac::MTU; @@ -64,7 +63,7 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { caps } - fn link_state(&mut self, cx: &mut Context) -> LinkState { + fn link_state(&mut self, _cx: &mut Context) -> LinkState { // if self.phy.poll_link(&mut self.station_management, cx) { // LinkState::Up // } else { @@ -82,7 +81,7 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { } pub struct RxToken<'d> { - rx: &'d Channel, + rx: &'d Channel, 1>, } impl<'d> embassy_net_driver::RxToken for RxToken<'d> { @@ -92,24 +91,18 @@ impl<'d> embassy_net_driver::RxToken for RxToken<'d> { { // Only valid data events should be put into the queue - let event = self.rx.try_recv().unwrap(); - let mac_event = event.mac_event().unwrap(); - let data_event = match mac_event { + let data_event = match *self.rx.try_recv().unwrap() { MacEvent::McpsDataInd(data_event) => data_event, _ => unreachable!(), }; - let pkt = &mut []; - let r = f(&mut pkt[0..]); - - // let r = f(&mut data_event.payload()); - r + f(&mut data_event.payload()) } } pub struct TxToken<'d> { - tx: &'d Channel, - tx_buf: &'d Channel, + tx: &'d Channel, + tx_buf: &'d Channel, } impl<'d> embassy_net_driver::TxToken for TxToken<'d> { @@ -122,7 +115,7 @@ impl<'d> embassy_net_driver::TxToken for TxToken<'d> { let r = f(&mut buf[..len]); // The tx channel should always be of equal capacity to the tx_buf channel - self.tx.try_send(buf).unwrap(); + self.tx.try_send((buf, len)).unwrap(); r } diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index a2bb7922..d975c5bd 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -1,4 +1,4 @@ -use core::mem; +use core::{mem, ops}; use super::indications::{ AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication, @@ -22,55 +22,104 @@ pub(crate) trait ParseableMacEvent: Sized { } } -pub struct Event { +pub struct Event<'a> { + #[allow(dead_code)] event_box: EvtBox, + mac_event: MacEvent<'a>, } -impl Event { - pub(crate) fn new(event_box: EvtBox) -> Self { - Self { event_box } - } - - pub fn mac_event<'a>(&'a self) -> Result, ()> { - let payload = self.event_box.payload(); +impl<'a> Event<'a> { + pub(crate) fn new(event_box: EvtBox) -> Result { + let payload = event_box.payload(); let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap()); let opcode = OpcodeM0ToM4::try_from(opcode)?; let buf = &payload[2..]; - match opcode { - OpcodeM0ToM4::MlmeAssociateCnf => Ok(MacEvent::MlmeAssociateCnf(AssociateConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeDisassociateCnf => { - Ok(MacEvent::MlmeDisassociateCnf(DisassociateConfirm::from_buffer(buf)?)) + // To avoid re-parsing the opcode, we store the result of the parse + // this requires use of unsafe because rust cannot assume that a reference will become + // invalid when the underlying result is moved. However, because we refer to a "heap" + // allocation, the underlying reference will not move until the struct is dropped. + + let mac_event = match opcode { + OpcodeM0ToM4::MlmeAssociateCnf => { + MacEvent::MlmeAssociateCnf(unsafe { &*(AssociateConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeDisassociateCnf => { + MacEvent::MlmeDisassociateCnf(unsafe { &*(DisassociateConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeGetCnf => MacEvent::MlmeGetCnf(unsafe { &*(GetConfirm::from_buffer(buf)? as *const _) }), + OpcodeM0ToM4::MlmeGtsCnf => MacEvent::MlmeGtsCnf(unsafe { &*(GtsConfirm::from_buffer(buf)? as *const _) }), + OpcodeM0ToM4::MlmeResetCnf => { + MacEvent::MlmeResetCnf(unsafe { &*(ResetConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeRxEnableCnf => { + MacEvent::MlmeRxEnableCnf(unsafe { &*(RxEnableConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeScanCnf => { + MacEvent::MlmeScanCnf(unsafe { &*(ScanConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeSetCnf => MacEvent::MlmeSetCnf(unsafe { &*(SetConfirm::from_buffer(buf)? as *const _) }), + OpcodeM0ToM4::MlmeStartCnf => { + MacEvent::MlmeStartCnf(unsafe { &*(StartConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmePollCnf => { + MacEvent::MlmePollCnf(unsafe { &*(PollConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeDpsCnf => MacEvent::MlmeDpsCnf(unsafe { &*(DpsConfirm::from_buffer(buf)? as *const _) }), + OpcodeM0ToM4::MlmeSoundingCnf => { + MacEvent::MlmeSoundingCnf(unsafe { &*(SoundingConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeCalibrateCnf => { + MacEvent::MlmeCalibrateCnf(unsafe { &*(CalibrateConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::McpsDataCnf => { + MacEvent::McpsDataCnf(unsafe { &*(DataConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::McpsPurgeCnf => { + MacEvent::McpsPurgeCnf(unsafe { &*(PurgeConfirm::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeAssociateInd => { + MacEvent::MlmeAssociateInd(unsafe { &*(AssociateIndication::from_buffer(buf)? as *const _) }) } - OpcodeM0ToM4::MlmeGetCnf => Ok(MacEvent::MlmeGetCnf(GetConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeGtsCnf => Ok(MacEvent::MlmeGtsCnf(GtsConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeResetCnf => Ok(MacEvent::MlmeResetCnf(ResetConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeRxEnableCnf => Ok(MacEvent::MlmeRxEnableCnf(RxEnableConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeScanCnf => Ok(MacEvent::MlmeScanCnf(ScanConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeSetCnf => Ok(MacEvent::MlmeSetCnf(SetConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeStartCnf => Ok(MacEvent::MlmeStartCnf(StartConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::MlmePollCnf => Ok(MacEvent::MlmePollCnf(PollConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeDpsCnf => Ok(MacEvent::MlmeDpsCnf(DpsConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeSoundingCnf => Ok(MacEvent::MlmeSoundingCnf(SoundingConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeCalibrateCnf => Ok(MacEvent::MlmeCalibrateCnf(CalibrateConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::McpsDataCnf => Ok(MacEvent::McpsDataCnf(DataConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::McpsPurgeCnf => Ok(MacEvent::McpsPurgeCnf(PurgeConfirm::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeAssociateInd => Ok(MacEvent::MlmeAssociateInd(AssociateIndication::from_buffer(buf)?)), OpcodeM0ToM4::MlmeDisassociateInd => { - Ok(MacEvent::MlmeDisassociateInd(DisassociateIndication::from_buffer(buf)?)) + MacEvent::MlmeDisassociateInd(unsafe { &*(DisassociateIndication::from_buffer(buf)? as *const _) }) } OpcodeM0ToM4::MlmeBeaconNotifyInd => { - Ok(MacEvent::MlmeBeaconNotifyInd(BeaconNotifyIndication::from_buffer(buf)?)) + MacEvent::MlmeBeaconNotifyInd(unsafe { &*(BeaconNotifyIndication::from_buffer(buf)? as *const _) }) } - OpcodeM0ToM4::MlmeCommStatusInd => Ok(MacEvent::MlmeCommStatusInd(CommStatusIndication::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeGtsInd => Ok(MacEvent::MlmeGtsInd(GtsIndication::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeOrphanInd => Ok(MacEvent::MlmeOrphanInd(OrphanIndication::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeSyncLossInd => Ok(MacEvent::MlmeSyncLossInd(SyncLossIndication::from_buffer(buf)?)), - OpcodeM0ToM4::MlmeDpsInd => Ok(MacEvent::MlmeDpsInd(DpsIndication::from_buffer(buf)?)), - OpcodeM0ToM4::McpsDataInd => Ok(MacEvent::McpsDataInd(DataIndication::from_buffer(buf)?)), - OpcodeM0ToM4::MlmePollInd => Ok(MacEvent::MlmePollInd(PollIndication::from_buffer(buf)?)), - } + OpcodeM0ToM4::MlmeCommStatusInd => { + MacEvent::MlmeCommStatusInd(unsafe { &*(CommStatusIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeGtsInd => { + MacEvent::MlmeGtsInd(unsafe { &*(GtsIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeOrphanInd => { + MacEvent::MlmeOrphanInd(unsafe { &*(OrphanIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeSyncLossInd => { + MacEvent::MlmeSyncLossInd(unsafe { &*(SyncLossIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmeDpsInd => { + MacEvent::MlmeDpsInd(unsafe { &*(DpsIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::McpsDataInd => { + MacEvent::McpsDataInd(unsafe { &*(DataIndication::from_buffer(buf)? as *const _) }) + } + OpcodeM0ToM4::MlmePollInd => { + MacEvent::MlmePollInd(unsafe { &*(PollIndication::from_buffer(buf)? as *const _) }) + } + }; + + Ok(Self { event_box, mac_event }) + } +} + +impl<'a> ops::Deref for Event<'a> { + type Target = MacEvent<'a>; + + fn deref(&self) -> &Self::Target { + &self.mac_event } } diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index 5445fb4a..4349af9c 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -236,8 +236,8 @@ pub struct DataIndication { impl ParseableMacEvent for DataIndication {} impl DataIndication { - pub fn payload<'a>(&'a self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.msdu_ptr, self.msdu_length as usize) } + pub fn payload<'a>(&'a self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.msdu_ptr as *mut _, self.msdu_length as usize) } } } diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index 3dcda17a..a93f7a69 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -10,8 +10,6 @@ pub mod responses; pub mod runner; pub mod typedefs; -use core::slice; - pub use crate::mac::control::{Control, Error as ControlError}; use crate::mac::driver::Driver; pub use crate::mac::runner::Runner; @@ -21,8 +19,3 @@ const MTU: usize = 127; pub async fn new<'a>(runner: &'a Runner<'a>) -> (Control<'a>, Driver<'a>) { (Control::new(runner), Driver::new(runner)) } - -fn slice8_mut(x: &mut [u32]) -> &mut [u8] { - let len = x.len() * 4; - unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } -} diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index 9edcff9b..779712cd 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -10,9 +10,9 @@ use crate::sub::mac::Mac; pub struct Runner<'a> { mac_subsystem: Mac, - pub(crate) rx_channel: Channel, - pub(crate) tx_channel: Channel, - pub(crate) tx_buf_channel: Channel, + pub(crate) rx_channel: Channel, 1>, + pub(crate) tx_channel: Channel, + pub(crate) tx_buf_channel: Channel, } impl<'a> Runner<'a> { @@ -31,15 +31,14 @@ impl<'a> Runner<'a> { this } - pub async fn run(&self) -> ! { + pub async fn run(&'a self) -> ! { join::join( async { loop { - let event = self.mac_subsystem.read().await; - if let Ok(evt) = event.mac_event() { - match evt { + if let Ok(mac_event) = self.mac_subsystem.read().await { + match *mac_event { MacEvent::McpsDataInd(_) => { - self.rx_channel.send(event).await; + self.rx_channel.send(mac_event).await; } _ => {} } @@ -48,7 +47,7 @@ impl<'a> Runner<'a> { }, async { loop { - let buf = self.tx_channel.recv().await; + let (buf, len) = self.tx_channel.recv().await; self.mac_subsystem .send_command( @@ -63,7 +62,7 @@ impl<'a> Runner<'a> { security_level: SecurityLevel::Unsecure, ..Default::default() } - .set_buffer(&buf), + .set_buffer(&buf[..len]), ) .await .unwrap(); diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index d9bf4c90..5ecbfe8c 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -94,7 +94,7 @@ impl Mac { } } - pub async fn read(&self) -> Event { + pub async fn read(&self) -> Result, ()> { Event::new(self.tl_read().await) } } diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index bc71e29a..7de30778 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -74,8 +74,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("setting extended address"); @@ -88,8 +88,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("setting short address"); @@ -102,8 +102,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("setting association permit"); @@ -116,8 +116,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("setting TX power"); @@ -130,8 +130,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("starting FFD device"); @@ -148,8 +148,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("setting RX on when idle"); @@ -162,17 +162,17 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } loop { let evt = mbox.mac_subsystem.read().await; - if let Ok(evt) = evt.mac_event() { + if let Ok(evt) = evt { defmt::info!("parsed mac event"); - defmt::info!("{:#x}", evt); + defmt::info!("{:#x}", *evt); - match evt { + match *evt { MacEvent::MlmeAssociateInd(association) => mbox .mac_subsystem .send_command(&AssociateResponse { diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs index 7b8c4b9d..16c33e14 100644 --- a/examples/stm32wb/src/bin/mac_ffd_net.rs +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -80,8 +80,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("setting extended address"); @@ -94,8 +94,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("setting short address"); @@ -108,8 +108,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("setting association permit"); @@ -122,8 +122,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("setting TX power"); @@ -136,8 +136,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("starting FFD device"); @@ -154,8 +154,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("setting RX on when idle"); @@ -168,8 +168,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } let tx_queue = [ diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index 7cb401d8..d1307a84 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -76,8 +76,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("setting extended address"); @@ -90,8 +90,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - defmt::info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + defmt::info!("{:#x}", *evt); } info!("getting extended address"); @@ -104,10 +104,10 @@ async fn main(spawner: Spawner) { .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", *evt); - if let Ok(MacEvent::MlmeGetCnf(evt)) = evt.mac_event() { + if let MacEvent::MlmeGetCnf(evt) = *evt { if evt.pib_attribute_value_len == 8 { let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; @@ -132,10 +132,10 @@ async fn main(spawner: Spawner) { info!("{}", a); mbox.mac_subsystem.send_command(&a).await.unwrap(); let short_addr = { - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", *evt); - if let Ok(MacEvent::MlmeAssociateCnf(conf)) = evt.mac_event() { + if let MacEvent::MlmeAssociateCnf(conf) = *evt { conf.assoc_short_address } else { defmt::panic!() @@ -151,8 +151,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", *evt); } info!("sending data"); @@ -175,12 +175,14 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", *evt); } loop { - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt.mac_event()); + match mbox.mac_subsystem.read().await { + Ok(evt) => info!("{:#x}", *evt), + _ => continue, + }; } } diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs index d64a5ef8..2b0d67bb 100644 --- a/tests/stm32/src/bin/wpan_mac.rs +++ b/tests/stm32/src/bin/wpan_mac.rs @@ -56,8 +56,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", *evt); } info!("setting extended address"); @@ -70,8 +70,8 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt.mac_event()); + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", *evt); } info!("getting extended address"); @@ -82,11 +82,12 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt.mac_event()); - if let Ok(MacEvent::MlmeGetCnf(evt)) = evt.mac_event() { + { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", *evt); + + if let MacEvent::MlmeGetCnf(evt) = *evt { if evt.pib_attribute_value_len == 8 { let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; @@ -110,10 +111,18 @@ async fn main(spawner: Spawner) { }; info!("{}", a); mbox.mac_subsystem.send_command(&a).await.unwrap(); - { - let evt = mbox.mac_subsystem.read().await; - info!("{:#x}", evt.mac_event()); - } + let short_addr = { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", *evt); + + if let MacEvent::MlmeAssociateCnf(conf) = *evt { + conf.assoc_short_address + } else { + defmt::panic!() + } + }; + + _ = short_addr; info!("Test OK"); cortex_m::asm::bkpt(); From 02d57afd51451fe9e7d224a0ea665a665ba2b72f Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 19 Jul 2023 17:52:07 -0500 Subject: [PATCH 052/298] rustfmt --- examples/stm32wb/src/bin/mac_ffd_net.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs index 16c33e14..a55b1fc7 100644 --- a/examples/stm32wb/src/bin/mac_ffd_net.rs +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -6,8 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; -use embassy_stm32_wpan::mac::commands::{AssociateResponse, ResetRequest, SetRequest, StartRequest}; -use embassy_stm32_wpan::mac::typedefs::{MacChannel, MacStatus, PanId, PibId, SecurityLevel}; +use embassy_stm32_wpan::mac::commands::{ResetRequest, SetRequest, StartRequest}; +use embassy_stm32_wpan::mac::typedefs::{MacChannel, PanId, PibId}; use embassy_stm32_wpan::mac::{self, Runner}; use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; @@ -185,4 +185,7 @@ async fn main(spawner: Spawner) { spawner.spawn(run_mac(runner)).unwrap(); let (driver, control) = mac::new(runner).await; + + let _ = driver; + let _ = control; } From a3d4ae85b021b888a78ef2214a7ecae0a49741c4 Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 20 Jul 2023 15:48:59 +0200 Subject: [PATCH 053/298] rp: disable adc hardware on Adc drop the adc constantly pulls a small but significant amount of current while the hardware is enabled. this can have quite an effect on sleeping devices that also use the adc. --- embassy-rp/src/adc.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index dfa1b877..95780c06 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -81,6 +81,16 @@ pub struct Adc<'d, M: Mode> { phantom: PhantomData<(&'d ADC, M)>, } +impl<'d, M: Mode> Drop for Adc<'d, M> { + fn drop(&mut self) { + let r = Self::regs(); + // disable ADC. leaving it enabled comes with a ~150µA static + // current draw. the temperature sensor has already been disabled + // by the temperature-reading methods, so we don't need to touch that. + r.cs().write(|w| w.set_en(false)); + } +} + impl<'d, M: Mode> Adc<'d, M> { #[inline] fn regs() -> pac::adc::Adc { From 4d6b3c57b1a0b99409cae743a102408f23ca828b Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 20 Jul 2023 16:08:59 +0200 Subject: [PATCH 054/298] 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 468e8470..89a2680a 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 809d3476aac88ddba01cd16f0df3565e35beddea Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 20 Jul 2023 16:45:04 -0500 Subject: [PATCH 055/298] wpan: further optimize mac event --- embassy-stm32-wpan/src/mac/driver.rs | 6 +- embassy-stm32-wpan/src/mac/event.rs | 83 ++++++++++++------------- embassy-stm32-wpan/src/mac/runner.rs | 6 +- embassy-stm32-wpan/src/sub/mac.rs | 10 +-- examples/stm32wb/src/bin/mac_ffd.rs | 39 +++--------- examples/stm32wb/src/bin/mac_ffd_net.rs | 35 +++-------- examples/stm32wb/src/bin/mac_rfd.rs | 24 +++---- tests/stm32/src/bin/wpan_mac.rs | 23 +++---- 8 files changed, 86 insertions(+), 140 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 3017808f..fffbb9ed 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -7,7 +7,7 @@ use embassy_net_driver::{Capabilities, LinkState, Medium}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; -use crate::mac::event::{Event, MacEvent}; +use crate::mac::event::MacEvent; use crate::mac::runner::Runner; use crate::mac::MTU; @@ -81,7 +81,7 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { } pub struct RxToken<'d> { - rx: &'d Channel, 1>, + rx: &'d Channel, 1>, } impl<'d> embassy_net_driver::RxToken for RxToken<'d> { @@ -91,7 +91,7 @@ impl<'d> embassy_net_driver::RxToken for RxToken<'d> { { // Only valid data events should be put into the queue - let data_event = match *self.rx.try_recv().unwrap() { + let data_event = match self.rx.try_recv().unwrap() { MacEvent::McpsDataInd(data_event) => data_event, _ => unreachable!(), }; diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index d975c5bd..b6f57fda 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -1,4 +1,4 @@ -use core::{mem, ops}; +use core::{mem, ptr}; use super::indications::{ AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication, @@ -8,9 +8,9 @@ use super::responses::{ AssociateConfirm, CalibrateConfirm, DataConfirm, DisassociateConfirm, DpsConfirm, GetConfirm, GtsConfirm, PollConfirm, PurgeConfirm, ResetConfirm, RxEnableConfirm, ScanConfirm, SetConfirm, SoundingConfirm, StartConfirm, }; -use crate::evt::EvtBox; +use crate::evt::{EvtBox, MemoryManager}; use crate::mac::opcodes::OpcodeM0ToM4; -use crate::sub::mac::Mac; +use crate::sub::mac::{self, Mac}; pub(crate) trait ParseableMacEvent: Sized { fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> { @@ -22,13 +22,36 @@ pub(crate) trait ParseableMacEvent: Sized { } } -pub struct Event<'a> { - #[allow(dead_code)] - event_box: EvtBox, - mac_event: MacEvent<'a>, +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum MacEvent<'a> { + MlmeAssociateCnf(&'a AssociateConfirm), + MlmeDisassociateCnf(&'a DisassociateConfirm), + MlmeGetCnf(&'a GetConfirm), + MlmeGtsCnf(&'a GtsConfirm), + MlmeResetCnf(&'a ResetConfirm), + MlmeRxEnableCnf(&'a RxEnableConfirm), + MlmeScanCnf(&'a ScanConfirm), + MlmeSetCnf(&'a SetConfirm), + MlmeStartCnf(&'a StartConfirm), + MlmePollCnf(&'a PollConfirm), + MlmeDpsCnf(&'a DpsConfirm), + MlmeSoundingCnf(&'a SoundingConfirm), + MlmeCalibrateCnf(&'a CalibrateConfirm), + McpsDataCnf(&'a DataConfirm), + McpsPurgeCnf(&'a PurgeConfirm), + MlmeAssociateInd(&'a AssociateIndication), + MlmeDisassociateInd(&'a DisassociateIndication), + MlmeBeaconNotifyInd(&'a BeaconNotifyIndication), + MlmeCommStatusInd(&'a CommStatusIndication), + MlmeGtsInd(&'a GtsIndication), + MlmeOrphanInd(&'a OrphanIndication), + MlmeSyncLossInd(&'a SyncLossIndication), + MlmeDpsInd(&'a DpsIndication), + McpsDataInd(&'a DataIndication), + MlmePollInd(&'a PollIndication), } -impl<'a> Event<'a> { +impl<'a> MacEvent<'a> { pub(crate) fn new(event_box: EvtBox) -> Result { let payload = event_box.payload(); let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap()); @@ -111,43 +134,17 @@ impl<'a> Event<'a> { } }; - Ok(Self { event_box, mac_event }) + // Forget the event box so that drop isn't called + // We want to handle the lifetime ourselves + + mem::forget(event_box); + + Ok(mac_event) } } -impl<'a> ops::Deref for Event<'a> { - type Target = MacEvent<'a>; - - fn deref(&self) -> &Self::Target { - &self.mac_event +impl<'a> Drop for MacEvent<'a> { + fn drop(&mut self) { + unsafe { mac::Mac::drop_event_packet(ptr::null_mut()) }; } } - -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum MacEvent<'a> { - MlmeAssociateCnf(&'a AssociateConfirm), - MlmeDisassociateCnf(&'a DisassociateConfirm), - MlmeGetCnf(&'a GetConfirm), - MlmeGtsCnf(&'a GtsConfirm), - MlmeResetCnf(&'a ResetConfirm), - MlmeRxEnableCnf(&'a RxEnableConfirm), - MlmeScanCnf(&'a ScanConfirm), - MlmeSetCnf(&'a SetConfirm), - MlmeStartCnf(&'a StartConfirm), - MlmePollCnf(&'a PollConfirm), - MlmeDpsCnf(&'a DpsConfirm), - MlmeSoundingCnf(&'a SoundingConfirm), - MlmeCalibrateCnf(&'a CalibrateConfirm), - McpsDataCnf(&'a DataConfirm), - McpsPurgeCnf(&'a PurgeConfirm), - MlmeAssociateInd(&'a AssociateIndication), - MlmeDisassociateInd(&'a DisassociateIndication), - MlmeBeaconNotifyInd(&'a BeaconNotifyIndication), - MlmeCommStatusInd(&'a CommStatusIndication), - MlmeGtsInd(&'a GtsIndication), - MlmeOrphanInd(&'a OrphanIndication), - MlmeSyncLossInd(&'a SyncLossIndication), - MlmeDpsInd(&'a DpsIndication), - McpsDataInd(&'a DataIndication), - MlmePollInd(&'a PollIndication), -} diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index 779712cd..a0090012 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -3,14 +3,14 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use crate::mac::commands::DataRequest; -use crate::mac::event::{Event, MacEvent}; +use crate::mac::event::MacEvent; use crate::mac::typedefs::{AddressMode, MacAddress, PanId, SecurityLevel}; use crate::mac::MTU; use crate::sub::mac::Mac; pub struct Runner<'a> { mac_subsystem: Mac, - pub(crate) rx_channel: Channel, 1>, + pub(crate) rx_channel: Channel, 1>, pub(crate) tx_channel: Channel, pub(crate) tx_buf_channel: Channel, } @@ -36,7 +36,7 @@ impl<'a> Runner<'a> { async { loop { if let Ok(mac_event) = self.mac_subsystem.read().await { - match *mac_event { + match mac_event { MacEvent::McpsDataInd(_) => { self.rx_channel.send(mac_event).await; } diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index 5ecbfe8c..b0cf0248 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -12,7 +12,7 @@ use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{EvtBox, EvtPacket}; use crate::mac::commands::MacCommand; -use crate::mac::event::Event; +use crate::mac::event::MacEvent; use crate::mac::typedefs::MacError; use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; use crate::{channels, evt}; @@ -94,14 +94,16 @@ impl Mac { } } - pub async fn read(&self) -> Result, ()> { - Event::new(self.tl_read().await) + pub async fn read(&self) -> Result, ()> { + MacEvent::new(self.tl_read().await) } } impl evt::MemoryManager for Mac { /// SAFETY: passing a pointer to something other than a managed event packet is UB unsafe fn drop_event_packet(_: *mut EvtPacket) { + trace!("mac drop event"); + // Write the ack CmdPacket::write_into( MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _, @@ -111,7 +113,7 @@ impl evt::MemoryManager for Mac { ); // Clear the rx flag - let _ = poll_once(Ipcc::receive::( + let _ = poll_once(Ipcc::receive::<()>( channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, || None, )); diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 7de30778..1379ac6b 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -73,10 +73,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting extended address"); let extended_address: u64 = 0xACDE480000000001; @@ -87,10 +84,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting short address"); let short_address: u16 = 0x1122; @@ -101,10 +95,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting association permit"); let association_permit: bool = true; @@ -115,10 +106,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting TX power"); let transmit_power: i8 = 2; @@ -129,10 +117,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("starting FFD device"); mbox.mac_subsystem @@ -147,10 +132,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting RX on when idle"); let rx_on_while_idle: bool = true; @@ -161,18 +143,15 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); loop { let evt = mbox.mac_subsystem.read().await; if let Ok(evt) = evt { defmt::info!("parsed mac event"); - defmt::info!("{:#x}", *evt); + defmt::info!("{:#x}", evt); - match *evt { + match evt { MacEvent::MlmeAssociateInd(association) => mbox .mac_subsystem .send_command(&AssociateResponse { diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs index a55b1fc7..bbcd0a70 100644 --- a/examples/stm32wb/src/bin/mac_ffd_net.rs +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -79,10 +79,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting extended address"); let extended_address: u64 = 0xACDE480000000001; @@ -93,10 +90,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting short address"); let short_address: u16 = 0x1122; @@ -107,10 +101,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting association permit"); let association_permit: bool = true; @@ -121,10 +112,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting TX power"); let transmit_power: i8 = 2; @@ -135,10 +123,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("starting FFD device"); mbox.mac_subsystem @@ -153,10 +138,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting RX on when idle"); let rx_on_while_idle: bool = true; @@ -167,10 +149,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); let tx_queue = [ make_static!([0u8; 127]), diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index d1307a84..4d8b6601 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -75,10 +75,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("setting extended address"); let extended_address: u64 = 0xACDE480000000002; @@ -89,10 +86,7 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - { - let evt = mbox.mac_subsystem.read().await.unwrap(); - defmt::info!("{:#x}", *evt); - } + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); info!("getting extended address"); mbox.mac_subsystem @@ -105,9 +99,9 @@ async fn main(spawner: Spawner) { { let evt = mbox.mac_subsystem.read().await.unwrap(); - info!("{:#x}", *evt); + info!("{:#x}", evt); - if let MacEvent::MlmeGetCnf(evt) = *evt { + if let MacEvent::MlmeGetCnf(evt) = evt { if evt.pib_attribute_value_len == 8 { let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; @@ -133,9 +127,9 @@ async fn main(spawner: Spawner) { mbox.mac_subsystem.send_command(&a).await.unwrap(); let short_addr = { let evt = mbox.mac_subsystem.read().await.unwrap(); - info!("{:#x}", *evt); + info!("{:#x}", evt); - if let MacEvent::MlmeAssociateCnf(conf) = *evt { + if let MacEvent::MlmeAssociateCnf(conf) = evt { conf.assoc_short_address } else { defmt::panic!() @@ -152,7 +146,7 @@ async fn main(spawner: Spawner) { .unwrap(); { let evt = mbox.mac_subsystem.read().await.unwrap(); - info!("{:#x}", *evt); + info!("{:#x}", evt); } info!("sending data"); @@ -176,12 +170,12 @@ async fn main(spawner: Spawner) { .unwrap(); { let evt = mbox.mac_subsystem.read().await.unwrap(); - info!("{:#x}", *evt); + info!("{:#x}", evt); } loop { match mbox.mac_subsystem.read().await { - Ok(evt) => info!("{:#x}", *evt), + Ok(evt) => info!("{:#x}", evt), _ => continue, }; } diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs index 2b0d67bb..b04a19ee 100644 --- a/tests/stm32/src/bin/wpan_mac.rs +++ b/tests/stm32/src/bin/wpan_mac.rs @@ -57,7 +57,7 @@ async fn main(spawner: Spawner) { .unwrap(); { let evt = mbox.mac_subsystem.read().await.unwrap(); - info!("{:#x}", *evt); + info!("{:#x}", evt); } info!("setting extended address"); @@ -71,7 +71,7 @@ async fn main(spawner: Spawner) { .unwrap(); { let evt = mbox.mac_subsystem.read().await.unwrap(); - info!("{:#x}", *evt); + info!("{:#x}", evt); } info!("getting extended address"); @@ -85,9 +85,9 @@ async fn main(spawner: Spawner) { { let evt = mbox.mac_subsystem.read().await.unwrap(); - info!("{:#x}", *evt); + info!("{:#x}", evt); - if let MacEvent::MlmeGetCnf(evt) = *evt { + if let MacEvent::MlmeGetCnf(evt) = evt { if evt.pib_attribute_value_len == 8 { let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; @@ -111,18 +111,13 @@ async fn main(spawner: Spawner) { }; info!("{}", a); mbox.mac_subsystem.send_command(&a).await.unwrap(); - let short_addr = { - let evt = mbox.mac_subsystem.read().await.unwrap(); - info!("{:#x}", *evt); - - if let MacEvent::MlmeAssociateCnf(conf) = *evt { - conf.assoc_short_address - } else { - defmt::panic!() - } + let short_addr = if let MacEvent::MlmeAssociateCnf(conf) = mbox.mac_subsystem.read().await.unwrap() { + conf.assoc_short_address + } else { + defmt::panic!() }; - _ = short_addr; + info!("{}", short_addr); info!("Test OK"); cortex_m::asm::bkpt(); From 83ff626c4745d73def4e0cf88ea64c21a19d6d1f Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 20 Jul 2023 17:00:03 -0500 Subject: [PATCH 056/298] wpan/mac: incr. runner msdu handle --- embassy-stm32-wpan/src/mac/runner.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index a0090012..007544c6 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -46,6 +46,8 @@ impl<'a> Runner<'a> { } }, async { + let mut msdu_handle = 0x02; + loop { let (buf, len) = self.tx_channel.recv().await; @@ -56,7 +58,7 @@ impl<'a> Runner<'a> { dst_addr_mode: AddressMode::Short, dst_pan_id: PanId([0x1A, 0xAA]), dst_address: MacAddress::BROADCAST, - msdu_handle: 0x02, + msdu_handle: msdu_handle, ack_tx: 0x00, gts_tx: false, security_level: SecurityLevel::Unsecure, @@ -67,6 +69,8 @@ impl<'a> Runner<'a> { .await .unwrap(); + msdu_handle += 1; + // The tx channel should always be of equal capacity to the tx_buf channel self.tx_buf_channel.try_send(buf).unwrap(); } From c80c232a72a561c3d89c8292437996bc07bfb689 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 20 Jul 2023 19:52:36 -0500 Subject: [PATCH 057/298] wpan: impl. debug for structs --- embassy-stm32-wpan/src/mac/event.rs | 1 + embassy-stm32-wpan/src/mac/indications.rs | 10 +++++++ embassy-stm32-wpan/src/mac/responses.rs | 15 +++++++++++ embassy-stm32-wpan/src/mac/typedefs.rs | 32 +++++++++++++++++------ 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index b6f57fda..8415bc11 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -23,6 +23,7 @@ pub(crate) trait ParseableMacEvent: Sized { } #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug)] pub enum MacEvent<'a> { MlmeAssociateCnf(&'a AssociateConfirm), MlmeDisassociateCnf(&'a DisassociateConfirm), diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index 4349af9c..c0b86d74 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -10,6 +10,7 @@ use super::typedefs::{ /// MLME ASSOCIATE Indication which will be used by the MAC /// to indicate the reception of an association request command #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateIndication { /// Extended address of the device requesting association @@ -31,6 +32,7 @@ impl ParseableMacEvent for AssociateIndication {} /// MLME DISASSOCIATE indication which will be used to send /// disassociation indication to the application. #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DisassociateIndication { /// Extended address of the device requesting association @@ -52,6 +54,7 @@ impl ParseableMacEvent for DisassociateIndication {} /// MLME BEACON NOTIIFY Indication which is used to send parameters contained /// within a beacon frame received by the MAC to the application #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct BeaconNotifyIndication { /// he set of octets comprising the beacon payload to be transferred @@ -73,6 +76,7 @@ impl ParseableMacEvent for BeaconNotifyIndication {} /// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CommStatusIndication { /// The 16-bit PAN identifier of the device from which the frame @@ -103,6 +107,7 @@ impl ParseableMacEvent for CommStatusIndication {} /// MLME GTS Indication indicates that a GTS has been allocated or that a /// previously allocated GTS has been deallocated #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsIndication { /// The short address of the device that has been allocated or deallocated a GTS @@ -126,6 +131,7 @@ impl ParseableMacEvent for GtsIndication {} /// MLME ORPHAN Indication which is used by the coordinator to notify the /// application of the presence of an orphaned device #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OrphanIndication { /// Extended address of the orphaned device @@ -147,6 +153,7 @@ impl ParseableMacEvent for OrphanIndication {} /// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss /// of synchronization with the coordinator #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SyncLossIndication { /// The PAN identifier with which the device lost synchronization or to which it was realigned @@ -172,6 +179,7 @@ impl ParseableMacEvent for SyncLossIndication {} /// MLME DPS Indication which indicates the expiration of the DPSIndexDuration /// and the resetting of the DPS values in the PHY #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DpsIndication { /// byte stuffing to keep 32 bit alignment @@ -181,6 +189,7 @@ pub struct DpsIndication { impl ParseableMacEvent for DpsIndication {} #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataIndication { /// Pointer to the set of octets forming the MSDU being indicated @@ -244,6 +253,7 @@ impl DataIndication { /// MLME POLL Indication which will be used for indicating the Data Request /// reception to upper layer as defined in Zigbee r22 - D.8.2 #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollIndication { /// addressing mode used diff --git a/embassy-stm32-wpan/src/mac/responses.rs b/embassy-stm32-wpan/src/mac/responses.rs index 5d203084..9c407a36 100644 --- a/embassy-stm32-wpan/src/mac/responses.rs +++ b/embassy-stm32-wpan/src/mac/responses.rs @@ -8,6 +8,7 @@ use super::typedefs::{ /// MLME ASSOCIATE Confirm used to inform of the initiating device whether /// its request to associate was successful or unsuccessful #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AssociateConfirm { /// short address allocated by the coordinator on successful association @@ -30,6 +31,7 @@ impl ParseableMacEvent for AssociateConfirm {} /// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application. #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DisassociateConfirm { /// status of the disassociation attempt @@ -46,6 +48,7 @@ impl ParseableMacEvent for DisassociateConfirm {} /// MLME GET Confirm which requests information about a given PIB attribute #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GetConfirm { /// The pointer to the value of the PIB attribute attempted to read @@ -65,6 +68,7 @@ impl ParseableMacEvent for GetConfirm {} /// MLME GTS Confirm which eports the results of a request to allocate a new GTS /// or to deallocate an existing GTS #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct GtsConfirm { /// The characteristics of the GTS @@ -79,6 +83,7 @@ impl ParseableMacEvent for GtsConfirm {} /// MLME RESET Confirm which is used to report the results of the reset operation #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetConfirm { /// The result of the reset operation @@ -92,6 +97,7 @@ impl ParseableMacEvent for ResetConfirm {} /// MLME RX ENABLE Confirm which is used to report the results of the attempt /// to enable or disable the receiver #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RxEnableConfirm { /// Result of the request to enable or disable the receiver @@ -104,6 +110,7 @@ impl ParseableMacEvent for RxEnableConfirm {} /// MLME SCAN Confirm which is used to report the result of the channel scan request #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ScanConfirm { /// Status of the scan request @@ -130,6 +137,7 @@ impl ParseableMacEvent for ScanConfirm {} /// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SetConfirm { /// The result of the set operation @@ -145,6 +153,7 @@ impl ParseableMacEvent for SetConfirm {} /// MLME START Confirm which is used to report the results of the attempt to /// start using a new superframe configuration #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct StartConfirm { /// Result of the attempt to start using an updated superframe configuration @@ -157,6 +166,7 @@ impl ParseableMacEvent for StartConfirm {} /// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PollConfirm { /// The status of the data request @@ -169,6 +179,7 @@ impl ParseableMacEvent for PollConfirm {} /// MLME DPS Confirm which reports the results of the attempt to enable or disable the DPS #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DpsConfirm { /// The status of the DPS request @@ -182,6 +193,7 @@ impl ParseableMacEvent for DpsConfirm {} /// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide /// channel sounding information #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SoundingConfirm { /// Results of the sounding measurement @@ -195,6 +207,7 @@ impl ParseableMacEvent for SoundingConfirm {} /// MLME CALIBRATE Confirm which reports the result of a request to the PHY /// to provide internal propagation path information #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct CalibrateConfirm { /// The status of the attempt to return sounding data @@ -214,6 +227,7 @@ impl ParseableMacEvent for CalibrateConfirm {} /// MCPS DATA Confirm which will be used for reporting the results of /// MAC data related requests from the application #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DataConfirm { /// The handle associated with the MSDU being confirmed @@ -245,6 +259,7 @@ impl ParseableMacEvent for DataConfirm {} /// MCPS PURGE Confirm which will be used by the MAC to notify the application of /// the status of its request to purge an MSDU from the transaction queue #[repr(C)] +#[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PurgeConfirm { /// Handle associated with the MSDU requested to be purged from the transaction queue diff --git a/embassy-stm32-wpan/src/mac/typedefs.rs b/embassy-stm32-wpan/src/mac/typedefs.rs index 98c67c86..0552b8ea 100644 --- a/embassy-stm32-wpan/src/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/mac/typedefs.rs @@ -1,3 +1,5 @@ +use core::fmt::Debug; + use crate::numeric_enum; #[derive(Debug)] @@ -37,7 +39,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] /// this enum contains all the MAC PIB Ids - #[derive(Default)] + #[derive(Default, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum PibId { // PHY @@ -96,7 +98,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] - #[derive(Default, Clone, Copy)] + #[derive(Default, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AddressMode { #[default] @@ -113,6 +115,18 @@ pub union MacAddress { pub extended: [u8; 8], } +impl Debug for MacAddress { + fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + unsafe { + write!( + fmt, + "MacAddress {{ short: {:?}, extended: {:?} }}", + self.short, self.extended + ) + } + } +} + #[cfg(feature = "defmt")] impl defmt::Format for MacAddress { fn format(&self, fmt: defmt::Formatter) { @@ -159,7 +173,7 @@ pub struct GtsCharacteristics { /// MAC PAN Descriptor which contains the network details of the device from /// which the beacon is received -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PanDescriptor { /// PAN identifier of the coordinator @@ -223,7 +237,7 @@ impl TryFrom<&[u8]> for PanDescriptor { numeric_enum! { #[repr(u8)] - #[derive(Default, Clone, Copy)] + #[derive(Default, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// Building wireless applications with STM32WB series MCUs - Application note 13.10.3 pub enum MacChannel { @@ -289,7 +303,7 @@ defmt::bitflags! { numeric_enum! { #[repr(u8)] - #[derive(Default, Clone, Copy)] + #[derive(Default, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum KeyIdMode { #[default] @@ -306,6 +320,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AssociationStatus { /// Association successful @@ -319,7 +334,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] - #[derive(Clone, Copy)] + #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum DisassociationReason { /// The coordinator wishes the device to leave the PAN. @@ -331,7 +346,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] - #[derive(Default, Clone, Copy)] + #[derive(Default, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SecurityLevel { /// MAC Unsecured Mode Security @@ -346,6 +361,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ScanType { EdScan = 0x00, @@ -356,7 +372,7 @@ numeric_enum! { } /// newtype for Pan Id -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PanId(pub [u8; 2]); From 899a68325c322a813155c7ba9c6e43c79be99bd8 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 20 Jul 2023 20:51:49 -0500 Subject: [PATCH 058/298] wpan: impl. draft control scheme --- embassy-stm32-wpan/src/mac/control.rs | 86 ++++++++++++++++++++++++--- embassy-stm32-wpan/src/mac/event.rs | 2 + embassy-stm32-wpan/src/mac/mod.rs | 2 +- embassy-stm32-wpan/src/mac/runner.rs | 31 +++++++++- 4 files changed, 109 insertions(+), 12 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs index c45f6407..fd8c22b2 100644 --- a/embassy-stm32-wpan/src/mac/control.rs +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -1,12 +1,16 @@ +use core::future::Future; +use core::task; + +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::mutex::MutexGuard; +use embassy_sync::signal::Signal; +use futures::FutureExt; + +use super::commands::MacCommand; +use super::typedefs::MacError; use crate::mac::runner::Runner; -#[derive(Debug)] -pub struct Error { - pub status: u32, -} - pub struct Control<'a> { - #[allow(dead_code)] runner: &'a Runner<'a>, } @@ -15,7 +19,73 @@ impl<'a> Control<'a> { Self { runner: runner } } - pub async fn init(&mut self) { - // TODO + pub async fn send_command(&self, cmd: &T) -> Result<(), MacError> + where + T: MacCommand, + { + let _wm = self.runner.write_mutex.lock().await; + + self.runner.mac_subsystem.send_command(cmd).await + } + + pub async fn send_command_and_get_response(&self, cmd: &T) -> Result, MacError> + where + T: MacCommand, + { + let _wm = self.runner.write_mutex.lock().await; + let rm = self.runner.read_mutex.lock().await; + let token = EventToken::new(self.runner, rm); + + self.runner.mac_subsystem.send_command(cmd).await?; + + Ok(token) + } +} + +pub struct EventToken<'a> { + runner: &'a Runner<'a>, + _mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>, +} + +impl<'a> EventToken<'a> { + pub(crate) fn new(runner: &'a Runner<'a>, mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>) -> Self { + // Enable event receiving + runner.rx_event_channel.lock(|s| { + *s.borrow_mut() = Some(Signal::new()); + }); + + Self { + runner: runner, + _mutex_guard: mutex_guard, + } + } +} + +impl<'a> Future for EventToken<'a> { + // TODO: output something + type Output = (); + + fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + self.get_mut().runner.rx_event_channel.lock(|s| { + let signal = s.borrow_mut(); + let signal = match &*signal { + Some(s) => s, + _ => unreachable!(), + }; + + let _ = signal.wait().poll_unpin(cx); + }); + + todo!() + } +} + +impl<'a> Drop for EventToken<'a> { + fn drop(&mut self) { + // Disable event receiving + // This will also drop the contained event, if it exists, and will free up receiving the next event + self.runner.rx_event_channel.lock(|s| { + *s.borrow_mut() = None; + }); } } diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index 8415bc11..9ca4f5a2 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -144,6 +144,8 @@ impl<'a> MacEvent<'a> { } } +unsafe impl<'a> Send for MacEvent<'a> {} + impl<'a> Drop for MacEvent<'a> { fn drop(&mut self) { unsafe { mac::Mac::drop_event_packet(ptr::null_mut()) }; diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index a93f7a69..c847a5cc 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -10,7 +10,7 @@ pub mod responses; pub mod runner; pub mod typedefs; -pub use crate::mac::control::{Control, Error as ControlError}; +pub use crate::mac::control::Control; use crate::mac::driver::Driver; pub use crate::mac::runner::Runner; diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index 007544c6..f964d6b3 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -1,6 +1,11 @@ +use core::cell::RefCell; + use embassy_futures::join; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::blocking_mutex; +use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; use embassy_sync::channel::Channel; +use embassy_sync::mutex::Mutex; +use embassy_sync::signal::Signal; use crate::mac::commands::DataRequest; use crate::mac::event::MacEvent; @@ -9,7 +14,13 @@ use crate::mac::MTU; use crate::sub::mac::Mac; pub struct Runner<'a> { - mac_subsystem: Mac, + pub(crate) mac_subsystem: Mac, + + // rx event backpressure is already provided through the MacEvent drop mechanism + pub(crate) rx_event_channel: + blocking_mutex::Mutex>>>>, + pub(crate) read_mutex: Mutex, + pub(crate) write_mutex: Mutex, pub(crate) rx_channel: Channel, 1>, pub(crate) tx_channel: Channel, pub(crate) tx_buf_channel: Channel, @@ -19,6 +30,9 @@ impl<'a> Runner<'a> { pub fn new(mac: Mac, tx_buf_queue: [&'a mut [u8; MTU]; 5]) -> Self { let this = Self { mac_subsystem: mac, + rx_event_channel: blocking_mutex::Mutex::new(RefCell::new(None)), + read_mutex: Mutex::new(()), + write_mutex: Mutex::new(()), rx_channel: Channel::new(), tx_channel: Channel::new(), tx_buf_channel: Channel::new(), @@ -40,7 +54,16 @@ impl<'a> Runner<'a> { MacEvent::McpsDataInd(_) => { self.rx_channel.send(mac_event).await; } - _ => {} + _ => { + self.rx_event_channel.lock(|s| { + match &*s.borrow() { + Some(signal) => { + signal.signal(mac_event); + } + None => {} + }; + }); + } } } } @@ -50,7 +73,9 @@ impl<'a> Runner<'a> { loop { let (buf, len) = self.tx_channel.recv().await; + let _wm = self.write_mutex.lock().await; + // The mutex should be dropped on the next loop iteration self.mac_subsystem .send_command( DataRequest { From e9445ec72d8713aa7705e0a727c13c259d529e71 Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 20 Jul 2023 16:57:54 +0200 Subject: [PATCH 059/298] 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 4f205a16..50f028d4 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 89a2680a..91576180 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 c675208b8a90bee39e99c8cd3bb620b99c439482 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 21 Jul 2023 16:10:34 -0500 Subject: [PATCH 060/298] wpan: complete prelim. command impl. --- embassy-stm32-wpan/src/mac/control.rs | 16 ++++++++++------ embassy-stm32-wpan/src/mac/runner.rs | 7 ++++--- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs index fd8c22b2..ded41920 100644 --- a/embassy-stm32-wpan/src/mac/control.rs +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -1,5 +1,6 @@ use core::future::Future; use core::task; +use core::task::Poll; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::MutexGuard; @@ -7,6 +8,7 @@ use embassy_sync::signal::Signal; use futures::FutureExt; use super::commands::MacCommand; +use super::event::MacEvent; use super::typedefs::MacError; use crate::mac::runner::Runner; @@ -62,10 +64,9 @@ impl<'a> EventToken<'a> { } impl<'a> Future for EventToken<'a> { - // TODO: output something - type Output = (); + type Output = MacEvent<'a>; - fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { self.get_mut().runner.rx_event_channel.lock(|s| { let signal = s.borrow_mut(); let signal = match &*signal { @@ -73,10 +74,13 @@ impl<'a> Future for EventToken<'a> { _ => unreachable!(), }; - let _ = signal.wait().poll_unpin(cx); - }); + let result = match signal.wait().poll_unpin(cx) { + Poll::Ready(mac_event) => Poll::Ready(mac_event), + Poll::Pending => Poll::Pending, + }; - todo!() + result + }) } } diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index f964d6b3..482321b9 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -13,12 +13,13 @@ use crate::mac::typedefs::{AddressMode, MacAddress, PanId, SecurityLevel}; use crate::mac::MTU; use crate::sub::mac::Mac; +type ZeroCopyPubSub = blocking_mutex::Mutex>>>; + pub struct Runner<'a> { pub(crate) mac_subsystem: Mac, - // rx event backpressure is already provided through the MacEvent drop mechanism - pub(crate) rx_event_channel: - blocking_mutex::Mutex>>>>, + // therefore, we don't need to worry about overwriting events + pub(crate) rx_event_channel: ZeroCopyPubSub>, pub(crate) read_mutex: Mutex, pub(crate) write_mutex: Mutex, pub(crate) rx_channel: Channel, 1>, From f4d6a23f92c8f6d3eb97a09e5bc51bac5e8d6837 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 21 Jul 2023 17:02:36 -0500 Subject: [PATCH 061/298] wpan/mac: misc fixes --- embassy-stm32-wpan/src/mac/control.rs | 2 +- embassy-stm32-wpan/src/mac/responses.rs | 6 +++--- embassy-stm32-wpan/src/mac/runner.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs index ded41920..8a13de81 100644 --- a/embassy-stm32-wpan/src/mac/control.rs +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -34,8 +34,8 @@ impl<'a> Control<'a> { where T: MacCommand, { - let _wm = self.runner.write_mutex.lock().await; let rm = self.runner.read_mutex.lock().await; + let _wm = self.runner.write_mutex.lock().await; let token = EventToken::new(self.runner, rm); self.runner.mac_subsystem.send_command(cmd).await?; diff --git a/embassy-stm32-wpan/src/mac/responses.rs b/embassy-stm32-wpan/src/mac/responses.rs index 9c407a36..544fdaae 100644 --- a/embassy-stm32-wpan/src/mac/responses.rs +++ b/embassy-stm32-wpan/src/mac/responses.rs @@ -87,7 +87,7 @@ impl ParseableMacEvent for GtsConfirm {} #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ResetConfirm { /// The result of the reset operation - status: MacStatus, + pub status: MacStatus, /// byte stuffing to keep 32 bit alignment a_stuffing: [u8; 3], } @@ -101,7 +101,7 @@ impl ParseableMacEvent for ResetConfirm {} #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct RxEnableConfirm { /// Result of the request to enable or disable the receiver - status: MacStatus, + pub status: MacStatus, /// byte stuffing to keep 32 bit alignment a_stuffing: [u8; 3], } @@ -197,7 +197,7 @@ impl ParseableMacEvent for DpsConfirm {} #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SoundingConfirm { /// Results of the sounding measurement - sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED], + pub sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED], status: u8, } diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index 482321b9..1be6df8a 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -95,7 +95,7 @@ impl<'a> Runner<'a> { .await .unwrap(); - msdu_handle += 1; + msdu_handle = msdu_handle.wrapping_add(1); // The tx channel should always be of equal capacity to the tx_buf channel self.tx_buf_channel.try_send(buf).unwrap(); From c83552eadcf7546acf8bf4de47d7d9243ffe56d0 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Sat, 22 Jul 2023 12:45:18 +0100 Subject: [PATCH 062/298] 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 3a621671..aaedcfec 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 586b4154..ee078286 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 ade43eb3..0193a248 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 063/298] 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 31a2d886..3dee242e 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 064/298] 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 c1c82136..a7e60b7b 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 19c6c698b54514aff1fc2ad22953a5d63b6193ee Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 22 Jul 2023 11:43:14 -0500 Subject: [PATCH 065/298] stm32: update metapac --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ec934e8b..0fb6fdb5 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"] From 5693ed1178bf77fc131749b65d27bbaf3b3cf3fd Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 22 Jul 2023 11:50:30 -0500 Subject: [PATCH 066/298] stm32: add minimal fdcan impl --- embassy-stm32/src/can/fdcan.rs | 66 ++++++++++++++++++++++++++++++++++ embassy-stm32/src/can/mod.rs | 1 + 2 files changed, 67 insertions(+) create mode 100644 embassy-stm32/src/can/fdcan.rs diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs new file mode 100644 index 00000000..c31a7fc6 --- /dev/null +++ b/embassy-stm32/src/can/fdcan.rs @@ -0,0 +1,66 @@ +pub use bxcan; +use embassy_hal_common::PeripheralRef; + +use crate::peripherals; + +pub(crate) mod sealed { + use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; + use embassy_sync::channel::Channel; + use embassy_sync::waitqueue::AtomicWaker; + + pub struct State { + pub tx_waker: AtomicWaker, + pub err_waker: AtomicWaker, + pub rx_queue: Channel, + } + + impl State { + pub const fn new() -> Self { + Self { + tx_waker: AtomicWaker::new(), + err_waker: AtomicWaker::new(), + rx_queue: Channel::new(), + } + } + } + + pub trait Instance { + const REGISTERS: *mut bxcan::RegisterBlock; + + fn regs() -> &'static crate::pac::can::Fdcan; + fn state() -> &'static State; + } +} + +pub trait InterruptableInstance {} +pub trait Instance: sealed::Instance + InterruptableInstance + 'static {} + +pub struct BxcanInstance<'a, T>(PeripheralRef<'a, T>); + +unsafe impl<'d, T: Instance> bxcan::Instance for BxcanInstance<'d, T> { + const REGISTERS: *mut bxcan::RegisterBlock = T::REGISTERS; +} + +foreach_peripheral!( + (can, $inst:ident) => { + impl sealed::Instance for peripherals::$inst { + const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.as_ptr() as *mut _; + + fn regs() -> &'static crate::pac::can::Fdcan { + &crate::pac::$inst + } + + fn state() -> &'static sealed::State { + static STATE: sealed::State = sealed::State::new(); + &STATE + } + } + + impl Instance for peripherals::$inst {} + + impl InterruptableInstance for peripherals::$inst {} + }; +); + +pin_trait!(RxPin, Instance); +pin_trait!(TxPin, Instance); diff --git a/embassy-stm32/src/can/mod.rs b/embassy-stm32/src/can/mod.rs index c7e2e620..4ff5aa0d 100644 --- a/embassy-stm32/src/can/mod.rs +++ b/embassy-stm32/src/can/mod.rs @@ -1,5 +1,6 @@ #![macro_use] #[cfg_attr(can_bxcan, path = "bxcan.rs")] +#[cfg_attr(can_fdcan, path = "fdcan.rs")] mod _version; pub use _version::*; From 64f8a779ca4453a40f07433c0bb1ebcaeeeba74c Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 22 Jul 2023 11:54:54 -0500 Subject: [PATCH 067/298] stm32: add dac fix --- embassy-stm32/src/dac/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 31a2d886..3d58914b 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -51,7 +51,10 @@ impl Ch1Trigger { fn tsel(&self) -> dac::vals::Tsel1 { match self { Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO, + #[cfg(not(dac_v3))] Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO, + #[cfg(dac_v3)] + Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM1_TRGO, Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO, Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO, Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO, From 192cdc2f858b8c8ded7fb50338e2045e784951e3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 22 Jul 2023 12:07:02 -0500 Subject: [PATCH 068/298] stm32: suppress adc f3 --- embassy-stm32/src/adc/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 56ecd63c..94a8538b 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -1,5 +1,6 @@ #![macro_use] +#[cfg(not(adc_f3))] #[cfg_attr(adc_f1, path = "f1.rs")] #[cfg_attr(adc_v1, path = "v1.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] @@ -7,14 +8,16 @@ #[cfg_attr(adc_v4, path = "v4.rs")] mod _version; -#[cfg(not(adc_f1))] +#[cfg(not(any(adc_f1, adc_f3)))] mod resolution; mod sample_time; +#[cfg(not(adc_f3))] #[allow(unused)] pub use _version::*; -#[cfg(not(adc_f1))] +#[cfg(not(any(adc_f1, adc_f3)))] pub use resolution::Resolution; +#[cfg(not(adc_f3))] pub use sample_time::SampleTime; use crate::peripherals; @@ -22,13 +25,14 @@ use crate::peripherals; pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::PeripheralRef<'d, T>, + #[cfg(not(adc_f3))] sample_time: SampleTime, } pub(crate) mod sealed { pub trait Instance { fn regs() -> crate::pac::adc::Adc; - #[cfg(all(not(adc_f1), not(adc_v1)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3)))] fn common_regs() -> crate::pac::adccommon::AdcCommon; } @@ -56,7 +60,7 @@ foreach_peripheral!( fn regs() -> crate::pac::adc::Adc { crate::pac::$inst } - #[cfg(all(not(adc_f1), not(adc_v1)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3)))] fn common_regs() -> crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, $common_inst:ident) => { From ba8e5d8589efda450bf118051be097f320b40ff3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 22 Jul 2023 12:10:50 -0500 Subject: [PATCH 069/298] rustfmt --- embassy-stm32/src/adc/sample_time.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index 0faa1e3c..df052556 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -1,3 +1,4 @@ +#[cfg(not(adc_f3))] macro_rules! impl_sample_time { ($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] 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 070/298] 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 ec934e8b..0fb6fdb5 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 3dee242e..6712585c 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 58d438af..f1408459 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 071/298] 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 bf15eaac..979748bb 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 072/298] 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 698292bf..434c56a4 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 073/298] 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 b05e55aa..28407b8b 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 26837abe..e74c5357 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 b7b5eaa9..620a139a 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 29cad5b6..8450c541 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 652210c7..147e2ecb 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 489d0ff4..6ac5d57e 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 c1c82136..e1ef40f9 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 84d7b79c..bbc99fee 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 c88282d9..4a14568a 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 18bd03c3..935997a7 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 227bc28b..aebc795c 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 768702fa..2d82c0d0 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 747cec7b..e6a5a4c1 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 c5555851..41c9869b 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 54911482..5d66c59c 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 835e3294..a43a5590 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 7c0b83e6..48e34026 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 e2c66f45..6e6f269a 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 074/298] update readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 28407b8b..c4c01dfb 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 075/298] 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 5a87888b..68c78123 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 58d438af..9459fb76 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 a2bde986..19079397 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 076/298] 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 a24cba9f..e100ca5c 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 886fc0b9..4ae65d3e 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 e6123821..41f602c0 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 077/298] 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 5a015346..8b8244d4 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 08bed88d..f84f74d3 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 078/298] 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 00000000..13566b81 --- /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 00000000..06ff4b23 --- /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 00000000..9b4cf845 --- /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 00000000..b2bdec2d --- /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 00000000..14d05f4d --- /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 079/298] 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 41f602c0..91a11a95 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 080/298] 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 ea8e525e..3b9226fd 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 081/298] 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 91a11a95..4322b950 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 3ad8aca4..452da77a 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 b04a19ee..7eab2fd3 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 62ab0bf2e75c560aa255ab51aab1f5ebf591ba97 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Tue, 25 Jul 2023 12:07:09 +0300 Subject: [PATCH 082/298] stm32/can: implement proper RX timestamps --- embassy-stm32/src/can/bxcan.rs | 39 +++++++++++++++++++++++++-------- examples/stm32f4/src/bin/can.rs | 18 +++++++++++++-- examples/stm32f7/src/bin/can.rs | 4 ++-- tests/stm32/src/bin/can.rs | 13 ++++++++--- 4 files changed, 58 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 8b8244d4..795becab 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -16,6 +16,17 @@ use crate::rcc::RccPeripheral; use crate::time::Hertz; use crate::{interrupt, peripherals, Peripheral}; +/// Contains CAN frame and additional metadata. +/// +/// Timestamp is available if `time` feature is enabled. +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Envelope { + #[cfg(feature = "time")] + pub ts: embassy_time::Instant, + pub frame: bxcan::Frame, +} + /// Interrupt handler. pub struct TxInterruptHandler { _phantom: PhantomData, @@ -199,11 +210,11 @@ impl<'d, T: Instance> Can<'d, T> { } /// 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> { + pub async fn read(&mut self) -> Result { 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))); + if let Poll::Ready(envelope) = T::state().rx_queue.recv().poll_unpin(cx) { + return Poll::Ready(Ok(envelope)); } else if let Some(err) = self.curr_error() { return Poll::Ready(Err(err)); } @@ -228,6 +239,10 @@ impl<'d, T: Instance> Can<'d, T> { } unsafe fn receive_fifo(fifo: RxFifo) { + // Generate timestamp as early as possible + #[cfg(feature = "time")] + let ts = embassy_time::Instant::now(); + let state = T::state(); let regs = T::regs(); let fifo_idx = match fifo { @@ -257,15 +272,19 @@ impl<'d, T: Instance> Can<'d, T> { data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); - let time = fifo.rdtr().read().time(); let frame = Frame::new_data(id, Data::new(&data[0..data_len]).unwrap()); + let envelope = Envelope { + #[cfg(feature = "time")] + ts, + frame, + }; rfr.modify(|v| v.set_rfom(true)); /* NOTE: consensus was reached that if rx_queue is full, packets should be dropped */ - let _ = state.rx_queue.try_send((time, frame)); + let _ = state.rx_queue.try_send(envelope); } } @@ -405,11 +424,11 @@ pub struct CanRx<'c, 'd, T: Instance> { } impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { - pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> { + pub async fn read(&mut self) -> Result { 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))); + if let Poll::Ready(envelope) = T::state().rx_queue.recv().poll_unpin(cx) { + return Poll::Ready(Ok(envelope)); } else if let Some(err) = self.curr_error() { return Poll::Ready(Err(err)); } @@ -467,10 +486,12 @@ pub(crate) mod sealed { use embassy_sync::channel::Channel; use embassy_sync::waitqueue::AtomicWaker; + use super::Envelope; + pub struct State { pub tx_waker: AtomicWaker, pub err_waker: AtomicWaker, - pub rx_queue: Channel, + pub rx_queue: Channel, } impl State { diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs index f84f74d3..20ce4edc 100644 --- a/examples/stm32f4/src/bin/can.rs +++ b/examples/stm32f4/src/bin/can.rs @@ -10,6 +10,7 @@ use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN1; +use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -51,9 +52,22 @@ async fn main(_spawner: Spawner) { let mut i: u8 = 0; loop { let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); + let tx_ts = Instant::now(); can.write(&tx_frame).await; - let (_, rx_frame) = can.read().await.unwrap(); - info!("loopback frame {=u8}", unwrap!(rx_frame.data())[0]); + + let envelope = can.read().await.unwrap(); + + // We can measure loopback latency by using receive timestamp in the `Envelope`. + // Our frame is ~55 bits long (exlcuding bit stuffing), so at 1mbps loopback delay is at least 55 us. + // When measured with `tick-hz-1_000_000` actual latency is 80~83 us, giving a combined hardware and software + // overhead of ~25 us. Note that CPU frequency can greatly affect the result. + let latency = envelope.ts.saturating_duration_since(tx_ts); + + info!( + "loopback frame {=u8}, latency: {} us", + unwrap!(envelope.frame.data())[0], + latency.as_micros() + ); i += 1; } } diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs index 1b5b377e..e9650f23 100644 --- a/examples/stm32f7/src/bin/can.rs +++ b/examples/stm32f7/src/bin/can.rs @@ -60,7 +60,7 @@ async fn main(spawner: Spawner) { spawner.spawn(send_can_message(tx)).unwrap(); loop { - let frame = rx.read().await.unwrap(); - println!("Received: {:?}", frame); + let envelope = rx.read().await.unwrap(); + println!("Received: {:?}", envelope); } } diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index 8bdd3c24..93253ab8 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -7,6 +7,7 @@ #[path = "../common.rs"] mod common; use common::*; +use defmt::assert; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::can::bxcan::filter::Mask32; @@ -14,6 +15,7 @@ use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN1; +use embassy_time::{Duration, Instant}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -62,13 +64,18 @@ async fn main(_spawner: Spawner) { let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); info!("Transmitting frame..."); + let tx_ts = Instant::now(); can.write(&tx_frame).await; info!("Receiving frame..."); - let (time, rx_frame) = can.read().await.unwrap(); + let envelope = can.read().await.unwrap(); - info!("loopback time {}", time); - info!("loopback frame {=u8}", rx_frame.data().unwrap()[0]); + info!("loopback time {}", envelope.ts); + info!("loopback frame {=u8}", envelope.frame.data().unwrap()[0]); + + // Theoretical minimum latency is 55us, actual is usually ~80us + let latency = envelope.ts.saturating_duration_since(tx_ts); + assert!(Duration::from_micros(50) < latency && latency < Duration::from_micros(100)); i += 1; if i > 10 { From a56ef685f3bfd9148a79c9dbbdde83a2c1642ba5 Mon Sep 17 00:00:00 2001 From: chemicstry Date: Tue, 25 Jul 2023 12:19:42 +0300 Subject: [PATCH 083/298] stm32: forward defmt feature to embassy-time --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 0fb6fdb5..cb064433 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -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-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"] memory-x = ["stm32-metapac/memory-x"] exti = [] @@ -1445,4 +1445,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" ] From a5f21520771270984e7c2af1a3f78c79cc91c4fd Mon Sep 17 00:00:00 2001 From: Derek Hageman Date: Tue, 25 Jul 2023 16:39:11 -0600 Subject: [PATCH 084/298] rp: Fix ROM cache ptr() returning the trampoline Make sure that the ptr() function for ROM functions always returns the actual ROM pointer. This allows the use of flash I/O while the function cache is enabled. --- embassy-rp/src/rom_data.rs | 40 +++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data.rs index 805c1f09..baebe5b6 100644 --- a/embassy-rp/src/rom_data.rs +++ b/embassy-rp/src/rom_data.rs @@ -88,9 +88,8 @@ macro_rules! declare_rom_function { #[doc = stringify!($name)] #[doc = r"` ROM function."] pub mod $name { - /// Retrieve a function pointer. #[cfg(not(feature = "rom-func-cache"))] - pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { + pub(crate) fn outer_call() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { let p: *const u32 = $lookup; unsafe { let func : $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret @@ -99,6 +98,12 @@ macro_rules! declare_rom_function { } } + /// Retrieve a function pointer. + #[cfg(not(feature = "rom-func-cache"))] + pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { + outer_call() + } + #[cfg(feature = "rom-func-cache")] // unlike rp2040-hal we store a full word, containing the full function pointer. // rp2040-hal saves two bytes by storing only the rom offset, at the cost of @@ -119,9 +124,8 @@ macro_rules! declare_rom_function { } } - /// Retrieve a function pointer. #[cfg(feature = "rom-func-cache")] - pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { + pub(crate) fn outer_call() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { use core::sync::atomic::{compiler_fence, Ordering}; // This is safe because the lookup will always resolve @@ -138,11 +142,37 @@ macro_rules! declare_rom_function { CACHE } } + + /// Retrieve a function pointer. + #[cfg(feature = "rom-func-cache")] + pub fn ptr() -> $( $maybe_unsafe )? extern "C" fn( $($argname: $ty),* ) -> $ret { + use core::sync::atomic::{compiler_fence, Ordering}; + + // We can't just return the trampoline here because we need + // the actual resolved function address (e.x. flash operations + // can't reference a trampoline which itself is in flash). We + // can still utilize the cache, but we have to make sure it has + // been resolved already. Like the normal call path, we + // don't need anything stronger than fences because the + // final value always resolves to the same thing and SRAM + // itself is not cached. + compiler_fence(Ordering::Acquire); + #[allow(unused_unsafe)] + unsafe { + // ROM is 16kB in size at 0x0, so anything outside is cached + if CACHE as u32 >> 14 != 0 { + let p: *const u32 = $lookup; + CACHE = core::mem::transmute(p); + compiler_fence(Ordering::Release); + } + CACHE + } + } } $(#[$outer])* pub $( $maybe_unsafe )? extern "C" fn $name( $($argname: $ty),* ) -> $ret { - $name::ptr()($($argname),*) + $name::outer_call()($($argname),*) } }; } From 858ddf6777d6df0e8c02921d29cd6a8095a7cdad Mon Sep 17 00:00:00 2001 From: Piotr Esden-Tempski Date: Wed, 26 Jul 2023 18:32:40 -0700 Subject: [PATCH 085/298] 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 30b67b7b..068474e7 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 ded3b7db..715f1ecf 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 9b41ec5a..5d6bf54e 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 f1d45f33..b0e51dcf 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 c812cb3e..f2fe5da4 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 92933ab5..42adede1 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 e74c5357..8534921a 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 620a139a..46b6db45 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 8450c541..5d32992c 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 147e2ecb..9857fb63 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 6ac5d57e..bd594d16 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 bbc99fee..a6964c7b 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 4a14568a..b4dfe3c6 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 935997a7..ce883860 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 e6a5a4c1..f2ebae77 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 dcca1cc3..329d44ca 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 41c9869b..0f770e2f 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 5d66c59c..1afd0039 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 a43a5590..db251eaf 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 48e34026..1a5aff35 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 6e6f269a..3e99b101 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 3679e385..2791cc34 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 086/298] 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 e4524af5..c4857ace 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 9b41ec5a..f39f9323 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 c812cb3e..8a675443 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 e6a5a4c1..e794cf1e 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 6e6f269a..b3f57af5 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 087/298] 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 c4857ace..a77ed003 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 088/298] 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 6cd12220..2c6089c5 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 089/298] 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 3007cd1e..17320649 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 00000000..67a7d5b5 --- /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 3d2a9b8e..ca5cb43a 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 090/298] 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 c812cb3e..2a018ad0 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 14d05f4d..eeb213e1 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 091/298] 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 13566b81..00000000 --- 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 06ff4b23..00000000 --- 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 9b4cf845..00000000 --- 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 b2bdec2d..00000000 --- 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 092/298] 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 2a018ad0..c812cb3e 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 093/298] 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 eeb213e1..c978f8f0 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 094/298] 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 8b8244d4..d4ec2381 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 095/298] 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 d014e4bd..2892bcf8 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 18c758d7..42e03199 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 00000000..d6539701 --- /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 235964aa..3640ea18 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 a77ed003..402ad2d7 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 57dd22f1..d10cd2c3 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 9bc1c1e7..5a0a3c7c 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 8776000c..70e4b486 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 5519e895..7416d391 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 d5367c59..58801068 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 78517044..0ecddaf3 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 b77564a5..ae39628d 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 bff7f4eb..b8830b33 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 9b005082..a490cb07 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 410ae921..afc2c4a7 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 6ac78308..dee666a6 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 67ea032f..495285ba 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 895ab934..ea2b7609 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 6550f2ab..7488bc08 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 fea38c4c..907acdf4 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 d23759f9..355a0049 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 91a5a272..de840b88 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 217884d1..01f41e9f 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 40ccb2f0..0bc7f821 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 ff6593bd..5b4a6438 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 1fe89862..3e9e9fc8 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 c8c81fa0..2f039763 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 8bac87d3..2aa50a2b 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 baefc796..36ee33f6 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 923b8b46..e2803f0d 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 23292924..662b0561 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 b7dc332e..4673a017 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 aa438415..21282512 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 491e92c0..cec46d8d 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 04748238..3dbfdac4 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 2ad0d19b..fdea480e 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 a115d561..c6c02055 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 48d57fea..e79df356 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 76cf40ac..e26b49db 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 8f3ed885..b53c7a01 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 95780c06..4fba3116 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 acb21dce..976d06de 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 1a458778..c8f74180 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 96d2d454..0ed6808e 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 a3d330cd..2807eb67 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 9b85b234..536ad747 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 50f028d4..ebec9fec 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 72a2f44e..464988b2 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 20bb8844..c0ddb2a9 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 1b33fdf8..60ca8627 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 af101cf4..544b542e 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 30eeb547..9d96db12 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 7b94bce5..69c6ac2f 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 2c6089c5..96c47484 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 6836d7a8..5ecce2cc 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 0fb6fdb5..6f34c741 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 995ad144..0e9606ec 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 2322204d..e577ec28 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 d9af0c55..e8245884 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 091c1d44..9a7acea5 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 3a6e58cf..821cc7f6 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 c51c6840..64d0f0c7 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 8b8244d4..55d34201 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 c31a7fc6..f77788db 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 3946a2d4..154f2eb9 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 f337055a..de0c0875 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 979748bb..a2040b85 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 78b026cb..7497f4aa 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 68c78123..d956047d 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 c27ec7bd..219ef2eb 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 b7bcf779..97cc200d 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 0858587b..4f1a58ae 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 2a6ea35f..a1e0240c 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 bb681c42..ada495fd 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 3ff92c9e..925cf39b 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 f175349c..e966e2a7 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 2a374733..16c51129 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 4cb39e03..728f6d60 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 60d7a00e..177e66a9 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 af3a8eac..cda59714 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 aa485cd8..e5254a8c 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 208d1527..eaf980a4 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 62dda69b..1ccad732 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 45a7b547..ebd0e7cd 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 4d64d005..64bb32c3 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 995f59c2..51479693 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 31b67608..32382fb2 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 b8447044..7aa9f0fd 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 7e5cd0d1..bbc0e083 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 8a9b4adb..dc5f55d0 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 b2faec53..27415c2d 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 12a2ac79..323be318 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 434c56a4..6b532363 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 d5f63f84..bdf3c85b 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 433ad299..ca117da8 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 3b9226fd..d99034bc 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 c74d7e09..80261d04 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 ecdd1d0b..cef19635 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 fd0e22ad..348f0f79 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 b03e81d6..eafd0336 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 ce883860..cf3e2ce9 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 096/298] 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 c978f8f0..ca1c7f39 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 097/298] 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 81c751a2..3f915016 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 098/298] 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 0e9606ec..409a943d 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 ebd0e7cd..bb2ef2fc 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 5aba2663..00000000 --- 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 09b7a377..6c2d6d82 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 7c590205..4f130c26 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 a8a68ed6..8cc2a411 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 8f7842ed..b5a9b995 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 d360df08..45b0872b 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 c5c0dd29..adf2ea9c 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 099/298] 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 376cc8f4..19628b50 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 c52d1d11f92d58eaafada26a11ae9b4402e129a6 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Fri, 28 Jul 2023 15:54:31 +0200 Subject: [PATCH 100/298] Expose IEEE802.15.4 address in Driver --- embassy-net-driver-channel/src/lib.rs | 15 +++++++++++++++ embassy-net-driver/src/lib.rs | 6 ++++++ 2 files changed, 21 insertions(+) diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs index 02a4c00d..e101bf38 100644 --- a/embassy-net-driver-channel/src/lib.rs +++ b/embassy-net-driver-channel/src/lib.rs @@ -43,6 +43,7 @@ struct Shared { link_state: LinkState, waker: WakerRegistration, ethernet_address: [u8; 6], + ieee802154_address: [u8; 8], } pub struct Runner<'d, const MTU: usize> { @@ -93,6 +94,14 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { }); } + pub fn set_ieee802154_address(&mut self, address: [u8; 8]) { + self.shared.lock(|s| { + let s = &mut *s.borrow_mut(); + s.ieee802154_address = address; + s.waker.wake(); + }); + } + pub async fn rx_buf(&mut self) -> &mut [u8] { let p = self.rx_chan.send().await; &mut p.buf @@ -207,6 +216,7 @@ impl<'d, const MTU: usize> TxRunner<'d, MTU> { pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( state: &'d mut State, ethernet_address: [u8; 6], + ieee802154_address: [u8; 8], ) -> (Runner<'d, MTU>, Device<'d, MTU>) { let mut caps = Capabilities::default(); caps.max_transmission_unit = MTU; @@ -223,6 +233,7 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( shared: Mutex::new(RefCell::new(Shared { link_state: LinkState::Down, ethernet_address, + ieee802154_address, waker: WakerRegistration::new(), })), }); @@ -293,6 +304,10 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { self.shared.lock(|s| s.borrow().ethernet_address) } + fn ieee802154_address(&self) -> [u8; 8] { + self.shared.lock(|s| s.borrow().ieee802154_address) + } + fn link_state(&mut self, cx: &mut Context) -> LinkState { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs index 09def20c..beb1a1c7 100644 --- a/embassy-net-driver/src/lib.rs +++ b/embassy-net-driver/src/lib.rs @@ -53,6 +53,9 @@ pub trait Driver { /// Get the device's Ethernet address. fn ethernet_address(&self) -> [u8; 6]; + + /// Get the device's IEEE 802.15.4 address. + fn ieee802154_address(&self) -> [u8; 8]; } impl Driver for &mut T { @@ -78,6 +81,9 @@ impl Driver for &mut T { fn ethernet_address(&self) -> [u8; 6] { T::ethernet_address(self) } + fn ieee802154_address(&self) -> [u8; 8] { + T::ieee802154_address(self) + } } /// A token to receive a single network packet. From c3ba08ffb6570589726db44d40ba5b724a8950d4 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Fri, 28 Jul 2023 16:22:03 +0200 Subject: [PATCH 101/298] Add IEEE802.15.4 address to embassy net Stack --- embassy-net/src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 3f915016..53693563 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -247,7 +247,7 @@ impl Stack { #[cfg(feature = "medium-ip")] Medium::Ip => HardwareAddress::Ip, #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::Absent), + Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::Extended(device.ieee802154_address())), #[allow(unreachable_patterns)] _ => panic!( "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.", @@ -747,6 +747,13 @@ impl Inner { ))); } + #[cfg(feature = "medium-ieee802154")] + if self.device.capabilities().medium == Medium::Ieee802154 { + s.iface.set_hardware_addr(HardwareAddress::Ieee802154(Ieee802154Address::Extended( + self.device.ieee802154_address(), + ))); + } + let timestamp = instant_to_smoltcp(Instant::now()); let mut smoldev = DriverAdapter { cx: Some(cx), 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 102/298] 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 2d82c0d0..3c1232e6 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 026b14b9..00000000 --- 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 00000000..a9cb5d1e --- /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 103/298] 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 a6964c7b..a379cbbe 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 2b5d412a..8cd32d7e 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 899f7a4b..00000000 --- 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 aebc795c..51d3bad1 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 45606150..00000000 --- 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 5f680dbc..4160bf85 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 0f770e2f..3b27d8e8 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 30691aa9..8cd32d7e 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 eb87d1b5..00000000 --- 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 69c0a89aa5d52e048fdd8ddc5d47b767da07e88b Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Fri, 28 Jul 2023 16:19:24 +0200 Subject: [PATCH 104/298] Use HardwareAddress in Driver --- embassy-net-driver-channel/src/lib.rs | 23 +++++------------------ embassy-net-driver/Cargo.toml | 3 ++- embassy-net-driver/src/lib.rs | 16 ++++++---------- embassy-net/src/lib.rs | 27 ++++++++++----------------- embassy-stm32-wpan/src/mac/driver.rs | 4 ++-- examples/std/src/tuntap.rs | 5 +++-- 6 files changed, 28 insertions(+), 50 deletions(-) diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs index e101bf38..53b66afb 100644 --- a/embassy-net-driver-channel/src/lib.rs +++ b/embassy-net-driver-channel/src/lib.rs @@ -42,8 +42,7 @@ struct StateInner<'d, const MTU: usize> { struct Shared { link_state: LinkState, waker: WakerRegistration, - ethernet_address: [u8; 6], - ieee802154_address: [u8; 8], + hardware_address: HardwareAddress, } pub struct Runner<'d, const MTU: usize> { @@ -86,18 +85,10 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { }); } - pub fn set_ethernet_address(&mut self, address: [u8; 6]) { + pub fn set_hardware_address(&mut self, address: HardwareAddress) { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); - s.ethernet_address = address; - s.waker.wake(); - }); - } - - pub fn set_ieee802154_address(&mut self, address: [u8; 8]) { - self.shared.lock(|s| { - let s = &mut *s.borrow_mut(); - s.ieee802154_address = address; + s.hardware_address = address; s.waker.wake(); }); } @@ -300,12 +291,8 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { self.caps.clone() } - fn ethernet_address(&self) -> [u8; 6] { - self.shared.lock(|s| s.borrow().ethernet_address) - } - - fn ieee802154_address(&self) -> [u8; 8] { - self.shared.lock(|s| s.borrow().ieee802154_address) + fn hardware_address(&self) -> HardwareAddress { + self.shared.lock(|s| s.borrow().hardware_address) } fn link_state(&mut self, cx: &mut Context) -> LinkState { diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml index da6d9ad6..1af0d61b 100644 --- a/embassy-net-driver/Cargo.toml +++ b/embassy-net-driver/Cargo.toml @@ -21,4 +21,5 @@ target = "thumbv7em-none-eabi" features = ["defmt"] [dependencies] -defmt = { version = "0.3", optional = true } \ No newline at end of file +defmt = { version = "0.3", optional = true } +smoltcp = { version = "0.10", default-features = false } diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs index beb1a1c7..93a02e46 100644 --- a/embassy-net-driver/src/lib.rs +++ b/embassy-net-driver/src/lib.rs @@ -4,6 +4,8 @@ use core::task::Context; +use smoltcp::wire::HardwareAddress; + /// Main `embassy-net` driver API. /// /// This is essentially an interface for sending and receiving raw network frames. @@ -51,11 +53,8 @@ pub trait Driver { /// Get a description of device capabilities. fn capabilities(&self) -> Capabilities; - /// Get the device's Ethernet address. - fn ethernet_address(&self) -> [u8; 6]; - - /// Get the device's IEEE 802.15.4 address. - fn ieee802154_address(&self) -> [u8; 8]; + /// Get the device's hardware address. + fn hardware_address(&self) -> HardwareAddress; } impl Driver for &mut T { @@ -78,11 +77,8 @@ impl Driver for &mut T { fn link_state(&mut self, cx: &mut Context) -> LinkState { T::link_state(self, cx) } - fn ethernet_address(&self) -> [u8; 6] { - T::ethernet_address(self) - } - fn ieee802154_address(&self) -> [u8; 8] { - T::ieee802154_address(self) + fn hardware_address(&self) -> HardwareAddress { + T::hardware_address(self) } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 53693563..efd820a6 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -243,11 +243,11 @@ impl Stack { let hardware_addr = match medium { #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())), + Medium::Ethernet => device.hardware_address(), #[cfg(feature = "medium-ip")] Medium::Ip => HardwareAddress::Ip, #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::Extended(device.ieee802154_address())), + Medium::Ieee802154 => device.hardware_address(), #[allow(unreachable_patterns)] _ => panic!( "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.", @@ -336,9 +336,9 @@ impl Stack { f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) } - /// Get the MAC address of the network interface. - pub fn ethernet_address(&self) -> [u8; 6] { - self.with(|_s, i| i.device.ethernet_address()) + /// Get the hardware address of the network interface. + pub fn hardware_address(&self) -> HardwareAddress { + self.with(|_s, i| i.device.hardware_address()) } /// Get whether the link is up. @@ -740,18 +740,11 @@ impl Inner { fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { s.waker.register(cx.waker()); - #[cfg(feature = "medium-ethernet")] - if self.device.capabilities().medium == Medium::Ethernet { - s.iface.set_hardware_addr(HardwareAddress::Ethernet(EthernetAddress( - self.device.ethernet_address(), - ))); - } - - #[cfg(feature = "medium-ieee802154")] - if self.device.capabilities().medium == Medium::Ieee802154 { - s.iface.set_hardware_addr(HardwareAddress::Ieee802154(Ieee802154Address::Extended( - self.device.ieee802154_address(), - ))); + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] + if self.device.capabilities().medium == Medium::Ethernet + || self.device.capabilities().medium == Medium::Ieee802154 + { + s.iface.set_hardware_addr(self.device.hardware_address()); } let timestamp = instant_to_smoltcp(Instant::now()); diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index fffbb9ed..06ff0cf6 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -73,10 +73,10 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { LinkState::Down } - fn ethernet_address(&self) -> [u8; 6] { + fn hardware_address(&self) -> HardwareAddress { // self.mac_addr - [0; 6] + HardwareAddress::Ethernet(EthernetAddress([0; 6])) } } diff --git a/examples/std/src/tuntap.rs b/examples/std/src/tuntap.rs index d918a2e6..932bdaf9 100644 --- a/examples/std/src/tuntap.rs +++ b/examples/std/src/tuntap.rs @@ -4,6 +4,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::task::Context; use async_io::Async; +use embassy_net::HardwareAddress; use embassy_net_driver::{self, Capabilities, Driver, LinkState}; use log::*; @@ -180,8 +181,8 @@ impl Driver for TunTapDevice { LinkState::Up } - fn ethernet_address(&self) -> [u8; 6] { - [0x02, 0x03, 0x04, 0x05, 0x06, 0x07] + fn hardware_address(&self) -> HardwareAddress { + HardwareAddress::Ethernet(EthernetAddress([0x02, 0x03, 0x04, 0x05, 0x06, 0x07])) } } 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 105/298] 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 6f34c741..ba279f79 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 bb2ef2fc..9e67596b 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 5a8704b4d8820fe629e9c9d586eb78d165b5f476 Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Fri, 28 Jul 2023 11:16:43 -0500 Subject: [PATCH 106/298] TimeoutI2c: allow ref to live shorter than peripheral --- embassy-stm32/src/i2c/timeout.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index 939e2750..8dc228b3 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -6,8 +6,8 @@ use super::{Error, I2c, Instance}; /// /// This is useful for recovering from a shorted bus or a device stuck in a clock stretching state. /// A regular [I2c] would freeze until condition is removed. -pub struct TimeoutI2c<'d, T: Instance, TXDMA, RXDMA> { - i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, +pub struct TimeoutI2c<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> { + i2c: &'a mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration, } @@ -22,8 +22,8 @@ fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { } } -impl<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> { - pub fn new(i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self { +impl<'a, 'd, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { + pub fn new(i2c: &'a mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self { Self { i2c, timeout } } @@ -65,7 +65,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> { } } -impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T, TXDMA, RXDMA> { +impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { type Error = Error; fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> { @@ -73,7 +73,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for Tim } } -impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T, TXDMA, RXDMA> { +impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { type Error = Error; fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> { @@ -81,7 +81,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for Ti } } -impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T, TXDMA, RXDMA> { +impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead + for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> +{ type Error = Error; fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { @@ -93,11 +95,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead fo mod eh1 { use super::*; - impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T, TXDMA, RXDMA> { + impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { type Error = Error; } - impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T, TXDMA, RXDMA> { + impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, read) } From cbc8871a0bb40eb5fec82e7c27ed4c0127844c3c Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 28 Jul 2023 18:45:57 +0200 Subject: [PATCH 107/298] 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 dca30c74..830a5b44 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 ebec9fec..45156458 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 464988b2..3de398af 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 9cb279cc..b35b4ed7 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 c001d644..a6d6144b 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 9ab72e1f..86e5017a 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 8aedd24b..d80c5c24 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 ca1c7f39..5fddbe29 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 3de2bd48..bc87016e 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 45004424..bdea63ea 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 00000000..356f1679 --- /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 108/298] 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 109/298] 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 7381fdc5..db3d9c9c 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 110/298] 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 79f8cde7..37f220da 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 111/298] 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 398ab9d2..e2adfe0b 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 112/298] 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 bc127e2e..fffdabc9 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); From 28136579e98ea4d9745867d7c1ffff0ad826b504 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 28 Jul 2023 17:07:08 -0500 Subject: [PATCH 113/298] stm32/hrtim: extract into mod --- embassy-stm32/build.rs | 20 +++++++++--------- .../src/{pwm/advanced_pwm.rs => hrtim/mod.rs} | 13 +++++++++++- embassy-stm32/src/lib.rs | 9 ++++---- embassy-stm32/src/pwm/mod.rs | 21 ------------------- examples/stm32f334/src/bin/pwm.rs | 2 +- 5 files changed, 28 insertions(+), 37 deletions(-) rename embassy-stm32/src/{pwm/advanced_pwm.rs => hrtim/mod.rs} (94%) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index d2b1cfd0..c2b84797 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -587,16 +587,16 @@ fn main() { (("timer", "BKIN2"), quote!(crate::pwm::BreakInput2Pin)), (("timer", "BKIN2_COMP1"), quote!(crate::pwm::BreakInput2Comparator1Pin)), (("timer", "BKIN2_COMP2"), quote!(crate::pwm::BreakInput2Comparator2Pin)), - (("hrtim", "CHA1"), quote!(crate::pwm::ChannelAPin)), - (("hrtim", "CHA2"), quote!(crate::pwm::ChannelAComplementaryPin)), - (("hrtim", "CHB1"), quote!(crate::pwm::ChannelBPin)), - (("hrtim", "CHB2"), quote!(crate::pwm::ChannelBComplementaryPin)), - (("hrtim", "CHC1"), quote!(crate::pwm::ChannelCPin)), - (("hrtim", "CHC2"), quote!(crate::pwm::ChannelCComplementaryPin)), - (("hrtim", "CHD1"), quote!(crate::pwm::ChannelDPin)), - (("hrtim", "CHD2"), quote!(crate::pwm::ChannelDComplementaryPin)), - (("hrtim", "CHE1"), quote!(crate::pwm::ChannelEPin)), - (("hrtim", "CHE2"), quote!(crate::pwm::ChannelEComplementaryPin)), + (("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)), + (("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)), + (("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)), + (("hrtim", "CHB2"), quote!(crate::hrtim::ChannelBComplementaryPin)), + (("hrtim", "CHC1"), quote!(crate::hrtim::ChannelCPin)), + (("hrtim", "CHC2"), quote!(crate::hrtim::ChannelCComplementaryPin)), + (("hrtim", "CHD1"), quote!(crate::hrtim::ChannelDPin)), + (("hrtim", "CHD2"), quote!(crate::hrtim::ChannelDComplementaryPin)), + (("hrtim", "CHE1"), quote!(crate::hrtim::ChannelEPin)), + (("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)), (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/hrtim/mod.rs similarity index 94% rename from embassy-stm32/src/pwm/advanced_pwm.rs rename to embassy-stm32/src/hrtim/mod.rs index 9e40c5bf..7e3a8f14 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -2,10 +2,10 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; -use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::AnyPin; +use crate::pwm::HighResolutionCaptureCompare16bitInstance; use crate::time::Hertz; use crate::Peripheral; @@ -394,3 +394,14 @@ impl> Resona self.max_period } } + +pin_trait!(ChannelAPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelAComplementaryPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelBPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelBComplementaryPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelCPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelCComplementaryPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelDPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelDComplementaryPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelEPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelEComplementaryPin, HighResolutionCaptureCompare16bitInstance); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 45a7b547..8c005bfe 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -23,6 +23,8 @@ pub mod timer; pub mod adc; #[cfg(can)] pub mod can; +#[cfg(crc)] +pub mod crc; #[cfg(dac)] pub mod dac; #[cfg(dcmi)] @@ -31,14 +33,13 @@ pub mod dcmi; pub mod eth; #[cfg(feature = "exti")] pub mod exti; +pub mod flash; #[cfg(fmc)] pub mod fmc; +#[cfg(hrtim_v1)] +pub mod hrtim; #[cfg(i2c)] pub mod i2c; - -#[cfg(crc)] -pub mod crc; -pub mod flash; #[cfg(all(spi_v1, rcc_f4))] pub mod i2s; #[cfg(stm32wb)] diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 429a290e..c4b38ebd 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -1,5 +1,3 @@ -#[cfg(hrtim_v1)] -pub mod advanced_pwm; pub mod complementary_pwm; pub mod simple_pwm; @@ -468,22 +466,3 @@ pin_trait!(BreakInputComparator2Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); - -#[cfg(hrtim_v1)] -mod hrtim_pins { - use super::*; - - pin_trait!(ChannelAPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelAComplementaryPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelBPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelBComplementaryPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelCPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelCComplementaryPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelDPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelDComplementaryPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelEPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelEComplementaryPin, HighResolutionCaptureCompare16bitInstance); -} - -#[cfg(hrtim_v1)] -pub use hrtim_pins::*; diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index 36411974..2660b10c 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::advanced_pwm::*; +use embassy_stm32::hrtim::*; use embassy_stm32::time::{khz, mhz}; use embassy_stm32::Config; use embassy_time::{Duration, Timer}; From e495d606ec62ccfd72eaf9fc868455da217e2d5c Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 28 Jul 2023 17:16:46 -0500 Subject: [PATCH 114/298] stm32/hrtim: extract traits --- embassy-stm32/src/hrtim/mod.rs | 6 +- embassy-stm32/src/hrtim/traits.rs | 196 +++++++++++++++++++++++++++++ embassy-stm32/src/pwm/mod.rs | 199 ------------------------------ 3 files changed, 200 insertions(+), 201 deletions(-) create mode 100644 embassy-stm32/src/hrtim/traits.rs diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 7e3a8f14..ddf8cc2a 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -1,3 +1,5 @@ +mod traits; + use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -5,7 +7,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::AnyPin; -use crate::pwm::HighResolutionCaptureCompare16bitInstance; +use crate::hrtim::traits::HighResolutionCaptureCompare16bitInstance; use crate::time::Hertz; use crate::Peripheral; @@ -41,7 +43,7 @@ pub struct ChE { } mod sealed { - use crate::pwm::HighResolutionCaptureCompare16bitInstance; + use super::HighResolutionCaptureCompare16bitInstance; pub trait AdvancedChannel { fn raw() -> usize; diff --git a/embassy-stm32/src/hrtim/traits.rs b/embassy-stm32/src/hrtim/traits.rs new file mode 100644 index 00000000..16193a45 --- /dev/null +++ b/embassy-stm32/src/hrtim/traits.rs @@ -0,0 +1,196 @@ +use crate::time::Hertz; + +#[derive(Clone, Copy)] +pub(crate) enum HighResolutionControlPrescaler { + Div1, + Div2, + Div4, + Div8, + Div16, + Div32, + Div64, + Div128, +} + +impl From for u32 { + fn from(val: HighResolutionControlPrescaler) -> Self { + match val { + HighResolutionControlPrescaler::Div1 => 1, + HighResolutionControlPrescaler::Div2 => 2, + HighResolutionControlPrescaler::Div4 => 4, + HighResolutionControlPrescaler::Div8 => 8, + HighResolutionControlPrescaler::Div16 => 16, + HighResolutionControlPrescaler::Div32 => 32, + HighResolutionControlPrescaler::Div64 => 64, + HighResolutionControlPrescaler::Div128 => 128, + } + } +} + +impl From for u8 { + fn from(val: HighResolutionControlPrescaler) -> Self { + match val { + HighResolutionControlPrescaler::Div1 => 0b000, + HighResolutionControlPrescaler::Div2 => 0b001, + HighResolutionControlPrescaler::Div4 => 0b010, + HighResolutionControlPrescaler::Div8 => 0b011, + HighResolutionControlPrescaler::Div16 => 0b100, + HighResolutionControlPrescaler::Div32 => 0b101, + HighResolutionControlPrescaler::Div64 => 0b110, + HighResolutionControlPrescaler::Div128 => 0b111, + } + } +} + +impl From for HighResolutionControlPrescaler { + fn from(val: u8) -> Self { + match val { + 0b000 => HighResolutionControlPrescaler::Div1, + 0b001 => HighResolutionControlPrescaler::Div2, + 0b010 => HighResolutionControlPrescaler::Div4, + 0b011 => HighResolutionControlPrescaler::Div8, + 0b100 => HighResolutionControlPrescaler::Div16, + 0b101 => HighResolutionControlPrescaler::Div32, + 0b110 => HighResolutionControlPrescaler::Div64, + 0b111 => HighResolutionControlPrescaler::Div128, + _ => unreachable!(), + } + } +} + +impl HighResolutionControlPrescaler { + pub fn compute_min_high_res(val: u32) -> Self { + *[ + HighResolutionControlPrescaler::Div1, + HighResolutionControlPrescaler::Div2, + HighResolutionControlPrescaler::Div4, + HighResolutionControlPrescaler::Div8, + HighResolutionControlPrescaler::Div16, + HighResolutionControlPrescaler::Div32, + HighResolutionControlPrescaler::Div64, + HighResolutionControlPrescaler::Div128, + ] + .iter() + .skip_while(|psc| >::into(**psc) <= val) + .next() + .unwrap() + } + + pub fn compute_min_low_res(val: u32) -> Self { + *[ + HighResolutionControlPrescaler::Div32, + HighResolutionControlPrescaler::Div64, + HighResolutionControlPrescaler::Div128, + ] + .iter() + .skip_while(|psc| >::into(**psc) <= val) + .next() + .unwrap() + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait HighResolutionCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { + fn set_master_frequency(frequency: Hertz); + + fn set_channel_frequency(channnel: usize, frequency: Hertz); + + /// Set the dead time as a proportion of max_duty + fn set_channel_dead_time(channnel: usize, dead_time: u16); + + // fn enable_outputs(enable: bool); + // + // fn enable_channel(&mut self, channel: usize, enable: bool); + } +} + +pub trait HighResolutionCaptureCompare16bitInstance: + sealed::HighResolutionCaptureCompare16bitInstance + 'static +{ +} + +foreach_interrupt! { + ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { + impl sealed::HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { + fn set_master_frequency(frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + use crate::timer::sealed::HighResolutionControlInstance; + + let f = frequency.0; + let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = if Self::regs().isr().read().dllrdy() { + HighResolutionControlPrescaler::compute_min_high_res(psc_min) + } else { + HighResolutionControlPrescaler::compute_min_low_res(psc_min) + }; + + let psc_val: u32 = psc.into(); + let timer_f = 32 * (timer_f / psc_val); + let per: u16 = (timer_f / f) as u16; + + let regs = Self::regs(); + + regs.mcr().modify(|w| w.set_ckpsc(psc.into())); + regs.mper().modify(|w| w.set_mper(per)); + } + + fn set_channel_frequency(channel: usize, frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + use crate::timer::sealed::HighResolutionControlInstance; + + let f = frequency.0; + let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = if Self::regs().isr().read().dllrdy() { + HighResolutionControlPrescaler::compute_min_high_res(psc_min) + } else { + HighResolutionControlPrescaler::compute_min_low_res(psc_min) + }; + + let psc_val: u32 = psc.into(); + let timer_f = 32 * (timer_f / psc_val); + let per: u16 = (timer_f / f) as u16; + + let regs = Self::regs(); + + regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into())); + regs.tim(channel).per().modify(|w| w.set_per(per)); + } + + fn set_channel_dead_time(channel: usize, dead_time: u16) { + use crate::timer::sealed::HighResolutionControlInstance; + + let regs = Self::regs(); + + let channel_psc: HighResolutionControlPrescaler = regs.tim(channel).cr().read().ckpsc().into(); + let psc_val: u32 = channel_psc.into(); + + + // The dead-time base clock runs 4 times slower than the hrtim base clock + // u9::MAX = 511 + let psc_min = (psc_val * dead_time as u32) / (4 * 511); + let psc = if Self::regs().isr().read().dllrdy() { + HighResolutionControlPrescaler::compute_min_high_res(psc_min) + } else { + HighResolutionControlPrescaler::compute_min_low_res(psc_min) + }; + + let dt_psc_val: u32 = psc.into(); + let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); + + regs.tim(channel).dt().modify(|w| { + w.set_dtprsc(psc.into()); + w.set_dtf(dt_val as u16); + w.set_dtr(dt_val as u16); + }); + } + } + + impl HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { + + } + }; +} diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index c4b38ebd..5aba2663 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -3,9 +3,6 @@ pub mod simple_pwm; use stm32_metapac::timer::vals::Ckd; -#[cfg(hrtim_v1)] -use crate::time::Hertz; - #[cfg(feature = "unstable-pac")] pub mod low_level { pub use super::sealed::*; @@ -57,117 +54,9 @@ impl From for stm32_metapac::timer::vals::Ocm { } } -#[cfg(hrtim_v1)] -#[derive(Clone, Copy)] -pub(crate) enum HighResolutionControlPrescaler { - Div1, - Div2, - Div4, - Div8, - Div16, - Div32, - Div64, - Div128, -} - -#[cfg(hrtim_v1)] -impl From for u32 { - fn from(val: HighResolutionControlPrescaler) -> Self { - match val { - HighResolutionControlPrescaler::Div1 => 1, - HighResolutionControlPrescaler::Div2 => 2, - HighResolutionControlPrescaler::Div4 => 4, - HighResolutionControlPrescaler::Div8 => 8, - HighResolutionControlPrescaler::Div16 => 16, - HighResolutionControlPrescaler::Div32 => 32, - HighResolutionControlPrescaler::Div64 => 64, - HighResolutionControlPrescaler::Div128 => 128, - } - } -} - -#[cfg(hrtim_v1)] -impl From for u8 { - fn from(val: HighResolutionControlPrescaler) -> Self { - match val { - HighResolutionControlPrescaler::Div1 => 0b000, - HighResolutionControlPrescaler::Div2 => 0b001, - HighResolutionControlPrescaler::Div4 => 0b010, - HighResolutionControlPrescaler::Div8 => 0b011, - HighResolutionControlPrescaler::Div16 => 0b100, - HighResolutionControlPrescaler::Div32 => 0b101, - HighResolutionControlPrescaler::Div64 => 0b110, - HighResolutionControlPrescaler::Div128 => 0b111, - } - } -} - -#[cfg(hrtim_v1)] -impl From for HighResolutionControlPrescaler { - fn from(val: u8) -> Self { - match val { - 0b000 => HighResolutionControlPrescaler::Div1, - 0b001 => HighResolutionControlPrescaler::Div2, - 0b010 => HighResolutionControlPrescaler::Div4, - 0b011 => HighResolutionControlPrescaler::Div8, - 0b100 => HighResolutionControlPrescaler::Div16, - 0b101 => HighResolutionControlPrescaler::Div32, - 0b110 => HighResolutionControlPrescaler::Div64, - 0b111 => HighResolutionControlPrescaler::Div128, - _ => unreachable!(), - } - } -} - -#[cfg(hrtim_v1)] -impl HighResolutionControlPrescaler { - pub fn compute_min_high_res(val: u32) -> Self { - *[ - HighResolutionControlPrescaler::Div1, - HighResolutionControlPrescaler::Div2, - HighResolutionControlPrescaler::Div4, - HighResolutionControlPrescaler::Div8, - HighResolutionControlPrescaler::Div16, - HighResolutionControlPrescaler::Div32, - HighResolutionControlPrescaler::Div64, - HighResolutionControlPrescaler::Div128, - ] - .iter() - .skip_while(|psc| >::into(**psc) <= val) - .next() - .unwrap() - } - - pub fn compute_min_low_res(val: u32) -> Self { - *[ - HighResolutionControlPrescaler::Div32, - HighResolutionControlPrescaler::Div64, - HighResolutionControlPrescaler::Div128, - ] - .iter() - .skip_while(|psc| >::into(**psc) <= val) - .next() - .unwrap() - } -} - pub(crate) mod sealed { use super::*; - #[cfg(hrtim_v1)] - pub trait HighResolutionCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { - fn set_master_frequency(frequency: Hertz); - - fn set_channel_frequency(channnel: usize, frequency: Hertz); - - /// Set the dead time as a proportion of max_duty - fn set_channel_dead_time(channnel: usize, dead_time: u16); - - // fn enable_outputs(enable: bool); - // - // fn enable_channel(&mut self, channel: usize, enable: bool); - } - 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); @@ -200,12 +89,6 @@ pub(crate) mod sealed { } } -#[cfg(hrtim_v1)] -pub trait HighResolutionCaptureCompare16bitInstance: - sealed::HighResolutionCaptureCompare16bitInstance + 'static -{ -} - pub trait CaptureCompare16bitInstance: sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static { @@ -367,88 +250,6 @@ foreach_interrupt! { } }; - - ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { - impl crate::pwm::sealed::HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { - fn set_master_frequency(frequency: Hertz) { - use crate::rcc::sealed::RccPeripheral; - use crate::timer::sealed::HighResolutionControlInstance; - - let f = frequency.0; - let timer_f = Self::frequency().0; - let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); - let psc = if Self::regs().isr().read().dllrdy() { - HighResolutionControlPrescaler::compute_min_high_res(psc_min) - } else { - HighResolutionControlPrescaler::compute_min_low_res(psc_min) - }; - - let psc_val: u32 = psc.into(); - let timer_f = 32 * (timer_f / psc_val); - let per: u16 = (timer_f / f) as u16; - - let regs = Self::regs(); - - regs.mcr().modify(|w| w.set_ckpsc(psc.into())); - regs.mper().modify(|w| w.set_mper(per)); - } - - fn set_channel_frequency(channel: usize, frequency: Hertz) { - use crate::rcc::sealed::RccPeripheral; - use crate::timer::sealed::HighResolutionControlInstance; - - let f = frequency.0; - let timer_f = Self::frequency().0; - let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); - let psc = if Self::regs().isr().read().dllrdy() { - HighResolutionControlPrescaler::compute_min_high_res(psc_min) - } else { - HighResolutionControlPrescaler::compute_min_low_res(psc_min) - }; - - let psc_val: u32 = psc.into(); - let timer_f = 32 * (timer_f / psc_val); - let per: u16 = (timer_f / f) as u16; - - let regs = Self::regs(); - - regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into())); - regs.tim(channel).per().modify(|w| w.set_per(per)); - } - - fn set_channel_dead_time(channel: usize, dead_time: u16) { - use crate::timer::sealed::HighResolutionControlInstance; - - let regs = Self::regs(); - - let channel_psc: HighResolutionControlPrescaler = regs.tim(channel).cr().read().ckpsc().into(); - let psc_val: u32 = channel_psc.into(); - - - // The dead-time base clock runs 4 times slower than the hrtim base clock - // u9::MAX = 511 - let psc_min = (psc_val * dead_time as u32) / (4 * 511); - let psc = if Self::regs().isr().read().dllrdy() { - HighResolutionControlPrescaler::compute_min_high_res(psc_min) - } else { - HighResolutionControlPrescaler::compute_min_low_res(psc_min) - }; - - let dt_psc_val: u32 = psc.into(); - let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); - - regs.tim(channel).dt().modify(|w| { - w.set_dtprsc(psc.into()); - w.set_dtf(dt_val as u16); - w.set_dtr(dt_val as u16); - }); - } - } - - impl HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { - - } - }; } pin_trait!(Channel1Pin, CaptureCompare16bitInstance); From ec787d3518211a91cbecc4056fde88d7d9eb3a34 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 28 Jul 2023 17:27:15 -0500 Subject: [PATCH 115/298] stm32/hrtim: cleanup merge issues --- embassy-stm32/src/hrtim/mod.rs | 2 +- embassy-stm32/src/hrtim/traits.rs | 12 ++++++++---- embassy-stm32/src/timer/mod.rs | 13 ------------- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index ddf8cc2a..3a05719b 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -2,7 +2,7 @@ mod traits; use core::marker::PhantomData; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; diff --git a/embassy-stm32/src/hrtim/traits.rs b/embassy-stm32/src/hrtim/traits.rs index 16193a45..7f2cedda 100644 --- a/embassy-stm32/src/hrtim/traits.rs +++ b/embassy-stm32/src/hrtim/traits.rs @@ -1,3 +1,4 @@ +use crate::rcc::sealed::RccPeripheral; use crate::time::Hertz; #[derive(Clone, Copy)] @@ -92,7 +93,9 @@ impl HighResolutionControlPrescaler { pub(crate) mod sealed { use super::*; - pub trait HighResolutionCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { + pub trait HighResolutionCaptureCompare16bitInstance: RccPeripheral { + fn regs() -> crate::pac::hrtim::Hrtim; + fn set_master_frequency(frequency: Hertz); fn set_channel_frequency(channnel: usize, frequency: Hertz); @@ -114,9 +117,12 @@ pub trait HighResolutionCaptureCompare16bitInstance: foreach_interrupt! { ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { impl sealed::HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { + fn regs() -> crate::pac::hrtim::Hrtim { + crate::pac::$inst + } + fn set_master_frequency(frequency: Hertz) { use crate::rcc::sealed::RccPeripheral; - use crate::timer::sealed::HighResolutionControlInstance; let f = frequency.0; let timer_f = Self::frequency().0; @@ -139,7 +145,6 @@ foreach_interrupt! { fn set_channel_frequency(channel: usize, frequency: Hertz) { use crate::rcc::sealed::RccPeripheral; - use crate::timer::sealed::HighResolutionControlInstance; let f = frequency.0; let timer_f = Self::frequency().0; @@ -161,7 +166,6 @@ foreach_interrupt! { } fn set_channel_dead_time(channel: usize, dead_time: u16) { - use crate::timer::sealed::HighResolutionControlInstance; let regs = Self::regs(); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 286ab556..6c2d6d82 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -429,17 +429,4 @@ foreach_interrupt! { }; - - ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { - impl sealed::HighResolutionControlInstance for crate::peripherals::$inst { - type Interrupt = crate::interrupt::typelevel::$irq; - - fn regs() -> crate::pac::hrtim::Hrtim { - crate::pac::$inst - } - } - - impl HighResolutionControlInstance for crate::peripherals::$inst { - } - }; } From a8d3bcbb759293f93b7a623910ead15f2cae0a95 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 28 Jul 2023 17:37:14 -0500 Subject: [PATCH 116/298] stm32/hrtim: shorten names --- embassy-stm32/src/hrtim/mod.rs | 64 ++++++++-------- embassy-stm32/src/hrtim/traits.rs | 119 ++++++++++++++---------------- 2 files changed, 88 insertions(+), 95 deletions(-) diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 3a05719b..9ed03a21 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -7,7 +7,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::AnyPin; -use crate::hrtim::traits::HighResolutionCaptureCompare16bitInstance; +use crate::hrtim::traits::Instance; use crate::time::Hertz; use crate::Peripheral; @@ -20,37 +20,37 @@ pub enum Source { ChE, } -pub struct BurstController { +pub struct BurstController { phantom: PhantomData, } -pub struct Master { +pub struct Master { phantom: PhantomData, } -pub struct ChA { +pub struct ChA { phantom: PhantomData, } -pub struct ChB { +pub struct ChB { phantom: PhantomData, } -pub struct ChC { +pub struct ChC { phantom: PhantomData, } -pub struct ChD { +pub struct ChD { phantom: PhantomData, } -pub struct ChE { +pub struct ChE { phantom: PhantomData, } mod sealed { - use super::HighResolutionCaptureCompare16bitInstance; + use super::Instance; - pub trait AdvancedChannel { + pub trait AdvancedChannel { fn raw() -> usize; } } -pub trait AdvancedChannel: sealed::AdvancedChannel {} +pub trait AdvancedChannel: sealed::AdvancedChannel {} pub struct PwmPin<'d, Perip, Channel> { _pin: PeripheralRef<'d, AnyPin>, @@ -64,7 +64,7 @@ pub struct ComplementaryPwmPin<'d, Perip, Channel> { macro_rules! advanced_channel_impl { ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { - impl<'d, Perip: HighResolutionCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { + impl<'d, Perip: Instance> PwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -80,7 +80,7 @@ macro_rules! advanced_channel_impl { } } - impl<'d, Perip: HighResolutionCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { + impl<'d, Perip: Instance> ComplementaryPwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -96,12 +96,12 @@ macro_rules! advanced_channel_impl { } } - impl sealed::AdvancedChannel for $channel { + impl sealed::AdvancedChannel for $channel { fn raw() -> usize { $ch_num } } - impl AdvancedChannel for $channel {} + impl AdvancedChannel for $channel {} }; } @@ -112,7 +112,7 @@ advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin); advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin); /// Struct used to divide a high resolution timer into multiple channels -pub struct AdvancedPwm<'d, T: HighResolutionCaptureCompare16bitInstance> { +pub struct AdvancedPwm<'d, T: Instance> { _inner: PeripheralRef<'d, T>, pub master: Master, pub burst_controller: BurstController, @@ -123,7 +123,7 @@ pub struct AdvancedPwm<'d, T: HighResolutionCaptureCompare16bitInstance> { pub ch_e: ChE, } -impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> { +impl<'d, T: Instance> AdvancedPwm<'d, T> { pub fn new( tim: impl Peripheral

+ 'd, _cha: Option>>, @@ -171,7 +171,7 @@ impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> { } } -impl BurstController { +impl BurstController { pub fn set_source(&mut self, _source: Source) { todo!("burst mode control registers not implemented") } @@ -186,7 +186,7 @@ impl BurstController { /// It is important to remember that in synchronous topologies, energy can flow in reverse during /// light loading conditions, and that the low-side switch must be active for a short time to drive /// a bootstrapped high-side switch. -pub struct BridgeConverter> { +pub struct BridgeConverter> { timer: PhantomData, channel: PhantomData, dead_time: u16, @@ -195,7 +195,7 @@ pub struct BridgeConverter> BridgeConverter { +impl> BridgeConverter { pub fn new(_channel: C, frequency: Hertz) -> Self { use crate::pac::hrtim::vals::{Activeeffect, Inactiveeffect}; @@ -333,14 +333,14 @@ impl> Bridge /// This implementation of a resonsant converter is appropriate for a half or full bridge, /// but does not include secondary rectification, which is appropriate for applications /// with a low-voltage on the secondary side. -pub struct ResonantConverter> { +pub struct ResonantConverter> { timer: PhantomData, channel: PhantomData, min_period: u16, max_period: u16, } -impl> ResonantConverter { +impl> ResonantConverter { pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { T::set_channel_frequency(C::raw(), min_frequency); @@ -397,13 +397,13 @@ impl> Resona } } -pin_trait!(ChannelAPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelAComplementaryPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelBPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelBComplementaryPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelCPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelCComplementaryPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelDPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelDComplementaryPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelEPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelEComplementaryPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelAPin, Instance); +pin_trait!(ChannelAComplementaryPin, Instance); +pin_trait!(ChannelBPin, Instance); +pin_trait!(ChannelBComplementaryPin, Instance); +pin_trait!(ChannelCPin, Instance); +pin_trait!(ChannelCComplementaryPin, Instance); +pin_trait!(ChannelDPin, Instance); +pin_trait!(ChannelDComplementaryPin, Instance); +pin_trait!(ChannelEPin, Instance); +pin_trait!(ChannelEComplementaryPin, Instance); diff --git a/embassy-stm32/src/hrtim/traits.rs b/embassy-stm32/src/hrtim/traits.rs index 7f2cedda..158a6886 100644 --- a/embassy-stm32/src/hrtim/traits.rs +++ b/embassy-stm32/src/hrtim/traits.rs @@ -2,7 +2,7 @@ use crate::rcc::sealed::RccPeripheral; use crate::time::Hertz; #[derive(Clone, Copy)] -pub(crate) enum HighResolutionControlPrescaler { +pub(crate) enum Prescaler { Div1, Div2, Div4, @@ -13,87 +13,83 @@ pub(crate) enum HighResolutionControlPrescaler { Div128, } -impl From for u32 { - fn from(val: HighResolutionControlPrescaler) -> Self { +impl From for u32 { + fn from(val: Prescaler) -> Self { match val { - HighResolutionControlPrescaler::Div1 => 1, - HighResolutionControlPrescaler::Div2 => 2, - HighResolutionControlPrescaler::Div4 => 4, - HighResolutionControlPrescaler::Div8 => 8, - HighResolutionControlPrescaler::Div16 => 16, - HighResolutionControlPrescaler::Div32 => 32, - HighResolutionControlPrescaler::Div64 => 64, - HighResolutionControlPrescaler::Div128 => 128, + Prescaler::Div1 => 1, + Prescaler::Div2 => 2, + Prescaler::Div4 => 4, + Prescaler::Div8 => 8, + Prescaler::Div16 => 16, + Prescaler::Div32 => 32, + Prescaler::Div64 => 64, + Prescaler::Div128 => 128, } } } -impl From for u8 { - fn from(val: HighResolutionControlPrescaler) -> Self { +impl From for u8 { + fn from(val: Prescaler) -> Self { match val { - HighResolutionControlPrescaler::Div1 => 0b000, - HighResolutionControlPrescaler::Div2 => 0b001, - HighResolutionControlPrescaler::Div4 => 0b010, - HighResolutionControlPrescaler::Div8 => 0b011, - HighResolutionControlPrescaler::Div16 => 0b100, - HighResolutionControlPrescaler::Div32 => 0b101, - HighResolutionControlPrescaler::Div64 => 0b110, - HighResolutionControlPrescaler::Div128 => 0b111, + Prescaler::Div1 => 0b000, + Prescaler::Div2 => 0b001, + Prescaler::Div4 => 0b010, + Prescaler::Div8 => 0b011, + Prescaler::Div16 => 0b100, + Prescaler::Div32 => 0b101, + Prescaler::Div64 => 0b110, + Prescaler::Div128 => 0b111, } } } -impl From for HighResolutionControlPrescaler { +impl From for Prescaler { fn from(val: u8) -> Self { match val { - 0b000 => HighResolutionControlPrescaler::Div1, - 0b001 => HighResolutionControlPrescaler::Div2, - 0b010 => HighResolutionControlPrescaler::Div4, - 0b011 => HighResolutionControlPrescaler::Div8, - 0b100 => HighResolutionControlPrescaler::Div16, - 0b101 => HighResolutionControlPrescaler::Div32, - 0b110 => HighResolutionControlPrescaler::Div64, - 0b111 => HighResolutionControlPrescaler::Div128, + 0b000 => Prescaler::Div1, + 0b001 => Prescaler::Div2, + 0b010 => Prescaler::Div4, + 0b011 => Prescaler::Div8, + 0b100 => Prescaler::Div16, + 0b101 => Prescaler::Div32, + 0b110 => Prescaler::Div64, + 0b111 => Prescaler::Div128, _ => unreachable!(), } } } -impl HighResolutionControlPrescaler { +impl Prescaler { pub fn compute_min_high_res(val: u32) -> Self { *[ - HighResolutionControlPrescaler::Div1, - HighResolutionControlPrescaler::Div2, - HighResolutionControlPrescaler::Div4, - HighResolutionControlPrescaler::Div8, - HighResolutionControlPrescaler::Div16, - HighResolutionControlPrescaler::Div32, - HighResolutionControlPrescaler::Div64, - HighResolutionControlPrescaler::Div128, + Prescaler::Div1, + Prescaler::Div2, + Prescaler::Div4, + Prescaler::Div8, + Prescaler::Div16, + Prescaler::Div32, + Prescaler::Div64, + Prescaler::Div128, ] .iter() - .skip_while(|psc| >::into(**psc) <= val) + .skip_while(|psc| >::into(**psc) <= val) .next() .unwrap() } pub fn compute_min_low_res(val: u32) -> Self { - *[ - HighResolutionControlPrescaler::Div32, - HighResolutionControlPrescaler::Div64, - HighResolutionControlPrescaler::Div128, - ] - .iter() - .skip_while(|psc| >::into(**psc) <= val) - .next() - .unwrap() + *[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128] + .iter() + .skip_while(|psc| >::into(**psc) <= val) + .next() + .unwrap() } } pub(crate) mod sealed { use super::*; - pub trait HighResolutionCaptureCompare16bitInstance: RccPeripheral { + pub trait Instance: RccPeripheral { fn regs() -> crate::pac::hrtim::Hrtim; fn set_master_frequency(frequency: Hertz); @@ -109,14 +105,11 @@ pub(crate) mod sealed { } } -pub trait HighResolutionCaptureCompare16bitInstance: - sealed::HighResolutionCaptureCompare16bitInstance + 'static -{ -} +pub trait Instance: sealed::Instance + 'static {} foreach_interrupt! { ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { - impl sealed::HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { + impl sealed::Instance for crate::peripherals::$inst { fn regs() -> crate::pac::hrtim::Hrtim { crate::pac::$inst } @@ -128,9 +121,9 @@ foreach_interrupt! { let timer_f = Self::frequency().0; let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); let psc = if Self::regs().isr().read().dllrdy() { - HighResolutionControlPrescaler::compute_min_high_res(psc_min) + Prescaler::compute_min_high_res(psc_min) } else { - HighResolutionControlPrescaler::compute_min_low_res(psc_min) + Prescaler::compute_min_low_res(psc_min) }; let psc_val: u32 = psc.into(); @@ -150,9 +143,9 @@ foreach_interrupt! { let timer_f = Self::frequency().0; let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); let psc = if Self::regs().isr().read().dllrdy() { - HighResolutionControlPrescaler::compute_min_high_res(psc_min) + Prescaler::compute_min_high_res(psc_min) } else { - HighResolutionControlPrescaler::compute_min_low_res(psc_min) + Prescaler::compute_min_low_res(psc_min) }; let psc_val: u32 = psc.into(); @@ -169,7 +162,7 @@ foreach_interrupt! { let regs = Self::regs(); - let channel_psc: HighResolutionControlPrescaler = regs.tim(channel).cr().read().ckpsc().into(); + let channel_psc: Prescaler = regs.tim(channel).cr().read().ckpsc().into(); let psc_val: u32 = channel_psc.into(); @@ -177,9 +170,9 @@ foreach_interrupt! { // u9::MAX = 511 let psc_min = (psc_val * dead_time as u32) / (4 * 511); let psc = if Self::regs().isr().read().dllrdy() { - HighResolutionControlPrescaler::compute_min_high_res(psc_min) + Prescaler::compute_min_high_res(psc_min) } else { - HighResolutionControlPrescaler::compute_min_low_res(psc_min) + Prescaler::compute_min_low_res(psc_min) }; let dt_psc_val: u32 = psc.into(); @@ -193,7 +186,7 @@ foreach_interrupt! { } } - impl HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { + impl Instance for crate::peripherals::$inst { } }; From 5bb5654d847b8d7a15f242d4b4078369a1b7285f Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 28 Jul 2023 17:39:01 -0500 Subject: [PATCH 117/298] stm32/hrtim: pub instance --- embassy-stm32/src/hrtim/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 9ed03a21..a930ff73 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -3,11 +3,11 @@ mod traits; use core::marker::PhantomData; use embassy_hal_internal::{into_ref, PeripheralRef}; +pub use traits::Instance; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::AnyPin; -use crate::hrtim::traits::Instance; use crate::time::Hertz; use crate::Peripheral; From 7ed9e29326e42bf286b4f7c5883ef216cfb21531 Mon Sep 17 00:00:00 2001 From: Derek Hageman Date: Tue, 25 Jul 2023 15:54:33 -0600 Subject: [PATCH 118/298] rp: add async flash Implement an async flash mode using the XIP background best effort read interface. Only reads are actually async, write and erase remain blocking. --- embassy-boot/rp/src/lib.rs | 14 +- embassy-rp/Cargo.toml | 3 +- embassy-rp/src/flash.rs | 203 +++++++++++++++++++++- examples/boot/application/rp/src/bin/a.rs | 4 +- examples/rp/src/bin/flash.rs | 42 ++++- tests/rp/src/bin/flash.rs | 14 +- 6 files changed, 254 insertions(+), 26 deletions(-) diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 25329f9e..35fc104e 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -6,7 +6,7 @@ mod fmt; #[cfg(feature = "nightly")] pub use embassy_boot::FirmwareUpdater; pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; -use embassy_rp::flash::{Flash, ERASE_SIZE}; +use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE}; use embassy_rp::peripherals::{FLASH, WATCHDOG}; use embassy_rp::watchdog::Watchdog; use embassy_time::Duration; @@ -58,14 +58,14 @@ impl /// A flash implementation that will feed a watchdog when touching flash. pub struct WatchdogFlash<'d, const SIZE: usize> { - flash: Flash<'d, FLASH, SIZE>, + flash: Flash<'d, FLASH, Blocking, SIZE>, watchdog: Watchdog, } impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { /// Start a new watchdog with a given flash and watchdog peripheral and a timeout pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self { - let flash: Flash<'_, FLASH, SIZE> = Flash::new(flash); + let flash = Flash::<_, Blocking, SIZE>::new(flash); let mut watchdog = Watchdog::new(watchdog); watchdog.start(timeout); Self { flash, watchdog } @@ -73,12 +73,12 @@ impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { } impl<'d, const SIZE: usize> ErrorType for WatchdogFlash<'d, SIZE> { - type Error = as ErrorType>::Error; + type Error = as ErrorType>::Error; } impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> { - const WRITE_SIZE: usize = as NorFlash>::WRITE_SIZE; - const ERASE_SIZE: usize = as NorFlash>::ERASE_SIZE; + const WRITE_SIZE: usize = as NorFlash>::WRITE_SIZE; + const ERASE_SIZE: usize = as NorFlash>::ERASE_SIZE; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { self.watchdog.feed(); @@ -91,7 +91,7 @@ impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> { } impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> { - const READ_SIZE: usize = as ReadNorFlash>::READ_SIZE; + const READ_SIZE: usize = as ReadNorFlash>::READ_SIZE; fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> { self.watchdog.feed(); self.flash.read(offset, data) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index b53c7a01..6310ffb6 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -48,7 +48,7 @@ boot2-w25x10cl = [] run-from-ram = [] # Enable nightly-only features -nightly = ["embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] +nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. @@ -73,6 +73,7 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa chrono = { version = "0.4", default-features = false, optional = true } embedded-io = { version = "0.4.0", features = ["async"], optional = true } embedded-storage = { version = "0.3" } +embedded-storage-async = { version = "0.4.0", optional = true } rand_core = "0.6.4" fixed = "1.23.1" diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 0ed6808e..70d86731 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -1,11 +1,15 @@ +use core::future::Future; use core::marker::PhantomData; +use core::pin::Pin; +use core::task::{Context, Poll}; -use embassy_hal_internal::Peripheral; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embedded_storage::nor_flash::{ check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, }; +use crate::dma::{AnyChannel, Channel, Transfer}; use crate::pac; use crate::peripherals::FLASH; @@ -24,6 +28,7 @@ pub const PAGE_SIZE: usize = 256; pub const WRITE_SIZE: usize = 1; pub const READ_SIZE: usize = 1; pub const ERASE_SIZE: usize = 4096; +pub const ASYNC_READ_SIZE: usize = 4; /// Error type for NVMC operations. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -57,13 +62,46 @@ impl NorFlashError for Error { } } -pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize>(PhantomData<&'d mut T>); +/// Future that waits for completion of a background read +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct BackgroundRead<'a, 'd, T: Instance, const FLASH_SIZE: usize> { + flash: PhantomData<&'a mut Flash<'d, T, Async, FLASH_SIZE>>, + transfer: Transfer<'a, AnyChannel>, +} -impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { - pub fn new(_flash: impl Peripheral

+ 'd) -> Self { - Self(PhantomData) +impl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Future for BackgroundRead<'a, 'd, T, FLASH_SIZE> { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut self.transfer).poll(cx) } +} +impl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Drop for BackgroundRead<'a, 'd, T, FLASH_SIZE> { + fn drop(&mut self) { + if pac::XIP_CTRL.stream_ctr().read().0 == 0 { + return; + } + pac::XIP_CTRL + .stream_ctr() + .write_value(pac::xip_ctrl::regs::StreamCtr(0)); + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + // Errata RP2040-E8: Perform an uncached read to make sure there's not a transfer in + // flight that might effect an address written to start a new transfer. This stalls + // until after any transfer is complete, so the address will not change anymore. + const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x13000000 as *const _; + unsafe { + core::ptr::read_volatile(XIP_NOCACHE_NOALLOC_BASE); + } + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + } +} + +pub struct Flash<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> { + dma: Option>, + phantom: PhantomData<(&'d mut T, M)>, +} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SIZE> { pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { trace!( "Reading from 0x{:x} to 0x{:x}", @@ -182,6 +220,8 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { let ch = crate::pac::DMA.ch(n); while ch.read_addr().read() < SRAM_LOWER && ch.ctrl_trig().read().busy() {} } + // Wait for completion of any background reads + while pac::XIP_CTRL.stream_ctr().read().0 > 0 {} // Run our flash operation in RAM operation(); @@ -210,11 +250,73 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { } } -impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Blocking, FLASH_SIZE> { + pub fn new(_flash: impl Peripheral

+ 'd) -> Self { + Self { + dma: None, + phantom: PhantomData, + } + } +} + +impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> { + pub fn new(_flash: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd) -> Self { + into_ref!(dma); + Self { + dma: Some(dma.map_into()), + phantom: PhantomData, + } + } + + pub fn background_read<'a>( + &'a mut self, + offset: u32, + data: &'a mut [u32], + ) -> Result, Error> { + trace!( + "Reading in background from 0x{:x} to 0x{:x}", + FLASH_BASE as u32 + offset, + FLASH_BASE as u32 + offset + (data.len() * 4) as u32 + ); + // Can't use check_read because we need to enforce 4-byte alignment + let offset = offset as usize; + let length = data.len() * 4; + if length > self.capacity() || offset > self.capacity() - length { + return Err(Error::OutOfBounds); + } + if offset % 4 != 0 { + return Err(Error::Unaligned); + } + + while !pac::XIP_CTRL.stat().read().fifo_empty() { + pac::XIP_CTRL.stream_fifo().read(); + } + + pac::XIP_CTRL + .stream_addr() + .write_value(pac::xip_ctrl::regs::StreamAddr(FLASH_BASE as u32 + offset as u32)); + pac::XIP_CTRL + .stream_ctr() + .write_value(pac::xip_ctrl::regs::StreamCtr(data.len() as u32)); + + // Use the XIP AUX bus port, rather than the FIFO register access (e.x. + // pac::XIP_CTRL.stream_fifo().as_ptr()) to avoid DMA stalling on + // general XIP access. + const XIP_AUX_BASE: *const u32 = 0x50400000 as *const _; + let transfer = unsafe { crate::dma::read(self.dma.as_mut().unwrap(), XIP_AUX_BASE, data, 37) }; + + Ok(BackgroundRead { + flash: PhantomData, + transfer, + }) + } +} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, M, FLASH_SIZE> { type Error = Error; } -impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, M, FLASH_SIZE> { const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -226,9 +328,9 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLA } } -impl<'d, T: Instance, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, FLASH_SIZE> {} +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, M, FLASH_SIZE> {} -impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, M, FLASH_SIZE> { const WRITE_SIZE: usize = WRITE_SIZE; const ERASE_SIZE: usize = ERASE_SIZE; @@ -242,6 +344,74 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_S } } +#[cfg(feature = "nightly")] +impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::ReadNorFlash + for Flash<'d, T, Async, FLASH_SIZE> +{ + const READ_SIZE: usize = ASYNC_READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + use core::mem::MaybeUninit; + + // Checked early to simplify address validity checks + if bytes.len() % 4 != 0 { + return Err(Error::Unaligned); + } + + // If the destination address is already aligned, then we can just DMA directly + if (bytes.as_ptr() as u32) % 4 == 0 { + // Safety: alignment and size have been checked for compatibility + let mut buf: &mut [u32] = + unsafe { core::slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut u32, bytes.len() / 4) }; + self.background_read(offset, &mut buf)?.await; + return Ok(()); + } + + // Destination address is unaligned, so use an intermediate buffer + const REALIGN_CHUNK: usize = PAGE_SIZE; + // Safety: MaybeUninit requires no initialization + let mut buf: [MaybeUninit; REALIGN_CHUNK / 4] = unsafe { MaybeUninit::uninit().assume_init() }; + let mut chunk_offset: usize = 0; + while chunk_offset < bytes.len() { + let chunk_size = (bytes.len() - chunk_offset).min(REALIGN_CHUNK); + let buf = &mut buf[..(chunk_size / 4)]; + + // Safety: this is written to completely by DMA before any reads + let buf = unsafe { &mut *(buf as *mut [MaybeUninit] as *mut [u32]) }; + self.background_read(offset + chunk_offset as u32, buf)?.await; + + // Safety: [u8] has more relaxed alignment and size requirements than [u32], so this is just aliasing + let buf = unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const _, buf.len() * 4) }; + bytes[chunk_offset..(chunk_offset + chunk_size)].copy_from_slice(&buf[..chunk_size]); + + chunk_offset += chunk_size; + } + + Ok(()) + } + + fn capacity(&self) -> usize { + self.capacity() + } +} + +#[cfg(feature = "nightly")] +impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::NorFlash + for Flash<'d, T, Async, FLASH_SIZE> +{ + const WRITE_SIZE: usize = WRITE_SIZE; + + const ERASE_SIZE: usize = ERASE_SIZE; + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to) + } + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes) + } +} + #[allow(dead_code)] mod ram_helpers { use core::marker::PhantomData; @@ -699,9 +869,24 @@ mod ram_helpers { mod sealed { pub trait Instance {} + pub trait Mode {} } pub trait Instance: sealed::Instance {} +pub trait Mode: sealed::Mode {} impl sealed::Instance for FLASH {} impl Instance for FLASH {} + +macro_rules! impl_mode { + ($name:ident) => { + impl sealed::Mode for $name {} + impl Mode for $name {} + }; +} + +pub struct Blocking; +pub struct Async; + +impl_mode!(Blocking); +impl_mode!(Async); diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index c8497494..b5e1950c 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -7,7 +7,7 @@ use core::cell::RefCell; use defmt_rtt as _; use embassy_boot_rp::*; use embassy_executor::Spawner; -use embassy_rp::flash::Flash; +use embassy_rp::flash::{self, Flash}; use embassy_rp::gpio::{Level, Output}; use embassy_rp::watchdog::Watchdog; use embassy_sync::blocking_mutex::Mutex; @@ -34,7 +34,7 @@ async fn main(_s: Spawner) { let mut watchdog = Watchdog::new(p.WATCHDOG); watchdog.start(Duration::from_secs(8)); - let flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); + let flash = Flash::<_, flash::Blocking, FLASH_SIZE>::new(p.FLASH); let flash = Mutex::new(RefCell::new(flash)); let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index 4c4982ac..88bb931d 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; +use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE}; use embassy_rp::peripherals::FLASH; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -25,7 +25,7 @@ async fn main(_spawner: Spawner) { // https://github.com/knurling-rs/defmt/pull/683 Timer::after(Duration::from_millis(10)).await; - let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); + let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0); // Get JEDEC id let jedec = flash.jedec_id().unwrap(); @@ -40,10 +40,12 @@ async fn main(_spawner: Spawner) { multiwrite_bytes(&mut flash, ERASE_SIZE as u32); + background_read(&mut flash, (ERASE_SIZE * 2) as u32).await; + loop {} } -fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { +fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { info!(">>>> [multiwrite_bytes]"); let mut read_buf = [0u8; ERASE_SIZE]; defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); @@ -71,7 +73,7 @@ fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, } } -fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { +fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { info!(">>>> [erase_write_sector]"); let mut buf = [0u8; ERASE_SIZE]; defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); @@ -99,3 +101,35 @@ fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE defmt::panic!("unexpected"); } } + +async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { + info!(">>>> [background_read]"); + + let mut buf = [0u32; 8]; + defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; + + info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); + info!("Contents start with {=u32:x}", buf[0]); + + defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + + defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; + info!("Contents after erase starts with {=u32:x}", buf[0]); + if buf.iter().any(|x| *x != 0xFFFFFFFF) { + defmt::panic!("unexpected"); + } + + for b in buf.iter_mut() { + *b = 0xDABA1234; + } + + defmt::unwrap!(flash.write(ADDR_OFFSET + offset, unsafe { + core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4) + })); + + defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; + info!("Contents after write starts with {=u32:x}", buf[0]); + if buf.iter().any(|x| *x != 0xDABA1234) { + defmt::panic!("unexpected"); + } +} diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index cf9b86df..c31d6dec 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs @@ -6,11 +6,11 @@ mod common; use defmt::*; use embassy_executor::Spawner; -use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; +use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; -const ADDR_OFFSET: u32 = 0x4000; +const ADDR_OFFSET: u32 = 0x8000; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) { // https://github.com/knurling-rs/defmt/pull/683 Timer::after(Duration::from_millis(10)).await; - let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH); + let mut flash = embassy_rp::flash::Flash::<_, Async, { 2 * 1024 * 1024 }>::new(p.FLASH, p.DMA_CH0); // Get JEDEC id let jedec = defmt::unwrap!(flash.jedec_id()); @@ -60,6 +60,14 @@ async fn main(_spawner: Spawner) { defmt::panic!("unexpected"); } + let mut buf = [0u32; ERASE_SIZE / 4]; + + defmt::unwrap!(flash.background_read(ADDR_OFFSET, &mut buf)).await; + info!("Contents after write starts with {=u32:x}", buf[0]); + if buf.iter().any(|x| *x != 0xDADADADA) { + defmt::panic!("unexpected"); + } + info!("Test OK"); cortex_m::asm::bkpt(); } From 0d7b005252a0168c779292bf9457f1a654e42386 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 29 Jul 2023 12:01:32 -0500 Subject: [PATCH 119/298] stm32/pwm: add output type control --- embassy-stm32/src/gpio.rs | 15 +++++++++++++++ embassy-stm32/src/timer/complementary_pwm.rs | 16 ++++++++-------- embassy-stm32/src/timer/simple_pwm.rs | 6 +++--- examples/stm32f4/src/bin/pwm.rs | 3 ++- examples/stm32f4/src/bin/pwm_complementary.rs | 5 +++-- examples/stm32g4/src/bin/pwm.rs | 3 ++- examples/stm32h7/src/bin/pwm.rs | 3 ++- 7 files changed, 35 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index cda59714..d2e61d23 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -4,6 +4,7 @@ use core::convert::Infallible; use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use crate::pac::gpio::{self, vals}; +use crate::usb_otg::Out; use crate::{pac, peripherals, Peripheral}; /// GPIO flexible pin. @@ -502,6 +503,20 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { } } +pub enum OutputType { + PushPull, + OpenDrain, +} + +impl From for sealed::AFType { + fn from(value: OutputType) -> Self { + match value { + OutputType::OpenDrain => sealed::AFType::OutputOpenDrain, + OutputType::PushPull => sealed::AFType::OutputPushPull, + } + } +} + pub(crate) mod sealed { use super::*; diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 64bb32c3..48cb610f 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -7,7 +7,7 @@ use super::simple_pwm::*; use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; -use crate::gpio::AnyPin; +use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; use crate::Peripheral; @@ -17,13 +17,13 @@ pub struct ComplementaryPwmPin<'d, Perip, Channel> { } macro_rules! complementary_channel_impl { - ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => { + ($new_chx:ident, $channel:ident, $pin_trait:ident) => { impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + pub fn $new_chx(pin: impl Peripheral

> + 'd, output_type: OutputType) -> Self { into_ref!(pin); critical_section::with(|_| { pin.set_low(); - pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + pin.set_as_af(pin.af_num(), output_type.into()); #[cfg(gpio_v2)] pin.set_speed(crate::gpio::Speed::VeryHigh); }); @@ -36,10 +36,10 @@ macro_rules! complementary_channel_impl { }; } -complementary_channel_impl!(new_ch1, Ch1, Channel1Pin, Channel1ComplementaryPin); -complementary_channel_impl!(new_ch2, Ch2, Channel2Pin, Channel2ComplementaryPin); -complementary_channel_impl!(new_ch3, Ch3, Channel3Pin, Channel3ComplementaryPin); -complementary_channel_impl!(new_ch4, Ch4, Channel4Pin, Channel4ComplementaryPin); +complementary_channel_impl!(new_ch1, Ch1, Channel1ComplementaryPin); +complementary_channel_impl!(new_ch2, Ch2, Channel2ComplementaryPin); +complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin); +complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin); pub struct ComplementaryPwm<'d, T> { inner: PeripheralRef<'d, T>, diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 51479693..e0a81792 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -5,7 +5,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; -use crate::gpio::AnyPin; +use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; use crate::Peripheral; @@ -22,11 +22,11 @@ pub struct PwmPin<'d, Perip, Channel> { macro_rules! channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident) => { impl<'d, Perip: CaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + pub fn $new_chx(pin: impl Peripheral

> + 'd, output_type: OutputType) -> Self { into_ref!(pin); critical_section::with(|_| { pin.set_low(); - pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + pin.set_as_af(pin.af_num(), output_type.into()); #[cfg(gpio_v2)] pin.set_speed(crate::gpio::Speed::VeryHigh); }); diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 4f130c26..1013a844 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::timer::Channel; @@ -15,7 +16,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PE9); + let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10)); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index 8cc2a411..83a3c753 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; use embassy_stm32::timer::simple_pwm::PwmPin; @@ -16,8 +17,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PE9); - let ch1n = ComplementaryPwmPin::new_ch1(p.PA7); + let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); + let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull); let mut pwm = ComplementaryPwm::new( p.TIM1, Some(ch1), diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index b5a9b995..01e9cb47 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::timer::Channel; @@ -15,7 +16,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PC0); + let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10)); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index adf2ea9c..aa5ec1bc 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::gpio::OutputType; use embassy_stm32::time::{khz, mhz}; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::timer::Channel; @@ -24,7 +25,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PA6); + let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10)); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); From a9f6e30bcdd3d288f1fba71311c9ae1f67f2d25a Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 29 Jul 2023 12:03:01 -0500 Subject: [PATCH 120/298] rustfmt --- embassy-stm32/src/gpio.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index d2e61d23..0cc269cf 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -4,7 +4,6 @@ use core::convert::Infallible; use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use crate::pac::gpio::{self, vals}; -use crate::usb_otg::Out; use crate::{pac, peripherals, Peripheral}; /// GPIO flexible pin. From e0ce7fcde7906fc219d294e858388e62ab107ec3 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Sun, 30 Jul 2023 01:00:50 +0100 Subject: [PATCH 121/298] stm32f2 pll overflow with crystal With a large enough HSE input frequency, the vco clock calculation will overflow a u32. Therefore, in this specific case we have to use the inner value and cast to u64 to ensure the mul isn't clipped before applying the divider. --- embassy-stm32/src/rcc/f2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs index 1525cc3c..bc240fcb 100644 --- a/embassy-stm32/src/rcc/f2.rs +++ b/embassy-stm32/src/rcc/f2.rs @@ -58,7 +58,7 @@ impl Default for PLLConfig { impl PLLConfig { pub fn clocks(&self, src_freq: Hertz) -> PLLClocks { let in_freq = src_freq / self.pre_div; - let vco_freq = src_freq * self.mul / self.pre_div; + let vco_freq = Hertz((src_freq.0 as u64 * self.mul.0 as u64 / self.pre_div.0 as u64) as u32); let main_freq = vco_freq / self.main_div; let pll48_freq = vco_freq / self.pll48_div; PLLClocks { From bae31ebce7f5cf30a24e0af2157f962feb74f72c Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 29 Jul 2023 19:25:18 -0500 Subject: [PATCH 122/298] stm32/dma: rename ringbuf --- embassy-stm32/src/dma/bdma.rs | 16 ++++++++-------- embassy-stm32/src/dma/dma.rs | 12 ++++++------ embassy-stm32/src/dma/ringbuffer.rs | 18 +++++++++--------- embassy-stm32/src/usart/ringbuffered.rs | 6 +++--- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index d956047d..1dad364b 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -9,7 +9,7 @@ use atomic_polyfill::AtomicUsize; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; +use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::BDMA_CHANNEL_COUNT; @@ -395,13 +395,13 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { } } -pub struct RingBuffer<'a, C: Channel, W: Word> { +pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { cr: regs::Cr, channel: PeripheralRef<'a, C>, - ringbuf: DmaRingBuffer<'a, W>, + ringbuf: ReadableDmaRingBuffer<'a, W>, } -impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { +impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { pub unsafe fn new_read( channel: impl Peripheral

+ 'a, _request: Request, @@ -442,7 +442,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { let mut this = Self { channel, cr: w, - ringbuf: DmaRingBuffer::new(buffer), + ringbuf: ReadableDmaRingBuffer::new(buffer), }; this.clear_irqs(); @@ -475,7 +475,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) } - /// Read an exact number of elements from the ringbuffer. + /// Read an exact number of elements from the ReadableRingBuffer. /// /// 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. @@ -513,7 +513,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { .await } - /// The capacity of the ringbuffer + /// The capacity of the ReadableRingBuffer pub fn cap(&self) -> usize { self.ringbuf.cap() } @@ -550,7 +550,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { } } -impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> { +impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> { fn drop(&mut self) { self.request_stop(); while self.is_running() {} diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 219ef2eb..9157f72d 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -7,7 +7,7 @@ use core::task::{Context, Poll, Waker}; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; +use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::DMA_CHANNEL_COUNT; @@ -625,13 +625,13 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { } } -pub struct RingBuffer<'a, C: Channel, W: Word> { +pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { cr: regs::Cr, channel: PeripheralRef<'a, C>, - ringbuf: DmaRingBuffer<'a, W>, + ringbuf: ReadableDmaRingBuffer<'a, W>, } -impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { +impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { pub unsafe fn new_read( channel: impl Peripheral

+ 'a, _request: Request, @@ -677,7 +677,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { let mut this = Self { channel, cr: w, - ringbuf: DmaRingBuffer::new(buffer), + ringbuf: ReadableDmaRingBuffer::new(buffer), }; this.clear_irqs(); @@ -797,7 +797,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { } } -impl<'a, C: Channel, W: Word> Drop for RingBuffer<'a, C, W> { +impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> { fn drop(&mut self) { self.request_stop(); while self.is_running() {} diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 19079397..92be3334 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -29,7 +29,7 @@ use super::word::Word; /// | | | | /// +- end --------------------+ +- start ----------------+ /// ``` -pub struct DmaRingBuffer<'a, W: Word> { +pub struct ReadableDmaRingBuffer<'a, W: Word> { pub(crate) dma_buf: &'a mut [W], start: usize, } @@ -51,7 +51,7 @@ pub trait DmaCtrl { fn reset_complete_count(&mut self) -> usize; } -impl<'a, W: Word> DmaRingBuffer<'a, W> { +impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { pub fn new(dma_buf: &'a mut [W]) -> Self { Self { dma_buf, start: 0 } } @@ -263,7 +263,7 @@ mod tests { #[test] fn empty_and_read_not_started() { let mut dma_buf = [0u8; 16]; - let ringbuf = DmaRingBuffer::new(&mut dma_buf); + let ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); } @@ -273,7 +273,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -314,7 +314,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -349,7 +349,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -384,7 +384,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -420,7 +420,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); @@ -454,7 +454,7 @@ mod tests { let mut dma = TestCircularTransfer::new(16); let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = DmaRingBuffer::new(&mut dma_buf); + let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); assert_eq!(0, ringbuf.start); assert_eq!(16, ringbuf.cap()); diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 80261d04..71077c07 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -6,12 +6,12 @@ use embassy_hal_internal::PeripheralRef; use futures::future::{select, Either}; use super::{clear_interrupt_flags, rdr, sr, BasicInstance, Error, UartRx}; -use crate::dma::RingBuffer; +use crate::dma::ReadableRingBuffer; use crate::usart::{Regs, Sr}; pub struct RingBufferedUartRx<'d, T: BasicInstance, RxDma: super::RxDma> { _peri: PeripheralRef<'d, T>, - ring_buf: RingBuffer<'d, RxDma, u8>, + ring_buf: ReadableRingBuffer<'d, RxDma, u8>, } impl<'d, T: BasicInstance, RxDma: super::RxDma> UartRx<'d, T, RxDma> { @@ -24,7 +24,7 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> UartRx<'d, T, RxDma> { let request = self.rx_dma.request(); let opts = Default::default(); - let ring_buf = unsafe { RingBuffer::new_read(self.rx_dma, request, rdr(T::regs()), dma_buf, opts) }; + let ring_buf = unsafe { ReadableRingBuffer::new_read(self.rx_dma, request, rdr(T::regs()), dma_buf, opts) }; RingBufferedUartRx { _peri: self._peri, From 6256a6c57c82b775241228636b6f344ca1722fa3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 29 Jul 2023 19:27:16 -0500 Subject: [PATCH 123/298] fix comments --- embassy-stm32/src/dma/bdma.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 1dad364b..7b5008f0 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -475,7 +475,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) } - /// Read an exact number of elements from the ReadableRingBuffer. + /// 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. @@ -513,7 +513,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { .await } - /// The capacity of the ReadableRingBuffer + /// The capacity of the ringbuffer. pub fn cap(&self) -> usize { self.ringbuf.cap() } From 8064f4bfe07c407884d412ce4820153e607c68b4 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 29 Jul 2023 20:10:29 -0500 Subject: [PATCH 124/298] stm32/dma: add draft writable dma buf --- embassy-stm32/src/dma/ringbuffer.rs | 93 +++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 92be3334..db367298 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -197,6 +197,99 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { length } } + +pub struct WritableDmaRingBuffer<'a, W: Word> { + pub(crate) dma_buf: &'a mut [W], + end: usize, +} + +impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { + pub fn new(dma_buf: &'a mut [W]) -> Self { + Self { dma_buf, end: 0 } + } + + /// Reset the ring buffer to its initial state + pub fn clear(&mut self, mut dma: impl DmaCtrl) { + self.end = 0; + dma.reset_complete_count(); + } + + /// The capacity of the ringbuffer + pub const fn cap(&self) -> usize { + self.dma_buf.len() + } + + /// The current position of the ringbuffer + fn pos(&self, remaining_transfers: usize) -> usize { + self.cap() - remaining_transfers + } + + /// Write elements from the ring buffer + /// Return a tuple of the length written and the capacity remaining to be written in the buffer + pub fn write(&mut self, mut dma: impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { + let start = self.pos(dma.get_remaining_transfers()); + if start < self.end && self.end + buf.len() < self.cap() { + // The available, unwritten portion in the ring buffer DOES NOT wrap + // and copying elements into the buffer will not cause it to + + // Copy into the dma buffer + let len = self.copy_from(buf, self.end..self.cap()); + + compiler_fence(Ordering::SeqCst); + + // Confirm that the DMA is not inside data we could have written + let pos = self.pos(dma.get_remaining_transfers()); + if pos > self.end || pos <= start || dma.get_complete_count() > 1 { + Err(OverrunError) + } else { + self.end = (self.end + len) % self.cap(); + + Ok((len, self.cap() - (self.end - start))) + } + } else if self.end > start { + // The available, unwritten portion in the ring buffer DOES wrap + let len = self.copy_from(buf, self.end..start); + + compiler_fence(Ordering::SeqCst); + + dma.get_complete_count(); + + todo!() + } else if start < self.end && self.end + buf.len() >= self.cap() { + // The available, unwritten portion in the ring buffer DOES NOT wrap + // and copying elements into the buffer will cause it to + + let tail = self.copy_from(buf, self.end..self.cap()); + let head = self.copy_from(&buf[tail..], 0..start); + + compiler_fence(Ordering::SeqCst); + + dma.reset_complete_count(); + + todo!() + } else { + todo!() + } + } + /// Copy into the dma buffer at `data_range` from `buf` + fn copy_from(&mut self, buf: &[W], data_range: Range) -> usize { + // Limit the number of elements that can be copied + let length = usize::min(data_range.len(), buf.len()); + + // Copy into dma buffer from read buffer + // We need to do it like this instead of a simple copy_from_slice() because + // reading from a part of memory that may be simultaneously written to is unsafe + unsafe { + let dma_buf = self.dma_buf.as_mut_ptr(); + + for i in 0..length { + core::ptr::write_volatile(dma_buf.offset((data_range.start + i) as isize), buf[i]); + } + } + + length + } +} #[cfg(test)] mod tests { use core::array; From 603c4cb4fa5f3dc2d95c5e47f13149beaa227bf5 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 09:18:33 -0500 Subject: [PATCH 125/298] stm32/dma: complete initial ringbuf impl. --- embassy-stm32/src/dma/dma.rs | 171 +++++++++++++++++++++++++++- embassy-stm32/src/dma/ringbuffer.rs | 50 ++++---- 2 files changed, 200 insertions(+), 21 deletions(-) diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 9157f72d..3c5c79fd 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -7,7 +7,7 @@ use core::task::{Context, Poll, Waker}; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer}; +use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::DMA_CHANNEL_COUNT; @@ -806,3 +806,172 @@ impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> { fence(Ordering::SeqCst); } } + +pub struct WritableRingBuffer<'a, C: Channel, W: Word> { + cr: regs::Cr, + channel: PeripheralRef<'a, C>, + ringbuf: WritableDmaRingBuffer<'a, W>, +} + +impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { + pub unsafe fn new_read( + channel: impl Peripheral

+ 'a, + _request: Request, + peri_addr: *mut W, + buffer: &'a mut [W], + options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let len = buffer.len(); + assert!(len > 0 && len <= 0xFFFF); + + let dir = Dir::MemoryToPeripheral; + let data_size = W::size(); + + let channel_number = channel.num(); + let dma = channel.regs(); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + + let mut w = regs::Cr(0); + w.set_dir(dir.into()); + w.set_msize(data_size.into()); + w.set_psize(data_size.into()); + w.set_pl(vals::Pl::VERYHIGH); + w.set_minc(vals::Inc::INCREMENTED); + w.set_pinc(vals::Inc::FIXED); + w.set_teie(true); + w.set_htie(options.half_transfer_ir); + w.set_tcie(true); + w.set_circ(vals::Circ::ENABLED); + #[cfg(dma_v1)] + w.set_trbuff(true); + #[cfg(dma_v2)] + w.set_chsel(_request); + w.set_pburst(options.pburst.into()); + w.set_mburst(options.mburst.into()); + w.set_pfctrl(options.flow_ctrl.into()); + w.set_en(true); + + let buffer_ptr = buffer.as_mut_ptr(); + let mut this = Self { + channel, + cr: w, + ringbuf: WritableDmaRingBuffer::new(buffer), + }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); + + let ch = dma.st(channel_number); + ch.par().write_value(peri_addr as u32); + ch.m0ar().write_value(buffer_ptr as u32); + ch.ndtr().write_value(regs::Ndtr(len as _)); + ch.fcr().write(|w| { + if let Some(fth) = options.fifo_threshold { + // FIFO mode + w.set_dmdis(vals::Dmdis::DISABLED); + w.set_fth(fth.into()); + } else { + // Direct mode + w.set_dmdis(vals::Dmdis::ENABLED); + } + }); + + this + } + + pub fn start(&mut self) { + let ch = self.channel.regs().st(self.channel.num()); + ch.cr().write_value(self.cr); + } + + pub fn clear(&mut self) { + self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + } + + /// Write elements from the ring buffer + /// Return a tuple of the length written and the length remaining in the buffer + pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { + self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) + } + + /// Write an exact number of elements from the ringbuffer. + pub async fn write_exact(&mut self, buffer: &[W]) -> Result { + use core::future::poll_fn; + use core::sync::atomic::compiler_fence; + + let mut written_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + self.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + match self.write(&buffer[written_data..buffer_len]) { + Ok((len, remaining)) => { + written_data += len; + if written_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() + } + + pub fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.channel.index()].register(waker); + } + + fn clear_irqs(&mut self) { + let channel_number = self.channel.num(); + let dma = self.channel.regs(); + let isrn = channel_number / 4; + let isrbit = channel_number % 4; + + dma.ifcr(isrn).write(|w| { + w.set_htif(isrbit, true); + w.set_tcif(isrbit, true); + w.set_teif(isrbit, true); + }); + } + + pub fn request_stop(&mut self) { + let ch = self.channel.regs().st(self.channel.num()); + + // Disable the channel. Keep the IEs enabled so the irqs still fire. + ch.cr().write(|w| { + w.set_teie(true); + w.set_htie(true); + w.set_tcie(true); + }); + } + + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().st(self.channel.num()); + ch.cr().read().en() + } +} + +impl<'a, C: Channel, W: Word> Drop for WritableRingBuffer<'a, C, W> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } +} diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index db367298..e9d33021 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -228,9 +228,24 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { /// Return a tuple of the length written and the capacity remaining to be written in the buffer pub fn write(&mut self, mut dma: impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { let start = self.pos(dma.get_remaining_transfers()); - if start < self.end && self.end + buf.len() < self.cap() { - // The available, unwritten portion in the ring buffer DOES NOT wrap - // and copying elements into the buffer will not cause it to + if start > self.end { + // The occupied portion in the ring buffer DOES wrap + let len = self.copy_from(buf, self.end..start); + + compiler_fence(Ordering::SeqCst); + + // Confirm that the DMA is not inside data we could have written + let pos = self.pos(dma.get_remaining_transfers()); + if (pos > self.end && pos <= start) || dma.get_complete_count() > 1 { + Err(OverrunError) + } else { + self.end = (self.end + len) % self.cap(); + + Ok((len, self.cap() - (start - self.end))) + } + } else if start <= self.end && self.end + buf.len() < self.cap() { + // The occupied portion in the ring buffer DOES NOT wrap + // and copying elements into the buffer WILL NOT cause it to // Copy into the dma buffer let len = self.copy_from(buf, self.end..self.cap()); @@ -239,36 +254,31 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { // Confirm that the DMA is not inside data we could have written let pos = self.pos(dma.get_remaining_transfers()); - if pos > self.end || pos <= start || dma.get_complete_count() > 1 { + if pos > self.end || pos < start || dma.get_complete_count() > 1 { Err(OverrunError) } else { self.end = (self.end + len) % self.cap(); Ok((len, self.cap() - (self.end - start))) } - } else if self.end > start { - // The available, unwritten portion in the ring buffer DOES wrap - let len = self.copy_from(buf, self.end..start); - - compiler_fence(Ordering::SeqCst); - - dma.get_complete_count(); - - todo!() - } else if start < self.end && self.end + buf.len() >= self.cap() { - // The available, unwritten portion in the ring buffer DOES NOT wrap - // and copying elements into the buffer will cause it to + } else { + // The occupied portion in the ring buffer DOES NOT wrap + // and copying elements into the buffer WILL cause it to let tail = self.copy_from(buf, self.end..self.cap()); let head = self.copy_from(&buf[tail..], 0..start); compiler_fence(Ordering::SeqCst); - dma.reset_complete_count(); + // Confirm that the DMA is not inside data we could have written + let pos = self.pos(dma.get_remaining_transfers()); + if pos > self.end || pos < start || dma.reset_complete_count() > 1 { + Err(OverrunError) + } else { + self.end = head; - todo!() - } else { - todo!() + Ok((tail + head, self.cap() - (start - self.end))) + } } } /// Copy into the dma buffer at `data_range` from `buf` From fd9b6487e12dff80bf9e23cba474af5d8773c8a7 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 09:25:58 -0500 Subject: [PATCH 126/298] stm32/dma: impl. wringbuf for bdma --- embassy-stm32/src/dma/bdma.rs | 154 +++++++++++++++++++++++++++++++++- embassy-stm32/src/dma/dma.rs | 4 +- 2 files changed, 155 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 7b5008f0..2905338d 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -9,7 +9,7 @@ use atomic_polyfill::AtomicUsize; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer}; +use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use super::word::{Word, WordSize}; use super::Dir; use crate::_generated::BDMA_CHANNEL_COUNT; @@ -559,3 +559,155 @@ impl<'a, C: Channel, W: Word> Drop for ReadableRingBuffer<'a, C, W> { fence(Ordering::SeqCst); } } + +pub struct WritableRingBuffer<'a, C: Channel, W: Word> { + cr: regs::Cr, + channel: PeripheralRef<'a, C>, + ringbuf: WritableDmaRingBuffer<'a, W>, +} + +impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { + pub unsafe fn new_write( + channel: impl Peripheral

+ 'a, + _request: Request, + peri_addr: *mut W, + buffer: &'a mut [W], + _options: TransferOptions, + ) -> Self { + into_ref!(channel); + + let len = buffer.len(); + assert!(len > 0 && len <= 0xFFFF); + + let dir = Dir::MemoryToPeripheral; + let data_size = W::size(); + + let channel_number = channel.num(); + let dma = channel.regs(); + + // "Preceding reads and writes cannot be moved past subsequent writes." + fence(Ordering::SeqCst); + + #[cfg(bdma_v2)] + critical_section::with(|_| channel.regs().cselr().modify(|w| w.set_cs(channel.num(), _request))); + + let mut w = regs::Cr(0); + w.set_psize(data_size.into()); + w.set_msize(data_size.into()); + w.set_minc(vals::Inc::ENABLED); + w.set_dir(dir.into()); + w.set_teie(true); + w.set_htie(true); + w.set_tcie(true); + w.set_circ(vals::Circ::ENABLED); + w.set_pl(vals::Pl::VERYHIGH); + w.set_en(true); + + let buffer_ptr = buffer.as_mut_ptr(); + let mut this = Self { + channel, + cr: w, + ringbuf: WritableDmaRingBuffer::new(buffer), + }; + this.clear_irqs(); + + #[cfg(dmamux)] + super::dmamux::configure_dmamux(&mut *this.channel, _request); + + let ch = dma.ch(channel_number); + ch.par().write_value(peri_addr as u32); + ch.mar().write_value(buffer_ptr as u32); + ch.ndtr().write(|w| w.set_ndt(len as u16)); + + this + } + + pub fn start(&mut self) { + let ch = self.channel.regs().ch(self.channel.num()); + ch.cr().write_value(self.cr) + } + + pub fn clear(&mut self) { + self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + } + + /// Write elements from the ring buffer + /// Return a tuple of the length written and the length remaining in the buffer + pub fn read(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { + self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) + } + + /// Write an exact number of elements to the ringbuffer. + pub async fn write_exact(&mut self, buffer: &[W]) -> Result { + use core::future::poll_fn; + use core::sync::atomic::compiler_fence; + + let mut written_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + self.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + match self.read(&buffer[written_data..buffer_len]) { + Ok((len, remaining)) => { + written_data += len; + if written_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() + } + + pub fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.channel.index()].register(waker); + } + + fn clear_irqs(&mut self) { + let dma = self.channel.regs(); + dma.ifcr().write(|w| { + w.set_htif(self.channel.num(), true); + w.set_tcif(self.channel.num(), true); + w.set_teif(self.channel.num(), true); + }); + } + + pub fn request_stop(&mut self) { + let ch = self.channel.regs().ch(self.channel.num()); + + // Disable the channel. Keep the IEs enabled so the irqs still fire. + // If the channel is enabled and transfer is not completed, we need to perform + // two separate write access to the CR register to disable the channel. + ch.cr().write(|w| { + w.set_teie(true); + w.set_htie(true); + w.set_tcie(true); + }); + } + + pub fn is_running(&mut self) -> bool { + let ch = self.channel.regs().ch(self.channel.num()); + ch.cr().read().en() + } +} + +impl<'a, C: Channel, W: Word> Drop for WritableRingBuffer<'a, C, W> { + fn drop(&mut self) { + self.request_stop(); + while self.is_running() {} + + // "Subsequent reads and writes cannot be moved ahead of preceding reads." + fence(Ordering::SeqCst); + } +} diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 3c5c79fd..9cd7aa8d 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -814,7 +814,7 @@ pub struct WritableRingBuffer<'a, C: Channel, W: Word> { } impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { - pub unsafe fn new_read( + pub unsafe fn new_write( channel: impl Peripheral

+ 'a, _request: Request, peri_addr: *mut W, @@ -899,7 +899,7 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) } - /// Write an exact number of elements from the ringbuffer. + /// Write an exact number of elements to the ringbuffer. pub async fn write_exact(&mut self, buffer: &[W]) -> Result { use core::future::poll_fn; use core::sync::atomic::compiler_fence; From 087e649bc297676c483f1f2a94a5abf101dea9a2 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 09:28:02 -0500 Subject: [PATCH 127/298] stm32/dma: fix typos --- embassy-stm32/src/dma/bdma.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 2905338d..60f4fbd0 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -631,9 +631,9 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); } - /// Write elements from the ring buffer + /// Write elements to the ring buffer /// Return a tuple of the length written and the length remaining in the buffer - pub fn read(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { + pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) } @@ -650,7 +650,7 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { compiler_fence(Ordering::SeqCst); - match self.read(&buffer[written_data..buffer_len]) { + match self.write(&buffer[written_data..buffer_len]) { Ok((len, remaining)) => { written_data += len; if written_data == buffer_len { From 2f18770e276d985c16ba865636bdc641cb2cf74a Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 22 Apr 2023 14:26:40 -0500 Subject: [PATCH 128/298] stm32/rcc: extract and combine ahb/apb prescalers --- embassy-stm32/src/rcc/c0.rs | 55 +--------- embassy-stm32/src/rcc/common.rs | 174 ++++++++++++++++++++++++++++++++ embassy-stm32/src/rcc/f2.rs | 118 ++-------------------- embassy-stm32/src/rcc/g0.rs | 76 ++------------ embassy-stm32/src/rcc/g4.rs | 36 +------ embassy-stm32/src/rcc/h5.rs | 104 ++----------------- embassy-stm32/src/rcc/h7.rs | 16 +-- embassy-stm32/src/rcc/l0.rs | 53 +--------- embassy-stm32/src/rcc/l1.rs | 53 +--------- embassy-stm32/src/rcc/l4.rs | 53 +--------- embassy-stm32/src/rcc/l5.rs | 53 +--------- embassy-stm32/src/rcc/mod.rs | 2 + embassy-stm32/src/rcc/u5.rs | 79 ++------------- embassy-stm32/src/rcc/wb.rs | 68 +------------ embassy-stm32/src/rcc/wl.rs | 87 ++-------------- 15 files changed, 229 insertions(+), 798 deletions(-) create mode 100644 embassy-stm32/src/rcc/common.rs diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index df6e9047..6a932634 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -1,5 +1,6 @@ +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::{Hpre, Hsidiv, Ppre, Sw}; +use crate::pac::rcc::vals::{Hsidiv, Ppre, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -45,58 +46,6 @@ impl Into for HSIPrescaler { } } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -impl Into for APBPrescaler { - fn into(self) -> Ppre { - match self { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl Into for AHBPrescaler { - fn into(self) -> Hpre { - match self { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - /// Clocks configutation pub struct Config { pub mux: ClockSrc, diff --git a/embassy-stm32/src/rcc/common.rs b/embassy-stm32/src/rcc/common.rs new file mode 100644 index 00000000..7debb143 --- /dev/null +++ b/embassy-stm32/src/rcc/common.rs @@ -0,0 +1,174 @@ +use core::ops::Div; + +#[allow(unused_imports)] +use crate::pac::rcc; +use crate::time::Hertz; + +/// Voltage Scale +/// +/// Represents the voltage range feeding the CPU core. The maximum core +/// clock frequency depends on this value. +/// +/// Scale0 represents the highest voltage range +#[derive(Copy, Clone, PartialEq)] +pub enum VoltageScale { + Scale0, + Scale1, + #[cfg(not(any(rcc_wl5, rcc_wle)))] + Scale2, + #[cfg(not(any(rcc_wl5, rcc_wle)))] + Scale3, +} + +/// AHB prescaler +#[derive(Clone, Copy, PartialEq)] +pub enum AHBPrescaler { + NotDivided, + Div2, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + Div3, + Div4, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + Div5, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + Div6, + Div8, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + Div10, + Div16, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + Div32, + Div64, + Div128, + Div256, + Div512, +} + +impl Div for Hertz { + type Output = Hertz; + + fn div(self, rhs: AHBPrescaler) -> Self::Output { + let divisor = match rhs { + AHBPrescaler::NotDivided => 1, + AHBPrescaler::Div2 => 2, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + AHBPrescaler::Div3 => 3, + AHBPrescaler::Div4 => 4, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + AHBPrescaler::Div5 => 5, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + AHBPrescaler::Div6 => 6, + AHBPrescaler::Div8 => 8, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + AHBPrescaler::Div10 => 10, + AHBPrescaler::Div16 => 16, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + AHBPrescaler::Div32 => 32, + AHBPrescaler::Div64 => 64, + AHBPrescaler::Div128 => 128, + AHBPrescaler::Div256 => 256, + AHBPrescaler::Div512 => 512, + }; + Hertz(self.0 / divisor) + } +} + +#[cfg(not(any(rcc_g4, rcc_wb, rcc_wl5, rcc_wle)))] +impl From for rcc::vals::Hpre { + fn from(val: AHBPrescaler) -> rcc::vals::Hpre { + use rcc::vals::Hpre; + + match val { + #[cfg(not(rcc_u5))] + AHBPrescaler::NotDivided => Hpre::DIV1, + #[cfg(rcc_u5)] + AHBPrescaler::NotDivided => Hpre::NONE, + AHBPrescaler::Div2 => Hpre::DIV2, + AHBPrescaler::Div4 => Hpre::DIV4, + AHBPrescaler::Div8 => Hpre::DIV8, + AHBPrescaler::Div16 => Hpre::DIV16, + AHBPrescaler::Div64 => Hpre::DIV64, + AHBPrescaler::Div128 => Hpre::DIV128, + AHBPrescaler::Div256 => Hpre::DIV256, + AHBPrescaler::Div512 => Hpre::DIV512, + } + } +} + +#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] +impl From for u8 { + fn from(val: AHBPrescaler) -> u8 { + match val { + AHBPrescaler::NotDivided => 0x0, + AHBPrescaler::Div2 => 0x08, + AHBPrescaler::Div3 => 0x01, + AHBPrescaler::Div4 => 0x09, + AHBPrescaler::Div5 => 0x02, + AHBPrescaler::Div6 => 0x05, + AHBPrescaler::Div8 => 0x0a, + AHBPrescaler::Div10 => 0x06, + AHBPrescaler::Div16 => 0x0b, + AHBPrescaler::Div32 => 0x07, + AHBPrescaler::Div64 => 0x0c, + AHBPrescaler::Div128 => 0x0d, + AHBPrescaler::Div256 => 0x0e, + AHBPrescaler::Div512 => 0x0f, + } + } +} + +/// APB prescaler +#[derive(Clone, Copy)] +pub enum APBPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, +} + +impl Div for Hertz { + type Output = Hertz; + + fn div(self, rhs: APBPrescaler) -> Self::Output { + let divisor = match rhs { + APBPrescaler::NotDivided => 1, + APBPrescaler::Div2 => 2, + APBPrescaler::Div4 => 4, + APBPrescaler::Div8 => 8, + APBPrescaler::Div16 => 16, + }; + Hertz(self.0 / divisor) + } +} + +#[cfg(not(any(rcc_f1, rcc_g4, rcc_h7, rcc_wb, rcc_wl5, rcc_wle)))] +impl From for rcc::vals::Ppre { + fn from(val: APBPrescaler) -> rcc::vals::Ppre { + use rcc::vals::Ppre; + + match val { + #[cfg(not(rcc_u5))] + APBPrescaler::NotDivided => Ppre::DIV1, + #[cfg(rcc_u5)] + APBPrescaler::NotDivided => Ppre::NONE, + APBPrescaler::Div2 => Ppre::DIV2, + APBPrescaler::Div4 => Ppre::DIV4, + APBPrescaler::Div8 => Ppre::DIV8, + APBPrescaler::Div16 => Ppre::DIV16, + } + } +} + +#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] +impl From for u8 { + fn from(val: APBPrescaler) -> u8 { + match val { + APBPrescaler::NotDivided => 1, + APBPrescaler::Div2 => 0x04, + APBPrescaler::Div4 => 0x05, + APBPrescaler::Div8 => 0x06, + APBPrescaler::Div16 => 0x07, + } + } +} diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs index 1525cc3c..69e89113 100644 --- a/embassy-stm32/src/rcc/f2.rs +++ b/embassy-stm32/src/rcc/f2.rs @@ -1,8 +1,9 @@ use core::convert::TryFrom; use core::ops::{Div, Mul}; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::{Hpre, Pllp, Pllsrc, Ppre, Sw}; +use crate::pac::rcc::vals::{Pllp, Pllsrc, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -200,114 +201,15 @@ pub struct PLLClocks { pub pll48_freq: Hertz, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} +pub use super::common::VoltageScale; -impl Div for Hertz { - type Output = Hertz; - - fn div(self, rhs: AHBPrescaler) -> Self::Output { - let divisor = match rhs { - AHBPrescaler::NotDivided => 1, - AHBPrescaler::Div2 => 2, - AHBPrescaler::Div4 => 4, - AHBPrescaler::Div8 => 8, - AHBPrescaler::Div16 => 16, - AHBPrescaler::Div64 => 64, - AHBPrescaler::Div128 => 128, - AHBPrescaler::Div256 => 256, - AHBPrescaler::Div512 => 512, - }; - Hertz(self.0 / divisor) - } -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -impl Div for Hertz { - type Output = Hertz; - - fn div(self, rhs: APBPrescaler) -> Self::Output { - let divisor = match rhs { - APBPrescaler::NotDivided => 1, - APBPrescaler::Div2 => 2, - APBPrescaler::Div4 => 4, - APBPrescaler::Div8 => 8, - APBPrescaler::Div16 => 16, - }; - Hertz(self.0 / divisor) - } -} - -impl Into for APBPrescaler { - fn into(self) -> Ppre { - match self { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl Into for AHBPrescaler { - fn into(self) -> Hpre { - match self { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - -/// Voltage Range -/// -/// Represents the system supply voltage range -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageRange { - /// 1.8 to 3.6 V - Min1V8, - /// 2.1 to 3.6 V - Min2V1, - /// 2.4 to 3.6 V - Min2V4, - /// 2.7 to 3.6 V - Min2V7, -} - -impl VoltageRange { +impl VoltageScale { const fn wait_states(&self, ahb_freq: Hertz) -> Option { let ahb_freq = ahb_freq.0; // Reference: RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock // frequency match self { - VoltageRange::Min1V8 => { + VoltageScale::Scale3 => { if ahb_freq <= 16_000_000 { Some(Latency::WS0) } else if ahb_freq <= 32_000_000 { @@ -328,7 +230,7 @@ impl VoltageRange { None } } - VoltageRange::Min2V1 => { + VoltageScale::Scale2 => { if ahb_freq <= 18_000_000 { Some(Latency::WS0) } else if ahb_freq <= 36_000_000 { @@ -347,7 +249,7 @@ impl VoltageRange { None } } - VoltageRange::Min2V4 => { + VoltageScale::Scale1 => { if ahb_freq <= 24_000_000 { Some(Latency::WS0) } else if ahb_freq <= 48_000_000 { @@ -362,7 +264,7 @@ impl VoltageRange { None } } - VoltageRange::Min2V7 => { + VoltageScale::Scale0 => { if ahb_freq <= 30_000_000 { Some(Latency::WS0) } else if ahb_freq <= 60_000_000 { @@ -386,7 +288,7 @@ pub struct Config { pub pll_mux: PLLSrc, pub pll: PLLConfig, pub mux: ClockSrc, - pub voltage: VoltageRange, + pub voltage: VoltageScale, pub ahb_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, @@ -400,7 +302,7 @@ impl Default for Config { hsi: true, pll_mux: PLLSrc::HSI, pll: PLLConfig::default(), - voltage: VoltageRange::Min1V8, + voltage: VoltageScale::Scale3, mux: ClockSrc::HSI, ahb_pre: AHBPrescaler::NotDivided, apb1_pre: APBPrescaler::NotDivided, diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index 5e3a7911..1aaf983a 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs @@ -1,5 +1,6 @@ +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::{self, Hpre, Hsidiv, Ppre, Sw}; +use crate::pac::rcc::vals::{self, Hsidiv, Ppre, Sw}; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -172,58 +173,6 @@ impl From for u32 { } } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -impl Into for APBPrescaler { - fn into(self) -> Ppre { - match self { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl Into for AHBPrescaler { - fn into(self) -> Hpre { - match self { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - /// Clocks configutation pub struct Config { pub mux: ClockSrc, @@ -425,25 +374,14 @@ pub(crate) unsafe fn init(config: Config) { FLASH.acr().modify(|w| w.set_latency(target_flash_latency)); } - let ahb_div = match config.ahb_pre { - AHBPrescaler::NotDivided => 1, - AHBPrescaler::Div2 => 2, - AHBPrescaler::Div4 => 4, - AHBPrescaler::Div8 => 8, - AHBPrescaler::Div16 => 16, - AHBPrescaler::Div64 => 64, - AHBPrescaler::Div128 => 128, - AHBPrescaler::Div256 => 256, - AHBPrescaler::Div512 => 512, - }; - let ahb_freq = sys_clk / ahb_div; + let ahb_freq = Hertz(sys_clk) / config.ahb_pre; let (apb_freq, apb_tim_freq) = match config.apb_pre { - APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + APBPrescaler::NotDivided => (ahb_freq.0, ahb_freq.0), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.to_bits() - 3); - let freq = ahb_freq / pre as u32; + let pre: u8 = 1 << (pre.0 - 3); + let freq = ahb_freq.0 / pre as u32; (freq, freq * 2) } }; @@ -455,7 +393,7 @@ pub(crate) unsafe fn init(config: Config) { set_freqs(Clocks { sys: Hertz(sys_clk), - ahb1: Hertz(ahb_freq), + ahb1: ahb_freq, apb1: Hertz(apb_freq), apb1_tim: Hertz(apb_tim_freq), }); diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index ff8f9754..5489b718 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -2,6 +2,7 @@ use stm32_metapac::flash::vals::Latency; use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw}; use stm32_metapac::FLASH; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::{PWR, RCC}; use crate::rcc::sealed::RccPeripheral; use crate::rcc::{set_freqs, Clocks}; @@ -21,39 +22,8 @@ pub enum ClockSrc { PLL, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -/// PLL clock input source -#[derive(Clone, Copy, Debug)] -pub enum PllSrc { - HSI16, - HSE(Hertz), -} - -impl Into for PllSrc { - fn into(self) -> Pllsrc { +impl Into for APBPrescaler { + fn into(self) -> u8 { match self { PllSrc::HSE(..) => Pllsrc::HSE, PllSrc::HSI16 => Pllsrc::HSI16, diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs index 7e2f75ab..2e72b193 100644 --- a/embassy-stm32/src/rcc/h5.rs +++ b/embassy-stm32/src/rcc/h5.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use stm32_metapac::rcc::vals::{Hpre, Ppre, Timpre}; +use stm32_metapac::rcc::vals::Timpre; use crate::pac::pwr::vals::Vos; use crate::pac::rcc::vals::{Hseext, Hsidiv, Mco1, Mco2, Pllrge, Pllsrc, Pllvcosel, Sw}; @@ -26,21 +26,7 @@ const VCO_MAX: u32 = 420_000_000; const VCO_WIDE_MIN: u32 = 128_000_000; const VCO_WIDE_MAX: u32 = 560_000_000; -/// Voltage Scale -/// -/// Represents the voltage range feeding the CPU core. The maximum core -/// clock frequency depends on this value. -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageScale { - /// VOS 0 range VCORE 1.30V - 1.40V - Scale0, - /// VOS 1 range VCORE 1.15V - 1.26V - Scale1, - /// VOS 2 range VCORE 1.05V - 1.15V - Scale2, - /// VOS 3 range VCORE 0.95V - 1.05V - Scale3, -} +pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale}; pub enum HseMode { /// crystal/ceramic oscillator (HSEBYP=0) @@ -105,57 +91,7 @@ pub struct Pll { pub divr: Option, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -impl AHBPrescaler { - fn div(&self, clk: Hertz) -> Hertz { - match self { - Self::NotDivided => clk, - Self::Div2 => clk / 2u32, - Self::Div4 => clk / 4u32, - Self::Div8 => clk / 8u32, - Self::Div16 => clk / 16u32, - Self::Div64 => clk / 64u32, - Self::Div128 => clk / 128u32, - Self::Div256 => clk / 256u32, - Self::Div512 => clk / 512u32, - } - } -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - impl APBPrescaler { - fn div(&self, clk: Hertz) -> Hertz { - match self { - Self::NotDivided => clk, - Self::Div2 => clk / 2u32, - Self::Div4 => clk / 4u32, - Self::Div8 => clk / 8u32, - Self::Div16 => clk / 16u32, - } - } - fn div_tim(&self, clk: Hertz, tim: TimerPrescaler) -> Hertz { match (tim, self) { // The timers kernel clock is equal to rcc_hclk1 if PPRE1 or PPRE2 corresponds to a @@ -193,34 +129,6 @@ impl From for Timpre { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - /// Configuration of the core clocks #[non_exhaustive] pub struct Config { @@ -406,13 +314,13 @@ pub(crate) unsafe fn init(config: Config) { }; assert!(sys <= max_clk); - let hclk = config.ahb_pre.div(sys); + let hclk = sys / config.ahb_pre; - let apb1 = config.apb1_pre.div(hclk); + let apb1 = hclk / config.apb1_pre; let apb1_tim = config.apb1_pre.div_tim(hclk, config.timer_prescaler); - let apb2 = config.apb2_pre.div(hclk); + let apb2 = hclk / config.apb2_pre; let apb2_tim = config.apb2_pre.div_tim(hclk, config.timer_prescaler); - let apb3 = config.apb3_pre.div(hclk); + let apb3 = hclk / config.apb3_pre; flash_setup(hclk, config.voltage_scale); diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs index bbc0e083..0788b064 100644 --- a/embassy-stm32/src/rcc/h7.rs +++ b/embassy-stm32/src/rcc/h7.rs @@ -24,21 +24,7 @@ pub const HSI48_FREQ: Hertz = Hertz(48_000_000); /// LSI speed pub const LSI_FREQ: Hertz = Hertz(32_000); -/// Voltage Scale -/// -/// Represents the voltage range feeding the CPU core. The maximum core -/// clock frequency depends on this value. -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageScale { - /// VOS 0 range VCORE 1.26V - 1.40V - Scale0, - /// VOS 1 range VCORE 1.15V - 1.26V - Scale1, - /// VOS 2 range VCORE 1.05V - 1.15V - Scale2, - /// VOS 3 range VCORE 0.95V - 1.05V - Scale3, -} +pub use super::common::VoltageScale; #[derive(Clone, Copy)] pub enum AdcClockSource { diff --git a/embassy-stm32/src/rcc/l0.rs b/embassy-stm32/src/rcc/l0.rs index 46a528e3..46b58ca7 100644 --- a/embassy-stm32/src/rcc/l0.rs +++ b/embassy-stm32/src/rcc/l0.rs @@ -1,3 +1,4 @@ +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; use crate::pac::RCC; #[cfg(crs)] @@ -70,30 +71,6 @@ pub enum PLLMul { Mul48, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - /// PLL clock input source #[derive(Clone, Copy)] pub enum PLLSource { @@ -136,34 +113,6 @@ impl From for Pllsrc { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - impl From for Msirange { fn from(val: MSIRange) -> Msirange { match val { diff --git a/embassy-stm32/src/rcc/l1.rs b/embassy-stm32/src/rcc/l1.rs index 59a6eac8..bdfc5b87 100644 --- a/embassy-stm32/src/rcc/l1.rs +++ b/embassy-stm32/src/rcc/l1.rs @@ -1,3 +1,4 @@ +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -68,30 +69,6 @@ pub enum PLLMul { Mul48, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - /// PLL clock input source #[derive(Clone, Copy)] pub enum PLLSource { @@ -134,34 +111,6 @@ impl From for Pllsrc { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - impl From for Msirange { fn from(val: MSIRange) -> Msirange { match val { diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index dc5f55d0..237b7bc9 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -4,6 +4,7 @@ use embassy_hal_internal::into_ref; use stm32_metapac::rcc::regs::Cfgr; use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel}; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::gpio::sealed::AFType; use crate::gpio::Speed; use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; @@ -78,30 +79,6 @@ pub enum PLLDiv { Div4, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - /// PLL clock input source #[derive(Clone, Copy)] pub enum PLLSource { @@ -209,34 +186,6 @@ impl From for Pllsrc { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - impl From for Msirange { fn from(val: MSIRange) -> Msirange { match val { diff --git a/embassy-stm32/src/rcc/l5.rs b/embassy-stm32/src/rcc/l5.rs index 16da65d5..a85e1488 100644 --- a/embassy-stm32/src/rcc/l5.rs +++ b/embassy-stm32/src/rcc/l5.rs @@ -1,5 +1,6 @@ use stm32_metapac::PWR; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -71,30 +72,6 @@ pub enum PLLDiv { Div4, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - /// PLL clock input source #[derive(Clone, Copy)] pub enum PLLSource { @@ -202,34 +179,6 @@ impl From for Pllsrc { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - impl From for Msirange { fn from(val: MSIRange) -> Msirange { match val { diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 4ae65d3e..5c69037e 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -1,5 +1,7 @@ #![macro_use] +pub mod common; + use core::mem::MaybeUninit; use crate::time::Hertz; diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index cfc07f06..b5feeb0c 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -1,5 +1,6 @@ -use stm32_metapac::rcc::vals::{Hpre, Msirange, Msirgsel, Pllm, Pllsrc, Ppre, Sw}; +use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllsrc, Sw}; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -10,19 +11,7 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000); /// LSI speed pub const LSI_FREQ: Hertz = Hertz(32_000); -/// Voltage Scale -/// -/// Represents the voltage range feeding the CPU core. The maximum core -/// clock frequency depends on this value. -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageScale { - // Highest frequency - Range1, - Range2, - Range3, - // Lowest power - Range4, -} +pub use super::common::VoltageScale; #[derive(Copy, Clone)] pub enum ClockSrc { @@ -130,36 +119,6 @@ impl Into for PllM { } } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -impl Into for AHBPrescaler { - fn into(self) -> Hpre { - match self { - AHBPrescaler::NotDivided => Hpre::NONE, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - impl Into for AHBPrescaler { fn into(self) -> u8 { match self { @@ -182,28 +141,6 @@ impl Default for AHBPrescaler { } } -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -impl Into for APBPrescaler { - fn into(self) -> Ppre { - match self { - APBPrescaler::NotDivided => Ppre::NONE, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - impl Default for APBPrescaler { fn default() -> Self { APBPrescaler::NotDivided @@ -389,12 +326,12 @@ pub(crate) unsafe fn init(config: Config) { } // TODO make configurable - let power_vos = VoltageScale::Range4; + let power_vos = VoltageScale::Scale3; // states and programming delay let wait_states = match power_vos { // VOS 0 range VCORE 1.26V - 1.40V - VoltageScale::Range1 => { + VoltageScale::Scale0 => { if sys_clk < 32_000_000 { 0 } else if sys_clk < 64_000_000 { @@ -408,7 +345,7 @@ pub(crate) unsafe fn init(config: Config) { } } // VOS 1 range VCORE 1.15V - 1.26V - VoltageScale::Range2 => { + VoltageScale::Scale1 => { if sys_clk < 30_000_000 { 0 } else if sys_clk < 60_000_000 { @@ -420,7 +357,7 @@ pub(crate) unsafe fn init(config: Config) { } } // VOS 2 range VCORE 1.05V - 1.15V - VoltageScale::Range3 => { + VoltageScale::Scale2 => { if sys_clk < 24_000_000 { 0 } else if sys_clk < 48_000_000 { @@ -430,7 +367,7 @@ pub(crate) unsafe fn init(config: Config) { } } // VOS 3 range VCORE 0.95V - 1.05V - VoltageScale::Range4 => { + VoltageScale::Scale3 => { if sys_clk < 12_000_000 { 0 } else { diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index 4322b950..b8ef01a9 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -1,5 +1,7 @@ -use crate::rcc::Clocks; -use crate::time::{khz, mhz, Hertz}; +pub use super::common::{AHBPrescaler, APBPrescaler}; +use crate::pac::RCC; +use crate::rcc::{set_freqs, Clocks, Clocks}; +use crate::time::{khz, mhz, Hertz, 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. @@ -102,68 +104,6 @@ pub struct Pll { pub divr: Option, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div3, - Div4, - Div5, - Div6, - Div8, - Div10, - Div16, - Div32, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -impl Into for APBPrescaler { - fn into(self) -> u8 { - match self { - APBPrescaler::NotDivided => 1, - APBPrescaler::Div2 => 0x04, - APBPrescaler::Div4 => 0x05, - APBPrescaler::Div8 => 0x06, - APBPrescaler::Div16 => 0x07, - } - } -} - -impl Into for AHBPrescaler { - fn into(self) -> u8 { - match self { - AHBPrescaler::NotDivided => 0x0, - AHBPrescaler::Div2 => 0x08, - AHBPrescaler::Div3 => 0x01, - AHBPrescaler::Div4 => 0x09, - AHBPrescaler::Div5 => 0x02, - AHBPrescaler::Div6 => 0x05, - AHBPrescaler::Div8 => 0x0a, - AHBPrescaler::Div10 => 0x06, - AHBPrescaler::Div16 => 0x0b, - AHBPrescaler::Div32 => 0x07, - AHBPrescaler::Div64 => 0x0c, - AHBPrescaler::Div128 => 0x0d, - AHBPrescaler::Div256 => 0x0e, - AHBPrescaler::Div512 => 0x0f, - } - } -} - /// Clocks configutation pub struct Config { pub hse: Option, diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index 6b69bb1c..eca1f613 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -1,5 +1,6 @@ +pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale}; use crate::pac::pwr::vals::Dbp; -use crate::pac::{FLASH, PWR, RCC}; +use crate::pac::{FLASH, FLASH, PWR, RCC, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -73,9 +74,9 @@ impl MSIRange { fn vos(&self) -> VoltageScale { if self > &MSIRange::Range8 { - VoltageScale::Range1 + VoltageScale::Scale0 } else { - VoltageScale::Range2 + VoltageScale::Scale1 } } } @@ -105,78 +106,6 @@ impl Into for MSIRange { } } -/// Voltage Scale -/// -/// Represents the voltage range feeding the CPU core. The maximum core -/// clock frequency depends on this value. -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageScale { - Range1, - Range2, -} - -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div3, - Div4, - Div5, - Div6, - Div8, - Div10, - Div16, - Div32, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -impl Into for APBPrescaler { - fn into(self) -> u8 { - match self { - APBPrescaler::NotDivided => 1, - APBPrescaler::Div2 => 0x04, - APBPrescaler::Div4 => 0x05, - APBPrescaler::Div8 => 0x06, - APBPrescaler::Div16 => 0x07, - } - } -} - -impl Into for AHBPrescaler { - fn into(self) -> u8 { - match self { - AHBPrescaler::NotDivided => 0x0, - AHBPrescaler::Div2 => 0x08, - AHBPrescaler::Div3 => 0x01, - AHBPrescaler::Div4 => 0x09, - AHBPrescaler::Div5 => 0x02, - AHBPrescaler::Div6 => 0x05, - AHBPrescaler::Div8 => 0x0a, - AHBPrescaler::Div10 => 0x06, - AHBPrescaler::Div16 => 0x0b, - AHBPrescaler::Div32 => 0x07, - AHBPrescaler::Div64 => 0x0c, - AHBPrescaler::Div128 => 0x0d, - AHBPrescaler::Div256 => 0x0e, - AHBPrescaler::Div512 => 0x0f, - } - } -} - /// Clocks configutation pub struct Config { pub mux: ClockSrc, @@ -220,8 +149,8 @@ pub enum Lsedrv { pub(crate) unsafe fn init(config: Config) { let (sys_clk, sw, vos) = match config.mux { - ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Range2), - ClockSrc::HSE32 => (HSE32_FREQ.0, 0x02, VoltageScale::Range1), + ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Scale1), + ClockSrc::HSE32 => (HSE32_FREQ.0, 0x02, VoltageScale::Scale0), ClockSrc::MSI(range) => (range.freq(), 0x00, range.vos()), }; @@ -266,12 +195,12 @@ pub(crate) unsafe fn init(config: Config) { // Adjust flash latency let flash_clk_src_freq: u32 = shd_ahb_freq; let ws = match vos { - VoltageScale::Range1 => match flash_clk_src_freq { + VoltageScale::Scale0 => match flash_clk_src_freq { 0..=18_000_000 => 0b000, 18_000_001..=36_000_000 => 0b001, _ => 0b010, }, - VoltageScale::Range2 => match flash_clk_src_freq { + VoltageScale::Scale1 => match flash_clk_src_freq { 0..=6_000_000 => 0b000, 6_000_001..=12_000_000 => 0b001, _ => 0b010, From a8a491212bc0b4de0c3d5e64b3891ebf3087eb44 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 10:18:54 -0500 Subject: [PATCH 129/298] stm32/rcc: cleanup merge --- embassy-stm32/src/rcc/common.rs | 2 +- embassy-stm32/src/rcc/g0.rs | 2 +- embassy-stm32/src/rcc/g4.rs | 11 +++++++++-- embassy-stm32/src/rcc/wb.rs | 5 ++--- embassy-stm32/src/rcc/wl.rs | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/rcc/common.rs b/embassy-stm32/src/rcc/common.rs index 7debb143..62736a43 100644 --- a/embassy-stm32/src/rcc/common.rs +++ b/embassy-stm32/src/rcc/common.rs @@ -142,7 +142,7 @@ impl Div for Hertz { } } -#[cfg(not(any(rcc_f1, rcc_g4, rcc_h7, rcc_wb, rcc_wl5, rcc_wle)))] +#[cfg(not(any(rcc_f1, rcc_f100, rcc_f1cl, rcc_g4, rcc_h7, rcc_h7ab, rcc_wb, rcc_wl5, rcc_wle)))] impl From for rcc::vals::Ppre { fn from(val: APBPrescaler) -> rcc::vals::Ppre { use rcc::vals::Ppre; diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index 1aaf983a..bf2d5199 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs @@ -380,7 +380,7 @@ pub(crate) unsafe fn init(config: Config) { APBPrescaler::NotDivided => (ahb_freq.0, ahb_freq.0), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.0 - 3); + let pre: u8 = 1 << (pre.to_bits() - 3); let freq = ahb_freq.0 / pre as u32; (freq, freq * 2) } diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 5489b718..dff04023 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -22,8 +22,15 @@ pub enum ClockSrc { PLL, } -impl Into for APBPrescaler { - fn into(self) -> u8 { +/// PLL clock input source +#[derive(Clone, Copy, Debug)] +pub enum PllSrc { + HSI16, + HSE(Hertz), +} + +impl Into for PllSrc { + fn into(self) -> Pllsrc { match self { PllSrc::HSE(..) => Pllsrc::HSE, PllSrc::HSI16 => Pllsrc::HSI16, diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index b8ef01a9..21aacec5 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -1,7 +1,6 @@ pub use super::common::{AHBPrescaler, APBPrescaler}; -use crate::pac::RCC; -use crate::rcc::{set_freqs, Clocks, Clocks}; -use crate::time::{khz, mhz, Hertz, 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. diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index eca1f613..ea6e8dde 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -1,6 +1,6 @@ pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale}; use crate::pac::pwr::vals::Dbp; -use crate::pac::{FLASH, FLASH, PWR, RCC, RCC}; +use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; From 73057ee241faf20f4b461766239d7cd805741cd7 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sun, 30 Jul 2023 16:46:33 +0100 Subject: [PATCH 130/298] wpan: fix examples --- examples/stm32wb/src/bin/eddystone_beacon.rs | 5 ++++- examples/stm32wb/src/bin/gatt_server.rs | 5 ++++- examples/stm32wb/src/bin/mac_ffd.rs | 9 ++++++--- examples/stm32wb/src/bin/mac_ffd_net.rs | 9 ++++++--- examples/stm32wb/src/bin/mac_rfd.rs | 9 ++++++--- examples/stm32wb/src/bin/tl_mbox.rs | 5 ++++- examples/stm32wb/src/bin/tl_mbox_ble.rs | 5 ++++- examples/stm32wb/src/bin/tl_mbox_mac.rs | 9 ++++++--- 8 files changed, 40 insertions(+), 16 deletions(-) diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs index 451bd7d2..ea150c67 100644 --- a/examples/stm32wb/src/bin/eddystone_beacon.rs +++ b/examples/stm32wb/src/bin/eddystone_beacon.rs @@ -8,6 +8,7 @@ use defmt::*; 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; @@ -54,7 +55,9 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs index 0f6419d4..dd67249c 100644 --- a/examples/stm32wb/src/bin/gatt_server.rs +++ b/examples/stm32wb/src/bin/gatt_server.rs @@ -8,6 +8,7 @@ use defmt::*; 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::event::command::{CommandComplete, ReturnParameters}; use embassy_stm32_wpan::hci::host::uart::{Packet, UartHci}; use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; @@ -62,7 +63,9 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 1379ac6b..881dc488 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -6,6 +6,7 @@ use defmt::*; 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::{AssociateResponse, ResetRequest, SetRequest, StartRequest}; use embassy_stm32_wpan::mac::event::MacEvent; use embassy_stm32_wpan::mac::typedefs::{MacChannel, MacStatus, PanId, PibId, SecurityLevel}; @@ -30,7 +31,7 @@ async fn main(spawner: Spawner) { - Obtain a NUCLEO-STM32WB55 from your preferred supplier. - Download and Install STM32CubeProgrammer. - - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x - Open STM32CubeProgrammer - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. @@ -39,7 +40,7 @@ async fn main(spawner: Spawner) { - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the - stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Select "Start Wireless Stack". - Disconnect from the device. @@ -49,7 +50,9 @@ async fn main(spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs index bbcd0a70..f8c76b5a 100644 --- a/examples/stm32wb/src/bin/mac_ffd_net.rs +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -6,6 +6,7 @@ use defmt::*; 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::{ResetRequest, SetRequest, StartRequest}; use embassy_stm32_wpan::mac::typedefs::{MacChannel, PanId, PibId}; use embassy_stm32_wpan::mac::{self, Runner}; @@ -36,7 +37,7 @@ async fn main(spawner: Spawner) { - Obtain a NUCLEO-STM32WB55 from your preferred supplier. - Download and Install STM32CubeProgrammer. - - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x - Open STM32CubeProgrammer - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. @@ -45,7 +46,7 @@ async fn main(spawner: Spawner) { - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the - stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Select "Start Wireless Stack". - Disconnect from the device. @@ -55,7 +56,9 @@ async fn main(spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index 4d8b6601..000355de 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -6,6 +6,7 @@ use defmt::*; 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, DataRequest, GetRequest, ResetRequest, SetRequest}; use embassy_stm32_wpan::mac::event::MacEvent; use embassy_stm32_wpan::mac::typedefs::{ @@ -32,7 +33,7 @@ async fn main(spawner: Spawner) { - Obtain a NUCLEO-STM32WB55 from your preferred supplier. - Download and Install STM32CubeProgrammer. - - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x - Open STM32CubeProgrammer - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. @@ -41,7 +42,7 @@ async fn main(spawner: Spawner) { - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the - stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Select "Start Wireless Stack". - Disconnect from the device. @@ -51,7 +52,9 @@ async fn main(spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index 9fc4b8aa..fc49c3c4 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -6,6 +6,7 @@ use defmt::*; 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::TlMbox; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -41,7 +42,9 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/tl_mbox_ble.rs b/examples/stm32wb/src/bin/tl_mbox_ble.rs index 90349422..5745ebd0 100644 --- a/examples/stm32wb/src/bin/tl_mbox_ble.rs +++ b/examples/stm32wb/src/bin/tl_mbox_ble.rs @@ -6,6 +6,7 @@ use defmt::*; 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::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -40,7 +41,9 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs index 5931c392..f32e07d9 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac.rs +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs @@ -6,6 +6,7 @@ use defmt::*; 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::sub::mm; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -27,7 +28,7 @@ async fn main(spawner: Spawner) { - Obtain a NUCLEO-STM32WB55 from your preferred supplier. - Download and Install STM32CubeProgrammer. - - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x - Open STM32CubeProgrammer - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. @@ -36,7 +37,7 @@ async fn main(spawner: Spawner) { - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the - stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Select "Start Wireless Stack". - Disconnect from the device. @@ -46,7 +47,9 @@ async fn main(spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); From 6f30e92c7aecc80d9fdf23e462a5128c07446f2f Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 10:57:17 -0500 Subject: [PATCH 131/298] stm32/dma: don't write to full ringbuf --- embassy-stm32/src/dma/ringbuffer.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index e9d33021..800f1906 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -243,6 +243,8 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { Ok((len, self.cap() - (start - self.end))) } + } else if start == self.end && dma.get_complete_count() == 0 { + Ok((0, 0)) } else if start <= self.end && self.end + buf.len() < self.cap() { // The occupied portion in the ring buffer DOES NOT wrap // and copying elements into the buffer WILL NOT cause it to From 6b1d802caa9ca5a2b6d33bf345c0599b990311fa Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 18:01:34 +0200 Subject: [PATCH 132/298] Move frequency to SPI config --- embassy-stm32/src/spi/mod.rs | 66 ++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index bdf3c85b..bbc7c3b9 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -36,6 +36,7 @@ pub enum BitOrder { pub struct Config { pub mode: Mode, pub bit_order: BitOrder, + pub frequency: Hertz, } impl Default for Config { @@ -43,6 +44,7 @@ impl Default for Config { Self { mode: MODE_0, bit_order: BitOrder::MsbFirst, + frequency: Hertz(1_000_000), } } } @@ -88,7 +90,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { miso: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, - freq: Hertz, config: Config, ) -> Self { into_ref!(peri, sck, mosi, miso); @@ -112,7 +113,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { Some(miso.map_into()), txdma, rxdma, - freq, config, ) } @@ -123,7 +123,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { miso: impl Peripheral

> + 'd, txdma: impl Peripheral

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

+ 'd, - freq: Hertz, config: Config, ) -> Self { into_ref!(sck, miso); @@ -139,7 +138,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { Some(miso.map_into()), txdma, rxdma, - freq, config, ) } @@ -150,7 +148,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { mosi: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, // TODO remove - freq: Hertz, config: Config, ) -> Self { into_ref!(sck, mosi); @@ -166,7 +163,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { None, txdma, rxdma, - freq, config, ) } @@ -176,14 +172,13 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { mosi: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, // TODO: remove - freq: Hertz, config: Config, ) -> Self { into_ref!(mosi); mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down); mosi.set_speed(crate::gpio::Speed::Medium); - Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, freq, config) + Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, config) } #[cfg(stm32wl)] @@ -201,7 +196,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let mut config = Config::default(); config.mode = MODE_0; config.bit_order = BitOrder::MsbFirst; - Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config) + config.frequency = freq; + Self::new_inner(peri, None, None, None, txdma, rxdma, config) } #[allow(dead_code)] @@ -209,10 +205,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, - freq: Hertz, config: Config, ) -> Self { - Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config) + Self::new_inner(peri, None, None, None, txdma, rxdma, config) } fn new_inner( @@ -222,12 +217,12 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { miso: Option>, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, - freq: Hertz, config: Config, ) -> Self { into_ref!(peri, txdma, rxdma); let pclk = T::frequency(); + let freq = config.frequency; let br = compute_baud_rate(pclk, freq.into()); let cpha = config.raw_phase(); @@ -334,19 +329,29 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let lsbfirst = config.raw_byte_order(); + let pclk = T::frequency(); + let freq = config.frequency; + let br = compute_baud_rate(pclk, freq.into()); + #[cfg(any(spi_v1, spi_f1, spi_v2))] T::REGS.cr1().modify(|w| { w.set_cpha(cpha); w.set_cpol(cpol); + w.set_br(br); w.set_lsbfirst(lsbfirst); }); #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cfg2().modify(|w| { - w.set_cpha(cpha); - w.set_cpol(cpol); - w.set_lsbfirst(lsbfirst); - }); + { + T::REGS.cfg2().modify(|w| { + w.set_cpha(cpha); + w.set_cpol(cpol); + w.set_lsbfirst(lsbfirst); + }); + T::REGS.cfg1().modify(|w| { + w.set_mbr(br); + }); + } } pub fn get_current_config(&self) -> Config { @@ -354,6 +359,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let cfg = T::REGS.cr1().read(); #[cfg(any(spi_v3, spi_v4, spi_v5))] let cfg = T::REGS.cfg2().read(); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + let cfg1 = T::REGS.cfg1().read(); + let polarity = if cfg.cpol() == vals::Cpol::IDLELOW { Polarity::IdleLow } else { @@ -371,9 +379,18 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { BitOrder::MsbFirst }; + #[cfg(any(spi_v1, spi_f1, spi_v2))] + let br = cfg.br(); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + let br = cfg1.mbr(); + + let pclk = T::frequency(); + let frequency = compute_frequency(pclk, br); + Config { mode: Mode { polarity, phase }, bit_order, + frequency, } } @@ -653,6 +670,21 @@ fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br { Br::from_bits(val) } +fn compute_frequency(clocks: Hertz, br: Br) -> Hertz { + let div: u16 = match br { + Br::DIV2 => 2, + Br::DIV4 => 4, + Br::DIV8 => 8, + Br::DIV16 => 16, + Br::DIV32 => 32, + Br::DIV64 => 64, + Br::DIV128 => 128, + Br::DIV256 => 256, + }; + + clocks / div +} + trait RegsExt { fn tx_ptr(&self) -> *mut W; fn rx_ptr(&self) -> *mut W; From aef93246b429cf892ad212aefc8ca0e5ddc29c18 Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 18:11:39 +0200 Subject: [PATCH 133/298] Fix Spi::new_internal call in i2s --- embassy-stm32/src/i2s.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index 1ccad732..f5103676 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -165,7 +165,9 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { mck.set_as_af(mck.af_num(), AFType::OutputPushPull); mck.set_speed(crate::gpio::Speed::VeryHigh); - let spi = Spi::new_internal(peri, txdma, rxdma, freq, SpiConfig::default()); + let mut spi_cfg = SpiConfig::default(); + spi_cfg.freq = freq; + let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg); #[cfg(all(rcc_f4, not(stm32f410)))] let pclk = unsafe { get_freqs() }.plli2s.unwrap(); From 1d815f4ba09b7be88e650690806e18c8a4d8e051 Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 18:20:36 +0200 Subject: [PATCH 134/298] Fix typo --- embassy-stm32/src/i2s.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index f5103676..8fd3a8c6 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -166,7 +166,7 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { mck.set_speed(crate::gpio::Speed::VeryHigh); let mut spi_cfg = SpiConfig::default(); - spi_cfg.freq = freq; + spi_cfg.frequency = freq; let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg); #[cfg(all(rcc_f4, not(stm32f410)))] From d2127f6b82e7a97744c81e2a3628ced59d8f1b49 Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 18:58:40 +0200 Subject: [PATCH 135/298] Fix stm32 SPI examples --- examples/stm32f3/src/bin/spi_dma.rs | 2 -- examples/stm32f4/src/bin/spi.rs | 12 +----------- examples/stm32f4/src/bin/spi_dma.rs | 2 -- examples/stm32g0/src/bin/spi_neopixel.rs | 4 +++- examples/stm32l0/src/bin/spi.rs | 2 -- examples/stm32l1/src/bin/spi.rs | 2 -- examples/stm32l4/src/bin/spi.rs | 12 +----------- examples/stm32l4/src/bin/spi_blocking_async.rs | 2 -- examples/stm32l4/src/bin/spi_dma.rs | 2 -- 9 files changed, 5 insertions(+), 35 deletions(-) diff --git a/examples/stm32f3/src/bin/spi_dma.rs b/examples/stm32f3/src/bin/spi_dma.rs index 95b2b686..d8cdb7e6 100644 --- a/examples/stm32f3/src/bin/spi_dma.rs +++ b/examples/stm32f3/src/bin/spi_dma.rs @@ -8,7 +8,6 @@ use core::str::from_utf8; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -24,7 +23,6 @@ async fn main(_spawner: Spawner) { p.PB4, p.DMA1_CH3, p.DMA1_CH2, - Hertz(1_000_000), Config::default(), ); diff --git a/examples/stm32f4/src/bin/spi.rs b/examples/stm32f4/src/bin/spi.rs index 05b48f47..b0c62b3f 100644 --- a/examples/stm32f4/src/bin/spi.rs +++ b/examples/stm32f4/src/bin/spi.rs @@ -7,7 +7,6 @@ use defmt::*; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[entry] @@ -16,16 +15,7 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); - let mut spi = Spi::new( - p.SPI3, - p.PC10, - p.PC12, - p.PC11, - NoDma, - NoDma, - Hertz(1_000_000), - Config::default(), - ); + let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, Config::default()); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32f4/src/bin/spi_dma.rs b/examples/stm32f4/src/bin/spi_dma.rs index 3d2a1a1a..d421b9d5 100644 --- a/examples/stm32f4/src/bin/spi_dma.rs +++ b/examples/stm32f4/src/bin/spi_dma.rs @@ -8,7 +8,6 @@ use core::str::from_utf8; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -24,7 +23,6 @@ async fn main(_spawner: Spawner) { p.PB4, p.DMA2_CH3, p.DMA2_CH2, - Hertz(1_000_000), Config::default(), ); diff --git a/examples/stm32g0/src/bin/spi_neopixel.rs b/examples/stm32g0/src/bin/spi_neopixel.rs index 81fdd15c..ee7aaf33 100644 --- a/examples/stm32g0/src/bin/spi_neopixel.rs +++ b/examples/stm32g0/src/bin/spi_neopixel.rs @@ -76,7 +76,9 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Start test using spi as neopixel driver"); - let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, Hertz(4_000_000), Config::default()); + let mut config = Config::default(); + config.frequency = Hertz(4_000_000); + let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, config); let mut neopixels = Ws2812::new(); diff --git a/examples/stm32l0/src/bin/spi.rs b/examples/stm32l0/src/bin/spi.rs index 9b5b3e27..54b7a2dd 100644 --- a/examples/stm32l0/src/bin/spi.rs +++ b/examples/stm32l0/src/bin/spi.rs @@ -7,7 +7,6 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -22,7 +21,6 @@ async fn main(_spawner: Spawner) { p.PA6, NoDma, NoDma, - Hertz(1_000_000), Config::default(), ); diff --git a/examples/stm32l1/src/bin/spi.rs b/examples/stm32l1/src/bin/spi.rs index 0a532e8e..34faf061 100644 --- a/examples/stm32l1/src/bin/spi.rs +++ b/examples/stm32l1/src/bin/spi.rs @@ -7,7 +7,6 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -22,7 +21,6 @@ async fn main(_spawner: Spawner) { p.PA6, NoDma, NoDma, - Hertz(1_000_000), Config::default(), ); diff --git a/examples/stm32l4/src/bin/spi.rs b/examples/stm32l4/src/bin/spi.rs index 76e316a2..a3097fb4 100644 --- a/examples/stm32l4/src/bin/spi.rs +++ b/examples/stm32l4/src/bin/spi.rs @@ -6,7 +6,6 @@ use defmt::*; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[cortex_m_rt::entry] @@ -15,16 +14,7 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); - let mut spi = Spi::new( - p.SPI3, - p.PC10, - p.PC12, - p.PC11, - NoDma, - NoDma, - Hertz(1_000_000), - Config::default(), - ); + let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, Config::default()); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/src/bin/spi_blocking_async.rs b/examples/stm32l4/src/bin/spi_blocking_async.rs index 62ef0130..868e2211 100644 --- a/examples/stm32l4/src/bin/spi_blocking_async.rs +++ b/examples/stm32l4/src/bin/spi_blocking_async.rs @@ -8,7 +8,6 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use embedded_hal_async::spi::SpiBus; use {defmt_rtt as _, panic_probe as _}; @@ -24,7 +23,6 @@ async fn main(_spawner: Spawner) { p.PC11, NoDma, NoDma, - Hertz(1_000_000), Config::default(), ); diff --git a/examples/stm32l4/src/bin/spi_dma.rs b/examples/stm32l4/src/bin/spi_dma.rs index 89471db5..01265c49 100644 --- a/examples/stm32l4/src/bin/spi_dma.rs +++ b/examples/stm32l4/src/bin/spi_dma.rs @@ -6,7 +6,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -21,7 +20,6 @@ async fn main(_spawner: Spawner) { p.PC11, p.DMA1_CH1, p.DMA1_CH2, - Hertz(1_000_000), Config::default(), ); From 4f791799a9c05e7e34c70d47ac0b2e885d5714a0 Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 19:12:49 +0200 Subject: [PATCH 136/298] Fix formatting --- examples/stm32f3/src/bin/spi_dma.rs | 10 +--------- examples/stm32f4/src/bin/spi_dma.rs | 10 +--------- examples/stm32l0/src/bin/spi.rs | 10 +--------- examples/stm32l1/src/bin/spi.rs | 10 +--------- examples/stm32l4/src/bin/spi_blocking_async.rs | 10 +--------- 5 files changed, 5 insertions(+), 45 deletions(-) diff --git a/examples/stm32f3/src/bin/spi_dma.rs b/examples/stm32f3/src/bin/spi_dma.rs index d8cdb7e6..6a39097d 100644 --- a/examples/stm32f3/src/bin/spi_dma.rs +++ b/examples/stm32f3/src/bin/spi_dma.rs @@ -16,15 +16,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut spi = Spi::new( - p.SPI1, - p.PB3, - p.PB5, - p.PB4, - p.DMA1_CH3, - p.DMA1_CH2, - Config::default(), - ); + let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA1_CH3, p.DMA1_CH2, Config::default()); for n in 0u32.. { let mut write: String<128> = String::new(); diff --git a/examples/stm32f4/src/bin/spi_dma.rs b/examples/stm32f4/src/bin/spi_dma.rs index d421b9d5..4edea93a 100644 --- a/examples/stm32f4/src/bin/spi_dma.rs +++ b/examples/stm32f4/src/bin/spi_dma.rs @@ -16,15 +16,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut spi = Spi::new( - p.SPI1, - p.PB3, - p.PB5, - p.PB4, - p.DMA2_CH3, - p.DMA2_CH2, - Config::default(), - ); + let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA2_CH3, p.DMA2_CH2, Config::default()); for n in 0u32.. { let mut write: String<128> = String::new(); diff --git a/examples/stm32l0/src/bin/spi.rs b/examples/stm32l0/src/bin/spi.rs index 54b7a2dd..24ea9a68 100644 --- a/examples/stm32l0/src/bin/spi.rs +++ b/examples/stm32l0/src/bin/spi.rs @@ -14,15 +14,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World, folks!"); - let mut spi = Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - NoDma, - NoDma, - Config::default(), - ); + let mut spi = Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, NoDma, NoDma, Config::default()); let mut cs = Output::new(p.PA15, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l1/src/bin/spi.rs b/examples/stm32l1/src/bin/spi.rs index 34faf061..53523140 100644 --- a/examples/stm32l1/src/bin/spi.rs +++ b/examples/stm32l1/src/bin/spi.rs @@ -14,15 +14,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World, folks!"); - let mut spi = Spi::new( - p.SPI1, - p.PA5, - p.PA7, - p.PA6, - NoDma, - NoDma, - Config::default(), - ); + let mut spi = Spi::new(p.SPI1, p.PA5, p.PA7, p.PA6, NoDma, NoDma, Config::default()); let mut cs = Output::new(p.PA4, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/src/bin/spi_blocking_async.rs b/examples/stm32l4/src/bin/spi_blocking_async.rs index 868e2211..45af8a9f 100644 --- a/examples/stm32l4/src/bin/spi_blocking_async.rs +++ b/examples/stm32l4/src/bin/spi_blocking_async.rs @@ -16,15 +16,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let spi = Spi::new( - p.SPI3, - p.PC10, - p.PC12, - p.PC11, - NoDma, - NoDma, - Config::default(), - ); + let spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, Config::default()); let mut spi = BlockingAsync::new(spi); From 55fb1d51268e87b522ba47e4cdc04978e9880ab0 Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 19:26:24 +0200 Subject: [PATCH 137/298] Fix more stm32 SPI examples --- examples/stm32h7/src/bin/spi.rs | 6 ++++-- examples/stm32h7/src/bin/spi_dma.rs | 6 ++++-- examples/stm32l0/src/bin/lora_cad.rs | 6 ++++-- examples/stm32l0/src/bin/lora_lorawan.rs | 6 ++++-- examples/stm32l0/src/bin/lora_p2p_receive.rs | 6 ++++-- examples/stm32l0/src/bin/lora_p2p_send.rs | 6 ++++-- tests/stm32/src/bin/spi.rs | 1 - tests/stm32/src/bin/spi_dma.rs | 1 - 8 files changed, 24 insertions(+), 14 deletions(-) diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs index 1f407f00..051a8392 100644 --- a/examples/stm32h7/src/bin/spi.rs +++ b/examples/stm32h7/src/bin/spi.rs @@ -43,6 +43,9 @@ fn main() -> ! { config.rcc.pll1.q_ck = Some(mhz(100)); let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = mhz(1); + let spi = spi::Spi::new( p.SPI3, p.PB3, @@ -50,8 +53,7 @@ fn main() -> ! { p.PB4, NoDma, NoDma, - mhz(1), - spi::Config::default(), + spi_config, ); let executor = EXECUTOR.init(Executor::new()); diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs index 53004fc9..5e878ee6 100644 --- a/examples/stm32h7/src/bin/spi_dma.rs +++ b/examples/stm32h7/src/bin/spi_dma.rs @@ -39,6 +39,9 @@ fn main() -> ! { config.rcc.pll1.q_ck = Some(mhz(100)); let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = mhz(1); + let spi = spi::Spi::new( p.SPI3, p.PB3, @@ -46,8 +49,7 @@ fn main() -> ! { p.PB4, p.DMA1_CH3, p.DMA1_CH4, - mhz(1), - spi::Config::default(), + spi_config, ); let executor = EXECUTOR.init(Executor::new()); diff --git a/examples/stm32l0/src/bin/lora_cad.rs b/examples/stm32l0/src/bin/lora_cad.rs index 588cea1e..ae2393e5 100644 --- a/examples/stm32l0/src/bin/lora_cad.rs +++ b/examples/stm32l0/src/bin/lora_cad.rs @@ -27,6 +27,9 @@ async fn main(_spawner: Spawner) { config.rcc.enable_hsi48 = true; let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = khz(200); + // SPI for sx1276 let spi = spi::Spi::new( p.SPI1, @@ -35,8 +38,7 @@ async fn main(_spawner: Spawner) { p.PA6, p.DMA1_CH3, p.DMA1_CH2, - khz(200), - spi::Config::default(), + spi_config, ); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_lorawan.rs b/examples/stm32l0/src/bin/lora_lorawan.rs index c397edd5..2202deea 100644 --- a/examples/stm32l0/src/bin/lora_lorawan.rs +++ b/examples/stm32l0/src/bin/lora_lorawan.rs @@ -32,6 +32,9 @@ async fn main(_spawner: Spawner) { config.rcc.enable_hsi48 = true; let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = khz(200); + // SPI for sx1276 let spi = spi::Spi::new( p.SPI1, @@ -40,8 +43,7 @@ async fn main(_spawner: Spawner) { p.PA6, p.DMA1_CH3, p.DMA1_CH2, - khz(200), - spi::Config::default(), + spi_config, ); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_p2p_receive.rs b/examples/stm32l0/src/bin/lora_p2p_receive.rs index bb750950..9b6b18b8 100644 --- a/examples/stm32l0/src/bin/lora_p2p_receive.rs +++ b/examples/stm32l0/src/bin/lora_p2p_receive.rs @@ -27,6 +27,9 @@ async fn main(_spawner: Spawner) { config.rcc.enable_hsi48 = true; let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = khz(200); + // SPI for sx1276 let spi = spi::Spi::new( p.SPI1, @@ -35,8 +38,7 @@ async fn main(_spawner: Spawner) { p.PA6, p.DMA1_CH3, p.DMA1_CH2, - khz(200), - spi::Config::default(), + spi_config, ); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_p2p_send.rs b/examples/stm32l0/src/bin/lora_p2p_send.rs index e6fadc01..90bae31e 100644 --- a/examples/stm32l0/src/bin/lora_p2p_send.rs +++ b/examples/stm32l0/src/bin/lora_p2p_send.rs @@ -27,6 +27,9 @@ async fn main(_spawner: Spawner) { config.rcc.enable_hsi48 = true; let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = khz(200); + // SPI for sx1276 let spi = spi::Spi::new( p.SPI1, @@ -35,8 +38,7 @@ async fn main(_spawner: Spawner) { p.PA6, p.DMA1_CH3, p.DMA1_CH2, - khz(200), - spi::Config::default(), + spi_config, ); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 819ecae3..72a8c92e 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -42,7 +42,6 @@ async fn main(_spawner: Spawner) { miso, // Arduino D12 NoDma, NoDma, - Hertz(1_000_000), spi::Config::default(), ); diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index 78aad24e..443aab57 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -41,7 +41,6 @@ async fn main(_spawner: Spawner) { miso, // Arduino D12 tx_dma, rx_dma, - Hertz(1_000_000), spi::Config::default(), ); From 04ed45941a22867c8879582586677ba719bc2b4b Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 19:31:22 +0200 Subject: [PATCH 138/298] Fix format in stm32 SPI examples --- examples/stm32h7/src/bin/spi.rs | 10 +--------- examples/stm32h7/src/bin/spi_dma.rs | 10 +--------- examples/stm32l0/src/bin/lora_cad.rs | 10 +--------- examples/stm32l0/src/bin/lora_lorawan.rs | 10 +--------- examples/stm32l0/src/bin/lora_p2p_receive.rs | 10 +--------- examples/stm32l0/src/bin/lora_p2p_send.rs | 10 +--------- 6 files changed, 6 insertions(+), 54 deletions(-) diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs index 051a8392..28bba2b8 100644 --- a/examples/stm32h7/src/bin/spi.rs +++ b/examples/stm32h7/src/bin/spi.rs @@ -46,15 +46,7 @@ fn main() -> ! { let mut spi_config = spi::Config::default(); spi_config.frequency = mhz(1); - let spi = spi::Spi::new( - p.SPI3, - p.PB3, - p.PB5, - p.PB4, - NoDma, - NoDma, - spi_config, - ); + let spi = spi::Spi::new(p.SPI3, p.PB3, p.PB5, p.PB4, NoDma, NoDma, spi_config); let executor = EXECUTOR.init(Executor::new()); diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs index 5e878ee6..f6e30cfa 100644 --- a/examples/stm32h7/src/bin/spi_dma.rs +++ b/examples/stm32h7/src/bin/spi_dma.rs @@ -42,15 +42,7 @@ fn main() -> ! { let mut spi_config = spi::Config::default(); spi_config.frequency = mhz(1); - let spi = spi::Spi::new( - p.SPI3, - p.PB3, - p.PB5, - p.PB4, - p.DMA1_CH3, - p.DMA1_CH4, - spi_config, - ); + let spi = spi::Spi::new(p.SPI3, p.PB3, p.PB5, p.PB4, p.DMA1_CH3, p.DMA1_CH4, spi_config); let executor = EXECUTOR.init(Executor::new()); diff --git a/examples/stm32l0/src/bin/lora_cad.rs b/examples/stm32l0/src/bin/lora_cad.rs index ae2393e5..7729b416 100644 --- a/examples/stm32l0/src/bin/lora_cad.rs +++ b/examples/stm32l0/src/bin/lora_cad.rs @@ -31,15 +31,7 @@ async fn main(_spawner: Spawner) { spi_config.frequency = khz(200); // SPI for sx1276 - let spi = spi::Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - p.DMA1_CH3, - p.DMA1_CH2, - spi_config, - ); + let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_lorawan.rs b/examples/stm32l0/src/bin/lora_lorawan.rs index 2202deea..620c9f4f 100644 --- a/examples/stm32l0/src/bin/lora_lorawan.rs +++ b/examples/stm32l0/src/bin/lora_lorawan.rs @@ -36,15 +36,7 @@ async fn main(_spawner: Spawner) { spi_config.frequency = khz(200); // SPI for sx1276 - let spi = spi::Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - p.DMA1_CH3, - p.DMA1_CH2, - spi_config, - ); + let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_p2p_receive.rs b/examples/stm32l0/src/bin/lora_p2p_receive.rs index 9b6b18b8..0f9f6095 100644 --- a/examples/stm32l0/src/bin/lora_p2p_receive.rs +++ b/examples/stm32l0/src/bin/lora_p2p_receive.rs @@ -31,15 +31,7 @@ async fn main(_spawner: Spawner) { spi_config.frequency = khz(200); // SPI for sx1276 - let spi = spi::Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - p.DMA1_CH3, - p.DMA1_CH2, - spi_config, - ); + let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_p2p_send.rs b/examples/stm32l0/src/bin/lora_p2p_send.rs index 90bae31e..c85c3c2b 100644 --- a/examples/stm32l0/src/bin/lora_p2p_send.rs +++ b/examples/stm32l0/src/bin/lora_p2p_send.rs @@ -31,15 +31,7 @@ async fn main(_spawner: Spawner) { spi_config.frequency = khz(200); // SPI for sx1276 - let spi = spi::Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - p.DMA1_CH3, - p.DMA1_CH2, - spi_config, - ); + let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); From d8420ed5a0cd96eef86c213a40c0bd84f4da0afe Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 19:34:27 +0200 Subject: [PATCH 139/298] Remove unused imports --- tests/stm32/src/bin/spi.rs | 1 - tests/stm32/src/bin/spi_dma.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 72a8c92e..7c2425fc 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -9,7 +9,6 @@ use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::spi::{self, Spi}; -use embassy_stm32::time::Hertz; #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index 443aab57..e70c09f7 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -8,7 +8,6 @@ use common::*; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::spi::{self, Spi}; -use embassy_stm32::time::Hertz; #[embassy_executor::main] async fn main(_spawner: Spawner) { From 538cf2bc24c6c9b299b01a63f775fa37d66c635b Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 14:02:41 -0500 Subject: [PATCH 140/298] stm32/dma: fix condition check --- embassy-stm32/src/dma/ringbuffer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 800f1906..1235e532 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -236,7 +236,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { // Confirm that the DMA is not inside data we could have written let pos = self.pos(dma.get_remaining_transfers()); - if (pos > self.end && pos <= start) || dma.get_complete_count() > 1 { + if (pos > self.end && pos <= start) || dma.get_complete_count() > 0 { Err(OverrunError) } else { self.end = (self.end + len) % self.cap(); From 56b21ad42937c450b10e26b098c0cbe2bb8b3c3e Mon Sep 17 00:00:00 2001 From: Michael van Niekerk Date: Sun, 30 Jul 2023 22:13:27 +0200 Subject: [PATCH 141/298] Uart pio fix zeros Prevent UART from only getting 0s from the output --- examples/rp/src/bin/pio_uart.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index 5fddbe29..4c382c2e 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -338,7 +338,7 @@ 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 - rx_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-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles jmp pin good_rx_stop ; Check stop bit (should be high) @@ -347,7 +347,8 @@ mod uart_rx { 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_rx_stop: ; No delay before returning to start; a little slack is + good_rx_stop: ; No delay before returning to start; a little slack is + in null 24 push ; important in case the TX clock is slightly too fast. "# ); @@ -361,8 +362,9 @@ mod uart_rx { sm_rx.set_pin_dirs(Direction::In, &[&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.shift_in.auto_fill = false; + cfg.shift_in.direction = ShiftDirection::Right; + cfg.shift_in.threshold = 32; cfg.fifo_join = FifoJoin::RxOnly; sm_rx.set_config(&cfg); sm_rx.set_enable(true); From b65406791a718e4e15a388b20c843fff2ab88037 Mon Sep 17 00:00:00 2001 From: Jan Christoph Bernack Date: Sat, 1 Jul 2023 03:15:39 +0200 Subject: [PATCH 142/298] add RNG conditioning --- embassy-stm32/src/rng.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 27415c2d..5d61d036 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -31,6 +31,7 @@ impl<'d, T: Instance> Rng<'d, T> { random } + #[cfg(rng_v1)] pub fn reset(&mut self) { // rng_v2 locks up on seed error, needs reset #[cfg(rng_v2)] @@ -49,6 +50,38 @@ impl<'d, T: Instance> Rng<'d, T> { let _ = self.next_u32(); } + #[cfg(not(rng_v1))] + pub fn reset(&mut self) { + T::regs().cr().modify(|reg| { + reg.set_rngen(false); + reg.set_condrst(true); + // set RNG config "A" according to reference manual + // this has to be written within the same write access as setting the CONDRST bit + reg.set_nistc(pac::rng::vals::Nistc::DEFAULT); + reg.set_rng_config1(pac::rng::vals::RngConfig1::CONFIGA); + reg.set_rng_config2(pac::rng::vals::RngConfig2::CONFIGA_B); + reg.set_rng_config3(pac::rng::vals::RngConfig3::CONFIGA); + reg.set_clkdiv(pac::rng::vals::Clkdiv::NODIV); + }); + // wait for CONDRST to be set + while !T::regs().cr().read().condrst() {} + // magic number must be written immediately before every read or write access to HTCR + T::regs().htcr().write(|w| w.set_htcfg(pac::rng::vals::Htcfg::MAGIC)); + // write recommended value according to reference manual + // note: HTCR can only be written during conditioning + T::regs() + .htcr() + .write(|w| w.set_htcfg(pac::rng::vals::Htcfg::RECOMMENDED)); + // finish conditioning + T::regs().cr().modify(|reg| { + reg.set_rngen(true); + reg.set_condrst(false); + reg.set_ie(true); + }); + // wait for CONDRST to be reset + while T::regs().cr().read().condrst() {} + } + pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { T::regs().cr().modify(|reg| { reg.set_rngen(true); From d6c5c1772cf2c6099ab08675afc0fead2e50fffb Mon Sep 17 00:00:00 2001 From: Jan Christoph Bernack Date: Sat, 1 Jul 2023 03:32:01 +0200 Subject: [PATCH 143/298] improve RNG polling --- embassy-stm32/src/rng.rs | 105 ++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 5d61d036..c4b77d01 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -3,11 +3,12 @@ use core::future::poll_fn; use core::task::Poll; +use embassy_hal_internal::interrupt::InterruptExt; use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use rand_core::{CryptoRng, RngCore}; -use crate::{pac, peripherals, Peripheral}; +use crate::{interrupt, pac, peripherals, Peripheral}; pub(crate) static RNG_WAKER: AtomicWaker = AtomicWaker::new(); @@ -28,31 +29,31 @@ impl<'d, T: Instance> Rng<'d, T> { into_ref!(inner); let mut random = Self { _inner: inner }; random.reset(); + unsafe { + interrupt::RNG.enable(); + } random } #[cfg(rng_v1)] pub fn reset(&mut self) { - // rng_v2 locks up on seed error, needs reset - #[cfg(rng_v2)] - if T::regs().sr().read().seis() { - T::reset(); - } - T::regs().cr().modify(|reg| { - reg.set_rngen(true); - reg.set_ie(true); + T::regs().cr().write(|reg| { + reg.set_rngen(false); }); T::regs().sr().modify(|reg| { reg.set_seis(false); reg.set_ceis(false); }); + T::regs().cr().modify(|reg| { + reg.set_rngen(true); + }); // Reference manual says to discard the first. let _ = self.next_u32(); } #[cfg(not(rng_v1))] pub fn reset(&mut self) { - T::regs().cr().modify(|reg| { + T::regs().cr().write(|reg| { reg.set_rngen(false); reg.set_condrst(true); // set RNG config "A" according to reference manual @@ -76,42 +77,68 @@ impl<'d, T: Instance> Rng<'d, T> { T::regs().cr().modify(|reg| { reg.set_rngen(true); reg.set_condrst(false); - reg.set_ie(true); }); // wait for CONDRST to be reset while T::regs().cr().read().condrst() {} } + pub fn recover_seed_error(&mut self) -> () { + self.reset(); + // reset should also clear the SEIS flag + if T::regs().sr().read().seis() { + warn!("recovering from seed error failed"); + return; + } + // wait for SECS to be cleared by RNG + while T::regs().sr().read().secs() {} + } + pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - T::regs().cr().modify(|reg| { - reg.set_rngen(true); - }); - for chunk in dest.chunks_mut(4) { - poll_fn(|cx| { - RNG_WAKER.register(cx.waker()); - T::regs().cr().modify(|reg| { - reg.set_ie(true); - }); - - let bits = T::regs().sr().read(); - - if bits.drdy() { - Poll::Ready(Ok(())) - } else if bits.seis() { - self.reset(); - Poll::Ready(Err(Error::SeedError)) - } else if bits.ceis() { - self.reset(); - Poll::Ready(Err(Error::ClockError)) - } else { - Poll::Pending + let bits = T::regs().sr().read(); + if bits.seis() { + // in case of noise-source or seed error we try to recover here + // but we must not use the data in DR and we return an error + // to leave retry-logic to the application + self.recover_seed_error(); + return Err(Error::SeedError); + } else if bits.ceis() { + // clock error detected, DR could still be used but keep it safe, + // clear the error and abort + T::regs().sr().modify(|sr| sr.set_ceis(false)); + return Err(Error::ClockError); + } else if bits.drdy() { + // DR can be read up to four times until the output buffer is empty + // DRDY is cleared automatically when that happens + let random_word = T::regs().dr().read(); + // reference manual: always check if DR is zero + if random_word == 0 { + return Err(Error::SeedError); } - }) - .await?; - let random_bytes = T::regs().dr().read().to_be_bytes(); - for (dest, src) in chunk.iter_mut().zip(random_bytes.iter()) { - *dest = *src + // write bytes to chunk + for (dest, src) in chunk.iter_mut().zip(random_word.to_be_bytes().iter()) { + *dest = *src + } + } else { + // wait for interrupt + poll_fn(|cx| { + // quick check to avoid registration if already done. + let bits = T::regs().sr().read(); + if bits.drdy() || bits.seis() || bits.ceis() { + return Poll::Ready(()); + } + RNG_WAKER.register(cx.waker()); + T::regs().cr().modify(|reg| reg.set_ie(true)); + // Need to check condition **after** `register` to avoid a race + // condition that would result in lost notifications. + let bits = T::regs().sr().read(); + if bits.drdy() || bits.seis() || bits.ceis() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; } } @@ -186,7 +213,7 @@ macro_rules! irq { unsafe fn $irq() { let bits = $crate::pac::RNG.sr().read(); if bits.drdy() || bits.seis() || bits.ceis() { - $crate::pac::RNG.cr().write(|reg| reg.set_ie(false)); + $crate::pac::RNG.cr().modify(|reg| reg.set_ie(false)); $crate::rng::RNG_WAKER.wake(); } } From e8d3e865912c304e4c495295360955707fffa07c Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Sun, 30 Jul 2023 21:22:48 +0100 Subject: [PATCH 144/298] stm32f2: Avoid resetting rtc backup domain Also ensure the pwr is enabled before trying to initialize. For the F2 series this is in a seperate clock control register. --- embassy-stm32/src/rtc/v2.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index a2eace6d..e3b9dfb8 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -39,9 +39,8 @@ impl<'d, T: Instance> super::Rtc<'d, T> { let rtcsel = reg.rtcsel().to_bits(); if !reg.rtcen() || rtcsel != clock_config { - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + #[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))] crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] let cr = crate::pac::RCC.bdcr(); #[cfg(any(rtc_v2l0, rtc_v2l1))] @@ -201,6 +200,11 @@ impl sealed::Instance for crate::peripherals::RTC { // read to allow the pwr clock to enable crate::pac::PWR.cr1().read(); } + #[cfg(any(rtc_v2f2))] + { + crate::pac::RCC.apb1enr().modify(|w| w.set_pwren(true)); + crate::pac::PWR.cr().read(); + } } fn read_backup_register(rtc: &Rtc, register: usize) -> Option { From d8f02e151b50f9d5156d5dd60911ea0f27ddedf0 Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Mon, 31 Jul 2023 00:02:50 +0200 Subject: [PATCH 145/298] Set frequency in stm32 SPI examples --- examples/stm32f3/src/bin/spi_dma.rs | 6 +++++- examples/stm32f4/src/bin/spi.rs | 6 +++++- examples/stm32f4/src/bin/spi_dma.rs | 6 +++++- examples/stm32l0/src/bin/spi.rs | 6 +++++- examples/stm32l1/src/bin/spi.rs | 6 +++++- examples/stm32l4/src/bin/spi.rs | 6 +++++- examples/stm32l4/src/bin/spi_blocking_async.rs | 6 +++++- examples/stm32l4/src/bin/spi_dma.rs | 14 +++++--------- tests/stm32/src/bin/spi.rs | 11 ++++++----- tests/stm32/src/bin/spi_dma.rs | 11 ++++++----- 10 files changed, 52 insertions(+), 26 deletions(-) diff --git a/examples/stm32f3/src/bin/spi_dma.rs b/examples/stm32f3/src/bin/spi_dma.rs index 6a39097d..a27c1d54 100644 --- a/examples/stm32f3/src/bin/spi_dma.rs +++ b/examples/stm32f3/src/bin/spi_dma.rs @@ -8,6 +8,7 @@ use core::str::from_utf8; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -16,7 +17,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA1_CH3, p.DMA1_CH2, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA1_CH3, p.DMA1_CH2, spi_config); for n in 0u32.. { let mut write: String<128> = String::new(); diff --git a/examples/stm32f4/src/bin/spi.rs b/examples/stm32f4/src/bin/spi.rs index b0c62b3f..0919e987 100644 --- a/examples/stm32f4/src/bin/spi.rs +++ b/examples/stm32f4/src/bin/spi.rs @@ -7,6 +7,7 @@ use defmt::*; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[entry] @@ -15,7 +16,10 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); - let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32f4/src/bin/spi_dma.rs b/examples/stm32f4/src/bin/spi_dma.rs index 4edea93a..f291f7db 100644 --- a/examples/stm32f4/src/bin/spi_dma.rs +++ b/examples/stm32f4/src/bin/spi_dma.rs @@ -8,6 +8,7 @@ use core::str::from_utf8; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -16,7 +17,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA2_CH3, p.DMA2_CH2, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA2_CH3, p.DMA2_CH2, spi_config); for n in 0u32.. { let mut write: String<128> = String::new(); diff --git a/examples/stm32l0/src/bin/spi.rs b/examples/stm32l0/src/bin/spi.rs index 24ea9a68..583e3d12 100644 --- a/examples/stm32l0/src/bin/spi.rs +++ b/examples/stm32l0/src/bin/spi.rs @@ -7,6 +7,7 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +15,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World, folks!"); - let mut spi = Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, NoDma, NoDma, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, NoDma, NoDma, spi_config); let mut cs = Output::new(p.PA15, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l1/src/bin/spi.rs b/examples/stm32l1/src/bin/spi.rs index 53523140..905b4d75 100644 --- a/examples/stm32l1/src/bin/spi.rs +++ b/examples/stm32l1/src/bin/spi.rs @@ -7,6 +7,7 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +15,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World, folks!"); - let mut spi = Spi::new(p.SPI1, p.PA5, p.PA7, p.PA6, NoDma, NoDma, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI1, p.PA5, p.PA7, p.PA6, NoDma, NoDma, spi_config); let mut cs = Output::new(p.PA4, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/src/bin/spi.rs b/examples/stm32l4/src/bin/spi.rs index a3097fb4..54cf68f7 100644 --- a/examples/stm32l4/src/bin/spi.rs +++ b/examples/stm32l4/src/bin/spi.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[cortex_m_rt::entry] @@ -14,7 +15,10 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); - let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/src/bin/spi_blocking_async.rs b/examples/stm32l4/src/bin/spi_blocking_async.rs index 45af8a9f..f1b80087 100644 --- a/examples/stm32l4/src/bin/spi_blocking_async.rs +++ b/examples/stm32l4/src/bin/spi_blocking_async.rs @@ -8,6 +8,7 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use embedded_hal_async::spi::SpiBus; use {defmt_rtt as _, panic_probe as _}; @@ -16,7 +17,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); let mut spi = BlockingAsync::new(spi); diff --git a/examples/stm32l4/src/bin/spi_dma.rs b/examples/stm32l4/src/bin/spi_dma.rs index 01265c49..ff9b5b43 100644 --- a/examples/stm32l4/src/bin/spi_dma.rs +++ b/examples/stm32l4/src/bin/spi_dma.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -13,15 +14,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut spi = Spi::new( - p.SPI3, - p.PC10, - p.PC12, - p.PC11, - p.DMA1_CH1, - p.DMA1_CH2, - Config::default(), - ); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, p.DMA1_CH1, p.DMA1_CH2, spi_config); // These are the pins for the Inventek eS-Wifi SPI Wifi Adapter. diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 7c2425fc..e51dd5bf 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -9,6 +9,7 @@ use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::spi::{self, Spi}; +use embassy_stm32::time::Hertz; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -34,14 +35,14 @@ async fn main(_spawner: Spawner) { #[cfg(feature = "stm32c031c6")] let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); + let mut spi_config = spi::Config::default(); + spi_config.frequency = Hertz(1_000_000); + let mut spi = Spi::new( - spi, - sck, // Arduino D13 + spi, sck, // Arduino D13 mosi, // Arduino D11 miso, // Arduino D12 - NoDma, - NoDma, - spi::Config::default(), + NoDma, NoDma, spi_config, ); let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE]; diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index e70c09f7..d45cbe45 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -8,6 +8,7 @@ use common::*; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::spi::{self, Spi}; +use embassy_stm32::time::Hertz; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -33,14 +34,14 @@ async fn main(_spawner: Spawner) { #[cfg(feature = "stm32c031c6")] let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); + let mut spi_config = spi::Config::default(); + spi_config.frequency = Hertz(1_000_000); + let mut spi = Spi::new( - spi, - sck, // Arduino D13 + spi, sck, // Arduino D13 mosi, // Arduino D11 miso, // Arduino D12 - tx_dma, - rx_dma, - spi::Config::default(), + tx_dma, rx_dma, spi_config, ); let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE]; From 4999b045df4e5956733b0341795714a9214c12d3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 31 Jul 2023 01:41:12 +0200 Subject: [PATCH 146/298] stm32/rng: use bind_interrupts!. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/rng.rs | 86 ++++++++++-------------- examples/stm32f4/src/bin/eth.rs | 6 +- examples/stm32f4/src/bin/usb_ethernet.rs | 5 +- examples/stm32f7/src/bin/eth.rs | 6 +- examples/stm32h5/src/bin/eth.rs | 6 +- examples/stm32h5/src/bin/rng.rs | 7 +- examples/stm32h7/src/bin/eth.rs | 6 +- examples/stm32h7/src/bin/eth_client.rs | 6 +- examples/stm32h7/src/bin/rng.rs | 7 +- examples/stm32l0/src/bin/lora_lorawan.rs | 8 ++- examples/stm32l4/src/bin/rng.rs | 8 ++- examples/stm32l5/src/bin/rng.rs | 8 ++- examples/stm32l5/src/bin/usb_ethernet.rs | 5 +- examples/stm32wl/src/bin/lora_lorawan.rs | 7 +- examples/stm32wl/src/bin/random.rs | 10 ++- 16 files changed, 103 insertions(+), 82 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ba279f79..a1323e85 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 = "13" +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-1f8ab493e029fc601edebc6bac105a63cc9858fe" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -75,7 +75,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "13", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-1f8ab493e029fc601edebc6bac105a63cc9858fe", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index c4b77d01..2a4978ec 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -1,16 +1,17 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_internal::interrupt::InterruptExt; use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use rand_core::{CryptoRng, RngCore}; +use crate::interrupt::typelevel::Interrupt; use crate::{interrupt, pac, peripherals, Peripheral}; -pub(crate) static RNG_WAKER: AtomicWaker = AtomicWaker::new(); +static RNG_WAKER: AtomicWaker = AtomicWaker::new(); #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -18,20 +19,38 @@ pub enum Error { ClockError, } +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let bits = T::regs().sr().read(); + if bits.drdy() || bits.seis() || bits.ceis() { + T::regs().cr().modify(|reg| reg.set_ie(false)); + RNG_WAKER.wake(); + } + } +} + pub struct Rng<'d, T: Instance> { _inner: PeripheralRef<'d, T>, } impl<'d, T: Instance> Rng<'d, T> { - pub fn new(inner: impl Peripheral

+ 'd) -> Self { + pub fn new( + inner: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { T::enable(); T::reset(); into_ref!(inner); let mut random = Self { _inner: inner }; random.reset(); - unsafe { - interrupt::RNG.enable(); - } + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + random } @@ -189,57 +208,20 @@ pub(crate) mod sealed { } } -pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral {} +pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { + type Interrupt: interrupt::typelevel::Interrupt; +} -foreach_peripheral!( - (rng, $inst:ident) => { - impl Instance for peripherals::$inst {} +foreach_interrupt!( + ($inst:ident, rng, RNG, GLOBAL, $irq:ident) => { + impl Instance for peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + } impl sealed::Instance for peripherals::$inst { fn regs() -> crate::pac::rng::Rng { - crate::pac::RNG + crate::pac::$inst } } }; ); - -#[cfg(feature = "rt")] -macro_rules! irq { - ($irq:ident) => { - mod rng_irq { - use crate::interrupt; - - #[interrupt] - unsafe fn $irq() { - let bits = $crate::pac::RNG.sr().read(); - if bits.drdy() || bits.seis() || bits.ceis() { - $crate::pac::RNG.cr().modify(|reg| reg.set_ie(false)); - $crate::rng::RNG_WAKER.wake(); - } - } - } - }; -} - -#[cfg(feature = "rt")] -foreach_interrupt!( - (RNG) => { - irq!(RNG); - }; - - (RNG_LPUART1) => { - irq!(RNG_LPUART1); - }; - - (AES_RNG_LPUART1) => { - irq!(AES_RNG_LPUART1); - }; - - (AES_RNG) => { - irq!(AES_RNG); - }; - - (HASH_RNG) => { - irq!(HASH_RNG); - }; -); diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index d0b16439..49601668 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs @@ -11,13 +11,15 @@ use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; -use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + HASH_RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -36,7 +38,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; let _ = rng.async_fill_bytes(&mut seed).await; let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index b1f01417..740d3018 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; -use embassy_stm32::rng::Rng; +use embassy_stm32::rng::{self, Rng}; use embassy_stm32::time::mhz; use embassy_stm32::usb_otg::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; @@ -38,6 +38,7 @@ async fn net_task(stack: &'static Stack>) -> ! { bind_interrupts!(struct Irqs { OTG_FS => usb_otg::InterruptHandler; + HASH_RNG => rng::InterruptHandler; }); #[embassy_executor::main] @@ -104,7 +105,7 @@ async fn main(spawner: Spawner) { //}); // Generate random seed - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; unwrap!(rng.async_fill_bytes(&mut seed).await); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index c6b2ba45..e5abf52b 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -11,14 +11,16 @@ use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; -use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -37,7 +39,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 0bff85ed..2aa2ab62 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -12,14 +12,16 @@ use embassy_stm32::peripherals::ETH; use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; use embassy_stm32::rng::Rng; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -56,7 +58,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32h5/src/bin/rng.rs b/examples/stm32h5/src/bin/rng.rs index af9be0b6..7c8c50ec 100644 --- a/examples/stm32h5/src/bin/rng.rs +++ b/examples/stm32h5/src/bin/rng.rs @@ -5,14 +5,19 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rng::Rng; +use embassy_stm32::{bind_interrupts, peripherals, rng}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index cfafcaed..c93be9f0 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -11,14 +11,16 @@ use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; -use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -38,7 +40,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 4ed73757..78005e91 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -11,15 +11,17 @@ use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; -use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -39,7 +41,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32h7/src/bin/rng.rs b/examples/stm32h7/src/bin/rng.rs index af9be0b6..7c8c50ec 100644 --- a/examples/stm32h7/src/bin/rng.rs +++ b/examples/stm32h7/src/bin/rng.rs @@ -5,14 +5,19 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rng::Rng; +use embassy_stm32::{bind_interrupts, peripherals, rng}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); diff --git a/examples/stm32l0/src/bin/lora_lorawan.rs b/examples/stm32l0/src/bin/lora_lorawan.rs index c397edd5..03c793b3 100644 --- a/examples/stm32l0/src/bin/lora_lorawan.rs +++ b/examples/stm32l0/src/bin/lora_lorawan.rs @@ -12,8 +12,8 @@ use embassy_lora::LoraTimer; use embassy_stm32::exti::{Channel, ExtiInput}; use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; use embassy_stm32::rng::Rng; -use embassy_stm32::spi; use embassy_stm32::time::khz; +use embassy_stm32::{bind_interrupts, peripherals, rng, spi}; use embassy_time::Delay; use lora_phy::mod_params::*; use lora_phy::sx1276_7_8_9::SX1276_7_8_9; @@ -23,6 +23,10 @@ use lorawan_device::async_device::lora_radio::LoRaRadio; use lorawan_device::async_device::{region, Device, JoinMode}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG_LPUART1 => rng::InterruptHandler; +}); + const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region #[embassy_executor::main] @@ -66,7 +70,7 @@ async fn main(_spawner: Spawner) { let radio = LoRaRadio::new(lora); let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); - let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs)); defmt::info!("Joining LoRaWAN network"); diff --git a/examples/stm32l4/src/bin/rng.rs b/examples/stm32l4/src/bin/rng.rs index c9302bb9..806e49f5 100644 --- a/examples/stm32l4/src/bin/rng.rs +++ b/examples/stm32l4/src/bin/rng.rs @@ -6,9 +6,13 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; use embassy_stm32::rng::Rng; -use embassy_stm32::Config; +use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -24,7 +28,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); diff --git a/examples/stm32l5/src/bin/rng.rs b/examples/stm32l5/src/bin/rng.rs index d359847e..9549d64d 100644 --- a/examples/stm32l5/src/bin/rng.rs +++ b/examples/stm32l5/src/bin/rng.rs @@ -6,9 +6,13 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; use embassy_stm32::rng::Rng; -use embassy_stm32::Config; +use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -23,7 +27,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 32eba427..5e75b21c 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -9,7 +9,7 @@ use embassy_net::{Stack, StackResources}; use embassy_stm32::rcc::*; use embassy_stm32::rng::Rng; use embassy_stm32::usb::Driver; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{bind_interrupts, peripherals, rng, usb, Config}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, UsbDevice}; @@ -24,6 +24,7 @@ const MTU: usize = 1514; bind_interrupts!(struct Irqs { USB_FS => usb::InterruptHandler; + RNG => rng::InterruptHandler; }); #[embassy_executor::task] @@ -99,7 +100,7 @@ async fn main(spawner: Spawner) { //}); // Generate random seed - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let seed = rng.next_u64(); // Init network stack diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs index 805d2141..2c9c9886 100644 --- a/examples/stm32wl/src/bin/lora_lorawan.rs +++ b/examples/stm32wl/src/bin/lora_lorawan.rs @@ -10,9 +10,9 @@ use embassy_executor::Spawner; use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; use embassy_lora::LoraTimer; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; -use embassy_stm32::rng::Rng; +use embassy_stm32::rng::{self, Rng}; use embassy_stm32::spi::Spi; -use embassy_stm32::{bind_interrupts, pac}; +use embassy_stm32::{bind_interrupts, pac, peripherals}; use embassy_time::Delay; use lora_phy::mod_params::*; use lora_phy::sx1261_2::SX1261_2; @@ -26,6 +26,7 @@ const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set th bind_interrupts!(struct Irqs{ SUBGHZ_RADIO => InterruptHandler; + RNG => rng::InterruptHandler; }); #[embassy_executor::main] @@ -58,7 +59,7 @@ async fn main(_spawner: Spawner) { }; let radio = LoRaRadio::new(lora); let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); - let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs)); defmt::info!("Joining LoRaWAN network"); diff --git a/examples/stm32wl/src/bin/random.rs b/examples/stm32wl/src/bin/random.rs index d8562fca..592e65f4 100644 --- a/examples/stm32wl/src/bin/random.rs +++ b/examples/stm32wl/src/bin/random.rs @@ -4,10 +4,14 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pac; -use embassy_stm32::rng::Rng; +use embassy_stm32::rng::{self, Rng}; +use embassy_stm32::{bind_interrupts, pac, peripherals}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs{ + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); @@ -21,7 +25,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); From c38c85ef1fef86a5fc73d1329616df17afb3d385 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 19:39:17 -0500 Subject: [PATCH 147/298] stm32/dma: add traces --- embassy-stm32/src/dma/ringbuffer.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 1235e532..8056a7c3 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -229,6 +229,13 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { pub fn write(&mut self, mut dma: impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { let start = self.pos(dma.get_remaining_transfers()); if start > self.end { + trace!( + "[1]: start, end, complete_count: {}, {}, {}", + start, + self.end, + dma.get_complete_count() + ); + // The occupied portion in the ring buffer DOES wrap let len = self.copy_from(buf, self.end..start); @@ -244,8 +251,22 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { Ok((len, self.cap() - (start - self.end))) } } else if start == self.end && dma.get_complete_count() == 0 { + trace!( + "[2]: start, end, complete_count: {}, {}, {}", + start, + self.end, + dma.get_complete_count() + ); + Ok((0, 0)) } else if start <= self.end && self.end + buf.len() < self.cap() { + trace!( + "[3]: start, end, complete_count: {}, {}, {}", + start, + self.end, + dma.get_complete_count() + ); + // The occupied portion in the ring buffer DOES NOT wrap // and copying elements into the buffer WILL NOT cause it to @@ -264,6 +285,13 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { Ok((len, self.cap() - (self.end - start))) } } else { + trace!( + "[4]: start, end, complete_count: {}, {}, {}", + start, + self.end, + dma.get_complete_count() + ); + // The occupied portion in the ring buffer DOES NOT wrap // and copying elements into the buffer WILL cause it to From ffa0c08140be6c90bde9f11e797eda95e4b8331b Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 20:22:14 -0500 Subject: [PATCH 148/298] stm32/dma: fix condition check --- embassy-stm32/src/dma/ringbuffer.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 8056a7c3..c3e4f20c 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -230,9 +230,10 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { let start = self.pos(dma.get_remaining_transfers()); if start > self.end { trace!( - "[1]: start, end, complete_count: {}, {}, {}", + "[1]: start, end, len, complete_count: {}, {}, {}, {}", start, self.end, + buf.len(), dma.get_complete_count() ); @@ -242,8 +243,9 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { compiler_fence(Ordering::SeqCst); // Confirm that the DMA is not inside data we could have written - let pos = self.pos(dma.get_remaining_transfers()); - if (pos > self.end && pos <= start) || dma.get_complete_count() > 0 { + let (pos, complete_count) = + critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count())); + if (pos >= self.end && pos < start) || (complete_count > 0 && pos >= start) || complete_count > 1 { Err(OverrunError) } else { self.end = (self.end + len) % self.cap(); @@ -252,18 +254,20 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } } else if start == self.end && dma.get_complete_count() == 0 { trace!( - "[2]: start, end, complete_count: {}, {}, {}", + "[2]: start, end, len, complete_count: {}, {}, {}, {}", start, self.end, + buf.len(), dma.get_complete_count() ); Ok((0, 0)) } else if start <= self.end && self.end + buf.len() < self.cap() { trace!( - "[3]: start, end, complete_count: {}, {}, {}", + "[3]: start, end, len, complete_count: {}, {}, {}, {}", start, self.end, + buf.len(), dma.get_complete_count() ); @@ -286,9 +290,10 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } } else { trace!( - "[4]: start, end, complete_count: {}, {}, {}", + "[4]: start, end, len, complete_count: {}, {}, {}, {}", start, self.end, + buf.len(), dma.get_complete_count() ); From ad85beb6779cf4e970ab91bb0229b9fdf3c2a8ba Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 31 Jul 2023 10:32:17 +0300 Subject: [PATCH 149/298] stm32/can: Add more derives for CAN Envelope --- embassy-stm32/src/can/bxcan.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 448be1cd..a3e3ec86 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -19,7 +19,7 @@ use crate::{interrupt, peripherals, Peripheral}; /// Contains CAN frame and additional metadata. /// /// Timestamp is available if `time` feature is enabled. -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Envelope { #[cfg(feature = "time")] From 027801db6029890f806d87b5fae85184c020f318 Mon Sep 17 00:00:00 2001 From: Julian <20155974+JuliDi@users.noreply.github.com> Date: Mon, 31 Jul 2023 10:17:44 +0200 Subject: [PATCH 150/298] Fix probe chip type in stm32h7 example --- examples/stm32h7/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32h7/.cargo/config.toml b/examples/stm32h7/.cargo/config.toml index 4160bf85..5f680dbc 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 STM32H7A3ZITxQ' +runner = 'probe-rs run --chip STM32H743ZITx' [build] target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) From 42b21fd7ae93fe0a1542ab265d01b1a808390f26 Mon Sep 17 00:00:00 2001 From: dev-guruprasath <96348958+dev-guruprasath@users.noreply.github.com> Date: Mon, 31 Jul 2023 13:56:16 +0530 Subject: [PATCH 151/298] added flash support --- embassy-stm32/src/flash/g0.rs | 115 +++++++++++++++++++++++++++++++++ embassy-stm32/src/flash/mod.rs | 3 +- 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 embassy-stm32/src/flash/g0.rs diff --git a/embassy-stm32/src/flash/g0.rs b/embassy-stm32/src/flash/g0.rs new file mode 100644 index 00000000..60ff1abf --- /dev/null +++ b/embassy-stm32/src/flash/g0.rs @@ -0,0 +1,115 @@ +use core::convert::TryInto; +use core::ptr::write_volatile; +use core::sync::atomic::{fence, Ordering}; + +use cortex_m::interrupt; + +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use crate::flash::Error; +use crate::pac; + +pub const fn is_default_layout() -> bool { + true +} + +pub const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + +pub(crate) unsafe fn lock() { + pac::FLASH.cr().modify(|w| w.set_lock(true)); +} +pub(crate) unsafe fn unlock() { + // Wait, while the memory interface is busy. + while pac::FLASH.sr().read().bsy() {} + + // Unlock flash + pac::FLASH.keyr().write(|w| w.set_keyr(0x4567_0123)); + pac::FLASH.keyr().write(|w| w.set_keyr(0xCDEF_89AB)); +} + +pub(crate) unsafe fn enable_blocking_write() { + assert_eq!(0, WRITE_SIZE % 4); + pac::FLASH.cr().write(|w| w.set_pg(true)); +} + +pub(crate) unsafe fn disable_blocking_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); + } + + wait_ready_blocking() +} + +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; + while pac::FLASH.sr().read().bsy() {} + clear_all_err(); + + interrupt::free(|_| { + pac::FLASH.cr().modify(|w| { + w.set_per(true); + w.set_pnb(idx as u8); + w.set_strt(true); + }); + }); + + let ret: Result<(), Error> = wait_ready_blocking(); + pac::FLASH.cr().modify(|w| w.set_per(false)); + ret +} + +pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { + while pac::FLASH.sr().read().bsy() {} + + let sr = pac::FLASH.sr().read(); + + if sr.progerr() { + return Err(Error::Prog); + } + + if sr.wrperr() { + return Err(Error::Protected); + } + + if sr.pgaerr() { + return Err(Error::Unaligned); + } + + Ok(()) +} + +pub(crate) unsafe fn clear_all_err() { + pac::FLASH.sr().modify(|w| { + if w.progerr() { + w.set_progerr(true); + } + if w.pgserr() { + w.set_pgserr(true); + } + if w.rderr() { + w.set_rderr(true); + } + if w.optverr() { + w.set_optverr(true); + } + if w.sizerr() { + w.set_sizerr(true); + } + if w.pgaerr() { + w.set_pgaerr(true); + } + if w.wrperr() { + w.set_wrperr(true); + } + }); +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index f44ef2c1..4308037f 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -63,10 +63,11 @@ impl FlashRegion { #[cfg_attr(flash_f3, path = "f3.rs")] #[cfg_attr(flash_f4, path = "f4.rs")] #[cfg_attr(flash_f7, path = "f7.rs")] +#[cfg_attr(flash_g0, path = "g0.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] #[cfg_attr( not(any( - flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_h7 + flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_g0, flash_h7 )), path = "other.rs" )] From c3357f884a5c7b5145221c4f96bc5328004b393a Mon Sep 17 00:00:00 2001 From: dev-guruprasath <96348958+dev-guruprasath@users.noreply.github.com> Date: Mon, 31 Jul 2023 14:45:23 +0530 Subject: [PATCH 152/298] added working example for flash --- examples/stm32g0/src/bin/flash.rs | 47 +++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 examples/stm32g0/src/bin/flash.rs diff --git a/examples/stm32g0/src/bin/flash.rs b/examples/stm32g0/src/bin/flash.rs new file mode 100644 index 00000000..36427e38 --- /dev/null +++ b/examples/stm32g0/src/bin/flash.rs @@ -0,0 +1,47 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; +use embassy_stm32::flash::Flash; + + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let ADDR: u32 = 0x8000; + + let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region; + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); + info!("Erasing..."); + unwrap!(f.blocking_erase(ADDR, ADDR + 2 * 1024)); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(ADDR, &mut buf)); + info!("Read after erase: {=[u8]:x}", buf); + + info!("Writing..."); + unwrap!(f.blocking_write( + ADDR, + &[ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32 + ] + )); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(ADDR, &mut buf)); + info!("Read: {=[u8]:x}", buf); +} From 5b4c099afcaa10028912ddc4edfa73c7bce0ffc1 Mon Sep 17 00:00:00 2001 From: dev-guruprasath <96348958+dev-guruprasath@users.noreply.github.com> Date: Mon, 31 Jul 2023 15:06:15 +0530 Subject: [PATCH 153/298] added working example for flash --- examples/stm32g0/src/bin/flash.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/stm32g0/src/bin/flash.rs b/examples/stm32g0/src/bin/flash.rs index 36427e38..3f6e4649 100644 --- a/examples/stm32g0/src/bin/flash.rs +++ b/examples/stm32g0/src/bin/flash.rs @@ -4,12 +4,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; use embassy_stm32::flash::Flash; - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); From 5a2f61a0315d51bb44618470962ad5b8dc6c03ff Mon Sep 17 00:00:00 2001 From: dev-guruprasath <96348958+dev-guruprasath@users.noreply.github.com> Date: Mon, 31 Jul 2023 15:29:26 +0530 Subject: [PATCH 154/298] added working example for flash --- examples/stm32g0/src/bin/flash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32g0/src/bin/flash.rs b/examples/stm32g0/src/bin/flash.rs index 3f6e4649..04c164bf 100644 --- a/examples/stm32g0/src/bin/flash.rs +++ b/examples/stm32g0/src/bin/flash.rs @@ -4,8 +4,8 @@ use defmt::*; use embassy_executor::Spawner; -use {defmt_rtt as _, panic_probe as _}; use embassy_stm32::flash::Flash; +use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { From b4d0f24bf95d6d0f4055d922d967750175534a93 Mon Sep 17 00:00:00 2001 From: dev-guruprasath <96348958+dev-guruprasath@users.noreply.github.com> Date: Mon, 31 Jul 2023 15:36:25 +0530 Subject: [PATCH 155/298] changed ADDR variable to addr --- examples/stm32g0/src/bin/flash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32g0/src/bin/flash.rs b/examples/stm32g0/src/bin/flash.rs index 04c164bf..a4d131cd 100644 --- a/examples/stm32g0/src/bin/flash.rs +++ b/examples/stm32g0/src/bin/flash.rs @@ -12,7 +12,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ADDR: u32 = 0x8000; + let addr: u32 = 0x8000; let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region; From 0ddabf04234ca8ad877d6ae13cc690567d6f4832 Mon Sep 17 00:00:00 2001 From: dev-guruprasath <96348958+dev-guruprasath@users.noreply.github.com> Date: Mon, 31 Jul 2023 15:37:01 +0530 Subject: [PATCH 156/298] changed ADDR variable to addr --- examples/stm32g0/src/bin/flash.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/stm32g0/src/bin/flash.rs b/examples/stm32g0/src/bin/flash.rs index a4d131cd..ed9f2e84 100644 --- a/examples/stm32g0/src/bin/flash.rs +++ b/examples/stm32g0/src/bin/flash.rs @@ -18,19 +18,19 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.blocking_read(ADDR, &mut buf)); + unwrap!(f.blocking_read(addr, &mut buf)); info!("Read: {=[u8]:x}", buf); info!("Erasing..."); - unwrap!(f.blocking_erase(ADDR, ADDR + 2 * 1024)); + unwrap!(f.blocking_erase(addr, addr + 2 * 1024)); info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.blocking_read(ADDR, &mut buf)); + unwrap!(f.blocking_read(addr, &mut buf)); info!("Read after erase: {=[u8]:x}", buf); info!("Writing..."); unwrap!(f.blocking_write( - ADDR, + addr, &[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 @@ -39,6 +39,6 @@ async fn main(_spawner: Spawner) { info!("Reading..."); let mut buf = [0u8; 32]; - unwrap!(f.blocking_read(ADDR, &mut buf)); + unwrap!(f.blocking_read(addr, &mut buf)); info!("Read: {=[u8]:x}", buf); } From 83ab8e057a759ac76c8cddeec1ebdc28c4516b2b Mon Sep 17 00:00:00 2001 From: chemicstry Date: Mon, 31 Jul 2023 13:24:10 +0300 Subject: [PATCH 157/298] stm32/can: Fix latency measurement in tests --- tests/stm32/src/bin/can.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index 93253ab8..8737ca8e 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -67,15 +67,25 @@ async fn main(_spawner: Spawner) { let tx_ts = Instant::now(); can.write(&tx_frame).await; - info!("Receiving frame..."); let envelope = can.read().await.unwrap(); + info!("Frame received!"); info!("loopback time {}", envelope.ts); info!("loopback frame {=u8}", envelope.frame.data().unwrap()[0]); - // Theoretical minimum latency is 55us, actual is usually ~80us let latency = envelope.ts.saturating_duration_since(tx_ts); - assert!(Duration::from_micros(50) < latency && latency < Duration::from_micros(100)); + info!("loopback latency {} us", latency.as_micros()); + + // Theoretical minimum latency is 55us, actual is usually ~80us + const MIN_LATENCY: Duration = Duration::from_micros(50); + const MAX_LATENCY: Duration = Duration::from_micros(150); + assert!( + MIN_LATENCY < latency && latency < MAX_LATENCY, + "{} < {} < {}", + MIN_LATENCY, + latency, + MAX_LATENCY + ); i += 1; if i > 10 { From 036e00113e1525060e120394c718b5b5af09434a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 31 Jul 2023 12:48:52 +0200 Subject: [PATCH 158/298] stm32/flash: avoid pointless "if flag is set, set it". --- embassy-stm32/src/flash/f0.rs | 14 +++---------- embassy-stm32/src/flash/f3.rs | 14 +++---------- embassy-stm32/src/flash/f4.rs | 9 +++------ embassy-stm32/src/flash/f7.rs | 20 +++--------------- embassy-stm32/src/flash/g0.rs | 26 +++--------------------- embassy-stm32/src/flash/h7.rs | 36 +++------------------------------ embassy-stm32/src/flash/l.rs | 38 +++-------------------------------- 7 files changed, 21 insertions(+), 136 deletions(-) diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index ec8343e7..d011522b 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -76,17 +76,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E } pub(crate) unsafe fn clear_all_err() { - pac::FLASH.sr().modify(|w| { - if w.pgerr() { - w.set_pgerr(true); - } - if w.wrprt() { - w.set_wrprt(true) - }; - if w.eop() { - w.set_eop(true); - } - }); + // read and write back the same value. + // This clears all "write 0 to clear" bits. + pac::FLASH.sr().modify(|_| {}); } unsafe fn wait_ready_blocking() -> Result<(), Error> { diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index 40335d64..065369f6 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -76,17 +76,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E } pub(crate) unsafe fn clear_all_err() { - pac::FLASH.sr().modify(|w| { - if w.pgerr() { - w.set_pgerr(true); - } - if w.wrprterr() { - w.set_wrprterr(true); - } - if w.eop() { - w.set_eop(true); - } - }); + // read and write back the same value. + // This clears all "write 0 to clear" bits. + pac::FLASH.sr().modify(|_| {}); } unsafe fn wait_ready_blocking() -> Result<(), Error> { diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 728f6d60..913950fe 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -336,12 +336,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E } pub(crate) fn clear_all_err() { - pac::FLASH.sr().write(|w| { - w.set_pgserr(true); - w.set_pgperr(true); - w.set_pgaerr(true); - w.set_wrperr(true); - }); + // read and write back the same value. + // This clears all "write 0 to clear" bits. + pac::FLASH.sr().modify(|_| {}); } pub(crate) async fn wait_ready() -> Result<(), Error> { diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 1a6d6bb0..3a5bdf9c 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -66,23 +66,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E } pub(crate) unsafe fn clear_all_err() { - pac::FLASH.sr().modify(|w| { - if w.erserr() { - w.set_erserr(true); - } - if w.pgperr() { - w.set_pgperr(true); - } - if w.pgaerr() { - w.set_pgaerr(true); - } - if w.wrperr() { - w.set_wrperr(true); - } - if w.eop() { - w.set_eop(true); - } - }); + // read and write back the same value. + // This clears all "write 0 to clear" bits. + pac::FLASH.sr().modify(|_| {}); } unsafe fn blocking_wait_ready() -> Result<(), Error> { diff --git a/embassy-stm32/src/flash/g0.rs b/embassy-stm32/src/flash/g0.rs index 60ff1abf..3a457601 100644 --- a/embassy-stm32/src/flash/g0.rs +++ b/embassy-stm32/src/flash/g0.rs @@ -89,27 +89,7 @@ pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { } pub(crate) unsafe fn clear_all_err() { - pac::FLASH.sr().modify(|w| { - if w.progerr() { - w.set_progerr(true); - } - if w.pgserr() { - w.set_pgserr(true); - } - if w.rderr() { - w.set_rderr(true); - } - if w.optverr() { - w.set_optverr(true); - } - if w.sizerr() { - w.set_sizerr(true); - } - if w.pgaerr() { - w.set_pgaerr(true); - } - if w.wrperr() { - w.set_wrperr(true); - } - }); + // read and write back the same value. + // This clears all "write 0 to clear" bits. + pac::FLASH.sr().modify(|_| {}); } diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index bf17b5b1..bb429d77 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -104,39 +104,9 @@ pub(crate) unsafe fn clear_all_err() { } unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { - bank.sr().modify(|w| { - if w.wrperr() { - w.set_wrperr(true); - } - if w.pgserr() { - w.set_pgserr(true); - } - if w.strberr() { - // single address was written multiple times, can be ignored - w.set_strberr(true); - } - if w.incerr() { - // writing to a different address when programming 256 bit word was not finished - w.set_incerr(true); - } - if w.operr() { - w.set_operr(true); - } - if w.sneccerr1() { - // single ECC error - w.set_sneccerr1(true); - } - if w.dbeccerr() { - // double ECC error - w.set_dbeccerr(true); - } - if w.rdperr() { - w.set_rdperr(true); - } - if w.rdserr() { - w.set_rdserr(true); - } - }); + // read and write back the same value. + // This clears all "write 0 to clear" bits. + bank.sr().modify(|_| {}); } unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 243c8b51..24dcf99b 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -113,41 +113,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E } pub(crate) unsafe fn clear_all_err() { - pac::FLASH.sr().modify(|w| { - #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l0))] - if w.rderr() { - w.set_rderr(true); - } - #[cfg(any(flash_wl, flash_wb, flash_l4))] - if w.fasterr() { - w.set_fasterr(true); - } - #[cfg(any(flash_wl, flash_wb, flash_l4))] - if w.miserr() { - w.set_miserr(true); - } - #[cfg(any(flash_wl, flash_wb, flash_l4))] - if w.pgserr() { - w.set_pgserr(true); - } - if w.sizerr() { - w.set_sizerr(true); - } - if w.pgaerr() { - w.set_pgaerr(true); - } - if w.wrperr() { - w.set_wrperr(true); - } - #[cfg(any(flash_wl, flash_wb, flash_l4))] - if w.progerr() { - w.set_progerr(true); - } - #[cfg(any(flash_wl, flash_wb, flash_l4))] - if w.operr() { - w.set_operr(true); - } - }); + // read and write back the same value. + // This clears all "write 0 to clear" bits. + pac::FLASH.sr().modify(|_| {}); } unsafe fn wait_ready_blocking() -> Result<(), Error> { From 4afdce4ec5e39324f8a690961070a25f16397f56 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Mon, 31 Jul 2023 10:40:48 +0200 Subject: [PATCH 159/298] Introduce driver::HardwareAddress without smoltcp dependency --- cyw43/src/lib.rs | 2 +- embassy-net-driver-channel/src/lib.rs | 22 +++++++++++++------- embassy-net-driver/Cargo.toml | 1 - embassy-net-driver/src/lib.rs | 10 ++++++++- embassy-net-esp-hosted/src/lib.rs | 2 +- embassy-net-w5500/src/lib.rs | 2 +- embassy-net/src/lib.rs | 21 +++++++++++++++---- embassy-stm32-wpan/src/mac/driver.rs | 4 ++-- embassy-stm32/src/eth/mod.rs | 6 +++--- embassy-usb/src/class/cdc_ncm/embassy_net.rs | 5 ++++- examples/std/src/tuntap.rs | 5 ++--- 11 files changed, 54 insertions(+), 26 deletions(-) diff --git a/cyw43/src/lib.rs b/cyw43/src/lib.rs index fd11f367..30a3d5f2 100644 --- a/cyw43/src/lib.rs +++ b/cyw43/src/lib.rs @@ -216,7 +216,7 @@ where PWR: OutputPin, SPI: SpiBusCyw43, { - let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); + let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6])); let state_ch = ch_runner.state_runner(); let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events); diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs index 53b66afb..076238ba 100644 --- a/embassy-net-driver-channel/src/lib.rs +++ b/embassy-net-driver-channel/src/lib.rs @@ -42,7 +42,7 @@ struct StateInner<'d, const MTU: usize> { struct Shared { link_state: LinkState, waker: WakerRegistration, - hardware_address: HardwareAddress, + hardware_address: driver::HardwareAddress, } pub struct Runner<'d, const MTU: usize> { @@ -85,7 +85,7 @@ impl<'d, const MTU: usize> Runner<'d, MTU> { }); } - pub fn set_hardware_address(&mut self, address: HardwareAddress) { + pub fn set_hardware_address(&mut self, address: driver::HardwareAddress) { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); s.hardware_address = address; @@ -150,7 +150,15 @@ impl<'d> StateRunner<'d> { pub fn set_ethernet_address(&self, address: [u8; 6]) { self.shared.lock(|s| { let s = &mut *s.borrow_mut(); - s.ethernet_address = address; + s.hardware_address = driver::HardwareAddress::Ethernet(address); + s.waker.wake(); + }); + } + + pub fn set_ieee802154_address(&self, address: [u8; 8]) { + self.shared.lock(|s| { + let s = &mut *s.borrow_mut(); + s.hardware_address = driver::HardwareAddress::Ieee802154(address); s.waker.wake(); }); } @@ -206,8 +214,7 @@ impl<'d, const MTU: usize> TxRunner<'d, MTU> { pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( state: &'d mut State, - ethernet_address: [u8; 6], - ieee802154_address: [u8; 8], + hardware_address: driver::HardwareAddress, ) -> (Runner<'d, MTU>, Device<'d, MTU>) { let mut caps = Capabilities::default(); caps.max_transmission_unit = MTU; @@ -223,8 +230,7 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( tx: zerocopy_channel::Channel::new(&mut state.tx[..]), shared: Mutex::new(RefCell::new(Shared { link_state: LinkState::Down, - ethernet_address, - ieee802154_address, + hardware_address, waker: WakerRegistration::new(), })), }); @@ -291,7 +297,7 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { self.caps.clone() } - fn hardware_address(&self) -> HardwareAddress { + fn hardware_address(&self) -> driver::HardwareAddress { self.shared.lock(|s| s.borrow().hardware_address) } diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml index 1af0d61b..e25950b6 100644 --- a/embassy-net-driver/Cargo.toml +++ b/embassy-net-driver/Cargo.toml @@ -22,4 +22,3 @@ features = ["defmt"] [dependencies] defmt = { version = "0.3", optional = true } -smoltcp = { version = "0.10", default-features = false } diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs index 93a02e46..ab17d190 100644 --- a/embassy-net-driver/src/lib.rs +++ b/embassy-net-driver/src/lib.rs @@ -4,7 +4,15 @@ use core::task::Context; -use smoltcp::wire::HardwareAddress; +/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum HardwareAddress { + /// A six-octet Ethernet address + Ethernet([u8; 6]), + /// An eight-octet IEEE802.15.4 address + Ieee802154([u8; 8]), +} /// Main `embassy-net` driver API. /// diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index a35adfca..96fddce5 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -124,7 +124,7 @@ where IN: InputPin + Wait, OUT: OutputPin, { - let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); + let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6])); let state_ch = ch_runner.state_runner(); let mut runner = Runner { diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs index efd9bed6..52494b44 100644 --- a/embassy-net-w5500/src/lib.rs +++ b/embassy-net-w5500/src/lib.rs @@ -96,7 +96,7 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: let mac = W5500::new(spi_dev, mac_addr).await.unwrap(); - let (runner, device) = ch::new(&mut state.ch_state, mac_addr); + let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr)); ( device, Runner { diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index efd820a6..7587b46f 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -230,6 +230,18 @@ pub(crate) struct SocketStack { next_local_port: u16, } +fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> HardwareAddress { + match addr { + #[cfg(feature = "medium-ethernet")] + driver::HardwareAddress::Ethernet(eth) => HardwareAddress::Ethernet(EthernetAddress(eth)), + #[cfg(feature = "medium-ieee802154")] + driver::HardwareAddress::Ieee802154(ieee) => HardwareAddress::Ieee802154(Ieee802154Address::Extended(ieee)), + + #[allow(unreachable_patterns)] + _ => panic!("Unsupported address {:?}. Make sure to enable medium-ethernet or medium-ieee802154 in embassy-net's Cargo features.", addr), + } +} + impl Stack { /// Create a new network stack. pub fn new( @@ -243,11 +255,11 @@ impl Stack { let hardware_addr = match medium { #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => device.hardware_address(), + Medium::Ethernet => to_smoltcp_hardware_address(device.hardware_address()), #[cfg(feature = "medium-ip")] Medium::Ip => HardwareAddress::Ip, #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => device.hardware_address(), + Medium::Ieee802154 => to_smoltcp_hardware_address(device.hardware_address()), #[allow(unreachable_patterns)] _ => panic!( "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.", @@ -338,7 +350,7 @@ impl Stack { /// Get the hardware address of the network interface. pub fn hardware_address(&self) -> HardwareAddress { - self.with(|_s, i| i.device.hardware_address()) + self.with(|_s, i| to_smoltcp_hardware_address(i.device.hardware_address())) } /// Get whether the link is up. @@ -744,7 +756,8 @@ impl Inner { if self.device.capabilities().medium == Medium::Ethernet || self.device.capabilities().medium == Medium::Ieee802154 { - s.iface.set_hardware_addr(self.device.hardware_address()); + s.iface + .set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address())); } let timestamp = instant_to_smoltcp(Instant::now()); diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 06ff0cf6..f8e3a2b0 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -3,7 +3,7 @@ use core::task::Context; -use embassy_net_driver::{Capabilities, LinkState, Medium}; +use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; @@ -76,7 +76,7 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { fn hardware_address(&self) -> HardwareAddress { // self.mac_addr - HardwareAddress::Ethernet(EthernetAddress([0; 6])) + HardwareAddress::Ieee802154([0; 8]) } } diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 1687cb31..1e057235 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -8,7 +8,7 @@ pub mod generic_smi; use core::mem::MaybeUninit; use core::task::Context; -use embassy_net_driver::{Capabilities, LinkState}; +use embassy_net_driver::{Capabilities, HardwareAddress, LinkState}; use embassy_sync::waitqueue::AtomicWaker; pub use self::_version::{InterruptHandler, *}; @@ -88,8 +88,8 @@ impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P> } } - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr + fn hardware_address(&self) -> HardwareAddress { + HardwareAddress::Ethernet(self.mac_addr) } } diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs index 67070902..57d32294 100644 --- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -87,7 +87,10 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { ethernet_address: [u8; 6], ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) { let (tx_usb, rx_usb) = self.split(); - let (runner, device) = ch::new(&mut state.ch_state, ethernet_address); + let (runner, device) = ch::new( + &mut state.ch_state, + ch::driver::HardwareAddress::Ethernet(ethernet_address), + ); ( Runner { diff --git a/examples/std/src/tuntap.rs b/examples/std/src/tuntap.rs index 932bdaf9..167c3da5 100644 --- a/examples/std/src/tuntap.rs +++ b/examples/std/src/tuntap.rs @@ -4,8 +4,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::task::Context; use async_io::Async; -use embassy_net::HardwareAddress; -use embassy_net_driver::{self, Capabilities, Driver, LinkState}; +use embassy_net_driver::{self, Capabilities, Driver, HardwareAddress, LinkState}; use log::*; pub const SIOCGIFMTU: libc::c_ulong = 0x8921; @@ -182,7 +181,7 @@ impl Driver for TunTapDevice { } fn hardware_address(&self) -> HardwareAddress { - HardwareAddress::Ethernet(EthernetAddress([0x02, 0x03, 0x04, 0x05, 0x06, 0x07])) + HardwareAddress::Ethernet([0x02, 0x03, 0x04, 0x05, 0x06, 0x07]) } } From 83ff3cbc69875f93c5a9bb36825c12df39f04f71 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Mon, 31 Jul 2023 12:19:04 +0200 Subject: [PATCH 160/298] Add Ip hardware address to Driver --- embassy-net-driver/src/lib.rs | 2 ++ embassy-net/src/lib.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs index ab17d190..b64c1000 100644 --- a/embassy-net-driver/src/lib.rs +++ b/embassy-net-driver/src/lib.rs @@ -12,6 +12,8 @@ pub enum HardwareAddress { Ethernet([u8; 6]), /// An eight-octet IEEE802.15.4 address Ieee802154([u8; 8]), + /// Indicates that a Driver is IP-native, and has no hardware address + Ip, } /// Main `embassy-net` driver API. diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 7587b46f..746acae4 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -236,6 +236,8 @@ fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> HardwareAddress driver::HardwareAddress::Ethernet(eth) => HardwareAddress::Ethernet(EthernetAddress(eth)), #[cfg(feature = "medium-ieee802154")] driver::HardwareAddress::Ieee802154(ieee) => HardwareAddress::Ieee802154(Ieee802154Address::Extended(ieee)), + #[cfg(feature = "medium-ip")] + driver::HardwareAddress::Ip => HardwareAddress::Ip, #[allow(unreachable_patterns)] _ => panic!("Unsupported address {:?}. Make sure to enable medium-ethernet or medium-ieee802154 in embassy-net's Cargo features.", addr), From 9f55228be07cb4edf680a57afc0fefd69fed7742 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Mon, 31 Jul 2023 12:20:13 +0200 Subject: [PATCH 161/298] Use hardware_address() for all media --- embassy-net/src/lib.rs | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 746acae4..86a11e92 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -34,7 +34,7 @@ use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; use smoltcp::socket::dhcpv4::{self, RetryConfig}; #[cfg(feature = "medium-ethernet")] pub use smoltcp::wire::EthernetAddress; -#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154", feature = "medium-ip"))] pub use smoltcp::wire::HardwareAddress; #[cfg(feature = "udp")] pub use smoltcp::wire::IpListenEndpoint; @@ -252,23 +252,7 @@ impl Stack { resources: &'static mut StackResources, random_seed: u64, ) -> Self { - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - let medium = device.capabilities().medium; - - let hardware_addr = match medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => to_smoltcp_hardware_address(device.hardware_address()), - #[cfg(feature = "medium-ip")] - Medium::Ip => HardwareAddress::Ip, - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => to_smoltcp_hardware_address(device.hardware_address()), - #[allow(unreachable_patterns)] - _ => panic!( - "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.", - medium - ), - }; - let mut iface_cfg = smoltcp::iface::Config::new(hardware_addr); + let mut iface_cfg = smoltcp::iface::Config::new(to_smoltcp_hardware_address(device.hardware_address())); iface_cfg.random_seed = random_seed; let iface = Interface::new( From bdd59b89885bff676a73d990613c1b74955885b3 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Mon, 31 Jul 2023 12:25:57 +0200 Subject: [PATCH 162/298] Only skip default-gateway assignment with Medium::Ip --- embassy-net/src/lib.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 86a11e92..2fb34f43 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -583,9 +583,6 @@ impl SocketStack { impl Inner { #[cfg(feature = "proto-ipv4")] fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) { - #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] - let medium = self.device.capabilities().medium; - debug!("Acquired IP configuration:"); debug!(" IP address: {}", config.address); @@ -600,8 +597,12 @@ impl Inner { addrs.push(IpCidr::Ipv4(config.address)).unwrap(); }); - #[cfg(feature = "medium-ethernet")] - if medium == Medium::Ethernet { + #[cfg(feature = "medium-ip")] + let skip_gateway = self.device.capabilities().medium != Medium::Ip; + #[cfg(not(feature = "medium-ip"))] + let skip_gateway = false; + + if !skip_gateway { if let Some(gateway) = config.gateway { debug!(" Default gateway: {}", gateway); s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap(); From 3d68d42132e5589d19c52d078099e0933b4d6e99 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Mon, 31 Jul 2023 14:04:36 +0200 Subject: [PATCH 163/298] CI: ip, ethernet and ieee802.15.4 should be able to co-exist --- ci.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ci.sh b/ci.sh index 19628b50..6310a6f5 100755 --- a/ci.sh +++ b/ci.sh @@ -36,6 +36,9 @@ cargo batch \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,nightly \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,unstable-traits,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,unstable-traits,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154,unstable-traits,nightly \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ From b394cc3394d8a5e3426fea44f4b3e08f2be99e88 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Mon, 31 Jul 2023 15:02:55 +0200 Subject: [PATCH 164/298] Enable IEEE802.15.4 doc building --- embassy-net/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 9b6a11c1..15b3ba2d 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -14,11 +14,11 @@ categories = [ [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"] +features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"] target = "thumbv7em-none-eabi" [package.metadata.docs.rs] -features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"] +features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"] [features] default = [] From 5c2ba3b212f51950bfebf51d64edcc8d6ac7170a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 31 Jul 2023 15:42:03 +0200 Subject: [PATCH 165/298] stm32: add hrtim v2 --- ci.sh | 1 + embassy-stm32/build.rs | 2 ++ embassy-stm32/src/hrtim/mod.rs | 18 ++++++++++++++++++ embassy-stm32/src/lib.rs | 2 +- 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/ci.sh b/ci.sh index 32ae7855..ac830fd3 100755 --- a/ci.sh +++ b/ci.sh @@ -88,6 +88,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5jb,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32g474pe,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \ diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 9b3caefd..30d4d7b0 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -597,6 +597,8 @@ fn main() { (("hrtim", "CHD2"), quote!(crate::hrtim::ChannelDComplementaryPin)), (("hrtim", "CHE1"), quote!(crate::hrtim::ChannelEPin)), (("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)), + (("hrtim", "CHF1"), quote!(crate::hrtim::ChannelFPin)), + (("hrtim", "CHF2"), quote!(crate::hrtim::ChannelFComplementaryPin)), (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index a930ff73..31c48814 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -18,6 +18,8 @@ pub enum Source { ChC, ChD, ChE, + #[cfg(hrtim_v2)] + ChF, } pub struct BurstController { @@ -41,6 +43,10 @@ pub struct ChD { pub struct ChE { phantom: PhantomData, } +#[cfg(hrtim_v2)] +pub struct ChF { + phantom: PhantomData, +} mod sealed { use super::Instance; @@ -110,6 +116,8 @@ advanced_channel_impl!(new_chb, ChB, 1, ChannelBPin, ChannelBComplementaryPin); advanced_channel_impl!(new_chc, ChC, 2, ChannelCPin, ChannelCComplementaryPin); advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin); advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin); +#[cfg(hrtim_v2)] +advanced_channel_impl!(new_chf, ChF, 5, ChannelFPin, ChannelFComplementaryPin); /// Struct used to divide a high resolution timer into multiple channels pub struct AdvancedPwm<'d, T: Instance> { @@ -121,6 +129,8 @@ pub struct AdvancedPwm<'d, T: Instance> { pub ch_c: ChC, pub ch_d: ChD, pub ch_e: ChE, + #[cfg(hrtim_v2)] + pub ch_f: ChF, } impl<'d, T: Instance> AdvancedPwm<'d, T> { @@ -136,6 +146,8 @@ impl<'d, T: Instance> AdvancedPwm<'d, T> { _chdn: Option>>, _che: Option>>, _chen: Option>>, + #[cfg(hrtim_v2)] _chf: Option>>, + #[cfg(hrtim_v2)] _chfn: Option>>, ) -> Self { Self::new_inner(tim) } @@ -167,6 +179,8 @@ impl<'d, T: Instance> AdvancedPwm<'d, T> { ch_c: ChC { phantom: PhantomData }, ch_d: ChD { phantom: PhantomData }, ch_e: ChE { phantom: PhantomData }, + #[cfg(hrtim_v2)] + ch_f: ChF { phantom: PhantomData }, } } } @@ -407,3 +421,7 @@ pin_trait!(ChannelDPin, Instance); pin_trait!(ChannelDComplementaryPin, Instance); pin_trait!(ChannelEPin, Instance); pin_trait!(ChannelEComplementaryPin, Instance); +#[cfg(hrtim_v2)] +pin_trait!(ChannelFPin, Instance); +#[cfg(hrtim_v2)] +pin_trait!(ChannelFComplementaryPin, Instance); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 34220fbf..fb0279dc 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -39,7 +39,7 @@ pub mod exti; pub mod flash; #[cfg(fmc)] pub mod fmc; -#[cfg(hrtim_v1)] +#[cfg(hrtim)] pub mod hrtim; #[cfg(i2c)] pub mod i2c; From e6d404327928756f2975542122a782343f46e75e Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 31 Jul 2023 18:28:31 +0200 Subject: [PATCH 166/298] rp: rename gpio::Pin::io to gpio::Pin::gpio we'll need access to the pin io bank registers for an upcoming fix, and having both `io` and `io_bank` or similar can get confusing quickly. rename `io` to `gpio` to avoid this, and also match the type while there. --- embassy-rp/src/clocks.rs | 8 ++++---- embassy-rp/src/gpio.rs | 6 +++--- embassy-rp/src/i2c.rs | 4 ++-- embassy-rp/src/pio.rs | 2 +- embassy-rp/src/pwm.rs | 8 ++++---- embassy-rp/src/spi.rs | 8 ++++---- embassy-rp/src/uart/mod.rs | 8 ++++---- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 976d06de..a3398023 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -702,7 +702,7 @@ impl<'d, T: Pin> Gpin<'d, T> { pub fn new(gpin: impl Peripheral

+ 'd) -> Gpin<'d, P> { into_ref!(gpin); - gpin.io().ctrl().write(|w| w.set_funcsel(0x08)); + gpin.gpio().ctrl().write(|w| w.set_funcsel(0x08)); Gpin { gpin: gpin.map_into(), @@ -718,7 +718,7 @@ impl<'d, T: Pin> Gpin<'d, T> { impl<'d, T: Pin> Drop for Gpin<'d, T> { fn drop(&mut self) { self.gpin - .io() + .gpio() .ctrl() .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _)); } @@ -766,7 +766,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> { pub fn new(gpout: impl Peripheral

+ 'd) -> Self { into_ref!(gpout); - gpout.io().ctrl().write(|w| w.set_funcsel(0x08)); + gpout.gpio().ctrl().write(|w| w.set_funcsel(0x08)); Self { gpout } } @@ -831,7 +831,7 @@ impl<'d, T: GpoutPin> Drop for Gpout<'d, T> { fn drop(&mut self) { self.disable(); self.gpout - .io() + .gpio() .ctrl() .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _)); } diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 2807eb67..7153325d 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -451,7 +451,7 @@ impl<'d, T: Pin> Flex<'d, T> { w.set_ie(true); }); - pin.io().ctrl().write(|w| { + pin.gpio().ctrl().write(|w| { w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0 as _); }); @@ -617,7 +617,7 @@ impl<'d, T: Pin> Drop for Flex<'d, T> { #[inline] fn drop(&mut self) { self.pin.pad_ctrl().write(|_| {}); - self.pin.io().ctrl().write(|w| { + self.pin.gpio().ctrl().write(|w| { w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _); }); } @@ -643,7 +643,7 @@ pub(crate) mod sealed { } } - fn io(&self) -> pac::io::Gpio { + fn gpio(&self) -> pac::io::Gpio { let block = match self._bank() { Bank::Bank0 => crate::pac::IO_BANK0, Bank::Qspi => crate::pac::IO_QSPI, diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 536ad747..7a5ddd32 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -353,8 +353,8 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> { p.ic_rx_tl().write(|w| w.set_rx_tl(0)); // Configure SCL & SDA pins - scl.io().ctrl().write(|w| w.set_funcsel(3)); - sda.io().ctrl().write(|w| w.set_funcsel(3)); + scl.gpio().ctrl().write(|w| w.set_funcsel(3)); + sda.gpio().ctrl().write(|w| w.set_funcsel(3)); scl.pad_ctrl().write(|w| { w.set_schmitt(true); diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 3de398af..c09d0914 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -852,7 +852,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> { /// of [`Pio`] do not keep pin registrations alive.** pub fn make_pio_pin(&mut self, pin: impl Peripheral

+ 'd) -> Pin<'d, PIO> { into_ref!(pin); - pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _)); + pin.gpio().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _)); // we can be relaxed about this because we're &mut here and nothing is cached PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); Pin { diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index c0ddb2a9..c297d69a 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -79,10 +79,10 @@ impl<'d, T: Channel> Pwm<'d, T> { Self::configure(p, &config); if let Some(pin) = &a { - pin.io().ctrl().write(|w| w.set_funcsel(4)); + pin.gpio().ctrl().write(|w| w.set_funcsel(4)); } if let Some(pin) = &b { - pin.io().ctrl().write(|w| w.set_funcsel(4)); + pin.gpio().ctrl().write(|w| w.set_funcsel(4)); } Self { inner, @@ -243,10 +243,10 @@ impl<'d, T: Channel> Drop for Pwm<'d, T> { fn drop(&mut self) { self.inner.regs().csr().write_clear(|w| w.set_en(false)); if let Some(pin) = &self.pin_a { - pin.io().ctrl().write(|w| w.set_funcsel(31)); + pin.gpio().ctrl().write(|w| w.set_funcsel(31)); } if let Some(pin) = &self.pin_b { - pin.io().ctrl().write(|w| w.set_funcsel(31)); + pin.gpio().ctrl().write(|w| w.set_funcsel(31)); } } } diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index 544b542e..46c440b8 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -100,16 +100,16 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { p.cr1().write(|w| w.set_sse(true)); if let Some(pin) = &clk { - pin.io().ctrl().write(|w| w.set_funcsel(1)); + pin.gpio().ctrl().write(|w| w.set_funcsel(1)); } if let Some(pin) = &mosi { - pin.io().ctrl().write(|w| w.set_funcsel(1)); + pin.gpio().ctrl().write(|w| w.set_funcsel(1)); } if let Some(pin) = &miso { - pin.io().ctrl().write(|w| w.set_funcsel(1)); + pin.gpio().ctrl().write(|w| w.set_funcsel(1)); } if let Some(pin) = &cs { - pin.io().ctrl().write(|w| w.set_funcsel(1)); + pin.gpio().ctrl().write(|w| w.set_funcsel(1)); } Self { inner, diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 69c6ac2f..00070b80 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -565,7 +565,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { ) { let r = T::regs(); if let Some(pin) = &tx { - pin.io().ctrl().write(|w| { + pin.gpio().ctrl().write(|w| { w.set_funcsel(2); w.set_outover(if config.invert_tx { Outover::INVERT @@ -576,7 +576,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rx { - pin.io().ctrl().write(|w| { + pin.gpio().ctrl().write(|w| { w.set_funcsel(2); w.set_inover(if config.invert_rx { Inover::INVERT @@ -587,7 +587,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &cts { - pin.io().ctrl().write(|w| { + pin.gpio().ctrl().write(|w| { w.set_funcsel(2); w.set_inover(if config.invert_cts { Inover::INVERT @@ -598,7 +598,7 @@ impl<'d, T: Instance + 'd, M: Mode> Uart<'d, T, M> { pin.pad_ctrl().write(|w| w.set_ie(true)); } if let Some(pin) = &rts { - pin.io().ctrl().write(|w| { + pin.gpio().ctrl().write(|w| { w.set_funcsel(2); w.set_outover(if config.invert_rts { Outover::INVERT From 2c6fcdbd3f6ff40d77e200fdd98b727164379769 Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 31 Jul 2023 18:36:37 +0200 Subject: [PATCH 167/298] rp: add gpio::Pin::io() for access to io banks --- embassy-rp/src/gpio.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 7153325d..bac126d4 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -643,12 +643,15 @@ pub(crate) mod sealed { } } - fn gpio(&self) -> pac::io::Gpio { - let block = match self._bank() { + fn io(&self) -> pac::io::Io { + match self._bank() { Bank::Bank0 => crate::pac::IO_BANK0, Bank::Qspi => crate::pac::IO_QSPI, - }; - block.gpio(self._pin() as _) + } + } + + fn gpio(&self) -> pac::io::Gpio { + self.io().gpio(self._pin() as _) } fn pad_ctrl(&self) -> Reg { @@ -672,12 +675,8 @@ pub(crate) mod sealed { } fn int_proc(&self) -> pac::io::Int { - let io_block = match self._bank() { - Bank::Bank0 => crate::pac::IO_BANK0, - Bank::Qspi => crate::pac::IO_QSPI, - }; let proc = SIO.cpuid().read(); - io_block.int_proc(proc as _) + self.io().int_proc(proc as _) } } } From dca1777a2f3659b1acaa87aba31caf9afb09eae4 Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 31 Jul 2023 19:13:10 +0200 Subject: [PATCH 168/298] rp: make QSPI gpio support optional this will be mostly not useful to anyone since flash is attached to qspi, and using flash chips that don't use the *entire* qspi interface will severly slow down the chip. the code overhead is minimal right now, but if we also fix interrupt support on qspi pins this will change (adding more code to potentially hot paths, using more memory for wakers that are never used, and preventing the qspi gpio irq from being used in software interrupts as RTIC applications may want to do). --- ci.sh | 1 + ci_stable.sh | 1 + embassy-rp/Cargo.toml | 4 ++++ embassy-rp/src/gpio.rs | 17 +++++++++++++---- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/ci.sh b/ci.sh index 2693c1be..954ad3e9 100755 --- a/ci.sh +++ b/ci.sh @@ -58,6 +58,7 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \ + --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,qspi-as-gpio \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any \ diff --git a/ci_stable.sh b/ci_stable.sh index daae9896..56f72131 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -36,6 +36,7 @@ cargo batch \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,defmt \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,log \ --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi \ + --- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features qspi-as-gpio \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits \ diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 6310ffb6..564d44ec 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -42,6 +42,10 @@ boot2-ram-memcpy = [] boot2-w25q080 = [] boot2-w25x10cl = [] +# Allow using QSPI pins as GPIO pins. This is mostly not what you want (because your flash lives there) +# and would add both code and memory overhead when enabled needlessly. +qspi-as-gpio = [] + # Indicate code is running from RAM. # Set this if all code is in RAM, and the cores never access memory-mapped flash memory through XIP. # This allows the flash driver to not force pausing execution on both cores when doing flash operations. diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index bac126d4..9861429f 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -67,6 +67,7 @@ pub enum SlewRate { #[derive(Debug, Eq, PartialEq)] pub enum Bank { Bank0 = 0, + #[cfg(feature = "qspi-as-gpio")] Qspi = 1, } @@ -636,16 +637,17 @@ pub(crate) mod sealed { #[inline] fn _bank(&self) -> Bank { - if self.pin_bank() & 0x20 == 0 { - Bank::Bank0 - } else { - Bank::Qspi + match self.pin_bank() & 0x20 { + #[cfg(feature = "qspi-as-gpio")] + 1 => Bank::Qspi, + _ => Bank::Bank0, } } fn io(&self) -> pac::io::Io { match self._bank() { Bank::Bank0 => crate::pac::IO_BANK0, + #[cfg(feature = "qspi-as-gpio")] Bank::Qspi => crate::pac::IO_QSPI, } } @@ -657,6 +659,7 @@ pub(crate) mod sealed { fn pad_ctrl(&self) -> Reg { let block = match self._bank() { Bank::Bank0 => crate::pac::PADS_BANK0, + #[cfg(feature = "qspi-as-gpio")] Bank::Qspi => crate::pac::PADS_QSPI, }; block.gpio(self._pin() as _) @@ -766,11 +769,17 @@ impl_pin!(PIN_27, Bank::Bank0, 27); impl_pin!(PIN_28, Bank::Bank0, 28); impl_pin!(PIN_29, Bank::Bank0, 29); +#[cfg(feature = "qspi-as-gpio")] impl_pin!(PIN_QSPI_SCLK, Bank::Qspi, 0); +#[cfg(feature = "qspi-as-gpio")] impl_pin!(PIN_QSPI_SS, Bank::Qspi, 1); +#[cfg(feature = "qspi-as-gpio")] impl_pin!(PIN_QSPI_SD0, Bank::Qspi, 2); +#[cfg(feature = "qspi-as-gpio")] impl_pin!(PIN_QSPI_SD1, Bank::Qspi, 3); +#[cfg(feature = "qspi-as-gpio")] impl_pin!(PIN_QSPI_SD2, Bank::Qspi, 4); +#[cfg(feature = "qspi-as-gpio")] impl_pin!(PIN_QSPI_SD3, Bank::Qspi, 5); // ==================== From f3ad0c6adeb4212af52f4ff974a488765551e81e Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 31 Jul 2023 20:02:06 +0200 Subject: [PATCH 169/298] rp: fix qspi gpio interrupts so far only bank0 interrupts were processed and configured, even if a qspi pin was given. this is obviously not the right thing to do, so let's rectify that. the fact that no problems have shown up so far does suggest that most, if not all, applications don't use this functionality at all. --- embassy-rp/src/gpio.rs | 45 +++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 9861429f..73e89352 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -11,9 +11,13 @@ use crate::pac::common::{Reg, RW}; use crate::pac::SIO; use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; -const PIN_COUNT: usize = 30; const NEW_AW: AtomicWaker = AtomicWaker::new(); -static INTERRUPT_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT]; +const BANK0_PIN_COUNT: usize = 30; +static BANK0_WAKERS: [AtomicWaker; BANK0_PIN_COUNT] = [NEW_AW; BANK0_PIN_COUNT]; +#[cfg(feature = "qspi-as-gpio")] +const QSPI_PIN_COUNT: usize = 6; +#[cfg(feature = "qspi-as-gpio")] +static QSPI_WAKERS: [AtomicWaker; QSPI_PIN_COUNT] = [NEW_AW; QSPI_PIN_COUNT]; /// Represents a digital input or output level. #[derive(Debug, Eq, PartialEq, Clone, Copy)] @@ -141,17 +145,23 @@ pub(crate) unsafe fn init() { interrupt::IO_IRQ_BANK0.disable(); interrupt::IO_IRQ_BANK0.set_priority(interrupt::Priority::P3); interrupt::IO_IRQ_BANK0.enable(); + + #[cfg(feature = "qspi-as-gpio")] + { + interrupt::IO_IRQ_QSPI.disable(); + interrupt::IO_IRQ_QSPI.set_priority(interrupt::Priority::P3); + interrupt::IO_IRQ_QSPI.enable(); + } } #[cfg(feature = "rt")] -#[interrupt] -fn IO_IRQ_BANK0() { +fn irq_handler(bank: pac::io::Io, wakers: &[AtomicWaker; N]) { let cpu = SIO.cpuid().read() as usize; // There are two sets of interrupt registers, one for cpu0 and one for cpu1 // and here we are selecting the set that belongs to the currently executing // cpu. - let proc_intx: pac::io::Int = pac::IO_BANK0.int_proc(cpu); - for pin in 0..PIN_COUNT { + let proc_intx: pac::io::Int = bank.int_proc(cpu); + for pin in 0..N { // There are 4 raw interrupt status registers, PROCx_INTS0, PROCx_INTS1, // PROCx_INTS2, and PROCx_INTS3, and we are selecting the one that the // current pin belongs to. @@ -172,11 +182,23 @@ fn IO_IRQ_BANK0() { w.set_level_high(pin_group, true); w.set_level_low(pin_group, true); }); - INTERRUPT_WAKERS[pin as usize].wake(); + wakers[pin as usize].wake(); } } } +#[cfg(feature = "rt")] +#[interrupt] +fn IO_IRQ_BANK0() { + irq_handler(pac::IO_BANK0, &BANK0_WAKERS); +} + +#[cfg(all(feature = "rt", feature = "qspi-as-gpio"))] +#[interrupt] +fn IO_IRQ_QSPI() { + irq_handler(pac::IO_QSPI, &QSPI_WAKERS); +} + #[must_use = "futures do nothing unless you `.await` or poll them"] struct InputFuture<'a, T: Pin> { pin: PeripheralRef<'a, T>, @@ -195,7 +217,7 @@ impl<'d, T: Pin> InputFuture<'d, T> { // (the alternative being checking the current level and waiting for // its inverse, but that requires reading the current level and thus // missing anything that happened before the level was read.) - pac::IO_BANK0.intr(pin.pin() as usize / 8).write(|w| { + pin.io().intr(pin.pin() as usize / 8).write(|w| { w.set_edge_high(pin_group, true); w.set_edge_low(pin_group, true); }); @@ -235,7 +257,12 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> { fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { // We need to register/re-register the waker for each poll because any // calls to wake will deregister the waker. - INTERRUPT_WAKERS[self.pin.pin() as usize].register(cx.waker()); + let waker = match self.pin.bank() { + Bank::Bank0 => &BANK0_WAKERS[self.pin.pin() as usize], + #[cfg(feature = "qspi-as-gpio")] + Bank::Qspi => &QSPI_WAKERS[self.pin.pin() as usize], + }; + waker.register(cx.waker()); // self.int_proc() will get the register offset for the current cpu, // then we want to access the interrupt enable register for our From 1b0f4ee65390ec0e511c2455c0f26c8d4f661ec3 Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Mon, 31 Jul 2023 13:35:06 -0500 Subject: [PATCH 170/298] stm32: add outlives bounds to TimeoutI2c impl blocks This should make usage and error messages more clear. --- embassy-stm32/src/i2c/timeout.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index 8dc228b3..bb6e105d 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -22,7 +22,7 @@ fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { } } -impl<'a, 'd, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { +impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { pub fn new(i2c: &'a mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self { Self { i2c, timeout } } @@ -65,7 +65,9 @@ impl<'a, 'd, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { } } -impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { +impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read + for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> +{ type Error = Error; fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> { @@ -73,7 +75,9 @@ impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for } } -impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { +impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write + for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> +{ type Error = Error; fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> { @@ -81,7 +85,7 @@ impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write fo } } -impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead +impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { type Error = Error; @@ -95,11 +99,11 @@ impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRea mod eh1 { use super::*; - impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { + impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { type Error = Error; } - impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { + impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, read) } From 26cc0e634d1e0f51e7c4c5bb55fea4f7bec299aa Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Mon, 31 Jul 2023 13:47:03 -0500 Subject: [PATCH 171/298] stm32: add async timeout functions to I2c and TimeoutI2c --- embassy-stm32/src/i2c/timeout.rs | 74 ++++++++++++++++++++++++++++++++ embassy-stm32/src/i2c/v2.rs | 70 ++++++++++++++++++++++++++---- 2 files changed, 135 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index bb6e105d..830de8e9 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -27,6 +27,80 @@ impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> Self { i2c, timeout } } + // ========================= + // Async public API + + pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_timeout(address, write, self.timeout).await + } + + pub async fn write_timeout(&mut self, address: u8, write: &[u8], timeout: Duration) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.i2c.write_timeout(address, write, timeout_fn(timeout)).await + } + + pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_vectored_timeout(address, write, self.timeout).await + } + + pub async fn write_vectored_timeout(&mut self, address: u8, write: &[&[u8]], timeout: Duration) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.i2c + .write_vectored_timeout(address, write, timeout_fn(timeout)) + .await + } + + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> + where + RXDMA: crate::i2c::RxDma, + { + self.read_timeout(address, buffer, self.timeout).await + } + + pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> + where + RXDMA: crate::i2c::RxDma, + { + self.i2c.read_timeout(address, buffer, timeout_fn(timeout)).await + } + + pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> + where + TXDMA: super::TxDma, + RXDMA: super::RxDma, + { + self.write_read_timeout(address, write, read, self.timeout).await + } + + pub async fn write_read_timeout( + &mut self, + address: u8, + write: &[u8], + read: &mut [u8], + timeout: Duration, + ) -> Result<(), Error> + where + TXDMA: super::TxDma, + RXDMA: super::RxDma, + { + self.i2c + .write_read_timeout(address, write, read, timeout_fn(timeout)) + .await + } + + // ========================= + // Blocking public API + /// Blocking read with a custom timeout pub fn blocking_read_timeout(&mut self, addr: u8, read: &mut [u8], timeout: Duration) -> Result<(), Error> { self.i2c.blocking_read_timeout(addr, read, timeout_fn(timeout)) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index eaf980a4..4327899b 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -595,17 +595,41 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Async public API pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_timeout(address, write, || Ok(())).await + } + + pub async fn write_timeout( + &mut self, + address: u8, + write: &[u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { if write.is_empty() { - self.write_internal(address, write, true, || Ok(())) + self.write_internal(address, write, true, check_timeout) } else { - self.write_dma_internal(address, write, true, true, || Ok(())).await + self.write_dma_internal(address, write, true, true, check_timeout).await } } pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> + where + TXDMA: crate::i2c::TxDma, + { + self.write_vectored_timeout(address, write, || Ok(())).await + } + + pub async fn write_vectored_timeout( + &mut self, + address: u8, + write: &[&[u8]], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, { @@ -620,7 +644,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let next = iter.next(); let is_last = next.is_none(); - self.write_dma_internal(address, c, first, is_last, || Ok(())).await?; + self.write_dma_internal(address, c, first, is_last, || check_timeout()) + .await?; first = false; current = next; } @@ -628,31 +653,58 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> + where + RXDMA: crate::i2c::RxDma, + { + self.read_timeout(address, buffer, || Ok(())).await + } + + pub async fn read_timeout( + &mut self, + address: u8, + buffer: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, { if buffer.is_empty() { - self.read_internal(address, buffer, false, || Ok(())) + self.read_internal(address, buffer, false, check_timeout) } else { - self.read_dma_internal(address, buffer, false, || Ok(())).await + self.read_dma_internal(address, buffer, false, check_timeout).await } } pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> + where + TXDMA: super::TxDma, + RXDMA: super::RxDma, + { + self.write_read_timeout(address, write, read, || Ok(())).await + } + + pub async fn write_read_timeout( + &mut self, + address: u8, + write: &[u8], + read: &mut [u8], + check_timeout: impl Fn() -> Result<(), Error>, + ) -> Result<(), Error> where TXDMA: super::TxDma, RXDMA: super::RxDma, { if write.is_empty() { - self.write_internal(address, write, false, || Ok(()))?; + self.write_internal(address, write, false, || check_timeout())?; } else { - self.write_dma_internal(address, write, true, true, || Ok(())).await?; + self.write_dma_internal(address, write, true, true, || check_timeout()) + .await?; } if read.is_empty() { - self.read_internal(address, read, true, || Ok(()))?; + self.read_internal(address, read, true, check_timeout)?; } else { - self.read_dma_internal(address, read, true, || Ok(())).await?; + self.read_dma_internal(address, read, true, check_timeout).await?; } Ok(()) From 036bc669cd46012e37e09af070ddb8353454719a Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Mon, 31 Jul 2023 14:17:50 -0500 Subject: [PATCH 172/298] stm32: only enable async TimeoutI2c on V2 I2C peripheral --- embassy-stm32/src/i2c/timeout.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index 830de8e9..103017cd 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -30,6 +30,7 @@ impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> // ========================= // Async public API + #[cfg(i2c_v2)] pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, @@ -37,6 +38,7 @@ impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> self.write_timeout(address, write, self.timeout).await } + #[cfg(i2c_v2)] pub async fn write_timeout(&mut self, address: u8, write: &[u8], timeout: Duration) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, @@ -44,6 +46,7 @@ impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> self.i2c.write_timeout(address, write, timeout_fn(timeout)).await } + #[cfg(i2c_v2)] pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, @@ -51,6 +54,7 @@ impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> self.write_vectored_timeout(address, write, self.timeout).await } + #[cfg(i2c_v2)] pub async fn write_vectored_timeout(&mut self, address: u8, write: &[&[u8]], timeout: Duration) -> Result<(), Error> where TXDMA: crate::i2c::TxDma, @@ -60,6 +64,7 @@ impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> .await } + #[cfg(i2c_v2)] pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, @@ -67,6 +72,7 @@ impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> self.read_timeout(address, buffer, self.timeout).await } + #[cfg(i2c_v2)] pub async fn read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> where RXDMA: crate::i2c::RxDma, @@ -74,6 +80,7 @@ impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> self.i2c.read_timeout(address, buffer, timeout_fn(timeout)).await } + #[cfg(i2c_v2)] pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> where TXDMA: super::TxDma, @@ -82,6 +89,7 @@ impl<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> self.write_read_timeout(address, write, read, self.timeout).await } + #[cfg(i2c_v2)] pub async fn write_read_timeout( &mut self, address: u8, From bbc8424a5b502187d62cb0ff607e1ac0a719c1c4 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 31 Jul 2023 17:55:25 -0500 Subject: [PATCH 173/298] stm32/dma: remove trace --- embassy-stm32/src/dma/ringbuffer.rs | 32 ----------------------------- 1 file changed, 32 deletions(-) diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index c3e4f20c..945c7508 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -229,14 +229,6 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { pub fn write(&mut self, mut dma: impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { let start = self.pos(dma.get_remaining_transfers()); if start > self.end { - trace!( - "[1]: start, end, len, complete_count: {}, {}, {}, {}", - start, - self.end, - buf.len(), - dma.get_complete_count() - ); - // The occupied portion in the ring buffer DOES wrap let len = self.copy_from(buf, self.end..start); @@ -253,24 +245,8 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { Ok((len, self.cap() - (start - self.end))) } } else if start == self.end && dma.get_complete_count() == 0 { - trace!( - "[2]: start, end, len, complete_count: {}, {}, {}, {}", - start, - self.end, - buf.len(), - dma.get_complete_count() - ); - Ok((0, 0)) } else if start <= self.end && self.end + buf.len() < self.cap() { - trace!( - "[3]: start, end, len, complete_count: {}, {}, {}, {}", - start, - self.end, - buf.len(), - dma.get_complete_count() - ); - // The occupied portion in the ring buffer DOES NOT wrap // and copying elements into the buffer WILL NOT cause it to @@ -289,14 +265,6 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { Ok((len, self.cap() - (self.end - start))) } } else { - trace!( - "[4]: start, end, len, complete_count: {}, {}, {}, {}", - start, - self.end, - buf.len(), - dma.get_complete_count() - ); - // The occupied portion in the ring buffer DOES NOT wrap // and copying elements into the buffer WILL cause it to From 5fcebd28f477f9e34ae2075b0cb6afdd545689ce Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 1 Aug 2023 13:46:34 +0930 Subject: [PATCH 174/298] Fix unlocking the backup domain when enabling LSE Set PWREN bit to enable the power interface clock before enabling access to the backup domain. --- embassy-stm32/src/rcc/l4.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index 237b7bc9..36c9eb2f 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -412,6 +412,7 @@ pub(crate) unsafe fn init(config: Config) { match config.rtc_mux { RtcClockSource::LSE32 => { // 1. Unlock the backup domain + RCC.apb1enr1().modify(|w| w.set_pwren(true)); PWR.cr1().modify(|w| w.set_dbp(true)); // 2. Setup the LSE From 48eac0b1462726ab292354b50e299c8ebfde85c6 Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 21 Jul 2023 21:01:56 +0200 Subject: [PATCH 175/298] rp: add AdcChannel trait this is more general than AdcPin and can also cover the temperature sensor. --- embassy-rp/src/adc.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 4fba3116..e0c2910b 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -223,21 +223,23 @@ impl interrupt::typelevel::Handler for Inter } mod sealed { - pub trait AdcPin: crate::gpio::sealed::Pin { + pub trait AdcChannel { fn channel(&mut self) -> u8; } } -pub trait AdcPin: sealed::AdcPin + gpio::Pin {} +pub trait AdcChannel: sealed::AdcChannel {} +pub trait AdcPin: AdcChannel + gpio::Pin {} macro_rules! impl_pin { ($pin:ident, $channel:expr) => { - impl sealed::AdcPin for peripherals::$pin { + impl sealed::AdcChannel for peripherals::$pin { fn channel(&mut self) -> u8 { $channel } } + impl AdcChannel for peripherals::$pin {} impl AdcPin for peripherals::$pin {} }; } From 54d31c98fe44533c955c494ea58dd16810367c4f Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 21 Jul 2023 21:33:53 +0200 Subject: [PATCH 176/298] rp: remove AdcChannel::channel we're not using it, and actually using it is more trouble than it's worth. remove the false assurance instead. --- embassy-rp/src/adc.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index e0c2910b..38b323ec 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -223,28 +223,21 @@ impl interrupt::typelevel::Handler for Inter } mod sealed { - pub trait AdcChannel { - fn channel(&mut self) -> u8; - } + pub trait AdcChannel {} } pub trait AdcChannel: sealed::AdcChannel {} pub trait AdcPin: AdcChannel + gpio::Pin {} macro_rules! impl_pin { - ($pin:ident, $channel:expr) => { - impl sealed::AdcChannel for peripherals::$pin { - fn channel(&mut self) -> u8 { - $channel - } - } - + ($pin:ident) => { + impl sealed::AdcChannel for peripherals::$pin {} impl AdcChannel for peripherals::$pin {} impl AdcPin for peripherals::$pin {} }; } -impl_pin!(PIN_26, 0); -impl_pin!(PIN_27, 1); -impl_pin!(PIN_28, 2); -impl_pin!(PIN_29, 3); +impl_pin!(PIN_26); +impl_pin!(PIN_27); +impl_pin!(PIN_28); +impl_pin!(PIN_29); From b166ed6b78db0737005a65c1e444ce7563de7da3 Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 21 Jul 2023 21:31:44 +0200 Subject: [PATCH 177/298] rp: generalize adc inputs from pins to channels this lets us treat pins and the temperature sensor uniformly using the same interface. uniformity in turn lets us add more adc features without combinatorial explosion of methods and types needed to handle them all. --- embassy-rp/src/adc.rs | 102 +++++++++++++++++-------------------- embassy-rp/src/lib.rs | 1 + examples/rp/src/bin/adc.rs | 11 ++-- tests/rp/src/bin/adc.rs | 22 ++++---- 4 files changed, 66 insertions(+), 70 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 38b323ec..2824d893 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -10,8 +10,8 @@ use crate::gpio::sealed::Pin as GpioPin; use crate::gpio::{self, AnyPin, Pull}; use crate::interrupt::typelevel::Binding; use crate::interrupt::InterruptExt; -use crate::peripherals::ADC; -use crate::{interrupt, pac, peripherals, Peripheral}; +use crate::peripherals::{ADC, ADC_TEMP_SENSOR}; +use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; static WAKER: AtomicWaker = AtomicWaker::new(); @@ -24,12 +24,15 @@ impl Default for Config { } } -pub struct Pin<'p> { - pin: PeripheralRef<'p, AnyPin>, +enum Source<'p> { + Pin(PeripheralRef<'p, AnyPin>), + TempSensor(PeripheralRef<'p, ADC_TEMP_SENSOR>), } -impl<'p> Pin<'p> { - pub fn new(pin: impl Peripheral

+ 'p, pull: Pull) -> Self { +pub struct Channel<'p>(Source<'p>); + +impl<'p> Channel<'p> { + pub fn new_pin(pin: impl Peripheral

+ 'p, pull: Pull) -> Self { into_ref!(pin); pin.pad_ctrl().modify(|w| { // manual says: @@ -42,24 +45,40 @@ impl<'p> Pin<'p> { w.set_pue(pull == Pull::Up); w.set_pde(pull == Pull::Down); }); - Self { pin: pin.map_into() } + Self(Source::Pin(pin.map_into())) + } + + pub fn new_sensor(s: impl Peripheral

+ 'p) -> Self { + let r = pac::ADC; + r.cs().write_set(|w| w.set_ts_en(true)); + Self(Source::TempSensor(s.into_ref())) } fn channel(&self) -> u8 { - // this requires adc pins to be sequential and matching the adc channels, - // which is the case for rp2040 - self.pin._pin() - 26 + match &self.0 { + // this requires adc pins to be sequential and matching the adc channels, + // which is the case for rp2040 + Source::Pin(p) => p._pin() - 26, + Source::TempSensor(_) => 4, + } } } -impl<'d> Drop for Pin<'d> { +impl<'p> Drop for Source<'p> { fn drop(&mut self) { - self.pin.pad_ctrl().modify(|w| { - w.set_ie(true); - w.set_od(false); - w.set_pue(false); - w.set_pde(true); - }); + match self { + Source::Pin(p) => { + p.pad_ctrl().modify(|w| { + w.set_ie(true); + w.set_od(false); + w.set_pue(false); + w.set_pde(true); + }); + } + Source::TempSensor(_) => { + pac::ADC.cs().write_clear(|w| w.set_ts_en(true)); + } + } } } @@ -115,10 +134,10 @@ impl<'d, M: Mode> Adc<'d, M> { while !r.cs().read().ready() {} } - fn sample_blocking(channel: u8) -> Result { + pub fn blocking_read(&mut self, ch: &mut Channel) -> Result { let r = Self::regs(); r.cs().modify(|w| { - w.set_ainsel(channel); + w.set_ainsel(ch.channel()); w.set_start_once(true); w.set_err(true); }); @@ -128,19 +147,6 @@ impl<'d, M: Mode> Adc<'d, M> { false => Ok(r.result().read().result().into()), } } - - pub fn blocking_read(&mut self, pin: &mut Pin) -> Result { - Self::sample_blocking(pin.channel()) - } - - pub fn blocking_read_temperature(&mut self) -> Result { - let r = Self::regs(); - r.cs().modify(|w| w.set_ts_en(true)); - while !r.cs().read().ready() {} - let result = Self::sample_blocking(4); - r.cs().modify(|w| w.set_ts_en(false)); - result - } } impl<'d> Adc<'d, Async> { @@ -172,10 +178,10 @@ impl<'d> Adc<'d, Async> { .await; } - async fn sample_async(channel: u8) -> Result { + pub async fn read(&mut self, ch: &mut Channel<'_>) -> Result { let r = Self::regs(); r.cs().modify(|w| { - w.set_ainsel(channel); + w.set_ainsel(ch.channel()); w.set_start_once(true); w.set_err(true); }); @@ -185,21 +191,6 @@ impl<'d> Adc<'d, Async> { false => Ok(r.result().read().result().into()), } } - - pub async fn read(&mut self, pin: &mut Pin<'_>) -> Result { - Self::sample_async(pin.channel()).await - } - - pub async fn read_temperature(&mut self) -> Result { - let r = Self::regs(); - r.cs().modify(|w| w.set_ts_en(true)); - if !r.cs().read().ready() { - Self::wait_for_ready().await; - } - let result = Self::sample_async(4).await; - r.cs().modify(|w| w.set_ts_en(false)); - result - } } impl<'d> Adc<'d, Blocking> { @@ -230,14 +221,17 @@ pub trait AdcChannel: sealed::AdcChannel {} pub trait AdcPin: AdcChannel + gpio::Pin {} macro_rules! impl_pin { - ($pin:ident) => { + ($pin:ident, $channel:expr) => { impl sealed::AdcChannel for peripherals::$pin {} impl AdcChannel for peripherals::$pin {} impl AdcPin for peripherals::$pin {} }; } -impl_pin!(PIN_26); -impl_pin!(PIN_27); -impl_pin!(PIN_28); -impl_pin!(PIN_29); +impl_pin!(PIN_26, 0); +impl_pin!(PIN_27, 1); +impl_pin!(PIN_28, 2); +impl_pin!(PIN_29, 3); + +impl sealed::AdcChannel for peripherals::ADC_TEMP_SENSOR {} +impl AdcChannel for peripherals::ADC_TEMP_SENSOR {} diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 45156458..49bd3533 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -183,6 +183,7 @@ embassy_hal_internal::peripherals! { FLASH, ADC, + ADC_TEMP_SENSOR, CORE1, diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index 81a8b834..c5869551 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin}; +use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; use embassy_rp::bind_interrupts; use embassy_rp::gpio::Pull; use embassy_time::{Duration, Timer}; @@ -22,9 +22,10 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let mut adc = Adc::new(p.ADC, Irqs, Config::default()); - let mut p26 = Pin::new(p.PIN_26, Pull::None); - let mut p27 = Pin::new(p.PIN_27, Pull::None); - let mut p28 = Pin::new(p.PIN_28, Pull::None); + let mut p26 = Channel::new_pin(p.PIN_26, Pull::None); + let mut p27 = Channel::new_pin(p.PIN_27, Pull::None); + let mut p28 = Channel::new_pin(p.PIN_28, Pull::None); + let mut ts = Channel::new_sensor(p.ADC_TEMP_SENSOR); loop { let level = adc.read(&mut p26).await.unwrap(); @@ -33,7 +34,7 @@ async fn main(_spawner: Spawner) { info!("Pin 27 ADC: {}", level); let level = adc.read(&mut p28).await.unwrap(); info!("Pin 28 ADC: {}", level); - let temp = adc.read_temperature().await.unwrap(); + let temp = adc.read(&mut ts).await.unwrap(); info!("Temp: {} degrees", convert_to_celsius(temp)); Timer::after(Duration::from_secs(1)).await; } diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs index e659844a..9006ce8c 100644 --- a/tests/rp/src/bin/adc.rs +++ b/tests/rp/src/bin/adc.rs @@ -6,7 +6,7 @@ mod common; use defmt::*; use embassy_executor::Spawner; -use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin}; +use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; use embassy_rp::bind_interrupts; use embassy_rp::gpio::Pull; use {defmt_rtt as _, panic_probe as _}; @@ -22,12 +22,12 @@ async fn main(_spawner: Spawner) { { { - let mut p = Pin::new(&mut p.PIN_26, Pull::Down); + let mut p = Channel::new_pin(&mut p.PIN_26, Pull::Down); defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); defmt::assert!(adc.read(&mut p).await.unwrap() < 0b01_0000_0000); } { - let mut p = Pin::new(&mut p.PIN_26, Pull::Up); + let mut p = Channel::new_pin(&mut p.PIN_26, Pull::Up); defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); defmt::assert!(adc.read(&mut p).await.unwrap() > 0b11_0000_0000); } @@ -35,21 +35,21 @@ async fn main(_spawner: Spawner) { // not bothering with async reads from now on { { - let mut p = Pin::new(&mut p.PIN_27, Pull::Down); + let mut p = Channel::new_pin(&mut p.PIN_27, Pull::Down); defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); } { - let mut p = Pin::new(&mut p.PIN_27, Pull::Up); + let mut p = Channel::new_pin(&mut p.PIN_27, Pull::Up); defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); } } { { - let mut p = Pin::new(&mut p.PIN_28, Pull::Down); + let mut p = Channel::new_pin(&mut p.PIN_28, Pull::Down); defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); } { - let mut p = Pin::new(&mut p.PIN_28, Pull::Up); + let mut p = Channel::new_pin(&mut p.PIN_28, Pull::Up); defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); } } @@ -57,22 +57,22 @@ async fn main(_spawner: Spawner) { // gp29 is connected to vsys through a 200k/100k divider, // adding pulls should change the value let low = { - let mut p = Pin::new(&mut p.PIN_29, Pull::Down); + let mut p = Channel::new_pin(&mut p.PIN_29, Pull::Down); adc.blocking_read(&mut p).unwrap() }; let none = { - let mut p = Pin::new(&mut p.PIN_29, Pull::None); + let mut p = Channel::new_pin(&mut p.PIN_29, Pull::None); adc.blocking_read(&mut p).unwrap() }; let up = { - let mut p = Pin::new(&mut p.PIN_29, Pull::Up); + let mut p = Channel::new_pin(&mut p.PIN_29, Pull::Up); adc.blocking_read(&mut p).unwrap() }; defmt::assert!(low < none); defmt::assert!(none < up); } - let temp = convert_to_celsius(adc.read_temperature().await.unwrap()); + let temp = convert_to_celsius(adc.read(&mut Channel::new_sensor(p.ADC_TEMP_SENSOR)).await.unwrap()); defmt::assert!(temp > 0.0); defmt::assert!(temp < 60.0); From a6b8f3d99478266b4f110e9c150ce3add5c3ffc6 Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 21 Jul 2023 23:34:12 +0200 Subject: [PATCH 178/298] rp: add single-channel dma from adc with uniform treatment of adc inputs it's easy enough to add a new sampling method. dma sampling only supports one channel at the moment, though round-robin sampling would be a simple extension (probably a new trait that's implemented for Channel and &[Channel]). continuous dma as proposed in #1608 also isn't done here, we'd expect that to be a compound dma::Channel that internally splits a buffer in half and dispatches callbacks or something like that. --- embassy-rp/src/adc.rs | 115 ++++++++++++++++++++++++++++++++++++- examples/rp/src/bin/adc.rs | 2 +- tests/rp/src/bin/adc.rs | 55 ++++++++++++++++-- 3 files changed, 165 insertions(+), 7 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 2824d893..bac45574 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -1,5 +1,6 @@ use core::future::poll_fn; use core::marker::PhantomData; +use core::mem; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; @@ -11,7 +12,7 @@ use crate::gpio::{self, AnyPin, Pull}; use crate::interrupt::typelevel::Binding; use crate::interrupt::InterruptExt; use crate::peripherals::{ADC, ADC_TEMP_SENSOR}; -use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; +use crate::{dma, interrupt, pac, peripherals, Peripheral, RegExt}; static WAKER: AtomicWaker = AtomicWaker::new(); @@ -48,7 +49,7 @@ impl<'p> Channel<'p> { Self(Source::Pin(pin.map_into())) } - pub fn new_sensor(s: impl Peripheral

+ 'p) -> Self { + pub fn new_temp_sensor(s: impl Peripheral

+ 'p) -> Self { let r = pac::ADC; r.cs().write_set(|w| w.set_ts_en(true)); Self(Source::TempSensor(s.into_ref())) @@ -82,6 +83,21 @@ impl<'p> Drop for Source<'p> { } } +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(transparent)] +pub struct Sample(u16); + +impl Sample { + pub fn good(&self) -> bool { + self.0 < 0x8000 + } + + pub fn value(&self) -> u16 { + self.0 & !0x8000 + } +} + #[derive(Debug, Eq, PartialEq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -191,6 +207,91 @@ impl<'d> Adc<'d, Async> { false => Ok(r.result().read().result().into()), } } + + async fn read_many_inner( + &mut self, + ch: &mut Channel<'_>, + buf: &mut [W], + fcs_err: bool, + dma: impl Peripheral

, + ) -> Result<(), Error> { + let r = Self::regs(); + // clear previous errors and set channel + r.cs().modify(|w| { + w.set_ainsel(ch.channel()); + w.set_err_sticky(true); // clear previous errors + w.set_start_many(false); + }); + // wait for previous conversions and drain fifo. an earlier batch read may have + // been cancelled, leaving the adc running. + while !r.cs().read().ready() {} + while !r.fcs().read().empty() { + r.fifo().read(); + } + + // set up fifo for dma + r.fcs().write(|w| { + w.set_thresh(1); + w.set_dreq_en(true); + w.set_shift(mem::size_of::() == 1); + w.set_en(true); + w.set_err(fcs_err); + }); + + // reset dma config on drop, regardless of whether it was a future being cancelled + // or the method returning normally. + struct ResetDmaConfig; + impl Drop for ResetDmaConfig { + fn drop(&mut self) { + pac::ADC.cs().write_clear(|w| w.set_start_many(true)); + while !pac::ADC.cs().read().ready() {} + pac::ADC.fcs().write_clear(|w| { + w.set_dreq_en(true); + w.set_shift(true); + w.set_en(true); + }); + } + } + let auto_reset = ResetDmaConfig; + + let dma = unsafe { dma::read(dma, r.fifo().as_ptr() as *const W, buf as *mut [W], 36) }; + // start conversions and wait for dma to finish. we can't report errors early + // because there's no interrupt to signal them, and inspecting every element + // of the fifo is too costly to do here. + r.cs().write_set(|w| w.set_start_many(true)); + dma.await; + mem::drop(auto_reset); + // we can't report errors before the conversions have ended since no interrupt + // exists to report them early, and since they're exceedingly rare we probably don't + // want to anyway. + match r.cs().read().err_sticky() { + false => Ok(()), + true => Err(Error::ConversionFailed), + } + } + + #[inline] + pub async fn read_many( + &mut self, + ch: &mut Channel<'_>, + buf: &mut [S], + dma: impl Peripheral

, + ) -> Result<(), Error> { + self.read_many_inner(ch, buf, false, dma).await + } + + #[inline] + pub async fn read_many_raw( + &mut self, + ch: &mut Channel<'_>, + buf: &mut [Sample], + dma: impl Peripheral

, + ) { + // errors are reported in individual samples + let _ = self + .read_many_inner(ch, unsafe { mem::transmute::<_, &mut [u16]>(buf) }, true, dma) + .await; + } } impl<'d> Adc<'d, Blocking> { @@ -214,9 +315,19 @@ impl interrupt::typelevel::Handler for Inter } mod sealed { + pub trait AdcSample: crate::dma::Word {} + pub trait AdcChannel {} } +pub trait AdcSample: sealed::AdcSample {} + +impl sealed::AdcSample for u16 {} +impl AdcSample for u16 {} + +impl sealed::AdcSample for u8 {} +impl AdcSample for u8 {} + pub trait AdcChannel: sealed::AdcChannel {} pub trait AdcPin: AdcChannel + gpio::Pin {} diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index c5869551..02bc493b 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -25,7 +25,7 @@ async fn main(_spawner: Spawner) { let mut p26 = Channel::new_pin(p.PIN_26, Pull::None); let mut p27 = Channel::new_pin(p.PIN_27, Pull::None); let mut p28 = Channel::new_pin(p.PIN_28, Pull::None); - let mut ts = Channel::new_sensor(p.ADC_TEMP_SENSOR); + let mut ts = Channel::new_temp_sensor(p.ADC_TEMP_SENSOR); loop { let level = adc.read(&mut p26).await.unwrap(); diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs index 9006ce8c..d6d58f0c 100644 --- a/tests/rp/src/bin/adc.rs +++ b/tests/rp/src/bin/adc.rs @@ -6,7 +6,7 @@ mod common; use defmt::*; use embassy_executor::Spawner; -use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; +use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler, Sample}; use embassy_rp::bind_interrupts; use embassy_rp::gpio::Pull; use {defmt_rtt as _, panic_probe as _}; @@ -71,10 +71,57 @@ async fn main(_spawner: Spawner) { defmt::assert!(low < none); defmt::assert!(none < up); } + { + let temp = convert_to_celsius( + adc.read(&mut Channel::new_temp_sensor(&mut p.ADC_TEMP_SENSOR)) + .await + .unwrap(), + ); + defmt::assert!(temp > 0.0); + defmt::assert!(temp < 60.0); + } - let temp = convert_to_celsius(adc.read(&mut Channel::new_sensor(p.ADC_TEMP_SENSOR)).await.unwrap()); - defmt::assert!(temp > 0.0); - defmt::assert!(temp < 60.0); + // run a bunch of conversions. we'll only check gp29 and the temp + // sensor here for brevity, if those two work the rest will too. + { + // gp29 is connected to vsys through a 200k/100k divider, + // adding pulls should change the value + let mut low = [0u16; 16]; + let mut none = [0u8; 16]; + let mut up = [Sample::default(); 16]; + adc.read_many( + &mut Channel::new_pin(&mut p.PIN_29, Pull::Down), + &mut low, + &mut p.DMA_CH0, + ) + .await + .unwrap(); + adc.read_many( + &mut Channel::new_pin(&mut p.PIN_29, Pull::None), + &mut none, + &mut p.DMA_CH0, + ) + .await + .unwrap(); + adc.read_many_raw(&mut Channel::new_pin(&mut p.PIN_29, Pull::Up), &mut up, &mut p.DMA_CH0) + .await; + defmt::assert!(low.iter().zip(none.iter()).all(|(l, n)| *l >> 4 < *n as u16)); + defmt::assert!(up.iter().all(|s| s.good())); + defmt::assert!(none.iter().zip(up.iter()).all(|(n, u)| (*n as u16) < u.value())); + } + { + let mut temp = [0u16; 16]; + adc.read_many( + &mut Channel::new_temp_sensor(&mut p.ADC_TEMP_SENSOR), + &mut temp, + &mut p.DMA_CH0, + ) + .await + .unwrap(); + let temp = temp.map(convert_to_celsius); + defmt::assert!(temp.iter().all(|t| *t > 0.0)); + defmt::assert!(temp.iter().all(|t| *t < 60.0)); + } info!("Test OK"); cortex_m::asm::bkpt(); From 33778d37721a076e1f64b0913cf1277257bd2b09 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Wed, 2 Aug 2023 20:13:48 +0000 Subject: [PATCH 179/298] Fix link to embassy-time in embassy-net/README.md --- embassy-net/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/README.md b/embassy-net/README.md index 48f9fd83..811321ca 100644 --- a/embassy-net/README.md +++ b/embassy-net/README.md @@ -49,7 +49,7 @@ trait has not had breaking changes. This crate can run on any executor. -[`embassy-time`](https://crates.io/crates/embassy-net-driver) is used for timekeeping and timeouts. You must +[`embassy-time`](https://crates.io/crates/embassy-time) is used for timekeeping and timeouts. You must link an `embassy-time` driver in your project to use this crate. ## License From bcaef1de189732f1744804cd7c9c038fe79f0be9 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 2 Aug 2023 22:57:42 +0200 Subject: [PATCH 180/298] feat: make nrf bootloader watchdog generic for any flash --- embassy-boot/nrf/src/lib.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index bb702073..65f57fcd 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -6,7 +6,7 @@ mod fmt; #[cfg(feature = "nightly")] pub use embassy_boot::FirmwareUpdater; pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig}; -use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE}; +use embassy_nrf::nvmc::PAGE_SIZE; use embassy_nrf::peripherals::WDT; use embassy_nrf::wdt; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; @@ -104,15 +104,15 @@ impl } } -/// A flash implementation that wraps NVMC and will pet a watchdog when touching flash. -pub struct WatchdogFlash<'d> { - flash: Nvmc<'d>, +/// A flash implementation that wraps any flash and will pet a watchdog when touching flash. +pub struct WatchdogFlash { + flash: FLASH, wdt: wdt::WatchdogHandle, } -impl<'d> WatchdogFlash<'d> { +impl WatchdogFlash { /// Start a new watchdog with a given flash and WDT peripheral and a timeout - pub fn start(flash: Nvmc<'d>, wdt: WDT, config: wdt::Config) -> Self { + pub fn start(flash: FLASH, wdt: WDT, config: wdt::Config) -> Self { let (_wdt, [wdt]) = match wdt::Watchdog::try_new(wdt, config) { Ok(x) => x, Err(_) => { @@ -127,13 +127,13 @@ impl<'d> WatchdogFlash<'d> { } } -impl<'d> ErrorType for WatchdogFlash<'d> { - type Error = as ErrorType>::Error; +impl ErrorType for WatchdogFlash { + type Error = FLASH::Error; } -impl<'d> NorFlash for WatchdogFlash<'d> { - const WRITE_SIZE: usize = as NorFlash>::WRITE_SIZE; - const ERASE_SIZE: usize = as NorFlash>::ERASE_SIZE; +impl NorFlash for WatchdogFlash { + const WRITE_SIZE: usize = FLASH::WRITE_SIZE; + const ERASE_SIZE: usize = FLASH::ERASE_SIZE; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { self.wdt.pet(); @@ -145,8 +145,8 @@ impl<'d> NorFlash for WatchdogFlash<'d> { } } -impl<'d> ReadNorFlash for WatchdogFlash<'d> { - const READ_SIZE: usize = as ReadNorFlash>::READ_SIZE; +impl ReadNorFlash for WatchdogFlash { + const READ_SIZE: usize = FLASH::READ_SIZE; fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> { self.wdt.pet(); self.flash.read(offset, data) From 4d60c715e683aaadf25d9f066bde805c725fefb4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 3 Aug 2023 14:23:11 +0200 Subject: [PATCH 181/298] net: move tuntap from std example to separate crate. (#1737) --- embassy-net-tuntap/Cargo.toml | 19 +++++++++++++++++++ embassy-net-tuntap/README.md | 17 +++++++++++++++++ .../src/lib.rs | 1 + examples/std/Cargo.toml | 3 +-- examples/std/src/bin/net.rs | 5 +---- examples/std/src/bin/net_dns.rs | 5 +---- examples/std/src/bin/net_udp.rs | 5 +---- examples/std/src/bin/tcp_accept.rs | 5 +---- 8 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 embassy-net-tuntap/Cargo.toml create mode 100644 embassy-net-tuntap/README.md rename examples/std/src/tuntap.rs => embassy-net-tuntap/src/lib.rs (99%) diff --git a/embassy-net-tuntap/Cargo.toml b/embassy-net-tuntap/Cargo.toml new file mode 100644 index 00000000..08d30968 --- /dev/null +++ b/embassy-net-tuntap/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "embassy-net-tuntap" +version = "0.1.0" +description = "embassy-net driver for Linux TUN/TAP interfaces." +keywords = ["embedded", "tuntap", "embassy-net", "embedded-hal-async", "ethernet", "async"] +categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] +license = "MIT OR Apache-2.0" +edition = "2021" + +[dependencies] +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } +async-io = "1.6.0" +log = "0.4.14" +libc = "0.2.101" + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-tuntap-v$VERSION/embassy-net-tuntap/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-tuntap/src/" +target = "thumbv7em-none-eabi" \ No newline at end of file diff --git a/embassy-net-tuntap/README.md b/embassy-net-tuntap/README.md new file mode 100644 index 00000000..c5d9e746 --- /dev/null +++ b/embassy-net-tuntap/README.md @@ -0,0 +1,17 @@ +# `embassy-net` integration for Linux TUN/TAP interfaces. + +[`embassy-net`](https://crates.io/crates/embassy-net) integration for for Linux TUN (IP medium) and TAP (Ethernet medium) interfaces. + +## Interoperability + +This crate can run on any executor. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. diff --git a/examples/std/src/tuntap.rs b/embassy-net-tuntap/src/lib.rs similarity index 99% rename from examples/std/src/tuntap.rs rename to embassy-net-tuntap/src/lib.rs index 167c3da5..75c54c48 100644 --- a/examples/std/src/tuntap.rs +++ b/embassy-net-tuntap/src/lib.rs @@ -19,6 +19,7 @@ const ETHERNET_HEADER_LEN: usize = 14; #[repr(C)] #[derive(Debug)] +#[allow(non_camel_case_types)] struct ifreq { ifr_name: [libc::c_char; libc::IF_NAMESIZE], ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */ diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 42adede1..54417682 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -9,7 +9,7 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["lo embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["log", "std", "nightly"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } -embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } +embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } @@ -18,7 +18,6 @@ env_logger = "0.9.0" futures = { version = "0.3.17" } log = "0.4.14" nix = "0.26.2" -libc = "0.2.101" 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 } diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 3aadb029..e0de1416 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -6,6 +6,7 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_tuntap::TunTapDevice; use embassy_time::Duration; use embedded_io::asynch::Write; use heapless::Vec; @@ -13,10 +14,6 @@ use log::*; use rand_core::{OsRng, RngCore}; use static_cell::{make_static, StaticCell}; -#[path = "../tuntap.rs"] -mod tuntap; - -use crate::tuntap::TunTapDevice; #[derive(Parser)] #[clap(version = "1.0")] struct Opts { diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index 65b5a2cd..6c19874d 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -6,15 +6,12 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::dns::DnsQueryType; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_tuntap::TunTapDevice; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; use static_cell::{make_static, StaticCell}; -#[path = "../tuntap.rs"] -mod tuntap; - -use crate::tuntap::TunTapDevice; #[derive(Parser)] #[clap(version = "1.0")] struct Opts { diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index 3fc46156..98dcc992 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -4,15 +4,12 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::udp::{PacketMetadata, UdpSocket}; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_tuntap::TunTapDevice; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; use static_cell::{make_static, StaticCell}; -#[path = "../tuntap.rs"] -mod tuntap; - -use crate::tuntap::TunTapDevice; #[derive(Parser)] #[clap(version = "1.0")] struct Opts { diff --git a/examples/std/src/bin/tcp_accept.rs b/examples/std/src/bin/tcp_accept.rs index df09986a..0c920a3f 100644 --- a/examples/std/src/bin/tcp_accept.rs +++ b/examples/std/src/bin/tcp_accept.rs @@ -7,6 +7,7 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net_tuntap::TunTapDevice; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write as _; use heapless::Vec; @@ -14,10 +15,6 @@ use log::*; use rand_core::{OsRng, RngCore}; use static_cell::{make_static, StaticCell}; -#[path = "../tuntap.rs"] -mod tuntap; - -use crate::tuntap::TunTapDevice; #[derive(Parser)] #[clap(version = "1.0")] struct Opts { From 9dfda46e0c43559a6ca2ed87c0ceb32d19456c62 Mon Sep 17 00:00:00 2001 From: pennae Date: Sun, 30 Jul 2023 02:10:48 +0200 Subject: [PATCH 182/298] rp: add dormant-wake functionality for Input this temporarily takes ownership of pins because we need to clear edge interrupts before waiting for them (otherwise we may wait indefinitely), we want to clean up the dormant-wake bits after a wakeup, and doing anything *else* with the input while we're waiting for a wakeup isn't possible at all. doing it like this lets us not impose any cost on those who don't use dormant wakes without entangling dormant waits too badly with regular interrupt waits. --- embassy-rp/src/gpio.rs | 61 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 73e89352..ad9d4262 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -75,6 +75,15 @@ pub enum Bank { Qspi = 1, } +#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DormantWakeConfig { + pub edge_high: bool, + pub edge_low: bool, + pub level_high: bool, + pub level_low: bool, +} + pub struct Input<'d, T: Pin> { pin: Flex<'d, T>, } @@ -128,6 +137,11 @@ impl<'d, T: Pin> Input<'d, T> { pub async fn wait_for_any_edge(&mut self) { self.pin.wait_for_any_edge().await; } + + #[inline] + pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake { + self.pin.dormant_wake(cfg) + } } /// Interrupt trigger levels. @@ -639,15 +653,62 @@ impl<'d, T: Pin> Flex<'d, T> { pub async fn wait_for_any_edge(&mut self) { InputFuture::new(&mut self.pin, InterruptTrigger::AnyEdge).await; } + + #[inline] + pub fn dormant_wake(&mut self, cfg: DormantWakeConfig) -> DormantWake { + let idx = self.pin._pin() as usize; + self.pin.io().intr(idx / 8).write(|w| { + w.set_edge_high(idx % 8, cfg.edge_high); + w.set_edge_low(idx % 8, cfg.edge_low); + }); + self.pin.io().int_dormant_wake().inte(idx / 8).write_set(|w| { + w.set_edge_high(idx % 8, cfg.edge_high); + w.set_edge_low(idx % 8, cfg.edge_low); + w.set_level_high(idx % 8, cfg.level_high); + w.set_level_low(idx % 8, cfg.level_low); + }); + DormantWake { + pin: self.pin.reborrow(), + cfg, + } + } } impl<'d, T: Pin> Drop for Flex<'d, T> { #[inline] fn drop(&mut self) { + let idx = self.pin._pin() as usize; self.pin.pad_ctrl().write(|_| {}); self.pin.gpio().ctrl().write(|w| { w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _); }); + self.pin.io().int_dormant_wake().inte(idx / 8).write_clear(|w| { + w.set_edge_high(idx % 8, true); + w.set_edge_low(idx % 8, true); + w.set_level_high(idx % 8, true); + w.set_level_low(idx % 8, true); + }); + } +} + +pub struct DormantWake<'w, T: Pin> { + pin: PeripheralRef<'w, T>, + cfg: DormantWakeConfig, +} + +impl<'w, T: Pin> Drop for DormantWake<'w, T> { + fn drop(&mut self) { + let idx = self.pin._pin() as usize; + self.pin.io().intr(idx / 8).write(|w| { + w.set_edge_high(idx % 8, self.cfg.edge_high); + w.set_edge_low(idx % 8, self.cfg.edge_low); + }); + self.pin.io().int_dormant_wake().inte(idx / 8).write_clear(|w| { + w.set_edge_high(idx % 8, true); + w.set_edge_low(idx % 8, true); + w.set_level_high(idx % 8, true); + w.set_level_low(idx % 8, true); + }); } } From dc5acc687f5a1a2984efaeca16fb167a49228399 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Thu, 3 Aug 2023 21:57:29 +0100 Subject: [PATCH 183/298] Fix package name for stm32f334-examples --- examples/stm32f334/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml index 6410891a..d8f6b8fe 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/Cargo.toml @@ -1,6 +1,6 @@ [package] edition = "2021" -name = "embassy-stm32f3-examples" +name = "embassy-stm32f334-examples" version = "0.1.0" license = "MIT OR Apache-2.0" From 55e07712e54a8afec70025e9ff0c007be956e11d Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 3 Aug 2023 22:56:39 +0200 Subject: [PATCH 184/298] rp: fix adc test flakiness GP29 is connected to the cyw43 SCK pin. cyw43 is selected by default (due to rp2040 pins being input/pulldown by default), so the wifi chip is always selected and watches the SCK pin. this little bit of load on the SCK pin is enough to disturb the 300k voltage divider used for VSYS sensing, making the test flaky. --- tests/rp/src/bin/adc.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs index d6d58f0c..b29a3a7c 100644 --- a/tests/rp/src/bin/adc.rs +++ b/tests/rp/src/bin/adc.rs @@ -8,7 +8,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler, Sample}; use embassy_rp::bind_interrupts; -use embassy_rp::gpio::Pull; +use embassy_rp::gpio::{Level, Output, Pull}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -18,6 +18,8 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut p = embassy_rp::init(Default::default()); + let _power_reg_pwm_mode = Output::new(p.PIN_23, Level::High); + let _wifi_off = Output::new(p.PIN_25, Level::High); let mut adc = Adc::new(p.ADC, Irqs, Config::default()); { From 7e269f6f1721ef5ca07c6e7354f2dd8b164644b7 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 3 Aug 2023 21:12:34 -0500 Subject: [PATCH 185/298] stm32/dma: consolidate ringbuf --- embassy-stm32/src/dma/bdma.rs | 70 +++++-------------- embassy-stm32/src/dma/dma.rs | 66 ++++-------------- embassy-stm32/src/dma/ringbuffer.rs | 103 +++++++++++++++++++++++----- 3 files changed, 114 insertions(+), 125 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 60f4fbd0..e79a159e 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -393,6 +393,10 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { fn reset_complete_count(&mut self) -> usize { STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel) } + + fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.0.index()].register(waker); + } } pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { @@ -463,7 +467,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { } pub fn clear(&mut self) { - self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); } /// Read elements from the ring buffer @@ -472,7 +476,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { /// 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) + self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf) } /// Read an exact number of elements from the ringbuffer. @@ -487,30 +491,9 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { /// - 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 + self.ringbuf + .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + .await } /// The capacity of the ringbuffer. @@ -519,7 +502,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { } pub fn set_waker(&mut self, waker: &Waker) { - STATE.ch_wakers[self.channel.index()].register(waker); + DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); } fn clear_irqs(&mut self) { @@ -628,41 +611,20 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { } pub fn clear(&mut self) { - self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); } /// Write elements to the ring buffer /// Return a tuple of the length written and the length remaining in the buffer pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { - self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) + self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf) } /// Write an exact number of elements to the ringbuffer. pub async fn write_exact(&mut self, buffer: &[W]) -> Result { - use core::future::poll_fn; - use core::sync::atomic::compiler_fence; - - let mut written_data = 0; - let buffer_len = buffer.len(); - - poll_fn(|cx| { - self.set_waker(cx.waker()); - - compiler_fence(Ordering::SeqCst); - - match self.write(&buffer[written_data..buffer_len]) { - Ok((len, remaining)) => { - written_data += len; - if written_data == buffer_len { - Poll::Ready(Ok(remaining)) - } else { - Poll::Pending - } - } - Err(e) => Poll::Ready(Err(e)), - } - }) - .await + self.ringbuf + .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + .await } /// The capacity of the ringbuffer. @@ -671,7 +633,7 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { } pub fn set_waker(&mut self, waker: &Waker) { - STATE.ch_wakers[self.channel.index()].register(waker); + DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); } fn clear_irqs(&mut self) { diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 9cd7aa8d..91d8b63b 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -623,6 +623,10 @@ impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> { fn reset_complete_count(&mut self) -> usize { STATE.complete_count[self.0.index()].swap(0, Ordering::AcqRel) } + + fn set_waker(&mut self, waker: &Waker) { + STATE.ch_wakers[self.0.index()].register(waker); + } } pub struct ReadableRingBuffer<'a, C: Channel, W: Word> { @@ -708,7 +712,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { } pub fn clear(&mut self) { - self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); } /// Read elements from the ring buffer @@ -717,7 +721,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { /// 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) + self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf) } /// Read an exact number of elements from the ringbuffer. @@ -732,30 +736,9 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { /// - 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 + self.ringbuf + .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + .await } // The capacity of the ringbuffer @@ -890,41 +873,20 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { } pub fn clear(&mut self) { - self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); + self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); } /// Write elements from the ring buffer /// Return a tuple of the length written and the length remaining in the buffer pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { - self.ringbuf.write(DmaCtrlImpl(self.channel.reborrow()), buf) + self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf) } /// Write an exact number of elements to the ringbuffer. pub async fn write_exact(&mut self, buffer: &[W]) -> Result { - use core::future::poll_fn; - use core::sync::atomic::compiler_fence; - - let mut written_data = 0; - let buffer_len = buffer.len(); - - poll_fn(|cx| { - self.set_waker(cx.waker()); - - compiler_fence(Ordering::SeqCst); - - match self.write(&buffer[written_data..buffer_len]) { - Ok((len, remaining)) => { - written_data += len; - if written_data == buffer_len { - Poll::Ready(Ok(remaining)) - } else { - Poll::Pending - } - } - Err(e) => Poll::Ready(Err(e)), - } - }) - .await + self.ringbuf + .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) + .await } // The capacity of the ringbuffer diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 945c7508..5a97152e 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -1,7 +1,9 @@ #![cfg_attr(gpdma, allow(unused))] +use core::future::poll_fn; use core::ops::Range; use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::{Poll, Waker}; use super::word::Word; @@ -49,6 +51,9 @@ pub trait DmaCtrl { /// Reset the transfer completed counter to 0 and return the value just prior to the reset. fn reset_complete_count(&mut self) -> usize; + + /// Set the waker for a running poll_fn + fn set_waker(&mut self, waker: &Waker); } impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { @@ -57,7 +62,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { } /// Reset the ring buffer to its initial state - pub fn clear(&mut self, mut dma: impl DmaCtrl) { + pub fn clear(&mut self, dma: &mut impl DmaCtrl) { self.start = 0; dma.reset_complete_count(); } @@ -68,8 +73,43 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { } /// The current position of the ringbuffer - fn pos(&self, remaining_transfers: usize) -> usize { - self.cap() - remaining_transfers + fn pos(&self, dma: &mut impl DmaCtrl) -> usize { + self.cap() - dma.get_remaining_transfers() + } + + /// 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, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result { + let mut read_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + dma.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + match self.read(dma, &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 } /// Read elements from the ring buffer @@ -77,7 +117,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { /// 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> { + pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { /* This algorithm is optimistic: we assume we haven't overrun more than a full buffer and then check after we've done our work to see we have. This is because on stm32, an interrupt is not guaranteed @@ -93,7 +133,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { rather than the data we actually copied because it costs nothing and confirms an error condition earlier. */ - let end = self.pos(dma.get_remaining_transfers()); + let end = self.pos(dma); if self.start == end && dma.get_complete_count() == 0 { // No elements are available in the buffer Ok((0, self.cap())) @@ -114,8 +154,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { then, get the current position of of the dma write and check if it's inside data we could have copied */ - let (pos, complete_count) = - critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count())); + let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count())); if (pos >= self.start && pos < end) || (complete_count > 0 && pos >= end) || complete_count > 1 { Err(OverrunError) } else { @@ -141,7 +180,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { then, get the current position of of the dma write and check if it's inside data we could have copied */ - let pos = self.pos(dma.get_remaining_transfers()); + let pos = self.pos(dma); if pos > self.start || pos < end || dma.get_complete_count() > 1 { Err(OverrunError) } else { @@ -169,7 +208,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { then, get the current position of of the dma write and check if it's inside data we could have copied */ - let pos = self.pos(dma.get_remaining_transfers()); + let pos = self.pos(dma); if pos > self.start || pos < end || dma.reset_complete_count() > 1 { Err(OverrunError) } else { @@ -209,7 +248,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } /// Reset the ring buffer to its initial state - pub fn clear(&mut self, mut dma: impl DmaCtrl) { + pub fn clear(&mut self, dma: &mut impl DmaCtrl) { self.end = 0; dma.reset_complete_count(); } @@ -220,14 +259,39 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } /// The current position of the ringbuffer - fn pos(&self, remaining_transfers: usize) -> usize { - self.cap() - remaining_transfers + fn pos(&self, dma: &mut impl DmaCtrl) -> usize { + self.cap() - dma.get_remaining_transfers() + } + + /// Write an exact number of elements to the ringbuffer. + pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result { + let mut written_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + dma.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + match self.write(dma, &buffer[written_data..buffer_len]) { + Ok((len, remaining)) => { + written_data += len; + if written_data == buffer_len { + Poll::Ready(Ok(remaining)) + } else { + Poll::Pending + } + } + Err(e) => Poll::Ready(Err(e)), + } + }) + .await } /// Write elements from the ring buffer /// Return a tuple of the length written and the capacity remaining to be written in the buffer - pub fn write(&mut self, mut dma: impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { - let start = self.pos(dma.get_remaining_transfers()); + pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { + let start = self.pos(dma); if start > self.end { // The occupied portion in the ring buffer DOES wrap let len = self.copy_from(buf, self.end..start); @@ -235,8 +299,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { compiler_fence(Ordering::SeqCst); // Confirm that the DMA is not inside data we could have written - let (pos, complete_count) = - critical_section::with(|_| (self.pos(dma.get_remaining_transfers()), dma.get_complete_count())); + let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count())); if (pos >= self.end && pos < start) || (complete_count > 0 && pos >= start) || complete_count > 1 { Err(OverrunError) } else { @@ -256,7 +319,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { compiler_fence(Ordering::SeqCst); // Confirm that the DMA is not inside data we could have written - let pos = self.pos(dma.get_remaining_transfers()); + let pos = self.pos(dma); if pos > self.end || pos < start || dma.get_complete_count() > 1 { Err(OverrunError) } else { @@ -274,7 +337,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { compiler_fence(Ordering::SeqCst); // Confirm that the DMA is not inside data we could have written - let pos = self.pos(dma.get_remaining_transfers()); + let pos = self.pos(dma); if pos > self.end || pos < start || dma.reset_complete_count() > 1 { Err(OverrunError) } else { @@ -323,7 +386,7 @@ mod tests { requests: cell::RefCell>, } - impl DmaCtrl for &mut TestCircularTransfer { + impl DmaCtrl for TestCircularTransfer { fn get_remaining_transfers(&self) -> usize { match self.requests.borrow_mut().pop().unwrap() { TestCircularTransferRequest::PositionRequest(pos) => { @@ -350,6 +413,8 @@ mod tests { _ => unreachable!(), } } + + fn set_waker(&mut self, waker: &Waker) {} } impl TestCircularTransfer { From e80db420610cc1bf2619bb64688f87712c1eee1c Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 4 Aug 2023 17:15:56 -0500 Subject: [PATCH 186/298] stm32/dma: minor cleanup, optmization --- embassy-stm32/src/dma/bdma.rs | 4 ++-- embassy-stm32/src/dma/dma.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index e79a159e..20ff29be 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -497,7 +497,7 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { } /// The capacity of the ringbuffer. - pub fn cap(&self) -> usize { + pub const fn cap(&self) -> usize { self.ringbuf.cap() } @@ -628,7 +628,7 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { } /// The capacity of the ringbuffer. - pub fn cap(&self) -> usize { + pub const fn cap(&self) -> usize { self.ringbuf.cap() } diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 91d8b63b..5033ae47 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -742,12 +742,12 @@ impl<'a, C: Channel, W: Word> ReadableRingBuffer<'a, C, W> { } // The capacity of the ringbuffer - pub fn cap(&self) -> usize { + pub const fn cap(&self) -> usize { self.ringbuf.cap() } pub fn set_waker(&mut self, waker: &Waker) { - STATE.ch_wakers[self.channel.index()].register(waker); + DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); } fn clear_irqs(&mut self) { @@ -890,12 +890,12 @@ impl<'a, C: Channel, W: Word> WritableRingBuffer<'a, C, W> { } // The capacity of the ringbuffer - pub fn cap(&self) -> usize { + pub const fn cap(&self) -> usize { self.ringbuf.cap() } pub fn set_waker(&mut self, waker: &Waker) { - STATE.ch_wakers[self.channel.index()].register(waker); + DmaCtrlImpl(self.channel.reborrow()).set_waker(waker); } fn clear_irqs(&mut self) { From 3c5f011245948d44dc88286ecd4e1554c8e4b28c Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 5 Aug 2023 00:02:39 +0200 Subject: [PATCH 187/298] rp: add generic dormant-sleep functionality this is "generic" in that it doesn't require the user to set up anything specific to go to dormant sleep, unlike the C sdk which requires clock sources to be configured explicitly and doesn't much care about PLLs. we will instead take a snapshot of the current clock configuration, switch to a known clock source (very slow rosc, in this case), go to sleep, and on wakeup undo everything we've done (ensuring stability of PLLs and such). tested locally, but adding tests to HIL seems infeasible. we'd need at least another pico or extensive modifications to teleprobe since dormant-sleep breaks SWD (except to rescue-dp), neither of which is feasible at this point. if we *did* want to add tests we should check for both rtc wakeups (with an external rtc clock source) and gpio wakeups. --- embassy-rp/src/clocks.rs | 130 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index a3398023..7b25ecff 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -1,3 +1,4 @@ +use core::arch::asm; use core::marker::PhantomData; use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; @@ -6,6 +7,7 @@ use pac::clocks::vals::*; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; +use crate::pac::common::{Reg, RW}; use crate::{pac, reset, Peripheral}; // NOTE: all gpin handling is commented out for future reference. @@ -873,3 +875,131 @@ impl rand_core::RngCore for RoscRng { dest.fill_with(Self::next_u8) } } +/// Enter the `DORMANT` sleep state. This will stop *all* internal clocks +/// and can only be exited through resets, dormant-wake GPIO interrupts, +/// and RTC interrupts. If RTC is clocked from an internal clock source +/// it will be stopped and not function as a wakeup source. +#[cfg(target_arch = "arm")] +pub fn dormant_sleep() { + struct Set(Reg, T, F); + + impl Drop for Set { + fn drop(&mut self) { + self.0.write_value(self.1); + self.2(); + } + } + + fn set_with_post_restore After>( + reg: Reg, + f: F, + ) -> Set { + reg.modify(|w| { + let old = *w; + let after = f(w); + Set(reg, old, after) + }) + } + + fn set(reg: Reg, f: F) -> Set { + set_with_post_restore(reg, |r| { + f(r); + || () + }) + } + + // disable all clocks that are not vital in preparation for disabling clock sources. + // we'll keep gpout and rtc clocks untouched, gpout because we don't care about them + // and rtc because it's a possible wakeup source. if clk_rtc is not configured for + // gpin we'll never wake from rtc, but that's what the user asked for then. + let _stop_adc = set(pac::CLOCKS.clk_adc_ctrl(), |w| w.set_enable(false)); + let _stop_usb = set(pac::CLOCKS.clk_usb_ctrl(), |w| w.set_enable(false)); + let _stop_peri = set(pac::CLOCKS.clk_peri_ctrl(), |w| w.set_enable(false)); + // set up rosc. we could ask the use to tell us which clock source to wake from like + // the C SDK does, but that seems rather unfriendly. we *may* disturb rtc by changing + // rosc configuration if it's currently the rtc clock source, so we'll configure rosc + // to the slowest frequency to minimize that impact. + let _configure_rosc = ( + set(pac::ROSC.ctrl(), |w| { + w.set_enable(pac::rosc::vals::Enable::ENABLE); + w.set_freq_range(pac::rosc::vals::FreqRange::LOW); + }), + // div=32 + set(pac::ROSC.div(), |w| w.set_div(pac::rosc::vals::Div(0xaa0))), + ); + while !pac::ROSC.status().read().stable() {} + // switch over to rosc as the system clock source. this will change clock sources for + // watchdog and timer clocks, but timers won't be a concern and the watchdog won't + // speed up by enough to worry about (unless it's clocked from gpin, which we don't + // support anyway). + let _switch_clk_ref = set(pac::CLOCKS.clk_ref_ctrl(), |w| { + w.set_src(pac::clocks::vals::ClkRefCtrlSrc::ROSC_CLKSRC_PH); + }); + let _switch_clk_sys = set(pac::CLOCKS.clk_sys_ctrl(), |w| { + w.set_src(pac::clocks::vals::ClkSysCtrlSrc::CLK_REF); + }); + // oscillator dormancy does not power down plls, we have to do that ourselves. we'll + // restore them to their prior glory when woken though since the system may be clocked + // from either (and usb/adc will probably need the USB PLL anyway) + let _stop_pll_sys = set_with_post_restore(pac::PLL_SYS.pwr(), |w| { + let wake = !w.pd() && !w.vcopd(); + w.set_pd(true); + w.set_vcopd(true); + move || while wake && !pac::PLL_SYS.cs().read().lock() {} + }); + let _stop_pll_usb = set_with_post_restore(pac::PLL_USB.pwr(), |w| { + let wake = !w.pd() && !w.vcopd(); + w.set_pd(true); + w.set_vcopd(true); + move || while wake && !pac::PLL_USB.cs().read().lock() {} + }); + // dormancy only stops the oscillator we're telling to go dormant, the other remains + // running. nothing can use xosc at this point any more. not doing this costs an 200µA. + let _stop_xosc = set_with_post_restore(pac::XOSC.ctrl(), |w| { + let wake = w.enable() == pac::xosc::vals::Enable::ENABLE; + if wake { + w.set_enable(pac::xosc::vals::Enable::DISABLE); + } + move || while wake && !pac::XOSC.status().read().stable() {} + }); + let _power_down_xip_cache = set(pac::XIP_CTRL.ctrl(), |w| w.set_power_down(true)); + + // only power down memory if we're running from XIP (or ROM? how?). + // powering down memory otherwise would require a lot of exacting checks that + // are better done by the user in a local copy of this function. + // powering down memories saves ~100µA, so it's well worth doing. + unsafe { + let is_in_flash = { + // we can't rely on the address of this function as rust sees it since linker + // magic or even boot2 may place it into ram. + let pc: usize; + asm!( + "mov {pc}, pc", + pc = out (reg) pc + ); + pc < 0x20000000 + }; + if is_in_flash { + // we will be powering down memories, so we must be *absolutely* + // certain that we're running entirely from XIP and registers until + // memories are powered back up again. accessing memory that's powered + // down may corrupt memory contents (see section 2.11.4 of the manual). + // additionally a 20ns wait time is needed after powering up memories + // again. rosc is likely to run at only a few MHz at most, so the + // inter-instruction delay alone will be enough to satisfy this bound. + asm!( + "ldr {old_mem}, [{mempowerdown}]", + "str {power_down_mems}, [{mempowerdown}]", + "str {coma}, [{dormant}]", + "str {old_mem}, [{mempowerdown}]", + old_mem = out (reg) _, + mempowerdown = in (reg) pac::SYSCFG.mempowerdown().as_ptr(), + power_down_mems = in (reg) 0b11111111, + dormant = in (reg) pac::ROSC.dormant().as_ptr(), + coma = in (reg) 0x636f6d61, + ); + } else { + pac::ROSC.dormant().write_value(0x636f6d61); + } + } +} From a2fd7108ff869d7c7c8f06832a2c6c837345d4a4 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 4 Aug 2023 19:08:53 -0500 Subject: [PATCH 188/298] stm32: update metapac version --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8c7dd38c..723c5029 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 = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-1f8ab493e029fc601edebc6bac105a63cc9858fe" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-82a24863823a3daf0ca664c7fdf008379d0a0d42" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -75,7 +75,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-1f8ab493e029fc601edebc6bac105a63cc9858fe", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-82a24863823a3daf0ca664c7fdf008379d0a0d42", default-features = false, features = ["metadata"]} [features] default = ["rt"] From 66faba2df76bf76bcfcca30d159c5a39b64819e8 Mon Sep 17 00:00:00 2001 From: loris <76793979+lorislibralato@users.noreply.github.com> Date: Sat, 5 Aug 2023 21:04:51 +0200 Subject: [PATCH 189/298] add wake_task_no_pend for expired timer enqueue inside run_queue --- embassy-executor/src/raw/mod.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index f3760f58..25c2ab0d 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -409,7 +409,7 @@ impl SyncExecutor { #[allow(clippy::never_loop)] loop { #[cfg(feature = "integrated-timers")] - self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); + self.timer_queue.dequeue_expired(Instant::now(), wake_task_no_pend); self.run_queue.dequeue_all(|p| { let task = p.header(); @@ -573,6 +573,31 @@ pub fn wake_task(task: TaskRef) { } } +/// Wake a task by `TaskRef` without calling pend. +/// +/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`]. +pub fn wake_task_no_pend(task: TaskRef) { + let header = task.header(); + + let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { + // If already scheduled, or if not started, + if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { + None + } else { + // Mark it as scheduled + Some(state | STATE_RUN_QUEUED) + } + }); + + if res.is_ok() { + // We have just marked the task as scheduled, so enqueue it. + unsafe { + let executor = header.executor.get().unwrap_unchecked(); + executor.run_queue.enqueue(task); + } + } +} + #[cfg(feature = "integrated-timers")] struct TimerQueue; From 7b3d7a382635307b03b0b94920272a04e7516395 Mon Sep 17 00:00:00 2001 From: Josh Junon Date: Sun, 6 Aug 2023 05:58:38 +0200 Subject: [PATCH 190/298] stm32: add note about Output/OutputOpenDrain drop behavior --- embassy-stm32/src/gpio.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 0cc269cf..8fd48423 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -351,6 +351,10 @@ impl From for bool { } /// GPIO output driver. +/// +/// Note that pins will **return to their floating state** when `Output` is dropped. +/// If pins should retain their state indefinitely, either keep ownership of the +/// `Output`, or pass it to [`core::mem::forget`]. pub struct Output<'d, T: Pin> { pub(crate) pin: Flex<'d, T>, } @@ -418,6 +422,10 @@ impl<'d, T: Pin> Output<'d, T> { } /// GPIO output open-drain driver. +/// +/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped. +/// If pins should retain their state indefinitely, either keep ownership of the +/// `OutputOpenDrain`, or pass it to [`core::mem::forget`]. pub struct OutputOpenDrain<'d, T: Pin> { pub(crate) pin: Flex<'d, T>, } From 66c171211822e9b680976f73f971ac27e5870934 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 6 Aug 2023 11:11:53 -0500 Subject: [PATCH 191/298] stm32/rtc: enable in rcc mod --- embassy-stm32/src/rcc/mod.rs | 4 ++++ embassy-stm32/src/rcc/wb.rs | 25 +++++++++++++++++++++++++ embassy-stm32/src/rtc/mod.rs | 15 +++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 5c69037e..62c19bda 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -73,6 +73,10 @@ pub struct Clocks { #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] pub adc: Option, + + #[cfg(rcc_wb)] + /// Set only if the lsi or lse is configured + pub rtc: Option, } /// Frozen clock frequencies diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index 21aacec5..97e6aa60 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -1,5 +1,6 @@ pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::rcc::Clocks; +use crate::rtc::{enable_rtc, RtcClockSource}; use crate::time::{khz, mhz, Hertz}; /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, @@ -110,6 +111,7 @@ pub struct Config { pub sys: Sysclk, pub mux: Option, pub pll48: Option, + pub rtc: Option, pub pll: Option, pub pllsai: Option, @@ -133,6 +135,7 @@ pub const WPAN_DEFAULT: Config = Config { prediv: 2, }), pll48: None, + rtc: None, pll: Some(Pll { mul: 12, @@ -160,6 +163,7 @@ impl Default for Config { pll48: None, pll: None, pllsai: None, + rtc: None, ahb1_pre: AHBPrescaler::NotDivided, ahb2_pre: AHBPrescaler::NotDivided, @@ -251,6 +255,12 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks { } }; + let rtc_clk = match config.rtc { + Some(RtcClockSource::LSI) => Some(LSI_FREQ), + Some(RtcClockSource::LSE) => Some(config.lse.unwrap()), + _ => None, + }; + Clocks { sys: sys_clk, ahb1: ahb1_clk, @@ -260,6 +270,7 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks { apb2: apb2_clk, apb1_tim: apb1_tim_clk, apb2_tim: apb2_tim_clk, + rtc: rtc_clk, } } @@ -281,6 +292,18 @@ pub(crate) fn configure_clocks(config: &Config) { while !rcc.cr().read().hsirdy() {} } + let needs_lsi = if let Some(rtc_mux) = &config.rtc { + *rtc_mux == RtcClockSource::LSI + } else { + false + }; + + if needs_lsi { + rcc.csr().modify(|w| w.set_lsi1on(true)); + + while !rcc.csr().read().lsi1rdy() {} + } + match &config.lse { Some(_) => { rcc.cfgr().modify(|w| w.set_stopwuck(true)); @@ -351,4 +374,6 @@ pub(crate) fn configure_clocks(config: &Config) { w.set_c2hpre(config.ahb2_pre.into()); w.set_shdhpre(config.ahb3_pre.into()); }); + + config.rtc.map(|clock_source| enable_rtc(clock_source)); } diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 323be318..551d8043 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -33,6 +33,21 @@ pub struct Rtc<'d, T: Instance> { rtc_config: RtcConfig, } +pub(crate) fn enable_rtc(clock_source: RtcClockSource) { + // TODO: rewrite the RTC module so that enable is separated from configure + + assert!(clock_source == RtcClockSource::LSI || clock_source == RtcClockSource::LSE); + + let _ = Rtc::new( + unsafe { crate::Peripherals::steal().RTC }, + RtcConfig { + clock_config: clock_source, + async_prescaler: 1, + sync_prescaler: 1, + }, + ); +} + #[derive(Copy, Clone, Debug, PartialEq)] #[repr(u8)] pub enum RtcClockSource { From 28618d12a11a73a5c0c4e2b176b7096796f79cdb Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 6 Aug 2023 11:58:28 -0500 Subject: [PATCH 192/298] stm32/rtc: restructure --- embassy-stm32/src/rcc/wb.rs | 2 +- embassy-stm32/src/rtc/mod.rs | 33 ++++++++++++++++----------------- embassy-stm32/src/rtc/v2.rs | 21 +++++++++------------ embassy-stm32/src/rtc/v3.rs | 19 +++++++++---------- 4 files changed, 35 insertions(+), 40 deletions(-) diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index 97e6aa60..e9b7296d 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -1,6 +1,6 @@ pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::rcc::Clocks; -use crate::rtc::{enable_rtc, RtcClockSource}; +use crate::rtc::{enable as enable_rtc, RtcClockSource}; use crate::time::{khz, mhz, Hertz}; /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 551d8043..2d54c926 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -33,19 +33,15 @@ pub struct Rtc<'d, T: Instance> { rtc_config: RtcConfig, } -pub(crate) fn enable_rtc(clock_source: RtcClockSource) { - // TODO: rewrite the RTC module so that enable is separated from configure +#[allow(dead_code)] +pub(crate) fn enable(clock_source: RtcClockSource) { + Rtc::::enable(clock_source); +} - assert!(clock_source == RtcClockSource::LSI || clock_source == RtcClockSource::LSE); - - let _ = Rtc::new( - unsafe { crate::Peripherals::steal().RTC }, - RtcConfig { - clock_config: clock_source, - async_prescaler: 1, - sync_prescaler: 1, - }, - ); +#[cfg(feature = "time")] +#[allow(dead_code)] +pub(crate) fn set_wakeup_timer(_duration: embassy_time::Duration) { + todo!() } #[derive(Copy, Clone, Debug, PartialEq)] @@ -64,7 +60,7 @@ pub enum RtcClockSource { #[derive(Copy, Clone, PartialEq)] pub struct RtcConfig { /// RTC clock source - clock_config: RtcClockSource, + clock_source: RtcClockSource, /// Asynchronous prescaler factor /// This is the asynchronous division factor: /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1) @@ -82,7 +78,7 @@ impl Default for RtcConfig { /// Raw sub-seconds in 1/256. fn default() -> Self { RtcConfig { - clock_config: RtcClockSource::LSI, + clock_source: RtcClockSource::LSI, async_prescaler: 127, sync_prescaler: 255, } @@ -91,8 +87,8 @@ impl Default for RtcConfig { impl RtcConfig { /// Sets the clock source of RTC config - pub fn clock_config(mut self, cfg: RtcClockSource) -> Self { - self.clock_config = cfg; + pub fn clock_source(mut self, clock_source: RtcClockSource) -> Self { + self.clock_source = clock_source; self } @@ -135,7 +131,10 @@ impl<'d, T: Instance> Rtc<'d, T> { rtc_config, }; - rtc_struct.apply_config(rtc_config); + Self::enable(rtc_config.clock_source); + + rtc_struct.configure(rtc_config); + rtc_struct.rtc_config = rtc_config; rtc_struct } diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index e3b9dfb8..743a04d6 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -1,15 +1,10 @@ use stm32_metapac::rtc::vals::{Init, Osel, Pol}; -use super::{sealed, Instance, RtcConfig}; +use super::{sealed, Instance, RtcClockSource, RtcConfig}; use crate::pac::rtc::Rtc; impl<'d, T: Instance> super::Rtc<'d, T> { - /// Applies the RTC config - /// It this changes the RTC clock source the time will be reset - pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { - // Unlock the backup domain - let clock_config = rtc_config.clock_config as u8; - + pub(super) fn enable(clock_source: RtcClockSource) { #[cfg(not(rtc_v2wb))] use stm32_metapac::rcc::vals::Rtcsel; @@ -38,7 +33,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { #[cfg(not(rtc_v2wb))] let rtcsel = reg.rtcsel().to_bits(); - if !reg.rtcen() || rtcsel != clock_config { + if !reg.rtcen() || rtcsel != clock_source as u8 { #[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))] crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] @@ -53,9 +48,9 @@ impl<'d, T: Instance> super::Rtc<'d, T> { // Select RTC source #[cfg(not(rtc_v2wb))] - w.set_rtcsel(Rtcsel::from_bits(clock_config)); + w.set_rtcsel(Rtcsel::from_bits(clock_source as u8)); #[cfg(rtc_v2wb)] - w.set_rtcsel(clock_config); + w.set_rtcsel(clock_source as u8); w.set_rtcen(true); // Restore bcdr @@ -71,7 +66,11 @@ impl<'d, T: Instance> super::Rtc<'d, T> { w.set_lsebyp(reg.lsebyp()); }); } + } + /// Applies the RTC config + /// It this changes the RTC clock source the time will be reset + pub(super) fn configure(&mut self, rtc_config: RtcConfig) { self.write(true, |rtc| { rtc.cr().modify(|w| { #[cfg(rtc_v2f2)] @@ -87,8 +86,6 @@ impl<'d, T: Instance> super::Rtc<'d, T> { w.set_prediv_a(rtc_config.async_prescaler); }); }); - - self.rtc_config = rtc_config; } /// Calibrate the clock drift. diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 8ef0ec51..64ecf4fb 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -1,12 +1,10 @@ use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType}; -use super::{sealed, Instance, RtcCalibrationCyclePeriod, RtcConfig}; +use super::{sealed, Instance, RtcCalibrationCyclePeriod, RtcClockSource, RtcConfig}; use crate::pac::rtc::Rtc; impl<'d, T: Instance> super::Rtc<'d, T> { - /// Applies the RTC config - /// It this changes the RTC clock source the time will be reset - pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) { + pub(super) fn enable(clock_source: RtcClockSource) { // Unlock the backup domain #[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))] { @@ -24,11 +22,10 @@ impl<'d, T: Instance> super::Rtc<'d, T> { let reg = crate::pac::RCC.bdcr().read(); assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - let config_rtcsel = rtc_config.clock_config as u8; #[cfg(not(any(rcc_wl5, rcc_wle)))] - let config_rtcsel = crate::pac::rcc::vals::Rtcsel::from_bits(config_rtcsel); + let config_rtcsel = crate::pac::rcc::vals::Rtcsel::from_bits(clock_source as u8); - if !reg.rtcen() || reg.rtcsel() != config_rtcsel { + if !reg.rtcen() || reg.rtcsel() != clock_source as u8 { crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); crate::pac::RCC.bdcr().modify(|w| { @@ -36,7 +33,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { w.set_bdrst(false); // Select RTC source - w.set_rtcsel(config_rtcsel); + w.set_rtcsel(clock_source as u8); w.set_rtcen(true); @@ -49,7 +46,11 @@ impl<'d, T: Instance> super::Rtc<'d, T> { w.set_lsebyp(reg.lsebyp()); }); } + } + /// Applies the RTC config + /// It this changes the RTC clock source the time will be reset + pub(super) fn configure(&mut self, rtc_config: RtcConfig) { self.write(true, |rtc| { rtc.cr().modify(|w| { w.set_fmt(Fmt::TWENTYFOURHOUR); @@ -69,8 +70,6 @@ impl<'d, T: Instance> super::Rtc<'d, T> { w.set_tampalrm_pu(TampalrmPu::NOPULLUP); }); }); - - self.rtc_config = rtc_config; } const RTC_CALR_MIN_PPM: f32 = -487.1; From ae608cf2fa009556953b72f18683496c17b8dca0 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 6 Aug 2023 12:06:29 -0500 Subject: [PATCH 193/298] stm32: fix rtc and examples --- embassy-stm32/src/rtc/v3.rs | 7 ++++--- examples/stm32wl/src/bin/rtc.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 64ecf4fb..9f04c9b6 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -22,10 +22,11 @@ impl<'d, T: Instance> super::Rtc<'d, T> { let reg = crate::pac::RCC.bdcr().read(); assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + let clock_source = clock_source as u8; #[cfg(not(any(rcc_wl5, rcc_wle)))] - let config_rtcsel = crate::pac::rcc::vals::Rtcsel::from_bits(clock_source as u8); + let clock_source = crate::pac::rcc::vals::Rtcsel::from_bits(clock_source); - if !reg.rtcen() || reg.rtcsel() != clock_source as u8 { + if !reg.rtcen() || reg.rtcsel() != clock_source { crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); crate::pac::RCC.bdcr().modify(|w| { @@ -33,7 +34,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { w.set_bdrst(false); // Select RTC source - w.set_rtcsel(clock_source as u8); + w.set_rtcsel(clock_source); w.set_rtcen(true); diff --git a/examples/stm32wl/src/bin/rtc.rs b/examples/stm32wl/src/bin/rtc.rs index e1182549..0d7906bd 100644 --- a/examples/stm32wl/src/bin/rtc.rs +++ b/examples/stm32wl/src/bin/rtc.rs @@ -29,7 +29,7 @@ async fn main(_spawner: Spawner) { let mut rtc = Rtc::new( p.RTC, - RtcConfig::default().clock_config(embassy_stm32::rtc::RtcClockSource::LSE), + RtcConfig::default().clock_source(embassy_stm32::rtc::RtcClockSource::LSE), ); info!("Got RTC! {:?}", now.timestamp()); From b555af1c5d1016d2a99ee20a1beadb00fddbed84 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 6 Aug 2023 12:12:18 -0500 Subject: [PATCH 194/298] stm32/rtc: fix exampel --- examples/stm32l4/src/bin/rtc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32l4/src/bin/rtc.rs b/examples/stm32l4/src/bin/rtc.rs index d72d5ddb..fe2aabb4 100644 --- a/examples/stm32l4/src/bin/rtc.rs +++ b/examples/stm32l4/src/bin/rtc.rs @@ -35,7 +35,7 @@ async fn main(_spawner: Spawner) { let mut rtc = Rtc::new( p.RTC, - RtcConfig::default().clock_config(embassy_stm32::rtc::RtcClockSource::LSE), + RtcConfig::default().clock_source(embassy_stm32::rtc::RtcClockSource::LSE), ); info!("Got RTC! {:?}", now.timestamp()); From a34331ae5fbf76a61bb2f65dbb13af4d34fcb176 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 3 Aug 2023 20:56:04 +0200 Subject: [PATCH 195/298] Refactor firmware updater * Allow manipulating state without accessing DFU partition. * Provide aligned buffer when creating updater to reduce potential wrong parameters passed. --- .../boot/src/firmware_updater/asynch.rs | 194 +++++++++-------- .../boot/src/firmware_updater/blocking.rs | 200 ++++++++++-------- embassy-boot/boot/src/firmware_updater/mod.rs | 4 +- embassy-boot/boot/src/lib.rs | 79 ++++--- embassy-boot/nrf/src/lib.rs | 6 +- embassy-boot/rp/src/lib.rs | 6 +- embassy-boot/stm32/src/lib.rs | 6 +- examples/boot/application/nrf/src/bin/a.rs | 8 +- examples/boot/application/rp/src/bin/a.rs | 7 +- .../boot/application/stm32f3/src/bin/a.rs | 8 +- .../boot/application/stm32f7/src/bin/a.rs | 6 +- .../boot/application/stm32h7/src/bin/a.rs | 6 +- .../boot/application/stm32l0/src/bin/a.rs | 8 +- .../boot/application/stm32l1/src/bin/a.rs | 8 +- .../boot/application/stm32l4/src/bin/a.rs | 8 +- .../boot/application/stm32wl/src/bin/a.rs | 8 +- 16 files changed, 307 insertions(+), 255 deletions(-) diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs index 20731ee0..ae713bb6 100644 --- a/embassy-boot/boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/boot/src/firmware_updater/asynch.rs @@ -10,9 +10,9 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAG /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to /// 'mess up' the internal bootloader state -pub struct FirmwareUpdater { +pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { dfu: DFU, - state: STATE, + state: FirmwareState<'d, STATE>, } #[cfg(target_os = "none")] @@ -47,22 +47,12 @@ impl<'a, FLASH: NorFlash> } } -impl FirmwareUpdater { +impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> { /// Create a firmware updater instance with partition ranges for the update and state partitions. - pub fn new(config: FirmwareUpdaterConfig) -> Self { + pub fn new(config: FirmwareUpdaterConfig, aligned: &'d mut [u8]) -> Self { Self { dfu: config.dfu, - state: config.state, - } - } - - // Make sure we are running a booted firmware to avoid reverting to a bad state. - async fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - if self.get_state(aligned).await? == State::Boot { - Ok(()) - } else { - Err(FirmwareUpdaterError::BadState) + state: FirmwareState::new(config.state, aligned), } } @@ -71,14 +61,8 @@ impl FirmwareUpdater { /// This is useful to check if the bootloader has just done a swap, in order /// to do verifications and self-tests of the new image before calling /// `mark_booted`. - pub async fn get_state(&mut self, aligned: &mut [u8]) -> Result { - self.state.read(0, aligned).await?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } + pub async fn get_state(&mut self) -> Result { + self.state.get_state().await } /// Verify the DFU given a public key. If there is an error then DO NOT @@ -92,23 +76,16 @@ impl FirmwareUpdater { /// /// If no signature feature is set then this method will always return a /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. #[cfg(feature = "_verify")] pub async fn verify_and_mark_updated( &mut self, _public_key: &[u8], _signature: &[u8], _update_len: u32, - _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), STATE::WRITE_SIZE); assert!(_update_len <= self.dfu.capacity() as u32); - self.verify_booted(_aligned).await?; + self.state.verify_booted().await?; #[cfg(feature = "ed25519-dalek")] { @@ -121,8 +98,9 @@ impl FirmwareUpdater { let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?; let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; + let mut chunk_buf = [0; 2]; let mut message = [0; 64]; - self.hash::(_update_len, _aligned, &mut message).await?; + self.hash::(_update_len, &mut chunk_buf, &mut message).await?; public_key.verify(&message, &signature).map_err(into_signature_error)? } @@ -143,7 +121,8 @@ impl FirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash::(_update_len, _aligned, &mut message).await?; + let mut chunk_buf = [0; 2]; + self.hash::(_update_len, &mut chunk_buf, &mut message).await?; let r = public_key.verify(&message, &signature); trace!( @@ -156,7 +135,7 @@ impl FirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic(_aligned, SWAP_MAGIC).await + self.state.mark_updated().await } /// Verify the update in DFU with any digest. @@ -177,49 +156,14 @@ impl FirmwareUpdater { } /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. #[cfg(not(feature = "_verify"))] - pub async fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC).await + pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { + self.state.mark_updated().await } /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub async fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.set_magic(aligned, BOOT_MAGIC).await - } - - async fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> { - self.state.read(0, aligned).await?; - - if aligned.iter().any(|&b| b != magic) { - // Read progress validity - self.state.read(STATE::WRITE_SIZE as u32, aligned).await?; - - if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { - // The current progress validity marker is invalid - } else { - // Invalidate progress - aligned.fill(!STATE_ERASE_VALUE); - self.state.write(STATE::WRITE_SIZE as u32, aligned).await?; - } - - // Clear magic and progress - self.state.erase(0, self.state.capacity() as u32).await?; - - // Set magic - aligned.fill(magic); - self.state.write(0, aligned).await?; - } - Ok(()) + pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + self.state.mark_booted().await } /// Write data to a flash page. @@ -229,16 +173,10 @@ impl FirmwareUpdater { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub async fn write_firmware( - &mut self, - aligned: &mut [u8], - offset: usize, - data: &[u8], - ) -> Result<(), FirmwareUpdaterError> { + pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= DFU::ERASE_SIZE); - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.verify_booted(aligned).await?; + self.state.verify_booted().await?; self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; @@ -252,20 +190,94 @@ impl FirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub async fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.verify_booted(aligned).await?; - + pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { + self.state.verify_booted().await?; self.dfu.erase(0, self.dfu.capacity() as u32).await?; Ok(&mut self.dfu) } } +/// Manages the state partition of the firmware update. +/// +/// Can be used standalone for more fine grained control, or as part of the updater. +pub struct FirmwareState<'d, STATE> { + state: STATE, + aligned: &'d mut [u8], +} + +impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { + /// Create a firmware state instance with a buffer for magic content and state partition. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + Self { state, aligned } + } + + // Make sure we are running a booted firmware to avoid reverting to a bad state. + async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + if self.get_state().await? == State::Boot { + Ok(()) + } else { + Err(FirmwareUpdaterError::BadState) + } + } + + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub async fn get_state(&mut self) -> Result { + self.state.read(0, &mut self.aligned).await?; + + if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Mark to trigger firmware swap on next boot. + pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { + self.set_magic(SWAP_MAGIC).await + } + + /// Mark firmware boot successful and stop rollback on reset. + pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + self.set_magic(BOOT_MAGIC).await + } + + async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> { + self.state.read(0, &mut self.aligned).await?; + + if self.aligned.iter().any(|&b| b != magic) { + // Read progress validity + self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?; + + if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + self.aligned.fill(!STATE_ERASE_VALUE); + self.state.write(STATE::WRITE_SIZE as u32, &self.aligned).await?; + } + + // Clear magic and progress + self.state.erase(0, self.state.capacity() as u32).await?; + + // Set magic + self.aligned.fill(magic); + self.state.write(0, &self.aligned).await?; + } + Ok(()) + } +} + #[cfg(test)] mod tests { use embassy_embedded_hal::flash::partition::Partition; @@ -288,8 +300,8 @@ mod tests { let mut to_write = [0; 4096]; to_write[..7].copy_from_slice(update.as_slice()); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); - block_on(updater.write_firmware(&mut aligned, 0, to_write.as_slice())).unwrap(); + let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); + block_on(updater.write_firmware(0, to_write.as_slice())).unwrap(); let mut chunk_buf = [0; 2]; let mut hash = [0; 20]; block_on(updater.hash::(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs index f03f53e4..76e4264a 100644 --- a/embassy-boot/boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/boot/src/firmware_updater/blocking.rs @@ -10,9 +10,9 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAG /// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to /// 'mess up' the internal bootloader state -pub struct BlockingFirmwareUpdater { +pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> { dfu: DFU, - state: STATE, + state: BlockingFirmwareState<'d, STATE>, } #[cfg(target_os = "none")] @@ -49,22 +49,17 @@ impl<'a, FLASH: NorFlash> } } -impl BlockingFirmwareUpdater { +impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> { /// Create a firmware updater instance with partition ranges for the update and state partitions. - pub fn new(config: FirmwareUpdaterConfig) -> Self { + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + pub fn new(config: FirmwareUpdaterConfig, aligned: &'d mut [u8]) -> Self { Self { dfu: config.dfu, - state: config.state, - } - } - - // Make sure we are running a booted firmware to avoid reverting to a bad state. - fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - if self.get_state(aligned)? == State::Boot { - Ok(()) - } else { - Err(FirmwareUpdaterError::BadState) + state: BlockingFirmwareState::new(config.state, aligned), } } @@ -73,14 +68,8 @@ impl BlockingFirmwareUpdater { /// This is useful to check if the bootloader has just done a swap, in order /// to do verifications and self-tests of the new image before calling /// `mark_booted`. - pub fn get_state(&mut self, aligned: &mut [u8]) -> Result { - self.state.read(0, aligned)?; - - if !aligned.iter().any(|&b| b != SWAP_MAGIC) { - Ok(State::Swap) - } else { - Ok(State::Boot) - } + pub fn get_state(&mut self) -> Result { + self.state.get_state() } /// Verify the DFU given a public key. If there is an error then DO NOT @@ -94,23 +83,16 @@ impl BlockingFirmwareUpdater { /// /// If no signature feature is set then this method will always return a /// signature error. - /// - /// # Safety - /// - /// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from - /// and written to. #[cfg(feature = "_verify")] pub fn verify_and_mark_updated( &mut self, _public_key: &[u8], _signature: &[u8], _update_len: u32, - _aligned: &mut [u8], ) -> Result<(), FirmwareUpdaterError> { - assert_eq!(_aligned.len(), STATE::WRITE_SIZE); assert!(_update_len <= self.dfu.capacity() as u32); - self.verify_booted(_aligned)?; + self.state.verify_booted()?; #[cfg(feature = "ed25519-dalek")] { @@ -124,7 +106,8 @@ impl BlockingFirmwareUpdater { let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash::(_update_len, _aligned, &mut message)?; + let mut chunk_buf = [0; 2]; + self.hash::(_update_len, &mut chunk_buf, &mut message)?; public_key.verify(&message, &signature).map_err(into_signature_error)? } @@ -145,7 +128,8 @@ impl BlockingFirmwareUpdater { let signature = Signature::try_from(&signature).map_err(into_signature_error)?; let mut message = [0; 64]; - self.hash::(_update_len, _aligned, &mut message)?; + let mut chunk_buf = [0; 2]; + self.hash::(_update_len, &mut chunk_buf, &mut message)?; let r = public_key.verify(&message, &signature); trace!( @@ -158,7 +142,7 @@ impl BlockingFirmwareUpdater { r.map_err(into_signature_error)? } - self.set_magic(_aligned, SWAP_MAGIC) + self.state.mark_updated() } /// Verify the update in DFU with any digest. @@ -179,49 +163,14 @@ impl BlockingFirmwareUpdater { } /// Mark to trigger firmware swap on next boot. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. #[cfg(not(feature = "_verify"))] - pub fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.set_magic(aligned, SWAP_MAGIC) + pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { + self.state.mark_updated() } /// Mark firmware boot successful and stop rollback on reset. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.set_magic(aligned, BOOT_MAGIC) - } - - fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> { - self.state.read(0, aligned)?; - - if aligned.iter().any(|&b| b != magic) { - // Read progress validity - self.state.read(STATE::WRITE_SIZE as u32, aligned)?; - - if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { - // The current progress validity marker is invalid - } else { - // Invalidate progress - aligned.fill(!STATE_ERASE_VALUE); - self.state.write(STATE::WRITE_SIZE as u32, aligned)?; - } - - // Clear magic and progress - self.state.erase(0, self.state.capacity() as u32)?; - - // Set magic - aligned.fill(magic); - self.state.write(0, aligned)?; - } - Ok(()) + pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + self.state.mark_booted() } /// Write data to a flash page. @@ -231,15 +180,9 @@ impl BlockingFirmwareUpdater { /// # Safety /// /// Failing to meet alignment and size requirements may result in a panic. - pub fn write_firmware( - &mut self, - aligned: &mut [u8], - offset: usize, - data: &[u8], - ) -> Result<(), FirmwareUpdaterError> { + pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { assert!(data.len() >= DFU::ERASE_SIZE); - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.verify_booted(aligned)?; + self.state.verify_booted()?; self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; @@ -253,19 +196,94 @@ impl BlockingFirmwareUpdater { /// /// Using this instead of `write_firmware` allows for an optimized API in /// exchange for added complexity. - /// - /// # Safety - /// - /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to. - pub fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> { - assert_eq!(aligned.len(), STATE::WRITE_SIZE); - self.verify_booted(aligned)?; + pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { + self.state.verify_booted()?; self.dfu.erase(0, self.dfu.capacity() as u32)?; Ok(&mut self.dfu) } } +/// Manages the state partition of the firmware update. +/// +/// Can be used standalone for more fine grained control, or as part of the updater. +pub struct BlockingFirmwareState<'d, STATE> { + state: STATE, + aligned: &'d mut [u8], +} + +impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> { + /// Create a firmware state instance with a buffer for magic content and state partition. + /// + /// # Safety + /// + /// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from + /// and written to. + pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self { + assert_eq!(aligned.len(), STATE::WRITE_SIZE); + Self { state, aligned } + } + + // Make sure we are running a booted firmware to avoid reverting to a bad state. + fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + if self.get_state()? == State::Boot { + Ok(()) + } else { + Err(FirmwareUpdaterError::BadState) + } + } + + /// Obtain the current state. + /// + /// This is useful to check if the bootloader has just done a swap, in order + /// to do verifications and self-tests of the new image before calling + /// `mark_booted`. + pub fn get_state(&mut self) -> Result { + self.state.read(0, &mut self.aligned)?; + + if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) { + Ok(State::Swap) + } else { + Ok(State::Boot) + } + } + + /// Mark to trigger firmware swap on next boot. + pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> { + self.set_magic(SWAP_MAGIC) + } + + /// Mark firmware boot successful and stop rollback on reset. + pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> { + self.set_magic(BOOT_MAGIC) + } + + fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> { + self.state.read(0, &mut self.aligned)?; + + if self.aligned.iter().any(|&b| b != magic) { + // Read progress validity + self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned)?; + + if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) { + // The current progress validity marker is invalid + } else { + // Invalidate progress + self.aligned.fill(!STATE_ERASE_VALUE); + self.state.write(STATE::WRITE_SIZE as u32, &self.aligned)?; + } + + // Clear magic and progress + self.state.erase(0, self.state.capacity() as u32)?; + + // Set magic + self.aligned.fill(magic); + self.state.write(0, &self.aligned)?; + } + Ok(()) + } +} + #[cfg(test)] mod tests { use core::cell::RefCell; @@ -283,14 +301,14 @@ mod tests { let flash = Mutex::::new(RefCell::new(MemFlash::<131072, 4096, 8>::default())); let state = BlockingPartition::new(&flash, 0, 4096); let dfu = BlockingPartition::new(&flash, 65536, 65536); + let mut aligned = [0; 8]; let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; let mut to_write = [0; 4096]; to_write[..7].copy_from_slice(update.as_slice()); - let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); - let mut aligned = [0; 8]; - updater.write_firmware(&mut aligned, 0, to_write.as_slice()).unwrap(); + let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned); + updater.write_firmware(0, to_write.as_slice()).unwrap(); let mut chunk_buf = [0; 2]; let mut hash = [0; 20]; updater diff --git a/embassy-boot/boot/src/firmware_updater/mod.rs b/embassy-boot/boot/src/firmware_updater/mod.rs index 55ce8f36..937ddcc6 100644 --- a/embassy-boot/boot/src/firmware_updater/mod.rs +++ b/embassy-boot/boot/src/firmware_updater/mod.rs @@ -3,8 +3,8 @@ mod asynch; mod blocking; #[cfg(feature = "nightly")] -pub use asynch::FirmwareUpdater; -pub use blocking::BlockingFirmwareUpdater; +pub use asynch::{FirmwareState, FirmwareUpdater}; +pub use blocking::{BlockingFirmwareState, BlockingFirmwareUpdater}; use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; /// Firmware updater flash configuration holding the two flashes used by the updater diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 016362b8..47f7c179 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs @@ -16,9 +16,11 @@ mod test_flash; // TODO: Use the value provided by NorFlash when available pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF; pub use boot_loader::{BootError, BootLoader, BootLoaderConfig}; +pub use firmware_updater::{ + BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError, +}; #[cfg(feature = "nightly")] -pub use firmware_updater::FirmwareUpdater; -pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError}; +pub use firmware_updater::{FirmwareState, FirmwareUpdater}; pub(crate) const BOOT_MAGIC: u8 = 0xD0; pub(crate) const SWAP_MAGIC: u8 = 0xF0; @@ -118,15 +120,18 @@ mod tests { block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); - block_on(updater.mark_updated(&mut aligned)).unwrap(); + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); + block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.mark_updated()).unwrap(); // Writing after marking updated is not allowed until marked as booted. - let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)); + let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(0, &UPDATE)); assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState))); let flash = flash.into_blocking(); @@ -158,11 +163,14 @@ mod tests { // Mark as booted let flash = flash.into_async(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - block_on(updater.mark_booted(&mut aligned)).unwrap(); + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); + block_on(updater.mark_booted()).unwrap(); let flash = flash.into_blocking(); let mut bootloader = BootLoader::new(BootLoaderConfig { @@ -190,12 +198,15 @@ mod tests { block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); - block_on(updater.mark_updated(&mut aligned)).unwrap(); + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); + block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.mark_updated()).unwrap(); let flash = flash.into_blocking(); let mut bootloader = BootLoader::new(BootLoaderConfig { @@ -232,12 +243,15 @@ mod tests { block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap(); block_on(flash.active().write(0, &ORIGINAL)).unwrap(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap(); - block_on(updater.mark_updated(&mut aligned)).unwrap(); + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); + block_on(updater.write_firmware(0, &UPDATE)).unwrap(); + block_on(updater.mark_updated()).unwrap(); let flash = flash.into_blocking(); let mut bootloader = BootLoader::new(BootLoaderConfig { @@ -293,18 +307,19 @@ mod tests { // On with the test let flash = flash.into_async(); - let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { - dfu: flash.dfu(), - state: flash.state(), - }); - let mut aligned = [0; 4]; + let mut updater = FirmwareUpdater::new( + FirmwareUpdaterConfig { + dfu: flash.dfu(), + state: flash.state(), + }, + &mut aligned, + ); assert!(block_on(updater.verify_and_mark_updated( &public_key.to_bytes(), &signature.to_bytes(), firmware_len as u32, - &mut aligned, )) .is_ok()); } diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 65f57fcd..df94819f 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -3,9 +3,11 @@ #![doc = include_str!("../README.md")] mod fmt; +pub use embassy_boot::{ + AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, +}; #[cfg(feature = "nightly")] -pub use embassy_boot::FirmwareUpdater; -pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig}; +pub use embassy_boot::{FirmwareState, FirmwareUpdater}; use embassy_nrf::nvmc::PAGE_SIZE; use embassy_nrf::peripherals::WDT; use embassy_nrf::wdt; diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 35fc104e..f5aefa41 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -3,9 +3,11 @@ #![doc = include_str!("../README.md")] mod fmt; +pub use embassy_boot::{ + AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State, +}; #[cfg(feature = "nightly")] -pub use embassy_boot::FirmwareUpdater; -pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; +pub use embassy_boot::{FirmwareState, FirmwareUpdater}; use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE}; use embassy_rp::peripherals::{FLASH, WATCHDOG}; use embassy_rp::watchdog::Watchdog; diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 069de0d1..25f02942 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -3,9 +3,11 @@ #![doc = include_str!("../README.md")] mod fmt; +pub use embassy_boot::{ + AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State, +}; #[cfg(feature = "nightly")] -pub use embassy_boot::FirmwareUpdater; -pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; +pub use embassy_boot::{FirmwareState, FirmwareUpdater}; use embedded_storage::nor_flash::NorFlash; /// A bootloader for STM32 devices. diff --git a/examples/boot/application/nrf/src/bin/a.rs b/examples/boot/application/nrf/src/bin/a.rs index 021d77f3..8b510ed3 100644 --- a/examples/boot/application/nrf/src/bin/a.rs +++ b/examples/boot/application/nrf/src/bin/a.rs @@ -52,20 +52,20 @@ async fn main(_spawner: Spawner) { let nvmc = Mutex::new(BlockingAsync::new(nvmc)); let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc); - let mut updater = FirmwareUpdater::new(config); + let mut magic = [0; 4]; + let mut updater = FirmwareUpdater::new(config, &mut magic); loop { led.set_low(); button.wait_for_any_edge().await; if button.is_low() { let mut offset = 0; - let mut magic = [0; 4]; for chunk in APP_B.chunks(4096) { let mut buf: [u8; 4096] = [0; 4096]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(&mut magic, offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(&mut magic).await.unwrap(); + updater.mark_updated().await.unwrap(); led.set_high(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index b5e1950c..f0dda39d 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -38,7 +38,8 @@ async fn main(_s: Spawner) { let flash = Mutex::new(RefCell::new(flash)); let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); - let mut updater = BlockingFirmwareUpdater::new(config); + let mut aligned = AlignedBuffer([0; 4]); + let mut updater = BlockingFirmwareUpdater::new(config, &mut aligned.0); Timer::after(Duration::from_secs(5)).await; watchdog.feed(); @@ -47,7 +48,7 @@ async fn main(_s: Spawner) { let mut buf: AlignedBuffer<4096> = AlignedBuffer([0; 4096]); defmt::info!("preparing update"); let writer = updater - .prepare_update(&mut buf.0[..1]) + .prepare_update() .map_err(|e| defmt::warn!("E: {:?}", defmt::Debug2Format(&e))) .unwrap(); defmt::info!("writer created, starting write"); @@ -59,7 +60,7 @@ async fn main(_s: Spawner) { } watchdog.feed(); defmt::info!("firmware written, marking update"); - updater.mark_updated(&mut buf.0[..1]).unwrap(); + updater.mark_updated().unwrap(); Timer::after(Duration::from_secs(2)).await; led.set_low(); defmt::info!("update marked, resetting"); diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index c0a11d69..8be39bfb 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -31,17 +31,17 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; let mut offset = 0; - let mut magic = AlignedBuffer([0; WRITE_SIZE]); for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index dea682a9..0c3819be 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -33,9 +33,9 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); - let mut updater = BlockingFirmwareUpdater::new(config); let mut magic = AlignedBuffer([0; WRITE_SIZE]); - let writer = updater.prepare_update(magic.as_mut()).unwrap(); + let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0); + let writer = updater.prepare_update().unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; let mut buf = AlignedBuffer([0; 4096]); @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { writer.write(offset, buf.as_ref()).unwrap(); offset += chunk.len() as u32; } - updater.mark_updated(magic.as_mut()).unwrap(); + updater.mark_updated().unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 71917669..f239e373 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -34,8 +34,8 @@ async fn main(_spawner: Spawner) { let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); let mut magic = AlignedBuffer([0; WRITE_SIZE]); - let mut updater = BlockingFirmwareUpdater::new(config); - let writer = updater.prepare_update(magic.as_mut()).unwrap(); + let mut updater = BlockingFirmwareUpdater::new(config, &mut magic.0); + let writer = updater.prepare_update().unwrap(); button.wait_for_rising_edge().await; let mut offset = 0; let mut buf = AlignedBuffer([0; 4096]); @@ -44,7 +44,7 @@ async fn main(_spawner: Spawner) { writer.write(offset, buf.as_ref()).unwrap(); offset += chunk.len() as u32; } - updater.mark_updated(magic.as_mut()).unwrap(); + updater.mark_updated().unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index ce80056e..b4cdcd44 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -33,18 +33,18 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; let mut offset = 0; - let mut magic = AlignedBuffer([0; WRITE_SIZE]); for chunk in APP_B.chunks(128) { let mut buf: [u8; 128] = [0; 128]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().await.unwrap(); led.set_low(); Timer::after(Duration::from_secs(1)).await; cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index 1e9bf3cb..b4cdcd44 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -33,18 +33,18 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); - button.wait_for_falling_edge().await; let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); + button.wait_for_falling_edge().await; let mut offset = 0; for chunk in APP_B.chunks(128) { let mut buf: [u8; 128] = [0; 128]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().await.unwrap(); led.set_low(); Timer::after(Duration::from_secs(1)).await; cortex_m::peripheral::SCB::sys_reset(); diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index a514ab5b..eefa25f7 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -31,17 +31,17 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); - button.wait_for_falling_edge().await; let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); + button.wait_for_falling_edge().await; let mut offset = 0; for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().await.unwrap(); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); } diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 52a197a5..c837e47b 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -31,19 +31,19 @@ async fn main(_spawner: Spawner) { led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash); - let mut updater = FirmwareUpdater::new(config); + let mut magic = AlignedBuffer([0; WRITE_SIZE]); + let mut updater = FirmwareUpdater::new(config, &mut magic.0); button.wait_for_falling_edge().await; //defmt::info!("Starting update"); - let mut magic = AlignedBuffer([0; WRITE_SIZE]); let mut offset = 0; for chunk in APP_B.chunks(2048) { let mut buf: [u8; 2048] = [0; 2048]; buf[..chunk.len()].copy_from_slice(chunk); // defmt::info!("Writing chunk at 0x{:x}", offset); - updater.write_firmware(magic.as_mut(), offset, &buf).await.unwrap(); + updater.write_firmware(offset, &buf).await.unwrap(); offset += chunk.len(); } - updater.mark_updated(magic.as_mut()).await.unwrap(); + updater.mark_updated().await.unwrap(); //defmt::info!("Marked as updated"); led.set_low(); cortex_m::peripheral::SCB::sys_reset(); From d49f40dd5c9480376484bd024aabc3651bde105e Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sun, 6 Aug 2023 15:00:39 -0500 Subject: [PATCH 196/298] embassy-stm32: Misc clippy fixes --- embassy-hal-internal/src/macros.rs | 1 + embassy-stm32/build.rs | 36 ++++++++++---------- embassy-stm32/src/can/bxcan.rs | 4 +-- embassy-stm32/src/dma/ringbuffer.rs | 2 +- embassy-stm32/src/flash/common.rs | 2 +- embassy-stm32/src/gpio.rs | 27 ++++++++++----- embassy-stm32/src/i2c/v1.rs | 15 ++------ embassy-stm32/src/spi/mod.rs | 12 +++---- embassy-stm32/src/timer/complementary_pwm.rs | 5 ++- embassy-stm32/src/usart/ringbuffered.rs | 9 +++-- embassy-stm32/src/wdg/mod.rs | 12 +++---- 11 files changed, 61 insertions(+), 64 deletions(-) diff --git a/embassy-hal-internal/src/macros.rs b/embassy-hal-internal/src/macros.rs index f06b4600..0eea4b66 100644 --- a/embassy-hal-internal/src/macros.rs +++ b/embassy-hal-internal/src/macros.rs @@ -116,6 +116,7 @@ macro_rules! impl_peripheral { #[inline] unsafe fn clone_unchecked(&self) -> Self::P { + #[allow(clippy::needless_update)] $type { ..*self } } } diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 30d4d7b0..30e25aef 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -126,7 +126,7 @@ fn main() { _ => panic!("unknown time_driver {:?}", time_driver), }; - if time_driver_singleton != "" { + if !time_driver_singleton.is_empty() { println!("cargo:rustc-cfg=time_driver_{}", time_driver_singleton.to_lowercase()); } @@ -634,7 +634,7 @@ fn main() { || regs.version.starts_with("h7") || regs.version.starts_with("f4") { - peri = format_ident!("{}", pin.signal.replace("_", "")); + peri = format_ident!("{}", pin.signal.replace('_', "")); } else { continue; } @@ -774,10 +774,11 @@ fn main() { .filter(|m| m.kind == MemoryRegionKind::Flash && m.settings.is_some()) { let settings = m.settings.as_ref().unwrap(); - let mut row = Vec::new(); - row.push(get_flash_region_type_name(m.name)); - row.push(settings.write_size.to_string()); - row.push(settings.erase_size.to_string()); + let row = vec![ + get_flash_region_type_name(m.name), + settings.write_size.to_string(), + settings.erase_size.to_string(), + ]; flash_regions_table.push(row); } @@ -787,7 +788,7 @@ fn main() { for p in METADATA.peripherals { if let Some(regs) = &p.registers { if regs.kind == "gpio" { - let port_letter = p.name.chars().skip(4).next().unwrap(); + let port_letter = p.name.chars().nth(4).unwrap(); assert_eq!(0, (p.address as u32 - gpio_base) % gpio_stride); let port_num = (p.address as u32 - gpio_base) / gpio_stride; @@ -804,18 +805,17 @@ fn main() { } for irq in p.interrupts { - let mut row = Vec::new(); - row.push(p.name.to_string()); - row.push(regs.kind.to_string()); - row.push(regs.block.to_string()); - row.push(irq.signal.to_string()); - row.push(irq.interrupt.to_ascii_uppercase()); + let row = vec![ + p.name.to_string(), + regs.kind.to_string(), + regs.block.to_string(), + irq.signal.to_string(), + irq.interrupt.to_ascii_uppercase(), + ]; interrupts_table.push(row) } - let mut row = Vec::new(); - row.push(regs.kind.to_string()); - row.push(p.name.to_string()); + let row = vec![regs.kind.to_string(), p.name.to_string()]; peripherals_table.push(row); } } @@ -975,7 +975,7 @@ macro_rules! {} {{ .unwrap(); for row in data { - write!(out, " __{}_inner!(({}));\n", name, row.join(",")).unwrap(); + writeln!(out, " __{}_inner!(({}));", name, row.join(",")).unwrap(); } write!( @@ -999,5 +999,5 @@ fn get_flash_region_type_name(name: &str) -> String { get_flash_region_name(name) .replace("BANK", "Bank") .replace("REGION", "Region") - .replace("_", "") + .replace('_', "") } diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index a3e3ec86..fb223e4a 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -271,7 +271,7 @@ impl<'d, T: Instance> Can<'d, T> { } else { let stid = (rir.stid() & 0x7FF) as u32; let exid = rir.exid() & 0x3FFFF; - let id = (stid << 18) | (exid as u32); + let id = (stid << 18) | (exid); Id::from(ExtendedId::new_unchecked(id)) }; let data_len = fifo.rdtr().read().dlc() as usize; @@ -383,7 +383,7 @@ impl<'d, T: Instance> Can<'d, T> { let sjw = 1; // Pack into BTR register values - Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler as u32 - 1)) + Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler - 1)) } pub fn split<'c>(&'c self) -> (CanTx<'c, 'd, T>, CanRx<'c, 'd, T>) { diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index 945c7508..6ce59e45 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -356,7 +356,7 @@ mod tests { pub fn new(len: usize) -> Self { Self { requests: cell::RefCell::new(vec![]), - len: len, + len, } } diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 16c51129..8acad1c7 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -147,7 +147,7 @@ pub(super) unsafe fn erase_sector_unlocked(sector: &FlashSector) -> Result<(), E let _on_drop = OnDrop::new(|| family::lock()); - family::blocking_erase_sector(§or) + family::blocking_erase_sector(sector) } pub(super) unsafe fn erase_sector_with_critical_section(sector: &FlashSector) -> Result<(), Error> { diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 0cc269cf..268ba042 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -777,12 +777,14 @@ mod eh02 { #[inline] fn set_high(&mut self) -> Result<(), Self::Error> { - Ok(self.set_high()) + self.set_high(); + Ok(()) } #[inline] fn set_low(&mut self) -> Result<(), Self::Error> { - Ok(self.set_low()) + self.set_low(); + Ok(()) } } @@ -803,7 +805,8 @@ mod eh02 { type Error = Infallible; #[inline] fn toggle(&mut self) -> Result<(), Self::Error> { - Ok(self.toggle()) + self.toggle(); + Ok(()) } } @@ -812,12 +815,14 @@ mod eh02 { #[inline] fn set_high(&mut self) -> Result<(), Self::Error> { - Ok(self.set_high()) + self.set_high(); + Ok(()) } #[inline] fn set_low(&mut self) -> Result<(), Self::Error> { - Ok(self.set_low()) + self.set_low(); + Ok(()) } } @@ -838,7 +843,8 @@ mod eh02 { type Error = Infallible; #[inline] fn toggle(&mut self) -> Result<(), Self::Error> { - Ok(self.toggle()) + self.toggle(); + Ok(()) } } @@ -861,12 +867,14 @@ mod eh02 { #[inline] fn set_high(&mut self) -> Result<(), Self::Error> { - Ok(self.set_high()) + self.set_high(); + Ok(()) } #[inline] fn set_low(&mut self) -> Result<(), Self::Error> { - Ok(self.set_low()) + self.set_low(); + Ok(()) } } @@ -887,7 +895,8 @@ mod eh02 { type Error = Infallible; #[inline] fn toggle(&mut self) -> Result<(), Self::Error> { - Ok(self.toggle()) + self.toggle(); + Ok(()) } } } diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index e5254a8c..618d85af 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -21,21 +21,12 @@ impl interrupt::typelevel::Handler for InterruptHandl } #[non_exhaustive] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub struct Config { pub sda_pullup: bool, pub scl_pullup: bool, } -impl Default for Config { - fn default() -> Self { - Self { - sda_pullup: false, - scl_pullup: false, - } - } -} - pub struct State {} impl State { @@ -90,7 +81,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { //reg.set_anfoff(false); }); - let timings = Timings::new(T::frequency(), freq.into()); + let timings = Timings::new(T::frequency(), freq); T::regs().cr2().modify(|reg| { reg.set_freq(timings.freq); @@ -461,7 +452,7 @@ impl Timings { let speed = speed.0; let clock = i2cclk.0; let freq = clock / 1_000_000; - assert!(freq >= 2 && freq <= 50); + assert!((2..=50).contains(&freq)); // Configure bus frequency into I2C peripheral let trise = if speed <= 100_000 { diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index bbc7c3b9..e2bc8d7f 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -223,7 +223,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let pclk = T::frequency(); let freq = config.frequency; - let br = compute_baud_rate(pclk, freq.into()); + let br = compute_baud_rate(pclk, freq); let cpha = config.raw_phase(); let cpol = config.raw_polarity(); @@ -331,7 +331,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let pclk = T::frequency(); let freq = config.frequency; - let br = compute_baud_rate(pclk, freq.into()); + let br = compute_baud_rate(pclk, freq); #[cfg(any(spi_v1, spi_f1, spi_v2))] T::REGS.cr1().modify(|w| { @@ -447,7 +447,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { where Tx: TxDma, { - if data.len() == 0 { + if data.is_empty() { return Ok(()); } @@ -481,7 +481,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { Tx: TxDma, Rx: RxDma, { - if data.len() == 0 { + if data.is_empty() { return Ok(()); } @@ -843,7 +843,7 @@ fn transfer_word(regs: Regs, tx_word: W) -> Result { spin_until_rx_ready(regs)?; let rx_word = unsafe { ptr::read_volatile(regs.rx_ptr()) }; - return Ok(rx_word); + Ok(rx_word) } mod eh02 { @@ -978,7 +978,7 @@ mod word_impl { impl_word!(u16, vals::Dff::SIXTEENBIT); } -#[cfg(any(spi_v2))] +#[cfg(spi_v2)] mod word_impl { use super::*; diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 48cb610f..acd67048 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -186,9 +186,8 @@ fn compute_dead_time_value(value: u16) -> (Ckd, u8) { error = this_error; } - match error { - 0 => break, - _ => {} + if error == 0 { + break; } } diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 71077c07..b0e626df 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -19,7 +19,7 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> UartRx<'d, T, RxDma> { /// without the possibility of loosing bytes. The `dma_buf` is a buffer registered to the /// DMA controller, and must be sufficiently large, such that it will not overflow. pub fn into_ring_buffered(self, dma_buf: &'d mut [u8]) -> RingBufferedUartRx<'d, T, RxDma> { - assert!(dma_buf.len() > 0 && dma_buf.len() <= 0xFFFF); + assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); let request = self.rx_dma.request(); let opts = Default::default(); @@ -111,10 +111,9 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma> RingBufferedUartRx<'d, T, RxD let r = T::regs(); // Start background receive if it was not already started - match r.cr3().read().dmar() { - false => self.start()?, - _ => {} - }; + if !r.cr3().read().dmar() { + self.start()?; + } check_for_errors(clear_idle_flag(T::regs()))?; diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index eafd0336..c7c2694e 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs @@ -60,9 +60,7 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> { rl ); - IndependentWatchdog { - wdg: PhantomData::default(), - } + IndependentWatchdog { wdg: PhantomData } } pub fn unleash(&mut self) { @@ -104,16 +102,16 @@ mod tests { assert_eq!(512_000, get_timeout_us(4, MAX_RL)); assert_eq!(8_000, get_timeout_us(256, 0)); - assert_eq!(32768_000, get_timeout_us(256, MAX_RL)); + assert_eq!(32_768_000, get_timeout_us(256, MAX_RL)); - assert_eq!(8000_000, get_timeout_us(64, 3999)); + assert_eq!(8_000_000, get_timeout_us(64, 3999)); } #[test] fn can_compute_reload_value() { assert_eq!(0xFFF, reload_value(4, 512_000)); - assert_eq!(0xFFF, reload_value(256, 32768_000)); + assert_eq!(0xFFF, reload_value(256, 32_768_000)); - assert_eq!(3999, reload_value(64, 8000_000)); + assert_eq!(3999, reload_value(64, 8_000_000)); } } From 8b837fae071e38084e438544139cbc7049987118 Mon Sep 17 00:00:00 2001 From: Dave Hylands Date: Sun, 6 Aug 2023 13:08:08 -0700 Subject: [PATCH 197/298] Make usb-logger read and discard input data This allows normal linux terminal emulators, like screen or picocom to be used with the usb_logger. Without this, calling `tcsetattr` with `TCSAFLUSH` will hang. --- embassy-usb-logger/src/lib.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index 6e50c40b..9178dd6d 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -77,22 +77,29 @@ impl UsbLogger { ); // Create classes on the builder. - let mut class = CdcAcmClass::new(&mut builder, &mut state.state, MAX_PACKET_SIZE as u16); + let class = CdcAcmClass::new(&mut builder, &mut state.state, MAX_PACKET_SIZE as u16); + let (mut sender, mut receiver) = class.split(); // Build the builder. let mut device = builder.build(); - loop { let run_fut = device.run(); let log_fut = async { let mut rx: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; - class.wait_connection().await; + sender.wait_connection().await; loop { let len = self.buffer.read(&mut rx[..]).await; - let _ = class.write_packet(&rx[..len]).await; + let _ = sender.write_packet(&rx[..len]).await; } }; - join(run_fut, log_fut).await; + let discard_fut = async { + let mut discard_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; + receiver.wait_connection().await; + loop { + let _ = receiver.read_packet(&mut discard_buf).await; + } + }; + join(run_fut, join(log_fut, discard_fut)).await; } } } From 5d5cd2371504915a531e669dce3558485a51a2e1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 7 Aug 2023 13:43:09 +0200 Subject: [PATCH 198/298] Update to embedded-io 0.5 (#1752) --- ci.sh | 14 +++----- ci_stable.sh | 2 -- embassy-net/Cargo.toml | 11 +++--- embassy-net/src/dns.rs | 2 +- embassy-net/src/tcp.rs | 34 +++++++++---------- embassy-nrf/Cargo.toml | 7 ++-- embassy-nrf/src/buffered_uarte.rs | 22 ++++++------ embassy-rp/Cargo.toml | 5 +-- embassy-rp/src/uart/buffered.rs | 26 +++++++------- embassy-stm32/Cargo.toml | 7 ++-- embassy-stm32/src/usart/buffered.rs | 32 ++++++++--------- embassy-stm32/src/usart/mod.rs | 7 ++-- embassy-stm32/src/usart/ringbuffered.rs | 5 ++- embassy-sync/Cargo.toml | 4 +-- embassy-sync/src/pipe.rs | 20 +++++------ examples/nrf52840/Cargo.toml | 5 +-- examples/nrf52840/src/bin/buffered_uart.rs | 2 +- examples/nrf52840/src/bin/usb_ethernet.rs | 2 +- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 4 +-- .../rp/src/bin/ethernet_w5500_multisocket.rs | 2 +- .../rp/src/bin/ethernet_w5500_tcp_client.rs | 2 +- .../rp/src/bin/ethernet_w5500_tcp_server.rs | 2 +- examples/rp/src/bin/pio_uart.rs | 12 +++---- examples/rp/src/bin/uart_buffered_split.rs | 2 +- examples/rp/src/bin/usb_ethernet.rs | 2 +- examples/rp/src/bin/wifi_ap_tcp_server.rs | 2 +- examples/rp/src/bin/wifi_tcp_server.rs | 2 +- examples/std/Cargo.toml | 5 +-- examples/std/src/bin/net.rs | 2 +- examples/std/src/bin/serial.rs | 4 +-- examples/std/src/bin/tcp_accept.rs | 2 +- examples/stm32f4/Cargo.toml | 3 +- examples/stm32f4/src/bin/eth.rs | 2 +- examples/stm32f4/src/bin/usart_buffered.rs | 2 +- examples/stm32f4/src/bin/usb_ethernet.rs | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32f7/src/bin/eth.rs | 2 +- examples/stm32h5/Cargo.toml | 6 ++-- examples/stm32h5/src/bin/eth.rs | 2 +- examples/stm32h7/Cargo.toml | 6 ++-- examples/stm32h7/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth_client.rs | 2 +- examples/stm32l0/Cargo.toml | 5 +-- examples/stm32l0/src/bin/usart_irq.rs | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32l5/src/bin/usb_ethernet.rs | 2 +- tests/nrf/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/rp/src/bin/uart_buffered.rs | 2 +- tests/rp/src/bin/uart_upgrade.rs | 2 +- 52 files changed, 149 insertions(+), 155 deletions(-) diff --git a/ci.sh b/ci.sh index e7ad0ead..146a1c50 100755 --- a/ci.sh +++ b/ci.sh @@ -20,25 +20,19 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ - --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \ + --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- 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 \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,nightly \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits,nightly \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,unstable-traits,nightly \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,unstable-traits,nightly \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154,unstable-traits,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154,nightly \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ diff --git a/ci_stable.sh b/ci_stable.sh index 56f72131..4ee5f410 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -16,9 +16,7 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,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 \ - --- 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-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \ diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 15b3ba2d..0c551f20 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -14,11 +14,11 @@ categories = [ [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"] +features = ["nightly", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"] target = "thumbv7em-none-eabi" [package.metadata.docs.rs] -features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"] +features = ["nightly", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"] [features] default = [] @@ -26,8 +26,7 @@ std = [] defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"] -nightly = ["dep:embedded-io", "embedded-io?/async", "dep:embedded-nal-async"] -unstable-traits = [] +nightly = ["dep:embedded-io-async", "dep:embedded-nal-async"] udp = ["smoltcp/socket-udp"] tcp = ["smoltcp/socket-tcp"] @@ -53,7 +52,7 @@ smoltcp = { version = "0.10.0", default-features = false, features = [ embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-time = { version = "0.1.2", path = "../embassy-time" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embedded-io = { version = "0.4.0", optional = true } +embedded-io-async = { version = "0.5.0", optional = true } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } heapless = { version = "0.7.5", default-features = false } @@ -62,5 +61,5 @@ generic-array = { version = "0.14.4", default-features = false } stable_deref_trait = { version = "1.2.0", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } atomic-pool = "1.0" -embedded-nal-async = { version = "0.4.0", optional = true } +embedded-nal-async = { version = "0.5.0", optional = true } atomic-polyfill = { version = "1.0" } diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 94f75f10..fdd45b31 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -68,7 +68,7 @@ where } } -#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +#[cfg(feature = "nightly")] impl<'a, D> embedded_nal_async::Dns for DnsSocket<'a, D> where D: Driver + 'static, diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 367675b1..c903fb24 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -382,29 +382,29 @@ impl<'d> TcpIo<'d> { mod embedded_io_impls { use super::*; - impl embedded_io::Error for ConnectError { - fn kind(&self) -> embedded_io::ErrorKind { - embedded_io::ErrorKind::Other + impl embedded_io_async::Error for ConnectError { + fn kind(&self) -> embedded_io_async::ErrorKind { + embedded_io_async::ErrorKind::Other } } - impl embedded_io::Error for Error { - fn kind(&self) -> embedded_io::ErrorKind { - embedded_io::ErrorKind::Other + impl embedded_io_async::Error for Error { + fn kind(&self) -> embedded_io_async::ErrorKind { + embedded_io_async::ErrorKind::Other } } - impl<'d> embedded_io::Io for TcpSocket<'d> { + impl<'d> embedded_io_async::ErrorType for TcpSocket<'d> { type Error = Error; } - impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { + impl<'d> embedded_io_async::Read for TcpSocket<'d> { async fn read(&mut self, buf: &mut [u8]) -> Result { self.io.read(buf).await } } - impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { + impl<'d> embedded_io_async::Write for TcpSocket<'d> { async fn write(&mut self, buf: &[u8]) -> Result { self.io.write(buf).await } @@ -414,21 +414,21 @@ mod embedded_io_impls { } } - impl<'d> embedded_io::Io for TcpReader<'d> { + impl<'d> embedded_io_async::ErrorType for TcpReader<'d> { type Error = Error; } - impl<'d> embedded_io::asynch::Read for TcpReader<'d> { + impl<'d> embedded_io_async::Read for TcpReader<'d> { async fn read(&mut self, buf: &mut [u8]) -> Result { self.io.read(buf).await } } - impl<'d> embedded_io::Io for TcpWriter<'d> { + impl<'d> embedded_io_async::ErrorType for TcpWriter<'d> { type Error = Error; } - impl<'d> embedded_io::asynch::Write for TcpWriter<'d> { + impl<'d> embedded_io_async::Write for TcpWriter<'d> { async fn write(&mut self, buf: &[u8]) -> Result { self.io.write(buf).await } @@ -440,7 +440,7 @@ mod embedded_io_impls { } /// TCP client compatible with `embedded-nal-async` traits. -#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +#[cfg(feature = "nightly")] pub mod client { use core::cell::UnsafeCell; use core::mem::MaybeUninit; @@ -527,13 +527,13 @@ pub mod client { } } - impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::Io + impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::ErrorType for TcpConnection<'d, N, TX_SZ, RX_SZ> { type Error = Error; } - impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Read + impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::Read for TcpConnection<'d, N, TX_SZ, RX_SZ> { async fn read(&mut self, buf: &mut [u8]) -> Result { @@ -541,7 +541,7 @@ pub mod client { } } - impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Write + impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::Write for TcpConnection<'d, N, TX_SZ, RX_SZ> { async fn write(&mut self, buf: &[u8]) -> Result { diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index d10cd2c3..da1cd38e 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -32,10 +32,10 @@ rt = [ time = ["dep:embassy-time"] -defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"] +defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embassy-embedded-hal/defmt"] # Enable nightly-only features -nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"] +nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io-async", "embassy-embedded-hal/nightly"] # Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`. # This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version. @@ -100,7 +100,8 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} -embedded-io = { version = "0.4.0", features = ["async"], optional = true } +embedded-io = { version = "0.5.0" } +embedded-io-async = { version = "0.5.0", optional = true } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 5a0a3c7c..10b8b0fb 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -572,37 +572,37 @@ impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'u, 'd, U, T> { mod _embedded_io { use super::*; - impl embedded_io::Error for Error { - fn kind(&self) -> embedded_io::ErrorKind { + impl embedded_io_async::Error for Error { + fn kind(&self) -> embedded_io_async::ErrorKind { match *self {} } } - impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> { + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarte<'d, U, T> { type Error = Error; } - impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> { + impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarteRx<'u, 'd, U, T> { type Error = Error; } - impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> { + impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarteTx<'u, 'd, U, T> { type Error = Error; } - impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> { + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::Read for BufferedUarte<'d, U, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { self.inner_read(buf).await } } - impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> { + impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::Read for BufferedUarteRx<'u, 'd, U, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { self.inner.inner_read(buf).await } } - impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> { + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarte<'d, U, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { self.inner_fill_buf().await } @@ -612,7 +612,7 @@ mod _embedded_io { } } - impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> { + impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarteRx<'u, 'd, U, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { self.inner.inner_fill_buf().await } @@ -622,7 +622,7 @@ mod _embedded_io { } } - impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> { + impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::Write for BufferedUarte<'d, U, T> { async fn write(&mut self, buf: &[u8]) -> Result { self.inner_write(buf).await } @@ -632,7 +632,7 @@ mod _embedded_io { } } - impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> { + impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::Write for BufferedUarteTx<'u, 'd, U, T> { async fn write(&mut self, buf: &[u8]) -> Result { self.inner.inner_write(buf).await } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 564d44ec..b1680d48 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -52,7 +52,7 @@ qspi-as-gpio = [] run-from-ram = [] # Enable nightly-only features -nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] +nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io-async"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. @@ -75,7 +75,8 @@ cortex-m = "0.7.6" critical-section = "1.1" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } chrono = { version = "0.4", default-features = false, optional = true } -embedded-io = { version = "0.4.0", features = ["async"], optional = true } +embedded-io = { version = "0.5.0" } +embedded-io-async = { version = "0.5.0", optional = true } embedded-storage = { version = "0.3" } embedded-storage-async = { version = "0.4.0", optional = true } rand_core = "0.6.4" diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 9d96db12..58dc0bf1 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -574,31 +574,31 @@ impl embedded_io::Error for Error { } } -impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> { +impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUart<'d, T> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Io for BufferedUartRx<'d, T> { +impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUartRx<'d, T> { type Error = Error; } -impl<'d, T: Instance> embedded_io::Io for BufferedUartTx<'d, T> { +impl<'d, T: Instance> embedded_io_async::ErrorType for BufferedUartTx<'d, T> { type Error = Error; } -impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io_async::Read for BufferedUart<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { BufferedUartRx::<'d, T>::read(buf).await } } -impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> { +impl<'d, T: Instance + 'd> embedded_io_async::Read for BufferedUartRx<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { Self::read(buf).await } } -impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io_async::BufRead for BufferedUart<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { BufferedUartRx::<'d, T>::fill_buf().await } @@ -608,7 +608,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> } } -impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { +impl<'d, T: Instance + 'd> embedded_io_async::BufRead for BufferedUartRx<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { Self::fill_buf().await } @@ -618,7 +618,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T } } -impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io_async::Write for BufferedUart<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { BufferedUartTx::<'d, T>::write(buf).await } @@ -628,7 +628,7 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> { } } -impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> { +impl<'d, T: Instance + 'd> embedded_io_async::Write for BufferedUartTx<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { Self::write(buf).await } @@ -638,19 +638,19 @@ impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> } } -impl<'d, T: Instance + 'd> embedded_io::blocking::Read for BufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::Read for BufferedUart<'d, T> { fn read(&mut self, buf: &mut [u8]) -> Result { self.rx.blocking_read(buf) } } -impl<'d, T: Instance + 'd> embedded_io::blocking::Read for BufferedUartRx<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::Read for BufferedUartRx<'d, T> { fn read(&mut self, buf: &mut [u8]) -> Result { self.blocking_read(buf) } } -impl<'d, T: Instance + 'd> embedded_io::blocking::Write for BufferedUart<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUart<'d, T> { fn write(&mut self, buf: &[u8]) -> Result { self.tx.blocking_write(buf) } @@ -660,7 +660,7 @@ impl<'d, T: Instance + 'd> embedded_io::blocking::Write for BufferedUart<'d, T> } } -impl<'d, T: Instance + 'd> embedded_io::blocking::Write for BufferedUartTx<'d, T> { +impl<'d, T: Instance + 'd> embedded_io::Write for BufferedUartTx<'d, T> { fn write(&mut self, buf: &[u8]) -> Result { self.blocking_write(buf) } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 723c5029..0d713f60 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -64,7 +64,8 @@ nb = "1.0.0" stm32-fmc = "0.2.4" seq-macro = "0.3.0" cfg-if = "1.0.0" -embedded-io = { version = "0.4.0", features = ["async"], optional = true } +embedded-io = { version = "0.5.0" } +embedded-io-async = { version = "0.5.0", optional = true } chrono = { version = "^0.4", default-features = false, optional = true} bit_field = "0.10.2" document-features = "0.2.7" @@ -84,7 +85,7 @@ default = ["rt"] 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", "embassy-time?/defmt"] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async?/defmt-03", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"] exti = [] @@ -92,7 +93,7 @@ exti = [] 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"] +nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io-async", "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. diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index ca117da8..329fc7da 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -400,37 +400,37 @@ impl<'d, T: BasicInstance> Drop for BufferedUartTx<'d, T> { } } -impl embedded_io::Error for Error { - fn kind(&self) -> embedded_io::ErrorKind { - embedded_io::ErrorKind::Other +impl embedded_io_async::Error for Error { + fn kind(&self) -> embedded_io_async::ErrorKind { + embedded_io_async::ErrorKind::Other } } -impl<'d, T: BasicInstance> embedded_io::Io for BufferedUart<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::ErrorType for BufferedUart<'d, T> { type Error = Error; } -impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartRx<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::ErrorType for BufferedUartRx<'d, T> { type Error = Error; } -impl<'d, T: BasicInstance> embedded_io::Io for BufferedUartTx<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::ErrorType for BufferedUartTx<'d, T> { type Error = Error; } -impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUart<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::Read for BufferedUart<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { self.rx.read(buf).await } } -impl<'d, T: BasicInstance> embedded_io::asynch::Read for BufferedUartRx<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::Read for BufferedUartRx<'d, T> { async fn read(&mut self, buf: &mut [u8]) -> Result { Self::read(self, buf).await } } -impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::BufRead for BufferedUart<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { self.rx.fill_buf().await } @@ -440,7 +440,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUart<'d, T> } } -impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::BufRead for BufferedUartRx<'d, T> { async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { Self::fill_buf(self).await } @@ -450,7 +450,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::BufRead for BufferedUartRx<'d, T } } -impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::Write for BufferedUart<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { self.tx.write(buf).await } @@ -460,7 +460,7 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUart<'d, T> { } } -impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'d, T> { +impl<'d, T: BasicInstance> embedded_io_async::Write for BufferedUartTx<'d, T> { async fn write(&mut self, buf: &[u8]) -> Result { Self::write(self, buf).await } @@ -470,19 +470,19 @@ impl<'d, T: BasicInstance> embedded_io::asynch::Write for BufferedUartTx<'d, T> } } -impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUart<'d, T> { +impl<'d, T: BasicInstance> embedded_io::Read for BufferedUart<'d, T> { fn read(&mut self, buf: &mut [u8]) -> Result { self.rx.blocking_read(buf) } } -impl<'d, T: BasicInstance> embedded_io::blocking::Read for BufferedUartRx<'d, T> { +impl<'d, T: BasicInstance> embedded_io::Read for BufferedUartRx<'d, T> { fn read(&mut self, buf: &mut [u8]) -> Result { self.blocking_read(buf) } } -impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> { +impl<'d, T: BasicInstance> embedded_io::Write for BufferedUart<'d, T> { fn write(&mut self, buf: &[u8]) -> Result { self.tx.blocking_write(buf) } @@ -492,7 +492,7 @@ impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUart<'d, T> } } -impl<'d, T: BasicInstance> embedded_io::blocking::Write for BufferedUartTx<'d, T> { +impl<'d, T: BasicInstance> embedded_io::Write for BufferedUartTx<'d, T> { fn write(&mut self, buf: &[u8]) -> Result { Self::blocking_write(self, buf) } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index d99034bc..0c69a488 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1022,12 +1022,11 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eio { - use embedded_io::asynch::Write; - use embedded_io::Io; + use embedded_io_async::{ErrorType, Write}; use super::*; - impl Io for Uart<'_, T, TxDma, RxDma> + impl ErrorType for Uart<'_, T, TxDma, RxDma> where T: BasicInstance, { @@ -1049,7 +1048,7 @@ mod eio { } } - impl Io for UartTx<'_, T, TxDma> + impl ErrorType for UartTx<'_, T, TxDma> where T: BasicInstance, { diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index b0e626df..b3f57062 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -220,13 +220,12 @@ fn clear_idle_flag(r: Regs) -> Sr { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eio { - use embedded_io::asynch::Read; - use embedded_io::Io; + use embedded_io_async::{ErrorType, Read}; use super::RingBufferedUartRx; use crate::usart::{BasicInstance, Error, RxDma}; - impl Io for RingBufferedUartRx<'_, T, Rx> + impl ErrorType for RingBufferedUartRx<'_, T, Rx> where T: BasicInstance, Rx: RxDma, diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 340724ea..94d6799e 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -23,7 +23,7 @@ target = "thumbv7em-none-eabi" features = ["nightly"] [features] -nightly = ["embedded-io/async"] +nightly = ["dep:embedded-io-async"] std = [] turbowakers = [] @@ -35,7 +35,7 @@ futures-util = { version = "0.3.17", default-features = false } critical-section = "1.1" heapless = "0.7.5" cfg-if = "1.0.0" -embedded-io = "0.4.0" +embedded-io-async = { version = "0.5.0", optional = true } [dev-dependencies] futures-executor = { version = "0.3.17", features = [ "thread-pool" ] } diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 13bf4ef0..6e3e77b4 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -381,17 +381,17 @@ mod io_impls { use super::*; - impl embedded_io::Io for Pipe { + impl embedded_io_async::ErrorType for Pipe { type Error = Infallible; } - impl embedded_io::asynch::Read for Pipe { + impl embedded_io_async::Read for Pipe { async fn read(&mut self, buf: &mut [u8]) -> Result { Ok(Pipe::read(self, buf).await) } } - impl embedded_io::asynch::Write for Pipe { + impl embedded_io_async::Write for Pipe { async fn write(&mut self, buf: &[u8]) -> Result { Ok(Pipe::write(self, buf).await) } @@ -401,17 +401,17 @@ mod io_impls { } } - impl embedded_io::Io for &Pipe { + impl embedded_io_async::ErrorType for &Pipe { type Error = Infallible; } - impl embedded_io::asynch::Read for &Pipe { + impl embedded_io_async::Read for &Pipe { async fn read(&mut self, buf: &mut [u8]) -> Result { Ok(Pipe::read(self, buf).await) } } - impl embedded_io::asynch::Write for &Pipe { + impl embedded_io_async::Write for &Pipe { async fn write(&mut self, buf: &[u8]) -> Result { Ok(Pipe::write(self, buf).await) } @@ -421,21 +421,21 @@ mod io_impls { } } - impl embedded_io::Io for Reader<'_, M, N> { + impl embedded_io_async::ErrorType for Reader<'_, M, N> { type Error = Infallible; } - impl embedded_io::asynch::Read for Reader<'_, M, N> { + impl embedded_io_async::Read for Reader<'_, M, N> { async fn read(&mut self, buf: &mut [u8]) -> Result { Ok(Reader::read(self, buf).await) } } - impl embedded_io::Io for Writer<'_, M, N> { + impl embedded_io_async::ErrorType for Writer<'_, M, N> { type Error = Infallible; } - impl embedded_io::asynch::Write for Writer<'_, M, N> { + impl embedded_io_async::Write for Writer<'_, M, N> { async fn write(&mut self, buf: &[u8]) -> Result { Ok(Writer::write(self, buf).await) } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 780aaeac..15fe22d3 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -17,7 +17,7 @@ nightly = [ "embassy-time/unstable-traits", "static_cell/nightly", "embassy-usb", - "embedded-io/async", + "embedded-io-async", "embassy-net", "embassy-lora", "lora-phy", @@ -33,7 +33,8 @@ embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["de embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } -embedded-io = "0.4.0" +embedded-io = { version = "0.5.0", features = ["defmt-03"] } +embedded-io-async = { version = "0.5.0", optional = true, features = ["defmt-03"] } 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/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs index 23869537..d9c50578 100644 --- a/examples/nrf52840/src/bin/buffered_uart.rs +++ b/examples/nrf52840/src/bin/buffered_uart.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_nrf::buffered_uarte::{self, BufferedUarte}; use embassy_nrf::{bind_interrupts, peripherals, uarte}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index f527c0d7..b7806f41 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -15,7 +15,7 @@ use embassy_nrf::{bind_interrupts, pac, peripherals, rng, usb}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index 112e41bc..e3b80d82 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -12,7 +12,7 @@ use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::Delay; use embedded_hal_async::spi::ExclusiveDevice; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index b0e51dcf..57660f12 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -37,7 +37,7 @@ embassy-net = { version = "0.1.0", path = "../../embassy-net", features = [ embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = [ "defmt", ] } -embedded-io = { version = "0.4.0", features = [ "async" ]} +embedded-io-async = { version = "0.5.0" } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 8c61dc5e..d83e370d 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -44,7 +44,7 @@ usbd-hid = "0.6.1" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } embedded-hal-async = "0.2.0-alpha.2" -embedded-io = { version = "0.4.0", features = ["async", "defmt"] } +embedded-io-async = { version = "0.5.0", features = ["defmt-03"] } embedded-storage = { version = "0.3" } static_cell = { version = "1.1", features = ["nightly"]} log = "0.4" @@ -53,4 +53,4 @@ pio = "0.2.1" rand = { version = "0.8.5", default-features = false } [profile.release] -debug = 2 +debug = 2 \ No newline at end of file diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index e81da177..9f800d0d 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -17,7 +17,7 @@ use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; use embassy_time::{Delay, Duration}; use embedded_hal_async::spi::ExclusiveDevice; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index 9dd7ae97..fee84b61 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -19,7 +19,7 @@ use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; use embassy_time::{Delay, Duration, Timer}; use embedded_hal_async::spi::ExclusiveDevice; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index db21c2b6..02457426 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -18,7 +18,7 @@ use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; use embassy_time::{Delay, Duration}; use embedded_hal_async::spi::ExclusiveDevice; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index 4c382c2e..707c99b7 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -23,7 +23,7 @@ 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}; -use embedded_io::asynch::{Read, Write}; +use embedded_io_async::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; use crate::uart::PioUart; @@ -240,8 +240,7 @@ 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 embedded_io::asynch::Write; - use embedded_io::Io; + use embedded_io_async::{ErrorType, Write}; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; @@ -293,7 +292,7 @@ mod uart_tx { } } - impl Io for PioUartTx<'_> { + impl ErrorType for PioUartTx<'_> { type Error = Infallible; } @@ -313,8 +312,7 @@ 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 embedded_io::asynch::Read; - use embedded_io::Io; + use embedded_io_async::{ErrorType, Read}; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; @@ -377,7 +375,7 @@ mod uart_rx { } } - impl Io for PioUartRx<'_> { + impl ErrorType for PioUartRx<'_> { type Error = Infallible; } diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs index 73520171..d3e67c8e 100644 --- a/examples/rp/src/bin/uart_buffered_split.rs +++ b/examples/rp/src/bin/uart_buffered_split.rs @@ -14,7 +14,7 @@ use embassy_rp::bind_interrupts; use embassy_rp::peripherals::UART0; use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::{Read, Write}; +use embedded_io_async::{Read, Write}; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 0a08f667..6c2f27ac 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -16,7 +16,7 @@ use embassy_rp::{bind_interrupts, peripherals}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, Config, UsbDevice}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index e3e39344..cd61ad78 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -19,7 +19,7 @@ use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; use embassy_rp::pio::{InterruptHandler, Pio}; use embassy_time::Duration; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index 0223a363..55fcb4a6 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -19,7 +19,7 @@ use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; use embassy_rp::pio::{InterruptHandler, Pio}; use embassy_time::Duration; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 54417682..3e26d2e2 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -8,9 +8,10 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["log", "std", "nightly"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } -embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } +embedded-io-async = { version = "0.5.0" } +embedded-io-adapters = { version = "0.5.0", features = ["futures-03"] } critical-section = { version = "1.1", features = ["std"] } async-io = "1.6.0" diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index e0de1416..8d834505 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -8,7 +8,7 @@ use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; use embassy_net_tuntap::TunTapDevice; use embassy_time::Duration; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; diff --git a/examples/std/src/bin/serial.rs b/examples/std/src/bin/serial.rs index 85ee54f7..0b289c74 100644 --- a/examples/std/src/bin/serial.rs +++ b/examples/std/src/bin/serial.rs @@ -5,7 +5,7 @@ mod serial_port; use async_io::Async; use embassy_executor::Executor; -use embedded_io::asynch::Read; +use embedded_io_async::Read; use log::*; use nix::sys::termios; use static_cell::StaticCell; @@ -29,7 +29,7 @@ async fn run() { // // This is not really needed, you could write the code below using futures::io directly. // It's useful if you want to have portable code across embedded and std. - let mut port = embedded_io::adapters::FromFutures::new(port); + let mut port = embedded_io_adapters::futures_03::FromFutures::new(port); info!("Serial opened!"); diff --git a/examples/std/src/bin/tcp_accept.rs b/examples/std/src/bin/tcp_accept.rs index 0c920a3f..199e4c9e 100644 --- a/examples/std/src/bin/tcp_accept.rs +++ b/examples/std/src/bin/tcp_accept.rs @@ -9,7 +9,7 @@ use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; use embassy_net_tuntap::TunTapDevice; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::Write as _; +use embedded_io_async::Write as _; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index fdd3b7d4..65a4e51f 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -19,7 +19,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-io = "0.4.0" +embedded-io = { version = "0.5.0" } +embedded-io-async = { version = "0.5.0" } 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 } diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index 49601668..393e60b7 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs @@ -13,7 +13,7 @@ use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/src/bin/usart_buffered.rs b/examples/stm32f4/src/bin/usart_buffered.rs index c573dc3a..c0a64d94 100644 --- a/examples/stm32f4/src/bin/usart_buffered.rs +++ b/examples/stm32f4/src/bin/usart_buffered.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::usart::{BufferedUart, Config}; use embassy_stm32::{bind_interrupts, peripherals, usart}; -use embedded_io::asynch::BufRead; +use embedded_io_async::BufRead; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 740d3018..763e3a9e 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -13,7 +13,7 @@ use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, UsbDevice}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index a379cbbe..41170f2c 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -11,7 +11,7 @@ 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", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } -embedded-io = { version = "0.4.0", features = ["async"] } +embedded-io-async = { version = "0.5.0" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index e5abf52b..f0e280c3 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -13,7 +13,7 @@ use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 51d3bad1..44d0a957 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -10,8 +10,8 @@ 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", "unstable-traits", "tick-hz-32_768"] } -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-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } +embedded-io-async = { version = "0.5.0" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" @@ -22,7 +22,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" } -embedded-nal-async = "0.4.0" +embedded-nal-async = { version = "0.5.0" } 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 } diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 2aa2ab62..763520ab 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -14,7 +14,7 @@ use embassy_stm32::rng::Rng; use embassy_stm32::time::Hertz; use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 3c1232e6..e2e5f936 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -10,8 +10,8 @@ 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", "unstable-traits", "tick-hz-32_768"] } -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-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } +embedded-io-async = { version = "0.5.0" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" @@ -22,7 +22,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" } -embedded-nal-async = "0.4.0" +embedded-nal-async = { version = "0.5.0" } 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 } diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index c93be9f0..26a386e4 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -13,7 +13,7 @@ use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 78005e91..6664410c 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -13,7 +13,7 @@ use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; use rand_core::RngCore; use static_cell::make_static; diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index c325751c..33aa05e6 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [features] default = ["nightly"] nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstable-traits", "embassy-executor/nightly", - "embassy-lora", "lora-phy", "lorawan-device", "lorawan", "embedded-io/async"] + "embassy-lora", "lora-phy", "lorawan-device", "lorawan", "dep:embedded-io-async"] [dependencies] # Change stm32l072cz to your chip name, if necessary. @@ -24,7 +24,8 @@ defmt = "0.3" defmt-rtt = "0.4" embedded-storage = "0.3.0" -embedded-io = "0.4.0" +embedded-io = { version = "0.5.0" } +embedded-io-async = { version = "0.5.0", optional = true } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" diff --git a/examples/stm32l0/src/bin/usart_irq.rs b/examples/stm32l0/src/bin/usart_irq.rs index f2c72a10..f5dabcc4 100644 --- a/examples/stm32l0/src/bin/usart_irq.rs +++ b/examples/stm32l0/src/bin/usart_irq.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::usart::{BufferedUart, Config}; use embassy_stm32::{bind_interrupts, peripherals, usart}; -use embedded_io::asynch::{Read, Write}; +use embedded_io_async::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 1afd0039..9dfc08f2 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -25,7 +25,7 @@ embedded-hal = "0.2.6" futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 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"] } +embedded-io-async = { version = "0.5.0" } static_cell = { version = "1.1", features = ["nightly"]} [profile.release] diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 5e75b21c..15b84761 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -13,7 +13,7 @@ use embassy_stm32::{bind_interrupts, peripherals, rng, usb, Config}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, UsbDevice}; -use embedded_io::asynch::Write; +use embedded_io_async::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 7ce51aa5..974a9413 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -12,7 +12,7 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } -embedded-io = { version = "0.4.0", features = ["async"] } +embedded-io-async = { version = "0.5.0" } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } embedded-hal-async = { version = "0.2.0-alpha.2" } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index f2c90278..90a3bd0c 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -26,7 +26,7 @@ embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } embedded-hal-async = { version = "=0.2.0-alpha.2" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } -embedded-io = { version = "0.4.0", features = ["async"] } +embedded-io-async = { version = "0.5.0" } embedded-storage = { version = "0.3" } static_cell = { version = "1.1", features = ["nightly"]} pio = "0.2" diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index e74e9986..edabf55b 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -11,7 +11,7 @@ use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::UART0; use embassy_rp::uart::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, Config, Error, Instance, Parity}; use embassy_time::{Duration, Timer}; -use embedded_io::asynch::{Read, ReadExactError, Write}; +use embedded_io_async::{Read, ReadExactError, Write}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/tests/rp/src/bin/uart_upgrade.rs b/tests/rp/src/bin/uart_upgrade.rs index 760e5395..effd0bc4 100644 --- a/tests/rp/src/bin/uart_upgrade.rs +++ b/tests/rp/src/bin/uart_upgrade.rs @@ -9,7 +9,7 @@ use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::UART0; use embassy_rp::uart::{BufferedInterruptHandler, Config, Uart}; -use embedded_io::asynch::{Read, Write}; +use embedded_io_async::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { From 2ab9a07b64d4badb3671ed5065c520ff9bb83038 Mon Sep 17 00:00:00 2001 From: Sam Lakerveld Date: Tue, 8 Aug 2023 11:22:01 +0200 Subject: [PATCH 199/298] embassy-sync: manual Copy impls for channel and pipe --- embassy-sync/src/channel.rs | 12 ++++++++---- embassy-sync/src/pipe.rs | 6 ++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index f421af39..d6f36f53 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -30,7 +30,6 @@ use crate::blocking_mutex::Mutex; use crate::waitqueue::WakerRegistration; /// Send-only access to a [`Channel`]. -#[derive(Copy)] pub struct Sender<'ch, M, T, const N: usize> where M: RawMutex, @@ -47,6 +46,8 @@ where } } +impl<'ch, M, T, const N: usize> Copy for Sender<'ch, M, T, N> where M: RawMutex {} + impl<'ch, M, T, const N: usize> Sender<'ch, M, T, N> where M: RawMutex, @@ -67,7 +68,6 @@ where } /// Send-only access to a [`Channel`] without knowing channel size. -#[derive(Copy)] pub struct DynamicSender<'ch, T> { channel: &'ch dyn DynamicChannel, } @@ -78,6 +78,8 @@ impl<'ch, T> Clone for DynamicSender<'ch, T> { } } +impl<'ch, T> Copy for DynamicSender<'ch, T> {} + impl<'ch, M, T, const N: usize> From> for DynamicSender<'ch, T> where M: RawMutex, @@ -107,7 +109,6 @@ impl<'ch, T> DynamicSender<'ch, T> { } /// Receive-only access to a [`Channel`]. -#[derive(Copy)] pub struct Receiver<'ch, M, T, const N: usize> where M: RawMutex, @@ -124,6 +125,8 @@ where } } +impl<'ch, M, T, const N: usize> Copy for Receiver<'ch, M, T, N> where M: RawMutex {} + impl<'ch, M, T, const N: usize> Receiver<'ch, M, T, N> where M: RawMutex, @@ -144,7 +147,6 @@ where } /// Receive-only access to a [`Channel`] without knowing channel size. -#[derive(Copy)] pub struct DynamicReceiver<'ch, T> { channel: &'ch dyn DynamicChannel, } @@ -155,6 +157,8 @@ impl<'ch, T> Clone for DynamicReceiver<'ch, T> { } } +impl<'ch, T> Copy for DynamicReceiver<'ch, T> {} + impl<'ch, T> DynamicReceiver<'ch, T> { /// Receive the next value. /// diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 6e3e77b4..21d451ea 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -11,7 +11,6 @@ use crate::ring_buffer::RingBuffer; use crate::waitqueue::WakerRegistration; /// Write-only access to a [`Pipe`]. -#[derive(Copy)] pub struct Writer<'p, M, const N: usize> where M: RawMutex, @@ -28,6 +27,8 @@ where } } +impl<'p, M, const N: usize> Copy for Writer<'p, M, N> where M: RawMutex {} + impl<'p, M, const N: usize> Writer<'p, M, N> where M: RawMutex, @@ -74,7 +75,6 @@ where impl<'p, M, const N: usize> Unpin for WriteFuture<'p, M, N> where M: RawMutex {} /// Read-only access to a [`Pipe`]. -#[derive(Copy)] pub struct Reader<'p, M, const N: usize> where M: RawMutex, @@ -91,6 +91,8 @@ where } } +impl<'p, M, const N: usize> Copy for Reader<'p, M, N> where M: RawMutex {} + impl<'p, M, const N: usize> Reader<'p, M, N> where M: RawMutex, From e31545860e2c44e67f45dd5132e85f18b6fab5d7 Mon Sep 17 00:00:00 2001 From: Don Reilly Date: Tue, 8 Aug 2023 16:46:30 -0500 Subject: [PATCH 200/298] don't generate adc peripheral for f3 series --- embassy-stm32/build.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 30e25aef..743631b8 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -310,7 +310,10 @@ fn main() { for p in METADATA.peripherals { // generating RccPeripheral impl for H7 ADC3 would result in bad frequency - if !singletons.contains(&p.name.to_string()) || (p.name == "ADC3" && METADATA.line.starts_with("STM32H7")) { + if !singletons.contains(&p.name.to_string()) + || (p.name == "ADC3" && METADATA.line.starts_with("STM32H7")) + || (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "f3")) + { continue; } From c40b944da6646ba9d7c6e99d7ca48bc3f87a652e Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 9 Aug 2023 00:10:48 +0200 Subject: [PATCH 201/298] Mark Ticker stream as FusedStream --- embassy-time/src/timer.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index d3d1f9f5..ad5026e6 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -3,6 +3,7 @@ use core::pin::Pin; use core::task::{Context, Poll, Waker}; use futures_util::future::{select, Either}; +use futures_util::stream::FusedStream; use futures_util::{pin_mut, Stream}; use crate::{Duration, Instant}; @@ -163,6 +164,13 @@ impl Stream for Ticker { } } +impl FusedStream for Ticker { + fn is_terminated(&self) -> bool { + // `Ticker` keeps yielding values until dropped, it never terminates. + false + } +} + extern "Rust" { fn _embassy_time_schedule_wake(at: Instant, waker: &Waker); } From 6fc5c608f8a99e0275916b7c86116ac6eb14c97a Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 8 Aug 2023 19:47:01 -0500 Subject: [PATCH 202/298] stm32/rtc: remove generics and segregate clock sel --- embassy-stm32/src/rcc/wb.rs | 4 +-- embassy-stm32/src/rtc/mod.rs | 52 ++++++++---------------------- embassy-stm32/src/rtc/v2.rs | 57 +++++++++++++++++++++------------ embassy-stm32/src/rtc/v3.rs | 56 +++++++++++++++++++++----------- examples/stm32wl/src/bin/rtc.rs | 5 +-- 5 files changed, 90 insertions(+), 84 deletions(-) diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index e9b7296d..ae708b37 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -1,6 +1,6 @@ pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::rcc::Clocks; -use crate::rtc::{enable as enable_rtc, RtcClockSource}; +use crate::rtc::{Rtc, RtcClockSource}; use crate::time::{khz, mhz, Hertz}; /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, @@ -375,5 +375,5 @@ pub(crate) fn configure_clocks(config: &Config) { w.set_shdhpre(config.ahb3_pre.into()); }); - config.rtc.map(|clock_source| enable_rtc(clock_source)); + config.rtc.map(|clock_source| Rtc::set_clock_source(clock_source)); } diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 2d54c926..945bfafd 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -1,5 +1,4 @@ //! RTC peripheral abstraction -use core::marker::PhantomData; mod datetime; pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; @@ -17,6 +16,9 @@ mod _version; pub use _version::*; use embassy_hal_internal::Peripheral; +use crate::peripherals::RTC; +use crate::rtc::sealed::Instance; + /// Errors that can occur on methods on [RtcClock] #[derive(Clone, Debug, PartialEq, Eq)] pub enum RtcError { @@ -28,22 +30,10 @@ pub enum RtcError { } /// RTC Abstraction -pub struct Rtc<'d, T: Instance> { - phantom: PhantomData<&'d mut T>, +pub struct Rtc { rtc_config: RtcConfig, } -#[allow(dead_code)] -pub(crate) fn enable(clock_source: RtcClockSource) { - Rtc::::enable(clock_source); -} - -#[cfg(feature = "time")] -#[allow(dead_code)] -pub(crate) fn set_wakeup_timer(_duration: embassy_time::Duration) { - todo!() -} - #[derive(Copy, Clone, Debug, PartialEq)] #[repr(u8)] pub enum RtcClockSource { @@ -59,8 +49,6 @@ pub enum RtcClockSource { #[derive(Copy, Clone, PartialEq)] pub struct RtcConfig { - /// RTC clock source - clock_source: RtcClockSource, /// Asynchronous prescaler factor /// This is the asynchronous division factor: /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1) @@ -78,7 +66,6 @@ impl Default for RtcConfig { /// Raw sub-seconds in 1/256. fn default() -> Self { RtcConfig { - clock_source: RtcClockSource::LSI, async_prescaler: 127, sync_prescaler: 255, } @@ -86,12 +73,6 @@ impl Default for RtcConfig { } impl RtcConfig { - /// Sets the clock source of RTC config - pub fn clock_source(mut self, clock_source: RtcClockSource) -> Self { - self.clock_source = clock_source; - self - } - /// Set the asynchronous prescaler of RTC config pub fn async_prescaler(mut self, prescaler: u8) -> Self { self.async_prescaler = prescaler; @@ -122,16 +103,13 @@ impl Default for RtcCalibrationCyclePeriod { } } -impl<'d, T: Instance> Rtc<'d, T> { - pub fn new(_rtc: impl Peripheral

+ 'd, rtc_config: RtcConfig) -> Self { - T::enable_peripheral_clk(); +impl Rtc { + pub fn new(_rtc: impl Peripheral

, rtc_config: RtcConfig) -> Self { + RTC::enable_peripheral_clk(); - let mut rtc_struct = Self { - phantom: PhantomData, - rtc_config, - }; + let mut rtc_struct = Self { rtc_config }; - Self::enable(rtc_config.clock_source); + Self::enable(); rtc_struct.configure(rtc_config); rtc_struct.rtc_config = rtc_config; @@ -157,7 +135,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`]. pub fn now(&self) -> Result { - let r = T::regs(); + let r = RTC::regs(); let tr = r.tr().read(); let second = bcd2_to_byte((tr.st(), tr.su())); let minute = bcd2_to_byte((tr.mnt(), tr.mnu())); @@ -176,7 +154,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// Check if daylight savings time is active. pub fn get_daylight_savings(&self) -> bool { - let cr = T::regs().cr().read(); + let cr = RTC::regs().cr().read(); cr.bkp() } @@ -191,14 +169,14 @@ impl<'d, T: Instance> Rtc<'d, T> { self.rtc_config } - pub const BACKUP_REGISTER_COUNT: usize = T::BACKUP_REGISTER_COUNT; + pub const BACKUP_REGISTER_COUNT: usize = RTC::BACKUP_REGISTER_COUNT; /// Read content of the backup register. /// /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. pub fn read_backup_register(&self, register: usize) -> Option { - T::read_backup_register(&T::regs(), register) + RTC::read_backup_register(&RTC::regs(), register) } /// Set content of the backup register. @@ -206,7 +184,7 @@ impl<'d, T: Instance> Rtc<'d, T> { /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. pub fn write_backup_register(&self, register: usize, value: u32) { - T::write_backup_register(&T::regs(), register, value) + RTC::write_backup_register(&RTC::regs(), register, value) } } @@ -257,5 +235,3 @@ pub(crate) mod sealed { // fn apply_config(&mut self, rtc_config: RtcConfig); } } - -pub trait Instance: sealed::Instance + 'static {} diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 743a04d6..5b896069 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -1,13 +1,12 @@ use stm32_metapac::rtc::vals::{Init, Osel, Pol}; -use super::{sealed, Instance, RtcClockSource, RtcConfig}; +use super::{sealed, RtcClockSource, RtcConfig}; use crate::pac::rtc::Rtc; +use crate::peripherals::RTC; +use crate::rtc::sealed::Instance; -impl<'d, T: Instance> super::Rtc<'d, T> { - pub(super) fn enable(clock_source: RtcClockSource) { - #[cfg(not(rtc_v2wb))] - use stm32_metapac::rcc::vals::Rtcsel; - +impl super::Rtc { + fn unlock_registers() { #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] let cr = crate::pac::PWR.cr(); #[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))] @@ -16,10 +15,35 @@ impl<'d, T: Instance> super::Rtc<'d, T> { // TODO: Missing from PAC for l0 and f0? #[cfg(not(any(rtc_v2f0, rtc_v2l0)))] { - cr.modify(|w| w.set_dbp(true)); - while !cr.read().dbp() {} + if !cr.read().dbp() { + cr.modify(|w| w.set_dbp(true)); + while !cr.read().dbp() {} + } } + } + #[allow(dead_code)] + pub(crate) fn set_clock_source(clock_source: RtcClockSource) { + #[cfg(not(rtc_v2wb))] + use stm32_metapac::rcc::vals::Rtcsel; + + #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + let cr = crate::pac::RCC.bdcr(); + #[cfg(any(rtc_v2l0, rtc_v2l1))] + let cr = crate::pac::RCC.csr(); + + Self::unlock_registers(); + + cr.modify(|w| { + // Select RTC source + #[cfg(not(rtc_v2wb))] + w.set_rtcsel(Rtcsel::from_bits(clock_source as u8)); + #[cfg(rtc_v2wb)] + w.set_rtcsel(clock_source as u8); + }); + } + + pub(super) fn enable() { #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] let reg = crate::pac::RCC.bdcr().read(); #[cfg(any(rtc_v2l0, rtc_v2l1))] @@ -28,12 +52,9 @@ impl<'d, T: Instance> super::Rtc<'d, T> { #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb))] assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - #[cfg(rtc_v2wb)] - let rtcsel = reg.rtcsel(); - #[cfg(not(rtc_v2wb))] - let rtcsel = reg.rtcsel().to_bits(); + if !reg.rtcen() { + Self::unlock_registers(); - if !reg.rtcen() || rtcsel != clock_source as u8 { #[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))] crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] @@ -46,12 +67,8 @@ impl<'d, T: Instance> super::Rtc<'d, T> { #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] w.set_bdrst(false); - // Select RTC source - #[cfg(not(rtc_v2wb))] - w.set_rtcsel(Rtcsel::from_bits(clock_source as u8)); - #[cfg(rtc_v2wb)] - w.set_rtcsel(clock_source as u8); w.set_rtcen(true); + w.set_rtcsel(reg.rtcsel()); // Restore bcdr #[cfg(any(rtc_v2l4, rtc_v2wb))] @@ -157,7 +174,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { where F: FnOnce(&crate::pac::rtc::Rtc) -> R, { - let r = T::regs(); + let r = RTC::regs(); // Disable write protection. // This is safe, as we're only writin the correct and expected values. r.wpr().write(|w| w.set_key(0xca)); @@ -218,5 +235,3 @@ impl sealed::Instance for crate::peripherals::RTC { } } } - -impl Instance for crate::peripherals::RTC {} diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 9f04c9b6..3297303e 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -1,42 +1,62 @@ use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType}; -use super::{sealed, Instance, RtcCalibrationCyclePeriod, RtcClockSource, RtcConfig}; +use super::{sealed, RtcCalibrationCyclePeriod, RtcClockSource, RtcConfig}; use crate::pac::rtc::Rtc; +use crate::peripherals::RTC; +use crate::rtc::sealed::Instance; -impl<'d, T: Instance> super::Rtc<'d, T> { - pub(super) fn enable(clock_source: RtcClockSource) { +impl super::Rtc { + fn unlock_registers() { // Unlock the backup domain #[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))] { - crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); - while !crate::pac::PWR.cr1().read().dbp() {} + if !crate::pac::PWR.cr1().read().dbp() { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(true)); + while !crate::pac::PWR.cr1().read().dbp() {} + } } #[cfg(any(rcc_wl5, rcc_wle))] { use crate::pac::pwr::vals::Dbp; - crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); - while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {} + if crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED { + crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); + while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {} + } } + } - let reg = crate::pac::RCC.bdcr().read(); - assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); - + #[allow(dead_code)] + pub(crate) fn set_clock_source(clock_source: RtcClockSource) { let clock_source = clock_source as u8; #[cfg(not(any(rcc_wl5, rcc_wle)))] let clock_source = crate::pac::rcc::vals::Rtcsel::from_bits(clock_source); - if !reg.rtcen() || reg.rtcsel() != clock_source { - crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); + Self::unlock_registers(); - crate::pac::RCC.bdcr().modify(|w| { + crate::pac::RCC.bdcr().modify(|w| { + // Select RTC source + w.set_rtcsel(clock_source); + }); + } + + pub(super) fn enable() { + let bdcr = crate::pac::RCC.bdcr(); + + let reg = bdcr.read(); + assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet."); + + if !reg.rtcen() { + Self::unlock_registers(); + + bdcr.modify(|w| w.set_bdrst(true)); + + bdcr.modify(|w| { // Reset w.set_bdrst(false); - // Select RTC source - w.set_rtcsel(clock_source); - w.set_rtcen(true); + w.set_rtcsel(reg.rtcsel()); // Restore bcdr w.set_lscosel(reg.lscosel()); @@ -141,7 +161,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> { where F: FnOnce(&crate::pac::rtc::Rtc) -> R, { - let r = T::regs(); + let r = RTC::regs(); // Disable write protection. // This is safe, as we're only writin the correct and expected values. r.wpr().write(|w| w.set_key(Key::DEACTIVATE1)); @@ -188,5 +208,3 @@ impl sealed::Instance for crate::peripherals::RTC { } } } - -impl Instance for crate::peripherals::RTC {} diff --git a/examples/stm32wl/src/bin/rtc.rs b/examples/stm32wl/src/bin/rtc.rs index 0d7906bd..fb1bc6e3 100644 --- a/examples/stm32wl/src/bin/rtc.rs +++ b/examples/stm32wl/src/bin/rtc.rs @@ -27,10 +27,7 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - let mut rtc = Rtc::new( - p.RTC, - RtcConfig::default().clock_source(embassy_stm32::rtc::RtcClockSource::LSE), - ); + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); info!("Got RTC! {:?}", now.timestamp()); rtc.set_datetime(now.into()).expect("datetime not set"); From 6a73ab1afa76944c413cb91932881c35ddfbcbcd Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 8 Aug 2023 19:58:03 -0500 Subject: [PATCH 203/298] stm32/l4: set rtc clock source in rcc --- embassy-stm32/src/rcc/l4.rs | 5 +++++ examples/stm32l4/src/bin/rtc.rs | 5 +---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index 36c9eb2f..b2828e58 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -10,6 +10,7 @@ use crate::gpio::Speed; use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; +use crate::rtc::{Rtc, RtcClockSource as RCS}; use crate::time::Hertz; use crate::{peripherals, Peripheral}; @@ -426,6 +427,8 @@ pub(crate) unsafe fn init(config: Config) { // Wait until LSE is running while !RCC.bdcr().read().lserdy() {} + + Rtc::set_clock_source(RCS::LSE); } RtcClockSource::LSI32 => { // Turn on the internal 32 kHz LSI oscillator @@ -433,6 +436,8 @@ pub(crate) unsafe fn init(config: Config) { // Wait until LSI is running while !RCC.csr().read().lsirdy() {} + + Rtc::set_clock_source(RCS::LSI); } } diff --git a/examples/stm32l4/src/bin/rtc.rs b/examples/stm32l4/src/bin/rtc.rs index fe2aabb4..294ea456 100644 --- a/examples/stm32l4/src/bin/rtc.rs +++ b/examples/stm32l4/src/bin/rtc.rs @@ -33,10 +33,7 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - let mut rtc = Rtc::new( - p.RTC, - RtcConfig::default().clock_source(embassy_stm32::rtc::RtcClockSource::LSE), - ); + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); info!("Got RTC! {:?}", now.timestamp()); rtc.set_datetime(now.into()).expect("datetime not set"); From 32fdd4c787372d44bf134cb5e547eb635c680256 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 8 Aug 2023 20:33:24 -0500 Subject: [PATCH 204/298] tests/stm32: fix rtc test --- embassy-stm32/src/rcc/f4.rs | 14 ++++++++++++++ tests/stm32/src/bin/rtc.rs | 16 ++++++---------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index 7aa9f0fd..2ae0d15c 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -9,6 +9,7 @@ use crate::gpio::Speed; use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; +use crate::rtc::{Rtc, RtcClockSource}; use crate::time::Hertz; use crate::{peripherals, Peripheral}; @@ -33,6 +34,7 @@ pub struct Config { pub plli2s: Option, pub pll48: bool, + pub rtc: Option, } #[cfg(stm32f410)] @@ -459,6 +461,18 @@ pub(crate) unsafe fn init(config: Config) { }) }); + match config.rtc { + Some(RtcClockSource::LSI) => { + RCC.csr().modify(|w| w.set_lsion(true)); + while !RCC.csr().read().lsirdy() {} + } + _ => {} + } + + config.rtc.map(|clock_source| { + Rtc::set_clock_source(clock_source); + }); + set_freqs(Clocks { sys: Hertz(sysclk), apb1: Hertz(pclk1), diff --git a/tests/stm32/src/bin/rtc.rs b/tests/stm32/src/bin/rtc.rs index 194b153d..7df415b4 100644 --- a/tests/stm32/src/bin/rtc.rs +++ b/tests/stm32/src/bin/rtc.rs @@ -10,13 +10,16 @@ use chrono::{NaiveDate, NaiveDateTime}; use common::*; use defmt::assert; use embassy_executor::Spawner; -use embassy_stm32::pac; -use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::rtc::{Rtc, RtcClockSource, RtcConfig}; use embassy_time::{Duration, Timer}; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = embassy_stm32::init(config()); + let mut config = config(); + + config.rcc.rtc = Some(RtcClockSource::LSI); + + let p = embassy_stm32::init(config); info!("Hello World!"); let now = NaiveDate::from_ymd_opt(2020, 5, 15) @@ -24,13 +27,6 @@ async fn main(_spawner: Spawner) { .and_hms_opt(10, 30, 15) .unwrap(); - info!("Starting LSI"); - - pac::RCC.csr().modify(|w| w.set_lsion(true)); - while !pac::RCC.csr().read().lsirdy() {} - - info!("Started LSI"); - let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); rtc.set_datetime(now.into()).expect("datetime not set"); From 820852be28bb68910048f016459847c6a03b767c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 9 Aug 2023 18:33:20 +0200 Subject: [PATCH 205/298] fmt mod must not be public. --- embassy-stm32-wpan/src/lib.rs | 2 +- embassy-stm32/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 5ecce2cc..2a8ab725 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -3,7 +3,7 @@ #![cfg_attr(feature = "mac", feature(type_alias_impl_trait, concat_bytes))] // This must go FIRST so that all the other modules see its macros. -pub mod fmt; +mod fmt; use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, Ordering}; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index fb0279dc..adb3054d 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -5,7 +5,7 @@ #![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; +mod fmt; include!(concat!(env!("OUT_DIR"), "/_macros.rs")); // Utilities From 0705152105287a4c03ffdc21a460d1a26e67c9f6 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 9 Aug 2023 20:15:14 -0500 Subject: [PATCH 206/298] stm32/rtc: add start/stop wakeup --- embassy-stm32/src/rtc/v2.rs | 67 +++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 5b896069..f15ab806 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -22,6 +22,73 @@ impl super::Rtc { } } + #[allow(dead_code)] + #[cfg(all(feature = "time", stm32wb))] + // start the wakeup alarm with the given duration + pub(crate) fn start_wakeup_alarm(duration: embassy_time::Duration) { + use embassy_time::TICK_HZ; + use stm32_metapac::rtc::vals::Wucksel; + + use crate::interrupt::typelevel::Interrupt; + use crate::rcc::get_freqs; + + let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; + + // Choose the lowest prescaler available + #[cfg(stm32wb)] + let rtc_hz = rtc_hz / 2; + + let rtc_ticks = duration.as_ticks() * rtc_hz / TICK_HZ; + let rtc_ticks = if rtc_ticks > u16::MAX as u64 { + u16::MAX + } else { + rtc_ticks as u16 + }; + + while !RTC::regs().isr().read().wutf() {} + + RTC::regs().isr().modify(|w| w.set_wutf(false)); + + RTC::regs().wutr().modify(|w| w.set_wut(rtc_ticks)); + + crate::interrupt::typelevel::RTC_WKUP::unpend(); + unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; + + RTC::regs().cr().modify(|w| { + // Choose the lowest prescaler available + #[cfg(stm32wb)] + w.set_wucksel(Wucksel::DIV2); + + w.set_wutie(true); + w.set_wute(true); + }); + } + + #[allow(dead_code)] + #[cfg(all(feature = "time", stm32wb))] + // stop the wakeup alarm and return the time remaining + pub(crate) fn stop_wakeup_alarm() -> embassy_time::Duration { + use embassy_time::{Duration, TICK_HZ}; + + use crate::interrupt::typelevel::Interrupt; + use crate::rcc::get_freqs; + + crate::interrupt::typelevel::RTC_WKUP::disable(); + + RTC::regs().cr().modify(|w| { + w.set_wute(false); + }); + + let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; + + // Choose the lowest prescaler available + #[cfg(stm32wb)] + let rtc_hz = rtc_hz / 2; + let rtc_ticks = RTC::regs().wutr().read().wut(); + + Duration::from_ticks(rtc_ticks as u64 * TICK_HZ / rtc_hz) + } + #[allow(dead_code)] pub(crate) fn set_clock_source(clock_source: RtcClockSource) { #[cfg(not(rtc_v2wb))] From 8a9622ec2ecb9960f17ea0bd5b662783ba522cbd Mon Sep 17 00:00:00 2001 From: Don Reilly Date: Thu, 10 Aug 2023 08:03:35 -0500 Subject: [PATCH 207/298] update metapac tag --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 0d713f60..8f765e9d 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 = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-82a24863823a3daf0ca664c7fdf008379d0a0d42" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7eddb78e705905af4c1dd2359900db3e78a3c500" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-82a24863823a3daf0ca664c7fdf008379d0a0d42", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7eddb78e705905af4c1dd2359900db3e78a3c500", default-features = false, features = ["metadata"]} [features] default = ["rt"] From bc156afbb2950ae073d0f271f26517cf16b35263 Mon Sep 17 00:00:00 2001 From: Lucas Granberg Date: Thu, 10 Aug 2023 16:16:45 +0300 Subject: [PATCH 208/298] fix rng ced toggling sequence on reset. --- embassy-stm32/src/rng.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 2a4978ec..30816e43 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -73,15 +73,20 @@ impl<'d, T: Instance> Rng<'d, T> { #[cfg(not(rng_v1))] pub fn reset(&mut self) { T::regs().cr().write(|reg| { - reg.set_rngen(false); reg.set_condrst(true); + reg.set_nistc(pac::rng::vals::Nistc::CUSTOM); // set RNG config "A" according to reference manual // this has to be written within the same write access as setting the CONDRST bit - reg.set_nistc(pac::rng::vals::Nistc::DEFAULT); reg.set_rng_config1(pac::rng::vals::RngConfig1::CONFIGA); + reg.set_clkdiv(pac::rng::vals::Clkdiv::NODIV); reg.set_rng_config2(pac::rng::vals::RngConfig2::CONFIGA_B); reg.set_rng_config3(pac::rng::vals::RngConfig3::CONFIGA); - reg.set_clkdiv(pac::rng::vals::Clkdiv::NODIV); + reg.set_ced(true); + reg.set_ie(false); + reg.set_rngen(true); + }); + T::regs().cr().write(|reg| { + reg.set_ced(false); }); // wait for CONDRST to be set while !T::regs().cr().read().condrst() {} From c3128846923718d94921617776008c1eadcd4fec Mon Sep 17 00:00:00 2001 From: Don Reilly Date: Thu, 10 Aug 2023 08:21:03 -0500 Subject: [PATCH 209/298] added exclusion for adc v4 as well --- .vscode/settings.json | 1 + embassy-stm32/build.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 29e8812e..139b432f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -41,4 +41,5 @@ // "examples/stm32wl/Cargo.toml", // "examples/wasm/Cargo.toml", ], + "rust-analyzer.showUnlinkedFileNotification": false, } \ No newline at end of file diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 743631b8..8a731620 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -313,6 +313,7 @@ fn main() { if !singletons.contains(&p.name.to_string()) || (p.name == "ADC3" && METADATA.line.starts_with("STM32H7")) || (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "f3")) + || (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "v4")) { continue; } From 7d8e3951ba3ff3a89df1d21210c4a76686d4f142 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 10 Aug 2023 22:18:26 +0200 Subject: [PATCH 210/298] fix: ensure spi irq is disabled when dropped --- embassy-nrf/src/spim.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 4673a017..d131a43d 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -378,6 +378,9 @@ impl<'d, T: Instance> Drop for Spim<'d, T> { gpio::deconfigure_pin(r.psel.miso.read().bits()); gpio::deconfigure_pin(r.psel.mosi.read().bits()); + // Disable all events interrupts + T::Interrupt::disable(); + trace!("spim drop: done"); } } From 858d520882df986758eac491fbde916071ebaceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 10 Aug 2023 23:14:52 +0200 Subject: [PATCH 211/298] Executor: Add changelog --- embassy-executor/CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 4fd3dccf..5aecbec1 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Avoid calling `pend()` when waking expired timers +- Properly reset finished task state with `integrated-timers` enabled +- Introduce `InterruptExecutor::spawner()` +- Fix incorrect critical section in Xtensa executor + ## 0.2.0 - 2023-04-27 - Replace unnecessary atomics in runqueue From baef856206dd2cc6f0326914b6db68ed1ada1a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 10 Aug 2023 23:15:29 +0200 Subject: [PATCH 212/298] Bump version --- embassy-executor/CHANGELOG.md | 2 +- embassy-executor/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 5aecbec1..86a47758 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 0.2.1 - 2023-08-10 - Avoid calling `pend()` when waking expired timers - Properly reset finished task state with `integrated-timers` enabled diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 590718e3..ce5e2741 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-executor" -version = "0.2.0" +version = "0.2.1" edition = "2021" license = "MIT OR Apache-2.0" description = "async/await executor designed for embedded usage" From a0c69ffe024588aab3c4cca70a73275851967616 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 10 Aug 2023 18:59:18 -0500 Subject: [PATCH 213/298] stm32/rtc: autocompute wakeup psc. --- embassy-stm32/src/rtc/v2.rs | 96 ++++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index f15ab806..32ccb845 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -5,6 +5,69 @@ use crate::pac::rtc::Rtc; use crate::peripherals::RTC; use crate::rtc::sealed::Instance; +#[derive(Clone, Copy)] +pub(crate) enum WakeupPrescaler { + Div2, + Div4, + Div8, + Div16, +} + +#[cfg(stm32wb)] +impl From for crate::pac::rtc::vals::Wucksel { + fn from(val: WakeupPrescaler) -> Self { + use crate::pac::rtc::vals::Wucksel; + + match val { + WakeupPrescaler::Div2 => Wucksel::DIV2, + WakeupPrescaler::Div4 => Wucksel::DIV4, + WakeupPrescaler::Div8 => Wucksel::DIV8, + WakeupPrescaler::Div16 => Wucksel::DIV16, + } + } +} + +#[cfg(stm32wb)] +impl From for WakeupPrescaler { + fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { + use crate::pac::rtc::vals::Wucksel; + + match val { + Wucksel::DIV2 => WakeupPrescaler::Div2, + Wucksel::DIV4 => WakeupPrescaler::Div4, + Wucksel::DIV8 => WakeupPrescaler::Div8, + Wucksel::DIV16 => WakeupPrescaler::Div16, + _ => unreachable!(), + } + } +} + +impl From for u32 { + fn from(val: WakeupPrescaler) -> Self { + match val { + WakeupPrescaler::Div2 => 2, + WakeupPrescaler::Div4 => 4, + WakeupPrescaler::Div8 => 8, + WakeupPrescaler::Div16 => 16, + } + } +} + +impl WakeupPrescaler { + pub fn compute_min(val: u32) -> Self { + *[ + WakeupPrescaler::Div2, + WakeupPrescaler::Div4, + WakeupPrescaler::Div8, + WakeupPrescaler::Div16, + ] + .iter() + .skip_while(|psc| >::into(**psc) <= val) + .next() + .unwrap_or(&WakeupPrescaler::Div16) + } +} + impl super::Rtc { fn unlock_registers() { #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))] @@ -24,44 +87,45 @@ impl super::Rtc { #[allow(dead_code)] #[cfg(all(feature = "time", stm32wb))] - // start the wakeup alarm with the given duration - pub(crate) fn start_wakeup_alarm(duration: embassy_time::Duration) { - use embassy_time::TICK_HZ; - use stm32_metapac::rtc::vals::Wucksel; + /// start the wakeup alarm and return the actual duration of the alarm + /// the actual duration will be the closest value possible that is less + /// than the requested duration. + pub(crate) fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> embassy_time::Duration { + use embassy_time::{Duration, TICK_HZ}; use crate::interrupt::typelevel::Interrupt; use crate::rcc::get_freqs; let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; - // Choose the lowest prescaler available - #[cfg(stm32wb)] - let rtc_hz = rtc_hz / 2; + let rtc_ticks = requested_duration.as_ticks() * rtc_hz / TICK_HZ; + let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); - let rtc_ticks = duration.as_ticks() * rtc_hz / TICK_HZ; + // adjust the rtc ticks to the prescaler + let rtc_ticks = rtc_ticks / (>::into(prescaler) as u64); let rtc_ticks = if rtc_ticks > u16::MAX as u64 { u16::MAX } else { rtc_ticks as u16 }; - while !RTC::regs().isr().read().wutf() {} - - RTC::regs().isr().modify(|w| w.set_wutf(false)); - - RTC::regs().wutr().modify(|w| w.set_wut(rtc_ticks)); + let duration = Duration::from_ticks( + rtc_ticks as u64 * TICK_HZ * (>::into(prescaler) as u64) / rtc_hz, + ); crate::interrupt::typelevel::RTC_WKUP::unpend(); unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; + RTC::regs().wutr().modify(|w| w.set_wut(rtc_ticks)); + RTC::regs().cr().modify(|w| { - // Choose the lowest prescaler available - #[cfg(stm32wb)] - w.set_wucksel(Wucksel::DIV2); + w.set_wucksel(prescaler.into()); w.set_wutie(true); w.set_wute(true); }); + + duration } #[allow(dead_code)] From b69861013aaacad913d99268fa6da634ab7e0c10 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 10 Aug 2023 19:13:48 -0500 Subject: [PATCH 214/298] stm32/rtc: implement stop_wakeup_alarm --- embassy-stm32/src/rtc/v2.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 32ccb845..5cd5adef 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -137,20 +137,24 @@ impl super::Rtc { use crate::interrupt::typelevel::Interrupt; use crate::rcc::get_freqs; - crate::interrupt::typelevel::RTC_WKUP::disable(); - RTC::regs().cr().modify(|w| { w.set_wute(false); }); - let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; + // Wait for the wakeup timer to stop + while !RTC::regs().isr().read().wutf() {} - // Choose the lowest prescaler available - #[cfg(stm32wb)] - let rtc_hz = rtc_hz / 2; + RTC::regs().isr().modify(|w| w.set_wutf(false)); + + crate::interrupt::typelevel::RTC_WKUP::disable(); + + let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; + let prescaler: WakeupPrescaler = RTC::regs().cr().read().wucksel().into(); let rtc_ticks = RTC::regs().wutr().read().wut(); - Duration::from_ticks(rtc_ticks as u64 * TICK_HZ / rtc_hz) + Duration::from_ticks( + rtc_ticks as u64 * TICK_HZ * (>::into(prescaler) as u64) / rtc_hz, + ) } #[allow(dead_code)] From 3a3f3b492f95ac54b3cae2ea1ba9f61d2ab6496d Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 10 Aug 2023 19:14:55 -0500 Subject: [PATCH 215/298] rustfmt --- embassy-stm32/src/rtc/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 945bfafd..a6102077 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -13,6 +13,7 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; )] #[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")] mod _version; +#[allow(unused_imports)] pub use _version::*; use embassy_hal_internal::Peripheral; From b658f10db9a963d85b8465759692b0aa7973a1d1 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Wed, 9 Aug 2023 11:50:26 +0200 Subject: [PATCH 216/298] Expose poll_ready_to_{send,receive} in Sender/Receiver --- embassy-sync/src/channel.rs | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index d6f36f53..3896f70a 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -65,6 +65,13 @@ where pub fn try_send(&self, message: T) -> Result<(), TrySendError> { self.channel.try_send(message) } + + /// Allows a poll_fn to poll until the channel is ready to send + /// + /// See [`Channel::poll_ready_to_send()`] + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + self.channel.poll_ready_to_send(cx) + } } /// Send-only access to a [`Channel`] without knowing channel size. @@ -106,6 +113,13 @@ impl<'ch, T> DynamicSender<'ch, T> { pub fn try_send(&self, message: T) -> Result<(), TrySendError> { self.channel.try_send_with_context(message, None) } + + /// Allows a poll_fn to poll until the channel is ready to send + /// + /// See [`Channel::poll_ready_to_send()`] + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + self.channel.poll_ready_to_send(cx) + } } /// Receive-only access to a [`Channel`]. @@ -144,6 +158,13 @@ where pub fn try_recv(&self) -> Result { self.channel.try_recv() } + + /// Allows a poll_fn to poll until the channel is ready to receive + /// + /// See [`Channel::poll_ready_to_receive()`] + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + self.channel.poll_ready_to_receive(cx) + } } /// Receive-only access to a [`Channel`] without knowing channel size. @@ -173,6 +194,13 @@ impl<'ch, T> DynamicReceiver<'ch, T> { pub fn try_recv(&self) -> Result { self.channel.try_recv_with_context(None) } + + /// Allows a poll_fn to poll until the channel is ready to receive + /// + /// See [`Channel::poll_ready_to_receive()`] + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + self.channel.poll_ready_to_receive(cx) + } } impl<'ch, M, T, const N: usize> From> for DynamicReceiver<'ch, T> @@ -286,6 +314,9 @@ trait DynamicChannel { fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError>; fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result; + + fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool; + fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool; } /// Error returned by [`try_recv`](Channel::try_recv). @@ -492,6 +523,14 @@ where fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { Channel::try_recv_with_context(self, cx) } + + fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + Channel::poll_ready_to_send(self, cx) + } + + fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + Channel::poll_ready_to_receive(self, cx) + } } #[cfg(test)] From f9d251cd5cd9c84718cd66e7697a8502c4d61a0a Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Fri, 11 Aug 2023 11:15:17 +0200 Subject: [PATCH 217/298] Channel poll methods return Poll instead of bool --- embassy-stm32-wpan/src/mac/driver.rs | 4 +++- embassy-stm32/src/can/bxcan.rs | 9 +------ embassy-sync/src/channel.rs | 36 +++++++++++++++++----------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index f8e3a2b0..a58ee231 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -28,7 +28,9 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { type TxToken<'a> = TxToken<'d> where Self: 'a; fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - if self.runner.rx_channel.poll_ready_to_receive(cx) && self.runner.tx_buf_channel.poll_ready_to_receive(cx) { + if self.runner.rx_channel.poll_ready_to_receive(cx).is_ready() + && self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() + { Some(( RxToken { rx: &self.runner.rx_channel, diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index fb223e4a..e439207e 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -506,14 +506,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { /// 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 + poll_fn(|cx| T::state().rx_queue.poll_ready_to_receive(cx)).await } fn curr_error(&self) -> Option { diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index 3896f70a..e7224856 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -69,7 +69,7 @@ where /// Allows a poll_fn to poll until the channel is ready to send /// /// See [`Channel::poll_ready_to_send()`] - pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_send(cx) } } @@ -117,7 +117,7 @@ impl<'ch, T> DynamicSender<'ch, T> { /// Allows a poll_fn to poll until the channel is ready to send /// /// See [`Channel::poll_ready_to_send()`] - pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_send(cx) } } @@ -162,7 +162,7 @@ where /// Allows a poll_fn to poll until the channel is ready to receive /// /// See [`Channel::poll_ready_to_receive()`] - pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_receive(cx) } } @@ -198,7 +198,7 @@ impl<'ch, T> DynamicReceiver<'ch, T> { /// Allows a poll_fn to poll until the channel is ready to receive /// /// See [`Channel::poll_ready_to_receive()`] - pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_receive(cx) } } @@ -315,8 +315,8 @@ trait DynamicChannel { fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result; - fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool; - fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool; + fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()>; + fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()>; } /// Error returned by [`try_recv`](Channel::try_recv). @@ -370,10 +370,14 @@ impl ChannelState { } } - fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> bool { + fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> Poll<()> { self.receiver_waker.register(cx.waker()); - !self.queue.is_empty() + if !self.queue.is_empty() { + Poll::Ready(()) + } else { + Poll::Pending + } } fn try_send(&mut self, message: T) -> Result<(), TrySendError> { @@ -395,10 +399,14 @@ impl ChannelState { } } - fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> bool { + fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> Poll<()> { self.senders_waker.register(cx.waker()); - !self.queue.is_full() + if !self.queue.is_full() { + Poll::Ready(()) + } else { + Poll::Pending + } } } @@ -449,12 +457,12 @@ where } /// Allows a poll_fn to poll until the channel is ready to receive - pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { self.lock(|c| c.poll_ready_to_receive(cx)) } /// Allows a poll_fn to poll until the channel is ready to send - pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { self.lock(|c| c.poll_ready_to_send(cx)) } @@ -524,11 +532,11 @@ where Channel::try_recv_with_context(self, cx) } - fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { Channel::poll_ready_to_send(self, cx) } - fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { Channel::poll_ready_to_receive(self, cx) } } From b1ec460b9af131ef80fcafd79a7f63aa326aaf94 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Fri, 11 Aug 2023 11:30:29 +0200 Subject: [PATCH 218/298] Implement Channel::poll_receive(..) -> Poll --- embassy-stm32-wpan/src/mac/driver.rs | 2 +- embassy-sync/src/channel.rs | 43 +++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index a58ee231..93898d88 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -46,7 +46,7 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { } fn transmit(&mut self, cx: &mut Context) -> Option> { - if self.runner.tx_buf_channel.poll_ready_to_receive(cx) { + if self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() { Some(TxToken { tx: &self.runner.tx_channel, tx_buf: &self.runner.tx_buf_channel, diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index e7224856..dc727fb1 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -165,6 +165,13 @@ where pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_receive(cx) } + + /// Poll the channel for the next item + /// + /// See [`Channel::poll_receive()`] + pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { + self.channel.poll_receive(cx) + } } /// Receive-only access to a [`Channel`] without knowing channel size. @@ -201,6 +208,13 @@ impl<'ch, T> DynamicReceiver<'ch, T> { pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_receive(cx) } + + /// Poll the channel for the next item + /// + /// See [`Channel::poll_receive()`] + pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { + self.channel.poll_receive(cx) + } } impl<'ch, M, T, const N: usize> From> for DynamicReceiver<'ch, T> @@ -228,10 +242,7 @@ where type Output = T; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.channel.try_recv_with_context(Some(cx)) { - Ok(v) => Poll::Ready(v), - Err(TryRecvError::Empty) => Poll::Pending, - } + self.channel.poll_receive(cx) } } @@ -317,6 +328,8 @@ trait DynamicChannel { fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()>; fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()>; + + fn poll_receive(&self, cx: &mut Context<'_>) -> Poll; } /// Error returned by [`try_recv`](Channel::try_recv). @@ -370,6 +383,19 @@ impl ChannelState { } } + fn poll_receive(&mut self, cx: &mut Context<'_>) -> Poll { + if self.queue.is_full() { + self.senders_waker.wake(); + } + + if let Some(message) = self.queue.pop_front() { + Poll::Ready(message) + } else { + self.receiver_waker.register(cx.waker()); + Poll::Pending + } + } + fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> Poll<()> { self.receiver_waker.register(cx.waker()); @@ -452,6 +478,11 @@ where self.lock(|c| c.try_recv_with_context(cx)) } + /// Poll the channel for the next message + pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { + self.lock(|c| c.poll_receive(cx)) + } + fn try_send_with_context(&self, m: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError> { self.lock(|c| c.try_send_with_context(m, cx)) } @@ -539,6 +570,10 @@ where fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()> { Channel::poll_ready_to_receive(self, cx) } + + fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { + Channel::poll_receive(self, cx) + } } #[cfg(test)] From 55ff397c0cde8a04c41cfc228645c3fd33383cd1 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 11 Aug 2023 19:47:24 +0200 Subject: [PATCH 219/298] boot: release flash after prepare and refactor api This refactoring of the chip specific bootloader creates the internal boot instance and aligned buffer in the prepare stage, so that they are automatically dropped after. This unlocks a use case where peripherals owning the flash need to be Drop'ed before load() happens. --- embassy-boot/nrf/src/lib.rs | 35 +++++++--------------- embassy-boot/rp/src/lib.rs | 33 +++++++------------- embassy-boot/stm32/src/lib.rs | 33 +++++++------------- examples/boot/bootloader/nrf/src/main.rs | 4 +-- examples/boot/bootloader/rp/src/main.rs | 4 +-- examples/boot/bootloader/stm32/src/main.rs | 4 +-- 6 files changed, 34 insertions(+), 79 deletions(-) diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index df94819f..b9d86eb1 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs @@ -14,28 +14,17 @@ use embassy_nrf::wdt; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for nRF devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, - aligned_buf: AlignedBuffer, -} +pub struct BootLoader; -impl - BootLoader -{ - /// Create a new bootloader instance using the supplied partitions for active, dfu and state. - pub fn new(config: BootLoaderConfig) -> Self { - Self { - boot: embassy_boot::BootLoader::new(config), - aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), - } - } - - /// Inspect the bootloader state and perform actions required before booting, such as swapping - /// firmware. - pub fn prepare(&mut self) { - self.boot - .prepare_boot(&mut self.aligned_buf.0) - .expect("Boot prepare error"); +impl BootLoader { + /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware. + pub fn prepare( + config: BootLoaderConfig, + ) -> Self { + let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]); + let mut boot = embassy_boot::BootLoader::new(config); + boot.prepare_boot(&mut aligned_buf.0).expect("Boot prepare error"); + Self } /// Boots the application without softdevice mechanisms. @@ -45,8 +34,6 @@ impl /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(not(feature = "softdevice"))] pub unsafe fn load(self, start: u32) -> ! { - core::mem::drop(self.boot); - let mut p = cortex_m::Peripherals::steal(); p.SCB.invalidate_icache(); p.SCB.vtor.write(start); @@ -59,7 +46,7 @@ impl /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. #[cfg(feature = "softdevice")] - pub unsafe fn load(&mut self, _app: u32) -> ! { + pub unsafe fn load(self, _app: u32) -> ! { use nrf_softdevice_mbr as mbr; const NRF_SUCCESS: u32 = 0; diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index f5aefa41..96bcf3bf 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -15,28 +15,17 @@ use embassy_time::Duration; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for RP2040 devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, - aligned_buf: AlignedBuffer, -} +pub struct BootLoader; -impl - BootLoader -{ - /// Create a new bootloader instance using the supplied partitions for active, dfu and state. - pub fn new(config: BootLoaderConfig) -> Self { - Self { - boot: embassy_boot::BootLoader::new(config), - aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), - } - } - - /// Inspect the bootloader state and perform actions required before booting, such as swapping - /// firmware. - pub fn prepare(&mut self) { - self.boot - .prepare_boot(self.aligned_buf.as_mut()) - .expect("Boot prepare error"); +impl BootLoader { + /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware + pub fn prepare( + config: BootLoaderConfig, + ) -> Self { + let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]); + let mut boot = embassy_boot::BootLoader::new(config); + boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error"); + Self } /// Boots the application. @@ -45,8 +34,6 @@ impl /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. pub unsafe fn load(self, start: u32) -> ! { - core::mem::drop(self.boot); - trace!("Loading app at 0x{:x}", start); #[allow(unused_mut)] let mut p = cortex_m::Peripherals::steal(); diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index 25f02942..c6350c49 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs @@ -11,28 +11,17 @@ pub use embassy_boot::{FirmwareState, FirmwareUpdater}; use embedded_storage::nor_flash::NorFlash; /// A bootloader for STM32 devices. -pub struct BootLoader { - boot: embassy_boot::BootLoader, - aligned_buf: AlignedBuffer, -} +pub struct BootLoader; -impl - BootLoader -{ - /// Create a new bootloader instance using the supplied partitions for active, dfu and state. - pub fn new(config: BootLoaderConfig) -> Self { - Self { - boot: embassy_boot::BootLoader::new(config), - aligned_buf: AlignedBuffer([0; BUFFER_SIZE]), - } - } - - /// Inspect the bootloader state and perform actions required before booting, such as swapping - /// firmware. - pub fn prepare(&mut self) { - self.boot - .prepare_boot(self.aligned_buf.as_mut()) - .expect("Boot prepare error"); +impl BootLoader { + /// Inspect the bootloader state and perform actions required before booting, such as swapping firmware + pub fn prepare( + config: BootLoaderConfig, + ) -> Self { + let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]); + let mut boot = embassy_boot::BootLoader::new(config); + boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error"); + Self } /// Boots the application. @@ -41,8 +30,6 @@ impl /// /// This modifies the stack pointer and reset vector and will run code placed in the active partition. pub unsafe fn load(self, start: u32) -> ! { - core::mem::drop(self.boot); - trace!("Loading app at 0x{:x}", start); #[allow(unused_mut)] let mut p = cortex_m::Peripherals::steal(); diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 72c95c02..74e2e293 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -33,9 +33,7 @@ fn main() -> ! { let config = BootLoaderConfig::from_linkerfile_blocking(&flash); let active_offset = config.active.offset(); - let mut bl: BootLoader<_, _, _> = BootLoader::new(config); - - bl.prepare(); + let bl: BootLoader = BootLoader::prepare(config); unsafe { bl.load(active_offset) } } diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index 6a81db80..c0e75d1e 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs @@ -29,9 +29,7 @@ fn main() -> ! { let config = BootLoaderConfig::from_linkerfile_blocking(&flash); let active_offset = config.active.offset(); - let mut bl: BootLoader<_, _, _> = BootLoader::new(config); - - bl.prepare(); + let bl: BootLoader = BootLoader::prepare(config); unsafe { bl.load(embassy_rp::flash::FLASH_BASE as u32 + active_offset) } } diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 262eed20..5fd9ea58 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -27,9 +27,7 @@ fn main() -> ! { let config = BootLoaderConfig::from_linkerfile_blocking(&flash); let active_offset = config.active.offset(); - let mut bl: BootLoader<_, _, _, 2048> = BootLoader::new(config); - - bl.prepare(); + let bl = BootLoader::prepare::<_, _, _, 2048>(config); unsafe { bl.load(BANK1_REGION.base + active_offset) } } From 675b7fb6056d8c3dfaca759b7cd373e2f4a0e111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 12 Aug 2023 16:00:18 +0200 Subject: [PATCH 220/298] POC: allow custom executors --- embassy-executor/Cargo.toml | 7 +- embassy-executor/src/arch/cortex_m.rs | 252 +++++++------------------- embassy-executor/src/arch/riscv32.rs | 100 ++++------ embassy-executor/src/arch/std.rs | 76 +++----- embassy-executor/src/arch/wasm.rs | 37 ++-- embassy-executor/src/arch/xtensa.rs | 117 +++++------- embassy-executor/src/interrupt.rs | 127 +++++++++++++ embassy-executor/src/lib.rs | 5 + embassy-executor/src/raw/mod.rs | 35 +++- embassy-executor/src/thread.rs | 80 ++++++++ 10 files changed, 448 insertions(+), 388 deletions(-) create mode 100644 embassy-executor/src/interrupt.rs create mode 100644 embassy-executor/src/thread.rs diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index ce5e2741..182dd693 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -31,11 +31,11 @@ features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-th # Architecture _arch = [] # some arch was picked -arch-std = ["_arch", "critical-section/std"] +arch-std = ["_arch", "critical-section/std", "thread-context"] arch-cortex-m = ["_arch", "dep:cortex-m"] arch-xtensa = ["_arch"] arch-riscv32 = ["_arch"] -arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"] +arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys", "thread-context"] # Enable creating a `Pender` from an arbitrary function pointer callback. pender-callback = [] @@ -45,6 +45,9 @@ executor-thread = [] # Enable the interrupt-mode executor (available in Cortex-M only) executor-interrupt = [] +# Pass a context to the thread-mode executor. +thread-context = [] + # Enable nightly-only features nightly = [] diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 94c8134d..ca1675c0 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -1,224 +1,98 @@ #[cfg(feature = "executor-thread")] pub use thread::*; + #[cfg(feature = "executor-thread")] mod thread { - use core::arch::asm; - use core::marker::PhantomData; #[cfg(feature = "nightly")] pub use embassy_macros::main_cortex_m as main; - use crate::raw::{Pender, PenderInner}; - use crate::{raw, Spawner}; + use crate::raw::OpaqueThreadContext; + use crate::thread::ThreadContext; - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender; + #[export_name = "__thread_mode_pender"] + fn __thread_mode_pender(_core_id: OpaqueThreadContext) { + unsafe { core::arch::asm!("sev") } + } - impl ThreadPender { - pub(crate) fn pend(self) { - unsafe { core::arch::asm!("sev") } + /// TODO + // Name pending + #[derive(Default)] // Default enables Executor::new + pub struct CortexMThreadContext { + _not_send: core::marker::PhantomData<*mut ()>, + } + + impl ThreadContext for CortexMThreadContext { + #[cfg(feature = "thread-context")] + fn context(&self) -> OpaqueThreadContext { + // Enabling thread-context is not incorrect, just wasteful. + OpaqueThreadContext(0) + } + + #[cfg(not(feature = "thread-context"))] + fn context(&self) -> OpaqueThreadContext { + OpaqueThreadContext(()) + } + + fn wait(&mut self) { + unsafe { core::arch::asm!("wfe") } } } - /// Thread mode executor, using WFE/SEV. - /// - /// This is the simplest and most common kind of executor. It runs on - /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction - /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction - /// is executed, to make the `WFE` exit from sleep and poll the task. - /// - /// This executor allows for ultra low power consumption for chips where `WFE` - /// triggers low-power sleep without extra steps. If your chip requires extra steps, - /// you may use [`raw::Executor`] directly to program custom behavior. - pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, - } - - impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), - not_send: PhantomData, - } - } - - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { - self.inner.poll(); - asm!("wfe"); - }; - } - } - } + /// TODO + // Type alias for backwards compatibility + pub type Executor = crate::thread::ThreadModeExecutor; } +// None of this has to be public, I guess? #[cfg(feature = "executor-interrupt")] pub use interrupt::*; #[cfg(feature = "executor-interrupt")] mod interrupt { - use core::cell::UnsafeCell; - use core::mem::MaybeUninit; - - use atomic_polyfill::{AtomicBool, Ordering}; use cortex_m::interrupt::InterruptNumber; use cortex_m::peripheral::NVIC; - use crate::raw::{self, Pender, PenderInner}; + use crate::interrupt::InterruptContext; + use crate::raw::OpaqueInterruptContext; #[derive(Clone, Copy)] - pub(crate) struct InterruptPender(u16); + struct CortexMInterruptContext(u16); - impl InterruptPender { - pub(crate) fn pend(self) { - // STIR is faster, but is only available in v7 and higher. - #[cfg(not(armv6m))] - { - let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) }; - nvic.request(self); - } - - #[cfg(armv6m)] - cortex_m::peripheral::NVIC::pend(self); - } - } - - unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender { + unsafe impl cortex_m::interrupt::InterruptNumber for CortexMInterruptContext { fn number(self) -> u16 { self.0 } } - /// Interrupt mode executor. - /// - /// This executor runs tasks in interrupt mode. The interrupt handler is set up - /// to poll tasks, and when a task is woken the interrupt is pended from software. - /// - /// This allows running async tasks at a priority higher than thread mode. One - /// use case is to leave thread mode free for non-async tasks. Another use case is - /// to run multiple executors: one in thread mode for low priority tasks and another in - /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower - /// priority ones. - /// - /// It is even possible to run multiple interrupt mode executors at different priorities, - /// by assigning different priorities to the interrupts. For an example on how to do this, - /// See the 'multiprio' example for 'embassy-nrf'. - /// - /// To use it, you have to pick an interrupt that won't be used by the hardware. - /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). - /// If this is not the case, you may use an interrupt from any unused peripheral. - /// - /// It is somewhat more complex to use, it's recommended to use the thread-mode - /// [`Executor`] instead, if it works for your use case. - pub struct InterruptExecutor { - started: AtomicBool, - executor: UnsafeCell>, - } - - unsafe impl Send for InterruptExecutor {} - unsafe impl Sync for InterruptExecutor {} - - impl InterruptExecutor { - /// Create a new, not started `InterruptExecutor`. - #[inline] - pub const fn new() -> Self { - Self { - started: AtomicBool::new(false), - executor: UnsafeCell::new(MaybeUninit::uninit()), - } + impl InterruptContext for T + where + T: InterruptNumber, + { + fn context(&self) -> OpaqueInterruptContext { + OpaqueInterruptContext(self.number() as usize) } - /// Executor interrupt callback. - /// - /// # Safety - /// - /// You MUST call this from the interrupt handler, and from nowhere else. - pub unsafe fn on_interrupt(&'static self) { - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - executor.poll(); - } - - /// Start the executor. - /// - /// This initializes the executor, enables the interrupt, and returns. - /// The executor keeps running in the background through the interrupt. - /// - /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] - /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a - /// different "thread" (the interrupt), so spawning tasks on it is effectively - /// sending them. - /// - /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from - /// a task running in it. - /// - /// # Interrupt requirements - /// - /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). - /// - /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. - /// - /// You must set the interrupt priority before calling this method. You MUST NOT - /// do it after. - /// - pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner { - if self - .started - .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) - .is_err() - { - panic!("InterruptExecutor::start() called multiple times on the same executor."); - } - - unsafe { - (&mut *self.executor.get()) - .as_mut_ptr() - .write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender( - irq.number(), - ))))) - } - - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - - unsafe { NVIC::unmask(irq) } - - executor.spawner().make_send() - } - - /// Get a SendSpawner for this executor - /// - /// This returns a [`SendSpawner`] you can use to spawn tasks on this - /// executor. - /// - /// This MUST only be called on an executor that has already been spawned. - /// The function will panic otherwise. - pub fn spawner(&'static self) -> crate::SendSpawner { - if !self.started.load(Ordering::Acquire) { - panic!("InterruptExecutor::spawner() called on uninitialized executor."); - } - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - executor.spawner().make_send() + fn enable(&self) { + unsafe { NVIC::unmask(*self) } } } + + #[export_name = "__interrupt_mode_pender"] + fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext) { + let interrupt = CortexMInterruptContext(unsafe { core::mem::transmute::<_, usize>(interrupt) as u16 }); + + // STIR is faster, but is only available in v7 and higher. + #[cfg(not(armv6m))] + { + let mut nvic: NVIC = unsafe { core::mem::transmute(()) }; + nvic.request(interrupt); + } + + #[cfg(armv6m)] + NVIC::pend(interrupt); + } + + /// TODO + // Type alias for backwards compatibility + pub type InterruptExecutor = crate::interrupt::InterruptModeExecutor; } diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index ff7ec157..5f766442 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -1,6 +1,9 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-riscv32`."); +#[cfg(feature = "thread-context")] +compile_error!("`thread-context` is not supported with `arch-riscv32`."); + #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] @@ -11,77 +14,50 @@ mod thread { #[cfg(feature = "nightly")] pub use embassy_macros::main_riscv as main; - use crate::raw::{Pender, PenderInner}; - use crate::{raw, Spawner}; - - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender; - - impl ThreadPender { - #[allow(unused)] - pub(crate) fn pend(self) { - SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); - } - } + use crate::raw::OpaqueThreadContext; + use crate::thread::ThreadContext; /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - /// RISCV32 Executor - pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, + #[export_name = "__thread_mode_pender"] + fn __thread_mode_pender(_core_id: OpaqueThreadContext) { + SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } - impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), - not_send: PhantomData, - } + /// TODO + // Name pending + #[derive(Default)] // Default enables Executor::new + pub struct RiscVThreadContext { + _not_send: PhantomData<*mut ()>, + } + + impl ThreadContext for RiscVThreadContext { + fn context(&self) -> OpaqueThreadContext { + OpaqueThreadContext(()) } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { - self.inner.poll(); - // we do not care about race conditions between the load and store operations, interrupts - //will only set this value to true. - critical_section::with(|_| { - // if there is work to do, loop back to polling - // TODO can we relax this? - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - } - // if not, wait for interrupt - else { - core::arch::asm!("wfi"); - } - }); - // if an interrupt occurred while waiting, it will be serviced here + fn wait(&mut self) { + // We do not care about race conditions between the load and store operations, + // interrupts will only set this value to true. + critical_section::with(|_| { + // if there is work to do, loop back to polling + // TODO can we relax this? + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); } - } + // if not, wait for interrupt + else { + unsafe { + core::arch::asm!("wfi"); + } + } + }); + // if an interrupt occurred while waiting, it will be serviced here } } + + /// TODO + // Type alias for backwards compatibility + pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index 4e4a178f..28e25fbd 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -1,6 +1,9 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-std`."); +#[cfg(not(feature = "thread-context"))] +compile_error!("`arch-std` requires `thread-context`."); + #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] @@ -11,63 +14,40 @@ mod thread { #[cfg(feature = "nightly")] pub use embassy_macros::main_std as main; - use crate::raw::{Pender, PenderInner}; - use crate::{raw, Spawner}; + use crate::raw::OpaqueThreadContext; + use crate::thread::ThreadContext; - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender(&'static Signaler); - - impl ThreadPender { - #[allow(unused)] - pub(crate) fn pend(self) { - self.0.signal() - } - } - - /// Single-threaded std-based executor. - pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, + /// TODO + // Name pending + pub struct StdThreadCtx { + _not_send: PhantomData<*mut ()>, signaler: &'static Signaler, } - impl Executor { - /// Create a new Executor. - pub fn new() -> Self { + impl Default for StdThreadCtx { + fn default() -> Self { let signaler = &*Box::leak(Box::new(Signaler::new())); Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))), - not_send: PhantomData, + _not_send: PhantomData, signaler, } } + } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { self.inner.poll() }; - self.signaler.wait() - } + impl ThreadContext for StdThreadCtx { + fn context(&self) -> OpaqueThreadContext { + OpaqueThreadContext(self.signaler as *const _ as usize) } + + fn wait(&mut self) { + self.signaler.wait() + } + } + + #[export_name = "__thread_mode_pender"] + fn __thread_mode_pender(core_id: OpaqueThreadContext) { + let signaler: &'static Signaler = unsafe { std::mem::transmute(core_id) }; + signaler.signal() } struct Signaler { @@ -97,4 +77,8 @@ mod thread { self.condvar.notify_one(); } } + + /// TODO + // Type alias for backwards compatibility + pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 08ab16b9..4f5ce9c9 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -1,6 +1,9 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-wasm`."); +#[cfg(not(feature = "thread-context"))] +compile_error!("`arch-wasm` requires `thread-context`."); + #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] @@ -14,14 +17,13 @@ mod thread { use wasm_bindgen::prelude::*; use crate::raw::util::UninitCell; - use crate::raw::{Pender, PenderInner}; + use crate::raw::{OpaqueThreadContext, Pender, PenderInner}; use crate::{raw, Spawner}; - /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. - pub struct Executor { - inner: raw::Executor, - ctx: &'static WasmContext, - not_send: PhantomData<*mut ()>, + #[export_name = "__thread_mode_pender"] + fn __thread_mode_pender(context: OpaqueThreadContext) { + let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) }; + let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() }); } pub(crate) struct WasmContext { @@ -29,16 +31,6 @@ mod thread { closure: UninitCell>, } - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender(&'static WasmContext); - - impl ThreadPender { - #[allow(unused)] - pub(crate) fn pend(self) { - let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() }); - } - } - impl WasmContext { pub fn new() -> Self { Self { @@ -48,14 +40,23 @@ mod thread { } } + /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. + pub struct Executor { + inner: raw::Executor, + ctx: &'static WasmContext, + not_send: PhantomData<*mut ()>, + } + impl Executor { /// Create a new Executor. pub fn new() -> Self { let ctx = &*Box::leak(Box::new(WasmContext::new())); Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))), - not_send: PhantomData, + inner: raw::Executor::new(Pender(PenderInner::Thread(OpaqueThreadContext( + ctx as *const _ as usize, + )))), ctx, + not_send: PhantomData, } } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 017b2c52..8e1b917d 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -1,6 +1,12 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-xtensa`."); +#[cfg(feature = "thread-context")] +compile_error!( + "`thread-context` is not supported with `arch-xtensa`.\ + Use a multicore-safe executor from esp-hal instead." // obviously, this is too specific to ESP32 +); + #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] @@ -8,86 +14,63 @@ mod thread { use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; - use crate::raw::{Pender, PenderInner}; - use crate::{raw, Spawner}; + use crate::raw::OpaqueThreadContext; + use crate::thread::ThreadContext; - #[derive(Copy, Clone)] - pub(crate) struct ThreadPender; - - impl ThreadPender { - #[allow(unused)] - pub(crate) fn pend(self) { - SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); - } - } - - /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa + /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - /// Xtensa Executor - pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, + #[export_name = "__thread_mode_pender"] + fn __thread_mode_pender(_core_id: OpaqueThreadContext) { + SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } - impl Executor { - /// Create a new Executor. - pub fn new() -> Self { - Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), - not_send: PhantomData, - } + /// TODO + // Name pending + pub struct XtensaThreadContext { + _not_send: PhantomData<*mut ()>, + } + + impl Default for XtensaThreadContext { + fn default() -> Self { + Self { _not_send: PhantomData } + } + } + + impl ThreadContext for XtensaThreadContext { + fn context(&self) -> OpaqueThreadContext { + OpaqueThreadContext(()) } - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); + fn wait(&mut self) { + unsafe { + // Manual critical section implementation that only masks interrupts handlers. + // We must not acquire the cross-core on dual-core systems because that would + // prevent the other core from doing useful work while this core is sleeping. + let token: critical_section::RawRestoreState; + core::arch::asm!("rsil {0}, 5", out(reg) token); - loop { - unsafe { - self.inner.poll(); + // we do not care about race conditions between the load and store operations, interrupts + // will only set this value to true. + // if there is work to do, loop back to polling + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - // Manual critical section implementation that only masks interrupts handlers. - // We must not acquire the cross-core on dual-core systems because that would - // prevent the other core from doing useful work while this core is sleeping. - let token: critical_section::RawRestoreState; - core::arch::asm!("rsil {0}, 5", out(reg) token); - - // we do not care about race conditions between the load and store operations, interrupts - // will only set this value to true. - // if there is work to do, loop back to polling - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - - core::arch::asm!( + core::arch::asm!( "wsr.ps {0}", "rsync", in(reg) token) - } else { - // waiti sets the PS.INTLEVEL when slipping into sleep - // because critical sections in Xtensa are implemented via increasing - // PS.INTLEVEL the critical section ends here - // take care not add code after `waiti` if it needs to be inside the CS - core::arch::asm!("waiti 0"); // critical section ends here - } + } else { + // waiti sets the PS.INTLEVEL when slipping into sleep + // because critical sections in Xtensa are implemented via increasing + // PS.INTLEVEL the critical section ends here + // take care not add code after `waiti` if it needs to be inside the CS + core::arch::asm!("waiti 0"); // critical section ends here } } } } + + /// TODO + // Type alias for backwards compatibility + pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/interrupt.rs b/embassy-executor/src/interrupt.rs new file mode 100644 index 00000000..f8b0809d --- /dev/null +++ b/embassy-executor/src/interrupt.rs @@ -0,0 +1,127 @@ +//! Interrupt-mode executor. + +use core::cell::UnsafeCell; +use core::mem::MaybeUninit; + +use atomic_polyfill::{AtomicBool, Ordering}; + +use crate::raw::{self, OpaqueInterruptContext, Pender, PenderInner}; + +/// An interrupt source that can be used to drive an [`InterruptExecutor`]. +// Name pending +pub trait InterruptContext { + /// Creates an opaque identifier for this interrupt. + fn context(&self) -> OpaqueInterruptContext; + + /// Sets up the interrupt request. + fn enable(&self); +} + +/// Interrupt mode executor. +/// +/// This executor runs tasks in interrupt mode. The interrupt handler is set up +/// to poll tasks, and when a task is woken the interrupt is pended from software. +/// +/// This allows running async tasks at a priority higher than thread mode. One +/// use case is to leave thread mode free for non-async tasks. Another use case is +/// to run multiple executors: one in thread mode for low priority tasks and another in +/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower +/// priority ones. +/// +/// It is even possible to run multiple interrupt mode executors at different priorities, +/// by assigning different priorities to the interrupts. For an example on how to do this, +/// See the 'multiprio' example for 'embassy-nrf'. +/// +/// To use it, you have to pick an interrupt that won't be used by the hardware. +/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). +/// If this is not the case, you may use an interrupt from any unused peripheral. +/// +/// It is somewhat more complex to use, it's recommended to use the thread-mode +/// [`Executor`] instead, if it works for your use case. +pub struct InterruptModeExecutor { + started: AtomicBool, + executor: UnsafeCell>, +} + +unsafe impl Send for InterruptModeExecutor {} +unsafe impl Sync for InterruptModeExecutor {} + +impl InterruptModeExecutor { + /// Create a new, not started `InterruptExecutor`. + #[inline] + pub const fn new() -> Self { + Self { + started: AtomicBool::new(false), + executor: UnsafeCell::new(MaybeUninit::uninit()), + } + } + + /// Executor interrupt callback. + /// + /// # Safety + /// + /// You MUST call this from the interrupt handler, and from nowhere else. + pub unsafe fn on_interrupt(&'static self) { + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.poll(); + } + + /// Start the executor. + /// + /// This initializes the executor, enables the interrupt, and returns. + /// The executor keeps running in the background through the interrupt. + /// + /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] + /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a + /// different "thread" (the interrupt), so spawning tasks on it is effectively + /// sending them. + /// + /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from + /// a task running in it. + /// + /// # Interrupt requirements + /// + /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). + /// + /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. + /// + /// You must set the interrupt priority before calling this method. You MUST NOT + /// do it after. + /// + pub fn start(&'static self, irq: impl InterruptContext) -> crate::SendSpawner { + if self + .started + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_err() + { + panic!("InterruptExecutor::start() called multiple times on the same executor."); + } + + unsafe { + (&mut *self.executor.get()) + .as_mut_ptr() + .write(raw::Executor::new(Pender(PenderInner::Interrupt(irq.context())))) + } + + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + + irq.enable(); + + executor.spawner().make_send() + } + + /// Get a SendSpawner for this executor + /// + /// This returns a [`SendSpawner`] you can use to spawn tasks on this + /// executor. + /// + /// This MUST only be called on an executor that has already been spawned. + /// The function will panic otherwise. + pub fn spawner(&'static self) -> crate::SendSpawner { + if !self.started.load(Ordering::Acquire) { + panic!("InterruptExecutor::spawner() called on uninitialized executor."); + } + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.spawner().make_send() + } +} diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 3ce687eb..ca67c948 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -37,6 +37,11 @@ pub use arch::*; pub mod raw; +#[cfg(feature = "executor-interrupt")] +pub mod interrupt; +#[cfg(feature = "executor-thread")] +pub mod thread; + mod spawner; pub use spawner::*; diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 25c2ab0d..b4d70b1e 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -291,12 +291,29 @@ impl TaskPool { } } +/// Context given to the thread-mode executor's pender. +#[cfg(all(feature = "executor-thread", not(feature = "thread-context")))] +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct OpaqueThreadContext(pub(crate) ()); + +/// Context given to the thread-mode executor's pender. +#[cfg(all(feature = "executor-thread", feature = "thread-context"))] +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct OpaqueThreadContext(pub(crate) usize); + +/// Context given to the interrupt-mode executor's pender. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct OpaqueInterruptContext(pub(crate) usize); + #[derive(Clone, Copy)] pub(crate) enum PenderInner { #[cfg(feature = "executor-thread")] - Thread(crate::arch::ThreadPender), + Thread(OpaqueThreadContext), #[cfg(feature = "executor-interrupt")] - Interrupt(crate::arch::InterruptPender), + Interrupt(OpaqueInterruptContext), #[cfg(feature = "pender-callback")] Callback { func: fn(*mut ()), context: *mut () }, } @@ -333,9 +350,19 @@ impl Pender { pub(crate) fn pend(&self) { match self.0 { #[cfg(feature = "executor-thread")] - PenderInner::Thread(x) => x.pend(), + PenderInner::Thread(core_id) => { + extern "Rust" { + fn __thread_mode_pender(core_id: OpaqueThreadContext); + } + unsafe { __thread_mode_pender(core_id) }; + } #[cfg(feature = "executor-interrupt")] - PenderInner::Interrupt(x) => x.pend(), + PenderInner::Interrupt(interrupt) => { + extern "Rust" { + fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext); + } + unsafe { __interrupt_mode_pender(interrupt) }; + } #[cfg(feature = "pender-callback")] PenderInner::Callback { func, context } => func(context), } diff --git a/embassy-executor/src/thread.rs b/embassy-executor/src/thread.rs new file mode 100644 index 00000000..9bbe2950 --- /dev/null +++ b/embassy-executor/src/thread.rs @@ -0,0 +1,80 @@ +//! Thread-mode executor. + +use core::marker::PhantomData; + +use crate::raw::{OpaqueThreadContext, Pender, PenderInner}; +use crate::{raw, Spawner}; + +/// TODO +// Name pending +pub trait ThreadContext: Sized { + /// TODO + fn context(&self) -> OpaqueThreadContext; + + /// TODO + fn wait(&mut self); +} + +/// Thread mode executor, using WFE/SEV. +/// +/// This is the simplest and most common kind of executor. It runs on +/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction +/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction +/// is executed, to make the `WFE` exit from sleep and poll the task. +/// +/// This executor allows for ultra low power consumption for chips where `WFE` +/// triggers low-power sleep without extra steps. If your chip requires extra steps, +/// you may use [`raw::Executor`] directly to program custom behavior. +pub struct ThreadModeExecutor { + inner: raw::Executor, + context: C, + not_send: PhantomData<*mut ()>, +} + +impl ThreadModeExecutor { + /// Create a new Executor. + pub fn new() -> Self + where + C: Default, + { + Self::with_context(C::default()) + } + + /// Create a new Executor. + pub fn with_context(context: C) -> Self { + Self { + inner: raw::Executor::new(Pender(PenderInner::Thread(context.context()))), + context, + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + self.context.wait(); + }; + } + } +} From fbf50cdae899dc1cd2f232b880e096d0fc51f49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 12 Aug 2023 22:05:19 +0200 Subject: [PATCH 221/298] Remove Pender wrapper --- embassy-executor/src/arch/wasm.rs | 6 ++-- embassy-executor/src/interrupt.rs | 4 +-- embassy-executor/src/raw/mod.rs | 46 +++++++++++++++---------------- embassy-executor/src/thread.rs | 4 +-- 4 files changed, 29 insertions(+), 31 deletions(-) diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 4f5ce9c9..e244c0b3 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -17,7 +17,7 @@ mod thread { use wasm_bindgen::prelude::*; use crate::raw::util::UninitCell; - use crate::raw::{OpaqueThreadContext, Pender, PenderInner}; + use crate::raw::{OpaqueThreadContext, Pender}; use crate::{raw, Spawner}; #[export_name = "__thread_mode_pender"] @@ -52,9 +52,7 @@ mod thread { pub fn new() -> Self { let ctx = &*Box::leak(Box::new(WasmContext::new())); Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(OpaqueThreadContext( - ctx as *const _ as usize, - )))), + inner: raw::Executor::new(Pender::Thread(OpaqueThreadContext(ctx as *const _ as usize))), ctx, not_send: PhantomData, } diff --git a/embassy-executor/src/interrupt.rs b/embassy-executor/src/interrupt.rs index f8b0809d..c1084ea7 100644 --- a/embassy-executor/src/interrupt.rs +++ b/embassy-executor/src/interrupt.rs @@ -5,7 +5,7 @@ use core::mem::MaybeUninit; use atomic_polyfill::{AtomicBool, Ordering}; -use crate::raw::{self, OpaqueInterruptContext, Pender, PenderInner}; +use crate::raw::{self, OpaqueInterruptContext, Pender}; /// An interrupt source that can be used to drive an [`InterruptExecutor`]. // Name pending @@ -100,7 +100,7 @@ impl InterruptModeExecutor { unsafe { (&mut *self.executor.get()) .as_mut_ptr() - .write(raw::Executor::new(Pender(PenderInner::Interrupt(irq.context())))) + .write(raw::Executor::new(Pender::Interrupt(irq.context()))) } let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index b4d70b1e..7fd29db4 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -308,19 +308,6 @@ pub struct OpaqueThreadContext(pub(crate) usize); #[repr(transparent)] pub struct OpaqueInterruptContext(pub(crate) usize); -#[derive(Clone, Copy)] -pub(crate) enum PenderInner { - #[cfg(feature = "executor-thread")] - Thread(OpaqueThreadContext), - #[cfg(feature = "executor-interrupt")] - Interrupt(OpaqueInterruptContext), - #[cfg(feature = "pender-callback")] - Callback { func: fn(*mut ()), context: *mut () }, -} - -unsafe impl Send for PenderInner {} -unsafe impl Sync for PenderInner {} - /// Platform/architecture-specific action executed when an executor has pending work. /// /// When a task within an executor is woken, the `Pender` is called. This does a @@ -328,7 +315,23 @@ unsafe impl Sync for PenderInner {} /// When this happens, you must arrange for [`Executor::poll`] to be called. /// /// You can think of it as a waker, but for the whole executor. -pub struct Pender(pub(crate) PenderInner); +#[derive(Clone, Copy)] +pub enum Pender { + /// Pender for a thread-mode executor. + #[cfg(feature = "executor-thread")] + Thread(OpaqueThreadContext), + + /// Pender for an interrupt-mode executor. + #[cfg(feature = "executor-interrupt")] + Interrupt(OpaqueInterruptContext), + + /// Arbitrary, dynamically dispatched pender. + #[cfg(feature = "pender-callback")] + Callback { func: fn(*mut ()), context: *mut () }, +} + +unsafe impl Send for Pender {} +unsafe impl Sync for Pender {} impl Pender { /// Create a `Pender` that will call an arbitrary function pointer. @@ -339,32 +342,29 @@ impl Pender { /// - `context`: Opaque context pointer, that will be passed to the function pointer. #[cfg(feature = "pender-callback")] pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self { - Self(PenderInner::Callback { - func, - context: context.into(), - }) + Self::Callback { func, context } } } impl Pender { - pub(crate) fn pend(&self) { - match self.0 { + pub(crate) fn pend(self) { + match self { #[cfg(feature = "executor-thread")] - PenderInner::Thread(core_id) => { + Pender::Thread(core_id) => { extern "Rust" { fn __thread_mode_pender(core_id: OpaqueThreadContext); } unsafe { __thread_mode_pender(core_id) }; } #[cfg(feature = "executor-interrupt")] - PenderInner::Interrupt(interrupt) => { + Pender::Interrupt(interrupt) => { extern "Rust" { fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext); } unsafe { __interrupt_mode_pender(interrupt) }; } #[cfg(feature = "pender-callback")] - PenderInner::Callback { func, context } => func(context), + Pender::Callback { func, context } => func(context), } } } diff --git a/embassy-executor/src/thread.rs b/embassy-executor/src/thread.rs index 9bbe2950..2d2c6daa 100644 --- a/embassy-executor/src/thread.rs +++ b/embassy-executor/src/thread.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; -use crate::raw::{OpaqueThreadContext, Pender, PenderInner}; +use crate::raw::{OpaqueThreadContext, Pender}; use crate::{raw, Spawner}; /// TODO @@ -43,7 +43,7 @@ impl ThreadModeExecutor { /// Create a new Executor. pub fn with_context(context: C) -> Self { Self { - inner: raw::Executor::new(Pender(PenderInner::Thread(context.context()))), + inner: raw::Executor::new(Pender::Thread(context.context())), context, not_send: PhantomData, } From bce250bbdc18f025547f59c30a7bec24826b3aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 12 Aug 2023 22:08:46 +0200 Subject: [PATCH 222/298] Remove unnecessary !Send markers --- embassy-executor/src/arch/cortex_m.rs | 4 +--- embassy-executor/src/arch/riscv32.rs | 5 +---- embassy-executor/src/arch/std.rs | 6 +----- embassy-executor/src/arch/xtensa.rs | 11 ++--------- 4 files changed, 5 insertions(+), 21 deletions(-) diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index ca1675c0..355a0f08 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -18,9 +18,7 @@ mod thread { /// TODO // Name pending #[derive(Default)] // Default enables Executor::new - pub struct CortexMThreadContext { - _not_send: core::marker::PhantomData<*mut ()>, - } + pub struct CortexMThreadContext; impl ThreadContext for CortexMThreadContext { #[cfg(feature = "thread-context")] diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 5f766442..becc0245 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -8,7 +8,6 @@ compile_error!("`thread-context` is not supported with `arch-riscv32`."); pub use thread::*; #[cfg(feature = "executor-thread")] mod thread { - use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; #[cfg(feature = "nightly")] @@ -28,9 +27,7 @@ mod thread { /// TODO // Name pending #[derive(Default)] // Default enables Executor::new - pub struct RiscVThreadContext { - _not_send: PhantomData<*mut ()>, - } + pub struct RiscVThreadContext; impl ThreadContext for RiscVThreadContext { fn context(&self) -> OpaqueThreadContext { diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index 28e25fbd..b08974a0 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -8,7 +8,6 @@ compile_error!("`arch-std` requires `thread-context`."); pub use thread::*; #[cfg(feature = "executor-thread")] mod thread { - use std::marker::PhantomData; use std::sync::{Condvar, Mutex}; #[cfg(feature = "nightly")] @@ -20,16 +19,13 @@ mod thread { /// TODO // Name pending pub struct StdThreadCtx { - _not_send: PhantomData<*mut ()>, signaler: &'static Signaler, } impl Default for StdThreadCtx { fn default() -> Self { - let signaler = &*Box::leak(Box::new(Signaler::new())); Self { - _not_send: PhantomData, - signaler, + signaler: &*Box::leak(Box::new(Signaler::new())), } } } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 8e1b917d..6357bfef 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -27,15 +27,8 @@ mod thread { /// TODO // Name pending - pub struct XtensaThreadContext { - _not_send: PhantomData<*mut ()>, - } - - impl Default for XtensaThreadContext { - fn default() -> Self { - Self { _not_send: PhantomData } - } - } + #[derive(Default)] // Default enables Executor::new + pub struct XtensaThreadContext; impl ThreadContext for XtensaThreadContext { fn context(&self) -> OpaqueThreadContext { From d5e66f6f87222de65ac575c4b923b2fee5487388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 12 Aug 2023 22:20:11 +0200 Subject: [PATCH 223/298] Lift thread-context feature restrictions --- embassy-executor/src/arch/riscv32.rs | 10 +++++++--- embassy-executor/src/arch/xtensa.rs | 19 ++++++++++--------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index becc0245..c4e772e3 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -1,9 +1,6 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-riscv32`."); -#[cfg(feature = "thread-context")] -compile_error!("`thread-context` is not supported with `arch-riscv32`."); - #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] @@ -30,6 +27,13 @@ mod thread { pub struct RiscVThreadContext; impl ThreadContext for RiscVThreadContext { + #[cfg(feature = "thread-context")] + fn context(&self) -> OpaqueThreadContext { + // Enabling thread-context is not incorrect, just wasteful. + OpaqueThreadContext(0) + } + + #[cfg(not(feature = "thread-context"))] fn context(&self) -> OpaqueThreadContext { OpaqueThreadContext(()) } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 6357bfef..1097bff8 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -1,12 +1,6 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-xtensa`."); -#[cfg(feature = "thread-context")] -compile_error!( - "`thread-context` is not supported with `arch-xtensa`.\ - Use a multicore-safe executor from esp-hal instead." // obviously, this is too specific to ESP32 -); - #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] @@ -17,7 +11,7 @@ mod thread { use crate::raw::OpaqueThreadContext; use crate::thread::ThreadContext; - /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV + /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__thread_mode_pender"] @@ -31,6 +25,13 @@ mod thread { pub struct XtensaThreadContext; impl ThreadContext for XtensaThreadContext { + #[cfg(feature = "thread-context")] + fn context(&self) -> OpaqueThreadContext { + // Enabling thread-context is not incorrect, just wasteful. + OpaqueThreadContext(0) + } + + #[cfg(not(feature = "thread-context"))] fn context(&self) -> OpaqueThreadContext { OpaqueThreadContext(()) } @@ -43,8 +44,8 @@ mod thread { let token: critical_section::RawRestoreState; core::arch::asm!("rsil {0}, 5", out(reg) token); - // we do not care about race conditions between the load and store operations, interrupts - // will only set this value to true. + // we do not care about race conditions between the load and store operations, + // interrupts will only set this value to true. // if there is work to do, loop back to polling if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); From 6ab0d71d9246cdc65f392212d03d639a51d21098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 12 Aug 2023 22:42:50 +0200 Subject: [PATCH 224/298] Tweak identifiers and comments --- embassy-executor/src/arch/cortex_m.rs | 13 ++++-------- embassy-executor/src/arch/riscv32.rs | 13 ++++-------- embassy-executor/src/arch/std.rs | 12 +++++------ embassy-executor/src/arch/xtensa.rs | 13 ++++-------- embassy-executor/src/interrupt.rs | 16 ++++++++------ embassy-executor/src/thread.rs | 30 ++++++++++++++++++++------- 6 files changed, 50 insertions(+), 47 deletions(-) diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 355a0f08..6c1300ae 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -11,27 +11,22 @@ mod thread { use crate::thread::ThreadContext; #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_core_id: OpaqueThreadContext) { + fn __thread_mode_pender(_context: OpaqueThreadContext) { unsafe { core::arch::asm!("sev") } } /// TODO // Name pending #[derive(Default)] // Default enables Executor::new - pub struct CortexMThreadContext; + pub struct Context; - impl ThreadContext for CortexMThreadContext { + impl ThreadContext for Context { #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext { // Enabling thread-context is not incorrect, just wasteful. OpaqueThreadContext(0) } - #[cfg(not(feature = "thread-context"))] - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(()) - } - fn wait(&mut self) { unsafe { core::arch::asm!("wfe") } } @@ -39,7 +34,7 @@ mod thread { /// TODO // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; + pub type Executor = crate::thread::ThreadModeExecutor; } // None of this has to be public, I guess? diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index c4e772e3..08720400 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -17,27 +17,22 @@ mod thread { static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_core_id: OpaqueThreadContext) { + fn __thread_mode_pender(_context: OpaqueThreadContext) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } /// TODO // Name pending #[derive(Default)] // Default enables Executor::new - pub struct RiscVThreadContext; + pub struct Context; - impl ThreadContext for RiscVThreadContext { + impl ThreadContext for Context { #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext { // Enabling thread-context is not incorrect, just wasteful. OpaqueThreadContext(0) } - #[cfg(not(feature = "thread-context"))] - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(()) - } - fn wait(&mut self) { // We do not care about race conditions between the load and store operations, // interrupts will only set this value to true. @@ -60,5 +55,5 @@ mod thread { /// TODO // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; + pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index b08974a0..2731e275 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -18,11 +18,11 @@ mod thread { /// TODO // Name pending - pub struct StdThreadCtx { + pub struct Context { signaler: &'static Signaler, } - impl Default for StdThreadCtx { + impl Default for Context { fn default() -> Self { Self { signaler: &*Box::leak(Box::new(Signaler::new())), @@ -30,7 +30,7 @@ mod thread { } } - impl ThreadContext for StdThreadCtx { + impl ThreadContext for Context { fn context(&self) -> OpaqueThreadContext { OpaqueThreadContext(self.signaler as *const _ as usize) } @@ -41,8 +41,8 @@ mod thread { } #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(core_id: OpaqueThreadContext) { - let signaler: &'static Signaler = unsafe { std::mem::transmute(core_id) }; + fn __thread_mode_pender(context: OpaqueThreadContext) { + let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; signaler.signal() } @@ -76,5 +76,5 @@ mod thread { /// TODO // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; + pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 1097bff8..54c84202 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -15,27 +15,22 @@ mod thread { static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_core_id: OpaqueThreadContext) { + fn __thread_mode_pender(_context: OpaqueThreadContext) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } /// TODO // Name pending #[derive(Default)] // Default enables Executor::new - pub struct XtensaThreadContext; + pub struct Context; - impl ThreadContext for XtensaThreadContext { + impl ThreadContext for Context { #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext { // Enabling thread-context is not incorrect, just wasteful. OpaqueThreadContext(0) } - #[cfg(not(feature = "thread-context"))] - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(()) - } - fn wait(&mut self) { unsafe { // Manual critical section implementation that only masks interrupts handlers. @@ -66,5 +61,5 @@ mod thread { /// TODO // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; + pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/interrupt.rs b/embassy-executor/src/interrupt.rs index c1084ea7..6f310651 100644 --- a/embassy-executor/src/interrupt.rs +++ b/embassy-executor/src/interrupt.rs @@ -7,13 +7,17 @@ use atomic_polyfill::{AtomicBool, Ordering}; use crate::raw::{self, OpaqueInterruptContext, Pender}; -/// An interrupt source that can be used to drive an [`InterruptExecutor`]. -// Name pending +/// Architecture-specific interface for an interrupt-mode executor. This trait describes what data +/// should be passed to the [`InterruptExecutor`]'s pender, and how to enable the interrupt that +/// triggers polling the executor. +// TODO: Name pending pub trait InterruptContext { - /// Creates an opaque identifier for this interrupt. + /// A pointer-sized piece of data that is passed to the pender function. + /// + /// Usually, the context contains the interrupt that should be used to wake the executor. fn context(&self) -> OpaqueInterruptContext; - /// Sets up the interrupt request. + /// Enabled the interrupt request. fn enable(&self); } @@ -36,8 +40,8 @@ pub trait InterruptContext { /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). /// If this is not the case, you may use an interrupt from any unused peripheral. /// -/// It is somewhat more complex to use, it's recommended to use the thread-mode -/// [`Executor`] instead, if it works for your use case. +/// It is somewhat more complex to use, it's recommended to use the +/// [`crate::thread::ThreadModeExecutor`] instead, if it works for your use case. pub struct InterruptModeExecutor { started: AtomicBool, executor: UnsafeCell>, diff --git a/embassy-executor/src/thread.rs b/embassy-executor/src/thread.rs index 2d2c6daa..f977d41e 100644 --- a/embassy-executor/src/thread.rs +++ b/embassy-executor/src/thread.rs @@ -5,13 +5,21 @@ use core::marker::PhantomData; use crate::raw::{OpaqueThreadContext, Pender}; use crate::{raw, Spawner}; -/// TODO -// Name pending +/// Architecture-specific interface for a thread-mode executor. This trait describes what the +/// executor should do when idle, and what data should be passed to its pender. +// TODO: Name pending pub trait ThreadContext: Sized { - /// TODO + /// A pointer-sized piece of data that is passed to the pender function. + /// + /// For example, on multi-core systems, this can be used to store the ID of the core that + /// should be woken up. + #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext; - /// TODO + /// Waits for the executor to be waken. + /// + /// While it is valid for this function can be empty, it is recommended to use a WFE instruction + /// or equivalent to let the CPU sleep. fn wait(&mut self); } @@ -40,11 +48,17 @@ impl ThreadModeExecutor { Self::with_context(C::default()) } - /// Create a new Executor. - pub fn with_context(context: C) -> Self { + /// Create a new Executor using the given thread context. + pub fn with_context(thread_context: C) -> Self { + #[cfg(not(feature = "thread-context"))] + let context = OpaqueThreadContext(()); + + #[cfg(feature = "thread-context")] + let context = thread_context.context(); + Self { - inner: raw::Executor::new(Pender::Thread(context.context())), - context, + inner: raw::Executor::new(Pender::Thread(context)), + context: thread_context, not_send: PhantomData, } } From ec6bd27df6101bc5f77fa4eace0e8963970231ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 08:22:22 +0200 Subject: [PATCH 225/298] Remove thread-context feature --- embassy-executor/Cargo.toml | 7 ++----- embassy-executor/src/arch/cortex_m.rs | 2 -- embassy-executor/src/arch/riscv32.rs | 2 -- embassy-executor/src/arch/std.rs | 3 --- embassy-executor/src/arch/wasm.rs | 3 --- embassy-executor/src/arch/xtensa.rs | 2 -- embassy-executor/src/raw/mod.rs | 7 ------- embassy-executor/src/thread.rs | 13 +++---------- 8 files changed, 5 insertions(+), 34 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 182dd693..ce5e2741 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -31,11 +31,11 @@ features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-th # Architecture _arch = [] # some arch was picked -arch-std = ["_arch", "critical-section/std", "thread-context"] +arch-std = ["_arch", "critical-section/std"] arch-cortex-m = ["_arch", "dep:cortex-m"] arch-xtensa = ["_arch"] arch-riscv32 = ["_arch"] -arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys", "thread-context"] +arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"] # Enable creating a `Pender` from an arbitrary function pointer callback. pender-callback = [] @@ -45,9 +45,6 @@ executor-thread = [] # Enable the interrupt-mode executor (available in Cortex-M only) executor-interrupt = [] -# Pass a context to the thread-mode executor. -thread-context = [] - # Enable nightly-only features nightly = [] diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 6c1300ae..a29e5b23 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -21,9 +21,7 @@ mod thread { pub struct Context; impl ThreadContext for Context { - #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext { - // Enabling thread-context is not incorrect, just wasteful. OpaqueThreadContext(0) } diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 08720400..976e7bcb 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -27,9 +27,7 @@ mod thread { pub struct Context; impl ThreadContext for Context { - #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext { - // Enabling thread-context is not incorrect, just wasteful. OpaqueThreadContext(0) } diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index 2731e275..ceaa5c7a 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -1,9 +1,6 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-std`."); -#[cfg(not(feature = "thread-context"))] -compile_error!("`arch-std` requires `thread-context`."); - #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index e244c0b3..c393722b 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -1,9 +1,6 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-wasm`."); -#[cfg(not(feature = "thread-context"))] -compile_error!("`arch-wasm` requires `thread-context`."); - #[cfg(feature = "executor-thread")] pub use thread::*; #[cfg(feature = "executor-thread")] diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 54c84202..28abf352 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -25,9 +25,7 @@ mod thread { pub struct Context; impl ThreadContext for Context { - #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext { - // Enabling thread-context is not incorrect, just wasteful. OpaqueThreadContext(0) } diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 7fd29db4..7795f1e4 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -292,13 +292,6 @@ impl TaskPool { } /// Context given to the thread-mode executor's pender. -#[cfg(all(feature = "executor-thread", not(feature = "thread-context")))] -#[derive(Clone, Copy)] -#[repr(transparent)] -pub struct OpaqueThreadContext(pub(crate) ()); - -/// Context given to the thread-mode executor's pender. -#[cfg(all(feature = "executor-thread", feature = "thread-context"))] #[repr(transparent)] #[derive(Clone, Copy)] pub struct OpaqueThreadContext(pub(crate) usize); diff --git a/embassy-executor/src/thread.rs b/embassy-executor/src/thread.rs index f977d41e..ef703003 100644 --- a/embassy-executor/src/thread.rs +++ b/embassy-executor/src/thread.rs @@ -13,7 +13,6 @@ pub trait ThreadContext: Sized { /// /// For example, on multi-core systems, this can be used to store the ID of the core that /// should be woken up. - #[cfg(feature = "thread-context")] fn context(&self) -> OpaqueThreadContext; /// Waits for the executor to be waken. @@ -49,16 +48,10 @@ impl ThreadModeExecutor { } /// Create a new Executor using the given thread context. - pub fn with_context(thread_context: C) -> Self { - #[cfg(not(feature = "thread-context"))] - let context = OpaqueThreadContext(()); - - #[cfg(feature = "thread-context")] - let context = thread_context.context(); - + pub fn with_context(context: C) -> Self { Self { - inner: raw::Executor::new(Pender::Thread(context)), - context: thread_context, + inner: raw::Executor::new(Pender::Thread(context.context())), + context, not_send: PhantomData, } } From 454a7cbf4c0eb3a4e651e7da5512ec49ff7d4050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 08:32:26 +0200 Subject: [PATCH 226/298] Remove pender-callback --- embassy-executor/Cargo.toml | 7 ++----- embassy-executor/src/raw/mod.rs | 19 ------------------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index ce5e2741..d190c95a 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -14,7 +14,7 @@ categories = [ [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" -features = ["nightly", "defmt", "pender-callback"] +features = ["nightly", "defmt"] flavors = [ { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] }, { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] }, @@ -25,7 +25,7 @@ flavors = [ [package.metadata.docs.rs] default-target = "thumbv7em-none-eabi" targets = ["thumbv7em-none-eabi"] -features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-thread", "executor-interrupt"] +features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"] [features] @@ -37,9 +37,6 @@ arch-xtensa = ["_arch"] arch-riscv32 = ["_arch"] arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"] -# Enable creating a `Pender` from an arbitrary function pointer callback. -pender-callback = [] - # Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs) executor-thread = [] # Enable the interrupt-mode executor (available in Cortex-M only) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 7795f1e4..81ad1e53 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -317,28 +317,11 @@ pub enum Pender { /// Pender for an interrupt-mode executor. #[cfg(feature = "executor-interrupt")] Interrupt(OpaqueInterruptContext), - - /// Arbitrary, dynamically dispatched pender. - #[cfg(feature = "pender-callback")] - Callback { func: fn(*mut ()), context: *mut () }, } unsafe impl Send for Pender {} unsafe impl Sync for Pender {} -impl Pender { - /// Create a `Pender` that will call an arbitrary function pointer. - /// - /// # Arguments - /// - /// - `func`: The function pointer to call. - /// - `context`: Opaque context pointer, that will be passed to the function pointer. - #[cfg(feature = "pender-callback")] - pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self { - Self::Callback { func, context } - } -} - impl Pender { pub(crate) fn pend(self) { match self { @@ -356,8 +339,6 @@ impl Pender { } unsafe { __interrupt_mode_pender(interrupt) }; } - #[cfg(feature = "pender-callback")] - Pender::Callback { func, context } => func(context), } } } From f6007869bffd3ed4f48e74222dc40d11c7c87ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 08:57:14 +0200 Subject: [PATCH 227/298] Remove the Pender enum --- embassy-executor/src/arch/cortex_m.rs | 103 +++++++++++++++++--------- embassy-executor/src/arch/riscv32.rs | 10 +-- embassy-executor/src/arch/std.rs | 10 +-- embassy-executor/src/arch/wasm.rs | 6 +- embassy-executor/src/arch/xtensa.rs | 8 +- embassy-executor/src/interrupt.rs | 6 +- embassy-executor/src/raw/mod.rs | 48 ++---------- embassy-executor/src/thread.rs | 8 +- 8 files changed, 98 insertions(+), 101 deletions(-) diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index a29e5b23..7f8a97ef 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -1,28 +1,84 @@ #[cfg(feature = "executor-thread")] pub use thread::*; +use crate::raw::PenderContext; + +#[cfg(feature = "executor-interrupt")] + +/// # Safety +/// +/// `irq` must be a valid interrupt request number +unsafe fn nvic_pend(irq: u16) { + use cortex_m::interrupt::InterruptNumber; + + #[derive(Clone, Copy)] + struct Irq(u16); + unsafe impl InterruptNumber for Irq { + fn number(self) -> u16 { + self.0 + } + } + + let irq = Irq(irq); + + // STIR is faster, but is only available in v7 and higher. + #[cfg(not(armv6m))] + { + let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) }; + nvic.request(irq); + } + + #[cfg(armv6m)] + cortex_m::peripheral::NVIC::pend(irq); +} + +#[cfg(all(feature = "executor-thread", feature = "executor-interrupt"))] +#[export_name = "__pender"] +fn __pender(context: PenderContext) { + unsafe { + // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt + // request number given to `InterruptExecutor::start`. + if context as usize == usize::MAX { + core::arch::asm!("sev") + } else { + nvic_pend(context as u16) + } + } +} + +#[cfg(all(feature = "executor-thread", not(feature = "executor-interrupt")))] +#[export_name = "__pender"] +fn __pender(_context: PenderContext) { + unsafe { core::arch::asm!("sev") } +} + +#[cfg(all(not(feature = "executor-thread"), feature = "executor-interrupt"))] +#[export_name = "__pender"] +fn __pender(context: PenderContext) { + unsafe { + // Safety: `context` is the same value we passed to `InterruptExecutor::start`, which must + // be a valid interrupt request number. + nvic_pend(context as u16) + } +} + #[cfg(feature = "executor-thread")] mod thread { #[cfg(feature = "nightly")] pub use embassy_macros::main_cortex_m as main; - use crate::raw::OpaqueThreadContext; + use crate::raw::PenderContext; use crate::thread::ThreadContext; - #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_context: OpaqueThreadContext) { - unsafe { core::arch::asm!("sev") } - } - /// TODO // Name pending #[derive(Default)] // Default enables Executor::new pub struct Context; impl ThreadContext for Context { - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(0) + fn context(&self) -> PenderContext { + usize::MAX } fn wait(&mut self) { @@ -35,7 +91,6 @@ mod thread { pub type Executor = crate::thread::ThreadModeExecutor; } -// None of this has to be public, I guess? #[cfg(feature = "executor-interrupt")] pub use interrupt::*; #[cfg(feature = "executor-interrupt")] @@ -44,23 +99,14 @@ mod interrupt { use cortex_m::peripheral::NVIC; use crate::interrupt::InterruptContext; - use crate::raw::OpaqueInterruptContext; - - #[derive(Clone, Copy)] - struct CortexMInterruptContext(u16); - - unsafe impl cortex_m::interrupt::InterruptNumber for CortexMInterruptContext { - fn number(self) -> u16 { - self.0 - } - } + use crate::raw::PenderContext; impl InterruptContext for T where T: InterruptNumber, { - fn context(&self) -> OpaqueInterruptContext { - OpaqueInterruptContext(self.number() as usize) + fn context(&self) -> PenderContext { + self.number() as usize } fn enable(&self) { @@ -68,21 +114,6 @@ mod interrupt { } } - #[export_name = "__interrupt_mode_pender"] - fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext) { - let interrupt = CortexMInterruptContext(unsafe { core::mem::transmute::<_, usize>(interrupt) as u16 }); - - // STIR is faster, but is only available in v7 and higher. - #[cfg(not(armv6m))] - { - let mut nvic: NVIC = unsafe { core::mem::transmute(()) }; - nvic.request(interrupt); - } - - #[cfg(armv6m)] - NVIC::pend(interrupt); - } - /// TODO // Type alias for backwards compatibility pub type InterruptExecutor = crate::interrupt::InterruptModeExecutor; diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 976e7bcb..886056e8 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -10,14 +10,14 @@ mod thread { #[cfg(feature = "nightly")] pub use embassy_macros::main_riscv as main; - use crate::raw::OpaqueThreadContext; + use crate::raw::PenderContext; use crate::thread::ThreadContext; /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_context: OpaqueThreadContext) { + #[export_name = "__pender"] + fn __thread_mode_pender(_context: PenderContext) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } @@ -27,8 +27,8 @@ mod thread { pub struct Context; impl ThreadContext for Context { - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(0) + fn context(&self) -> PenderContext { + 0 } fn wait(&mut self) { diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index ceaa5c7a..d2a069d1 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -10,7 +10,7 @@ mod thread { #[cfg(feature = "nightly")] pub use embassy_macros::main_std as main; - use crate::raw::OpaqueThreadContext; + use crate::raw::PenderContext; use crate::thread::ThreadContext; /// TODO @@ -28,8 +28,8 @@ mod thread { } impl ThreadContext for Context { - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(self.signaler as *const _ as usize) + fn context(&self) -> PenderContext { + self.signaler as *const _ as usize } fn wait(&mut self) { @@ -37,8 +37,8 @@ mod thread { } } - #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(context: OpaqueThreadContext) { + #[export_name = "__pender"] + fn __pender(context: PenderContext) { let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; signaler.signal() } diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index c393722b..634f48d1 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -14,11 +14,11 @@ mod thread { use wasm_bindgen::prelude::*; use crate::raw::util::UninitCell; - use crate::raw::{OpaqueThreadContext, Pender}; + use crate::raw::PenderContext; use crate::{raw, Spawner}; #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(context: OpaqueThreadContext) { + fn __thread_mode_pender(context: PenderContext) { let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) }; let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() }); } @@ -49,7 +49,7 @@ mod thread { pub fn new() -> Self { let ctx = &*Box::leak(Box::new(WasmContext::new())); Self { - inner: raw::Executor::new(Pender::Thread(OpaqueThreadContext(ctx as *const _ as usize))), + inner: raw::Executor::new(ctx as *const _ as usize), ctx, not_send: PhantomData, } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 28abf352..3986c6c1 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -8,14 +8,14 @@ mod thread { use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; - use crate::raw::OpaqueThreadContext; + use crate::raw::PenderContext; use crate::thread::ThreadContext; /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_context: OpaqueThreadContext) { + fn __thread_mode_pender(_context: PenderContext) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } @@ -25,8 +25,8 @@ mod thread { pub struct Context; impl ThreadContext for Context { - fn context(&self) -> OpaqueThreadContext { - OpaqueThreadContext(0) + fn context(&self) -> PenderContext { + 0 } fn wait(&mut self) { diff --git a/embassy-executor/src/interrupt.rs b/embassy-executor/src/interrupt.rs index 6f310651..28a1cd52 100644 --- a/embassy-executor/src/interrupt.rs +++ b/embassy-executor/src/interrupt.rs @@ -5,7 +5,7 @@ use core::mem::MaybeUninit; use atomic_polyfill::{AtomicBool, Ordering}; -use crate::raw::{self, OpaqueInterruptContext, Pender}; +use crate::raw::{self, PenderContext}; /// Architecture-specific interface for an interrupt-mode executor. This trait describes what data /// should be passed to the [`InterruptExecutor`]'s pender, and how to enable the interrupt that @@ -15,7 +15,7 @@ pub trait InterruptContext { /// A pointer-sized piece of data that is passed to the pender function. /// /// Usually, the context contains the interrupt that should be used to wake the executor. - fn context(&self) -> OpaqueInterruptContext; + fn context(&self) -> PenderContext; /// Enabled the interrupt request. fn enable(&self); @@ -104,7 +104,7 @@ impl InterruptModeExecutor { unsafe { (&mut *self.executor.get()) .as_mut_ptr() - .write(raw::Executor::new(Pender::Interrupt(irq.context()))) + .write(raw::Executor::new(irq.context())) } let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 81ad1e53..a0a940e2 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -292,54 +292,20 @@ impl TaskPool { } /// Context given to the thread-mode executor's pender. -#[repr(transparent)] -#[derive(Clone, Copy)] -pub struct OpaqueThreadContext(pub(crate) usize); +pub type PenderContext = usize; -/// Context given to the interrupt-mode executor's pender. #[derive(Clone, Copy)] -#[repr(transparent)] -pub struct OpaqueInterruptContext(pub(crate) usize); - -/// Platform/architecture-specific action executed when an executor has pending work. -/// -/// When a task within an executor is woken, the `Pender` is called. This does a -/// platform/architecture-specific action to signal there is pending work in the executor. -/// When this happens, you must arrange for [`Executor::poll`] to be called. -/// -/// You can think of it as a waker, but for the whole executor. -#[derive(Clone, Copy)] -pub enum Pender { - /// Pender for a thread-mode executor. - #[cfg(feature = "executor-thread")] - Thread(OpaqueThreadContext), - - /// Pender for an interrupt-mode executor. - #[cfg(feature = "executor-interrupt")] - Interrupt(OpaqueInterruptContext), -} +pub(crate) struct Pender(PenderContext); unsafe impl Send for Pender {} unsafe impl Sync for Pender {} impl Pender { pub(crate) fn pend(self) { - match self { - #[cfg(feature = "executor-thread")] - Pender::Thread(core_id) => { - extern "Rust" { - fn __thread_mode_pender(core_id: OpaqueThreadContext); - } - unsafe { __thread_mode_pender(core_id) }; - } - #[cfg(feature = "executor-interrupt")] - Pender::Interrupt(interrupt) => { - extern "Rust" { - fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext); - } - unsafe { __interrupt_mode_pender(interrupt) }; - } + extern "Rust" { + fn __pender(context: PenderContext); } + unsafe { __pender(self.0) }; } } @@ -499,9 +465,9 @@ impl Executor { /// When the executor has work to do, it will call the [`Pender`]. /// /// See [`Executor`] docs for details on `Pender`. - pub fn new(pender: Pender) -> Self { + pub fn new(context: PenderContext) -> Self { Self { - inner: SyncExecutor::new(pender), + inner: SyncExecutor::new(Pender(context)), _not_sync: PhantomData, } } diff --git a/embassy-executor/src/thread.rs b/embassy-executor/src/thread.rs index ef703003..8ff4071d 100644 --- a/embassy-executor/src/thread.rs +++ b/embassy-executor/src/thread.rs @@ -2,8 +2,8 @@ use core::marker::PhantomData; -use crate::raw::{OpaqueThreadContext, Pender}; -use crate::{raw, Spawner}; +use crate::raw::{self, PenderContext}; +use crate::Spawner; /// Architecture-specific interface for a thread-mode executor. This trait describes what the /// executor should do when idle, and what data should be passed to its pender. @@ -13,7 +13,7 @@ pub trait ThreadContext: Sized { /// /// For example, on multi-core systems, this can be used to store the ID of the core that /// should be woken up. - fn context(&self) -> OpaqueThreadContext; + fn context(&self) -> PenderContext; /// Waits for the executor to be waken. /// @@ -50,7 +50,7 @@ impl ThreadModeExecutor { /// Create a new Executor using the given thread context. pub fn with_context(context: C) -> Self { Self { - inner: raw::Executor::new(Pender::Thread(context.context())), + inner: raw::Executor::new(context.context()), context, not_send: PhantomData, } From 4c4b12c307bf77516299eb73f9da00ef777b9814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 15:16:40 +0200 Subject: [PATCH 228/298] Make PenderContext opaque --- embassy-executor/src/arch/cortex_m.rs | 6 ++++-- embassy-executor/src/arch/riscv32.rs | 2 +- embassy-executor/src/arch/std.rs | 2 +- embassy-executor/src/arch/wasm.rs | 2 +- embassy-executor/src/arch/xtensa.rs | 2 +- embassy-executor/src/raw/mod.rs | 25 ++++++++++++++++++++++--- 6 files changed, 30 insertions(+), 9 deletions(-) diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 7f8a97ef..439db0fc 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -36,6 +36,7 @@ unsafe fn nvic_pend(irq: u16) { #[export_name = "__pender"] fn __pender(context: PenderContext) { unsafe { + let context: usize = core::mem::transmute(context); // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt // request number given to `InterruptExecutor::start`. if context as usize == usize::MAX { @@ -56,6 +57,7 @@ fn __pender(_context: PenderContext) { #[export_name = "__pender"] fn __pender(context: PenderContext) { unsafe { + let context: usize = core::mem::transmute(context); // Safety: `context` is the same value we passed to `InterruptExecutor::start`, which must // be a valid interrupt request number. nvic_pend(context as u16) @@ -78,7 +80,7 @@ mod thread { impl ThreadContext for Context { fn context(&self) -> PenderContext { - usize::MAX + unsafe { core::mem::transmute(usize::MAX) } } fn wait(&mut self) { @@ -106,7 +108,7 @@ mod interrupt { T: InterruptNumber, { fn context(&self) -> PenderContext { - self.number() as usize + unsafe { core::mem::transmute(self.number() as usize) } } fn enable(&self) { diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 886056e8..f76a4bcf 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -28,7 +28,7 @@ mod thread { impl ThreadContext for Context { fn context(&self) -> PenderContext { - 0 + unsafe { core::mem::transmute(0) } } fn wait(&mut self) { diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index d2a069d1..d55de118 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -29,7 +29,7 @@ mod thread { impl ThreadContext for Context { fn context(&self) -> PenderContext { - self.signaler as *const _ as usize + unsafe { core::mem::transmute(self.signaler) } } fn wait(&mut self) { diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 634f48d1..452c3e39 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -49,7 +49,7 @@ mod thread { pub fn new() -> Self { let ctx = &*Box::leak(Box::new(WasmContext::new())); Self { - inner: raw::Executor::new(ctx as *const _ as usize), + inner: raw::Executor::new(unsafe { core::mem::transmute(ctx) }), ctx, not_send: PhantomData, } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 3986c6c1..1aea9f23 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -26,7 +26,7 @@ mod thread { impl ThreadContext for Context { fn context(&self) -> PenderContext { - 0 + unsafe { core::mem::transmute(0) } } fn wait(&mut self) { diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index a0a940e2..4a6e4553 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -292,10 +292,29 @@ impl TaskPool { } /// Context given to the thread-mode executor's pender. -pub type PenderContext = usize; - +#[repr(transparent)] #[derive(Clone, Copy)] -pub(crate) struct Pender(PenderContext); +pub struct PenderContext(usize); + +/// Platform/architecture-specific action executed when an executor has pending work. +/// +/// When a task within an executor is woken, the `Pender` is called. This does a +/// platform/architecture-specific action to signal there is pending work in the executor. +/// When this happens, you must arrange for [`Executor::poll`] to be called. +/// +/// You can think of it as a waker, but for the whole executor. +/// +/// Platform/architecture implementations must provide a function that can be referred to as: +/// +/// ```rust +/// use embassy_executor::raw::PenderContext; +/// +/// extern "Rust" { +/// fn __pender(context: PenderContext); +/// } +/// ``` +#[derive(Clone, Copy)] +pub struct Pender(PenderContext); unsafe impl Send for Pender {} unsafe impl Sync for Pender {} From 986a63ebb8611a4dc7c6b14e03146286942ec8e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 15:35:22 +0200 Subject: [PATCH 229/298] Remove the non-specific thread-mode executor --- embassy-executor/src/arch/cortex_m.rs | 296 ++++++++++++++++++-------- embassy-executor/src/arch/riscv32.rs | 92 +++++--- embassy-executor/src/arch/std.rs | 70 +++--- embassy-executor/src/arch/xtensa.rs | 94 +++++--- embassy-executor/src/interrupt.rs | 2 +- embassy-executor/src/lib.rs | 4 +- embassy-executor/src/thread.rs | 87 -------- 7 files changed, 369 insertions(+), 276 deletions(-) delete mode 100644 embassy-executor/src/thread.rs diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 439db0fc..2ed70dd1 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -1,122 +1,236 @@ -#[cfg(feature = "executor-thread")] -pub use thread::*; +const THREAD_PENDER: usize = usize::MAX; -use crate::raw::PenderContext; - -#[cfg(feature = "executor-interrupt")] - -/// # Safety -/// -/// `irq` must be a valid interrupt request number -unsafe fn nvic_pend(irq: u16) { - use cortex_m::interrupt::InterruptNumber; - - #[derive(Clone, Copy)] - struct Irq(u16); - unsafe impl InterruptNumber for Irq { - fn number(self) -> u16 { - self.0 - } - } - - let irq = Irq(irq); - - // STIR is faster, but is only available in v7 and higher. - #[cfg(not(armv6m))] - { - let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) }; - nvic.request(irq); - } - - #[cfg(armv6m)] - cortex_m::peripheral::NVIC::pend(irq); -} - -#[cfg(all(feature = "executor-thread", feature = "executor-interrupt"))] #[export_name = "__pender"] -fn __pender(context: PenderContext) { +#[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] +fn __pender(context: crate::raw::PenderContext) { unsafe { - let context: usize = core::mem::transmute(context); // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt // request number given to `InterruptExecutor::start`. - if context as usize == usize::MAX { - core::arch::asm!("sev") - } else { - nvic_pend(context as u16) + + let context: usize = core::mem::transmute(context); + + #[cfg(feature = "executor-thread")] + if context == THREAD_PENDER { + core::arch::asm!("sev"); + return; + } + + #[cfg(feature = "executor-interrupt")] + { + use cortex_m::interrupt::InterruptNumber; + use cortex_m::peripheral::NVIC; + + #[derive(Clone, Copy)] + struct Irq(u16); + unsafe impl InterruptNumber for Irq { + fn number(self) -> u16 { + self.0 + } + } + + let irq = Irq(context as u16); + + // STIR is faster, but is only available in v7 and higher. + #[cfg(not(armv6m))] + { + let mut nvic: NVIC = core::mem::transmute(()); + nvic.request(irq); + } + + #[cfg(armv6m)] + NVIC::pend(irq); } } } -#[cfg(all(feature = "executor-thread", not(feature = "executor-interrupt")))] -#[export_name = "__pender"] -fn __pender(_context: PenderContext) { - unsafe { core::arch::asm!("sev") } -} - -#[cfg(all(not(feature = "executor-thread"), feature = "executor-interrupt"))] -#[export_name = "__pender"] -fn __pender(context: PenderContext) { - unsafe { - let context: usize = core::mem::transmute(context); - // Safety: `context` is the same value we passed to `InterruptExecutor::start`, which must - // be a valid interrupt request number. - nvic_pend(context as u16) - } -} - +#[cfg(feature = "executor-thread")] +pub use thread::*; #[cfg(feature = "executor-thread")] mod thread { + use core::arch::asm; + use core::marker::PhantomData; #[cfg(feature = "nightly")] pub use embassy_macros::main_cortex_m as main; - use crate::raw::PenderContext; - use crate::thread::ThreadContext; + use crate::arch::THREAD_PENDER; + use crate::{raw, Spawner}; - /// TODO - // Name pending - #[derive(Default)] // Default enables Executor::new - pub struct Context; - - impl ThreadContext for Context { - fn context(&self) -> PenderContext { - unsafe { core::mem::transmute(usize::MAX) } - } - - fn wait(&mut self) { - unsafe { core::arch::asm!("wfe") } - } + /// Thread mode executor, using WFE/SEV. + /// + /// This is the simplest and most common kind of executor. It runs on + /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction + /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction + /// is executed, to make the `WFE` exit from sleep and poll the task. + /// + /// This executor allows for ultra low power consumption for chips where `WFE` + /// triggers low-power sleep without extra steps. If your chip requires extra steps, + /// you may use [`raw::Executor`] directly to program custom behavior. + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, } - /// TODO - // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(unsafe { core::mem::transmute(THREAD_PENDER) }), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + asm!("wfe"); + }; + } + } + } } #[cfg(feature = "executor-interrupt")] pub use interrupt::*; #[cfg(feature = "executor-interrupt")] mod interrupt { + use core::cell::UnsafeCell; + use core::mem::MaybeUninit; + + use atomic_polyfill::{AtomicBool, Ordering}; use cortex_m::interrupt::InterruptNumber; use cortex_m::peripheral::NVIC; - use crate::interrupt::InterruptContext; - use crate::raw::PenderContext; + use crate::raw; - impl InterruptContext for T - where - T: InterruptNumber, - { - fn context(&self) -> PenderContext { - unsafe { core::mem::transmute(self.number() as usize) } - } - - fn enable(&self) { - unsafe { NVIC::unmask(*self) } - } + /// Interrupt mode executor. + /// + /// This executor runs tasks in interrupt mode. The interrupt handler is set up + /// to poll tasks, and when a task is woken the interrupt is pended from software. + /// + /// This allows running async tasks at a priority higher than thread mode. One + /// use case is to leave thread mode free for non-async tasks. Another use case is + /// to run multiple executors: one in thread mode for low priority tasks and another in + /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower + /// priority ones. + /// + /// It is even possible to run multiple interrupt mode executors at different priorities, + /// by assigning different priorities to the interrupts. For an example on how to do this, + /// See the 'multiprio' example for 'embassy-nrf'. + /// + /// To use it, you have to pick an interrupt that won't be used by the hardware. + /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). + /// If this is not the case, you may use an interrupt from any unused peripheral. + /// + /// It is somewhat more complex to use, it's recommended to use the thread-mode + /// [`Executor`] instead, if it works for your use case. + pub struct InterruptExecutor { + started: AtomicBool, + executor: UnsafeCell>, } - /// TODO - // Type alias for backwards compatibility - pub type InterruptExecutor = crate::interrupt::InterruptModeExecutor; + unsafe impl Send for InterruptExecutor {} + unsafe impl Sync for InterruptExecutor {} + + impl InterruptExecutor { + /// Create a new, not started `InterruptExecutor`. + #[inline] + pub const fn new() -> Self { + Self { + started: AtomicBool::new(false), + executor: UnsafeCell::new(MaybeUninit::uninit()), + } + } + + /// Executor interrupt callback. + /// + /// # Safety + /// + /// You MUST call this from the interrupt handler, and from nowhere else. + pub unsafe fn on_interrupt(&'static self) { + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.poll(); + } + + /// Start the executor. + /// + /// This initializes the executor, enables the interrupt, and returns. + /// The executor keeps running in the background through the interrupt. + /// + /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] + /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a + /// different "thread" (the interrupt), so spawning tasks on it is effectively + /// sending them. + /// + /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from + /// a task running in it. + /// + /// # Interrupt requirements + /// + /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). + /// + /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. + /// + /// You must set the interrupt priority before calling this method. You MUST NOT + /// do it after. + /// + pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner { + if self + .started + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_err() + { + panic!("InterruptExecutor::start() called multiple times on the same executor."); + } + + unsafe { + let context = core::mem::transmute(irq.number() as usize); + (&mut *self.executor.get()) + .as_mut_ptr() + .write(raw::Executor::new(context)) + } + + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + + unsafe { NVIC::unmask(irq) } + + executor.spawner().make_send() + } + + /// Get a SendSpawner for this executor + /// + /// This returns a [`SendSpawner`] you can use to spawn tasks on this + /// executor. + /// + /// This MUST only be called on an executor that has already been spawned. + /// The function will panic otherwise. + pub fn spawner(&'static self) -> crate::SendSpawner { + if !self.started.load(Ordering::Acquire) { + panic!("InterruptExecutor::spawner() called on uninitialized executor."); + } + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.spawner().make_send() + } + } } diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index f76a4bcf..551d7527 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -5,53 +5,77 @@ compile_error!("`executor-interrupt` is not supported with `arch-riscv32`."); pub use thread::*; #[cfg(feature = "executor-thread")] mod thread { + use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; #[cfg(feature = "nightly")] pub use embassy_macros::main_riscv as main; - use crate::raw::PenderContext; - use crate::thread::ThreadContext; + use crate::{raw, Spawner}; /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__pender"] - fn __thread_mode_pender(_context: PenderContext) { + fn __thread_mode_pender(_context: crate::raw::PenderContext) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } - /// TODO - // Name pending - #[derive(Default)] // Default enables Executor::new - pub struct Context; - - impl ThreadContext for Context { - fn context(&self) -> PenderContext { - unsafe { core::mem::transmute(0) } - } - - fn wait(&mut self) { - // We do not care about race conditions between the load and store operations, - // interrupts will only set this value to true. - critical_section::with(|_| { - // if there is work to do, loop back to polling - // TODO can we relax this? - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); - } - // if not, wait for interrupt - else { - unsafe { - core::arch::asm!("wfi"); - } - } - }); - // if an interrupt occurred while waiting, it will be serviced here - } + /// RISCV32 Executor + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, } - /// TODO - // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(unsafe { core::mem::transmute(0) }), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + // we do not care about race conditions between the load and store operations, interrupts + //will only set this value to true. + critical_section::with(|_| { + // if there is work to do, loop back to polling + // TODO can we relax this? + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + } + // if not, wait for interrupt + else { + core::arch::asm!("wfi"); + } + }); + // if an interrupt occurred while waiting, it will be serviced here + } + } + } + } } diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index d55de118..f490084d 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -5,42 +5,64 @@ compile_error!("`executor-interrupt` is not supported with `arch-std`."); pub use thread::*; #[cfg(feature = "executor-thread")] mod thread { + use std::marker::PhantomData; use std::sync::{Condvar, Mutex}; #[cfg(feature = "nightly")] pub use embassy_macros::main_std as main; - use crate::raw::PenderContext; - use crate::thread::ThreadContext; + use crate::{raw, Spawner}; - /// TODO - // Name pending - pub struct Context { + #[export_name = "__pender"] + fn __pender(context: crate::raw::PenderContext) { + let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; + signaler.signal() + } + + /// Single-threaded std-based executor. + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, signaler: &'static Signaler, } - impl Default for Context { - fn default() -> Self { + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + let signaler = &*Box::leak(Box::new(Signaler::new())); Self { - signaler: &*Box::leak(Box::new(Signaler::new())), + inner: raw::Executor::new(unsafe { std::mem::transmute(signaler) }), + not_send: PhantomData, + signaler, } } - } - impl ThreadContext for Context { - fn context(&self) -> PenderContext { - unsafe { core::mem::transmute(self.signaler) } + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { self.inner.poll() }; + self.signaler.wait() + } } - - fn wait(&mut self) { - self.signaler.wait() - } - } - - #[export_name = "__pender"] - fn __pender(context: PenderContext) { - let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; - signaler.signal() } struct Signaler { @@ -70,8 +92,4 @@ mod thread { self.condvar.notify_one(); } } - - /// TODO - // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 1aea9f23..8665a9cb 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -8,56 +8,80 @@ mod thread { use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; - use crate::raw::PenderContext; - use crate::thread::ThreadContext; + use crate::{raw, Spawner}; /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_context: PenderContext) { + fn __thread_mode_pender(_context: crate::raw::PenderContext) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } - /// TODO - // Name pending - #[derive(Default)] // Default enables Executor::new - pub struct Context; + /// Xtensa Executor + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + } - impl ThreadContext for Context { - fn context(&self) -> PenderContext { - unsafe { core::mem::transmute(0) } + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(unsafe { core::mem::transmute(0) }), + not_send: PhantomData, + } } - fn wait(&mut self) { - unsafe { - // Manual critical section implementation that only masks interrupts handlers. - // We must not acquire the cross-core on dual-core systems because that would - // prevent the other core from doing useful work while this core is sleeping. - let token: critical_section::RawRestoreState; - core::arch::asm!("rsil {0}, 5", out(reg) token); + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); - // we do not care about race conditions between the load and store operations, - // interrupts will only set this value to true. - // if there is work to do, loop back to polling - if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { - SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + loop { + unsafe { + self.inner.poll(); - core::arch::asm!( - "wsr.ps {0}", - "rsync", in(reg) token) - } else { - // waiti sets the PS.INTLEVEL when slipping into sleep - // because critical sections in Xtensa are implemented via increasing - // PS.INTLEVEL the critical section ends here - // take care not add code after `waiti` if it needs to be inside the CS - core::arch::asm!("waiti 0"); // critical section ends here + // Manual critical section implementation that only masks interrupts handlers. + // We must not acquire the cross-core on dual-core systems because that would + // prevent the other core from doing useful work while this core is sleeping. + let token: critical_section::RawRestoreState; + core::arch::asm!("rsil {0}, 5", out(reg) token); + + // we do not care about race conditions between the load and store operations, interrupts + // will only set this value to true. + // if there is work to do, loop back to polling + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + + core::arch::asm!( + "wsr.ps {0}", + "rsync", in(reg) token) + } else { + // waiti sets the PS.INTLEVEL when slipping into sleep + // because critical sections in Xtensa are implemented via increasing + // PS.INTLEVEL the critical section ends here + // take care not add code after `waiti` if it needs to be inside the CS + core::arch::asm!("waiti 0"); // critical section ends here + } } } } } - - /// TODO - // Type alias for backwards compatibility - pub type Executor = crate::thread::ThreadModeExecutor; } diff --git a/embassy-executor/src/interrupt.rs b/embassy-executor/src/interrupt.rs index 28a1cd52..b68754ab 100644 --- a/embassy-executor/src/interrupt.rs +++ b/embassy-executor/src/interrupt.rs @@ -41,7 +41,7 @@ pub trait InterruptContext { /// If this is not the case, you may use an interrupt from any unused peripheral. /// /// It is somewhat more complex to use, it's recommended to use the -/// [`crate::thread::ThreadModeExecutor`] instead, if it works for your use case. +/// thread-mode executor instead, if it works for your use case. pub struct InterruptModeExecutor { started: AtomicBool, executor: UnsafeCell>, diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index ca67c948..3be32d9c 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -39,8 +39,8 @@ pub mod raw; #[cfg(feature = "executor-interrupt")] pub mod interrupt; -#[cfg(feature = "executor-thread")] -pub mod thread; +#[cfg(feature = "executor-interrupt")] +pub use interrupt::*; mod spawner; pub use spawner::*; diff --git a/embassy-executor/src/thread.rs b/embassy-executor/src/thread.rs deleted file mode 100644 index 8ff4071d..00000000 --- a/embassy-executor/src/thread.rs +++ /dev/null @@ -1,87 +0,0 @@ -//! Thread-mode executor. - -use core::marker::PhantomData; - -use crate::raw::{self, PenderContext}; -use crate::Spawner; - -/// Architecture-specific interface for a thread-mode executor. This trait describes what the -/// executor should do when idle, and what data should be passed to its pender. -// TODO: Name pending -pub trait ThreadContext: Sized { - /// A pointer-sized piece of data that is passed to the pender function. - /// - /// For example, on multi-core systems, this can be used to store the ID of the core that - /// should be woken up. - fn context(&self) -> PenderContext; - - /// Waits for the executor to be waken. - /// - /// While it is valid for this function can be empty, it is recommended to use a WFE instruction - /// or equivalent to let the CPU sleep. - fn wait(&mut self); -} - -/// Thread mode executor, using WFE/SEV. -/// -/// This is the simplest and most common kind of executor. It runs on -/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction -/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction -/// is executed, to make the `WFE` exit from sleep and poll the task. -/// -/// This executor allows for ultra low power consumption for chips where `WFE` -/// triggers low-power sleep without extra steps. If your chip requires extra steps, -/// you may use [`raw::Executor`] directly to program custom behavior. -pub struct ThreadModeExecutor { - inner: raw::Executor, - context: C, - not_send: PhantomData<*mut ()>, -} - -impl ThreadModeExecutor { - /// Create a new Executor. - pub fn new() -> Self - where - C: Default, - { - Self::with_context(C::default()) - } - - /// Create a new Executor using the given thread context. - pub fn with_context(context: C) -> Self { - Self { - inner: raw::Executor::new(context.context()), - context, - not_send: PhantomData, - } - } - - /// Run the executor. - /// - /// The `init` closure is called with a [`Spawner`] that spawns tasks on - /// this executor. Use it to spawn the initial task(s). After `init` returns, - /// the executor starts running the tasks. - /// - /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), - /// for example by passing it as an argument to the initial tasks. - /// - /// This function requires `&'static mut self`. This means you have to store the - /// Executor instance in a place where it'll live forever and grants you mutable - /// access. There's a few ways to do this: - /// - /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) - /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(self.inner.spawner()); - - loop { - unsafe { - self.inner.poll(); - self.context.wait(); - }; - } - } -} From 3a51e2d9cae6fad2fd903c07634b4a66de59b3bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 15:45:43 +0200 Subject: [PATCH 230/298] Make PenderContext actually pointer-size --- embassy-executor/src/raw/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 4a6e4553..2bbbb132 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -294,7 +294,7 @@ impl TaskPool { /// Context given to the thread-mode executor's pender. #[repr(transparent)] #[derive(Clone, Copy)] -pub struct PenderContext(usize); +pub struct PenderContext(*mut ()); /// Platform/architecture-specific action executed when an executor has pending work. /// From 995434614384bc5c218a16a026ce7c06737ca860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 15:59:47 +0200 Subject: [PATCH 231/298] Remove interrupt executor, remove PenderContext --- embassy-executor/src/arch/cortex_m.rs | 9 +- embassy-executor/src/arch/riscv32.rs | 4 +- embassy-executor/src/arch/std.rs | 6 +- embassy-executor/src/arch/wasm.rs | 7 +- embassy-executor/src/arch/xtensa.rs | 4 +- embassy-executor/src/interrupt.rs | 131 -------------------------- embassy-executor/src/lib.rs | 5 - embassy-executor/src/raw/mod.rs | 17 +--- 8 files changed, 19 insertions(+), 164 deletions(-) delete mode 100644 embassy-executor/src/interrupt.rs diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 2ed70dd1..8fe5644d 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -2,12 +2,12 @@ const THREAD_PENDER: usize = usize::MAX; #[export_name = "__pender"] #[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] -fn __pender(context: crate::raw::PenderContext) { +fn __pender(context: *mut ()) { unsafe { // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt // request number given to `InterruptExecutor::start`. - let context: usize = core::mem::transmute(context); + let context = context as usize; #[cfg(feature = "executor-thread")] if context == THREAD_PENDER { @@ -75,7 +75,7 @@ mod thread { /// Create a new Executor. pub fn new() -> Self { Self { - inner: raw::Executor::new(unsafe { core::mem::transmute(THREAD_PENDER) }), + inner: raw::Executor::new(THREAD_PENDER as *mut ()), not_send: PhantomData, } } @@ -205,10 +205,9 @@ mod interrupt { } unsafe { - let context = core::mem::transmute(irq.number() as usize); (&mut *self.executor.get()) .as_mut_ptr() - .write(raw::Executor::new(context)) + .write(raw::Executor::new(irq.number() as *mut ())) } let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 551d7527..ce78bc25 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -17,7 +17,7 @@ mod thread { static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__pender"] - fn __thread_mode_pender(_context: crate::raw::PenderContext) { + fn __thread_mode_pender(_context: *mut ()) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } @@ -31,7 +31,7 @@ mod thread { /// Create a new Executor. pub fn new() -> Self { Self { - inner: raw::Executor::new(unsafe { core::mem::transmute(0) }), + inner: raw::Executor::new(core::ptr::null_mut()), not_send: PhantomData, } } diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index f490084d..5b2f7e2e 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -14,7 +14,7 @@ mod thread { use crate::{raw, Spawner}; #[export_name = "__pender"] - fn __pender(context: crate::raw::PenderContext) { + fn __pender(context: *mut ()) { let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; signaler.signal() } @@ -29,9 +29,9 @@ mod thread { impl Executor { /// Create a new Executor. pub fn new() -> Self { - let signaler = &*Box::leak(Box::new(Signaler::new())); + let signaler = Box::leak(Box::new(Signaler::new())); Self { - inner: raw::Executor::new(unsafe { std::mem::transmute(signaler) }), + inner: raw::Executor::new(signaler as *mut Signaler as *mut ()), not_send: PhantomData, signaler, } diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 452c3e39..5f9b2e70 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -14,11 +14,10 @@ mod thread { use wasm_bindgen::prelude::*; use crate::raw::util::UninitCell; - use crate::raw::PenderContext; use crate::{raw, Spawner}; #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(context: PenderContext) { + fn __thread_mode_pender(context: *mut ()) { let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) }; let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() }); } @@ -47,9 +46,9 @@ mod thread { impl Executor { /// Create a new Executor. pub fn new() -> Self { - let ctx = &*Box::leak(Box::new(WasmContext::new())); + let ctx = Box::leak(Box::new(WasmContext::new())); Self { - inner: raw::Executor::new(unsafe { core::mem::transmute(ctx) }), + inner: raw::Executor::new(ctx as *mut WasmContext as *mut ()), ctx, not_send: PhantomData, } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 8665a9cb..66b3351c 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -14,7 +14,7 @@ mod thread { static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_context: crate::raw::PenderContext) { + fn __thread_mode_pender(_context: *mut ()) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } @@ -28,7 +28,7 @@ mod thread { /// Create a new Executor. pub fn new() -> Self { Self { - inner: raw::Executor::new(unsafe { core::mem::transmute(0) }), + inner: raw::Executor::new(core::ptr::null_mut()), not_send: PhantomData, } } diff --git a/embassy-executor/src/interrupt.rs b/embassy-executor/src/interrupt.rs deleted file mode 100644 index b68754ab..00000000 --- a/embassy-executor/src/interrupt.rs +++ /dev/null @@ -1,131 +0,0 @@ -//! Interrupt-mode executor. - -use core::cell::UnsafeCell; -use core::mem::MaybeUninit; - -use atomic_polyfill::{AtomicBool, Ordering}; - -use crate::raw::{self, PenderContext}; - -/// Architecture-specific interface for an interrupt-mode executor. This trait describes what data -/// should be passed to the [`InterruptExecutor`]'s pender, and how to enable the interrupt that -/// triggers polling the executor. -// TODO: Name pending -pub trait InterruptContext { - /// A pointer-sized piece of data that is passed to the pender function. - /// - /// Usually, the context contains the interrupt that should be used to wake the executor. - fn context(&self) -> PenderContext; - - /// Enabled the interrupt request. - fn enable(&self); -} - -/// Interrupt mode executor. -/// -/// This executor runs tasks in interrupt mode. The interrupt handler is set up -/// to poll tasks, and when a task is woken the interrupt is pended from software. -/// -/// This allows running async tasks at a priority higher than thread mode. One -/// use case is to leave thread mode free for non-async tasks. Another use case is -/// to run multiple executors: one in thread mode for low priority tasks and another in -/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower -/// priority ones. -/// -/// It is even possible to run multiple interrupt mode executors at different priorities, -/// by assigning different priorities to the interrupts. For an example on how to do this, -/// See the 'multiprio' example for 'embassy-nrf'. -/// -/// To use it, you have to pick an interrupt that won't be used by the hardware. -/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). -/// If this is not the case, you may use an interrupt from any unused peripheral. -/// -/// It is somewhat more complex to use, it's recommended to use the -/// thread-mode executor instead, if it works for your use case. -pub struct InterruptModeExecutor { - started: AtomicBool, - executor: UnsafeCell>, -} - -unsafe impl Send for InterruptModeExecutor {} -unsafe impl Sync for InterruptModeExecutor {} - -impl InterruptModeExecutor { - /// Create a new, not started `InterruptExecutor`. - #[inline] - pub const fn new() -> Self { - Self { - started: AtomicBool::new(false), - executor: UnsafeCell::new(MaybeUninit::uninit()), - } - } - - /// Executor interrupt callback. - /// - /// # Safety - /// - /// You MUST call this from the interrupt handler, and from nowhere else. - pub unsafe fn on_interrupt(&'static self) { - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - executor.poll(); - } - - /// Start the executor. - /// - /// This initializes the executor, enables the interrupt, and returns. - /// The executor keeps running in the background through the interrupt. - /// - /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] - /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a - /// different "thread" (the interrupt), so spawning tasks on it is effectively - /// sending them. - /// - /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from - /// a task running in it. - /// - /// # Interrupt requirements - /// - /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). - /// - /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. - /// - /// You must set the interrupt priority before calling this method. You MUST NOT - /// do it after. - /// - pub fn start(&'static self, irq: impl InterruptContext) -> crate::SendSpawner { - if self - .started - .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) - .is_err() - { - panic!("InterruptExecutor::start() called multiple times on the same executor."); - } - - unsafe { - (&mut *self.executor.get()) - .as_mut_ptr() - .write(raw::Executor::new(irq.context())) - } - - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - - irq.enable(); - - executor.spawner().make_send() - } - - /// Get a SendSpawner for this executor - /// - /// This returns a [`SendSpawner`] you can use to spawn tasks on this - /// executor. - /// - /// This MUST only be called on an executor that has already been spawned. - /// The function will panic otherwise. - pub fn spawner(&'static self) -> crate::SendSpawner { - if !self.started.load(Ordering::Acquire) { - panic!("InterruptExecutor::spawner() called on uninitialized executor."); - } - let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - executor.spawner().make_send() - } -} diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 3be32d9c..3ce687eb 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -37,11 +37,6 @@ pub use arch::*; pub mod raw; -#[cfg(feature = "executor-interrupt")] -pub mod interrupt; -#[cfg(feature = "executor-interrupt")] -pub use interrupt::*; - mod spawner; pub use spawner::*; diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 2bbbb132..aa99b4cf 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -291,11 +291,6 @@ impl TaskPool { } } -/// Context given to the thread-mode executor's pender. -#[repr(transparent)] -#[derive(Clone, Copy)] -pub struct PenderContext(*mut ()); - /// Platform/architecture-specific action executed when an executor has pending work. /// /// When a task within an executor is woken, the `Pender` is called. This does a @@ -306,15 +301,13 @@ pub struct PenderContext(*mut ()); /// /// Platform/architecture implementations must provide a function that can be referred to as: /// -/// ```rust -/// use embassy_executor::raw::PenderContext; -/// +/// ```rust/// /// extern "Rust" { -/// fn __pender(context: PenderContext); +/// fn __pender(context: *mut ()); /// } /// ``` #[derive(Clone, Copy)] -pub struct Pender(PenderContext); +pub struct Pender(*mut ()); unsafe impl Send for Pender {} unsafe impl Sync for Pender {} @@ -322,7 +315,7 @@ unsafe impl Sync for Pender {} impl Pender { pub(crate) fn pend(self) { extern "Rust" { - fn __pender(context: PenderContext); + fn __pender(context: *mut ()); } unsafe { __pender(self.0) }; } @@ -484,7 +477,7 @@ impl Executor { /// When the executor has work to do, it will call the [`Pender`]. /// /// See [`Executor`] docs for details on `Pender`. - pub fn new(context: PenderContext) -> Self { + pub fn new(context: *mut ()) -> Self { Self { inner: SyncExecutor::new(Pender(context)), _not_sync: PhantomData, From fbec797d6403348f5c8c306c1d9a7c396b063f10 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Sun, 13 Aug 2023 23:39:06 +0100 Subject: [PATCH 232/298] embassy-net:tcp:send/recv - Add async versions of smoltcp's `send` and `recv` closure based API. --- embassy-net/src/tcp.rs | 102 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index c903fb24..b4ce4094 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -82,6 +82,17 @@ impl<'a> TcpReader<'a> { pub async fn read(&mut self, buf: &mut [u8]) -> Result { self.io.read(buf).await } + + /// Call `f` with the largest contiguous slice of octets in the receive buffer, + /// and dequeue the amount of elements returned by `f`. + /// + /// If no data is available, it waits until there is at least one byte available. + pub async fn read_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + self.io.read_with(f).await + } } impl<'a> TcpWriter<'a> { @@ -100,6 +111,17 @@ impl<'a> TcpWriter<'a> { pub async fn flush(&mut self) -> Result<(), Error> { self.io.flush().await } + + /// Call `f` with the largest contiguous slice of octets in the transmit buffer, + /// and enqueue the amount of elements returned by `f`. + /// + /// If the socket is not ready to accept data, it waits until it is. + pub async fn write_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + self.io.write_with(f).await + } } impl<'a> TcpSocket<'a> { @@ -121,6 +143,28 @@ impl<'a> TcpSocket<'a> { } } + /// Call `f` with the largest contiguous slice of octets in the transmit buffer, + /// and enqueue the amount of elements returned by `f`. + /// + /// If the socket is not ready to accept data, it waits until it is. + pub async fn write_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + self.io.write_with(f).await + } + + /// Call `f` with the largest contiguous slice of octets in the receive buffer, + /// and dequeue the amount of elements returned by `f`. + /// + /// If no data is available, it waits until there is at least one byte available. + pub async fn read_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + self.io.read_with(f).await + } + /// Split the socket into reader and a writer halves. pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) { (TcpReader { io: self.io }, TcpWriter { io: self.io }) @@ -359,6 +403,64 @@ impl<'d> TcpIo<'d> { .await } + async fn write_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + let mut f = Some(f); + poll_fn(move |cx| { + self.with_mut(|s, _| { + if !s.can_send() { + if s.may_send() { + // socket buffer is full wait until it has atleast one byte free + s.register_send_waker(cx.waker()); + Poll::Pending + } else { + // if we can't transmit because the transmit half of the duplex connection is closed then return an error + Poll::Ready(Err(Error::ConnectionReset)) + } + } else { + Poll::Ready(match s.send(f.take().unwrap()) { + // Connection reset. TODO: this can also be timeouts etc, investigate. + Err(tcp::SendError::InvalidState) => Err(Error::ConnectionReset), + Ok(r) => Ok(r), + }) + } + }) + }) + .await + } + + async fn read_with(&mut self, f: F) -> Result + where + F: FnOnce(&mut [u8]) -> (usize, R), + { + let mut f = Some(f); + poll_fn(move |cx| { + self.with_mut(|s, _| { + if !s.can_recv() { + if s.may_recv() { + // socket buffer is empty wait until it has atleast one byte has arrived + s.register_recv_waker(cx.waker()); + Poll::Pending + } else { + // if we can't receive because the recieve half of the duplex connection is closed then return an error + Poll::Ready(Err(Error::ConnectionReset)) + } + } else { + Poll::Ready(match s.recv(f.take().unwrap()) { + // Connection reset. TODO: this can also be timeouts etc, investigate. + Err(tcp::RecvError::Finished) | Err(tcp::RecvError::InvalidState) => { + Err(Error::ConnectionReset) + } + Ok(r) => Ok(r), + }) + } + }) + }) + .await + } + async fn flush(&mut self) -> Result<(), Error> { poll_fn(move |cx| { self.with_mut(|s, _| { From da4f15d94492667dd33087f95e28747b74b07811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 16:33:49 +0200 Subject: [PATCH 233/298] Fix fn name --- embassy-executor/src/arch/riscv32.rs | 2 +- embassy-executor/src/arch/wasm.rs | 4 ++-- embassy-executor/src/arch/xtensa.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index ce78bc25..40c6877e 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -17,7 +17,7 @@ mod thread { static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); #[export_name = "__pender"] - fn __thread_mode_pender(_context: *mut ()) { + fn __pender(_context: *mut ()) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 5f9b2e70..934fd69e 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -16,8 +16,8 @@ mod thread { use crate::raw::util::UninitCell; use crate::{raw, Spawner}; - #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(context: *mut ()) { + #[export_name = "__pender"] + fn __pender(context: *mut ()) { let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) }; let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() }); } diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 66b3351c..601d8500 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs @@ -13,8 +13,8 @@ mod thread { /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - #[export_name = "__thread_mode_pender"] - fn __thread_mode_pender(_context: *mut ()) { + #[export_name = "__pender"] + fn __pender(_context: *mut ()) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } From 07c36001271ab0a033a08a6535719729efb677c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 16:35:12 +0200 Subject: [PATCH 234/298] Hide Pender --- embassy-executor/src/raw/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index aa99b4cf..06483102 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -301,13 +301,13 @@ impl TaskPool { /// /// Platform/architecture implementations must provide a function that can be referred to as: /// -/// ```rust/// +/// ```rust /// extern "Rust" { /// fn __pender(context: *mut ()); /// } /// ``` #[derive(Clone, Copy)] -pub struct Pender(*mut ()); +pub(crate) struct Pender(*mut ()); unsafe impl Send for Pender {} unsafe impl Sync for Pender {} From e4f3979ec8dd12fef1f44e78733917980045def0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 16:46:02 +0200 Subject: [PATCH 235/298] Don't check context if only thread-mode is enabled --- embassy-executor/src/arch/cortex_m.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 8fe5644d..0806a22a 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -10,7 +10,8 @@ fn __pender(context: *mut ()) { let context = context as usize; #[cfg(feature = "executor-thread")] - if context == THREAD_PENDER { + // Try to make Rust optimize the branching away if we only use thread mode. + if !cfg!(feature = "executor-interrupt") || context == THREAD_PENDER { core::arch::asm!("sev"); return; } From 890f29ccfe129f3205cf835c7131862c579d9349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 14 Aug 2023 17:53:42 +0200 Subject: [PATCH 236/298] Update docs --- embassy-executor/CHANGELOG.md | 4 +++ embassy-executor/src/raw/mod.rs | 49 +++++++++++++++++---------------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 86a47758..e2e7bce3 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Replaced Pender. Implementations now must define an extern function called `__pender`. + ## 0.2.1 - 2023-08-10 - Avoid calling `pend()` when waking expired timers diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 06483102..7caa3302 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -291,21 +291,6 @@ impl TaskPool { } } -/// Platform/architecture-specific action executed when an executor has pending work. -/// -/// When a task within an executor is woken, the `Pender` is called. This does a -/// platform/architecture-specific action to signal there is pending work in the executor. -/// When this happens, you must arrange for [`Executor::poll`] to be called. -/// -/// You can think of it as a waker, but for the whole executor. -/// -/// Platform/architecture implementations must provide a function that can be referred to as: -/// -/// ```rust -/// extern "Rust" { -/// fn __pender(context: *mut ()); -/// } -/// ``` #[derive(Clone, Copy)] pub(crate) struct Pender(*mut ()); @@ -451,15 +436,31 @@ impl SyncExecutor { /// /// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks /// that "want to run"). -/// - You must supply a [`Pender`]. The executor will call it to notify you it has work -/// to do. You must arrange for `poll()` to be called as soon as possible. +/// - You must supply a pender function, as shown below. The executor will call it to notify you +/// it has work to do. You must arrange for `poll()` to be called as soon as possible. +/// - Enabling `arch-xx` features will define a pender function for you. This means that you +/// are limited to using the executors provided to you by the architecture/platform +/// implementation. If you need a different executor, you must not enable `arch-xx` features. /// -/// The [`Pender`] can be called from *any* context: any thread, any interrupt priority +/// The pender can be called from *any* context: any thread, any interrupt priority /// level, etc. It may be called synchronously from any `Executor` method call as well. /// You must deal with this correctly. /// /// In particular, you must NOT call `poll` directly from the pender callback, as this violates /// the requirement for `poll` to not be called reentrantly. +/// +/// The pender function must be exported with the name `__pender` and have the following signature: +/// +/// ```rust +/// #[export_name = "__pender"] +/// fn pender(context: *mut ()) { +/// // schedule `poll()` to be called +/// } +/// ``` +/// +/// The `context` argument is a piece of arbitrary data the executor will pass to the pender. +/// You can set the `context` when calling [`Executor::new()`]. You can use it to, for example, +/// differentiate between executors, or to pass a pointer to a callback that should be called. #[repr(transparent)] pub struct Executor { pub(crate) inner: SyncExecutor, @@ -474,9 +475,9 @@ impl Executor { /// Create a new executor. /// - /// When the executor has work to do, it will call the [`Pender`]. + /// When the executor has work to do, it will call the pender function and pass `context` to it. /// - /// See [`Executor`] docs for details on `Pender`. + /// See [`Executor`] docs for details on the pender. pub fn new(context: *mut ()) -> Self { Self { inner: SyncExecutor::new(Pender(context)), @@ -502,16 +503,16 @@ impl Executor { /// This loops over all tasks that are queued to be polled (i.e. they're /// freshly spawned or they've been woken). Other tasks are not polled. /// - /// You must call `poll` after receiving a call to the [`Pender`]. It is OK - /// to call `poll` even when not requested by the `Pender`, but it wastes + /// You must call `poll` after receiving a call to the pender. It is OK + /// to call `poll` even when not requested by the pender, but it wastes /// energy. /// /// # Safety /// /// You must NOT call `poll` reentrantly on the same executor. /// - /// In particular, note that `poll` may call the `Pender` synchronously. Therefore, you - /// must NOT directly call `poll()` from the `Pender` callback. Instead, the callback has to + /// In particular, note that `poll` may call the pender synchronously. Therefore, you + /// must NOT directly call `poll()` from the pender callback. Instead, the callback has to /// somehow schedule for `poll()` to be called later, at a time you know for sure there's /// no `poll()` already running. pub unsafe fn poll(&'static self) { From 0fd9d7400b47f42d1043347cc2982862de7cffc7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 00:18:23 +0200 Subject: [PATCH 237/298] Build stm32 docs last. --- .github/ci/doc.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 06c6fa00..e410ffa3 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -15,7 +15,6 @@ export BUILDER_COMPRESS=true # which makes rustup very sad rustc --version > /dev/null -docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup @@ -43,4 +42,12 @@ docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/g export KUBECONFIG=/ci/secrets/kubeconfig.yml POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) kubectl cp webroot/crates $POD:/data -kubectl cp webroot/static $POD:/data \ No newline at end of file +kubectl cp webroot/static $POD:/data + +# build and upload stm32 last +# so that it doesn't prevent other crates from getting docs updates when it breaks. +rm -rf webroot +docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup + +POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) +kubectl cp webroot/crates $POD:/data From 2c1402843ae8d4f07db3670bd7153e794a1553ae Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 5 Aug 2023 00:10:23 +0200 Subject: [PATCH 238/298] wip: enc28j60 driver. --- embassy-net-enc28j60/Cargo.toml | 23 + embassy-net-enc28j60/README.md | 1 + embassy-net-enc28j60/src/bank0.rs | 69 +++ embassy-net-enc28j60/src/bank1.rs | 84 ++++ embassy-net-enc28j60/src/bank2.rs | 86 ++++ embassy-net-enc28j60/src/bank3.rs | 53 +++ embassy-net-enc28j60/src/common.rs | 106 +++++ embassy-net-enc28j60/src/fmt.rs | 225 ++++++++++ embassy-net-enc28j60/src/header.rs | 30 ++ embassy-net-enc28j60/src/lib.rs | 693 +++++++++++++++++++++++++++++ embassy-net-enc28j60/src/macros.rs | 89 ++++ embassy-net-enc28j60/src/phy.rs | 36 ++ embassy-net-enc28j60/src/traits.rs | 57 +++ 13 files changed, 1552 insertions(+) create mode 100644 embassy-net-enc28j60/Cargo.toml create mode 100644 embassy-net-enc28j60/README.md create mode 100644 embassy-net-enc28j60/src/bank0.rs create mode 100644 embassy-net-enc28j60/src/bank1.rs create mode 100644 embassy-net-enc28j60/src/bank2.rs create mode 100644 embassy-net-enc28j60/src/bank3.rs create mode 100644 embassy-net-enc28j60/src/common.rs create mode 100644 embassy-net-enc28j60/src/fmt.rs create mode 100644 embassy-net-enc28j60/src/header.rs create mode 100644 embassy-net-enc28j60/src/lib.rs create mode 100644 embassy-net-enc28j60/src/macros.rs create mode 100644 embassy-net-enc28j60/src/phy.rs create mode 100644 embassy-net-enc28j60/src/traits.rs diff --git a/embassy-net-enc28j60/Cargo.toml b/embassy-net-enc28j60/Cargo.toml new file mode 100644 index 00000000..c502ed04 --- /dev/null +++ b/embassy-net-enc28j60/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "embassy-net-enc28j60" +version = "0.1.0" +description = "embassy-net driver for the ENC28J60 ethernet chip" +keywords = ["embedded", "enc28j60", "embassy-net", "embedded-hal-async", "ethernet", "async"] +categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] +license = "MIT OR Apache-2.0" +edition = "2021" + +[dependencies] +embedded-hal = { version = "1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } +embassy-time = { version = "0.1.2", path = "../embassy-time" } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } + +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-enc28j60-v$VERSION/embassy-net-enc28j60/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-enc28j60/src/" +target = "thumbv7em-none-eabi" \ No newline at end of file diff --git a/embassy-net-enc28j60/README.md b/embassy-net-enc28j60/README.md new file mode 100644 index 00000000..e12d240c --- /dev/null +++ b/embassy-net-enc28j60/README.md @@ -0,0 +1 @@ +# `embassy-net-enc28j60` diff --git a/embassy-net-enc28j60/src/bank0.rs b/embassy-net-enc28j60/src/bank0.rs new file mode 100644 index 00000000..1c1b3a7f --- /dev/null +++ b/embassy-net-enc28j60/src/bank0.rs @@ -0,0 +1,69 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + ERDPTL = 0x00, + ERDPTH = 0x01, + EWRPTL = 0x02, + EWRPTH = 0x03, + ETXSTL = 0x04, + ETXSTH = 0x05, + ETXNDL = 0x06, + ETXNDH = 0x07, + ERXSTL = 0x08, + ERXSTH = 0x09, + ERXNDL = 0x0a, + ERXNDH = 0x0b, + ERXRDPTL = 0x0c, + ERXRDPTH = 0x0d, + ERXWRPTL = 0x0e, + ERXWRPTH = 0x0f, + EDMASTL = 0x10, + EDMASTH = 0x11, + EDMANDL = 0x12, + EDMANDH = 0x13, + EDMADSTL = 0x14, + EDMADSTH = 0x15, + EDMACSL = 0x16, + EDMACSH = 0x17, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::ERDPTL => true, + Register::ERDPTH => true, + Register::EWRPTL => true, + Register::EWRPTH => true, + Register::ETXSTL => true, + Register::ETXSTH => true, + Register::ETXNDL => true, + Register::ETXNDH => true, + Register::ERXSTL => true, + Register::ERXSTH => true, + Register::ERXNDL => true, + Register::ERXNDH => true, + Register::ERXRDPTL => true, + Register::ERXRDPTH => true, + Register::ERXWRPTL => true, + Register::ERXWRPTH => true, + Register::EDMASTL => true, + Register::EDMASTH => true, + Register::EDMANDL => true, + Register::EDMANDH => true, + Register::EDMADSTL => true, + Register::EDMADSTH => true, + Register::EDMACSL => true, + Register::EDMACSH => true, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Bank0(self) + } +} diff --git a/embassy-net-enc28j60/src/bank1.rs b/embassy-net-enc28j60/src/bank1.rs new file mode 100644 index 00000000..30560edf --- /dev/null +++ b/embassy-net-enc28j60/src/bank1.rs @@ -0,0 +1,84 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + EHT0 = 0x00, + EHT1 = 0x01, + EHT2 = 0x02, + EHT3 = 0x03, + EHT4 = 0x04, + EHT5 = 0x05, + EHT6 = 0x06, + EHT7 = 0x07, + EPMM0 = 0x08, + EPMM1 = 0x09, + EPMM2 = 0x0a, + EPMM3 = 0x0b, + EPMM4 = 0x0c, + EPMM5 = 0x0d, + EPMM6 = 0x0e, + EPMM7 = 0x0f, + EPMCSL = 0x10, + EPMCSH = 0x11, + EPMOL = 0x14, + EPMOH = 0x15, + ERXFCON = 0x18, + EPKTCNT = 0x19, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::EHT0 => true, + Register::EHT1 => true, + Register::EHT2 => true, + Register::EHT3 => true, + Register::EHT4 => true, + Register::EHT5 => true, + Register::EHT6 => true, + Register::EHT7 => true, + Register::EPMM0 => true, + Register::EPMM1 => true, + Register::EPMM2 => true, + Register::EPMM3 => true, + Register::EPMM4 => true, + Register::EPMM5 => true, + Register::EPMM6 => true, + Register::EPMM7 => true, + Register::EPMCSL => true, + Register::EPMCSH => true, + Register::EPMOL => true, + Register::EPMOH => true, + Register::ERXFCON => true, + Register::EPKTCNT => true, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Bank1(self) + } +} + +register!(ERXFCON, 0b1010_0001, u8, { + #[doc = "Broadcast Filter Enable bit"] + bcen @ 0, + #[doc = "Multicast Filter Enable bit"] + mcen @ 1, + #[doc = "Hash Table Filter Enable bit"] + hten @ 2, + #[doc = "Magic Packetâ„¢ Filter Enable bit"] + mpen @ 3, + #[doc = "Pattern Match Filter Enable bit"] + pmen @ 4, + #[doc = "Post-Filter CRC Check Enable bit"] + crcen @ 5, + #[doc = "AND/OR Filter Select bit"] + andor @ 6, + #[doc = "Unicast Filter Enable bit"] + ucen @ 7, +}); diff --git a/embassy-net-enc28j60/src/bank2.rs b/embassy-net-enc28j60/src/bank2.rs new file mode 100644 index 00000000..74a1d245 --- /dev/null +++ b/embassy-net-enc28j60/src/bank2.rs @@ -0,0 +1,86 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + MACON1 = 0x00, + MACON3 = 0x02, + MACON4 = 0x03, + MABBIPG = 0x04, + MAIPGL = 0x06, + MAIPGH = 0x07, + MACLCON1 = 0x08, + MACLCON2 = 0x09, + MAMXFLL = 0x0a, + MAMXFLH = 0x0b, + MICMD = 0x12, + MIREGADR = 0x14, + MIWRL = 0x16, + MIWRH = 0x17, + MIRDL = 0x18, + MIRDH = 0x19, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::MACON1 => false, + Register::MACON3 => false, + Register::MACON4 => false, + Register::MABBIPG => false, + Register::MAIPGL => false, + Register::MAIPGH => false, + Register::MACLCON1 => false, + Register::MACLCON2 => false, + Register::MAMXFLL => false, + Register::MAMXFLH => false, + Register::MICMD => false, + Register::MIREGADR => false, + Register::MIWRL => false, + Register::MIWRH => false, + Register::MIRDL => false, + Register::MIRDH => false, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Bank2(self) + } +} + +register!(MACON1, 0, u8, { + #[doc = "Enable packets to be received by the MAC"] + marxen @ 0, + #[doc = "Control frames will be discarded after being processed by the MAC"] + passall @ 1, + #[doc = "Inhibit transmissions when pause control frames are received"] + rxpaus @ 2, + #[doc = "Allow the MAC to transmit pause control frames"] + txpaus @ 3, +}); + +register!(MACON3, 0, u8, { + #[doc = "MAC will operate in Full-Duplex mode"] + fuldpx @ 0, + #[doc = "The type/length field of transmitted and received frames will be checked"] + frmlnen @ 1, + #[doc = "Frames bigger than MAMXFL will be aborted when transmitted or received"] + hfrmen @ 2, + #[doc = "No proprietary header is present"] + phdren @ 3, + #[doc = "MAC will append a valid CRC to all frames transmitted regardless of PADCFG bit"] + txcrcen @ 4, + #[doc = "All short frames will be zero-padded to 64 bytes and a valid CRC will then be appended"] + padcfg @ 5..7, +}); + +register!(MICMD, 0, u8, { + #[doc = "MII Read Enable bit"] + miird @ 0, + #[doc = "MII Scan Enable bit"] + miiscan @ 1, +}); diff --git a/embassy-net-enc28j60/src/bank3.rs b/embassy-net-enc28j60/src/bank3.rs new file mode 100644 index 00000000..4f7eb940 --- /dev/null +++ b/embassy-net-enc28j60/src/bank3.rs @@ -0,0 +1,53 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + MAADR5 = 0x00, + MAADR6 = 0x01, + MAADR3 = 0x02, + MAADR4 = 0x03, + MAADR1 = 0x04, + MAADR2 = 0x05, + EBSTSD = 0x06, + EBSTCON = 0x07, + EBSTCSL = 0x08, + EBSTCSH = 0x09, + MISTAT = 0x0a, + EREVID = 0x12, + ECOCON = 0x15, + EFLOCON = 0x17, + EPAUSL = 0x18, + EPAUSH = 0x19, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::MAADR5 => false, + Register::MAADR6 => false, + Register::MAADR3 => false, + Register::MAADR4 => false, + Register::MAADR1 => false, + Register::MAADR2 => false, + Register::EBSTSD => true, + Register::EBSTCON => true, + Register::EBSTCSL => true, + Register::EBSTCSH => true, + Register::MISTAT => false, + Register::EREVID => true, + Register::ECOCON => true, + Register::EFLOCON => true, + Register::EPAUSL => true, + Register::EPAUSH => true, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Bank3(self) + } +} diff --git a/embassy-net-enc28j60/src/common.rs b/embassy-net-enc28j60/src/common.rs new file mode 100644 index 00000000..ef339dd2 --- /dev/null +++ b/embassy-net-enc28j60/src/common.rs @@ -0,0 +1,106 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + ECON1 = 0x1f, + ECON2 = 0x1e, + EIE = 0x1b, + EIR = 0x1c, + ESTAT = 0x1d, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } + + pub(crate) fn is_eth_register(&self) -> bool { + match *self { + Register::ECON1 => true, + Register::ECON2 => true, + Register::EIE => true, + Register::EIR => true, + Register::ESTAT => true, + } + } +} + +impl Into for Register { + fn into(self) -> super::Register { + super::Register::Common(self) + } +} + +register!(EIE, 0, u8, { + #[doc = "Receive Error Interrupt Enable bit"] + rxerie @ 0, + #[doc = "Transmit Error Interrupt Enable bit"] + txerie @ 1, + #[doc = "Transmit Enable bit"] + txie @ 3, + #[doc = "Link Status Change Interrupt Enable bit"] + linkie @ 4, + #[doc = "DMA Interrupt Enable bit"] + dmaie @ 5, + #[doc = "Receive Packet Pending Interrupt Enable bit"] + pktie @ 6, + #[doc = "Global INT Interrupt Enable bit"] + intie @ 7, +}); + +register!(EIR, 0, u8, { + #[doc = "Receive Error Interrupt Flag bit"] + rxerif @ 0, + #[doc = "Transmit Error Interrupt Flag bit"] + txerif @ 1, + #[doc = "Transmit Interrupt Flag bit"] + txif @ 3, + #[doc = "Link Change Interrupt Flag bit"] + linkif @ 4, + #[doc = "DMA Interrupt Flag bit"] + dmaif @ 5, + #[doc = "Receive Packet Pending Interrupt Flag bit"] + pktif @ 6, +}); + +register!(ESTAT, 0, u8, { + #[doc = "Clock Ready bit"] + clkrdy @ 0, + #[doc = "Transmit Abort Error bit"] + txabrt @ 1, + #[doc = "Receive Busy bit"] + rxbusy @ 2, + #[doc = "Late Collision Error bit"] + latecol @ 4, + #[doc = "Ethernet Buffer Error Status bit"] + bufer @ 6, + #[doc = "INT Interrupt Flag bit"] + int @ 7, +}); + +register!(ECON2, 0b1000_0000, u8, { + #[doc = "Voltage Regulator Power Save Enable bit"] + vrps @ 3, + #[doc = "Power Save Enable bit"] + pwrsv @ 5, + #[doc = "Packet Decrement bit"] + pktdec @ 6, + #[doc = "Automatic Buffer Pointer Increment Enable bit"] + autoinc @ 7, +}); + +register!(ECON1, 0, u8, { + #[doc = "Bank Select bits"] + bsel @ 0..1, + #[doc = "Receive Enable bi"] + rxen @ 2, + #[doc = "Transmit Request to Send bit"] + txrts @ 3, + #[doc = "DMA Checksum Enable bit"] + csumen @ 4, + #[doc = "DMA Start and Busy Status bit"] + dmast @ 5, + #[doc = "Receive Logic Reset bit"] + rxrst @ 6, + #[doc = "Transmit Logic Reset bit"] + txrst @ 7, +}); diff --git a/embassy-net-enc28j60/src/fmt.rs b/embassy-net-enc28j60/src/fmt.rs new file mode 100644 index 00000000..06697081 --- /dev/null +++ b/embassy-net-enc28j60/src/fmt.rs @@ -0,0 +1,225 @@ +#![macro_use] +#![allow(unused_macros)] + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +macro_rules! unreachable { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::unreachable!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::unreachable!($($x)*); + } + }; +} + +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +macro_rules! trace { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::trace!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::trace!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! debug { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::debug!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::debug!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! info { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::info!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::info!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! warn { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::warn!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::warn!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! error { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::error!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::error!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} diff --git a/embassy-net-enc28j60/src/header.rs b/embassy-net-enc28j60/src/header.rs new file mode 100644 index 00000000..c2d4e468 --- /dev/null +++ b/embassy-net-enc28j60/src/header.rs @@ -0,0 +1,30 @@ +register!(RxStatus, 0, u32, { + #[doc = "Indicates length of the received frame"] + byte_count @ 0..15, + #[doc = "Indicates a packet over 50,000 bit times occurred or that a packet was dropped since the last receive"] + long_event @ 16, + #[doc = "Indicates that at some time since the last receive, a carrier event was detected"] + carrier_event @ 18, + #[doc = "Indicates that frame CRC field value does not match the CRC calculated by the MAC"] + crc_error @ 20, + #[doc = "Indicates that frame length field value in the packet does not match the actual data byte length and specifies a valid length"] + length_check_error @ 21, + #[doc = "Indicates that frame type/length field was larger than 1500 bytes (type field)"] + length_out_of_range @ 22, + #[doc = "Indicates that at the packet had a valid CRC and no symbol errors"] + received_ok @ 23, + #[doc = "Indicates packet received had a valid Multicast address"] + multicast @ 24, + #[doc = "Indicates packet received had a valid Broadcast address."] + broadcast @ 25, + #[doc = "Indicates that after the end of this packet, an additional 1 to 7 bits were received"] + dribble_nibble @ 26, + #[doc = "Current frame was recognized as a control frame for having a valid type/length designating it as a control frame"] + receive_control_frame @ 27, + #[doc = "Current frame was recognized as a control frame containing a valid pause frame opcode and a valid destination address"] + receive_pause_control_frame @ 28, + #[doc = "Current frame was recognized as a control frame but it contained an unknown opcode"] + receive_unknown_opcode @ 29, + #[doc = "Current frame was recognized as a VLAN tagged frame"] + receive_vlan_type_detected @ 30, +}); diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs new file mode 100644 index 00000000..4f129b6b --- /dev/null +++ b/embassy-net-enc28j60/src/lib.rs @@ -0,0 +1,693 @@ +#![no_std] +#![doc = include_str!("../README.md")] + +// must go first. +mod fmt; + +#[macro_use] +mod macros; +mod bank0; +mod bank1; +mod bank2; +mod bank3; +mod common; +mod header; +mod phy; +mod traits; + +use core::cmp; +use core::convert::TryInto; + +use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium}; +use embassy_time::Duration; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{Operation, SpiDevice}; +use traits::U16Ext; + +// Total buffer size (see section 3.2) +const BUF_SZ: u16 = 8 * 1024; + +// Maximum frame length +const MAX_FRAME_LENGTH: u16 = 1518; // value recommended in the data sheet + +// Size of the Frame check sequence (32-bit CRC) +const CRC_SZ: u16 = 4; + +// define the boundaries of the TX and RX buffers +// to workaround errata #5 we do the opposite of what section 6.1 of the data sheet +// says: we place the RX buffer at address 0 and the TX buffer after it +const RXST: u16 = 0x0000; +const RXND: u16 = 0x19ff; +const TXST: u16 = 0x1a00; +const _TXND: u16 = 0x1fff; + +const MTU: usize = 1514; // 1500 IP + 14 ethernet header + +pub struct Enc28j60 { + mac_addr: [u8; 6], + + spi: S, + rst: Option, + + bank: Bank, + + // address of the next packet in buffer memory + next_packet: u16, +} + +impl Enc28j60 +where + S: SpiDevice, + O: OutputPin, +{ + pub fn new(spi: S, rst: Option, mac_addr: [u8; 6]) -> Self { + let mut res = Self { + mac_addr, + spi, + rst, + + bank: Bank::Bank0, + next_packet: RXST, + }; + res.init(); + res + } + + fn init(&mut self) { + if let Some(rst) = &mut self.rst { + rst.set_low().unwrap(); + embassy_time::block_for(Duration::from_millis(5)); + rst.set_high().unwrap(); + embassy_time::block_for(Duration::from_millis(5)); + } else { + embassy_time::block_for(Duration::from_millis(5)); + self.soft_reset(); + embassy_time::block_for(Duration::from_millis(5)); + } + + debug!( + "enc28j60: erevid {=u8:x}", + self.read_control_register(bank3::Register::EREVID) + ); + debug!("enc28j60: waiting for clk"); + while common::ESTAT(self.read_control_register(common::Register::ESTAT)).clkrdy() == 0 {} + debug!("enc28j60: clk ok"); + + if self.read_control_register(bank3::Register::EREVID) == 0 { + panic!("ErevidIsZero"); + } + + // disable CLKOUT output + self.write_control_register(bank3::Register::ECOCON, 0); + + // RX start + // "It is recommended that the ERXST Pointer be programmed with an even address" + self.write_control_register(bank0::Register::ERXSTL, RXST.low()); + self.write_control_register(bank0::Register::ERXSTH, RXST.high()); + + // RX read pointer + // NOTE Errata #14 so we are using an *odd* address here instead of ERXST + self.write_control_register(bank0::Register::ERXRDPTL, RXND.low()); + self.write_control_register(bank0::Register::ERXRDPTH, RXND.high()); + + // RX end + self.write_control_register(bank0::Register::ERXNDL, RXND.low()); + self.write_control_register(bank0::Register::ERXNDH, RXND.high()); + + // TX start + // "It is recommended that an even address be used for ETXST" + debug_assert_eq!(TXST % 2, 0); + self.write_control_register(bank0::Register::ETXSTL, TXST.low()); + self.write_control_register(bank0::Register::ETXSTH, TXST.high()); + + // TX end is set in `transmit` + + // MAC initialization (see section 6.5) + // 1. Set the MARXEN bit in MACON1 to enable the MAC to receive frames. + self.write_control_register( + bank2::Register::MACON1, + bank2::MACON1::default().marxen(1).passall(0).rxpaus(1).txpaus(1).bits(), + ); + + // 2. Configure the PADCFG, TXCRCEN and FULDPX bits of MACON3. + self.write_control_register( + bank2::Register::MACON3, + bank2::MACON3::default().frmlnen(1).txcrcen(1).padcfg(0b001).bits(), + ); + + // 4. Program the MAMXFL registers with the maximum frame length to be permitted to be + // received or transmitted + self.write_control_register(bank2::Register::MAMXFLL, MAX_FRAME_LENGTH.low()); + self.write_control_register(bank2::Register::MAMXFLH, MAX_FRAME_LENGTH.high()); + + // 5. Configure the Back-to-Back Inter-Packet Gap register, MABBIPG. + // Use recommended value of 0x12 + self.write_control_register(bank2::Register::MABBIPG, 0x12); + + // 6. Configure the Non-Back-to-Back Inter-Packet Gap register low byte, MAIPGL. + // Use recommended value of 0x12 + self.write_control_register(bank2::Register::MAIPGL, 0x12); + self.write_control_register(bank2::Register::MAIPGH, 0x0c); + + // 9. Program the local MAC address into the MAADR1:MAADR6 registers + self.write_control_register(bank3::Register::MAADR1, self.mac_addr[0]); + self.write_control_register(bank3::Register::MAADR2, self.mac_addr[1]); + self.write_control_register(bank3::Register::MAADR3, self.mac_addr[2]); + self.write_control_register(bank3::Register::MAADR4, self.mac_addr[3]); + self.write_control_register(bank3::Register::MAADR5, self.mac_addr[4]); + self.write_control_register(bank3::Register::MAADR6, self.mac_addr[5]); + + // Set the PHCON2.HDLDIS bit to prevent automatic loopback of the data which is transmitted + self.write_phy_register(phy::Register::PHCON2, phy::PHCON2::default().hdldis(1).bits()); + + // Globally enable interrupts + //self.bit_field_set(common::Register::EIE, common::EIE::mask().intie()); + + // Set the per packet control byte; we'll always use the value 0 + self.write_buffer_memory(Some(TXST), &mut [0]); + + // decrease the packet count to 0 + while self.read_control_register(bank1::Register::EPKTCNT) != 0 { + self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec()); + } + + // Enable reception + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen()); + } + + /// Flushes the transmit buffer, ensuring all pending transmissions have completed + /// NOTE: The returned packet *must* be `read` or `ignore`-d, otherwise this method will always + /// return `None` on subsequent invocations + pub fn receive<'a>(&mut self, buf: &'a mut [u8]) -> Option<&'a mut [u8]> { + if self.pending_packets() == 0 { + // Errata #6: we can't rely on PKTIF so we check PKTCNT + return None; + } + + let curr_packet = self.next_packet; + + // read out the first 6 bytes + let mut temp_buf = [0; 6]; + self.read_buffer_memory(Some(curr_packet), &mut temp_buf); + + // next packet pointer + let next_packet = u16::from_parts(temp_buf[0], temp_buf[1]); + if next_packet > RXND { + panic!("CorruptRxBuffer"); + } + + // status vector + let status = header::RxStatus(u32::from_le_bytes(temp_buf[2..].try_into().unwrap())); + let len = status.byte_count() as u16 - CRC_SZ; + + if len > RXND { + panic!("CorruptRxBuffer 2"); + } + + self.read_buffer_memory(None, &mut buf[..len as usize]); + + // update ERXRDPT + // due to Errata #14 we must write an odd address to ERXRDPT + // we know that ERXST = 0, that ERXND is odd and that next_packet is even + let rxrdpt = if self.next_packet < 1 || self.next_packet > RXND + 1 { + RXND + } else { + self.next_packet - 1 + }; + // "To move ERXRDPT, the host controller must write to ERXRDPTL first." + self.write_control_register(bank0::Register::ERXRDPTL, rxrdpt.low()); + self.write_control_register(bank0::Register::ERXRDPTH, rxrdpt.high()); + + // decrease the packet count + self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec()); + + self.next_packet = next_packet; + + Some(&mut buf[..len as usize]) + } + + fn wait_tx_ready(&mut self) { + for _ in 0u32..10000 { + if common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 0 { + return; + } + } + + // work around errata #12 by resetting the transmit logic before every new + // transmission + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrst()); + self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().txrst()); + //self.bit_field_clear(common::Register::EIR, { + // let mask = common::EIR::mask(); + // mask.txerif() | mask.txif() + //}); + } + + /// Starts the transmission of `bytes` + /// + /// It's up to the caller to ensure that `bytes` is a valid Ethernet frame. The interface will + /// take care of appending a (4 byte) CRC to the frame and of padding the frame to the minimum + /// size allowed by the Ethernet specification (64 bytes, or 46 bytes of payload). + /// + /// NOTE This method will flush any previous transmission that's in progress + /// + /// # Panics + /// + /// If `bytes` length is greater than 1514, the maximum frame length allowed by the interface, + /// or greater than the transmit buffer + pub fn transmit(&mut self, bytes: &[u8]) { + assert!(bytes.len() <= self.mtu() as usize); + + self.wait_tx_ready(); + + // NOTE the plus one is to not overwrite the per packet control byte + let wrpt = TXST + 1; + + // 1. ETXST was set during initialization + + // 2. write the frame to the IC memory + self.write_buffer_memory(Some(wrpt), bytes); + + let txnd = wrpt + bytes.len() as u16 - 1; + + // 3. Set the end address of the transmit buffer + self.write_control_register(bank0::Register::ETXNDL, txnd.low()); + self.write_control_register(bank0::Register::ETXNDH, txnd.high()); + + // 4. reset interrupt flag + //self.bit_field_clear(common::Register::EIR, common::EIR::mask().txif()); + + // 5. start transmission + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrts()); + + // Wait until transmission finishes + //while common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 1 {} + + /* + // read the transmit status vector + let mut tx_stat = [0; 7]; + self.read_buffer_memory(None, &mut tx_stat); + + let stat = common::ESTAT(self.read_control_register(common::Register::ESTAT)); + + if stat.txabrt() == 1 { + // work around errata #12 by reading the transmit status vector + if stat.latecol() == 1 || (tx_stat[2] & (1 << 5)) != 0 { + panic!("LateCollision") + } else { + panic!("TransmitAbort") + } + }*/ + } + + pub fn is_link_up(&mut self) -> bool { + let bits = self.read_phy_register(phy::Register::PHSTAT2); + phy::PHSTAT2(bits).lstat() == 1 + } + + /// Returns the interface Maximum Transmission Unit (MTU) + /// + /// The value returned by this function will never exceed 1514 bytes. The actual value depends + /// on the memory assigned to the transmission buffer when initializing the device + pub fn mtu(&self) -> u16 { + cmp::min(BUF_SZ - RXND - 1, MAX_FRAME_LENGTH - CRC_SZ) + } + + /* Miscellaneous */ + /// Returns the number of packets that have been received but have not been processed yet + pub fn pending_packets(&mut self) -> u8 { + self.read_control_register(bank1::Register::EPKTCNT) + } + + /// Adjusts the receive filter to *accept* these packet types + pub fn accept(&mut self, packets: &[Packet]) { + let mask = bank1::ERXFCON::mask(); + let mut val = 0; + for packet in packets { + match packet { + Packet::Broadcast => val |= mask.bcen(), + Packet::Multicast => val |= mask.mcen(), + Packet::Unicast => val |= mask.ucen(), + } + } + + self.bit_field_set(bank1::Register::ERXFCON, val) + } + + /// Adjusts the receive filter to *ignore* these packet types + pub fn ignore(&mut self, packets: &[Packet]) { + let mask = bank1::ERXFCON::mask(); + let mut val = 0; + for packet in packets { + match packet { + Packet::Broadcast => val |= mask.bcen(), + Packet::Multicast => val |= mask.mcen(), + Packet::Unicast => val |= mask.ucen(), + } + } + + self.bit_field_clear(bank1::Register::ERXFCON, val) + } + + /* Private */ + /* Read */ + fn read_control_register(&mut self, register: R) -> u8 + where + R: Into, + { + self._read_control_register(register.into()) + } + + fn _read_control_register(&mut self, register: Register) -> u8 { + self.change_bank(register); + + let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0]; + self.spi.transfer_in_place(&mut buffer).unwrap(); + + buffer[1] + } + + fn read_phy_register(&mut self, register: phy::Register) -> u16 { + embassy_time::block_for(Duration::from_millis(1)); + + // set PHY register address + self.write_control_register(bank2::Register::MIREGADR, register.addr()); + + // start read operation + self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(1).bits()); + + // wait until the read operation finishes + while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {} + + let h = self.read_control_register(bank2::Register::MIRDH); + let l = self.read_control_register(bank2::Register::MIRDL); + + self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(0).bits()); + + (l as u16) | (h as u16) << 8 + } + + /* Write */ + fn _write_control_register(&mut self, register: Register, value: u8) { + self.change_bank(register); + + let buffer = [Instruction::WCR.opcode() | register.addr(), value]; + self.spi.write(&buffer).unwrap(); + } + + fn write_control_register(&mut self, register: R, value: u8) + where + R: Into, + { + self._write_control_register(register.into(), value) + } + + fn write_phy_register(&mut self, register: phy::Register, value: u16) { + // set PHY register address + self.write_control_register(bank2::Register::MIREGADR, register.addr()); + + self.write_control_register(bank2::Register::MIWRL, (value & 0xff) as u8); + // this starts the write operation + self.write_control_register(bank2::Register::MIWRH, (value >> 8) as u8); + + // wait until the write operation finishes + while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {} + } + + /* RMW */ + fn modify_control_register(&mut self, register: R, f: F) + where + F: FnOnce(u8) -> u8, + R: Into, + { + self._modify_control_register(register.into(), f) + } + + fn _modify_control_register(&mut self, register: Register, f: F) + where + F: FnOnce(u8) -> u8, + { + let r = self._read_control_register(register); + self._write_control_register(register, f(r)) + } + + /* Auxiliary */ + fn change_bank(&mut self, register: Register) { + let bank = register.bank(); + + if let Some(bank) = bank { + if self.bank == bank { + // already on the register bank + return; + } + + // change bank + self.bank = bank; + match bank { + Bank::Bank0 => self.bit_field_clear(common::Register::ECON1, 0b11), + Bank::Bank1 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b01), + Bank::Bank2 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b10), + Bank::Bank3 => self.bit_field_set(common::Register::ECON1, 0b11), + } + } else { + // common register + } + } + + /* Primitive operations */ + fn bit_field_clear(&mut self, register: R, mask: u8) + where + R: Into, + { + self._bit_field_clear(register.into(), mask) + } + + fn _bit_field_clear(&mut self, register: Register, mask: u8) { + debug_assert!(register.is_eth_register()); + + self.change_bank(register); + + self.spi + .write(&[Instruction::BFC.opcode() | register.addr(), mask]) + .unwrap(); + } + + fn bit_field_set(&mut self, register: R, mask: u8) + where + R: Into, + { + self._bit_field_set(register.into(), mask) + } + + fn _bit_field_set(&mut self, register: Register, mask: u8) { + debug_assert!(register.is_eth_register()); + + self.change_bank(register); + + self.spi + .write(&[Instruction::BFS.opcode() | register.addr(), mask]) + .unwrap(); + } + + fn read_buffer_memory(&mut self, addr: Option, buf: &mut [u8]) { + if let Some(addr) = addr { + self.write_control_register(bank0::Register::ERDPTL, addr.low()); + self.write_control_register(bank0::Register::ERDPTH, addr.high()); + } + + self.spi + .transaction(&mut [Operation::Write(&[Instruction::RBM.opcode()]), Operation::Read(buf)]) + .unwrap(); + } + + fn soft_reset(&mut self) { + self.spi.write(&[Instruction::SRC.opcode()]).unwrap(); + } + + fn write_buffer_memory(&mut self, addr: Option, buffer: &[u8]) { + if let Some(addr) = addr { + self.write_control_register(bank0::Register::EWRPTL, addr.low()); + self.write_control_register(bank0::Register::EWRPTH, addr.high()); + } + + self.spi + .transaction(&mut [Operation::Write(&[Instruction::WBM.opcode()]), Operation::Write(buffer)]) + .unwrap(); + } +} + +#[derive(Clone, Copy, PartialEq)] +enum Bank { + Bank0, + Bank1, + Bank2, + Bank3, +} + +#[derive(Clone, Copy)] +enum Instruction { + /// Read Control Register + RCR = 0b000_00000, + /// Read Buffer Memory + RBM = 0b001_11010, + /// Write Control Register + WCR = 0b010_00000, + /// Write Buffer Memory + WBM = 0b011_11010, + /// Bit Field Set + BFS = 0b100_00000, + /// Bit Field Clear + BFC = 0b101_00000, + /// System Reset Command + SRC = 0b111_11111, +} + +impl Instruction { + fn opcode(&self) -> u8 { + *self as u8 + } +} + +#[derive(Clone, Copy)] +enum Register { + Bank0(bank0::Register), + Bank1(bank1::Register), + Bank2(bank2::Register), + Bank3(bank3::Register), + Common(common::Register), +} + +impl Register { + fn addr(&self) -> u8 { + match *self { + Register::Bank0(r) => r.addr(), + Register::Bank1(r) => r.addr(), + Register::Bank2(r) => r.addr(), + Register::Bank3(r) => r.addr(), + Register::Common(r) => r.addr(), + } + } + + fn bank(&self) -> Option { + Some(match *self { + Register::Bank0(_) => Bank::Bank0, + Register::Bank1(_) => Bank::Bank1, + Register::Bank2(_) => Bank::Bank2, + Register::Bank3(_) => Bank::Bank3, + Register::Common(_) => return None, + }) + } + + fn is_eth_register(&self) -> bool { + match *self { + Register::Bank0(r) => r.is_eth_register(), + Register::Bank1(r) => r.is_eth_register(), + Register::Bank2(r) => r.is_eth_register(), + Register::Bank3(r) => r.is_eth_register(), + Register::Common(r) => r.is_eth_register(), + } + } +} + +/// Packet type, used to configure receive filters +#[non_exhaustive] +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Packet { + /// Broadcast packets + Broadcast, + /// Multicast packets + Multicast, + /// Unicast packets + Unicast, +} + +static mut TX_BUF: [u8; MTU] = [0; MTU]; +static mut RX_BUF: [u8; MTU] = [0; MTU]; + +impl embassy_net_driver::Driver for Enc28j60 +where + S: SpiDevice, + O: OutputPin, +{ + type RxToken<'a> = RxToken<'a> + where + Self: 'a; + + type TxToken<'a> = TxToken<'a, S, O> + where + Self: 'a; + + fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + let rx_buf = unsafe { &mut RX_BUF }; + let tx_buf = unsafe { &mut TX_BUF }; + if let Some(pkt) = self.receive(rx_buf) { + let n = pkt.len(); + Some((RxToken { buf: &mut pkt[..n] }, TxToken { buf: tx_buf, eth: self })) + } else { + cx.waker().wake_by_ref(); + None + } + } + + fn transmit(&mut self, _cx: &mut core::task::Context) -> Option> { + let tx_buf = unsafe { &mut TX_BUF }; + Some(TxToken { buf: tx_buf, eth: self }) + } + + fn link_state(&mut self, cx: &mut core::task::Context) -> LinkState { + cx.waker().wake_by_ref(); + match self.is_link_up() { + true => LinkState::Up, + false => LinkState::Down, + } + } + + fn capabilities(&self) -> Capabilities { + let mut caps = Capabilities::default(); + caps.max_transmission_unit = MTU; + caps.medium = Medium::Ethernet; + caps + } + + fn hardware_address(&self) -> HardwareAddress { + HardwareAddress::Ethernet(self.mac_addr) + } +} + +pub struct RxToken<'a> { + buf: &'a mut [u8], +} + +impl<'a> embassy_net_driver::RxToken for RxToken<'a> { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + f(self.buf) + } +} + +pub struct TxToken<'a, S, O> +where + S: SpiDevice, + O: OutputPin, +{ + eth: &'a mut Enc28j60, + buf: &'a mut [u8], +} + +impl<'a, S, O> embassy_net_driver::TxToken for TxToken<'a, S, O> +where + S: SpiDevice, + O: OutputPin, +{ + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + assert!(len <= self.buf.len()); + let r = f(&mut self.buf[..len]); + self.eth.transmit(&self.buf[..len]); + r + } +} diff --git a/embassy-net-enc28j60/src/macros.rs b/embassy-net-enc28j60/src/macros.rs new file mode 100644 index 00000000..8d064957 --- /dev/null +++ b/embassy-net-enc28j60/src/macros.rs @@ -0,0 +1,89 @@ +macro_rules! register { + ($REGISTER:ident, $reset_value:expr, $uxx:ty, { + $(#[$($attr:tt)*] $bitfield:ident @ $range:expr,)+ + }) => { + #[derive(Clone, Copy)] + pub(crate) struct $REGISTER { + bits: $uxx, + _mode: ::core::marker::PhantomData, + } + + impl $REGISTER { + #[allow(dead_code)] + pub(crate) fn mask() -> $REGISTER { + $REGISTER { bits: 0, _mode: ::core::marker::PhantomData } + } + + $( + #[allow(dead_code)] + pub(crate) fn $bitfield(&self) -> $uxx { + use super::traits::OffsetSize; + + let size = $range.size(); + let offset = $range.offset(); + ((1 << size) - 1) << offset + } + )+ + } + + impl ::core::default::Default for $REGISTER { + fn default() -> Self { + $REGISTER { bits: $reset_value, _mode: ::core::marker::PhantomData } + } + } + + #[allow(non_snake_case)] + #[allow(dead_code)] + pub(crate) fn $REGISTER(bits: $uxx) -> $REGISTER { + $REGISTER { bits, _mode: ::core::marker::PhantomData } + } + + impl $REGISTER { + #[allow(dead_code)] + pub(crate) fn modify(self) -> $REGISTER { + $REGISTER { bits: self.bits, _mode: ::core::marker::PhantomData } + } + + $( + #[$($attr)*] + #[allow(dead_code)] + pub(crate) fn $bitfield(&self) -> $uxx { + use super::traits::OffsetSize; + + let offset = $range.offset(); + let size = $range.size(); + let mask = (1 << size) - 1; + + (self.bits >> offset) & mask + } + )+ + } + + impl $REGISTER { + #[allow(dead_code)] + pub(crate) fn bits(self) -> $uxx { + self.bits + } + + $( + #[$($attr)*] + #[allow(dead_code)] + pub(crate) fn $bitfield(&mut self, mut bits: $uxx) -> &mut Self { + use super::traits::OffsetSize; + + let offset = $range.offset(); + let size = $range.size(); + let mask = (1 << size) - 1; + + debug_assert!(bits <= mask); + bits &= mask; + + self.bits &= !(mask << offset); + self.bits |= bits << offset; + + self + } + )+ + } + } +} diff --git a/embassy-net-enc28j60/src/phy.rs b/embassy-net-enc28j60/src/phy.rs new file mode 100644 index 00000000..7f62b5f2 --- /dev/null +++ b/embassy-net-enc28j60/src/phy.rs @@ -0,0 +1,36 @@ +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub enum Register { + PHCON1 = 0x00, + PHSTAT1 = 0x01, + PHID1 = 0x02, + PHID2 = 0x03, + PHCON2 = 0x10, + PHSTAT2 = 0x11, + PHIE = 0x12, + PHIR = 0x13, + PHLCON = 0x14, +} + +impl Register { + pub(crate) fn addr(&self) -> u8 { + *self as u8 + } +} + +register!(PHCON2, 0, u16, { + #[doc = "PHY Half-Duplex Loopback Disable bit"] + hdldis @ 8, + #[doc = "Jabber Correction Disable bit"] + jabber @ 10, + #[doc = "Twisted-Pair Transmitter Disable bit"] + txdis @ 13, + #[doc = "PHY Force Linkup bit"] + frclnk @ 14, +}); + +register!(PHSTAT2, 0, u16, { + // Datasheet says it's bit 10, but it's actually bit 2 ?!?! + #[doc = "Link Status bit"] + lstat @ 2, +}); diff --git a/embassy-net-enc28j60/src/traits.rs b/embassy-net-enc28j60/src/traits.rs new file mode 100644 index 00000000..08f94045 --- /dev/null +++ b/embassy-net-enc28j60/src/traits.rs @@ -0,0 +1,57 @@ +use core::ops::Range; + +pub(crate) trait OffsetSize { + fn offset(self) -> u8; + fn size(self) -> u8; +} + +impl OffsetSize for u8 { + fn offset(self) -> u8 { + self + } + + fn size(self) -> u8 { + 1 + } +} + +impl OffsetSize for Range { + fn offset(self) -> u8 { + self.start + } + + fn size(self) -> u8 { + self.end - self.start + } +} + +pub(crate) trait U16Ext { + fn from_parts(low: u8, high: u8) -> Self; + + fn low(self) -> u8; + + fn high(self) -> u8; +} + +impl U16Ext for u16 { + fn from_parts(low: u8, high: u8) -> u16 { + ((high as u16) << 8) + low as u16 + } + + fn low(self) -> u8 { + (self & 0xff) as u8 + } + + fn high(self) -> u8 { + (self >> 8) as u8 + } +} + +#[derive(Clone, Copy)] +pub struct Mask; + +#[derive(Clone, Copy)] +pub struct R; + +#[derive(Clone, Copy)] +pub struct W; From 4af1cf88d29f29df52f8c9e7928409e60a91ffbc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 5 Aug 2023 19:10:45 +0200 Subject: [PATCH 239/298] net-enc28j60: add example. --- examples/nrf52840/Cargo.toml | 4 + .../nrf52840/src/bin/ethernet_enc28j60.rs | 124 ++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 examples/nrf52840/src/bin/ethernet_enc28j60.rs diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 15fe22d3..8c6f6bcc 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -12,6 +12,7 @@ nightly = [ "embassy-nrf/nightly", "embassy-net/nightly", "embassy-net-esp-hosted", + "embassy-net-enc28j60", "embassy-nrf/unstable-traits", "embassy-time/nightly", "embassy-time/unstable-traits", @@ -40,6 +41,7 @@ lora-phy = { version = "1", optional = true } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true } +embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"], optional = true } defmt = "0.3" defmt-rtt = "0.4" @@ -54,7 +56,9 @@ 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 } +embedded-hal = { version = "1.0.0-alpha.11" } embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } +embedded-hal-bus = { version = "0.1.0-alpha.3" } num-integer = { version = "0.1.45", default-features = false } microfft = "0.5.0" diff --git a/examples/nrf52840/src/bin/ethernet_enc28j60.rs b/examples/nrf52840/src/bin/ethernet_enc28j60.rs new file mode 100644 index 00000000..d1b796fa --- /dev/null +++ b/examples/nrf52840/src/bin/ethernet_enc28j60.rs @@ -0,0 +1,124 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Stack, StackResources}; +use embassy_net_enc28j60::Enc28j60; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::rng::Rng; +use embassy_nrf::spim::Spim; +use embassy_nrf::{bind_interrupts, peripherals, spim}; +use embassy_time::Delay; +use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_io_async::Write; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + SPIM3 => spim::InterruptHandler; + RNG => embassy_nrf::rng::InterruptHandler; +}); + +#[embassy_executor::task] +async fn net_task( + stack: &'static Stack< + Enc28j60< + ExclusiveDevice, Output<'static, peripherals::P0_15>, Delay>, + Output<'static, peripherals::P0_13>, + >, + >, +) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("running!"); + + let eth_sck = p.P0_20; + let eth_mosi = p.P0_22; + let eth_miso = p.P0_24; + let eth_cs = p.P0_15; + let eth_rst = p.P0_13; + let _eth_irq = p.P0_12; + + let mut config = spim::Config::default(); + config.frequency = spim::Frequency::M16; + let spi = spim::Spim::new(p.SPI3, Irqs, eth_sck, eth_miso, eth_mosi, config); + let cs = Output::new(eth_cs, Level::High, OutputDrive::Standard); + let spi = ExclusiveDevice::new(spi, cs, Delay); + + let rst = Output::new(eth_rst, Level::High, OutputDrive::Standard); + let mac_addr = [2, 3, 4, 5, 6, 7]; + let device = Enc28j60::new(spi, Some(rst), mac_addr); + + let config = embassy_net::Config::dhcpv4(Default::default()); + // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + // }); + + // Generate random seed + let mut rng = Rng::new(p.RNG, Irqs); + let mut seed = [0; 8]; + rng.blocking_fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + // And now we can use it! + + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut buf = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); + + info!("Listening on TCP:1234..."); + if let Err(e) = socket.accept(1234).await { + warn!("accept error: {:?}", e); + continue; + } + + info!("Received connection from {:?}", socket.remote_endpoint()); + + loop { + let n = match socket.read(&mut buf).await { + Ok(0) => { + warn!("read EOF"); + break; + } + Ok(n) => n, + Err(e) => { + warn!("read error: {:?}", e); + break; + } + }; + + info!("rxd {:02x}", &buf[..n]); + + match socket.write_all(&buf[..n]).await { + Ok(()) => {} + Err(e) => { + warn!("write error: {:?}", e); + break; + } + }; + } + } +} From 253b28debaf13d839087a2b9160f416f4be22438 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 12 Aug 2023 03:07:13 +0200 Subject: [PATCH 240/298] net-enc28j60: fix PHY read unreliable due to missing dummy byte. --- embassy-net-enc28j60/src/lib.rs | 19 ++++++++++++------- embassy-net-enc28j60/src/phy.rs | 3 +-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs index 4f129b6b..d77dc2c5 100644 --- a/embassy-net-enc28j60/src/lib.rs +++ b/embassy-net-enc28j60/src/lib.rs @@ -361,10 +361,16 @@ where fn _read_control_register(&mut self, register: Register) -> u8 { self.change_bank(register); - let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0]; - self.spi.transfer_in_place(&mut buffer).unwrap(); - - buffer[1] + if register.is_eth_register() { + let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0]; + self.spi.transfer_in_place(&mut buffer).unwrap(); + buffer[1] + } else { + // MAC, MII regs need a dummy byte. + let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0, 0]; + self.spi.transfer_in_place(&mut buffer).unwrap(); + buffer[2] + } } fn read_phy_register(&mut self, register: phy::Register) -> u16 { @@ -379,11 +385,10 @@ where // wait until the read operation finishes while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {} - let h = self.read_control_register(bank2::Register::MIRDH); - let l = self.read_control_register(bank2::Register::MIRDL); - self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(0).bits()); + let l = self.read_control_register(bank2::Register::MIRDL); + let h = self.read_control_register(bank2::Register::MIRDH); (l as u16) | (h as u16) << 8 } diff --git a/embassy-net-enc28j60/src/phy.rs b/embassy-net-enc28j60/src/phy.rs index 7f62b5f2..89144ada 100644 --- a/embassy-net-enc28j60/src/phy.rs +++ b/embassy-net-enc28j60/src/phy.rs @@ -30,7 +30,6 @@ register!(PHCON2, 0, u16, { }); register!(PHSTAT2, 0, u16, { - // Datasheet says it's bit 10, but it's actually bit 2 ?!?! #[doc = "Link Status bit"] - lstat @ 2, + lstat @ 10, }); From f7f75167accd1fb91238b05f30a280bf115baddf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 14:15:29 +0200 Subject: [PATCH 241/298] cleanup vscode settings. --- .vscode/settings.json | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 139b432f..d48f7ba1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,16 +6,21 @@ "rust-analyzer.check.allTargets": false, "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, - "rust-analyzer.cargo.target": "thumbv7m-none-eabi", + "rust-analyzer.showUnlinkedFileNotification": false, + // uncomment the target of your chip. + //"rust-analyzer.cargo.target": "thumbv6m-none-eabi", + //"rust-analyzer.cargo.target": "thumbv7m-none-eabi", + "rust-analyzer.cargo.target": "thumbv7em-none-eabi", //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ - ///"nightly", + // Uncomment if the example has a "nightly" feature. + "nightly", ], "rust-analyzer.linkedProjects": [ - // Declare for the target you wish to develop - // "embassy-executor/Cargo.toml", - // "embassy-sync/Cargo.toml", - "examples/stm32wl/Cargo.toml", + // Uncomment ONE line for the chip you want to work on. + // This makes rust-analyzer work on the example crate and all its dependencies. + "examples/nrf52840/Cargo.toml", + // "examples/nrf52840-rtic/Cargo.toml", // "examples/nrf5340/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", @@ -41,5 +46,4 @@ // "examples/stm32wl/Cargo.toml", // "examples/wasm/Cargo.toml", ], - "rust-analyzer.showUnlinkedFileNotification": false, } \ No newline at end of file From ea9f887ee168aab502ab595aa460c7c0910ff6b9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 14:27:31 +0200 Subject: [PATCH 242/298] net-enc28j60: add docs, readme. --- embassy-net-enc28j60/README.md | 18 ++++++++++++++++++ embassy-net-enc28j60/src/lib.rs | 9 +++++++++ 2 files changed, 27 insertions(+) diff --git a/embassy-net-enc28j60/README.md b/embassy-net-enc28j60/README.md index e12d240c..39011ca1 100644 --- a/embassy-net-enc28j60/README.md +++ b/embassy-net-enc28j60/README.md @@ -1 +1,19 @@ # `embassy-net-enc28j60` + +[`embassy-net`](https://crates.io/crates/embassy-net) integration for the Microchip ENC28J60 Ethernet chip. + +Based on [@japaric](https://github.com/japaric)'s [`enc28j60`](https://github.com/japaric/enc28j60) crate. + +## Interoperability + +This crate can run on any executor. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs index d77dc2c5..09e77baf 100644 --- a/embassy-net-enc28j60/src/lib.rs +++ b/embassy-net-enc28j60/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![doc = include_str!("../README.md")] +#![warn(missing_docs)] // must go first. mod fmt; @@ -43,6 +44,7 @@ const _TXND: u16 = 0x1fff; const MTU: usize = 1514; // 1500 IP + 14 ethernet header +/// ENC28J60 embassy-net driver pub struct Enc28j60 { mac_addr: [u8; 6], @@ -60,6 +62,10 @@ where S: SpiDevice, O: OutputPin, { + /// Create a new ENC28J60 driver instance. + /// + /// The RST pin is optional. If None, reset will be done with a SPI + /// soft reset command, instead of via the RST pin. pub fn new(spi: S, rst: Option, mac_addr: [u8; 6]) -> Self { let mut res = Self { mac_addr, @@ -300,6 +306,7 @@ where }*/ } + /// Get whether the link is up pub fn is_link_up(&mut self) -> bool { let bits = self.read_phy_register(phy::Register::PHSTAT2); phy::PHSTAT2(bits).lstat() == 1 @@ -659,6 +666,7 @@ where } } +/// embassy-net RX token. pub struct RxToken<'a> { buf: &'a mut [u8], } @@ -672,6 +680,7 @@ impl<'a> embassy_net_driver::RxToken for RxToken<'a> { } } +/// embassy-net TX token. pub struct TxToken<'a, S, O> where S: SpiDevice, From b6b44480457d272aace977e885d5dba252fd2bed Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 15:13:03 +0200 Subject: [PATCH 243/298] ci/docs: build embassy-net-enc28j60. --- .github/ci/doc.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index e410ffa3..f829582a 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -36,6 +36,7 @@ docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/g docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup +docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static From b1ef009c6b08ae54595ca558f4571d430e99cb0b Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Mon, 14 Aug 2023 20:58:44 +0100 Subject: [PATCH 244/298] Add tcp capacity impls --- embassy-net/src/tcp.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index b4ce4094..ab22ffb7 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -93,6 +93,11 @@ impl<'a> TcpReader<'a> { { self.io.read_with(f).await } + + /// Return the maximum number of bytes inside the transmit buffer. + pub fn recv_capacity(&self) -> usize { + self.io.recv_capacity() + } } impl<'a> TcpWriter<'a> { @@ -122,6 +127,11 @@ impl<'a> TcpWriter<'a> { { self.io.write_with(f).await } + + /// Return the maximum number of bytes inside the transmit buffer. + pub fn send_capacity(&self) -> usize { + self.io.send_capacity() + } } impl<'a> TcpSocket<'a> { @@ -143,6 +153,16 @@ impl<'a> TcpSocket<'a> { } } + /// Return the maximum number of bytes inside the recv buffer. + pub fn recv_capacity(&self) -> usize { + self.io.recv_capacity() + } + + /// Return the maximum number of bytes inside the transmit buffer. + pub fn send_capacity(&self) -> usize { + self.io.send_capacity() + } + /// Call `f` with the largest contiguous slice of octets in the transmit buffer, /// and enqueue the amount of elements returned by `f`. /// @@ -478,6 +498,14 @@ impl<'d> TcpIo<'d> { }) .await } + + fn recv_capacity(&self) -> usize { + self.with(|s, _| s.recv_capacity()) + } + + fn send_capacity(&self) -> usize { + self.with(|s, _| s.send_capacity()) + } } #[cfg(feature = "nightly")] From c114ea024a2b74e3e220e6af1d8a494dd598b130 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Tue, 15 Aug 2023 15:24:58 +0100 Subject: [PATCH 245/298] Add udp capacity impls --- embassy-net/src/udp.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 0d97b6db..0a5a7b8f 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -184,6 +184,26 @@ impl<'a> UdpSocket<'a> { pub fn may_recv(&self) -> bool { self.with(|s, _| s.can_recv()) } + + /// Return the maximum number packets the socket can receive. + pub fn packet_recv_capacity(&self) -> usize { + self.with(|s, _| s.packet_recv_capacity()) + } + + /// Return the maximum number packets the socket can receive. + pub fn packet_send_capacity(&self) -> usize { + self.with(|s, _| s.packet_send_capacity()) + } + + /// Return the maximum number of bytes inside the recv buffer. + pub fn payload_recv_capacity(&self) -> usize { + self.with(|s, _| s.payload_recv_capacity()) + } + + /// Return the maximum number of bytes inside the transmit buffer. + pub fn payload_send_capacity(&self) -> usize { + self.with(|s, _| s.payload_send_capacity()) + } } impl Drop for UdpSocket<'_> { From 098fcb14b504dea617c38e90410c639ecbcb6aa7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 16:47:45 +0200 Subject: [PATCH 246/298] net-w5500: simplify rx logic. --- embassy-net-w5500/src/device.rs | 38 ++++++++++----------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs index 9874df0d..4933625e 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-w5500/src/device.rs @@ -58,20 +58,11 @@ impl W5500 { } /// Read bytes from the RX buffer. Returns the number of bytes read. - async fn read_bytes(&mut self, buffer: &mut [u8], offset: u16) -> Result { - let rx_size = socket::get_rx_size(&mut self.bus).await? as usize; + async fn read_bytes(&mut self, read_ptr: &mut u16, buffer: &mut [u8]) -> Result<(), SPI::Error> { + self.bus.read_frame(RegisterBlock::RxBuf, *read_ptr, buffer).await?; + *read_ptr = (*read_ptr).wrapping_add(buffer.len() as u16); - let read_buffer = if rx_size > buffer.len() + offset as usize { - buffer - } else { - &mut buffer[..rx_size - offset as usize] - }; - - let read_ptr = socket::get_rx_read_ptr(&mut self.bus).await?.wrapping_add(offset); - self.bus.read_frame(RegisterBlock::RxBuf, read_ptr, read_buffer).await?; - socket::set_rx_read_ptr(&mut self.bus, read_ptr.wrapping_add(read_buffer.len() as u16)).await?; - - Ok(read_buffer.len()) + Ok(()) } /// Read an ethernet frame from the device. Returns the number of bytes read. @@ -83,31 +74,24 @@ impl W5500 { socket::reset_interrupt(&mut self.bus, socket::Interrupt::Receive).await?; + let mut read_ptr = socket::get_rx_read_ptr(&mut self.bus).await?; + // First two bytes gives the size of the received ethernet frame let expected_frame_size: usize = { let mut frame_bytes = [0u8; 2]; - assert!(self.read_bytes(&mut frame_bytes[..], 0).await? == 2); + self.read_bytes(&mut read_ptr, &mut frame_bytes).await?; u16::from_be_bytes(frame_bytes) as usize - 2 }; // Read the ethernet frame - let read_buffer = if frame.len() > expected_frame_size { - &mut frame[..expected_frame_size] - } else { - frame - }; - - let recvd_frame_size = self.read_bytes(read_buffer, 2).await?; + self.read_bytes(&mut read_ptr, &mut frame[..expected_frame_size]) + .await?; // Register RX as completed + socket::set_rx_read_ptr(&mut self.bus, read_ptr).await?; socket::command(&mut self.bus, socket::Command::Receive).await?; - // If the whole frame wasn't read, drop it - if recvd_frame_size < expected_frame_size { - Ok(0) - } else { - Ok(recvd_frame_size) - } + Ok(expected_frame_size) } /// Write an ethernet frame to the device. Returns number of bytes written From 8655ba110c95777c3edab3aabef34ae71d64957a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 15 Aug 2023 19:13:36 +0200 Subject: [PATCH 247/298] Fix typo --- embassy-sync/src/blocking_mutex/raw.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/src/blocking_mutex/raw.rs b/embassy-sync/src/blocking_mutex/raw.rs index 15796f1b..a8afcad3 100644 --- a/embassy-sync/src/blocking_mutex/raw.rs +++ b/embassy-sync/src/blocking_mutex/raw.rs @@ -11,7 +11,7 @@ use core::marker::PhantomData; /// /// Note that, unlike other mutexes, implementations only guarantee no /// concurrent access from other threads: concurrent access from the current -/// thread is allwed. For example, it's possible to lock the same mutex multiple times reentrantly. +/// thread is allowed. For example, it's possible to lock the same mutex multiple times reentrantly. /// /// Therefore, locking a `RawMutex` is only enough to guarantee safe shared (`&`) access /// to the data, it is not enough to guarantee exclusive (`&mut`) access. From 46f671ae420f27b527edf03756d1da76d86216c7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 22:47:03 +0200 Subject: [PATCH 248/298] rp: fix async spi read sometimes hanging. --- embassy-rp/src/dma.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index c8f74180..45ca21a7 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -76,7 +76,8 @@ pub unsafe fn write<'a, C: Channel, W: Word>( ) } -static DUMMY: u32 = 0; +// static mut so that this is allocated in RAM. +static mut DUMMY: u32 = 0; pub unsafe fn write_repeated<'a, C: Channel, W: Word>( ch: impl Peripheral

+ 'a, @@ -86,7 +87,7 @@ pub unsafe fn write_repeated<'a, C: Channel, W: Word>( ) -> Transfer<'a, C> { copy_inner( ch, - &DUMMY as *const u32, + &mut DUMMY as *const u32, to as *mut u32, len, W::size(), From c367b84ee53b9d7a31f442f962c939f822aadb0d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 17:11:24 +0200 Subject: [PATCH 249/298] net-w5500: add Address type. --- embassy-net-w5500/src/device.rs | 41 +++++++++++++------------------- embassy-net-w5500/src/socket.rs | 42 ++++++++++++++++----------------- embassy-net-w5500/src/spi.rs | 14 ++++++----- 3 files changed, 44 insertions(+), 53 deletions(-) diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs index 4933625e..1793baaa 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-w5500/src/device.rs @@ -1,12 +1,12 @@ use embedded_hal_async::spi::SpiDevice; use crate::socket; -use crate::spi::SpiInterface; +use crate::spi::{Address, SpiInterface}; -pub const MODE: u16 = 0x00; -pub const MAC: u16 = 0x09; -pub const SOCKET_INTR: u16 = 0x18; -pub const PHY_CFG: u16 = 0x2E; +pub const MODE: Address = (RegisterBlock::Common, 0x00); +pub const MAC: Address = (RegisterBlock::Common, 0x09); +pub const SOCKET_INTR: Address = (RegisterBlock::Common, 0x18); +pub const PHY_CFG: Address = (RegisterBlock::Common, 0x2E); #[repr(u8)] pub enum RegisterBlock { @@ -28,30 +28,24 @@ impl W5500 { pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result, SPI::Error> { let mut bus = SpiInterface(spi); // Reset device - bus.write_frame(RegisterBlock::Common, MODE, &[0x80]).await?; + bus.write_frame(MODE, &[0x80]).await?; // Enable interrupt pin - bus.write_frame(RegisterBlock::Common, SOCKET_INTR, &[0x01]).await?; + bus.write_frame(SOCKET_INTR, &[0x01]).await?; // Enable receive interrupt - bus.write_frame( - RegisterBlock::Socket0, - socket::SOCKET_INTR_MASK, - &[socket::Interrupt::Receive as u8], - ) - .await?; + bus.write_frame(socket::SOCKET_INTR_MASK, &[socket::Interrupt::Receive as u8]) + .await?; // Set MAC address - bus.write_frame(RegisterBlock::Common, MAC, &mac_addr).await?; + bus.write_frame(MAC, &mac_addr).await?; // Set the raw socket RX/TX buffer sizes to 16KB - bus.write_frame(RegisterBlock::Socket0, socket::TXBUF_SIZE, &[16]) - .await?; - bus.write_frame(RegisterBlock::Socket0, socket::RXBUF_SIZE, &[16]) - .await?; + bus.write_frame(socket::TXBUF_SIZE, &[16]).await?; + bus.write_frame(socket::RXBUF_SIZE, &[16]).await?; // MACRAW mode with MAC filtering. let mode: u8 = (1 << 2) | (1 << 7); - bus.write_frame(RegisterBlock::Socket0, socket::MODE, &[mode]).await?; + bus.write_frame(socket::MODE, &[mode]).await?; socket::command(&mut bus, socket::Command::Open).await?; Ok(Self { bus }) @@ -59,7 +53,7 @@ impl W5500 { /// Read bytes from the RX buffer. Returns the number of bytes read. async fn read_bytes(&mut self, read_ptr: &mut u16, buffer: &mut [u8]) -> Result<(), SPI::Error> { - self.bus.read_frame(RegisterBlock::RxBuf, *read_ptr, buffer).await?; + self.bus.read_frame((RegisterBlock::RxBuf, *read_ptr), buffer).await?; *read_ptr = (*read_ptr).wrapping_add(buffer.len() as u16); Ok(()) @@ -98,7 +92,7 @@ impl W5500 { pub async fn write_frame(&mut self, frame: &[u8]) -> Result { while socket::get_tx_free_size(&mut self.bus).await? < frame.len() as u16 {} let write_ptr = socket::get_tx_write_ptr(&mut self.bus).await?; - self.bus.write_frame(RegisterBlock::TxBuf, write_ptr, frame).await?; + self.bus.write_frame((RegisterBlock::TxBuf, write_ptr), frame).await?; socket::set_tx_write_ptr(&mut self.bus, write_ptr.wrapping_add(frame.len() as u16)).await?; socket::command(&mut self.bus, socket::Command::Send).await?; Ok(frame.len()) @@ -106,10 +100,7 @@ impl W5500 { pub async fn is_link_up(&mut self) -> bool { let mut link = [0]; - self.bus - .read_frame(RegisterBlock::Common, PHY_CFG, &mut link) - .await - .ok(); + self.bus.read_frame(PHY_CFG, &mut link).await.ok(); link[0] & 1 == 1 } } diff --git a/embassy-net-w5500/src/socket.rs b/embassy-net-w5500/src/socket.rs index 3d65583c..1c96cca1 100644 --- a/embassy-net-w5500/src/socket.rs +++ b/embassy-net-w5500/src/socket.rs @@ -1,17 +1,18 @@ use embedded_hal_async::spi::SpiDevice; use crate::device::RegisterBlock; -use crate::spi::SpiInterface; +use crate::spi::{Address, SpiInterface}; -pub const MODE: u16 = 0x00; -pub const COMMAND: u16 = 0x01; -pub const RXBUF_SIZE: u16 = 0x1E; -pub const TXBUF_SIZE: u16 = 0x1F; -pub const TX_FREE_SIZE: u16 = 0x20; -pub const TX_DATA_WRITE_PTR: u16 = 0x24; -pub const RECVD_SIZE: u16 = 0x26; -pub const RX_DATA_READ_PTR: u16 = 0x28; -pub const SOCKET_INTR_MASK: u16 = 0x2C; +pub const MODE: Address = (RegisterBlock::Socket0, 0x00); +pub const COMMAND: Address = (RegisterBlock::Socket0, 0x01); +pub const RXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1E); +pub const TXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1F); +pub const TX_FREE_SIZE: Address = (RegisterBlock::Socket0, 0x20); +pub const TX_DATA_WRITE_PTR: Address = (RegisterBlock::Socket0, 0x24); +pub const RECVD_SIZE: Address = (RegisterBlock::Socket0, 0x26); +pub const RX_DATA_READ_PTR: Address = (RegisterBlock::Socket0, 0x28); +pub const SOCKET_INTR_MASK: Address = (RegisterBlock::Socket0, 0x2C); +pub const INTR: Address = (RegisterBlock::Socket0, 0x02); #[repr(u8)] pub enum Command { @@ -20,7 +21,6 @@ pub enum Command { Receive = 0x40, } -pub const INTR: u16 = 0x02; #[repr(u8)] pub enum Interrupt { Receive = 0b00100_u8, @@ -28,45 +28,43 @@ pub enum Interrupt { pub async fn reset_interrupt(bus: &mut SpiInterface, code: Interrupt) -> Result<(), SPI::Error> { let data = [code as u8]; - bus.write_frame(RegisterBlock::Socket0, INTR, &data).await + bus.write_frame(INTR, &data).await } pub async fn get_tx_write_ptr(bus: &mut SpiInterface) -> Result { let mut data = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &mut data) - .await?; + bus.read_frame(TX_DATA_WRITE_PTR, &mut data).await?; Ok(u16::from_be_bytes(data)) } pub async fn set_tx_write_ptr(bus: &mut SpiInterface, ptr: u16) -> Result<(), SPI::Error> { let data = ptr.to_be_bytes(); - bus.write_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &data).await + bus.write_frame(TX_DATA_WRITE_PTR, &data).await } pub async fn get_rx_read_ptr(bus: &mut SpiInterface) -> Result { let mut data = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &mut data) - .await?; + bus.read_frame(RX_DATA_READ_PTR, &mut data).await?; Ok(u16::from_be_bytes(data)) } pub async fn set_rx_read_ptr(bus: &mut SpiInterface, ptr: u16) -> Result<(), SPI::Error> { let data = ptr.to_be_bytes(); - bus.write_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &data).await + bus.write_frame(RX_DATA_READ_PTR, &data).await } pub async fn command(bus: &mut SpiInterface, command: Command) -> Result<(), SPI::Error> { let data = [command as u8]; - bus.write_frame(RegisterBlock::Socket0, COMMAND, &data).await + bus.write_frame(COMMAND, &data).await } pub async fn get_rx_size(bus: &mut SpiInterface) -> Result { loop { // Wait until two sequential reads are equal let mut res0 = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res0).await?; + bus.read_frame(RECVD_SIZE, &mut res0).await?; let mut res1 = [0u8; 2]; - bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res1).await?; + bus.read_frame(RECVD_SIZE, &mut res1).await?; if res0 == res1 { break Ok(u16::from_be_bytes(res0)); } @@ -75,6 +73,6 @@ pub async fn get_rx_size(bus: &mut SpiInterface) -> Result< pub async fn get_tx_free_size(bus: &mut SpiInterface) -> Result { let mut data = [0; 2]; - bus.read_frame(RegisterBlock::Socket0, TX_FREE_SIZE, &mut data).await?; + bus.read_frame(TX_FREE_SIZE, &mut data).await?; Ok(u16::from_be_bytes(data)) } diff --git a/embassy-net-w5500/src/spi.rs b/embassy-net-w5500/src/spi.rs index 07749d6b..316c6521 100644 --- a/embassy-net-w5500/src/spi.rs +++ b/embassy-net-w5500/src/spi.rs @@ -2,14 +2,16 @@ use embedded_hal_async::spi::{Operation, SpiDevice}; use crate::device::RegisterBlock; +pub type Address = (RegisterBlock, u16); + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SpiInterface(pub SPI); impl SpiInterface { - pub async fn read_frame(&mut self, block: RegisterBlock, address: u16, data: &mut [u8]) -> Result<(), SPI::Error> { - let address_phase = address.to_be_bytes(); - let control_phase = [(block as u8) << 3]; + pub async fn read_frame(&mut self, address: Address, data: &mut [u8]) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3]; let operations = &mut [ Operation::Write(&address_phase), Operation::Write(&control_phase), @@ -18,9 +20,9 @@ impl SpiInterface { self.0.transaction(operations).await } - pub async fn write_frame(&mut self, block: RegisterBlock, address: u16, data: &[u8]) -> Result<(), SPI::Error> { - let address_phase = address.to_be_bytes(); - let control_phase = [(block as u8) << 3 | 0b0000_0100]; + pub async fn write_frame(&mut self, address: Address, data: &[u8]) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3 | 0b0000_0100]; let data_phase = data; let operations = &mut [ Operation::Write(&address_phase[..]), From a436bd068f6059fa073a9f1f246a77e1ce38dd31 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 17:20:41 +0200 Subject: [PATCH 250/298] net-w5500: inline socket into device. --- embassy-net-w5500/src/device.rs | 124 +++++++++++++++++++++++++------- embassy-net-w5500/src/lib.rs | 1 - embassy-net-w5500/src/socket.rs | 78 -------------------- 3 files changed, 99 insertions(+), 104 deletions(-) delete mode 100644 embassy-net-w5500/src/socket.rs diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs index 1793baaa..cf451b33 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-w5500/src/device.rs @@ -1,12 +1,34 @@ use embedded_hal_async::spi::SpiDevice; -use crate::socket; use crate::spi::{Address, SpiInterface}; -pub const MODE: Address = (RegisterBlock::Common, 0x00); -pub const MAC: Address = (RegisterBlock::Common, 0x09); -pub const SOCKET_INTR: Address = (RegisterBlock::Common, 0x18); -pub const PHY_CFG: Address = (RegisterBlock::Common, 0x2E); +pub const COMMON_MODE: Address = (RegisterBlock::Common, 0x00); +pub const COMMON_MAC: Address = (RegisterBlock::Common, 0x09); +pub const COMMON_SOCKET_INTR: Address = (RegisterBlock::Common, 0x18); +pub const COMMON_PHY_CFG: Address = (RegisterBlock::Common, 0x2E); + +pub const SOCKET_MODE: Address = (RegisterBlock::Socket0, 0x00); +pub const SOCKET_COMMAND: Address = (RegisterBlock::Socket0, 0x01); +pub const SOCKET_RXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1E); +pub const SOCKET_TXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1F); +pub const SOCKET_TX_FREE_SIZE: Address = (RegisterBlock::Socket0, 0x20); +pub const SOCKET_TX_DATA_WRITE_PTR: Address = (RegisterBlock::Socket0, 0x24); +pub const SOCKET_RECVD_SIZE: Address = (RegisterBlock::Socket0, 0x26); +pub const SOCKET_RX_DATA_READ_PTR: Address = (RegisterBlock::Socket0, 0x28); +pub const SOCKET_INTR_MASK: Address = (RegisterBlock::Socket0, 0x2C); +pub const SOCKET_INTR: Address = (RegisterBlock::Socket0, 0x02); + +#[repr(u8)] +pub enum Command { + Open = 0x01, + Send = 0x20, + Receive = 0x40, +} + +#[repr(u8)] +pub enum Interrupt { + Receive = 0b00100_u8, +} #[repr(u8)] pub enum RegisterBlock { @@ -28,27 +50,78 @@ impl W5500 { pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result, SPI::Error> { let mut bus = SpiInterface(spi); // Reset device - bus.write_frame(MODE, &[0x80]).await?; + bus.write_frame(COMMON_MODE, &[0x80]).await?; // Enable interrupt pin - bus.write_frame(SOCKET_INTR, &[0x01]).await?; + bus.write_frame(COMMON_SOCKET_INTR, &[0x01]).await?; // Enable receive interrupt - bus.write_frame(socket::SOCKET_INTR_MASK, &[socket::Interrupt::Receive as u8]) - .await?; + bus.write_frame(SOCKET_INTR_MASK, &[Interrupt::Receive as u8]).await?; // Set MAC address - bus.write_frame(MAC, &mac_addr).await?; + bus.write_frame(COMMON_MAC, &mac_addr).await?; // Set the raw socket RX/TX buffer sizes to 16KB - bus.write_frame(socket::TXBUF_SIZE, &[16]).await?; - bus.write_frame(socket::RXBUF_SIZE, &[16]).await?; + bus.write_frame(SOCKET_TXBUF_SIZE, &[16]).await?; + bus.write_frame(SOCKET_RXBUF_SIZE, &[16]).await?; // MACRAW mode with MAC filtering. let mode: u8 = (1 << 2) | (1 << 7); - bus.write_frame(socket::MODE, &[mode]).await?; - socket::command(&mut bus, socket::Command::Open).await?; + bus.write_frame(SOCKET_MODE, &[mode]).await?; + let mut this = Self { bus }; + this.command(Command::Open).await?; - Ok(Self { bus }) + Ok(this) + } + + async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> { + let data = [code as u8]; + self.bus.write_frame(SOCKET_INTR, &data).await + } + + async fn get_tx_write_ptr(&mut self) -> Result { + let mut data = [0u8; 2]; + self.bus.read_frame(SOCKET_TX_DATA_WRITE_PTR, &mut data).await?; + Ok(u16::from_be_bytes(data)) + } + + async fn set_tx_write_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> { + let data = ptr.to_be_bytes(); + self.bus.write_frame(SOCKET_TX_DATA_WRITE_PTR, &data).await + } + + async fn get_rx_read_ptr(&mut self) -> Result { + let mut data = [0u8; 2]; + self.bus.read_frame(SOCKET_RX_DATA_READ_PTR, &mut data).await?; + Ok(u16::from_be_bytes(data)) + } + + async fn set_rx_read_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> { + let data = ptr.to_be_bytes(); + self.bus.write_frame(SOCKET_RX_DATA_READ_PTR, &data).await + } + + async fn command(&mut self, command: Command) -> Result<(), SPI::Error> { + let data = [command as u8]; + self.bus.write_frame(SOCKET_COMMAND, &data).await + } + + async fn get_rx_size(&mut self) -> Result { + loop { + // Wait until two sequential reads are equal + let mut res0 = [0u8; 2]; + self.bus.read_frame(SOCKET_RECVD_SIZE, &mut res0).await?; + let mut res1 = [0u8; 2]; + self.bus.read_frame(SOCKET_RECVD_SIZE, &mut res1).await?; + if res0 == res1 { + break Ok(u16::from_be_bytes(res0)); + } + } + } + + async fn get_tx_free_size(&mut self) -> Result { + let mut data = [0; 2]; + self.bus.read_frame(SOCKET_TX_FREE_SIZE, &mut data).await?; + Ok(u16::from_be_bytes(data)) } /// Read bytes from the RX buffer. Returns the number of bytes read. @@ -61,14 +134,14 @@ impl W5500 { /// Read an ethernet frame from the device. Returns the number of bytes read. pub async fn read_frame(&mut self, frame: &mut [u8]) -> Result { - let rx_size = socket::get_rx_size(&mut self.bus).await? as usize; + let rx_size = self.get_rx_size().await? as usize; if rx_size == 0 { return Ok(0); } - socket::reset_interrupt(&mut self.bus, socket::Interrupt::Receive).await?; + self.reset_interrupt(Interrupt::Receive).await?; - let mut read_ptr = socket::get_rx_read_ptr(&mut self.bus).await?; + let mut read_ptr = self.get_rx_read_ptr().await?; // First two bytes gives the size of the received ethernet frame let expected_frame_size: usize = { @@ -82,25 +155,26 @@ impl W5500 { .await?; // Register RX as completed - socket::set_rx_read_ptr(&mut self.bus, read_ptr).await?; - socket::command(&mut self.bus, socket::Command::Receive).await?; + self.set_rx_read_ptr(read_ptr).await?; + self.command(Command::Receive).await?; Ok(expected_frame_size) } /// Write an ethernet frame to the device. Returns number of bytes written pub async fn write_frame(&mut self, frame: &[u8]) -> Result { - while socket::get_tx_free_size(&mut self.bus).await? < frame.len() as u16 {} - let write_ptr = socket::get_tx_write_ptr(&mut self.bus).await?; + while self.get_tx_free_size().await? < frame.len() as u16 {} + let write_ptr = self.get_tx_write_ptr().await?; self.bus.write_frame((RegisterBlock::TxBuf, write_ptr), frame).await?; - socket::set_tx_write_ptr(&mut self.bus, write_ptr.wrapping_add(frame.len() as u16)).await?; - socket::command(&mut self.bus, socket::Command::Send).await?; + self.set_tx_write_ptr(write_ptr.wrapping_add(frame.len() as u16)) + .await?; + self.command(Command::Send).await?; Ok(frame.len()) } pub async fn is_link_up(&mut self) -> bool { let mut link = [0]; - self.bus.read_frame(PHY_CFG, &mut link).await.ok(); + self.bus.read_frame(COMMON_PHY_CFG, &mut link).await.ok(); link[0] & 1 == 1 } } diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs index 52494b44..3c54777d 100644 --- a/embassy-net-w5500/src/lib.rs +++ b/embassy-net-w5500/src/lib.rs @@ -2,7 +2,6 @@ #![no_std] mod device; -mod socket; mod spi; use embassy_futures::select::{select, Either}; diff --git a/embassy-net-w5500/src/socket.rs b/embassy-net-w5500/src/socket.rs deleted file mode 100644 index 1c96cca1..00000000 --- a/embassy-net-w5500/src/socket.rs +++ /dev/null @@ -1,78 +0,0 @@ -use embedded_hal_async::spi::SpiDevice; - -use crate::device::RegisterBlock; -use crate::spi::{Address, SpiInterface}; - -pub const MODE: Address = (RegisterBlock::Socket0, 0x00); -pub const COMMAND: Address = (RegisterBlock::Socket0, 0x01); -pub const RXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1E); -pub const TXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1F); -pub const TX_FREE_SIZE: Address = (RegisterBlock::Socket0, 0x20); -pub const TX_DATA_WRITE_PTR: Address = (RegisterBlock::Socket0, 0x24); -pub const RECVD_SIZE: Address = (RegisterBlock::Socket0, 0x26); -pub const RX_DATA_READ_PTR: Address = (RegisterBlock::Socket0, 0x28); -pub const SOCKET_INTR_MASK: Address = (RegisterBlock::Socket0, 0x2C); -pub const INTR: Address = (RegisterBlock::Socket0, 0x02); - -#[repr(u8)] -pub enum Command { - Open = 0x01, - Send = 0x20, - Receive = 0x40, -} - -#[repr(u8)] -pub enum Interrupt { - Receive = 0b00100_u8, -} - -pub async fn reset_interrupt(bus: &mut SpiInterface, code: Interrupt) -> Result<(), SPI::Error> { - let data = [code as u8]; - bus.write_frame(INTR, &data).await -} - -pub async fn get_tx_write_ptr(bus: &mut SpiInterface) -> Result { - let mut data = [0u8; 2]; - bus.read_frame(TX_DATA_WRITE_PTR, &mut data).await?; - Ok(u16::from_be_bytes(data)) -} - -pub async fn set_tx_write_ptr(bus: &mut SpiInterface, ptr: u16) -> Result<(), SPI::Error> { - let data = ptr.to_be_bytes(); - bus.write_frame(TX_DATA_WRITE_PTR, &data).await -} - -pub async fn get_rx_read_ptr(bus: &mut SpiInterface) -> Result { - let mut data = [0u8; 2]; - bus.read_frame(RX_DATA_READ_PTR, &mut data).await?; - Ok(u16::from_be_bytes(data)) -} - -pub async fn set_rx_read_ptr(bus: &mut SpiInterface, ptr: u16) -> Result<(), SPI::Error> { - let data = ptr.to_be_bytes(); - bus.write_frame(RX_DATA_READ_PTR, &data).await -} - -pub async fn command(bus: &mut SpiInterface, command: Command) -> Result<(), SPI::Error> { - let data = [command as u8]; - bus.write_frame(COMMAND, &data).await -} - -pub async fn get_rx_size(bus: &mut SpiInterface) -> Result { - loop { - // Wait until two sequential reads are equal - let mut res0 = [0u8; 2]; - bus.read_frame(RECVD_SIZE, &mut res0).await?; - let mut res1 = [0u8; 2]; - bus.read_frame(RECVD_SIZE, &mut res1).await?; - if res0 == res1 { - break Ok(u16::from_be_bytes(res0)); - } - } -} - -pub async fn get_tx_free_size(bus: &mut SpiInterface) -> Result { - let mut data = [0; 2]; - bus.read_frame(TX_FREE_SIZE, &mut data).await?; - Ok(u16::from_be_bytes(data)) -} From 76276c326aaa4fd64a73253a480e2ea22f5ff740 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 17:50:56 +0200 Subject: [PATCH 251/298] net-w5500: extract chip-specific stuff to a trait. --- embassy-net-w5500/src/chip/mod.rs | 41 ++++++++ embassy-net-w5500/src/chip/w5500.rs | 67 +++++++++++++ embassy-net-w5500/src/device.rs | 94 +++++++++---------- embassy-net-w5500/src/lib.rs | 19 ++-- embassy-net-w5500/src/spi.rs | 34 ------- .../rp/src/bin/ethernet_w5500_multisocket.rs | 2 + .../rp/src/bin/ethernet_w5500_tcp_client.rs | 2 + .../rp/src/bin/ethernet_w5500_tcp_server.rs | 2 + examples/rp/src/bin/ethernet_w5500_udp.rs | 2 + 9 files changed, 169 insertions(+), 94 deletions(-) create mode 100644 embassy-net-w5500/src/chip/mod.rs create mode 100644 embassy-net-w5500/src/chip/w5500.rs delete mode 100644 embassy-net-w5500/src/spi.rs diff --git a/embassy-net-w5500/src/chip/mod.rs b/embassy-net-w5500/src/chip/mod.rs new file mode 100644 index 00000000..b7d550e2 --- /dev/null +++ b/embassy-net-w5500/src/chip/mod.rs @@ -0,0 +1,41 @@ +mod w5500; +pub use w5500::W5500; + +pub(crate) mod sealed { + use embedded_hal_async::spi::SpiDevice; + + pub trait Chip { + type Address; + + const COMMON_MODE: Self::Address; + const COMMON_MAC: Self::Address; + const COMMON_SOCKET_INTR: Self::Address; + const COMMON_PHY_CFG: Self::Address; + const SOCKET_MODE: Self::Address; + const SOCKET_COMMAND: Self::Address; + const SOCKET_RXBUF_SIZE: Self::Address; + const SOCKET_TXBUF_SIZE: Self::Address; + const SOCKET_TX_FREE_SIZE: Self::Address; + const SOCKET_TX_DATA_WRITE_PTR: Self::Address; + const SOCKET_RECVD_SIZE: Self::Address; + const SOCKET_RX_DATA_READ_PTR: Self::Address; + const SOCKET_INTR_MASK: Self::Address; + const SOCKET_INTR: Self::Address; + + fn rx_addr(addr: u16) -> Self::Address; + fn tx_addr(addr: u16) -> Self::Address; + + async fn bus_read( + spi: &mut SPI, + address: Self::Address, + data: &mut [u8], + ) -> Result<(), SPI::Error>; + async fn bus_write( + spi: &mut SPI, + address: Self::Address, + data: &[u8], + ) -> Result<(), SPI::Error>; + } +} + +pub trait Chip: sealed::Chip {} diff --git a/embassy-net-w5500/src/chip/w5500.rs b/embassy-net-w5500/src/chip/w5500.rs new file mode 100644 index 00000000..f514e12a --- /dev/null +++ b/embassy-net-w5500/src/chip/w5500.rs @@ -0,0 +1,67 @@ +use embedded_hal_async::spi::{Operation, SpiDevice}; + +#[repr(u8)] +pub enum RegisterBlock { + Common = 0x00, + Socket0 = 0x01, + TxBuf = 0x02, + RxBuf = 0x03, +} + +pub enum W5500 {} + +impl super::Chip for W5500 {} +impl super::sealed::Chip for W5500 { + type Address = (RegisterBlock, u16); + + const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x00); + const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x09); + const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x18); + const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x2E); + + const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x00); + const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x01); + const SOCKET_RXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1E); + const SOCKET_TXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1F); + const SOCKET_TX_FREE_SIZE: Self::Address = (RegisterBlock::Socket0, 0x20); + const SOCKET_TX_DATA_WRITE_PTR: Self::Address = (RegisterBlock::Socket0, 0x24); + const SOCKET_RECVD_SIZE: Self::Address = (RegisterBlock::Socket0, 0x26); + const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x28); + const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C); + const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02); + + fn rx_addr(addr: u16) -> Self::Address { + (RegisterBlock::RxBuf, addr) + } + + fn tx_addr(addr: u16) -> Self::Address { + (RegisterBlock::TxBuf, addr) + } + + async fn bus_read( + spi: &mut SPI, + address: Self::Address, + data: &mut [u8], + ) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3]; + let operations = &mut [ + Operation::Write(&address_phase), + Operation::Write(&control_phase), + Operation::TransferInPlace(data), + ]; + spi.transaction(operations).await + } + + async fn bus_write(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> { + let address_phase = address.1.to_be_bytes(); + let control_phase = [(address.0 as u8) << 3 | 0b0000_0100]; + let data_phase = data; + let operations = &mut [ + Operation::Write(&address_phase[..]), + Operation::Write(&control_phase), + Operation::Write(&data_phase), + ]; + spi.transaction(operations).await + } +} diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs index cf451b33..a6ee8f8f 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-w5500/src/device.rs @@ -1,117 +1,107 @@ +use core::marker::PhantomData; + use embedded_hal_async::spi::SpiDevice; -use crate::spi::{Address, SpiInterface}; - -pub const COMMON_MODE: Address = (RegisterBlock::Common, 0x00); -pub const COMMON_MAC: Address = (RegisterBlock::Common, 0x09); -pub const COMMON_SOCKET_INTR: Address = (RegisterBlock::Common, 0x18); -pub const COMMON_PHY_CFG: Address = (RegisterBlock::Common, 0x2E); - -pub const SOCKET_MODE: Address = (RegisterBlock::Socket0, 0x00); -pub const SOCKET_COMMAND: Address = (RegisterBlock::Socket0, 0x01); -pub const SOCKET_RXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1E); -pub const SOCKET_TXBUF_SIZE: Address = (RegisterBlock::Socket0, 0x1F); -pub const SOCKET_TX_FREE_SIZE: Address = (RegisterBlock::Socket0, 0x20); -pub const SOCKET_TX_DATA_WRITE_PTR: Address = (RegisterBlock::Socket0, 0x24); -pub const SOCKET_RECVD_SIZE: Address = (RegisterBlock::Socket0, 0x26); -pub const SOCKET_RX_DATA_READ_PTR: Address = (RegisterBlock::Socket0, 0x28); -pub const SOCKET_INTR_MASK: Address = (RegisterBlock::Socket0, 0x2C); -pub const SOCKET_INTR: Address = (RegisterBlock::Socket0, 0x02); +use crate::chip::Chip; #[repr(u8)] -pub enum Command { +enum Command { Open = 0x01, Send = 0x20, Receive = 0x40, } #[repr(u8)] -pub enum Interrupt { +enum Interrupt { Receive = 0b00100_u8, } -#[repr(u8)] -pub enum RegisterBlock { - Common = 0x00, - Socket0 = 0x01, - TxBuf = 0x02, - RxBuf = 0x03, -} - /// W5500 in MACRAW mode #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct W5500 { - bus: SpiInterface, +pub(crate) struct WiznetDevice { + spi: SPI, + _phantom: PhantomData, } -impl W5500 { +impl WiznetDevice { /// Create and initialize the W5500 driver - pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result, SPI::Error> { - let mut bus = SpiInterface(spi); + pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result { + let mut this = Self { + spi, + _phantom: PhantomData, + }; + // Reset device - bus.write_frame(COMMON_MODE, &[0x80]).await?; + this.bus_write(C::COMMON_MODE, &[0x80]).await?; // Enable interrupt pin - bus.write_frame(COMMON_SOCKET_INTR, &[0x01]).await?; + this.bus_write(C::COMMON_SOCKET_INTR, &[0x01]).await?; // Enable receive interrupt - bus.write_frame(SOCKET_INTR_MASK, &[Interrupt::Receive as u8]).await?; + this.bus_write(C::SOCKET_INTR_MASK, &[Interrupt::Receive as u8]).await?; // Set MAC address - bus.write_frame(COMMON_MAC, &mac_addr).await?; + this.bus_write(C::COMMON_MAC, &mac_addr).await?; // Set the raw socket RX/TX buffer sizes to 16KB - bus.write_frame(SOCKET_TXBUF_SIZE, &[16]).await?; - bus.write_frame(SOCKET_RXBUF_SIZE, &[16]).await?; + this.bus_write(C::SOCKET_TXBUF_SIZE, &[16]).await?; + this.bus_write(C::SOCKET_RXBUF_SIZE, &[16]).await?; // MACRAW mode with MAC filtering. let mode: u8 = (1 << 2) | (1 << 7); - bus.write_frame(SOCKET_MODE, &[mode]).await?; - let mut this = Self { bus }; + this.bus_write(C::SOCKET_MODE, &[mode]).await?; this.command(Command::Open).await?; Ok(this) } + async fn bus_read(&mut self, address: C::Address, data: &mut [u8]) -> Result<(), SPI::Error> { + C::bus_read(&mut self.spi, address, data).await + } + + async fn bus_write(&mut self, address: C::Address, data: &[u8]) -> Result<(), SPI::Error> { + C::bus_write(&mut self.spi, address, data).await + } + async fn reset_interrupt(&mut self, code: Interrupt) -> Result<(), SPI::Error> { let data = [code as u8]; - self.bus.write_frame(SOCKET_INTR, &data).await + self.bus_write(C::SOCKET_INTR, &data).await } async fn get_tx_write_ptr(&mut self) -> Result { let mut data = [0u8; 2]; - self.bus.read_frame(SOCKET_TX_DATA_WRITE_PTR, &mut data).await?; + self.bus_read(C::SOCKET_TX_DATA_WRITE_PTR, &mut data).await?; Ok(u16::from_be_bytes(data)) } async fn set_tx_write_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> { let data = ptr.to_be_bytes(); - self.bus.write_frame(SOCKET_TX_DATA_WRITE_PTR, &data).await + self.bus_write(C::SOCKET_TX_DATA_WRITE_PTR, &data).await } async fn get_rx_read_ptr(&mut self) -> Result { let mut data = [0u8; 2]; - self.bus.read_frame(SOCKET_RX_DATA_READ_PTR, &mut data).await?; + self.bus_read(C::SOCKET_RX_DATA_READ_PTR, &mut data).await?; Ok(u16::from_be_bytes(data)) } async fn set_rx_read_ptr(&mut self, ptr: u16) -> Result<(), SPI::Error> { let data = ptr.to_be_bytes(); - self.bus.write_frame(SOCKET_RX_DATA_READ_PTR, &data).await + self.bus_write(C::SOCKET_RX_DATA_READ_PTR, &data).await } async fn command(&mut self, command: Command) -> Result<(), SPI::Error> { let data = [command as u8]; - self.bus.write_frame(SOCKET_COMMAND, &data).await + self.bus_write(C::SOCKET_COMMAND, &data).await } async fn get_rx_size(&mut self) -> Result { loop { // Wait until two sequential reads are equal let mut res0 = [0u8; 2]; - self.bus.read_frame(SOCKET_RECVD_SIZE, &mut res0).await?; + self.bus_read(C::SOCKET_RECVD_SIZE, &mut res0).await?; let mut res1 = [0u8; 2]; - self.bus.read_frame(SOCKET_RECVD_SIZE, &mut res1).await?; + self.bus_read(C::SOCKET_RECVD_SIZE, &mut res1).await?; if res0 == res1 { break Ok(u16::from_be_bytes(res0)); } @@ -120,13 +110,13 @@ impl W5500 { async fn get_tx_free_size(&mut self) -> Result { let mut data = [0; 2]; - self.bus.read_frame(SOCKET_TX_FREE_SIZE, &mut data).await?; + self.bus_read(C::SOCKET_TX_FREE_SIZE, &mut data).await?; Ok(u16::from_be_bytes(data)) } /// Read bytes from the RX buffer. Returns the number of bytes read. async fn read_bytes(&mut self, read_ptr: &mut u16, buffer: &mut [u8]) -> Result<(), SPI::Error> { - self.bus.read_frame((RegisterBlock::RxBuf, *read_ptr), buffer).await?; + self.bus_read(C::rx_addr(*read_ptr), buffer).await?; *read_ptr = (*read_ptr).wrapping_add(buffer.len() as u16); Ok(()) @@ -165,7 +155,7 @@ impl W5500 { pub async fn write_frame(&mut self, frame: &[u8]) -> Result { while self.get_tx_free_size().await? < frame.len() as u16 {} let write_ptr = self.get_tx_write_ptr().await?; - self.bus.write_frame((RegisterBlock::TxBuf, write_ptr), frame).await?; + self.bus_write(C::tx_addr(write_ptr), frame).await?; self.set_tx_write_ptr(write_ptr.wrapping_add(frame.len() as u16)) .await?; self.command(Command::Send).await?; @@ -174,7 +164,7 @@ impl W5500 { pub async fn is_link_up(&mut self) -> bool { let mut link = [0]; - self.bus.read_frame(COMMON_PHY_CFG, &mut link).await.ok(); + self.bus_read(C::COMMON_PHY_CFG, &mut link).await.ok(); link[0] & 1 == 1 } } diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs index 3c54777d..9b53e961 100644 --- a/embassy-net-w5500/src/lib.rs +++ b/embassy-net-w5500/src/lib.rs @@ -1,8 +1,9 @@ //! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. #![no_std] +#![feature(async_fn_in_trait)] +pub mod chip; mod device; -mod spi; use embassy_futures::select::{select, Either}; use embassy_net_driver_channel as ch; @@ -12,7 +13,9 @@ use embedded_hal::digital::OutputPin; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::SpiDevice; -use crate::device::W5500; +use crate::chip::Chip; +use crate::device::WiznetDevice; + const MTU: usize = 1514; /// Type alias for the embassy-net driver for W5500 @@ -35,15 +38,15 @@ impl State { /// Background runner for the W5500. /// /// You must call `.run()` in a background task for the W5500 to operate. -pub struct Runner<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> { - mac: W5500, +pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> { + mac: WiznetDevice, ch: ch::Runner<'d, MTU>, int: INT, _reset: RST, } /// You must call this in a background task for the W5500 to operate. -impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { +impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> { pub async fn run(mut self) -> ! { let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); loop { @@ -78,13 +81,13 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> { } /// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net). -pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>( +pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin>( mac_addr: [u8; 6], state: &'a mut State, spi_dev: SPI, int: INT, mut reset: RST, -) -> (Device<'a>, Runner<'a, SPI, INT, RST>) { +) -> (Device<'a>, Runner<'a, C, SPI, INT, RST>) { // Reset the W5500. reset.set_low().ok(); // Ensure the reset is registered. @@ -93,7 +96,7 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: // Wait for the W5500 to achieve PLL lock. Timer::after(Duration::from_millis(2)).await; - let mac = W5500::new(spi_dev, mac_addr).await.unwrap(); + let mac = WiznetDevice::new(spi_dev, mac_addr).await.unwrap(); let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr)); ( diff --git a/embassy-net-w5500/src/spi.rs b/embassy-net-w5500/src/spi.rs deleted file mode 100644 index 316c6521..00000000 --- a/embassy-net-w5500/src/spi.rs +++ /dev/null @@ -1,34 +0,0 @@ -use embedded_hal_async::spi::{Operation, SpiDevice}; - -use crate::device::RegisterBlock; - -pub type Address = (RegisterBlock, u16); - -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct SpiInterface(pub SPI); - -impl SpiInterface { - pub async fn read_frame(&mut self, address: Address, data: &mut [u8]) -> Result<(), SPI::Error> { - let address_phase = address.1.to_be_bytes(); - let control_phase = [(address.0 as u8) << 3]; - let operations = &mut [ - Operation::Write(&address_phase), - Operation::Write(&control_phase), - Operation::TransferInPlace(data), - ]; - self.0.transaction(operations).await - } - - pub async fn write_frame(&mut self, address: Address, data: &[u8]) -> Result<(), SPI::Error> { - let address_phase = address.1.to_be_bytes(); - let control_phase = [(address.0 as u8) << 3 | 0b0000_0100]; - let data_phase = data; - let operations = &mut [ - Operation::Write(&address_phase[..]), - Operation::Write(&control_phase), - Operation::Write(&data_phase), - ]; - self.0.transaction(operations).await - } -} diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index 9f800d0d..3677f3cd 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -10,6 +10,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::chip::W5500; use embassy_net_w5500::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; @@ -26,6 +27,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index fee84b61..b78a0977 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -12,6 +12,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::chip::W5500; use embassy_net_w5500::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; @@ -28,6 +29,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index 02457426..34f054d9 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -11,6 +11,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::chip::W5500; use embassy_net_w5500::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; @@ -26,6 +27,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index 038432b1..8f38e453 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -11,6 +11,7 @@ use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::udp::{PacketMetadata, UdpSocket}; use embassy_net::{Stack, StackResources}; +use embassy_net_w5500::chip::W5500; use embassy_net_w5500::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; @@ -25,6 +26,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, + W5500, ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, From 1d4b941d52a7e257b0305935034e999facb537bd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 18:19:03 +0200 Subject: [PATCH 252/298] net-w5500: add w5100s support. --- embassy-net-w5500/Cargo.toml | 3 +- embassy-net-w5500/src/chip/mod.rs | 7 ++++ embassy-net-w5500/src/chip/w5100s.rs | 61 ++++++++++++++++++++++++++++ embassy-net-w5500/src/chip/w5500.rs | 5 +++ embassy-net-w5500/src/device.rs | 41 +++++++++++++++---- embassy-net-w5500/src/lib.rs | 24 +++++++---- 6 files changed, 123 insertions(+), 18 deletions(-) create mode 100644 embassy-net-w5500/src/chip/w5100s.rs diff --git a/embassy-net-w5500/Cargo.toml b/embassy-net-w5500/Cargo.toml index 8972b814..c4559868 100644 --- a/embassy-net-w5500/Cargo.toml +++ b/embassy-net-w5500/Cargo.toml @@ -18,4 +18,5 @@ defmt = { version = "0.3", optional = true } [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-w5500-v$VERSION/embassy-net-w5500/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-w5500/src/" -target = "thumbv7em-none-eabi" \ No newline at end of file +target = "thumbv7em-none-eabi" +features = ["defmt"] \ No newline at end of file diff --git a/embassy-net-w5500/src/chip/mod.rs b/embassy-net-w5500/src/chip/mod.rs index b7d550e2..562db515 100644 --- a/embassy-net-w5500/src/chip/mod.rs +++ b/embassy-net-w5500/src/chip/mod.rs @@ -1,5 +1,7 @@ mod w5500; pub use w5500::W5500; +mod w5100s; +pub use w5100s::W5100S; pub(crate) mod sealed { use embedded_hal_async::spi::SpiDevice; @@ -22,6 +24,11 @@ pub(crate) mod sealed { const SOCKET_INTR_MASK: Self::Address; const SOCKET_INTR: Self::Address; + const SOCKET_MODE_VALUE: u8; + + const BUF_SIZE: u16; + const AUTO_WRAP: bool; + fn rx_addr(addr: u16) -> Self::Address; fn tx_addr(addr: u16) -> Self::Address; diff --git a/embassy-net-w5500/src/chip/w5100s.rs b/embassy-net-w5500/src/chip/w5100s.rs new file mode 100644 index 00000000..07a84037 --- /dev/null +++ b/embassy-net-w5500/src/chip/w5100s.rs @@ -0,0 +1,61 @@ +use embedded_hal_async::spi::{Operation, SpiDevice}; + +const SOCKET_BASE: u16 = 0x400; +const TX_BASE: u16 = 0x4000; +const RX_BASE: u16 = 0x6000; + +pub enum W5100S {} + +impl super::Chip for W5100S {} +impl super::sealed::Chip for W5100S { + type Address = u16; + + const COMMON_MODE: Self::Address = 0x00; + const COMMON_MAC: Self::Address = 0x09; + const COMMON_SOCKET_INTR: Self::Address = 0x16; + const COMMON_PHY_CFG: Self::Address = 0x3c; + + const SOCKET_MODE: Self::Address = SOCKET_BASE + 0x00; + const SOCKET_COMMAND: Self::Address = SOCKET_BASE + 0x01; + const SOCKET_RXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1E; + const SOCKET_TXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1F; + const SOCKET_TX_FREE_SIZE: Self::Address = SOCKET_BASE + 0x20; + const SOCKET_TX_DATA_WRITE_PTR: Self::Address = SOCKET_BASE + 0x24; + const SOCKET_RECVD_SIZE: Self::Address = SOCKET_BASE + 0x26; + const SOCKET_RX_DATA_READ_PTR: Self::Address = SOCKET_BASE + 0x28; + const SOCKET_INTR_MASK: Self::Address = SOCKET_BASE + 0x2C; + const SOCKET_INTR: Self::Address = SOCKET_BASE + 0x02; + + const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 6); + + const BUF_SIZE: u16 = 0x2000; + const AUTO_WRAP: bool = false; + + fn rx_addr(addr: u16) -> Self::Address { + RX_BASE + addr + } + + fn tx_addr(addr: u16) -> Self::Address { + TX_BASE + addr + } + + async fn bus_read( + spi: &mut SPI, + address: Self::Address, + data: &mut [u8], + ) -> Result<(), SPI::Error> { + spi.transaction(&mut [ + Operation::Write(&[0x0F, (address >> 8) as u8, address as u8]), + Operation::Read(data), + ]) + .await + } + + async fn bus_write(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> { + spi.transaction(&mut [ + Operation::Write(&[0xF0, (address >> 8) as u8, address as u8]), + Operation::Write(data), + ]) + .await + } +} diff --git a/embassy-net-w5500/src/chip/w5500.rs b/embassy-net-w5500/src/chip/w5500.rs index f514e12a..61e51294 100644 --- a/embassy-net-w5500/src/chip/w5500.rs +++ b/embassy-net-w5500/src/chip/w5500.rs @@ -30,6 +30,11 @@ impl super::sealed::Chip for W5500 { const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C); const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02); + const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7); + + const BUF_SIZE: u16 = 0x4000; + const AUTO_WRAP: bool = true; + fn rx_addr(addr: u16) -> Self::Address { (RegisterBlock::RxBuf, addr) } diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs index a6ee8f8f..f367bc3e 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-w5500/src/device.rs @@ -43,13 +43,13 @@ impl WiznetDevice { // Set MAC address this.bus_write(C::COMMON_MAC, &mac_addr).await?; - // Set the raw socket RX/TX buffer sizes to 16KB - this.bus_write(C::SOCKET_TXBUF_SIZE, &[16]).await?; - this.bus_write(C::SOCKET_RXBUF_SIZE, &[16]).await?; + // Set the raw socket RX/TX buffer sizes. + let buf_kbs = (C::BUF_SIZE / 1024) as u8; + this.bus_write(C::SOCKET_TXBUF_SIZE, &[buf_kbs]).await?; + this.bus_write(C::SOCKET_RXBUF_SIZE, &[buf_kbs]).await?; // MACRAW mode with MAC filtering. - let mode: u8 = (1 << 2) | (1 << 7); - this.bus_write(C::SOCKET_MODE, &[mode]).await?; + this.bus_write(C::SOCKET_MODE, &[C::SOCKET_MODE_VALUE]).await?; this.command(Command::Open).await?; Ok(this) @@ -114,9 +114,21 @@ impl WiznetDevice { Ok(u16::from_be_bytes(data)) } - /// Read bytes from the RX buffer. Returns the number of bytes read. + /// Read bytes from the RX buffer. async fn read_bytes(&mut self, read_ptr: &mut u16, buffer: &mut [u8]) -> Result<(), SPI::Error> { - self.bus_read(C::rx_addr(*read_ptr), buffer).await?; + if C::AUTO_WRAP { + self.bus_read(C::rx_addr(*read_ptr), buffer).await?; + } else { + let addr = *read_ptr % C::BUF_SIZE; + if addr as usize + buffer.len() <= C::BUF_SIZE as usize { + self.bus_read(C::rx_addr(addr), buffer).await?; + } else { + let n = C::BUF_SIZE - addr; + self.bus_read(C::rx_addr(addr), &mut buffer[..n as usize]).await?; + self.bus_read(C::rx_addr(0), &mut buffer[n as usize..]).await?; + } + } + *read_ptr = (*read_ptr).wrapping_add(buffer.len() as u16); Ok(()) @@ -155,7 +167,20 @@ impl WiznetDevice { pub async fn write_frame(&mut self, frame: &[u8]) -> Result { while self.get_tx_free_size().await? < frame.len() as u16 {} let write_ptr = self.get_tx_write_ptr().await?; - self.bus_write(C::tx_addr(write_ptr), frame).await?; + + if C::AUTO_WRAP { + self.bus_write(C::tx_addr(write_ptr), frame).await?; + } else { + let addr = write_ptr % C::BUF_SIZE; + if addr as usize + frame.len() <= C::BUF_SIZE as usize { + self.bus_write(C::tx_addr(addr), frame).await?; + } else { + let n = C::BUF_SIZE - addr; + self.bus_write(C::tx_addr(addr), &frame[..n as usize]).await?; + self.bus_write(C::tx_addr(0), &frame[n as usize..]).await?; + } + } + self.set_tx_write_ptr(write_ptr.wrapping_add(frame.len() as u16)) .await?; self.command(Command::Send).await?; diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs index 9b53e961..3030dfb9 100644 --- a/embassy-net-w5500/src/lib.rs +++ b/embassy-net-w5500/src/lib.rs @@ -1,4 +1,4 @@ -//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. +//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for WIZnet ethernet chips. #![no_std] #![feature(async_fn_in_trait)] @@ -18,7 +18,7 @@ use crate::device::WiznetDevice; const MTU: usize = 1514; -/// Type alias for the embassy-net driver for W5500 +/// Type alias for the embassy-net driver. pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>; /// Internal state for the embassy-net integration. @@ -35,9 +35,9 @@ impl State { } } -/// Background runner for the W5500. +/// Background runner for the driver. /// -/// You must call `.run()` in a background task for the W5500 to operate. +/// You must call `.run()` in a background task for the driver to operate. pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> { mac: WiznetDevice, ch: ch::Runner<'d, MTU>, @@ -45,7 +45,7 @@ pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> { _reset: RST, } -/// You must call this in a background task for the W5500 to operate. +/// You must call this in a background task for the driver to operate. impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> { pub async fn run(mut self) -> ! { let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); @@ -80,7 +80,11 @@ impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, } } -/// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net). +/// Create a Wiznet ethernet chip driver for [`embassy-net`](https://crates.io/crates/embassy-net). +/// +/// This returns two structs: +/// - a `Device` that you must pass to the `embassy-net` stack. +/// - a `Runner`. You must call `.run()` on it in a background task. pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin>( mac_addr: [u8; 6], state: &'a mut State, @@ -88,13 +92,15 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevi int: INT, mut reset: RST, ) -> (Device<'a>, Runner<'a, C, SPI, INT, RST>) { - // Reset the W5500. + // Reset the chip. reset.set_low().ok(); // Ensure the reset is registered. Timer::after(Duration::from_millis(1)).await; reset.set_high().ok(); - // Wait for the W5500 to achieve PLL lock. - Timer::after(Duration::from_millis(2)).await; + + // Wait for PLL lock. Some chips are slower than others. + // Slowest is w5100s which is 100ms, so let's just wait that. + Timer::after(Duration::from_millis(100)).await; let mac = WiznetDevice::new(spi_dev, mac_addr).await.unwrap(); From 11b66a73b4fc85469eeb8f718531492aed128e26 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 23:02:53 +0200 Subject: [PATCH 253/298] net-wiznet: rename from embassy-net-w5500. --- .github/ci/doc.sh | 2 +- embassy-net-driver-channel/README.md | 2 +- embassy-net-w5500/README.md | 7 ----- .../Cargo.toml | 10 +++---- embassy-net-wiznet/README.md | 27 +++++++++++++++++++ .../src/chip/mod.rs | 0 .../src/chip/w5100s.rs | 0 .../src/chip/w5500.rs | 0 .../src/device.rs | 4 +-- .../src/lib.rs | 0 embassy-net/README.md | 2 +- examples/rp/Cargo.toml | 2 +- .../rp/src/bin/ethernet_w5500_multisocket.rs | 6 ++--- .../rp/src/bin/ethernet_w5500_tcp_client.rs | 6 ++--- .../rp/src/bin/ethernet_w5500_tcp_server.rs | 7 ++--- examples/rp/src/bin/ethernet_w5500_udp.rs | 7 ++--- 16 files changed, 52 insertions(+), 30 deletions(-) delete mode 100644 embassy-net-w5500/README.md rename {embassy-net-w5500 => embassy-net-wiznet}/Cargo.toml (74%) create mode 100644 embassy-net-wiznet/README.md rename {embassy-net-w5500 => embassy-net-wiznet}/src/chip/mod.rs (100%) rename {embassy-net-w5500 => embassy-net-wiznet}/src/chip/w5100s.rs (100%) rename {embassy-net-w5500 => embassy-net-wiznet}/src/chip/w5500.rs (100%) rename {embassy-net-w5500 => embassy-net-wiznet}/src/device.rs (98%) rename {embassy-net-w5500 => embassy-net-wiznet}/src/lib.rs (100%) diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index f829582a..57184dc1 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -35,7 +35,7 @@ docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/g docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup -docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup +docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static diff --git a/embassy-net-driver-channel/README.md b/embassy-net-driver-channel/README.md index dd90e7ad..8f904ce9 100644 --- a/embassy-net-driver-channel/README.md +++ b/embassy-net-driver-channel/README.md @@ -76,7 +76,7 @@ These `embassy-net` drivers are implemented using this crate. You can look at th - [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W - [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support. -- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip. +- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips. - [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. diff --git a/embassy-net-w5500/README.md b/embassy-net-w5500/README.md deleted file mode 100644 index 9eaf4b70..00000000 --- a/embassy-net-w5500/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# WIZnet W5500 `embassy-net` integration - -[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet W5500 SPI ethernet chip, operating in MACRAW mode. - -Supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async) - -See [`examples`](https://github.com/kalkyl/embassy-net-w5500/tree/main/examples) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module. \ No newline at end of file diff --git a/embassy-net-w5500/Cargo.toml b/embassy-net-wiznet/Cargo.toml similarity index 74% rename from embassy-net-w5500/Cargo.toml rename to embassy-net-wiznet/Cargo.toml index c4559868..dff03ac8 100644 --- a/embassy-net-w5500/Cargo.toml +++ b/embassy-net-wiznet/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "embassy-net-w5500" +name = "embassy-net-wiznet" version = "0.1.0" -description = "embassy-net driver for the W5500 ethernet chip" -keywords = ["embedded", "w5500", "embassy-net", "embedded-hal-async", "ethernet", "async"] +description = "embassy-net driver for WIZnet SPI Ethernet chips" +keywords = ["embedded", "wiznet", "embassy-net", "embedded-hal-async", "ethernet", "async"] categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"] license = "MIT OR Apache-2.0" edition = "2021" @@ -16,7 +16,7 @@ embassy-futures = { version = "0.1.0", path = "../embassy-futures" } defmt = { version = "0.3", optional = true } [package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-w5500-v$VERSION/embassy-net-w5500/src/" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-w5500/src/" +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-wiznet-v$VERSION/embassy-net-wiznet/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-wiznet/src/" target = "thumbv7em-none-eabi" features = ["defmt"] \ No newline at end of file diff --git a/embassy-net-wiznet/README.md b/embassy-net-wiznet/README.md new file mode 100644 index 00000000..b8e4bdc8 --- /dev/null +++ b/embassy-net-wiznet/README.md @@ -0,0 +1,27 @@ +# WIZnet `embassy-net` integration + +[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet SPI ethernet chips, operating in MACRAW mode. + +See [`examples`](https://github.com/embassy-rs/embassy/tree/main/examples/rp) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module. + +## Supported chips + +- W5500 +- W5100S + +## Interoperability + +This crate can run on any executor. + +It supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async). + + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. diff --git a/embassy-net-w5500/src/chip/mod.rs b/embassy-net-wiznet/src/chip/mod.rs similarity index 100% rename from embassy-net-w5500/src/chip/mod.rs rename to embassy-net-wiznet/src/chip/mod.rs diff --git a/embassy-net-w5500/src/chip/w5100s.rs b/embassy-net-wiznet/src/chip/w5100s.rs similarity index 100% rename from embassy-net-w5500/src/chip/w5100s.rs rename to embassy-net-wiznet/src/chip/w5100s.rs diff --git a/embassy-net-w5500/src/chip/w5500.rs b/embassy-net-wiznet/src/chip/w5500.rs similarity index 100% rename from embassy-net-w5500/src/chip/w5500.rs rename to embassy-net-wiznet/src/chip/w5500.rs diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-wiznet/src/device.rs similarity index 98% rename from embassy-net-w5500/src/device.rs rename to embassy-net-wiznet/src/device.rs index f367bc3e..43f9512a 100644 --- a/embassy-net-w5500/src/device.rs +++ b/embassy-net-wiznet/src/device.rs @@ -16,7 +16,7 @@ enum Interrupt { Receive = 0b00100_u8, } -/// W5500 in MACRAW mode +/// Wiznet chip in MACRAW mode #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub(crate) struct WiznetDevice { @@ -25,7 +25,7 @@ pub(crate) struct WiznetDevice { } impl WiznetDevice { - /// Create and initialize the W5500 driver + /// Create and initialize the driver pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result { let mut this = Self { spi, diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-wiznet/src/lib.rs similarity index 100% rename from embassy-net-w5500/src/lib.rs rename to embassy-net-wiznet/src/lib.rs diff --git a/embassy-net/README.md b/embassy-net/README.md index 811321ca..7bb2283c 100644 --- a/embassy-net/README.md +++ b/embassy-net/README.md @@ -22,7 +22,7 @@ unimplemented features of the network protocols. - [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W - [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support. - [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5). -- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip. +- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips (W5100S, W5500) - [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU. ## Examples diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index d83e370d..6742ce7e 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -13,7 +13,7 @@ embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["ni embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } -embassy-net-w5500 = { version = "0.1.0", path = "../../embassy-net-w5500", features = ["defmt"] } +embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"] } diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index 3677f3cd..d4af8764 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -10,8 +10,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; -use embassy_net_w5500::chip::W5500; -use embassy_net_w5500::*; +use embassy_net_wiznet::chip::W5500; +use embassy_net_wiznet::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; @@ -56,7 +56,7 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( + let (device, runner) = embassy_net_wiznet::new( mac_addr, state, ExclusiveDevice::new(spi, cs, Delay), diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index b78a0977..78340492 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -12,8 +12,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; -use embassy_net_w5500::chip::W5500; -use embassy_net_w5500::*; +use embassy_net_wiznet::chip::W5500; +use embassy_net_wiznet::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; @@ -59,7 +59,7 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( + let (device, runner) = embassy_net_wiznet::new( mac_addr, state, ExclusiveDevice::new(spi, cs, Delay), diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index 34f054d9..e6d13274 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -11,8 +11,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::{Stack, StackResources}; -use embassy_net_w5500::chip::W5500; -use embassy_net_w5500::*; +use embassy_net_wiznet::chip::W5500; +use embassy_net_wiznet::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; @@ -23,6 +23,7 @@ use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + #[embassy_executor::task] async fn ethernet_task( runner: Runner< @@ -57,7 +58,7 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( + let (device, runner) = embassy_net_wiznet::new( mac_addr, state, ExclusiveDevice::new(spi, cs, Delay), diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index 8f38e453..b546714d 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -11,8 +11,8 @@ use embassy_executor::Spawner; use embassy_futures::yield_now; use embassy_net::udp::{PacketMetadata, UdpSocket}; use embassy_net::{Stack, StackResources}; -use embassy_net_w5500::chip::W5500; -use embassy_net_w5500::*; +use embassy_net_wiznet::chip::W5500; +use embassy_net_wiznet::*; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; @@ -22,6 +22,7 @@ use embedded_hal_async::spi::ExclusiveDevice; use rand::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + #[embassy_executor::task] async fn ethernet_task( runner: Runner< @@ -55,7 +56,7 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = embassy_net_w5500::new( + let (device, runner) = embassy_net_wiznet::new( mac_addr, state, ExclusiveDevice::new(spi, cs, Delay), From f26dd54f6378be87a71f0c9f351b56851df96014 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 16 Aug 2023 00:40:56 +0200 Subject: [PATCH 254/298] Update embedded-hal to 1.0.0-rc.1 (#1783) --- cyw43/Cargo.toml | 2 +- embassy-embedded-hal/Cargo.toml | 4 +- .../src/adapter/blocking_async.rs | 11 +---- embassy-lora/Cargo.toml | 2 +- embassy-net-enc28j60/Cargo.toml | 4 +- embassy-net-esp-hosted/Cargo.toml | 4 +- embassy-net-wiznet/Cargo.toml | 4 +- embassy-nrf/Cargo.toml | 4 +- embassy-nrf/src/uarte.rs | 48 ------------------- embassy-rp/Cargo.toml | 6 +-- embassy-rp/src/uart/buffered.rs | 26 ++-------- embassy-rp/src/uart/mod.rs | 38 ++++----------- embassy-stm32/Cargo.toml | 6 +-- embassy-stm32/src/usart/buffered.rs | 26 ++-------- embassy-stm32/src/usart/mod.rs | 40 ++++------------ embassy-time/Cargo.toml | 4 +- examples/nrf52840/Cargo.toml | 10 ++-- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 2 +- examples/rp/Cargo.toml | 10 ++-- .../rp/src/bin/ethernet_w5500_multisocket.rs | 2 +- .../rp/src/bin/ethernet_w5500_tcp_client.rs | 2 +- .../rp/src/bin/ethernet_w5500_tcp_server.rs | 2 +- examples/rp/src/bin/ethernet_w5500_udp.rs | 2 +- examples/stm32h5/Cargo.toml | 4 +- examples/stm32h7/Cargo.toml | 4 +- examples/stm32l0/Cargo.toml | 3 ++ examples/stm32l4/Cargo.toml | 4 +- examples/stm32wl/Cargo.toml | 3 ++ tests/nrf/Cargo.toml | 3 +- tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 2 +- tests/rp/Cargo.toml | 4 +- tests/stm32/Cargo.toml | 4 +- 32 files changed, 84 insertions(+), 206 deletions(-) diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index 50fb7c5d..855d54b1 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -24,7 +24,7 @@ cortex-m = "0.7.6" cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.11" } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.1" } num_enum = { version = "0.5.7", default-features = false } [package.metadata.embassy_docs] diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index bba3d48b..fd921d27 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -25,8 +25,8 @@ embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ "unproven", ] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } nb = "1.0.0" diff --git a/embassy-embedded-hal/src/adapter/blocking_async.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs index 98ae2b02..ae0d0a7f 100644 --- a/embassy-embedded-hal/src/adapter/blocking_async.rs +++ b/embassy-embedded-hal/src/adapter/blocking_async.rs @@ -1,4 +1,4 @@ -use embedded_hal_02::{blocking, serial}; +use embedded_hal_02::blocking; /// Wrapper that implements async traits using blocking implementations. /// @@ -103,15 +103,6 @@ where } } -// Uart implementatinos -impl embedded_hal_1::serial::ErrorType for BlockingAsync -where - T: serial::Read, - E: embedded_hal_1::serial::Error + 'static, -{ - type Error = E; -} - /// NOR flash wrapper use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash}; diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 402ad2d7..90c544d8 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -23,7 +23,7 @@ log = { version = "0.4.14", optional = true } 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-async = { version = "=0.2.0-alpha.2" } +embedded-hal-async = { version = "=1.0.0-rc.1" } embedded-hal = { version = "0.2", features = ["unproven"] } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } diff --git a/embassy-net-enc28j60/Cargo.toml b/embassy-net-enc28j60/Cargo.toml index c502ed04..e02c984e 100644 --- a/embassy-net-enc28j60/Cargo.toml +++ b/embassy-net-enc28j60/Cargo.toml @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" edition = "2021" [dependencies] -embedded-hal = { version = "1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal = { version = "1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-time = { version = "0.1.2", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index 0053c49a..d334cf3f 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml @@ -12,8 +12,8 @@ embassy-sync = { version = "0.2.0", path = "../embassy-sync"} embassy-futures = { version = "0.1.0", path = "../embassy-futures"} embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} -embedded-hal = { version = "1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal = { version = "1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } #noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } diff --git a/embassy-net-wiznet/Cargo.toml b/embassy-net-wiznet/Cargo.toml index dff03ac8..adf0b45f 100644 --- a/embassy-net-wiznet/Cargo.toml +++ b/embassy-net-wiznet/Cargo.toml @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" edition = "2021" [dependencies] -embedded-hal = { version = "1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal = { version = "1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } embassy-time = { version = "0.1.2", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index da1cd38e..c4790f2c 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -98,8 +98,8 @@ embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} embedded-io = { version = "0.5.0" } embedded-io-async = { version = "0.5.0", optional = true } diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index e79df356..95434e7a 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -949,51 +949,3 @@ mod eh02 { } } } - -#[cfg(feature = "unstable-traits")] -mod eh1 { - use super::*; - - impl embedded_hal_1::serial::Error for Error { - fn kind(&self) -> embedded_hal_1::serial::ErrorKind { - match *self { - Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, - Self::BufferNotInRAM => embedded_hal_1::serial::ErrorKind::Other, - } - } - } - - // ===================== - - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for Uarte<'d, T> { - type Error = Error; - } - - impl<'d, T: Instance> embedded_hal_1::serial::Write for Uarte<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - } - - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteTx<'d, T> { - type Error = Error; - } - - impl<'d, T: Instance> embedded_hal_1::serial::Write for UarteTx<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - } - - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> { - type Error = Error; - } -} diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index b1680d48..b9ebcc86 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -85,9 +85,9 @@ fixed = "1.23.1" rp-pac = { version = "6" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} +embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true} paste = "1.0" pio-proc = {version= "0.2" } diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 58dc0bf1..e57b7259 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -749,15 +749,15 @@ mod eh02 { mod eh1 { use super::*; - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> { + impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartRx<'d, T> { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> { + impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUartTx<'d, T> { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> { + impl<'d, T: Instance> embedded_hal_nb::serial::ErrorType for BufferedUart<'d, T> { type Error = Error; } @@ -767,16 +767,6 @@ mod eh1 { } } - impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer).map(drop) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) @@ -793,16 +783,6 @@ mod eh1 { } } - impl<'d, T: Instance> embedded_hal_1::serial::Write for BufferedUart<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer).map(drop) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: Instance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 00070b80..202b0883 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -807,26 +807,26 @@ mod eh02 { mod eh1 { use super::*; - impl embedded_hal_1::serial::Error for Error { - fn kind(&self) -> embedded_hal_1::serial::ErrorKind { + impl embedded_hal_nb::serial::Error for Error { + fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { match *self { - Self::Framing => embedded_hal_1::serial::ErrorKind::FrameFormat, - Self::Break => embedded_hal_1::serial::ErrorKind::Other, - Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun, - Self::Parity => embedded_hal_1::serial::ErrorKind::Parity, + Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, + Self::Break => embedded_hal_nb::serial::ErrorKind::Other, + Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, + Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity, } } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for Uart<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, M> { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartTx<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, M> { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartRx<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::ErrorType for Uart<'d, T, M> { type Error = Error; } @@ -851,16 +851,6 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for UartTx<'d, T, M> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) @@ -877,16 +867,6 @@ mod eh1 { } } - impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for Uart<'d, T, M> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8f765e9d..c3f5706e 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -40,9 +40,9 @@ embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} +embedded-hal-nb = { version = "=1.0.0-rc.1", optional = true} embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 329fc7da..596d40bf 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -584,15 +584,15 @@ mod eh02 { mod eh1 { use super::*; - impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUart<'d, T> { + impl<'d, T: BasicInstance> embedded_hal_nb::serial::ErrorType for BufferedUart<'d, T> { type Error = Error; } - impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartTx<'d, T> { + impl<'d, T: BasicInstance> embedded_hal_nb::serial::ErrorType for BufferedUartTx<'d, T> { type Error = Error; } - impl<'d, T: BasicInstance> embedded_hal_1::serial::ErrorType for BufferedUartRx<'d, T> { + impl<'d, T: BasicInstance> embedded_hal_nb::serial::ErrorType for BufferedUartRx<'d, T> { type Error = Error; } @@ -602,16 +602,6 @@ mod eh1 { } } - impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUartTx<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer).map(drop) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUartTx<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) @@ -628,16 +618,6 @@ mod eh1 { } } - impl<'d, T: BasicInstance> embedded_hal_1::serial::Write for BufferedUart<'d, T> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.tx.blocking_write(buffer).map(drop) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.tx.blocking_flush() - } - } - impl<'d, T: BasicInstance> embedded_hal_nb::serial::Write for BufferedUart<'d, T> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.tx.blocking_write(&[char]).map(drop).map_err(nb::Error::Other) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 0c69a488..e203336e 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -943,27 +943,27 @@ mod eh02 { mod eh1 { use super::*; - impl embedded_hal_1::serial::Error for Error { - fn kind(&self) -> embedded_hal_1::serial::ErrorKind { + impl embedded_hal_nb::serial::Error for Error { + fn kind(&self) -> embedded_hal_nb::serial::ErrorKind { match *self { - Self::Framing => embedded_hal_1::serial::ErrorKind::FrameFormat, - Self::Noise => embedded_hal_1::serial::ErrorKind::Noise, - Self::Overrun => embedded_hal_1::serial::ErrorKind::Overrun, - Self::Parity => embedded_hal_1::serial::ErrorKind::Parity, - Self::BufferTooLong => embedded_hal_1::serial::ErrorKind::Other, + Self::Framing => embedded_hal_nb::serial::ErrorKind::FrameFormat, + Self::Noise => embedded_hal_nb::serial::ErrorKind::Noise, + Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, + Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity, + Self::BufferTooLong => embedded_hal_nb::serial::ErrorKind::Other, } } } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::ErrorType for Uart<'d, T, TxDma, RxDma> { + impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::ErrorType for Uart<'d, T, TxDma, RxDma> { type Error = Error; } - impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::ErrorType for UartTx<'d, T, TxDma> { + impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::ErrorType for UartTx<'d, T, TxDma> { type Error = Error; } - impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::ErrorType for UartRx<'d, T, RxDma> { + impl<'d, T: BasicInstance, RxDma> embedded_hal_nb::serial::ErrorType for UartRx<'d, T, RxDma> { type Error = Error; } @@ -973,16 +973,6 @@ mod eh1 { } } - impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::Write for UartTx<'d, T, TxDma> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::Write for UartTx<'d, T, TxDma> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) @@ -999,16 +989,6 @@ mod eh1 { } } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::Write for Uart<'d, T, TxDma, RxDma> { - fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() - } - } - impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Write for Uart<'d, T, TxDma, RxDma> { fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> { self.blocking_write(&[char]).map_err(nb::Error::Other) diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 0afb1103..ec1f2ec3 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -152,8 +152,8 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} +embedded-hal-async = { version = "=1.0.0-rc.1", optional = true} futures-util = { version = "0.3.17", default-features = false } atomic-polyfill = "1.0.1" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 8c6f6bcc..2ce44b51 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -19,6 +19,7 @@ nightly = [ "static_cell/nightly", "embassy-usb", "embedded-io-async", + "embedded-hal-bus/async", "embassy-net", "embassy-lora", "lora-phy", @@ -56,11 +57,14 @@ 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 } -embedded-hal = { version = "1.0.0-alpha.11" } -embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } -embedded-hal-bus = { version = "0.1.0-alpha.3" } +embedded-hal = { version = "1.0.0-rc.1" } +embedded-hal-async = { version = "1.0.0-rc.1", optional = true } +embedded-hal-bus = { version = "0.1.0-rc.1" } num-integer = { version = "0.1.45", default-features = false } microfft = "0.5.0" [profile.release] debug = 2 + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index e3b80d82..e114e50b 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -11,7 +11,7 @@ use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::Delay; -use embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io_async::Write; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 6742ce7e..102611bc 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -42,8 +42,9 @@ smart-leds = "0.3.0" heapless = "0.7.15" usbd-hid = "0.6.1" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = "0.2.0-alpha.2" +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = "1.0.0-rc.1" +embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] } embedded-io-async = { version = "0.5.0", features = ["defmt-03"] } embedded-storage = { version = "0.3" } static_cell = { version = "1.1", features = ["nightly"]} @@ -53,4 +54,7 @@ pio = "0.2.1" rand = { version = "0.8.5", default-features = false } [profile.release] -debug = 2 \ No newline at end of file +debug = 2 + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index d4af8764..c0fde62a 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -17,7 +17,7 @@ use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; use embassy_time::{Delay, Duration}; -use embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index 78340492..e593acae 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -19,7 +19,7 @@ use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; use embassy_time::{Delay, Duration, Timer}; -use embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index e6d13274..c62caed7 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -18,7 +18,7 @@ use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; use embassy_time::{Delay, Duration}; -use embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io_async::Write; use rand::RngCore; use static_cell::make_static; diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index b546714d..76dabce1 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -18,7 +18,7 @@ use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; use embassy_time::Delay; -use embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use rand::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 44d0a957..5d73e435 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -20,8 +20,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 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" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } embedded-nal-async = { version = "0.5.0" } panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index e2e5f936..c78c4c60 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -20,8 +20,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } 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" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } embedded-nal-async = { version = "0.5.0" } panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 33aa05e6..332a6c5e 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -37,3 +37,6 @@ static_cell = "1.1" [profile.release] debug = 2 + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 3b27d8e8..944c8c27 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -19,8 +19,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } 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 } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 48b69c8d..5440807f 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -30,3 +30,6 @@ chrono = { version = "^0.4", default-features = false } [profile.release] debug = 2 + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 974a9413..e2dab81a 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -15,7 +15,8 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm embedded-io-async = { version = "0.5.0" } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } -embedded-hal-async = { version = "0.2.0-alpha.2" } +embedded-hal-async = { version = "1.0.0-rc.1" } +embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] } static_cell = { version = "1.1", features = [ "nightly" ] } defmt = "0.3" diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index e2adfe0b..16055fae 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -15,7 +15,7 @@ use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::{with_timeout, Delay, Duration, Timer}; -use embedded_hal_async::spi::ExclusiveDevice; +use embedded_hal_bus::spi::ExclusiveDevice; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 90a3bd0c..c69bf7a8 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -22,8 +22,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6" } 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" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } embedded-io-async = { version = "0.5.0" } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 17320649..e2638897 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -41,8 +41,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } -embedded-hal-async = { version = "=0.2.0-alpha.2" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } +embedded-hal-async = { version = "=1.0.0-rc.1" } micromath = "2.0.0" panic-probe = { version = "0.3.0", features = ["print-defmt"] } rand_core = { version = "0.6", default-features = false } From df6952648e15c56d2d8b3224a69641296a780eed Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Wed, 16 Aug 2023 14:11:09 +0200 Subject: [PATCH 255/298] Make sure to check RCC settings for compatibility before applying --- embassy-stm32/src/rcc/f2.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs index d016d1de..ec4ed99b 100644 --- a/embassy-stm32/src/rcc/f2.rs +++ b/embassy-stm32/src/rcc/f2.rs @@ -378,22 +378,6 @@ pub(crate) unsafe fn init(config: Config) { // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions assert!(ahb_freq <= Hertz(120_000_000)); - let flash_ws = unwrap!(config.voltage.wait_states(ahb_freq)); - FLASH.acr().modify(|w| w.set_latency(flash_ws)); - - 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()); - }); - while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {} - - // Turn off HSI to save power if we don't need it - if !config.hsi { - RCC.cr().modify(|w| w.set_hsion(false)); - } - let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { APBPrescaler::NotDivided => (ahb_freq, ahb_freq), pre => { @@ -414,6 +398,22 @@ pub(crate) unsafe fn init(config: Config) { // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions assert!(apb2_freq <= Hertz(60_000_000)); + let flash_ws = unwrap!(config.voltage.wait_states(ahb_freq)); + FLASH.acr().modify(|w| w.set_latency(flash_ws)); + + 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()); + }); + while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {} + + // Turn off HSI to save power if we don't need it + if !config.hsi { + RCC.cr().modify(|w| w.set_hsion(false)); + } + set_freqs(Clocks { sys: sys_clk, ahb1: ahb_freq, From c80c3236340c7fa083a81bcbbe4d37525fc840b1 Mon Sep 17 00:00:00 2001 From: Olle Sandberg Date: Wed, 16 Aug 2023 13:57:03 +0200 Subject: [PATCH 256/298] stm32-wl: set RTC clock source on RCC init --- embassy-stm32/src/rcc/wl.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index ea6e8dde..f1dd2bd7 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -2,6 +2,7 @@ pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale}; use crate::pac::pwr::vals::Dbp; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; +use crate::rtc::{Rtc, RtcClockSource as RCS}; use crate::time::Hertz; /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, @@ -229,6 +230,8 @@ pub(crate) unsafe fn init(config: Config) { // Wait until LSE is running while !RCC.bdcr().read().lserdy() {} + + Rtc::set_clock_source(RCS::LSE); } RtcClockSource::LSI32 => { // Turn on the internal 32 kHz LSI oscillator @@ -236,6 +239,8 @@ pub(crate) unsafe fn init(config: Config) { // Wait until LSI is running while !RCC.csr().read().lsirdy() {} + + Rtc::set_clock_source(RCS::LSI); } } From 8a9f49c163f2e559c87e54b7abf819ea026f04f6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 16 Aug 2023 17:51:47 +0200 Subject: [PATCH 257/298] net-wiznet: add HIL test using w5100s. --- tests/rp/Cargo.toml | 5 +- tests/rp/src/bin/adc.rs | 3 +- tests/rp/src/bin/cyw43-perf.rs | 3 +- tests/rp/src/bin/dma_copy_async.rs | 3 +- tests/rp/src/bin/ethernet_w5100s_perf.rs | 249 +++++++++++++++++++++++ tests/rp/src/bin/flash.rs | 3 +- tests/rp/src/bin/float.rs | 3 +- tests/rp/src/bin/gpio.rs | 3 +- tests/rp/src/bin/gpio_async.rs | 3 +- tests/rp/src/bin/gpio_multicore.rs | 3 +- tests/rp/src/bin/multicore.rs | 3 +- tests/rp/src/bin/pio_irq.rs | 3 +- tests/rp/src/bin/pio_multi_load.rs | 3 +- tests/rp/src/bin/pwm.rs | 3 +- tests/rp/src/bin/spi.rs | 3 +- tests/rp/src/bin/spi_async.rs | 3 +- tests/rp/src/bin/uart.rs | 3 +- tests/rp/src/bin/uart_buffered.rs | 3 +- tests/rp/src/bin/uart_dma.rs | 3 +- tests/rp/src/bin/uart_upgrade.rs | 3 +- tests/rp/src/common.rs | 1 - 21 files changed, 271 insertions(+), 38 deletions(-) create mode 100644 tests/rp/src/bin/ethernet_w5100s_perf.rs delete mode 100644 tests/rp/src/common.rs diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index c69bf7a8..6a3df4b9 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -9,10 +9,11 @@ teleprobe-meta = "1.1" 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"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } +embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } @@ -24,6 +25,7 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" } embedded-hal-async = { version = "=1.0.0-rc.1" } +embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } embedded-io-async = { version = "0.5.0" } @@ -31,6 +33,7 @@ embedded-storage = { version = "0.3" } static_cell = { version = "1.1", features = ["nightly"]} pio = "0.2" pio-proc = "0.2" +rand = { version = "0.8.5", default-features = false } [profile.dev] debug = 2 diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs index b29a3a7c..0250fd5f 100644 --- a/tests/rp/src/bin/adc.rs +++ b/tests/rp/src/bin/adc.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::*; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index fffdabc9..1c665f95 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use cyw43_pio::PioSpi; use defmt::{assert, panic, *}; diff --git a/tests/rp/src/bin/dma_copy_async.rs b/tests/rp/src/bin/dma_copy_async.rs index 2c0b559a..b071ed9d 100644 --- a/tests/rp/src/bin/dma_copy_async.rs +++ b/tests/rp/src/bin/dma_copy_async.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/ethernet_w5100s_perf.rs b/tests/rp/src/bin/ethernet_w5100s_perf.rs new file mode 100644 index 00000000..faa8638c --- /dev/null +++ b/tests/rp/src/bin/ethernet_w5100s_perf.rs @@ -0,0 +1,249 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +teleprobe_meta::target!(b"w5100s-evb-pico"); +teleprobe_meta::timeout!(120); + +use defmt::{assert, *}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_net_wiznet::chip::W5100S; +use embassy_net_wiznet::*; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; +use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embassy_time::{with_timeout, Delay, Duration, Timer}; +use embedded_hal_bus::spi::ExclusiveDevice; +use rand::RngCore; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn ethernet_task( + runner: Runner< + 'static, + W5100S, + ExclusiveDevice, Output<'static, PIN_17>, Delay>, + Input<'static, PIN_21>, + Output<'static, PIN_20>, + >, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack>) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = 50_000_000; + let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18); + let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); + let cs = Output::new(p.PIN_17, Level::High); + let w5500_int = Input::new(p.PIN_21, Pull::Up); + let w5500_reset = Output::new(p.PIN_20, Level::High); + + let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; + let state = make_static!(State::<8, 8>::new()); + let (device, runner) = embassy_net_wiznet::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs, Delay), + w5500_int, + w5500_reset, + ) + .await; + unwrap!(spawner.spawn(ethernet_task(runner))); + + // Generate random seed + let seed = rng.next_u64(); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + embassy_net::Config::dhcpv4(Default::default()), + make_static!(StackResources::<2>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Waiting for DHCP up..."); + while stack.config_v4().is_none() { + Timer::after(Duration::from_millis(100)).await; + } + info!("IP addressing up!"); + + let down = test_download(stack).await; + let up = test_upload(stack).await; + let updown = test_upload_download(stack).await; + + assert!(down > TEST_EXPECTED_DOWNLOAD_KBPS); + assert!(up > TEST_EXPECTED_UPLOAD_KBPS); + assert!(updown > TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +const TEST_DURATION: usize = 10; +const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 500; +const TEST_EXPECTED_UPLOAD_KBPS: usize = 500; +const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 300; +const RX_BUFFER_SIZE: usize = 4096; +const TX_BUFFER_SIZE: usize = 4096; +const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); +const DOWNLOAD_PORT: u16 = 4321; +const UPLOAD_PORT: u16 = 4322; +const UPLOAD_DOWNLOAD_PORT: u16 = 4323; + +async fn test_download(stack: &'static Stack>) -> usize { + info!("Testing download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("download: {} kB/s", kbps); + kbps +} + +async fn test_upload(stack: &'static Stack>) -> usize { + info!("Testing upload..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.write(&buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload: {} kB/s", kbps); + kbps +} + +async fn test_upload_download(stack: &'static Stack>) -> usize { + info!("Testing upload+download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let (mut reader, mut writer) = socket.split(); + + let tx_buf = [0; 4096]; + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + let tx_fut = async { + loop { + match writer.write(&tx_buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(_) => {} + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }; + + let rx_fut = async { + loop { + match reader.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }; + + with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut)) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload+download: {} kB/s", kbps); + kbps +} diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index c31d6dec..6e65fbdc 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::*; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/float.rs b/tests/rp/src/bin/float.rs index 0e0de85f..2874aa91 100644 --- a/tests/rp/src/bin/float.rs +++ b/tests/rp/src/bin/float.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::*; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs index 946b7dc8..1a43a928 100644 --- a/tests/rp/src/bin/gpio.rs +++ b/tests/rp/src/bin/gpio.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/gpio_async.rs b/tests/rp/src/bin/gpio_async.rs index 532494de..60c65b7a 100644 --- a/tests/rp/src/bin/gpio_async.rs +++ b/tests/rp/src/bin/gpio_async.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/gpio_multicore.rs b/tests/rp/src/bin/gpio_multicore.rs index 780112bc..22be7824 100644 --- a/tests/rp/src/bin/gpio_multicore.rs +++ b/tests/rp/src/bin/gpio_multicore.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{info, unwrap}; use embassy_executor::Executor; diff --git a/tests/rp/src/bin/multicore.rs b/tests/rp/src/bin/multicore.rs index 114889de..ec794c48 100644 --- a/tests/rp/src/bin/multicore.rs +++ b/tests/rp/src/bin/multicore.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{info, unwrap}; use embassy_executor::Executor; diff --git a/tests/rp/src/bin/pio_irq.rs b/tests/rp/src/bin/pio_irq.rs index bdea63ea..c71e55d9 100644 --- a/tests/rp/src/bin/pio_irq.rs +++ b/tests/rp/src/bin/pio_irq.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::info; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/pio_multi_load.rs b/tests/rp/src/bin/pio_multi_load.rs index 356f1679..6343ff3a 100644 --- a/tests/rp/src/bin/pio_multi_load.rs +++ b/tests/rp/src/bin/pio_multi_load.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::info; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/pwm.rs b/tests/rp/src/bin/pwm.rs index c71d21ef..8c02b844 100644 --- a/tests/rp/src/bin/pwm.rs +++ b/tests/rp/src/bin/pwm.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert, assert_eq, assert_ne, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/spi.rs b/tests/rp/src/bin/spi.rs index 84dfa5a2..f347b80c 100644 --- a/tests/rp/src/bin/spi.rs +++ b/tests/rp/src/bin/spi.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/spi_async.rs b/tests/rp/src/bin/spi_async.rs index a4080b03..80d5a714 100644 --- a/tests/rp/src/bin/spi_async.rs +++ b/tests/rp/src/bin/spi_async.rs @@ -4,8 +4,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart.rs b/tests/rp/src/bin/uart.rs index 2331c7d3..00f3e194 100644 --- a/tests/rp/src/bin/uart.rs +++ b/tests/rp/src/bin/uart.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart_buffered.rs b/tests/rp/src/bin/uart_buffered.rs index edabf55b..6ab7de29 100644 --- a/tests/rp/src/bin/uart_buffered.rs +++ b/tests/rp/src/bin/uart_buffered.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, panic, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart_dma.rs b/tests/rp/src/bin/uart_dma.rs index fee6c825..cd4af1ef 100644 --- a/tests/rp/src/bin/uart_dma.rs +++ b/tests/rp/src/bin/uart_dma.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/bin/uart_upgrade.rs b/tests/rp/src/bin/uart_upgrade.rs index effd0bc4..c07fc08c 100644 --- a/tests/rp/src/bin/uart_upgrade.rs +++ b/tests/rp/src/bin/uart_upgrade.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/rp/src/common.rs b/tests/rp/src/common.rs deleted file mode 100644 index 955674f2..00000000 --- a/tests/rp/src/common.rs +++ /dev/null @@ -1 +0,0 @@ -teleprobe_meta::target!(b"rpi-pico"); From bb275f7e25e9f0a3cceb3045bceb1822167e7b31 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 16 Aug 2023 20:06:15 +0200 Subject: [PATCH 258/298] nrf: enable defmt for embassy-hal-internal. --- ci.sh | 2 +- embassy-nrf/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci.sh b/ci.sh index 146a1c50..f82cc627 100755 --- a/ci.sh +++ b/ci.sh @@ -3,7 +3,7 @@ set -euo pipefail export RUSTFLAGS=-Dwarnings -export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info +export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info TARGET=$(rustc -vV | sed -n 's|host: ||p') diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index c4790f2c..67ec4eb9 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -32,7 +32,7 @@ rt = [ time = ["dep:embassy-time"] -defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embassy-embedded-hal/defmt"] +defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embassy-embedded-hal/defmt"] # Enable nightly-only features nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io-async", "embassy-embedded-hal/nightly"] From 065b0f34af4215cff81a799044f73980ed751120 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 17 Aug 2023 00:11:15 +0200 Subject: [PATCH 259/298] net-esp-hosted: sane error handling in control requests. --- embassy-net-esp-hosted/src/control.rs | 126 +++++++++++-------- embassy-net-esp-hosted/src/lib.rs | 9 +- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 4 +- tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 4 +- 4 files changed, 79 insertions(+), 64 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index 37f220da..b722bdd7 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -5,9 +5,12 @@ use heapless::String; use crate::ioctl::Shared; use crate::proto::{self, CtrlMsg}; -#[derive(Debug)] -pub struct Error { - pub status: u32, +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + Failed(u32), + Timeout, + Internal, } pub struct Control<'a> { @@ -23,58 +26,68 @@ enum WifiMode { ApSta = 3, } +macro_rules! ioctl { + ($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => { + let mut msg = proto::CtrlMsg { + msg_id: proto::CtrlMsgId::$req_variant as _, + msg_type: proto::CtrlMsgType::Req as _, + payload: Some(proto::CtrlMsgPayload::$req_variant($req)), + }; + $self.ioctl(&mut msg).await?; + let Some(proto::CtrlMsgPayload::$resp_variant($resp)) = msg.payload else { + warn!("unexpected response variant"); + return Err(Error::Internal); + }; + if $resp.resp != 0 { + return Err(Error::Failed($resp.resp)); + } + }; +} + impl<'a> Control<'a> { pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self { Self { state_ch, shared } } - pub async fn init(&mut self) { + pub async fn init(&mut self) -> Result<(), Error> { debug!("wait for init event..."); self.shared.init_wait().await; debug!("set wifi mode"); - self.set_wifi_mode(WifiMode::Sta as _).await; + self.set_wifi_mode(WifiMode::Sta as _).await?; - let mac_addr = self.get_mac_addr().await; + let mac_addr = self.get_mac_addr().await?; debug!("mac addr: {:02x}", mac_addr); self.state_ch.set_ethernet_address(mac_addr); + + Ok(()) } - pub async fn join(&mut self, ssid: &str, password: &str) { - let req = proto::CtrlMsg { - msg_id: proto::CtrlMsgId::ReqConnectAp as _, - msg_type: proto::CtrlMsgType::Req as _, - payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp { - ssid: String::from(ssid), - pwd: String::from(password), - bssid: String::new(), - listen_interval: 3, - is_wpa3_supported: false, - })), + pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> { + let req = proto::CtrlMsgReqConnectAp { + ssid: String::from(ssid), + pwd: String::from(password), + bssid: String::new(), + listen_interval: 3, + is_wpa3_supported: false, }; - let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { - panic!("unexpected resp") - }; - assert_eq!(resp.resp, 0); + ioctl!(self, ReqConnectAp, RespConnectAp, req, resp); self.state_ch.set_link_state(LinkState::Up); + Ok(()) } - async fn get_mac_addr(&mut self) -> [u8; 6] { - let req = proto::CtrlMsg { - msg_id: proto::CtrlMsgId::ReqGetMacAddress as _, - msg_type: proto::CtrlMsgType::Req as _, - payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress( - proto::CtrlMsgReqGetMacAddress { - mode: WifiMode::Sta as _, - }, - )), + pub async fn disconnect(&mut self) -> Result<(), Error> { + let req = proto::CtrlMsgReqGetStatus {}; + ioctl!(self, ReqDisconnectAp, RespDisconnectAp, req, resp); + self.state_ch.set_link_state(LinkState::Up); + Ok(()) + } + + async fn get_mac_addr(&mut self) -> Result<[u8; 6], Error> { + let req = proto::CtrlMsgReqGetMacAddress { + mode: WifiMode::Sta as _, }; - let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { - panic!("unexpected resp") - }; - assert_eq!(resp.resp, 0); + ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp); // WHY IS THIS A STRING? WHYYYY fn nibble_from_hex(b: u8) -> u8 { @@ -88,32 +101,32 @@ impl<'a> Control<'a> { let mac = resp.mac.as_bytes(); let mut res = [0; 6]; - assert_eq!(mac.len(), 17); + if mac.len() != 17 { + warn!("unexpected MAC respnse length"); + return Err(Error::Internal); + } for (i, b) in res.iter_mut().enumerate() { *b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1]) } - res + Ok(res) } - async fn set_wifi_mode(&mut self, mode: u32) { - let req = proto::CtrlMsg { - msg_id: proto::CtrlMsgId::ReqSetWifiMode as _, - msg_type: proto::CtrlMsgType::Req as _, - payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })), - }; - let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { - panic!("unexpected resp") - }; - assert_eq!(resp.resp, 0); + async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> { + let req = proto::CtrlMsgReqSetMode { mode }; + ioctl!(self, ReqSetWifiMode, RespSetWifiMode, req, resp); + + Ok(()) } - async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg { - debug!("ioctl req: {:?}", &req); + async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> { + debug!("ioctl req: {:?}", &msg); let mut buf = [0u8; 128]; - let req_len = noproto::write(&req, &mut buf).unwrap(); + let req_len = noproto::write(msg, &mut buf).map_err(|_| { + warn!("failed to serialize control request"); + Error::Internal + })?; struct CancelOnDrop<'a>(&'a Shared); @@ -135,9 +148,12 @@ impl<'a> Control<'a> { ioctl.defuse(); - let res = noproto::read(&buf[..resp_len]).unwrap(); - debug!("ioctl resp: {:?}", &res); + *msg = noproto::read(&buf[..resp_len]).map_err(|_| { + warn!("failed to serialize control request"); + Error::Internal + })?; + debug!("ioctl resp: {:?}", msg); - res + Ok(()) } } diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index 96fddce5..b1fa775c 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -1,17 +1,14 @@ #![no_std] -use control::Control; use embassy_futures::select::{select3, Either3}; use embassy_net_driver_channel as ch; use embassy_time::{Duration, Instant, Timer}; use embedded_hal::digital::{InputPin, OutputPin}; use embedded_hal_async::digital::Wait; use embedded_hal_async::spi::SpiDevice; -use ioctl::Shared; -use proto::CtrlMsg; -use crate::ioctl::PendingIoctl; -use crate::proto::CtrlMsgPayload; +use crate::ioctl::{PendingIoctl, Shared}; +use crate::proto::{CtrlMsg, CtrlMsgPayload}; mod proto; @@ -21,6 +18,8 @@ mod fmt; mod control; mod ioctl; +pub use control::*; + const MTU: usize = 1514; macro_rules! impl_bytes { diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index e114e50b..a60822fd 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -72,8 +72,8 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(wifi_task(runner))); - control.init().await; - control.join(WIFI_NETWORK, WIFI_PASSWORD).await; + unwrap!(control.init().await); + unwrap!(control.connect(WIFI_NETWORK, WIFI_PASSWORD).await); let config = embassy_net::Config::dhcpv4(Default::default()); // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index 16055fae..4b221a81 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -76,8 +76,8 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(wifi_task(runner))); - control.init().await; - control.join(WIFI_NETWORK, WIFI_PASSWORD).await; + unwrap!(control.init().await); + unwrap!(control.connect(WIFI_NETWORK, WIFI_PASSWORD).await); // Generate random seed let mut rng = Rng::new(p.RNG, Irqs); From ef7523e5b7a36f0f15b77d4e673afdda0df51f3a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 17 Aug 2023 00:53:25 +0200 Subject: [PATCH 260/298] net-esp-hosted: put link down on wifi disconnect. --- embassy-net-esp-hosted/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index b1fa775c..c2d9d609 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -128,6 +128,7 @@ where let mut runner = Runner { ch: ch_runner, + state_ch, shared: &state.shared, next_seq: 1, handshake, @@ -142,6 +143,7 @@ where pub struct Runner<'a, SPI, IN, OUT> { ch: ch::Runner<'a, MTU>, + state_ch: ch::StateRunner<'a>, shared: &'a Shared, next_seq: u16, @@ -322,6 +324,10 @@ where match payload { CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(), + CtrlMsgPayload::EventStationDisconnectFromAp(e) => { + info!("disconnected, code {}", e.resp); + self.state_ch.set_link_state(LinkState::Down); + } _ => {} } } From 1cb76e0d99f8da12891491b31176c8247a7a81a4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 17 Aug 2023 00:57:54 +0200 Subject: [PATCH 261/298] net-esp-hosted: enable heartbeats from esp32 to detect if it crashes. --- embassy-net-esp-hosted/src/control.rs | 10 ++++++++++ embassy-net-esp-hosted/src/lib.rs | 21 +++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index b722bdd7..6cd57f68 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -53,6 +53,9 @@ impl<'a> Control<'a> { debug!("wait for init event..."); self.shared.init_wait().await; + debug!("set heartbeat"); + self.set_heartbeat(10).await?; + debug!("set wifi mode"); self.set_wifi_mode(WifiMode::Sta as _).await?; @@ -83,6 +86,13 @@ impl<'a> Control<'a> { Ok(()) } + /// duration in seconds, clamped to [10, 3600] + async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> { + let req = proto::CtrlMsgReqConfigHeartbeat { enable: true, duration }; + ioctl!(self, ReqConfigHeartbeat, RespConfigHeartbeat, req, resp); + Ok(()) + } + async fn get_mac_addr(&mut self) -> Result<[u8; 6], Error> { let req = proto::CtrlMsgReqGetMacAddress { mode: WifiMode::Sta as _, diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index c2d9d609..4a318b20 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -1,7 +1,8 @@ #![no_std] -use embassy_futures::select::{select3, Either3}; +use embassy_futures::select::{select4, Either4}; use embassy_net_driver_channel as ch; +use embassy_net_driver_channel::driver::LinkState; use embassy_time::{Duration, Instant, Timer}; use embedded_hal::digital::{InputPin, OutputPin}; use embedded_hal_async::digital::Wait; @@ -94,6 +95,7 @@ enum InterfaceType { } const MAX_SPI_BUFFER_SIZE: usize = 1600; +const HEARTBEAT_MAX_GAP: Duration = Duration::from_secs(20); pub struct State { shared: Shared, @@ -135,6 +137,7 @@ where ready, reset, spi, + heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP, }; runner.init().await; @@ -147,6 +150,7 @@ pub struct Runner<'a, SPI, IN, OUT> { shared: &'a Shared, next_seq: u16, + heartbeat_deadline: Instant, spi: SPI, handshake: IN, @@ -178,9 +182,10 @@ where let ioctl = self.shared.ioctl_wait_pending(); let tx = self.ch.tx_buf(); let ev = async { self.ready.wait_for_high().await.unwrap() }; + let hb = Timer::at(self.heartbeat_deadline); - match select3(ioctl, tx, ev).await { - Either3::First(PendingIoctl { buf, req_len }) => { + match select4(ioctl, tx, ev, hb).await { + Either4::First(PendingIoctl { buf, req_len }) => { tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02"); tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes()); tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]); @@ -199,7 +204,7 @@ where header.checksum = checksum(&tx_buf[..26 + req_len]); tx_buf[0..12].copy_from_slice(&header.to_bytes()); } - Either3::Second(packet) => { + Either4::Second(packet) => { tx_buf[12..][..packet.len()].copy_from_slice(packet); let mut header = PayloadHeader { @@ -218,9 +223,12 @@ where self.ch.tx_done(); } - Either3::Third(()) => { + Either4::Third(()) => { tx_buf[..PayloadHeader::SIZE].fill(0); } + Either4::Fourth(()) => { + panic!("heartbeat from esp32 stopped") + } } if tx_buf[0] != 0 { @@ -309,7 +317,7 @@ where } } - fn handle_event(&self, data: &[u8]) { + fn handle_event(&mut self, data: &[u8]) { let Ok(event) = noproto::read::(data) else { warn!("failed to parse event"); return; @@ -324,6 +332,7 @@ where match payload { CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(), + CtrlMsgPayload::EventHeartbeat(_) => self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP, CtrlMsgPayload::EventStationDisconnectFromAp(e) => { info!("disconnected, code {}", e.resp); self.state_ch.set_link_state(LinkState::Down); From 62e66cdda39d3689c5884bd883621f5fb7553117 Mon Sep 17 00:00:00 2001 From: Adam Rizkalla Date: Thu, 17 Aug 2023 19:16:03 -0500 Subject: [PATCH 262/298] stm32l4: set pwren in rcc regardless of clock source --- embassy-stm32/src/rcc/l4.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index b2828e58..b34b8caa 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -410,10 +410,11 @@ pub(crate) unsafe fn init(config: Config) { while RCC.cfgr().read().sws() != Sw::MSI {} } + RCC.apb1enr1().modify(|w| w.set_pwren(true)); + match config.rtc_mux { RtcClockSource::LSE32 => { // 1. Unlock the backup domain - RCC.apb1enr1().modify(|w| w.set_pwren(true)); PWR.cr1().modify(|w| w.set_dbp(true)); // 2. Setup the LSE From 91b10dd79912b92de5b16a2ea6e2769876fe13fa Mon Sep 17 00:00:00 2001 From: Lukas Joeressen Date: Fri, 18 Aug 2023 13:09:27 +0200 Subject: [PATCH 263/298] Fixed the final null terminator for RegMultiSz. The RegMultiSz value should be terminated by an empty UTF-16 string, i.e. 2 null bytes. --- embassy-usb/src/msos.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 187b2ff8..847338e5 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -526,7 +526,7 @@ impl<'a> PropertyData<'a> { PropertyData::Binary(val) => val.len(), PropertyData::DwordLittleEndian(val) | PropertyData::DwordBigEndian(val) => core::mem::size_of_val(val), PropertyData::RegMultiSz(val) => { - core::mem::size_of::() * val.iter().map(|x| x.encode_utf16().count() + 1).sum::() + 1 + core::mem::size_of::() * (val.iter().map(|x| x.encode_utf16().count() + 1).sum::() + 1) } } } From b948e3776969ac488abb6507ce429fee33ceb48b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 18 Aug 2023 13:12:19 +0200 Subject: [PATCH 264/298] rp/flash: change naming to `blocking_*`, `new_blocking`. - Needed for consistency with other drivers. - Having two `new()` functions sometimes resulted in 'multiple applicable methods' errors. --- embassy-boot/rp/src/lib.rs | 8 +- embassy-rp/src/flash.rs | 100 +++++++++++----------- examples/boot/application/rp/src/bin/a.rs | 4 +- examples/rp/src/bin/flash.rs | 34 ++++---- tests/rp/src/bin/flash.rs | 14 +-- 5 files changed, 82 insertions(+), 78 deletions(-) diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 96bcf3bf..989e7521 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -54,7 +54,7 @@ pub struct WatchdogFlash<'d, const SIZE: usize> { impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { /// Start a new watchdog with a given flash and watchdog peripheral and a timeout pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self { - let flash = Flash::<_, Blocking, SIZE>::new(flash); + let flash = Flash::<_, Blocking, SIZE>::new_blocking(flash); let mut watchdog = Watchdog::new(watchdog); watchdog.start(timeout); Self { flash, watchdog } @@ -71,11 +71,11 @@ impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> { fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { self.watchdog.feed(); - self.flash.erase(from, to) + self.flash.blocking_erase(from, to) } fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> { self.watchdog.feed(); - self.flash.write(offset, data) + self.flash.blocking_write(offset, data) } } @@ -83,7 +83,7 @@ impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> { const READ_SIZE: usize = as ReadNorFlash>::READ_SIZE; fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> { self.watchdog.feed(); - self.flash.read(offset, data) + self.flash.blocking_read(offset, data) } fn capacity(&self) -> usize { self.flash.capacity() diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 70d86731..1c1c2449 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -102,7 +102,7 @@ pub struct Flash<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> { } impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SIZE> { - pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { + pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { trace!( "Reading from 0x{:x} to 0x{:x}", FLASH_BASE as u32 + offset, @@ -120,7 +120,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI FLASH_SIZE } - pub fn erase(&mut self, from: u32, to: u32) -> Result<(), Error> { + pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> { check_erase(self, from, to)?; trace!( @@ -136,7 +136,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI Ok(()) } - pub fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { + pub fn blocking_write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Error> { check_write(self, offset, bytes.len())?; trace!("Writing {:?} bytes to 0x{:x}", bytes.len(), FLASH_BASE as u32 + offset); @@ -233,13 +233,13 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI } /// Read SPI flash unique ID - pub fn unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { + pub fn blocking_unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid))? }; Ok(()) } /// Read SPI flash JEDEC ID - pub fn jedec_id(&mut self) -> Result { + pub fn blocking_jedec_id(&mut self) -> Result { let mut jedec = None; unsafe { self.in_ram(|| { @@ -251,7 +251,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI } impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Blocking, FLASH_SIZE> { - pub fn new(_flash: impl Peripheral

+ 'd) -> Self { + pub fn new_blocking(_flash: impl Peripheral

+ 'd) -> Self { Self { dma: None, phantom: PhantomData, @@ -310,47 +310,8 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> { transfer, }) } -} -impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, M, FLASH_SIZE> { - type Error = Error; -} - -impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, M, FLASH_SIZE> { - const READ_SIZE: usize = READ_SIZE; - - fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { - self.read(offset, bytes) - } - - fn capacity(&self) -> usize { - self.capacity() - } -} - -impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, M, FLASH_SIZE> {} - -impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, M, FLASH_SIZE> { - const WRITE_SIZE: usize = WRITE_SIZE; - - const ERASE_SIZE: usize = ERASE_SIZE; - - fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase(from, to) - } - - fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(offset, bytes) - } -} - -#[cfg(feature = "nightly")] -impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::ReadNorFlash - for Flash<'d, T, Async, FLASH_SIZE> -{ - const READ_SIZE: usize = ASYNC_READ_SIZE; - - async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + pub async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { use core::mem::MaybeUninit; // Checked early to simplify address validity checks @@ -389,6 +350,49 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash Ok(()) } +} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, M, FLASH_SIZE> { + type Error = Error; +} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, M, FLASH_SIZE> { + const READ_SIZE: usize = READ_SIZE; + + fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(offset, bytes) + } + + fn capacity(&self) -> usize { + self.capacity() + } +} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, M, FLASH_SIZE> {} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, M, FLASH_SIZE> { + const WRITE_SIZE: usize = WRITE_SIZE; + + const ERASE_SIZE: usize = ERASE_SIZE; + + fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.blocking_erase(from, to) + } + + fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(offset, bytes) + } +} + +#[cfg(feature = "nightly")] +impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::ReadNorFlash + for Flash<'d, T, Async, FLASH_SIZE> +{ + const READ_SIZE: usize = ASYNC_READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + self.read(offset, bytes).await + } fn capacity(&self) -> usize { self.capacity() @@ -404,11 +408,11 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash const ERASE_SIZE: usize = ERASE_SIZE; async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { - self.erase(from, to) + self.blocking_erase(from, to) } async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(offset, bytes) + self.blocking_write(offset, bytes) } } diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index f0dda39d..15fdaca8 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -7,7 +7,7 @@ use core::cell::RefCell; use defmt_rtt as _; use embassy_boot_rp::*; use embassy_executor::Spawner; -use embassy_rp::flash::{self, Flash}; +use embassy_rp::flash::Flash; use embassy_rp::gpio::{Level, Output}; use embassy_rp::watchdog::Watchdog; use embassy_sync::blocking_mutex::Mutex; @@ -34,7 +34,7 @@ async fn main(_s: Spawner) { let mut watchdog = Watchdog::new(p.WATCHDOG); watchdog.start(Duration::from_secs(8)); - let flash = Flash::<_, flash::Blocking, FLASH_SIZE>::new(p.FLASH); + let flash = Flash::<_, _, FLASH_SIZE>::new_blocking(p.FLASH); let flash = Mutex::new(RefCell::new(flash)); let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index 88bb931d..911a657e 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs @@ -28,12 +28,12 @@ async fn main(_spawner: Spawner) { let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0); // Get JEDEC id - let jedec = flash.jedec_id().unwrap(); + let jedec = flash.blocking_jedec_id().unwrap(); info!("jedec id: 0x{:x}", jedec); // Get unique id let mut uid = [0; 8]; - flash.unique_id(&mut uid).unwrap(); + flash.blocking_unique_id(&mut uid).unwrap(); info!("unique id: {:?}", uid); erase_write_sector(&mut flash, 0x00); @@ -48,25 +48,25 @@ async fn main(_spawner: Spawner) { fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { info!(">>>> [multiwrite_bytes]"); let mut read_buf = [0u8; ERASE_SIZE]; - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf)); info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); info!("Contents start with {=[u8]}", read_buf[0..4]); - defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf)); info!("Contents after erase starts with {=[u8]}", read_buf[0..4]); if read_buf.iter().any(|x| *x != 0xFF) { defmt::panic!("unexpected"); } - defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &[0x01])); - defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 1, &[0x02])); - defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 2, &[0x03])); - defmt::unwrap!(flash.write(ADDR_OFFSET + offset + 3, &[0x04])); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &[0x01])); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 1, &[0x02])); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 2, &[0x03])); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset + 3, &[0x04])); - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf)); info!("Contents after write starts with {=[u8]}", read_buf[0..4]); if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] { defmt::panic!("unexpected"); @@ -76,14 +76,14 @@ fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { info!(">>>> [erase_write_sector]"); let mut buf = [0u8; ERASE_SIZE]; - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf)); info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); info!("Contents start with {=[u8]}", buf[0..4]); - defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf)); info!("Contents after erase starts with {=[u8]}", buf[0..4]); if buf.iter().any(|x| *x != 0xFF) { defmt::panic!("unexpected"); @@ -93,9 +93,9 @@ fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLA *b = 0xDA; } - defmt::unwrap!(flash.write(ADDR_OFFSET + offset, &buf)); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, &buf)); - defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut buf)); info!("Contents after write starts with {=[u8]}", buf[0..4]); if buf.iter().any(|x| *x != 0xDA) { defmt::panic!("unexpected"); @@ -111,7 +111,7 @@ async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); info!("Contents start with {=u32:x}", buf[0]); - defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; info!("Contents after erase starts with {=u32:x}", buf[0]); @@ -123,7 +123,7 @@ async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, *b = 0xDABA1234; } - defmt::unwrap!(flash.write(ADDR_OFFSET + offset, unsafe { + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET + offset, unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4) })); diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index 6e65fbdc..75be2bf0 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs @@ -25,23 +25,23 @@ async fn main(_spawner: Spawner) { let mut flash = embassy_rp::flash::Flash::<_, Async, { 2 * 1024 * 1024 }>::new(p.FLASH, p.DMA_CH0); // Get JEDEC id - let jedec = defmt::unwrap!(flash.jedec_id()); + let jedec = defmt::unwrap!(flash.blocking_jedec_id()); info!("jedec id: 0x{:x}", jedec); // Get unique id let mut uid = [0; 8]; - defmt::unwrap!(flash.unique_id(&mut uid)); + defmt::unwrap!(flash.blocking_unique_id(&mut uid)); info!("unique id: {:?}", uid); let mut buf = [0u8; ERASE_SIZE]; - defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET, &mut buf)); info!("Addr of flash block is {:x}", ADDR_OFFSET + FLASH_BASE as u32); info!("Contents start with {=[u8]}", buf[0..4]); - defmt::unwrap!(flash.erase(ADDR_OFFSET, ADDR_OFFSET + ERASE_SIZE as u32)); + defmt::unwrap!(flash.blocking_erase(ADDR_OFFSET, ADDR_OFFSET + ERASE_SIZE as u32)); - defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET, &mut buf)); info!("Contents after erase starts with {=[u8]}", buf[0..4]); if buf.iter().any(|x| *x != 0xFF) { defmt::panic!("unexpected"); @@ -51,9 +51,9 @@ async fn main(_spawner: Spawner) { *b = 0xDA; } - defmt::unwrap!(flash.write(ADDR_OFFSET, &mut buf)); + defmt::unwrap!(flash.blocking_write(ADDR_OFFSET, &mut buf)); - defmt::unwrap!(flash.read(ADDR_OFFSET, &mut buf)); + defmt::unwrap!(flash.blocking_read(ADDR_OFFSET, &mut buf)); info!("Contents after write starts with {=[u8]}", buf[0..4]); if buf.iter().any(|x| *x != 0xDA) { defmt::panic!("unexpected"); From 3ebb93e47d47095f35d561cd49f0797a74061465 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 18 Aug 2023 15:44:52 +0200 Subject: [PATCH 265/298] net-enc28j60: remove useless 1ms sleep. --- embassy-net-enc28j60/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs index 09e77baf..e958e7e3 100644 --- a/embassy-net-enc28j60/src/lib.rs +++ b/embassy-net-enc28j60/src/lib.rs @@ -381,8 +381,6 @@ where } fn read_phy_register(&mut self, register: phy::Register) -> u16 { - embassy_time::block_for(Duration::from_millis(1)); - // set PHY register address self.write_control_register(bank2::Register::MIREGADR, register.addr()); From 73942f50cb03070026246701e75d641e34d6d308 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 18 Aug 2023 15:45:23 +0200 Subject: [PATCH 266/298] net-enc28j60: reset rx logic when buffer corrupts. --- embassy-net-enc28j60/src/lib.rs | 54 ++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs index e958e7e3..b44cefaf 100644 --- a/embassy-net-enc28j60/src/lib.rs +++ b/embassy-net-enc28j60/src/lib.rs @@ -106,19 +106,7 @@ where // disable CLKOUT output self.write_control_register(bank3::Register::ECOCON, 0); - // RX start - // "It is recommended that the ERXST Pointer be programmed with an even address" - self.write_control_register(bank0::Register::ERXSTL, RXST.low()); - self.write_control_register(bank0::Register::ERXSTH, RXST.high()); - - // RX read pointer - // NOTE Errata #14 so we are using an *odd* address here instead of ERXST - self.write_control_register(bank0::Register::ERXRDPTL, RXND.low()); - self.write_control_register(bank0::Register::ERXRDPTH, RXND.high()); - - // RX end - self.write_control_register(bank0::Register::ERXNDL, RXND.low()); - self.write_control_register(bank0::Register::ERXNDH, RXND.high()); + self.init_rx(); // TX start // "It is recommended that an even address be used for ETXST" @@ -172,12 +160,37 @@ where // Set the per packet control byte; we'll always use the value 0 self.write_buffer_memory(Some(TXST), &mut [0]); + // Enable reception + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen()); + } + + fn init_rx(&mut self) { + // RX start + // "It is recommended that the ERXST Pointer be programmed with an even address" + self.write_control_register(bank0::Register::ERXSTL, RXST.low()); + self.write_control_register(bank0::Register::ERXSTH, RXST.high()); + + // RX read pointer + // NOTE Errata #14 so we are using an *odd* address here instead of ERXST + self.write_control_register(bank0::Register::ERXRDPTL, RXND.low()); + self.write_control_register(bank0::Register::ERXRDPTH, RXND.high()); + + // RX end + self.write_control_register(bank0::Register::ERXNDL, RXND.low()); + self.write_control_register(bank0::Register::ERXNDH, RXND.high()); + // decrease the packet count to 0 while self.read_control_register(bank1::Register::EPKTCNT) != 0 { self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec()); } - // Enable reception + self.next_packet = RXST; + } + + fn reset_rx(&mut self) { + self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxrst()); + self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().rxrst()); + self.init_rx(); self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen()); } @@ -198,18 +211,17 @@ where // next packet pointer let next_packet = u16::from_parts(temp_buf[0], temp_buf[1]); - if next_packet > RXND { - panic!("CorruptRxBuffer"); - } - // status vector let status = header::RxStatus(u32::from_le_bytes(temp_buf[2..].try_into().unwrap())); - let len = status.byte_count() as u16 - CRC_SZ; + let len_with_crc = status.byte_count() as u16; - if len > RXND { - panic!("CorruptRxBuffer 2"); + if len_with_crc < CRC_SZ || len_with_crc > 1600 || next_packet > RXND { + warn!("RX buffer corrupted, resetting RX logic to recover..."); + self.reset_rx(); + return None; } + let len = len_with_crc - CRC_SZ; self.read_buffer_memory(None, &mut buf[..len as usize]); // update ERXRDPT From 78bb261246559e0447dc472f89480564e8b7a3fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Jacobs?= Date: Fri, 18 Aug 2023 16:10:49 +0200 Subject: [PATCH 267/298] stm32: allow setting PWM duty cycle to 100% Setting the compare_value to max_compare_value make the PWM output go low when the timer reach the max_compare_value and go high again on the next timer clock, when the value roll back to 0. So to allow a 100% PWM that never goes low, the compare_value must be set to max_compare_value + 1. --- embassy-stm32/src/timer/complementary_pwm.rs | 4 ++-- embassy-stm32/src/timer/simple_pwm.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index acd67048..533267cf 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -100,11 +100,11 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { } pub fn get_max_duty(&self) -> u16 { - self.inner.get_max_compare_value() + self.inner.get_max_compare_value() + 1 } pub fn set_duty(&mut self, channel: Channel, duty: u16) { - assert!(duty < self.get_max_duty()); + assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty) } diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index e0a81792..40e3dd1b 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -97,11 +97,11 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { } pub fn get_max_duty(&self) -> u16 { - self.inner.get_max_compare_value() + self.inner.get_max_compare_value() + 1 } pub fn set_duty(&mut self, channel: Channel, duty: u16) { - assert!(duty < self.get_max_duty()); + assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty) } } From 7f97efd9222725d2f0a4582a64d8d8e5e0fa6b0f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 18 Aug 2023 16:11:18 +0200 Subject: [PATCH 268/298] net-enc28j60: add HIL test. --- tests/nrf/Cargo.toml | 1 + tests/nrf/src/bin/buffered_uart.rs | 3 +- tests/nrf/src/bin/buffered_uart_spam.rs | 3 +- tests/nrf/src/bin/ethernet_enc28j60_perf.rs | 250 ++++++++++++++++++++ tests/nrf/src/bin/timer.rs | 3 +- tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 7 +- tests/nrf/src/common.rs | 1 - 7 files changed, 256 insertions(+), 12 deletions(-) create mode 100644 tests/nrf/src/bin/ethernet_enc28j60_perf.rs delete mode 100644 tests/nrf/src/common.rs diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index e2dab81a..034ed85e 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -15,6 +15,7 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm embedded-io-async = { version = "0.5.0" } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } +embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"] } embedded-hal-async = { version = "1.0.0-rc.1" } embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] } static_cell = { version = "1.1", features = [ "nightly" ] } diff --git a/tests/nrf/src/bin/buffered_uart.rs b/tests/nrf/src/bin/buffered_uart.rs index 72a4cb4e..932e5926 100644 --- a/tests/nrf/src/bin/buffered_uart.rs +++ b/tests/nrf/src/bin/buffered_uart.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"nrf52840-dk"); use defmt::{assert_eq, *}; use embassy_executor::Spawner; diff --git a/tests/nrf/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs index 50960206..8abeae6d 100644 --- a/tests/nrf/src/bin/buffered_uart_spam.rs +++ b/tests/nrf/src/bin/buffered_uart_spam.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"nrf52840-dk"); use core::mem; use core::ptr::NonNull; diff --git a/tests/nrf/src/bin/ethernet_enc28j60_perf.rs b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs new file mode 100644 index 00000000..0446d39a --- /dev/null +++ b/tests/nrf/src/bin/ethernet_enc28j60_perf.rs @@ -0,0 +1,250 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +teleprobe_meta::target!(b"ak-gwe-r7"); +teleprobe_meta::timeout!(120); + +use defmt::{error, info, unwrap}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_net_enc28j60::Enc28j60; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::rng::Rng; +use embassy_nrf::spim::{self, Spim}; +use embassy_nrf::{bind_interrupts, peripherals}; +use embassy_time::{with_timeout, Delay, Duration, Timer}; +use embedded_hal_bus::spi::ExclusiveDevice; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + SPIM3 => spim::InterruptHandler; + RNG => embassy_nrf::rng::InterruptHandler; +}); + +type MyDriver = Enc28j60< + ExclusiveDevice, Output<'static, peripherals::P0_15>, Delay>, + Output<'static, peripherals::P0_13>, +>; + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("running!"); + + let eth_sck = p.P0_20; + let eth_mosi = p.P0_22; + let eth_miso = p.P0_24; + let eth_cs = p.P0_15; + let eth_rst = p.P0_13; + let _eth_irq = p.P0_12; + + let mut config = spim::Config::default(); + config.frequency = spim::Frequency::M16; + let spi = spim::Spim::new(p.SPI3, Irqs, eth_sck, eth_miso, eth_mosi, config); + let cs = Output::new(eth_cs, Level::High, OutputDrive::Standard); + let spi = ExclusiveDevice::new(spi, cs, Delay); + + let rst = Output::new(eth_rst, Level::High, OutputDrive::Standard); + let mac_addr = [2, 3, 4, 5, 6, 7]; + let device = Enc28j60::new(spi, Some(rst), mac_addr); + + let config = embassy_net::Config::dhcpv4(Default::default()); + // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + // }); + + // Generate random seed + let mut rng = Rng::new(p.RNG, Irqs); + let mut seed = [0; 8]; + rng.blocking_fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); + + unwrap!(spawner.spawn(net_task(stack))); + + info!("Waiting for DHCP up..."); + while stack.config_v4().is_none() { + Timer::after(Duration::from_millis(100)).await; + } + info!("IP addressing up!"); + + let down = test_download(stack).await; + let up = test_upload(stack).await; + let updown = test_upload_download(stack).await; + + assert!(down > TEST_EXPECTED_DOWNLOAD_KBPS); + assert!(up > TEST_EXPECTED_UPLOAD_KBPS); + assert!(updown > TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +const TEST_DURATION: usize = 10; +const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 200; +const TEST_EXPECTED_UPLOAD_KBPS: usize = 200; +const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 150; +const RX_BUFFER_SIZE: usize = 4096; +const TX_BUFFER_SIZE: usize = 4096; +const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); +const DOWNLOAD_PORT: u16 = 4321; +const UPLOAD_PORT: u16 = 4322; +const UPLOAD_DOWNLOAD_PORT: u16 = 4323; + +async fn test_download(stack: &'static Stack) -> usize { + info!("Testing download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("download: {} kB/s", kbps); + kbps +} + +async fn test_upload(stack: &'static Stack) -> usize { + info!("Testing upload..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let buf = [0; 4096]; + let mut total: usize = 0; + with_timeout(Duration::from_secs(TEST_DURATION as _), async { + loop { + match socket.write(&buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload: {} kB/s", kbps); + kbps +} + +async fn test_upload_download(stack: &'static Stack) -> usize { + info!("Testing upload+download..."); + + let mut rx_buffer = [0; RX_BUFFER_SIZE]; + let mut tx_buffer = [0; TX_BUFFER_SIZE]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + + info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT); + if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await { + error!("connect error: {:?}", e); + return 0; + } + info!("connected, testing..."); + + let (mut reader, mut writer) = socket.split(); + + let tx_buf = [0; 4096]; + let mut rx_buf = [0; 4096]; + let mut total: usize = 0; + let tx_fut = async { + loop { + match writer.write(&tx_buf).await { + Ok(0) => { + error!("write zero?!??!?!"); + return 0; + } + Ok(_) => {} + Err(e) => { + error!("write error: {:?}", e); + return 0; + } + } + } + }; + + let rx_fut = async { + loop { + match reader.read(&mut rx_buf).await { + Ok(0) => { + error!("read EOF"); + return 0; + } + Ok(n) => total += n, + Err(e) => { + error!("read error: {:?}", e); + return 0; + } + } + } + }; + + with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut)) + .await + .ok(); + + let kbps = (total + 512) / 1024 / TEST_DURATION; + info!("upload+download: {} kB/s", kbps); + kbps +} diff --git a/tests/nrf/src/bin/timer.rs b/tests/nrf/src/bin/timer.rs index 607c5bbf..c00f35fd 100644 --- a/tests/nrf/src/bin/timer.rs +++ b/tests/nrf/src/bin/timer.rs @@ -1,8 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"nrf52840-dk"); use defmt::{assert, info}; use embassy_executor::Spawner; diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index 16055fae..ee46af2a 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -1,9 +1,8 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] - -#[path = "../common.rs"] -mod common; +teleprobe_meta::target!(b"nrf52840-dk"); +teleprobe_meta::timeout!(120); use defmt::{error, info, unwrap}; use embassy_executor::Spawner; @@ -19,8 +18,6 @@ use embedded_hal_bus::spi::ExclusiveDevice; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; -teleprobe_meta::timeout!(120); - bind_interrupts!(struct Irqs { SPIM3 => spim::InterruptHandler; RNG => embassy_nrf::rng::InterruptHandler; diff --git a/tests/nrf/src/common.rs b/tests/nrf/src/common.rs deleted file mode 100644 index 1a05ac1c..00000000 --- a/tests/nrf/src/common.rs +++ /dev/null @@ -1 +0,0 @@ -teleprobe_meta::target!(b"nrf52840-dk"); From 2ea17d27836d98f55659e23502c91a33e2c06d29 Mon Sep 17 00:00:00 2001 From: Aurelien Jacobs Date: Fri, 18 Aug 2023 16:37:44 +0200 Subject: [PATCH 269/298] stm32: allow setting the PWM output polarity --- embassy-stm32/src/timer/complementary_pwm.rs | 5 ++ embassy-stm32/src/timer/mod.rs | 49 ++++++++++++++++++++ embassy-stm32/src/timer/simple_pwm.rs | 4 ++ 3 files changed, 58 insertions(+) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 533267cf..4f033e3a 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -108,6 +108,11 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { self.inner.set_compare_value(channel, duty) } + pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + self.inner.set_output_polarity(channel, polarity); + self.inner.set_complementary_output_polarity(channel, polarity); + } + /// Set the dead time as a proportion of max_duty pub fn set_dead_time(&mut self, value: u16) { let (ckd, value) = compute_dead_time_value(value); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 6c2d6d82..4ffb2a28 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -53,6 +53,8 @@ pub(crate) mod sealed { fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity); + fn enable_channel(&mut self, channel: Channel, enable: bool); fn set_compare_value(&mut self, channel: Channel, value: u16); @@ -61,6 +63,8 @@ pub(crate) mod sealed { } pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { + fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity); + fn set_dead_time_clock_division(&mut self, value: vals::Ckd); fn set_dead_time_value(&mut self, value: u8); @@ -71,6 +75,8 @@ pub(crate) mod sealed { pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance { fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity); + fn enable_channel(&mut self, channel: Channel, enable: bool); fn set_compare_value(&mut self, channel: Channel, value: u32); @@ -125,6 +131,21 @@ impl From for stm32_metapac::timer::vals::Ocm { } } +#[derive(Clone, Copy)] +pub enum OutputPolarity { + ActiveHigh, + ActiveLow, +} + +impl From for bool { + fn from(mode: OutputPolarity) -> Self { + match mode { + OutputPolarity::ActiveHigh => false, + OutputPolarity::ActiveLow => true, + } + } +} + pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} @@ -265,6 +286,13 @@ macro_rules! impl_compare_capable_16bit { .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); } + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16() + .ccer() + .modify(|w| w.set_ccp(channel.raw(), polarity.into())); + } + fn enable_channel(&mut self, channel: Channel, enable: bool) { use sealed::GeneralPurpose16bitInstance; Self::regs_gp16() @@ -325,6 +353,13 @@ foreach_interrupt! { Self::regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into())); } + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32() + .ccer() + .modify(|w| w.set_ccp(channel.raw(), polarity.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)); @@ -388,6 +423,13 @@ foreach_interrupt! { .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); } + fn set_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_ccp(channel.raw(), polarity.into())); + } + fn enable_channel(&mut self, channel: Channel, enable: bool) { use crate::timer::sealed::AdvancedControlInstance; Self::regs_advanced() @@ -409,6 +451,13 @@ foreach_interrupt! { } impl sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { + fn set_complementary_output_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_ccnp(channel.raw(), polarity.into())); + } + 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)); diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 40e3dd1b..9e28878b 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -104,4 +104,8 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty) } + + pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { + self.inner.set_output_polarity(channel, polarity); + } } From eb05a18c452ec6152c50c75f99e57ce769013c68 Mon Sep 17 00:00:00 2001 From: Alec Cox Date: Fri, 18 Aug 2023 13:50:14 -0700 Subject: [PATCH 270/298] add error translation to tcp errors Translation of tpc client ConnectError and Error to the appropriate embedded_io_async errors --- embassy-net/src/tcp.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index c903fb24..1a876692 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -384,13 +384,20 @@ mod embedded_io_impls { impl embedded_io_async::Error for ConnectError { fn kind(&self) -> embedded_io_async::ErrorKind { - embedded_io_async::ErrorKind::Other + match self { + ConnectError::ConnectionReset => embedded_io_async::ErrorKind::ConnectionReset, + ConnectError::TimedOut => embedded_io_async::ErrorKind::TimedOut, + ConnectError::NoRoute => embedded_io_async::ErrorKind::NotConnected, + ConnectError::InvalidState => embedded_io_async::ErrorKind::Other, + } } } impl embedded_io_async::Error for Error { fn kind(&self) -> embedded_io_async::ErrorKind { - embedded_io_async::ErrorKind::Other + match self { + Error::ConnectionReset => embedded_io_async::ErrorKind::ConnectionReset, + } } } From 5bc0175be9482be4737dfffd3a615cee3301e775 Mon Sep 17 00:00:00 2001 From: Dominik Sliwa Date: Fri, 18 Aug 2023 22:10:13 +0200 Subject: [PATCH 271/298] configure flash latency after axi clock and handle different flash in STM32H7A/B devices --- embassy-stm32/Cargo.toml | 4 +-- embassy-stm32/src/flash/h7.rs | 12 +++++++-- embassy-stm32/src/flash/mod.rs | 4 ++- embassy-stm32/src/rcc/h7.rs | 49 ++++++++++++++++++++++++++++++++-- 4 files changed, 62 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index c3f5706e..fe5dc443 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 = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7eddb78e705905af4c1dd2359900db3e78a3c500" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b87e34c661e19ff6dc603fabfe7fe99ab7261f7" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7eddb78e705905af4c1dd2359900db3e78a3c500", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b87e34c661e19ff6dc603fabfe7fe99ab7261f7", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index bb429d77..625bf13f 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -11,7 +11,7 @@ pub const fn is_default_layout() -> bool { } const fn is_dual_bank() -> bool { - FLASH_REGIONS.len() == 2 + FLASH_REGIONS.len() >= 2 } pub fn get_flash_regions() -> &'static [&'static FlashRegion] { @@ -49,6 +49,7 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) }; bank.cr().write(|w| { w.set_pg(true); + #[cfg(flash_h7)] w.set_psize(2); // 32 bits at once }); cortex_m::asm::isb(); @@ -85,7 +86,10 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E let bank = pac::FLASH.bank(sector.bank as usize); bank.cr().modify(|w| { w.set_ser(true); - w.set_snb(sector.index_in_bank) + #[cfg(flash_h7)] + w.set_snb(sector.index_in_bank); + #[cfg(flash_h7ab)] + w.set_ssn(sector.index_in_bank); }); bank.cr().modify(|w| { @@ -126,6 +130,10 @@ unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { error!("incerr"); return Err(Error::Seq); } + if sr.crcrderr() { + error!("crcrderr"); + return Err(Error::Seq); + } if sr.operr() { return Err(Error::Prog); } diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 4308037f..fb20dcd3 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -65,9 +65,11 @@ impl FlashRegion { #[cfg_attr(flash_f7, path = "f7.rs")] #[cfg_attr(flash_g0, path = "g0.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] +#[cfg_attr(flash_h7ab, path = "h7.rs")] #[cfg_attr( not(any( - flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_g0, flash_h7 + flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f3, flash_f4, flash_f7, flash_g0, flash_h7, + flash_h7ab )), path = "other.rs" )] diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs index 0788b064..7fb4fb95 100644 --- a/embassy-stm32/src/rcc/h7.rs +++ b/embassy-stm32/src/rcc/h7.rs @@ -200,6 +200,7 @@ fn flash_setup(rcc_aclk: u32, vos: VoltageScale) { // See RM0433 Rev 7 Table 17. FLASH recommended number of wait // states and programming delay + #[cfg(flash_h7)] let (wait_states, progr_delay) = match vos { // VOS 0 range VCORE 1.26V - 1.40V VoltageScale::Scale0 => match rcc_aclk_mhz { @@ -239,6 +240,50 @@ fn flash_setup(rcc_aclk: u32, vos: VoltageScale) { }, }; + // See RM0455 Rev 10 Table 16. FLASH recommended number of wait + // states and programming delay + #[cfg(flash_h7ab)] + let (wait_states, progr_delay) = match vos { + // VOS 0 range VCORE 1.25V - 1.35V + VoltageScale::Scale0 => match rcc_aclk_mhz { + 0..=42 => (0, 0), + 43..=84 => (1, 0), + 85..=126 => (2, 1), + 127..=168 => (3, 1), + 169..=210 => (4, 2), + 211..=252 => (5, 2), + 253..=280 => (6, 3), + _ => (7, 3), + }, + // VOS 1 range VCORE 1.15V - 1.25V + VoltageScale::Scale1 => match rcc_aclk_mhz { + 0..=38 => (0, 0), + 39..=76 => (1, 0), + 77..=114 => (2, 1), + 115..=152 => (3, 1), + 153..=190 => (4, 2), + 191..=225 => (5, 2), + _ => (7, 3), + }, + // VOS 2 range VCORE 1.05V - 1.15V + VoltageScale::Scale2 => match rcc_aclk_mhz { + 0..=34 => (0, 0), + 35..=68 => (1, 0), + 69..=102 => (2, 1), + 103..=136 => (3, 1), + 137..=160 => (4, 2), + _ => (7, 3), + }, + // VOS 3 range VCORE 0.95V - 1.05V + VoltageScale::Scale3 => match rcc_aclk_mhz { + 0..=22 => (0, 0), + 23..=44 => (1, 0), + 45..=66 => (2, 1), + 67..=88 => (3, 1), + _ => (7, 3), + }, + }; + FLASH.acr().write(|w| { w.set_wrhighfreq(progr_delay); w.set_latency(wait_states) @@ -538,8 +583,6 @@ pub(crate) unsafe fn init(mut config: Config) { let requested_pclk4 = config.pclk4.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2)); let (rcc_pclk4, ppre4_bits, ppre4, _) = ppre_calculate(requested_pclk4, rcc_hclk, pclk_max, None); - flash_setup(rcc_aclk, pwr_vos); - // Start switching clocks ------------------- // Ensure CSI is on and stable @@ -595,6 +638,8 @@ pub(crate) unsafe fn init(mut config: Config) { // core voltage while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {} + flash_setup(rcc_aclk, pwr_vos); + // APB1 / APB2 Prescaler RCC.d2cfgr().modify(|w| { w.set_d2ppre1(Dppre::from_bits(ppre1_bits)); From cc400aa17813ff9f2329842856725e1b265f39a2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 19 Aug 2023 00:59:37 +0200 Subject: [PATCH 272/298] stm32: fix f37x build. originally broke in https://github.com/embassy-rs/embassy/pull/1762 --- ci.sh | 1 + embassy-stm32/src/adc/mod.rs | 16 ++++++++-------- embassy-stm32/src/adc/sample_time.rs | 2 +- embassy-stm32/src/rcc/mod.rs | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ci.sh b/ci.sh index f82cc627..e83b3b84 100755 --- a/ci.sh +++ b/ci.sh @@ -81,6 +81,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f378cc,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \ diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 94a8538b..e57889aa 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -1,6 +1,6 @@ #![macro_use] -#[cfg(not(adc_f3))] +#[cfg(not(any(adc_f3, adc_f3_v2)))] #[cfg_attr(adc_f1, path = "f1.rs")] #[cfg_attr(adc_v1, path = "v1.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] @@ -8,16 +8,16 @@ #[cfg_attr(adc_v4, path = "v4.rs")] mod _version; -#[cfg(not(any(adc_f1, adc_f3)))] +#[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] mod resolution; mod sample_time; -#[cfg(not(adc_f3))] +#[cfg(not(any(adc_f3, adc_f3_v2)))] #[allow(unused)] pub use _version::*; -#[cfg(not(any(adc_f1, adc_f3)))] +#[cfg(not(any(adc_f1, adc_f3, adc_f3_v2)))] pub use resolution::Resolution; -#[cfg(not(adc_f3))] +#[cfg(not(any(adc_f3, adc_f3_v2)))] pub use sample_time::SampleTime; use crate::peripherals; @@ -25,14 +25,14 @@ use crate::peripherals; pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::PeripheralRef<'d, T>, - #[cfg(not(adc_f3))] + #[cfg(not(any(adc_f3, adc_f3_v2)))] sample_time: SampleTime, } pub(crate) mod sealed { pub trait Instance { fn regs() -> crate::pac::adc::Adc; - #[cfg(not(any(adc_f1, adc_v1, adc_f3)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))] fn common_regs() -> crate::pac::adccommon::AdcCommon; } @@ -60,7 +60,7 @@ foreach_peripheral!( fn regs() -> crate::pac::adc::Adc { crate::pac::$inst } - #[cfg(not(any(adc_f1, adc_v1, adc_f3)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3, adc_f3_v2)))] fn common_regs() -> crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, $common_inst:ident) => { diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index df052556..5480e7a7 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -1,4 +1,4 @@ -#[cfg(not(adc_f3))] +#[cfg(not(any(adc_f3, adc_f3_v2)))] macro_rules! impl_sample_time { ($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 62c19bda..e5dd0019 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -9,7 +9,7 @@ use crate::time::Hertz; #[cfg_attr(rcc_f0, path = "f0.rs")] #[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")] #[cfg_attr(rcc_f2, path = "f2.rs")] -#[cfg_attr(rcc_f3, path = "f3.rs")] +#[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")] #[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")] #[cfg_attr(rcc_f7, path = "f7.rs")] #[cfg_attr(rcc_c0, path = "c0.rs")] From 5d4da78c944e5f7e851b7ff7843b97d9466a4e64 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 19 Aug 2023 01:05:13 +0200 Subject: [PATCH 273/298] Fix lora docs build. --- embassy-lora/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 90c544d8..feea0658 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -29,3 +29,6 @@ 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 } + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"} \ No newline at end of file From 2d980068c09aa8fcc6bb4fd4d2e46107ef0e997f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 19 Aug 2023 19:31:49 +0200 Subject: [PATCH 274/298] Update Nightly. --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 179ed1d6..7b34afa2 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,7 +1,7 @@ # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly-2023-06-28" +channel = "nightly-2023-08-19" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", From 0032f8a2ec05ed61b17dc1992f907f23f1b37f92 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 19 Aug 2023 19:35:10 +0200 Subject: [PATCH 275/298] rustfmt. --- embassy-net-esp-hosted/src/control.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index 6cd57f68..b8026611 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -35,9 +35,9 @@ macro_rules! ioctl { }; $self.ioctl(&mut msg).await?; let Some(proto::CtrlMsgPayload::$resp_variant($resp)) = msg.payload else { - warn!("unexpected response variant"); - return Err(Error::Internal); - }; + warn!("unexpected response variant"); + return Err(Error::Internal); + }; if $resp.resp != 0 { return Err(Error::Failed($resp.resp)); } From 17e9a8ebe13a254417d323b0f634d18eb81caa3d Mon Sep 17 00:00:00 2001 From: Isaikin Roman Date: Sun, 20 Aug 2023 07:42:54 +0200 Subject: [PATCH 276/298] Fix hardcoded buffer length in USB NCM causing broken link on USB 2.0. --- embassy-usb/src/class/cdc_ncm/mod.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index fcfa0bfc..830e9b76 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -248,6 +248,8 @@ pub struct CdcNcmClass<'d, D: Driver<'d>> { write_ep: D::EndpointIn, _control: &'d ControlShared, + + max_packet_size: usize, } impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { @@ -338,6 +340,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { read_ep, write_ep, _control: &state.shared, + max_packet_size: max_packet_size as usize, } } @@ -349,6 +352,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { Sender { write_ep: self.write_ep, seq: 0, + max_packet_size: self.max_packet_size, }, Receiver { data_if: self.data_if, @@ -365,6 +369,7 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { pub struct Sender<'d, D: Driver<'d>> { write_ep: D::EndpointIn, seq: u16, + max_packet_size: usize, } impl<'d, D: Driver<'d>> Sender<'d, D> { @@ -375,8 +380,8 @@ impl<'d, D: Driver<'d>> Sender<'d, D> { let seq = self.seq; self.seq = self.seq.wrapping_add(1); - const MAX_PACKET_SIZE: usize = 64; // TODO unhardcode const OUT_HEADER_LEN: usize = 28; + const ABS_MAX_PACKET_SIZE: usize = 512; let header = NtbOutHeader { nth_sig: SIG_NTH, @@ -395,27 +400,27 @@ impl<'d, D: Driver<'d>> Sender<'d, D> { }; // Build first packet on a buffer, send next packets straight from `data`. - let mut buf = [0; MAX_PACKET_SIZE]; + let mut buf = [0; ABS_MAX_PACKET_SIZE]; let n = byteify(&mut buf, header); assert_eq!(n.len(), OUT_HEADER_LEN); - if OUT_HEADER_LEN + data.len() < MAX_PACKET_SIZE { + if OUT_HEADER_LEN + data.len() < self.max_packet_size { // First packet is not full, just send it. // No need to send ZLP because it's short for sure. buf[OUT_HEADER_LEN..][..data.len()].copy_from_slice(data); self.write_ep.write(&buf[..OUT_HEADER_LEN + data.len()]).await?; } else { - let (d1, d2) = data.split_at(MAX_PACKET_SIZE - OUT_HEADER_LEN); + let (d1, d2) = data.split_at(self.max_packet_size - OUT_HEADER_LEN); - buf[OUT_HEADER_LEN..].copy_from_slice(d1); - self.write_ep.write(&buf).await?; + buf[OUT_HEADER_LEN..self.max_packet_size].copy_from_slice(d1); + self.write_ep.write(&buf[..self.max_packet_size]).await?; - for chunk in d2.chunks(MAX_PACKET_SIZE) { + for chunk in d2.chunks(self.max_packet_size) { self.write_ep.write(&chunk).await?; } // Send ZLP if needed. - if d2.len() % MAX_PACKET_SIZE == 0 { + if d2.len() % self.max_packet_size == 0 { self.write_ep.write(&[]).await?; } } From 67ca88d5a031369f9bae99c55243625644c93a71 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 20 Aug 2023 10:57:28 +0200 Subject: [PATCH 277/298] stm32/can: make latency assertion more lenient. --- tests/stm32/src/bin/can.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index 8737ca8e..acf54521 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -80,8 +80,8 @@ async fn main(_spawner: Spawner) { const MIN_LATENCY: Duration = Duration::from_micros(50); const MAX_LATENCY: Duration = Duration::from_micros(150); assert!( - MIN_LATENCY < latency && latency < MAX_LATENCY, - "{} < {} < {}", + MIN_LATENCY <= latency && latency <= MAX_LATENCY, + "{} <= {} <= {}", MIN_LATENCY, latency, MAX_LATENCY From 977ae5e3e46b5b3ffe06b53d58147338f74f9473 Mon Sep 17 00:00:00 2001 From: soypat Date: Sun, 20 Aug 2023 20:01:47 -0300 Subject: [PATCH 278/298] don't pay for what you don't use --- cyw43/src/bus.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cyw43/src/bus.rs b/cyw43/src/bus.rs index e26f1112..0b5632cf 100644 --- a/cyw43/src/bus.rs +++ b/cyw43/src/bus.rs @@ -102,7 +102,7 @@ where cmd_buf[0] = cmd; cmd_buf[1..][..buf.len()].copy_from_slice(buf); - self.status = self.spi.cmd_write(&cmd_buf).await; + self.status = self.spi.cmd_write(&cmd_buf[..buf.len() + 1]).await; } #[allow(unused)] From 0a73c84df0936facecb3e1a97cf6f4795d321b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 21 Aug 2023 13:55:30 +0200 Subject: [PATCH 279/298] Make AvailableTask public, deduplicate --- embassy-executor/CHANGELOG.md | 2 + embassy-executor/src/raw/mod.rs | 119 ++++++++++++++++++-------------- embassy-executor/src/spawner.rs | 3 +- 3 files changed, 70 insertions(+), 54 deletions(-) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index e2e7bce3..43d94e54 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - Replaced Pender. Implementations now must define an extern function called `__pender`. +- Made `raw::AvailableTask` public +- Made `SpawnToken::new_failed` public ## 0.2.1 - 2023-08-10 diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 7caa3302..c1d82e18 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -147,10 +147,7 @@ impl TaskStorage { pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { let task = AvailableTask::claim(self); match task { - Some(task) => { - let task = task.initialize(future); - unsafe { SpawnToken::::new(task) } - } + Some(task) => task.initialize(future), None => SpawnToken::new_failed(), } } @@ -186,12 +183,16 @@ impl TaskStorage { } } -struct AvailableTask { +/// An uninitialized [`TaskStorage`]. +pub struct AvailableTask { task: &'static TaskStorage, } impl AvailableTask { - fn claim(task: &'static TaskStorage) -> Option { + /// Try to claim a [`TaskStorage`]. + /// + /// This function returns `None` if a task has already been spawned and has not finished running. + pub fn claim(task: &'static TaskStorage) -> Option { task.raw .state .compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire) @@ -199,61 +200,30 @@ impl AvailableTask { .map(|_| Self { task }) } - fn initialize(self, future: impl FnOnce() -> F) -> TaskRef { + fn initialize_impl(self, future: impl FnOnce() -> F) -> SpawnToken { unsafe { self.task.raw.poll_fn.set(Some(TaskStorage::::poll)); self.task.future.write(future()); - } - TaskRef::new(self.task) - } -} -/// Raw storage that can hold up to N tasks of the same type. -/// -/// This is essentially a `[TaskStorage; N]`. -pub struct TaskPool { - pool: [TaskStorage; N], -} + let task = TaskRef::new(self.task); -impl TaskPool { - /// Create a new TaskPool, with all tasks in non-spawned state. - pub const fn new() -> Self { - Self { - pool: [TaskStorage::NEW; N], + SpawnToken::new(task) } } - /// Try to spawn a task in the pool. - /// - /// See [`TaskStorage::spawn()`] for details. - /// - /// This will loop over the pool and spawn the task in the first storage that - /// is currently free. If none is free, a "poisoned" SpawnToken is returned, - /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. - pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { - let task = self.pool.iter().find_map(AvailableTask::claim); - match task { - Some(task) => { - let task = task.initialize(future); - unsafe { SpawnToken::::new(task) } - } - None => SpawnToken::new_failed(), - } + /// Initialize the [`TaskStorage`] to run the given future. + pub fn initialize(self, future: impl FnOnce() -> F) -> SpawnToken { + self.initialize_impl::(future) } - /// Like spawn(), but allows the task to be send-spawned if the args are Send even if - /// the future is !Send. + /// Initialize the [`TaskStorage`] to run the given future. /// - /// Not covered by semver guarantees. DO NOT call this directly. Intended to be used - /// by the Embassy macros ONLY. + /// # Safety /// - /// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` + /// `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` /// is an `async fn`, NOT a hand-written `Future`. #[doc(hidden)] - pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken - where - FutFn: FnOnce() -> F, - { + pub unsafe fn __initialize_async_fn(self, future: impl FnOnce() -> F) -> SpawnToken { // When send-spawning a task, we construct the future in this thread, and effectively // "send" it to the executor thread by enqueuing it in its queue. Therefore, in theory, // send-spawning should require the future `F` to be `Send`. @@ -279,16 +249,59 @@ impl TaskPool { // // This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly // by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken`. + self.initialize_impl::(future) + } +} - let task = self.pool.iter().find_map(AvailableTask::claim); - match task { - Some(task) => { - let task = task.initialize(future); - unsafe { SpawnToken::::new(task) } - } +/// Raw storage that can hold up to N tasks of the same type. +/// +/// This is essentially a `[TaskStorage; N]`. +pub struct TaskPool { + pool: [TaskStorage; N], +} + +impl TaskPool { + /// Create a new TaskPool, with all tasks in non-spawned state. + pub const fn new() -> Self { + Self { + pool: [TaskStorage::NEW; N], + } + } + + fn spawn_impl(&'static self, future: impl FnOnce() -> F) -> SpawnToken { + match self.pool.iter().find_map(AvailableTask::claim) { + Some(task) => task.initialize_impl::(future), None => SpawnToken::new_failed(), } } + + /// Try to spawn a task in the pool. + /// + /// See [`TaskStorage::spawn()`] for details. + /// + /// This will loop over the pool and spawn the task in the first storage that + /// is currently free. If none is free, a "poisoned" SpawnToken is returned, + /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. + pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { + self.spawn_impl::(future) + } + + /// Like spawn(), but allows the task to be send-spawned if the args are Send even if + /// the future is !Send. + /// + /// Not covered by semver guarantees. DO NOT call this directly. Intended to be used + /// by the Embassy macros ONLY. + /// + /// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` + /// is an `async fn`, NOT a hand-written `Future`. + #[doc(hidden)] + pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken + where + FutFn: FnOnce() -> F, + { + // See the comment in AvailableTask::__initialize_async_fn for explanation. + self.spawn_impl::(future) + } } #[derive(Clone, Copy)] diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 2b622404..5a3a0dee 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -33,7 +33,8 @@ impl SpawnToken { } } - pub(crate) fn new_failed() -> Self { + /// Return a SpawnToken that represents a failed spawn. + pub fn new_failed() -> Self { Self { raw_task: None, phantom: PhantomData, From f723982beccc1306a74560e6455f38834ea95689 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 21 Aug 2023 17:44:38 -0500 Subject: [PATCH 280/298] rtc: impl. draft rtcinstant api --- embassy-stm32/src/rtc/v2.rs | 123 ++++++++++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 26 deletions(-) diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 5cd5adef..ba881e3f 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -5,6 +5,63 @@ use crate::pac::rtc::Rtc; use crate::peripherals::RTC; use crate::rtc::sealed::Instance; +#[cfg(all(feature = "time", any(stm32wb, stm32f4)))] +pub struct RtcInstant { + ssr: u16, + st: u8, +} + +#[cfg(all(feature = "time", any(stm32wb, stm32f4)))] +impl RtcInstant { + pub fn now() -> Self { + // TODO: read value twice + use crate::rtc::bcd2_to_byte; + + let tr = RTC::regs().tr().read(); + let ssr = RTC::regs().ssr().read().ss(); + + let st = bcd2_to_byte((tr.st(), tr.su())); + + let _ = RTC::regs().dr().read(); + + trace!("ssr: {}", ssr); + trace!("st: {}", st); + + Self { ssr, st } + } +} + +#[cfg(all(feature = "time", any(stm32wb, stm32f4)))] +impl core::ops::Sub for RtcInstant { + type Output = embassy_time::Duration; + + fn sub(self, rhs: Self) -> Self::Output { + use embassy_time::{Duration, TICK_HZ}; + + trace!("self st: {}", self.st); + trace!("other st: {}", rhs.st); + + let st = if self.st < rhs.st { self.st + 60 } else { self.st }; + + trace!("self st: {}", st); + + let self_ticks = (st as u32 * 256 + self.ssr as u32); + let other_ticks = (rhs.st as u32 * 256 + rhs.ssr as u32); + let rtc_ticks = self_ticks - other_ticks; + + trace!("self ticks: {}", self_ticks); + trace!("other ticks: {}", other_ticks); + trace!("rtc ticks: {}", rtc_ticks); + + // TODO: read prescaler + + Duration::from_ticks( + ((((st as u32 * 256 + self.ssr as u32) - (rhs.st as u32 * 256 + rhs.ssr as u32)) * TICK_HZ as u32) as u32 + / 256u32) as u64, + ) + } +} + #[derive(Clone, Copy)] pub(crate) enum WakeupPrescaler { Div2, @@ -13,7 +70,7 @@ pub(crate) enum WakeupPrescaler { Div16, } -#[cfg(stm32wb)] +#[cfg(any(stm32wb, stm32f4))] impl From for crate::pac::rtc::vals::Wucksel { fn from(val: WakeupPrescaler) -> Self { use crate::pac::rtc::vals::Wucksel; @@ -27,7 +84,7 @@ impl From for crate::pac::rtc::vals::Wucksel { } } -#[cfg(stm32wb)] +#[cfg(any(stm32wb, stm32f4))] impl From for WakeupPrescaler { fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { use crate::pac::rtc::vals::Wucksel; @@ -86,11 +143,14 @@ impl super::Rtc { } #[allow(dead_code)] - #[cfg(all(feature = "time", stm32wb))] + #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] /// start the wakeup alarm and return the actual duration of the alarm /// the actual duration will be the closest value possible that is less /// than the requested duration. - pub(crate) fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> embassy_time::Duration { + /// + /// note: this api is exposed for testing purposes until low power is implemented. + /// it is not intended to be public + pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { use embassy_time::{Duration, TICK_HZ}; use crate::interrupt::typelevel::Interrupt; @@ -103,8 +163,8 @@ impl super::Rtc { // adjust the rtc ticks to the prescaler let rtc_ticks = rtc_ticks / (>::into(prescaler) as u64); - let rtc_ticks = if rtc_ticks > u16::MAX as u64 { - u16::MAX + let rtc_ticks = if rtc_ticks >= u16::MAX as u64 { + u16::MAX - 1 } else { rtc_ticks as u16 }; @@ -113,8 +173,10 @@ impl super::Rtc { rtc_ticks as u64 * TICK_HZ * (>::into(prescaler) as u64) / rtc_hz, ); - crate::interrupt::typelevel::RTC_WKUP::unpend(); - unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; + trace!("set wakeup timer for {} ms", duration.as_millis()); + + RTC::regs().wpr().write(|w| w.set_key(0xca)); + RTC::regs().wpr().write(|w| w.set_key(0x53)); RTC::regs().wutr().modify(|w| w.set_wut(rtc_ticks)); @@ -125,36 +187,45 @@ impl super::Rtc { w.set_wute(true); }); - duration + if !RTC::regs().cr().read().wute() { + trace!("wakeup timer not enabled"); + } else { + trace!("wakeup timer enabled"); + } + + crate::interrupt::typelevel::RTC_WKUP::unpend(); + unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; + + RtcInstant::now() } #[allow(dead_code)] - #[cfg(all(feature = "time", stm32wb))] - // stop the wakeup alarm and return the time remaining - pub(crate) fn stop_wakeup_alarm() -> embassy_time::Duration { - use embassy_time::{Duration, TICK_HZ}; - + #[cfg(all(feature = "time", any(stm32wb, stm32f4)))] + /// stop the wakeup alarm and return the time remaining + /// + /// note: this api is exposed for testing purposes until low power is implemented. + /// it is not intended to be public + pub fn stop_wakeup_alarm() -> RtcInstant { use crate::interrupt::typelevel::Interrupt; - use crate::rcc::get_freqs; + + crate::interrupt::typelevel::RTC_WKUP::disable(); + + trace!("disable wakeup timer..."); RTC::regs().cr().modify(|w| { w.set_wute(false); }); + trace!("wait for wakeup timer stop..."); + // Wait for the wakeup timer to stop - while !RTC::regs().isr().read().wutf() {} + // while !RTC::regs().isr().read().wutf() {} + // + // RTC::regs().isr().modify(|w| w.set_wutf(false)); - RTC::regs().isr().modify(|w| w.set_wutf(false)); + trace!("wait for wakeup timer stop...done"); - crate::interrupt::typelevel::RTC_WKUP::disable(); - - let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; - let prescaler: WakeupPrescaler = RTC::regs().cr().read().wucksel().into(); - let rtc_ticks = RTC::regs().wutr().read().wut(); - - Duration::from_ticks( - rtc_ticks as u64 * TICK_HZ * (>::into(prescaler) as u64) / rtc_hz, - ) + RtcInstant::now() } #[allow(dead_code)] From 8c12453544131a5ef8a6be78e87c8e49a12cbec3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 21 Aug 2023 17:50:18 -0500 Subject: [PATCH 281/298] stm32/rcc: set rtc clock on f4 --- embassy-stm32/src/rcc/f4.rs | 7 +++++++ embassy-stm32/src/rcc/mod.rs | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index 2ae0d15c..ee9cb289 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -473,6 +473,11 @@ pub(crate) unsafe fn init(config: Config) { Rtc::set_clock_source(clock_source); }); + let rtc = match config.rtc { + Some(RtcClockSource::LSI) => Some(LSI_FREQ), + _ => None, + }; + set_freqs(Clocks { sys: Hertz(sysclk), apb1: Hertz(pclk1), @@ -492,6 +497,8 @@ pub(crate) unsafe fn init(config: Config) { #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))] pllsai: None, + + rtc: rtc, }); } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 62c19bda..698da8d8 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -74,7 +74,7 @@ pub struct Clocks { #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] pub adc: Option, - #[cfg(rcc_wb)] + #[cfg(any(rcc_wb, rcc_f4))] /// Set only if the lsi or lse is configured pub rtc: Option, } From 7148397771a7c1a374d4b298b206f815573772f8 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 21 Aug 2023 18:00:49 -0500 Subject: [PATCH 282/298] stm32/rtc: misc fixes --- embassy-stm32/src/rtc/v2.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index ba881e3f..7acf1b6a 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -18,9 +18,15 @@ impl RtcInstant { use crate::rtc::bcd2_to_byte; let tr = RTC::regs().tr().read(); + let tr2 = RTC::regs().tr().read(); let ssr = RTC::regs().ssr().read().ss(); + let ssr2 = RTC::regs().ssr().read().ss(); let st = bcd2_to_byte((tr.st(), tr.su())); + let st2 = bcd2_to_byte((tr2.st(), tr2.su())); + + assert!(st == st2); + assert!(ssr == ssr2); let _ = RTC::regs().dr().read(); @@ -41,12 +47,15 @@ impl core::ops::Sub for RtcInstant { trace!("self st: {}", self.st); trace!("other st: {}", rhs.st); + trace!("self ssr: {}", self.ssr); + trace!("other ssr: {}", rhs.ssr); + let st = if self.st < rhs.st { self.st + 60 } else { self.st }; trace!("self st: {}", st); - let self_ticks = (st as u32 * 256 + self.ssr as u32); - let other_ticks = (rhs.st as u32 * 256 + rhs.ssr as u32); + let self_ticks = st as u32 * 256 + self.ssr as u32; + let other_ticks = rhs.st as u32 * 256 + rhs.ssr as u32; let rtc_ticks = self_ticks - other_ticks; trace!("self ticks: {}", self_ticks); From 5bfddfc9b6c5b64cecba377cc6c42e1e270e98cc Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 21 Aug 2023 18:10:10 -0500 Subject: [PATCH 283/298] stm32/rcc: add rtc to f410 --- embassy-stm32/src/rcc/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 698da8d8..4d4c6b61 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -74,7 +74,7 @@ pub struct Clocks { #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))] pub adc: Option, - #[cfg(any(rcc_wb, rcc_f4))] + #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] /// Set only if the lsi or lse is configured pub rtc: Option, } From 8878ce046c2665eb641f33bc484ca68083d7d353 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 21 Aug 2023 18:33:10 -0500 Subject: [PATCH 284/298] rtc: fix rtcinstant delay computation --- embassy-stm32/src/rtc/v2.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 7acf1b6a..e9b83123 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -54,8 +54,8 @@ impl core::ops::Sub for RtcInstant { trace!("self st: {}", st); - let self_ticks = st as u32 * 256 + self.ssr as u32; - let other_ticks = rhs.st as u32 * 256 + rhs.ssr as u32; + let self_ticks = st as u32 * 256 + (255 - self.ssr as u32); + let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32); let rtc_ticks = self_ticks - other_ticks; trace!("self ticks: {}", self_ticks); @@ -65,7 +65,8 @@ impl core::ops::Sub for RtcInstant { // TODO: read prescaler Duration::from_ticks( - ((((st as u32 * 256 + self.ssr as u32) - (rhs.st as u32 * 256 + rhs.ssr as u32)) * TICK_HZ as u32) as u32 + ((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32))) + * TICK_HZ as u32) as u32 / 256u32) as u64, ) } From c39671266e21dd9e35e60cc680453cd5c38162db Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Fri, 11 Aug 2023 11:58:22 +0200 Subject: [PATCH 285/298] Deprecate *recv* in favor of *receive* --- embassy-stm32-wpan/src/mac/driver.rs | 4 +- embassy-stm32-wpan/src/mac/runner.rs | 2 +- embassy-stm32/src/can/bxcan.rs | 4 +- embassy-sync/src/channel.rs | 88 +++++++++---------- examples/nrf52840/src/bin/channel.rs | 2 +- .../src/bin/channel_sender_receiver.rs | 2 +- examples/nrf52840/src/bin/uart_split.rs | 2 +- .../rp/src/bin/lora_p2p_send_multicore.rs | 2 +- examples/rp/src/bin/multicore.rs | 2 +- examples/stm32f3/src/bin/button_events.rs | 4 +- examples/stm32h5/src/bin/usart_split.rs | 2 +- examples/stm32h7/src/bin/usart_split.rs | 2 +- tests/rp/src/bin/gpio_multicore.rs | 6 +- tests/rp/src/bin/multicore.rs | 4 +- 14 files changed, 63 insertions(+), 63 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 93898d88..bfc4f1ee 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -93,7 +93,7 @@ impl<'d> embassy_net_driver::RxToken for RxToken<'d> { { // Only valid data events should be put into the queue - let data_event = match self.rx.try_recv().unwrap() { + let data_event = match self.rx.try_receive().unwrap() { MacEvent::McpsDataInd(data_event) => data_event, _ => unreachable!(), }; @@ -113,7 +113,7 @@ impl<'d> embassy_net_driver::TxToken for TxToken<'d> { F: FnOnce(&mut [u8]) -> R, { // Only valid tx buffers should be put into the queue - let buf = self.tx_buf.try_recv().unwrap(); + let buf = self.tx_buf.try_receive().unwrap(); let r = f(&mut buf[..len]); // The tx channel should always be of equal capacity to the tx_buf channel diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index 1be6df8a..d3099b6b 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -73,7 +73,7 @@ impl<'a> Runner<'a> { let mut msdu_handle = 0x02; loop { - let (buf, len) = self.tx_channel.recv().await; + let (buf, len) = self.tx_channel.receive().await; let _wm = self.write_mutex.lock().await; // The mutex should be dropped on the next loop iteration diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index e439207e..7ad13cec 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -478,7 +478,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { pub async fn read(&mut self) -> Result { poll_fn(|cx| { T::state().err_waker.register(cx.waker()); - if let Poll::Ready(envelope) = T::state().rx_queue.recv().poll_unpin(cx) { + if let Poll::Ready(envelope) = T::state().rx_queue.receive().poll_unpin(cx) { return Poll::Ready(Ok(envelope)); } else if let Some(err) = self.curr_error() { return Poll::Ready(Err(err)); @@ -493,7 +493,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result { - if let Ok(envelope) = T::state().rx_queue.try_recv() { + if let Ok(envelope) = T::state().rx_queue.try_receive() { return Ok(envelope); } diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index dc727fb1..62ea1307 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -147,16 +147,16 @@ where { /// Receive the next value. /// - /// See [`Channel::recv()`]. - pub fn recv(&self) -> RecvFuture<'_, M, T, N> { - self.channel.recv() + /// See [`Channel::receive()`]. + pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> { + self.channel.receive() } /// Attempt to immediately receive the next value. /// - /// See [`Channel::try_recv()`] - pub fn try_recv(&self) -> Result { - self.channel.try_recv() + /// See [`Channel::try_receive()`] + pub fn try_receive(&self) -> Result { + self.channel.try_receive() } /// Allows a poll_fn to poll until the channel is ready to receive @@ -190,16 +190,16 @@ impl<'ch, T> Copy for DynamicReceiver<'ch, T> {} impl<'ch, T> DynamicReceiver<'ch, T> { /// Receive the next value. /// - /// See [`Channel::recv()`]. - pub fn recv(&self) -> DynamicRecvFuture<'_, T> { - DynamicRecvFuture { channel: self.channel } + /// See [`Channel::receive()`]. + pub fn receive(&self) -> DynamicReceiveFuture<'_, T> { + DynamicReceiveFuture { channel: self.channel } } /// Attempt to immediately receive the next value. /// - /// See [`Channel::try_recv()`] - pub fn try_recv(&self) -> Result { - self.channel.try_recv_with_context(None) + /// See [`Channel::try_receive()`] + pub fn try_receive(&self) -> Result { + self.channel.try_receive_with_context(None) } /// Allows a poll_fn to poll until the channel is ready to receive @@ -226,16 +226,16 @@ where } } -/// Future returned by [`Channel::recv`] and [`Receiver::recv`]. +/// Future returned by [`Channel::receive`] and [`Receiver::receive`]. #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct RecvFuture<'ch, M, T, const N: usize> +pub struct ReceiveFuture<'ch, M, T, const N: usize> where M: RawMutex, { channel: &'ch Channel, } -impl<'ch, M, T, const N: usize> Future for RecvFuture<'ch, M, T, N> +impl<'ch, M, T, const N: usize> Future for ReceiveFuture<'ch, M, T, N> where M: RawMutex, { @@ -246,19 +246,19 @@ where } } -/// Future returned by [`DynamicReceiver::recv`]. +/// Future returned by [`DynamicReceiver::receive`]. #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct DynamicRecvFuture<'ch, T> { +pub struct DynamicReceiveFuture<'ch, T> { channel: &'ch dyn DynamicChannel, } -impl<'ch, T> Future for DynamicRecvFuture<'ch, T> { +impl<'ch, T> Future for DynamicReceiveFuture<'ch, T> { type Output = T; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.channel.try_recv_with_context(Some(cx)) { + match self.channel.try_receive_with_context(Some(cx)) { Ok(v) => Poll::Ready(v), - Err(TryRecvError::Empty) => Poll::Pending, + Err(TryReceiveError::Empty) => Poll::Pending, } } } @@ -324,7 +324,7 @@ impl<'ch, T> Unpin for DynamicSendFuture<'ch, T> {} trait DynamicChannel { fn try_send_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), TrySendError>; - fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result; + fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result; fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()>; fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> Poll<()>; @@ -332,10 +332,10 @@ trait DynamicChannel { fn poll_receive(&self, cx: &mut Context<'_>) -> Poll; } -/// Error returned by [`try_recv`](Channel::try_recv). +/// Error returned by [`try_receive`](Channel::try_receive). #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum TryRecvError { +pub enum TryReceiveError { /// A message could not be received because the channel is empty. Empty, } @@ -364,11 +364,11 @@ impl ChannelState { } } - fn try_recv(&mut self) -> Result { - self.try_recv_with_context(None) + fn try_receive(&mut self) -> Result { + self.try_receive_with_context(None) } - fn try_recv_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result { + fn try_receive_with_context(&mut self, cx: Option<&mut Context<'_>>) -> Result { if self.queue.is_full() { self.senders_waker.wake(); } @@ -379,7 +379,7 @@ impl ChannelState { if let Some(cx) = cx { self.receiver_waker.register(cx.waker()); } - Err(TryRecvError::Empty) + Err(TryReceiveError::Empty) } } @@ -474,8 +474,8 @@ where self.inner.lock(|rc| f(&mut *rc.borrow_mut())) } - fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { - self.lock(|c| c.try_recv_with_context(cx)) + fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { + self.lock(|c| c.try_receive_with_context(cx)) } /// Poll the channel for the next message @@ -536,16 +536,16 @@ where /// /// If there are no messages in the channel's buffer, this method will /// wait until a message is sent. - pub fn recv(&self) -> RecvFuture<'_, M, T, N> { - RecvFuture { channel: self } + pub fn receive(&self) -> ReceiveFuture<'_, M, T, N> { + ReceiveFuture { channel: self } } /// Attempt to immediately receive a message. /// /// This method will either receive a message from the channel immediately or return an error /// if the channel is empty. - pub fn try_recv(&self) -> Result { - self.lock(|c| c.try_recv()) + pub fn try_receive(&self) -> Result { + self.lock(|c| c.try_receive()) } } @@ -559,8 +559,8 @@ where Channel::try_send_with_context(self, m, cx) } - fn try_recv_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { - Channel::try_recv_with_context(self, cx) + fn try_receive_with_context(&self, cx: Option<&mut Context<'_>>) -> Result { + Channel::try_receive_with_context(self, cx) } fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { @@ -616,15 +616,15 @@ mod tests { fn receiving_once_with_one_send() { let mut c = ChannelState::::new(); assert!(c.try_send(1).is_ok()); - assert_eq!(c.try_recv().unwrap(), 1); + assert_eq!(c.try_receive().unwrap(), 1); assert_eq!(capacity(&c), 3); } #[test] fn receiving_when_empty() { let mut c = ChannelState::::new(); - match c.try_recv() { - Err(TryRecvError::Empty) => assert!(true), + match c.try_receive() { + Err(TryReceiveError::Empty) => assert!(true), _ => assert!(false), } assert_eq!(capacity(&c), 3); @@ -634,7 +634,7 @@ mod tests { fn simple_send_and_receive() { let c = Channel::::new(); assert!(c.try_send(1).is_ok()); - assert_eq!(c.try_recv().unwrap(), 1); + assert_eq!(c.try_receive().unwrap(), 1); } #[test] @@ -654,7 +654,7 @@ mod tests { let r: DynamicReceiver<'_, u32> = c.receiver().into(); assert!(s.try_send(1).is_ok()); - assert_eq!(r.try_recv().unwrap(), 1); + assert_eq!(r.try_receive().unwrap(), 1); } #[futures_test::test] @@ -669,14 +669,14 @@ mod tests { assert!(c2.try_send(1).is_ok()); }) .is_ok()); - assert_eq!(c.recv().await, 1); + assert_eq!(c.receive().await, 1); } #[futures_test::test] async fn sender_send_completes_if_capacity() { let c = Channel::::new(); c.send(1).await; - assert_eq!(c.recv().await, 1); + assert_eq!(c.receive().await, 1); } #[futures_test::test] @@ -694,11 +694,11 @@ mod tests { // Wish I could think of a means of determining that the async send is waiting instead. // However, I've used the debugger to observe that the send does indeed wait. Delay::new(Duration::from_millis(500)).await; - assert_eq!(c.recv().await, 1); + assert_eq!(c.receive().await, 1); assert!(executor .spawn(async move { loop { - c.recv().await; + c.receive().await; } }) .is_ok()); diff --git a/examples/nrf52840/src/bin/channel.rs b/examples/nrf52840/src/bin/channel.rs index d782a79e..bd9c909d 100644 --- a/examples/nrf52840/src/bin/channel.rs +++ b/examples/nrf52840/src/bin/channel.rs @@ -35,7 +35,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(my_task())); loop { - match CHANNEL.recv().await { + match CHANNEL.receive().await { LedState::On => led.set_high(), LedState::Off => led.set_low(), } diff --git a/examples/nrf52840/src/bin/channel_sender_receiver.rs b/examples/nrf52840/src/bin/channel_sender_receiver.rs index fcccdaed..ec4f1d80 100644 --- a/examples/nrf52840/src/bin/channel_sender_receiver.rs +++ b/examples/nrf52840/src/bin/channel_sender_receiver.rs @@ -33,7 +33,7 @@ async fn recv_task(led: AnyPin, receiver: Receiver<'static, NoopRawMutex, LedSta let mut led = Output::new(led, Level::Low, OutputDrive::Standard); loop { - match receiver.recv().await { + match receiver.receive().await { LedState::On => led.set_high(), LedState::Off => led.set_low(), } diff --git a/examples/nrf52840/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs index 9979a1d5..b748bfcd 100644 --- a/examples/nrf52840/src/bin/uart_split.rs +++ b/examples/nrf52840/src/bin/uart_split.rs @@ -46,7 +46,7 @@ async fn main(spawner: Spawner) { // back out the buffer we receive from the read // task. loop { - let buf = CHANNEL.recv().await; + let buf = CHANNEL.receive().await; info!("writing..."); unwrap!(tx.write(&buf).await); } diff --git a/examples/rp/src/bin/lora_p2p_send_multicore.rs b/examples/rp/src/bin/lora_p2p_send_multicore.rs index 89a62818..b54cc92f 100644 --- a/examples/rp/src/bin/lora_p2p_send_multicore.rs +++ b/examples/rp/src/bin/lora_p2p_send_multicore.rs @@ -113,7 +113,7 @@ async fn core1_task( }; loop { - let buffer: [u8; 3] = CHANNEL.recv().await; + let buffer: [u8; 3] = CHANNEL.receive().await; match lora.prepare_for_tx(&mdltn_params, 20, false).await { Ok(()) => {} Err(err) => { diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index 893b724b..bf017f6a 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -56,7 +56,7 @@ async fn core0_task() { async fn core1_task(mut led: Output<'static, PIN_25>) { info!("Hello from core 1"); loop { - match CHANNEL.recv().await { + match CHANNEL.receive().await { LedState::On => led.set_high(), LedState::Off => led.set_low(), } diff --git a/examples/stm32f3/src/bin/button_events.rs b/examples/stm32f3/src/bin/button_events.rs index 02c475f6..8e97e85e 100644 --- a/examples/stm32f3/src/bin/button_events.rs +++ b/examples/stm32f3/src/bin/button_events.rs @@ -49,12 +49,12 @@ impl<'a> Leds<'a> { async fn show(&mut self) { self.leds[self.current_led].set_high(); - if let Ok(new_message) = with_timeout(Duration::from_millis(500), CHANNEL.recv()).await { + if let Ok(new_message) = with_timeout(Duration::from_millis(500), CHANNEL.receive()).await { self.leds[self.current_led].set_low(); self.process_event(new_message).await; } else { self.leds[self.current_led].set_low(); - if let Ok(new_message) = with_timeout(Duration::from_millis(200), CHANNEL.recv()).await { + if let Ok(new_message) = with_timeout(Duration::from_millis(200), CHANNEL.receive()).await { self.process_event(new_message).await; } } diff --git a/examples/stm32h5/src/bin/usart_split.rs b/examples/stm32h5/src/bin/usart_split.rs index debd6f45..a6b2e690 100644 --- a/examples/stm32h5/src/bin/usart_split.rs +++ b/examples/stm32h5/src/bin/usart_split.rs @@ -44,7 +44,7 @@ async fn main(spawner: Spawner) -> ! { unwrap!(spawner.spawn(reader(rx))); loop { - let buf = CHANNEL.recv().await; + let buf = CHANNEL.receive().await; info!("writing..."); unwrap!(tx.write(&buf).await); } diff --git a/examples/stm32h7/src/bin/usart_split.rs b/examples/stm32h7/src/bin/usart_split.rs index 330d1ce0..aa075345 100644 --- a/examples/stm32h7/src/bin/usart_split.rs +++ b/examples/stm32h7/src/bin/usart_split.rs @@ -44,7 +44,7 @@ async fn main(spawner: Spawner) -> ! { unwrap!(spawner.spawn(reader(rx))); loop { - let buf = CHANNEL.recv().await; + let buf = CHANNEL.receive().await; info!("writing..."); unwrap!(tx.write(&buf).await); } diff --git a/tests/rp/src/bin/gpio_multicore.rs b/tests/rp/src/bin/gpio_multicore.rs index 780112bc..611cecb7 100644 --- a/tests/rp/src/bin/gpio_multicore.rs +++ b/tests/rp/src/bin/gpio_multicore.rs @@ -38,11 +38,11 @@ async fn core0_task(p: PIN_0) { let mut pin = Output::new(p, Level::Low); CHANNEL0.send(()).await; - CHANNEL1.recv().await; + CHANNEL1.receive().await; pin.set_high(); - CHANNEL1.recv().await; + CHANNEL1.receive().await; info!("Test OK"); cortex_m::asm::bkpt(); @@ -52,7 +52,7 @@ async fn core0_task(p: PIN_0) { async fn core1_task(p: PIN_1) { info!("CORE1 is running"); - CHANNEL0.recv().await; + CHANNEL0.receive().await; let mut pin = Input::new(p, Pull::Down); let wait = pin.wait_for_rising_edge(); diff --git a/tests/rp/src/bin/multicore.rs b/tests/rp/src/bin/multicore.rs index 114889de..c4579e5b 100644 --- a/tests/rp/src/bin/multicore.rs +++ b/tests/rp/src/bin/multicore.rs @@ -34,7 +34,7 @@ async fn core0_task() { info!("CORE0 is running"); let ping = true; CHANNEL0.send(ping).await; - let pong = CHANNEL1.recv().await; + let pong = CHANNEL1.receive().await; assert_eq!(ping, pong); info!("Test OK"); @@ -44,6 +44,6 @@ async fn core0_task() { #[embassy_executor::task] async fn core1_task() { info!("CORE1 is running"); - let ping = CHANNEL0.recv().await; + let ping = CHANNEL0.receive().await; CHANNEL1.send(ping).await; } From a5484cd119293967d1d1c663ee2e2ee3a5827e3d Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 22 Aug 2023 20:52:02 +0200 Subject: [PATCH 286/298] Modified the brr calculation to be fully 32-bit --- embassy-stm32/src/usart/mod.rs | 46 +++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index e203336e..255ddfd4 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -809,45 +809,57 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: Kind::Uart => (1, 0x10, 0x1_0000), }; + fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 { + // The calculation to be done to get the BRR is `mul * pclk / presc / baud` + // To do this in 32-bit only we can't multiply `mul` and `pclk` + let clock = pclk / presc; + + // The mul is applied as the last operation to prevent overflow + let brr = clock / baud * mul; + + // The BRR calculation will be a bit off because of integer rounding. + // Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul. + let rounding = ((clock % baud) * mul + (baud / 2)) / baud; + + brr + rounding + } + #[cfg(not(usart_v1))] let mut over8 = false; - let mut found = None; + let mut found_brr = None; for &(presc, _presc_val) in &DIVS { - let denom = (config.baudrate * presc as u32) as u64; - let div = (pclk_freq.0 as u64 * mul + (denom / 2)) / denom; + let brr = calculate_brr(config.baudrate, pclk_freq.0, presc as u32, mul); trace!( "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", presc, - div, - div >> 4, - div & 0x0F + brr, + brr >> 4, + brr & 0x0F ); - if div < brr_min { + if brr < brr_min { #[cfg(not(usart_v1))] - if div * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { + if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { over8 = true; - let div = div as u32; - r.brr().write_value(regs::Brr(((div << 1) & !0xF) | (div & 0x07))); + r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07))); #[cfg(usart_v4)] r.presc().write(|w| w.set_prescaler(_presc_val)); - found = Some(div); + found_brr = Some(brr); break; } panic!("USART: baudrate too high"); } - if div < brr_max { - let div = div as u32; - r.brr().write_value(regs::Brr(div)); + if brr < brr_max { + r.brr().write_value(regs::Brr(brr)); #[cfg(usart_v4)] r.presc().write(|w| w.set_prescaler(_presc_val)); - found = Some(div); + found_brr = Some(brr); break; } } - let div = found.expect("USART: baudrate too low"); + let brr = found_brr.expect("USART: baudrate too low"); #[cfg(not(usart_v1))] let oversampling = if over8 { "8 bit" } else { "16 bit" }; @@ -857,7 +869,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 * mul as u32) / div + pclk_freq.0 / brr * mul ); r.cr2().write(|w| { From 048bdf6968773a642c60ddcac46958d3a54af7c3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 22 Aug 2023 16:48:08 -0500 Subject: [PATCH 287/298] stm32/rtc: allow dead code --- embassy-stm32/src/rtc/v2.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index e9b83123..53d0161d 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -72,6 +72,7 @@ impl core::ops::Sub for RtcInstant { } } +#[allow(dead_code)] #[derive(Clone, Copy)] pub(crate) enum WakeupPrescaler { Div2, @@ -120,6 +121,7 @@ impl From for u32 { } } +#[allow(dead_code)] impl WakeupPrescaler { pub fn compute_min(val: u32) -> Self { *[ From 9e3266b74554ea397bdd963ff12a26aa51e77b63 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 22 Aug 2023 16:58:19 -0500 Subject: [PATCH 288/298] rtc: make fns private --- embassy-stm32/src/rtc/v2.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index e9b83123..4a166e19 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -160,7 +160,7 @@ impl super::Rtc { /// /// note: this api is exposed for testing purposes until low power is implemented. /// it is not intended to be public - pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { + pub(crate) fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { use embassy_time::{Duration, TICK_HZ}; use crate::interrupt::typelevel::Interrupt; @@ -215,7 +215,7 @@ impl super::Rtc { /// /// note: this api is exposed for testing purposes until low power is implemented. /// it is not intended to be public - pub fn stop_wakeup_alarm() -> RtcInstant { + pub(crate) fn stop_wakeup_alarm() -> RtcInstant { use crate::interrupt::typelevel::Interrupt; crate::interrupt::typelevel::RTC_WKUP::disable(); From 6d35bcc3d9818c3380e0d7071c78275b50856c90 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 22 Aug 2023 17:00:00 -0500 Subject: [PATCH 289/298] stm32: add low-power mod --- embassy-stm32/Cargo.toml | 3 ++- embassy-stm32/src/lib.rs | 2 ++ embassy-stm32/src/low_power.rs | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 embassy-stm32/src/low_power.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index fe5dc443..828414f2 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32/src/" -features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time"] +features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "time", "low-power"] flavors = [ { regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" }, @@ -88,6 +88,7 @@ rt = ["stm32-metapac/rt"] defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async?/defmt-03", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"] exti = [] +low-power = [] ## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) memory-x = ["stm32-metapac/memory-x"] diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index adb3054d..8c87ea7d 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -47,6 +47,8 @@ pub mod i2c; pub mod i2s; #[cfg(stm32wb)] pub mod ipcc; +#[cfg(feature = "low-power")] +pub mod low_power; #[cfg(quadspi)] pub mod qspi; #[cfg(rng)] diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs new file mode 100644 index 00000000..ddcfbbb1 --- /dev/null +++ b/embassy-stm32/src/low_power.rs @@ -0,0 +1,15 @@ +use crate::rtc::{Rtc, RtcInstant}; + +static mut RTC: Option<&'static Rtc> = None; + +pub fn stop_with_rtc(rtc: &'static Rtc) { + unsafe { RTC = Some(rtc) }; +} + +pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { + unsafe { RTC }.unwrap().start_wakeup_alarm(requested_duration) +} + +pub fn stop_wakeup_alarm() -> RtcInstant { + unsafe { RTC }.unwrap().stop_wakeup_alarm() +} From faab2d0d53d52f7f311d3958e7e72dd1ee66c3b7 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 22 Aug 2023 17:31:40 -0500 Subject: [PATCH 290/298] stm32: add executor to low-power mod --- embassy-stm32/Cargo.toml | 4 ++- embassy-stm32/src/low_power.rs | 66 ++++++++++++++++++++++++++++++++++ embassy-stm32/src/rtc/v2.rs | 4 +-- 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 828414f2..048d231d 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -38,6 +38,7 @@ embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", fea 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 } +embassy-executor = { version = "0.2.0", path = "../embassy-executor", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true} @@ -88,7 +89,8 @@ rt = ["stm32-metapac/rt"] defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async?/defmt-03", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"] exti = [] -low-power = [] +low-power = [ "dep:embassy-executor", "embassy-executor/arch-cortex-m" ] +embassy-executor = [] ## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) memory-x = ["stm32-metapac/memory-x"] diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index ddcfbbb1..3fe28b9d 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -1,3 +1,10 @@ +use core::arch::asm; +use core::marker::PhantomData; + +use embassy_executor::*; + +const THREAD_PENDER: usize = usize::MAX; + use crate::rtc::{Rtc, RtcInstant}; static mut RTC: Option<&'static Rtc> = None; @@ -13,3 +20,62 @@ pub fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInst pub fn stop_wakeup_alarm() -> RtcInstant { unsafe { RTC }.unwrap().stop_wakeup_alarm() } + +/// Thread mode executor, using WFE/SEV. +/// +/// This is the simplest and most common kind of executor. It runs on +/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction +/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction +/// is executed, to make the `WFE` exit from sleep and poll the task. +/// +/// This executor allows for ultra low power consumption for chips where `WFE` +/// triggers low-power sleep without extra steps. If your chip requires extra steps, +/// you may use [`raw::Executor`] directly to program custom behavior. +pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, +} + +impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(THREAD_PENDER as *mut ()), + not_send: PhantomData, + } + } + + fn configure_power(&self) { + todo!() + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + self.configure_power(); + asm!("wfe"); + }; + } + } +} diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 744e9a11..f6565a72 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -162,7 +162,7 @@ impl super::Rtc { /// /// note: this api is exposed for testing purposes until low power is implemented. /// it is not intended to be public - pub(crate) fn start_wakeup_alarm(requested_duration: embassy_time::Duration) -> RtcInstant { + pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) -> RtcInstant { use embassy_time::{Duration, TICK_HZ}; use crate::interrupt::typelevel::Interrupt; @@ -217,7 +217,7 @@ impl super::Rtc { /// /// note: this api is exposed for testing purposes until low power is implemented. /// it is not intended to be public - pub(crate) fn stop_wakeup_alarm() -> RtcInstant { + pub(crate) fn stop_wakeup_alarm(&self) -> RtcInstant { use crate::interrupt::typelevel::Interrupt; crate::interrupt::typelevel::RTC_WKUP::disable(); From 1ea4c58c39d831408d7584be3f37b9f2d880d4ca Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 22 Aug 2023 20:00:00 -0500 Subject: [PATCH 291/298] stm32: impl. draft cfgr pwr --- embassy-stm32/src/low_power.rs | 63 ++++++++++++++++++++++++++++++++-- embassy-stm32/src/rtc/v2.rs | 8 ----- 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 3fe28b9d..7d5093b6 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -1,15 +1,33 @@ use core::arch::asm; use core::marker::PhantomData; +use cortex_m::peripheral::SCB; use embassy_executor::*; +use embassy_time::Duration; + +use crate::interrupt; +use crate::interrupt::typelevel::Interrupt; const THREAD_PENDER: usize = usize::MAX; +const THRESHOLD: Duration = Duration::from_millis(500); use crate::rtc::{Rtc, RtcInstant}; static mut RTC: Option<&'static Rtc> = None; +foreach_interrupt! { + (RTC, rtc, $block:ident, WKUP, $irq:ident) => { + #[interrupt] + unsafe fn $irq() { + Executor::on_wakeup_irq(); + } + }; +} + pub fn stop_with_rtc(rtc: &'static Rtc) { + crate::interrupt::typelevel::RTC_WKUP::unpend(); + unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; + unsafe { RTC = Some(rtc) }; } @@ -45,8 +63,47 @@ impl Executor { } } - fn configure_power(&self) { - todo!() + unsafe fn on_wakeup_irq() { + info!("on wakeup irq"); + + cortex_m::asm::bkpt(); + } + + fn low_power_ready(&self) -> bool { + true + } + + fn time_until_next_alarm(&self) -> Duration { + Duration::from_secs(3) + } + + fn get_scb(&self) -> SCB { + unsafe { cortex_m::Peripherals::steal() }.SCB + } + + fn configure_pwr(&self) { + trace!("low power before wfe"); + + if !self.low_power_ready() { + return; + } + + let time_until_next_alarm = self.time_until_next_alarm(); + if time_until_next_alarm < THRESHOLD { + return; + } + + trace!("low power stop required"); + + critical_section::with(|_| { + trace!("executor: set wakeup alarm..."); + + start_wakeup_alarm(time_until_next_alarm); + + trace!("low power wait for rtc ready..."); + + self.get_scb().set_sleepdeep(); + }); } /// Run the executor. @@ -73,7 +130,7 @@ impl Executor { loop { unsafe { self.inner.poll(); - self.configure_power(); + self.configure_pwr(); asm!("wfe"); }; } diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index f6565a72..e03c7d2a 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -165,7 +165,6 @@ impl super::Rtc { pub(crate) fn start_wakeup_alarm(&self, requested_duration: embassy_time::Duration) -> RtcInstant { use embassy_time::{Duration, TICK_HZ}; - use crate::interrupt::typelevel::Interrupt; use crate::rcc::get_freqs; let rtc_hz = unsafe { get_freqs() }.rtc.unwrap().0 as u64; @@ -205,9 +204,6 @@ impl super::Rtc { trace!("wakeup timer enabled"); } - crate::interrupt::typelevel::RTC_WKUP::unpend(); - unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; - RtcInstant::now() } @@ -218,10 +214,6 @@ impl super::Rtc { /// note: this api is exposed for testing purposes until low power is implemented. /// it is not intended to be public pub(crate) fn stop_wakeup_alarm(&self) -> RtcInstant { - use crate::interrupt::typelevel::Interrupt; - - crate::interrupt::typelevel::RTC_WKUP::disable(); - trace!("disable wakeup timer..."); RTC::regs().cr().modify(|w| { From a6d22e199aa2fe7e76bffa104911d954151a03ef Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 23 Aug 2023 11:37:50 +0200 Subject: [PATCH 292/298] net-esp-hosted: fix set link down on disconnect. --- embassy-net-esp-hosted/src/control.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index b8026611..ce6636a8 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -82,7 +82,7 @@ impl<'a> Control<'a> { pub async fn disconnect(&mut self) -> Result<(), Error> { let req = proto::CtrlMsgReqGetStatus {}; ioctl!(self, ReqDisconnectAp, RespDisconnectAp, req, resp); - self.state_ch.set_link_state(LinkState::Up); + self.state_ch.set_link_state(LinkState::Down); Ok(()) } From a2c718f61c0587e19d10c5c86ff4cd79272e2b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Wed, 23 Aug 2023 20:34:37 +0200 Subject: [PATCH 293/298] Bump executor crate version to 0.3.0 --- docs/modules/ROOT/examples/basic/Cargo.toml | 2 +- .../ROOT/examples/layer-by-layer/blinky-async/Cargo.toml | 2 +- embassy-executor/CHANGELOG.md | 2 +- embassy-executor/Cargo.toml | 2 +- embassy-rp/Cargo.toml | 2 +- embassy-time/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32c0/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f334/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- examples/wasm/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- tests/riscv32/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 44 files changed, 44 insertions(+), 44 deletions(-) diff --git a/docs/modules/ROOT/examples/basic/Cargo.toml b/docs/modules/ROOT/examples/basic/Cargo.toml index 237ae0ac..e94358a9 100644 --- a/docs/modules/ROOT/examples/basic/Cargo.toml +++ b/docs/modules/ROOT/examples/basic/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.2.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.3.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] } diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml index a7236ed5..b39f02ae 100644 --- a/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml +++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" cortex-m = "0.7" cortex-m-rt = "0.7" embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] } -embassy-executor = { version = "0.2.0", features = ["nightly", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.3.0", features = ["nightly", "arch-cortex-m", "executor-thread"] } defmt = "0.3.0" defmt-rtt = "0.3.0" diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 43d94e54..4853ba29 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 0.3.0 - TBD - Replaced Pender. Implementations now must define an extern function called `__pender`. - Made `raw::AvailableTask` public diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index d190c95a..2a67f70d 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-executor" -version = "0.2.1" +version = "0.3.0" edition = "2021" license = "MIT OR Apache-2.0" description = "async/await executor designed for embedded usage" diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index b9ebcc86..60143c2b 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -95,5 +95,5 @@ pio = {version= "0.2.1" } rp2040-boot2 = "0.3" [dev-dependencies] -embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["nightly", "arch-std", "executor-thread"] } +embassy-executor = { version = "0.3.0", path = "../embassy-executor", features = ["nightly", "arch-std", "executor-thread"] } static_cell = "1.1" diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index ec1f2ec3..00d31d30 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -169,4 +169,4 @@ wasm-timer = { version = "0.2.5", optional = true } [dev-dependencies] serial_test = "0.9" critical-section = { version = "1.1", features = ["std"] } -embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["nightly"] } +embassy-executor = { version = "0.3.0", path = "../embassy-executor", features = ["nightly"] } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 2a0cf781..46ce7ba9 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot", features = ["nightly"] } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 95b2da95..be85f443 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly"] } embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] } embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp", features = ["nightly"] } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 3b0fc4d9..ea878933 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/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 = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 323b4ab2..b39bc292 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/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 = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index b2abdc89..f015b2ca 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 0b7e72d5..f221e1de 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/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 = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 5f3f365c..2896afa3 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/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 = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 44eb5aba..50d8967a 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/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 = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index fdad5506..27541439 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/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 = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.3.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 068474e7..b06722f5 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -17,7 +17,7 @@ log = [ [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } +embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time" } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 2ce44b51..1b2f1eb1 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -30,7 +30,7 @@ nightly = [ [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 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", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 57660f12..4968a79a 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -9,7 +9,7 @@ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 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", +embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "defmt", "integrated-timers", diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 102611bc..3efc804a 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", 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-executor = { version = "0.3.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"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 3e26d2e2..0d4d5fa1 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["log", "std", "nightly"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 8534921a..9d188513 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # 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-executor = { version = "0.3.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"] } defmt = "0.3" diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 46b6db45..ca9ab1cf 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -15,7 +15,7 @@ defmt = "0.3" defmt-rtt = "0.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-executor = { version = "0.3.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"]} diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 5d32992c..16796841 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # 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-executor = { version = "0.3.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-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 9857fb63..54eadd1a 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # 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-executor = { version = "0.3.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"] } defmt = "0.3" diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index bd594d16..5d8f5f74 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # 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-executor = { version = "0.3.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-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml index d8f6b8fe..65183b88 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/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"] } +embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 65a4e51f..ed456c5c 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # 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"] } +embassy-executor = { version = "0.3.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-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 41170f2c..b658a964 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f767zi to your chip name, if necessary. 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-executor = { version = "0.3.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-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io-async = { version = "0.5.0" } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index b4dfe3c6..4e88151a 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # 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-executor = { version = "0.3.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"] } defmt = "0.3" diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index cf3e2ce9..7bb05dbc 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # 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-executor = { version = "0.3.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-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 5d73e435..f7a1de63 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h563zi to your chip name, if necessary. 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-executor = { version = "0.3.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-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } embedded-io-async = { version = "0.5.0" } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index c78c4c60..1ee11b07 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # 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", "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-executor = { version = "0.3.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-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } embedded-io-async = { version = "0.5.0" } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 332a6c5e..d4e0da0b 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -13,7 +13,7 @@ nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstab # 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-executor = { version = "0.3.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-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } lora-phy = { version = "1", optional = true } diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 329d44ca..192fd3e3 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/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", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.3.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", "stm32l151cb-a", "time-driver-any", "memory-x"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 944c8c27..f552a610 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32l4s5vi to your chip name, if necessary. 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-executor = { version = "0.3.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-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 9dfc08f2..b46c2570 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # 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-executor = { version = "0.3.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-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"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index db251eaf..f928e7a6 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # 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-executor = { version = "0.3.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-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 1a5aff35..f58a5189 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" 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-executor = { version = "0.3.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-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "udp", "medium-ieee802154", "nightly"], optional=true } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 5440807f..1c771ddc 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # 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-executor = { version = "0.3.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-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 2791cc34..5b206d71 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } wasm-logger = "0.2.0" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 034ed85e..4e31bed5 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -9,7 +9,7 @@ teleprobe-meta = "1" embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } +embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io-async = { version = "0.5.0" } diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml index 61f886c0..be610b1c 100644 --- a/tests/riscv32/Cargo.toml +++ b/tests/riscv32/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] critical-section = { version = "1.1.1", features = ["restore-state-bool"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } -embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-riscv32", "nightly", "executor-thread"] } +embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["arch-riscv32", "nightly", "executor-thread"] } embassy-time = { version = "0.1.2", path = "../../embassy-time" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 6a3df4b9..c494b66e 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" teleprobe-meta = "1.1" 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-executor = { version = "0.3.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", "nightly", "unstable-traits"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index e2638897..754356cb 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -29,7 +29,7 @@ dac-adc-pin = [] teleprobe-meta = "1" 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-executor = { version = "0.3.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", "tick-hz-32_768", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } From fc04d2a33caa300bce38f7d0a46f1a0c814bb613 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 23 Aug 2023 19:52:32 -0500 Subject: [PATCH 294/298] rtc: get wakup irq working --- embassy-stm32/src/low_power.rs | 4 +++ embassy-stm32/src/rtc/v2.rs | 48 ++++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 7d5093b6..f4bf7bbe 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -7,6 +7,7 @@ use embassy_time::Duration; use crate::interrupt; use crate::interrupt::typelevel::Interrupt; +use crate::pac::EXTI; const THREAD_PENDER: usize = usize::MAX; const THRESHOLD: Duration = Duration::from_millis(500); @@ -28,6 +29,9 @@ pub fn stop_with_rtc(rtc: &'static Rtc) { crate::interrupt::typelevel::RTC_WKUP::unpend(); unsafe { crate::interrupt::typelevel::RTC_WKUP::enable() }; + EXTI.rtsr(0).modify(|w| w.set_line(22, true)); + EXTI.imr(0).modify(|w| w.set_line(22, true)); + unsafe { RTC = Some(rtc) }; } diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index e03c7d2a..1d4f4df3 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -1,3 +1,4 @@ +use defmt::Format; use stm32_metapac::rtc::vals::{Init, Osel, Pol}; use super::{sealed, RtcClockSource, RtcConfig}; @@ -73,7 +74,7 @@ impl core::ops::Sub for RtcInstant { } #[allow(dead_code)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Format)] pub(crate) enum WakeupPrescaler { Div2, Div4, @@ -185,17 +186,26 @@ impl super::Rtc { ); trace!("set wakeup timer for {} ms", duration.as_millis()); + trace!("set wakeup timer for {} ticks with pre {}", rtc_ticks, prescaler); - RTC::regs().wpr().write(|w| w.set_key(0xca)); - RTC::regs().wpr().write(|w| w.set_key(0x53)); + self.write(false, |regs| { + regs.cr().modify(|w| w.set_wutie(true)); - RTC::regs().wutr().modify(|w| w.set_wut(rtc_ticks)); + trace!("clear wute"); + regs.cr().modify(|w| w.set_wute(false)); + regs.isr().modify(|w| w.set_wutf(false)); - RTC::regs().cr().modify(|w| { - w.set_wucksel(prescaler.into()); + trace!("wait for wutwf..."); + while !regs.isr().read().wutwf() {} + trace!("wait for wutwf...done"); - w.set_wutie(true); - w.set_wute(true); + regs.cr().modify(|w| { + w.set_wucksel(prescaler.into()); + + w.set_wutie(true); + }); + + regs.cr().modify(|w| w.set_wute(true)); }); if !RTC::regs().cr().read().wute() { @@ -204,6 +214,12 @@ impl super::Rtc { trace!("wakeup timer enabled"); } + if !RTC::regs().cr().read().wutie() { + trace!("wakeup timer interrupt not enabled"); + } else { + trace!("wakeup timer interrupt enabled"); + } + RtcInstant::now() } @@ -216,19 +232,11 @@ impl super::Rtc { pub(crate) fn stop_wakeup_alarm(&self) -> RtcInstant { trace!("disable wakeup timer..."); - RTC::regs().cr().modify(|w| { - w.set_wute(false); + self.write(false, |regs| { + regs.cr().modify(|w| w.set_wute(false)); + regs.isr().modify(|w| w.set_wutf(false)); }); - trace!("wait for wakeup timer stop..."); - - // Wait for the wakeup timer to stop - // while !RTC::regs().isr().read().wutf() {} - // - // RTC::regs().isr().modify(|w| w.set_wutf(false)); - - trace!("wait for wakeup timer stop...done"); - RtcInstant::now() } @@ -380,7 +388,7 @@ impl super::Rtc { }) } - pub(super) fn write(&mut self, init_mode: bool, f: F) -> R + pub(super) fn write(&self, init_mode: bool, f: F) -> R where F: FnOnce(&crate::pac::rtc::Rtc) -> R, { From e987259716c56f9854cc4730620d7b5a8be9962d Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 23 Aug 2023 20:01:35 -0500 Subject: [PATCH 295/298] rtc: cleanup --- embassy-stm32/src/rtc/v2.rs | 41 ++++--------------------------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 1d4f4df3..bcb127ec 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -1,4 +1,3 @@ -use defmt::Format; use stm32_metapac::rtc::vals::{Init, Osel, Pol}; use super::{sealed, RtcClockSource, RtcConfig}; @@ -45,25 +44,15 @@ impl core::ops::Sub for RtcInstant { fn sub(self, rhs: Self) -> Self::Output { use embassy_time::{Duration, TICK_HZ}; - trace!("self st: {}", self.st); - trace!("other st: {}", rhs.st); - - trace!("self ssr: {}", self.ssr); - trace!("other ssr: {}", rhs.ssr); - let st = if self.st < rhs.st { self.st + 60 } else { self.st }; - trace!("self st: {}", st); + // TODO: read prescaler let self_ticks = st as u32 * 256 + (255 - self.ssr as u32); let other_ticks = rhs.st as u32 * 256 + (255 - rhs.ssr as u32); let rtc_ticks = self_ticks - other_ticks; - trace!("self ticks: {}", self_ticks); - trace!("other ticks: {}", other_ticks); - trace!("rtc ticks: {}", rtc_ticks); - - // TODO: read prescaler + trace!("self, other, rtc ticks: {}, {}, {}", self_ticks, other_ticks, rtc_ticks); Duration::from_ticks( ((((st as u32 * 256 + (255u32 - self.ssr as u32)) - (rhs.st as u32 * 256 + (255u32 - rhs.ssr as u32))) @@ -74,7 +63,7 @@ impl core::ops::Sub for RtcInstant { } #[allow(dead_code)] -#[derive(Clone, Copy, Debug, Format)] +#[derive(Clone, Copy, Debug)] pub(crate) enum WakeupPrescaler { Div2, Div4, @@ -186,40 +175,18 @@ impl super::Rtc { ); trace!("set wakeup timer for {} ms", duration.as_millis()); - trace!("set wakeup timer for {} ticks with pre {}", rtc_ticks, prescaler); self.write(false, |regs| { regs.cr().modify(|w| w.set_wutie(true)); - trace!("clear wute"); regs.cr().modify(|w| w.set_wute(false)); regs.isr().modify(|w| w.set_wutf(false)); - - trace!("wait for wutwf..."); while !regs.isr().read().wutwf() {} - trace!("wait for wutwf...done"); - - regs.cr().modify(|w| { - w.set_wucksel(prescaler.into()); - - w.set_wutie(true); - }); + regs.cr().modify(|w| w.set_wucksel(prescaler.into())); regs.cr().modify(|w| w.set_wute(true)); }); - if !RTC::regs().cr().read().wute() { - trace!("wakeup timer not enabled"); - } else { - trace!("wakeup timer enabled"); - } - - if !RTC::regs().cr().read().wutie() { - trace!("wakeup timer interrupt not enabled"); - } else { - trace!("wakeup timer interrupt enabled"); - } - RtcInstant::now() } From 83f224e14094488244792dc1bc6d7425d8dcd68f Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 23 Aug 2023 20:18:34 -0500 Subject: [PATCH 296/298] stm32/lp: add refcount --- embassy-stm32/build.rs | 4 ++++ embassy-stm32/src/rcc/mod.rs | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 8a731620..6c364f7b 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -356,6 +356,8 @@ fn main() { } fn enable() { critical_section::with(|_| { + #[cfg(feature = "low-power")] + crate::rcc::clock_refcount_add(); crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true)); #after_enable }) @@ -363,6 +365,8 @@ fn main() { fn disable() { critical_section::with(|_| { crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false)); + #[cfg(feature = "low-power")] + crate::rcc::clock_refcount_sub(); }) } fn reset() { diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index ac9ae9c6..3c75923e 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -26,6 +26,8 @@ use crate::time::Hertz; #[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")] mod _version; pub use _version::*; +#[cfg(feature = "low-power")] +use atomic_polyfill::{AtomicU32, Ordering}; #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -79,6 +81,25 @@ pub struct Clocks { pub rtc: Option, } +#[cfg(feature = "low-power")] +static CLOCK_REFCOUNT: AtomicU32 = AtomicU32::new(0); + +#[cfg(feature = "low-power")] +pub fn low_power_ready() -> bool { + CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0 +} + +#[cfg(feature = "low-power")] +pub(crate) fn clock_refcount_add() { + // We don't check for overflow because constructing more than u32 peripherals is unlikely + CLOCK_REFCOUNT.fetch_add(1, Ordering::Relaxed); +} + +#[cfg(feature = "low-power")] +pub(crate) fn clock_refcount_sub() { + assert!(CLOCK_REFCOUNT.fetch_sub(1, Ordering::Relaxed) != 0); +} + /// Frozen clock frequencies /// /// The existence of this value indicates that the clock configuration can no longer be changed From 00aadf6085d080546ebbed91e2b1e2d6ecbbedea Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 23 Aug 2023 20:22:11 -0500 Subject: [PATCH 297/298] stm32/lp: pipe through lp ready --- embassy-stm32/src/low_power.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index f4bf7bbe..7814fa38 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -8,6 +8,7 @@ use embassy_time::Duration; use crate::interrupt; use crate::interrupt::typelevel::Interrupt; use crate::pac::EXTI; +use crate::rcc::low_power_ready; const THREAD_PENDER: usize = usize::MAX; const THRESHOLD: Duration = Duration::from_millis(500); @@ -73,22 +74,18 @@ impl Executor { cortex_m::asm::bkpt(); } - fn low_power_ready(&self) -> bool { - true - } - fn time_until_next_alarm(&self) -> Duration { Duration::from_secs(3) } - fn get_scb(&self) -> SCB { + fn get_scb() -> SCB { unsafe { cortex_m::Peripherals::steal() }.SCB } fn configure_pwr(&self) { - trace!("low power before wfe"); + trace!("configure_pwr"); - if !self.low_power_ready() { + if !low_power_ready() { return; } @@ -106,7 +103,7 @@ impl Executor { trace!("low power wait for rtc ready..."); - self.get_scb().set_sleepdeep(); + Self::get_scb().set_sleepdeep(); }); } From ecc305bbfe1007f9daa4d6a585dfc66f6ca69218 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 23 Aug 2023 20:28:41 -0500 Subject: [PATCH 298/298] stm32: fix executor version --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 048d231d..150014af 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -38,7 +38,7 @@ embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", fea 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 } -embassy-executor = { version = "0.2.0", path = "../embassy-executor", optional = true } +embassy-executor = { version = "0.3.0", path = "../embassy-executor", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}