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 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 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 diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index ad98d7f68..81c751a2c 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -490,30 +490,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)), + } }) } @@ -542,11 +590,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")] @@ -581,11 +632,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")] @@ -653,13 +707,21 @@ impl Inner { socket.set_retry_config(config.retry_config); } - #[allow(unused)] // used only with dhcp - fn unapply_config(&mut self, s: &mut SocketStack) { + #[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| ip_addrs.clear()); + s.iface.update_ip_addrs(|ip_addrs| { + #[cfg(feature = "proto-ipv4")] + if let Some((index, _)) = ip_addrs + .iter() + .enumerate() + .find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_))) + { + ip_addrs.remove(index); + } + }); #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { #[cfg(feature = "proto-ipv4")] @@ -706,7 +768,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, @@ -718,7 +780,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 { diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 0e30f7002..217884d1c 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,294 +1,500 @@ -//! 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 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::pac::pdm::mode::{EDGE_A, OPERATION_A}; +pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency; +#[cfg(any( + feature = "nrf52840", + feature = "nrf52833", + feature = "_nrf5340-app", + feature = "_nrf9160", +))] +pub use crate::pac::pdm::ratio::RATIO_A as Ratio; +use crate::{interrupt, Peripheral}; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + + if r.events_end.read().bits() != 0 { + r.intenclr.write(|w| w.end().clear()); + } + + if r.events_started.read().bits() != 0 { + r.intenclr.write(|w| w.started().clear()); + } + + if r.events_stopped.read().bits() != 0 { + r.intenclr.write(|w| w.stopped().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, + /// PDM is already running + AlreadyRunning, +} + +static DUMMY_BUFFER: [i16; 1] = [0; 1]; + +/// 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 { + /// The sampler processed the samples and is ready for more. + Sampled, + /// The sampler is done processing samples. + Stopped, +} + +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 + 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()); + w.edge().variant(config.edge.into()); + w + }); + + Self::_set_gain(r, config.gain_left, config.gain_right); + + // Disable all events interrupts + r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); + + // IRQ + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + r.enable.write(|w| w.enable().set_bit()); + + Self { _peri: pdm } + } + + 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); + + r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) }); + r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) }); + } + + /// Adjust the gain of the PDM microphone on the fly + pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) { + Self::_set_gain(T::regs(), gain_left, gain_right) + } + + /// 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); + } + + /// Continuous sampling with double buffers. + /// + /// 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, + ) -> Result<(), Error> + where + S: FnMut(&[i16; N]) -> SamplerState, + { + let r = T::regs(); + + if r.events_started.read().bits() != 0 { + return Err(Error::AlreadyRunning); + } + + 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| unsafe { w.bits(1) }); + + let mut current_buffer = 0; + + let mut done = false; + + let drop = OnDrop::new(|| { + 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 {} + }); + + // Wait for events and complete when the sampler indicates it has had enough. + poll_fn(|cx| { + let r = T::regs(); + + T::state().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| unsafe { w.bits(1) }); + 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 { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + drop.defuse(); + Ok(()) + } +} + +/// 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, + /// 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, + /// Gain right in dB + pub gain_right: I7F1, +} + +impl Default for Config { + fn default() -> Self { + Self { + 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, + } + } +} + +/// PDM operation mode. +#[derive(PartialEq)] +pub enum OperationMode { + /// Mono (1 channel) + Mono, + /// Stereo (2 channels) + Stereo, +} + +impl From for OPERATION_A { + fn from(mode: OperationMode) -> Self { + match mode { + OperationMode::Mono => OPERATION_A::MONO, + OperationMode::Stereo => OPERATION_A::STEREO, + } + } +} + +/// PDM edge polarity +#[derive(PartialEq)] +pub enum Edge { + /// Left edge is rising + LeftRising, + /// Left edge is falling + LeftFalling, +} + +impl From for EDGE_A { + fn from(edge: Edge) -> Self { + match edge { + Edge::LeftRising => EDGE_A::LEFT_RISING, + Edge::LeftFalling => EDGE_A::LEFT_FALLING, + } + } +} + +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-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 { 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?; } } 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 } 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 {} + }; +); diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index c97efbf0a..ea8e525ea 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,22 @@ 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); + // 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); @@ -847,6 +867,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 diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 7b9c371bb..9b41ec5ab 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -43,6 +43,7 @@ embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-host defmt = "0.3" defmt-rtt = "0.4" +fixed = "1.10.0" static_cell = "1.1" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" @@ -53,6 +54,8 @@ embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } +num-integer = { version = "0.1.45", default-features = false } +microfft = "0.5.0" [patch.crates-io] lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/nrf52840/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs index 6b41320ca..444b9137f 100644 --- a/examples/nrf52840/src/bin/pdm.rs +++ b/examples/nrf52840/src/bin/pdm.rs @@ -7,6 +7,8 @@ use embassy_executor::Spawner; use embassy_nrf::pdm::{self, Config, Pdm}; use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::{Duration, Timer}; +use fixed::types::I7F1; +use num_integer::Roots; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -20,18 +22,36 @@ async fn main(_p: Spawner) { let mut pdm = Pdm::new(p.PDM, Irqs, p.P0_01, p.P0_00, config); loop { - pdm.start().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)); + pdm.start().await; - // wait some time till the microphon settled - Timer::after(Duration::from_millis(1000)).await; + // wait some time till the microphon settled + Timer::after(Duration::from_millis(1000)).await; - const SAMPLES: usize = 2048; - let mut buf = [0i16; SAMPLES]; - pdm.sample(&mut buf).await.unwrap(); + const SAMPLES: usize = 2048; + let mut buf = [0i16; SAMPLES]; + pdm.sample(&mut buf).await.unwrap(); - info!("samples: {:?}", &buf); + let mean = (buf.iter().map(|v| i32::from(*v)).sum::() / buf.len() as i32) as i16; + info!( + "{} 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 - mean).pow(2)) + .fold(0i32, |a, b| a.saturating_add(b)) + / buf.len() as i32) + .sqrt() as i16, + ); - pdm.stop().await; - Timer::after(Duration::from_millis(100)).await; + info!("samples: {:?}", &buf); + + pdm.stop().await; + Timer::after(Duration::from_millis(100)).await; + } } } diff --git a/examples/nrf52840/src/bin/pdm_continuous.rs b/examples/nrf52840/src/bin/pdm_continuous.rs new file mode 100644 index 000000000..7d8531475 --- /dev/null +++ b/examples/nrf52840/src/bin/pdm_continuous.rs @@ -0,0 +1,81 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +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 fixed::types::I7F1; +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 + +bind_interrupts!(struct Irqs { + PDM => pdm::InterruptHandler; +}); + +#[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.frequency = Frequency::_1280K; // 16 kHz sample rate + config.ratio = Ratio::RATIO80; + config.operation_mode = OperationMode::Mono; + config.gain_left = I7F1::from_bits(5); // 2.5 dB + let mut pdm = Pdm::new(p.PDM, Irqs, &mut p.P0_00, &mut p.P0_01, config); + + 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(); +} + +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()) + .enumerate() + .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Equal)) + .map(|(i, v)| (i, ((v * 32768.0) as u32).sqrt())) + .unwrap() +}