| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- #pragma once
- // 7-colour (ACeP, 4bpp packed) palette/blank-code resolver for the descriptor
- // emitter. Same approach as emit_palette_4c.h: drive the vendor driver's own
- // GxEPD2_7C wrapper (fillScreen of each named colour), read the wire code it
- // emits for each, then CONFIRM that table matches the engine's
- // GXEPD2_PALETTE_7C codes (epd_command.h) before referencing it. A panel
- // whose codes don't match stays unresolved and emits the old TODO plus the
- // observed table, so a surprise is visible, never silently mislabelled.
- //
- // Unlike 4-colour, GxEPD2_7C's color7() maps all seven GxEPD_* constants to
- // distinct codes (no collisions), so EXPECT_7C below is a 1:1 check against
- // GXEPD2_PALETTE_7C rather than a many-to-one map.
- //
- // Include order (tools/capture.py's generated 7C main handles this): the
- // driver header and GxEPD2_7C.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_7C.h"
- #include "emit_descriptor.h"
- #include "trace.h"
- namespace emit {
- // The engine's canonical 7-colour map (mirrors epd_command.h GXEPD2_PALETTE_7C
- // / GxEPD2_7C.h color7()): the wire code each named colour must resolve to
- // for the standard palette to be the right answer.
- struct Expect7C {
- uint16_t color;
- uint8_t code;
- const char *label;
- };
- inline const Expect7C EXPECT_7C[] = {
- {GxEPD_BLACK, 0x00, "black"}, {GxEPD_WHITE, 0x01, "white"},
- {GxEPD_GREEN, 0x02, "green"}, {GxEPD_BLUE, 0x03, "blue"},
- {GxEPD_RED, 0x04, "red"}, {GxEPD_YELLOW, 0x05, "yellow"},
- {GxEPD_ORANGE, 0x06, "orange"},
- };
- // First run of exactly plane_bytes data bytes in the current Trace::record
- // (one fillScreen flush), or empty. Mirrors emit_palette_4c.h / run_diagB_
- // harness.h.
- inline std::vector<uint8_t> first_plane_burst_7c(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 7-colour packed resolver for `Driver`. It instantiates the vendor
- // GxEPD2_7C<Peek<Driver>, HEIGHT> wrapper (single full-height page) and reads
- // each colour's code off the wire.
- template<typename Driver> PackedResolverFn make_resolver_7c() {
- return [](uint8_t /*clear_fill*/, const PlaneInvert & /*pv*/) -> PackedResolved {
- constexpr int bpp = 4, ppb = 2; // 7-colour: 4 bits/pixel, 2 pixels/byte, MSB-first (high nibble first)
- const uint16_t W = Driver::WIDTH, H = Driver::HEIGHT;
- const uint32_t plane_bytes = (uint32_t) W * H * bpp / 8;
- using Wrapper = GxEPD2_7C<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_7c(plane_bytes);
- if (b.empty())
- return -1;
- uint8_t byte0 = b[0];
- uint8_t f0 = (byte0 >> ((ppb - 1) * bpp)) & 0xF; // MSB field (high nibble)
- uniform = true;
- for (int j = 1; j < ppb; j++)
- if (((byte0 >> ((ppb - 1 - j) * bpp)) & 0xF) != f0)
- uniform = false;
- return f0;
- };
- const int ncol = (int) (sizeof(EXPECT_7C) / sizeof(EXPECT_7C[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_7C[k].color, uniform);
- char buf[48];
- snprintf(buf, sizeof(buf), "%s%s=%d", k ? " " : "", EXPECT_7C[k].label, code);
- observed += buf;
- if (EXPECT_7C[k].color == GxEPD_WHITE)
- white_code = code;
- if (!uniform || code != (int) EXPECT_7C[k].code)
- matches = false;
- }
- delete dp;
- PackedResolved r;
- if (matches) {
- r.resolved = true;
- r.bits_per_pixel = bpp;
- r.color_count = ncol;
- r.palette_expr = "GXEPD2_PALETTE_7C";
- r.palette_len_expr = "GXEPD2_PALETTE_7C_LEN";
- r.blank_code = white_code; // panel's white/clear code (0x01 for GxEPD2 7C)
- } else {
- r.resolved = false;
- r.note = "7C probe mismatch, observed [" + observed +
- "] (expected black=0 white=1 green=2 blue=3 red=4 yellow=5 orange=6)";
- }
- return r;
- };
- }
- // 7-colour capture entry: like emit_descriptor(), but resolves the packed
- // fields by observing the GxEPD2_7C wrapper. tools/capture.py calls this for
- // an epd7c/ driver.
- template<typename Driver> void emit_descriptor_7c(const char *class_name, const char *macro) {
- emit_descriptor_impl<Driver>(class_name, macro, make_resolver_7c<Driver>());
- }
- } // namespace emit
|