embassy_stm32/
dcmi.rs

1//! Digital Camera Interface (DCMI)
2use 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
14/// Interrupt handler.
15pub 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/// 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)]
41pub 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)]
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/// DCMI error.
76#[derive(Debug, Eq, PartialEq, Copy, Clone)]
77#[cfg_attr(feature = "defmt", derive(defmt::Format))]
78#[non_exhaustive]
79pub 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]
88pub 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
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
118/// DCMI driver.
119pub 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    /// 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
432trait SealedInstance: crate::rcc::RccPeripheral {
433    fn regs(&self) -> crate::pac::dcmi::Dcmi;
434}
435
436/// DCMI instance.
437#[allow(private_bounds)]
438pub trait Instance: SealedInstance + 'static {
439    /// Interrupt for this instance.
440    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 as U5 sources do not contain interrupt nor dma data
462#[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);