1 | //! Digital Camera Interface (DCMI) |
2 | use core::future::poll_fn; |
3 | use core::marker::PhantomData; |
4 | use core::task::Poll; |
5 | |
6 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
7 | use embassy_sync::waitqueue::AtomicWaker; |
8 | |
9 | use crate::dma::Transfer; |
10 | use crate::gpio::{AfType, Pull}; |
11 | use crate::interrupt::typelevel::Interrupt; |
12 | use crate::{interrupt, rcc, Peripheral}; |
13 | |
14 | /// Interrupt handler. |
15 | pub struct InterruptHandler<T: Instance> { |
16 | _phantom: PhantomData<T>, |
17 | } |
18 | |
19 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { |
20 | unsafe fn on_interrupt() { |
21 | let ris: Ris = crate::pac::DCMI.ris().read(); |
22 | if ris.err_ris() { |
23 | trace!("DCMI IRQ: Error." ); |
24 | crate::pac::DCMI.ier().modify(|ier: &mut Ier| ier.set_err_ie(val:false)); |
25 | } |
26 | if ris.ovr_ris() { |
27 | trace!("DCMI IRQ: Overrun." ); |
28 | crate::pac::DCMI.ier().modify(|ier: &mut Ier| ier.set_ovr_ie(val:false)); |
29 | } |
30 | if ris.frame_ris() { |
31 | trace!("DCMI IRQ: Frame captured." ); |
32 | crate::pac::DCMI.ier().modify(|ier: &mut Ier| ier.set_frame_ie(val:false)); |
33 | } |
34 | STATE.waker.wake(); |
35 | } |
36 | } |
37 | |
38 | /// The level on the VSync pin when the data is not valid on the parallel interface. |
39 | #[allow (missing_docs)] |
40 | #[derive (Clone, Copy, PartialEq)] |
41 | pub enum VSyncDataInvalidLevel { |
42 | Low, |
43 | High, |
44 | } |
45 | |
46 | /// The level on the VSync pin when the data is not valid on the parallel interface. |
47 | #[allow (missing_docs)] |
48 | #[derive (Clone, Copy, PartialEq)] |
49 | pub enum HSyncDataInvalidLevel { |
50 | Low, |
51 | High, |
52 | } |
53 | |
54 | #[derive (Clone, Copy, PartialEq)] |
55 | #[allow (missing_docs)] |
56 | pub enum PixelClockPolarity { |
57 | RisingEdge, |
58 | FallingEdge, |
59 | } |
60 | |
61 | struct State { |
62 | waker: AtomicWaker, |
63 | } |
64 | |
65 | impl State { |
66 | const fn new() -> State { |
67 | State { |
68 | waker: AtomicWaker::new(), |
69 | } |
70 | } |
71 | } |
72 | |
73 | static STATE: State = State::new(); |
74 | |
75 | /// DCMI error. |
76 | #[derive (Debug, Eq, PartialEq, Copy, Clone)] |
77 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
78 | #[non_exhaustive ] |
79 | pub enum Error { |
80 | /// Overrun error: the hardware generated data faster than we could read it. |
81 | Overrun, |
82 | /// Internal peripheral error. |
83 | PeripheralError, |
84 | } |
85 | |
86 | /// DCMI configuration. |
87 | #[non_exhaustive ] |
88 | pub struct Config { |
89 | /// VSYNC level. |
90 | pub vsync_level: VSyncDataInvalidLevel, |
91 | /// HSYNC level. |
92 | pub hsync_level: HSyncDataInvalidLevel, |
93 | /// PIXCLK polarity. |
94 | pub pixclk_polarity: PixelClockPolarity, |
95 | } |
96 | |
97 | impl Default for Config { |
98 | fn default() -> Self { |
99 | Self { |
100 | vsync_level: VSyncDataInvalidLevel::High, |
101 | hsync_level: HSyncDataInvalidLevel::Low, |
102 | pixclk_polarity: PixelClockPolarity::RisingEdge, |
103 | } |
104 | } |
105 | } |
106 | |
107 | macro_rules! config_pins { |
108 | ($($pin:ident),*) => { |
109 | into_ref!($($pin),*); |
110 | critical_section::with(|_| { |
111 | $( |
112 | $pin.set_as_af($pin.af_num(), AfType::input(Pull::None)); |
113 | )* |
114 | }) |
115 | }; |
116 | } |
117 | |
118 | /// DCMI driver. |
119 | pub struct Dcmi<'d, T: Instance, Dma: FrameDma<T>> { |
120 | inner: PeripheralRef<'d, T>, |
121 | dma: PeripheralRef<'d, Dma>, |
122 | } |
123 | |
124 | impl<'d, T, Dma> Dcmi<'d, T, Dma> |
125 | where |
126 | T: Instance, |
127 | Dma: FrameDma<T>, |
128 | { |
129 | /// Create a new DCMI driver with 8 data bits. |
130 | pub fn new_8bit( |
131 | peri: impl Peripheral<P = T> + 'd, |
132 | dma: impl Peripheral<P = Dma> + 'd, |
133 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
134 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, |
135 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, |
136 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, |
137 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, |
138 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, |
139 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, |
140 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, |
141 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, |
142 | v_sync: impl Peripheral<P = impl VSyncPin<T>> + 'd, |
143 | h_sync: impl Peripheral<P = impl HSyncPin<T>> + 'd, |
144 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, |
145 | config: Config, |
146 | ) -> Self { |
147 | into_ref!(peri, dma); |
148 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7); |
149 | config_pins!(v_sync, h_sync, pixclk); |
150 | |
151 | Self::new_inner(peri, dma, config, false, 0b00) |
152 | } |
153 | |
154 | /// Create a new DCMI driver with 10 data bits. |
155 | pub fn new_10bit( |
156 | peri: impl Peripheral<P = T> + 'd, |
157 | dma: impl Peripheral<P = Dma> + 'd, |
158 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
159 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, |
160 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, |
161 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, |
162 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, |
163 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, |
164 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, |
165 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, |
166 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, |
167 | d8: impl Peripheral<P = impl D8Pin<T>> + 'd, |
168 | d9: impl Peripheral<P = impl D9Pin<T>> + 'd, |
169 | v_sync: impl Peripheral<P = impl VSyncPin<T>> + 'd, |
170 | h_sync: impl Peripheral<P = impl HSyncPin<T>> + 'd, |
171 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, |
172 | config: Config, |
173 | ) -> Self { |
174 | into_ref!(peri, dma); |
175 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9); |
176 | config_pins!(v_sync, h_sync, pixclk); |
177 | |
178 | Self::new_inner(peri, dma, config, false, 0b01) |
179 | } |
180 | |
181 | /// Create a new DCMI driver with 12 data bits. |
182 | pub fn new_12bit( |
183 | peri: impl Peripheral<P = T> + 'd, |
184 | dma: impl Peripheral<P = Dma> + 'd, |
185 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
186 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, |
187 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, |
188 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, |
189 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, |
190 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, |
191 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, |
192 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, |
193 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, |
194 | d8: impl Peripheral<P = impl D8Pin<T>> + 'd, |
195 | d9: impl Peripheral<P = impl D9Pin<T>> + 'd, |
196 | d10: impl Peripheral<P = impl D10Pin<T>> + 'd, |
197 | d11: impl Peripheral<P = impl D11Pin<T>> + 'd, |
198 | v_sync: impl Peripheral<P = impl VSyncPin<T>> + 'd, |
199 | h_sync: impl Peripheral<P = impl HSyncPin<T>> + 'd, |
200 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, |
201 | config: Config, |
202 | ) -> Self { |
203 | into_ref!(peri, dma); |
204 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11); |
205 | config_pins!(v_sync, h_sync, pixclk); |
206 | |
207 | Self::new_inner(peri, dma, config, false, 0b10) |
208 | } |
209 | |
210 | /// Create a new DCMI driver with 14 data bits. |
211 | pub fn new_14bit( |
212 | peri: impl Peripheral<P = T> + 'd, |
213 | dma: impl Peripheral<P = Dma> + 'd, |
214 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
215 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, |
216 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, |
217 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, |
218 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, |
219 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, |
220 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, |
221 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, |
222 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, |
223 | d8: impl Peripheral<P = impl D8Pin<T>> + 'd, |
224 | d9: impl Peripheral<P = impl D9Pin<T>> + 'd, |
225 | d10: impl Peripheral<P = impl D10Pin<T>> + 'd, |
226 | d11: impl Peripheral<P = impl D11Pin<T>> + 'd, |
227 | d12: impl Peripheral<P = impl D12Pin<T>> + 'd, |
228 | d13: impl Peripheral<P = impl D13Pin<T>> + 'd, |
229 | v_sync: impl Peripheral<P = impl VSyncPin<T>> + 'd, |
230 | h_sync: impl Peripheral<P = impl HSyncPin<T>> + 'd, |
231 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, |
232 | config: Config, |
233 | ) -> Self { |
234 | into_ref!(peri, dma); |
235 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13); |
236 | config_pins!(v_sync, h_sync, pixclk); |
237 | |
238 | Self::new_inner(peri, dma, config, false, 0b11) |
239 | } |
240 | |
241 | /// Create a new DCMI driver with 8 data bits, with embedded synchronization. |
242 | pub fn new_es_8bit( |
243 | peri: impl Peripheral<P = T> + 'd, |
244 | dma: impl Peripheral<P = Dma> + 'd, |
245 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
246 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, |
247 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, |
248 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, |
249 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, |
250 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, |
251 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, |
252 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, |
253 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, |
254 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, |
255 | config: Config, |
256 | ) -> Self { |
257 | into_ref!(peri, dma); |
258 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7); |
259 | config_pins!(pixclk); |
260 | |
261 | Self::new_inner(peri, dma, config, true, 0b00) |
262 | } |
263 | |
264 | /// Create a new DCMI driver with 10 data bits, with embedded synchronization. |
265 | pub fn new_es_10bit( |
266 | peri: impl Peripheral<P = T> + 'd, |
267 | dma: impl Peripheral<P = Dma> + 'd, |
268 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
269 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, |
270 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, |
271 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, |
272 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, |
273 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, |
274 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, |
275 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, |
276 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, |
277 | d8: impl Peripheral<P = impl D8Pin<T>> + 'd, |
278 | d9: impl Peripheral<P = impl D9Pin<T>> + 'd, |
279 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, |
280 | config: Config, |
281 | ) -> Self { |
282 | into_ref!(peri, dma); |
283 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9); |
284 | config_pins!(pixclk); |
285 | |
286 | Self::new_inner(peri, dma, config, true, 0b01) |
287 | } |
288 | |
289 | /// Create a new DCMI driver with 12 data bits, with embedded synchronization. |
290 | pub fn new_es_12bit( |
291 | peri: impl Peripheral<P = T> + 'd, |
292 | dma: impl Peripheral<P = Dma> + 'd, |
293 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
294 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, |
295 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, |
296 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, |
297 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, |
298 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, |
299 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, |
300 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, |
301 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, |
302 | d8: impl Peripheral<P = impl D8Pin<T>> + 'd, |
303 | d9: impl Peripheral<P = impl D9Pin<T>> + 'd, |
304 | d10: impl Peripheral<P = impl D10Pin<T>> + 'd, |
305 | d11: impl Peripheral<P = impl D11Pin<T>> + 'd, |
306 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, |
307 | config: Config, |
308 | ) -> Self { |
309 | into_ref!(peri, dma); |
310 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11); |
311 | config_pins!(pixclk); |
312 | |
313 | Self::new_inner(peri, dma, config, true, 0b10) |
314 | } |
315 | |
316 | /// Create a new DCMI driver with 14 data bits, with embedded synchronization. |
317 | pub fn new_es_14bit( |
318 | peri: impl Peripheral<P = T> + 'd, |
319 | dma: impl Peripheral<P = Dma> + 'd, |
320 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, |
321 | d0: impl Peripheral<P = impl D0Pin<T>> + 'd, |
322 | d1: impl Peripheral<P = impl D1Pin<T>> + 'd, |
323 | d2: impl Peripheral<P = impl D2Pin<T>> + 'd, |
324 | d3: impl Peripheral<P = impl D3Pin<T>> + 'd, |
325 | d4: impl Peripheral<P = impl D4Pin<T>> + 'd, |
326 | d5: impl Peripheral<P = impl D5Pin<T>> + 'd, |
327 | d6: impl Peripheral<P = impl D6Pin<T>> + 'd, |
328 | d7: impl Peripheral<P = impl D7Pin<T>> + 'd, |
329 | d8: impl Peripheral<P = impl D8Pin<T>> + 'd, |
330 | d9: impl Peripheral<P = impl D9Pin<T>> + 'd, |
331 | d10: impl Peripheral<P = impl D10Pin<T>> + 'd, |
332 | d11: impl Peripheral<P = impl D11Pin<T>> + 'd, |
333 | d12: impl Peripheral<P = impl D12Pin<T>> + 'd, |
334 | d13: impl Peripheral<P = impl D13Pin<T>> + 'd, |
335 | pixclk: impl Peripheral<P = impl PixClkPin<T>> + 'd, |
336 | config: Config, |
337 | ) -> Self { |
338 | into_ref!(peri, dma); |
339 | config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13); |
340 | config_pins!(pixclk); |
341 | |
342 | Self::new_inner(peri, dma, config, true, 0b11) |
343 | } |
344 | |
345 | fn new_inner( |
346 | peri: PeripheralRef<'d, T>, |
347 | dma: PeripheralRef<'d, Dma>, |
348 | config: Config, |
349 | use_embedded_synchronization: bool, |
350 | edm: u8, |
351 | ) -> Self { |
352 | rcc::enable_and_reset::<T>(); |
353 | |
354 | peri.regs().cr().modify(|r| { |
355 | r.set_cm(true); // disable continuous mode (snapshot mode) |
356 | r.set_ess(use_embedded_synchronization); |
357 | r.set_pckpol(config.pixclk_polarity == PixelClockPolarity::RisingEdge); |
358 | r.set_vspol(config.vsync_level == VSyncDataInvalidLevel::High); |
359 | r.set_hspol(config.hsync_level == HSyncDataInvalidLevel::High); |
360 | r.set_fcrc(0x00); // capture every frame |
361 | r.set_edm(edm); // extended data mode |
362 | }); |
363 | |
364 | T::Interrupt::unpend(); |
365 | unsafe { T::Interrupt::enable() }; |
366 | |
367 | Self { inner: peri, dma } |
368 | } |
369 | |
370 | fn toggle(enable: bool) { |
371 | crate::pac::DCMI.cr().modify(|r| { |
372 | r.set_enable(enable); |
373 | r.set_capture(enable); |
374 | }) |
375 | } |
376 | |
377 | fn enable_irqs() { |
378 | crate::pac::DCMI.ier().modify(|r| { |
379 | r.set_err_ie(true); |
380 | r.set_ovr_ie(true); |
381 | r.set_frame_ie(true); |
382 | }); |
383 | } |
384 | |
385 | fn clear_interrupt_flags() { |
386 | crate::pac::DCMI.icr().write(|r| { |
387 | r.set_ovr_isc(true); |
388 | r.set_err_isc(true); |
389 | r.set_frame_isc(true); |
390 | }) |
391 | } |
392 | |
393 | /// This method starts the capture and finishes when both the dma transfer and DCMI finish the frame transfer. |
394 | /// The implication is that the input buffer size must be exactly the size of the captured frame. |
395 | pub async fn capture(&mut self, buffer: &mut [u32]) -> Result<(), Error> { |
396 | let r = self.inner.regs(); |
397 | let src = r.dr().as_ptr() as *mut u32; |
398 | let request = self.dma.request(); |
399 | let dma_read = unsafe { Transfer::new_read(&mut self.dma, request, src, buffer, Default::default()) }; |
400 | |
401 | Self::clear_interrupt_flags(); |
402 | Self::enable_irqs(); |
403 | |
404 | Self::toggle(true); |
405 | |
406 | let result = poll_fn(|cx| { |
407 | STATE.waker.register(cx.waker()); |
408 | |
409 | let ris = crate::pac::DCMI.ris().read(); |
410 | if ris.err_ris() { |
411 | crate::pac::DCMI.icr().write(|r| r.set_err_isc(true)); |
412 | Poll::Ready(Err(Error::PeripheralError)) |
413 | } else if ris.ovr_ris() { |
414 | crate::pac::DCMI.icr().write(|r| r.set_ovr_isc(true)); |
415 | Poll::Ready(Err(Error::Overrun)) |
416 | } else if ris.frame_ris() { |
417 | crate::pac::DCMI.icr().write(|r| r.set_frame_isc(true)); |
418 | Poll::Ready(Ok(())) |
419 | } else { |
420 | Poll::Pending |
421 | } |
422 | }); |
423 | |
424 | let (_, result) = embassy_futures::join::join(dma_read, result).await; |
425 | |
426 | Self::toggle(false); |
427 | |
428 | result |
429 | } |
430 | } |
431 | |
432 | trait SealedInstance: crate::rcc::RccPeripheral { |
433 | fn regs(&self) -> crate::pac::dcmi::Dcmi; |
434 | } |
435 | |
436 | /// DCMI instance. |
437 | #[allow (private_bounds)] |
438 | pub trait Instance: SealedInstance + 'static { |
439 | /// Interrupt for this instance. |
440 | type Interrupt: interrupt::typelevel::Interrupt; |
441 | } |
442 | |
443 | pin_trait!(D0Pin, Instance); |
444 | pin_trait!(D1Pin, Instance); |
445 | pin_trait!(D2Pin, Instance); |
446 | pin_trait!(D3Pin, Instance); |
447 | pin_trait!(D4Pin, Instance); |
448 | pin_trait!(D5Pin, Instance); |
449 | pin_trait!(D6Pin, Instance); |
450 | pin_trait!(D7Pin, Instance); |
451 | pin_trait!(D8Pin, Instance); |
452 | pin_trait!(D9Pin, Instance); |
453 | pin_trait!(D10Pin, Instance); |
454 | pin_trait!(D11Pin, Instance); |
455 | pin_trait!(D12Pin, Instance); |
456 | pin_trait!(D13Pin, Instance); |
457 | pin_trait!(HSyncPin, Instance); |
458 | pin_trait!(VSyncPin, Instance); |
459 | pin_trait!(PixClkPin, Instance); |
460 | |
461 | // allow unused as U5 sources do not contain interrupt nor dma data |
462 | #[allow (unused)] |
463 | macro_rules! impl_peripheral { |
464 | ($inst:ident, $irq:ident) => { |
465 | impl SealedInstance for crate::peripherals::$inst { |
466 | fn regs(&self) -> crate::pac::dcmi::Dcmi { |
467 | crate::pac::$inst |
468 | } |
469 | } |
470 | |
471 | impl Instance for crate::peripherals::$inst { |
472 | type Interrupt = crate::interrupt::typelevel::$irq; |
473 | } |
474 | }; |
475 | } |
476 | |
477 | foreach_interrupt! { |
478 | ($inst:ident, dcmi, $block:ident, GLOBAL, $irq:ident) => { |
479 | impl_peripheral!($inst, $irq); |
480 | }; |
481 | } |
482 | |
483 | dma_trait!(FrameDma, Instance); |
484 | |