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