Files
STM32F4-Base/Drivers/BSP/UI/ui_port.c
T

163 lines
5.8 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称: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();
}