|
@@ -25,6 +25,8 @@
|
|
|
#include <cstdio>
|
|
#include <cstdio>
|
|
|
#include <cstdlib>
|
|
#include <cstdlib>
|
|
|
#include <set>
|
|
#include <set>
|
|
|
|
|
+#include <type_traits>
|
|
|
|
|
+#include <utility>
|
|
|
#include <vector>
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "spy/Arduino.h"
|
|
#include "spy/Arduino.h"
|
|
@@ -124,6 +126,66 @@ inline void print_cmd(const char *macro, const char *suffix, const Cmd &c) {
|
|
|
printf("},\n");
|
|
printf("},\n");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 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;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Detects the two-arg writeScreenBuffer(black_value, color_value) that colour
|
|
|
|
|
+// (plane_count > 1) drivers have and mono drivers don't.
|
|
|
|
|
+template<typename T, typename = void> struct has_wsb2 : std::false_type {};
|
|
|
|
|
+template<typename T>
|
|
|
|
|
+struct has_wsb2<T, std::void_t<decltype(std::declval<T &>().writeScreenBuffer(uint8_t{}, uint8_t{}))>>
|
|
|
|
|
+ : std::true_type {};
|
|
|
|
|
+
|
|
|
|
|
+struct PlaneInvert {
|
|
|
|
|
+ bool inv0 = false, inv1 = false;
|
|
|
|
|
+ bool plane1_valid = false;
|
|
|
|
|
+ bool is_1bpp = false; // white fill => an all-bits-same wire byte (not packed)
|
|
|
|
|
+ int w0_white = -1, w1_red = -1;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// plane_invert by capture (see probe_plane_invert.h for the rationale): drive
|
|
|
|
|
+// known full-screen fills and read each plane's wire byte, reconciling against
|
|
|
|
|
+// the engine's fixed buffer convention (plane0 bit=white, plane1 bit=red). A
|
|
|
|
|
+// white-fill plane0 wire that isn't 0x00/0xFF means a packed (>1bpp) panel,
|
|
|
|
|
+// where plane_invert is unused and bits_per_pixel/palette come from the diag
|
|
|
|
|
+// pass instead.
|
|
|
|
|
+template<typename Driver> PlaneInvert probe_plane_invert_values(uint8_t p0cmd, uint8_t p1cmd, size_t plane_count) {
|
|
|
|
|
+ constexpr int16_t CS = 10, DC = 11, RST = 12, BUSY = 13;
|
|
|
|
|
+ Trace::set_dc_pin(DC);
|
|
|
|
|
+ Peek<Driver> d(CS, DC, RST, BUSY);
|
|
|
|
|
+ Trace::last_busy_release_value = d.busy_level() == HIGH ? LOW : HIGH;
|
|
|
|
|
+ d.selectSPI(SPI, SPISettings());
|
|
|
|
|
+ d.init(0);
|
|
|
|
|
+
|
|
|
|
|
+ PlaneInvert r;
|
|
|
|
|
+ auto cap0 = [&](uint8_t value) -> int {
|
|
|
|
|
+ Trace::start_event_record();
|
|
|
|
|
+ d.writeScreenBuffer(value); // 1-arg form all drivers have; colour defaults blank
|
|
|
|
|
+ Trace::stop_event_record();
|
|
|
|
|
+ return burst_byte_after(group(0, Trace::events.size()), p0cmd);
|
|
|
|
|
+ };
|
|
|
|
|
+ r.w0_white = cap0(0xFF);
|
|
|
|
|
+ r.is_1bpp = (r.w0_white == 0x00 || r.w0_white == 0xFF);
|
|
|
|
|
+ r.inv0 = (r.w0_white == 0x00); // engine plane0 bit=white; white wire 0x00 => invert
|
|
|
|
|
+
|
|
|
|
|
+ if (plane_count > 1) {
|
|
|
|
|
+ if constexpr (has_wsb2<Driver>::value) {
|
|
|
|
|
+ Trace::start_event_record();
|
|
|
|
|
+ d.writeScreenBuffer((uint8_t) 0xFF, (uint8_t) 0x00); // all-red fill
|
|
|
|
|
+ Trace::stop_event_record();
|
|
|
|
|
+ r.w1_red = burst_byte_after(group(0, Trace::events.size()), p1cmd);
|
|
|
|
|
+ r.plane1_valid = (r.w1_red == 0x00 || r.w1_red == 0xFF);
|
|
|
|
|
+ r.inv1 = (r.w1_red == 0x00); // engine plane1 bit=red; red wire 0x00 => invert
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return r;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
template<typename Driver> void emit_descriptor(const char *class_name, const char *macro) {
|
|
template<typename Driver> void emit_descriptor(const char *class_name, const char *macro) {
|
|
|
constexpr int16_t CS = 10, DC = 11, RST = 12, BUSY = 13;
|
|
constexpr int16_t CS = 10, DC = 11, RST = 12, BUSY = 13;
|
|
|
Trace::set_dc_pin(DC);
|
|
Trace::set_dc_pin(DC);
|
|
@@ -287,6 +349,11 @@ template<typename Driver> void emit_descriptor(const char *class_name, const cha
|
|
|
const size_t cip = plane_index(init_stage);
|
|
const size_t cip = plane_index(init_stage);
|
|
|
const uint8_t clear_fill = (cip < init_stage.size() && !init_stage[cip].data.empty()) ? init_stage[cip].data[0] : 0x00;
|
|
const uint8_t clear_fill = (cip < init_stage.size() && !init_stage[cip].data.empty()) ? init_stage[cip].data[0] : 0x00;
|
|
|
|
|
|
|
|
|
|
+ // plane_invert + 1bpp/packed detection, derived by the fill-capture probe.
|
|
|
|
|
+ const uint8_t p0cmd = plane_cmds.empty() ? 0x00 : plane_cmds[0];
|
|
|
|
|
+ const uint8_t p1cmd = plane_cmds.size() > 1 ? plane_cmds[1] : 0x00;
|
|
|
|
|
+ const PlaneInvert pv = probe_plane_invert_values<Driver>(p0cmd, p1cmd, nplanes);
|
|
|
|
|
+
|
|
|
// Refresh = post-write commands after the LAST burst (e.g. UC8179's 0x92),
|
|
// Refresh = post-write commands after the LAST burst (e.g. UC8179's 0x92),
|
|
|
// then the refresh call minus any window-set it repeats.
|
|
// then the refresh call minus any window-set it repeats.
|
|
|
std::set<uint8_t> all_wops;
|
|
std::set<uint8_t> all_wops;
|
|
@@ -348,15 +415,31 @@ template<typename Driver> void emit_descriptor(const char *class_name, const cha
|
|
|
printf(" .window_sequence_len = {sizeof(%s_WINDOW_SEQ0) / sizeof(%s_WINDOW_SEQ0[0]), 0},\n", macro, macro);
|
|
printf(" .window_sequence_len = {sizeof(%s_WINDOW_SEQ0) / sizeof(%s_WINDOW_SEQ0[0]), 0},\n", macro, macro);
|
|
|
}
|
|
}
|
|
|
printf(" .plane_count = %zu,\n", nplanes);
|
|
printf(" .plane_count = %zu,\n", nplanes);
|
|
|
- printf(" .plane_write_cmd = {0x%02X, 0x%02X},\n", plane_cmds.empty() ? 0x00 : plane_cmds[0],
|
|
|
|
|
- plane_cmds.size() > 1 ? plane_cmds[1] : 0x00);
|
|
|
|
|
- printf(" .plane_invert = {false, false}, // TODO: diag pass\n");
|
|
|
|
|
- printf(" .bits_per_pixel = 1, // TODO: diag pass (clear fill byte observed: 0x%02X)\n", clear_fill);
|
|
|
|
|
- printf(" .palette = nullptr, // TODO: diag pass\n");
|
|
|
|
|
- printf(" .palette_len = 0,\n");
|
|
|
|
|
|
|
+ printf(" .plane_write_cmd = {0x%02X, 0x%02X},\n", p0cmd, p1cmd);
|
|
|
|
|
+ // Fields below are emitted in struct-declaration order (plane_invert,
|
|
|
|
|
+ // plane_width, row_interleave, bits_per_pixel, palette, palette_len,
|
|
|
|
|
+ // blank_code) so the designated initializer stays well-formed.
|
|
|
|
|
+ if (pv.is_1bpp) {
|
|
|
|
|
+ const bool i1 = (nplanes > 1 && pv.plane1_valid) ? pv.inv1 : false;
|
|
|
|
|
+ printf(" .plane_invert = {%s, %s}, // probe-derived\n", pv.inv0 ? "true" : "false", i1 ? "true" : "false");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ printf(" .plane_invert = {false, false}, // unused: packed panel\n");
|
|
|
|
|
+ }
|
|
|
if (per_plane)
|
|
if (per_plane)
|
|
|
printf(" .plane_width = 0, // TODO: diag pass (separate-region width)\n");
|
|
printf(" .plane_width = 0, // TODO: diag pass (separate-region width)\n");
|
|
|
- printf(" .blank_code = 0x00, // TODO: derive from clear fill 0x%02X\n", clear_fill);
|
|
|
|
|
|
|
+ if (pv.is_1bpp) {
|
|
|
|
|
+ // 1bpp plane model: depth confirmed by the probe; palette/blank_code unused.
|
|
|
|
|
+ printf(" .bits_per_pixel = 1, // probe: white fill => all-bits wire byte 0x%02X\n", pv.w0_white & 0xFF);
|
|
|
|
|
+ printf(" .palette = nullptr,\n");
|
|
|
|
|
+ printf(" .palette_len = 0,\n");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Packed panel: depth/palette/blank_code are the diag pass's job.
|
|
|
|
|
+ printf(" .bits_per_pixel = 2, // TODO: diag pass - PACKED (white fill wire 0x%02X, not 1bpp)\n",
|
|
|
|
|
+ pv.w0_white & 0xFF);
|
|
|
|
|
+ printf(" .palette = nullptr, // TODO: diag pass\n");
|
|
|
|
|
+ printf(" .palette_len = 0,\n");
|
|
|
|
|
+ printf(" .blank_code = 0x00, // TODO: diag pass (clear fill observed 0x%02X)\n", clear_fill);
|
|
|
|
|
+ }
|
|
|
printf(" .refresh_sequence = %s_REFRESH,\n", macro);
|
|
printf(" .refresh_sequence = %s_REFRESH,\n", macro);
|
|
|
printf(" .refresh_sequence_len = sizeof(%s_REFRESH) / sizeof(%s_REFRESH[0]),\n", macro, macro);
|
|
printf(" .refresh_sequence_len = sizeof(%s_REFRESH) / sizeof(%s_REFRESH[0]),\n", macro, macro);
|
|
|
printf("};\n\n} // namespace esphome::gxepd2\n");
|
|
printf("};\n\n} // namespace esphome::gxepd2\n");
|