公共应用模块
本文档介绍 AW30N BLE SDK 中 sdk/apps/app/bsp/common/ 目录下的公共应用模块,涵盖蓝牙公共应用框架(HID、SPP_and_LE 双模示例)与电源管理模块的组织方式、运行机制、关键数据流与扩展方法。
Purpose and Scope
本页面讲解 AW30N 固件中所有产品方案共享的公共应用层:它们位于 sdk/apps/app/bsp/common/ 下,被各种示例工程(keyboard、dongle、multi_conn、noconn_24g、reduce_ram 等)复用,是上层业务与底层 BLE 协议栈之间的桥梁。
本页面覆盖:
- 公共应用目录的组织结构与分层关系(
bt_common、power_manage) - 应用主入口
app_main.c的任务表、全局状态变量与生命周期 - SPP_and_LE 与 HID 两套公共蓝牙应用框架的差异与适用场景
- 电源管理模块
app_power_mg.c的低电检测与关机机制 - 公共模块的配置宏、弱函数与扩展点
以下内容属于其他页面,不在本文展开:BLE 协议栈内部实现(btstack/btctrler)、具体产品的差异化业务(如 RCSP 协议、涂鸦 TUYA 云)、音频编解码与 USB 等外设驱动,仅在本页作为任务表中的依赖项提及。
概述
AW30N 是杰理(Jieli)科技推出的低功耗 BLE SoC。其 SDK 采用"平台 + 方案"的架构:底层由 btstack、btctrler、systimer 等系统任务组成,而上层应用则按照"公共模块 + 示例(examples)"的方式组织。bsp/common 即"板级支持包中的公共部分",它把不同产品(键盘、dongle、多连接、24G 无连接等)都会用到的逻辑抽象出来,避免每个示例工程重复实现。
公共应用模块承担三类核心职责:
- 应用框架初始化:通过
app_main.c声明任务列表task_info_table、初始化全局应用变量app_var、提供应用状态机入口main_application_operation_state(),并处理开机、关机、充电等全局事件。 - 蓝牙业务框架:
bt_common/hid/提供 HID(人机交互设备,如键盘)应用框架;bt_common/spp_and_le/提供 SPP(串口透传)+ LE 双模应用框架。每个框架内部再按modules/bt/封装 BLE 通信(app_comm_ble.c)。 - 电源管理:
power_manage/app_power_mg.c周期扫描电池电压(VBAT),在低电且未充电时累计计时,达到阈值后发送MSG_POWER_OFF消息触发关机,并维护低电告警电压与当前电压的查询接口。
设计意图:把"所有方案都要做的事"(开机流程、低电保护、BLE 连接管理、任务划分)集中到公共层,把"每个产品不同的事"(键值表、连接策略、业务逻辑)留给 examples/ 子目录。这样新产品的开发只需在 examples 下新建一个目录并复用公共模块,即可快速获得可运行的固件框架。
架构
下图展示公共应用模块在 SDK 中的位置及其内部组成(节点名称均来自实际源码):
flowchart TD
subgraph sg_Common["sdk/apps/app/bsp/common(公共应用模块)"]
subgraph sg_BtCommon["bt_common"]
subgraph sg_Hid["hid 框架"]
HID_MAIN["hid/app_main.c"]
HID_BLE["hid/modules/bt/app_comm_ble.c"]
end
subgraph sg_SppLe["spp_and_le 框架"]
SPP_MAIN["spp_and_le/app_main.c"]
SPP_BLE["spp_and_le/modules/bt/app_comm_ble.c"]
end
end
subgraph sg_Power["power_manage"]
PWR_MG["app_power_mg.c"]
end
end
subgraph sg_Examples["examples(产品示例)"]
KB["keyboard"]
DG["dongle"]
MC["multi_conn"]
NC["noconn_24g"]
RM["reduce_ram"]
end
subgraph sg_Platform["平台层(底层依赖)"]
BT["btstack / btctler"]
SYS["sys_event / systimer"]
ADC["adc_api(PMU_VBAT 通道)"]
end
KB --> HID_MAIN
DG --> SPP_MAIN
MC --> SPP_MAIN
NC --> SPP_MAIN
RM --> SPP_MAIN
HID_MAIN --> HID_BLE
SPP_MAIN --> SPP_BLE
HID_MAIN --> PWR_MG
SPP_MAIN --> PWR_MG
PWR_MG --> ADC
HID_BLE --> BT
SPP_BLE --> BT
HID_MAIN --> SYS
SPP_MAIN --> SYS
架构说明:
- examples 层:每个示例工程(如
keyboard、dongle、multi_conn)只包含产品差异化代码(如app_hid_comm.c、app_dongle_comm.c),公共逻辑全部下沉到bsp/common。从文件列表可以看到,HID 示例的app_hid_comm.c与 SPP_and_LE 的app_dongle_comm.c、app_multi_conn.c、app_nonconn_24g.c、app_reduce_ram.c并列存在,它们各自#include公共框架。 - bt_common 层:按通信模式分为
hid与spp_and_le两套框架。每个框架的app_main.c负责任务与全局状态,modules/bt/app_comm_ble.c封装 BLE 的建立连接、配对、数据收发等公共逻辑,供不同示例复用。 - power_manage 层:独立于蓝牙业务,通过
adc_api.h的AD_CH_PMU_VBAT通道周期采样电池电压,并依赖msg.h的post_msg()向系统事件任务投递关机消息。 - 平台层:
btstack/btctler提供 BLE 协议栈,sys_event/systimer提供消息与定时服务,adc_api提供电压采集,均为公共模块的下游依赖。
应用主入口与全局状态
公共蓝牙框架(HID 与 SPP_and_LE)都包含一个 app_main.c,它定义了应用的进程骨架。以 spp_and_le/app_main.c 为例,其关键结构如下:
任务表 task_info_table
SDK 是 RTOS 多任务架构,每个系统模块以独立任务运行。app_main.c 中通过 task_info_table 声明任务名、优先级、栈大小等参数:
const struct task_info task_info_table[] = {
{"app_core", 1, 0, 640, 128 },
{"sys_event", 7, 0, 256, 0 },
{"btctrler", 4, 0, 512, 256 },
{"btencry", 1, 0, 512, 128 },
{"btstack", 3, 0, 768, 256 },
{"systimer", 7, 0, 128, 0 },
{"update", 1, 0, 512, 0 },
{"dw_update", 2, 0, 256, 128 },
{"usb_msd", 1, 0, 512, 128 },
{0, 0},
};
Source: app_main.c
表格每行含义为:{任务名, 优先级, 未用参数, 栈大小, 消息队列大小}。app_core 承载应用主逻辑,sys_event 负责系统事件分发(MSG_POWER_OFF 等消息由此任务消费),btctler/btstack 是 BLE 协议栈任务,systimer 提供软件定时。任务表以 {0, 0} 结尾。注意该表在源码中整体被 #if 0 屏蔽,实际任务参数由各工程的 app_config.h/board 配置决定——这正是"公共模块提供默认骨架、示例工程按需覆盖"的体现。
全局应用变量 app_var
公共框架定义了一个全局 app_var 结构,集中存放跨模块共享的应用状态:
APP_VAR app_var;
void app_var_init(void)
{
app_var.play_poweron_tone = 1;
app_var.auto_off_time = 0; //TCFG_AUTO_SHUT_DOWN_TIME;
app_var.warning_tone_v = 340;
app_var.poweroff_tone_v = 330;
}
Source: app_main.c
app_var_init() 在系统启动早期被调用,为开机提示音、自动关机时间、低电/关机提示音音量等设置默认值。其中 auto_off_time = 0 且原代码注释掉了 TCFG_AUTO_SHUT_DOWN_TIME,说明自动关机默认关闭,具体工程可通过配置宏开启。warning_tone_v(340)与 poweroff_tone_v(330)是提示音的音量等级,供音频播报模块使用。
弱函数与跨模块接口
公共框架提供了一些 __attribute__((weak)) 弱函数,允许产品层在不修改公共代码的前提下覆盖默认行为:
__attribute__((weak))
u8 get_charge_online_flag(void)
{
return 0;
}
Source: app_main.c
get_charge_online_flag() 默认返回 0(未在充电),若某工程接了充电检测(如 app_charge 模块),可在自己的源文件中定义同名强符号,链接时即覆盖公共弱函数。这是公共模块典型的扩展手段:公共层给默认值,产品层按需重定义,公共代码零改动。
分段编译
为支持 BLE 固件升级/覆盖(overlay),公共应用代码可被放入独立段:
#ifdef SUPPORT_MS_EXTENSIONS
#pragma bss_seg(".ble_app_bss")
#pragma data_seg(".ble_app_data")
#pragma const_seg(".ble_app_text_const")
#pragma code_seg(".ble_app_text")
#endif
Source: app_main.c
这组 #pragma 把公共应用模块的 bss/数据/常量/代码分别放入 .ble_app_* 段,便于链接脚本将整个应用层与协议栈分离放置,配合 app_ld_overlay_*.c(如 app_ld_overlay_dongle.c、app_ld_overlay_rc.c)实现 Flash 分区与 OTA 覆盖布局。
电源管理模块
power_manage/app_power_mg.c 是公共电源管理实现,负责电池电压采集与低电关机保护。它通过 adc_api.h 的 ADC 采样通道读取 VBAT,并依赖 msg.h 的消息机制通知系统关机。
模块状态与初始化
static u16 lvd_warning_voltage; //低电检测电压,比lvd电压大300mV
static u16 vbat_voltage;
static void lvd_warning_init(void)
{
extern u32 get_lvd_vol(void);
u32 lvd_voltage = get_lvd_vol();
lvd_warning_voltage = lvd_voltage + 300;
log_info("lvd_warning_voltage : %d\n", lvd_warning_voltage);
}
void app_power_init(void)
{
low_power_warning_init();
adc_add_sample_ch(AD_CH_PMU_VBAT);
app_power_scan();
}
Source: app_power_mg.c
初始化分三步:先由 low_power_warning_init()(内部再调用 lvd_warning_init())根据芯片低压检测阈值 get_lvd_vol() 推算出告警电压(比 LVD 电压高 300mV,提前预警),再向 ADC 驱动注册 AD_CH_PMU_VBAT 采样通道,最后立即执行一次电压扫描得到初始电量。
电压扫描与低电关机
void app_power_scan(void)
{
static u16 low_power_cnt = 0;
u16 vol = adc_get_voltage(AD_CH_PMU_VBAT) * 4;
if (0 == vol) {
return;
}
/* log_info("vbat voltage : %d", vol); */
if ((vol <= LOW_POWER_VOL) && (0 == CHARGE_IN_DET)) {
//充电时不进入低功耗
low_power_cnt++;
if (low_power_cnt == (TCFG_SHUTDOWN_TIME / 500)) {
log_error(LOW_POWER_LOG);
post_msg(1, MSG_POWER_OFF);
}
} else {
low_power_cnt = 0;
}
vbat_voltage = vol;
}
Source: app_power_mg.c
核心逻辑与设计意图:
- 采样换算:
adc_get_voltage() * 4将 ADC 原始电压换算为实际电池电压(分压电阻比例补偿)。 - 防误判:
vol == 0时直接返回,避免 ADC 采样异常(如未就绪)导致误关机。 - 去抖计时:低电状态不是立即关机,而是每 500ms 扫描一次并累加
low_power_cnt,累计到TCFG_SHUTDOWN_TIME / 500次才触发关机,防止电池电压瞬时波动造成误关机;TCFG_SHUTDOWN_TIME以 ms 为单位的关机延时由此换算。 - 充电保护:
CHARGE_IN_DET在使能TCFG_CHARGE_ENABLE时展开为charge_get_lvcmp_det()(充电器接入检测),充电中不进入低功耗流程——这是为了避免"边充边用"时误报低电。 - 消息驱动:最终通过
post_msg(1, MSG_POWER_OFF)投递关机消息,由sys_event任务统一处理关机动作,电源模块本身不做关机的具体实现,保持关注点分离。
电压查询接口
u16 app_power_get_vbat(void)
{
return vbat_voltage;
}
Source: app_power_mg.c
该接口返回最近一次扫描得到的电池电压,供 UI 电量显示、低电提示音等上层模块轮询使用。注意返回值是静态变量,只在 app_power_scan() 被周期调用时更新,调用方应通过定时器或系统事件周期读取。
核心流程:低电检测 → 关机
公共电源管理的端到端流程如下(以实际调用的函数为准):
sequenceDiagram
participant T as 定时/事件触发
participant PS as app_power_scan()
participant ADC as adc_api (AD_CH_PMU_VBAT)
participant CHG as charge_get_lvcmp_det()
participant SE as sys_event 任务
participant APP as 应用层
T->>PS: 周期调用(约 500ms)
activate PS
PS->>ADC: adc_get_voltage(AD_CH_PMU_VBAT)
ADC-->>PS: 原始电压
PS->>PS: vol = 原始值 * 4
PS->>PS: vol == 0 ? 直接返回(防误判)
PS->>PS: vol <= LOW_POWER_VOL ?
alt 低电
PS->>CHG: 是否在充电?
alt 未充电
PS->>PS: low_power_cnt++
PS->>PS: low_power_cnt == TCFG_SHUTDOWN_TIME/500 ?
Note over PS: 连续低电达到延时阈值
PS->>SE: post_msg(1, MSG_POWER_OFF)
SE-->>APP: 执行关机流程
else 充电中
PS->>PS: 清零计数,不关机
end
else 电压正常
PS->>PS: low_power_cnt = 0
end
PS->>PS: vbat_voltage = vol(更新查询值)
deactivate PS
流程要点:
- 电压扫描由上层定时驱动(500ms 周期),电源模块自身不创建定时器,保持被动采样。
- 低电判定必须同时满足"电压 ≤
LOW_POWER_VOL"且"未插入充电器",两个条件缺一不可。 - 关机不是即时的:连续低电计数达到
TCFG_SHUTDOWN_TIME / 500才触发,等效于低电状态持续TCFG_SHUTDOWN_TIME毫秒。 - 关机通过消息
MSG_POWER_OFF异步投递,sys_event任务收到后执行实际关机动作,电源模块与关机实现解耦。
配置选项
公共应用模块的行为由以下配置宏控制(定义于各工程的 app_config.h 或公共头文件,默认值以源码为准):
| 配置项 | 类型 | 默认行为 | 说明 |
|---|---|---|---|
TCFG_CHARGE_ENABLE | 宏 | 关闭(未定义时 CHARGE_IN_DET 恒为 0) | 使能充电检测,充电时不进入低电关机流程 |
TCFG_SHUTDOWN_TIME | 数值 | 由工程定义(ms) | 低电持续该时长后触发 MSG_POWER_OFF,与 500ms 扫描周期配合换算计数 |
LOW_POWER_VOL | 数值 | 由工程定义(mV) | 低电判定电压阈值 |
LOW_POWER_LOG | 字符串 | 由工程定义 | 低电触发关机时打印的错误日志标签 |
TCFG_AUTO_SHUT_DOWN_TIME | 数值 | 默认 0(注释状态) | 无操作自动关机时间,公共层默认关闭 |
TRANS_DATA_SPPLE_EN | 宏 | 工程使能 | 是否编译 SPP_and_LE 公共应用框架(app_main.c 外层 #if) |
SUPPORT_MS_EXTENSIONS | 宏 | 编译器特性 | 是否启用 .ble_app_* 分段编译布局 |
TCFG_KWS_VOICE_RECOGNITION_ENABLE | 宏 | 关闭 | 使能关键词唤醒(KWS)任务,对应任务表 kws |
RCSP_BTMATE_EN | 宏 | 关闭 | 使能 RCSP 杰理私有协议任务 rcsp_task |
USER_UART_UPDATE_ENABLE | 宏 | 关闭 | 使能 UART 升级任务 uart_update |
XM_MMA_EN | 宏 | 关闭 | 使能小马智联(MMA)任务 xm_mma |
TUYA_DEMO_EN | 宏 | 关闭 | 使能涂鸦云示例任务 user_deal |
其中任务表相关宏(RCSP_BTMATE_EN、USER_UART_UPDATE_ENABLE、XM_MMA_EN、TUYA_DEMO_EN、TCFG_KWS_VOICE_RECOGNITION_ENABLE)展示了公共框架的按需裁剪机制:任务表在编译期通过 #if 决定是否包含对应任务,不使能的模块不占任务资源。
API 参考
void app_power_init(void)
电源管理初始化。注册 VBAT 采样通道、初始化低电告警电压并执行首次电压扫描。应在应用早期(进入主循环前)调用一次。
参数: 无 返回: 无
u16 app_power_get_vbat(void)
获取最近一次扫描的电池电压值(单位 mV,已含分压补偿)。
参数: 无 返回: vbat_voltage,未扫描过时为 0。
void app_power_scan(void)
执行一次电池电压扫描与低电判断。电压 ≤ LOW_POWER_VOL 且未充电时累加计数,达到 TCFG_SHUTDOWN_TIME / 500 次后投递 MSG_POWER_OFF 消息。应每 500ms 周期调用。
参数: 无 返回: 无 副作用: 可能调用 post_msg(1, MSG_POWER_OFF) 触发系统关机流程。
void app_var_init(void)
初始化全局应用变量 app_var(开机提示音、自动关机时间、提示音音量默认值)。
参数: 无 返回: 无
struct application *main_application_operation_state(char *name, u8 name_len, struct application *app, enum app_state state, struct intent *it)
应用状态机入口(在 app_main.c 中声明)。当应用发生状态迁移(如 APP_STATE_POWER_ON/APP_STATE_POWER_OFF)时由框架回调,返回当前应用上下文。各示例工程通过实现此函数接管状态迁移逻辑。
参数:
name(char *):应用名称name_len(u8):名称长度app(struct application *):应用上下文state(enum app_state):目标状态it(struct intent *):状态迁移意图(携带事件数据)
返回: 指向当前应用上下文的指针。
蓝牙公共框架:HID 与 SPP_and_LE
bt_common 目录下包含两套并行框架,分别面向不同产品形态,二者共享同一套平台任务(btctler/btstack)与电源管理,仅在连接语义与数据通道上不同。
HID 框架(hid/)
面向人机交互设备(典型产品:蓝牙键盘)。文件组成:
hid/app_main.c:HID 应用主入口,任务与状态机骨架。hid/modules/bt/app_comm_ble.c:HID 场景下 BLE 连接与报告的公共封装。hid/examples/keyboard/app_hid_comm.c:键盘示例,展示如何基于公共框架实现具体 HID 产品。
HID 框架的核心语义是报告(Report):键盘等设备通过 HID Report 协议上报按键状态。示例层只需把键值表映射为 HID 报告,连接管理与重连策略由公共框架承担。
SPP_and_LE 框架(spp_and_le/)
面向串口透传、dongle、多连接等场景,支持 SPP(经典蓝牙串口)与 BLE 双模。文件组成:
spp_and_le/app_main.c:双模应用主入口(本文档前述代码均出自此文件)。spp_and_le/modules/bt/app_comm_ble.c:双模 BLE 通信公共封装。spp_and_le/examples/:多个产品示例:app_dongle_comm.c:dongle(接收端)示例app_multi_conn.c:多连接示例app_nonconn_24g.c:24G 无连接广播示例app_reduce_ram.c:低 RAM 优化示例
框架通过 TRANS_DATA_SPPLE_EN 宏控制编译,app_main.c 整体包在 #if (TRANS_DATA_SPPLE_EN) 内,未使能时该框架完全不占资源。
两套框架的选型:
| 维度 | HID 框架 | SPP_and_LE 框架 |
|---|---|---|
| 典型产品 | 键盘、遥控器 | dongle、透传模块、多连接设备 |
| 连接语义 | HID Report(报告协议) | SPP 串口 + BLE GATT 透传 |
| 示例 | keyboard | dongle / multi_conn / noconn_24g / reduce_ram |
| 编译开关 | 工程内使能 | TRANS_DATA_SPPLE_EN |
故障模式、边界情况与并发
公共应用模块在源码中体现出的容错设计:
- ADC 采样异常防护:
app_power_scan()中vol == 0直接返回,不更新电压也不累计低电计数。这防止了 ADC 通道未初始化或采样失败时误触发关机。 - 低电去抖(防误关机):连续
TCFG_SHUTDOWN_TIME / 500次低电采样才关机,电池电压瞬时跌落(如射频突发发射引起的压降)不会立即触发关机。 - 充电互斥:
CHARGE_IN_DET检测到充电器接入时清零计数,避免边充边用时低电误判;这也保证了"插着充电器关机"不会发生。 - 并发安全:
app_power_scan()与app_power_get_vbat()之间共享vbat_voltage,扫描通常在单一任务(如app_core或定时回调)中执行,读取方若在其他任务应容忍短暂的不一致(读取到旧值)。low_power_cnt为函数内静态变量,天然串行访问,无需加锁。 - 弱函数默认行为:
get_charge_online_flag()默认返回 0。若产品未实现充电检测而实际存在充电场景,需注意此默认值会导致充电中仍可能低电关机——这是弱函数机制的已知边界,产品层必须按需覆盖。 - 任务按需裁剪:任务表通过
#if宏控制kws、rcsp_task、uart_update、xm_mma、user_deal等任务的编译,宏不使能时相关代码不链接,避免无谓的 RAM/Flash 占用(如app_reduce_ram.c示例即针对 RAM 受限场景)。
性能与运行注意事项
- 扫描周期:
app_power_scan()设计为 500ms 周期(由TCFG_SHUTDOWN_TIME / 500换算可推断),ADC 单通道采样开销极小,不会成为热点;不应提高扫描频率以免增加 ADC 功耗。 - 低功耗考量:低电告警电压比 LVD 阈值高 300mV,目的是在硬件强制复位前留出时间播放低电提示音(
app_var.warning_tone_v),设计上属于"软预警 + 硬保护"两级策略。 - Flash/RAM 布局:
.ble_app_*分段把应用层与协议栈隔离,配合post_build/bd49/mbox_flash/app_ld_overlay_*.c可实现不同产品(dongle/rc/fullduplex_radio)的独立 overlay 布局;新增公共代码时注意保持分段指令,否则可能破坏覆盖布局。
扩展点
公共应用模块为产品定制预留了四个扩展机制:
- examples 子目录:新产品的差异化代码放在对应框架的
examples/下(如 HID 的keyboard、SPP_and_LE 的multi_conn),公共框架零改动。 - 弱函数覆盖:
get_charge_online_flag()等__attribute__((weak))接口可在产品层重定义强符号覆盖默认行为。 - 状态机接管:实现
main_application_operation_state()处理APP_STATE_*迁移,产品可自定义开机/关机/低电等状态下的业务动作。 - 配置宏裁剪:通过
app_config.h中的TCFG_*与任务开关宏(RCSP_BTMATE_EN、TUYA_DEMO_EN等)决定公共模块包含哪些子能力,实现按需编译。
测试与验证
SDK 仓库中与公共模块直接对应的验证方式是多示例工程编译:同一公共框架被 dongle、multi_conn、noconn_24g、reduce_ram 四个示例同时复用,任何对公共代码的改动都会在多个链接配置下被验证(不同 Flash overlay 布局、不同 RAM 预算),这本身就是对公共层接口稳定性的回归保障。此外,补丁包/ 目录下提供的裁剪补丁(如 AW30N_v1.2.0_SDK裁剪到256KB以内的补丁包)验证了公共模块在极小 Flash 约束下的可裁剪性。
Related Links
- app_main.c(SPP_and_LE 公共主入口)
- app_comm_ble.c(SPP_and_LE BLE 公共封装)
- app_power_mg.c(公共电源管理)
- app_main.c(HID 公共主入口)
- app_hid_comm.c(键盘示例)
- app_ld_overlay_dongle.c(overlay 链接布局示例)
- 相关目录:BLE 协议栈任务(btctler/btstack)、外设驱动(adc_api)、系统消息(sys_event)详见各自平台文档页。