Add 4-level greyscale support for ThinkInk_420_Grayscale4_MFGN#2
Draft
tyeth wants to merge 4 commits into
Draft
Conversation
The chip-default OTP LUT is mono. Driving 4 levels needs three pieces working together that the existing driver was missing: - A panel-specific 227-byte waveform LUT loaded via 0x32. This is ported verbatim from Adafruit_EPD's src/panels/ThinkInk_420_Grayscale4_MFGN.h (ti_420mfgn_gray4_lut_code) and exposed as THINKINK_420_GRAYSCALE4_MFGN_LUT. - Extra init bytes the chip default omits — boost-softstart (0x0C), end-option override (0x18), gate voltage (0x03), source voltage (0x04), VCOM (0x2C), plus the greyscale border value on 0x3C — exposed as THINKINK_420_GRAYSCALE4_MFGN_INIT and spliced via a new extra_init kwarg. - Display Update Control 2 (0x22) set to 0xCF instead of 0xC7 when custom_lut is provided, so the chip runs the Display-Mode-2 refresh path (bit 3) and actually uses the LUT we just pushed via 0x32. Previously 0xC7 selected Display-Mode-1 (the mono waveform), which silently ignored any custom LUT and prevented 4-level rendering. Plus a conditional swap of write_black_ram_command and write_color_ram_command when grayscale=True: the LUT encodes the lighter mid-tone as (black_RAM=0, color_RAM=1) and the darker mid-tone as (1, 0), but displayio's grayscale palette quantiser puts luma bit 7 on pass 0 (the black-RAM command) and luma bit 6 on pass 1 (the color-RAM command), giving the opposite mid-tone bits. Swapping the commands aligns displayio's encoding with the LUT; white (1,1) and black (0,0) are unaffected. Verified end-to-end on a Pi e-Ink Bonnet driving the ThinkInk_420_Grayscale4_MFGN: a 4-band swatch (0xFFFFFF / 0xAAAAAA / 0x555555 / 0x000000) renders as four visibly distinct, monotonic grey levels. Adds examples/4_2_inch_400x300_grayscale.py wiring the new constants together for the MFGN panel.
CI: - Collapse the aligned-comment whitespace padding in THINKINK_420_GRAYSCALE4_MFGN_INIT. - Wrap the 227-byte LUT bytes block in `# fmt: off` / `# fmt: on` so ruff format doesn't expand each byte to its own line. - Collapse the start_sequence assembly back to one line. The grayscale4-lut-followup doc records the rationale for the RAM-command swap and an alternative fix path that modifies the LUT data itself, including what we tried and what would still need to be done to make that path land. The conditional command swap stays in place as the working fix.
Member
Author
|
Drafting this while I investigate if the black_ram_command/white_ram_command need swapping or if the LUT can be adjusted for displayio compatibility instead. |
Member
Author
|
ZJY400300-042CAAMFGN_revA is the panel id to add in comments and in adafruit/Wippersnapper_Components#304 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Drives 4 distinct grey levels on the 4.2" 400×300 ThinkInk_420_Grayscale4_MFGN https://www.adafruit.com/product/6381 . The chip-default OTP LUT is mono and cannot resolve 4 levels — so even with
grayscale=Truethe existing driver collapsed to two levels in practice. This adds the missing pieces:THINKINK_420_GRAYSCALE4_MFGN_LUT) — verbatim port ofti_420mfgn_gray4_lut_codefromAdafruit_EPD/src/panels/ThinkInk_420_Grayscale4_MFGN.h.THINKINK_420_GRAYSCALE4_MFGN_INIT) the chip default omits — boost-softstart (0x0C), end-option override (0x18), gate voltage (0x03), source voltage (0x04), VCOM (0x2C), plus the greyscale border value on0x3C. Threaded through a newextra_initkwarg that is byte-packed and spliced between the chip-default_START_SEQUENCEand the LUT load.custom_lutis provided, set command0x22to0xCF(Display Mode 2, bit 3 set) instead of0xC7.0xC7selects Display Mode 1 — the chip's mono waveform path — which silently ignores any LUT loaded via0x32. With0xCFthe LUT actually drives the panel.write_black_ram_command↔write_color_ram_commandswap whengrayscale=True. The Adafruit_EPD waveform LUT encodes the lighter mid-tone as(black_RAM=0, color_RAM=1)and the darker mid-tone as(1, 0), butdisplayio's greyscale palette quantiser puts luma bit 7 on pass 0 (the black-RAM command) and luma bit 6 on pass 1 (the color-RAM command), giving the opposite mid-tone bits. Swapping the two0x24/0x26command IDs alignsdisplayio's encoding with the LUT; the white(1,1)and black(0,0)corners are unaffected.Adds
examples/4_2_inch_400x300_grayscale.pywiring the new constants together for the MFGN panel.Test plan
0xAAAAAAto black.ssd1683_simpletest.py(mono path) andssd1683_ThinkInk_420_Tricolor_MFGNR.pycontinue to work unchanged —extra_initdefaults tob"", the RAM-command swap only fires whengrayscale=True, and the0xC7→0xCFchange only fires whencustom_lutis provided.🤖 Generated with Claude Code