This commit is contained in:
xoviat 2023-07-21 16:24:48 -05:00
commit 2cdd593290
14 changed files with 1137 additions and 666 deletions

41
.gitattributes vendored Normal file
View file

@ -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

17
.github/ci/crlf.sh vendored Executable file
View file

@ -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

1
.github/ci/doc.sh vendored
View file

@ -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

View file

@ -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"]

View file

@ -490,30 +490,78 @@ impl<D: Driver + 'static> Stack<D> {
}
#[cfg(feature = "igmp")]
impl<D: Driver + smoltcp::phy::Device + 'static> Stack<D> {
impl<D: Driver + 'static> Stack<D> {
/// Join a multicast group.
pub fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
pub async fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
where
T: Into<IpAddress>,
{
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<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>>
where
T: Into<IpAddress>,
{
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<T>(&self, addr: T) -> Result<bool, MulticastError>
pub async fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
where
T: Into<IpAddress>,
{
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<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>>
where
T: Into<IpAddress>,
{
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<D: Driver + 'static> Inner<D> {
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<D: Driver + 'static> Inner<D> {
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<D: Driver + 'static> Inner<D> {
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<D: Driver + 'static> Inner<D> {
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<D: Driver + 'static> Inner<D> {
}
} else if old_link_up {
socket.reset();
self.unapply_config(s);
self.unapply_config_v4(s);
}
}
//if old_link_up || self.link_up {

View file

@ -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<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
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<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
clk: impl Peripheral<P = impl GpioPin> + 'd,
din: impl Peripheral<P = impl GpioPin> + '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<P = Self> + 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<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
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<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
clk: impl Peripheral<P = impl GpioPin> + 'd,
din: impl Peripheral<P = impl GpioPin> + '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::<u8>()
.clamp(0, 0x50);
let gain_right = gain_right
.saturating_add(I7F1::from_bits(40))
.saturating_to_num::<u8>()
.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<S, const N: usize>(
&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<OperationMode> 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<Edge> 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<P = Self> + 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;
}
};
}

View file

@ -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 {

View file

@ -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?;
}
}

View file

@ -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
}

View file

@ -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<u32>,
/// Number of dummy cycles (DCYC)
pub dummy: DummyCycles,
/// Length of data
pub data_len: Option<usize>,
}
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<PeripheralRef<'d, AnyPin>>,
d0: Option<PeripheralRef<'d, AnyPin>>,
d1: Option<PeripheralRef<'d, AnyPin>>,
d2: Option<PeripheralRef<'d, AnyPin>>,
d3: Option<PeripheralRef<'d, AnyPin>>,
nss: Option<PeripheralRef<'d, AnyPin>>,
dma: PeripheralRef<'d, Dma>,
config: Config,
}
impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
pub fn new(
peri: impl Peripheral<P = T> + 'd,
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
dma: impl Peripheral<P = Dma> + '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<P = T> + 'd,
d0: Option<PeripheralRef<'d, AnyPin>>,
d1: Option<PeripheralRef<'d, AnyPin>>,
d2: Option<PeripheralRef<'d, AnyPin>>,
d3: Option<PeripheralRef<'d, AnyPin>>,
sck: Option<PeripheralRef<'d, AnyPin>>,
nss: Option<PeripheralRef<'d, AnyPin>>,
dma: impl Peripheral<P = Dma> + '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<T>,
{
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<T>,
{
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<P = Self> + 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<u32>,
/// Number of dummy cycles (DCYC)
pub dummy: DummyCycles,
/// Length of data
pub data_len: Option<usize>,
}
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<PeripheralRef<'d, AnyPin>>,
d0: Option<PeripheralRef<'d, AnyPin>>,
d1: Option<PeripheralRef<'d, AnyPin>>,
d2: Option<PeripheralRef<'d, AnyPin>>,
d3: Option<PeripheralRef<'d, AnyPin>>,
nss: Option<PeripheralRef<'d, AnyPin>>,
dma: PeripheralRef<'d, Dma>,
config: Config,
}
impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
pub fn new(
peri: impl Peripheral<P = T> + 'd,
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
dma: impl Peripheral<P = Dma> + '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<P = T> + 'd,
d0: Option<PeripheralRef<'d, AnyPin>>,
d1: Option<PeripheralRef<'d, AnyPin>>,
d2: Option<PeripheralRef<'d, AnyPin>>,
d3: Option<PeripheralRef<'d, AnyPin>>,
sck: Option<PeripheralRef<'d, AnyPin>>,
nss: Option<PeripheralRef<'d, AnyPin>>,
dma: impl Peripheral<P = Dma> + '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<T>,
{
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<T>,
{
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<P = Self> + 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 {}
};
);

View file

@ -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

View file

@ -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" }

View file

@ -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::<i32>() / 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;
}
}
}

View file

@ -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<peripherals::PDM>;
});
#[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::<i32>() / 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()
}