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
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
Merge Phase 3.5 Workstream D: internal-RTC MMIO introspection
Brings the ESP32 internal RTC to console parity with the I2C chips:
- Engine: BusKind::Mmio + ChipRegmap::mmioBase + 32-bit register width,
hardened validator (width 4 is mmio-only, base zero rejected, writable
implies critical) and a structurally read-only MmioRegIo (write forms
no pointer and always refuses, even under WriteGate::Forced).
- Per-SoC RTC_CNTL descriptor tables, offsets and fields verified against
the vendored IDF 6.0.2 soc headers: ESP32-S3 (21 registers @0x60008000)
and classic ESP32 (17 @0x3FF48000), every register /// documented, with
device static_asserts that provably bind (perturbed offset fails the
build) plus table-size pins.
- Device wiring gated to the four internal-RTC boards (!HAS_BM8563_RTC);
console REGS?/REGR support with REGW/REGW! refusal (layer 3 of the
three-layer write block — composition verified by exhaustive write-path
enumeration in the final review); QA extension.
- Live HIL: sticks3 (21 regs, live TIME read, both write forms refused),
m5stack_gray (17 regs), plus2 BM8563 regression clean.
- Docs: docs/hardware/introspection/rtc-cntl.md + index + cross-links.
Gates: native 1665/1665, fleet 18/18 -Werror (plus1 58.1% ceiling, +0B),
pio check 0, guards + doxygen clean, QA no-HW green. Whole-branch review:
ready to merge (0 Critical/Important).
Claude-Session: https://claude.ai/code/session_01U878bNJtg9bCvAMM8CFKqS
Add trackball driver layer: ITrackball, quadrature driver, factory
Bring the trackball to the same device-driver structure as PMIC/RTC/IMU/
keyboard. A trackball is a passive GPIO device — no bus, registers, or chip
identity — so the layer is honest about that: it cannot be electrically
probed, and its motion needs a physical finger.
- i_trackball.h: device-level ITrackball contract (init/isAvailable/name,
drainSteps raw quadrature snapshot, isClickDown, inject, selfTest) + the
QuadratureSteps transport struct.
- quadrature_trackball.{h,cpp}: generic four-direction + click GPIO quadrature
driver parameterized on its pin mapping (the T-Deck is one mapping). Holds
the falling-edge IRAM ISRs, ISR-resident step accumulators and velocity
timestamps (a hardware singleton), moved verbatim from the HAL wrapper so
ISR discipline and velocity math are unchanged.
- trackball_factory.{h,cpp}: flag-gated createTrackball (TRACKBALL_TYPE_*,
like the LilyGo keyboard leg) with a natively-testable constexpr classifier
and pin-validity check standing in for the impossible address probe; returns
the shared NullTrackball otherwise.
- trackball_selftest.h: pure, hardware-free pass/fail helpers (drain coherence,
informational click-idle level, overall gate) — the passive-device analogue
of imu_selftest.h / keyboard_selftest.h.
- null_trackball.h: never-null no-op driver for boards without a trackball.
- TRACKBALL_TYPE_QUADRATURE flag default (platform_defaults.h) + T-Deck enable;
drv_trackball Doxygen group.
Claude-Session: https://claude.ai/code/session_01SV58JXhfxhhc9DC6vdjBo4