aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32
diff options
context:
space:
mode:
authorDavid Haig <[email protected]>2024-06-27 20:13:20 +0100
committerDavid Haig <[email protected]>2024-06-27 20:13:20 +0100
commit0e84bd8a91095c035b1b2fdf0c0d8d95b736cc9f (patch)
tree43aa42333cecdca822bac3555b1ecada345219fb /embassy-stm32
parent26e660722cca9151e5a9331c328421145509ab20 (diff)
Add support for the stm32 ltdc display peripheral
Diffstat (limited to 'embassy-stm32')
-rw-r--r--embassy-stm32/Cargo.toml5
-rw-r--r--embassy-stm32/src/ltdc.rs491
2 files changed, 446 insertions, 50 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 77b517dba..7a81b705d 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -72,7 +72,8 @@ rand_core = "0.6.3"
72sdio-host = "0.5.0" 72sdio-host = "0.5.0"
73critical-section = "1.1" 73critical-section = "1.1"
74#stm32-metapac = { version = "15" } 74#stm32-metapac = { version = "15" }
75stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a8ab0a3421ed0ca4b282f54028a0a2decacd8631" } 75#stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a8ab0a3421ed0ca4b282f54028a0a2decacd8631" }
76stm32-metapac = { git = "https://github.com/ninjasource/stm32-data-generated", branch = "stm32-ltdc", commit = "4c902fcd0889619e8af8bc03fa13b45c56fb3540" }
76 77
77vcell = "0.1.3" 78vcell = "0.1.3"
78nb = "1.0.0" 79nb = "1.0.0"
@@ -97,7 +98,7 @@ proc-macro2 = "1.0.36"
97quote = "1.0.15" 98quote = "1.0.15"
98 99
99#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} 100#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
100stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a8ab0a3421ed0ca4b282f54028a0a2decacd8631", default-features = false, features = ["metadata"] } 101stm32-metapac = { git = "https://github.com/ninjasource/stm32-data-generated", branch = "stm32-ltdc", commit = "4c902fcd0889619e8af8bc03fa13b45c56fb3540", default-features = false, features = ["metadata"] }
101 102
102[features] 103[features]
103default = ["rt"] 104default = ["rt"]
diff --git a/embassy-stm32/src/ltdc.rs b/embassy-stm32/src/ltdc.rs
index 481d77843..adc9b8862 100644
--- a/embassy-stm32/src/ltdc.rs
+++ b/embassy-stm32/src/ltdc.rs
@@ -1,19 +1,184 @@
1//! LTDC 1//! LTDC - LCD-TFT Display Controller
2use core::marker::PhantomData; 2//! See ST application note AN4861: Introduction to LCD-TFT display controller (LTDC) on STM32 MCUs for high level details
3//! This module was tested against the stm32h735g-dk using the RM0468 ST reference manual for detailed register information
3 4
4use crate::rcc::{self, RccPeripheral}; 5use crate::{
5use crate::{peripherals, Peripheral}; 6 gpio::{AfType, OutputType, Speed},
7 interrupt::{self, typelevel::Interrupt},
8 peripherals, rcc, Peripheral,
9};
10use core::{future::poll_fn, marker::PhantomData, task::Poll};
11use embassy_hal_internal::{into_ref, PeripheralRef};
12use embassy_sync::waitqueue::AtomicWaker;
13use stm32_metapac::ltdc::{
14 regs::Dccr,
15 vals::{Bf1, Bf2, Cfuif, Clif, Crrif, Cterrif, Pf, Vbr},
16};
17
18static LTDC_WAKER: AtomicWaker = AtomicWaker::new();
19
20/// LTDC error
21#[derive(Debug, PartialEq, Eq, Clone, Copy)]
22#[cfg_attr(feature = "defmt", derive(defmt::Format))]
23pub enum Error {
24 /// FIFO underrun. Generated when a pixel is requested while the FIFO is empty
25 FifoUnderrun,
26 /// Transfer error. Generated when a bus error occurs
27 TransferError,
28}
29
30/// Display configuration parameters
31#[derive(Clone, Copy, Debug, PartialEq)]
32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
33pub struct LtdcConfiguration {
34 /// Active width in pixels
35 pub active_width: u16,
36 /// Active height in pixels
37 pub active_height: u16,
38
39 /// Horizontal back porch (in units of pixel clock period)
40 pub h_back_porch: u16,
41 /// Horizontal front porch (in units of pixel clock period)
42 pub h_front_porch: u16,
43 /// Vertical back porch (in units of horizontal scan line)
44 pub v_back_porch: u16,
45 /// Vertical front porch (in units of horizontal scan line)
46 pub v_front_porch: u16,
47
48 /// Horizontal synchronization width (in units of pixel clock period)
49 pub h_sync: u16,
50 /// Vertical synchronization height (in units of horizontal scan line)
51 pub v_sync: u16,
52
53 /// Horizontal synchronization polarity: `false`: active low, `true`: active high
54 pub h_sync_polarity: PolarityActive,
55 /// Vertical synchronization polarity: `false`: active low, `true`: active high
56 pub v_sync_polarity: PolarityActive,
57 /// Data enable polarity: `false`: active low, `true`: active high
58 pub data_enable_polarity: PolarityActive,
59 /// Pixel clock polarity: `false`: falling edge, `true`: rising edge
60 pub pixel_clock_polarity: PolarityEdge,
61}
62
63/// Edge polarity
64#[derive(Clone, Copy, Debug, PartialEq)]
65#[cfg_attr(feature = "defmt", derive(defmt::Format))]
66pub enum PolarityEdge {
67 /// Falling edge
68 FallingEdge,
69 /// Rising edge
70 RisingEdge,
71}
72
73/// Active polarity
74#[derive(Clone, Copy, Debug, PartialEq)]
75#[cfg_attr(feature = "defmt", derive(defmt::Format))]
76pub enum PolarityActive {
77 /// Active low
78 ActiveLow,
79 /// Active high
80 ActiveHigh,
81}
6 82
7/// LTDC driver. 83/// LTDC driver.
8pub struct Ltdc<'d, T: Instance> { 84pub struct Ltdc<'d, T: Instance> {
9 _peri: PhantomData<&'d mut T>, 85 _peri: PeripheralRef<'d, T>,
86}
87
88/// LTDC interrupt handler.
89pub struct InterruptHandler<T: Instance> {
90 _phantom: PhantomData<T>,
91}
92
93/// 24 bit color
94#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
95#[cfg_attr(feature = "defmt", derive(defmt::Format))]
96pub struct RgbColor {
97 /// Red
98 pub red: u8,
99 /// Green
100 pub green: u8,
101 /// Blue
102 pub blue: u8,
103}
104
105/// Layer
106#[derive(Debug, PartialEq, Eq, Clone, Copy)]
107#[cfg_attr(feature = "defmt", derive(defmt::Format))]
108pub struct LtdcLayerConfig {
109 /// Layer number
110 pub layer: LtdcLayer,
111 /// Pixel format
112 pub pixel_format: PixelFormat,
113 /// Window left x in pixels
114 pub window_x0: u16,
115 /// Window right x in pixels
116 pub window_x1: u16,
117 /// Window top y in pixels
118 pub window_y0: u16,
119 /// Window bottom y in pixels
120 pub window_y1: u16,
121}
122
123/// Pixel format
124#[repr(u8)]
125#[derive(Debug, PartialEq, Eq, Clone, Copy)]
126#[cfg_attr(feature = "defmt", derive(defmt::Format))]
127pub enum PixelFormat {
128 /// ARGB8888
129 ARGB8888 = Pf::ARGB8888 as u8,
130 /// RGB888
131 RGB888 = Pf::RGB888 as u8,
132 /// RGB565
133 RGB565 = Pf::RGB565 as u8,
134 /// ARGB1555
135 ARGB1555 = Pf::ARGB1555 as u8,
136 /// ARGB4444
137 ARGB4444 = Pf::ARGB4444 as u8,
138 /// L8 (8-bit luminance)
139 L8 = Pf::L8 as u8,
140 /// AL44 (4-bit alpha, 4-bit luminance
141 AL44 = Pf::AL44 as u8,
142 /// AL88 (8-bit alpha, 8-bit luminance)
143 AL88 = Pf::AL88 as u8,
144}
145
146impl PixelFormat {
147 /// Number of bytes per pixel
148 pub fn bytes_per_pixel(&self) -> usize {
149 match self {
150 PixelFormat::ARGB8888 => 4,
151 PixelFormat::RGB888 => 3,
152 PixelFormat::RGB565 | PixelFormat::ARGB4444 | PixelFormat::ARGB1555 | PixelFormat::AL88 => 2,
153 PixelFormat::AL44 | PixelFormat::L8 => 1,
154 }
155 }
156}
157
158/// Ltdc Blending Layer
159#[repr(usize)]
160#[derive(Debug, PartialEq, Eq, Clone, Copy)]
161#[cfg_attr(feature = "defmt", derive(defmt::Format))]
162pub enum LtdcLayer {
163 /// Bottom layer
164 Layer1 = 0,
165 /// Top layer
166 Layer2 = 1,
167}
168
169impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
170 unsafe fn on_interrupt() {
171 cortex_m::asm::dsb();
172 Ltdc::<T>::enable_interrupts(false);
173 LTDC_WAKER.wake();
174 }
10} 175}
11 176
12impl<'d, T: Instance> Ltdc<'d, T> { 177impl<'d, T: Instance> Ltdc<'d, T> {
13 /// Note: Full-Duplex modes are not supported at this time 178 /// Create a new LTDC driver. 8 pins per color channel for blue, green and red
14 pub fn new( 179 pub fn new(
15 _peri: impl Peripheral<P = T> + 'd, 180 peri: impl Peripheral<P = T> + 'd,
16 /* 181 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
17 clk: impl Peripheral<P = impl ClkPin<T>> + 'd, 182 clk: impl Peripheral<P = impl ClkPin<T>> + 'd,
18 hsync: impl Peripheral<P = impl HsyncPin<T>> + 'd, 183 hsync: impl Peripheral<P = impl HsyncPin<T>> + 'd,
19 vsync: impl Peripheral<P = impl VsyncPin<T>> + 'd, 184 vsync: impl Peripheral<P = impl VsyncPin<T>> + 'd,
@@ -41,49 +206,274 @@ impl<'d, T: Instance> Ltdc<'d, T> {
41 r5: impl Peripheral<P = impl R5Pin<T>> + 'd, 206 r5: impl Peripheral<P = impl R5Pin<T>> + 'd,
42 r6: impl Peripheral<P = impl R6Pin<T>> + 'd, 207 r6: impl Peripheral<P = impl R6Pin<T>> + 'd,
43 r7: impl Peripheral<P = impl R7Pin<T>> + 'd, 208 r7: impl Peripheral<P = impl R7Pin<T>> + 'd,
44 */
45 ) -> Self { 209 ) -> Self {
46 //into_ref!(clk); 210 rcc::enable_and_reset::<T>();
47 211
48 critical_section::with(|_cs| { 212 into_ref!(peri);
49 // RM says the pllsaidivr should only be changed when pllsai is off. But this could have other unintended side effects. So let's just give it a try like this. 213 new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh));
50 // According to the debugger, this bit gets set, anyway. 214 new_pin!(hsync, AfType::output(OutputType::PushPull, Speed::VeryHigh));
51 #[cfg(stm32f7)] 215 new_pin!(vsync, AfType::output(OutputType::PushPull, Speed::VeryHigh));
52 stm32_metapac::RCC 216 new_pin!(b0, AfType::output(OutputType::PushPull, Speed::VeryHigh));
53 .dckcfgr1() 217 new_pin!(b1, AfType::output(OutputType::PushPull, Speed::VeryHigh));
54 .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2)); 218 new_pin!(b2, AfType::output(OutputType::PushPull, Speed::VeryHigh));
55 219 new_pin!(b3, AfType::output(OutputType::PushPull, Speed::VeryHigh));
56 // It is set to RCC_PLLSAIDIVR_2 in ST's BSP example for the STM32469I-DISCO. 220 new_pin!(b4, AfType::output(OutputType::PushPull, Speed::VeryHigh));
57 #[cfg(not(any(stm32f7, stm32u5)))] 221 new_pin!(b5, AfType::output(OutputType::PushPull, Speed::VeryHigh));
58 stm32_metapac::RCC 222 new_pin!(b6, AfType::output(OutputType::PushPull, Speed::VeryHigh));
59 .dckcfgr() 223 new_pin!(b7, AfType::output(OutputType::PushPull, Speed::VeryHigh));
60 .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2)); 224 new_pin!(g0, AfType::output(OutputType::PushPull, Speed::VeryHigh));
225 new_pin!(g1, AfType::output(OutputType::PushPull, Speed::VeryHigh));
226 new_pin!(g2, AfType::output(OutputType::PushPull, Speed::VeryHigh));
227 new_pin!(g3, AfType::output(OutputType::PushPull, Speed::VeryHigh));
228 new_pin!(g4, AfType::output(OutputType::PushPull, Speed::VeryHigh));
229 new_pin!(g5, AfType::output(OutputType::PushPull, Speed::VeryHigh));
230 new_pin!(g6, AfType::output(OutputType::PushPull, Speed::VeryHigh));
231 new_pin!(g7, AfType::output(OutputType::PushPull, Speed::VeryHigh));
232 new_pin!(r0, AfType::output(OutputType::PushPull, Speed::VeryHigh));
233 new_pin!(r1, AfType::output(OutputType::PushPull, Speed::VeryHigh));
234 new_pin!(r2, AfType::output(OutputType::PushPull, Speed::VeryHigh));
235 new_pin!(r3, AfType::output(OutputType::PushPull, Speed::VeryHigh));
236 new_pin!(r4, AfType::output(OutputType::PushPull, Speed::VeryHigh));
237 new_pin!(r5, AfType::output(OutputType::PushPull, Speed::VeryHigh));
238 new_pin!(r6, AfType::output(OutputType::PushPull, Speed::VeryHigh));
239 new_pin!(r7, AfType::output(OutputType::PushPull, Speed::VeryHigh));
240
241 Self { _peri: peri }
242 }
243
244 fn clear_interrupt_flags() {
245 T::regs().icr().write(|w| {
246 w.set_cfuif(Cfuif::CLEAR);
247 w.set_clif(Clif::CLEAR);
248 w.set_crrif(Crrif::CLEAR);
249 w.set_cterrif(Cterrif::CLEAR);
61 }); 250 });
251 }
62 252
63 rcc::enable_and_reset::<T>(); 253 fn enable_interrupts(enable: bool) {
254 T::regs().ier().write(|w| {
255 w.set_fuie(enable);
256 w.set_lie(false); // we are not interested in the line interrupt enable event
257 w.set_rrie(enable);
258 w.set_terrie(enable)
259 });
260
261 // enable interrupts for LTDC peripheral
262 T::Interrupt::unpend();
263 if enable {
264 unsafe { T::Interrupt::enable() };
265 } else {
266 T::Interrupt::disable()
267 }
268 }
64 269
65 //new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)); 270 /// Set the current buffer. The async function will return when buffer has been completely copied to the LCD screen
271 /// frame_buffer_addr is a pointer to memory that should not move (best to make it static)
272 pub async fn set_buffer(&mut self, layer: LtdcLayer, frame_buffer_addr: *const ()) -> Result<(), Error> {
273 let mut bits = T::regs().isr().read();
66 274
67 // Set Tearing Enable pin according to CubeMx example 275 // if all clear
68 //te.set_as_af_pull(te.af_num(), AfType::output(OutputType::PushPull, Speed::Low)); 276 if !bits.fuif() && !bits.lif() && !bits.rrif() && !bits.terrif() {
69 /* 277 // wait for interrupt
70 T::regs().wcr().modify(|w| { 278 poll_fn(|cx| {
71 w.set_dsien(true); 279 // quick check to avoid registration if already done.
280 let bits = T::regs().isr().read();
281 if bits.fuif() || bits.lif() || bits.rrif() || bits.terrif() {
282 return Poll::Ready(());
283 }
284
285 LTDC_WAKER.register(cx.waker());
286 Self::clear_interrupt_flags(); // don't poison the request with old flags
287 Self::enable_interrupts(true);
288
289 // set the new frame buffer address
290 let layer = T::regs().layer(layer as usize);
291 layer.cfbar().modify(|w| w.set_cfbadd(frame_buffer_addr as u32));
292
293 // configure a shadow reload for the next blanking period
294 T::regs().srcr().write(|w| {
295 w.set_vbr(Vbr::RELOAD);
72 }); 296 });
73 */ 297
74 Self { _peri: PhantomData } 298 // need to check condition after register to avoid a race
299 // condition that would result in lost notifications.
300 let bits = T::regs().isr().read();
301 if bits.fuif() || bits.lif() || bits.rrif() || bits.terrif() {
302 Poll::Ready(())
303 } else {
304 Poll::Pending
305 }
306 })
307 .await;
308
309 // re-read the status register after wait.
310 bits = T::regs().isr().read();
311 }
312
313 let result = if bits.fuif() {
314 Err(Error::FifoUnderrun)
315 } else if bits.terrif() {
316 Err(Error::TransferError)
317 } else if bits.lif() {
318 panic!("line interrupt event is disabled")
319 } else if bits.rrif() {
320 // register reload flag is expected
321 Ok(())
322 } else {
323 unreachable!("all interrupt status values checked")
324 };
325
326 Self::clear_interrupt_flags();
327 result
75 } 328 }
76 329
77 /// Set the enable bit in the control register and assert that it has been enabled 330 /// Initialize the display
78 pub fn enable(&mut self) { 331 pub fn init(&mut self, config: &LtdcConfiguration) {
79 T::regs().gcr().modify(|w| w.set_ltdcen(true)); 332 use stm32_metapac::ltdc::vals::{Depol, Hspol, Pcpol, Vspol};
80 assert!(T::regs().gcr().read().ltdcen()) 333 let ltdc = T::regs();
334
335 // check bus access
336 assert!(ltdc.gcr().read().0 == 0x2220); // reset value
337
338 // configure the HS, VS, DE and PC polarity
339 ltdc.gcr().modify(|w| {
340 w.set_hspol(match config.h_sync_polarity {
341 PolarityActive::ActiveHigh => Hspol::ACTIVEHIGH,
342 PolarityActive::ActiveLow => Hspol::ACTIVELOW,
343 });
344
345 w.set_vspol(match config.v_sync_polarity {
346 PolarityActive::ActiveHigh => Vspol::ACTIVEHIGH,
347 PolarityActive::ActiveLow => Vspol::ACTIVELOW,
348 });
349
350 w.set_depol(match config.data_enable_polarity {
351 PolarityActive::ActiveHigh => Depol::ACTIVEHIGH,
352 PolarityActive::ActiveLow => Depol::ACTIVELOW,
353 });
354
355 w.set_pcpol(match config.pixel_clock_polarity {
356 PolarityEdge::RisingEdge => Pcpol::RISINGEDGE,
357 PolarityEdge::FallingEdge => Pcpol::FALLINGEDGE,
358 });
359 });
360
361 // set synchronization pulse width
362 ltdc.sscr().modify(|w| {
363 w.set_vsh(config.v_sync - 1);
364 w.set_hsw(config.h_sync - 1);
365 });
366
367 // set accumulated back porch
368 ltdc.bpcr().modify(|w| {
369 w.set_avbp(config.v_sync + config.v_back_porch - 1);
370 w.set_ahbp(config.h_sync + config.h_back_porch - 1);
371 });
372
373 // set accumulated active width
374 let aa_height = config.v_sync + config.v_back_porch + config.active_height - 1;
375 let aa_width = config.h_sync + config.h_back_porch + config.active_width - 1;
376 ltdc.awcr().modify(|w| {
377 w.set_aah(aa_height);
378 w.set_aaw(aa_width);
379 });
380
381 // set total width and height
382 let total_height: u16 = config.v_sync + config.v_back_porch + config.active_height + config.v_front_porch - 1;
383 let total_width: u16 = config.h_sync + config.h_back_porch + config.active_width + config.h_front_porch - 1;
384 ltdc.twcr().modify(|w| {
385 w.set_totalh(total_height);
386 w.set_totalw(total_width)
387 });
388
389 // set the background color value to black
390 ltdc.bccr().modify(|w| {
391 w.set_bcred(0);
392 w.set_bcgreen(0);
393 w.set_bcblue(0);
394 });
395
396 // enable LTDC by setting LTDCEN bit
397 ltdc.gcr().modify(|w| {
398 w.set_ltdcen(true);
399 });
81 } 400 }
82 401
83 /// Unset the enable bit in the control register and assert that it has been disabled 402 /// Enable the layer
84 pub fn disable(&mut self) { 403 ///
85 T::regs().gcr().modify(|w| w.set_ltdcen(false)); 404 /// clut - color look-up table applies to L8, AL44 and AL88 pixel format and will default to greyscale if None supplied and these pixel formats are used
86 assert!(!T::regs().gcr().read().ltdcen()) 405 pub fn enable_layer(&mut self, layer_config: &LtdcLayerConfig, clut: Option<&[RgbColor]>) {
406 let ltdc = T::regs();
407 let layer = ltdc.layer(layer_config.layer as usize);
408
409 // 256 color look-up table for L8, AL88 and AL88 pixel formats
410 if let Some(clut) = clut {
411 assert_eq!(clut.len(), 256, "Color lookup table must be exactly 256 in length");
412 for (index, color) in clut.iter().enumerate() {
413 layer.clutwr().write(|w| {
414 w.set_clutadd(index as u8);
415 w.set_red(color.red);
416 w.set_green(color.green);
417 w.set_blue(color.blue);
418 });
419 }
420 }
421
422 // configure the horizontal start and stop position
423 let h_win_start = layer_config.window_x0 + ltdc.bpcr().read().ahbp() + 1;
424 let h_win_stop = layer_config.window_x1 + ltdc.bpcr().read().ahbp();
425 layer.whpcr().write(|w| {
426 w.set_whstpos(h_win_start);
427 w.set_whsppos(h_win_stop);
428 });
429
430 // configure the vertical start and stop position
431 let v_win_start = layer_config.window_y0 + ltdc.bpcr().read().avbp() + 1;
432 let v_win_stop = layer_config.window_y1 + ltdc.bpcr().read().avbp();
433 layer.wvpcr().write(|w| {
434 w.set_wvstpos(v_win_start);
435 w.set_wvsppos(v_win_stop)
436 });
437
438 // set the pixel format
439 layer
440 .pfcr()
441 .write(|w| w.set_pf(Pf::from_bits(layer_config.pixel_format as u8)));
442
443 // set the default color value to transparent black
444 layer.dccr().write_value(Dccr::default());
445
446 // set the global constant alpha value
447 let alpha = 0xFF;
448 layer.cacr().write(|w| w.set_consta(alpha));
449
450 // set the blending factors.
451 layer.bfcr().modify(|w| {
452 w.set_bf1(Bf1::PIXEL);
453 w.set_bf2(Bf2::PIXEL);
454 });
455
456 // calculate framebuffer pixel size in bytes
457 let bytes_per_pixel = layer_config.pixel_format.bytes_per_pixel() as u16;
458 let width = layer_config.window_x1 - layer_config.window_x0;
459 let height = layer_config.window_y1 - layer_config.window_y0;
460
461 // framebuffer pitch and line length
462 layer.cfblr().modify(|w| {
463 w.set_cfbp(width * bytes_per_pixel);
464 w.set_cfbll(width * bytes_per_pixel + 7);
465 });
466
467 // framebuffer line number
468 layer.cfblnr().modify(|w| w.set_cfblnbr(height));
469
470 // enable LTDC_Layer by setting LEN bit
471 layer.cr().modify(|w| {
472 if clut.is_some() {
473 w.set_cluten(true);
474 }
475 w.set_len(true);
476 });
87 } 477 }
88} 478}
89 479
@@ -95,9 +485,12 @@ trait SealedInstance: crate::rcc::SealedRccPeripheral {
95 fn regs() -> crate::pac::ltdc::Ltdc; 485 fn regs() -> crate::pac::ltdc::Ltdc;
96} 486}
97 487
98/// DSI instance trait. 488/// LTDC instance trait.
99#[allow(private_bounds)] 489#[allow(private_bounds)]
100pub trait Instance: SealedInstance + RccPeripheral + 'static {} 490pub trait Instance: SealedInstance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
491 /// Interrupt for this LTDC instance.
492 type Interrupt: interrupt::typelevel::Interrupt;
493}
101 494
102pin_trait!(ClkPin, Instance); 495pin_trait!(ClkPin, Instance);
103pin_trait!(HsyncPin, Instance); 496pin_trait!(HsyncPin, Instance);
@@ -128,14 +521,16 @@ pin_trait!(B5Pin, Instance);
128pin_trait!(B6Pin, Instance); 521pin_trait!(B6Pin, Instance);
129pin_trait!(B7Pin, Instance); 522pin_trait!(B7Pin, Instance);
130 523
131foreach_peripheral!( 524foreach_interrupt!(
132 (ltdc, $inst:ident) => { 525 ($inst:ident, ltdc, LTDC, GLOBAL, $irq:ident) => {
133 impl crate::ltdc::SealedInstance for peripherals::$inst { 526 impl Instance for peripherals::$inst {
527 type Interrupt = crate::interrupt::typelevel::$irq;
528 }
529
530 impl SealedInstance for peripherals::$inst {
134 fn regs() -> crate::pac::ltdc::Ltdc { 531 fn regs() -> crate::pac::ltdc::Ltdc {
135 crate::pac::$inst 532 crate::pac::$inst
136 } 533 }
137 } 534 }
138
139 impl crate::ltdc::Instance for peripherals::$inst {}
140 }; 535 };
141); 536);