Files
planar-EL512.256-H/docs/README.md
T
mangavalkandClaude Opus 5.5 df78b5e72c Fix VCLK at half rate; chunk the DMA stream; cycle test images
PCNT measurement showed I2S0 BCK (VCLK) rising once per two bytes in 8-bit
LCD mode, so the panel saw every other byte. Each VCLK is now two identical
bytes at a 10 MHz byte rate (VCLK 5 MHz). The doubled ~142 KB stream is
allocated as 38 per-descriptor chunks.

The test sketch now cycles through 12 labelled test images.

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

97 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).
- Stream: 261 lines × 544 bytes ≈ 142 KB, too big for one DMA block, so it is
allocated as 38 chunks of up to 7 lines (3808 bytes), one descriptor each, looped;
`eof` set once per frame.
## 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).
- [ ] Scope: data changing away from the rising VCLK edge with >= 8 ns setup/hold
(Table 6); which BCK edge lines up with the byte-pair boundary is unverified. If
pixels look shifted or noisy, set `INVERT_VCLK`.
- [ ] 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 `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).
Software:
- [ ] Tear-free updates: `show()` rewrites the stream while DMA reads it. Encode into
a second buffer and swap descriptors at the per-frame EOF, or encode only during
vertical blanking.
- [ ] Unit test for `encodeRow()` (see `test/README`).
- [ ] The ~142 KB stream (in 38 chunks) leaves ~190 KB heap; with Wi-Fi on it may
not fit. Fallback: a line ring refilled from the EOF interrupt.