《lvgl基础学习 —— 消息机制》
1.为什么还要“消息机制”?直接调 Service 不行吗?
如果直接用service代码可能是这样:
static void slider_cb(lv_event_t *e) { int val = lv_slider_get_value(slider); service_set_brightness(val); // 直接调用 }
在 Demo 阶段 OK,但在真实产品会出问题:
-
Service 里可能:
-
访问硬件(慢)
-
等待锁
-
等待 I/O
-
-
UI 回调 被阻塞
-
表现为:
-
UI 卡顿
-
滑动不顺
-
点击无响应
-
UI 回调必须“非常快”
2.消息机制能解决什么问题?
一句话:
UI 只“发消息”,
Service 在自己的上下文里“慢慢处理”。
对比以下两种模型:❌ 同步调用模型
UI 回调
└── service_set_xxx()
└── driver_xxx()
└── 阻塞
✅ 消息驱动模型(推荐)
UI 回调 └── post_message(MSG_SET_XXX) ↓ Service 线程 / 轮询 ↓ driver_xxx()
这样UI就肯定不会卡顿。
3.最小可行的”消息模型“
我们需要 3 个东西:
-
消息类型
-
消息队列
-
消息处理循环
1.定义消息类型(enum)
typedef enum { MSG_SET_BRIGHTNESS, MSG_WIFI_ENABLE, MSG_MOTOR_MOVE, } msg_type_t;
2.定义消息结构体
typedef struct { msg_type_t type; int value; } app_msg_t;
3.UI ---> Service:只发消息
app_post_msg(MSG_SET_BRIGHTNESS, val);
4.service集中处理消息
switch(msg.type) { case MSG_SET_BRIGHTNESS: driver_set_brightness(msg.value); break; }
4.两种常见的实现方式
方案 A(强烈推荐新手)
👉 环形队列 + 轮询(无多线程)
-
简单
-
稳定
-
适合你现在的 PC / 嵌入式 Linux 学习阶段
方案 B(进阶)
👉 线程 + condition / message queue
-
更复杂
-
更强
-
后期再学
👉 我们现在先用方案 A
5.完整”消息队列“最小demo
场景
📌 Slider 改亮度
📌 UI 不直接调 Service
📌 Service 通过消息慢慢处理
1.消息定义(app_msg.h)
#ifndef APP_MSG_H #define APP_MSG_H typedef enum { MSG_SET_BRIGHTNESS, } msg_type_t; typedef struct { msg_type_t type; int value; } app_msg_t; void app_msg_init(void); int app_msg_post(app_msg_t *msg); int app_msg_fetch(app_msg_t *msg); #endif
2.消息队列实现(app_msg.c)
#include "app_msg.h" #define MSG_QUEUE_SIZE 16 static app_msg_t msg_queue[MSG_QUEUE_SIZE]; static int head = 0; static int tail = 0; void app_msg_init(void) { head = tail = 0; } int app_msg_post(app_msg_t *msg) { int next = (tail + 1) % MSG_QUEUE_SIZE; if(next == head) { /* queue full */ return -1; } msg_queue[tail] = *msg; tail = next; return 0; } int app_msg_fetch(app_msg_t *msg) { if(head == tail) { /* queue empty */ return -1; } *msg = msg_queue[head]; head = (head + 1) % MSG_QUEUE_SIZE; return 0; }
3.Service层(service_task.c)
#include "app_msg.h" #include "driver_brightness.h" void service_task_run(void) { app_msg_t msg; while(app_msg_fetch(&msg) == 0) { switch(msg.type) { case MSG_SET_BRIGHTNESS: driver_set_brightness(msg.value); break; default: break; } } }
4.driver层(driver.c)
#include <stdio.h> int driver_set_brightness(int val) { printf("[DRIVER] brightness = %d\n", val); return 0; }
5.UI层(ui.c)
#include "lvgl.h" #include "app_msg.h" static lv_obj_t *label; static void slider_cb(lv_event_t *e) { lv_obj_t *slider = lv_event_get_target(e); int val = lv_slider_get_value(slider); app_msg_t msg = { .type = MSG_SET_BRIGHTNESS, .value = val, }; app_msg_post(&msg); lv_label_set_text_fmt(label, "Brightness: %d", val); } void ui_brightness_init(void) { label = lv_label_create(lv_scr_act()); lv_label_set_text(label, "Brightness: 50"); lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 20); lv_obj_t *slider = lv_slider_create(lv_scr_act()); lv_obj_set_width(slider, 260); lv_obj_center(slider); lv_slider_set_range(slider, 0, 100); lv_slider_set_value(slider, 50, LV_ANIM_OFF); lv_obj_add_event_cb(slider, slider_cb, LV_EVENT_VALUE_CHANGED, NULL); }
main.c
int main(void) { lv_init(); hal_init(); app_msg_init(); ui_brightness_init(); while(1) { lv_timer_handler(); service_task_run(); // 👈 关键 usleep(5000); } }
在实际工程项目中,只要把service_task_run放在一个线程中就好了。
常见坑(一定要避)
❌ UI 回调里 sleep
❌ 消息结构体用指针(生命周期错)
❌ 队列不判满
❌ Service 里调用 LVGL API
❌ 多线程同时操作 LVGL
浙公网安备 33010602011771号