UI 显示子系统
UI 显示子系统是 AW30N BLE SDK 中负责 LED 显示界面的完整机制:它通过 UI_menu_api() 驱动的菜单状态机,把系统事件(播放、音量、EQ、模式切换等)映射为 5x7 LED 点阵屏上的文字、数字与图标,并处理界面自动返回、定时刷新与闪烁显示等交互行为。
Purpose and Scope
本页面向工程师完整说明 UI 显示子系统的实现,覆盖:
- 菜单模型:
MENU_*枚举如何划分主界面与子界面; - 状态机核心:
UI_menu_api()的界面切换、自动返回与刷新过滤逻辑; - 驱动层:
led5x7_driver提供的显示缓存、图标位与扫描接口; - 工具层:
ui_common的数字转换函数; - 配置开关、扩展方式与边界行为。
不包含的内容:按键/红外输入的采集逻辑(KEY_IR_EN 相关的输入事件只在本页作为触发源提及)、音乐/FM 等业务模块的内部实现、以及硬件板级 IO 配置。这些主题由各自子系统的独立页面负责。
Overview
在嵌入式音响/收音机类产品中,UI 显示是用户感知系统状态的唯一窗口。该子系统采用菜单-状态机架构:
- 业务模块(播放器、EQ、音量等)不直接操作 LED 硬件,而是调用
UI_menu(menu, arg)请求显示某个界面; UI_menu_api()维护全局UI_var(当前菜单、主菜单、返回计数器、参数),决定是否接受该请求、是否需要清图标、是否启动自动返回计时;- 最终统一分发到
LED5X7_*系列显示函数,把语义化的菜单 ID 翻译为具体的字形、数字或图标; led5x7_driver持有显示缓存bShowBuff[5]与 8 个图标位,由LED5X7_scan()周期扫描刷新到 IO 端口。
这种分层使业务逻辑与显示硬件解耦:更换点阵屏型号或增加新界面时,只需要在 UI_menu_api() 的分发表中注册新菜单,并在驱动层实现对应的 LED5X7_show_xxx() 函数。
Architecture
flowchart TD
subgraph sg_App["应用/业务层"]
Player["music_play / play_file"]
KeyIR["按键/红外事件 (KEY_IR_EN)"]
Volume["音量/EQ 控制"]
end
subgraph sg_UI["UI 状态管理层"]
UI_menu["UI_menu_api()"]
UI_var["UI_var (bCurMenu/bMainMenu/bMenuReturnCnt/bCurArg)"]
MenuEnum["MENU_* 枚举定义 (ui_api.h)"]
end
subgraph sg_Show["显示分发层"]
ShowStr["LED5X7_show_string_menu()"]
ShowMusic["LED5X7_show_music_main()"]
ShowVol["LED5X7_show_volume()"]
ShowOther["LED5X7_show_*() 系列"]
end
subgraph sg_Driver["LED 驱动层 (led5x7_driver)"]
Buffer["LED5X7_var.bShowBuff[5] + 图标位"]
Scan["LED5X7_scan()"]
IO["GPIO 端口 (LED_PORT0~6)"]
end
Player -->|"UI_menu(MENU_*)"| UI_menu
KeyIR -->|"UI_menu(MENU_*)"| UI_menu
Volume -->|"UI_menu(MENU_*)"| UI_menu
UI_menu -->|"读/写"| UI_var
UI_menu -->|"switch 分发"| ShowStr
UI_menu -->|"switch 分发"| ShowMusic
UI_menu -->|"switch 分发"| ShowVol
UI_menu -->|"switch 分发"| ShowOther
ShowStr --> Buffer
ShowMusic --> Buffer
ShowVol --> Buffer
ShowOther --> Buffer
Buffer --> Scan
Scan --> IO
MenuEnum --> UI_menu
架构说明:
- 应用/业务层(调用方):播放器、EQ、红外按键等模块只依赖
UI_menu()宏与MENU_*枚举,对显示细节一无所知。这是典型的"请求-分发"解耦。 - UI 状态管理层(核心):
UI_menu_api()是唯一入口,负责过滤无效刷新、维护自动返回计数器,并把当前菜单路由到对应显示函数。UI_var是该层的全部状态。 - 显示分发层:
LED5X7_show_*()系列把菜单 ID + 参数(ui_arg)渲染为具体内容,写入驱动缓存。部分函数受编译开关保护(如AUDIO_HW_EQ_EN、KEY_IR_EN)。 - LED 驱动层:
LED5X7_var是显示缓存(5 字节字形 + 图标/状态位),LED5X7_scan()定时把缓存扫描输出到 7 个 IO 端口。硬件细节被完全封装在驱动内部。
菜单模型与状态机
MENU_* 枚举
ui_api.h 用枚举定义全部界面 ID,划分为三个区段(见 ui_api.h):
enum {
MENU_POWER_UP = 0,
MENU_IDLE,
MENU_WAIT,
#ifdef USB_DEVICE_EN
MENU_PC_MAIN,
MENU_PC_VOL_UP,
MENU_PC_VOL_DOWN,
#endif
MENU_AUX_MAIN,
MENU_PLAY,
MENU_PLAYMODE,
MENU_MAIN_VOL,
MENU_HW_EQ,
MENU_DEC_EQ,
MENU_NOFILE,
MENU_NODEVICE,
MENU_PLAY_TIME,
MENU_FILENUM,
MENU_INPUT_NUMBER,
MENU_MUSIC_MAIN,
MENU_PAUSE,
MENU_FM_MAIN,
MENU_FM_DISP_FRE,
MENU_FM_FIND_STATION,
MENU_FM_CHANNEL,
MENU_USBREMOVE,
MENU_SDREMOVE,
MENU_SCAN_DISK,
MENU_200MS_REFRESH = 0x80,
MENU_100MS_REFRESH,
MENU_SET_EQ,
MENU_SET_PLAY_MODE,
MENU_HALF_SEC_REFRESH,
MENU_POWER_DOWN,
MENU_MAIN = 0xFF,
};
来源:ui_api.h
区段划分的设计意图(UI_menu_api() 中的 menu > 0x80 判断依赖于此):
| 区段 | 取值 | 含义 |
|---|---|---|
| 普通界面 | 0x00 ~ 0x7F | 用户可见的具体界面,切换时会清空图标并可能启动自动返回计数 |
| 刷新界面 | 0x80 ~ 0xFE | 周期性刷新请求(如 100ms/200ms/半秒刷新播放时间),仅当当前界面是主界面时才生效 |
| 特殊值 | MENU_MAIN = 0xFF | 请求"返回主界面",驱动自动返回计数逻辑 |
UI_VAR 状态结构
typedef struct _UI_VAR {
u8 bCurMenu; //< 当前界面
u8 bMainMenu; //< 主界面(自动返回的目标)
u8 bMenuReturnCnt; //< 自动返回计数器
int bCurArg; //< 当前界面参数(如曲目号、频率)
} UI_VAR;
#define UI_RETURN 3 // n * 500ms:约 1.5s 无操作后自动返回主界面
来源:ui_api.h
UI_RETURN 定义为 3,配合外部 500ms 定时刷新周期,实现"子界面显示约 1.5 秒后自动回到主界面"的产品行为。bMenuReturnCnt 是单调递增的计数器,只有到达 UI_RETURN 阈值才真正切换界面,避免每 500ms 都触发一次界面重绘。
状态机核心:UI_menu_api()
UI_menu_api() 是子系统的枢纽函数,完整实现在 ui_api.c。其控制流如下:
void UI_menu_api(u8 menu, int arg)
{
int ui_arg = arg;
/*界面属性-非主界面自动返回*/
if (menu == MENU_MAIN) {
if (UI_var.bMenuReturnCnt < UI_RETURN) {
UI_var.bMenuReturnCnt++;
if (UI_var.bMenuReturnCnt == UI_RETURN) {
LED5X7_clear_icon();
#if KEY_IR_EN
if (UI_var.bCurMenu == MENU_INPUT_NUMBER) {
post_msg(1, MSG_INPUT_TIMEOUT); //输入超时
} else
#endif
{
UI_var.bCurMenu = UI_var.bMainMenu;
UI_var.bCurArg = ui_arg;
}
} else {
ui_arg = UI_var.bCurArg;
}
} else {
/*等待界面不重复刷新界面*/
if (UI_var.bCurMenu == UI_var.bMainMenu) {
return;
}
UI_var.bCurMenu = UI_var.bMainMenu;
UI_var.bCurArg = ui_arg;
}
} else {
if (menu > 0x80) { //仅在当前界面为主界面时刷新界面,例如:在主界面刷新播放时间
if (UI_var.bCurMenu != UI_var.bMainMenu) {
return;
}
} else {
LED5X7_clear_icon();
/*非主界面需要启动返回计数器*/
if (menu != UI_var.bMainMenu) {
UI_var.bMenuReturnCnt = 0;
}
UI_var.bCurMenu = menu;
UI_var.bCurArg = ui_arg;
}
}
LED5X7_setX(0);
// ... switch 分发到 LED5X7_show_*()
}
来源:ui_api.c
设计意图与三条关键路径:
MENU_MAIN自动返回路径:业务模块(或 500ms 定时器)周期性调用UI_menu(MENU_MAIN, arg)。计数器从 0 累加到UI_RETURN后,才把bCurMenu复位为bMainMenu并清空图标;在达到阈值之前,ui_arg保持上一次的bCurArg,防止刷新请求把参数冲掉。若当前已是主界面则直接 return,避免重复绘制。- 红外模式下若当前是
MENU_INPUT_NUMBER(频道数字输入),自动返回时还会post_msg(MSG_INPUT_TIMEOUT)通知上层输入超时——把"UI 行为"与"业务超时处理"通过消息机制衔接。
- 红外模式下若当前是
- 刷新请求路径(
menu > 0x80):只允许在主界面上生效。例如"在主界面刷新播放时间",当用户停留在某个子界面(如 EQ 设置)时,周期刷新请求被丢弃,保证子界面不被抢占。 - 普通界面切换路径(
menu <= 0x80):先LED5X7_clear_icon()清空上一界面的图标,若目标不是主界面则重置返回计数器(重新计时 1.5s),然后记录新菜单与参数。
随后统一执行 LED5X7_setX(0)(光标回到第 0 列)并进入 switch 分发:
switch (UI_var.bCurMenu) {
/*-----System Power On UI*/
case MENU_POWER_UP:
case MENU_IDLE:
case MENU_WAIT:
#ifdef USB_DEVICE_EN
case MENU_PC_MAIN:
case MENU_PC_VOL_UP:
case MENU_PC_VOL_DOWN:
#endif
case MENU_AUX_MAIN:
LED5X7_show_string_menu(UI_var.bCurMenu);
break;
/*-----Common Info UI*/
case MENU_MAIN_VOL:
LED5X7_show_volume();
break;
/*-----Music Related UI*/
case MENU_MUSIC_MAIN:
case MENU_PAUSE:
LED5X7_show_music_main(ui_arg);
break;
case MENU_FILENUM:
LED5X7_show_filenumber(ui_arg);
break;
#if (defined(AUDIO_HW_EQ_EN) && (AUDIO_HW_EQ_EN)) | (defined(PCM_SW_EQ_EN) && (PCM_SW_EQ_EN))
case MENU_HW_EQ:
LED5X7_show_hw_eq();
break;
#endif
case MENU_DEC_EQ:
LED5X7_show_dec_eq(ui_arg);
break;
#if KEY_IR_EN
case MENU_INPUT_NUMBER:
LED5X7_show_IR_number();
break;
#endif
case MENU_PLAYMODE:
LED5X7_show_playmode(ui_arg);
break;
default:
break;
}
来源:ui_api.c
分发表的设计意图:
- 多菜单共享一个显示函数:
MENU_POWER_UP/MENU_IDLE/MENU_WAIT/MENU_AUX_MAIN等字符串型界面共用LED5X7_show_string_menu(),由驱动内部根据菜单 ID 查字形表,减少重复代码。 - 参数透传:
MENU_MUSIC_MAIN、MENU_FILENUM、MENU_DEC_EQ、MENU_PLAYMODE等把ui_arg传给显示函数(曲目号、EQ 档位、播放模式),使同一界面可显示不同数据。 - 编译期裁剪:
AUDIO_HW_EQ_EN/PCM_SW_EQ_EN控制硬件 EQ 界面是否编译;KEY_IR_EN控制红外数字输入界面;FM/RTC 相关 case 被#if 0关闭——产品配置通过宏决定最终固件包含哪些界面。
Core Flow
界面切换时序
sequenceDiagram
participant Biz as 业务模块 (播放/音量/EQ)
participant UI as UI_menu_api()
participant Var as UI_var
participant Drv as LED5X7_show_*()
participant Hw as LED5X7_scan() → GPIO
Biz->>UI: UI_menu(MENU_MUSIC_MAIN, track_no)
UI->>UI: menu <= 0x80 且 != bMainMenu
UI->>Drv: LED5X7_clear_icon()
UI->>Var: bMenuReturnCnt = 0; bCurMenu = MENU_MUSIC_MAIN; bCurArg = track_no
UI->>Drv: LED5X7_setX(0)
UI->>Drv: LED5X7_show_music_main(ui_arg)
Drv->>Drv: 写 bShowBuff / 图标位
loop 周期扫描
Hw->>Hw: 扫描输出到 LED_PORT0~6
end
Note over Biz,UI: 1.5s 无操作(500ms 定时器发 MENU_MAIN ×3)
Biz->>UI: UI_menu(MENU_MAIN, arg) #1
UI->>Var: bMenuReturnCnt = 1 (未达阈值, ui_arg 保持 bCurArg)
Biz->>UI: UI_menu(MENU_MAIN, arg) #2
UI->>Var: bMenuReturnCnt = 2
Biz->>UI: UI_menu(MENU_MAIN, arg) #3
UI->>Drv: LED5X7_clear_icon()
UI->>Var: bCurMenu = bMainMenu; bCurArg = ui_arg
UI->>Drv: LED5X7_show_string_menu(bMainMenu) / 主界面刷新
菜单状态流转
stateDiagram-v2
[*] --> PowerUp: 上电 UI_init_api()
PowerUp --> MainMenu: MENU_POWER_UP 显示完毕
MainMenu --> SubMenu: UI_menu(子界面, arg) 且 menu != bMainMenu
SubMenu --> MainMenu: MENU_MAIN ×3 (bMenuReturnCnt == UI_RETURN)
MainMenu --> MainMenu: 刷新请求 (menu > 0x80)
SubMenu --> SubMenu: 刷新请求被丢弃 (bCurMenu != bMainMenu)
MainMenu --> InputNumber: KEY_IR_EN 且 MENU_INPUT_NUMBER
InputNumber --> MainMenu: MENU_MAIN ×3 → post_msg(MSG_INPUT_TIMEOUT)
MainMenu --> PowerDown: MENU_POWER_DOWN
PowerDown --> [*]
要点解读:
- 上电后
UI_init_api()调用LED5X7_init()完成驱动初始化(见 ui_api.c)。 - 主界面是"稳定态":只有
menu <= 0x80的显式切换才能离开;刷新请求(> 0x80)在任何时刻都不会改变bCurMenu。 - 子界面是"暂态":1.5 秒自动返回由
UI_RETURN = 3与外部 500ms 周期调用共同实现;返回时若处于红外数字输入态,会额外产生MSG_INPUT_TIMEOUT消息。
LED 5x7 驱动层
驱动头文件 led5x7_driver.h 定义了显示状态与硬件映射:
typedef struct _LED5X7_VAR {
u8 bCoordinateX; //<X 坐标
u8 bFlashChar; //<字符位闪烁
u8 bFlashIcon; //<图标闪烁
u8 bShowBuff[5]; //<显示缓存
u8 bBrightness; //<亮度控制
u8 bShowBuff1[8];
} LED5X7_VAR;
extern LED5X7_VAR LED5X7_var;
#define LED_STATUS LED5X7_var.bShowBuff[4]
缓存与图标位设计:
bShowBuff[5]是 5 位数字/字符显示缓存,bShowBuff[4]兼作LED_STATUS图标状态字节;- 8 个图标位
LED_PLAY/LED_PAUSE/LED_USB/LED_SD/LED_2POINT/LED_MHZ/LED_MP3/LED_FM对应BIT(0)~BIT(7)(见 led5x7_driver.h),例如播放中点亮LED_PLAY、暂停点亮LED_PAUSE、USB/SD 源分别点亮LED_USB/LED_SD; - 端口宏
LED_PORT0~LED_PORT6对应BIT(15)~BIT(9)(PA0-PA5/PA8 映射),LED_PORT_ALL用于一次性全灭/全亮(见 led5x7_driver.h)。
驱动的公共接口(声明于 led5x7_driver.h):
| 函数 | 职责 |
|---|---|
LED5X7_init() | 上电初始化(GPIO/扫描定时器) |
LED5X7_scan() | 周期扫描,把缓存刷新到 IO 端口 |
LED5X7_setX(u8 X) | 设置当前 X 坐标(光标) |
LED5X7_clear_icon() | 清除图标状态位 |
LED5X7_show_string_menu(u8 menu) | 按菜单 ID 显示字符串界面(开机/待机/PC/AUX) |
LED5X7_show_music_main(int arg) | 音乐主界面(曲目/时间,配合 LED_PLAY/LED_PAUSE 图标) |
LED5X7_show_filenumber(int arg) | 文件序号显示 |
LED5X7_show_volume() | 音量档位显示 |
LED5X7_show_dec_eq(u32 arg) / LED5X7_show_hw_eq() | 解码 EQ / 硬件 EQ 档位 |
LED5X7_show_IR_number() | 红外数字输入回显 |
LED5X7_show_playmode(int arg) | 播放模式(单曲/循环/随机等) |
set_LED_fade_out() / set_LED_all_on() | 亮度渐变 / 全亮(关机/提示场景) |
设计意图:驱动层只操作"缓存 + 图标位"这一中间表示,LED5X7_scan() 负责最终物理刷新。这样 UI 状态机(UI_menu_api)与硬件时序解耦——无论 LED 扫描周期如何,界面切换逻辑都不受影响;bFlashChar/bFlashIcon 还支持字符与图标独立闪烁(如待机时闪烁提示),由扫描任务按时间片翻转实现。
数字转换工具(ui_common)
ui_common.h 提供整数到 BCD/字形的转换工具(见 ui_common.h):
void itoa1(u8 i); // 1 位数字 → bcd_number
void itoa2(u8 i); // 2 位数字
void itoa3(u16 i); // 3 位数字
void itoa4(u16 i); // 4 位数字
extern const u8 asc_number[]; // 数字字形表
extern u8 bcd_number[5]; // 转换结果缓存
来源:ui_common.h
这些函数把数值拆成逐位数字写入 bcd_number[5] 供驱动查表显示,asc_number[] 是 ASCII 数字的字模表。播放时间(MENU_PLAY_TIME)、文件号(MENU_FILENUM)、音量(MENU_MAIN_VOL)等数值型界面均以此为基础渲染。
Configuration Options
UI 显示子系统的行为由编译期宏控制(无运行时可配置项,符合 MCU 固件"编译期裁剪"惯例):
| 宏/开关 | 类型 | 默认行为 | 作用 |
|---|---|---|---|
UI_ENABLE | 编译宏 | 取决于工程配置 | 总开关。为 0 时 UI_init/UI_menu/SET_UI_MAIN 全部展开为空操作(见 ui_api.h) |
LED_5X7 | 编译宏 | 1 | 是否启用 5x7 LED 驱动;UI_init_api() 内据此决定是否调用 LED5X7_init() |
KEY_IR_EN | 编译宏 | 取决于工程 | 使能红外按键与 MENU_INPUT_NUMBER 数字输入界面,并启用 MSG_INPUT_TIMEOUT 超时消息 |
USB_DEVICE_EN | 编译宏 | 取决于工程 | 使能 PC 联机界面(MENU_PC_MAIN/MENU_PC_VOL_UP/MENU_PC_VOL_DOWN) |
AUDIO_HW_EQ_EN / PCM_SW_EQ_EN | 编译宏 | 取决于工程 | 任一使能则编译 MENU_HW_EQ 界面(硬件 EQ) |
UI_RETURN | 常量宏 | 3 | 自动返回计数阈值,配合 500ms 周期约为 1.5s;改大可延长子界面停留时间 |
LED_PORT0~6 / LED_STATUS | 端口/状态宏 | 见驱动头文件 | 硬件 IO 映射与图标位定义,板级适配时修改 |
API Reference
void UI_menu_api(u8 menu, int arg)
UI 显示入口函数,由宏 UI_menu(x, y) 包装调用(见 ui_api.h)。
- 参数:
menu— 界面 ID(MENU_*枚举值);arg— 界面参数(曲目号、EQ 档位、播放模式等,非数值界面可传 0)。 - 行为:过滤刷新请求 → 维护
UI_var→ 分发到对应LED5X7_show_*()。 - 边界:
menu == MENU_MAIN(0xFF)触发自动返回计数;menu > 0x80仅在主界面生效;未知menu落入default分支静默忽略。
void UI_init_api(void)
初始化 UI 子系统(见 ui_api.c)。
- 行为:
LED_5X7使能时调用LED5X7_init()初始化 LED 驱动。 - 调用时机:系统上电早期,通过宏
UI_init()使用。
void SET_UI_MAIN(x)(宏)
设置主界面 UI_var.bMainMenu = x。主界面是自动返回的目标与刷新界面的生效前提,业务层在模式切换(如 USB→AUX→FM)时应同步更新。
LED 驱动接口
LED5X7_show_string_menu(u8 menu)、LED5X7_show_volume()、LED5X7_show_music_main(int arg)、LED5X7_show_filenumber(int arg)、LED5X7_show_dec_eq(u32 arg)、LED5X7_show_hw_eq()、LED5X7_show_IR_number()、LED5X7_show_playmode(int arg) 均由 UI_menu_api() 在 switch 分发中调用;LED5X7_scan() 由定时任务周期调用完成物理刷新。全部声明见 led5x7_driver.h。
Failure Modes、边界与并发
自动返回竞态
bMenuReturnCnt 的递增完全依赖外部周期性调用 UI_menu(MENU_MAIN, ...)。若 500ms 定时器停止或业务层不再发送 MENU_MAIN,子界面将永久停留——系统依赖"总有刷新源"这一隐含约定。新增业务模块时需保证主循环/定时器持续驱动 UI_menu(MENU_MAIN)。
刷新参数保护
自动返回计数期间(bMenuReturnCnt < UI_RETURN),代码用 ui_arg = UI_var.bCurArg 覆盖外部传入参数(见 ui_api.c),防止返回过程中的中间参数污染显示;达到阈值时则以本次 ui_arg 作为主界面参数。这是"谁最后写谁生效"的显示一致性设计。
主界面去重
bCurMenu == bMainMenu 时再次收到 MENU_MAIN 会直接 return(见 ui_api.c),避免 500ms 一次的重复清屏/重绘导致闪烁。刷新类请求(> 0x80)同样在非主界面时被丢弃。
红外输入超时衔接
MENU_INPUT_NUMBER 界面自动返回时通过 post_msg(1, MSG_INPUT_TIMEOUT) 把 UI 事件转为消息(见 ui_api.c),业务层据此清空输入缓冲。若 KEY_IR_EN 关闭,该路径不编译,输入回显界面也不存在。
编译开关不一致风险
ui_api.c 的 switch 中大量 case 受宏保护(USB_DEVICE_EN、AUDIO_HW_EQ_EN、KEY_IR_EN、#if 0 的 FM/RTC)。若业务层调用了未编译的菜单 ID,bCurMenu 会被设置但 switch 落入 default——界面"空白"。新增界面时必须同步修改枚举、分发 switch 与对应 LED5X7_show_xxx() 三处。
并发/中断模型
UI_var 与 LED5X7_var 均为单实例全局结构体(#pragma bss_seg(".led_ui.data.bss") 置于专用段,见 ui_api.c),状态读写发生在单线程主循环/定时器上下文中;LED5X7_scan() 若运行在中断上下文,与主循环对 bShowBuff 的写操作之间需保证临界区或一次性写入,避免扫描到半更新缓存产生残影。
Performance & Operational Notes
- 显示刷新代价极低:界面切换只做缓存写入(
bShowBuff[5]与图标位),物理刷新由LED5X7_scan()异步完成,UI_menu_api()不是热路径,可在定时器/消息上下文安全调用。 - 图标状态位共享:
LED_STATUS与第 5 位字符共用bShowBuff[4],显示 5 位数字与图标点亮互斥的场景需要驱动层保证位域布局(LED_PLAY等为BIT(0)~BIT(7),字符字形则占用整字节)。修改字形表或图标位时注意位冲突。 - 编译期裁剪降低体积:FM/RTC 界面以
#if 0关闭、EQ/红外界面按宏裁剪,未使用的LED5X7_show_*()会随链接器消除,帮助控制固件体积。 - 段属性:UI 数据/代码被放入
.led_ui.*专用段(见 ui_api.c),便于在链接脚本中单独放置(如常驻内存/低功耗保持区域),板级移植时不要随意移除这些 pragma。
Extension Points
- 新增界面(推荐路径):在
ui_api.h枚举中追加MENU_XXX(普通界面取值 ≤ 0x7F,刷新类 > 0x80)→ 在led5x7_driver.h声明并实现LED5X7_show_xxx()→ 在ui_api.c的 switch 中注册 case。三处修改后即可被业务层以UI_menu(MENU_XXX, arg)调用。 - 调整自动返回时长:修改
UI_RETURN(当前 3 × 500ms ≈ 1.5s)或改变外部MENU_MAIN的发送周期。 - 硬件适配:更换点阵屏/数码管时,只改
led5x7_driver.c内部实现与LED_PORT0~6映射,UI_menu_api()与所有MENU_*接口保持不变。 - 增加图标:在
led5x7_driver.h的LED_STATUS位域(BIT(0)~BIT(7))中申请新位并定义宏,在LED5X7_clear_icon()/扫描逻辑中同步维护。 - 输入超时联动:红外数字输入场景中,可通过
post_msg(MSG_INPUT_TIMEOUT)的既有机制,把自动返回事件接入任意消息驱动的业务逻辑。
Related Links
- ui_api.h — 菜单枚举与 API 定义
- ui_api.c — 状态机核心实现
- led5x7_driver.h — LED 驱动接口与图标/端口定义
- led5x7_driver.c — LED 驱动实现(扫描与字形)
- ui_common.h — 数字转换工具接口
- ui_common.c — 数字转换工具实现
- 相关子系统:按键/红外输入处理、音乐播放状态机(
music_play/play_file)、音量与 EQ 控制——这些模块是UI_menu()的主要调用方,其事件到界面的映射可参考本页 Core Flow 部分。