|
@@ -0,0 +1,75 @@
|
|
|
|
|
+#pragma once
|
|
|
|
|
+
|
|
|
|
|
+// Derive plane_invert by capture instead of analogy. plane_invert[k] is "does
|
|
|
|
|
+// the wire byte for plane k need to be the bit-complement of the engine's
|
|
|
|
|
+// buffer byte". The engine's buffer convention is fixed and known in code
|
|
|
|
|
+// (gxepd2_display.h draw_absolute_pixel_internal):
|
|
|
|
|
+// plane 0 bit set = white/blank; plane 1 bit set = red.
|
|
|
|
|
+// So drive the vendor's two-arg writeScreenBuffer(black_value, color_value)
|
|
|
|
|
+// with known full-screen fills, record each plane's wire byte, and reconcile:
|
|
|
|
|
+// plane 0: engine sets the bit for WHITE, so if the vendor's wire byte for a
|
|
|
|
|
+// white fill is 0xFF (bits set) the conventions agree => invert=false.
|
|
|
|
|
+// plane 1: engine sets the bit for RED, so if the vendor's wire byte for a
|
|
|
|
|
+// red fill is 0xFF the conventions agree => invert=false.
|
|
|
|
|
+// A wire byte of 0x00 for the ink-present fill means the opposite => invert=true.
|
|
|
|
|
+//
|
|
|
|
|
+// Deterministic and panel-agnostic for 1bpp two-plane (3-colour) panels. For a
|
|
|
|
|
+// single-plane panel only plane 0 applies. Prototype companion to
|
|
|
|
|
+// emit_descriptor.h; not yet folded into it.
|
|
|
|
|
+
|
|
|
|
|
+#include <cstdint>
|
|
|
|
|
+#include <cstdio>
|
|
|
|
|
+
|
|
|
|
|
+#include "emit_descriptor.h" // emit::group, emit::Cmd, emit::Peek, spy includes
|
|
|
|
|
+
|
|
|
|
|
+namespace emit {
|
|
|
|
|
+
|
|
|
|
|
+// First data byte of the burst written right after command `op`, or -1.
|
|
|
|
|
+inline int burst_byte_after(const std::vector<Cmd> &cmds, uint8_t op) {
|
|
|
|
|
+ for (const auto &c : cmds)
|
|
|
|
|
+ if (c.cmd == op && c.data.size() >= PLANE_BURST_MIN)
|
|
|
|
|
+ return c.data[0];
|
|
|
|
|
+ return -1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+template<typename Driver> void probe_plane_invert(const char *name, uint8_t plane0_cmd, uint8_t plane1_cmd, int plane_count) {
|
|
|
|
|
+ constexpr int16_t CS = 10, DC = 11, RST = 12, BUSY = 13;
|
|
|
|
|
+ Trace::set_dc_pin(DC);
|
|
|
|
|
+ Peek<Driver> driver(CS, DC, RST, BUSY);
|
|
|
|
|
+ Trace::last_busy_release_value = driver.busy_level() == HIGH ? LOW : HIGH;
|
|
|
|
|
+ driver.selectSPI(SPI, SPISettings());
|
|
|
|
|
+ driver.init(0);
|
|
|
|
|
+
|
|
|
|
|
+ struct Fill {
|
|
|
|
|
+ const char *label;
|
|
|
|
|
+ uint8_t bv, cv;
|
|
|
|
|
+ };
|
|
|
|
|
+ // writeScreenBuffer(black_value, color_value): 0xFF = "blank" for each input.
|
|
|
|
|
+ const Fill fills[] = {{"white", 0xFF, 0xFF}, {"black", 0x00, 0xFF}, {"red", 0xFF, 0x00}};
|
|
|
|
|
+
|
|
|
|
|
+ int wire0[3], wire1[3];
|
|
|
|
|
+ printf("=== %s plane-invert probe ===\n", name);
|
|
|
|
|
+ for (int i = 0; i < 3; i++) {
|
|
|
|
|
+ Trace::start_event_record();
|
|
|
|
|
+ driver.writeScreenBuffer(fills[i].bv, fills[i].cv);
|
|
|
|
|
+ Trace::stop_event_record();
|
|
|
|
|
+ auto cmds = group(0, Trace::events.size());
|
|
|
|
|
+ wire0[i] = burst_byte_after(cmds, plane0_cmd);
|
|
|
|
|
+ wire1[i] = plane_count > 1 ? burst_byte_after(cmds, plane1_cmd) : -1;
|
|
|
|
|
+ printf(" fill=%-5s black_value=0x%02X color_value=0x%02X -> plane0 wire=0x%02X plane1 wire=0x%02X\n",
|
|
|
|
|
+ fills[i].label, fills[i].bv, fills[i].cv, wire0[i] & 0xFF, wire1[i] & 0xFF);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // plane 0 ink = black; engine bit is set for WHITE. White fill is index 0.
|
|
|
|
|
+ bool inv0 = (wire0[0] == 0x00); // white wire 0xFF => agree(false); 0x00 => invert
|
|
|
|
|
+ printf(" => plane_invert[0] = %s (white-fill plane0 wire=0x%02X; engine bit=white)\n", inv0 ? "true" : "false",
|
|
|
|
|
+ wire0[0] & 0xFF);
|
|
|
|
|
+ if (plane_count > 1) {
|
|
|
|
|
+ // plane 1 ink = red; engine bit is set for RED. Red fill is index 2.
|
|
|
|
|
+ bool inv1 = (wire1[2] == 0x00);
|
|
|
|
|
+ printf(" => plane_invert[1] = %s (red-fill plane1 wire=0x%02X; engine bit=red)\n", inv1 ? "true" : "false",
|
|
|
|
|
+ wire1[2] & 0xFF);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+} // namespace emit
|