feat(display): 添加LVGL显示支持和PWM背光控制
添加了完整的LVGL集成支持,包括: - 在app.overlay中配置显示设备树,添加背光别名和SPI3总线支持 - 集成PWM背光控制,通过pwm-leds子系统管理背光亮度 - 配置LVGL自动初始化和工作队列运行模式 - 实现显示模块的工作队列更新机制,包含UI创建和定时刷新 - 添加详细的LVGL移植说明文档,涵盖设备树配置、调试步骤和常见问题 - 调整分区配置以适应LVGL固件大小需求 - 启用MCUBoot bootloader支持OTA功能 该变更使得系统能够在ST7789V显示屏上正常运行LVGL界面,并通过PWM控制背光。
This commit is contained in:
@@ -1,12 +1,10 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#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>
|
||||
|
||||
@@ -16,14 +14,17 @@
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
||||
|
||||
#define DISPLAY_UPDATE_PERIOD_MS 10
|
||||
#define DISPLAY_UPDATE_PERIOD_MS 1000
|
||||
#define DISPLAY_BACKLIGHT_BRIGHTNESS 100
|
||||
|
||||
struct display_ctx {
|
||||
const struct device *dev;
|
||||
struct k_work_delayable refresh_work;
|
||||
lv_obj_t *hello_label;
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -31,10 +32,70 @@ static struct display_ctx disp = {
|
||||
.dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)),
|
||||
};
|
||||
|
||||
static void display_refresh_work_fn(struct k_work *work)
|
||||
static const struct led_dt_spec display_backlight =
|
||||
LED_DT_SPEC_GET(DT_NODELABEL(backlight));
|
||||
|
||||
static void display_schedule_update(k_timeout_t delay)
|
||||
{
|
||||
char count_str[12];
|
||||
uint32_t wait_ms;
|
||||
#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);
|
||||
|
||||
@@ -44,17 +105,21 @@ static void display_refresh_work_fn(struct k_work *work)
|
||||
|
||||
lvgl_lock();
|
||||
|
||||
if ((disp.tick_count % 100U) == 0U) {
|
||||
snprintk(count_str, sizeof(count_str), "%u", disp.tick_count / 100U);
|
||||
lv_label_set_text(disp.count_label, count_str);
|
||||
if (!disp.ui_ready) {
|
||||
display_create_ui_locked();
|
||||
}
|
||||
|
||||
wait_ms = lv_timer_handler();
|
||||
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();
|
||||
|
||||
disp.tick_count++;
|
||||
k_work_reschedule(&disp.refresh_work,
|
||||
K_MSEC(MAX(wait_ms, DISPLAY_UPDATE_PERIOD_MS)));
|
||||
display_schedule_update(K_MSEC(DISPLAY_UPDATE_PERIOD_MS));
|
||||
}
|
||||
|
||||
static int display_demo_init(void)
|
||||
@@ -66,22 +131,13 @@ static int display_demo_init(void)
|
||||
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;
|
||||
k_work_init_delayable(&disp.refresh_work, display_refresh_work_fn);
|
||||
|
||||
lvgl_lock();
|
||||
lv_obj_clean(lv_screen_active());
|
||||
|
||||
disp.hello_label = lv_label_create(lv_screen_active());
|
||||
lv_label_set_text(disp.hello_label, "LVGL demo running");
|
||||
lv_obj_align(disp.hello_label, LV_ALIGN_CENTER, 0, -12);
|
||||
|
||||
disp.count_label = lv_label_create(lv_screen_active());
|
||||
lv_label_set_text(disp.count_label, "0");
|
||||
lv_obj_align(disp.count_label, LV_ALIGN_BOTTOM_MID, 0, -12);
|
||||
|
||||
lv_timer_handler();
|
||||
lvgl_unlock();
|
||||
disp.ui_ready = false;
|
||||
|
||||
err = display_blanking_off(disp.dev);
|
||||
if (err) {
|
||||
@@ -89,10 +145,15 @@ static int display_demo_init(void)
|
||||
return err;
|
||||
}
|
||||
|
||||
disp.initialized = true;
|
||||
k_work_reschedule(&disp.refresh_work, K_MSEC(DISPLAY_UPDATE_PERIOD_MS));
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user