aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf/src/pdm.rs
diff options
context:
space:
mode:
authorpbert <[email protected]>2022-07-10 20:12:25 +0200
committerpbert <[email protected]>2022-10-13 18:37:53 +0200
commita4afab46403aacd7bb555cadaefad7f216fb9931 (patch)
tree217bb3ae87b91cf9e740838509f533cb955cc0c7 /embassy-nrf/src/pdm.rs
parentf075e624440af121da7a27a145e2acee0730c542 (diff)
add support for pdm microphones in nrf driver
Diffstat (limited to 'embassy-nrf/src/pdm.rs')
-rw-r--r--embassy-nrf/src/pdm.rs242
1 files changed, 242 insertions, 0 deletions
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}