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}