Merge branch 'display'

This commit is contained in:
2026-03-27 09:40:19 +08:00
6 changed files with 1030 additions and 9 deletions

View File

@@ -0,0 +1,183 @@
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/display.h>
#include <zephyr/drivers/led.h>
#include <zephyr/kernel.h>
#include <app_event_manager.h>
#include <lvgl.h>
#include <lvgl_zephyr.h>
#define MODULE display
#include <caf/events/module_state_event.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
#define DISPLAY_UPDATE_PERIOD_MS 1000
#define DISPLAY_BACKLIGHT_BRIGHTNESS 100
struct display_ctx {
const struct device *dev;
struct display_capabilities caps;
struct k_work_delayable update_work;
lv_obj_t *title_label;
lv_obj_t *count_label;
uint32_t tick_count;
bool ui_ready;
bool initialized;
};
static struct display_ctx disp = {
.dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)),
};
static const struct led_dt_spec display_backlight =
LED_DT_SPEC_GET(DT_NODELABEL(backlight));
static void display_schedule_update(k_timeout_t delay)
{
#ifdef CONFIG_LV_Z_RUN_LVGL_ON_WORKQUEUE
k_work_schedule_for_queue(lvgl_get_workqueue(), &disp.update_work, delay);
#else
k_work_reschedule(&disp.update_work, delay);
#endif
}
static int display_backlight_init(void)
{
int err;
if (!led_is_ready_dt(&display_backlight)) {
LOG_WRN("Display backlight device not ready");
return 0;
}
/*
* 背光亮度交给 pwm-leds 驱动管理,这样后面如果要做调光、呼吸灯或亮度档位,
* 都可以直接沿用 Zephyr 的 LED/PWM 接口,而不需要再单独碰 PWM 寄存器。
*/
err = led_set_brightness_dt(&display_backlight, DISPLAY_BACKLIGHT_BRIGHTNESS);
if (err) {
LOG_ERR("Failed to set backlight brightness: %d", err);
return err;
}
return 0;
}
static void display_create_ui_locked(void)
{
lv_obj_t *screen = lv_screen_active();
/*
* 先显式设置背景和文字颜色,避免把“有画面但颜色刚好看不见”误判为
* “LVGL 没有刷新”。这里使用高对比度配色,便于快速验证渲染链路。
*/
lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_set_style_bg_color(screen, lv_color_hex(0x102A43), LV_PART_MAIN);
lv_obj_set_style_text_color(screen, lv_color_hex(0xF0F4F8), LV_PART_MAIN);
lv_obj_clean(screen);
disp.title_label = lv_label_create(screen);
lv_label_set_text(disp.title_label, "Zephyr LVGL running");
lv_obj_set_style_text_color(disp.title_label, lv_color_hex(0xF0F4F8), LV_PART_MAIN);
lv_obj_align(disp.title_label, LV_ALIGN_CENTER, 0, -16);
disp.count_label = lv_label_create(screen);
lv_label_set_text(disp.count_label, "tick 0");
lv_obj_set_style_text_color(disp.count_label, lv_color_hex(0xFFD166), LV_PART_MAIN);
lv_obj_align(disp.count_label, LV_ALIGN_CENTER, 0, 16);
disp.ui_ready = true;
}
static void display_update_work_fn(struct k_work *work)
{
char count_str[24];
lv_color_t bg_color;
ARG_UNUSED(work);
if (!disp.initialized) {
return;
}
lvgl_lock();
if (!disp.ui_ready) {
display_create_ui_locked();
}
bg_color = ((disp.tick_count & 0x01u) == 0U) ? lv_color_hex(0x102A43) :
lv_color_hex(0x1F6F8B);
lv_obj_set_style_bg_color(lv_screen_active(), bg_color, LV_PART_MAIN);
snprintk(count_str, sizeof(count_str), "tick %u", disp.tick_count++);
lv_label_set_text(disp.count_label, count_str);
lv_obj_invalidate(lv_screen_active());
lvgl_unlock();
display_schedule_update(K_MSEC(DISPLAY_UPDATE_PERIOD_MS));
}
static int display_demo_init(void)
{
int err;
if (!device_is_ready(disp.dev)) {
LOG_ERR("Display device not ready");
return -ENODEV;
}
display_get_capabilities(disp.dev, &disp.caps);
LOG_INF("Display caps: %ux%u fmt=%d", disp.caps.x_resolution, disp.caps.y_resolution,
disp.caps.current_pixel_format);
k_work_init_delayable(&disp.update_work, display_update_work_fn);
disp.tick_count = 0U;
disp.ui_ready = false;
err = display_blanking_off(disp.dev);
if (err) {
LOG_ERR("Display blanking off failed: %d", err);
return err;
}
err = display_backlight_init();
if (err) {
return err;
}
disp.initialized = true;
display_schedule_update(K_NO_WAIT);
LOG_INF("LVGL display demo initialized");
return 0;
}
static bool app_event_handler(const struct app_event_header *aeh)
{
if (is_module_state_event(aeh)) {
const struct module_state_event *event = cast_module_state_event(aeh);
if (check_state(event, MODULE_ID(main), MODULE_STATE_READY)) {
int err = display_demo_init();
if (err) {
module_set_state(MODULE_STATE_ERROR);
} else {
module_set_state(MODULE_STATE_READY);
}
}
return false;
}
__ASSERT_NO_MSG(false);
return false;
}
APP_EVENT_LISTENER(MODULE, app_event_handler);
APP_EVENT_SUBSCRIBE(MODULE, module_state_event);