esp_hal/lcd_cam/lcd/
dpi.rs

1//! # LCD - RGB/Digital Parallel Interface Mode
2//!
3//! ## Overview
4//!
5//! The LCD_CAM peripheral Dpi driver provides support for the DPI (commonly
6//! know as RGB) format/timing. The driver mandates DMA (Direct Memory Access)
7//! for efficient data transfer.
8//!
9//! ## Examples
10//!
11//! ### A display
12//!
13//! The following example shows how to setup and send a solid frame to a DPI
14//! display.
15//!
16//! ```rust, no_run
17#![doc = crate::before_snippet!()]
18//! # use esp_hal::gpio::Level;
19//! # use esp_hal::lcd_cam::{
20//! #     LcdCam,
21//! #     lcd::{
22//! #         ClockMode, Polarity, Phase,
23//! #         dpi::{Config, Dpi, Format, FrameTiming, self}
24//! #     }
25//! # };
26//! # use esp_hal::dma_loop_buffer;
27//!
28//! # let channel = peripherals.DMA_CH0;
29//! # let mut dma_buf = dma_loop_buffer!(32);
30//!
31//! let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
32//!
33//! let config = dpi::Config::default()
34//!     .with_frequency(Rate::from_mhz(1))
35//!     .with_clock_mode(ClockMode {
36//!         polarity: Polarity::IdleLow,
37//!         phase: Phase::ShiftLow,
38//!     })
39//!     .with_format(Format {
40//!         enable_2byte_mode: true,
41//!         ..Default::default()
42//!     })
43//!     .with_timing(FrameTiming {
44//!         horizontal_active_width: 480,
45//!         horizontal_total_width: 520,
46//!         horizontal_blank_front_porch: 10,
47//!
48//!         vertical_active_height: 480,
49//!         vertical_total_height: 510,
50//!         vertical_blank_front_porch: 10,
51//!
52//!         hsync_width: 10,
53//!         vsync_width: 10,
54//!
55//!         hsync_position: 0,
56//!     })
57//!     .with_vsync_idle_level(Level::High)
58//!     .with_hsync_idle_level(Level::High)
59//!     .with_de_idle_level(Level::Low)
60//!     .with_disable_black_region(false);
61//!
62//! let mut dpi = Dpi::new(lcd_cam.lcd, channel, config)?
63//!     .with_vsync(peripherals.GPIO3)
64//!     .with_hsync(peripherals.GPIO46)
65//!     .with_de(peripherals.GPIO17)
66//!     .with_pclk(peripherals.GPIO9)
67//!     // Blue
68//!     .with_data0(peripherals.GPIO10)
69//!     .with_data1(peripherals.GPIO11)
70//!     .with_data2(peripherals.GPIO12)
71//!     .with_data3(peripherals.GPIO13)
72//!     .with_data4(peripherals.GPIO14)
73//!     // Green
74//!     .with_data5(peripherals.GPIO21)
75//!     .with_data6(peripherals.GPIO8)
76//!     .with_data7(peripherals.GPIO18)
77//!     .with_data8(peripherals.GPIO45)
78//!     .with_data9(peripherals.GPIO38)
79//!     .with_data10(peripherals.GPIO39)
80//!     // Red
81//!     .with_data11(peripherals.GPIO40)
82//!     .with_data12(peripherals.GPIO41)
83//!     .with_data13(peripherals.GPIO42)
84//!     .with_data14(peripherals.GPIO2)
85//!     .with_data15(peripherals.GPIO1);
86//!
87//! let color: u16 = 0b11111_000000_00000; // RED
88//! for chunk in dma_buf.chunks_mut(2) {
89//!     chunk.copy_from_slice(&color.to_le_bytes());
90//! }
91//!
92//! let transfer = dpi.send(false, dma_buf).map_err(|e| e.0)?;
93//! transfer.wait();
94//! # Ok(())
95//! # }
96//! ```
97
98use core::{
99    marker::PhantomData,
100    mem::ManuallyDrop,
101    ops::{Deref, DerefMut},
102};
103
104use crate::{
105    Blocking,
106    DriverMode,
107    clock::Clocks,
108    dma::{ChannelTx, DmaError, DmaPeripheral, DmaTxBuffer, PeripheralTxChannel, TxChannelFor},
109    gpio::{Level, OutputConfig, OutputSignal, interconnect::PeripheralOutput},
110    lcd_cam::{
111        BitOrder,
112        ByteOrder,
113        ClockError,
114        calculate_clkm,
115        lcd::{ClockMode, DelayMode, Lcd, Phase, Polarity},
116    },
117    pac,
118    peripherals::LCD_CAM,
119    system::{self, GenericPeripheralGuard},
120    time::Rate,
121};
122
123/// Errors that can occur when configuring the DPI peripheral.
124#[derive(Debug, Clone, Copy, PartialEq)]
125#[cfg_attr(feature = "defmt", derive(defmt::Format))]
126pub enum ConfigError {
127    /// Clock configuration error.
128    Clock(ClockError),
129}
130
131/// Represents the RGB LCD interface.
132pub struct Dpi<'d, Dm: DriverMode> {
133    lcd_cam: LCD_CAM<'d>,
134    tx_channel: ChannelTx<Blocking, PeripheralTxChannel<LCD_CAM<'d>>>,
135    _guard: GenericPeripheralGuard<{ system::Peripheral::LcdCam as u8 }>,
136    _mode: PhantomData<Dm>,
137}
138
139impl<'d, Dm> Dpi<'d, Dm>
140where
141    Dm: DriverMode,
142{
143    /// Create a new instance of the RGB/DPI driver.
144    pub fn new(
145        lcd: Lcd<'d, Dm>,
146        channel: impl TxChannelFor<LCD_CAM<'d>>,
147        config: Config,
148    ) -> Result<Self, ConfigError> {
149        let tx_channel = ChannelTx::new(channel.degrade());
150
151        let mut this = Self {
152            lcd_cam: lcd.lcd_cam,
153            tx_channel,
154            _guard: lcd._guard,
155            _mode: PhantomData,
156        };
157
158        this.apply_config(&config)?;
159
160        Ok(this)
161    }
162
163    fn regs(&self) -> &pac::lcd_cam::RegisterBlock {
164        self.lcd_cam.register_block()
165    }
166
167    /// Applies the configuration to the peripheral.
168    ///
169    /// # Errors
170    ///
171    /// [`ConfigError::Clock`] variant will be returned if the frequency passed
172    /// in `Config` is too low.
173    pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
174        let clocks = Clocks::get();
175        // Due to https://www.espressif.com/sites/default/files/documentation/esp32-s3_errata_en.pdf
176        // the LCD_PCLK divider must be at least 2. To make up for this the user
177        // provided frequency is doubled to match.
178        let (i, divider) = calculate_clkm(
179            (config.frequency.as_hz() * 2) as _,
180            &[
181                clocks.xtal_clock.as_hz() as _,
182                clocks.cpu_clock.as_hz() as _,
183                clocks.crypto_pwm_clock.as_hz() as _,
184            ],
185        )
186        .map_err(ConfigError::Clock)?;
187
188        self.regs().lcd_clock().write(|w| unsafe {
189            // Force enable the clock for all configuration registers.
190            w.clk_en().set_bit();
191            w.lcd_clk_sel().bits((i + 1) as _);
192            w.lcd_clkm_div_num().bits(divider.div_num as _);
193            w.lcd_clkm_div_b().bits(divider.div_b as _);
194            w.lcd_clkm_div_a().bits(divider.div_a as _); // LCD_PCLK = LCD_CLK / 2
195            w.lcd_clk_equ_sysclk().clear_bit();
196            w.lcd_clkcnt_n().bits(2 - 1); // Must not be 0.
197            w.lcd_ck_idle_edge()
198                .bit(config.clock_mode.polarity == Polarity::IdleHigh);
199            w.lcd_ck_out_edge()
200                .bit(config.clock_mode.phase == Phase::ShiftHigh)
201        });
202        self.regs()
203            .lcd_user()
204            .modify(|_, w| w.lcd_reset().set_bit());
205
206        self.regs()
207            .lcd_rgb_yuv()
208            .write(|w| w.lcd_conv_bypass().clear_bit());
209
210        self.regs().lcd_user().modify(|_, w| {
211            if config.format.enable_2byte_mode {
212                w.lcd_8bits_order().bit(false);
213                w.lcd_byte_order()
214                    .bit(config.format.byte_order == ByteOrder::Inverted);
215            } else {
216                w.lcd_8bits_order()
217                    .bit(config.format.byte_order == ByteOrder::Inverted);
218                w.lcd_byte_order().bit(false);
219            }
220            w.lcd_bit_order()
221                .bit(config.format.bit_order == BitOrder::Inverted);
222            w.lcd_2byte_en().bit(config.format.enable_2byte_mode);
223
224            // Only valid in Intel8080 mode.
225            w.lcd_cmd().clear_bit();
226            w.lcd_dummy().clear_bit();
227
228            // This needs to be explicitly set for RGB mode.
229            w.lcd_dout().set_bit()
230        });
231
232        let timing = &config.timing;
233        self.regs().lcd_ctrl().modify(|_, w| unsafe {
234            // Enable RGB mode, and input VSYNC, HSYNC, and DE signals.
235            w.lcd_rgb_mode_en().set_bit();
236
237            w.lcd_hb_front()
238                .bits((timing.horizontal_blank_front_porch as u16).saturating_sub(1));
239            w.lcd_va_height()
240                .bits((timing.vertical_active_height as u16).saturating_sub(1));
241            w.lcd_vt_height()
242                .bits((timing.vertical_total_height as u16).saturating_sub(1))
243        });
244        self.regs().lcd_ctrl1().modify(|_, w| unsafe {
245            w.lcd_vb_front()
246                .bits((timing.vertical_blank_front_porch as u8).saturating_sub(1));
247            w.lcd_ha_width()
248                .bits((timing.horizontal_active_width as u16).saturating_sub(1));
249            w.lcd_ht_width()
250                .bits((timing.horizontal_total_width as u16).saturating_sub(1))
251        });
252        self.regs().lcd_ctrl2().modify(|_, w| unsafe {
253            w.lcd_vsync_width()
254                .bits((timing.vsync_width as u8).saturating_sub(1));
255            w.lcd_vsync_idle_pol().bit(config.vsync_idle_level.into());
256            w.lcd_de_idle_pol().bit(config.de_idle_level.into());
257            w.lcd_hs_blank_en().bit(config.hs_blank_en);
258            w.lcd_hsync_width()
259                .bits((timing.hsync_width as u8).saturating_sub(1));
260            w.lcd_hsync_idle_pol().bit(config.hsync_idle_level.into());
261            w.lcd_hsync_position().bits(timing.hsync_position as u8)
262        });
263
264        self.regs().lcd_misc().modify(|_, w| unsafe {
265            // TODO: Find out what this field actually does.
266            // Set the threshold for Async Tx FIFO full event. (5 bits)
267            w.lcd_afifo_threshold_num().bits((1 << 5) - 1);
268
269            // Doesn't matter for RGB mode.
270            w.lcd_vfk_cyclelen().bits(0);
271            w.lcd_vbk_cyclelen().bits(0);
272
273            // 1: Send the next frame data when the current frame is sent out.
274            // 0: LCD stops when the current frame is sent out.
275            w.lcd_next_frame_en().clear_bit();
276
277            // Enable blank region when LCD sends data out.
278            w.lcd_bk_en().bit(!config.disable_black_region)
279        });
280        self.regs().lcd_dly_mode().modify(|_, w| unsafe {
281            w.lcd_de_mode().bits(config.de_mode as u8);
282            w.lcd_hsync_mode().bits(config.hsync_mode as u8);
283            w.lcd_vsync_mode().bits(config.vsync_mode as u8);
284            w
285        });
286        self.regs().lcd_data_dout_mode().modify(|_, w| unsafe {
287            w.dout0_mode().bits(config.output_bit_mode as u8);
288            w.dout1_mode().bits(config.output_bit_mode as u8);
289            w.dout2_mode().bits(config.output_bit_mode as u8);
290            w.dout3_mode().bits(config.output_bit_mode as u8);
291            w.dout4_mode().bits(config.output_bit_mode as u8);
292            w.dout5_mode().bits(config.output_bit_mode as u8);
293            w.dout6_mode().bits(config.output_bit_mode as u8);
294            w.dout7_mode().bits(config.output_bit_mode as u8);
295            w.dout8_mode().bits(config.output_bit_mode as u8);
296            w.dout9_mode().bits(config.output_bit_mode as u8);
297            w.dout10_mode().bits(config.output_bit_mode as u8);
298            w.dout11_mode().bits(config.output_bit_mode as u8);
299            w.dout12_mode().bits(config.output_bit_mode as u8);
300            w.dout13_mode().bits(config.output_bit_mode as u8);
301            w.dout14_mode().bits(config.output_bit_mode as u8);
302            w.dout15_mode().bits(config.output_bit_mode as u8)
303        });
304
305        self.regs()
306            .lcd_user()
307            .modify(|_, w| w.lcd_update().set_bit());
308
309        Ok(())
310    }
311
312    /// Assign the VSYNC pin for the LCD_CAM.
313    ///
314    /// Sets the specified pin to push-pull output and connects it to the VSYNC
315    /// signal.
316    pub fn with_vsync(self, pin: impl PeripheralOutput<'d>) -> Self {
317        let pin = pin.into();
318        pin.apply_output_config(&OutputConfig::default());
319        pin.set_output_enable(true);
320        OutputSignal::LCD_V_SYNC.connect_to(&pin);
321
322        self
323    }
324
325    /// Assign the HSYNC pin for the LCD_CAM.
326    ///
327    /// Sets the specified pin to push-pull output and connects it to the HSYNC
328    /// signal.
329    pub fn with_hsync(self, pin: impl PeripheralOutput<'d>) -> Self {
330        let pin = pin.into();
331        pin.apply_output_config(&OutputConfig::default());
332        pin.set_output_enable(true);
333        OutputSignal::LCD_H_SYNC.connect_to(&pin);
334
335        self
336    }
337
338    /// Assign the DE pin for the LCD_CAM.
339    ///
340    /// Sets the specified pin to push-pull output and connects it to the DE
341    /// signal.
342    pub fn with_de(self, pin: impl PeripheralOutput<'d>) -> Self {
343        let pin = pin.into();
344        pin.apply_output_config(&OutputConfig::default());
345        pin.set_output_enable(true);
346        OutputSignal::LCD_H_ENABLE.connect_to(&pin);
347
348        self
349    }
350
351    /// Assign the PCLK pin for the LCD_CAM.
352    ///
353    /// Sets the specified pin to push-pull output and connects it to the PCLK
354    /// signal.
355    pub fn with_pclk(self, pin: impl PeripheralOutput<'d>) -> Self {
356        let pin = pin.into();
357        pin.apply_output_config(&OutputConfig::default());
358        pin.set_output_enable(true);
359        OutputSignal::LCD_PCLK.connect_to(&pin);
360
361        self
362    }
363
364    fn with_data_pin(self, signal: OutputSignal, pin: impl PeripheralOutput<'d>) -> Self {
365        let pin = pin.into();
366
367        pin.apply_output_config(&OutputConfig::default());
368        pin.set_output_enable(true);
369        signal.connect_to(&pin);
370
371        self
372    }
373
374    /// Assign the DATA_0 pin for the LCD_CAM.
375    ///
376    /// Sets the specified pin to push-pull output and connects it to the DATA_0
377    /// signal.
378    pub fn with_data0(self, pin: impl PeripheralOutput<'d>) -> Self {
379        self.with_data_pin(OutputSignal::LCD_DATA_0, pin)
380    }
381
382    /// Assign the DATA_1 pin for the LCD_CAM.
383    ///
384    /// Sets the specified pin to push-pull output and connects it to the DATA_1
385    /// signal.
386    pub fn with_data1(self, pin: impl PeripheralOutput<'d>) -> Self {
387        self.with_data_pin(OutputSignal::LCD_DATA_1, pin)
388    }
389
390    /// Assign the DATA_2 pin for the LCD_CAM.
391    ///
392    /// Sets the specified pin to push-pull output and connects it to the DATA_2
393    /// signal.
394    pub fn with_data2(self, pin: impl PeripheralOutput<'d>) -> Self {
395        self.with_data_pin(OutputSignal::LCD_DATA_2, pin)
396    }
397
398    /// Assign the DATA_3 pin for the LCD_CAM.
399    ///
400    /// Sets the specified pin to push-pull output and connects it to the DATA_3
401    /// signal.
402    pub fn with_data3(self, pin: impl PeripheralOutput<'d>) -> Self {
403        self.with_data_pin(OutputSignal::LCD_DATA_3, pin)
404    }
405
406    /// Assign the DATA_4 pin for the LCD_CAM.
407    ///
408    /// Sets the specified pin to push-pull output and connects it to the DATA_4
409    /// signal.
410    pub fn with_data4(self, pin: impl PeripheralOutput<'d>) -> Self {
411        self.with_data_pin(OutputSignal::LCD_DATA_4, pin)
412    }
413
414    /// Assign the DATA_5 pin for the LCD_CAM.
415    ///
416    /// Sets the specified pin to push-pull output and connects it to the DATA_5
417    /// signal.
418    pub fn with_data5(self, pin: impl PeripheralOutput<'d>) -> Self {
419        self.with_data_pin(OutputSignal::LCD_DATA_5, pin)
420    }
421
422    /// Assign the DATA_6 pin for the LCD_CAM.
423    ///
424    /// Sets the specified pin to push-pull output and connects it to the DATA_6
425    /// signal.
426    pub fn with_data6(self, pin: impl PeripheralOutput<'d>) -> Self {
427        self.with_data_pin(OutputSignal::LCD_DATA_6, pin)
428    }
429
430    /// Assign the DATA_7 pin for the LCD_CAM.
431    ///
432    /// Sets the specified pin to push-pull output and connects it to the DATA_7
433    /// signal.
434    pub fn with_data7(self, pin: impl PeripheralOutput<'d>) -> Self {
435        self.with_data_pin(OutputSignal::LCD_DATA_7, pin)
436    }
437
438    /// Assign the DATA_8 pin for the LCD_CAM.
439    ///
440    /// Sets the specified pin to push-pull output and connects it to the DATA_8
441    /// signal.
442    pub fn with_data8(self, pin: impl PeripheralOutput<'d>) -> Self {
443        self.with_data_pin(OutputSignal::LCD_DATA_8, pin)
444    }
445
446    /// Assign the DATA_9 pin for the LCD_CAM.
447    ///
448    /// Sets the specified pin to push-pull output and connects it to the DATA_9
449    /// signal.
450    pub fn with_data9(self, pin: impl PeripheralOutput<'d>) -> Self {
451        self.with_data_pin(OutputSignal::LCD_DATA_9, pin)
452    }
453
454    /// Assign the DATA_10 pin for the LCD_CAM.
455    ///
456    /// Sets the specified pin to push-pull output and connects it to the
457    /// DATA_10 signal.
458    pub fn with_data10(self, pin: impl PeripheralOutput<'d>) -> Self {
459        self.with_data_pin(OutputSignal::LCD_DATA_10, pin)
460    }
461
462    /// Assign the DATA_11 pin for the LCD_CAM.
463    ///
464    /// Sets the specified pin to push-pull output and connects it to the
465    /// DATA_11 signal.
466    pub fn with_data11(self, pin: impl PeripheralOutput<'d>) -> Self {
467        self.with_data_pin(OutputSignal::LCD_DATA_11, pin)
468    }
469
470    /// Assign the DATA_12 pin for the LCD_CAM.
471    ///
472    /// Sets the specified pin to push-pull output and connects it to the
473    /// DATA_12 signal.
474    pub fn with_data12(self, pin: impl PeripheralOutput<'d>) -> Self {
475        self.with_data_pin(OutputSignal::LCD_DATA_12, pin)
476    }
477
478    /// Assign the DATA_13 pin for the LCD_CAM.
479    ///
480    /// Sets the specified pin to push-pull output and connects it to the
481    /// DATA_13 signal.
482    pub fn with_data13(self, pin: impl PeripheralOutput<'d>) -> Self {
483        self.with_data_pin(OutputSignal::LCD_DATA_13, pin)
484    }
485
486    /// Assign the DATA_14 pin for the LCD_CAM.
487    ///
488    /// Sets the specified pin to push-pull output and connects it to the
489    /// DATA_14 signal.
490    pub fn with_data14(self, pin: impl PeripheralOutput<'d>) -> Self {
491        self.with_data_pin(OutputSignal::LCD_DATA_14, pin)
492    }
493
494    /// Assign the DATA_15 pin for the LCD_CAM.
495    ///
496    /// Sets the specified pin to push-pull output and connects it to the
497    /// DATA_15 signal.
498    pub fn with_data15(self, pin: impl PeripheralOutput<'d>) -> Self {
499        self.with_data_pin(OutputSignal::LCD_DATA_15, pin)
500    }
501
502    /// Sending out the [DmaTxBuffer] to the RGB/DPI interface.
503    ///
504    /// - `next_frame_en`: Automatically send the next frame data when the
505    ///   current frame is sent out.
506    pub fn send<TX: DmaTxBuffer>(
507        mut self,
508        next_frame_en: bool,
509        mut buf: TX,
510    ) -> Result<DpiTransfer<'d, TX, Dm>, (DmaError, Self, TX)> {
511        let result = unsafe {
512            self.tx_channel
513                .prepare_transfer(DmaPeripheral::LcdCam, &mut buf)
514        }
515        .and_then(|_| self.tx_channel.start_transfer());
516        if let Err(err) = result {
517            return Err((err, self, buf));
518        }
519
520        // Reset LCD control unit and Async Tx FIFO
521        self.regs()
522            .lcd_user()
523            .modify(|_, w| w.lcd_reset().set_bit());
524        self.regs()
525            .lcd_misc()
526            .modify(|_, w| w.lcd_afifo_reset().set_bit());
527
528        self.regs().lcd_misc().modify(|_, w| {
529            // 1: Send the next frame data when the current frame is sent out.
530            // 0: LCD stops when the current frame is sent out.
531            w.lcd_next_frame_en().bit(next_frame_en)
532        });
533
534        // Start the transfer.
535        self.regs().lcd_user().modify(|_, w| {
536            w.lcd_update().set_bit();
537            w.lcd_start().set_bit()
538        });
539
540        Ok(DpiTransfer {
541            dpi: ManuallyDrop::new(self),
542            buffer_view: ManuallyDrop::new(buf.into_view()),
543        })
544    }
545}
546
547/// Represents an ongoing (or potentially finished) transfer using the RGB LCD
548/// interface
549pub struct DpiTransfer<'d, BUF: DmaTxBuffer, Dm: DriverMode> {
550    dpi: ManuallyDrop<Dpi<'d, Dm>>,
551    buffer_view: ManuallyDrop<BUF::View>,
552}
553
554impl<'d, BUF: DmaTxBuffer, Dm: DriverMode> DpiTransfer<'d, BUF, Dm> {
555    /// Returns true when [Self::wait] will not block.
556    pub fn is_done(&self) -> bool {
557        self.dpi.regs().lcd_user().read().lcd_start().bit_is_clear()
558    }
559
560    /// Stops this transfer on the spot and returns the peripheral and buffer.
561    pub fn stop(mut self) -> (Dpi<'d, Dm>, BUF) {
562        self.stop_peripherals();
563        let (dpi, view) = self.release();
564        (dpi, BUF::from_view(view))
565    }
566
567    /// Waits for the transfer to finish and returns the peripheral and buffer.
568    ///
569    /// Note: If you specified `next_frame_en` as true in [Dpi::send], you're
570    /// just waiting for a DMA error when you call this.
571    pub fn wait(mut self) -> (Result<(), DmaError>, Dpi<'d, Dm>, BUF) {
572        while !self.is_done() {
573            core::hint::spin_loop();
574        }
575
576        // Stop the DMA.
577        //
578        // If the user sends more data to the DMA than the LCD_CAM needs for a single
579        // frame, the DMA will still be running after the LCD_CAM stops.
580        self.dpi.tx_channel.stop_transfer();
581
582        // Note: There is no "done" interrupt to clear.
583
584        let (dpi, view) = self.release();
585        let result = if dpi.tx_channel.has_error() {
586            Err(DmaError::DescriptorError)
587        } else {
588            Ok(())
589        };
590
591        (result, dpi, BUF::from_view(view))
592    }
593
594    fn release(mut self) -> (Dpi<'d, Dm>, BUF::View) {
595        // SAFETY: Since forget is called on self, we know that self.dpi and
596        // self.buffer_view won't be touched again.
597        let result = unsafe {
598            let dpi = ManuallyDrop::take(&mut self.dpi);
599            let view = ManuallyDrop::take(&mut self.buffer_view);
600            (dpi, view)
601        };
602        core::mem::forget(self);
603        result
604    }
605
606    fn stop_peripherals(&mut self) {
607        // Stop the LCD_CAM peripheral.
608        self.dpi
609            .regs()
610            .lcd_user()
611            .modify(|_, w| w.lcd_start().clear_bit());
612
613        // Stop the DMA
614        self.dpi.tx_channel.stop_transfer();
615    }
616}
617
618impl<BUF: DmaTxBuffer, Dm: DriverMode> Deref for DpiTransfer<'_, BUF, Dm> {
619    type Target = BUF::View;
620
621    fn deref(&self) -> &Self::Target {
622        &self.buffer_view
623    }
624}
625
626impl<BUF: DmaTxBuffer, Dm: DriverMode> DerefMut for DpiTransfer<'_, BUF, Dm> {
627    fn deref_mut(&mut self) -> &mut Self::Target {
628        &mut self.buffer_view
629    }
630}
631
632impl<BUF: DmaTxBuffer, Dm: DriverMode> Drop for DpiTransfer<'_, BUF, Dm> {
633    fn drop(&mut self) {
634        self.stop_peripherals();
635
636        // SAFETY: This is Drop, we know that self.dpi and self.buf_view
637        // won't be touched again.
638        let view = unsafe {
639            ManuallyDrop::drop(&mut self.dpi);
640            ManuallyDrop::take(&mut self.buffer_view)
641        };
642        let _ = BUF::from_view(view);
643    }
644}
645
646/// Configuration settings for the RGB/DPI interface.
647#[non_exhaustive]
648#[derive(Debug, Clone, Copy, PartialEq, procmacros::BuilderLite)]
649#[cfg_attr(feature = "defmt", derive(defmt::Format))]
650pub struct Config {
651    /// Specifies the clock mode, including polarity and phase settings.
652    clock_mode: ClockMode,
653
654    /// The frequency of the pixel clock.
655    frequency: Rate,
656
657    /// Format of the byte data sent out.
658    format: Format,
659
660    /// Timing settings for the peripheral.
661    timing: FrameTiming,
662
663    /// The vsync signal level in IDLE state.
664    vsync_idle_level: Level,
665
666    /// The hsync signal level in IDLE state.
667    hsync_idle_level: Level,
668
669    /// The de signal level in IDLE state.
670    de_idle_level: Level,
671
672    /// If enabled, the hsync pulse will be sent out in vertical blanking lines.
673    /// i.e. When no valid data is actually sent out. Otherwise, hysnc
674    /// pulses will only be sent out in active region lines.
675    hs_blank_en: bool,
676
677    /// Disables blank region when LCD sends data out.
678    disable_black_region: bool,
679
680    /// The output LCD_DE is delayed by module clock LCD_CLK.
681    de_mode: DelayMode,
682    /// The output LCD_HSYNC is delayed by module clock LCD_CLK.
683    hsync_mode: DelayMode,
684    /// The output LCD_VSYNC is delayed by module clock LCD_CLK.
685    vsync_mode: DelayMode,
686    /// The output data bits are delayed by module clock LCD_CLK.
687    output_bit_mode: DelayMode,
688}
689
690impl Default for Config {
691    fn default() -> Self {
692        Config {
693            clock_mode: Default::default(),
694            frequency: Rate::from_mhz(1),
695            format: Default::default(),
696            timing: Default::default(),
697            vsync_idle_level: Level::Low,
698            hsync_idle_level: Level::Low,
699            de_idle_level: Level::Low,
700            hs_blank_en: true,
701            disable_black_region: false,
702            de_mode: Default::default(),
703            hsync_mode: Default::default(),
704            vsync_mode: Default::default(),
705            output_bit_mode: Default::default(),
706        }
707    }
708}
709
710/// Controls how the peripheral should treat data received from the DMA.
711#[derive(Debug, Clone, Copy, PartialEq, Default)]
712#[cfg_attr(feature = "defmt", derive(defmt::Format))]
713pub struct Format {
714    /// Configures the bit order for data transmission.
715    pub bit_order: BitOrder,
716
717    /// Configures the byte order for data transmission.
718    ///
719    /// - In 8-bit mode, [ByteOrder::Inverted] means every two bytes are
720    ///   swapped.
721    /// - In 16-bit mode, this controls the byte order (endianness).
722    pub byte_order: ByteOrder,
723
724    /// If true, the width of the output is 16 bits.
725    /// Otherwise, the width of the output is 8 bits.
726    pub enable_2byte_mode: bool,
727}
728
729/// The timing numbers for the driver to follow.
730///
731/// Note: The names of the fields in this struct don't match what you
732/// would typically find in an LCD's datasheet. Carefully read the doc on each
733/// field to understand what to set it to.
734#[derive(Debug, Clone, Copy, PartialEq, Default)]
735#[cfg_attr(feature = "defmt", derive(defmt::Format))]
736pub struct FrameTiming {
737    /// The horizontal total width of a frame (in units of PCLK).
738    ///
739    /// This should be greater than `horizontal_blank_front_porch` +
740    /// `horizontal_active_width`.
741    ///
742    /// Max is 4096 (12 bits).
743    pub horizontal_total_width: usize,
744
745    /// The horizontal blank front porch of a frame (in units of PCLK).
746    ///
747    /// This is the number of PCLKs between the start of the line and the start
748    /// of active data in the line.
749    ///
750    /// Note: This includes `hsync_width`.
751    ///
752    /// Max is 2048 (11 bits).
753    pub horizontal_blank_front_porch: usize,
754
755    /// The horizontal active width of a frame. i.e. The number of pixels in a
756    /// line. This is typically the horizontal resolution of the screen.
757    ///
758    /// Max is 4096 (12 bits).
759    pub horizontal_active_width: usize,
760
761    /// The vertical total height of a frame (in units of lines).
762    ///
763    /// This should be greater than `vertical_blank_front_porch` +
764    /// `vertical_active_height`.
765    ///
766    /// Max is 1024 (10 bits).
767    pub vertical_total_height: usize,
768
769    /// The vertical blank front porch height of a frame (in units of lines).
770    ///
771    /// This is the number of (blank/invalid) lines before the start of the
772    /// frame.
773    ///
774    /// Note: This includes `vsync_width`.
775    ///
776    /// Max is 256 (8 bits).
777    pub vertical_blank_front_porch: usize,
778
779    /// The vertical active height of a frame. i.e. The number of lines in a
780    /// frame. This is typically the vertical resolution of the screen.
781    ///
782    /// Max is 1024 (10 bits).
783    pub vertical_active_height: usize,
784
785    /// It is the width of LCD_VSYNC active pulse in a line (in units of lines).
786    ///
787    /// Max is 128 (7 bits).
788    pub vsync_width: usize,
789
790    /// The width of LCD_HSYNC active pulse in a line (in units of PCLK).
791    ///
792    /// This should be less than vertical_blank_front_porch, otherwise the hsync
793    /// pulse will overlap with valid pixel data.
794    ///
795    /// Max is 128 (7 bits).
796    pub hsync_width: usize,
797
798    /// It is the position of LCD_HSYNC active pulse in a line (in units of
799    /// PCLK).
800    ///
801    /// This should be less than horizontal_total_width.
802    ///
803    /// Max is 128 (7 bits).
804    pub hsync_position: usize,
805}