From a46f33b2144df0b913b50bb8c78256e20bce84c8 Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Sat, 20 Aug 2022 16:37:51 -0400 Subject: [PATCH 01/25] 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 faa52d8fb..3e500098c 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 bbdf1cbe5..25c7c0d91 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 18b8eda67..2c6276d4a 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 39a0f93f9..3b33907d2 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 e3d8f34a1..ae59f8b25 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 f3b3ca0ca..205891954 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 000000000..629eab99f --- /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 2fcc31221..673bcfc6b 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 000000000..d5e90e27a --- /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 02/25] 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 629eab99f..b3cc87603 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 673bcfc6b..d0567ba8e 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 d5e90e27a..a73d01fb9 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 03/25] 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 b3cc87603..db4c74afd 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 a73d01fb9..85a59a529 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 000000000..e7d1806bb --- /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 04/25] 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 db4c74afd..c8375b1e3 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 85a59a529..605eca59e 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 05/25] 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 c8375b1e3..6a070e7bc 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 06/25] 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 6a070e7bc..b58a0e976 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 e7d1806bb..33ba1e274 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 07/25] 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 d0567ba8e..876dcf734 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 33ba1e274..e78bc40dc 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 08/25] 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 e78bc40dc..284a68af2 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 09/25] 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 b58a0e976..c2c54fba9 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 e4ad1aa542ad28bed23532b3a0bee0d062454516 Mon Sep 17 00:00:00 2001 From: Alex Ferro Date: Sun, 16 Jul 2023 19:59:35 -0600 Subject: [PATCH 10/25] 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 791c64554..9b85b2345 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 11/25] 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 90631b175..2ed46ca2c 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 f581831b86f1f3e46a1cc7a8c4d1a2baf3379e6a Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Tue, 18 Jul 2023 10:39:29 +0200 Subject: [PATCH 12/25] 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 0d0a986f6..f43e3797b 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 13/25] 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 f43e3797b..ae595d0d6 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 14/25] 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 000000000..457510407 --- /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 15/25] 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 000000000..4db9edae7 --- /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 16/25] 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 0e30f7002..efa1fbccc 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 e9db934bf..31b676088 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 17/25] 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 ae595d0d6..4fbafe752 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 18/25] 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 9e9c78a42..06c6fa00b 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 26f5b40bd..0053c49a4 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 19/25] 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 1fc717fd1..f2675bb7a 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 8309dda08..000000000 --- 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 20/25] 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 f2675bb7a..48668c7f4 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 47fe67733..444b9137f 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 9eaf30717..7d8531475 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 21/25] 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 48668c7f4..4cee566c8 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 22/25] 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 4cee566c8..217884d1c 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 3df2c71e6c27eedacef1ddd822537c8762fe0578 Mon Sep 17 00:00:00 2001 From: Alessandro Pezzato Date: Tue, 18 Jul 2023 22:24:52 +0200 Subject: [PATCH 23/25] 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 c97efbf0a..1ba182e21 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 24/25] 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 1ba182e21..ea8e525ea 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 a3d4ae85b021b888a78ef2214a7ecae0a49741c4 Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 20 Jul 2023 15:48:59 +0200 Subject: [PATCH 25/25] 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 dfa1b877a..95780c068 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 {