mirror of
https://github.com/ALLFATHER-BV/wadamesh.git
synced 2026-09-25 19:54:58 +00:00
Heltec V4-R8 — "are we getting full performance?" audit. Compute side was already at spec; six gaps closed (record: variants/heltec_v4/R8_AUDIT.md "Perf pass"): - FEM LNA defaulted OFF on a KCT8103L board: prefs v49 defaults it ON on the R8 (one-time flip of existing installs, no new field). Toggle stays in Radio & Mesh. - Display SPI 40 -> 80 MHz (LGFX_SPI_WRITE_HZ; -D to 40000000 steps back). - Async DMA band flush (LGFXDisplay::flushBandRGB565): swap into a 12 KB internal DMA buffer, one no-convert DMA per band, lv_disp_flush_ready at once so LVGL renders band N+1 while band N drains; lv_disp_flush_is_last closes the frame transaction so the shared micro-SD gets the bus between frames. Sync fallback. - Wi-Fi modem power save is a pref (wifi_ps; toggle in Wi-Fi settings, live once associated). Default ON everywhere (unchanged), OFF on the R8. - micro-SD 4 -> 20 MHz after the proven mount, read-verified (probe-file head captured at 4 MHz and byte-compared after the raise; falls back). Wired at all three mount sites; no-op without SD_SPI_FAST_HZ. Note: SDFS::begin returns true if already mounted, so every clock change goes through SD.end() first. - R8 env boots at ESP32_CPU_FREQ=240 (setup() no longer runs at the V4's 80 MHz). - Settings -> About "Perf:" row on the R8 (no serial console on that board): "CPU 240 · TFT 80 MHz DMA · SD 20 MHz · LNA on" is the all-green reading. ThinkNode M9 UI: - Spectrum page root gets NAV_SKIP_FLAG: nothing on it is actionable, so the d-pad no longer highlights the chart / scale box; Back closes it as before. - Map "Show tile z/x/y" line defaults OFF (prefs v50, one-time flip; toggle kept). - The tab bar is bypassed entirely on the M9: the accent "glow" indicator lived on the screen, not in the bar, so hiding the zero-height bar left it glowing over the content's bottom edge. No bar styling / gestures / key hints / indicator are built on the M9 now. Cross-compiled: R8, V4, M9, T-Deck. Flashed + booted on the user's R8 and M9. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
95 lines
3.6 KiB
C
95 lines
3.6 KiB
C
#pragma once
|
|
// Post-mount micro-SD operating-clock raise (perf pass 2026-08-20; Heltec V4-R8 first).
|
|
//
|
|
// Every SPI-SD board mounts the card with a conservative ladder that tops out at 4 MHz,
|
|
// and SD.begin()'s clock is the operating clock for the whole session — so the card that
|
|
// is the PRIMARY store on the R8 (contacts, chat history, sync log, file manager) ran at
|
|
// ~400 KB/s on traces that carry the display at 80 MHz. Standard SD bring-up is "initialise
|
|
// slow, then raise": once the 4 MHz mount has proved the card, re-begin at SD_SPI_FAST_HZ
|
|
// and READ-VERIFY it — a probe file's first 512 bytes are read at the proven clock first
|
|
// and compared byte-for-byte after the raise (SPI-mode SD has no data CRC by default, so a
|
|
// bare "mount succeeded" would not catch a marginal clock). Any mismatch or failure drops
|
|
// straight back to the clock that just worked. Boards that do not define SD_SPI_FAST_HZ
|
|
// (or set it <= 4 MHz) get a no-op — nothing changes for the T-Deck/M9/Pager ladders.
|
|
//
|
|
// Returns the operating clock after the call (cur_hz if the raise was refused or failed),
|
|
// or 0 if the card could not be re-mounted at all (the caller treats that as unmounted).
|
|
|
|
#include <Arduino.h>
|
|
|
|
#if defined(ESP32)
|
|
#include <SD.h>
|
|
#include <SPI.h>
|
|
#include <string.h>
|
|
|
|
#ifndef SD_SPI_FAST_HZ
|
|
#define SD_SPI_FAST_HZ 0
|
|
#endif
|
|
|
|
static inline uint32_t sdTryFastClock(uint8_t cs_pin, SPIClass& spi, uint32_t cur_hz, const char* tag) {
|
|
#if SD_SPI_FAST_HZ > 4000000
|
|
if (cur_hz == 0 || cur_hz >= (uint32_t)SD_SPI_FAST_HZ) return cur_hz;
|
|
|
|
// 1) Pick a probe file and capture its head at the proven clock.
|
|
char probe_path[96] = {0};
|
|
uint8_t ref[512];
|
|
size_t ref_len = 0;
|
|
{
|
|
File root = SD.open("/");
|
|
if (root && root.isDirectory()) {
|
|
for (int i = 0; i < 16; ++i) {
|
|
File e = root.openNextFile();
|
|
if (!e) break;
|
|
if (!e.isDirectory() && e.size() > 0 && e.path() && strlen(e.path()) < sizeof probe_path) {
|
|
strlcpy(probe_path, e.path(), sizeof probe_path);
|
|
ref_len = e.size() < sizeof ref ? e.size() : sizeof ref;
|
|
if (e.read(ref, ref_len) != ref_len) { probe_path[0] = 0; ref_len = 0; }
|
|
e.close();
|
|
break;
|
|
}
|
|
e.close();
|
|
}
|
|
root.close();
|
|
}
|
|
}
|
|
|
|
// 2) Re-begin at the fast clock.
|
|
SD.end();
|
|
delay(20);
|
|
bool ok = SD.begin(cs_pin, spi, SD_SPI_FAST_HZ, "/sd", 6) && SD.cardType() != CARD_NONE;
|
|
|
|
// 3) Verify: directory walk always; byte-compare of the probe head when we have one.
|
|
if (ok) {
|
|
File root = SD.open("/");
|
|
ok = root && root.isDirectory();
|
|
if (ok) { File e = root.openNextFile(); if (e) e.close(); root.close(); }
|
|
}
|
|
if (ok && probe_path[0]) {
|
|
uint8_t now[512];
|
|
File p = SD.open(probe_path, FILE_READ);
|
|
ok = p && p.read(now, ref_len) == ref_len && memcmp(now, ref, ref_len) == 0;
|
|
if (p) p.close();
|
|
}
|
|
if (ok) {
|
|
Serial.printf("[%s] SD clock %lu -> %lu Hz (%s)\n", tag, (unsigned long)cur_hz,
|
|
(unsigned long)SD_SPI_FAST_HZ, probe_path[0] ? "read-verified" : "dir-verified, no probe file");
|
|
return (uint32_t)SD_SPI_FAST_HZ;
|
|
}
|
|
|
|
// 4) Fall back to the clock that just worked.
|
|
Serial.printf("[%s] SD %lu Hz verify FAILED; back to %lu Hz\n", tag,
|
|
(unsigned long)SD_SPI_FAST_HZ, (unsigned long)cur_hz);
|
|
for (int attempt = 0; attempt < 2; ++attempt) {
|
|
SD.end();
|
|
delay(attempt == 0 ? 60 : 150);
|
|
if (SD.begin(cs_pin, spi, cur_hz, "/sd", 6) && SD.cardType() != CARD_NONE) return cur_hz;
|
|
}
|
|
Serial.printf("[%s] SD remount at %lu Hz failed\n", tag, (unsigned long)cur_hz);
|
|
return 0;
|
|
#else
|
|
(void)cs_pin; (void)spi; (void)tag;
|
|
return cur_hz;
|
|
#endif
|
|
}
|
|
#endif // ESP32
|