杰理 SDK 文档中心
首页
首页
  • SDK 概述与入门

    • SDK 总览
    • 支持芯片与蓝牙认证
    • 工程结构导航
  • 开发环境与构建

    • 环境搭建与工具链安装
    • 编译指南与工程选择
    • 烧录与生产工具
  • BLE 透传/数传应用

    • 透传应用框架与处理模块
    • 透传与数传示例
    • 多连接与自定义服务示例
    • FindMy 与查找网络示例
  • HID 人机交互应用

    • 键盘与按键设备示例
    • 鼠标设备示例
    • 遥控器示例
    • HID 蓝牙应用模块
  • 公共 BSP 模块

    • 按键、编码器与红外输入
    • 传感器驱动
    • LED 与显示控制
    • 串口与 USB 通信
    • 存储、参数与时钟
    • 电源与温度管理
    • 消息、内存与系统配置
    • OTA 升级框架
  • 蓝牙协议栈与库

    • BLE 控制器与协议栈适配
    • 经典蓝牙 BR/EDR 支持
    • 第三方蓝牙协议
    • 设备管理框架
    • DUT 测试与射频认证
  • 构建系统与开发工具

    • Makefile 构建系统
    • 固件后处理与配置工具
    • 辅助脚本与库合并
  • 文档与硬件资料

    • AT 命令参考
    • 硬件参考资料
    • SDK 文档与在线资源

HID 蓝牙应用模块

AW33N BLE SDK 中基于 BLE HID over GATT Profile(HOGP)实现 HID 设备应用的完整模块,覆盖键盘、鼠标、遥控器等产品形态,包含应用入口、分支选择、状态机调度、HID GATT 服务定义与板级配置。

Purpose and Scope

本页介绍 apps/demo/hid 工程所构成的 HID 蓝牙应用模块,内容包括:

  • 应用入口 app_main() 的启动流程与资源分配(栈、堆、蓝牙 RAM、中断优先级)
  • 基于编译宏的应用分支选择机制(键盘 / 鼠标 / 遥控器 / keyfob / keypage / idle)
  • 应用状态机与系统事件分发机制(list_for_each_app_main + state_machine + event_handler)
  • BLE HID GATT 服务的 UUID 定义(standard_hid.h,HID Service 0x1812 及其特征)
  • 板级配置目录结构与多种产品形态(demo / mouse / mouse_m143 / mouse_single / rc)
  • 与 HID 相关的 USB 设备类和 RCSP HID 交互模块的边界说明

USB HID 设备类(usb/device/hid_keyboard.c 等)与 RCSP(Jieli 私有遥控协议)本身属于独立能力,本页仅说明它们与 BLE HID 应用的关系;各自的详细实现请参见对应的 USB 设备章节与 RCSP 章节。BLE 协议栈底层的 GATT 服务注册、连接管理与 SMP 配对不在本页展开。

Overview

什么是 HID over BLE(HOGP)

HID(Human Interface Device,人机交互设备)是传统 USB 的设备类别,用于键盘、鼠标、遥控器等输入设备。BLE 通过 HID over GATT Profile(HOGP) 将 HID 报告(Report)承载在 GATT 服务之上,使低功耗蓝牙设备也能以标准 HID 报告格式向主机(手机、PC、平板)上报按键、指针位移和媒体控制事件。

在 AW33N SDK 中,HID 蓝牙应用模块位于 apps/demo/hid/,其核心设计是:

  1. 单工程多形态:同一套 app_main.c 通过 CONFIG_APP_* 编译宏切换出 hid_key、hid_rc、mouse_dual、mouse_low_latency、keyfob、keypage 等不同应用,避免为每种产品维护独立工程;
  2. 应用框架驱动:应用模块通过 struct application + struct application_operation(含 state_machine 与 event_handler 回调)注册到全局链表,由 main_application_operation_state 按名称查找并驱动;
  3. 标准 GATT 服务:standard_hid.h 集中定义 HID Service(0x1812)及其特征/描述符 UUID,供 BLE 协议栈注册标准 HOGP 服务使用。

模块组成

组成部分路径职责
应用入口apps/demo/hid/app_main.c系统启动、资源分配、应用分支选择、状态机/事件分发
HID GATT 定义apps/demo/hid/include/standard_hid.hBLE HID 服务与特征 UUID 宏
板级配置apps/demo/hid/board/bd57/外设、BLE 参数、产品形态配置(demo/mouse/rc)
工程文件apps/demo/hid/board/bd57/AW33N_hid.cbpCode::Blocks 工程描述
USB HID 类(相关)apps/app/bsp/common/usb/device/hid_*.cUSB 侧 HID 设备类,与 BLE HID 共用报告概念
RCSP HID(相关)apps/app/bsp/common/third_party_profile/jieli/JL_rcsp/rcsp_hid/Jieli 私有遥控协议对 HID 的封装

Architecture

下图展示了 HID 蓝牙应用模块的整体架构与数据/控制流:

flowchart TD
    subgraph sg_Board["板级配置 board/bd57"]
        BoardCfg["board_aw33n_*_cfg.h<br/>外设与BLE参数"]
        BoardInit["board_aw33n_*.c<br/>板级初始化"]
        Modules["app_modules.h<br/>模块注册"]
    end

    subgraph sg_App["应用层 apps/demo/hid"]
        Main["app_main()<br/>启动入口"]
        Branch{"CONFIG_APP_*<br/>分支选择"}
        HidKey["hid_key<br/>ACTION_HID_MAIN"]
        HidRc["hid_rc<br/>ACTION_REMOTE_CONTROL"]
        Mouse["mouse_dual / mouse_low_latency<br/>ACTION_MOUSE_MAIN"]
        OtherApp["keyfob / keypage / idle"]
        SM["state_machine 状态机<br/>list_for_each_app_main"]
        Event["event_handler<br/>系统事件分发"]
    end

    subgraph sg_Profile["BLE HID 协议层"]
        StdHid["standard_hid.h<br/>HID GATT UUID"]
        Gatt["BLE 协议栈 GATT<br/>HOGP 服务"]
    end

    subgraph sg_Related["相关能力(独立页面)"]
        UsbHid["USB HID 设备类<br/>hid_keyboard / hid_mouse / hid_media"]
        RcspHid["RCSP HID<br/>rcsp_hid_inter"]
    end

    Main --> Branch
    Branch --> HidKey
    Branch --> HidRc
    Branch --> Mouse
    Branch --> OtherApp
    HidKey --> SM
    HidRc --> SM
    Mouse --> SM
    OtherApp --> SM
    SM --> Event
    SM --> StdHid
    StdHid --> Gatt
    BoardCfg --> Main
    BoardInit --> Main
    Modules --> SM
    Gatt -.->|"HID 报告上报"| UsbHid
    RcspHid -.->|"私有协议封装"| HidRc

架构说明:

  • 应用层(apps/demo/hid):app_main() 是唯一入口,先完成更新检查、电源检测,再通过 main_app_get_name() 根据 CONFIG_APP_* 编译宏确定要运行的应用名称,最后由 main_application_operation_state() 在已注册的应用链表中查找匹配项并调用其 state_machine 回调。这一"按名查找 + 状态机驱动"的模式与 Android Intent/Activity 思想类似:应用名相当于 Intent 的 name,struct intent 携带 action,框架负责路由。
  • 板级配置(board/bd57):每个产品形态(demo、mouse、mouse_m143、mouse_single、rc)都有独立的 board_aw33n_*_cfg.h 与 board_aw33n_*.c,用于配置外设(按键、LED、传感器)与 BLE 参数;app_modules.h 负责向框架注册应用模块。
  • 协议层:standard_hid.h 提供标准 HOGP 服务所需的全部 UUID 宏,协议栈据此在 GATT 上暴露 HID Service、Report Map、Report、Protocol Mode 等特征。
  • 相关能力:USB HID 类与 BLE HID 在"报告"概念上同源(同为 HID Report 描述符),但走完全不同的传输路径;RCSP HID 则是 Jieli 私有遥控协议对 HID 操作的封装,仅与遥控器应用(hid_rc)相关。

启动流程与资源分配

内存布局定义

HID 应用工程在 app_main.c 顶部通过段属性(sec_used)静态分配系统关键内存,这部分直接决定了 SDK 可用的堆栈与蓝牙 RAM 容量:

//*********************************************************************************//
//                sdk 堆栈、堆、蓝牙ram的分配定义                                  //
//*********************************************************************************//
int _sstack_top[1] sec_used(.sstack_top);//fixed
int _ustack_top[1] sec_used(.ustack_top);//fixed

//for ld.c link
static int _sstack_space[SYS_STACK_SIZE_ALL / 4] sec_used(.sstack);
static int _ustack_space[USR_STACK_SIZE_ALL / 4] sec_used(.ustack);

static int _sys_heap_space[SYS_HEAP_SIZE / 4] sec_used(.sec_sys_heap);//最少占用
static int _bt_nk_ram_min[BT_NK_RAM_SIZE_ALL / 4] sec_used(.sec_bt_nk_ram);//最少占用
static int _bt_nv_ram_min[BT_NV_RAM_SIZE_ALL / 4] sec_used(.sec_bt_nv_ram);//最少占用

来源:app_main.c

设计意图:SYS_STACK_SIZE_ALL、USR_STACK_SIZE_ALL、SYS_HEAP_SIZE、BT_NK_RAM_SIZE_ALL、BT_NV_RAM_SIZE_ALL 等宏由板级配置(如 board_aw33n_demo_cfg.h)或全局构建配置定义。NK RAM 与 NV RAM 是蓝牙协议栈的两类内存池(NK 为常驻 RAM,NV 为可掉电保存区),app_main() 启动时会打印它们的起始地址与大小(nk_malloc/nv_malloc 日志),用于联调时确认内存分配是否合理。这些段名(.sstack、.ustack、.sec_sys_heap、.sec_bt_nk_ram、.sec_bt_nv_ram)必须与链接脚本(ld)中的段定义一致,改动内存大小时需同步检查 ld 文件。

中断优先级配置

BLE 协议栈对实时性敏感,app_main.c 集中定义了 BT 相关中断的优先级,这是 HID 低延迟上报(尤其鼠标)性能的基础:

//BT
const int IRQ_BT_TIMEBASE_IP  = 6;   //BT TIMEBASE
const int IRQ_BLE_EVENT_IP    = 5;   //BT RX_EVT
const int IRQ_BLE_RX_IP       = 5;   //BT RX
const int IRQ_BTSTACK_MSG_IP  = 4;   //BT STACK
const int IRQ_BREDR_IP        = 3;   //no use
const int IRQ_BT_RXMCH_IP     = 3;   //no use
const int IRQ_AES_IP          = 3;   //aes

来源:app_main.c

设计意图:BLE 接收事件(IRQ_BLE_EVENT_IP/IRQ_BLE_RX_IP,优先级 5)高于协议栈消息(IRQ_BTSTACK_MSG_IP,优先级 4),确保射频数据不被协议栈处理延迟阻塞;IRQ_BREDR_IP/IRQ_BT_RXMCH_IP 标注 "no use",因为该工程是纯 BLE 应用,经典蓝牙(BR/EDR)链路被关闭。HID 上报的端到端时延与这条中断链的响应速度直接相关。

启动入口 app_main()

app_main() 是整个 HID 应用的 C 入口,顺序执行:OTA 升级状态检查 → 开机键检测 → 低压检测 → 进入应用状态机:

void app_main()
{
#if UPDATE_V2_EN
    /* 初始化检查升级状态, 测试盒升级后要关机, APP-OTA升级后要开机*/
    update_success_boot_check();
#endif

    log_info(">>>>>>>>>>>>>>>>>>app_main...\n");
    log_info("nk_malloc: %08x,%04x, nv_malloc: %08x,%04x", NK_RAM_MALLOC_START_ADDR, NK_RAM_MALLOC_SIZE,
             NV_RAM_MALLOC_START_ADDR, NV_RAM_MALLOC_SIZE);

#if TCFG_POWER_ON_NEED_KEY
    check_power_on_key();
#endif

#if TCFG_SYS_LVD_EN
    app_power_vbat_check();
#endif

    main_application_operation_state(NULL, APP_STA_START);
}

来源:app_main.c

关键点:

  • update_success_boot_check():区分"测试盒升级后关机"与"APP-OTA 升级后开机"两种升级收尾行为,避免升级成功后错误掉电;
  • check_power_on_key():当 TCFG_POWER_ON_NEED_KEY 使能时,开机后需持续按住电源键等待开机确认,若按键松开则进入软关机(power_set_soft_poweroff),防止误上电;
  • app_power_vbat_check():TCFG_SYS_LVD_EN 使能时做低电压检测,电压不足直接保护关机;
  • 最后以 APP_STA_START 状态调用 main_application_operation_state() 启动所选应用的状态机。

应用分支选择机制

HID 模块最大的设计特点是一份入口代码,多产品形态。main_app_get_name() 依据编译宏决定运行哪个应用:

static void main_app_get_name(struct intent *it)
{
    init_intent(it);
    // 选择应用分支
#if(CONFIG_APP_KEYBOARD)
    it->name = "hid_key";
    it->action = ACTION_HID_MAIN;

#elif(CONFIG_APP_KEYFOB)
    it->name = "keyfob";
    it->action = ACTION_KEYFOB;

#elif(CONFIG_APP_KEYPAGE)
    it->name = "keypage";
    it->action = ACTION_KEYPAGE;

#elif(CONFIG_APP_REMOTE_CONTROL)
    it->name = "hid_rc";
    it->action = ACTION_REMOTE_CONTROL;

#elif(CONFIG_APP_MOUSE_DUAL)
    it->name = "mouse_dual";
    it->action = ACTION_MOUSE_MAIN;

#elif(CONFIG_APP_MOUSE_LOW_LATENCY)
    it->name = "mouse_low_latency";
    it->action = ACTION_MOUSE_MAIN;

#elif(CONFIG_APP_IDLE)
    it->name = "idle";
    it->action = ACTION_IDLE_MAIN;

#else
    ASSERT(0, "no app!!!");
#endif
}

来源:app_main.c

设计意图:

  • 编译期裁剪:CONFIG_APP_* 是编译宏,未被选择的代码路径根本不会链接进固件,天然实现代码裁剪与 ROM/RAM 节省;
  • Intent 建模:struct intent 携带 name(应用标识)与 action(行为意图),是 SDK 应用框架的"启动参数"对象;ACTION_HID_MAIN 表示键盘主界面,ACTION_MOUSE_MAIN 表示鼠标主界面,action 可在应用内部驱动界面/功能切换;
  • 强制唯一性:#if / #elif 链保证同一时间只有一个应用被选择,#else ASSERT(0, "no app!!!") 在没有配置任何应用时于编译/运行期报错,防止"空工程"上线。

状态机与事件分发框架

应用注册与查找

main_application_operation_state() 遍历框架维护的应用链表,按名称匹配后调用其 state_machine 回调:

static struct application *main_application_operation_state(struct application *app, enum app_state state)
{
    struct intent it;
    const struct  application *dev = NULL;

    main_app_get_name(&it);

    log_info("run app>>> %s", it.name);

    list_for_each_app_main(dev) {
        if (memcmp(dev->name, it.name, strlen(it.name)) == 0) {
            if (dev->ops) {
                dev->ops->state_machine(app, state, &it);
            }
        } else {
            log_info("no app run");
        }
    }

    return NULL;
}

来源:app_main.c

设计意图:list_for_each_app_main(dev) 遍历由 app_modules.h(板级目录下)注册的 struct application 链表。每个应用实现 struct application_operation,其中 state_machine 负责状态迁移(如 APP_STA_START → 初始化 → 运行),event_handler 负责处理外部系统事件。名称匹配使用 memcmp + strlen 而非 strcmp,属于嵌入式风格的长度受限比较。

系统事件分发

蓝牙协议栈、按键、电源等模块产生的事件通过消息队列投递到应用层,由 main_sys_event_msg_handle() 统一分发:

void main_sys_event_msg_handle(int *msg)
{
    // putchar('$');
    struct sys_event *event_ptr = (struct sys_event *)msg[1];
    const struct application_operation *ops_ptr = (const struct application_operation *)msg[2];
    ops_ptr->event_handler(NULL, event_ptr);
    event_pool_free(event_ptr);
}

来源:app_main.c

设计意图:msg[1] 是指向 struct sys_event 的指针(事件负载),msg[2] 指向目标应用的 application_operation(回调表)。分发后必须调用 event_pool_free(event_ptr) 归还事件池内存——这是典型的内存池复用设计,避免高频事件(按键、BLE 连接状态变化)导致的堆碎片与泄漏。所有系统事件都走这条统一管道,应用无需关心事件来源是 BLE、按键还是电源。

BLE HID GATT 服务定义

standard_hid.h 是 BLE HID 应用与协议栈之间的"契约"文件,集中定义 HOGP 标准服务所需的全部 16 位 UUID:

/*******************************************************************/
/*
 *-------------------hid for ble
 */
#define HID_UUID_16                                 0x1812
#define HID_INFORMATION_UUID_16                     0x2A4A
#define HID_REPORT_MAP_UUID_16                      0x2A4B
#define HID_CONTROL_POINT_UUID_16                   0x2A4C
#define HID_REPORT_UUID_16                          0x2A4D
#define PROTOCOL_MODE_UUID_16       				0x2A4E
#define HID_REPORT_REFERENCE_UUID_16                0x2908

来源:standard_hid.h

这些 UUID 与蓝牙 SIG 标准 HID Service(0x1812)完全一致,各特征用途如下:

UUID宏名角色说明
0x1812HID_UUID_16服务HID Service,HOGP 的 GATT 服务根
0x2A4AHID_INFORMATION_UUID_16特征HID Information:报告 HID 版本、国家码、标志位
0x2A4BHID_REPORT_MAP_UUID_16特征Report Map:HID 报告描述符(决定按键/指针的报文格式)
0x2A4CHID_CONTROL_POINT_UUID_16特征Control Point:主机下发 Suspend/Exit Suspend 控制
0x2A4DHID_REPORT_UUID_16特征Report:承载输入/输出/特征报告,键盘按键、鼠标位移都经此上报
0x2A4EPROTOCOL_MODE_UUID_16特征Protocol Mode:Boot Protocol / Report Protocol 切换
0x2908HID_REPORT_REFERENCE_UUID_16描述符Report Reference:关联 Report 特征与报告 ID/类型

设计意图:将 UUID 与业务代码解耦。应用/协议栈代码引用宏名而非裸数字,既避免魔法数,也便于跨工程复用——apps/demo/transfer/include/standard_hid.h 存在同一文件,说明该定义被多个 demo 工程共享。头文件中 USB HID 的包含被注释(// #include "usb/device/hid.h"),表明 BLE HID 应用刻意不依赖 USB 设备栈,两者通过各自的协议栈独立工作,仅在报告内容层面保持一致。

板级配置与产品形态

apps/demo/hid/board/bd57/ 是 HID 模块的板级支持目录,采用"一板多配置"策略,每个产品形态对应一组 board_aw33n_*_cfg.h + board_aw33n_*.c:

配置组文件目标产品
demoboard_aw33n_demo.c / board_aw33n_demo_cfg.h通用 HID demo 板
mouseboard_aw33n_mouse.c / board_aw33n_mouse_cfg.h双模鼠标(对应 mouse_dual)
mouse_m143board_aw33n_mouse_m143.c / board_aw33n_mouse_m143_cfg.hM143 传感器鼠标
mouse_singleboard_aw33n_mouse_single.c / board_aw33n_mouse_single_cfg.h单连接鼠标(对应 mouse_low_latency)
rcboard_aw33n_rc.c / board_aw33n_rc_cfg.h遥控器(对应 hid_rc)

配套文件还包括:

  • board_config.h:板级总配置入口;
  • app_modules.h:向应用框架注册应用模块(list_for_each_app_main 的数据来源);
  • AW33N_hid.cbp:Code::Blocks 工程文件,IDE 构建入口;
  • Makefile:命令行构建脚本;
  • board_aw33n_*_global_build_cfg.h:全局构建开关(如内存大小、协议栈裁剪宏)。

设计意图:把"产品差异"全部收敛到 board/bd57 一层,应用层 app_main.c 与 standard_hid.h 对产品差异无感知。新增一款 HID 产品时,只需在 board 目录新增一组 cfg/c 文件,并在 main_app_get_name() 的分支链中补充一个 CONFIG_APP_* 分支,应用框架代码无需改动。这是典型的"平台 + 产品配置"分层思想。

与 USB HID、RCSP HID 的关系

BLE HID 应用模块并非孤立存在,SDK 中还有两处 HID 相关能力:

  • USB HID 设备类(apps/app/bsp/common/usb/device/hid_keyboard.c、hid_mouse.c、hid_media.c、hid_no_ep.c、custom_hid.c):实现 USB 侧的 HID 键盘/鼠标/媒体类设备,用于有线模式或双模(BLE + USB)产品。BLE HID 与 USB HID 共享"HID 报告"这一抽象(Report Map、报告 ID、Boot Protocol 语义),但传输路径完全独立:USB 走端点中断传输,BLE 走 GATT Notification/Write。双模鼠标(mouse_dual)正是同时挂接这两条路径的产品形态。
  • RCSP HID(apps/app/bsp/common/third_party_profile/jieli/JL_rcsp/rcsp_hid/rcsp_hid_inter.c/.h):Jieli 私有 RCSP(Remote Control Serial Protocol)协议对 HID 功能的封装,供 hid_rc 遥控器应用通过私有协议实现按键透传/自定义命令,属于厂商私有通道,不依赖标准 HOGP。

本页不展开这两部分的内部实现,详细内容请参见对应的 USB 设备模块与 RCSP 模块页面。

核心流程

下图描述 HID 蓝牙应用从开机到事件处理的完整时序:

sequenceDiagram
    participant Boot as 复位/引导
    participant Main as app_main()
    participant Cfg as 板级配置宏
    participant SM as 应用状态机
    participant App as HID 应用模块
    participant Stack as BLE 协议栈
    participant Host as 手机/PC 主机

    Boot->>Main: 上电进入 app_main()
    Main->>Main: update_success_boot_check()<br/>OTA 升级收尾处理
    Main->>Main: check_power_on_key() / app_power_vbat_check()
    Main->>Cfg: 读取 CONFIG_APP_* 确定分支
    Main->>SM: main_application_operation_state(NULL, APP_STA_START)
    SM->>SM: list_for_each_app_main 查找 name 匹配
    SM->>App: dev->ops->state_machine(app, state, &it)
    App->>Stack: 注册 HID GATT 服务(0x1812)<br/>基于 standard_hid.h UUID
    App-->>Host: 广播 / 等待连接
    Host->>Stack: 连接建立
    Stack->>App: BLE 连接事件 -> sys_event
    App->>SM: event_handler 处理连接事件
    App->>App: 按键/位移 -> 组装 HID Report
    App->>Stack: Report 特征 Notification (0x2A4D)
    Stack-->>Host: GATT 通知上报

关键时序要点:

  1. 启动前置检查:升级检查、开机键、低压检测都在进入应用状态机前完成,保证应用启动时电源与固件状态安全;
  2. 按名路由:状态机通过 list_for_each_app_main 查找与 it.name 匹配的应用,未命中则打印 "no app run";
  3. 事件驱动上报:HID 应用不是轮询上报,而是由 BLE 连接事件、按键事件驱动;按键/位移数据组装成 HID Report 后经 HID_REPORT_UUID_16(0x2A4D)特征以 GATT Notification 上报,主机端无需轮询,符合 BLE 低功耗设计。

使用示例

示例 1:选择 HID 键盘应用分支

在板级配置(如 board_aw33n_demo_cfg.h)中使能 CONFIG_APP_KEYBOARD 后,main_app_get_name() 会选中 hid_key 应用:

#if(CONFIG_APP_KEYBOARD)
    it->name = "hid_key";
    it->action = ACTION_HID_MAIN;
#elif(CONFIG_APP_REMOTE_CONTROL)
    it->name = "hid_rc";
    it->action = ACTION_REMOTE_CONTROL;
#elif(CONFIG_APP_MOUSE_DUAL)
    it->name = "mouse_dual";
    it->action = ACTION_MOUSE_MAIN;
#endif

来源:app_main.c

这段代码展示了模块的扩展方式:新增产品时在 #elif 链末尾追加分支,为 it 指定新的 name 与 action,并在 app_modules.h 中注册同名应用模块。

示例 2:BLE HID 服务 UUID 引用

HID 应用或协议栈服务注册代码引用 standard_hid.h 中的宏,保证与蓝牙 SIG 标准一致:

#define HID_UUID_16                                 0x1812
#define HID_INFORMATION_UUID_16                     0x2A4A
#define HID_REPORT_MAP_UUID_16                      0x2A4B
#define HID_CONTROL_POINT_UUID_16                   0x2A4C
#define HID_REPORT_UUID_16                          0x2A4D
#define PROTOCOL_MODE_UUID_16       				0x2A4E
#define HID_REPORT_REFERENCE_UUID_16                0x2908

来源:standard_hid.h

示例 3:启动状态机驱动应用

在 app_main() 末尾以 APP_STA_START 启动所选应用,应用模块通过回调表完成初始化与运行:

main_application_operation_state(NULL, APP_STA_START);

来源:app_main.c

配置选项

应用分支宏(编译期)

宏选择的应用action说明
CONFIG_APP_KEYBOARDhid_keyACTION_HID_MAINBLE 键盘
CONFIG_APP_KEYFOBkeyfobACTION_KEYFOB车钥匙/防丢器
CONFIG_APP_KEYPAGEkeypageACTION_KEYPAGE翻页笔/演示笔
CONFIG_APP_REMOTE_CONTROLhid_rcACTION_REMOTE_CONTROLBLE 遥控器(可配合 RCSP)
CONFIG_APP_MOUSE_DUALmouse_dualACTION_MOUSE_MAIN双模(BLE+USB)鼠标
CONFIG_APP_MOUSE_LOW_LATENCYmouse_low_latencyACTION_MOUSE_MAIN低延迟单连接鼠标
CONFIG_APP_IDLEidleACTION_IDLE_MAIN空闲/无功能应用

来源:app_main.c

系统级开关

宏类型默认行为说明
TCFG_POWER_ON_NEED_KEY布尔关闭开机需长按电源键确认,松开进入软关机
TCFG_SYS_LVD_EN布尔关闭使能系统低电压检测(app_power_vbat_check)
UPDATE_V2_EN布尔—使能 V2 升级流程与开机升级状态检查

来源:app_main.c

BT 中断优先级

中断优先级说明
IRQ_BT_TIMEBASE_IP6BT 时基
IRQ_BLE_EVENT_IP5BLE RX 事件
IRQ_BLE_RX_IP5BLE 接收
IRQ_BTSTACK_MSG_IP4蓝牙协议栈消息
IRQ_BREDR_IP3经典蓝牙(本工程未使用)
IRQ_BT_RXMCH_IP3未使用
IRQ_AES_IP3AES 硬件加速

来源:app_main.c

内存段(由板级/全局配置宏决定大小)

段宏用途
.sstackSYS_STACK_SIZE_ALL系统栈
.ustackUSR_STACK_SIZE_ALL用户栈
.sec_sys_heapSYS_HEAP_SIZE系统堆
.sec_bt_nk_ramBT_NK_RAM_SIZE_ALL蓝牙 NK RAM(常驻)
.sec_bt_nv_ramBT_NV_RAM_SIZE_ALL蓝牙 NV RAM

来源:app_main.c

API 参考

void app_main(void)

HID 应用的 C 入口函数,由系统引导调用。

  • 流程:升级状态检查 → 打印内存分配 → 开机键检测(可选)→ 低压检测(可选)→ main_application_operation_state(NULL, APP_STA_START)
  • 返回:无(不返回,进入应用循环)
  • 注意:在调用应用状态机前完成所有电源/固件安全检查

static void main_app_get_name(struct intent *it)

根据 CONFIG_APP_* 编译宏选择应用分支,填充 struct intent 的 name 与 action。

  • 参数:it — 目标 intent 对象,由调用方分配,函数内部调用 init_intent(it) 初始化
  • 行为:#if/#elif 链保证单分支命中;无匹配时 ASSERT(0, "no app!!!")
  • 返回:无(通过 it 输出)

static struct application *main_application_operation_state(struct application *app, enum app_state state)

在应用链表中按名称查找目标应用并驱动其状态机。

  • 参数:
    • app — 当前应用实例(首次为 NULL)
    • state — 目标状态(如 APP_STA_START)
  • 流程:main_app_get_name(&it) → list_for_each_app_main(dev) → memcmp(dev->name, it.name, strlen(it.name)) 匹配 → dev->ops->state_machine(app, state, &it)
  • 返回:NULL

void main_sys_event_msg_handle(int *msg)

系统事件消息分发函数,供消息队列回调使用。

  • 参数:msg — 消息数组,msg[1] 为 struct sys_event *,msg[2] 为 struct application_operation *
  • 行为:调用目标应用的 event_handler(NULL, event_ptr) 后调用 event_pool_free(event_ptr) 归还事件池内存
  • 返回:无

来源:app_main.c

故障模式、边界情况与并发

故障模式

故障场景表现源码中的防护
未配置任何应用ASSERT(0, "no app!!!") 断言失败main_app_get_name() 的 #else 分支强制拦截,防止空工程发布
应用名不匹配日志打印 no app run,状态机不执行memcmp 匹配失败时仅记录日志,框架继续运行,便于排查模块注册遗漏
低电压上电应用可能运行不稳TCFG_SYS_LVD_EN 使能时 app_power_vbat_check() 在上电早期拦截
误触发上电设备意外开机耗电TCFG_POWER_ON_NEED_KEY 使能时需长按确认,松开即软关机
升级后状态异常升级完成却关机/开机错误update_success_boot_check() 区分测试盒升级(关机)与 APP-OTA(开机)
事件池耗尽事件丢失或内存耗尽event_pool_free(event_ptr) 强制归还;若应用未正确释放,事件池会耗尽,属实现错误

边界情况

  • 分支互斥:CONFIG_APP_* 使用 #if/#elif 链而非独立 #if,同时定义多个宏时只有最先命中的生效;若要启用新形态,必须保证链上宏互斥。
  • 名称匹配长度:memcmp(dev->name, it.name, strlen(it.name)) 只比较 it.name 的长度,要求应用注册名是唯一前缀(例如 mouse_dual 与 mouse_low_latency 必须以完整名注册,避免前缀冲突)。
  • 纯 BLE 工程:IRQ_BREDR_IP/IRQ_BT_RXMCH_IP 标注 "no use",若误开经典蓝牙功能,相关中断配置需重新评估。

并发与实时性

  • 中断优先级设计:BLE RX(5)高于协议栈消息(4),保证射频数据及时进入协议栈;HID 上报路径(按键 → 组装 Report → GATT Notification)运行在应用任务上下文,受 IRQ_BTSTACK_MSG_IP 以下优先级任务影响,鼠标低延迟形态依赖这条链的确定性。
  • 事件池复用:系统事件经 event_pool_free 归还内存池,事件在高频场景(快速按键、连接/断开抖动)下不会产生堆碎片;但同一事件指针在分发期间不能被其他任务释放,否则会造成 use-after-free,event_handler 必须同步完成事件消费。
  • 内存段隔离:系统栈/用户栈/系统堆/蓝牙 NK/NV RAM 分别映射到独立段,蓝牙 RAM 与应用堆互不侵占,保证协议栈在应用内存耗尽时仍可运行。

性能与运维注意事项

  • RAM 规划:BT_NK_RAM_SIZE_ALL 与 BT_NV_RAM_SIZE_ALL 决定 BLE 协议栈容量,改动需同步检查 ld 链接脚本;app_main() 启动日志(nk_malloc/nv_malloc)可用于核对实际布局。
  • 低延迟上报:鼠标低延迟形态(CONFIG_APP_MOUSE_LOW_LATENCY)对 GATT Notification 的调度时延敏感,建议优先保障 IRQ_BLE_EVENT_IP 与协议栈任务优先级不被应用任务阻塞。
  • 日志定位:应用框架打印 run app>>> <name> 与 no app run,启动阶段可通过这两条日志快速确认分支选择是否正确。
  • 升级收尾:修改升级策略时注意 update_success_boot_check() 的分支语义,避免升级成功后错误关机导致用户体验问题。

扩展点

  1. 新增产品形态:在 main_app_get_name() 的 #elif 链追加 CONFIG_APP_XXX → 新 name/action;在 apps/demo/hid/board/bd57/ 新增 board_aw33n_xxx_cfg.h 与 board_aw33n_xxx.c;在 app_modules.h 注册同名应用模块。
  2. 新增 HID 报告类型:standard_hid.h 的 UUID 覆盖标准 HOGP 特征;如需自定义报告(如厂商命令),可基于 HID_REPORT_UUID_16 扩展报告 ID,或在 RCSP 通道(rcsp_hid_inter)实现私有透传。
  3. 更换应用框架行为:应用通过 struct application_operation 的 state_machine 与 event_handler 两个回调接入框架;自定义应用只需实现这两个回调并在 app_modules.h 注册。
  4. 板级复用:standard_hid.h 被 apps/demo/transfer/ 等工程复用,HID GATT UUID 定义是跨工程的共享契约,新工程可直接 include。

测试情况说明

本页基于源码静态分析,未在仓库中发现 HID 应用模块的独立单元测试工程;SDK 采用硬件在环验证(真实设备 + 主机端 BLE 抓包/APP 验证)为主:

  • 连接验证:通过手机/PC 扫描 HID Service(0x1812),读取 Report Map(0x2A4B)确认报告格式;
  • 上报验证:按键/位移后抓取 Report 特征(0x2A4D)的 Notification,核对报告 ID 与数据;
  • 多形态验证:切换 CONFIG_APP_* 宏编译不同固件,验证 run app>>> 日志与对应产品行为一致。

Related Links

  • standard_hid.h(BLE HID GATT UUID 定义)
  • app_main.c(应用入口与框架)
  • HID demo 板级配置目录
  • USB HID 键盘设备类
  • USB HID 鼠标设备类
  • USB HID 媒体设备类
  • RCSP HID 交互模块
Prev
遥控器示例