| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- #pragma once
- // 4-colour (BWRY, 2bpp packed) palette/blank-code resolver for the descriptor
- // emitter. This is the diag Pattern-B colour->code derivation (run_diagB_
- // harness.h) folded into the one-command capture, so a 4-colour single-region
- // panel comes out of tools/capture.py complete - bits_per_pixel / palette /
- // blank_code filled - instead of carrying TODOs a human pastes in afterwards.
- //
- // It stays observation-based, not a hardcode: it drives the vendor driver's
- // own GxEPD2_4C wrapper (fillScreen of each named colour) and reads the wire
- // code the driver emits for each, then CONFIRMS that table matches the engine's
- // GXEPD2_PALETTE_4C codes (epd_command.h) before referencing it. A panel whose
- // codes don't match - a non-standard 4-colour map, or something that isn't
- // really 2bpp - stays unresolved and emits the old TODO plus the observed
- // table, so a surprise is visible, never silently mislabelled.
- //
- // Include order (tools/capture.py's generated 4C main handles this): the driver
- // header and GxEPD2_4C.h first (they define GxEPD_* and the WIDTH/HEIGHT the
- // wrapper references unqualified), then emit_descriptor.h, then this file.
- #include <cstdint>
- #include <cstdio>
- #include <string>
- #include <vector>
- #include "GxEPD2_4C.h"
- #include "emit_descriptor.h"
- #include "trace.h"
- namespace emit {
- // The engine's canonical 4-colour map (mirrors epd_command.h GXEPD2_PALETTE_4C
- // / GxEPD2_4C.h color4()): the wire code each named colour must resolve to for
- // the standard palette to be the right answer.
- struct Expect4C {
- uint16_t color;
- uint8_t code;
- const char *label;
- };
- inline const Expect4C EXPECT_4C[] = {
- {GxEPD_BLACK, 0x00, "black"}, {GxEPD_WHITE, 0x01, "white"}, {GxEPD_YELLOW, 0x02, "yellow"},
- {GxEPD_GREEN, 0x02, "green"}, {GxEPD_ORANGE, 0x02, "orange"}, {GxEPD_RED, 0x03, "red"},
- {GxEPD_BLUE, 0x00, "blue"},
- };
- // First run of exactly plane_bytes data bytes in the current Trace::record
- // (one fillScreen flush), or empty. Mirrors run_diagB_harness.h.
- inline std::vector<uint8_t> first_plane_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) // command byte: ends the current data run
- flush();
- else
- run.push_back(ev.second);
- }
- flush();
- return best;
- }
- // Build the 4-colour packed resolver for `Driver`. It instantiates the vendor
- // GxEPD2_4C<Peek<Driver>, HEIGHT> wrapper (single full-height page) and reads
- // each colour's code off the wire.
- template<typename Driver> PackedResolverFn make_resolver_4c() {
- return [](uint8_t /*clear_fill*/, const PlaneInvert & /*pv*/) -> PackedResolved {
- constexpr int bpp = 2, ppb = 4; // 4-colour: 2 bits/pixel, 4 pixels/byte, MSB-first
- const uint16_t W = Driver::WIDTH, H = Driver::HEIGHT;
- const uint32_t plane_bytes = (uint32_t) W * H * bpp / 8;
- using Wrapper = GxEPD2_4C<Peek<Driver>, Driver::HEIGHT>; // page_height=HEIGHT => one page
- Wrapper *dp = new Wrapper(Peek<Driver>(10, 11, 12, 13));
- Wrapper &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 buffer prime isn't read as a fill.
- display.firstPage();
- do {
- display.fillScreen(GxEPD_WHITE);
- } while (display.nextPage());
- auto code_of = [&](uint16_t color, bool &uniform) -> int {
- Trace::start_record();
- display.firstPage();
- do {
- display.fillScreen(color);
- } while (display.nextPage());
- Trace::stop_record();
- auto b = first_plane_burst(plane_bytes);
- if (b.empty())
- return -1;
- uint8_t byte0 = b[0];
- uint8_t f0 = (byte0 >> ((ppb - 1) * bpp)) & 0x3; // MSB field
- uniform = true;
- for (int j = 1; j < ppb; j++)
- if (((byte0 >> ((ppb - 1 - j) * bpp)) & 0x3) != f0)
- uniform = false;
- return f0;
- };
- const int ncol = (int) (sizeof(EXPECT_4C) / sizeof(EXPECT_4C[0]));
- bool matches = true;
- int white_code = -1;
- std::string observed;
- for (int k = 0; k < ncol; k++) {
- bool uniform = false;
- int code = code_of(EXPECT_4C[k].color, uniform);
- char buf[48];
- snprintf(buf, sizeof(buf), "%s%s=%d", k ? " " : "", EXPECT_4C[k].label, code);
- observed += buf;
- if (EXPECT_4C[k].color == GxEPD_WHITE)
- white_code = code;
- if (!uniform || code != (int) EXPECT_4C[k].code)
- matches = false;
- }
- delete dp;
- PackedResolved r;
- if (matches) {
- r.resolved = true;
- r.bits_per_pixel = bpp;
- r.palette_expr = "GXEPD2_PALETTE_4C";
- r.palette_len_expr = "GXEPD2_PALETTE_4C_LEN";
- r.blank_code = white_code; // panel's white/clear code (0x01 for GxEPD2 4C)
- } else {
- r.resolved = false;
- r.note = "4C probe mismatch, observed [" + observed + "] (expected black=0 white=1 yellow/green/orange=2 red=3 blue=0)";
- }
- return r;
- };
- }
- // 4-colour capture entry: like emit_descriptor(), but resolves the packed
- // fields by observing the GxEPD2_4C wrapper. tools/capture.py calls this for an
- // epd4c/ driver.
- template<typename Driver> void emit_descriptor_4c(const char *class_name, const char *macro) {
- emit_descriptor_impl<Driver>(class_name, macro, make_resolver_4c<Driver>());
- }
- } // namespace emit
|