1use core::future::poll_fn;
6use core::marker::PhantomData;
7use core::task::Poll;
8
9use embassy_hal_internal::PeripheralType;
10use embassy_sync::waitqueue::AtomicWaker;
11use stm32_metapac::ltdc::regs::Dccr;
12use stm32_metapac::ltdc::vals::{Bf1, Bf2, Cfuif, Clif, Crrif, Cterrif, Pf, Vbr};
13
14use crate::gpio::{AfType, OutputType, Speed};
15use crate::interrupt::typelevel::Interrupt;
16use crate::interrupt::{self};
17use crate::{peripherals, rcc, Peri};
18
19static LTDC_WAKER: AtomicWaker = AtomicWaker::new();
20
21#[derive(Debug, PartialEq, Eq, Clone, Copy)]
23#[cfg_attr(feature = "defmt", derive(defmt::Format))]
24pub enum Error {
25 FifoUnderrun,
27 TransferError,
29}
30
31#[derive(Clone, Copy, Debug, PartialEq)]
33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34pub struct LtdcConfiguration {
35 pub active_width: u16,
37 pub active_height: u16,
39
40 pub h_back_porch: u16,
42 pub h_front_porch: u16,
44 pub v_back_porch: u16,
46 pub v_front_porch: u16,
48
49 pub h_sync: u16,
51 pub v_sync: u16,
53
54 pub h_sync_polarity: PolarityActive,
56 pub v_sync_polarity: PolarityActive,
58 pub data_enable_polarity: PolarityActive,
60 pub pixel_clock_polarity: PolarityEdge,
62}
63
64#[derive(Clone, Copy, Debug, PartialEq)]
66#[cfg_attr(feature = "defmt", derive(defmt::Format))]
67pub enum PolarityEdge {
68 FallingEdge,
70 RisingEdge,
72}
73
74#[derive(Clone, Copy, Debug, PartialEq)]
76#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77pub enum PolarityActive {
78 ActiveLow,
80 ActiveHigh,
82}
83
84pub struct Ltdc<'d, T: Instance> {
86 _peri: Peri<'d, T>,
87}
88
89pub struct InterruptHandler<T: Instance> {
91 _phantom: PhantomData<T>,
92}
93
94#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
96#[cfg_attr(feature = "defmt", derive(defmt::Format))]
97pub struct RgbColor {
98 pub red: u8,
100 pub green: u8,
102 pub blue: u8,
104}
105
106#[derive(Debug, PartialEq, Eq, Clone, Copy)]
108#[cfg_attr(feature = "defmt", derive(defmt::Format))]
109pub struct LtdcLayerConfig {
110 pub layer: LtdcLayer,
112 pub pixel_format: PixelFormat,
114 pub window_x0: u16,
116 pub window_x1: u16,
118 pub window_y0: u16,
120 pub window_y1: u16,
122}
123
124#[repr(u8)]
126#[derive(Debug, PartialEq, Eq, Clone, Copy)]
127#[cfg_attr(feature = "defmt", derive(defmt::Format))]
128pub enum PixelFormat {
129 ARGB8888 = Pf::ARGB8888 as u8,
131 RGB888 = Pf::RGB888 as u8,
133 RGB565 = Pf::RGB565 as u8,
135 ARGB1555 = Pf::ARGB1555 as u8,
137 ARGB4444 = Pf::ARGB4444 as u8,
139 L8 = Pf::L8 as u8,
141 AL44 = Pf::AL44 as u8,
143 AL88 = Pf::AL88 as u8,
145}
146
147impl PixelFormat {
148 pub fn bytes_per_pixel(&self) -> usize {
150 match self {
151 PixelFormat::ARGB8888 => 4,
152 PixelFormat::RGB888 => 3,
153 PixelFormat::RGB565 | PixelFormat::ARGB4444 | PixelFormat::ARGB1555 | PixelFormat::AL88 => 2,
154 PixelFormat::AL44 | PixelFormat::L8 => 1,
155 }
156 }
157}
158
159#[repr(usize)]
161#[derive(Debug, PartialEq, Eq, Clone, Copy)]
162#[cfg_attr(feature = "defmt", derive(defmt::Format))]
163pub enum LtdcLayer {
164 Layer1 = 0,
166 Layer2 = 1,
168}
169
170impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
171 unsafe fn on_interrupt() {
172 cortex_m::asm::dsb();
173 Ltdc::<T>::enable_interrupts(false);
174 LTDC_WAKER.wake();
175 }
176}
177
178impl<'d, T: Instance> Ltdc<'d, T> {
179 pub fn new(peri: Peri<'d, T>) -> Self {
182 Self::setup_clocks();
183 Self { _peri: peri }
184 }
185
186 #[allow(clippy::too_many_arguments)]
188 pub fn new_with_pins(
189 peri: Peri<'d, T>,
190 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
191 clk: Peri<'d, impl ClkPin<T>>,
192 hsync: Peri<'d, impl HsyncPin<T>>,
193 vsync: Peri<'d, impl VsyncPin<T>>,
194 b0: Peri<'d, impl B0Pin<T>>,
195 b1: Peri<'d, impl B1Pin<T>>,
196 b2: Peri<'d, impl B2Pin<T>>,
197 b3: Peri<'d, impl B3Pin<T>>,
198 b4: Peri<'d, impl B4Pin<T>>,
199 b5: Peri<'d, impl B5Pin<T>>,
200 b6: Peri<'d, impl B6Pin<T>>,
201 b7: Peri<'d, impl B7Pin<T>>,
202 g0: Peri<'d, impl G0Pin<T>>,
203 g1: Peri<'d, impl G1Pin<T>>,
204 g2: Peri<'d, impl G2Pin<T>>,
205 g3: Peri<'d, impl G3Pin<T>>,
206 g4: Peri<'d, impl G4Pin<T>>,
207 g5: Peri<'d, impl G5Pin<T>>,
208 g6: Peri<'d, impl G6Pin<T>>,
209 g7: Peri<'d, impl G7Pin<T>>,
210 r0: Peri<'d, impl R0Pin<T>>,
211 r1: Peri<'d, impl R1Pin<T>>,
212 r2: Peri<'d, impl R2Pin<T>>,
213 r3: Peri<'d, impl R3Pin<T>>,
214 r4: Peri<'d, impl R4Pin<T>>,
215 r5: Peri<'d, impl R5Pin<T>>,
216 r6: Peri<'d, impl R6Pin<T>>,
217 r7: Peri<'d, impl R7Pin<T>>,
218 ) -> Self {
219 Self::setup_clocks();
220 new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh));
221 new_pin!(hsync, AfType::output(OutputType::PushPull, Speed::VeryHigh));
222 new_pin!(vsync, AfType::output(OutputType::PushPull, Speed::VeryHigh));
223 new_pin!(b0, AfType::output(OutputType::PushPull, Speed::VeryHigh));
224 new_pin!(b1, AfType::output(OutputType::PushPull, Speed::VeryHigh));
225 new_pin!(b2, AfType::output(OutputType::PushPull, Speed::VeryHigh));
226 new_pin!(b3, AfType::output(OutputType::PushPull, Speed::VeryHigh));
227 new_pin!(b4, AfType::output(OutputType::PushPull, Speed::VeryHigh));
228 new_pin!(b5, AfType::output(OutputType::PushPull, Speed::VeryHigh));
229 new_pin!(b6, AfType::output(OutputType::PushPull, Speed::VeryHigh));
230 new_pin!(b7, AfType::output(OutputType::PushPull, Speed::VeryHigh));
231 new_pin!(g0, AfType::output(OutputType::PushPull, Speed::VeryHigh));
232 new_pin!(g1, AfType::output(OutputType::PushPull, Speed::VeryHigh));
233 new_pin!(g2, AfType::output(OutputType::PushPull, Speed::VeryHigh));
234 new_pin!(g3, AfType::output(OutputType::PushPull, Speed::VeryHigh));
235 new_pin!(g4, AfType::output(OutputType::PushPull, Speed::VeryHigh));
236 new_pin!(g5, AfType::output(OutputType::PushPull, Speed::VeryHigh));
237 new_pin!(g6, AfType::output(OutputType::PushPull, Speed::VeryHigh));
238 new_pin!(g7, AfType::output(OutputType::PushPull, Speed::VeryHigh));
239 new_pin!(r0, AfType::output(OutputType::PushPull, Speed::VeryHigh));
240 new_pin!(r1, AfType::output(OutputType::PushPull, Speed::VeryHigh));
241 new_pin!(r2, AfType::output(OutputType::PushPull, Speed::VeryHigh));
242 new_pin!(r3, AfType::output(OutputType::PushPull, Speed::VeryHigh));
243 new_pin!(r4, AfType::output(OutputType::PushPull, Speed::VeryHigh));
244 new_pin!(r5, AfType::output(OutputType::PushPull, Speed::VeryHigh));
245 new_pin!(r6, AfType::output(OutputType::PushPull, Speed::VeryHigh));
246 new_pin!(r7, AfType::output(OutputType::PushPull, Speed::VeryHigh));
247
248 Self { _peri: peri }
249 }
250
251 pub fn init(&mut self, config: &LtdcConfiguration) {
253 use stm32_metapac::ltdc::vals::{Depol, Hspol, Pcpol, Vspol};
254 let ltdc = T::regs();
255
256 assert!(ltdc.gcr().read().0 == 0x2220); ltdc.gcr().modify(|w| {
261 w.set_hspol(match config.h_sync_polarity {
262 PolarityActive::ActiveHigh => Hspol::ACTIVE_HIGH,
263 PolarityActive::ActiveLow => Hspol::ACTIVE_LOW,
264 });
265
266 w.set_vspol(match config.v_sync_polarity {
267 PolarityActive::ActiveHigh => Vspol::ACTIVE_HIGH,
268 PolarityActive::ActiveLow => Vspol::ACTIVE_LOW,
269 });
270
271 w.set_depol(match config.data_enable_polarity {
272 PolarityActive::ActiveHigh => Depol::ACTIVE_HIGH,
273 PolarityActive::ActiveLow => Depol::ACTIVE_LOW,
274 });
275
276 w.set_pcpol(match config.pixel_clock_polarity {
277 PolarityEdge::RisingEdge => Pcpol::RISING_EDGE,
278 PolarityEdge::FallingEdge => Pcpol::FALLING_EDGE,
279 });
280 });
281
282 ltdc.sscr().modify(|w| {
284 w.set_vsh(config.v_sync - 1);
285 w.set_hsw(config.h_sync - 1);
286 });
287
288 ltdc.bpcr().modify(|w| {
290 w.set_avbp(config.v_sync + config.v_back_porch - 1);
291 w.set_ahbp(config.h_sync + config.h_back_porch - 1);
292 });
293
294 let aa_height = config.v_sync + config.v_back_porch + config.active_height - 1;
296 let aa_width = config.h_sync + config.h_back_porch + config.active_width - 1;
297 ltdc.awcr().modify(|w| {
298 w.set_aah(aa_height);
299 w.set_aaw(aa_width);
300 });
301
302 let total_height: u16 = config.v_sync + config.v_back_porch + config.active_height + config.v_front_porch - 1;
304 let total_width: u16 = config.h_sync + config.h_back_porch + config.active_width + config.h_front_porch - 1;
305 ltdc.twcr().modify(|w| {
306 w.set_totalh(total_height);
307 w.set_totalw(total_width)
308 });
309
310 ltdc.bccr().modify(|w| {
312 w.set_bcred(0);
313 w.set_bcgreen(0);
314 w.set_bcblue(0);
315 });
316
317 self.enable();
318 }
319
320 pub fn enable(&mut self) {
324 T::regs().gcr().modify(|w| w.set_ltdcen(true));
325 assert!(T::regs().gcr().read().ltdcen())
326 }
327
328 pub fn disable(&mut self) {
330 T::regs().gcr().modify(|w| w.set_ltdcen(false));
331 assert!(!T::regs().gcr().read().ltdcen())
332 }
333
334 pub fn init_layer(&mut self, layer_config: &LtdcLayerConfig, clut: Option<&[RgbColor]>) {
338 let ltdc = T::regs();
339 let layer = ltdc.layer(layer_config.layer as usize);
340
341 if let Some(clut) = clut {
343 assert_eq!(clut.len(), 256, "Color lookup table must be exactly 256 in length");
344 for (index, color) in clut.iter().enumerate() {
345 layer.clutwr().write(|w| {
346 w.set_clutadd(index as u8);
347 w.set_red(color.red);
348 w.set_green(color.green);
349 w.set_blue(color.blue);
350 });
351 }
352 }
353
354 let h_win_start = layer_config.window_x0 + ltdc.bpcr().read().ahbp() + 1;
356 let h_win_stop = layer_config.window_x1 + ltdc.bpcr().read().ahbp();
357 layer.whpcr().write(|w| {
358 w.set_whstpos(h_win_start);
359 w.set_whsppos(h_win_stop);
360 });
361
362 let v_win_start = layer_config.window_y0 + ltdc.bpcr().read().avbp() + 1;
364 let v_win_stop = layer_config.window_y1 + ltdc.bpcr().read().avbp();
365 layer.wvpcr().write(|w| {
366 w.set_wvstpos(v_win_start);
367 w.set_wvsppos(v_win_stop)
368 });
369
370 layer
372 .pfcr()
373 .write(|w| w.set_pf(Pf::from_bits(layer_config.pixel_format as u8)));
374
375 layer.dccr().write_value(Dccr::default());
377
378 let alpha = 0xFF;
380 layer.cacr().write(|w| w.set_consta(alpha));
381
382 layer.bfcr().modify(|w| {
384 w.set_bf1(Bf1::PIXEL);
385 w.set_bf2(Bf2::PIXEL);
386 });
387
388 let bytes_per_pixel = layer_config.pixel_format.bytes_per_pixel() as u16;
390 let width = layer_config.window_x1 - layer_config.window_x0;
391 let height = layer_config.window_y1 - layer_config.window_y0;
392
393 layer.cfblr().modify(|w| {
395 w.set_cfbp(width * bytes_per_pixel);
396 #[cfg(not(stm32u5))]
397 w.set_cfbll(width * bytes_per_pixel + 7);
398 #[cfg(stm32u5)]
399 w.set_cfbll(width * bytes_per_pixel + 3);
400 });
401
402 layer.cfblnr().modify(|w| w.set_cfblnbr(height));
404
405 layer.cr().modify(|w| {
407 if clut.is_some() {
408 w.set_cluten(true);
409 }
410 w.set_len(true);
411 });
412 }
413
414 pub async fn set_buffer(&mut self, layer: LtdcLayer, frame_buffer_addr: *const ()) -> Result<(), Error> {
417 let mut bits = T::regs().isr().read();
418
419 if !bits.fuif() && !bits.lif() && !bits.rrif() && !bits.terrif() {
421 poll_fn(|cx| {
423 let bits = T::regs().isr().read();
425 if bits.fuif() || bits.lif() || bits.rrif() || bits.terrif() {
426 return Poll::Ready(());
427 }
428
429 LTDC_WAKER.register(cx.waker());
430 Self::clear_interrupt_flags(); Self::enable_interrupts(true);
432
433 let layer = T::regs().layer(layer as usize);
435 layer.cfbar().modify(|w| w.set_cfbadd(frame_buffer_addr as u32));
436
437 T::regs().srcr().write(|w| {
439 w.set_vbr(Vbr::RELOAD);
440 });
441
442 let bits = T::regs().isr().read();
445 if bits.fuif() || bits.lif() || bits.rrif() || bits.terrif() {
446 Poll::Ready(())
447 } else {
448 Poll::Pending
449 }
450 })
451 .await;
452
453 bits = T::regs().isr().read();
455 }
456
457 let result = if bits.fuif() {
458 Err(Error::FifoUnderrun)
459 } else if bits.terrif() {
460 Err(Error::TransferError)
461 } else if bits.lif() {
462 panic!("line interrupt event is disabled")
463 } else if bits.rrif() {
464 Ok(())
466 } else {
467 unreachable!("all interrupt status values checked")
468 };
469
470 Self::clear_interrupt_flags();
471 result
472 }
473
474 fn setup_clocks() {
475 critical_section::with(|_cs| {
476 #[cfg(stm32f7)]
479 crate::pac::RCC
480 .dckcfgr1()
481 .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2));
482
483 #[cfg(stm32f4)]
485 crate::pac::RCC
486 .dckcfgr()
487 .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2));
488 });
489
490 rcc::enable_and_reset::<T>();
491 }
492
493 fn clear_interrupt_flags() {
494 T::regs().icr().write(|w| {
495 w.set_cfuif(Cfuif::CLEAR);
496 w.set_clif(Clif::CLEAR);
497 w.set_crrif(Crrif::CLEAR);
498 w.set_cterrif(Cterrif::CLEAR);
499 });
500 }
501
502 fn enable_interrupts(enable: bool) {
503 T::regs().ier().write(|w| {
504 w.set_fuie(enable);
505 w.set_lie(false); w.set_rrie(enable);
507 w.set_terrie(enable)
508 });
509
510 T::Interrupt::unpend();
512 if enable {
513 unsafe { T::Interrupt::enable() };
514 } else {
515 T::Interrupt::disable()
516 }
517 }
518}
519
520impl<'d, T: Instance> Drop for Ltdc<'d, T> {
521 fn drop(&mut self) {}
522}
523
524trait SealedInstance: crate::rcc::SealedRccPeripheral {
525 fn regs() -> crate::pac::ltdc::Ltdc;
526}
527
528#[allow(private_bounds)]
530pub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral + 'static + Send {
531 type Interrupt: interrupt::typelevel::Interrupt;
533}
534
535pin_trait!(ClkPin, Instance);
536pin_trait!(HsyncPin, Instance);
537pin_trait!(VsyncPin, Instance);
538pin_trait!(DePin, Instance);
539pin_trait!(R0Pin, Instance);
540pin_trait!(R1Pin, Instance);
541pin_trait!(R2Pin, Instance);
542pin_trait!(R3Pin, Instance);
543pin_trait!(R4Pin, Instance);
544pin_trait!(R5Pin, Instance);
545pin_trait!(R6Pin, Instance);
546pin_trait!(R7Pin, Instance);
547pin_trait!(G0Pin, Instance);
548pin_trait!(G1Pin, Instance);
549pin_trait!(G2Pin, Instance);
550pin_trait!(G3Pin, Instance);
551pin_trait!(G4Pin, Instance);
552pin_trait!(G5Pin, Instance);
553pin_trait!(G6Pin, Instance);
554pin_trait!(G7Pin, Instance);
555pin_trait!(B0Pin, Instance);
556pin_trait!(B1Pin, Instance);
557pin_trait!(B2Pin, Instance);
558pin_trait!(B3Pin, Instance);
559pin_trait!(B4Pin, Instance);
560pin_trait!(B5Pin, Instance);
561pin_trait!(B6Pin, Instance);
562pin_trait!(B7Pin, Instance);
563
564foreach_interrupt!(
565 ($inst:ident, ltdc, LTDC, GLOBAL, $irq:ident) => {
566 impl Instance for peripherals::$inst {
567 type Interrupt = crate::interrupt::typelevel::$irq;
568 }
569
570 impl SealedInstance for peripherals::$inst {
571 fn regs() -> crate::pac::ltdc::Ltdc {
572 crate::pac::$inst
573 }
574 }
575 };
576);