板级工程与配置
AC63 BT SDK 的板级工程(Board)体系:以「芯片系列 + 项目」为单位组织的板级源文件、TCFG_* 配置宏体系,以及从板级平台数据到驱动注册、电源与唤醒初始化的完整链路。
Purpose and Scope
本文档说明 AC63 BT SDK(fw-AC63_BT_SDK)中板级工程的组织方式与配置方法,覆盖:
apps/hid/board/目录下按芯片系列(bd19 / bd29 / br23 / br25)划分的板级文件布局;- 板级文件三件套的职责划分:
board_<chip>_<project>.c(平台数据与初始化)、board_<chip>_<project>_cfg.h(外设使能与引脚配置)、board_<chip>_<project>_global_build_cfg.h(全局编译配置); - 通过
CONFIG_BOARD_*宏实现板级文件的条件编译与选择机制; TCFG_*配置宏如何映射为power_param、uart0_data、charge_data、adkey_data、iokey_list、wk_param等平台数据结构,并被底层驱动消费;board_power_init()电源初始化、休眠/唤醒回调与软关机处理的实现细节。
与板级工程相邻但不属于本文档范围的专题(请参见对应目录页):SDK 的编译构建系统与工程模板(见 Getting Started 其他小节)、具体外设驱动(UART/SPI/IIC/Flash 等)的驱动层实现、低功耗协议栈行为(本文只涉及板级侧的电源参数与唤醒配置)。
Overview
在嵌入式蓝牙 SDK 中,「板级工程」是连接芯片硬件能力与上层应用的适配层。AC63 BT SDK 的板级体系基于以下设计思想:
- 按芯片系列 + 项目双维度组织。同一颗芯片(如 AC6321A)可以承载多种项目(demo 演示、鼠标、键盘、遥控器 keyfob),每种组合对应一组板级文件,互不干扰。
- 条件编译驱动的选择机制。每个板级
.c文件整体包裹在#ifdef CONFIG_BOARD_XXX中,编译时只展开被选中的板级文件,未选中的板级代码不参与链接,从根源上避免引脚冲突与资源浪费。 - 宏配置 → 平台数据结构 → 驱动消费的三层解耦。
cfg.h中定义TCFG_*宏(纯文本、易读、易改);板级.c文件把宏组装进带类型的平台数据结构(如struct low_power_param、UART0_PLATFORM_DATA等);驱动与系统服务只读取这些结构体,不直接感知宏名。这样硬件变更(改引脚、改波特率、关外设)只需编辑cfg.h,无需触碰驱动代码。
以 apps/hid/board/bd19/board_ac6321a_demo.c 为例,一个板级文件通常包含:低功耗参数、串口打印参数、充电参数、AD/IO 按键映射、端口唤醒配置,以及 board_power_init()、休眠/唤醒回调、软关机处理等函数,全部由 TCFG_* 宏驱动。
Architecture
下图展示板级工程的整体架构:CONFIG_BOARD_* 宏选择板级组合,cfg.h 提供 TCFG_* 配置,板级 .c 文件将其组装为平台数据结构并注册给驱动与电源管理模块。
flowchart TD
subgraph sg_Build["编译期选择"]
BuildCfg["board_*_global_build_cfg.h<br/>CONFIG_BOARD_XXX 定义"]
BoardC["board_*_demo.c<br/>(#ifdef CONFIG_BOARD_XXX 包裹)"]
end
subgraph sg_Cfg["配置层 cfg.h"]
CfgH["board_*_cfg.h<br/>TCFG_* 宏: UART/SPI/IIC/Flash/Key/Charge/低功耗"]
end
subgraph sg_Data["平台数据结构 (board .c)"]
PowerParam["power_param<br/>struct low_power_param"]
UartData["uart0_data<br/>UART0_PLATFORM_DATA"]
ChargeData["charge_data<br/>CHARGE_PLATFORM_DATA"]
KeyData["adkey_data / iokey_list<br/>AD 按键 / IO 按键"]
WkParam["wk_param + port_wakeup<br/>唤醒口配置"]
end
subgraph sg_Init["初始化与回调"]
BoardPowerInit["board_power_init()<br/>power_init / power_wakeup_init"]
SleepCb["sleep_enter/sleep_exit_callback<br/>board_set_soft_poweroff"]
end
subgraph sg_Drv["驱动与系统服务"]
PowerMod["电源管理 power 模块"]
UartDrv["UART 驱动"]
ChargeDrv["充电 chargestore 驱动"]
KeyDrv["按键 key_driver"]
WkupMod["唤醒 wakeup 模块"]
end
BuildCfg -->|"定义 CONFIG_BOARD_XXX"| BoardC
BoardC -->|"引用"| CfgH
CfgH -->|"TCFG_* 宏填充"| PowerParam
CfgH -->|"TCFG_* 宏填充"| UartData
CfgH -->|"TCFG_* 宏填充"| ChargeData
CfgH -->|"TCFG_* 宏填充"| KeyData
CfgH -->|"TCFG_* 宏填充"| WkParam
PowerParam --> BoardPowerInit
BoardPowerInit -->|"power_init()"| PowerMod
BoardPowerInit -->|"power_wakeup_init()"| WkupMod
BoardPowerInit -->|"power_set_callback()"| SleepCb
UartData -->|"平台数据注册"| UartDrv
ChargeData -->|"平台数据注册"| ChargeDrv
KeyData -->|"平台数据注册"| KeyDrv
WkParam --> WkupMod
架构分层解读:
- 编译期选择层:
global_build_cfg.h决定某个板级组合是否参与编译(定义CONFIG_BOARD_AC6321A_DEMO等宏);板级.c文件以此宏作为整文件的条件编译开关。 - 配置层(cfg.h):全部
TCFG_*宏集中在此,是开发者日常修改的主要入口。宏使用ENABLE_THIS_MOUDLE(1)/DISABLE_THIS_MOUDLE(0)/NO_CONFIG_PORT(-1)作为统一开关约定。 - 平台数据结构层:板级
.c文件用宏填充系统定义的结构体。这一层是「配置」与「驱动」的适配边界:结构体类型由 SDK 系统头文件定义,字段含义由驱动约定。 - 初始化与回调层:
board_power_init()在系统启动早期被调用,完成电源参数注册、唤醒口注册、休眠/唤醒/软关机回调挂接。 - 驱动消费层:UART、充电、按键、唤醒等模块从平台数据中读取实际硬件参数,对上层应用透明。
各芯片系列板级文件一览(apps/hid/board/):
| 系列 | 代表芯片 | 板级项目示例 |
|---|---|---|
| bd19 | AC6321A / AC6323A / AC6328A / AC6328B / AC6329B/C/E/F / AC632N | demo、mouse、stand_keyboard、keyfob |
| bd29 | AC631N | demo |
| br23 | AC6351D / AC635N | demo、keyboard |
| br25 | AC6363F / AC6366C / AC6368A / AC6368B | demo、mouse、keyfob |
板级文件三件套详解
每个板级组合由三个文件构成,职责边界清晰:
| 文件 | 职责 | 典型内容 |
|---|---|---|
board_<chip>_<project>.c | 板级实现:平台数据结构 + 初始化函数 + 回调 | 低功耗参数、UART/充电平台数据、AD/IO 按键表、唤醒口、board_power_init()、休眠回调 |
board_<chip>_<project>_cfg.h | 板级配置:全部 TCFG_* 宏 | 外设使能开关、引脚选择、速率/通道/按键值等 |
board_<chip>_<project>_global_build_cfg.h | 编译配置:芯片/板级宏定义 | 决定 CONFIG_BOARD_XXX 是否生效 |
以 bd19 系列的 board_ac6321a_demo.c 为例,文件整体结构为:
#include "app_config.h"
#ifdef CONFIG_BOARD_AC6321A_DEMO
#include "system/includes.h"
#include "device/key_driver.h"
#include "asm/chargestore.h"
#include "asm/charge.h"
#include "rtc_alarm.h"
#include "asm/pwm_led.h"
#include "user_cfg.h"
#include "usb/otg.h"
#include "norflash.h"
#include "asm/power/p33.h"
#include "ex_mcu_uart.h"
#define LOG_TAG_CONST BOARD
#define LOG_TAG "[BOARD]"
...
void board_power_init(void);
Source: board_ac6321a_demo.c
设计意图说明:
#ifdef CONFIG_BOARD_AC6321A_DEMO整文件包裹:这是板级选择机制的基石。app_config.h(经构建系统配置)定义了当前工程的板级宏,只有匹配的板级.c文件会被编译。由于每个板级文件都自带LOG_TAG "[BOARD]",同一芯片多板共存时日志也能区分来源。- 头文件按需包含:板级文件不包含全部 SDK 头文件,而是按实际用到的平台数据结构(充电、按键、唤醒、电源等)逐个包含,降低编译耦合。
- 前向声明
void board_power_init(void);:该函数由系统启动流程调用,板级文件只负责实现,不负责调度。
编译选择机制(CONFIG_BOARD_*)
每个板级 .c 文件的编译与否,取决于全局构建配置是否定义了对应的 CONFIG_BOARD_* 宏。global_build_cfg.h 就是存放这些选择的地方。例如 bd19 系列为每个项目(demo/mouse/keyfob 等)分别维护 board_*_global_build_cfg.h,其中定义 CONFIG_BOARD_AC6321A_DEMO 之类宏后,对应 .c 才会被编译、对应 cfg.h 才会被引用。
#ifndef CONFIG_BOARD_AC6321A_DEMO_H
#define CONFIG_BOARD_AC6321A_DEMO_H
#include "board_ac6321a_demo_global_build_cfg.h"
#ifdef CONFIG_BOARD_AC6321A_DEMO
#define CONFIG_SDFILE_ENABLE
...
Source: board_ac6321a_demo_cfg.h
注意 cfg.h 内部同样用 #ifdef CONFIG_BOARD_AC6321A_DEMO 包裹:宏定义与板级文件同步生效,保证「配置」和「实现」永远配套编译,不会出现 cfg.h 内容与 .c 内容脱节的情况。#define CONFIG_SDFILE_ENABLE 则展示了板级文件还可以直接向系统声明额外的功能使能(此处为文件系统/SD 卡支持),这类"板级级联使能"是 SDK 的常见扩展手法。
统一开关宏约定
cfg.h 顶部定义了三个语义化的开关宏,所有外设配置复用它们,保证可读性与一致性:
#define ENABLE_THIS_MOUDLE 1
#define DISABLE_THIS_MOUDLE 0
#define ENABLE 1
#define DISABLE 0
#define NO_CONFIG_PORT (-1)
Source: board_ac6321a_demo_cfg.h
ENABLE_THIS_MOUDLE/DISABLE_THIS_MOUDLE:模块级使能开关,用于TCFG_UART0_ENABLE、TCFG_CHARGE_ENABLE等整模块开关;ENABLE/DISABLE:细粒度开关,用于组合按键MULT_KEY_ENABLE等子功能;NO_CONFIG_PORT(-1):表示该引脚不配置,例如串口接收脚可设为NO_CONFIG_PORT以省脚位。
配置宏体系(cfg.h)详解
cfg.h 按外设分区块组织宏,是硬件移植的核心工作区。以下为 board_ac6321a_demo_cfg.h 中各区块及其含义。
UART 配置
#define TCFG_UART0_ENABLE ENABLE_THIS_MOUDLE //串口打印模块使能
#define TCFG_UART0_RX_PORT NO_CONFIG_PORT //串口接收脚配置(用于打印可以选择NO_CONFIG_PORT)
#define TCFG_UART0_TX_PORT IO_PORTA_00 //串口发送脚配置
#define TCFG_UART0_BAUDRATE 1000000 //串口波特率配置
Source: board_ac6321a_demo_cfg.h
设计意图:串口 0 同时承担调试打印功能。TCFG_UART0_RX_PORT 允许设为 NO_CONFIG_PORT,即只发不收的打印场景可释放 RX 脚。TCFG_UART0_BAUDRATE = 1000000 表明 SDK 默认调试波特率采用 1M(与官方调试工具配套)。
USB / OTG 配置
#define TCFG_PC_ENABLE DISABLE_THIS_MOUDLE //PC模块使能
#define TCFG_UDISK_ENABLE DISABLE_THIS_MOUDLE //U盘模块使能
#define TCFG_HID_HOST_ENABLE DISABLE_THIS_MOUDLE//ENABLE_THIS_MOUDLE //游戏盒子模式
#define TCFG_ADB_ENABLE DISABLE_THIS_MOUDLE//ENABLE_THIS_MOUDLE
#define TCFG_AOA_ENABLE DISABLE_THIS_MOUDLE//ENABLE_THIS_MOUDLE
#define TCFG_OTG_USB_DEV_EN (BIT(0) | BIT(1))//USB0 = BIT(0) USB1 = BIT(1)
Source: board_ac6321a_demo_cfg.h
TCFG_OTG_USB_DEV_EN 用位掩码选择启用哪一路 USB 控制器(BIT(0)=USB0,BIT(1)=USB1),这是 SDK 中「多实例外设按位使能」的典型写法。注释中保留的 //ENABLE_THIS_MOUDLE 是官方演示「如何快速切换某功能」的惯用法——被注释的备选值便于来回切换,无需查询文档。
IIC 配置(软件/硬件双通道)
/*软件IIC设置*/
#define TCFG_SW_I2C0_CLK_PORT IO_PORTA_09 //软件IIC CLK脚选择
#define TCFG_SW_I2C0_DAT_PORT IO_PORTA_10 //软件IIC DAT脚选择
#define TCFG_SW_I2C0_DELAY_CNT 50 //IIC延时参数,影响通讯时钟频率
/*硬件IIC端口选择
SCL SDA
'A': IO_PORT_DP IO_PORT_DM
'B': IO_PORTA_09 IO_PORTA_10
'C': IO_PORTA_07 IO_PORTA_08
'D': IO_PORTA_05 IO_PORTA_06
*/
#define TCFG_HW_I2C0_PORTS 'B'
#define TCFG_HW_I2C0_CLK 100000 //硬件IIC波特率
Source: board_ac6321a_demo_cfg.h
设计意图:同时提供软件 IIC(任意 GPIO 模拟,TCFG_SW_I2C0_DELAY_CNT 调节时序)与硬件 IIC(固定端口组,通过 'A'/'B'/'C'/'D' 字符宏选择 SCL/SDA 组合)。硬件 IIC 端口由芯片映射决定,因此 cfg.h 用注释直接列出可用组合,防止开发者配置到不存在的映射。
硬件 SPI 配置(多实例)
#define TCFG_HW_SPI1_ENABLE ENABLE_THIS_MOUDLE
//A组IO: DI: PB2 DO: PB1 CLK: PB0
//B组IO: DI: PC3 DO: PC5 CLK: PC4
#define TCFG_HW_SPI1_PORT 'A'
#define TCFG_HW_SPI1_BAUD 2000000L
#define TCFG_HW_SPI1_MODE SPI_MODE_BIDIR_1BIT
#define TCFG_HW_SPI1_ROLE SPI_ROLE_MASTER
#define TCFG_HW_SPI2_ENABLE ENABLE_THIS_MOUDLE
//A组IO: DI: PB8 DO: PB10 CLK: PB9
//B组IO: DI: PA13 DO: DM CLK: DP
#define TCFG_HW_SPI2_PORT 'A'
#define TCFG_HW_SPI2_BAUD 2000000L
#define TCFG_HW_SPI2_MODE SPI_MODE_BIDIR_1BIT
#define TCFG_HW_SPI2_ROLE SPI_ROLE_MASTER
Source: board_ac6321a_demo_cfg.h
SPI1/SPI2 共用同一套 _PORT/_BAUD/_MODE/_ROLE 参数模板,注释同样给出各端口组的引脚映射。SPI_MODE_BIDIR_1BIT 与 SPI_ROLE_MASTER 表明 SDK 以主模式、双向 1 线方式驱动片外 SPI 器件(如 Flash)。
Flash 配置
#define TCFG_CODE_FLASH_ENABLE DISABLE_THIS_MOUDLE
#define TCFG_FLASH_DEV_SPI_HW_NUM 1// 1: SPI1 2: SPI2
#define TCFG_FLASH_DEV_SPI_CS_PORT IO_PORTB_06
#define TCFG_NORFLASH_DEV_ENABLE DISABLE_THIS_MOUDLE
Source: board_ac6321a_demo_cfg.h
TCFG_FLASH_DEV_SPI_HW_NUM 选择挂载 Flash 的硬件 SPI 实例(1=SPI1),TCFG_FLASH_DEV_SPI_CS_PORT 指定片选脚——片选脚是 GPIO 而非 SPI 专用脚,因此单独配置。
按键配置(IO 键 / AD 键)
#define KEY_NUM_MAX 10
#define KEY_NUM 3
#define MULT_KEY_ENABLE DISABLE //是否使能组合按键消息, 使能后需要配置组合按键映射表
//...
#define TCFG_IOKEY_ENABLE DISABLE_THIS_MOUDLE //是否使能IO按键
#define TCFG_IOKEY_POWER_CONNECT_WAY ONE_PORT_TO_LOW //按键一端接低电平一端接IO
#define TCFG_IOKEY_POWER_ONE_PORT IO_PORTB_01 //IO按键端口
//...
#define TCFG_ADKEY_ENABLE ENABLE_THIS_MOUDLE //是否使能AD按键
#define TCFG_ADKEY_PORT IO_PORTB_01 //AD按键端口(需要注意选择的IO口是否支持AD功能)
#define TCFG_ADKEY_AD_CHANNEL AD_CH_PB1
#define TCFG_ADKEY_EXTERN_UP_ENABLE ENABLE_THIS_MOUDLE //是否使用外部上拉
Source: board_ac6321a_demo_cfg.h
KEY_NUM_MAX/KEY_NUM:按键表容量与按键数量上限;- IO 键:
TCFG_IOKEY_*_CONNECT_WAY描述按键接法(一端接低电平ONE_PORT_TO_LOW),*_ONE_PORT指定引脚; - AD 键:一个 ADC 通道经分压电阻可扩展出多个键值(
TCFG_ADKEY_AD_CHANNEL与端口需匹配,注释列出所有可用 AD 通道),TCFG_ADKEY_EXTERN_UP_ENABLE决定是否外接上拉。
平台数据结构(board .c)详解
cfg.h 的宏最终在板级 .c 文件中被组装为带类型的平台数据结构。以下逐一说明。
低功耗参数 power_param
const struct low_power_param power_param = {
.config = TCFG_LOWPOWER_LOWPOWER_SEL, //0:sniff时芯片不进入低功耗 1:sniff时芯片进入powerdown
.btosc_hz = TCFG_CLOCK_OSC_HZ, //外接晶振频率
.delay_us = TCFG_CLOCK_SYS_HZ / 1000000L, //提供给低功耗模块的延时(不需要需修改)
.btosc_disable = TCFG_LOWPOWER_BTOSC_DISABLE, //进入低功耗时BTOSC是否保持
.vddiom_lev = TCFG_LOWPOWER_VDDIOM_LEVEL, //强VDDIO等级,可选:2.0V 2.2V 2.4V 2.6V 2.8V 3.0V 3.2V 3.6V
.vddiow_lev = TCFG_LOWPOWER_VDDIOW_LEVEL, //弱VDDIO等级,可选:2.1V 2.4V 2.8V 3.2V
.osc_type = TCFG_LOWPOWER_OSC_TYPE,
.lpctmu_en = TCFG_LP_TOUCH_KEY_ENABLE,
.vd13_cap_en = TCFG_VD13_CAP_EN,
};
Source: board_ac6321a_demo.c
设计意图:低功耗参数与芯片电源域强相关。config 字段决定 sniff 期间是否进入 powerdown(0/1 两档);vddiom_lev/vddiow_lev 分别配置强/弱 VDDIO 电压等级,直接决定休眠电流与 IO 驱动能力——这是蓝牙低功耗产品的核心调参项。delay_us 用系统主频宏自动推导(TCFG_CLOCK_SYS_HZ / 1000000L),避免手动换算引入误差。
UART 平台数据(宏生成结构体)
#if TCFG_UART0_ENABLE
UART0_PLATFORM_DATA_BEGIN(uart0_data)
.tx_pin = TCFG_UART0_TX_PORT, //串口打印TX引脚选择
.rx_pin = TCFG_UART0_RX_PORT, //串口打印RX引脚选择
.baudrate = TCFG_UART0_BAUDRATE, //串口波特率
.flags = UART_DEBUG, //串口用来打印需要把改参数设置为UART_DEBUG
UART0_PLATFORM_DATA_END()
#endif //TCFG_UART0_ENABLE
Source: board_ac6321a_demo.c
UART0_PLATFORM_DATA_BEGIN/END 是 SDK 的宏生成器:自动为串口 0 生成带实例名的平台数据结构并注册到 UART 框架。.flags = UART_DEBUG 将串口 0 标记为调试口,系统 log_info/putchar 即走此口。整段由 TCFG_UART0_ENABLE 条件编译——配置关掉后平台数据不复存在,串口驱动完全不感知。
充电平台数据
#if TCFG_CHARGE_ENABLE
CHARGE_PLATFORM_DATA_BEGIN(charge_data)
.charge_en = TCFG_CHARGE_ENABLE, //内置充电使能
.charge_poweron_en = TCFG_CHARGE_POWERON_ENABLE, //是否支持充电开机
.charge_full_V = TCFG_CHARGE_FULL_V, //充电截止电压
.charge_full_mA = TCFG_CHARGE_FULL_MA, //充电截止电流
.charge_mA = TCFG_CHARGE_MA, //充电电流
/*ldo5v拔出过滤值,过滤时间 = (filter*2 + 20)ms,ldoin<0.6V且时间大于过滤时间才认为拔出
对于充满直接从5V掉到0V的充电仓,该值必须设置成0,对于充满由5V先掉到0V之后再升压到xV的充电,需要根据实际情况设置该值大小*/
.ldo5v_off_filter = 100,
.ldo5v_on_filter = 50,
.ldo5v_keep_filter = 220,
.ldo5v_pulldown_lvl = CHARGE_PULLDOWN_200K, //下拉电阻档位选择
.ldo5v_pulldown_keep = 1,
.ldo5v_pulldown_en = 1,
CHARGE_PLATFORM_DATA_END()
#endif//TCFG_CHARGE_ENABLE
Source: board_ac6321a_demo.c
充电配置是所有 TWS/耳机/遥控器产品的核心。ldo5v_* 系列字段解决「充电仓检测」这一经典难题:注释明确说明过滤时间公式 (filter*2 + 20)ms,以及不同充电仓行为(充满直接掉到 0V vs. 先掉 0V 再升压)需要不同的过滤值;ldo5v_pulldown_* 配置 LDOIN 检测所需的下拉电阻档位(如 CHARGE_PULLDOWN_200K)与维持策略。这些注释直接沉淀了硬件调试经验,是板级工程文档化的最佳实践。
AD 按键平台数据
const struct adkey_platform_data adkey_data = {
.enable = TCFG_ADKEY_ENABLE, //AD按键使能
.adkey_pin = TCFG_ADKEY_PORT, //AD按键对应引脚
.ad_channel = TCFG_ADKEY_AD_CHANNEL, //AD通道值
.extern_up_en = TCFG_ADKEY_EXTERN_UP_ENABLE, //是否使用外接上拉电阻
.ad_value = { //根据电阻算出来的电压值
TCFG_ADKEY_VOLTAGE0,
TCFG_ADKEY_VOLTAGE1,
...
},
.key_value = { //AD按键各个按键的键值
TCFG_ADKEY_VALUE0,
TCFG_ADKEY_VALUE1,
...
},
};
Source: board_ac6321a_demo.c
AD 按键的本质是「电阻分压 → ADC 采样 → 电压档位 → 键值」的查表映射:ad_value[] 是从分压电阻网络算出的各档位电压,key_value[] 是对应键值,两者按数组下标一一对应。开发者改硬件电阻网络时只需同步更新 TCFG_ADKEY_VOLTAGEx / TCFG_ADKEY_VALUEx 宏组,驱动逻辑零改动。
唤醒口配置 wk_param
struct port_wakeup vbat_port = {
.edge = BOTH_EDGE, //唤醒方式选择,可选:上升沿\下降沿\双边沿
.both_edge = 1,
.filter = PORT_FLT_16ms,
.iomap = IO_VBTCH_DET, //唤醒口选择
};
const struct wakeup_param wk_param = {
#if TCFG_ADKEY_ENABLE || TCFG_IOKEY_ENABLE
.port[1] = &port0,
#endif
#if TCFG_TEST_BOX_ENABLE
.port[2] = &port1,
#endif
#if TCFG_CHARGE_ENABLE
.aport[0] = &charge_port,
.aport[1] = &vbat_port,
.aport[2] = &ldoin_port,
#endif
};
Source: board_ac6321a_demo.c
唤醒配置分两类:数字口唤醒(port[],边沿触发 + 滤波)与模拟口唤醒(aport[],充电检测/电池检测/插入检测)。PORT_FLT_16ms 表示 16ms 硬件滤波,用于抑制机械抖动与电源噪声。wk_param 的成员同样由 TCFG_* 宏决定是否挂接——配置关掉的外设不会注册唤醒源,避免「未使用的引脚被意外唤醒」这类低功耗陷阱。
核心初始化流程(board_power_init 与电源管理)
板级工程的最终落点是 board_power_init()——系统启动早期由框架调用,把板级配置注册进电源管理模块。以下为 bd19 系列的实现:
void board_power_init(void)
{
log_info("Power init : %s", __FILE__);
power_init(&power_param);
//< close short key reset
/* power_mclr(0); */
//< close long key reset
/* power_pin_reset(0); */
power_set_callback(TCFG_LOWPOWER_LOWPOWER_SEL, sleep_enter_callback, sleep_exit_callback, board_set_soft_poweroff);
// wl_audio_clk_on();
power_keep_dacvdd_en(0);
power_wakeup_init(&wk_param);
aport_edge_wkup_set_callback(aport_wakeup_callback);
port_edge_wkup_set_callback(port_wakeup_callback);
}
Source: board_ac6321a_demo.c
执行顺序与设计意图:
log_info("Power init : %s", __FILE__):打印板级文件名,便于确认当前编译进的是哪块板——多板工程排查的首个调试点;power_init(&power_param):注册低功耗参数(sniff 低功耗策略、VDDIO 等级、晶振频率等);- 被注释的
power_mclr(0)/power_pin_reset(0):默认关闭短按复位与长按复位功能,避免按键误触复位——若产品需要按键复位可取消注释; power_set_callback(TCFG_LOWPOWER_LOWPOWER_SEL, sleep_enter_callback, sleep_exit_callback, board_set_soft_poweroff):挂接休眠进入/退出/软关机三个回调,且第一个参数复用低功耗选择宏,保证「不进入低功耗的模式不挂回调」;power_keep_dacvdd_en(0):低功耗时不保持 DACVDD,进一步省电(需确认硬件无外部依赖);power_wakeup_init(&wk_param):注册全部唤醒源(数字口 + 模拟口);aport_edge_wkup_set_callback/port_edge_wkup_set_callback:挂接模拟口/数字口边沿唤醒回调,把唤醒事件分发给充电检测(charge_wakeup_isr/ldoin_wakeup_isr)等处理函数。
休眠与软关机回调
void sleep_exit_callback(u32 usec)
{
putchar('>');
APP_IO_DEBUG_0(A, 5);
}
void sleep_enter_callback(u8 step)
{
/* 此函数禁止添加打印 */
if (step == 1) {
putchar('<');
APP_IO_DEBUG_1(A, 5);
/*dac_power_off();*/
} else {
close_gpio();
}
}
void board_set_soft_poweroff(void)
{
log_info("%s", __FUNCTION__);
mask_io_cfg();
#if TCFG_TEST_BOX_ENABLE
power_wakeup_index_disable(2);
#endif
close_gpio();
}
Source: board_ac6321a_demo.c
sleep_enter_callback按step分阶段执行:step == 1只做轻量动作(打印'<'与 IO 翻转,用于逻辑分析仪观测),后续步骤才close_gpio()关闭全部 GPIO。注释「此函数禁止添加打印」是因为进入深度休眠后打印本身可能触发外设活动,破坏休眠时序。board_set_soft_poweroff是软关机路径:先mask_io_cfg()屏蔽 IO 配置,测试盒模式(TCFG_TEST_BOX_ENABLE)下还需禁用对应唤醒索引,最后close_gpio()将 IO 全部置为高阻——确保关机后无漏电通路。APP_IO_DEBUG_*宏默认被注释为空操作,仅保留给调试期放开使用,避免生产代码携带多余 IO 翻转。
启动到驱动注册的完整时序
sequenceDiagram
participant Boot as 系统启动/框架
participant BoardC as board_*.c<br/>(CONFIG_BOARD_XXX 选中)
participant CfgH as board_*_cfg.h<br/>(TCFG_* 宏)
participant Power as 电源管理模块
participant Uart as UART 驱动
participant Charge as chargestore 驱动
participant Key as key_driver
participant Wkup as 唤醒模块
Note over Boot,CfgH: 编译期: CONFIG_BOARD_* 决定 .c/.h 是否参与编译
Boot->>BoardC: 调用 board_power_init()
BoardC->>CfgH: 读取 TCFG_* 宏
BoardC->>Power: power_init(&power_param)
BoardC->>Power: power_set_callback(... 休眠/软关机回调)
BoardC->>Wkup: power_wakeup_init(&wk_param)
BoardC->>Wkup: 注册 port/aport 唤醒回调
BoardC-->>Uart: uart0_data 平台数据 (UART0_PLATFORM_DATA_BEGIN)
BoardC-->>Charge: charge_data 平台数据 (CHARGE_PLATFORM_DATA_BEGIN)
BoardC-->>Key: adkey_data / iokey_list 平台数据
Power-->>Boot: 电源参数就绪
Note over Power,Wkup: 运行期: 按键/充电/唤醒事件经回调<br/>分发到对应驱动处理
该时序揭示板级工程在系统生命周期中的位置:它是启动早期第一个被调用的应用侧适配层,其产物(平台数据结构)在后续驱动初始化时被消费;运行期的低功耗、唤醒、充电检测等事件也经由板级注册的回调进入系统处理。
使用示例
示例 1:为一块新板子启用串口打印并调整波特率
修改 board_<chip>_<project>_cfg.h 中的 UART 区块:
#define TCFG_UART0_ENABLE ENABLE_THIS_MOUDLE //串口打印模块使能
#define TCFG_UART0_RX_PORT NO_CONFIG_PORT //串口接收脚配置(用于打印可以选择NO_CONFIG_PORT)
#define TCFG_UART0_TX_PORT IO_PORTA_00 //串口发送脚配置
#define TCFG_UART0_BAUDRATE 1000000 //串口波特率配置
Source: board_ac6321a_demo_cfg.h
示例 2:调整低功耗与唤醒策略
低功耗参数在 board_<chip>_<project>.c 中定义,由 TCFG_LOWPOWER_* 宏驱动:
const struct low_power_param power_param = {
.config = TCFG_LOWPOWER_LOWPOWER_SEL, //0:sniff时芯片不进入低功耗 1:sniff时芯片进入powerdown
.btosc_hz = TCFG_CLOCK_OSC_HZ, //外接晶振频率
.delay_us = TCFG_CLOCK_SYS_HZ / 1000000L, //提供给低功耗模块的延时(不需要需修改)
.btosc_disable = TCFG_LOWPOWER_BTOSC_DISABLE, //进入低功耗时BTOSC是否保持
.vddiom_lev = TCFG_LOWPOWER_VDDIOM_LEVEL, //强VDDIO等级,可选:2.0V 2.2V 2.4V 2.6V 2.8V 3.0V 3.2V 3.6V
.vddiow_lev = TCFG_LOWPOWER_VDDIOW_LEVEL, //弱VDDIO等级,可选:2.1V 2.4V 2.8V 3.2V
.osc_type = TCFG_LOWPOWER_OSC_TYPE,
.lpctmu_en = TCFG_LP_TOUCH_KEY_ENABLE,
.vd13_cap_en = TCFG_VD13_CAP_EN,
};
Source: board_ac6321a_demo.c
示例 3:自定义软关机动作
软关机时默认将 IO 全部置为高阻;需要在关机前保存状态或驱动特定外设时,修改 board_set_soft_poweroff():
void board_set_soft_poweroff(void)
{
log_info("%s", __FUNCTION__);
mask_io_cfg();
#if TCFG_TEST_BOX_ENABLE
power_wakeup_index_disable(2);
#endif
close_gpio();
}
Source: board_ac6321a_demo.c
配置选项速查
以下为 board_ac6321a_demo_cfg.h 中经核实的核心配置宏(不同芯片/项目板的宏集合以此为基础增减):
| 配置宏 | 类型 | 默认值(demo 板) | 说明 |
|---|---|---|---|
TCFG_UART0_ENABLE | int | ENABLE_THIS_MOUDLE | 串口打印模块使能 |
TCFG_UART0_RX_PORT | IO | NO_CONFIG_PORT | 串口 RX 脚,打印场景可省 |
TCFG_UART0_TX_PORT | IO | IO_PORTA_00 | 串口 TX 脚 |
TCFG_UART0_BAUDRATE | int | 1000000 | 调试波特率 |
TCFG_PC_ENABLE | int | DISABLE_THIS_MOUDLE | PC 透传模块使能 |
TCFG_UDISK_ENABLE | int | DISABLE_THIS_MOUDLE | U 盘模块使能 |
TCFG_HID_HOST_ENABLE | int | DISABLE_THIS_MOUDLE | HID Host(游戏盒子)使能 |
TCFG_ADB_ENABLE | int | DISABLE_THIS_MOUDLE | ADB 使能 |
TCFG_AOA_ENABLE | int | DISABLE_THIS_MOUDLE | AOA(Android Open Accessory)使能 |
TCFG_OTG_USB_DEV_EN | int | BIT(0) | BIT(1) | 启用的 USB 控制器位掩码 |
TCFG_SW_I2C0_CLK_PORT | IO | IO_PORTA_09 | 软件 IIC CLK 脚 |
TCFG_SW_I2C0_DAT_PORT | IO | IO_PORTA_10 | 软件 IIC DAT 脚 |
TCFG_SW_I2C0_DELAY_CNT | int | 50 | 软件 IIC 延时(影响时钟频率) |
TCFG_HW_I2C0_PORTS | char | 'B' | 硬件 IIC 端口组(A/B/C/D) |
TCFG_HW_I2C0_CLK | int | 100000 | 硬件 IIC 波特率 |
TCFG_HW_SPI1_ENABLE | int | ENABLE_THIS_MOUDLE | SPI1 使能 |
TCFG_HW_SPI1_PORT | char | 'A' | SPI1 端口组 |
TCFG_HW_SPI1_BAUD | long | 2000000L | SPI1 波特率 |
TCFG_HW_SPI1_MODE | enum | SPI_MODE_BIDIR_1BIT | SPI1 工作模式 |
TCFG_HW_SPI1_ROLE | enum | SPI_ROLE_MASTER | SPI1 主从角色 |
TCFG_HW_SPI2_* | — | 同 SPI1 | SPI2 同构参数组 |
TCFG_CODE_FLASH_ENABLE | int | DISABLE_THIS_MOUDLE | 代码 Flash 使能 |
TCFG_FLASH_DEV_SPI_HW_NUM | int | 1 | Flash 挂载的硬件 SPI(1/2) |
TCFG_FLASH_DEV_SPI_CS_PORT | IO | IO_PORTB_06 | Flash 片选脚 |
TCFG_NORFLASH_DEV_ENABLE | int | DISABLE_THIS_MOUDLE | NOR Flash 设备使能 |
KEY_NUM_MAX | int | 10 | 按键表容量上限 |
KEY_NUM | int | 3 | 实际按键数量 |
MULT_KEY_ENABLE | int | DISABLE | 组合按键消息使能 |
TCFG_IOKEY_ENABLE | int | DISABLE_THIS_MOUDLE | IO 按键使能 |
TCFG_IOKEY_*_CONNECT_WAY | enum | ONE_PORT_TO_LOW | IO 键接法 |
TCFG_IOKEY_*_ONE_PORT | IO | 各键不同 | IO 键引脚 |
TCFG_ADKEY_ENABLE | int | ENABLE_THIS_MOUDLE | AD 按键使能 |
TCFG_ADKEY_PORT | IO | IO_PORTB_01 | AD 键引脚 |
TCFG_ADKEY_AD_CHANNEL | enum | AD_CH_PB1 | AD 通道(须与引脚匹配) |
TCFG_ADKEY_EXTERN_UP_ENABLE | int | ENABLE_THIS_MOUDLE | 外接上拉使能 |
TCFG_CHARGE_ENABLE | int | 见板文件 | 内置充电使能 |
TCFG_LOWPOWER_LOWPOWER_SEL | int | 见板文件 | sniff 低功耗模式选择 |
TCFG_LOWPOWER_VDDIOM_LEVEL | enum | 见板文件 | 强 VDDIO 等级 |
TCFG_LOWPOWER_VDDIOW_LEVEL | enum | 见板文件 | 弱 VDDIO 等级 |
TCFG_LOWPOWER_OSC_TYPE | enum | 见板文件 | 低功耗振荡器类型 |
TCFG_TEST_BOX_ENABLE | int | 见板文件 | 测试盒模式(影响唤醒配置) |
注:
TCFG_CHARGE_*、TCFG_LOWPOWER_*、TCFG_ADKEY_VOLTAGE*/VALUE*等宏定义于cfg.h的后续区块(本页读取片段之外),其取值方式与上表同类宏一致。
失败模式、边界情况与并发/时序注意点
基于板级源码的实际实现,以下问题在移植与调试中最常见:
| 失败模式 | 根因 | 板级应对手段 |
|---|---|---|
| 引脚冲突/外设不工作 | 多块板级文件同时编译或引脚配置与硬件不符 | CONFIG_BOARD_* 条件编译保证同时只有一块板生效;cfg.h 注释直接列出各端口组可用映射 |
| 休眠电流偏大 | VDDIO 等级、下拉电阻或唤醒源配置不当 | power_param.vddiom/vddiow_lev 分级可调;ldo5v_pulldown_* 与 power_keep_dacvdd_en(0) 关闭无关电源域 |
| 无法唤醒/误唤醒 | 唤醒边沿、滤波时间或唤醒源未注册 | port_wakeup 配置 edge/filter;wk_param 成员按 TCFG_* 条件注册,未使能外设不挂唤醒源 |
| 充电仓检测异常 | LDOIN 拔插滤波值不匹配仓体行为 | ldo5v_off/on/keep_filter 按注释公式 (filter*2+20)ms 调整 |
| 软关机漏电 | IO 未置高阻 | board_set_soft_poweroff() 内 mask_io_cfg() + close_gpio() |
| 打印异常/无日志 | 调试口配置与工具不匹配 | TCFG_UART0_BAUDRATE 与 UART_DEBUG 标志需配套 |
时序/并发注意点:
sleep_enter_callback明确标注「此函数禁止添加打印」,说明休眠临界区不允许任何可能唤醒外设的操作;回调执行顺序(step 分级)不可重排。board_set_soft_poweroff()中power_wakeup_index_disable(2)必须在close_gpio()之前执行——先摘除唤醒源再关 IO,否则关闭 IO 的动作可能触发边沿中断造成竞态。wk_param的port[]与aport[]下标由条件编译决定(port[1]、port[2]在不同配置下含义不同),新增唤醒源时必须与power_wakeup_index_disable()的索引约定保持一致。- 板级平台数据结构多为
const(如power_param、adkey_data),运行期只读,天然避免多线程写竞争;可变状态集中在回调与电源模块内部管理。
性能与运维注意点
- 编译期裁剪:
TCFG_*模块开关直接决定平台数据是否生成,未使能模块的驱动代码不链接,同时节省 ROM 与启动初始化时间。 - 调试口复用:
UART_DEBUG标志让串口 0 兼任打印口,量产时可通过配置切换为普通功能口,无需改驱动。 - 日志分级:板级文件启用
LOG_ERROR/DEBUG/INFO,默认关闭LOG_DUMP;排查低功耗时序时可放开LOG_DUMP_ENABLE观察更细粒度日志,量产前应恢复。 - 电源参数是硬件强相关项:
vddiom_lev/vddiow_lev/晶振频率改动前必须核对芯片数据手册与原理图,错误配置可能导致休眠唤醒异常或 IO 电平不达标。
扩展点:如何新增一块板级工程
- 复制模板:以同系列最近的板级文件为模板,复制
board_<chip>_<project>.c、board_<chip>_<project>_cfg.h、board_<chip>_<project>_global_build_cfg.h三件套; - 定义板级宏:在
global_build_cfg.h中定义新的CONFIG_BOARD_<NEW>,并确保同一时间只定义一个板级宏(或由构建脚本选择); - 修改 cfg.h:按硬件原理图配置
TCFG_*引脚、速率、使能开关;引脚映射以 cfg.h 注释为准; - 调整 .c 平台数据:
power_param低功耗参数、wk_param唤醒源、按键表按实际电路修改; - 接入启动:确认系统启动流程会调用新板的
board_power_init()(若框架按板级宏分发,则无需改动); - 验证:先确认
log_info("Power init : %s", __FILE__)打印的是新板文件名,再逐项验证打印、按键、充电、休眠唤醒。
测试与验证要点
板级层没有独立单元测试,验证依赖硬件实测与日志观测:
- 编译选择验证:启动日志中的板级文件名(
log_info("Power init : %s", __FILE__))是「当前编译进哪块板」的直接证据; - 休眠波形验证:
APP_IO_DEBUG_*宏放开后可用逻辑分析仪观测'<'/'>'时刻的 IO 翻转,确认进出休眠的时序; - 唤醒源验证:
port_wakeup_callback/aport_wakeup_callback中保留的注释日志可放开,确认充电/按键/插入事件的唤醒分发路径; - 充电参数验证:
ldo5v_*_filter需配合真实充电仓实测拔插判定时间(公式(filter*2+20)ms)。
Related Links
- 板级工程源文件目录(apps/hid/board)
- board_ac6321a_demo.c(bd19 板级实现示例)
- board_ac6321a_demo_cfg.h(bd19 板级配置示例)
- board_ac6321a_demo_global_build_cfg.h(编译选择配置)
- 同系列其他板级示例:
apps/hid/board/bd19/board_ac6328a_keyfob.c、apps/hid/board/br23/board_ac6351d_keyboard.c、apps/hid/board/br25/board_ac6366c_demo.c等 - 相关专题(见对应目录页):SDK 构建与工程模板(Getting Started)、UART/SPI/IIC/Flash 驱动、低功耗协议栈