aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Smith <[email protected]>2022-08-20 16:37:51 -0400
committerQuentin Smith <[email protected]>2022-08-20 16:37:51 -0400
commita46f33b2144df0b913b50bb8c78256e20bce84c8 (patch)
tree9b1fab8cd08d36ec4f225b2f04b9786bd3100d6c
parentb7d77985cf230416e53190c4edde4030e42266ed (diff)
Initial PDM driver
-rw-r--r--embassy-nrf/src/chips/nrf52810.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52811.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52832.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52833.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52840.rs3
-rw-r--r--embassy-nrf/src/lib.rs8
-rw-r--r--embassy-nrf/src/pdm.rs185
-rw-r--r--examples/nrf/Cargo.toml1
-rw-r--r--examples/nrf/src/bin/pdm.rs34
9 files changed, 243 insertions, 0 deletions
diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs
index faa52d8fb..3e500098c 100644
--- a/embassy-nrf/src/chips/nrf52810.rs
+++ b/embassy-nrf/src/chips/nrf52810.rs
@@ -128,6 +128,9 @@ embassy_hal_common::peripherals! {
128 128
129 // QDEC 129 // QDEC
130 QDEC, 130 QDEC,
131
132 // PDM
133 PDM,
131} 134}
132 135
133impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 136impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs
index bbdf1cbe5..25c7c0d91 100644
--- a/embassy-nrf/src/chips/nrf52811.rs
+++ b/embassy-nrf/src/chips/nrf52811.rs
@@ -128,6 +128,9 @@ embassy_hal_common::peripherals! {
128 128
129 // QDEC 129 // QDEC
130 QDEC, 130 QDEC,
131
132 // PDM
133 PDM,
131} 134}
132 135
133impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 136impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs
index 18b8eda67..2c6276d4a 100644
--- a/embassy-nrf/src/chips/nrf52832.rs
+++ b/embassy-nrf/src/chips/nrf52832.rs
@@ -138,6 +138,9 @@ embassy_hal_common::peripherals! {
138 138
139 // QDEC 139 // QDEC
140 QDEC, 140 QDEC,
141
142 // PDM
143 PDM,
141} 144}
142 145
143impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 146impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs
index 39a0f93f9..3b33907d2 100644
--- a/embassy-nrf/src/chips/nrf52833.rs
+++ b/embassy-nrf/src/chips/nrf52833.rs
@@ -158,6 +158,9 @@ embassy_hal_common::peripherals! {
158 158
159 // QDEC 159 // QDEC
160 QDEC, 160 QDEC,
161
162 // PDM
163 PDM,
161} 164}
162 165
163#[cfg(feature = "nightly")] 166#[cfg(feature = "nightly")]
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs
index e3d8f34a1..ae59f8b25 100644
--- a/embassy-nrf/src/chips/nrf52840.rs
+++ b/embassy-nrf/src/chips/nrf52840.rs
@@ -161,6 +161,9 @@ embassy_hal_common::peripherals! {
161 161
162 // TEMP 162 // TEMP
163 TEMP, 163 TEMP,
164
165 // PDM
166 PDM,
164} 167}
165 168
166#[cfg(feature = "nightly")] 169#[cfg(feature = "nightly")]
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index f3b3ca0ca..205891954 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -103,6 +103,14 @@ pub mod uarte;
103pub mod usb; 103pub mod usb;
104#[cfg(not(feature = "_nrf5340"))] 104#[cfg(not(feature = "_nrf5340"))]
105pub mod wdt; 105pub mod wdt;
106#[cfg(any(
107 feature = "nrf52810",
108 feature = "nrf52811",
109 feature = "nrf52832",
110 feature = "nrf52833",
111 feature = "nrf52840",
112))]
113pub mod pdm;
106 114
107// This mod MUST go last, so that it sees all the `impl_foo!` macros 115// This mod MUST go last, so that it sees all the `impl_foo!` macros
108#[cfg_attr(feature = "nrf52805", path = "chips/nrf52805.rs")] 116#[cfg_attr(feature = "nrf52805", path = "chips/nrf52805.rs")]
diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs
new file mode 100644
index 000000000..629eab99f
--- /dev/null
+++ b/embassy-nrf/src/pdm.rs
@@ -0,0 +1,185 @@
1#![macro_use]
2
3use core::sync::atomic::{compiler_fence, Ordering};
4use core::task::Poll;
5
6use embassy_hal_common::{into_ref, PeripheralRef};
7use embassy_util::waitqueue::AtomicWaker;
8use futures::future::poll_fn;
9use pac::{pdm, PDM};
10use pdm::mode::{EDGE_A, OPERATION_A};
11use fixed::types::I7F1;
12
13use crate::interrupt::InterruptExt;
14use crate::gpio::Pin as GpioPin;
15use crate::{interrupt, pac, peripherals, Peripheral};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19#[non_exhaustive]
20pub enum Error {}
21
22/// One-shot and continuous PDM.
23pub struct Pdm<'d> {
24 _p: PeripheralRef<'d, peripherals::PDM>,
25}
26
27static WAKER: AtomicWaker = AtomicWaker::new();
28
29/// Used to configure the PDM peripheral.
30///
31/// See the `Default` impl for suitable default values.
32#[non_exhaustive]
33pub struct Config {
34 /// Clock
35 /// Clock ratio
36 /// Channels
37 pub channels: Channels,
38 /// Edge to sample on
39 pub left_edge: Edge,
40 /// Gain left in dB
41 pub gain_left: I7F1,
42 /// Gain right in dB
43 pub gain_right: I7F1,
44}
45
46impl Default for Config {
47 /// Default configuration for single channel sampling.
48 fn default() -> Self {
49 Self {
50 channels: Channels::Stereo,
51 left_edge: Edge::FallingEdge,
52 gain_left: I7F1::ZERO,
53 gain_right: I7F1::ZERO,
54 }
55 }
56}
57
58/// The state of a continuously running sampler. While it reflects
59/// the progress of a sampler, it also signals what should be done
60/// next. For example, if the sampler has stopped then the Pdm implementation
61/// can then tear down its infrastructure.
62#[derive(PartialEq)]
63pub enum SamplerState {
64 Sampled,
65 Stopped,
66}
67
68impl<'d> Pdm<'d> {
69 pub fn new(
70 pdm: impl Peripheral<P = peripherals::PDM> + 'd,
71 irq: impl Peripheral<P = interrupt::PDM> + 'd,
72 data: impl Peripheral<P = impl GpioPin> + 'd,
73 clock: impl Peripheral<P = impl GpioPin> + 'd,
74 config: Config,
75 ) -> Self {
76 into_ref!(pdm, irq, data, clock);
77
78 let r = unsafe { &*PDM::ptr() };
79
80 let Config { channels, left_edge, gain_left, gain_right } = config;
81
82 // Configure channels
83 r.enable.write(|w| w.enable().enabled());
84 // TODO: Clock control
85 r.mode.write(|w| {
86 w.operation().variant(channels.into());
87 w.edge().variant(left_edge.into());
88 w
89 });
90
91 r.psel.din.write(|w| unsafe { w.bits(data.psel_bits()) });
92 r.psel.clk.write(|w| unsafe { w.bits(clock.psel_bits()) });
93
94 // Disable all events interrupts
95 r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
96
97 irq.set_handler(Self::on_interrupt);
98 irq.unpend();
99 irq.enable();
100
101 Self { _p: pdm }
102 }
103
104 fn on_interrupt(_ctx: *mut ()) {
105 let r = Self::regs();
106
107 if r.events_end.read().bits() != 0 {
108 r.intenclr.write(|w| w.end().clear());
109 WAKER.wake();
110 }
111
112 if r.events_started.read().bits() != 0 {
113 r.intenclr.write(|w| w.started().clear());
114 WAKER.wake();
115 }
116 }
117
118 fn regs() -> &'static pdm::RegisterBlock {
119 unsafe { &*PDM::ptr() }
120 }
121
122 /// One shot sampling. If the PDM is configured for multiple channels, the samples will be interleaved.
123 pub async fn sample<const N: usize>(&mut self, buf: &mut [i16; N]) {
124 let r = Self::regs();
125
126 // Set up the DMA
127 r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(buf.as_mut_ptr() as u32) });
128 r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) });
129
130 // Reset and enable the end event
131 r.events_end.reset();
132 r.intenset.write(|w| w.end().set());
133
134 // Don't reorder the start event before the previous writes. Hopefully self
135 // wouldn't happen anyway.
136 compiler_fence(Ordering::SeqCst);
137
138 r.tasks_start.write(|w| { w.tasks_start().set_bit() });
139
140 // Wait for 'end' event.
141 poll_fn(|cx| {
142 let r = Self::regs();
143
144 WAKER.register(cx.waker());
145
146 if r.events_end.read().bits() != 0 {
147 r.events_end.reset();
148 return Poll::Ready(());
149 }
150
151 Poll::Pending
152 })
153 .await;
154 }
155}
156
157#[derive(Clone, Copy, PartialEq)]
158pub enum Edge {
159 FallingEdge,
160 RisingEdge,
161}
162
163impl From<Edge> for EDGE_A {
164 fn from(edge: Edge) -> Self {
165 match edge {
166 Edge::FallingEdge => EDGE_A::LEFTFALLING,
167 Edge::RisingEdge => EDGE_A::LEFTRISING,
168 }
169 }
170}
171
172#[derive(Clone, Copy, PartialEq)]
173pub enum Channels {
174 Stereo,
175 Mono,
176}
177
178impl From<Channels> for OPERATION_A {
179 fn from(ch: Channels) -> Self {
180 match ch {
181 Channels::Stereo => OPERATION_A::STEREO,
182 Channels::Mono => OPERATION_A::MONO,
183 }
184 }
185} \ No newline at end of file
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml
index 2fcc31221..673bcfc6b 100644
--- a/examples/nrf/Cargo.toml
+++ b/examples/nrf/Cargo.toml
@@ -27,6 +27,7 @@ cortex-m-rt = "0.7.0"
27panic-probe = { version = "0.3", features = ["print-defmt"] } 27panic-probe = { version = "0.3", features = ["print-defmt"] }
28futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 28futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
29rand = { version = "0.8.4", default-features = false } 29rand = { version = "0.8.4", default-features = false }
30fixed = "1.10.0"
30embedded-storage = "0.3.0" 31embedded-storage = "0.3.0"
31usbd-hid = "0.5.2" 32usbd-hid = "0.5.2"
32serde = { version = "1.0.136", default-features = false } 33serde = { version = "1.0.136", default-features = false }
diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf/src/bin/pdm.rs
new file mode 100644
index 000000000..d5e90e27a
--- /dev/null
+++ b/examples/nrf/src/bin/pdm.rs
@@ -0,0 +1,34 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::info;
6use embassy_executor::Spawner;
7use embassy_nrf::interrupt;
8use embassy_nrf::pdm::{Config, Channels, Pdm};
9use embassy_time::{Duration, Timer};
10use fixed::types::I7F1;
11use {defmt_rtt as _, panic_probe as _};
12
13#[embassy_executor::main]
14async fn main(_p: Spawner) {
15 let mut p = embassy_nrf::init(Default::default());
16 let mut config = Config::default();
17 // Pins are correct for the onboard microphone on the Feather nRF52840 Sense.
18 config.channels = Channels::Mono;
19 config.gain_left = I7F1::from_bits(5); // 2.5 dB
20 let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), &mut p.P0_00, &mut p.P0_01, config);
21
22 loop {
23 let mut buf = [0; 128];
24 pdm.sample(&mut buf).await;
25 info!(
26 "{} samples, min {=i16}, max {=i16}, mean {=i16}",
27 buf.len(),
28 buf.iter().min().unwrap(),
29 buf.iter().max().unwrap(),
30 (buf.iter().map(|v| i32::from(*v)).sum::<i32>() / buf.len() as i32) as i16,
31 );
32 Timer::after(Duration::from_millis(100)).await;
33 }
34}