1#[cfg_attr(test, allow(unused_imports))]
4use micromath::F32Ext;
5
6use crate::{
7 pac::{DMA2D, LTDC, RCC},
8 rcc::{Enable, HSEClock, Reset},
9};
10
11pub struct DisplayConfig {
13 pub active_width: u16,
14 pub active_height: u16,
15 pub h_back_porch: u16,
16 pub h_front_porch: u16,
17 pub v_back_porch: u16,
18 pub v_front_porch: u16,
19 pub h_sync: u16,
20 pub v_sync: u16,
21 pub frame_rate: u16,
22 pub h_sync_pol: bool,
24 pub v_sync_pol: bool,
26 pub no_data_enable_pol: bool,
28 pub pixel_clock_pol: bool,
30}
31
32pub enum Layer {
36 L1,
37 L2,
38}
39
40pub struct DisplayController<T: 'static + SupportedWord> {
41 _ltdc: LTDC,
43 _dma2d: DMA2D,
45 config: DisplayConfig,
47 buffer1: Option<&'static mut [T]>,
49 buffer2: Option<&'static mut [T]>,
51 pixel_format: PixelFormat,
53}
54
55impl<T: 'static + SupportedWord> DisplayController<T> {
56 pub fn new(
58 ltdc: LTDC,
59 dma2d: DMA2D,
60 pixel_format: PixelFormat,
61 config: DisplayConfig,
62 hse: Option<&HSEClock>,
63 ) -> DisplayController<T> {
64 let total_width: u16 =
66 config.h_sync + config.h_back_porch + config.active_width + config.h_front_porch - 1;
67 let total_height: u16 =
68 config.v_sync + config.v_back_porch + config.active_height + config.v_front_porch - 1;
69 let lcd_clk: u32 =
70 (total_width as u32) * (total_height as u32) * (config.frame_rate as u32);
71
72 unsafe {
74 LTDC::enable_unchecked();
76 LTDC::reset_unchecked();
78
79 DMA2D::enable_unchecked();
81 DMA2D::reset_unchecked();
83 }
84
85 let base_clk: u32;
87 match &hse {
88 Some(hse) => base_clk = hse.freq.raw(),
89 None => base_clk = 16_000_000,
91 }
92 let rcc = unsafe { &(*RCC::ptr()) };
93 let pllm: u8 = rcc.pllcfgr.read().pllm().bits();
94
95 let vco_in_mhz: f32 = (base_clk as f32 / pllm as f32) / 1_000_000.0;
99 let lcd_clk_mhz = (lcd_clk as f32) / 1_000_000.0;
100 let allowed_pllr = [2.0, 3.0, 4.0, 5.0, 6.0, 7.0];
101 let allowed_divr = [2.0, 4.0, 8.0, 16.0];
102 let mut best_pllr: f32 = allowed_pllr[0];
103 let mut best_divr: f32 = allowed_divr[0];
104 let mut best_plln: f32 = 100.0;
105 let mut best_error: f32 = (vco_in_mhz * best_plln) / (best_pllr * best_divr);
106 let mut error: f32;
107 let mut plln: f32;
108
109 for pllr in &allowed_pllr {
110 for divr in &allowed_divr {
111 plln = ((lcd_clk_mhz * divr * pllr) / vco_in_mhz).floor();
112 error = lcd_clk_mhz - (vco_in_mhz * plln) / (pllr * divr);
113
114 if 100.0 <= vco_in_mhz * plln
118 && vco_in_mhz * plln <= 432.0
119 && error >= 0.0
120 && error < best_error
121 {
122 best_pllr = *pllr;
123 best_divr = *divr;
124 best_plln = plln;
125 best_error = error;
126 }
127 }
128 }
129
130 let pllsaidivr: u8 = match best_divr as u16 {
131 2 => 0b00,
132 4 => 0b01,
133 8 => 0b10,
134 16 => 0b11,
135 _ => unreachable!(),
136 };
137
138 rcc.pllsaicfgr.write(|w| unsafe {
140 w.pllsain()
141 .bits(best_plln as u16)
142 .pllsair()
143 .bits(best_pllr as u8)
144 });
145 rcc.dckcfgr1.modify(|_, w| w.pllsaidivr().bits(pllsaidivr));
146
147 rcc.cr.modify(|_, w| w.pllsaion().on());
149 while rcc.cr.read().pllsairdy().is_not_ready() {}
150
151 ltdc.sscr.write(|w| {
153 w.hsw()
154 .bits((config.h_sync - 1) as u16)
155 .vsh()
156 .bits((config.v_sync - 1) as u16)
157 });
158 ltdc.bpcr.write(|w| {
159 w.ahbp()
160 .bits((config.h_sync + config.h_back_porch - 1) as u16)
161 .avbp()
162 .bits((config.v_sync + config.v_back_porch - 1) as u16)
163 });
164 ltdc.awcr.write(|w| {
165 w.aaw()
166 .bits((config.h_sync + config.h_back_porch + config.active_width - 1) as u16)
167 .aah()
168 .bits((config.v_sync + config.v_back_porch + config.active_height - 1) as u16)
169 });
170 ltdc.twcr.write(|w| {
171 w.totalw()
172 .bits(total_width as u16)
173 .totalh()
174 .bits(total_height as u16)
175 });
176
177 ltdc.gcr.write(|w| {
179 w.hspol()
180 .bit(config.h_sync_pol)
181 .vspol()
182 .bit(config.v_sync_pol)
183 .depol()
184 .bit(config.no_data_enable_pol)
185 .pcpol()
186 .bit(config.pixel_clock_pol)
187 });
188
189 ltdc.bccr.write(|w| unsafe { w.bits(0xAAAAAAAA) });
191
192 ltdc.srcr.modify(|_, w| w.imr().set_bit());
196 ltdc.gcr.modify(|_, w| w.ltdcen().set_bit().den().set_bit());
198
199 ltdc.srcr.modify(|_, w| w.imr().set_bit());
201
202 DisplayController {
203 _ltdc: ltdc,
204 _dma2d: dma2d,
205 config,
206 buffer1: None,
207 buffer2: None,
208 pixel_format,
209 }
210 }
211
212 pub fn config_layer(
219 &mut self,
220 layer: Layer,
221 buffer: &'static mut [T],
222 pixel_format: PixelFormat,
223 ) {
224 let _layer = match &layer {
225 Layer::L1 => &self._ltdc.layer1,
226 Layer::L2 => &self._ltdc.layer2,
227 };
228
229 let height = self.config.active_height;
230 let width = self.config.active_width;
231 assert!(buffer.len() == height as usize * width as usize);
232
233 let h_win_start = self.config.h_sync + self.config.h_back_porch - 1;
236 let v_win_start = self.config.v_sync + self.config.v_back_porch - 1;
237
238 _layer.whpcr.write(|w| {
239 w.whstpos()
240 .bits(h_win_start + 1)
241 .whsppos()
242 .bits(h_win_start + width)
243 });
244 _layer.wvpcr.write(|w| {
245 w.wvstpos()
246 .bits(v_win_start + 1)
247 .wvsppos()
248 .bits(v_win_start + height)
249 });
250
251 _layer.pfcr.write(|w| {
253 w.pf().bits(match &pixel_format {
254 PixelFormat::ARGB8888 => 0b000,
255 PixelFormat::RGB565 => 0b010,
257 PixelFormat::ARGB1555 => 0b011,
258 PixelFormat::ARGB4444 => 0b100,
259 PixelFormat::L8 => 0b101,
260 PixelFormat::AL44 => 0b110,
261 PixelFormat::AL88 => 0b111,
262 })
264 });
265
266 _layer.cacr.write(|w| w.consta().bits(0xFF));
268
269 _layer.dccr.write(|w| unsafe { w.bits(0xFFFF0000) });
273
274 _layer
279 .bfcr
280 .write(|w| unsafe { w.bf1().bits(0b100).bf2().bits(0b101) });
281
282 _layer
284 .cfbar
285 .write(|w| w.cfbadd().bits(buffer.as_ptr() as u32));
286
287 let byte_per_pixel: u16 = match &pixel_format {
289 PixelFormat::ARGB8888 => 4,
290 PixelFormat::RGB565 => 2,
292 PixelFormat::ARGB1555 => 2,
293 PixelFormat::ARGB4444 => 16,
294 PixelFormat::L8 => 1,
295 PixelFormat::AL44 => 1,
296 PixelFormat::AL88 => 2,
297 };
299 _layer.cfblr.write(|w| {
300 w.cfbp()
301 .bits(width * byte_per_pixel)
302 .cfbll()
303 .bits(width * byte_per_pixel + 3)
304 });
305
306 _layer.cfblnr.write(|w| w.cfblnbr().bits(height));
308
309 _layer.cr.modify(|_, w| w.cluten().clear_bit());
311
312 self._dma2d.fgpfccr.write(|w| unsafe {
314 w.bits(match &pixel_format {
315 PixelFormat::ARGB8888 => 0b000,
316 PixelFormat::RGB565 => 0b0010,
318 PixelFormat::ARGB1555 => 0b0011,
319 PixelFormat::ARGB4444 => 0b0100,
320 PixelFormat::L8 => 0b0101,
321 PixelFormat::AL44 => 0b0110,
322 PixelFormat::AL88 => 0b0111,
323 })
328 });
329
330 match &layer {
331 Layer::L1 => self.buffer1 = Some(buffer),
332 Layer::L2 => self.buffer2 = Some(buffer),
333 }
334 }
335
336 pub fn enable_layer(&self, layer: Layer) {
338 match layer {
339 Layer::L1 => self._ltdc.layer1.cr.modify(|_, w| w.len().set_bit()),
340 Layer::L2 => self._ltdc.layer2.cr.modify(|_, w| w.len().set_bit()),
341 }
342 }
343
344 pub fn draw_pixel(&mut self, layer: Layer, x: usize, y: usize, color: T) {
346 if x >= self.config.active_width as usize || y >= self.config.active_height as usize {
347 panic!("Invalid (x,y) pixel position");
348 }
349
350 match layer {
351 Layer::L1 => {
352 self.buffer1.as_mut().unwrap()[x + self.config.active_width as usize * y] = color
353 }
354 Layer::L2 => {
355 self.buffer2.as_mut().unwrap()[x + self.config.active_width as usize * y] = color
356 }
357 }
358 }
359
360 pub unsafe fn draw_rectangle(
366 &mut self,
367 layer: Layer,
368 top_left: (usize, usize),
369 bottom_right: (usize, usize),
370 color: u32,
371 ) {
372 self._dma2d.opfccr.write(|w| {
374 w.cm().bits(match &self.pixel_format {
375 PixelFormat::ARGB8888 => 0b000,
376 PixelFormat::RGB565 => 0b010,
378 PixelFormat::ARGB1555 => 0b011,
379 PixelFormat::ARGB4444 => 0b100,
380 _ => unreachable!(),
381 })
382 });
383
384 self._dma2d.ocolr.write_with_zero(|w| w.bits(color));
386
387 let offset: isize = (top_left.0 + self.config.active_width as usize * top_left.1) as isize;
389 self._dma2d.omar.write_with_zero(|w| {
390 w.bits(match &layer {
391 Layer::L1 => self.buffer1.as_ref().unwrap().as_ptr().offset(offset) as u32,
392 Layer::L2 => self.buffer2.as_ref().unwrap().as_ptr().offset(offset) as u32,
393 })
394 });
395
396 self._dma2d.nlr.write(|w| {
398 w.pl()
399 .bits((bottom_right.0 - top_left.0) as u16)
400 .nl()
401 .bits((bottom_right.1 - top_left.1) as u16)
402 });
403
404 self._dma2d.oor.write(|w| {
406 w.lo()
407 .bits(top_left.0 as u16 + self.config.active_width - bottom_right.0 as u16)
408 });
409
410 self._dma2d
412 .cr
413 .modify(|_, w| w.mode().bits(0b11).start().set_bit());
414 }
415
416 pub fn reload(&self) {
418 self._ltdc.srcr.modify(|_, w| w.imr().set_bit());
420 }
421}
422
423pub enum PixelFormat {
430 ARGB8888,
431 RGB565,
433 ARGB1555,
434 ARGB4444,
435 L8,
436 AL44,
437 AL88,
438}
439
440pub trait SupportedWord {}
441impl SupportedWord for u8 {}
442impl SupportedWord for u16 {}
443impl SupportedWord for u32 {}