aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf/src/pdm.rs
diff options
context:
space:
mode:
authorQuentin Smith <[email protected]>2023-07-17 21:31:43 -0400
committerQuentin Smith <[email protected]>2023-07-17 21:31:43 -0400
commit6f02403184eb7fb7990fb88fc9df9c4328a690a3 (patch)
tree748f510e190bb2724750507a6e69ed1a8e08cb20 /embassy-nrf/src/pdm.rs
parentd896f80405aa8963877049ed999e4aba25d6e2bb (diff)
parent6b5df4523aa1c4902f02e803450ae4b418e0e3ca (diff)
Merge remote-tracking branch 'origin/main' into nrf-pdm
Diffstat (limited to 'embassy-nrf/src/pdm.rs')
-rw-r--r--embassy-nrf/src/pdm.rs447
1 files changed, 285 insertions, 162 deletions
diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs
index c2c54fba9..1fc717fd1 100644
--- a/embassy-nrf/src/pdm.rs
+++ b/embassy-nrf/src/pdm.rs
@@ -1,66 +1,71 @@
1//! Pulse Density Modulation (PDM) mirophone driver.
2
1#![macro_use] 3#![macro_use]
2 4
5use core::marker::PhantomData;
3use core::sync::atomic::{compiler_fence, Ordering}; 6use core::sync::atomic::{compiler_fence, Ordering};
4use core::task::Poll; 7use core::task::Poll;
5 8
6use embassy_hal_common::{into_ref, PeripheralRef};
7use embassy_hal_common::drop::OnDrop; 9use embassy_hal_common::drop::OnDrop;
8use embassy_sync::waitqueue::AtomicWaker; 10use embassy_hal_common::{into_ref, PeripheralRef};
9use futures::future::poll_fn; 11use futures::future::poll_fn;
10use pac::{pdm, PDM};
11use pdm::mode::{EDGE_A, OPERATION_A};
12pub use pdm::pdmclkctrl::FREQ_A as Frequency;
13pub use pdm::ratio::RATIO_A as Ratio;
14use fixed::types::I7F1; 12use fixed::types::I7F1;
15 13
16use crate::interrupt::InterruptExt; 14use crate::chip::EASY_DMA_SIZE;
17use crate::gpio::Pin as GpioPin; 15use crate::gpio::sealed::Pin;
18use crate::{interrupt, pac, peripherals, Peripheral}; 16use crate::gpio::{AnyPin, Pin as GpioPin};
17use crate::interrupt::typelevel::Interrupt;
18use crate::{interrupt, Peripheral};
19use crate::pac::pdm::mode::{EDGE_A, OPERATION_A};
20pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
21pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
22
23/// Interrupt handler.
24pub struct InterruptHandler<T: Instance> {
25 _phantom: PhantomData<T>,
26}
19 27
20#[derive(Debug, Clone, Copy, PartialEq, Eq)] 28impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
21#[cfg_attr(feature = "defmt", derive(defmt::Format))] 29 unsafe fn on_interrupt() {
22#[non_exhaustive] 30 let r = T::regs();
23pub enum Error {} 31
32 if r.events_end.read().bits() != 0 {
33 r.intenclr.write(|w| w.end().clear());
34 }
24 35
25/// One-shot and continuous PDM. 36 if r.events_started.read().bits() != 0 {
26pub struct Pdm<'d> { 37 r.intenclr.write(|w| w.started().clear());
27 _p: PeripheralRef<'d, peripherals::PDM>, 38 }
39
40 if r.events_stopped.read().bits() != 0 {
41 r.intenclr.write(|w| w.stopped().clear());
42 }
43
44 T::state().waker.wake();
45 }
28} 46}
29 47
30static WAKER: AtomicWaker = AtomicWaker::new(); 48/// PDM microphone interface
49pub struct Pdm<'d, T: Instance> {
50 _peri: PeripheralRef<'d, T>,
51}
31 52
32/// Used to configure the PDM peripheral. 53/// PDM error.
33/// 54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34/// See the `Default` impl for suitable default values. 55#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35#[non_exhaustive] 56#[non_exhaustive]
36pub struct Config { 57pub enum Error {
37 /// Clock frequency 58 /// Buffer is too long.
38 pub frequency: Frequency, 59 BufferTooLong,
39 /// Clock ratio 60 /// Buffer is empty
40 pub ratio: Ratio, 61 BufferZeroLength,
41 /// Channels 62 /// PDM is not running
42 pub channels: Channels, 63 NotRunning,
43 /// Edge to sample on 64 /// PDM is already running
44 pub left_edge: Edge, 65 AlreadyRunning,
45 /// Gain left in dB
46 pub gain_left: I7F1,
47 /// Gain right in dB
48 pub gain_right: I7F1,
49} 66}
50 67
51impl Default for Config { 68static DUMMY_BUFFER: [i16; 1] = [0; 1];
52 /// Default configuration for single channel sampling.
53 fn default() -> Self {
54 Self {
55 frequency: Frequency::DEFAULT,
56 ratio: Ratio::RATIO80,
57 channels: Channels::Stereo,
58 left_edge: Edge::FallingEdge,
59 gain_left: I7F1::ZERO,
60 gain_right: I7F1::ZERO,
61 }
62 }
63}
64 69
65/// The state of a continuously running sampler. While it reflects 70/// The state of a continuously running sampler. While it reflects
66/// the progress of a sampler, it also signals what should be done 71/// the progress of a sampler, it also signals what should be done
@@ -68,69 +73,66 @@ impl Default for Config {
68/// can then tear down its infrastructure. 73/// can then tear down its infrastructure.
69#[derive(PartialEq)] 74#[derive(PartialEq)]
70pub enum SamplerState { 75pub enum SamplerState {
76 /// The sampler processed the samples and is ready for more.
71 Sampled, 77 Sampled,
78 /// The sampler is done processing samples.
72 Stopped, 79 Stopped,
73} 80}
74 81
75impl<'d> Pdm<'d> { 82impl<'d, T: Instance> Pdm<'d, T> {
83 /// Create PDM driver
76 pub fn new( 84 pub fn new(
77 pdm: impl Peripheral<P = peripherals::PDM> + 'd, 85 pdm: impl Peripheral<P = T> + 'd,
78 irq: impl Peripheral<P = interrupt::PDM> + 'd, 86 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
79 data: impl Peripheral<P = impl GpioPin> + 'd, 87 clk: impl Peripheral<P = impl GpioPin> + 'd,
80 clock: impl Peripheral<P = impl GpioPin> + 'd, 88 din: impl Peripheral<P = impl GpioPin> + 'd,
89 config: Config,
90 ) -> Self {
91 into_ref!(pdm, clk, din);
92 Self::new_inner(pdm, clk.map_into(), din.map_into(), config)
93 }
94
95 fn new_inner(
96 pdm: PeripheralRef<'d, T>,
97 clk: PeripheralRef<'d, AnyPin>,
98 din: PeripheralRef<'d, AnyPin>,
81 config: Config, 99 config: Config,
82 ) -> Self { 100 ) -> Self {
83 into_ref!(pdm, irq, data, clock); 101 into_ref!(pdm);
84 102
85 let r = unsafe { &*PDM::ptr() }; 103 let r = T::regs();
86 104
87 let Config { frequency, ratio, channels, left_edge, gain_left, gain_right } = config; 105 // setup gpio pins
106 din.conf().write(|w| w.input().set_bit());
107 r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) });
108 clk.set_low();
109 clk.conf().write(|w| w.dir().output());
110 r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
88 111
89 // Configure channels 112 // configure
90 r.enable.write(|w| w.enable().enabled()); 113 r.pdmclkctrl.write(|w| w.freq().variant(config.frequency));
91 r.pdmclkctrl.write(|w| w.freq().variant(frequency)); 114 r.ratio.write(|w| w.ratio().variant(config.ratio));
92 r.ratio.write(|w| w.ratio().variant(ratio));
93 r.mode.write(|w| { 115 r.mode.write(|w| {
94 w.operation().variant(channels.into()); 116 w.operation().variant(config.operation_mode.into());
95 w.edge().variant(left_edge.into()); 117 w.edge().variant(config.edge.into());
96 w 118 w
97 }); 119 });
98 120
99 Self::_set_gain(r, gain_left, gain_right); 121 Self::_set_gain(r, config.gain_left, config.gain_right);
100
101 r.psel.din.write(|w| unsafe { w.bits(data.psel_bits()) });
102 r.psel.clk.write(|w| unsafe { w.bits(clock.psel_bits()) });
103 122
104 // Disable all events interrupts 123 // Disable all events interrupts
105 r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); 124 r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
106 125
107 irq.set_handler(Self::on_interrupt); 126 // IRQ
108 irq.unpend(); 127 T::Interrupt::unpend();
109 irq.enable(); 128 unsafe { T::Interrupt::enable() };
110
111 Self { _p: pdm }
112 }
113
114 fn on_interrupt(_ctx: *mut ()) {
115 let r = Self::regs();
116
117 if r.events_end.read().bits() != 0 {
118 r.intenclr.write(|w| w.end().clear());
119 WAKER.wake();
120 }
121 129
122 if r.events_started.read().bits() != 0 { 130 r.enable.write(|w| w.enable().set_bit());
123 r.intenclr.write(|w| w.started().clear());
124 WAKER.wake();
125 }
126 131
127 if r.events_stopped.read().bits() != 0 { 132 Self { _peri: pdm }
128 r.intenclr.write(|w| w.stopped().clear());
129 WAKER.wake();
130 }
131 } 133 }
132 134
133 fn _set_gain(r: &pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) { 135 fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) {
134 let gain_left = gain_left.saturating_add(I7F1::from_bits(40)).saturating_to_num::<u8>().clamp(0, 0x50); 136 let gain_left = gain_left.saturating_add(I7F1::from_bits(40)).saturating_to_num::<u8>().clamp(0, 0x50);
135 let gain_right = gain_right.saturating_add(I7F1::from_bits(40)).saturating_to_num::<u8>().clamp(0, 0x50); 137 let gain_right = gain_right.saturating_add(I7F1::from_bits(40)).saturating_to_num::<u8>().clamp(0, 0x50);
136 138
@@ -138,81 +140,111 @@ impl<'d> Pdm<'d> {
138 r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) }); 140 r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) });
139 } 141 }
140 142
143 /// Adjust the gain of the PDM microphone on the fly
141 pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) { 144 pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) {
142 Self::_set_gain(Self::regs(), gain_left, gain_right) 145 Self::_set_gain(T::regs(), gain_left, gain_right)
143 } 146 }
144 147
145 fn regs() -> &'static pdm::RegisterBlock { 148 /// Start sampling microphon data into a dummy buffer
146 unsafe { &*PDM::ptr() } 149 /// Usefull to start the microphon and keep it active between recording samples
150 pub async fn start(&mut self) {
151 let r = T::regs();
152
153 // start dummy sampling because microphon needs some setup time
154 r.sample
155 .ptr
156 .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
157 r.sample
158 .maxcnt
159 .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
160
161 r.tasks_start.write(|w| unsafe { w.bits(1) });
147 } 162 }
148 163
149 /// One shot sampling. If the PDM is configured for multiple channels, the samples will be interleaved. 164 /// Stop sampling microphon data inta a dummy buffer
150 /// The first samples from the PDM peripheral and microphone usually contain garbage data, so the discard parameter sets the number of complete buffers to discard before returning. 165 pub async fn stop(&mut self) {
151 pub async fn sample<const N: usize>(&mut self, mut discard: usize, buf: &mut [i16; N]) { 166 let r = T::regs();
152 let r = Self::regs(); 167 r.tasks_stop.write(|w| unsafe { w.bits(1) });
168 r.events_started.reset();
169 }
153 170
154 // Set up the DMA 171 /// Sample data into the given buffer.
155 r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(buf.as_mut_ptr() as u32) }); 172 pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
156 r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) }); 173 if buffer.len() == 0 {
174 return Err(Error::BufferZeroLength);
175 }
176 if buffer.len() > EASY_DMA_SIZE {
177 return Err(Error::BufferTooLong);
178 }
157 179
158 // Reset and enable the events 180 let r = T::regs();
159 r.events_end.reset();
160 r.events_stopped.reset();
161 r.intenset.write(|w| {
162 w.end().set();
163 w.stopped().set();
164 w
165 });
166 181
167 // Don't reorder the start event before the previous writes. Hopefully self 182 if r.events_started.read().bits() == 0 {
168 // wouldn't happen anyway. 183 return Err(Error::NotRunning);
169 compiler_fence(Ordering::SeqCst); 184 }
170 185
171 r.tasks_start.write(|w| w.tasks_start().set_bit()); 186 let drop = OnDrop::new(move || {
187 r.intenclr.write(|w| w.end().clear());
188 r.events_stopped.reset();
172 189
173 let ondrop = OnDrop::new(|| { 190 // reset to dummy buffer
174 r.tasks_stop.write(|w| w.tasks_stop().set_bit()); 191 r.sample
175 // N.B. It would be better if this were async, but Drop only support sync code. 192 .ptr
176 while r.events_stopped.read().bits() != 0 {} 193 .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
194 r.sample
195 .maxcnt
196 .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
197
198 while r.events_stopped.read().bits() == 0 {}
177 }); 199 });
178 200
179 // Wait for 'end' event. 201 // setup user buffer
180 poll_fn(|cx| { 202 let ptr = buffer.as_ptr();
181 let r = Self::regs(); 203 let len = buffer.len();
204 r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) });
205 r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) });
182 206
183 WAKER.register(cx.waker()); 207 // wait till the current sample is finished and the user buffer sample is started
208 Self::wait_for_sample().await;
184 209
185 if r.events_end.read().bits() != 0 { 210 // reset the buffer back to the dummy buffer
186 compiler_fence(Ordering::SeqCst); 211 r.sample
187 // END means the whole buffer has been received. 212 .ptr
188 r.events_end.reset(); 213 .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
189 r.intenset.write(|w| w.end().set()); 214 r.sample
215 .maxcnt
216 .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
190 217
191 if discard > 0 { 218 // wait till the user buffer is sampled
192 discard -= 1; 219 Self::wait_for_sample().await;
193 } else { 220
194 // Note that the beginning of the buffer might be overwritten before the task fully stops :( 221 drop.defuse();
195 r.tasks_stop.write(|w| w.tasks_stop().set_bit()); 222
196 } 223 Ok(())
197 } 224 }
198 if r.events_stopped.read().bits() != 0 { 225
226 async fn wait_for_sample() {
227 let r = T::regs();
228
229 r.events_end.reset();
230 r.intenset.write(|w| w.end().set());
231
232 compiler_fence(Ordering::SeqCst);
233
234 poll_fn(|cx| {
235 T::state().waker.register(cx.waker());
236 if r.events_end.read().bits() != 0 {
199 return Poll::Ready(()); 237 return Poll::Ready(());
200 } 238 }
201
202 Poll::Pending 239 Poll::Pending
203 }) 240 })
204 .await; 241 .await;
205 ondrop.defuse(); 242
243 compiler_fence(Ordering::SeqCst);
206 } 244 }
207 245
208 /// Continuous sampling with double buffers. 246 /// Continuous sampling with double buffers.
209 /// 247 ///
210 /// A TIMER and two PPI peripherals are passed in so that precise sampling
211 /// can be attained. The sampling interval is expressed by selecting a
212 /// timer clock frequency to use along with a counter threshold to be reached.
213 /// For example, 1KHz can be achieved using a frequency of 1MHz and a counter
214 /// threshold of 1000.
215 ///
216 /// A sampler closure is provided that receives the buffer of samples, noting 248 /// A sampler closure is provided that receives the buffer of samples, noting
217 /// that the size of this buffer can be less than the original buffer's size. 249 /// that the size of this buffer can be less than the original buffer's size.
218 /// A command is return from the closure that indicates whether the sampling 250 /// A command is return from the closure that indicates whether the sampling
@@ -226,10 +258,14 @@ impl<'d> Pdm<'d> {
226 &mut self, 258 &mut self,
227 bufs: &mut [[i16; N]; 2], 259 bufs: &mut [[i16; N]; 2],
228 mut sampler: S, 260 mut sampler: S,
229 ) where 261 ) -> Result<(), Error> where
230 S: FnMut(&[i16; N]) -> SamplerState, 262 S: FnMut(&[i16; N]) -> SamplerState,
231 { 263 {
232 let r = Self::regs(); 264 let r = T::regs();
265
266 if r.events_started.read().bits() != 0 {
267 return Err(Error::AlreadyRunning);
268 }
233 269
234 r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) }); 270 r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) });
235 r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) }); 271 r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) });
@@ -255,7 +291,7 @@ impl<'d> Pdm<'d> {
255 291
256 let mut done = false; 292 let mut done = false;
257 293
258 let ondrop = OnDrop::new(|| { 294 let drop = OnDrop::new(|| {
259 r.tasks_stop.write(|w| w.tasks_stop().set_bit()); 295 r.tasks_stop.write(|w| w.tasks_stop().set_bit());
260 // N.B. It would be better if this were async, but Drop only support sync code. 296 // N.B. It would be better if this were async, but Drop only support sync code.
261 while r.events_stopped.read().bits() != 0 {} 297 while r.events_stopped.read().bits() != 0 {}
@@ -263,9 +299,9 @@ impl<'d> Pdm<'d> {
263 299
264 // Wait for events and complete when the sampler indicates it has had enough. 300 // Wait for events and complete when the sampler indicates it has had enough.
265 poll_fn(|cx| { 301 poll_fn(|cx| {
266 let r = Self::regs(); 302 let r = T::regs();
267 303
268 WAKER.register(cx.waker()); 304 T::state().waker.register(cx.waker());
269 305
270 if r.events_end.read().bits() != 0 { 306 if r.events_end.read().bits() != 0 {
271 compiler_fence(Ordering::SeqCst); 307 compiler_fence(Ordering::SeqCst);
@@ -301,43 +337,130 @@ impl<'d> Pdm<'d> {
301 Poll::Pending 337 Poll::Pending
302 }) 338 })
303 .await; 339 .await;
304 ondrop.defuse(); 340 drop.defuse();
341 Ok(())
305 } 342 }
306} 343}
307 344
308impl<'d> Drop for Pdm<'d> { 345/// PDM microphone driver Config
309 fn drop(&mut self) { 346pub struct Config {
310 let r = Self::regs(); 347 /// Use stero or mono operation
311 r.enable.write(|w| w.enable().disabled()); 348 pub operation_mode: OperationMode,
349 /// On which edge the left channel should be samples
350 pub edge: Edge,
351 /// Clock frequency
352 pub frequency: Frequency,
353 /// Clock ratio
354 pub ratio: Ratio,
355 /// Gain left in dB
356 pub gain_left: I7F1,
357 /// Gain right in dB
358 pub gain_right: I7F1,
359}
360
361impl Default for Config {
362 fn default() -> Self {
363 Self {
364 operation_mode: OperationMode::Mono,
365 edge: Edge::LeftFalling,
366 frequency: Frequency::DEFAULT,
367 ratio: Ratio::RATIO80,
368 gain_left: I7F1::ZERO,
369 gain_right: I7F1::ZERO,
370 }
371 }
372}
373
374/// PDM operation mode.
375#[derive(PartialEq)]
376pub enum OperationMode {
377 /// Mono (1 channel)
378 Mono,
379 /// Stereo (2 channels)
380 Stereo,
381}
382
383impl From<OperationMode> for OPERATION_A {
384 fn from(mode: OperationMode) -> Self {
385 match mode {
386 OperationMode::Mono => OPERATION_A::MONO,
387 OperationMode::Stereo => OPERATION_A::STEREO,
388 }
312 } 389 }
313} 390}
314 391
315#[derive(Clone, Copy, PartialEq)] 392/// PDM edge polarity
393#[derive(PartialEq)]
316pub enum Edge { 394pub enum Edge {
317 FallingEdge, 395 /// Left edge is rising
318 RisingEdge, 396 LeftRising,
397 /// Left edge is falling
398 LeftFalling,
319} 399}
320 400
321impl From<Edge> for EDGE_A { 401impl From<Edge> for EDGE_A {
322 fn from(edge: Edge) -> Self { 402 fn from(edge: Edge) -> Self {
323 match edge { 403 match edge {
324 Edge::FallingEdge => EDGE_A::LEFTFALLING, 404 Edge::LeftRising => EDGE_A::LEFT_RISING,
325 Edge::RisingEdge => EDGE_A::LEFTRISING, 405 Edge::LeftFalling => EDGE_A::LEFT_FALLING,
326 } 406 }
327 } 407 }
328} 408}
329 409
330#[derive(Clone, Copy, PartialEq)] 410impl<'d, T: Instance> Drop for Pdm<'d, T> {
331pub enum Channels { 411 fn drop(&mut self) {
332 Stereo, 412 let r = T::regs();
333 Mono, 413
414 r.tasks_stop.write(|w| unsafe { w.bits(1) });
415
416 r.enable.write(|w| w.enable().disabled());
417
418 r.psel.din.reset();
419 r.psel.clk.reset();
420 }
334} 421}
335 422
336impl From<Channels> for OPERATION_A { 423pub(crate) mod sealed {
337 fn from(ch: Channels) -> Self { 424 use embassy_sync::waitqueue::AtomicWaker;
338 match ch { 425
339 Channels::Stereo => OPERATION_A::STEREO, 426 /// Peripheral static state
340 Channels::Mono => OPERATION_A::MONO, 427 pub struct State {
428 pub waker: AtomicWaker,
429 }
430
431 impl State {
432 pub const fn new() -> Self {
433 Self {
434 waker: AtomicWaker::new(),
435 }
341 } 436 }
342 } 437 }
438
439 pub trait Instance {
440 fn regs() -> &'static crate::pac::pdm::RegisterBlock;
441 fn state() -> &'static State;
442 }
443}
444
445/// PDM peripheral instance.
446pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
447 /// Interrupt for this peripheral.
448 type Interrupt: interrupt::typelevel::Interrupt;
449}
450
451macro_rules! impl_pdm {
452 ($type:ident, $pac_type:ident, $irq:ident) => {
453 impl crate::pdm::sealed::Instance for peripherals::$type {
454 fn regs() -> &'static crate::pac::pdm::RegisterBlock {
455 unsafe { &*pac::$pac_type::ptr() }
456 }
457 fn state() -> &'static crate::pdm::sealed::State {
458 static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new();
459 &STATE
460 }
461 }
462 impl crate::pdm::Instance for peripherals::$type {
463 type Interrupt = crate::interrupt::typelevel::$irq;
464 }
465 };
343} \ No newline at end of file 466} \ No newline at end of file