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::PeripheralType;
7use embassy_sync::waitqueue::AtomicWaker;
8
9use crate::dma::Transfer;
10use crate::gpio::{AfType, Pull};
11use crate::interrupt::typelevel::Interrupt;
12use crate::{interrupt, rcc, Peri};
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                critical_section::with(|_| {
110            $(
111                $pin.set_as_af($pin.af_num(), AfType::input(Pull::None));
112            )*
113        })
114    };
115}
116
117/// DCMI driver.
118pub struct Dcmi<'d, T: Instance, Dma: FrameDma<T>> {
119    inner: Peri<'d, T>,
120    dma: Peri<'d, Dma>,
121}
122
123impl<'d, T, Dma> Dcmi<'d, T, Dma>
124where
125    T: Instance,
126    Dma: FrameDma<T>,
127{
128    /// Create a new DCMI driver with 8 data bits.
129    pub fn new_8bit(
130        peri: Peri<'d, T>,
131        dma: Peri<'d, Dma>,
132        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
133        d0: Peri<'d, impl D0Pin<T>>,
134        d1: Peri<'d, impl D1Pin<T>>,
135        d2: Peri<'d, impl D2Pin<T>>,
136        d3: Peri<'d, impl D3Pin<T>>,
137        d4: Peri<'d, impl D4Pin<T>>,
138        d5: Peri<'d, impl D5Pin<T>>,
139        d6: Peri<'d, impl D6Pin<T>>,
140        d7: Peri<'d, impl D7Pin<T>>,
141        v_sync: Peri<'d, impl VSyncPin<T>>,
142        h_sync: Peri<'d, impl HSyncPin<T>>,
143        pixclk: Peri<'d, impl PixClkPin<T>>,
144        config: Config,
145    ) -> Self {
146        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7);
147        config_pins!(v_sync, h_sync, pixclk);
148
149        Self::new_inner(peri, dma, config, false, 0b00)
150    }
151
152    /// Create a new DCMI driver with 10 data bits.
153    pub fn new_10bit(
154        peri: Peri<'d, T>,
155        dma: Peri<'d, Dma>,
156        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
157        d0: Peri<'d, impl D0Pin<T>>,
158        d1: Peri<'d, impl D1Pin<T>>,
159        d2: Peri<'d, impl D2Pin<T>>,
160        d3: Peri<'d, impl D3Pin<T>>,
161        d4: Peri<'d, impl D4Pin<T>>,
162        d5: Peri<'d, impl D5Pin<T>>,
163        d6: Peri<'d, impl D6Pin<T>>,
164        d7: Peri<'d, impl D7Pin<T>>,
165        d8: Peri<'d, impl D8Pin<T>>,
166        d9: Peri<'d, impl D9Pin<T>>,
167        v_sync: Peri<'d, impl VSyncPin<T>>,
168        h_sync: Peri<'d, impl HSyncPin<T>>,
169        pixclk: Peri<'d, impl PixClkPin<T>>,
170        config: Config,
171    ) -> Self {
172        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9);
173        config_pins!(v_sync, h_sync, pixclk);
174
175        Self::new_inner(peri, dma, config, false, 0b01)
176    }
177
178    /// Create a new DCMI driver with 12 data bits.
179    pub fn new_12bit(
180        peri: Peri<'d, T>,
181        dma: Peri<'d, Dma>,
182        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
183        d0: Peri<'d, impl D0Pin<T>>,
184        d1: Peri<'d, impl D1Pin<T>>,
185        d2: Peri<'d, impl D2Pin<T>>,
186        d3: Peri<'d, impl D3Pin<T>>,
187        d4: Peri<'d, impl D4Pin<T>>,
188        d5: Peri<'d, impl D5Pin<T>>,
189        d6: Peri<'d, impl D6Pin<T>>,
190        d7: Peri<'d, impl D7Pin<T>>,
191        d8: Peri<'d, impl D8Pin<T>>,
192        d9: Peri<'d, impl D9Pin<T>>,
193        d10: Peri<'d, impl D10Pin<T>>,
194        d11: Peri<'d, impl D11Pin<T>>,
195        v_sync: Peri<'d, impl VSyncPin<T>>,
196        h_sync: Peri<'d, impl HSyncPin<T>>,
197        pixclk: Peri<'d, impl PixClkPin<T>>,
198        config: Config,
199    ) -> Self {
200        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);
201        config_pins!(v_sync, h_sync, pixclk);
202
203        Self::new_inner(peri, dma, config, false, 0b10)
204    }
205
206    /// Create a new DCMI driver with 14 data bits.
207    pub fn new_14bit(
208        peri: Peri<'d, T>,
209        dma: Peri<'d, Dma>,
210        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
211        d0: Peri<'d, impl D0Pin<T>>,
212        d1: Peri<'d, impl D1Pin<T>>,
213        d2: Peri<'d, impl D2Pin<T>>,
214        d3: Peri<'d, impl D3Pin<T>>,
215        d4: Peri<'d, impl D4Pin<T>>,
216        d5: Peri<'d, impl D5Pin<T>>,
217        d6: Peri<'d, impl D6Pin<T>>,
218        d7: Peri<'d, impl D7Pin<T>>,
219        d8: Peri<'d, impl D8Pin<T>>,
220        d9: Peri<'d, impl D9Pin<T>>,
221        d10: Peri<'d, impl D10Pin<T>>,
222        d11: Peri<'d, impl D11Pin<T>>,
223        d12: Peri<'d, impl D12Pin<T>>,
224        d13: Peri<'d, impl D13Pin<T>>,
225        v_sync: Peri<'d, impl VSyncPin<T>>,
226        h_sync: Peri<'d, impl HSyncPin<T>>,
227        pixclk: Peri<'d, impl PixClkPin<T>>,
228        config: Config,
229    ) -> Self {
230        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13);
231        config_pins!(v_sync, h_sync, pixclk);
232
233        Self::new_inner(peri, dma, config, false, 0b11)
234    }
235
236    /// Create a new DCMI driver with 8 data bits, with embedded synchronization.
237    pub fn new_es_8bit(
238        peri: Peri<'d, T>,
239        dma: Peri<'d, Dma>,
240        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
241        d0: Peri<'d, impl D0Pin<T>>,
242        d1: Peri<'d, impl D1Pin<T>>,
243        d2: Peri<'d, impl D2Pin<T>>,
244        d3: Peri<'d, impl D3Pin<T>>,
245        d4: Peri<'d, impl D4Pin<T>>,
246        d5: Peri<'d, impl D5Pin<T>>,
247        d6: Peri<'d, impl D6Pin<T>>,
248        d7: Peri<'d, impl D7Pin<T>>,
249        pixclk: Peri<'d, impl PixClkPin<T>>,
250        config: Config,
251    ) -> Self {
252        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7);
253        config_pins!(pixclk);
254
255        Self::new_inner(peri, dma, config, true, 0b00)
256    }
257
258    /// Create a new DCMI driver with 10 data bits, with embedded synchronization.
259    pub fn new_es_10bit(
260        peri: Peri<'d, T>,
261        dma: Peri<'d, Dma>,
262        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
263        d0: Peri<'d, impl D0Pin<T>>,
264        d1: Peri<'d, impl D1Pin<T>>,
265        d2: Peri<'d, impl D2Pin<T>>,
266        d3: Peri<'d, impl D3Pin<T>>,
267        d4: Peri<'d, impl D4Pin<T>>,
268        d5: Peri<'d, impl D5Pin<T>>,
269        d6: Peri<'d, impl D6Pin<T>>,
270        d7: Peri<'d, impl D7Pin<T>>,
271        d8: Peri<'d, impl D8Pin<T>>,
272        d9: Peri<'d, impl D9Pin<T>>,
273        pixclk: Peri<'d, impl PixClkPin<T>>,
274        config: Config,
275    ) -> Self {
276        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9);
277        config_pins!(pixclk);
278
279        Self::new_inner(peri, dma, config, true, 0b01)
280    }
281
282    /// Create a new DCMI driver with 12 data bits, with embedded synchronization.
283    pub fn new_es_12bit(
284        peri: Peri<'d, T>,
285        dma: Peri<'d, Dma>,
286        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
287        d0: Peri<'d, impl D0Pin<T>>,
288        d1: Peri<'d, impl D1Pin<T>>,
289        d2: Peri<'d, impl D2Pin<T>>,
290        d3: Peri<'d, impl D3Pin<T>>,
291        d4: Peri<'d, impl D4Pin<T>>,
292        d5: Peri<'d, impl D5Pin<T>>,
293        d6: Peri<'d, impl D6Pin<T>>,
294        d7: Peri<'d, impl D7Pin<T>>,
295        d8: Peri<'d, impl D8Pin<T>>,
296        d9: Peri<'d, impl D9Pin<T>>,
297        d10: Peri<'d, impl D10Pin<T>>,
298        d11: Peri<'d, impl D11Pin<T>>,
299        pixclk: Peri<'d, impl PixClkPin<T>>,
300        config: Config,
301    ) -> Self {
302        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);
303        config_pins!(pixclk);
304
305        Self::new_inner(peri, dma, config, true, 0b10)
306    }
307
308    /// Create a new DCMI driver with 14 data bits, with embedded synchronization.
309    pub fn new_es_14bit(
310        peri: Peri<'d, T>,
311        dma: Peri<'d, Dma>,
312        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
313        d0: Peri<'d, impl D0Pin<T>>,
314        d1: Peri<'d, impl D1Pin<T>>,
315        d2: Peri<'d, impl D2Pin<T>>,
316        d3: Peri<'d, impl D3Pin<T>>,
317        d4: Peri<'d, impl D4Pin<T>>,
318        d5: Peri<'d, impl D5Pin<T>>,
319        d6: Peri<'d, impl D6Pin<T>>,
320        d7: Peri<'d, impl D7Pin<T>>,
321        d8: Peri<'d, impl D8Pin<T>>,
322        d9: Peri<'d, impl D9Pin<T>>,
323        d10: Peri<'d, impl D10Pin<T>>,
324        d11: Peri<'d, impl D11Pin<T>>,
325        d12: Peri<'d, impl D12Pin<T>>,
326        d13: Peri<'d, impl D13Pin<T>>,
327        pixclk: Peri<'d, impl PixClkPin<T>>,
328        config: Config,
329    ) -> Self {
330        config_pins!(d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13);
331        config_pins!(pixclk);
332
333        Self::new_inner(peri, dma, config, true, 0b11)
334    }
335
336    fn new_inner(
337        peri: Peri<'d, T>,
338        dma: Peri<'d, Dma>,
339        config: Config,
340        use_embedded_synchronization: bool,
341        edm: u8,
342    ) -> Self {
343        rcc::enable_and_reset::<T>();
344
345        peri.regs().cr().modify(|r| {
346            r.set_cm(true); // disable continuous mode (snapshot mode)
347            r.set_ess(use_embedded_synchronization);
348            r.set_pckpol(config.pixclk_polarity == PixelClockPolarity::RisingEdge);
349            r.set_vspol(config.vsync_level == VSyncDataInvalidLevel::High);
350            r.set_hspol(config.hsync_level == HSyncDataInvalidLevel::High);
351            r.set_fcrc(0x00); // capture every frame
352            r.set_edm(edm); // extended data mode
353        });
354
355        T::Interrupt::unpend();
356        unsafe { T::Interrupt::enable() };
357
358        Self { inner: peri, dma }
359    }
360
361    fn toggle(enable: bool) {
362        crate::pac::DCMI.cr().modify(|r| {
363            r.set_enable(enable);
364            r.set_capture(enable);
365        })
366    }
367
368    fn enable_irqs() {
369        crate::pac::DCMI.ier().modify(|r| {
370            r.set_err_ie(true);
371            r.set_ovr_ie(true);
372            r.set_frame_ie(true);
373        });
374    }
375
376    fn clear_interrupt_flags() {
377        crate::pac::DCMI.icr().write(|r| {
378            r.set_ovr_isc(true);
379            r.set_err_isc(true);
380            r.set_frame_isc(true);
381        })
382    }
383
384    /// This method starts the capture and finishes when both the dma transfer and DCMI finish the frame transfer.
385    /// The implication is that the input buffer size must be exactly the size of the captured frame.
386    pub async fn capture(&mut self, buffer: &mut [u32]) -> Result<(), Error> {
387        let r = self.inner.regs();
388        let src = r.dr().as_ptr() as *mut u32;
389        let request = self.dma.request();
390        let dma_read = unsafe { Transfer::new_read(self.dma.reborrow(), request, src, buffer, Default::default()) };
391
392        Self::clear_interrupt_flags();
393        Self::enable_irqs();
394
395        Self::toggle(true);
396
397        let result = poll_fn(|cx| {
398            STATE.waker.register(cx.waker());
399
400            let ris = crate::pac::DCMI.ris().read();
401            if ris.err_ris() {
402                crate::pac::DCMI.icr().write(|r| r.set_err_isc(true));
403                Poll::Ready(Err(Error::PeripheralError))
404            } else if ris.ovr_ris() {
405                crate::pac::DCMI.icr().write(|r| r.set_ovr_isc(true));
406                Poll::Ready(Err(Error::Overrun))
407            } else if ris.frame_ris() {
408                crate::pac::DCMI.icr().write(|r| r.set_frame_isc(true));
409                Poll::Ready(Ok(()))
410            } else {
411                Poll::Pending
412            }
413        });
414
415        let (_, result) = embassy_futures::join::join(dma_read, result).await;
416
417        Self::toggle(false);
418
419        result
420    }
421}
422
423trait SealedInstance: crate::rcc::RccPeripheral {
424    fn regs(&self) -> crate::pac::dcmi::Dcmi;
425}
426
427/// DCMI instance.
428#[allow(private_bounds)]
429pub trait Instance: SealedInstance + PeripheralType + 'static {
430    /// Interrupt for this instance.
431    type Interrupt: interrupt::typelevel::Interrupt;
432}
433
434pin_trait!(D0Pin, Instance);
435pin_trait!(D1Pin, Instance);
436pin_trait!(D2Pin, Instance);
437pin_trait!(D3Pin, Instance);
438pin_trait!(D4Pin, Instance);
439pin_trait!(D5Pin, Instance);
440pin_trait!(D6Pin, Instance);
441pin_trait!(D7Pin, Instance);
442pin_trait!(D8Pin, Instance);
443pin_trait!(D9Pin, Instance);
444pin_trait!(D10Pin, Instance);
445pin_trait!(D11Pin, Instance);
446pin_trait!(D12Pin, Instance);
447pin_trait!(D13Pin, Instance);
448pin_trait!(HSyncPin, Instance);
449pin_trait!(VSyncPin, Instance);
450pin_trait!(PixClkPin, Instance);
451
452// allow unused as U5 sources do not contain interrupt nor dma data
453#[allow(unused)]
454macro_rules! impl_peripheral {
455    ($inst:ident, $irq:ident) => {
456        impl SealedInstance for crate::peripherals::$inst {
457            fn regs(&self) -> crate::pac::dcmi::Dcmi {
458                crate::pac::$inst
459            }
460        }
461
462        impl Instance for crate::peripherals::$inst {
463            type Interrupt = crate::interrupt::typelevel::$irq;
464        }
465    };
466}
467
468foreach_interrupt! {
469    ($inst:ident, dcmi, $block:ident, GLOBAL, $irq:ident) => {
470        impl_peripheral!($inst, $irq);
471    };
472}
473
474dma_trait!(FrameDma, Instance);