do a full display reset after splashscreen

This commit is contained in:
liquidraver
2026-04-29 09:16:18 +02:00
parent 16ea239234
commit 51f7b5e7e4
3 changed files with 241 additions and 151 deletions
+80
View File
@@ -24,6 +24,7 @@
#include <zephyr/kernel.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(zephcore_display, CONFIG_ZEPHCORE_BOARD_LOG_LEVEL);
@@ -58,6 +59,31 @@ static const struct device *backlight_reg;
static bool backlight_on;
/* EPD frame change detection (Arduino-style):
* hash draw calls across a frame and skip hardware flush if unchanged. */
static uint32_t epd_frame_hash;
static uint32_t epd_last_frame_hash = UINT_MAX;
static inline void epd_hash_bytes(const void *data, size_t len)
{
if (!is_epd || !data || len == 0) {
return;
}
const uint8_t *p = (const uint8_t *)data;
for (size_t i = 0; i < len; i++) {
/* FNV-1a */
epd_frame_hash ^= p[i];
epd_frame_hash *= 16777619u;
}
}
static inline void epd_hash_u32(uint32_t v)
{
epd_hash_bytes(&v, sizeof(v));
}
static inline void backlight_set(bool on)
{
if (backlight_reg && device_is_ready(backlight_reg) && on != backlight_on) {
@@ -336,6 +362,9 @@ void mc_display_clear(void)
return;
}
if (is_epd) {
epd_frame_hash = 2166136261u;
}
cfb_framebuffer_clear(disp_dev, false);
}
@@ -349,6 +378,12 @@ void mc_display_text(int x, int y, const char *text, bool invert)
cfb_framebuffer_invert(disp_dev);
}
if (is_epd) {
epd_hash_u32((uint32_t)x);
epd_hash_u32((uint32_t)y);
epd_hash_u32(invert ? 1u : 0u);
epd_hash_bytes(text, strlen(text));
}
cfb_print(disp_dev, text, x + DISP_INSET, y + DISP_INSET);
if (invert) {
@@ -365,6 +400,12 @@ void mc_display_fill_rect(int x, int y, int w, int h)
/* CFB doesn't have a native fill_rect, so we draw line by line */
const int row_clamp = (int)disp_height - DISP_INSET;
if (is_epd) {
epd_hash_u32((uint32_t)x);
epd_hash_u32((uint32_t)y);
epd_hash_u32((uint32_t)w);
epd_hash_u32((uint32_t)h);
}
for (int row = y + DISP_INSET; row < y + h + DISP_INSET && row < row_clamp; row++) {
struct cfb_position start = { .x = x + DISP_INSET, .y = row };
struct cfb_position end = { .x = x + w - 1 + DISP_INSET, .y = row };
@@ -380,6 +421,12 @@ void mc_display_hline(int x, int y, int w)
struct cfb_position start = { .x = x + DISP_INSET, .y = y + DISP_INSET };
struct cfb_position end = { .x = x + w - 1 + DISP_INSET, .y = y + DISP_INSET };
if (is_epd) {
epd_hash_u32((uint32_t)x);
epd_hash_u32((uint32_t)y);
epd_hash_u32((uint32_t)w);
}
cfb_draw_line(disp_dev, &start, &end);
}
@@ -393,6 +440,15 @@ void mc_display_xbm(int x, int y, const uint8_t *data, int w, int h)
* This matches the Arduino MeshCore logo data from icons.h.
* Each row is padded to byte boundary: bytes_per_row = (w+7)/8 */
int bytes_per_row = (w + 7) / 8;
size_t bitmap_len = (size_t)bytes_per_row * (size_t)h;
if (is_epd) {
epd_hash_u32((uint32_t)x);
epd_hash_u32((uint32_t)y);
epd_hash_u32((uint32_t)w);
epd_hash_u32((uint32_t)h);
epd_hash_bytes(data, bitmap_len);
}
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
@@ -421,7 +477,13 @@ void mc_display_finalize(void)
return;
}
if (is_epd && epd_frame_hash == epd_last_frame_hash) {
return;
}
cfb_framebuffer_finalize(disp_dev);
if (is_epd) {
epd_last_frame_hash = epd_frame_hash;
}
}
void mc_display_reset_auto_off(void)
@@ -439,6 +501,24 @@ void mc_display_reset_auto_off(void)
#endif
}
void mc_display_epd_full_reset(void)
{
if (!disp_initialized || !is_epd) {
return;
}
/* Force the SSD16xx path back through a full-refresh cycle before
* entering steady-state partial updates for page rendering. */
display_blanking_on(disp_dev);
display_blanking_off(disp_dev);
epd_last_frame_hash = UINT_MAX;
/* After splash handoff, keep frontlight off; next user interaction
* wakes it via mc_display_on(). */
backlight_set(false);
disp_on = false;
}
const struct device *mc_display_get_device(void)
{
return disp_initialized ? disp_dev : NULL;
+157 -151
View File
@@ -1,151 +1,157 @@
/*
* ZephCore - Display Abstraction (CFB)
* Copyright (c) 2025 ZephCore
* SPDX-License-Identifier: Apache-2.0
*
* Wraps Zephyr's Character Framebuffer (CFB) subsystem with:
* - Auto-detection from devicetree (any Zephyr-supported display)
* - Runtime resolution query (supports any size, not just 128x64)
* - Auto-off timer via k_work_delayable
* - Simple text/rect drawing API for UI pages
*
* All functions prefixed mc_display_ to avoid collision with
* Zephyr's display_* namespace in <zephyr/drivers/display.h>.
*/
#ifndef ZEPHCORE_DISPLAY_H
#define ZEPHCORE_DISPLAY_H
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Initialize the display from devicetree.
* Detects any Zephyr-supported display via:
* 1. "zephyr,display" chosen node (standard)
* 2. Legacy nodelabels: sh1106, ssd1306 (backwards compat)
*
* Queries actual resolution from driver — no hardcoded dimensions.
*
* @return 0 on success, negative errno on failure, -ENODEV if no display
*/
int mc_display_init(void);
/**
* Get display width in pixels (queried from hardware at init).
* Returns 0 if display not initialized.
*/
uint16_t mc_display_width(void);
/**
* Get display height in pixels (queried from hardware at init).
* Returns 0 if display not initialized.
*/
uint16_t mc_display_height(void);
/**
* Get active font width in pixels.
* Returns 0 if display not initialized.
*/
uint8_t mc_display_font_width(void);
/**
* Get active font height in pixels.
* Returns 0 if display not initialized.
*/
uint8_t mc_display_font_height(void);
/**
* Turn display on (wake from blanking).
* Resets the auto-off timer.
*/
void mc_display_on(void);
/**
* Turn display off (blanking).
*/
void mc_display_off(void);
/**
* @return true if the display is currently on
*/
bool mc_display_is_on(void);
/**
* @return true if the display is an e-paper (EPD) type.
* EPD displays have slow refresh (~2s) and use zero power when static,
* so callers should use longer update intervals and skip blanking.
*/
bool mc_display_is_epd(void);
/**
* Clear the framebuffer (fill with black).
* Call before rendering a new frame.
*/
void mc_display_clear(void);
/**
* Draw text at position.
*
* @param x X position in pixels
* @param y Y position in pixels
* @param text Null-terminated string
* @param invert If true, draw black text on white background
*/
void mc_display_text(int x, int y, const char *text, bool invert);
/**
* Draw a filled rectangle.
*
* @param x Top-left X
* @param y Top-left Y
* @param w Width
* @param h Height
*/
void mc_display_fill_rect(int x, int y, int w, int h);
/**
* Draw a horizontal line.
*/
void mc_display_hline(int x, int y, int w);
/**
* Draw a monochrome bitmap (Adafruit/Arduino format).
* MSB first, row-major, 1=foreground.
* Compatible with Arduino's drawBitmap() and MeshCore icons.h data.
*
* @param x Top-left X position
* @param y Top-left Y position
* @param data Bitmap data (MSB first, row-major)
* @param w Width in pixels
* @param h Height in pixels
*/
void mc_display_xbm(int x, int y, const uint8_t *data, int w, int h);
/**
* Flush the framebuffer to the display hardware.
* Call after all drawing operations for a frame are complete.
*/
void mc_display_finalize(void);
/**
* Reset the auto-off timer (called on user interaction).
*/
void mc_display_reset_auto_off(void);
/**
* Get the raw display device pointer.
* Used by easter egg (Doom) to bypass CFB and write directly.
* Returns NULL if display not initialized.
*/
const struct device *mc_display_get_device(void);
#ifdef __cplusplus
}
#endif
#endif /* ZEPHCORE_DISPLAY_H */
/*
* ZephCore - Display Abstraction (CFB)
* Copyright (c) 2025 ZephCore
* SPDX-License-Identifier: Apache-2.0
*
* Wraps Zephyr's Character Framebuffer (CFB) subsystem with:
* - Auto-detection from devicetree (any Zephyr-supported display)
* - Runtime resolution query (supports any size, not just 128x64)
* - Auto-off timer via k_work_delayable
* - Simple text/rect drawing API for UI pages
*
* All functions prefixed mc_display_ to avoid collision with
* Zephyr's display_* namespace in <zephyr/drivers/display.h>.
*/
#ifndef ZEPHCORE_DISPLAY_H
#define ZEPHCORE_DISPLAY_H
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Initialize the display from devicetree.
* Detects any Zephyr-supported display via:
* 1. "zephyr,display" chosen node (standard)
* 2. Legacy nodelabels: sh1106, ssd1306 (backwards compat)
*
* Queries actual resolution from driver — no hardcoded dimensions.
*
* @return 0 on success, negative errno on failure, -ENODEV if no display
*/
int mc_display_init(void);
/**
* Get display width in pixels (queried from hardware at init).
* Returns 0 if display not initialized.
*/
uint16_t mc_display_width(void);
/**
* Get display height in pixels (queried from hardware at init).
* Returns 0 if display not initialized.
*/
uint16_t mc_display_height(void);
/**
* Get active font width in pixels.
* Returns 0 if display not initialized.
*/
uint8_t mc_display_font_width(void);
/**
* Get active font height in pixels.
* Returns 0 if display not initialized.
*/
uint8_t mc_display_font_height(void);
/**
* Turn display on (wake from blanking).
* Resets the auto-off timer.
*/
void mc_display_on(void);
/**
* Turn display off (blanking).
*/
void mc_display_off(void);
/**
* @return true if the display is currently on
*/
bool mc_display_is_on(void);
/**
* @return true if the display is an e-paper (EPD) type.
* EPD displays have slow refresh (~2s) and use zero power when static,
* so callers should use longer update intervals and skip blanking.
*/
bool mc_display_is_epd(void);
/**
* Clear the framebuffer (fill with black).
* Call before rendering a new frame.
*/
void mc_display_clear(void);
/**
* Draw text at position.
*
* @param x X position in pixels
* @param y Y position in pixels
* @param text Null-terminated string
* @param invert If true, draw black text on white background
*/
void mc_display_text(int x, int y, const char *text, bool invert);
/**
* Draw a filled rectangle.
*
* @param x Top-left X
* @param y Top-left Y
* @param w Width
* @param h Height
*/
void mc_display_fill_rect(int x, int y, int w, int h);
/**
* Draw a horizontal line.
*/
void mc_display_hline(int x, int y, int w);
/**
* Draw a monochrome bitmap (Adafruit/Arduino format).
* MSB first, row-major, 1=foreground.
* Compatible with Arduino's drawBitmap() and MeshCore icons.h data.
*
* @param x Top-left X position
* @param y Top-left Y position
* @param data Bitmap data (MSB first, row-major)
* @param w Width in pixels
* @param h Height in pixels
*/
void mc_display_xbm(int x, int y, const uint8_t *data, int w, int h);
/**
* Flush the framebuffer to the display hardware.
* Call after all drawing operations for a frame are complete.
*/
void mc_display_finalize(void);
/**
* Reset the auto-off timer (called on user interaction).
*/
void mc_display_reset_auto_off(void);
/**
* EPD-only: force a full panel reset cycle before normal page rendering.
* No-op on non-EPD displays or when display is not initialized.
*/
void mc_display_epd_full_reset(void);
/**
* Get the raw display device pointer.
* Used by easter egg (Doom) to bypass CFB and write directly.
* Returns NULL if display not initialized.
*/
const struct device *mc_display_get_device(void);
#ifdef __cplusplus
}
#endif
#endif /* ZEPHCORE_DISPLAY_H */
+4
View File
@@ -245,6 +245,8 @@ static void splash_work_handler(struct k_work *work)
splash_active = false;
#ifdef CONFIG_ZEPHCORE_UI_DISPLAY
mc_display_epd_full_reset();
/* Transition from splash to home page (first active page for this role) */
#ifdef ZEPHCORE_REPEATER
ui_pages_set(UI_PAGE_STATUS);
@@ -741,6 +743,8 @@ static void ui_input_cb(struct input_event *evt, void *user_data)
k_work_cancel_delayable(&splash_work);
splash_active = false;
#ifdef CONFIG_ZEPHCORE_UI_DISPLAY
mc_display_epd_full_reset();
#ifdef ZEPHCORE_REPEATER
ui_pages_set(UI_PAGE_STATUS);
#else