透传应用总览
本文档介绍 AW31N BLE SDK 中透传应用(apps/demo/transfer)的整体架构、入口流程、示例应用(AT 透传)以及相关配置,帮助开发者快速理解透传应用如何在 BLE 与串口之间桥接数据。
目的与范围
本页面是"透传应用"系列文档的总览页,覆盖以下内容:
apps/demo/transfer应用目录的整体结构与分层- 应用入口
app_main.c的启动流程与应用分支选择机制 - AT 透传示例(
examples/at_char_com)的核心流程:BLE GATT 服务/客户端与 UART 双向透传 - 低功耗管理、软关机处理等应用级行为
- 板级配置与库配置(
board/bd47、config)的作用
以下内容属于兄弟页面的范畴,本页只做定向指引、不展开细节:
- AT 命令集的具体格式与实现 → 参见 AT 命令相关页面(
at_char_cmds) - GATT 服务端/客户端的 Service 与 Characteristic 定义 → 参见 BLE AT 透传服务页面(
ble_at_char_server/ble_at_char_client) - UART 驱动与帧解析细节 → 参见 UART 透传页面(
at_char_uart) - HID 应用中的透传通道(
ble_hid_transfer_channel_send/recieve)属于 HID 应用范畴,参见 HID 相关页面
概述
透传(Transparent Transmission)是 BLE SDK 中最常见的数据通路应用形态:设备通过 UART 串口接收外部数据,打包后经 BLE GATT 服务(Notify/Write)发送给对端(手机 App 或另一台设备),反之亦然。开发者只需关注"串口进来什么、BLE 发出去什么",无需关心底层协议栈细节。
在 apps/demo/transfer 中,SDK 提供了完整的可编译工程骨架:
- 应用层:
app_main.c负责启动、内存布局与状态机;examples/at_char_com提供可工作的 AT 透传参考实现。 - 板级层:
board/bd47同时支持 AW31N demo 板(board_aw31n_demo.c)与 AW318N dongle(board_aw318n_dongle.c)两种硬件形态。 - 配置层:
config/下的lib_btstack_config.c、lib_btctrler_config.c、lib_profile_config.c等文件集中管理蓝牙协议栈、控制器与 profile 的裁剪开关。
透传应用的核心设计意图是消息驱动 + 状态机:app_main() 只做初始化并把控制权交给应用状态机,应用启动后进入 get_msg() 消息循环,所有事件(按键、UART 数据、BLE 事件、低功耗定时器)都以消息形式被 app_comm_process_handler() 串行处理,从而避免并发访问共享状态。
架构
flowchart TD
subgraph sg_App["应用层 apps/demo/transfer"]
AppMain["app_main.c<br/>入口 / 内存布局 / 状态机"]
AtChar["examples/at_char_com<br/>AT 透传示例"]
Board["board/bd47<br/>板级配置与驱动"]
Cfg["config<br/>协议栈/控制器/profile 裁剪"]
end
subgraph sg_Stack["蓝牙协议栈"]
Btstack["btstack<br/>BLE 协议栈"]
Btctrler["btctrler<br/>蓝牙控制器"]
GattSrv["ble_at_char_server<br/>GATT 服务端"]
GattCli["ble_at_char_client<br/>GATT 客户端"]
end
subgraph sg_Ext["外设与系统"]
Uart["UART 串口"]
Wkup["P33 唤醒 IO"]
Timer["sys_timer / sys_timeout"]
end
AppMain -->|"启动 / 状态切换"| AtChar
AppMain --> Board
AppMain --> Cfg
AtChar --> GattSrv
AtChar --> GattCli
GattSrv --> Btstack
GattCli --> Btstack
Btstack --> Btctrler
AtChar --> Uart
AtChar --> Wkup
AtChar --> Timer
各层职责
- app_main.c:定义系统栈/堆/BT RAM 的内存段(
.sstack、.ustack、.sec_sys_heap、.sec_bt_nk_ram、.sec_bt_nv_ram),配置中断优先级,并依据编译宏(CONFIG_APP_*)选择进入哪个应用分支。 - examples/at_char_com:透传参考实现。它把 BLE GATT 服务端/客户端、AT 命令解析、UART 收发和低功耗策略组装成一个可运行的应用;源码以
#if CONFIG_APP_AT_CHAR_COM保护,仅在该宏开启时参与编译。 - board/bd47:硬件相关配置,区分 AW31N demo 板与 AW318N dongle 两种板型,通过
board_config.h与各自的*_cfg.h暴露引脚、时钟、外设配置。 - config/:
lib_btstack_config.c、lib_btctrler_config.c、lib_profile_config.c、lib_driver_config.c、lib_system_config.c、lib_update_config.c、log_config.c,分别控制协议栈特性、控制器特性、GATT profile、驱动、系统、升级与日志的裁剪。
架构选择"消息循环 + 状态机"而非"多线程直接调用"的原因:BLE 协议栈回调与 UART 中断都可能随时触发数据到达,若直接在回调中操作共享缓冲会引入竞态;统一投递到消息队列由单个循环串行消费,既保证数据有序,也让低功耗判断(是否有活跃任务)变得集中可控。
应用入口与控制流
内存布局与启动初始化
app_main.c 在文件顶部定义了系统内存的静态分配。这些变量通过 sec_used() 链接段属性放置在固定内存区域,是链接脚本(ld.c)与蓝牙协议栈 RAM 分配的基础:
//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);//最少占用
Source: app_main.c
设计意图:SYS_STACK_SIZE_ALL(系统栈)、USR_STACK_SIZE_ALL(用户栈)、SYS_HEAP_SIZE(堆)、BT_NK_RAM_SIZE_ALL / BT_NV_RAM_SIZE_ALL(蓝牙 NK/NV RAM)这些容量宏在板级配置中定义,将"多少内存给系统、多少给蓝牙"的决定权上移给板级配置,应用层只负责把宏展开成实际的静态数组。
app_main() 是透传应用(以及 SDK 中所有 demo 应用共用)的 C 入口,先打印 RAM 分配信息,再按配置决定是否检查开机键与低电压检测,最后把控制权交给状态机:
void app_main()
{
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);
log_info("sstack:size,top= %04x, %08x,ustack:size,top= %04x, %08x", sizeof(_sstack_space), _sstack_top, sizeof(_ustack_space), _ustack_top);
#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);
}
Source: app_main.c
应用分支选择(CONFIG_APP_*)
SDK 使用同一套 app_main.c 支撑多个 demo(透传、dongle、idle、非连接 2.4G、多连接、AT 透传),分支由编译宏决定。main_app_get_name() 是分支选择的唯一入口:
static void main_app_get_name(struct intent *it)
{
init_intent(it);
// 选择应用分支
#if CONFIG_APP_LE_TRANS
it->name = "le_trans";
it->action = ACTION_LE_TRANS_MAIN;
#elif CONFIG_APP_DONGLE
it->name = "dongle";
it->action = ACTION_DONGLE_MAIN;
#elif CONFIG_APP_IDLE
it->name = "idle";
it->action = ACTION_IDLE_MAIN;
#elif CONFIG_APP_NONCONN_24G
it->name = "nonconn_24g";
it->action = ACTION_NOCONN_24G_MAIN;
#elif CONFIG_APP_MULTI
it->name = "multi_conn";
it->action = ACTION_MULTI_MAIN;
#elif CONFIG_APP_AT_CHAR_COM
it->name = "at_com";
it->action = ACTION_AT_COM;
#else
ASSERT(0, "no app!!!");
#endif
}
Source: app_main.c
这段代码揭示了"意图(intent)+ 动作(action)"的应用切换机制:状态机不直接调用函数,而是通过 struct intent 携带应用名与动作码,由 main_application_operation_state() 统一分发。这样新增一个应用只需:① 在配置头文件中定义新的 CONFIG_APP_* 宏;② 在 main_app_get_name() 中加入分支;③ 在对应应用的状态机中处理 APP_STA_* 状态。
透传数据通路
AT 透传示例将数据通路拆分为两条方向相反的链路,均以 UART 为物理边界:
flowchart LR
subgraph sg_Down["上行(设备 → 对端)"]
U1["UART 接收"] --> C1["at_char_uart<br/>帧解析"]
C1 --> S1["ble_at_char_server<br/>GATT Notify"]
S1 --> P1["对端设备/手机"]
end
subgraph sg_Up["下行(对端 → 设备)"]
P2["对端设备/手机"] --> C2["ble_at_char_server<br/>GATT Write 回调"]
C2 --> S2["at_char_cmds<br/>AT 命令解析"]
S2 --> U2["UART 发送"]
end
BLE["BLE 空中链路"] -.-> P1
P2 -.-> BLE
- 上行:外部数据从 UART 进入,经
at_char_uart按帧组织后,由ble_at_char_server通过 GATT Notify 推送给已连接的 peer。 - 下行:peer 通过 GATT Write 写入,
ble_at_char_server回调触发at_char_cmds的 AT 命令解析,最终结果经 UART 输出。
该通路的模块划分(at_char_uart、at_char_cmds、ble_at_char_server、ble_at_char_client 各自独立成文件)体现了关注点分离:串口协议、命令语义、GATT 承载三者可独立修改,互不耦合。
AT 透传示例(examples/at_char_com)
app_at_char_com.c 是 AT 透传示例的应用主体,整体被 #if CONFIG_APP_AT_CHAR_COM 包裹,只有开启了该宏的工程才会编译这段代码。
蓝牙启动序列
atchar_bt_start() 展示了 SDK 蓝牙初始化的固定四步:配置 PLL 参数 → 协议栈预启动 → 解析配置文件 → 初始化协议栈:
static void atchar_bt_start()
{
uint32_t sys_clk = clk_get("sys");
bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0);
btstack_ble_start_before_init(NULL, 0);
cfg_file_parse(0);
btstack_init();
}
Source: app_at_char_com.c
bt_pll_para():根据系统时钟配置蓝牙 PLL,保证 RF 时序与系统时钟同步;btstack_ble_start_before_init():在初始化前启动协议栈任务,注册必要的系统资源;cfg_file_parse(0):解析用户配置区(如配对信息、厂商信息等);btstack_init():正式初始化 BLE 协议栈,注册 GATT profile。
应用主循环
应用启动函数先设置系统时钟、初始化蓝牙,随后进入永真循环,从消息队列取消息并交给 app_comm_process_handler() 分发:
static void atchar_app_start()
{
log_info("=======================================");
log_info("-------------atchar_com demo---------------");
log_info("=======================================");
log_info("app_file: %s", __FILE__);
clk_set("sys", TCFG_CLOCK_SYS_HZ);
clk_set("lsb", TCFG_CLOCK_LSB_HZ);
clock_bt_init();
atchar_bt_start();
int msg[4] = {0};
while (1) {
get_msg(sizeof(msg) / sizeof(int), msg);
app_comm_process_handler(msg);
}
}
Source: app_at_char_com.c
设计要点:
clk_set("sys", ...)与clk_set("lsb", ...)在应用层显式设定系统时钟与低功耗时钟,保证后续 BT 初始化时的时钟依赖已就绪;get_msg()是阻塞式取消息,取到后由app_comm_process_handler()统一分发。AT 命令、UART 数据、BLE 事件都在这里被路由到对应模块;- 该循环永不退出,应用退出仅通过软关机(
app_power_set_soft_poweroff)路径实现。
状态机
应用状态机与 SDK 的 struct application 框架绑定,本示例处理 APP_STA_CREATE、APP_STA_START、APP_STA_PAUSE 等状态。APP_STA_START 中依据 intent 的 action 进入 atchar_app_start():
static int atchar_state_machine(struct application *app, enum app_state state, struct intent *it)
{
switch (state) {
case APP_STA_CREATE:
break;
case APP_STA_START:
if (!it) {
break;
}
switch (it->action) {
case ACTION_AT_COM:
atchar_app_start();
break;
}
break;
case APP_STA_PAUSE:
break;
...
Source: app_at_char_com.c
源码在 APP_STA_PAUSE 之后还有其余状态与消息处理分支(本次文档未逐行展示),完整实现请直接阅读源文件。
核心时序流程
sequenceDiagram
participant HW as 硬件上电
participant AM as app_main
participant SM as 应用状态机
participant AS as atchar_app_start
participant BT as btstack
participant ML as 消息循环
participant UR as UART
participant GS as GATT 服务/客户端
HW->>AM: 复位进入 app_main
AM->>AM: 检查开机键 / 低电压
AM->>SM: main_application_operation_state(APP_STA_START)
SM->>SM: main_app_get_name 选择分支
SM->>AS: ACTION_AT_COM
AS->>AS: clk_set 系统/低功耗时钟
AS->>BT: bt_pll_para + btstack_ble_start_before_init
AS->>BT: cfg_file_parse + btstack_init
AS->>ML: 进入 get_msg 循环
UR->>ML: UART 数据事件消息
ML->>GS: app_comm_process_handler 分发
GS-->>UR: 透传结果 / AT 应答
时序上值得注意的两点:
- 初始化顺序有严格依赖:时钟 → BT PLL → 协议栈任务 → 配置解析 → 协议栈初始化,任何一步提前都会导致蓝牙无法正常工作。
- 初始化完成后立即进入消息循环:后续所有业务(包括 GATT 连接、UART 收发)都通过消息驱动,因此应用层代码无需关心中断上下文,降低编写难度。
低功耗与软关机
透传设备常驻 UART,低功耗策略需要兼顾"省电"与"随时可被串口数据唤醒"。示例通过三个函数组合实现:
软关机前先断链
void atchar_set_soft_poweroff()
{
log_info("set_soft_poweroff\n");
#if (TCFG_LOWPOWER_PATTERN == SOFT_MODE)
atchar_app_is_active = 1;
#endif
//必须先主动断开蓝牙链路,否则要等链路超时断开
btstack_ble_exit(0);
//延时,确保BT退出链路断开
if (ble_comm_dev_is_connected(GATT_ROLE_SERVER) || ble_comm_dev_is_connected(GATT_ROLE_CLIENT)) {
#if (TCFG_LOWPOWER_PATTERN == SOFT_MODE)
//soft 方式非必须等链路断开
sys_timeout_add(NULL, app_power_set_soft_poweroff, WAIT_DISCONN_TIME_MS);
#elif (TCFG_LOWPOWER_PATTERN == SOFT_BY_POWER_MODE)
//must wait disconn
app_power_soft.wait_disconn = 1;
#endif
} else {
app_power_set_soft_poweroff(NULL);
}
}
Source: app_at_char_com.c
该函数体现了低功耗模式的两种策略差异:
SOFT_MODE:软关机不强制等待链路断开,通过sys_timeout_add延迟WAIT_DISCONN_TIME_MS后执行,期间链路自然断开即可;SOFT_BY_POWER_MODE:必须等链路断开,置位app_power_soft.wait_disconn = 1让电源管理模块在断开事件到达后再执行关机。
UART 唤醒与低功耗切换
void at_char_wake_up_set(P33_IO_WKUP_EDGE edge)
{
if (!atchar_app_is_active) {
atchar_app_is_active = 1;
sys_timeout_add(NULL, at_go_low_power, 1000);
}
}
Source: app_at_char_com.c
at_char_wake_up_set()由 UART 唤醒中断(P33 IO 边沿)触发,把应用标记为活跃,并用 1 秒超时定时器安排回落低功耗——即"UART 发数唤醒一秒";at_go_low_power()在超时回调中检查atchar_force_wakeup:若期间曾调用at_set_low_power_mode()(主动退出低功耗),则清标志并跳过本次进入低功耗,避免刚唤醒就被立刻休眠;at_set_low_power_mode(enable)/at_get_low_power_mode()提供外部模块查询/控制低功耗状态的接口。
这套机制的核心权衡是:唤醒必须快(IO 边沿中断),但进入低功耗必须稳(防抖 + 延时确认),因此在"立即休眠"与"永久活跃"之间用 1 秒窗口做缓冲。
配置选项
透传应用的配置分散在三层:应用分支宏(决定编译哪个应用)、板级配置(内存/时钟/低功耗)与库配置(协议栈裁剪)。下表汇总本次源码阅读中确认的配置项。
应用分支宏(app_main.c 选择逻辑)
| 配置项 | 类型 | 默认行为 | 说明 |
|---|---|---|---|
CONFIG_APP_LE_TRANS | 宏 | 0 | 使能 LE 透传应用(le_trans / ACTION_LE_TRANS_MAIN) |
CONFIG_APP_DONGLE | 宏 | 0 | 使能 dongle 应用(dongle / ACTION_DONGLE_MAIN) |
CONFIG_APP_IDLE | 宏 | 0 | 使能 idle 应用(idle / ACTION_IDLE_MAIN) |
CONFIG_APP_NONCONN_24G | 宏 | 0 | 使能非连接 2.4G 应用(nonconn_24g / ACTION_NOCONN_24G_MAIN) |
CONFIG_APP_MULTI | 宏 | 0 | 使能多连接应用(multi_conn / ACTION_MULTI_MAIN) |
CONFIG_APP_AT_CHAR_COM | 宏 | 0 | 使能 AT 透传应用(at_com / ACTION_AT_COM),本页核心示例 |
ACTION_AT_COM | 枚举 | - | main_app_get_name() 为 AT 透传赋的动作码,状态机据此进入 atchar_app_start() |
注意:上述分支宏按
#if / #elif顺序互斥判断,一次编译只能选中一个应用。
板级与电源配置
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
TCFG_POWER_ON_NEED_KEY | 宏 | 0 | 开机是否需要按键确认;开启后 check_power_on_key() 等待长按,松开则软关机 |
TCFG_SYS_LVD_EN | 宏 | 0 | 使能系统低电压检测,app_power_vbat_check() 在开机时执行 |
TCFG_LOWPOWER_PATTERN | 枚举 | - | 低功耗模式:SOFT_MODE(软关机不等待断链)或 SOFT_BY_POWER_MODE(必须等待断链) |
TCFG_CLOCK_SYS_HZ / TCFG_CLOCK_LSB_HZ | 数值 | 板级定义 | 系统时钟 / 低功耗时钟,atchar_app_start() 中 clk_set 使用 |
SYS_STACK_SIZE_ALL / USR_STACK_SIZE_ALL | 数值 | 板级定义 | 系统栈 / 用户栈总大小,决定 .sstack / .ustack 数组长度 |
SYS_HEAP_SIZE | 数值 | 板级定义 | 系统堆大小,决定 .sec_sys_heap 数组长度 |
BT_NK_RAM_SIZE_ALL / BT_NV_RAM_SIZE_ALL | 数值 | 板级定义 | 蓝牙 NK/NV RAM 大小,决定 .sec_bt_nk_ram / .sec_bt_nv_ram |
WAIT_DISCONN_TIME_MS | 数值 | 示例定义 | 软关机时等待链路断开的延时,经 sys_timeout_add 使用 |
测试与日志配置
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
TEST_ATCHAR_AUTO_BT_OPEN | 宏 | 1 | 测试用:应用启动时自动打开蓝牙 |
LOG_TAG / LOG_TAG_CONST | 宏 | "[AT_COM]" / AT_COM | 日志标签,配合 log_config.c 的等级开关过滤输出 |
库配置(config/ 目录)
| 文件 | 管理内容 |
|---|---|
lib_btstack_config.c | BLE 协议栈特性裁剪(GATT、配对、功耗相关开关) |
lib_btctrler_config.c | 蓝牙控制器特性(RF、连接参数等) |
lib_profile_config.c | GATT profile 注册与裁剪 |
lib_driver_config.c | 驱动模块裁剪 |
lib_system_config.c | 系统任务/资源配置 |
lib_update_config.c | OTA 升级配置 |
log_config.c | 日志等级与模块开关 |
API 参考
以下为透传应用源码中确认的关键函数。
void app_main(void)
应用 C 入口。打印内存布局信息,按配置执行开机键检查与低电压检测,最后调用 main_application_operation_state(NULL, APP_STA_START) 启动状态机。
参数: 无
返回: 无(永不返回,控制权移交状态机/消息循环)
static void main_app_get_name(struct intent *it)
根据 CONFIG_APP_* 编译宏选择应用分支,向 struct intent 写入应用名与动作码。
参数:
it(struct intent *):输出参数,init_intent(it)初始化后写入name与action
返回: 无
异常: 若无任何 CONFIG_APP_* 被定义,触发 ASSERT(0, "no app!!!")
static int atchar_state_machine(struct application *app, enum app_state state, struct intent *it)
AT 透传应用状态机,处理 APP_STA_CREATE / APP_STA_START / APP_STA_PAUSE 等状态。
参数:
app(struct application *):应用实例state(enum app_state):目标状态it(struct intent *):intent,APP_STA_START时依据it->action == ACTION_AT_COM进入atchar_app_start()
返回: 状态机处理结果(int)
static void atchar_app_start(void)
应用主流程:设置系统/低功耗时钟 → clock_bt_init() → atchar_bt_start() → 进入 get_msg() + app_comm_process_handler() 永真消息循环。
参数: 无 返回: 无
static void atchar_bt_start(void)
蓝牙初始化序列:bt_pll_para() → btstack_ble_start_before_init() → cfg_file_parse(0) → btstack_init()。
参数: 无 返回: 无
void atchar_set_soft_poweroff(void)
软关机入口。先 btstack_ble_exit(0) 主动断开链路;若仍处于连接态,按 TCFG_LOWPOWER_PATTERN 决定延时关机(SOFT_MODE)或等待断开事件(SOFT_BY_POWER_MODE);未连接则直接 app_power_set_soft_poweroff(NULL)。
参数: 无 返回: 无
void at_set_low_power_mode(uint8_t enable)
外部模块控制低功耗状态:enable 为真时置 atchar_force_wakeup,阻止定时器把应用拉回低功耗。
参数:
enable(uint8_t):1退出低功耗,0允许进入
返回: 无
uint8_t at_get_low_power_mode(void)
查询当前是否处于低功耗模式。
参数: 无 返回: !atchar_app_is_active,非 0 表示处于低功耗
static void at_go_low_power(void *priv)
sys_timeout_add 超时回调。若 atchar_force_wakeup 已置位则清标志并返回(防抖),否则 atchar_app_is_active = 0 进入低功耗。
参数:
priv(void *):超时回调私有参数,示例中传NULL
返回: 无
void at_char_wake_up_set(P33_IO_WKUP_EDGE edge)
UART 唤醒入口(P33 IO 边沿触发)。若当前处于低功耗,置活跃标志并用 1 秒超时安排回落低功耗。
参数:
edge(P33_IO_WKUP_EDGE):唤醒边沿类型
返回: 无
故障模式、边界情况与并发
故障模式
| 场景 | 表现 | 处理方式(源码依据) |
|---|---|---|
| 软关机时 BLE 仍处于连接态 | 若直接断电,对端会等待链路超时,体验差 | atchar_set_soft_poweroff() 先调用 btstack_ble_exit(0) 主动断开,再按模式决定等待策略 |
SOFT_MODE 下链路未及时断开 | 软关机被延时执行 | 通过 sys_timeout_add(NULL, app_power_set_soft_poweroff, WAIT_DISCONN_TIME_MS) 延时补偿 |
SOFT_BY_POWER_MODE 下链路未断开 | 必须等待断开事件 | 置位 app_power_soft.wait_disconn = 1,由电源管理在断开后继续关机 |
| 刚被 UART 唤醒又立即被定时器休眠 | 数据可能来不及处理 | at_go_low_power() 检查 atchar_force_wakeup 防抖,跳过本次休眠 |
多个 CONFIG_APP_* 同时开启 | 编译告警/行为不确定 | #if / #elif 顺序判断,建议每次只使能一个应用分支 |
边界情况
- 唤醒窗口:
at_char_wake_up_set()使用 1 秒超时(sys_timeout_add(..., 1000))作为活跃窗口,窗口内再次收到唤醒只重置标志、不重复添加定时器;窗口结束后若无强制唤醒标志则回落低功耗。 - 取消息大小:
atchar_app_start()中int msg[4],get_msg(sizeof(msg) / sizeof(int), msg)按 4 个 int 读取消息,消息结构超出会截断,属于框架约定。 - 开机键长按:
TCFG_POWER_ON_NEED_KEY开启时,check_power_on_key()循环清狗并延时 10ms 轮询按键,超过 70 次(约 700ms)才确认开机,期间松开立即软关机。
并发与一致性
透传应用采用单消息循环串行模型,这是其并发设计的关键:
app_comm_process_handler(msg)在单线程消息循环中被调用,BLE 回调、UART 事件最终都转为消息排队处理,避免了多任务并发修改共享缓冲;- 中断侧(如 UART 唤醒、BLE RX)只做最小工作(投递消息/置标志),耗时逻辑全部落在循环线程;
- 中断优先级在
app_main.c中显式配置:IRQ_BLE_RX_IP = 5、IRQ_BTSTACK_MSG_IP = 4、IRQ_BT_TIMEBASE_IP = 6、IRQ_TICKTMR_IP = 3等,保证蓝牙事件与系统节拍的中断延迟可控; - 低功耗状态由
atchar_app_is_active单一标志位管理,读(at_get_low_power_mode)写(at_set_low_power_mode/at_go_low_power)都在消息上下文或中断回调中完成,通过atchar_force_wakeup防止状态翻转竞争。
性能与运维注意事项
- 内存静态化:系统栈、堆、蓝牙 RAM 均为编译期静态数组(
sec_used链接段),运行时无动态堆碎片风险;调整容量需要同步修改板级宏并重新链接。 - 时钟依赖:
atchar_bt_start()前必须完成clk_set("sys"/"lsb")与clock_bt_init(),否则 BT PLL 参数与协议栈初始化可能失败;修改系统时钟时需同步TCFG_CLOCK_OSC_HZ等 PLL 参数。 - 透传吞吐:透传吞吐受 GATT MTU、连接间隔与 UART 波特率共同限制;吞吐调优属于 BLE 透传服务页面的主题,本页不做展开。
- 日志:
LOG_TAG为[AT_COM],log_config.c可裁剪各模块日志等级,量产时建议关闭LOG_DUMP_ENABLE级别输出以降低串口占用。
扩展点
- 新增应用分支:在板级配置定义新
CONFIG_APP_*宏,在main_app_get_name()增加#elif分支,在对应模块实现atchar_state_machine风格的状态机即可,无需修改app_main()主体。 - 新增示例:在
apps/demo/transfer/examples/下新建目录,参考at_char_com组织app_*.c/h,复用app_comm_process_handler消息分发。 - 自定义 AT 命令:扩展
at_char_cmds模块,新增命令字与解析分支,数据通路(UART ↔ GATT)无需改动。 - 低功耗策略定制:通过
at_set_low_power_mode()/at_get_low_power_mode()接口接入外部策略(如按键、传感器事件),或在atchar_set_soft_poweroff()中调整断链等待策略。 - 板级移植:复制
board/bd47并按新板型修改board_*_cfg.h与board_*.c,修改内存宏即可移植到其他 AW31N 系列芯片。
相关链接
- app_main.c(应用入口与状态机)
- app_at_char_com.c(AT 透传示例主体)
- app_at_char_com.h(AT 透传示例头文件)
- board/bd47 板级配置目录
- config 库配置目录
- AW31N_transfer.cbp(工程文件)
- 相关兄弟页面:AT 命令详解、BLE AT 透传 GATT 服务/客户端、UART 透传、HID 透传通道(
ble_hogp.c)