Files
planar-EL512.256-H/docs/README.md
T
mangavalkandClaude Opus 5.5 6d5e5315f0 Fix PlatformIO build; document ESPHome component and ring-buffer driver
- el512_panel.cpp: esp_cpu_get_cycle_count() exists only in ESP-IDF 5;
  use cpu_hal_get_cycle_count() on 4.4 (Arduino 2.x), which broke both
  PlatformIO environments.
- README: ring-buffer refresh, tear-free status, D1 mini ESP32, new layout
  (components/el512, esphome/, home_assistant/), ESPHome setup, Still Alive
  paths, and the Wi-Fi blanking findings with wiring advice.
- docs/README: ring, lookup table, double buffer, health counters; tear-free
  and memory items done; Wi-Fi blanking and boot blanking as open items.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 21:05:06 +02:00

118 lines
6.6 KiB
Markdown

# Notes
- Plan: https://claude.ai/code/artifact/3b9eda11-a87e-4eea-bbb9-e6454e558991
- Datasheet: [datasheet/EL512.256-H3 FRB.pdf](datasheet/EL512.256-H3%20FRB.pdf)
## Approach
I2S0 in 8-bit parallel (LCD) mode with a circular DMA descriptor chain. BCK rises once
per two DMA bytes (measured with PCNT: 2.5 MHz BCK at a 5 MHz byte rate), so each VCLK
period is two identical bytes:
| Bit | Signal |
|-----|--------|
| 0 | VID (even columns) |
| 1 | TVID (odd columns) |
| 2 | HS |
| 3 | VS |
The I2S0 BCK output drives VCLK. ENABLE (GPIO 25) and _LOWPWR (GPIO 26) are plain GPIO.
I2S0 setup (`EL512Panel::startI2S()`), following bitluni's ESP32Lib 8-bit mode:
- Clock: PLL_D2_CLK 160 MHz / `clkm_div_num` 16, `tx_bck_div_num` 1 -> 10 MHz byte
rate, 5 MHz BCK (two bytes per BCK period).
- `lcd_en`, `lcd_tx_wrx2_en`, `tx_bits_mod` 8, `tx_fifo_mod` 1, `tx_chan_mod` 1,
`tx_right_first`. With this setup each 32-bit DMA word goes out as bytes 2, 3, 0, 1,
so `encodeRow()` stores stream byte i at `i ^ 2`.
- Stream bits 0..3 -> `I2S0O_DATA_OUT16_IDX` + n; BCK -> VCLK (`INVERT_VCLK` flips it).
- Ring: 8 descriptors of 4 lines (2176 bytes each, 17 KB total), looped, `eof` on
every descriptor. The EOF interrupt (IRAM, so it keeps running during flash writes)
re-encodes the descriptor that just finished with the lines 32 later, so the ring
stays ~1.7 ms ahead of the panel. Each descriptor tracks its own first line, so a
missed interrupt costs one stale chunk instead of shifting the picture.
- Encoding: a 256-entry table turns one framebuffer byte (8 pixels) into its 8 stream
bytes, already in `^ 2` order; a line takes about a microsecond.
- Double buffer: drawing goes into `_fb`; `show()` asks the interrupt to copy it to
the displayed buffer just before it encodes line 0, and waits for that (≤ 1 frame).
- The driver uses ESP-IDF calls only and lives in `components/el512/`; PlatformIO
builds it from there as library `EL512Core` (`lib_extra_dirs`), ESPHome as the
`el512` external component.
- Health counters (`takeStats()`): refills, largest refill gap, swaps, swap timeouts,
TX FIFO underruns. With Wi-Fi on: ~4600 refills/s, max gap < 0.4 ms, no underruns.
## From the datasheet
The datasheet is the source of truth for everything in this section and wins over any
other doc, comment or the plan.
- Two-bit parallel mode is selected by removing jumper 2 (DCONFIG) of pin strip PS1
(Table 3; factory default has jumpers 1 and 2 fitted). Even columns on VID, odd
columns on TVID, numbered from the left as seen by the viewer; 256 VCLKs per row.
The datasheet numbers from 1 (Fig. 3: "row 1", "Last pixel (512 / 256)"), so the
leftmost pixel, x = 0 in the code, is column 1 and goes on TVID.
- VCLK is active on the rising edge and should be kept running.
- HS envelopes the pixels of a row: the visible pixels are the last 512 (two-bit mode:
last 256 clocks) before HS falls. The number of VCLKs during HS high should be even.
- The topmost row is the first HS high time ending after the rising edge of VS. Only
the rising edge of VS is used; VS high/low time is at least 1 VCLK (T2).
- Table 5: front porch T1 >= 100 µs, HS setup to VS T4 >= 1 VCLK, HS hold from VS
T5 >= 3 µs, HS low T6 >= 4 VCLK, HS period T8 >= 50 µs, VS 70 Hz typ / 75 Hz max.
- Figure 4 / Table 6: VID, TVID, HS and VS need 8 ns setup and 8 ns hold around the
rising VCLK edge; VCLK period >= 33 ns (30 MHz max).
- Inputs are HCT compatible with pull-ups (Table 4: high >= 2 V, low <= 0.8 V,
abs. max Vcc1 + 0.5 V). ENABLE and _LOWPWR are on J2 and pulled high internally.
## Open items
Done:
- [x] Implement `EL512Panel::startI2S()` (I2S0 LCD mode, GPIO matrix routing,
circular `lldesc_t` chain).
- [x] Handle the 8-bit LCD-mode byte-pair swap in `encodeRow()` (byte i at `i ^ 2`).
- [x] Map x = 0 (datasheet column 1, odd) to TVID, not VID.
- [x] VCLK rate: PCNT showed BCK at half the byte rate (panel saw every other byte,
image squeezed and repeated). Fixed with two bytes per VCLK at a 10 MHz byte
rate; PCNT now reads VCLK 5.0 MHz, VS 70 Hz.
- [x] I2S0 data signal base `I2S0O_DATA_OUT16_IDX`: GPIO 19/21/22/23 all toggle.
- [x] Panel shows a correct image in two-bit mode.
Hardware checks:
- [ ] Self test (Table 3): remove PS1 jumper 1, power up; all pixels except the
first half of the top row should light. Refit jumper 1.
- [ ] HS reads ~18,030 Hz on PCNT where 5 MHz / 272 = 18,382 Hz is expected (~2% low)
while VCLK reads 5.0 MHz. Frame rate is still ~70 Hz; cause unknown, check on a
scope (HS 256 clocks high / 16 low, T6-T8).
- [x] VCLK edge phase: with `INVERT_VCLK = false` the image jittered (rising edge on
the byte-pair boundary); `true` puts it mid-pair and the jitter is gone.
- [x] "row stripes" test image: brightness drifts in slow waves. Panel behaviour,
not the signal: unchanged at 73.7 Hz (260 clocks/row) vs 70.4 Hz, on a 15 V 4 A
Vcc2 supply, while the checkerboard (same average load, spread within each row)
and all other images are stable. Attributed to the panel's drive reacting to
whole rows alternating fully on / fully off; kept 272 clocks/row.
- [ ] Confirm the `^ 2` byte order. HS edges sit on word boundaries, so a wrong order
does not show on HS; on the panel it shuffles pixels in groups of 8 (a 1-pixel
line at x = 0 lands at x = 4).
- [ ] Confirm the column parity: the "checkerboard" and "VID/TVID columns" test images
in `src/test_images/main.cpp` must look as drawn, and the left border must sit on the panel's
first column.
- [ ] Add the 5 V buffer (74HCT245/74AHCT541) and a pull-down on ENABLE so the panel
stays off while the ESP32 boots without VCLK (Table 1: ENABLE pulled high
internally; Table 5 note 3: VCLK should be kept running). The panel blanks a few
times during boot until then.
- [ ] Wi-Fi blanking: with Wi-Fi on (RSSI -90 dB, so full transmit power) the panel
blanks for a moment now and then, mostly at page changes (each reports the new
page to Home Assistant). Gone with `wifi: enable_on_boot: false`; ENABLE/_LOWPWR
were not connected; the health counters stayed clean during 3 blinks in 2 min.
So the signal is disturbed on the wires, not late from the ESP32. Next: all six
J1 GND pins along the signal wires, antenna away from the cable, the 5 V buffer;
`output_power: 14dB` as a quick check.
Software:
- [x] Tear-free updates: back buffer copied to the displayed buffer at line 0.
- [x] Memory: the full-frame stream (142 KB) is replaced by a 17 KB ring; ~190 KB
internal heap is left with Wi-Fi, the API and the ESPHome pages running.
- [ ] Unit test for `encodeRow()` (see `test/README`).