panel_154_z90c.h 4.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. #pragma once
  2. // GDEH0154Z90, SSD1681 controller, 200x200, black + red (3-colour). Same
  3. // controller family and command shape as panel_213_z98c.h (SSD1680/1681
  4. // 3-colour), two 1bpp planes (0x24 black, 0x26 red) sharing one full-frame
  5. // RAM window.
  6. //
  7. // Derived by the descriptor emitter (tools/gxepd2_harness/emit_descriptor.h)
  8. // observing the real GxEPD2_154_Z90c driver, not hand-transcribed - the first
  9. // panel brought up that way. The emitter reproduced the 213 Z98c descriptor
  10. // byte-for-byte as a control, then produced this one; the capture also caught
  11. // the two real differences from the 213 (verified against the vendor source):
  12. // - SWRESET (0x12) here waits on the busy pin, where the 213 brackets it
  13. // with fixed 10ms delays instead;
  14. // - no 0x21 "display update control" in init (the 213 sets {0x00, 0x80}).
  15. //
  16. // plane_invert={false, false} is capture-derived, by the plane-invert probe
  17. // (tools/gxepd2_harness/probe_plane_invert.h): writeScreenBuffer() white/black/
  18. // red fills put plane0 wire=0xFF for white and plane1 wire=0xFF for red, which
  19. // agrees with the engine's buffer convention (plane0 bit=white, plane1 bit=red)
  20. // => no inversion. The same probe reproduces the bench-validated 213 Z98c's
  21. // {false, false}, so this is at the same confidence as the rest of the
  22. // descriptor, not an analogy guess.
  23. //
  24. // Bench-confirmed 2026-10-18: 3 colors, clean lines, correct color map, ~22s
  25. // full refresh. First emitter-derived descriptor validated on real hardware -
  26. // the probe-derived plane_invert={false,false} rendered red/white correctly.
  27. #include "epd_command.h"
  28. namespace esphome::gxepd2 {
  29. inline const uint8_t PANEL_154_Z90C_DRIVER_OUTPUT_CONTROL[] = {0xC7, 0x00, 0x00}; // gate = 0x00C7 = 199 (200 lines)
  30. inline const uint8_t PANEL_154_Z90C_DATA_ENTRY_MODE[] = {0x03};
  31. inline const uint8_t PANEL_154_Z90C_BORDER_WAVEFORM[] = {0x05};
  32. inline const uint8_t PANEL_154_Z90C_TEMP_SENSOR[] = {0x80};
  33. inline const EpdCommand PANEL_154_Z90C_INIT[] = {
  34. {0x12, nullptr, 0, /*delay_before_ms=*/0, /*delay_after_ms=*/0, /*wait_busy_after=*/true}, // SWRESET
  35. {0x01, PANEL_154_Z90C_DRIVER_OUTPUT_CONTROL, 3},
  36. {0x11, PANEL_154_Z90C_DATA_ENTRY_MODE, 1},
  37. {0x3C, PANEL_154_Z90C_BORDER_WAVEFORM, 1},
  38. {0x18, PANEL_154_Z90C_TEMP_SENSOR, 1},
  39. };
  40. inline const uint8_t PANEL_154_Z90C_UPDATE_MODE[] = {0xF7};
  41. inline const EpdCommand PANEL_154_Z90C_REFRESH[] = {
  42. {0x22, PANEL_154_Z90C_UPDATE_MODE, 1},
  43. {0x20, nullptr, 0, /*delay_before_ms=*/0, /*delay_after_ms=*/0, /*wait_busy_after=*/true},
  44. };
  45. // _setPartialRamArea(0, 0, 200, 200): SSD1681 byte-column X addressing, no
  46. // Y-reversal (same layout as the 213 Z98c).
  47. inline const uint8_t PANEL_154_Z90C_WINDOW_X[] = {0x00, 0x18}; // xe/8 = 199/8 = 24
  48. inline const uint8_t PANEL_154_Z90C_WINDOW_Y[] = {0x00, 0x00, 0xC7, 0x00}; // ye = 199
  49. inline const uint8_t PANEL_154_Z90C_WINDOW_XC[] = {0x00};
  50. inline const uint8_t PANEL_154_Z90C_WINDOW_YC[] = {0x00, 0x00};
  51. inline const EpdCommand PANEL_154_Z90C_WINDOW[] = {
  52. {0x44, PANEL_154_Z90C_WINDOW_X, 2},
  53. {0x45, PANEL_154_Z90C_WINDOW_Y, 4},
  54. {0x4E, PANEL_154_Z90C_WINDOW_XC, 1},
  55. {0x4F, PANEL_154_Z90C_WINDOW_YC, 2},
  56. };
  57. inline const uint8_t PANEL_154_Z90C_HIBERNATE_0_10[] = {0x01};
  58. inline const EpdCommand PANEL_154_Z90C_HIBERNATE[] = {
  59. {0x10, PANEL_154_Z90C_HIBERNATE_0_10, 1},
  60. };
  61. inline const EpdPanelDescriptor PANEL_154_Z90C = {
  62. .width = 200,
  63. .height = 200,
  64. .busy_active_level = true, // GxEPD2_EPD(..., /*busy_level=*/HIGH, ...)
  65. .busy_timeout_ms = 30000, // vendor full_refresh_time ~14000ms; err long
  66. .init_sequence = PANEL_154_Z90C_INIT,
  67. .init_sequence_len = sizeof(PANEL_154_Z90C_INIT) / sizeof(PANEL_154_Z90C_INIT[0]),
  68. .window_sequence = {PANEL_154_Z90C_WINDOW, nullptr},
  69. .window_sequence_len = {sizeof(PANEL_154_Z90C_WINDOW) / sizeof(PANEL_154_Z90C_WINDOW[0]), 0},
  70. .plane_count = 2,
  71. .plane_write_cmd = {0x24, 0x26},
  72. .plane_invert = {false, false}, // see header note (matches 213 Z98c's validated convention)
  73. .refresh_sequence = PANEL_154_Z90C_REFRESH,
  74. .refresh_sequence_len = sizeof(PANEL_154_Z90C_REFRESH) / sizeof(PANEL_154_Z90C_REFRESH[0]),
  75. .hibernate_sequence = PANEL_154_Z90C_HIBERNATE,
  76. .hibernate_sequence_len = sizeof(PANEL_154_Z90C_HIBERNATE) / sizeof(PANEL_154_Z90C_HIBERNATE[0]),
  77. };
  78. } // namespace esphome::gxepd2