杰理 SDK 文档中心
首页
首页
  • 项目概览

    • SDK 简介与核心特性
    • 芯片平台与硬件资料
    • SDK 版本与发布信息
  • 快速开始

    • 环境搭建与工具链
    • 编译工程
    • 烧录与量产工具
  • 工程结构与构建系统

    • 工程目录布局
    • 构建与链接配置
  • 应用层开发

    • mbox_flash 应用框架
    • 板级支持包 (BSP)
    • 公共应用模块
    • UI 显示子系统
  • 蓝牙子系统

    • BLE 控制器、链路层与 HCI 传输
    • GATT 服务框架
    • BLE 应用示例:遥控器 / Dongle / 对讲机
    • 经典蓝牙支持
  • 音频子系统

    • 音频编解码器
    • 音频设备接口 (DAC / ADC / APA)
    • 音效处理与 EQ
    • 播放、录音与 MIO 工作流
  • 设备与文件系统

    • 存储设备驱动 (NorFlash / SDMMC / USB)
    • 文件系统 (FAT / nor_fs / SYDF)
    • 设备管理框架 (dev_mg)
  • 系统服务与电源管理

    • 消息机制 (msg / hot_msg)
    • 配置与参数存储 (app_config / VM)
    • 电源管理 (SOFT OFF / POWER DOWN)
  • 固件升级

    • 升级框架总览 (code_v1 / code_v2)
    • 双 Bank 升级机制
    • 升级通道:UART / 测试盒 / BLE OTA / USB / SD
  • 补丁包与版本维护

    • 版本升级补丁链 (v1.1.0 → v1.4.0)
    • 问题修复补丁
    • 固件裁剪与资源优化
  • 开发工具与支持

    • 辅助工具与脚本
    • 文档、配置说明与常见问题

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 显示是用户感知系统状态的唯一窗口。该子系统采用菜单-状态机架构:

  1. 业务模块(播放器、EQ、音量等)不直接操作 LED 硬件,而是调用 UI_menu(menu, arg) 请求显示某个界面;
  2. UI_menu_api() 维护全局 UI_var(当前菜单、主菜单、返回计数器、参数),决定是否接受该请求、是否需要清图标、是否启动自动返回计时;
  3. 最终统一分发到 LED5X7_* 系列显示函数,把语义化的菜单 ID 翻译为具体的字形、数字或图标;
  4. 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

设计意图与三条关键路径:

  1. 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 行为"与"业务超时处理"通过消息机制衔接。
  2. 刷新请求路径(menu > 0x80):只允许在主界面上生效。例如"在主界面刷新播放时间",当用户停留在某个子界面(如 EQ 设置)时,周期刷新请求被丢弃,保证子界面不被抢占。
  3. 普通界面切换路径(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]

来源:led5x7_driver.h

缓存与图标位设计:

  • 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

  1. 新增界面(推荐路径):在 ui_api.h 枚举中追加 MENU_XXX(普通界面取值 ≤ 0x7F,刷新类 > 0x80)→ 在 led5x7_driver.h 声明并实现 LED5X7_show_xxx() → 在 ui_api.c 的 switch 中注册 case。三处修改后即可被业务层以 UI_menu(MENU_XXX, arg) 调用。
  2. 调整自动返回时长:修改 UI_RETURN(当前 3 × 500ms ≈ 1.5s)或改变外部 MENU_MAIN 的发送周期。
  3. 硬件适配:更换点阵屏/数码管时,只改 led5x7_driver.c 内部实现与 LED_PORT0~6 映射,UI_menu_api() 与所有 MENU_* 接口保持不变。
  4. 增加图标:在 led5x7_driver.h 的 LED_STATUS 位域(BIT(0)~BIT(7))中申请新位并定义宏,在 LED5X7_clear_icon()/扫描逻辑中同步维护。
  5. 输入超时联动:红外数字输入场景中,可通过 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 部分。
Prev
公共应用模块