aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-10-21 21:03:51 +0000
committerGitHub <[email protected]>2022-10-21 21:03:51 +0000
commitce1cba761c2942b7faa27f4098487c6468784729 (patch)
treefbce880d165f6e660253bbefa40ac637e1362029
parent495ca6108ce6febb06ae5438834935541be5a04f (diff)
parenta4afab46403aacd7bb555cadaefad7f216fb9931 (diff)
Merge #855
855: PDM microphone support for nrf r=Dirbaio a=pbert519 PDM microphones have a long startup phase, therefore the driver samples continuously and only switches the target buffer if the user requests sampling. Co-authored-by: pbert <[email protected]>
-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/nrf52833.rs3
-rw-r--r--embassy-nrf/src/chips/nrf52840.rs3
-rw-r--r--embassy-nrf/src/chips/nrf9160.rs3
-rw-r--r--embassy-nrf/src/lib.rs8
-rw-r--r--embassy-nrf/src/pdm.rs242
-rw-r--r--examples/nrf/src/bin/pdm.rs33
8 files changed, 298 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/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/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs
index a4be8564e..f8ed11e03 100644
--- a/embassy-nrf/src/chips/nrf9160.rs
+++ b/embassy-nrf/src/chips/nrf9160.rs
@@ -260,6 +260,9 @@ embassy_hal_common::peripherals! {
260 P0_29, 260 P0_29,
261 P0_30, 261 P0_30,
262 P0_31, 262 P0_31,
263
264 // PDM
265 PDM,
263} 266}
264 267
265impl_uarte!(UARTETWISPI0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); 268impl_uarte!(UARTETWISPI0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index d7bd21702..bc70fc2f6 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -76,6 +76,14 @@ pub mod gpio;
76pub mod gpiote; 76pub mod gpiote;
77#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] 77#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
78pub mod nvmc; 78pub mod nvmc;
79#[cfg(any(
80 feature = "nrf52810",
81 feature = "nrf52811",
82 feature = "nrf52833",
83 feature = "nrf52840",
84 feature = "_nrf9160"
85))]
86pub mod pdm;
79pub mod ppi; 87pub mod ppi;
80#[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))] 88#[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))]
81pub mod pwm; 89pub mod pwm;
diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs
new file mode 100644
index 000000000..b7c7022cf
--- /dev/null
+++ b/embassy-nrf/src/pdm.rs
@@ -0,0 +1,242 @@
1//! PDM mirophone interface
2
3use core::marker::PhantomData;
4use core::sync::atomic::{compiler_fence, Ordering};
5use core::task::Poll;
6
7use embassy_hal_common::drop::OnDrop;
8use embassy_hal_common::{into_ref, PeripheralRef};
9use embassy_sync::waitqueue::AtomicWaker;
10use futures::future::poll_fn;
11
12use crate::chip::EASY_DMA_SIZE;
13use crate::gpio::sealed::Pin;
14use crate::gpio::{AnyPin, Pin as GpioPin};
15use crate::interrupt::{self, InterruptExt};
16use crate::peripherals::PDM;
17use crate::{pac, Peripheral};
18
19/// PDM microphone interface
20pub struct Pdm<'d> {
21 irq: PeripheralRef<'d, interrupt::PDM>,
22 phantom: PhantomData<&'d PDM>,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27#[non_exhaustive]
28pub enum Error {
29 BufferTooLong,
30 BufferZeroLength,
31 NotRunning,
32}
33
34static WAKER: AtomicWaker = AtomicWaker::new();
35static DUMMY_BUFFER: [i16; 1] = [0; 1];
36
37impl<'d> Pdm<'d> {
38 /// Create PDM driver
39 pub fn new(
40 pdm: impl Peripheral<P = PDM> + 'd,
41 irq: impl Peripheral<P = interrupt::PDM> + 'd,
42 clk: impl Peripheral<P = impl GpioPin> + 'd,
43 din: impl Peripheral<P = impl GpioPin> + 'd,
44 config: Config,
45 ) -> Self {
46 into_ref!(clk, din);
47 Self::new_inner(pdm, irq, clk.map_into(), din.map_into(), config)
48 }
49
50 fn new_inner(
51 _pdm: impl Peripheral<P = PDM> + 'd,
52 irq: impl Peripheral<P = interrupt::PDM> + 'd,
53 clk: PeripheralRef<'d, AnyPin>,
54 din: PeripheralRef<'d, AnyPin>,
55 config: Config,
56 ) -> Self {
57 into_ref!(irq);
58
59 let r = Self::regs();
60
61 // setup gpio pins
62 din.conf().write(|w| w.input().set_bit());
63 r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) });
64 clk.set_low();
65 clk.conf().write(|w| w.dir().output());
66 r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
67
68 // configure
69 // use default for
70 // - gain right
71 // - gain left
72 // - clk
73 // - ratio
74 r.mode.write(|w| {
75 w.edge().bit(config.edge == Edge::LeftRising);
76 w.operation().bit(config.operation_mode == OperationMode::Mono);
77 w
78 });
79 r.gainl.write(|w| w.gainl().default_gain());
80 r.gainr.write(|w| w.gainr().default_gain());
81
82 // IRQ
83 irq.disable();
84 irq.set_handler(|_| {
85 let r = Self::regs();
86 r.intenclr.write(|w| w.end().clear());
87 WAKER.wake();
88 });
89 irq.enable();
90
91 r.enable.write(|w| w.enable().set_bit());
92
93 Self {
94 phantom: PhantomData,
95 irq,
96 }
97 }
98
99 /// Start sampling microphon data into a dummy buffer
100 /// Usefull to start the microphon and keep it active between recording samples
101 pub async fn start(&mut self) {
102 let r = Self::regs();
103
104 // start dummy sampling because microphon needs some setup time
105 r.sample
106 .ptr
107 .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
108 r.sample
109 .maxcnt
110 .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
111
112 r.tasks_start.write(|w| w.tasks_start().set_bit());
113 }
114
115 /// Stop sampling microphon data inta a dummy buffer
116 pub async fn stop(&mut self) {
117 let r = Self::regs();
118 r.tasks_stop.write(|w| w.tasks_stop().set_bit());
119 r.events_started.reset();
120 }
121
122 pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
123 if buffer.len() == 0 {
124 return Err(Error::BufferZeroLength);
125 }
126 if buffer.len() > EASY_DMA_SIZE {
127 return Err(Error::BufferTooLong);
128 }
129
130 let r = Self::regs();
131
132 if r.events_started.read().events_started().bit_is_clear() {
133 return Err(Error::NotRunning);
134 }
135
136 let drop = OnDrop::new(move || {
137 r.intenclr.write(|w| w.end().clear());
138 r.events_stopped.reset();
139
140 // reset to dummy buffer
141 r.sample
142 .ptr
143 .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
144 r.sample
145 .maxcnt
146 .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
147
148 while r.events_stopped.read().bits() == 0 {}
149 });
150
151 // setup user buffer
152 let ptr = buffer.as_ptr();
153 let len = buffer.len();
154 r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) });
155 r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) });
156
157 // wait till the current sample is finished and the user buffer sample is started
158 Self::wait_for_sample().await;
159
160 // reset the buffer back to the dummy buffer
161 r.sample
162 .ptr
163 .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
164 r.sample
165 .maxcnt
166 .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
167
168 // wait till the user buffer is sampled
169 Self::wait_for_sample().await;
170
171 drop.defuse();
172
173 Ok(())
174 }
175
176 async fn wait_for_sample() {
177 let r = Self::regs();
178
179 r.events_end.reset();
180 r.intenset.write(|w| w.end().set());
181
182 compiler_fence(Ordering::SeqCst);
183
184 poll_fn(|cx| {
185 WAKER.register(cx.waker());
186 if r.events_end.read().events_end().bit_is_set() {
187 return Poll::Ready(());
188 }
189 Poll::Pending
190 })
191 .await;
192
193 compiler_fence(Ordering::SeqCst);
194 }
195
196 fn regs() -> &'static pac::pdm::RegisterBlock {
197 unsafe { &*pac::PDM::ptr() }
198 }
199}
200
201/// PDM microphone driver Config
202pub struct Config {
203 /// Use stero or mono operation
204 pub operation_mode: OperationMode,
205 /// On which edge the left channel should be samples
206 pub edge: Edge,
207}
208
209impl Default for Config {
210 fn default() -> Self {
211 Self {
212 operation_mode: OperationMode::Mono,
213 edge: Edge::LeftFalling,
214 }
215 }
216}
217
218#[derive(PartialEq)]
219pub enum OperationMode {
220 Mono,
221 Stereo,
222}
223#[derive(PartialEq)]
224pub enum Edge {
225 LeftRising,
226 LeftFalling,
227}
228
229impl<'d> Drop for Pdm<'d> {
230 fn drop(&mut self) {
231 let r = Self::regs();
232
233 r.tasks_stop.write(|w| w.tasks_stop().set_bit());
234
235 self.irq.disable();
236
237 r.enable.write(|w| w.enable().disabled());
238
239 r.psel.din.reset();
240 r.psel.clk.reset();
241 }
242}
diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf/src/bin/pdm.rs
new file mode 100644
index 000000000..7388580fb
--- /dev/null
+++ b/examples/nrf/src/bin/pdm.rs
@@ -0,0 +1,33 @@
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, Pdm};
9use embassy_time::{Duration, Timer};
10use {defmt_rtt as _, panic_probe as _};
11
12#[embassy_executor::main]
13async fn main(_p: Spawner) {
14 let p = embassy_nrf::init(Default::default());
15 let config = Config::default();
16 let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), p.P0_01, p.P0_00, config);
17
18 loop {
19 pdm.start().await;
20
21 // wait some time till the microphon settled
22 Timer::after(Duration::from_millis(1000)).await;
23
24 const SAMPLES: usize = 2048;
25 let mut buf = [0i16; SAMPLES];
26 pdm.sample(&mut buf).await.unwrap();
27
28 info!("samples: {:?}", &buf);
29
30 pdm.stop().await;
31 Timer::after(Duration::from_millis(100)).await;
32 }
33}