LVGL 库提交
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 模块名称:UI Menu (菜单页面构建)
|
||||
* 模块功能:基于 lv_menu 复现 DTU-HMI 树形菜单,支持方向键导航与返回
|
||||
* 适用平台:STM32F407ZGTx
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-08-29
|
||||
* 修改记录:
|
||||
* 2026-08-29 王建锋 创建初始版本
|
||||
*/
|
||||
#include "ui_menu.h"
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
/* 菜单节点表(简化自 DTU-HMI menuTab):层级 + 名称 */
|
||||
typedef struct {
|
||||
uint8_t level; /* 0=根页面, 1..3=子层次 */
|
||||
const char *name;
|
||||
} ui_menu_node_t;
|
||||
|
||||
static const ui_menu_node_t g_menu_tab[] = {
|
||||
{0, "主界面"},
|
||||
{1, "装置信息"},
|
||||
{1, "实时数据"},
|
||||
{2, "交流量"},
|
||||
{2, "直流量"},
|
||||
{2, "开入量"},
|
||||
{1, "定值参数"},
|
||||
{2, "定值"},
|
||||
{3, "定值"},
|
||||
{3, "遥调"},
|
||||
{3, "零压"},
|
||||
{3, "电压"},
|
||||
{2, "查看"},
|
||||
{3, "定值"},
|
||||
{3, "遥调"},
|
||||
{3, "零压"},
|
||||
{3, "电压"},
|
||||
{1, "遥测量"},
|
||||
{2, "测量值"},
|
||||
{2, "遥调系数"},
|
||||
{2, "遥测系数"},
|
||||
{2, "双字节"},
|
||||
{2, "装置遥调"},
|
||||
{1, "装置维护"},
|
||||
{2, "时间设置"},
|
||||
{2, "强制复归"},
|
||||
{2, "手动录波"},
|
||||
{2, "自动录波"},
|
||||
{1, "通讯设置"},
|
||||
{2, "通讯参数"},
|
||||
{2, "网络配置"},
|
||||
{2, "SNTP设置"},
|
||||
{1, "记录查询"},
|
||||
{2, "SOE记录"},
|
||||
{2, "事故记录"},
|
||||
{2, "操作记录"},
|
||||
{2, "告警记录"},
|
||||
{2, "变位告警"},
|
||||
{2, "遥信记录"},
|
||||
{2, "自检记录"},
|
||||
{2, "定值记录"},
|
||||
{2, "录波记录"},
|
||||
{0, "厂家设置"},
|
||||
{1, "元件设置"},
|
||||
{1, "恢复默认"},
|
||||
{2, "全部恢复"},
|
||||
{2, "默认参数"},
|
||||
{2, "默认定值"},
|
||||
{2, "默认系数"},
|
||||
{2, "电压"},
|
||||
{2, "元件参数"},
|
||||
{1, "版本显示"},
|
||||
{1, "装置遥调"},
|
||||
{2, "测量遥调"},
|
||||
{2, "装置遥调"},
|
||||
{2, "直流遥调"},
|
||||
{2, "开出遥调"},
|
||||
{2, "装置记录"},
|
||||
{2, "告警记录"},
|
||||
{2, "遥调记录"},
|
||||
{2, "信号复归"},
|
||||
{1, "版本信息"},
|
||||
};
|
||||
|
||||
/*
|
||||
* 函数功能:构建菜单(lv_menu 页面树),复现参考项目菜单结构
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功,其他 - 失败(对象创建失败)
|
||||
* 限定条件:ui_port_init() 已成功调用,活动屏幕存在
|
||||
* 函数说明:1. 依据紧凑节点表按层级生成 lv_menu 页面与返回栈
|
||||
* 2. 将菜单对象加入默认输入组,支持远端按键导航
|
||||
* 3. 叶子节点挂占位页,等待后续逐项填充具体功能
|
||||
*/
|
||||
int ui_menu_init(void)
|
||||
{
|
||||
lv_obj_t *menu = lv_menu_create(lv_screen_active());
|
||||
if (menu == NULL) {
|
||||
return -1;
|
||||
}
|
||||
lv_obj_set_size(menu, 160, 160);
|
||||
lv_menu_set_mode_root_back_button(menu, LV_MENU_ROOT_BACK_BUTTON_ENABLED);
|
||||
lv_group_add_obj(lv_group_get_default(), menu);
|
||||
|
||||
uint32_t n = sizeof(g_menu_tab) / sizeof(g_menu_tab[0]);
|
||||
lv_obj_t *root_page = NULL;
|
||||
lv_obj_t *parent_stack[4] = {NULL, NULL, NULL, NULL};
|
||||
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
uint8_t L = g_menu_tab[i].level;
|
||||
const char *name = g_menu_tab[i].name;
|
||||
|
||||
if (i == 0) {
|
||||
/* 第 0 节点为根(主界面) */
|
||||
root_page = lv_menu_page_create(menu, name);
|
||||
parent_stack[0] = root_page;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 当前节点页面 */
|
||||
lv_obj_t *page = lv_menu_page_create(menu, name);
|
||||
|
||||
/* 叶子:页内挂占位内容 */
|
||||
int leaf = (i + 1 >= n) || (g_menu_tab[i + 1].level <= L);
|
||||
if (leaf) {
|
||||
lv_obj_t *ph = lv_menu_cont_create(page);
|
||||
lv_obj_t *pl = lv_label_create(ph);
|
||||
lv_label_set_text(pl, "功能未实现(占位)");
|
||||
}
|
||||
|
||||
/* 挂到父页:level0 直挂根,其余挂到上一级页面 */
|
||||
lv_obj_t *parent = (L == 0) ? root_page : parent_stack[L - 1];
|
||||
|
||||
/* 在父页创建一个容器项,点击加载当前页 */
|
||||
lv_obj_t *item = lv_menu_cont_create(parent);
|
||||
lv_obj_t *lbl = lv_label_create(item);
|
||||
lv_label_set_text(lbl, name);
|
||||
lv_menu_set_load_page_event(menu, item, page);
|
||||
|
||||
parent_stack[L] = page;
|
||||
}
|
||||
|
||||
lv_menu_set_page(menu, root_page);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 模块名称:UI Menu (菜单页面构建)
|
||||
* 模块功能:基于 lv_menu 复现 DTU-HMI 树形菜单,支持方向键导航与返回
|
||||
* 适用平台:STM32F407ZGTx
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-08-29
|
||||
* 修改记录:
|
||||
* 2026-08-29 王建锋 创建初始版本
|
||||
*/
|
||||
#ifndef __UI_MENU_H
|
||||
#define __UI_MENU_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 函数功能:构建菜单(lv_menu 页面树),复现参考项目菜单结构
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功,其他 - 失败(对象创建失败)
|
||||
* 限定条件:ui_port_init() 已成功调用,活动屏幕存在
|
||||
* 函数说明:1. 依据紧凑节点表按层级生成 lv_menu 页面与返回栈
|
||||
* 2. 将菜单对象加入默认输入组,支持远端按键导航
|
||||
* 3. 叶子节点挂占位页,等待后续逐项填充具体功能
|
||||
*/
|
||||
int ui_menu_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __UI_MENU_H */
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 模块名称:UI Network Sink (RemoDispBus 服务端)
|
||||
* 模块功能:实现 RemoDispBus 协议服务端,接收远端连接与导航按键,回送 1bpp 帧缓冲
|
||||
* 适用平台:STM32F407ZGTx + CH395F 以太网
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-08-29
|
||||
* 修改记录:
|
||||
* 2026-08-29 王建锋 创建初始版本
|
||||
* 2026-08-29 王建锋 修正监听端口为 7003;移除连接后主动上报 INIT(避免污染客户端首帧读取)
|
||||
*/
|
||||
#include "ui_net_sink.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "ui_port.h"
|
||||
#include "net_socket.h"
|
||||
#include "net_types.h"
|
||||
|
||||
/* ---------------- RemoDispBus 协议 ---------------- */
|
||||
#define TAG_CLIENT 0xAA
|
||||
#define TAG_DEVICE 0xBB
|
||||
#define CMD_KEEPLIVE 0
|
||||
#define CMD_INIT 1
|
||||
#define CMD_KEY 2
|
||||
#define CMD_LCDMEM 3
|
||||
|
||||
#define UI_W 160
|
||||
#define UI_H 160
|
||||
#define UI_MEMSIZE (UI_W * UI_H / 8) /* 3200 字节 */
|
||||
|
||||
/* 发送帧缓冲(最大:LCDMEM 应答 = 4 字节偏移 + 3200 帧 + 头4 + 校验1) */
|
||||
static uint8_t g_tx[4 + 4 + UI_MEMSIZE + 1];
|
||||
/* LCDMEM 应答数据区(4 字节大端偏移 + 整帧) */
|
||||
static uint8_t g_lcdmem_data[4 + UI_MEMSIZE];
|
||||
static uint8_t g_rx[64];
|
||||
|
||||
static int g_listen = -1;
|
||||
static int g_client = -1;
|
||||
|
||||
/*
|
||||
* 函数功能:计算 RemoDispBus 数据段异或校验值
|
||||
* 入口参数:d - 数据首地址 const uint8_t * 非空
|
||||
* len - 数据长度 int >= 0
|
||||
* 返回值:校验值 uint8_t 0 - 255
|
||||
* 限定条件:无
|
||||
* 函数说明:逐字节异或,结果为单字节,与 DTU-HMI 协议保持一致
|
||||
*/
|
||||
static uint8_t remo_crc(const uint8_t *d, int len)
|
||||
{
|
||||
uint8_t c = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
c ^= d[i];
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:以大端序写入 16 位无符号整数
|
||||
* 入口参数:p - 目标缓冲区 uint8_t * 至少 2 字节
|
||||
* v - 待写入数值 uint16_t 0 - 65535
|
||||
* 返回值:无
|
||||
* 限定条件:p 指向有效内存
|
||||
* 函数说明:高位在前、低位在后(网络字节序)
|
||||
*/
|
||||
static void put_u16(uint8_t *p, uint16_t v)
|
||||
{
|
||||
p[0] = (uint8_t)(v >> 8);
|
||||
p[1] = (uint8_t)(v & 0xFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:以大端序写入 32 位无符号整数
|
||||
* 入口参数:p - 目标缓冲区 uint8_t * 至少 4 字节
|
||||
* v - 待写入数值 uint32_t 0 - 0xFFFFFFFF
|
||||
* 返回值:无
|
||||
* 限定条件:p 指向有效内存
|
||||
* 函数说明:最高位在前,依次存放 4 字节(网络字节序)
|
||||
*/
|
||||
static void put_u32(uint8_t *p, uint32_t v)
|
||||
{
|
||||
p[0] = (uint8_t)(v >> 24);
|
||||
p[1] = (uint8_t)(v >> 16);
|
||||
p[2] = (uint8_t)(v >> 8);
|
||||
p[3] = (uint8_t)(v & 0xFF);
|
||||
}
|
||||
|
||||
/* 组包并发送一帧(tag = DEVICE) */
|
||||
/*
|
||||
* 函数功能:组包并发送一帧(设备 -> 客户端)
|
||||
* 入口参数:cmd - 命令字 uint8_t CMD_*
|
||||
* data - 数据段首地址 const uint8_t * 可为 NULL
|
||||
* len - 数据段长度 uint16_t 0 - 65535
|
||||
* 返回值:无
|
||||
* 限定条件:g_client >= 0(已有连接)
|
||||
* 函数说明:1. 帧格式 [TAG_DEVICE][cmd][len_hi][len_lo][data][crc]
|
||||
* 2. 无连接时直接返回,不发送
|
||||
* 3. 阻塞发送,直至整帧写入 Socket
|
||||
*/
|
||||
static void remo_send(uint8_t cmd, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
if (g_client < 0) {
|
||||
return;
|
||||
}
|
||||
uint8_t *p = g_tx;
|
||||
p[0] = TAG_DEVICE;
|
||||
p[1] = cmd;
|
||||
p[2] = (uint8_t)(len >> 8);
|
||||
p[3] = (uint8_t)(len & 0xFF);
|
||||
if (len && data) {
|
||||
memcpy(p + 4, data, len);
|
||||
}
|
||||
p[4 + len] = remo_crc(data, len);
|
||||
net_send(g_client, p, 4 + len + 1, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:发送 INIT 应答(上报分辨率与帧大小)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:g_client >= 0
|
||||
* 函数说明:数据段为 [宽][高][帧字节数(4)],供客户端分配显示缓冲
|
||||
*/
|
||||
static void remo_send_init(void)
|
||||
{
|
||||
uint8_t init[8];
|
||||
put_u16(init, UI_W);
|
||||
put_u16(init + 2, UI_H);
|
||||
put_u32(init + 4, UI_MEMSIZE);
|
||||
remo_send(CMD_INIT, init, 6);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:解析一帧 RemoDispBus 请求并分发处理
|
||||
* 入口参数:f - 整帧数据首地址 const uint8_t * 非空
|
||||
* n - 帧字节数 int >= 0
|
||||
* 返回值:无
|
||||
* 限定条件:f 指向完整帧缓冲
|
||||
* 函数说明:1. 校验长度与 CRC,标签必须为客户端 TAG
|
||||
* 2. 按命令字分发:KEEPLIVE/INIT 应答,KEY 驱动菜单,LCDMEM 回送帧
|
||||
* 3. 校验失败或格式错误时静默丢弃
|
||||
*/
|
||||
static void remo_handle(const uint8_t *f, int n)
|
||||
{
|
||||
if (n < 6) {
|
||||
return;
|
||||
}
|
||||
uint8_t tag = f[0];
|
||||
uint8_t cmd = f[1];
|
||||
uint16_t len = ((uint16_t)f[2] << 8) | f[3];
|
||||
const uint8_t *data = f + 4;
|
||||
if ((int)(4 + len + 1) > n) {
|
||||
return; /* 帧不完整 */
|
||||
}
|
||||
uint8_t crc = f[4 + len];
|
||||
if (crc != remo_crc(data, len)) {
|
||||
return; /* 校验失败,丢弃 */
|
||||
}
|
||||
if (tag != TAG_CLIENT) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case CMD_KEEPLIVE:
|
||||
remo_send(CMD_KEEPLIVE, NULL, 0);
|
||||
break;
|
||||
case CMD_INIT:
|
||||
remo_send_init();
|
||||
break;
|
||||
case CMD_KEY:
|
||||
if (len >= 1) {
|
||||
ui_port_queue_key(data[0]);
|
||||
}
|
||||
remo_send(CMD_KEY, NULL, 0);
|
||||
break;
|
||||
case CMD_LCDMEM: {
|
||||
if (len < 4) {
|
||||
break;
|
||||
}
|
||||
uint32_t off = ((uint32_t)data[0] << 24) |
|
||||
((uint32_t)data[1] << 16) |
|
||||
((uint32_t)data[2] << 8) |
|
||||
(uint32_t)data[3];
|
||||
if (off == 0) {
|
||||
uint8_t *fb = ui_port_get_framebuffer();
|
||||
g_lcdmem_data[0] = data[0];
|
||||
g_lcdmem_data[1] = data[1];
|
||||
g_lcdmem_data[2] = data[2];
|
||||
g_lcdmem_data[3] = data[3];
|
||||
memcpy(g_lcdmem_data + 4, fb, UI_MEMSIZE);
|
||||
remo_send(CMD_LCDMEM, g_lcdmem_data, 4 + UI_MEMSIZE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:启动 UI 专用 TCP 监听 Socket
|
||||
* 入口参数:port - 监听端口号 uint16_t 1 - 65535
|
||||
* 返回值:0 - 成功,其他 - 失败(socket 创建/绑定/监听失败)
|
||||
* 限定条件:net_init() 已完成,网络子系统就绪
|
||||
* 函数说明:创建流式 Socket 并绑定端口,进入监听等待连接
|
||||
*/
|
||||
int ui_net_sink_start(uint16_t port)
|
||||
{
|
||||
g_listen = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||||
if (g_listen < 0) {
|
||||
return -1;
|
||||
}
|
||||
struct net_sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = NET_AF_INET;
|
||||
addr.sin_port = net_htons(port);
|
||||
addr.sin_addr.s_addr = NET_INADDR_ANY;
|
||||
|
||||
if (net_bind(g_listen, (struct net_sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
net_close(g_listen);
|
||||
g_listen = -1;
|
||||
return -1;
|
||||
}
|
||||
if (net_listen(g_listen) < 0) {
|
||||
net_close(g_listen);
|
||||
g_listen = -1;
|
||||
return -1;
|
||||
}
|
||||
g_client = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:非阻塞轮询 RemoDispBus 协议
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:ui_net_sink_start() 已成功调用
|
||||
* 函数说明:1. 无客户端时尝试接受连接(不主动上报 INIT,由客户端按需请求)
|
||||
* 2. 有客户端时非阻塞收帧并解析
|
||||
* 3. 连接关闭或异常时复位,等待重监听,不阻塞界面刷新
|
||||
*/
|
||||
void ui_net_sink_poll(void)
|
||||
{
|
||||
if (g_listen < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_client < 0) {
|
||||
int c = net_accept(g_listen, NULL, NULL);
|
||||
if (c >= 0) {
|
||||
g_client = c;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int n = net_recv(g_client, g_rx, sizeof(g_rx), NET_MSG_DONTWAIT);
|
||||
if (n > 0) {
|
||||
remo_handle(g_rx, n);
|
||||
} else if (n == 0) {
|
||||
g_client = -1; /* 对端关闭:等待下次 accept 自动重监听,不释放控制块 */
|
||||
} else {
|
||||
if (net_get_errno() != NET_ERR_WOULDBLOCK) {
|
||||
g_client = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 模块名称:UI Network Sink (RemoDispBus 服务端)
|
||||
* 模块功能:实现 RemoDispBus 协议服务端,接收远端连接与导航按键,回送 1bpp 帧缓冲
|
||||
* 适用平台:STM32F407ZGTx + CH395F 以太网
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-08-29
|
||||
* 修改记录:
|
||||
* 2026-08-29 王建锋 创建初始版本
|
||||
* 2026-08-29 王建锋 修正监听端口为 7003,与 DTU-RemoteLCD 客户端默认端口对齐
|
||||
*/
|
||||
#ifndef __UI_NET_SINK_H
|
||||
#define __UI_NET_SINK_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* RemoDispBus 标签与命令 */
|
||||
#define UI_REMO_TAG_CLIENT 0xAA
|
||||
#define UI_REMO_TAG_DEVICE 0xBB
|
||||
#define UI_REMO_CMD_KEEP 0
|
||||
#define UI_REMO_CMD_INIT 1
|
||||
#define UI_REMO_CMD_KEY 2
|
||||
#define UI_REMO_CMD_LCDMEM 3
|
||||
|
||||
/* UI 推流监听端口(须与 DTU-RemoteLCD 客户端默认端口 7003 一致) */
|
||||
#define UI_NET_SINK_PORT 7003
|
||||
|
||||
/*
|
||||
* 函数功能:启动 UI 专用 TCP 监听 Socket
|
||||
* 入口参数:port - 监听端口号 uint16_t 1 - 65535
|
||||
* 返回值:0 - 成功,其他 - 失败(socket 创建/绑定/监听失败)
|
||||
* 限定条件:net_init() 已完成,网络子系统就绪
|
||||
* 函数说明:1. 创建流式 Socket 并绑定到指定端口与任意本地地址
|
||||
* 2. 进入监听状态,等待远端客户端连接
|
||||
* 3. 调用前需确保 CH395F 网络已初始化
|
||||
*/
|
||||
int ui_net_sink_start(uint16_t port);
|
||||
|
||||
/*
|
||||
* 函数功能:非阻塞轮询 RemoDispBus 协议
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:ui_net_sink_start() 已成功调用
|
||||
* 函数说明:1. 无客户端时尝试接受连接,成功即上报 INIT
|
||||
* 2. 有客户端时用 DONTWAIT 收帧并解析(KEEPLIVE/INIT/KEY/LCDMEM)
|
||||
* 3. 对端关闭或异常时复位客户端,等待下次重监听,不阻塞 LVGL 刷新
|
||||
*/
|
||||
void ui_net_sink_poll(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __UI_NET_SINK_H */
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 模块名称: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 绘制缓冲(与帧同尺寸,整屏渲染) */
|
||||
static uint8_t g_ui_draw_buf[UI_FB_SIZE];
|
||||
|
||||
/* 单键缓冲:菜单无需多键队列,仅保存最近一次待处理键 */
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 模块名称:UI Display Port (LVGL 离屏显示端口)
|
||||
* 模块功能:提供 LVGL 1bpp 离屏渲染端口与远程 keypad 输入设备
|
||||
* 适用平台:STM32F407ZGTx
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-08-29
|
||||
* 修改记录:
|
||||
* 2026-08-29 王建锋 创建初始版本
|
||||
*/
|
||||
#ifndef __UI_PORT_H
|
||||
#define __UI_PORT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 屏幕尺寸与帧缓冲(160x160 1bpp = 3200 字节) */
|
||||
#define UI_SCREEN_W 160
|
||||
#define UI_SCREEN_H 160
|
||||
#define UI_FB_SIZE ((UI_SCREEN_W * UI_SCREEN_H) / 8)
|
||||
|
||||
/* 远端按键码(与 DTU-HMI key.h 保持一致) */
|
||||
#define UI_KEY_UP 0x02
|
||||
#define UI_KEY_DOWN 0x40
|
||||
#define UI_KEY_LEFT 0x10
|
||||
#define UI_KEY_RIGHT 0x08
|
||||
#define UI_KEY_ENTER 0x20
|
||||
#define UI_KEY_ESC 0x01
|
||||
|
||||
/*
|
||||
* 函数功能:初始化 LVGL 显示端口与按键输入设备
|
||||
* 入口参数:无
|
||||
* 返回值:0 - 成功,其他 - 失败(显示或输入设备创建失败)
|
||||
* 限定条件:无
|
||||
* 函数说明:1. 初始化 LVGL 内核并创建 1bpp 离屏显示设备
|
||||
* 2. 创建 keypad 输入设备及默认输入组,用于菜单导航
|
||||
* 3. 需在 lv_tick_inc/lv_timer_handler 被周期调用后方可刷新
|
||||
*/
|
||||
int ui_port_init(void);
|
||||
|
||||
/*
|
||||
* 函数功能:获取内存帧缓冲首地址
|
||||
* 入口参数:无
|
||||
* 返回值:帧缓冲首地址 uint8_t * 固定长度 UI_FB_SIZE 字节
|
||||
* 限定条件:ui_port_init() 已成功调用
|
||||
* 函数说明:1. 帧缓冲由 flush 回调写入最新渲染结果
|
||||
* 2. 供 RemoDispBus 服务端读取并推送至远端
|
||||
*/
|
||||
uint8_t *ui_port_get_framebuffer(void);
|
||||
|
||||
/*
|
||||
* 函数功能:向输入队列喂入一个远端按键值
|
||||
* 入口参数:key_val - 远端键码 uint8_t UI_KEY_* 取值之一
|
||||
* 返回值:无
|
||||
* 限定条件:无
|
||||
* 函数说明:1. 将远端键码映射为 LVGL 按键后缓存
|
||||
* 2. 由 keypad 读取回调在下次轮询时上报,驱动菜单导航
|
||||
*/
|
||||
void ui_port_queue_key(uint8_t key_val);
|
||||
|
||||
/*
|
||||
* 函数功能:UI 周期步进(更新系统节拍并刷新 LVGL)
|
||||
* 入口参数:tick_ms - 距上次调用的毫秒数 uint32_t 建议 10
|
||||
* 返回值:无
|
||||
* 限定条件:ui_port_init() 已成功调用
|
||||
* 函数说明:1. 调用 lv_tick_inc 推进 LVGL 内部时钟
|
||||
* 2. 调用 lv_timer_handler 处理重绘,结果经 flush 写入帧缓冲
|
||||
* 3. 须由任务以固定周期调用,否则界面停止刷新
|
||||
*/
|
||||
void ui_port_step(uint32_t tick_ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __UI_PORT_H */
|
||||
Reference in New Issue
Block a user