163 lines
5.8 KiB
C
163 lines
5.8 KiB
C
/*
|
||
* 模块名称:UI Display Port (LVGL 离屏显示端口)
|
||
* 模块功能:提供 LVGL 1bpp 离屏渲染端口与远程 keypad 输入设备
|
||
* 适用平台:STM32F407ZGTx
|
||
* 作者:王建锋
|
||
* 创建日期:2026-08-29
|
||
* 修改记录:
|
||
* 2026-08-29 王建锋 创建初始版本
|
||
*/
|
||
#include "ui_port.h"
|
||
|
||
#include <string.h>
|
||
#include "lvgl.h"
|
||
|
||
/* 160x160 1bpp 帧 = 3200 字节 */
|
||
static uint8_t g_ui_fbuf[UI_FB_SIZE];
|
||
|
||
/* LVGL 绘制缓冲(整屏渲染)。LVGL 9.3.0 对 I1 格式的整帧渲染,会在其自身算出的
|
||
* stride*h(=3200) 之后再越界多写 1 个字节(实测台架金丝雀命中 draw_buf[3200]=0xFF)。
|
||
* 若紧邻其后的 .bss(本例 ui_menu_view.o 的 s_frame_box)恰有指针,其最低字节被
|
||
* 打成 0xFF → 野指针 → HardFault(PC=lv_obj_get_scroll_y,2026-09-20 存储套件实证,
|
||
* SRAM/CCM 两种堆布局下同一签名、与 CCM 无关)。物理多留 UI_DRAW_BUF_GUARD 字节
|
||
* 吸收该越界写;逻辑尺寸仍按 UI_FB_SIZE 上报 LVGL(渲染数学 stride*h 不变)。
|
||
* Lib/LVGL 为第三方库,不改其源码,越界容纳放在本移植层。 */
|
||
#define UI_DRAW_BUF_GUARD 32
|
||
static uint8_t g_ui_draw_buf[UI_FB_SIZE + UI_DRAW_BUF_GUARD];
|
||
|
||
/* 单键缓冲:菜单无需多键队列,仅保存最近一次待处理键 */
|
||
static volatile uint8_t g_pending_key = 0;
|
||
|
||
/* 远端键 -> LVGL 键 */
|
||
/*
|
||
* 函数功能:将远端键码映射为 LVGL 按键枚举值
|
||
* 入口参数:remo_key - 远端键码 uint8_t UI_KEY_* 取值之一
|
||
* 返回值:LVGL 按键值 uint8_t LV_KEY_*;未匹配返回 0
|
||
* 限定条件:无
|
||
* 函数说明:仅映射方向/确认/返回等导航键,其余返回 0 表示忽略
|
||
*/
|
||
static uint8_t ui_map_key(uint8_t remo_key) {
|
||
switch (remo_key) {
|
||
case UI_KEY_UP: return (uint8_t)LV_KEY_UP;
|
||
case UI_KEY_DOWN: return (uint8_t)LV_KEY_DOWN;
|
||
case UI_KEY_LEFT: return (uint8_t)LV_KEY_LEFT;
|
||
case UI_KEY_RIGHT: return (uint8_t)LV_KEY_RIGHT;
|
||
case UI_KEY_ENTER: return (uint8_t)LV_KEY_ENTER;
|
||
case UI_KEY_ESC: return (uint8_t)LV_KEY_ESC;
|
||
default: return 0;
|
||
}
|
||
}
|
||
|
||
/* keypad 读取回调,上报待处理键 */
|
||
/*
|
||
* 函数功能:keypad 输入设备读取回调,上报待处理按键
|
||
* 入口参数:indev - 输入设备句柄 lv_indev_t * 未使用
|
||
* data - 读取结果 lv_indev_data_t * 由本函数填充
|
||
* 返回值:无
|
||
* 限定条件:无
|
||
* 函数说明:1. 若有缓存键则上报 PRESSED 并清除缓存
|
||
* 2. 否则上报 RELEASED,等待下次按键
|
||
*/
|
||
static void ui_keypad_read(lv_indev_t * indev, lv_indev_data_t * data) {
|
||
(void)indev;
|
||
if (g_pending_key != 0) {
|
||
data->state = LV_INDEV_STATE_PRESSED;
|
||
data->key = g_pending_key;
|
||
g_pending_key = 0;
|
||
} else {
|
||
data->state = LV_INDEV_STATE_RELEASED;
|
||
data->key = 0;
|
||
}
|
||
}
|
||
|
||
/* flush 回调:把 LVGL 1bpp 渲染结果拷到内存帧缓冲 */
|
||
/*
|
||
* 函数功能:显示刷新回调,将 LVGL 渲染结果写入内存帧缓冲
|
||
* 入口参数:disp - 显示设备 lv_display_t * 未使用
|
||
* area - 刷新区域 const lv_area_t * 未使用
|
||
* color_p - 1bpp 像素数据首地址 uint8_t *
|
||
* 返回值:无
|
||
* 限定条件:ui_port_init() 已成功调用
|
||
* 函数说明:整帧拷贝到 g_ui_fbuf,供 RemoDispBus 推送至远端
|
||
*/
|
||
static void ui_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * color_p) {
|
||
(void)disp;
|
||
(void)area;
|
||
memcpy(g_ui_fbuf, color_p, UI_FB_SIZE);
|
||
lv_display_flush_ready(disp);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:初始化 LVGL 显示端口与按键输入设备
|
||
* 入口参数:无
|
||
* 返回值:0 - 成功,其他 - 失败(显示或输入设备创建失败)
|
||
* 限定条件:无
|
||
* 函数说明:1. 初始化 LVGL 并创建 1bpp 离屏显示设备与绘制缓冲
|
||
* 2. 创建 keypad 设备及默认输入组,绑定刷新回调
|
||
* 3. 后续须周期调用 ui_port_step 驱动刷新
|
||
*/
|
||
int ui_port_init(void) {
|
||
lv_init();
|
||
|
||
static lv_display_t *disp = NULL;
|
||
disp = lv_display_create(UI_SCREEN_W, UI_SCREEN_H);
|
||
if (disp == NULL) {
|
||
return -1;
|
||
}
|
||
lv_display_set_color_format(disp, LV_COLOR_FORMAT_I1);
|
||
lv_display_set_buffers(disp, g_ui_draw_buf, NULL, UI_FB_SIZE,
|
||
LV_DISPLAY_RENDER_MODE_FULL);
|
||
lv_display_set_flush_cb(disp, ui_flush_cb);
|
||
|
||
static lv_indev_t *indev = NULL;
|
||
indev = lv_indev_create();
|
||
lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD);
|
||
lv_indev_set_read_cb(indev, ui_keypad_read);
|
||
|
||
lv_group_t *g = lv_group_create();
|
||
if (g == NULL) {
|
||
return -1;
|
||
}
|
||
lv_group_set_default(g);
|
||
lv_indev_set_group(indev, g);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:获取内存帧缓冲首地址
|
||
* 入口参数:无
|
||
* 返回值:帧缓冲首地址 uint8_t * 固定长度 UI_FB_SIZE 字节
|
||
* 限定条件:ui_port_init() 已成功调用
|
||
* 函数说明:缓冲由 flush 回调更新,供网络推流读取
|
||
*/
|
||
uint8_t *ui_port_get_framebuffer(void) {
|
||
return g_ui_fbuf;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:向输入队列喂入一个远端按键值
|
||
* 入口参数:remo_key - 远端键码 uint8_t UI_KEY_* 取值之一
|
||
* 返回值:无
|
||
* 限定条件:无
|
||
* 函数说明:映射后缓存,由 keypad 回调在下次轮询上报
|
||
*/
|
||
void ui_port_queue_key(uint8_t remo_key) {
|
||
uint8_t k = ui_map_key(remo_key);
|
||
if (k != 0) {
|
||
g_pending_key = k;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:UI 周期步进(推进节拍并刷新 LVGL)
|
||
* 入口参数:tick_ms - 距上次调用毫秒数 uint32_t 建议 10
|
||
* 返回值:无
|
||
* 限定条件:ui_port_init() 已成功调用
|
||
* 函数说明:依次调用 lv_tick_inc 与 lv_timer_handler,须由任务周期调用
|
||
*/
|
||
void ui_port_step(uint32_t tick_ms) {
|
||
lv_tick_inc(tick_ms);
|
||
lv_timer_handler();
|
||
}
|