Tune the SSD1681 fast waveform to 5 fps and add 2-bit grayscale rendering
Optimization round on the CoreInk fast-animation mode, all measured on
silicon via the EINKLUT live-tuning command (sweep waveform variants over
serial without reflashing):
- Fast partials run 0x22 = 0x8C: skipping the enable-analog stage (its
HV-ready detection costs ~91 ms even with the rails already up) cuts the
per-update fixed cost to ~94 ms. The stage is mandatory after full
refreshes, whose sequence powers the analog down, so the bank-settle pass
keeps 0xCC and doubles as the analog power-up (fastSettleSequence).
- Built-in waveform is the owner-picked quality-ladder point: 4 drive
frames at +/-17 V source voltage (VCOM/VGH stay on the panel's OTP trim)
plus a 1-frame reinforcement poke, the only ladder variant whose
animation trail self-cleans: 194 ms/update, 5.1 fps (from 503 ms / 1.5
fps at the start of the effort). BUSY poll tightened to 1 ms; the
post-update RAM rewrite proved redundant (read-back-verified) and the
gate-scan-window experiment proved frame period is FR-fixed, so neither
ships.
- ssd1681_lut.h: pure constexpr builders for the 153-byte waveform layout
(fast differential LUT with kick/burst/rest/poke knobs, graded grayscale
LUT, source-voltage register codes), the single source of truth shared
by the controller tables, EINKLUT and EINKGRAY, with a native host test
suite pinning the layout against the datasheet.
- 2-bit grayscale rendering (EpaperPanelDriver::renderGrayImage +
display::renderEpaperGray + EINKGRAY): both RAM planes select one of
four LUT slots per pixel, so graded drive lengths yield 4 tones in one
update or 6 tones (white + 4 grays + black) in two stacked updates —
owner-validated on the panel. The command holds the tones until serial
input (returning lets the FSM repaint and collapse them).
- SSD1681 register read-back (readPanelRegister over the shared SDA line):
the 0x2F Status Bit Read chip-ID bits fingerprint the controller in
silicon, settling that this CoreInk unit carries the D67/SSD1681 panel
revision, and SPIRD now works on e-paper. ANIMTEST gains TAG plus an
on-panel firmware-revision letter so tuning-ladder observers always know
which build/step they are watching.
Claude-Session: https://claude.ai/code/session_01SV58JXhfxhhc9DC6vdjBo4
Add one-shot GPS time-sync engine with guaranteed power-down
- gps::SyncLogic (pure, header-only, host-testable): one-shot state
machine fed raw receiver bytes + a monotonic now; terminal phases are
latched until a fresh start(). Fixes are accepted only when the year
lies in [2024, 2099] (the set-clock widget range — the NMEA parser
alone admits ZDA years to 2255 and the epoch validator only floors at
2001) AND the epoch clears rtc::internal::kMinValidEpoch.
- gps_time_sync (device wiring, #if HAS_GPS with no-op stubs elsewhere):
UART facade begin -> drain/feed/tick -> on the first valid fix one
atomic rtc::setDateTime write + ClockSource::Gps tag -> power-down.
Power-down runs on EVERY exit path (success, timeout, cancel, begin
failure) with best-effort L76K $PCAS12 / u-blox UBX-RXM-PMREQ standby
frames before the UART teardown returns the pads to hi-Z inputs.
- Defense-in-depth: PowerManagerTDeck::enterDeepSleep cancels an
abandoned sync before cutting the BOARD_POWERON rail.
- 14 native tests: fix accept, checksum reject, year clamp (floor, cap,
boundaries), epoch floor, timeout boundary, cancel semantics, terminal
latching, uint32 wrap, nullptr feed. Non-GPS boards verified symbol-free.
Claude-Session: https://claude.ai/code/session_01SV58JXhfxhhc9DC6vdjBo4
Add M5PM1 PY32 register map table
Introspection descriptor table for the M5StickC Plus S3's custom
PY32-based M5PM1 PMIC (I2C 0x6E), feeding the generic regmap engine.
The M5PM1 has no public datasheet. Every entry is confirmed against the
repo's hardware-validated py32_pmic_regs.h / py32_pmic.cpp (mirrored from
M5GFX board_M5StickS3), independently cross-checked against Espressif's
esp-claw StickS3 power_manager.c (0x09 I2C config, 0x10/0x11/0x13/0x16
GPIO block, PYG2/PYG3 rail masks) and M5Stack docs (PYG0..PYG4 mapping).
Field tables only where the bit layout is hardware-confirmed; everything
else is prose. Criticals flag rail/power-off/timed-power-on/download-lock
registers (0x06, 0x07, 0x0C, 0x10/0x11/0x13/0x16, 0x3C, 0x3D, 0x49, 0x4A).
py32_pmic.cpp now registers the table after ready_ = true and logs the
DeviceId/Model identity bytes (previously read but discarded); boot is
not gated on them. Native test mirrors test_axp192_regmap: validator,
floor >= 20, anchors, criticals, no-truncation sweep.
Claude-Session: https://claude.ai/code/session_01Ciw3Ea5Bb2YAcmuZ1dywRY
Add IP5306 and AW9523 register map tables
Add datasheet/community-verified register-map descriptor tables for the
Injoinic IP5306 boost-charger (M5Stack Gray, 13 regs) and the AWINIC
AW9523B I/O expander (CoreS3 SE, 29 regs), feeding the Phase-2
introspection engine (decoded dumps, named-field access, critical write
blocklist). Register both maps on their drivers' begin() success and add
a combined native test suite (validator, floors, anchors, critical
classification, formatRegLine no-truncation fence).
IP5306 0x77 key events are read-to-clear, modeled Access::R with a doc
note; the SYS_CTL1/SYS_CTL2 bit layouts disagree across community maps
and are documented in prose only.
Claude-Session: https://claude.ai/code/session_01Ciw3Ea5Bb2YAcmuZ1dywRY
Tune the SSD1681 fast waveform to 5 fps and add 2-bit grayscale rendering
Optimization round on the CoreInk fast-animation mode, all measured on
silicon via the EINKLUT live-tuning command (sweep waveform variants over
serial without reflashing):
- Fast partials run 0x22 = 0x8C: skipping the enable-analog stage (its
HV-ready detection costs ~91 ms even with the rails already up) cuts the
per-update fixed cost to ~94 ms. The stage is mandatory after full
refreshes, whose sequence powers the analog down, so the bank-settle pass
keeps 0xCC and doubles as the analog power-up (fastSettleSequence).
- Built-in waveform is the owner-picked quality-ladder point: 4 drive
frames at +/-17 V source voltage (VCOM/VGH stay on the panel's OTP trim)
plus a 1-frame reinforcement poke, the only ladder variant whose
animation trail self-cleans: 194 ms/update, 5.1 fps (from 503 ms / 1.5
fps at the start of the effort). BUSY poll tightened to 1 ms; the
post-update RAM rewrite proved redundant (read-back-verified) and the
gate-scan-window experiment proved frame period is FR-fixed, so neither
ships.
- ssd1681_lut.h: pure constexpr builders for the 153-byte waveform layout
(fast differential LUT with kick/burst/rest/poke knobs, graded grayscale
LUT, source-voltage register codes), the single source of truth shared
by the controller tables, EINKLUT and EINKGRAY, with a native host test
suite pinning the layout against the datasheet.
- 2-bit grayscale rendering (EpaperPanelDriver::renderGrayImage +
display::renderEpaperGray + EINKGRAY): both RAM planes select one of
four LUT slots per pixel, so graded drive lengths yield 4 tones in one
update or 6 tones (white + 4 grays + black) in two stacked updates —
owner-validated on the panel. The command holds the tones until serial
input (returning lets the FSM repaint and collapse them).
- SSD1681 register read-back (readPanelRegister over the shared SDA line):
the 0x2F Status Bit Read chip-ID bits fingerprint the controller in
silicon, settling that this CoreInk unit carries the D67/SSD1681 panel
revision, and SPIRD now works on e-paper. ANIMTEST gains TAG plus an
on-panel firmware-revision letter so tuning-ladder observers always know
which build/step they are watching.
Claude-Session: https://claude.ai/code/session_01SV58JXhfxhhc9DC6vdjBo4
Document the GPS layer, add T-Deck HIL coverage, fix VALSET on real silicon
Add the u-blox M10 introspection chip page and index row, and a HIL suite
(test_gps_layer.py) exercised on the real T-Deck module. The first hardware
run surfaced three genuine M10 behaviors the unit tests could not:
- UBX-CFG-VALSET layers is a bitmask (RAM = bit 0 = 0x01), not the VALGET
layer enum: an all-zero mask writes nowhere and never ACKs, so every
GPSCFG set! silently failed. Fixed + native regression test.
- readStatus must enable periodic NAV-PVT/NAV-SAT/MON-HW UART1 output first:
the M10 boots NMEA-only and ignores on-demand status polls.
- Config/ACK timeouts raised and a VALSET resend budget added, since VALGET/
VALSET now compete with that periodic stream.
Fleet 43/43 -Werror, native 1716/1716, clang-tidy 0, doxygen 0. HIL on the
real u-blox M10: 6 passed / 1 skip (GPSPROBE u-blox M10, GPSSYNC_OK via NVS
fast-path, GPSCFG dump/get/critical-bang read-back-safe, GPSMON).
Claude-Session: https://claude.ai/code/session_01SV58JXhfxhhc9DC6vdjBo4