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