1use core::future::poll_fn;
3use core::marker::PhantomData;
4use core::task::Poll;
5
6use embassy_hal_internal::{into_ref, PeripheralRef};
7use embassy_sync::waitqueue::AtomicWaker;
8
9use crate::dma::Transfer;
10use crate::gpio::{AfType, Pull};
11use crate::interrupt::typelevel::Interrupt;
12use crate::{interrupt, rcc, Peripheral};
13
14pub struct InterruptHandler<T: Instance> {
16 _phantom: PhantomData<T>,
17}
18
19impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
20 unsafe fn on_interrupt() {
21 let ris = crate::pac::DCMI.ris().read();
22 if ris.err_ris() {
23 trace!("DCMI IRQ: Error.");
24 crate::pac::DCMI.ier().modify(|ier| ier.set_err_ie(false));
25 }
26 if ris.ovr_ris() {
27 trace!("DCMI IRQ: Overrun.");
28 crate::pac::DCMI.ier().modify(|ier| ier.set_ovr_ie(false));
29 }
30 if ris.frame_ris() {
31 trace!("DCMI IRQ: Frame captured.");
32 crate::pac::DCMI.ier().modify(|ier| ier.set_frame_ie(false));
33 }
34 STATE.waker.wake();
35 }
36}
37
38#[allow(missing_docs)]
40#[derive(Clone, Copy, PartialEq)]
41pub enum VSyncDataInvalidLevel {
42 Low,
43 High,
44}
45
46#[allow(missing_docs)]
48#[derive(Clone, Copy, PartialEq)]
49pub enum HSyncDataInvalidLevel {
50 Low,
51 High,
52}
53
54#[derive(Clone, Copy, PartialEq)]
55#[allow(missing_docs)]
56pub enum PixelClockPolarity {
57 RisingEdge,
58 FallingEdge,
59}
60
61struct State {
62 waker: AtomicWaker,
63}
64
65impl State {
66 const fn new() -> State {
67 State {
68 waker: AtomicWaker::new(),
69 }
70 }
71}
72
73static STATE: State = State::new();
74
75#[derive(Debug, Eq, PartialEq, Copy, Clone)]
77#[cfg_attr(feature = "defmt", derive(defmt::Format))]
78#[non_exhaustive]
79pub enum Error {
80 Overrun,
82 PeripheralError,
84}
85
86#[non_exhaustive]
88pub struct Config {
89 pub vsync_level: VSyncDataInvalidLevel,
91 pub hsync_level: HSyncDataInvalidLevel,
93 pub pixclk_polarity: PixelClockPolarity,
95}
96
97impl 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
107macro_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
118pub struct Dcmi<'d, T: Instance, Dma: FrameDma<T>> {
120 inner: PeripheralRef<'d, T>,
121 dma: PeripheralRef<'d, Dma>,
122}
123
124impl<'d, T, Dma> Dcmi<'d, T, Dma>
125where
126 T: Instance,
127 Dma: FrameDma<T>,
128{
129 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 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 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 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 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 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 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 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); 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); r.set_edm(edm); });
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 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
432trait SealedInstance: crate::rcc::RccPeripheral {
433 fn regs(&self) -> crate::pac::dcmi::Dcmi;
434}
435
436#[allow(private_bounds)]
438pub trait Instance: SealedInstance + 'static {
439 type Interrupt: interrupt::typelevel::Interrupt;
441}
442
443pin_trait!(D0Pin, Instance);
444pin_trait!(D1Pin, Instance);
445pin_trait!(D2Pin, Instance);
446pin_trait!(D3Pin, Instance);
447pin_trait!(D4Pin, Instance);
448pin_trait!(D5Pin, Instance);
449pin_trait!(D6Pin, Instance);
450pin_trait!(D7Pin, Instance);
451pin_trait!(D8Pin, Instance);
452pin_trait!(D9Pin, Instance);
453pin_trait!(D10Pin, Instance);
454pin_trait!(D11Pin, Instance);
455pin_trait!(D12Pin, Instance);
456pin_trait!(D13Pin, Instance);
457pin_trait!(HSyncPin, Instance);
458pin_trait!(VSyncPin, Instance);
459pin_trait!(PixClkPin, Instance);
460
461#[allow(unused)]
463macro_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
477foreach_interrupt! {
478 ($inst:ident, dcmi, $block:ident, GLOBAL, $irq:ident) => {
479 impl_peripheral!($inst, $irq);
480 };
481}
482
483dma_trait!(FrameDma, Instance);