Repository navigation
Fast e-paper updates that keep the panel balanced - #11739
mikeysklar wants to merge 29 commits into
Conversation
|
This PR description is way too long. Please simplify it. |
tannewt
left a comment
There was a problem hiding this comment.
Please explain these changes concisely.
|
Trimmed the description. What this PR does:
|
tannewt
left a comment
There was a problem hiding this comment.
The lag stuff is a lot of complexity and I don't really understand why/how they work to keep things balanced. I'd like to understand it more before we commit it.
|
Did some review to make sure I have this right. Updating code comments as well so the explanation is easy to find. Fast updates compare the new frame with the last one, so each change gets one weak push. Lag frames compare with the frame Each frame is used twice:
So the pushes always cancel. That is the balance: every pixel gets as many pushes toward black as toward white. Example with
2 pushes to black, 2 to white: balanced. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After a refresh finishes, write the same areas again with this command so the controller's previous image RAM matches the screen. This lets SSD168x style partial updates diff against what is actually shown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A keep_awake display ends a transaction still holding its bus when the VM ends, since Ctrl-C can leave one open and the stop sequence would otherwise wait for the bus forever. The stop sequence is also guarded when it follows a refresh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The previous RAM holds frame N-lag instead of N-1, so each changed pixel is driven for lag_frames updates, the same number toward black and toward white. Only the box around changed bytes is sent. clean_sequence runs before the first refresh and when the display is released, leaving the panel white and balanced. A waveform or panel reset cut short leaves the panel dark, so while one is under way an interrupt waits and is raised when it ends. A send it stops starts over. Waits are bounded per update so a stuck panel can't hang shutdown, and the watchdog is fed while interrupts are held. CIRCUITPY_EPAPERDISPLAY_LAG gates it, off by default and on for Feather RP2040 ThinkInk. clean_sequence is accepted on every build. lag_frames raises ValueError where the drive is off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings every pixel to white, optionally runs clean_sequence, then drives every pixel frames updates each way. For scripts to remove ghosting while running. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
release_displays() lost the owed drive and the stop sequence silently when something held the bus for the whole 30 s bound. Wait for the bus first, and raise with every display untouched, so the script can free it and try again. The end of the VM can't catch an exception, and doesn't need to: nothing can still hold the bus by then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The wait used the whole deadline, so a panel a reset would free got no clean. Reset once, wait once more with no renewal, and run the stored waveform if BUSY falls. If it stays high, nothing more is sent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The faster refresh options (write_previous_ram_command, keep_awake and update_refresh_mode's refresh_sequence) cost about 900 bytes, which no longer fits on several SAMD21 and SAMD51 boards. With the flag off, EPaperDisplay builds as it did before. Lag drive needs it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fast and lag arguments and clean() now exist on every build. Builds without them raise NotImplementedError when they are used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Those are the ports with e-paper boards. Other ports keep the same API and raise NotImplementedError, which saves flash on small boards such as the nRF52840 dongles that overflowed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It was only on for the Feather RP2040 ThinkInk, but external panels need it too. It costs about 7 KB of flash and 80 bytes of RAM. The frame memory is only allocated when lag_frames is used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each frame is driven toward once and away from once, lag steps later. Adds a small example of a pixel that changes back early. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bcbe057 to
4c2e227
Compare
Wouldn't frame 4 incorrectly show black? Maybe that's ok for a fast change. It seems like we always want lag frames when doing fast updates. Maybe we tie it's use to the FAST and partial ability? Then we omit both from SAMD and nRF52. |
|
What would inform the number of lag frames? That seems like an implementation detail that we could decide. I feel like we could simplify the API surface of this by simply having a "fast" mode that could be set. However, I don't know how dependent on the driver IC that is. |
Yep! Briefly. Frame 4 still shows black and clears at 5. On moving content that looks like a short trail. That's tradeoff for drive balanced.
Agreed. This is a good plan. I'll fold LAG into FAST and turn FAST off for SAMD and nRF52.
The two factors that would influence lag frame are :
The vendors don't specific guidance for these fast partial updates. We can use lag=4 to save memory and it looks acceptable. How about |
Lag drive is what makes fast updates safe for the panel, so it no longer has its own build flag. CIRCUITPY_EPAPERDISPLAY_LAG is gone and its code builds with CIRCUITPY_EPAPERDISPLAY_FAST. FAST stays on only for espressif and raspberrypi, so atmel-samd and nordic get neither. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fast=True is now the one switch for fast updates. It keeps the display awake between refreshes and drives with lag, so keep_awake is gone. lag_frames defaults to 4, must be 1 to 32 with fast, and is ignored without it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Simplified as you suggested.
It trades darker motion against smear and memory, so I kept it as an optional setting.
|
tannewt
left a comment
There was a problem hiding this comment.
Getting there! I like having LAG + FAST combined. Folks at supercon really wanted this for the badge epaper.
CIRCUITPY_EPAPERDISPLAY_FAST now follows CIRCUITPY_EPAPERDISPLAY, so new ports get it automatically. It is turned off where flash is tight: samd21, SAMx5x chips without 1MB flash (the same HAS_1MB_FLASH gate as ulab), and nordic boards that keep their filesystem in internal flash. The espressif and raspberrypi lines that set the same default are dropped. Add a default of 0 in circuitpy_mpconfig.h, since zephyr-cp builds the module without setting the flag. The feature cost 5.3 KB on espruino_pico, the tightest board that gains it, which keeps 16 KB free. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Per review. Also call the display "fast" in the end_held_bus comment, matching the renamed argument. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Per review. The lag_* members of the display object become an epaperdisplay_lag_t member named lag, with the prefix dropped (self->lag_head is now self->lag.head). No behavior change: the ThinkInk firmware is the same size. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It holds the fast argument, which replaced keep_awake. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Per review. The lag drive section and the helpers only it uses move verbatim from EPaperDisplay.c into lag_drive.c. The functions the two files share are declared, with what they return, in a private lag_drive.h, and get module-prefixed names: lag_refresh() becomes epaperdisplay_lag_refresh() and wait_for_busy() becomes epaperdisplay_epaperdisplay_wait_for_busy(). lag_drive.c compiles to nothing without CIRCUITPY_EPAPERDISPLAY_FAST, so zephyr-cp, which builds every file in the module, is unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # shared-bindings/epaperdisplay/EPaperDisplay.c
|
(FYI there are still unresolved/unresponded to comments.) |
|
Sorry about that. I totally missed those. |
Compile the fast keyword arguments and clean() only when CIRCUITPY_EPAPERDISPLAY_FAST is on, and keep the shared helpers static otherwise, so boards without fast build the same as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Off for ESP32-C3, C5 and C6, STM32F401 and F411, the WIZnet Picos and TG-Watch, which would have under 64 KB of flash free with it on. Also off for atmegazero_esp32s2 and microdev_micro_s2, which run out of internal RAM. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # ports/espressif/boards/atmegazero_esp32s2/mpconfigboard.mk # ports/espressif/boards/microdev_micro_s2/mpconfigboard.mk
What
E-paper displays can update in about 50 ms and drive every pixel equally both ways.
Why
Unbalanced fast updates can damage a panel.
Part 2 of 3, stacked on #11738.
write_previous_ram_commandfastlag_framesfastclean_sequence,clean()update_refresh_mode(refresh_sequence=)All off by default.
fastis on for espressif and raspberrypi; other ports raiseNotImplementedError.The docs warn that this exceeds panel makers' refresh limits, and long-term wear is unknown.
Question: should the previous RAM write move from
refresh()into the background task?AI assistance
Written with Claude Code.
🤖 Generated with Claude Code