|
@@ -0,0 +1,150 @@
|
|
|
|
|
+#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
|