|
@@ -0,0 +1,122 @@
|
|
|
|
|
+#pragma once
|
|
|
|
|
+
|
|
|
|
|
+// Hand-derived from vendor/src/epd4c/GxEPD2_579c_GDEY0579F51.{h,cpp}
|
|
|
|
|
+// (GDEY0579F51, HX8717 controller, 792x272, same JD79667-family 2bpp/4-color
|
|
|
|
|
+// packing as GDEY029F51 - see panel_290c_gdey029f51.h). The real dual-
|
|
|
|
|
+// region version, superseding panel_579c_gdey0579f51_master_only.h's
|
|
|
|
|
+// deliberate single-region simplification - uses CycleEngine's
|
|
|
|
|
+// window_per_plane mechanism to give "master" and "slave" (0xA2-select
|
|
|
|
|
+// logical regions, not separate physical CS pins) their own window-set
|
|
|
|
|
+// before each one's own 0x10 byte burst, "plane 0"/"plane 1" in the
|
|
|
|
|
+// engine's terms even though they're physical regions here, not black/red
|
|
|
|
|
+// color planes - see EpdPanelDescriptor::window_per_plane.
|
|
|
|
|
+//
|
|
|
|
|
+// Region physical placement (2026-09-19, confirmed two independent ways):
|
|
|
|
|
+// GxEPD2_579_GDEY0579T93.cpp (SSD1683, same 792x272 panel family) comments
|
|
|
|
|
+// its own master/slave split explicitly - "near connector" = slave,
|
|
|
|
|
+// "opposite to connector" = master. User independently confirmed the same
|
|
|
|
|
+// physical mapping driving this actual panel with the GDEY0213F51 driver
|
|
|
|
|
+// (same underlying chip): master = away from the FPC connector. So master
|
|
|
|
|
+// is region 0 here (window_sequence[0], written first, right after init
|
|
|
|
|
+// leaves the controller select=master already) and slave is region 1.
|
|
|
|
|
+//
|
|
|
|
|
+// The master-only descriptor's single-region run showed real, correctly-
|
|
|
|
|
+// decoded content but compressed/duplicated within half the driven
|
|
|
|
|
+// region's own width, plus noise/black bands on the untouched slave half.
|
|
|
|
|
+// The noise was explained and fixed there (0x12's refresh trigger fires a
|
|
|
|
|
+// real electrical refresh across the WHOLE panel regardless of 0xA2
|
|
|
|
|
+// select, so refreshing with slave's RAM never written "developed" its
|
|
|
|
|
+// stale/undefined content) - this descriptor writes both regions before
|
|
|
|
|
+// refreshing, which should eliminate that on its own. The duplication
|
|
|
|
|
+// artifact's cause is still unconfirmed; writing both regions may or may
|
|
|
|
|
+// not resolve it - that's what this descriptor is for finding out.
|
|
|
|
|
+
|
|
|
|
|
+#include "epd_command.h"
|
|
|
|
|
+
|
|
|
|
|
+namespace esphome::gxepd2 {
|
|
|
|
|
+
|
|
|
|
|
+inline const uint8_t PANEL_579C_GDEY0579F51_SELECT_MASTER[] = {0x01};
|
|
|
|
|
+inline const uint8_t PANEL_579C_GDEY0579F51_SELECT_SLAVE[] = {0x02};
|
|
|
|
|
+inline const uint8_t PANEL_579C_GDEY0579F51_SELECT_BOTH[] = {0x00};
|
|
|
|
|
+inline const uint8_t PANEL_579C_GDEY0579F51_PSR_MASTER[] = {0x07, 0x29};
|
|
|
|
|
+inline const uint8_t PANEL_579C_GDEY0579F51_PSR_SLAVE[] = {0x03, 0x29};
|
|
|
|
|
+inline const uint8_t PANEL_579C_GDEY0579F51_BTST[] = {0x32, 0x32, 0x32, 0x32};
|
|
|
|
|
+inline const uint8_t PANEL_579C_GDEY0579F51_TRES[] = {0x01, 0x8C, 0x01, 0x10}; // 396x272 - one region's resolution
|
|
|
|
|
+inline const uint8_t PANEL_579C_GDEY0579F51_CDI_INIT[] = {0x97};
|
|
|
|
|
+inline const uint8_t PANEL_579C_GDEY0579F51_E9[] = {0x01};
|
|
|
|
|
+inline const uint8_t PANEL_579C_GDEY0579F51_E0[] = {0x01};
|
|
|
|
|
+
|
|
|
|
|
+// _InitDisplay(): PSR written to each side individually (master and slave
|
|
|
|
|
+// scan in mirrored directions, hence different first PSR byte - 0x07 vs
|
|
|
|
|
+// 0x03), then BTST/TRES/CDI/E9/E0 written once under select=Both since
|
|
|
|
|
+// they're genuinely shared/global registers, then re-select master ready
|
|
|
|
|
+// for the first region's window-set.
|
|
|
|
|
+inline const EpdCommand PANEL_579C_GDEY0579F51_INIT[] = {
|
|
|
|
|
+ {0xA2, PANEL_579C_GDEY0579F51_SELECT_MASTER, 1},
|
|
|
|
|
+ {0x00, PANEL_579C_GDEY0579F51_PSR_MASTER, 2},
|
|
|
|
|
+ {0xA2, PANEL_579C_GDEY0579F51_SELECT_SLAVE, 1},
|
|
|
|
|
+ {0x00, PANEL_579C_GDEY0579F51_PSR_SLAVE, 2},
|
|
|
|
|
+ {0xA2, PANEL_579C_GDEY0579F51_SELECT_BOTH, 1},
|
|
|
|
|
+ {0x06, PANEL_579C_GDEY0579F51_BTST, 4},
|
|
|
|
|
+ {0x61, PANEL_579C_GDEY0579F51_TRES, 4},
|
|
|
|
|
+ {0x50, PANEL_579C_GDEY0579F51_CDI_INIT, 1},
|
|
|
|
|
+ {0xE9, PANEL_579C_GDEY0579F51_E9, 1},
|
|
|
|
|
+ {0xE0, PANEL_579C_GDEY0579F51_E0, 1},
|
|
|
|
|
+ {0xA2, PANEL_579C_GDEY0579F51_SELECT_MASTER, 1},
|
|
|
|
|
+ {0x04, nullptr, 0, /*delay_before_ms=*/0, /*delay_after_ms=*/0, /*wait_busy_after=*/true}, // power on
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// _setPartialRamAreaMaster/Slave(0, 0, 396, 272), default partial_mode=true
|
|
|
|
|
+// - both regions are the same shape, just a different select byte.
|
|
|
|
|
+inline const uint8_t PANEL_579C_GDEY0579F51_WINDOW_REGION[] = {
|
|
|
|
|
+ 0x00, 0x00, // x/256, x%256 = 0
|
|
|
|
|
+ 0x01, 0x8B, // xe/256, xe%256 = 395
|
|
|
|
|
+ 0x00, 0x00, // y/256, y%256 = 0
|
|
|
|
|
+ 0x01, 0x0F, // ye/256, ye%256 = 271
|
|
|
|
|
+ 0x01,
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+inline const EpdCommand PANEL_579C_GDEY0579F51_WINDOW_MASTER[] = {
|
|
|
|
|
+ {0xA2, PANEL_579C_GDEY0579F51_SELECT_MASTER, 1},
|
|
|
|
|
+ {0x83, PANEL_579C_GDEY0579F51_WINDOW_REGION, 9},
|
|
|
|
|
+};
|
|
|
|
|
+inline const EpdCommand PANEL_579C_GDEY0579F51_WINDOW_SLAVE[] = {
|
|
|
|
|
+ {0xA2, PANEL_579C_GDEY0579F51_SELECT_SLAVE, 1},
|
|
|
|
|
+ {0x83, PANEL_579C_GDEY0579F51_WINDOW_REGION, 9},
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// refresh(false): vendor's own _setPartialRamAreaBoth(0,0,396,272) then
|
|
|
|
|
+// _refresh(false) - select=Both is correct here (unlike the master-only
|
|
|
|
|
+// descriptor) since both regions' RAM genuinely holds real content now.
|
|
|
|
|
+inline const uint8_t PANEL_579C_GDEY0579F51_CDI_REFRESH[] = {0x37};
|
|
|
|
|
+inline const uint8_t PANEL_579C_GDEY0579F51_REFRESH_TRIGGER[] = {0x00};
|
|
|
|
|
+
|
|
|
|
|
+inline const EpdCommand PANEL_579C_GDEY0579F51_REFRESH[] = {
|
|
|
|
|
+ {0xA2, PANEL_579C_GDEY0579F51_SELECT_BOTH, 1},
|
|
|
|
|
+ {0x83, PANEL_579C_GDEY0579F51_WINDOW_REGION, 9},
|
|
|
|
|
+ {0x50, PANEL_579C_GDEY0579F51_CDI_REFRESH, 1},
|
|
|
|
|
+ {0x12, PANEL_579C_GDEY0579F51_REFRESH_TRIGGER, 1, /*delay_before_ms=*/0, /*delay_after_ms=*/0,
|
|
|
|
|
+ /*wait_busy_after=*/true},
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+inline const EpdPanelDescriptor PANEL_579C_GDEY0579F51 = {
|
|
|
|
|
+ .width = 792, // full logical panel - the canvas ESPHome draws into
|
|
|
|
|
+ .height = 272,
|
|
|
|
|
+ .busy_active_level = false, // GxEPD2_EPD(..., /*busy_level=*/LOW, ...)
|
|
|
|
|
+ .busy_timeout_ms = 25000, // full_refresh_time is 21000ms
|
|
|
|
|
+ .init_sequence = PANEL_579C_GDEY0579F51_INIT,
|
|
|
|
|
+ .init_sequence_len = sizeof(PANEL_579C_GDEY0579F51_INIT) / sizeof(PANEL_579C_GDEY0579F51_INIT[0]),
|
|
|
|
|
+ .window_sequence = {PANEL_579C_GDEY0579F51_WINDOW_MASTER, PANEL_579C_GDEY0579F51_WINDOW_SLAVE},
|
|
|
|
|
+ .window_sequence_len = {sizeof(PANEL_579C_GDEY0579F51_WINDOW_MASTER) / sizeof(PANEL_579C_GDEY0579F51_WINDOW_MASTER[0]),
|
|
|
|
|
+ sizeof(PANEL_579C_GDEY0579F51_WINDOW_SLAVE) / sizeof(PANEL_579C_GDEY0579F51_WINDOW_SLAVE[0])},
|
|
|
|
|
+ .window_per_plane = true,
|
|
|
|
|
+ .plane_count = 2, // "plane" 0 = master region, "plane" 1 = slave region
|
|
|
|
|
+ .plane_write_cmd = {0x10, 0x10},
|
|
|
|
|
+ .plane_invert = {false, false},
|
|
|
|
|
+ .plane_width = 396, // each region's own local width
|
|
|
|
|
+ .packed_2bpp_plane = true,
|
|
|
|
|
+ .refresh_sequence = PANEL_579C_GDEY0579F51_REFRESH,
|
|
|
|
|
+ .refresh_sequence_len = sizeof(PANEL_579C_GDEY0579F51_REFRESH) / sizeof(PANEL_579C_GDEY0579F51_REFRESH[0]),
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+} // namespace esphome::gxepd2
|