|
|
@@ -0,0 +1,99 @@
|
|
|
+#pragma once
|
|
|
+
|
|
|
+// Hand-derived from vendor/src/epd4c/GxEPD2_290c_GDEY029F51.{h,cpp}
|
|
|
+// (GDEY029F51, JD79667 controller, 128x296, 4-color BWRY panel used here as
|
|
|
+// black/white/red via the black+color buffer interface). First panel on a
|
|
|
+// completely different command set from the SSD1680/1677/UC8179 mono
|
|
|
+// panels (0x00 PSR, 0x01 PWRR, 0x03 POFS, 0x06 BTST, 0x50 CDI, 0x60 TCON,
|
|
|
+// 0x61 TRES, 0x83 window-set, refresh via 0x50+0x12) and, more importantly,
|
|
|
+// a fundamentally different pixel format: 2 bits/pixel, 4 pixels/byte
|
|
|
+// (white=00, black=01, red=11), not 1-bit-per-pixel planes - see
|
|
|
+// EpdPanelDescriptor::packed_2bpp_plane and GxEPD2Display's
|
|
|
+// draw_absolute_pixel_internal for the buffer-side half of this.
|
|
|
+//
|
|
|
+// _InitDisplay()'s reset pulse is itself gated on `_hibernating ||
|
|
|
+// _initial_write` in the vendor driver (steady-state cycles skip the
|
|
|
+// physical RST and go straight to re-issuing the command sequence) - this
|
|
|
+// engine always does a real hardware reset every cycle regardless (see
|
|
|
+// CycleEngine::request_full_update()), a superset of what the vendor
|
|
|
+// driver does on its own first cycle, so no special-casing needed here.
|
|
|
+
|
|
|
+#include "epd_command.h"
|
|
|
+
|
|
|
+namespace esphome::gxepd2 {
|
|
|
+
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_CMD_4D[] = {0x78};
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_PSR[] = {0x0F, 0x29};
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_PWRR[] = {0x07, 0x00};
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_POFS[] = {0x10, 0x54, 0x44};
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_BTST[] = {0x05, 0x00, 0x3F, 0x0A, 0x25, 0x12, 0x1A};
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_CDI[] = {0x37};
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_TCON[] = {0x02, 0x02};
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_TRES[] = {0x00, 0x80, 0x01, 0x28}; // WIDTH=128, HEIGHT=296
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_E7[] = {0x1C};
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_PWS[] = {0x22};
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_LVD[] = {0xD0};
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_B5[] = {0x03};
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_E9[] = {0x01};
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_PLL[] = {0x08};
|
|
|
+
|
|
|
+inline const EpdCommand PANEL_290C_GDEY029F51_INIT[] = {
|
|
|
+ {0x4D, PANEL_290C_GDEY029F51_CMD_4D, 1},
|
|
|
+ {0x00, PANEL_290C_GDEY029F51_PSR, 2},
|
|
|
+ {0x01, PANEL_290C_GDEY029F51_PWRR, 2},
|
|
|
+ {0x03, PANEL_290C_GDEY029F51_POFS, 3},
|
|
|
+ {0x06, PANEL_290C_GDEY029F51_BTST, 7},
|
|
|
+ {0x50, PANEL_290C_GDEY029F51_CDI, 1},
|
|
|
+ {0x60, PANEL_290C_GDEY029F51_TCON, 2},
|
|
|
+ {0x61, PANEL_290C_GDEY029F51_TRES, 4},
|
|
|
+ {0xE7, PANEL_290C_GDEY029F51_E7, 1},
|
|
|
+ {0xE3, PANEL_290C_GDEY029F51_PWS, 1},
|
|
|
+ {0xB4, PANEL_290C_GDEY029F51_LVD, 1},
|
|
|
+ {0xB5, PANEL_290C_GDEY029F51_B5, 1},
|
|
|
+ {0xE9, PANEL_290C_GDEY029F51_E9, 1},
|
|
|
+ {0x30, PANEL_290C_GDEY029F51_PLL, 1},
|
|
|
+ {0x04, nullptr, 0, /*delay_before_ms=*/0, /*delay_after_ms=*/0, /*wait_busy_after=*/true}, // power on
|
|
|
+};
|
|
|
+
|
|
|
+// _setPartialRamArea(0, 0, 128, 296), default partial_mode=true.
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_WINDOW[] = {
|
|
|
+ 0x00, 0x00, // x/256, x%256 = 0
|
|
|
+ 0x00, 0x7F, // xe/256, xe%256 = 127
|
|
|
+ 0x00, 0x00, // y/256, y%256 = 0
|
|
|
+ 0x01, 0x27, // ye/256, ye%256 = 295
|
|
|
+ 0x01,
|
|
|
+};
|
|
|
+
|
|
|
+inline const EpdCommand PANEL_290C_GDEY029F51_WINDOW_SEQ[] = {
|
|
|
+ {0x83, PANEL_290C_GDEY029F51_WINDOW, 9},
|
|
|
+};
|
|
|
+
|
|
|
+// _refresh(false): 0x50=0x37 (white/non-floating border, not partial),
|
|
|
+// then 0x12+delay(1)+_waitWhileBusy - collapsed into one wait_busy_after.
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_CDI_REFRESH[] = {0x37};
|
|
|
+inline const uint8_t PANEL_290C_GDEY029F51_REFRESH_TRIGGER[] = {0x00};
|
|
|
+
|
|
|
+inline const EpdCommand PANEL_290C_GDEY029F51_REFRESH[] = {
|
|
|
+ {0x50, PANEL_290C_GDEY029F51_CDI_REFRESH, 1},
|
|
|
+ {0x12, PANEL_290C_GDEY029F51_REFRESH_TRIGGER, 1, /*delay_before_ms=*/0, /*delay_after_ms=*/0,
|
|
|
+ /*wait_busy_after=*/true},
|
|
|
+};
|
|
|
+
|
|
|
+inline const EpdPanelDescriptor PANEL_290C_GDEY029F51 = {
|
|
|
+ .width = 128,
|
|
|
+ .height = 296,
|
|
|
+ .busy_active_level = false, // GxEPD2_EPD(..., /*busy_level=*/LOW, ...)
|
|
|
+ .busy_timeout_ms = 30000, // full_refresh_time is 25000ms
|
|
|
+ .init_sequence = PANEL_290C_GDEY029F51_INIT,
|
|
|
+ .init_sequence_len = sizeof(PANEL_290C_GDEY029F51_INIT) / sizeof(PANEL_290C_GDEY029F51_INIT[0]),
|
|
|
+ .window_sequence = PANEL_290C_GDEY029F51_WINDOW_SEQ,
|
|
|
+ .window_sequence_len = sizeof(PANEL_290C_GDEY029F51_WINDOW_SEQ) / sizeof(PANEL_290C_GDEY029F51_WINDOW_SEQ[0]),
|
|
|
+ .plane_count = 1,
|
|
|
+ .plane_write_cmd = {0x10, 0x00},
|
|
|
+ .plane_invert = {false, false},
|
|
|
+ .packed_2bpp_plane = true,
|
|
|
+ .refresh_sequence = PANEL_290C_GDEY029F51_REFRESH,
|
|
|
+ .refresh_sequence_len = sizeof(PANEL_290C_GDEY029F51_REFRESH) / sizeof(PANEL_290C_GDEY029F51_REFRESH[0]),
|
|
|
+};
|
|
|
+
|
|
|
+} // namespace esphome::gxepd2
|