|
|
@@ -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");
|
|
|
+ }
|
|
|
+}
|