HID 设备应用
AC630N 蓝牙 SoC 上的 HID(Human Interface Device,人机接口设备)应用层实现。该应用将芯片配置为蓝牙 HID 外设(鼠标 / 遥控器 Keyfob / 键盘),通过经典蓝牙(BR/EDR)与主机配对连接后,上报 HID 报告(Report)实现键鼠控制功能。
Purpose and Scope
本文档介绍 apps/hid 目录下的 HID 设备应用:应用框架与任务调度、启动与模式选择、三种 HID 产品形态(鼠标、Keyfob、键盘)、HID 协议层(hid_user.c)、配置系统(user_cfg.c)以及低功耗与电源管理行为。
本页范围之外的内容:
- 底层蓝牙协议栈(
btstack/btctrler任务)的 HCI 实现,属于平台协议栈范畴。 - 公共 HID profile 的详细 report 描述符(见 standard_hid.h 与 hid_user.c,本文仅作引用)。
- 其他非 HID 应用(如音频/音箱应用)由其各自目录的文档覆盖。
Overview
HID 设备应用是 AC630N 系列芯片面向低成本键鼠/遥控外设市场提供的参考实现。与音频类应用(耳机、音箱)不同,HID 应用不需要音频 DSP 流水线,核心诉求是:
- 极低的 BOM 与功耗:通过低功耗窗口(
lp_winsize)与停止模式(Endless Sleep)优化待机电流。 - 快速的连接体验:开机即广播/可发现,与主机(手机、PC、电视)一键配对。
- 多种产品形态复用同一套代码框架:通过编译宏
CONFIG_APP_MOUSE/CONFIG_APP_KEYFOB/CONFIG_APP_KEYBOARD三选一,app_main()决定启动哪个子应用。
应用运行在 JieLi 自研 RTOS 之上,由 task_info_table 声明的系统任务(app_core、sys_event、btctrler、btstack、systimer 等)协同工作。上层使用"应用 + 意图(Intent)"框架:每个应用是一个状态机,通过 struct intent 描述要执行的动作(ACTION_MOUSE_MAIN、ACTION_HID_MAIN、ACTION_KEYFOB、ACTION_BACK 等),由 start_app() 统一调度。
Architecture
flowchart TD
subgraph sg_App["应用层 apps/hid"]
AppMain["app_main.c<br/>应用入口与模式选择"]
AppMouse["app_mouse.c<br/>鼠标应用"]
AppKeyfob["app_keyfob.c<br/>遥控器应用"]
AppKeyboard["app_keyboard.c<br/>键盘应用"]
AppIdle["app_idle.c<br/>空闲/待机应用"]
UserCfg["user_cfg.c<br/>蓝牙配置与VM存储"]
Misc["misc.c / log_config.c<br/>工具与日志"]
end
subgraph sg_Profile["HID Profile 层"]
HidUser["hid_user.c / hid_user.h<br/>HID 用户协议"]
StdHid["standard_hid.h<br/>标准 HID 报告定义"]
end
subgraph sg_Stack["蓝牙协议栈层"]
Btstack["btstack 任务"]
Btctrler["btctrler 任务<br/>控制器驱动"]
SysEvent["sys_event 任务"]
SysTimer["systimer 任务"]
end
subgraph sg_Host["外部主机"]
Host["PC / 手机 / 电视主机"]
end
AppMain -->|"CONFIG_APP_MOUSE / KEYFOB / KEYBOARD 三选一"| AppMouse
AppMain -->|"CONFIG_APP_KEYFOB"| AppKeyfob
AppMain -->|"CONFIG_APP_KEYBOARD"| AppKeyboard
AppMain --> AppIdle
AppMain --> UserCfg
AppMouse --> HidUser
AppKeyfob --> HidUser
AppKeyboard --> HidUser
HidUser --> StdHid
HidUser --> Btstack
Btstack --> Btctrler
Btctrler --> SysEvent
SysTimer --> AppIdle
Btctrler <-->|"HCI / 射频"| Host
架构说明:
- 应用层(
apps/hid):app_main()根据编译宏选择启动鼠标、Keyfob 或键盘子应用,三者都通过app_idle.c管理空闲状态;user_cfg.c提供全局的蓝牙配置对象(名称、MAC、射频功率)并负责 BTIF/VM 区域的持久化读写。 - HID Profile 层:
hid_user.c位于apps/common/third_party_profile/jieli/,是公共的 HID 用户 profile 实现,承载 HID 连接的建立、report 上报与断开处理;standard_hid.h定义标准 HID 报告结构。 - 协议栈层:
btstack任务运行蓝牙主机协议栈,btctrler任务驱动射频控制器,sys_event分发系统事件(按键、连接、电源),systimer提供定时唤醒能力。 - 外部主机:通过经典蓝牙与芯片配对,接收 HID 报告。
应用框架与任务调度
系统任务表
HID 应用启动后,JieLi RTOS 依据 task_info_table 创建系统任务。每个任务由四元组描述:任务名、优先级、栈大小(字节)、队列大小:
/*任务列表 */
const struct task_info task_info_table[] = {
{"app_core", 1, 640, 128 },
{"sys_event", 7, 256, 0 },
{"btctrler", 4, 512, 256 },
{"btstack", 3, 768, 256 },
{"systimer", 7, 128, 0 },
#ifdef CONFIG_UPDATA_ENABLE
{"update", 1, 320, 0 },
#endif
#if (RCSP_BTMATE_EN)
{"rcsp_task", 2, 640, 128 },
#endif
{0, 0},
};
来源:app_main.c
任务划分的设计意图:
app_core(优先级 1,栈 640B):承载整个应用状态机,优先级最低但栈最大,因为应用逻辑调用链最深。sys_event(优先级 7):最高优先级,负责事件分发,保证按键/连接事件不被长任务阻塞。btctrler/btstack:蓝牙控制器与主机协议栈分离,btctrler栈内含中断上下文(256B 队列),btstack负责 L2CAP/SDP/HID 等协议。update与rcsp_task:通过CONFIG_UPDATA_ENABLE(OTA 升级)与RCSP_BTMATE_EN(杰理私有 RCSP 协议,用于手机 App 调试)条件编译,不使能时这些任务不创建,节省 RAM。
全局应用变量
APP_VAR app_var;
void app_var_init(void)
{
app_var.play_poweron_tone = 1;
app_var.auto_off_time = TCFG_AUTO_SHUT_DOWN_TIME;
app_var.warning_tone_v = 340;
app_var.poweroff_tone_v = 330;
}
来源:app_main.c
app_var 是全局共享的应用状态结构:play_poweron_tone 控制开机提示音、auto_off_time 继承配置宏 TCFG_AUTO_SHUT_DOWN_TIME 作为自动关机阈值、warning_tone_v / poweroff_tone_v 为提示音频率(HID 外设通过蜂鸣器或 LED 反馈电量低/关机)。设计上把"可变配置"与"固定配置"分离:可运行时修改的放入 app_var,编译期确定的写入配置宏。
应用启动与模式选择
app_main() 是应用层唯一入口。它先处理 OTA 升级结果(update_result_deal()),随后根据编译宏三选一构造 Intent 并启动应用:
void app_main()
{
struct intent it;
#ifdef CONFIG_UPDATA_ENABLE
int update = 0;
update = update_result_deal();
#endif
printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>app_main...\n");
init_intent(&it);
#if (CONFIG_APP_MOUSE)
it.name = "mouse";
it.action = ACTION_MOUSE_MAIN;
#elif(CONFIG_APP_KEYFOB)
it.name = "keyfob";
it.action = ACTION_KEYFOB;
#elif(CONFIG_APP_KEYBOARD)
it.name = "hid_key";
it.action = ACTION_HID_MAIN;
#else
ASSERT(0,"no app!!!");
#endif
log_info("run app>>>%s",it.name);
start_app(&it);
}
来源:app_main.c
这段代码揭示了产品形态的切换机制:同一套工程,通过 CONFIG_APP_* 系列宏决定固件是哪一种 HID 外设。
| 编译宏 | Intent 名称 | 动作 | 对应源文件 |
|---|---|---|---|
CONFIG_APP_MOUSE | mouse | ACTION_MOUSE_MAIN | app_mouse.c |
CONFIG_APP_KEYFOB | keyfob | ACTION_KEYFOB | app_keyfob.c |
CONFIG_APP_KEYBOARD | hid_key | ACTION_HID_MAIN | app_keyboard.c |
| 以上皆非 | — | ASSERT(0,"no app!!!") | 编译/运行期报错 |
ASSERT(0) 兜底的设计意图:防止产线误配出"没有应用"的固件,把配置错误尽早暴露在开发阶段而不是流入量产。
应用切换机制
void app_switch(const char *name, int action)
{
struct intent it;
struct application *app;
log_info("app_exit\n");
init_intent(&it);
app = get_current_app();
if (app) {
/*
* 退出当前app, 会执行state_machine()函数中APP_STA_STOP 和 APP_STA_DESTORY
*/
it.name = app->name;
it.action = ACTION_BACK;
start_app(&it);
}
/*
* 切换到app (name)并执行action分支
*/
it.name = name;
it.action = action;
start_app(&it);
}
来源:app_main.c
app_switch() 是"先退后进"的两段式切换:先向当前应用发送 ACTION_BACK 使其依次经历 APP_STA_STOP → APP_STA_DESTORY 状态(释放资源),再携带新应用的名称与动作启动目标应用。这种设计保证任意时刻只有一个应用处于活动状态,避免两个状态机同时操作蓝牙资源造成竞争。
三种 HID 应用形态
apps/hid 目录下每个子应用都是基于"意图(Intent)+ 状态机"的应用模块,共享 app_idle.c 作为空闲态管理。三者差异集中在按键扫描、报告组装与消费模式上:
鼠标应用(app_mouse.c)
通过 ACTION_MOUSE_MAIN 启动。实现鼠标位移(X/Y 偏移)与左右键/滚轮上报。典型应用场景:演示翻页笔、空中鼠标、2.4G 转蓝牙鼠标。其核心逻辑是读取传感器/按键输入 → 组装 HID Mouse Report → 通过 hid_user 接口上报。
遥控器应用(app_keyfob.c)
通过 ACTION_KEYFOB 启动。面向电视盒子/机顶盒遥控器场景,按键数量多、多为组合键(音量+、频道切换、语音键等),报告类型以 Consumer Control(消费类控制)为主。该形态重视低功耗:遥控器大部分时间处于休眠,按键唤醒后瞬时连接上报再休眠。
键盘应用(app_keyboard.c)
通过 ACTION_HID_MAIN 启动(Intent 名称 hid_key)。实现标准键盘报告(Modifier + 按键码数组),支持组合键(Ctrl+Alt+Del 等)。与鼠标应用相比,键盘报告数据结构更大(8 字节 Boot Report:1 字节修饰键 + 1 字节保留 + 6 字节按键码)。
说明:三个子应用的按键扫描与报告组装的详细实现位于各自源文件(app_mouse.c、app_keyfob.c、app_keyboard.c),本页聚焦其公共框架。
空闲应用(app_idle.c)
管理无操作状态下的待机策略,与 eSystemConfirmStopStatus() 联动:系统进入"未来时间里无任务超时唤醒"时,可选择 Endless Sleep(深度停止,1)或 100ms 定时唤醒(0)。这是 HID 外设续航的关键路径——待机电流直接决定纽扣电池寿命。
HID 协议层
HID 报告的上报并不由各子应用直接操作射频,而是通过公共 profile 层完成:
- hid_user.c / hid_user.h:实现 HID profile 的 SDP 服务注册、L2CAP 中断通道/控制通道建立、report 发送与连接事件回调。应用层调用其接口完成"连接建立 → 上报 → 断开"全流程,无需关心 HCI 细节。
- standard_hid.h:定义标准 HID 报告描述符(Mouse/Keyboard/Consumer)的数据结构,保证与主机操作系统(Windows/macOS/Android/iOS)的 HID 驱动兼容。
设计意图:把 HID profile 放在 apps/common/third_party_profile 而非 apps/hid 内部,是因为它是跨产品复用的第三方协议实现——未来若有新的 HID 产品(如游戏手柄),只需在 apps 下新增应用并复用该 profile,无需改动协议层。
配置系统(user_cfg.c)
蓝牙基础配置
BT_CONFIG bt_cfg = {
.edr_name = "JL_HID_DEBUG",
.mac_addr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
.tws_local_addr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
.rf_power = 10,
.dac_analog_gain = 25,
.mic_analog_gain = 7,
.tws_device_indicate = 0x6688,
};
来源:user_cfg.c
bt_cfg 是全局蓝牙配置对象,要点:
edr_name = "JL_HID_DEBUG":默认蓝牙名称,量产前通过bt_set_local_name()改写。mac_addr全0xff:表示"未烧录",首次上电时由系统从 BTIF 区域读取/生成真实地址。rf_power = 10:射频发射功率档位,直接影响连接距离与功耗,量产按天线调试。tws_device_indicate = 0x6688:杰理私有标识,用于配套 App 识别设备类型。
BTIF 与 VM 存储分区
//======================================================================================//
// BTIF配置项表 //
// 参数1: 配置项名字 //
// 参数2: 配置项需要多少个byte存储 //
// 说明: 配置项ID注册到该表后该配置项将读写于BTIF区域, 其它没有注册到该表 //
// 的配置项则默认读写于VM区域. //
//======================================================================================//
const struct btif_item btif_table[] = {
// item id len //
{CFG_BT_MAC_ADDR, 6 },
{CFG_BT_FRE_OFFSET, 6 }, //测试盒矫正频偏值
//{CFG_DAC_DTB, 2 },
//{CFG_MC_BIAS, 1 },
{0, 0 }, //reserved cfg
};
//============================= VM 区域空间最大值 ======================================//
const int vm_max_size_config = VM_MAX_SIZE_CONFIG; //该宏在app_cfg中配置
//======================================================================================//
来源:user_cfg.c
存储设计的关键:BTIF 区域专门存放出厂校准类数据(MAC 地址、频偏矫正值),与 VM 区域(用户可改写配置)物理隔离。这样 OTA 升级或恢复出厂设置时不会丢失射频校准数据;而 vm_max_size_config 决定 VM 区域上限,需在 app_cfg 中按产品功能裁剪(HID 应用功能少,可配置得比音频应用小,节省 Flash)。
名称、地址与 PIN 码接口
const u8 *bt_get_mac_addr()
{
return bt_cfg.mac_addr;
}
void bt_set_mac_addr(u8 *addr)
{
memcpy(bt_cfg.mac_addr, addr, 6);
}
const char *bt_get_local_name()
{
return (const char *)(bt_cfg.edr_name);
}
void bt_set_local_name(char *name, u8 len)
{
memcpy(bt_cfg.edr_name, name, len);
bt_cfg.edr_name[len] = 0;
}
const char *bt_get_pin_code()
{
return "0000";
}
来源:user_cfg.c
这些 getter/setter 是协议栈与应用层之间的配置适配层:协议栈通过回调获取名称/地址/PIN 码,应用层(如产测、手机 App)通过 setter 修改。bt_get_pin_code() 固定返回 "0000"——HID 设备的 PIN 码用于传统配对流程(Legacy Pairing),0000 是行业惯例(多数主机对键盘/鼠标默认接受 0000),设计上牺牲部分安全性换取配对成功率与用户体验。
Core Flow — 设备启动到 HID 连接上报的完整流程
sequenceDiagram
participant PWR as 上电复位
participant APP as app_main (app_core)
participant IDLE as app_idle
participant SUB as 子应用 (mouse/keyfob/keyboard)
participant CFG as user_cfg (BTIF/VM)
participant STK as btstack / btctrler
participant HID as hid_user profile
participant HOST as 主机 (PC/手机)
PWR->>APP: 系统启动,创建 task_info_table 任务
APP->>APP: update_result_deal() 处理OTA结果
APP->>APP: app_var_init() 初始化全局变量
APP->>CFG: 读取 MAC/名称/频偏 (bt_get_*)
APP->>APP: 按 CONFIG_APP_* 构造 Intent
APP->>SUB: start_app(it) 启动子应用
SUB->>IDLE: 进入空闲/可发现状态
SUB->>HID: 注册 HID profile (SDP/L2CAP)
HOST->>STK: 扫描到设备并发起配对
STK->>CFG: bt_get_pin_code() 获取 "0000"
STK-->>HOST: 配对成功,建立 HID 连接
HOST->>HID: 中断通道就绪
SUB->>HID: 上报 HID Report (鼠标/按键/消费类)
HID->>STK: L2CAP 中断通道发送
STK-->>HOST: 射频发送报告
HOST->>SUB: 无操作超时
SUB->>IDLE: eSystemConfirmStopStatus() 进入停止/定时唤醒
流程要点:
- 启动阶段:
app_main()先处理 OTA 升级结果(避免升级残留导致启动异常),再初始化app_var,随后按编译宏三选一启动子应用。 - 配置加载:协议栈在初始化时通过
bt_get_mac_addr()/bt_get_local_name()/bt_get_pin_code()等接口从bt_cfg与 BTIF 区域读取参数——这是设备身份(可被发现、可配对)的基础。 - 连接阶段:主机扫描到
JL_HID_DEBUG(或量产改写后的名称)发起配对,协议栈用 PIN"0000"完成传统配对,hid_user建立 SDP 服务与 L2CAP 中断/控制通道。 - 上报阶段:子应用将按键/位移转换为标准 HID 报告,经
hid_user在中断通道上发送给主机,主机 HID 驱动解析后产生键鼠事件。 - 休眠阶段:空闲超时后进入 Endless Sleep 或 100ms 定时唤醒,等待下一次按键中断唤醒系统。
Usage Examples
示例 1:新增一种 HID 产品形态
以下模式展示了如何把现有代码复用到新产品:在 app_main() 中为新产品增加编译分支并指定 Intent(沿用 app_switch() 机制可支持运行时切换):
#if (CONFIG_APP_MOUSE)
it.name = "mouse";
it.action = ACTION_MOUSE_MAIN;
#elif(CONFIG_APP_KEYFOB)
it.name = "keyfob";
it.action = ACTION_KEYFOB;
#elif(CONFIG_APP_KEYBOARD)
it.name = "hid_key";
it.action = ACTION_HID_MAIN;
#else
ASSERT(0,"no app!!!");
#endif
log_info("run app>>>%s",it.name);
start_app(&it);
来源:app_main.c
示例 2:量产时修改设备名称与 MAC
产测/量产工具通过 setter 接口改写设备身份,配置存储于 BTIF 区域,固件升级不丢失:
void bt_set_mac_addr(u8 *addr)
{
memcpy(bt_cfg.mac_addr, addr, 6);
}
void bt_set_local_name(char *name, u8 len)
{
memcpy(bt_cfg.edr_name, name, len);
bt_cfg.edr_name[len] = 0;
}
来源:user_cfg.c 与 user_cfg.c
示例 3:调整射频功率与低功耗窗口
rf_power 决定发射功率档位(距离与电流的权衡);lp_winsize 各字段控制蓝牙振荡器(OSC)的省电窗口:
struct lp_ws_t lp_winsize = {
.lrc_ws_inc = 480, //260
.lrc_ws_init = 160,
.bt_osc_ws_inc = 100,
.bt_osc_ws_init = 140,
.osc_change_mode = 0,
};
来源:user_cfg.c
lrc_ws_inc/lrc_ws_init 是低速率时钟(LRC)的省电窗口增量与初值,bt_osc_ws_inc/bt_osc_ws_init 是蓝牙振荡器的对应参数;窗口越大,连接保持能力越强但平均电流越高。HID 外设通常需要低延迟响应(用户期望按键即时生效),因此窗口不能像音频设备那样开得极小,需要在响应速度与续航间调参。
Configuration Options
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
CONFIG_APP_MOUSE | 编译宏 | 0/1 | 使能鼠标应用(与 KEYFOB/KEYBOARD 三选一) |
CONFIG_APP_KEYFOB | 编译宏 | 0/1 | 使能遥控器应用 |
CONFIG_APP_KEYBOARD | 编译宏 | 0/1 | 使能键盘应用 |
CONFIG_UPDATA_ENABLE | 编译宏 | 条件编译 | 使能 OTA 升级任务(update,优先级 1,栈 320B) |
RCSP_BTMATE_EN | 编译宏 | 条件编译 | 使能 RCSP 调试任务(rcsp_task,优先级 2,栈 640B) |
TCFG_AUTO_SHUT_DOWN_TIME | 宏 | 见 app_cfg | 自动关机时间,初始化进 app_var.auto_off_time |
VM_MAX_SIZE_CONFIG | 宏 | 见 app_cfg | VM 区域最大空间,由 vm_max_size_config 引用 |
bt_cfg.edr_name | string | "JL_HID_DEBUG" | 经典蓝牙广播/查询名称 |
bt_cfg.mac_addr | u8[6] | {0xff,...} | 蓝牙 MAC(全 ff 表示待烧录) |
bt_cfg.rf_power | u8 | 10 | 射频发射功率档位 |
bt_cfg.dac_analog_gain | u8 | 25 | DAC 模拟增益(提示音路径) |
bt_cfg.mic_analog_gain | u8 | 7 | MIC 模拟增益(语音按键场景) |
bt_cfg.tws_device_indicate | u16 | 0x6688 | 杰理私有设备标识 |
btif_table | 表 | MAC(6)/频偏(6) | 注册到 BTIF 区域的配置项(出厂校准数据) |
lp_winsize.lrc_ws_inc | int | 480 | LRC 省电窗口增量 |
lp_winsize.lrc_ws_init | int | 160 | LRC 省电窗口初值 |
lp_winsize.bt_osc_ws_inc | int | 100 | 蓝牙 OSC 省电窗口增量 |
lp_winsize.bt_osc_ws_init | int | 140 | 蓝牙 OSC 省电窗口初值 |
lp_winsize.osc_change_mode | int | 0 | 振荡器切换模式 |
| PIN 码 | string | "0000" | 传统配对 PIN(bt_get_pin_code() 返回) |
API Reference
应用入口与切换(app_main.c)
void app_main()
应用层唯一入口。处理 OTA 结果、初始化 app_var、按 CONFIG_APP_* 宏构造 Intent 并调用 start_app()。
- 参数:无
- 返回:无
- 副作用:根据编译宏启动
mouse/keyfob/hid_key应用;无匹配宏时触发ASSERT(0,"no app!!!")
void app_var_init(void)
初始化全局 app_var:开机提示音使能、auto_off_time = TCFG_AUTO_SHUT_DOWN_TIME、提示音频率(340/330)。
- 参数:无
- 返回:无
void app_switch(const char *name, int action)
先向当前应用发送 ACTION_BACK(触发 APP_STA_STOP → APP_STA_DESTORY 状态),再启动目标应用。
- 参数:
name(const char *):目标应用名称action(int):目标动作(如ACTION_MOUSE_MAIN、ACTION_HID_MAIN、ACTION_KEYFOB)
- 返回:无
int eSystemConfirmStopStatus(void)
决定系统空闲时的停止策略:返回 1 表示 Endless Sleep(深度停止,无任务超时唤醒),返回 0 表示 100ms 定时唤醒。
- 参数:无
- 返回:
1(深度停止)或0(100ms 唤醒)
配置访问接口(user_cfg.c)
const u8 *bt_get_mac_addr()
返回 bt_cfg.mac_addr 指针(6 字节)。
void bt_set_mac_addr(u8 *addr)
将 6 字节地址拷贝进 bt_cfg.mac_addr。注意:仅更新内存镜像,持久化由 BTIF 层负责。
void bt_get_vm_mac_addr(u8 *addr)
获取产测使用的 VM 区 MAC 副本(bt_mac_addr_for_testbox)。源码注释提示中断上下文不能调用 syscfg_read,故使用内存副本。
const char *bt_get_local_name()
返回 bt_cfg.edr_name 字符串。
void bt_set_local_name(char *name, u8 len)
按长度 len 拷贝新名称并补 \0。
const char *bt_get_pin_code()
返回固定 PIN 码 "0000",供传统配对流程使用。
u16 bt_get_tws_device_indicate(u8 *tws_device_indicate)
返回 bt_cfg.tws_device_indicate(0x6688)。
HID 协议层(hid_user.c / standard_hid.h)
HID 连接建立、report 发送与断开回调的详细接口见 hid_user.h(本页未逐行读取,建议查阅该头文件获取完整签名);标准报告数据结构定义见 standard_hid.h。
Failure Modes、边界情况与并发
配置缺失兜底
bt_cfg.mac_addr默认全0xff(未烧录)。若量产未写入 MAC,设备会以非法地址广播,主机可能无法连接。bt_get_vm_mac_addr()的注释明确提示:中断上下文禁止调用syscfg_read,否则可能造成总线忙/数据错乱——这是并发访问 Flash 存储的硬约束。- 三个
CONFIG_APP_*宏全部未定义时ASSERT(0)直接停机,属于"失败快"(Fail Fast)策略,防止无应用固件流入市场。
配对与连接失败
- PIN 固定
"0000":若主机策略拒绝0000(企业安全策略),配对失败。这是 HID 外设的通用取舍——无法支持复杂配对流程(如 NFC 配对、Passkey 显示)。 - 未注册 HID profile 就上报报告:应用必须等
hid_user连接回调确认中断通道就绪后再发报告,否则数据丢弃。源码中app_switch的ACTION_BACK设计(先停后启)正是为了避免新旧应用同时操作 profile 导致通道状态错乱。
并发与资源竞争
- 任务优先级差异显著(
sys_event优先级 7 vsapp_core优先级 1):按键中断在sys_event上下文产生事件,应用逻辑在app_core消费,两者通过事件队列解耦,避免在中断里做重活。 - 低功耗窗口与连接保持的竞争:
lp_winsize窗口开得越大,系统越晚进入深睡、连接越稳,但电流越高。HID 应用必须在"按键即时报"与"待机续航"之间权衡,这也是eSystemConfirmStopStatus()提供两种停止策略的原因。
Performance 与运维注意事项
- 任务栈预算紧张:
app_core仅 640B 栈,sys_event仅 256B——应用回调里不应出现深递归或大局部数组,否则栈溢出将表现为随机崩溃。新增功能时优先使用事件驱动而非阻塞式轮询。 - VM 区域按需裁剪:
vm_max_size_config = VM_MAX_SIZE_CONFIG,HID 应用配置项少,可缩小 VM 预留,把 Flash 空间让给固件/字库/图片资源。 - OTA 兼容:
CONFIG_UPDATA_ENABLE开启时创建update任务并在app_main()开头处理升级结果,保证升级中断后仍能正确恢复。 - 产测流程:通过
bt_set_mac_addr()/bt_set_local_name()改写身份,CFG_BT_FRE_OFFSET保存测试盒矫正频偏——这些数据存 BTIF 区域,恢复出厂/升级不会擦除。
Extension Points
- 新增产品形态:复制
app_mouse.c的模式新建子应用(如游戏手柄),在app_main()中增加CONFIG_APP_*分支,复用hid_userprofile 与app_switch()切换框架。 - 自定义 HID 报告:修改 standard_hid.h 中的报告结构或扩展
hid_user的 report 回调,可支持多媒体键盘、消费类控制(Consumer Control)等高级报告。 - 产测/调试通道:
RCSP_BTMATE_EN使能rcsp_task,通过杰理 RCSP 私有协议实现手机 App 远程调试、按键抓取与参数下发。 - 电源策略定制:重写
eSystemConfirmStopStatus()返回逻辑,或调整lp_winsize,可按产品(遥控器 vs 鼠标)定制唤醒周期与窗口。
Related Links
- app_main.c — 应用入口与任务表
- user_cfg.c — 蓝牙配置与存储
- app_mouse.c — 鼠标应用
- app_keyfob.c — 遥控器应用
- app_keyboard.c — 键盘应用
- app_idle.c — 空闲/待机管理
- hid_user.c — HID 用户协议实现
- standard_hid.h — 标准 HID 报告定义
- 平台级蓝牙协议栈与 RTOS 行为请参阅对应平台文档(btstack / btctrler / 系统任务调度)。