Files
planar-EL512.256-H/docs/README.md
T
mangavalkandClaude Opus 5.5 901a213079 Invert VCLK to fix pixel jitter
Uninverted, the rising BCK edge landed on the boundary between byte pairs,
so the panel sampled data mid-change and the image jittered. Inverted, the
edge sits mid-pair; verified stable on the panel. Also note the row-stripe
brightness drift as an open item.

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

99 lines
5.0 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).
- [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.
- [ ] "row stripes" test image: brightness drifts in slow waves. Unexplained;
suspected beat between the alternating full/empty row load and the panel's
DC/DC converter or row drive.
- [ ] 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.