| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- #pragma once
- // Diagnostic-fill harness, Pattern B (pixel packing + codes) - see the
- // gxepd2-diag-fill-spec memory. Packing geometry and the colour->code table
- // live in the vendor wrapper's draw path (color4()/color7() and _pixel_buffer
- // are private), so we derive them by observation: drive the wrapper's
- // fillScreen/drawPixel and read the emitted wire bytes (writeNative output).
- // Host-only, no hardware.
- //
- // B2 colour->code: fillScreen(colour) -> every byte is that colour's code
- // replicated across its pixel fields; read byte0, all fields equal = the
- // code. The set of codes across the named colours IS the palette.
- // B1 intra-byte order: fill background=black, set pixels 0..ppb-1 of row 0
- // to distinct-code colours; byte0's fields (read MSB-first) reveal which
- // pixel lands in which field -> MSB-first vs LSB-first, and confirms ppb.
- #include <cstdint>
- #include <cstdio>
- #include <vector>
- #include "spy/Arduino.h"
- #include "spy/SPI.h"
- #include "trace.h"
- template<typename Driver> struct DiagBPeek : Driver {
- using Driver::Driver;
- int busy_level() const { return this->_busy_level; }
- };
- // First plane-sized burst from the record (any content - unlike Pattern A we
- // do NOT skip uniform bursts, since a fillScreen burst is what B2 reads).
- inline std::vector<uint8_t> diagB_first_burst(uint32_t plane_bytes) {
- std::vector<uint8_t> run, best;
- auto flush = [&]() {
- if (run.size() == plane_bytes && best.empty())
- best = run;
- run.clear();
- };
- for (const auto &ev : Trace::record) {
- if (ev.first)
- flush();
- else
- run.push_back(ev.second);
- }
- flush();
- return best;
- }
- // Decode a byte's pixel fields, MSB field first (field 0 = highest bits).
- inline void diagB_fields(uint8_t byte, int bpp, int ppb, uint8_t out[]) {
- uint8_t mask = (uint8_t) ((1u << bpp) - 1);
- for (int j = 0; j < ppb; j++)
- out[j] = (byte >> ((ppb - 1 - j) * bpp)) & mask;
- }
- template<typename WrapperT, typename DriverT> void run_diagB(const char *name, uint16_t W, uint16_t H, int bpp) {
- const int ppb = 8 / bpp;
- const uint32_t plane_bytes = (uint32_t) W * H * bpp / 8; // per region/page
- printf("=== %s: DIAGNOSTIC FILL (Pattern B: packing + codes) bpp=%d ppb=%d ===\n", name, bpp, ppb);
- // Heap-allocate: the wrapper embeds a full-page _pixel_buffer, which for a
- // 7-colour panel (4bpp, e.g. 600x448 -> ~134KB) is too big for the stack.
- WrapperT *dp = new WrapperT(DriverT(10, 11, 12, 13));
- WrapperT &display = *dp;
- Trace::set_dc_pin(11);
- Trace::last_busy_release_value = display.epd2.busy_level() == HIGH ? LOW : HIGH;
- display.epd2.selectSPI(SPI, SPISettings());
- display.init(0);
- display.setFullWindow();
- // Prime (untracked) so the first-page screen-buffer prime doesn't land in a
- // recorded burst.
- display.firstPage();
- do {
- display.fillScreen(GxEPD_WHITE);
- } while (display.nextPage());
- struct NC {
- uint16_t color;
- const char *label;
- };
- const NC colors[] = {{GxEPD_BLACK, "black"}, {GxEPD_WHITE, "white"}, {GxEPD_RED, "red"}, {GxEPD_YELLOW, "yellow"},
- {GxEPD_GREEN, "green"}, {GxEPD_BLUE, "blue"}, {GxEPD_ORANGE, "orange"}};
- const int NCOL = (int) (sizeof(colors) / sizeof(colors[0]));
- uint8_t code_of[16] = {0};
- printf(" [B2] colour -> code (fillScreen, byte0 replicated field):\n");
- for (int k = 0; k < NCOL; k++) {
- Trace::start_record();
- display.firstPage();
- do {
- display.fillScreen(colors[k].color);
- } while (display.nextPage());
- Trace::stop_record();
- auto b = diagB_first_burst(plane_bytes);
- uint8_t byte0 = b.empty() ? 0 : b[0];
- uint8_t f[8];
- diagB_fields(byte0, bpp, ppb, f);
- bool uniform = true;
- for (int j = 1; j < ppb; j++)
- if (f[j] != f[0])
- uniform = false;
- code_of[k] = f[0];
- printf(" %-7s byte0=0x%02X code=%u%s\n", colors[k].label, byte0, f[0],
- uniform ? "" : " (WARN fields not uniform: bpp/ppb mismatch)");
- }
- auto code_for = [&](uint16_t col) -> int {
- for (int k = 0; k < NCOL; k++)
- if (colors[k].color == col)
- return code_of[k];
- return -1;
- };
- // [B1] packing order across bytes. Place one pixel per DISTINCT code along
- // row 0 (so every code the panel has is exercised - 4 on a 4-colour panel,
- // all 7 on ACeP), spanning ceil(ndist*bpp/8) bytes. Verify each pixel i
- // lands in byte i/ppb, field i%ppb (MSB-first), which also confirms the
- // byte-to-byte stride, not just intra-byte order.
- struct PC {
- uint16_t color;
- uint8_t code;
- const char *label;
- };
- PC distinct[16];
- int nd = 0;
- for (int k = 0; k < NCOL; k++) {
- bool seen = false;
- for (int j = 0; j < nd; j++)
- if (distinct[j].code == code_of[k])
- seen = true;
- if (!seen)
- distinct[nd++] = {colors[k].color, code_of[k], colors[k].label};
- }
- (void) code_for;
- int nbytes = (nd * bpp + 7) / 8;
- printf(" [B1] packing order: %d distinct-code pixels across %d byte(s):\n", nd, nbytes);
- Trace::start_record();
- display.firstPage();
- do {
- display.fillScreen(GxEPD_BLACK);
- for (int i = 0; i < nd; i++)
- display.drawPixel(i, 0, distinct[i].color);
- } while (display.nextPage());
- Trace::stop_record();
- auto b = diagB_first_burst(plane_bytes);
- bool msb_first = true, lsb_first = true;
- for (int i = 0; i < nd; i++) {
- int bi = i / ppb, fi = i % ppb;
- uint8_t f[8];
- diagB_fields(bi < (int) b.size() ? b[bi] : 0, bpp, ppb, f);
- if ((int) f[fi] != distinct[i].code)
- msb_first = false;
- if ((int) f[ppb - 1 - fi] != distinct[i].code)
- lsb_first = false;
- }
- printf(" bytes:");
- for (int t = 0; t < nbytes && t < (int) b.size(); t++)
- printf(" 0x%02X", b[t]);
- printf("\n pixels (label=code):");
- for (int i = 0; i < nd; i++)
- printf(" %s=%u", distinct[i].label, distinct[i].code);
- printf("\n => %s\n", msb_first ? "MSB-first: pixel i -> byte i/ppb, field i%ppb (shift=(ppb-1-f)*bpp)"
- : lsb_first ? "LSB-first within byte"
- : "UNKNOWN packing order");
- delete dp;
- }
|