diff --git a/zephcore/Kconfig b/zephcore/Kconfig index 8a345bc..3f7ee1d 100644 --- a/zephcore/Kconfig +++ b/zephcore/Kconfig @@ -736,6 +736,20 @@ config ZEPHCORE_DISPLAY_INSET hardware artefacts — a small inset hides them behind a clean background margin. Set to 0 on boards without edge artefacts. +config ZEPHCORE_DISPLAY_EPD_FULL_REFRESH_INTERVAL + int "E-paper full-refresh interval (partial refreshes)" + default 8 + depends on ZEPHCORE_UI_DISPLAY + help + On e-paper panels, partial refreshes never fully clear the previous + image, so ghosting accumulates where text changes most (status bar + name/time/battery). After this many partial refreshes, perform one + full refresh to clear the panel. A full refresh is the ~2 s + black/white flash, so keep this large enough that it isn't constant + but small enough that ghosting never becomes unreadable. Set to 0 to + disable periodic full refreshes (partial-only — accumulates ghosting). + Ignored on non-EPD displays (OLED/TFT never ghost). + config ZEPHCORE_DISPLAY_LARGE_FONT bool "Use a larger built-in CFB font" default n diff --git a/zephcore/helpers/ui/display.c b/zephcore/helpers/ui/display.c index 7efadee..44e9346 100644 --- a/zephcore/helpers/ui/display.c +++ b/zephcore/helpers/ui/display.c @@ -74,6 +74,11 @@ static const struct device *panel_vdd_reg; static uint32_t epd_frame_hash; static uint32_t epd_last_frame_hash = UINT_MAX; +/* Periodic full refresh: count partial refreshes and force a full refresh + * every CONFIG_ZEPHCORE_DISPLAY_EPD_FULL_REFRESH_INTERVAL frames to clear + * accumulated e-paper ghosting. 0 = disabled (partial-only). */ +static uint32_t epd_partial_count; + static inline void epd_hash_bytes(const void *data, size_t len) { if (!is_epd || !data || len == 0) { @@ -512,7 +517,23 @@ void mc_display_finalize(void) if (is_epd && epd_frame_hash == epd_last_frame_hash) { return; } - cfb_framebuffer_finalize(disp_dev); + + const int full_interval = CONFIG_ZEPHCORE_DISPLAY_EPD_FULL_REFRESH_INTERVAL; + + if (is_epd && full_interval > 0 && ++epd_partial_count >= (uint32_t)full_interval) { + /* Periodic full refresh to clear accumulated ghosting. + * blanking_on selects the FULL profile and loads RAM without + * updating the panel; cfb_framebuffer_finalize writes the + * current framebuffer into both RAM banks; blanking_off then + * triggers the full-refresh update with that content. */ + display_blanking_on(disp_dev); + cfb_framebuffer_finalize(disp_dev); + display_blanking_off(disp_dev); + epd_partial_count = 0; + } else { + cfb_framebuffer_finalize(disp_dev); + } + if (is_epd) { epd_last_frame_hash = epd_frame_hash; } @@ -553,6 +574,7 @@ void mc_display_epd_full_reset(void) display_blanking_on(disp_dev); display_blanking_off(disp_dev); epd_last_frame_hash = UINT_MAX; + epd_partial_count = 0; /* After splash handoff, keep frontlight off; next user interaction * wakes it via mc_display_on(). */