emit_palette_7c.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. #pragma once
  2. // 7-colour (ACeP, 4bpp packed) palette/blank-code resolver for the descriptor
  3. // emitter. Same approach as emit_palette_4c.h: drive the vendor driver's own
  4. // GxEPD2_7C wrapper (fillScreen of each named colour), read the wire code it
  5. // emits for each, then CONFIRM that table matches the engine's
  6. // GXEPD2_PALETTE_7C codes (epd_command.h) before referencing it. A panel
  7. // whose codes don't match stays unresolved and emits the old TODO plus the
  8. // observed table, so a surprise is visible, never silently mislabelled.
  9. //
  10. // Unlike 4-colour, GxEPD2_7C's color7() maps all seven GxEPD_* constants to
  11. // distinct codes (no collisions), so EXPECT_7C below is a 1:1 check against
  12. // GXEPD2_PALETTE_7C rather than a many-to-one map.
  13. //
  14. // Include order (tools/capture.py's generated 7C main handles this): the
  15. // driver header and GxEPD2_7C.h first (they define GxEPD_* and the
  16. // WIDTH/HEIGHT the wrapper references unqualified), then emit_descriptor.h,
  17. // then this file.
  18. #include <cstdint>
  19. #include <cstdio>
  20. #include <string>
  21. #include <vector>
  22. #include "GxEPD2_7C.h"
  23. #include "emit_descriptor.h"
  24. #include "trace.h"
  25. namespace emit {
  26. // The engine's canonical 7-colour map (mirrors epd_command.h GXEPD2_PALETTE_7C
  27. // / GxEPD2_7C.h color7()): the wire code each named colour must resolve to
  28. // for the standard palette to be the right answer.
  29. struct Expect7C {
  30. uint16_t color;
  31. uint8_t code;
  32. const char *label;
  33. };
  34. inline const Expect7C EXPECT_7C[] = {
  35. {GxEPD_BLACK, 0x00, "black"}, {GxEPD_WHITE, 0x01, "white"},
  36. {GxEPD_GREEN, 0x02, "green"}, {GxEPD_BLUE, 0x03, "blue"},
  37. {GxEPD_RED, 0x04, "red"}, {GxEPD_YELLOW, 0x05, "yellow"},
  38. {GxEPD_ORANGE, 0x06, "orange"},
  39. };
  40. // First run of exactly plane_bytes data bytes in the current Trace::record
  41. // (one fillScreen flush), or empty. Mirrors emit_palette_4c.h / run_diagB_
  42. // harness.h.
  43. inline std::vector<uint8_t> first_plane_burst_7c(uint32_t plane_bytes) {
  44. std::vector<uint8_t> run, best;
  45. auto flush = [&]() {
  46. if (run.size() == plane_bytes && best.empty())
  47. best = run;
  48. run.clear();
  49. };
  50. for (const auto &ev : Trace::record) {
  51. if (ev.first) // command byte: ends the current data run
  52. flush();
  53. else
  54. run.push_back(ev.second);
  55. }
  56. flush();
  57. return best;
  58. }
  59. // Build the 7-colour packed resolver for `Driver`. It instantiates the vendor
  60. // GxEPD2_7C<Peek<Driver>, HEIGHT> wrapper (single full-height page) and reads
  61. // each colour's code off the wire.
  62. template<typename Driver> PackedResolverFn make_resolver_7c() {
  63. return [](uint8_t /*clear_fill*/, const PlaneInvert & /*pv*/) -> PackedResolved {
  64. constexpr int bpp = 4, ppb = 2; // 7-colour: 4 bits/pixel, 2 pixels/byte, MSB-first (high nibble first)
  65. const uint16_t W = Driver::WIDTH, H = Driver::HEIGHT;
  66. const uint32_t plane_bytes = (uint32_t) W * H * bpp / 8;
  67. using Wrapper = GxEPD2_7C<Peek<Driver>, Driver::HEIGHT>; // page_height=HEIGHT => one page
  68. Wrapper *dp = new Wrapper(Peek<Driver>(10, 11, 12, 13));
  69. Wrapper &display = *dp;
  70. Trace::set_dc_pin(11);
  71. Trace::last_busy_release_value = display.epd2.busy_level() == HIGH ? LOW : HIGH;
  72. display.epd2.selectSPI(SPI, SPISettings());
  73. display.init(0);
  74. display.setFullWindow();
  75. // Prime (untracked) so the first-page buffer prime isn't read as a fill.
  76. display.firstPage();
  77. do {
  78. display.fillScreen(GxEPD_WHITE);
  79. } while (display.nextPage());
  80. auto code_of = [&](uint16_t color, bool &uniform) -> int {
  81. Trace::start_record();
  82. display.firstPage();
  83. do {
  84. display.fillScreen(color);
  85. } while (display.nextPage());
  86. Trace::stop_record();
  87. auto b = first_plane_burst_7c(plane_bytes);
  88. if (b.empty())
  89. return -1;
  90. uint8_t byte0 = b[0];
  91. uint8_t f0 = (byte0 >> ((ppb - 1) * bpp)) & 0xF; // MSB field (high nibble)
  92. uniform = true;
  93. for (int j = 1; j < ppb; j++)
  94. if (((byte0 >> ((ppb - 1 - j) * bpp)) & 0xF) != f0)
  95. uniform = false;
  96. return f0;
  97. };
  98. const int ncol = (int) (sizeof(EXPECT_7C) / sizeof(EXPECT_7C[0]));
  99. bool matches = true;
  100. int white_code = -1;
  101. std::string observed;
  102. for (int k = 0; k < ncol; k++) {
  103. bool uniform = false;
  104. int code = code_of(EXPECT_7C[k].color, uniform);
  105. char buf[48];
  106. snprintf(buf, sizeof(buf), "%s%s=%d", k ? " " : "", EXPECT_7C[k].label, code);
  107. observed += buf;
  108. if (EXPECT_7C[k].color == GxEPD_WHITE)
  109. white_code = code;
  110. if (!uniform || code != (int) EXPECT_7C[k].code)
  111. matches = false;
  112. }
  113. delete dp;
  114. PackedResolved r;
  115. if (matches) {
  116. r.resolved = true;
  117. r.bits_per_pixel = bpp;
  118. r.color_count = ncol;
  119. r.palette_expr = "GXEPD2_PALETTE_7C";
  120. r.palette_len_expr = "GXEPD2_PALETTE_7C_LEN";
  121. r.blank_code = white_code; // panel's white/clear code (0x01 for GxEPD2 7C)
  122. } else {
  123. r.resolved = false;
  124. r.note = "7C probe mismatch, observed [" + observed +
  125. "] (expected black=0 white=1 green=2 blue=3 red=4 yellow=5 orange=6)";
  126. }
  127. return r;
  128. };
  129. }
  130. // 7-colour capture entry: like emit_descriptor(), but resolves the packed
  131. // fields by observing the GxEPD2_7C wrapper. tools/capture.py calls this for
  132. // an epd7c/ driver.
  133. template<typename Driver> void emit_descriptor_7c(const char *class_name, const char *macro) {
  134. emit_descriptor_impl<Driver>(class_name, macro, make_resolver_7c<Driver>());
  135. }
  136. } // namespace emit