|
|
@@ -0,0 +1,138 @@
|
|
|
+#pragma once
|
|
|
+
|
|
|
+// Captured via tools/capture.py from
|
|
|
+// third_party/GxEPD2/src/epd7c/GxEPD2_730c_GDEP073E01.{h,cpp} (GDEP073E01,
|
|
|
+// controller unknown per the vendor header comment, 800x480, 7-color
|
|
|
+// Spectra6). Init/window/refresh command sequences are observed byte-for-
|
|
|
+// byte by the emitter, not hand-transcribed.
|
|
|
+//
|
|
|
+// FIRST panel on this engine's 4bpp/7-color path (bits_per_pixel=4,
|
|
|
+// GXEPD2_PALETTE_7C was scaffolded but never consumed before this).
|
|
|
+//
|
|
|
+// Two surprises the emitter flagged / the window sweep confirmed, both
|
|
|
+// genuinely new vs. the other 4 window-encoder families already seen
|
|
|
+// (gxepd2-window-encoders memory):
|
|
|
+//
|
|
|
+// 1. Non-standard 7-color code map: this driver's own
|
|
|
+// GxEPD2_730c_GDEP073E01::_convert_to_native() remaps the wire code
|
|
|
+// AFTER the vendor's GxEPD2_7C wrapper produces the standard
|
|
|
+// black/white/green/blue/red/yellow/orange = 0..6 codes. Driving the
|
|
|
+// real wrapper (emit_palette_7c.h) and reading the wire byte - not
|
|
|
+// hand-reading the switch statement - gave the true native codes:
|
|
|
+// black=0x00, white=0x01, green=0x06, blue=0x05, red=0x03, yellow=0x02,
|
|
|
+// orange=0x04 (white stays 0x01, only the other five rotate). Encoded
|
|
|
+// below as its own palette table rather than GXEPD2_PALETTE_7C.
|
|
|
+// 2. Asymmetric window-end encoding on its single 0x83 command: X is
|
|
|
+// inclusive-end (xe = x+w-1) but Y is exclusive-end (ye = y+h, one
|
|
|
+// more than X's convention) - confirmed by sweeping 5 distinct rects
|
|
|
+// (tools/gxepd2_harness/run_730c_gdep073e01.cpp) and seeing ye = y+h
|
|
|
+// hold at every point, including non-8-aligned y. The driver's own
|
|
|
+// comment on _setPartialRamArea calls this out: "controller issue (one
|
|
|
+// more)". Captured here as plain data bytes, so the asymmetry needs no
|
|
|
+// special engine support - just don't hand-rederive this window math
|
|
|
+// for a sibling panel and assume symmetric inclusive ends.
|
|
|
+//
|
|
|
+// Single plane, single region, plain row-major writeNative addressing (no
|
|
|
+// row_interleave, no window_per_plane) - structurally the simplest shape
|
|
|
+// once the above two facts are captured correctly.
|
|
|
+//
|
|
|
+// STATUS: structural only - not yet bench-verified (see display.py).
|
|
|
+
|
|
|
+#include "epd_command.h"
|
|
|
+
|
|
|
+namespace esphome::gxepd2 {
|
|
|
+
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_CMDH[] = {0x49, 0x55, 0x20, 0x08, 0x09, 0x18};
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_PWRR[] = {0x3F};
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_PSR[] = {0x5F, 0x69};
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_POFS[] = {0x00, 0x54, 0x00, 0x44};
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_BTST1[] = {0x40, 0x1F, 0x1F, 0x2C};
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_BTST2[] = {0x6F, 0x1F, 0x17, 0x49};
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_BTST3[] = {0x6F, 0x1F, 0x1F, 0x22};
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_PLL[] = {0x08};
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_CDI[] = {0x3F};
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_TCON[] = {0x02, 0x00};
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_TRES[] = {0x03, 0x20, 0x01, 0xE0}; // WIDTH=800, HEIGHT=480
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_T_VDCS[] = {0x01};
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_PWS[] = {0x2F};
|
|
|
+
|
|
|
+inline const EpdCommand PANEL_730C_GDEP073E01_INIT[] = {
|
|
|
+ {0xAA, PANEL_730C_GDEP073E01_CMDH, 6},
|
|
|
+ {0x01, PANEL_730C_GDEP073E01_PWRR, 1},
|
|
|
+ {0x00, PANEL_730C_GDEP073E01_PSR, 2},
|
|
|
+ {0x03, PANEL_730C_GDEP073E01_POFS, 4},
|
|
|
+ {0x05, PANEL_730C_GDEP073E01_BTST1, 4},
|
|
|
+ {0x06, PANEL_730C_GDEP073E01_BTST2, 4},
|
|
|
+ {0x08, PANEL_730C_GDEP073E01_BTST3, 4},
|
|
|
+ {0x30, PANEL_730C_GDEP073E01_PLL, 1},
|
|
|
+ {0x50, PANEL_730C_GDEP073E01_CDI, 1},
|
|
|
+ {0x60, PANEL_730C_GDEP073E01_TCON, 2},
|
|
|
+ {0x61, PANEL_730C_GDEP073E01_TRES, 4},
|
|
|
+ {0x84, PANEL_730C_GDEP073E01_T_VDCS, 1},
|
|
|
+ {0xE3, PANEL_730C_GDEP073E01_PWS, 1},
|
|
|
+ {0x04, nullptr, 0, /*delay_before_ms=*/0, /*delay_after_ms=*/0, /*wait_busy_after=*/true}, // power on
|
|
|
+};
|
|
|
+
|
|
|
+// _setPartialRamArea(0, 0, 800, 480): xe = x+w-1 = 799 (inclusive), but
|
|
|
+// ye = y+h = 480 (exclusive - see the file header note). Confirmed by the
|
|
|
+// 5-point window sweep, not just this one full-frame capture.
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_WINDOW[] = {
|
|
|
+ 0x00, 0x00, // x/256, x%256 = 0
|
|
|
+ 0x03, 0x1F, // xe/256, xe%256 = 799
|
|
|
+ 0x00, 0x00, // y/256, y%256 = 0
|
|
|
+ 0x01, 0xE0, // ye/256, ye%256 = 480
|
|
|
+ 0x01,
|
|
|
+};
|
|
|
+
|
|
|
+inline const EpdCommand PANEL_730C_GDEP073E01_WINDOW_SEQ[] = {
|
|
|
+ {0x83, PANEL_730C_GDEP073E01_WINDOW, 9},
|
|
|
+};
|
|
|
+
|
|
|
+// _refresh(false): 0x50=0x3F, then 0x12+delay(1)+_waitWhileBusy - collapsed
|
|
|
+// into one wait_busy_after.
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_CDI_REFRESH[] = {0x3F};
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_REFRESH_TRIGGER[] = {0x00};
|
|
|
+
|
|
|
+inline const EpdCommand PANEL_730C_GDEP073E01_REFRESH[] = {
|
|
|
+ {0x50, PANEL_730C_GDEP073E01_CDI_REFRESH, 1},
|
|
|
+ {0x12, PANEL_730C_GDEP073E01_REFRESH_TRIGGER, 1, /*delay_before_ms=*/0, /*delay_after_ms=*/0,
|
|
|
+ /*wait_busy_after=*/true},
|
|
|
+};
|
|
|
+
|
|
|
+// NOT GXEPD2_PALETTE_7C - see the file header. Codes observed by driving
|
|
|
+// the vendor's own GxEPD2_7C wrapper and reading the wire byte
|
|
|
+// (emit_palette_7c.h), then cross-checked against
|
|
|
+// GxEPD2_730c_GDEP073E01::_convert_to_native() in the driver source.
|
|
|
+inline const EpdPaletteEntry PANEL_730C_GDEP073E01_PALETTE[] = {
|
|
|
+ {0x00, 0x00, 0x00, 0x00}, // black
|
|
|
+ {0xFF, 0xFF, 0xFF, 0x01}, // white
|
|
|
+ {0x00, 0xFF, 0x00, 0x06}, // green
|
|
|
+ {0x00, 0x00, 0xFF, 0x05}, // blue
|
|
|
+ {0xFF, 0x00, 0x00, 0x03}, // red
|
|
|
+ {0xFF, 0xFF, 0x00, 0x02}, // yellow
|
|
|
+ {0xFF, 0xA4, 0x00, 0x04}, // orange
|
|
|
+};
|
|
|
+inline const uint8_t PANEL_730C_GDEP073E01_PALETTE_LEN =
|
|
|
+ sizeof(PANEL_730C_GDEP073E01_PALETTE) / sizeof(PANEL_730C_GDEP073E01_PALETTE[0]);
|
|
|
+
|
|
|
+inline const EpdPanelDescriptor PANEL_730C_GDEP073E01 = {
|
|
|
+ .width = 800,
|
|
|
+ .height = 480,
|
|
|
+ .busy_active_level = false, // GxEPD2_EPD(..., /*busy_level=*/LOW, ...)
|
|
|
+ .busy_timeout_ms = 60000, // full_refresh_time is 15000ms; generous diagnostic ceiling
|
|
|
+ .init_sequence = PANEL_730C_GDEP073E01_INIT,
|
|
|
+ .init_sequence_len = sizeof(PANEL_730C_GDEP073E01_INIT) / sizeof(PANEL_730C_GDEP073E01_INIT[0]),
|
|
|
+ .window_sequence = {PANEL_730C_GDEP073E01_WINDOW_SEQ, nullptr},
|
|
|
+ .window_sequence_len = {sizeof(PANEL_730C_GDEP073E01_WINDOW_SEQ) / sizeof(PANEL_730C_GDEP073E01_WINDOW_SEQ[0]), 0},
|
|
|
+ .plane_count = 1,
|
|
|
+ .plane_write_cmd = {0x10, 0x00},
|
|
|
+ .plane_invert = {false, false},
|
|
|
+ .bits_per_pixel = 4,
|
|
|
+ .palette = PANEL_730C_GDEP073E01_PALETTE,
|
|
|
+ .palette_len = PANEL_730C_GDEP073E01_PALETTE_LEN,
|
|
|
+ .blank_code = 0x01,
|
|
|
+ .refresh_sequence = PANEL_730C_GDEP073E01_REFRESH,
|
|
|
+ .refresh_sequence_len = sizeof(PANEL_730C_GDEP073E01_REFRESH) / sizeof(PANEL_730C_GDEP073E01_REFRESH[0]),
|
|
|
+};
|
|
|
+
|
|
|
+} // namespace esphome::gxepd2
|