Przeglądaj źródła

harness: native-path row ordering for packed panels

Pattern A's writeImage tag doesn't survive the 1bpp->packed re-pack, so
run_diagN_harness.h tags a packed NATIVE buffer and sends it via the
byte-preserving writeNative, recovering each region's line order (stride =
full bpr for one burst, half bpr for a dual-region panel's two bursts; nlines
= burst_len/stride, so it copes with a driver emitting more lines than H).

Host-only findings: 290c (single region) SEQUENTIAL; 579c (dual region)
both bursts paired top-reversed (H-1,0,H-2,1,...) with 2 lines per source
row (nlines=2H) - GxEPD2's 579 fills a 2x-height RAM, so the interleave is
in GxEPD2's own driver (derivable), differing in shape from our engine's
1-line/row interleaved_index_. Reconciliation bundled with the 579 hw re-test.
ZiB 3 tygodni temu
rodzic
commit
ca79e6cea3

+ 8 - 0
tools/gxepd2_harness/run_290c_gdey029f51_diagN.cpp

@@ -0,0 +1,8 @@
+#include "run_diagN_harness.h"
+
+#include "epd4c/GxEPD2_290c_GDEY029F51.h"
+
+int main() {
+  run_diagN<DiagNPeek<GxEPD2_290c_GDEY029F51>>("GxEPD2_290c_GDEY029F51", GxEPD2_290c_GDEY029F51::WIDTH, GxEPD2_290c_GDEY029F51::HEIGHT, 2);
+  return 0;
+}

+ 8 - 0
tools/gxepd2_harness/run_579c_gdey0579f51_diagN.cpp

@@ -0,0 +1,8 @@
+#include "run_diagN_harness.h"
+
+#include "epd4c/GxEPD2_579c_GDEY0579F51.h"
+
+int main() {
+  run_diagN<DiagNPeek<GxEPD2_579c_GDEY0579F51>>("GxEPD2_579c_GDEY0579F51", GxEPD2_579c_GDEY0579F51::WIDTH, GxEPD2_579c_GDEY0579F51::HEIGHT, 2);
+  return 0;
+}

+ 118 - 0
tools/gxepd2_harness/run_diagN_harness.h

@@ -0,0 +1,118 @@
+#pragma once
+
+// Diagnostic-fill harness, native-path row ordering - Pattern A for PACKED
+// panels. Pattern A's writeImage tag doesn't survive the 1bpp->2bpp/4bpp
+// re-pack, so instead we tag the NATIVE (already-packed) buffer at byte
+// granularity and send it via the driver's byte-preserving writeNative(),
+// then recover the buffer->wire row permutation per emitted burst.
+//
+// Tag: each native row r carries (r>>8),(r&0xFF) at the start of BOTH half-
+// width regions (byte offsets 0/1 and halfbpr/halfbpr+1), so a single-region
+// panel's one burst and a dual-region panel's master+slave bursts each find
+// the tag at their row start. We analyse each plane-sized/length burst with
+// its own bytes-per-row = burst_len/H.
+//
+// Reads out the real wire row order, including any interleave/flip the
+// driver's native send applies. Host-only.
+
+#include <cstdint>
+#include <cstdio>
+#include <vector>
+
+#include "spy/Arduino.h"
+#include "spy/SPI.h"
+#include "trace.h"
+
+template<typename Driver> struct DiagNPeek : Driver {
+  using Driver::Driver;
+  int busy_level() const { return this->_busy_level; }
+};
+
+// All data-byte bursts (maximal runs between command bytes) whose length is
+// a whole number of rows and >= one row - the native plane/region sends.
+inline std::vector<std::vector<uint8_t>> diagN_bursts(uint16_t H, uint32_t min_len) {
+  std::vector<std::vector<uint8_t>> out;
+  std::vector<uint8_t> run;
+  auto flush = [&]() {
+    if (run.size() >= min_len && run.size() % H == 0)
+      out.push_back(run);
+    run.clear();
+  };
+  for (const auto &ev : Trace::record) {
+    if (ev.first)
+      flush();
+    else
+      run.push_back(ev.second);
+  }
+  flush();
+  return out;
+}
+
+template<typename WrapperDriver> void run_diagN(const char *name, uint16_t W, uint16_t H, int bpp) {
+  const uint32_t bpr = (uint32_t) W * bpp / 8;    // full-width native row bytes
+  const uint32_t halfbpr = (uint32_t) (W / 2) * bpp / 8;
+  const uint32_t buf_bytes = bpr * H;
+  printf("=== %s: DIAGNOSTIC FILL (native-path row ordering) W=%u H=%u bpp=%d bpr=%u ===\n", name, W, H, bpp,
+         (unsigned) bpr);
+
+  auto *buf = new uint8_t[buf_bytes]();
+  for (uint16_t r = 0; r < H; r++) {
+    uint8_t hi = (r >> 8) & 0xFF, lo = r & 0xFF;
+    buf[(uint32_t) r * bpr + 0] = hi;
+    buf[(uint32_t) r * bpr + 1] = lo;
+    if (halfbpr + 1 < bpr) {  // dual-region: also tag the second half's row start
+      buf[(uint32_t) r * bpr + halfbpr] = hi;
+      buf[(uint32_t) r * bpr + halfbpr + 1] = lo;
+    }
+  }
+
+  WrapperDriver d(10, 11, 12, 13);
+  Trace::set_dc_pin(11);
+  Trace::last_busy_release_value = d.busy_level() == HIGH ? LOW : HIGH;
+  d.selectSPI(SPI, SPISettings());
+  d.init(0);
+  d.writeNative(buf, buf, 0, 0, W, H);  // prime (untracked): clears _initial_write
+  Trace::start_record();
+  d.writeNative(buf, buf, 0, 0, W, H);  // tagged native send
+  Trace::stop_record();
+  delete[] buf;
+
+  auto bursts = diagN_bursts(H, halfbpr > 0 ? halfbpr : bpr);
+  // One burst => full frame (stride = full bpr); >=2 => per-region (stride =
+  // half bpr). A driver may emit more lines than H (e.g. the 579 writes two
+  // lines per source row into a 2x-height RAM); nlines = burst_len / stride.
+  const uint32_t stride = (bursts.size() >= 2 && halfbpr > 0) ? halfbpr : bpr;
+  printf("  native bursts found=%zu, region row stride=%u bytes\n", bursts.size(), (unsigned) stride);
+  for (size_t p = 0; p < bursts.size(); p++) {
+    const auto &data = bursts[p];
+    uint32_t nlines = (uint32_t) data.size() / stride;
+    std::vector<int> rec(nlines);
+    for (uint32_t g = 0; g < nlines; g++)
+      rec[g] = (data[g * stride] << 8) | data[g * stride + 1];
+
+    bool identity = (nlines == H), reversed = (nlines == H), paired = (nlines == 2u * H);
+    for (uint32_t g = 0; g < nlines; g++) {
+      if (identity && rec[g] != (int) g)
+        identity = false;
+      if (reversed && rec[g] != (int) (H - 1 - g))
+        reversed = false;
+      // paired, top-reversed: H-1, 0, H-2, 1, H-3, 2, ... (two lines per row)
+      int expect = (g % 2 == 0) ? (int) (H - 1 - g / 2) : (int) ((g - 1) / 2);
+      if (paired && rec[g] != expect)
+        paired = false;
+    }
+    const char *verdict = identity ? "SEQUENTIAL (identity)"
+                          : reversed ? "REVERSED rows"
+                          : paired   ? "ROW_INTERLEAVE paired top-reversed (H-1,0,H-2,1,...; 2 lines/row)"
+                                     : "NON-SEQUENTIAL (see rows)";
+    printf("    burst %zu: len=%u, nlines=%u (H=%u) -> %s\n", p, (unsigned) data.size(), (unsigned) nlines, H,
+           verdict);
+    printf("      first lines:");
+    for (uint32_t g = 0; g < 8 && g < nlines; g++)
+      printf(" %d", rec[g]);
+    printf("  | last lines:");
+    for (uint32_t g = (nlines > 8 ? nlines - 8 : 0); g < nlines; g++)
+      printf(" %d", rec[g]);
+    printf("\n");
+  }
+}