目录
[1.2 按键线程延迟50ms的影响](#1.2 按键线程延迟50ms的影响)
问题1
按键扫描线程为什么不使用定时器?实际项目为什么选择了线程?按键线程延迟50ms会有什么影响?
1.1(线程与定时器的本质区别)
定时器是基于系统时钟中断,在中断上下文中执行回调函数,执行时间受限,不能有阻塞操作
线程是在任务上下文中执行,可以包含阻塞操作,执行时间更灵活
实际项目选择线程的原因
1、按键扫描的特点
一是需要持续监控按键状态变化,二是需要处理按键的按下和释放事件 ,包含状态机逻辑(空闲态、防抖态、保持按下态)
2、 线程的优势
-
逻辑完整性 :按键扫描的状态机逻辑需要连续执行,线程可以完整实现整个状态机流程
-
响应及时性 :线程可以在按键状态变化时立即处理,而定时器受定时周期限制
-
灵活性 :线程可以根据需要调整扫描间隔,适应不同的硬件特性
-
独立性 :按键处理逻辑独立于其他系统任务,不会影响主任务的执行
-
资源隔离 :线程有自己的栈空间,不会与其他任务共享栈资源 2.3 定时器的局限性
总结:
1、定时器回调函数执行时间有限制,不能执行复杂的状态机逻辑
2、定时器回调在中断上下文,不能使用可能引起阻塞的函数
3、多个定时器可能产生时间冲突,影响系统稳定性
1.2 按键线程延迟50ms的影响
- 扫描间隔的设计考虑
50ms 是一个平衡值,兼顾了响应速度和系统资源消耗,太短的间隔会增加CPU负担,太长的间隔会影响按键响应速度潜在负面影响。理论上,按键按下后最多需要50ms才能被检测到,对于非常快速的连续按键操作,可能会有轻微延迟。
- 用户体验 :50ms的延迟在大多数场景下对用户体验影响不大,但在需要极高响应速度的场景下可能不够理想
总结:
1、人类感知的反应时间通常在100-200ms之间
2、50ms的扫描间隔远低于人类感知阈值,因此在实际使用中不会被用户察觉
3、对于机械按键来说,50ms的扫描间隔已经足够捕捉按键状态变化
问题2
当前屏幕,上一个屏幕,下一个屏幕是怎么在多个线程间进行通信的?多线程同步使用互斥锁,互斥锁在此代码中保护了哪些共享变量?你觉得哪些变量需要保护?
- 标志位机制
-
使用 g_screen_switch_flag 作为线程间的信号,通知LVGL主线程执行屏幕切换
-
这种"标志位+定时器"的模式避免了线程间的直接阻塞等待,提高了系统响应速度
- 状态分离
-
g_current_screen :当前实际显示的屏幕
-
g_target_screen :即将要显示的目标屏幕
-
这种分离确保了即使在切换过程中,系统也能正确响应新的切换请求
- 互斥锁保护
-
所有对共享变量的访问都通过互斥锁保护,确保线程安全
-
特别是在计算下一个/上一个屏幕时,需要原子操作以避免数据竞争
- 优先级设计
-
按键线程优先级设置为15,低于LVGL主线程(通常为更高优先级)
-
确保UI更新的及时性,同时保证按键事件能够被及时处理
问题3
目前所有lvgl操作,包括屏幕切换及屏幕数据更新均在一个线程中进行?有什么好处?是否有更好的方案?
代码中所有LVGL操作(包括屏幕切换和数据更新)确实都在一个线程中进行,具体是在update_device_data定时器回调中执行。
集中在一个线程的好处
1.线程安全:
LVGL本身不是线程安全的,所有LVGL操作集中在一个线程中避免了多线程访问冲突,无需复杂的线程同步机制,降低了系统复杂度
2.性能优化:
减少了线程上下文切换的开销,避免了多线程竞争导致的性能下降
3.逻辑清晰:
所有UI相关操作集中管理,便于代码维护,避免了UI操作分散在多个线程的混乱情况
4.资源管理:
统一的内存分配和释放,减少内存泄漏风险,集中处理UI更新,便于控制更新频率
可能的改进方案
1.双线程方案:
UI线程:负责LVGL操作和屏幕更新
数据采集线程:负责从设备获取数据,通过队列或共享内存传递给UI线程
优点:数据采集和UI更新分离,提高系统响应速度
缺点:增加了线程同步的复杂度
2.多核心优化:
在多核系统上,将数据处理和UI更新分配到不同核心
利用现代处理器的多核心优势,提高系统整体性能
3.事件驱动方案:
使用事件队列代替定时器轮询
只有在数据变化时才触发UI更新,减少不必要的计算
问题4
全局数据初始化,首次初始化设备数据,避免频繁清零导致指针失效,为什么频繁清0会导致指针失效?
频繁清零导致指针失效的原因
- 指针覆盖 :
-
当 g_main_info 中包含指针成员时, memset 会将这些指针清零
-
清零后的指针变成野指针,再次访问会导致内存访问错误
- 动态内存管理 :
-
如果 g_main_info 中的指针指向动态分配的内存,频繁清零会导致内存泄漏
-
因为清零前没有释放已分配的内存,清零后无法再访问这些内存
- 数据一致性 :
-
频繁清零会破坏数据的一致性,特别是在多线程环境下
-
可能导致部分数据被清零,部分数据仍在使用,造成数据错乱
- 性能影响 :
- 频繁的 memset 操作会增加CPU负担,特别是对于大型结构体
解决方案
-
首次初始化 :只在首次使用时清零,之后不再清零
-
选择性更新 :只更新需要变化的字段,而不是整个结构体
-
内存管理 :对动态分配的内存进行正确的释放和重新分配
代码解读
cpp
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "lvgl.h"
#include "custom.h"
#include "ui_objects.h"
#include "aic_ui.h"
#include <inttypes.h>
#include <string.h>
#include <getopt.h>
#include <rtthread.h>
#include "rtdevice.h"
#include <rtdef.h>
#include "aic_core.h"
#include "aic_hal_gpio.h"
#include "../../../../../application/rt-thread/bl_hmi_ctl_app/driver/main_logic_proc.h"
#include "../../../../../application/rt-thread/bl_hmi_ctl_app/driver/sys_config.h"
/************************** 全局宏定义(集中管理,便于维护)**************************/
// 业务常量-模块与页面映射
#define MODULE_POWER_SCREEN 1
#define MODULE_WIFI_SCREEN 2
#define MODULE_SWITCH_SCREEN 3
#define MODULE_LOCATOR_SCREEN 4 // 精确定位
#define MODULE_TRAFFIC_LIGHT_SCREEN 5
#define MODULE_LOCATOR_SCREEN_VAGUE 7 // 区域定位
#define MAX_AVAILABLE_SCREENS 5
// 硬件参数-按键配置
#define KEY_UP_PIN "PE.13" // 上键引脚
#define KEY_DOWN_PIN "PE.14" // 下键引脚
#define KEY_DEBOUNCE_TICK 100 // 按键防抖时间(ms)
#define KEY_SCAN_INTERVAL 50 // 按键扫描间隔(ms)
// LVGL配置-定时器/动画
#define DATA_UPDATE_MS 500 // 数据更新频率(ms),LVGL操作主定时器
#define SCREEN_SWITCH_ANIM_MS 100 // 屏幕切换动画时间(ms),提升视觉体验
#define RESTORE_TIMER_MS 15000 // 按键后恢复自动轮询时间(ms)
#define DEFAULT_SCREEN_INTERVAL 4000 // 默认屏幕自动轮询间隔(ms)
// 业务配置-定位器/卡片
#define CARD_ID_LEN 5 // 卡号最大长度,防止越界
#define CARD_MAX_NUM 30 // 定位器最大显示卡片数
#define CARD_LINE_BREAK 9 // 区域定位卡片8个换行
#define CARD_PRECISE_LINE_BREAK 10 // 精确定位卡片10个换行
// 缓冲区配置-通用
#define IP_BUF_SIZE 31 // IP最大长度(255.255.255.255=15)+预留
#define HID_BUF_SIZE 16 // HID缓冲区大小
#define NUM_STR_SIZE 32 // 数字格式化字符串大小
#define CARD_SHOW_STR_SIZE 255 // 卡片拼接字符串大小
// 颜色宏定义
#define COLOR_CONNECT_GREEN lv_color_hex(0x00ff00)
#define COLOR_CONNECT_RED lv_color_hex(0xff0000)
#define COLOR_CONNECT_YELLOW lv_color_hex(0xffed2e)
// 供电类型适配(预留毫伏级注释,便于底层数据适配)
#define VOLTAGE_UNIT "V" // 若底层为毫伏,改为"mV"并去掉除1000逻辑
/************************** 多线程同步-互斥锁定义 **************************/
static rt_mutex_t g_ui_mutex = RT_NULL;
#define UI_MUTEX_TAKE() rt_mutex_take(g_ui_mutex, RT_WAITING_FOREVER)
#define UI_MUTEX_RELEASE() rt_mutex_release(g_ui_mutex)
/************************** 全局变量-业务核心(带互斥锁保护)**************************/
// 可用页面映射表
static rt_uint8_t g_available_screen_map[MAX_AVAILABLE_SCREENS] = {0};
static rt_uint8_t g_available_screen_count = 0;
// 屏幕管理-核心(按键/定时器仅修改标志位,LVGL线程处理实际切换)
static rt_uint8_t g_current_screen = 0;
static rt_uint8_t g_target_screen = 0; // 目标屏幕标志位
static rt_bool_t g_screen_switch_flag = RT_FALSE; // 屏幕切换请求标志
static rt_bool_t g_auto_rotate_en = RT_TRUE; // 自动轮询使能标志
// LVGL定时器句柄(仅做创建/销毁,无有效性判断,兼容低版本)
static lv_timer_t *g_screen_timer = NULL;
static lv_timer_t *g_restore_timer = NULL;
static lv_timer_t *g_data_update_timer = NULL;
// 设备数据-全局
extern sys_config_t g_sys_config;
static main_info_t g_main_info;
static rt_bool_t g_data_print_enable = RT_FALSE;
// 按键结构体(独立状态,防抖保护)
typedef struct
{
rt_base_t pin;
rt_uint8_t state;
rt_uint32_t press_tick;
rt_bool_t is_pressed;
const char *name; // 改为const,避免字符串修改
} my_key_t;
static my_key_t g_key_up = {RT_NULL, 0, 0, RT_FALSE, "key_up"};
static my_key_t g_key_down = {RT_NULL, 0, 0, RT_FALSE, "key_down"};
/************************** 静态函数声明(按功能分类,便于维护)**************************/
// 屏幕管理
static void screen_switch_proc(void);
static rt_uint8_t get_next_available_screen(void);
static rt_uint8_t get_prev_available_screen(void);
// 按键处理
static void key_scan_process(my_key_t *key);
static void key_scan_thread_entry(void *param);
static void key_up_handler(void);
static void key_down_handler(void);
// 定时器回调(仅修改标志位,无LVGL操作)
static void screen_timer_cb(lv_timer_t *timer);
static void restore_timer_cb(lv_timer_t *timer);
static void update_device_data(lv_timer_t *timer);
// 数据更新-各模块(纯LVGL操作,运行在LVGL主线程)
static void update_power_data(void);
static void update_wifi_data(void);
static void update_switch_data(void);
static void update_locator_data_vague(void); // 区域定位(screen3)
static void update_locator_data_precise(void); // 精确定位(screen7)
static void update_traffic_data(void);
// 辅助函数
static void print_main_info(void);
static rt_uint8_t get_screen_map_index(rt_uint8_t screen_idx);
static rt_uint8_t get_screen_from_map(rt_uint8_t map_idx);
static rt_uint8_t get_screen_display_page(rt_uint8_t screen_idx);
static const char *get_port_status_str(rt_uint8_t state);
static void update_port_label(lv_obj_t *label, rt_uint8_t state);
/************************** 外部接口-页面显示页码(供UI调用)**************************/
rt_uint8_t get_screen_display_page(rt_uint8_t screen_idx)
{
UI_MUTEX_TAKE();
rt_uint8_t map_idx = get_screen_map_index(screen_idx);
rt_uint8_t ret = 0;
if (map_idx < g_available_screen_count)
{
ret = (map_idx + 1);
}
UI_MUTEX_RELEASE();
return ret;
}
/************************** 核心函数-屏幕切换处理(纯LVGL操作,LVGL线程执行)**************************/
static void screen_switch_proc(void)
{
UI_MUTEX_TAKE();
// 1. 校验目标屏幕有效性
if (g_target_screen < 1 || g_target_screen > 7 ||
get_screen_map_index(g_target_screen) >= g_available_screen_count)
{
rt_kprintf("[UI] Invalid target screen: %d\n", g_target_screen);
g_screen_switch_flag = RT_FALSE;
UI_MUTEX_RELEASE();
return;
}
// 2. 若目标屏幕与当前一致,直接返回
if (g_target_screen == g_current_screen)
{
g_screen_switch_flag = RT_FALSE;
UI_MUTEX_RELEASE();
return;
}
// rt_kprintf("[UI] Switch screen: %d -> %d\n", g_current_screen, g_target_screen);
// 3. 安全删除旧屏幕(LVGL内部线程安全,立即删除避免野指针)
lv_obj_t *act_scr = lv_scr_act();
if (act_scr && !lv_obj_is_valid(act_scr))
{
act_scr = NULL;
rt_kprintf("[UI] Old screen obj invalid, force clear\n");
}
// 4. 根据模块类型动态创建目标屏幕(核心:定位器双界面逻辑)
lv_obj_t *new_screen = NULL;
switch (g_target_screen)
{
case MODULE_POWER_SCREEN:
screen_1_create(&ui_manager);
new_screen = screen_1_get(&ui_manager)->obj;
break;
case MODULE_WIFI_SCREEN:
screen_2_create(&ui_manager);
new_screen = screen_2_get(&ui_manager)->obj;
break;
case MODULE_SWITCH_SCREEN:
screen_5_create(&ui_manager);
new_screen = screen_5_get(&ui_manager)->obj;
break;
case MODULE_LOCATOR_SCREEN:
// 根据定位器类型创建 0 screen(区域)/1 screen7(精准)
screen_7_create(&ui_manager);
new_screen = screen_7_get(&ui_manager)->obj;
break;
case MODULE_TRAFFIC_LIGHT_SCREEN:
screen_4_create(&ui_manager);
new_screen = screen_4_get(&ui_manager)->obj;
break;
case MODULE_LOCATOR_SCREEN_VAGUE:
screen_3_create(&ui_manager);
new_screen = screen_3_get(&ui_manager)->obj;
break;
default:
break;
}
// 5. 加载新屏幕并更新当前屏幕
if (new_screen)
{
lv_scr_load_anim(new_screen, LV_SCR_LOAD_ANIM_FADE_ON, SCREEN_SWITCH_ANIM_MS, 0, RT_TRUE);
g_current_screen = g_target_screen; // 仅创建成功后更新
}
else
{
rt_kprintf("[UI] Failed to create screen %d\n", g_target_screen);
}
// 6. 清除切换标志
g_screen_switch_flag = RT_FALSE;
UI_MUTEX_RELEASE();
}
/************************** 按键处理-状态机+防抖(无LVGL操作,纯标志位修改)**************************/
static void key_scan_process(my_key_t *key)
{
if (!key || key->pin < 0)
return;
rt_uint8_t current_level = rt_pin_read(key->pin);
switch (key->state)
{
case 0: // 空闲态
if (current_level == PIN_LOW && !key->is_pressed)
{
key->state = 1;
key->press_tick = rt_tick_get();
}
break;
case 1: // 防抖态
if (rt_tick_get() - key->press_tick >= KEY_DEBOUNCE_TICK)
{
if (current_level == PIN_LOW)
{
key->state = 2;
key->is_pressed = RT_TRUE;
// 触发按键回调(仅修改标志位)
if (rt_strcmp(key->name, "key_up") == RT_EOK)
key_up_handler();
else if (rt_strcmp(key->name, "key_down") == RT_EOK)
key_down_handler();
}
else
{
key->state = 0; // 抖动脉冲,放弃
}
}
break;
case 2: // 保持按下态
if (current_level == PIN_HIGH)
{
key->state = 0;
key->is_pressed = RT_FALSE;
}
break;
default:
key->state = 0;
break;
}
}
static void key_up_handler(void)
{
UI_MUTEX_TAKE();
g_auto_rotate_en = RT_FALSE; // 暂停自动轮询
g_target_screen = get_prev_available_screen(); // 设置上一页
g_screen_switch_flag = RT_TRUE; // 触发切换请求
// 重置恢复定时器(恢复自动轮询)
if (g_restore_timer)
lv_timer_reset(g_restore_timer);
UI_MUTEX_RELEASE();
rt_kprintf("[KEY] Key up pressed, target screen: %d\n", g_target_screen);
}
static void key_down_handler(void)
{
UI_MUTEX_TAKE();
g_auto_rotate_en = RT_FALSE;
g_target_screen = get_next_available_screen();
g_screen_switch_flag = RT_TRUE;
if (g_restore_timer)
lv_timer_reset(g_restore_timer);
UI_MUTEX_RELEASE();
rt_kprintf("[KEY] Key down pressed, target screen: %d\n", g_target_screen);
}
static void key_scan_thread_entry(void *param)
{
// 初始化按键引脚
g_key_up.pin = rt_pin_get(KEY_UP_PIN);
g_key_down.pin = rt_pin_get(KEY_DOWN_PIN);
if (g_key_up.pin < 0 || g_key_down.pin < 0)
{
rt_kprintf("[KEY] Failed to get key pins (up: %ld, down: %ld)\n", g_key_up.pin, g_key_down.pin);
return;
}
// 设置上拉输入
rt_pin_mode(g_key_up.pin, PIN_MODE_INPUT_PULLUP);
rt_pin_mode(g_key_down.pin, PIN_MODE_INPUT_PULLUP);
rt_kprintf("[KEY] Key scan thread init success (pins: %s, %s)\n", KEY_UP_PIN, KEY_DOWN_PIN);
// 无限扫描
while (1)
{
key_scan_process(&g_key_up);
key_scan_process(&g_key_down);
rt_thread_mdelay(KEY_SCAN_INTERVAL);
}
}
/************************** 定时器回调-仅修改标志位(无LVGL操作,兼容低版本)**************************/
static void screen_timer_cb(lv_timer_t *timer)
{
UI_MUTEX_TAKE();
// 仅自动轮询使能时,执行自动切换
if (g_auto_rotate_en && g_available_screen_count > 0)
{
g_target_screen = get_next_available_screen();
g_screen_switch_flag = RT_TRUE;
}
UI_MUTEX_RELEASE();
}
static void restore_timer_cb(lv_timer_t *timer)
{
UI_MUTEX_TAKE();
g_auto_rotate_en = RT_TRUE; // 恢复自动轮询
// rt_kprintf("[TIMER] Restore auto screen rotate\n");
UI_MUTEX_RELEASE();
}
/************************** 核心LVGL定时器-设备数据更新(所有LVGL操作入口)**************************/
static void update_device_data(lv_timer_t *timer)
{
UI_MUTEX_TAKE();
// 1. 首次初始化设备数据,避免频繁清零导致指针失效
static rt_bool_t g_main_info_inited = RT_FALSE;
if (!g_main_info_inited)
{
memset(&g_main_info, 0, sizeof(main_info_t));
g_main_info_inited = RT_TRUE;
rt_kprintf("[DATA] Global device info init success\n");
}
UI_MUTEX_RELEASE();
// 2. 处理屏幕切换请求(核心:所有LVGL操作在此执行)
if (g_screen_switch_flag)
{
screen_switch_proc();
}
// 3. 调试数据打印(使能时)
if (g_data_print_enable)
{
print_main_info();
}
// 4. 校验当前屏幕有效性
UI_MUTEX_TAKE();
rt_uint8_t curr_screen = g_current_screen;
UI_MUTEX_RELEASE();
if (curr_screen == 0 || get_screen_map_index(curr_screen) >= g_available_screen_count)
{
return;
}
// 5. 按需获取设备数据并更新UI(当前屏幕仅更新对应模块,节省资源)
switch (curr_screen)
{
case MODULE_POWER_SCREEN:
get_battery_info(&g_main_info.m_battery_info);
update_power_data();
break;
case MODULE_WIFI_SCREEN:
get_io_info(&g_main_info.m_io_info);
update_wifi_data();
break;
case MODULE_SWITCH_SCREEN:
get_io_info(&g_main_info.m_io_info);
update_switch_data();
break;
case MODULE_LOCATOR_SCREEN:
get_locator_info(&g_main_info.m_locator_info);
update_locator_data_precise();
break;
case MODULE_TRAFFIC_LIGHT_SCREEN:
get_traffic_info(&g_main_info.m_traffic_light_info);
update_traffic_data();
break;
case MODULE_LOCATOR_SCREEN_VAGUE:
get_locator_info(&g_main_info.m_locator_info);
update_locator_data_vague();
break;
default:
break;
}
}
/************************** 设备数据打印-调试用(RT-Thread线程安全)**************************/
static void print_main_info(void)
{
rt_kprintf("==================== DEVICE DATA ====================\n");
// 定位器信息
rt_kprintf("Locator Type: %s\n", g_main_info.m_locator_info.locator_type == 0 ? "Precise" : "Vague");
// for (rt_uint8_t i = 0; i < locator_info.card_info.current_card_num; i++)
// {
// for (rt_uint8_t j = 0; j < CARD_ID_LEN; j++)
// rt_kprintf("%c", locator_info.card_info.card_id[i][j]);
// rt_kprintf("|");
// }
rt_kprintf("\n");
rt_kprintf("Locator State: %s\n", g_main_info.m_locator_info.is_dev_online ? "Online" : "Offline");
rt_kprintf("Antenna Dir: %d (0=Init,1=Left,2=Right)\n", g_main_info.m_locator_info.antenna_direction);
rt_kprintf("Card Total: %d, Current: %d\n",
g_main_info.m_locator_info.card_info.total_card_num,
g_main_info.m_locator_info.card_info.current_card_num);
// 电池信息
rt_kprintf("Battery SOC: %d%%, Power Type: %s\n",
g_main_info.m_battery_info.soc,
g_main_info.m_battery_info.power_type ? "Battery" : "AC");
rt_kprintf("Battery CH0: %d%s, %d.%02dA | CH1: %d%s, %d.%02dA\n",
g_main_info.m_battery_info.voltage[0], VOLTAGE_UNIT,
g_main_info.m_battery_info.current[0] / 100, g_main_info.m_battery_info.current[0] % 100,
g_main_info.m_battery_info.voltage[1], VOLTAGE_UNIT,
g_main_info.m_battery_info.current[1] / 100, g_main_info.m_battery_info.current[1] % 100);
rt_kprintf("Battery HID: 0x%08lX\n", g_main_info.m_battery_info.hid);
// 红绿灯信息
rt_kprintf("Traffic Light State: %s, Count: %d\n",
g_main_info.m_traffic_light_info.online_state ? "Online" : "Offline",
g_main_info.m_traffic_light_info.traffic_num);
for (rt_uint8_t i = 0; i < g_main_info.m_traffic_light_info.traffic_num; i++)
{
rt_kprintf("Traffic %d HID: 0x%08" PRIx32 "\n", i + 1, g_main_info.m_traffic_light_info.hid[i]);
}
rt_kprintf("=====================================================\n\n");
}
/************************** 各模块UI数据更新(纯LVGL操作,双层空指针校验)**************************/
static void update_power_data(void)
{
screen_1_t *scr = screen_1_get(&ui_manager);
if (!scr || !scr->obj)
return;
// 更新页码
if (scr->page_1)
{
rt_uint8_t page = get_screen_display_page(MODULE_POWER_SCREEN);
UI_MUTEX_TAKE();
lv_label_set_text_fmt(scr->page_1, "第%d/%d页", page, g_available_screen_count);
UI_MUTEX_RELEASE();
}
// 电池HID/状态核心逻辑
if (scr->Battery_Hid)
{
UI_MUTEX_TAKE();
rt_uint32_t bat_hid = g_main_info.m_battery_info.hid;
rt_uint8_t bat_soc = g_main_info.m_battery_info.soc;
rt_int32_t vol0 = g_main_info.m_battery_info.voltage[0];
rt_int32_t cur0 = g_main_info.m_battery_info.current[0];
rt_int32_t vol1 = g_main_info.m_battery_info.voltage[1];
rt_int32_t cur1 = g_main_info.m_battery_info.current[1];
rt_uint8_t power_type = g_main_info.m_battery_info.power_type;
UI_MUTEX_RELEASE();
if (bat_hid == 0)
{ // 离线状态
lv_obj_set_style_text_color(scr->Battery_Hid, COLOR_CONNECT_RED, LV_PART_MAIN);
if (scr->Power_Mode)
lv_obj_set_style_text_color(scr->Power_Mode, COLOR_CONNECT_YELLOW, LV_PART_MAIN);
if (scr->Battery_Hid)
lv_label_set_text(scr->Battery_Hid, "离线");
if (scr->Power_Mode)
lv_label_set_text(scr->Power_Mode, "未知");
if (scr->vol_1)
lv_label_set_text(scr->vol_1, "0" VOLTAGE_UNIT);
if (scr->cur_1)
lv_label_set_text(scr->cur_1, "0.00A");
if (scr->battery_1)
lv_label_set_text(scr->battery_1, "0%");
if (scr->vol_2)
lv_label_set_text(scr->vol_2, "0" VOLTAGE_UNIT);
if (scr->cur_2)
lv_label_set_text(scr->cur_2, "0.00A");
if (scr->battery_2)
lv_label_set_text(scr->battery_2, "0%");
}
else
{ // 在线状态
lv_obj_set_style_text_color(scr->Battery_Hid, COLOR_CONNECT_YELLOW, LV_PART_MAIN);
// 更新CH0
if (scr->vol_1)
lv_label_set_text_fmt(scr->vol_1, "%ld" VOLTAGE_UNIT, vol0);
if (scr->cur_1)
lv_label_set_text_fmt(scr->cur_1, "%ld.%02ldA", cur0 / 100, cur0 % 100);
if (scr->battery_1)
lv_label_set_text_fmt(scr->battery_1, "%d%%", bat_soc);
// 更新CH1
if (scr->vol_2)
lv_label_set_text_fmt(scr->vol_2, "%ld" VOLTAGE_UNIT, vol1);
if (scr->cur_2)
lv_label_set_text_fmt(scr->cur_2, "%ld.%02ldA", cur1 / 100, cur1 % 100);
if (scr->battery_2)
lv_label_set_text_fmt(scr->battery_2, "%d%%", bat_soc);
// 供电类型
if (scr->Power_Mode)
{
lv_label_set_text(scr->Power_Mode, power_type == 0 ? "交流供电" : "电池供电");
}
// HID/IP显示
if (g_sys_config.power_type == CAN_POWER)
{
lv_label_set_text_fmt(scr->Battery_Hid, "0x%08lX", bat_hid);
}
else
{
rt_uint8_t *ip = (rt_uint8_t *)&bat_hid;
lv_label_set_text_fmt(scr->Battery_Hid, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
}
}
}
}
static void update_wifi_data(void)
{
screen_2_t *scr = screen_2_get(&ui_manager);
if (!scr || !scr->obj)
return;
// 更新页码
if (scr->page_2)
{
rt_uint8_t page = get_screen_display_page(MODULE_WIFI_SCREEN);
UI_MUTEX_TAKE();
lv_label_set_text_fmt(scr->page_2, "第%d/%d页", page, g_available_screen_count);
UI_MUTEX_RELEASE();
}
// WiFi状态更新
if (scr->work_status)
{
UI_MUTEX_TAKE();
rt_uint8_t wifi_state = g_main_info.m_io_info.wifi7_state;
UI_MUTEX_RELEASE();
const char *state_str = NULL;
if (wifi_state == 1)
{ // 正常
state_str = "正常";
lv_obj_set_style_text_color(scr->work_status, COLOR_CONNECT_GREEN, LV_PART_MAIN);
// IP显示(复用宏定义,避免越界)
if (scr->ip_addr)
{
char ip_str[IP_BUF_SIZE] = {0};
snprintf(ip_str, sizeof(ip_str), "%d.%d.%d.%d",
g_sys_config.wifi_module_ip[0], g_sys_config.wifi_module_ip[1],
g_sys_config.wifi_module_ip[2], g_sys_config.wifi_module_ip[3]);
lv_label_set_text(scr->ip_addr, ip_str);
}
}
else
{ // 异常/默认
state_str = "异常";
lv_obj_set_style_text_color(scr->work_status, COLOR_CONNECT_RED, LV_PART_MAIN);
if (scr->ip_addr)
lv_label_set_text(scr->ip_addr, "");
}
lv_label_set_text(scr->work_status, state_str);
}
}
// 网口状态辅助函数
static const char *get_port_status_str(rt_uint8_t state)
{
return (state == 1) ? "在线" : "离线";
}
static void update_port_label(lv_obj_t *label, rt_uint8_t state)
{
if (!label)
return;
if (state == 1)
{
lv_obj_set_style_text_color(label, COLOR_CONNECT_GREEN, LV_PART_MAIN);
}
else if (state == 0)
{
lv_obj_set_style_text_color(label, COLOR_CONNECT_RED, LV_PART_MAIN);
}
else
{
lv_obj_set_style_text_color(label, COLOR_CONNECT_YELLOW, LV_PART_MAIN);
}
lv_label_set_text(label, get_port_status_str(state));
}
static void update_switch_data(void)
{
screen_5_t *scr = screen_5_get(&ui_manager);
if (!scr || !scr->obj)
return;
// 更新网口状态
UI_MUTEX_TAKE();
io_info_t io_info = g_main_info.m_io_info;
UI_MUTEX_RELEASE();
update_port_label(scr->Eport_1, io_info.ep1_100m_state);
update_port_label(scr->Eport_2, io_info.ep2_100m_state);
update_port_label(scr->Oport1_100, io_info.op1_100m_state);
update_port_label(scr->Oport2_100, io_info.op2_100m_state);
update_port_label(scr->Gport1_1000, io_info.op1_1000m_state);
update_port_label(scr->Gport2_1000, io_info.op2_1000m_state);
// 更新页码
if (scr->page_5)
{
rt_uint8_t page = get_screen_display_page(MODULE_SWITCH_SCREEN);
UI_MUTEX_TAKE();
lv_label_set_text_fmt(scr->page_5, "第%d/%d页", page, g_available_screen_count);
UI_MUTEX_RELEASE();
}
}
// 区域定位(screen3)-数据更新
static void update_locator_data_vague(void)
{
screen_3_t *scr = screen_3_get(&ui_manager);
if (!scr || !scr->obj)
return;
// 统一加互斥锁读取所有定位器数据,避免多线程数据错乱
UI_MUTEX_TAKE();
locator_info_t loc_info = g_main_info.m_locator_info;
UI_MUTEX_RELEASE();
// 服务器连接状态
if (scr->server_connect_status)
{
lv_color_t color = COLOR_CONNECT_YELLOW;
const char *state_str = "";
if (loc_info.server_link_state.len > 0)
{
color = loc_info.server_link_state.state == 1 ? COLOR_CONNECT_GREEN : COLOR_CONNECT_RED;
state_str = loc_info.server_link_state.state == 1 ? "在线" : "离线";
}
lv_obj_set_style_text_color(scr->server_connect_status, color, LV_PART_MAIN);
lv_label_set_text(scr->server_connect_status, state_str);
}
// IP/HID显示
if (scr->position_ip)
{
char ip_str[IP_BUF_SIZE] = {0};
if (loc_info.local_ip.len > 0 && loc_info.local_ip.len < IP_BUF_SIZE)
{
memcpy(ip_str, loc_info.local_ip.ip, loc_info.local_ip.len);
}
lv_label_set_text(scr->position_ip, strlen(ip_str) > 0 ? ip_str : "");
}
if (scr->position_hid)
{
char hid_str[HID_BUF_SIZE] = {0};
if (loc_info.local_hid.len > 0 && loc_info.local_hid.len < HID_BUF_SIZE)
{
memcpy(hid_str, loc_info.local_hid.hid, loc_info.local_hid.len);
}
lv_label_set_text(scr->position_hid, strlen(hid_str) > 0 ? hid_str : "");
}
// 卡片信息显示(核心修复:匹配实际卡号长度+正确取值)
if (scr->identify_cardID && scr->label_25)
{
// 设备离线:清空显示
if (loc_info.is_dev_online != 1)
{
lv_label_set_text(scr->label_25, "");
lv_label_set_text(scr->identify_cardID, "");
return;
}
// 加互斥锁读取卡片信息,避免数据错乱
UI_MUTEX_TAKE();
locator_card_info_t card_info = loc_info.card_info;
UI_MUTEX_RELEASE();
char card_show_str[CARD_SHOW_STR_SIZE] = {0};
// 核心修正1:取current_card_num(当前显示数),且限制≤MAX_CARD_NUM
rt_uint8_t valid_card_num = 0;
if (card_info.current_card_num > 0)
{
valid_card_num = card_info.current_card_num;
}
if (valid_card_num == 0)
{
strcpy(card_show_str, "无");
}
else
{
// 显示卡片总数
char num_str[NUM_STR_SIZE] = {0};
snprintf(num_str, sizeof(num_str), "(%d个)", card_info.total_card_num);
lv_label_set_text(scr->label_25, num_str);
// 拼接卡号:按8个/行,每个卡号后3个空格,行末换行
char line_buf[256] = {0}; // 单行缓冲区
rt_uint8_t line_count = 0; // 行内卡号计数
for (rt_uint8_t i = 0; i < valid_card_num; i++)
{
// 核心修正2:仅读取5位有效卡号(匹配实际日志),并强制加终止符
char single_card[6] = {0}; // 5位卡号 + '\0'
memcpy(single_card, card_info.card_id[i], 5); // 固定读取5位,舍弃多余乱码
single_card[5] = '\0'; // 强制终止,避免乱码
// 拼接当前卡号到行缓冲区
strcat(line_buf, single_card);
line_count++;
// 格式控制:
// 1. 行内未到8个,且不是最后一个卡号 → 加3个空格
if (line_count < CARD_LINE_BREAK && i < valid_card_num - 1)
{
strcat(line_buf, " ");
}
// 2. 行内满8个,且不是最后一个卡号 → 换行+重置行缓冲区
else if (line_count == CARD_LINE_BREAK && i < valid_card_num - 1)
{
strcat(card_show_str, line_buf);
strcat(card_show_str, "\n");
memset(line_buf, 0, sizeof(line_buf));
line_count = 0;
}
}
// 拼接最后一行剩余的卡号
if (strlen(line_buf) > 0)
{
strcat(card_show_str, line_buf);
}
}
// 设置最终显示的卡号字符串
lv_label_set_text(scr->identify_cardID, card_show_str);
}
// 页码显示
if (scr->page_3)
{
rt_uint8_t page = get_screen_display_page(MODULE_LOCATOR_SCREEN_VAGUE);
UI_MUTEX_TAKE();
lv_label_set_text_fmt(scr->page_3, "第%d/%d页", page, g_available_screen_count);
UI_MUTEX_RELEASE();
}
}
// 精确定位(screen7)-数据更新
static void update_locator_data_precise(void)
{
screen_7_t *scr = screen_7_get(&ui_manager);
if (!scr || !scr->obj)
return;
UI_MUTEX_TAKE();
locator_info_t loc_info = g_main_info.m_locator_info;
UI_MUTEX_RELEASE();
// 服务器状态
if (scr->server_status)
{
lv_color_t color = COLOR_CONNECT_YELLOW;
const char *state_str = "";
if (loc_info.server_link_state.len > 0)
{
color = loc_info.server_link_state.state == 1 ? COLOR_CONNECT_GREEN : COLOR_CONNECT_RED;
state_str = loc_info.server_link_state.state == 1 ? "在线" : "离线";
}
lv_obj_set_style_text_color(scr->server_status, color, LV_PART_MAIN);
lv_label_set_text(scr->server_status, state_str);
}
// 读卡器/天线方向(精确定位专属)
if (scr->head_status)
{
lv_color_t color = COLOR_CONNECT_YELLOW;
const char *state_str = "";
if (loc_info.card_module_state.len > 0)
{
color = loc_info.card_module_state.state == 1 ? COLOR_CONNECT_GREEN : COLOR_CONNECT_RED;
state_str = loc_info.card_module_state.state == 1 ? "在线" : "离线";
}
lv_obj_set_style_text_color(scr->head_status, color, LV_PART_MAIN);
lv_label_set_text(scr->head_status, state_str);
}
// 天线方向
#if 0
if (scr->antenna_orient)
{
const char *dir_str = "";
if (loc_info.antenna_direction == 1)
dir_str = "--→";
else if (loc_info.antenna_direction == 2)
dir_str = "←--";
lv_label_set_text(scr->antenna_orient, dir_str);
}
#endif
if (scr->image_left && scr->image_right)
{
// 根据antenna_direction控制箭头显示/隐藏
switch (loc_info.antenna_direction)
{
case 2: // 显示右箭头,隐藏左箭头
lv_obj_clear_flag(scr->image_right, LV_OBJ_FLAG_HIDDEN); // 清除隐藏标志=显示
lv_obj_add_flag(scr->image_left, LV_OBJ_FLAG_HIDDEN); // 添加隐藏标志=隐藏
break;
case 1: // 显示左箭头,隐藏右箭头
lv_obj_clear_flag(scr->image_left, LV_OBJ_FLAG_HIDDEN); // 清除隐藏标志=显示
lv_obj_add_flag(scr->image_right, LV_OBJ_FLAG_HIDDEN); // 添加隐藏标志=隐藏
break;
default: // 其他情况,都隐藏
lv_obj_add_flag(scr->image_left, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(scr->image_right, LV_OBJ_FLAG_HIDDEN);
break;
}
}
// IP/HID显示
if (scr->position_ip)
{
char ip_str[IP_BUF_SIZE] = {0};
if (loc_info.local_ip.len > 0 && loc_info.local_ip.len < IP_BUF_SIZE)
{
memcpy(ip_str, loc_info.local_ip.ip, loc_info.local_ip.len);
}
lv_label_set_text(scr->position_ip, strlen(ip_str) > 0 ? ip_str : "");
}
if (scr->position_hid)
{
char hid_str[HID_BUF_SIZE] = {0};
if (loc_info.local_hid.len > 0 && loc_info.local_hid.len < HID_BUF_SIZE)
{
memcpy(hid_str, loc_info.local_hid.hid, loc_info.local_hid.len);
}
lv_label_set_text(scr->position_hid, strlen(hid_str) > 0 ? hid_str : "");
}
// 卡片信息显示(带越界日志)
if (scr->label_2 && scr->card_num)
{
if (loc_info.is_dev_online == 1)
{
locator_card_info_t *card_info = &loc_info.card_info;
char card_show_str[CARD_SHOW_STR_SIZE] = {0};
if (card_info->total_card_num == 0 || card_info->current_card_num == 0 ||
card_info->current_card_num > card_info->total_card_num)
{
strcpy(card_show_str, "无");
}
else if (card_info->card_id == NULL)
{
strcpy(card_show_str, "无");
}
else
{
char num_str[NUM_STR_SIZE] = {0};
snprintf(num_str, sizeof(num_str), "(%d个)", card_info->total_card_num);
lv_label_set_text(scr->card_num, num_str);
rt_uint8_t spliced = 0;
size_t remain_len = sizeof(card_show_str) - 1;
for (rt_uint8_t i = 0; i < card_info->total_card_num && remain_len > 0; i++)
{
if (spliced > 30)
break;
char single_card[CARD_ID_LEN + 1] = {0};
if (card_info->card_id[i] != NULL)
{
// 修改:显式转换为const char* 消除指针符号警告
size_t card_len = strlen((const char *)card_info->card_id[i]);
// 取"卡片ID长度"和"CARD_ID_LEN"的较小值,防止single_card溢出
size_t copy_len = card_len > CARD_ID_LEN ? CARD_ID_LEN : card_len;
memcpy(single_card, card_info->card_id[i], copy_len);
}
spliced++;
if (remain_len > strlen(single_card))
{
strncat(card_show_str, single_card, remain_len);
remain_len -= strlen(single_card);
}
else
{
break;
}
if (i < card_info->total_card_num - 1 && remain_len >= 3)
{
strncat(card_show_str, " ", 3);
remain_len -= 3;
}
if ((spliced % CARD_PRECISE_LINE_BREAK == 0) && spliced != card_info->total_card_num && remain_len >= 1)
{
strncat(card_show_str, "\n", 1);
remain_len -= 1;
}
}
}
lv_label_set_text(scr->label_2, card_show_str);
}
else
{
lv_label_set_text(scr->label_2, "");
lv_label_set_text(scr->card_num, "");
}
}
// 更新页码
if (scr->page_3)
{
rt_uint8_t page = get_screen_display_page(MODULE_LOCATOR_SCREEN);
UI_MUTEX_TAKE();
lv_label_set_text_fmt(scr->page_3, "第%d/%d页", page, g_available_screen_count);
UI_MUTEX_RELEASE();
}
}
// 红绿灯模块-数据更新
static void update_traffic_data(void)
{
screen_4_t *scr = screen_4_get(&ui_manager);
if (!scr || !scr->obj)
return;
// 更新页码
if (scr->page_4)
{
rt_uint8_t page = get_screen_display_page(MODULE_TRAFFIC_LIGHT_SCREEN);
UI_MUTEX_TAKE();
lv_label_set_text_fmt(scr->page_4, "第%d/%d页", page, g_available_screen_count);
UI_MUTEX_RELEASE();
}
UI_MUTEX_TAKE();
traffic_light_info_t tl_info = g_main_info.m_traffic_light_info;
UI_MUTEX_RELEASE();
// 红绿灯整体状态
if (scr->work_status_led)
{
const char *state_str = tl_info.online_state ? "在线" : "离线";
lv_color_t color = tl_info.online_state ? COLOR_CONNECT_GREEN : COLOR_CONNECT_RED;
lv_obj_set_style_text_color(scr->work_status_led, color, LV_PART_MAIN);
lv_label_set_text(scr->work_status_led, state_str);
}
// HID映射表(结构化管理,便于扩展)
typedef struct
{
lv_obj_t *hid_label;
rt_uint8_t idx;
} tl_hid_map_t;
const tl_hid_map_t hid_map[] = {
{scr->led_hid1, 0}, {scr->led_hid2, 1}, {scr->led_hid3, 2}, {scr->led_hid4, 3}};
rt_uint8_t map_count = sizeof(hid_map) / sizeof(hid_map[0]);
// 先清空所有HID标签
for (rt_uint8_t i = 0; i < map_count; i++)
{
if (hid_map[i].hid_label)
lv_label_set_text(hid_map[i].hid_label, "");
}
// 在线时更新HID
if (tl_info.online_state == 1)
{
rt_uint8_t valid_count = (tl_info.traffic_num > map_count) ? map_count : tl_info.traffic_num;
if (valid_count == 0)
return;
for (rt_uint8_t i = 0; i < valid_count; i++)
{
if (!hid_map[i].hid_label)
continue;
if (hid_map[i].idx >= sizeof(tl_info.hid) / sizeof(rt_uint32_t))
continue;
rt_uint32_t hid = tl_info.hid[hid_map[i].idx];
if (hid != 0)
{
lv_label_set_text_fmt(hid_map[i].hid_label, "0x%08" PRIx32, hid);
}
else
{
lv_label_set_text(hid_map[i].hid_label, "未配置"); // 友好提示
}
}
}
else
{
// 离线时标注所有HID为离线
for (rt_uint8_t i = 0; i < map_count; i++)
{
if (hid_map[i].hid_label)
{
lv_label_set_text(hid_map[i].hid_label, "");
}
}
}
}
/************************** 屏幕管理辅助函数(带互斥锁保护)**************************/
static rt_uint8_t get_screen_map_index(rt_uint8_t screen_idx)
{
for (rt_uint8_t i = 0; i < g_available_screen_count; i++)
{
if (g_available_screen_map[i] == screen_idx)
{
return i;
}
}
return g_available_screen_count;
}
static rt_uint8_t get_screen_from_map(rt_uint8_t map_idx)
{
if (map_idx >= g_available_screen_count)
{
return 0;
}
return g_available_screen_map[map_idx];
}
static rt_uint8_t get_next_available_screen(void)
{
UI_MUTEX_TAKE();
rt_uint8_t ret = g_current_screen;
if (g_available_screen_count == 0)
{
UI_MUTEX_RELEASE();
return ret;
}
rt_uint8_t curr_idx = get_screen_map_index(g_current_screen);
rt_uint8_t next_idx = (curr_idx + 1) % g_available_screen_count;
ret = get_screen_from_map(next_idx);
UI_MUTEX_RELEASE();
return ret;
}
static rt_uint8_t get_prev_available_screen(void)
{
UI_MUTEX_TAKE();
rt_uint8_t ret = g_current_screen;
if (g_available_screen_count == 0)
{
UI_MUTEX_RELEASE();
return ret;
}
rt_uint8_t curr_idx = get_screen_map_index(g_current_screen);
rt_uint8_t prev_idx = (curr_idx == 0) ? (g_available_screen_count - 1) : (curr_idx - 1);
ret = get_screen_from_map(prev_idx);
UI_MUTEX_RELEASE();
return ret;
}
/************************** 可用页面映射表初始化(核心入口)**************************/
void init_available_screen_map(void)
{
UI_MUTEX_TAKE();
memset(g_available_screen_map, 0, sizeof(g_available_screen_map));
g_available_screen_count = 0;
// 根据系统配置添加可用页面
if (g_sys_config.power_module == RT_TRUE)
{
g_available_screen_map[g_available_screen_count++] = MODULE_POWER_SCREEN;
}
if (g_sys_config.wifi_module == RT_TRUE)
{
g_available_screen_map[g_available_screen_count++] = MODULE_WIFI_SCREEN;
}
if (g_sys_config.network_switch_module == RT_TRUE)
{
g_available_screen_map[g_available_screen_count++] = MODULE_SWITCH_SCREEN;
}
if (g_sys_config.locator_module == RT_TRUE)
{
rt_uint8_t locator_type = get_sys_config_locator_type();
if (locator_type == 0) // 0表示精确定位
{
g_available_screen_map[g_available_screen_count++] = MODULE_LOCATOR_SCREEN;
}
else
{
g_available_screen_map[g_available_screen_count++] = MODULE_LOCATOR_SCREEN_VAGUE;
}
}
if (g_sys_config.traffic_light_module == RT_TRUE)
{
g_available_screen_map[g_available_screen_count++] = MODULE_TRAFFIC_LIGHT_SCREEN;
}
// 打印映射表信息
rt_kprintf("[UI] Available screen count: %d\n", g_available_screen_count);
for (rt_uint8_t i = 0; i < g_available_screen_count; i++)
{
rt_kprintf("[UI] Map %d -> Screen %d\n", i, g_available_screen_map[i]);
}
// 初始化当前/目标屏幕为第一个可用页面,并主动创建+加载第一个页面
if (g_available_screen_count > 0)
{
g_current_screen = g_available_screen_map[0];
g_target_screen = g_current_screen;
rt_kprintf("[UI] Init first screen: %d\n", g_current_screen);
// 核心:主动创建第一个页面的UI对象
lv_obj_t *first_screen_obj = NULL;
switch (g_current_screen)
{
case MODULE_POWER_SCREEN:
screen_1_create(&ui_manager);
first_screen_obj = screen_1_get(&ui_manager)->obj;
break;
case MODULE_WIFI_SCREEN:
screen_2_create(&ui_manager);
first_screen_obj = screen_2_get(&ui_manager)->obj;
break;
case MODULE_SWITCH_SCREEN:
screen_5_create(&ui_manager);
first_screen_obj = screen_5_get(&ui_manager)->obj;
break;
case MODULE_LOCATOR_SCREEN: // 精确定位
screen_7_create(&ui_manager);
first_screen_obj = screen_7_get(&ui_manager)->obj;
break;
case MODULE_LOCATOR_SCREEN_VAGUE: // 区域定位
screen_3_create(&ui_manager);
first_screen_obj = screen_3_get(&ui_manager)->obj;
break;
case MODULE_TRAFFIC_LIGHT_SCREEN:
screen_4_create(&ui_manager);
first_screen_obj = screen_4_get(&ui_manager)->obj;
break;
default:
rt_kprintf("[UI] Unknown first screen type: %d\n", g_current_screen);
break;
}
// 加载第一个页面到LVGL(确保首次显示)
if (first_screen_obj && lv_obj_is_valid(first_screen_obj))
{
lv_scr_load(first_screen_obj);
rt_kprintf("[UI] Load first screen success (obj: %p)\n", first_screen_obj);
}
else
{
rt_kprintf("[UI] First screen obj invalid: %p\n", first_screen_obj);
}
// 保留切换标志,兼容后续定时器/按键逻辑
g_screen_switch_flag = RT_TRUE;
}
UI_MUTEX_RELEASE();
}
/************************** 模块初始化/销毁(全局入口,资源统一管理)**************************/
void custom_init(void)
{
// 1. 创建UI互斥锁(多线程同步核心)
g_ui_mutex = rt_mutex_create("ui_mutex", RT_IPC_FLAG_PRIO);
if (g_ui_mutex == RT_NULL)
{
rt_kprintf("[ERROR] Create UI mutex failed!\n");
return;
}
rt_kprintf("[UI] Create UI mutex success\n");
// 2. 初始化可用页面映射表
init_available_screen_map();
// 3. 创建LVGL定时器(均为无限循环,避免反复创建)
// 3.1 屏幕自动轮询定时器
rt_uint16_t screen_interval = get_sys_config_screen_fresh_interval() * 1000;
if (screen_interval == 0)
{
screen_interval = DEFAULT_SCREEN_INTERVAL;
rt_kprintf("[TIMER] Screen interval 0, use default %dms\n", DEFAULT_SCREEN_INTERVAL);
}
g_screen_timer = lv_timer_create(screen_timer_cb, screen_interval, NULL);
// 3.2 恢复自动轮询定时器
g_restore_timer = lv_timer_create(restore_timer_cb, RESTORE_TIMER_MS, NULL);
// 3.3 数据更新主定时器(所有LVGL操作入口)
g_data_update_timer = lv_timer_create(update_device_data, DATA_UPDATE_MS, NULL);
rt_kprintf("[TIMER] Create timers (screen: %dms, restore: %dms, data: %dms)\n",
screen_interval, RESTORE_TIMER_MS, DATA_UPDATE_MS);
// 4. 创建按键扫描线程(栈1024字节,足够使用,节省内存)
rt_thread_t key_thread = rt_thread_create("key_scan",
key_scan_thread_entry,
RT_NULL,
1024,
15,
20);
if (key_thread)
{
rt_thread_startup(key_thread);
rt_kprintf("[UI] Create key scan thread success (stack: 1024)\n");
}
else
{
rt_kprintf("[ERROR] Create key scan thread failed!\n");
}
rt_kprintf("[UI] Custom module init complete!\n");
}
void custom_deinit(void)
{
UI_MUTEX_TAKE();
// 1. 销毁所有LVGL定时器
if (g_screen_timer)
{
lv_timer_del(g_screen_timer);
g_screen_timer = NULL;
}
if (g_restore_timer)
{
lv_timer_del(g_restore_timer);
g_restore_timer = NULL;
}
if (g_data_update_timer)
{
lv_timer_del(g_data_update_timer);
g_data_update_timer = NULL;
}
UI_MUTEX_RELEASE();
// 2. 销毁按键扫描线程
rt_thread_t key_thread = rt_thread_find("key_scan");
if (key_thread)
{
rt_thread_delete(key_thread);
rt_kprintf("[UI] Delete key scan thread success\n");
}
// 3. 销毁UI互斥锁
if (g_ui_mutex)
{
rt_mutex_delete(g_ui_mutex);
g_ui_mutex = RT_NULL;
rt_kprintf("[UI] Delete UI mutex success\n");
}
rt_kprintf("[UI] Custom module deinit complete!\n");
}
/************************** MSH调试命令(RT-Thread规范,带分号)**************************/
static void lvgl_test(rt_int32_t argc, char *argv[])
{
if (argc == 1 || argc > 2)
{
rt_kprintf("Please input correct command!\r\n");
goto __usage;
}
if (rt_strcmp(argv[1], "--help") == RT_EOK)
{
goto __usage;
}
else if (rt_strcmp(argv[1], "--debug_on") == RT_EOK)
{
g_data_print_enable = RT_TRUE;
rt_kprintf("[DEBUG] Data print enable\n");
}
else if (rt_strcmp(argv[1], "--debug_off") == RT_EOK)
{
g_data_print_enable = RT_FALSE;
rt_kprintf("[DEBUG] Data print disable\n");
}
else
{
rt_kprintf("Unknown command: %s\r\n", argv[1]);
goto __usage;
}
return;
__usage:
rt_kprintf("Usage:\r\n");
rt_kprintf(" lvgl --help - Show help info\r\n");
rt_kprintf(" lvgl --debug_on - Enable device data print\r\n");
rt_kprintf(" lvgl --debug_off - Disable device data print\r\n");
}
// 修正:添加分号,解决语法错误
MSH_CMD_EXPORT_ALIAS(lvgl_test, lvgl, LVGL debug and data print command);