|
|
@@ -24,7 +24,9 @@
|
|
|
#include <cstdint>
|
|
|
#include <cstdio>
|
|
|
#include <cstdlib>
|
|
|
+#include <functional>
|
|
|
#include <set>
|
|
|
+#include <string>
|
|
|
#include <type_traits>
|
|
|
#include <utility>
|
|
|
#include <vector>
|
|
|
@@ -148,6 +150,37 @@ struct PlaneInvert {
|
|
|
int w0_white = -1, w1_red = -1;
|
|
|
};
|
|
|
|
|
|
+// A packed panel's depth / palette / blank code, resolved by observation for
|
|
|
+// the panel families the emitter knows how to probe (currently 4-colour, via
|
|
|
+// emit_palette_4c.h). `emit_descriptor_impl` calls a resolver of this shape
|
|
|
+// only for a packed panel (the plane-invert probe said white fill isn't an
|
|
|
+// all-bits wire byte); a null or unresolved result keeps the old TODO fields,
|
|
|
+// so an unknown packed family still emits a reviewable placeholder rather than
|
|
|
+// a wrong palette.
|
|
|
+struct PackedResolved {
|
|
|
+ bool resolved = false; // false => emit TODO placeholders
|
|
|
+ int bits_per_pixel = 2; // 2 (4-colour) or 4 (7-colour)
|
|
|
+ const char *palette_expr = "nullptr"; // e.g. "GXEPD2_PALETTE_4C"
|
|
|
+ const char *palette_len_expr = "0"; // e.g. "GXEPD2_PALETTE_4C_LEN"
|
|
|
+ int blank_code = 0; // per-pixel clear code (observed white)
|
|
|
+ std::string note; // appended as a comment (observed table, or why unresolved)
|
|
|
+};
|
|
|
+using PackedResolverFn = std::function<PackedResolved(uint8_t clear_fill, const PlaneInvert &pv)>;
|
|
|
+
|
|
|
+// A generous, class-aware default for busy_timeout_ms. The timeout is only a
|
|
|
+// diagnostic ceiling - the engine hard-aborts a busy-wait that runs past it and
|
|
|
+// logs a failure, where GxEPD2 itself just keeps polling - so erring long costs
|
|
|
+// nothing on a healthy panel and the exact number is meant to be dev-tuned. The
|
|
|
+// vendor full_refresh_time is a soft estimate, so we take 2x it, then clamp up
|
|
|
+// to a per-depth floor so a panel that under-reports (or whose colour pass is
|
|
|
+// genuinely slow) still gets room: mono/3-colour fail fast, 4-colour and
|
|
|
+// 7-colour (ACeP) get progressively more headroom.
|
|
|
+inline uint32_t default_busy_timeout_ms(unsigned full_refresh_time, int bits_per_pixel) {
|
|
|
+ uint32_t headroom = static_cast<uint32_t>(full_refresh_time) * 2;
|
|
|
+ uint32_t floor = bits_per_pixel >= 4 ? 60000u : bits_per_pixel == 2 ? 45000u : 15000u;
|
|
|
+ return headroom > floor ? headroom : floor;
|
|
|
+}
|
|
|
+
|
|
|
// 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
|
|
|
@@ -186,7 +219,8 @@ template<typename Driver> PlaneInvert probe_plane_invert_values(uint8_t p0cmd, u
|
|
|
return r;
|
|
|
}
|
|
|
|
|
|
-template<typename Driver> void emit_descriptor(const char *class_name, const char *macro) {
|
|
|
+template<typename Driver>
|
|
|
+void emit_descriptor_impl(const char *class_name, const char *macro, const PackedResolverFn &resolve_packed) {
|
|
|
constexpr int16_t CS = 10, DC = 11, RST = 12, BUSY = 13;
|
|
|
Trace::set_dc_pin(DC);
|
|
|
Peek<Driver> driver(CS, DC, RST, BUSY);
|
|
|
@@ -354,6 +388,14 @@ template<typename Driver> void emit_descriptor(const char *class_name, const cha
|
|
|
const uint8_t p1cmd = plane_cmds.size() > 1 ? plane_cmds[1] : 0x00;
|
|
|
const PlaneInvert pv = probe_plane_invert_values<Driver>(p0cmd, p1cmd, nplanes);
|
|
|
|
|
|
+ // Packed-family resolution (4-colour palette etc.), run once here so both the
|
|
|
+ // busy-timeout class and the packed fields below use the same answer. 1bpp
|
|
|
+ // panels skip it. An unresolved packed panel (no family resolver, e.g.
|
|
|
+ // 7-colour) is treated as the slow class for the timeout floor.
|
|
|
+ const PackedResolved pr =
|
|
|
+ (!pv.is_1bpp && resolve_packed) ? resolve_packed(clear_fill, pv) : PackedResolved{};
|
|
|
+ const int depth_bpp = pv.is_1bpp ? 1 : (pr.resolved ? pr.bits_per_pixel : 4);
|
|
|
+
|
|
|
// Refresh = post-write commands after the LAST burst (e.g. UC8179's 0x92),
|
|
|
// then the refresh call minus any window-set it repeats.
|
|
|
std::set<uint8_t> all_wops;
|
|
|
@@ -370,9 +412,11 @@ template<typename Driver> void emit_descriptor(const char *class_name, const cha
|
|
|
refresh_seq.push_back(c);
|
|
|
|
|
|
// --- emit ---
|
|
|
- printf("// GENERATED by emit_descriptor from %s - candidate, segmentation only.\n", class_name);
|
|
|
- printf("// bits_per_pixel/palette/plane_invert/plane_width/line_order still\n");
|
|
|
- printf("// come from the diag passes.\n");
|
|
|
+ printf("// GENERATED by emit_descriptor from %s - candidate.\n", class_name);
|
|
|
+ printf("// Command sequences, plane layout and plane_invert are observed here;\n");
|
|
|
+ printf("// for a packed panel bits_per_pixel/palette/blank_code are probe-filled\n");
|
|
|
+ printf("// when the colour family is recognised (4-colour), else left TODO.\n");
|
|
|
+ printf("// plane_width / line_order (dual-region) still come from the diag passes.\n");
|
|
|
printf("#pragma once\n#include \"epd_command.h\"\n\nnamespace esphome::gxepd2 {\n\n");
|
|
|
|
|
|
auto emit_seq = [&](const char *seqsuffix, std::vector<Cmd> &cmds) {
|
|
|
@@ -403,7 +447,11 @@ template<typename Driver> void emit_descriptor(const char *class_name, const cha
|
|
|
printf(" .width = %u,\n", (unsigned) Driver::WIDTH);
|
|
|
printf(" .height = %u,\n", (unsigned) Driver::HEIGHT);
|
|
|
printf(" .busy_active_level = %s,\n", bl == HIGH ? "true" : "false");
|
|
|
- printf(" .busy_timeout_ms = %u,\n", (unsigned) Driver::full_refresh_time + 5000u);
|
|
|
+ const char *depth_class = depth_bpp >= 4 ? "4bpp 7-colour" : depth_bpp == 2 ? "2bpp 4-colour" : "1bpp mono/3-colour";
|
|
|
+ printf(" .busy_timeout_ms = %u, // generous diagnostic ceiling (vendor full_refresh_time %ums, "
|
|
|
+ "%s class); dev-tunable\n",
|
|
|
+ (unsigned) default_busy_timeout_ms(Driver::full_refresh_time, depth_bpp),
|
|
|
+ (unsigned) Driver::full_refresh_time, depth_class);
|
|
|
printf(" .init_sequence = %s_INIT,\n", macro);
|
|
|
printf(" .init_sequence_len = sizeof(%s_INIT) / sizeof(%s_INIT[0]),\n", macro, macro);
|
|
|
if (per_plane) {
|
|
|
@@ -438,16 +486,35 @@ template<typename Driver> void emit_descriptor(const char *class_name, const cha
|
|
|
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);
|
|
|
+ // Packed panel. If a family resolver recognised it (4-colour, via
|
|
|
+ // emit_palette_4c.h), it derived depth/palette/blank_code by observing the
|
|
|
+ // driver's own colour fills - emit those (computed once, above). Otherwise
|
|
|
+ // fall back to a TODO the diag pass fills in by hand (an unknown packed
|
|
|
+ // family, e.g. 7-colour before its resolver exists).
|
|
|
+ if (pr.resolved) {
|
|
|
+ printf(" .bits_per_pixel = %d, // probe: %d-colour packed%s%s\n", pr.bits_per_pixel,
|
|
|
+ 1 << pr.bits_per_pixel, pr.note.empty() ? "" : " - ", pr.note.c_str());
|
|
|
+ printf(" .palette = %s,\n", pr.palette_expr);
|
|
|
+ printf(" .palette_len = %s,\n", pr.palette_len_expr);
|
|
|
+ printf(" .blank_code = 0x%02X, // observed white fill code\n", pr.blank_code & 0xFF);
|
|
|
+ } else {
|
|
|
+ printf(" .bits_per_pixel = 2, // TODO: diag pass - PACKED (white fill wire 0x%02X, not 1bpp)%s%s\n",
|
|
|
+ pv.w0_white & 0xFF, pr.note.empty() ? "" : " - ", pr.note.c_str());
|
|
|
+ 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_len = sizeof(%s_REFRESH) / sizeof(%s_REFRESH[0]),\n", macro, macro);
|
|
|
printf("};\n\n} // namespace esphome::gxepd2\n");
|
|
|
}
|
|
|
|
|
|
+// Generic entry: no packed-family resolver, so a packed panel's
|
|
|
+// depth/palette/blank_code emit as TODO placeholders (the pre-resolver
|
|
|
+// behaviour). The 4-colour entry is emit_descriptor_4c() in emit_palette_4c.h.
|
|
|
+template<typename Driver> void emit_descriptor(const char *class_name, const char *macro) {
|
|
|
+ emit_descriptor_impl<Driver>(class_name, macro, nullptr);
|
|
|
+}
|
|
|
+
|
|
|
} // namespace emit
|