|
@@ -0,0 +1,142 @@
|
|
|
|
|
+#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);
|
|
|
|
|
+
|
|
|
|
|
+ WrapperT display(DriverT(10, 11, 12, 13));
|
|
|
|
|
+ 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;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ printf(" [B1] intra-byte order (bg=black; px 0..%d = distinct-code colours):\n", ppb - 1);
|
|
|
|
|
+ const uint16_t probe[4] = {GxEPD_RED, GxEPD_YELLOW, GxEPD_WHITE, GxEPD_BLACK}; // codes 3,2,1,0 on 4-colour
|
|
|
|
|
+ Trace::start_record();
|
|
|
|
|
+ display.firstPage();
|
|
|
|
|
+ do {
|
|
|
|
|
+ display.fillScreen(GxEPD_BLACK);
|
|
|
|
|
+ for (int i = 0; i < ppb; i++)
|
|
|
|
|
+ display.drawPixel(i, 0, probe[i % 4]);
|
|
|
|
|
+ } 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);
|
|
|
|
|
+ printf(" byte0=0x%02X fields MSB..LSB:", byte0);
|
|
|
|
|
+ for (int j = 0; j < ppb; j++)
|
|
|
|
|
+ printf(" %u", f[j]);
|
|
|
|
|
+ printf("\n");
|
|
|
|
|
+
|
|
|
|
|
+ bool msb_first = true, lsb_first = true;
|
|
|
|
|
+ for (int i = 0; i < ppb; i++) {
|
|
|
|
|
+ int want = code_for(probe[i % 4]);
|
|
|
|
|
+ if ((int) f[i] != want)
|
|
|
|
|
+ msb_first = false; // pixel i in field i (MSB-first)
|
|
|
|
|
+ if ((int) f[ppb - 1 - i] != want)
|
|
|
|
|
+ lsb_first = false; // pixel i in field (ppb-1-i)
|
|
|
|
|
+ }
|
|
|
|
|
+ printf(" => %s\n", msb_first ? "MSB-first: pixel i in field i (shift=(ppb-1-i)*bpp)"
|
|
|
|
|
+ : lsb_first ? "LSB-first: pixel i in low fields"
|
|
|
|
|
+ : "UNKNOWN packing order");
|
|
|
|
|
+}
|