1use core::convert::Infallible;
71
72use bitfield::bitfield;
73use embedded_graphics::pixelcolor::RgbColor;
74use embedded_graphics::prelude::{DrawTarget, OriginDimensions, Point, Size};
75
76use crate::Color;
77use crate::FrameBuffer;
78use crate::WordSize;
79use crate::{FrameBufferOperations, MutableFrameBuffer};
80
81const BLANKING_DELAY: usize = 1;
82
83#[inline]
84const fn map_index(i: usize) -> usize {
85 #[cfg(feature = "esp32-ordering")]
86 {
87 i ^ 1
88 }
89 #[cfg(not(feature = "esp32-ordering"))]
90 {
91 i
92 }
93}
94
95#[inline]
96const fn make_data_template<const COLS: usize>(addr: u8, prev_addr: u8) -> [Entry; COLS] {
97 let mut data = [Entry::new(); COLS];
98 let mut i = 0;
99
100 while i < COLS {
101 let mut entry = Entry::new();
102 entry.0 = prev_addr as u16;
103
104 if i == 1 {
105 entry.0 |= 0b1_0000_0000; } else if i == COLS - BLANKING_DELAY - 1 {
107 } else if i == COLS - 1 {
109 entry.0 |= 0b0010_0000; entry.0 = (entry.0 & !0b0001_1111) | (addr as u16); } else if i > 1 && i < COLS - BLANKING_DELAY - 1 {
112 entry.0 |= 0b1_0000_0000; }
114
115 data[map_index(i)] = entry;
116 i += 1;
117 }
118
119 data
120}
121
122bitfield! {
123 #[derive(Clone, Copy, Default, PartialEq)]
124 #[repr(transparent)]
125 pub(crate) struct Entry(u16);
126 pub(crate) dummy2, set_dummy2: 15;
127 pub(crate) blu2, set_blu2: 14;
128 pub(crate) grn2, set_grn2: 13;
129 pub(crate) red2, set_red2: 12;
130 pub(crate) blu1, set_blu1: 11;
131 pub(crate) grn1, set_grn1: 10;
132 pub(crate) red1, set_red1: 9;
133 pub(crate) output_enable, set_output_enable: 8;
134 pub(crate) dummy1, set_dummy1: 7;
135 pub(crate) dummy0, set_dummy0: 6;
136 pub(crate) latch, set_latch: 5;
137 pub(crate) addr, set_addr: 4, 0;
138}
139
140impl core::fmt::Debug for Entry {
141 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
142 f.debug_tuple("Entry")
143 .field(&format_args!("{:#x}", self.0))
144 .finish()
145 }
146}
147
148impl Entry {
149 const fn new() -> Self {
150 Self(0)
151 }
152
153 const COLOR0_MASK: u16 = 0b0000_1110_0000_0000; const COLOR1_MASK: u16 = 0b0111_0000_0000_0000; #[inline]
157 fn set_color0_bits(&mut self, bits: u8) {
158 let bits16 = u16::from(bits) << 9;
159 self.0 = (self.0 & !Self::COLOR0_MASK) | (bits16 & Self::COLOR0_MASK);
160 }
161
162 #[inline]
163 fn set_color1_bits(&mut self, bits: u8) {
164 let bits16 = u16::from(bits) << 12;
165 self.0 = (self.0 & !Self::COLOR1_MASK) | (bits16 & Self::COLOR1_MASK);
166 }
167}
168
169#[derive(Clone, Copy, PartialEq, Debug)]
170#[repr(C)]
171pub struct Row<const COLS: usize> {
176 pub(crate) data: [Entry; COLS],
177}
178
179impl<const COLS: usize> Row<COLS> {
180 #[must_use]
184 pub const fn new() -> Self {
185 Self {
186 data: [Entry::new(); COLS],
187 }
188 }
189
190 #[inline]
195 pub fn format(&mut self, addr: u8, prev_addr: u8) {
196 let template = make_data_template::<COLS>(addr, prev_addr);
197 self.data.copy_from_slice(&template);
198 }
199}
200
201impl<const COLS: usize> Default for Row<COLS> {
202 fn default() -> Self {
203 Self::new()
204 }
205}
206
207#[derive(Copy, Clone)]
209#[repr(C)]
210pub struct DmaFrameBuffer<const NROWS: usize, const COLS: usize, const PLANES: usize> {
211 pub(crate) planes: [[Row<COLS>; NROWS]; PLANES],
212}
213
214impl<const NROWS: usize, const COLS: usize, const PLANES: usize>
215 DmaFrameBuffer<NROWS, COLS, PLANES>
216{
217 #[must_use]
219 pub fn new() -> Self {
220 let mut instance = Self {
221 planes: [[Row::new(); NROWS]; PLANES],
222 };
223 instance.format();
224 instance
225 }
226
227 #[must_use]
229 pub const fn bcm_chunk_count() -> usize {
230 PLANES
231 }
232
233 #[must_use]
235 pub const fn bcm_chunk_bytes() -> usize {
236 NROWS * core::mem::size_of::<Row<COLS>>()
237 }
238
239 #[inline]
241 pub fn format(&mut self) {
242 for plane in &mut self.planes {
243 for (row_idx, row) in plane.iter_mut().enumerate() {
244 let prev_addr = if row_idx == 0 {
245 NROWS as u8 - 1
246 } else {
247 row_idx as u8 - 1
248 };
249 row.format(row_idx as u8, prev_addr);
250 }
251 }
252 }
253
254 #[inline]
256 pub fn erase(&mut self) {
257 const MASK: u16 = !0b0111_1110_0000_0000; for plane in &mut self.planes {
259 for row in plane {
260 for entry in &mut row.data {
261 entry.0 &= MASK;
262 }
263 }
264 }
265 }
266
267 #[inline]
269 pub fn set_pixel(&mut self, p: Point, color: Color) {
270 if p.x < 0 || p.y < 0 {
271 return;
272 }
273 self.set_pixel_internal(p.x as usize, p.y as usize, color);
274 }
275
276 #[inline]
277 fn set_pixel_internal(&mut self, x: usize, y: usize, color: Color) {
278 if x >= COLS || y >= NROWS * 2 {
279 return;
280 }
281
282 let row_idx = if y < NROWS { y } else { y - NROWS };
283 let is_top = y < NROWS;
284 let red = color.r();
285 let green = color.g();
286 let blue = color.b();
287
288 for plane_idx in 0..PLANES {
289 let bit = 7_u32.saturating_sub(plane_idx as u32);
290 let bits = ((u8::from(((blue >> bit) & 1) != 0)) << 2)
291 | ((u8::from(((green >> bit) & 1) != 0)) << 1)
292 | u8::from(((red >> bit) & 1) != 0);
293 let col_idx = map_index(x);
294 let entry = &mut self.planes[plane_idx][row_idx].data[col_idx];
295 if is_top {
296 entry.set_color0_bits(bits);
297 } else {
298 entry.set_color1_bits(bits);
299 }
300 }
301 }
302}
303
304impl<const NROWS: usize, const COLS: usize, const PLANES: usize> Default
305 for DmaFrameBuffer<NROWS, COLS, PLANES>
306{
307 fn default() -> Self {
308 Self::new()
309 }
310}
311
312impl<const NROWS: usize, const COLS: usize, const PLANES: usize> core::fmt::Debug
313 for DmaFrameBuffer<NROWS, COLS, PLANES>
314{
315 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
316 f.debug_struct("DmaFrameBuffer")
317 .field("size", &core::mem::size_of_val(&self.planes))
318 .field("plane_count", &self.planes.len())
319 .field("plane_size", &core::mem::size_of_val(&self.planes[0]))
320 .finish()
321 }
322}
323
324#[cfg(feature = "defmt")]
325impl<const NROWS: usize, const COLS: usize, const PLANES: usize> defmt::Format
326 for DmaFrameBuffer<NROWS, COLS, PLANES>
327{
328 fn format(&self, f: defmt::Formatter) {
329 defmt::write!(f, "DmaFrameBuffer<{}, {}, {}>", NROWS, COLS, PLANES);
330 defmt::write!(f, " size: {}", core::mem::size_of_val(&self.planes));
331 defmt::write!(
332 f,
333 " plane_size: {}",
334 core::mem::size_of_val(&self.planes[0])
335 );
336 }
337}
338
339impl<const NROWS: usize, const COLS: usize, const PLANES: usize> FrameBuffer
340 for DmaFrameBuffer<NROWS, COLS, PLANES>
341{
342 fn get_word_size(&self) -> WordSize {
343 WordSize::Sixteen
344 }
345
346 fn plane_count(&self) -> usize {
347 PLANES
348 }
349
350 fn plane_ptr_len(&self, plane_idx: usize) -> (*const u8, usize) {
351 assert!(
352 plane_idx < PLANES,
353 "plane_idx {plane_idx} out of range for {PLANES} planes"
354 );
355 let ptr = self.planes[plane_idx].as_ptr().cast::<u8>();
356 let len = NROWS * core::mem::size_of::<Row<COLS>>();
357 (ptr, len)
358 }
359}
360
361impl<const NROWS: usize, const COLS: usize, const PLANES: usize> FrameBufferOperations
362 for DmaFrameBuffer<NROWS, COLS, PLANES>
363{
364 #[inline]
365 fn erase(&mut self) {
366 DmaFrameBuffer::<NROWS, COLS, PLANES>::erase(self);
367 }
368
369 #[inline]
370 fn set_pixel(&mut self, p: Point, color: Color) {
371 DmaFrameBuffer::<NROWS, COLS, PLANES>::set_pixel(self, p, color);
372 }
373}
374
375impl<const NROWS: usize, const COLS: usize, const PLANES: usize> MutableFrameBuffer
376 for DmaFrameBuffer<NROWS, COLS, PLANES>
377{
378}
379
380impl<const NROWS: usize, const COLS: usize, const PLANES: usize> OriginDimensions
381 for DmaFrameBuffer<NROWS, COLS, PLANES>
382{
383 fn size(&self) -> Size {
384 Size::new(COLS as u32, (NROWS * 2) as u32)
385 }
386}
387
388impl<const NROWS: usize, const COLS: usize, const PLANES: usize> DrawTarget
389 for DmaFrameBuffer<NROWS, COLS, PLANES>
390{
391 type Color = Color;
392 type Error = Infallible;
393
394 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
395 where
396 I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
397 {
398 for pixel in pixels {
399 self.set_pixel_internal(pixel.0.x as usize, pixel.0.y as usize, pixel.1);
400 }
401 Ok(())
402 }
403}
404
405#[cfg(test)]
406mod tests {
407 extern crate std;
408
409 use super::*;
410 use embedded_graphics::prelude::*;
411 use std::format;
412
413 type TestBuffer = DmaFrameBuffer<16, 64, 8>;
414
415 #[test]
416 fn row_format_sets_address_and_control_bits() {
417 let mut row = Row::<8>::new();
418 row.format(5, 4);
419
420 let last_idx = map_index(7);
421 assert_eq!(row.data[last_idx].latch(), true);
422 assert_eq!(row.data[last_idx].addr(), 5);
423
424 let first_idx = map_index(0);
425 assert_eq!(row.data[first_idx].addr(), 4);
426 assert_eq!(row.data[first_idx].latch(), false);
427 }
428
429 #[test]
430 fn format_sets_expected_row_addresses_for_all_rows() {
431 let mut fb = TestBuffer::new();
432 fb.format();
433
434 for plane_idx in 0..8 {
435 for row_idx in 0..16 {
436 let last_col = map_index(63);
437 assert_eq!(
438 fb.planes[plane_idx][row_idx].data[last_col].addr(),
439 row_idx as u16
440 );
441 assert_eq!(fb.planes[plane_idx][row_idx].data[last_col].latch(), true);
442 }
443 }
444 }
445
446 #[test]
447 fn set_pixel_maps_top_half_bits_per_plane() {
448 let mut fb = TestBuffer::new();
449 let color = Color::new(0b1010_0101, 0b0101_1010, 0b1111_0000);
450 fb.set_pixel(Point::new(2, 3), color);
451
452 for plane_idx in 0..8 {
453 let bit = 7 - plane_idx;
454 let entry = fb.planes[plane_idx][3].data[map_index(2)];
455 assert_eq!(entry.red1(), ((color.r() >> bit) & 1) != 0);
456 assert_eq!(entry.grn1(), ((color.g() >> bit) & 1) != 0);
457 assert_eq!(entry.blu1(), ((color.b() >> bit) & 1) != 0);
458 }
459 }
460
461 #[test]
462 fn set_pixel_maps_bottom_half_bits_per_plane() {
463 let mut fb = TestBuffer::new();
464 let color = Color::new(0b1100_0011, 0b0011_1100, 0b1001_0110);
465 fb.set_pixel(Point::new(4, 20), color);
466
467 for plane_idx in 0..8 {
468 let bit = 7 - plane_idx;
469 let entry = fb.planes[plane_idx][4].data[map_index(4)];
470 assert_eq!(entry.red2(), ((color.r() >> bit) & 1) != 0);
471 assert_eq!(entry.grn2(), ((color.g() >> bit) & 1) != 0);
472 assert_eq!(entry.blu2(), ((color.b() >> bit) & 1) != 0);
473 }
474 }
475
476 #[test]
477 fn erase_clears_only_color_bits() {
478 let mut fb = TestBuffer::new();
479 let oe_before = fb.planes[0][0].data[map_index(1)].output_enable();
480 fb.set_pixel(Point::new(0, 0), Color::WHITE);
481 fb.erase();
482
483 for plane in &fb.planes {
484 for row in plane {
485 for entry in &row.data {
486 assert!(!entry.red1());
487 assert!(!entry.grn1());
488 assert!(!entry.blu1());
489 assert!(!entry.red2());
490 assert!(!entry.grn2());
491 assert!(!entry.blu2());
492 }
493 }
494 }
495
496 assert_eq!(
497 fb.planes[0][0].data[map_index(1)].output_enable(),
498 oe_before
499 );
500 }
501
502 #[test]
503 fn draw_target_iter_sets_pixels() {
504 let mut fb = TestBuffer::new();
505 let pixels = [Pixel(Point::new(1, 1), Color::RED)];
506 let result = fb.draw_iter(pixels);
507 assert!(result.is_ok());
508
509 for plane_idx in 0..8 {
510 let bit = 7 - plane_idx;
511 let entry = fb.planes[plane_idx][1].data[map_index(1)];
512 assert_eq!(entry.red1(), ((Color::RED.r() >> bit) & 1) != 0);
513 assert!(!entry.grn1());
514 assert!(!entry.blu1());
515 }
516 }
517
518 #[test]
519 fn set_pixel_ignores_out_of_bounds_and_negative() {
520 let mut fb = TestBuffer::new();
521 let before = fb.planes;
522 fb.set_pixel(Point::new(-1, 0), Color::WHITE);
523 fb.set_pixel(Point::new(0, -1), Color::WHITE);
524 fb.set_pixel(Point::new(64, 0), Color::WHITE);
525 fb.set_pixel(Point::new(0, 32), Color::WHITE);
526 assert_eq!(fb.planes, before);
527 }
528
529 #[test]
530 fn bcm_chunk_info_for_common_panel() {
531 assert_eq!(TestBuffer::bcm_chunk_count(), 8);
532 assert_eq!(
533 TestBuffer::bcm_chunk_bytes(),
534 16 * core::mem::size_of::<Row<64>>()
535 );
536 }
537
538 #[test]
539 fn frame_buffer_trait_accessors_report_expected_values() {
540 let fb = TestBuffer::new();
541 let as_trait: &dyn FrameBuffer = &fb;
542 assert_eq!(as_trait.get_word_size(), WordSize::Sixteen);
543 assert_eq!(as_trait.plane_count(), 8);
544
545 let (ptr, len) = as_trait.plane_ptr_len(0);
546 assert_eq!(len, 16 * core::mem::size_of::<Row<64>>());
547 assert_eq!(ptr, fb.planes[0].as_ptr().cast::<u8>());
548 }
549
550 #[test]
551 #[should_panic(expected = "out of range")]
552 fn plane_ptr_len_panics_for_invalid_plane() {
553 let fb = TestBuffer::new();
554 let _ = fb.plane_ptr_len(8);
555 }
556
557 #[test]
558 fn origin_dimensions_match_panel_geometry() {
559 let fb = TestBuffer::new();
560 assert_eq!(fb.size(), Size::new(64, 32));
561 }
562
563 #[test]
564 fn debug_impl_includes_shape_information() {
565 let fb = TestBuffer::new();
566 let s = format!("{fb:?}");
567 assert!(s.contains("DmaFrameBuffer"));
568 assert!(s.contains("plane_count"));
569 assert!(s.contains("plane_size"));
570 }
571
572 #[test]
573 fn row_format_sets_expected_blank_and_latch_positions() {
574 let mut row = Row::<8>::new();
575 row.format(5, 4);
576
577 let idx_1 = map_index(1);
579 assert!(row.data[idx_1].output_enable());
580 assert_eq!(row.data[idx_1].addr(), 4);
581
582 let idx_blank = map_index(8 - BLANKING_DELAY - 1);
584 assert!(!row.data[idx_blank].output_enable());
585
586 let idx_last = map_index(7);
588 assert!(row.data[idx_last].latch());
589 assert_eq!(row.data[idx_last].addr(), 5);
590 }
591
592 #[test]
593 fn default_constructors_match_new() {
594 let row_default = Row::<8>::default();
595 let row_new = Row::<8>::new();
596 assert_eq!(row_default, row_new);
597
598 let fb_default = TestBuffer::default();
599 let fb_new = TestBuffer::new();
600 assert_eq!(fb_default.planes, fb_new.planes);
601 }
602
603 #[test]
604 fn framebuffer_operations_trait_delegates_correctly() {
605 let mut fb = TestBuffer::new();
606 FrameBufferOperations::set_pixel(&mut fb, Point::new(3, 5), Color::GREEN);
607
608 assert!(fb.planes[0][5].data[map_index(3)].grn1());
609
610 FrameBufferOperations::erase(&mut fb);
611 for plane in &fb.planes {
612 for row in plane {
613 for entry in &row.data {
614 assert!(!entry.red1());
615 assert!(!entry.grn1());
616 assert!(!entry.blu1());
617 assert!(!entry.red2());
618 assert!(!entry.grn2());
619 assert!(!entry.blu2());
620 }
621 }
622 }
623 }
624
625 #[test]
626 fn entry_debug_shows_hex_value() {
627 let mut entry = Entry::new();
628 entry.set_red1(true);
629 let s = format!("{entry:?}");
630 assert!(s.contains("Entry"));
631 assert!(s.contains("0x"));
632 }
633}