Merge branch 'display'
This commit is contained in:
183
src/modules/display_module.c
Normal file
183
src/modules/display_module.c
Normal 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);
|
||||
Reference in New Issue
Block a user