应用配置系统
AD16N 蓝牙音频 SoC 的 mbox_flash 应用固件配置体系,由编译期宏配置层(app_config.h)、运行期常量配置层(app_config.c)与公共配置接口层(config.h)共同构成,集中管理系统时钟、外设、电源、音频、存储、USB、日志与中断等全部应用级参数。
Purpose and Scope
本页面向「mbox_flash 应用(4-mbox-flash-app)」目录下的应用配置系统,说明:
app_config.h中所有TCFG_*/TFG_*/KEY_*/ALINK_*等编译期宏的用途与设计意图;app_config.c中运行期常量(中断优先级、日志标签、更新通道开关、MIDI/编解码参数)的语义;config.h提供的公共日志、调试 IO 宏接口;- 配置如何被预处理器分发到驱动层与应用模块(编译期数据流),以及更新通道等运行期开关如何影响固件行为。
以下内容不属于本页范围,请参见对应页面:
- 配置工具的在线 EQ 调音流程属于「配置工具 / 在线调试」相关页面;
- USB 设备/主机协议栈细节属于 USB 相关页面;
- 更新/OTA 的具体协议实现属于升级相关页面。
Overview
嵌入式固件的“配置系统”与传统后台服务的配置中心不同:AD16N SDK 在编译期通过大量 #define 宏将硬件选型(引脚、时钟、外设使能)静态固化进固件,在运行期通过 const 全局变量向 SDK 库导出中断优先级、日志开关、更新通道等参数。这样的设计源于 MCU 固件的三个约束:
- 零运行时开销:硬件引脚、时钟分频等一旦上电就必须正确,编译期常量可让编译器在优化时直接内联、消除分支;
- 库与应用的解耦:SDK 以静态库(
libs.a)形式发布,库代码通过extern const引用应用侧符号,应用通过定义这些符号决定库行为(类似于弱符号覆盖机制); - 单点维护:所有板级差异集中到
app_config.h/app_config.c两个文件,换板、换方案时只需修改配置而无需改动库代码。
配置系统的三大组成:
| 层次 | 文件 | 机制 | 作用 |
|---|---|---|---|
| 编译期宏层 | app_config.h | #define + 条件编译 #if | 引脚、时钟、外设使能、存储介质、USB class 等板级参数 |
| 运行期常量层 | app_config.c | const int/u8/bool 全局符号 | 中断优先级、日志标签、更新通道、编解码参数 |
| 公共接口层 | config.h | 宏 + extern 声明 | log_init 等日志接口、IO 调试宏、_OTP_CONST_ 条件修饰 |
Architecture
flowchart TD
subgraph sg_Header["编译期配置层 app_config.h"]
H_SYS["TCFG_* 系统宏<br/>时钟 / Cache / UART / 按键 / 电源"]
H_AUD["音频宏<br/>DAC / MIC / 自动mute / EQ / ALINK"]
H_STO["存储宏<br/>FLASH / SD / VM / ICACHE RAM"]
H_USB["USB 宏<br/>PC / UDISK / CDROM / Device Class"]
H_UI["UI 与更新宏<br/>LED / LCD / 升级支持"]
end
subgraph sg_Runtime["运行期常量层 app_config.c"]
R_IRQ["IRQ_*_IP 中断优先级"]
R_LOG["log_tag_const_* 日志标签开关"]
R_UPD["CONFIG_UPDATE_* 更新通道使能"]
R_MISC["MIDI / 编解码 / RTC / USB slave 参数"]
end
subgraph sg_Consumers["消费模块(SDK 库与应用)"]
C_INIT["init_app.c 系统初始化"]
C_APP["app.c 应用主流程"]
C_TOOL["cfg_tools.c 在线EQ调音"]
C_DEV["device 设备驱动 / flash_init"]
C_KEY["key 按键驱动"]
C_AUD["audio 音频通路"]
end
subgraph sg_API["公共接口层 config.h"]
A_LOG["log_init / log_uninit / log_u8hex"]
A_IO["IO_DEBUG_* 调试IO宏"]
A_OTP["_OTP_CONST_ 条件修饰"]
end
H_SYS --> C_INIT
H_SYS --> C_KEY
H_AUD --> C_AUD
H_AUD --> C_TOOL
H_STO --> C_DEV
H_USB --> C_APP
H_UI --> C_APP
R_IRQ --> C_INIT
R_LOG --> C_APP
R_UPD --> C_APP
R_MISC --> C_AUD
A_LOG --> C_APP
A_IO --> C_INIT
架构说明:
- **编译期配置层(
app_config.h)**是配置系统的核心入口,被 SDK 中几乎所有模块通过#include "app_config.h"引用。它的宏按功能域分组:TCFG_*前缀为“T 系列配置”(T 取自 Tool/Board),TFG_*为 Flash/升级相关,ALINK_*为音频链路相关。 - **运行期常量层(
app_config.c)**定义带const修饰的全局符号。SDK 静态库通过extern引用这些符号(例如IRQ_USB_IP决定 USB 中断抢占优先级),因此这些“变量”实际在链接期即被固化为常量。 - **公共接口层(
config.h)**为应用提供统一的日志与调试宏,其DEBUG_EN由UART_DEBUG宏驱动,是app_config.h中 UART 配置的下游消费者。 - 底层库(
libs.a)与驱动模块均直接依赖以上配置符号,因此修改配置后必须重新编译整个固件;不存在运行期热加载配置的机制。
配置分层详解
编译期宏配置层(app_config.h)
app_config.h 顶部定义了统一开关语义:ENABLE_THIS_MOUDLE / DISABLE_THIS_MOUDLE、ENABLE / DISABLE,以及哨兵值 NO_CONFIG_PORT (-1),所有模块使能宏统一使用 0/1 语义,避免混用 true/false 与 0/1 带来的可读性问题。
#define ENABLE_THIS_MOUDLE 1
#define DISABLE_THIS_MOUDLE 0
#define ENABLE 1
#define DISABLE 0
#define NO_CONFIG_PORT (-1)
Source: app_config.h
系统时钟与 Cache
/*---------系统时钟配置---------------------*/
#define TCFG_SYS_PLL_CLK 96000000
#define TCFG_PLL_SEL PLL_D1p0_192M
#define TCFG_PLL_DIV PLL_DIV2
#define TCFG_HSB_DIV HSB_DIV1
/*---------Cache Configuration--------------*/
#define CPU_USE_CACHE_WAY_NUMBER 4//cache_way范围:2~4
Source: app_config.h
系统主频通过“PLL 源选择 + 分频”两级组合得到:PLL_D1p0_192M 表示 PLL 输出 192 MHz,PLL_DIV2 二分频得到 96 MHz 系统时钟(TCFG_SYS_PLL_CLK 为注释性声明,实际生效值由分频组合决定)。CPU_USE_CACHE_WAY_NUMBER 指定 CPU 使用 4 路 cache(范围 2~4),直接影响 RAM 可用容量与代码执行性能——这是“性能与容量”权衡的典型配置点。
UART 调试口
#define TCFG_UART_TX_PORT IO_PORTA_05 //串口打印发送脚配置
// #define TCFG_UART_RX_PORT IO_PORTA_06 //串口打印接收脚配置
#define TCFG_UART_BAUDRATE 1000000 //串口打印波特率配置
Source: app_config.h
mbox_flash 方案为单 TX 调试口(RX 被注释),波特率 1 Mbps,用于 log_init() 初始化的调试串口。该宏同时通过 UART_DEBUG 联动 config.h 中的 DEBUG_EN,决定日志宏是否编译进固件。
按键输入矩阵
#define KEY_DOUBLE_CLICK_EN DISABLE//使能按键多击
#define KEY_IO_EN 0//<IO按键使能
#define KEY_AD_EN 1//<AD按键使能
#define KEY_MIC_EN 0//<耳机按键使能
#define KEY_MATRIX_EN 0//<矩阵按键使能
#define KEY_IR_EN 0//<IR按键使能
#define KEY_TOUCH_EN 0//<触摸按键使能
#define KEY_LPTOUCH_EN 0
//AD KEY
#define AD_KEY_IO IO_PORTA_08
Source: app_config.h
按键子系统采用“通道选择 + 引脚绑定”模式:KEY_*_EN 决定启用哪些物理通道(IO/AD/MIC/矩阵/IR/触摸/低功耗触摸),对应通道再绑定具体引脚(如 AD_KEY_IO = IO_PORTA_08)。mbox_flash 默认仅使能 AD 按键,触摸与矩阵通道均关闭,以节省引脚资源。
电源、低功耗与软关机
#define TCFG_VDDIOM_LEVEL VDDIOM_VOL_32V
#define POWER_WAKEUP_IO AD_KEY_IO
#define POWER_WAKEUP_EDGE FALLING_EDGE
#define TCFG_CONFIG_LOWPOWER_SEL ENABLE
#define TCFG_SHUTDOWN_TIME 5000 //uint:ms
Source: app_config.h
POWER_WAKEUP_IO 直接复用 AD_KEY_IO,即 AD 按键同时承担唤醒源职责,下降沿触发——这是硬件设计上“一 IO 两用”的典型体现。TCFG_CONFIG_LOWPOWER_SEL = ENABLE 开启低功耗模式,TCFG_SHUTDOWN_TIME = 5000ms 定义无操作后软关机延时。
充电管理
#define TCFG_CHARGE_ENABLE 0//ENABLE
#define TCFG_CHARGE_FULL_V CHARGE_FULL_V_4199
#define TCFG_CHARGE_FULL_MA CHARGE_FULL_mA_DIV10
#define TCFG_CHARGE_MA CHARGE_mA_90
#define TCFG_CHARGE_TRICKLE_MA CHARGE_mA_20
Source: app_config.h
mbox_flash 方案默认关闭片内充电(TCFG_CHARGE_ENABLE = 0),但保留完整的充电参数占位:满电电压 4.199V、恒流 90mA、涓流 20mA。这些 CHARGE_* 枚举来自 PMU 驱动,枚举值经宏转发,保证应用层与库层使用同一套电压/电流档位定义。
音频通路(DAC / MIC / 自动 mute / 淡入淡出 / EQ)
#define SR_DEFAULT 32000
#define AMM_RS_INSIDE_ENABLE 0
#define AMM_RS_OUTSIDE_ENABLE 1
#define AMM_DIFF_ENABLE 0
#define TCFG_AUDIO_AUTO_MUTE_ENABLE 0//不支持
#define TCFG_AUTO_MUTE_ENERGY 5//自动mute能量阈值
#define TCFG_AUTO_MUTE_TIME 1000//自动mute的时间
#define TCFG_AUTO_UNMUTE_ENERGY 10//自动解除mute能量阈值
#define TCFG_AUTO_UNMUTE_TIME 50//自动解除mute的时间
#define TCFG_DAC_VOL_FADE_EN DISABLE//硬件DAC淡入淡出
#define DAC_SOFT_FADE_EN DISABLE//软件运算淡入淡出
#define TCFG_DAC_VOL_FADE_STEP 1//0和1一样效果,配置范围0~15
#define TCFG_DAC_VOL_FADE_SLOW 15//配置范围0~15
#define TCFG_CFG_TOOL_ENABLE DISABLE//配置工具使能,需要在线调试eq时打开,默认关闭
#define TCFG_COMM_TYPE TCFG_USB_COMM//暂时只支持USB通信调音
Source: app_config.h
音频配置展示了“功能开关 + 阈值参数”的配置模式:
AMM_RS_*决定麦克风回采(reference sound)路径:mbox_flash 使用外侧回采(AMM_RS_OUTSIDE_ENABLE = 1),即采集环境声用于回声抑制;- 自动 mute 功能在 mbox_flash 上不支持(
TCFG_AUDIO_AUTO_MUTE_ENABLE = 0),但阈值/时间参数已预留,换方案板时只需使能开关; - DAC 数字音量淡入淡出提供硬件(
TCFG_DAC_VOL_FADE_EN)与软件(DAC_SOFT_FADE_EN)两种实现,STEP/SLOW控制爬坡速率(step 范围 0~15,slow 范围 0~15),用于消除音量突变爆音; TCFG_CFG_TOOL_ENABLE控制在线 EQ 调音工具,默认关闭;开启后config.h同层联动的USB_DEVICE_CLASS_CONFIG会切换为CDC_CLASS|AUDIO_CLASS|HID_CLASS(见下文 USB 章节),即一个宏改变 USB 枚举组合,体现宏配置的联动性。
AUDIO_LINK(音频链路)
#define ALINK_MODE_TYPE ALINK_MD_IIS
#define ALINK_ROLE_TYPE ALINK_ROLE_MASTER
#define ALINK_CLK_MODE_TYPE ALINK_CLK_FALL_UPDATE_RAISE_SAMPLE
#define ALINK_BIT_WIDE_TYPE ALINK_LEN_16BIT
#define ALINK_SCLK_PER_FRAME_TYPE ALINK_FRAME_64SCLK
#define ALINK_DMA_LEN ALNK_BUF_POINTS_NUM * 2 * 2 * 2
#define ALINK_SR_DEFAULT ALINK_SR_32000
#define ALINK_MAX_CHANNEL 4
#define ALINK_MCLK_IO IO_PORTA_02
#define ALINK_SCLK_IO IO_PORTA_10
#define ALINK_LCLK_IO IO_PORTA_09
#define ALINK_DATA_IO0 IO_PORTA_14
#define ALINK_DATA_IO1 IO_PORTA_15
#define ALINK_DATA_IO2 NULL
#define ALINK_DATA_IO3 NULL
Source: app_config.h
AUDIO_LINK 是 AD16N 与外部 Codec/功放之间的数字音频总线。mbox_flash 配置为 I2S 主机(ALINK_MD_IIS + ALINK_ROLE_MASTER),16bit、32kHz、每帧 64 SCLK,支持最多 4 通道。ALINK_DMA_LEN 由 ALNK_BUF_POINTS_NUM 派生(128 点 × 2CH × 2Byte × 双缓冲),配置引脚则采用 IO_PORTx_yy 形式,NULL 表示未使用的数据通道——音频链路数据宽度与 DMA 缓冲深度在此一次性定型,运行期不可改。
存储介质(FLASH / SD / VM)
#define VM_SFC_ENABLE ENABLE
#define USE_NEW_VM 1
#define USE_OLD_VM 2
#define SYS_MEMORY_SELECT USE_NEW_VM
#if RUN_APP_CUSTOM
#define TFG_EXT_FLASH_EN ENABLE
#else
#define TFG_EXT_FLASH_EN DISABLE
#endif
#define TFG_SPI_HW_NUM 1
#define TFG_SPI_UNIDIR_MODE_EN DISABLE//外挂flash单线模式
#define TFG_SPI_WORK_MODE SPI_MODE_BIDIR_1BIT
#define TFG_SPI_CS_PORT_SEL IO_PORTB_00
#define TFG_SPI_CLK_PORT_SEL IO_PORTB_01
#define TFG_SPI_DO_PORT_SEL IO_PORTB_02
#define TFG_SPI_DI_PORT_SEL IO_PORTB_03
Source: app_config.h
存储配置体现了条件编译的复用策略:RUN_APP_CUSTOM(由构建系统传入)决定是否使能外挂 Flash 与 SD 卡——同一份 app_config.h 通过外部宏开关服务两种衍生方案。VM(Value Memory,键值存储)选择新版本(USE_NEW_VM),VM_SFC_ENABLE 表示 VM 数据区放在 SFC(SPI Flash Controller)管理的外挂 Flash 上。SPI 引脚 IO_PORTB_00~03 为外挂 Flash 的 CS/CLK/DO/DI 四线。
USB 从机配置
#if ((HAS_USB_EN) && (0 == RUN_APP_RC))
#define TCFG_PC_ENABLE ENABLE //PC模块使能
#define TCFG_UDISK_ENABLE ENABLE //U盘模块使能
#define POWERDOWN_UDISK_MODE_EN ENABLE //U盘插入时可进入pdown
#else
#define TCFG_PC_ENABLE DISABLE
#define TCFG_UDISK_ENABLE DISABLE
#define POWERDOWN_UDISK_MODE_EN DISABLE
#endif
#define TCFG_USB_PORT_CHARGE DISABLE
#define TCFG_OTG_USB_DEV_EN BIT(0)
#if TCFG_PC_ENABLE
#define USB_DEVICE_EN //Enable USB SLAVE MODE
#endif
#if TCFG_UDISK_ENABLE
#define USB_DISK_EN //是否可以读U盘
#endif
#if TCFG_CFG_TOOL_ENABLE
#define USB_DEVICE_CLASS_CONFIG (CDC_CLASS|AUDIO_CLASS|HID_CLASS)
#elif defined(CONFIG_APP_OTA_EN) && (CONFIG_APP_OTA_EN == 1)
#define USB_DEVICE_CLASS_CONFIG (AUDIO_CLASS|HID_CLASS|CUSTOM_HID_CLASS)
#else
#define USB_DEVICE_CLASS_CONFIG (AUDIO_CLASS|HID_CLASS|MASSSTORAGE_CLASS)
#endif
Source: app_config.h
USB 配置是三层联动的典型:
- 能力层:
HAS_USB_EN(全局 USB 能力)与RUN_APP_RC(方案类型)共同决定 PC/U 盘模块是否使能,并进一步派生出USB_DEVICE_EN/USB_DISK_EN两个次级宏,供驱动层#ifdef使用; - Class 层:
USB_DEVICE_CLASS_CONFIG按优先级(在线调音工具 > OTA 升级 > 默认)选择 USB 枚举的 Class 组合——同一硬件根据固件用途呈现为不同的 USB 设备形态(声卡+U 盘 / 声卡+HID 升级口 / 声卡+CDC 调音口); - 电源层:
TCFG_USB_PORT_CHARGE与POWERDOWN_UDISK_MODE_EN决定 USB 口是否参与充电及插入 U 盘时的低功耗策略。
UI 与更新
#define TFG_DEV_UPGRADE_SUPPORT ENABLE
#define TFG_UPGRADE_FILE_NAME "/update.ufw"
#if RUN_APP_CUSTOM
#define LED_5X7 ENABLE
#else
#define LED_5X7 DISABLE
#endif
#define LCD_4X8_EN 0
#if (LED_5X7)
#define UI_ENABLE ENABLE
#else
#define UI_ENABLE DISABLE
#endif
Source: app_config.h
TFG_DEV_UPGRADE_SUPPORT = ENABLE 开启设备升级,升级固件文件固定为根目录 /update.ufw。UI 层通过 LED_5X7/LCD_4X8_EN 派生总开关 UI_ENABLE,且 LED 5×7 点阵仅在 RUN_APP_CUSTOM 定制方案上使能——UI 使能逻辑完全由显示硬件存在性推导,避免“开了 UI 但无显示器件”的无效代码。
运行期常量配置层(app_config.c)
app_config.c 定义被 SDK 库引用的 const 全局符号。与宏不同,这些符号必须以外部链接可见(非 static),库才能通过 extern 引用。
中断优先级
//系统使用到的
const int IRQ_IRTMR_IP = 6;
const int IRQ_AUDIO_IP = 5;
const int IRQ_DECODER_IP = 0;
const int IRQ_WFILE_IP = 2;
const int IRQ_RF_QUEUE_IP = 2;
const int IRQ_ADC_IP = 3;
const int IRQ_ENCODER_IP = 1;
const int IRQ_TICKTMR_IP = 3;
const int IRQ_USB_IP = 4;
const int IRQ_SD_IP = 3;
const int IRQ_CTMU_IP = 2;
const int IRQ_STREAM_IP = 4;
const int IRQ_SPEAKER_IP = 1;
const int IRQ_LEDC_IP = 1;
const int IRQ_SLCD_IP = 2;
const int IRQ_PMU_TMR1_IP = 3;
const int IRQ_ALINK_IP = 3;
//系统还未使用到的
const int IRQ_UART0_IP = 3;
const int IRQ_UART1_IP = 3;
const int IRQ_ALINK0_IP = 3;
Source: app_config.c
中断优先级采用数字越小优先级越高的嵌入式惯例(IRQ_DECODER_IP = 0 最高)。设计意图:解码器实时性最强(音频流不能断),故分配最高优先级;日志/调试相关(IRQ_UART0_IP = 3)居中;IRQ_BT_TIMEBASE_IP = 6 等 BT 相关中断在 mbox_flash 非蓝牙方案中预留。文件注释明确区分“系统使用到”与“系统还未使用到”,为裁剪提供依据。
日志标签总开关
const char libs_debug AT(.LOG_TAG_CONST) = TRUE; //打印总开关
#define CONFIG_DEBUG_LIBS(X) (X & libs_debug)
const char log_tag_const_i_MAIN AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIBS(1);
const char log_tag_const_d_MAIN AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIBS(1);
const char log_tag_const_e_MAIN AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIBS(1);
const char log_tag_const_c_MAIN AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIBS(1);
const char log_tag_const_i_KEYM AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIBS(1);
const char log_tag_const_d_KEYM AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIBS(0);
const char log_tag_const_e_KEYM AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIBS(0);
Source: app_config.c
日志系统通过 AT(.LOG_TAG_CONST) 段属性把每个模块的 i/d/e/c(info/debug/error/close)四个级别标签放入专用段,CONFIG_DEBUG_LIBS(X) 与 libs_debug 总开关做与运算:总开关关掉时,所有模块日志全部关闭。模块级可按需单独开 debug(如 d_MAIN 开、d_KEYM 关),在不重新编译库的前提下(符号在链接期解析)实现日志裁剪。
更新通道使能
const int CONFIG_UPDATE_STORAGE_DEV_EN = 1;
const int CONFIG_UPDATE_TESTBOX_UART_EN = 1;
const int CONFIG_UPDATE_APP_OTA_EN = 1;
const int CONFIG_UPDATE_TESTBOX_BLE_EN = 1;
Source: app_config.c
mbox_flash 同时使能四条升级通道:本地存储设备升级(STORAGE_DEV)、测试盒 UART 升级(TESTBOX_UART)、App OTA 升级(APP_OTA)、测试盒 BLE 升级(TESTBOX_BLE)。这些 const int 符号由升级库在运行期读取,决定固件启动时轮询哪些升级源——与 app_config.h 中的 TFG_DEV_UPGRADE_SUPPORT(总开关)形成“编译期总开关 + 运行期通道选择”的两级控制。
MIDI / 编解码 / 杂项参数
#if (DECODER_MIDI_EN || DECODER_MIDI_KEYBOARD_EN)
const int MAINTRACK_USE_CHN = 0; //0:用track号来区分 1:用channel号来区分
const int MAX_DEC_PLAYER_CNT = 8; //midi乐谱解码最大同时发声的key数,范围[1,31]
const int MAX_CTR_PLAYER_CNT = 8; //midi琴最大同时发声的key数,范围[1,31]
const int NOTE_OFF_TRIGGER = 0; //midi琴note_off回调
const int MIDI_OUT_CHANNEL = 2; //midi输出声道1or2
const int MIDI_CTRL_OUT_CHANNEL = 2; //midi琴输出声道1or2
#endif
#if RTC_EN
const int config_rtc_enable = 1;
#else
const int config_rtc_enable = 0;
#endif
const bool config_usbslave_ctl_mic = 1;
bool const config_decoder_auto_mutex = 1;
const u8 config_no_osc_enable = 0;//ini:PLL=LRC;
Source: app_config.c
- MIDI 参数被
DECODER_MIDI_EN || DECODER_MIDI_KEYBOARD_EN条件包裹,解码器未使能时整个段不参与编译——条件编译同样适用于运行期常量文件,避免无意义符号占用空间; config_rtc_enable由RTC_EN宏推导为 1/0 常量,与库中#if config_rtc_enable运行时分支配合,使同一份库适配有无 RTC 的芯片;config_usbslave_ctl_mic使能 USB 从机麦克风控制(声卡模式录音路径);config_no_osc_enable = 0关闭无晶振模式(该模式下 PLL 由 LRC 时钟驱动,省 BOM 但精度受限)。
公共配置接口层(config.h)
config.h 是应用可见的公共配置接口头,被 includes.h 体系统一引用,提供日志与调试设施:
#if UART_DEBUG
#define DEBUG_EN
#endif
extern const char libs_debug;
#ifdef DEBUG_EN
#define log_init(x) debug_uart_init(x)
#define log_uninit() uart_uninit()
#define log_u8hex(x) put_u8hex(x)
#else
#define log_init(...)
#define log_uninit()
#define log_u8hex(...)
#endif
#define log(...)
#define IO_DEBUG_0(i,x) {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT &= ~BIT(x);}
#define IO_DEBUG_1(i,x) {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT |= BIT(x);}
#define IO_DEBUG_TOGGLE(i,x) {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT ^= BIT(x);}
#ifdef D_IS_FLASH_SYSTEM
#define _OTP_CONST_
#else
#define _OTP_CONST_ const
#endif
Source: config.h
设计意图:
- 日志宏按
UART_DEBUG自动失效:未定义UART_DEBUG时,log_init(...)等宏被展开为空操作,调用点零开销,无需逐处#ifdef——这是“配置驱动接口裁剪”的典型手法; extern const char libs_debug与app_config.c中的定义对应,构成库对应用符号的引用闭环;IO_DEBUG_*宏直接操作 GPIO 寄存器(DIR 置输出 + OUT 置位/清零/翻转),用于示波器打点测时序,是低层调试利器;_OTP_CONST_根据D_IS_FLASH_SYSTEM决定常量是否真正放入 const 段——Flash 系统(D_IS_FLASH_SYSTEM)可直接把只读数据放 Flash 省 RAM,普通系统则退化为const。
核心配置流
配置从“开发者修改”到“固件行为生效”的完整链路:
flowchart LR
Dev["开发者修改宏/常量"] --> H["app_config.h 宏定义"]
Dev --> C["app_config.c const 符号"]
H --> P["预处理器 #if / #ifdef / #define 展开"]
P --> K["SDK 库源码条件编译"]
P --> A["应用模块条件编译"]
C --> L["链接器符号解析 extern"]
K --> B["固件镜像 .fw"]
A --> B
L --> B
B --> F["烧录到 SPI Flash"]
F --> R["运行期行为<br/>时钟/外设/中断/日志/升级通道"]
关键结论:宏路径(左路)在预处理期决定“代码是否存在”,符号路径(右路)在链接期决定“参数取值”。两条路径最终都固化进二进制,因此配置系统的任何修改都必须触发全量重新编译 + 重新烧录。
配置联动示例:开启在线 EQ 调音
flowchart TD
Start["TCFG_CFG_TOOL_ENABLE = ENABLE"] --> U1["USB_DEVICE_CLASS_CONFIG<br/>= CDC|AUDIO|HID"]
Start --> U2["TCFG_COMM_TYPE = TCFG_USB_COMM"]
U1 --> D1["USB 枚举为 声卡+CDC+HID"]
U2 --> D2["cfg_tools.c 走 USB 收发调音指令"]
D1 --> E["PC 端 EQ 助手可连接"]
D2 --> E
这一个宏开启后触发 USB Class 切换与通信通道选择两级联动,最终让 PC 端调音工具可连接——展示配置宏之间“总开关驱动派生宏”的联动设计,换方案时只需改一处。
使用示例
示例 1:为定制方案启用外挂 Flash 与 SD 卡
app_config.h 通过 RUN_APP_CUSTOM 一键切换存储介质配置,无需改动任何库代码:
#if RUN_APP_CUSTOM
#define TFG_EXT_FLASH_EN ENABLE
#else
#define TFG_EXT_FLASH_EN DISABLE
#endif
...
#if RUN_APP_CUSTOM
#define TFG_SD_EN ENABLE
#else
#define TFG_SD_EN DISABLE
#endif
Source: app_config.h
示例 2:切换 USB 设备形态
默认(音乐播放器形态)枚举为 AUDIO|HID|MASSSTORAGE,开启 OTA 后变为 AUDIO|HID|CUSTOM_HID:
#if TCFG_CFG_TOOL_ENABLE
#define USB_DEVICE_CLASS_CONFIG (CDC_CLASS|AUDIO_CLASS|HID_CLASS) //在线EQ调试助手
#elif defined(CONFIG_APP_OTA_EN) && (CONFIG_APP_OTA_EN == 1)
#define USB_DEVICE_CLASS_CONFIG (AUDIO_CLASS|HID_CLASS|CUSTOM_HID_CLASS) //ota升级
#else
#define USB_DEVICE_CLASS_CONFIG (AUDIO_CLASS|HID_CLASS|MASSSTORAGE_CLASS) //默认配置
#endif
Source: app_config.h
示例 3:运行期读取更新通道开关
升级库在启动阶段轮询各更新源时读取 CONFIG_UPDATE_* 常量,应用只需修改 app_config.c 即可增删升级通道:
const int CONFIG_UPDATE_STORAGE_DEV_EN = 1;
const int CONFIG_UPDATE_TESTBOX_UART_EN = 1;
const int CONFIG_UPDATE_APP_OTA_EN = 1;
const int CONFIG_UPDATE_TESTBOX_BLE_EN = 1;
Source: app_config.c
配置选项参考
系统级配置(app_config.h)
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
TCFG_SYS_PLL_CLK | int | 96000000 | 目标系统时钟(Hz),实际由 PLL 组合决定 |
TCFG_PLL_SEL | enum | PLL_D1p0_192M | PLL 输出源选择(192MHz) |
TCFG_PLL_DIV | enum | PLL_DIV2 | PLL 分频(二分频 → 96MHz) |
TCFG_HSB_DIV | enum | HSB_DIV1 | 高速总线分频 |
CPU_USE_CACHE_WAY_NUMBER | int | 4 | CPU cache 路数,范围 2~4 |
TCFG_UART_TX_PORT | IO | IO_PORTA_05 | 调试串口发送脚 |
TCFG_UART_BAUDRATE | int | 1000000 | 调试串口波特率 |
TCFG_VDDIOM_LEVEL | enum | VDDIOM_VOL_32V | IO 电源电压档位 |
POWER_WAKEUP_IO | IO | AD_KEY_IO | 低功耗唤醒 IO |
POWER_WAKEUP_EDGE | enum | FALLING_EDGE | 唤醒触发沿 |
TCFG_CONFIG_LOWPOWER_SEL | 0/1 | ENABLE | 低功耗模式使能 |
TCFG_SHUTDOWN_TIME | int | 5000 | 软关机延时(ms) |
按键配置(app_config.h)
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
KEY_DOUBLE_CLICK_EN | 0/1 | DISABLE | 按键多击使能 |
KEY_IO_EN | 0/1 | 0 | IO 按键使能 |
KEY_AD_EN | 0/1 | 1 | AD 按键使能(mbox_flash 默认通道) |
KEY_MIC_EN | 0/1 | 0 | 耳机按键使能 |
KEY_MATRIX_EN | 0/1 | 0 | 矩阵按键使能 |
KEY_IR_EN | 0/1 | 0 | IR 按键使能 |
KEY_TOUCH_EN | 0/1 | 0 | 触摸按键使能 |
KEY_LPTOUCH_EN | 0/1 | 0 | 低功耗触摸按键使能 |
AD_KEY_IO | IO | IO_PORTA_08 | AD 按键采样脚 |
音频配置(app_config.h)
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
SR_DEFAULT | int | 32000 | 默认采样率(Hz) |
AMM_RS_INSIDE_ENABLE | 0/1 | 0 | 内侧回采使能(回声抑制) |
AMM_RS_OUTSIDE_ENABLE | 0/1 | 1 | 外侧回采使能 |
TCFG_AUDIO_AUTO_MUTE_ENABLE | 0/1 | 0 | 自动 mute 使能(mbox_flash 不支持) |
TCFG_AUTO_MUTE_ENERGY | int | 5 | 自动 mute 能量阈值 |
TCFG_AUTO_UNMUTE_ENERGY | int | 10 | 自动解除 mute 能量阈值 |
TCFG_DAC_VOL_FADE_EN | 0/1 | DISABLE | 硬件 DAC 音量淡入淡出 |
DAC_SOFT_FADE_EN | 0/1 | DISABLE | 软件音量淡入淡出 |
TCFG_DAC_VOL_FADE_STEP | int | 1 | 淡入淡出步进(0~15) |
TCFG_DAC_VOL_FADE_SLOW | int | 15 | 淡入淡出慢速系数(0~15) |
TCFG_CFG_TOOL_ENABLE | 0/1 | DISABLE | 在线 EQ 配置工具使能 |
TCFG_COMM_TYPE | enum | TCFG_USB_COMM | 调音通信通道(USB) |
AUDIO_LINK 配置(app_config.h)
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ALINK_MODE_TYPE | enum | ALINK_MD_IIS | 音频链路模式(I2S) |
ALINK_ROLE_TYPE | enum | ALINK_ROLE_MASTER | 主/从角色(主机) |
ALINK_CLK_MODE_TYPE | enum | ALINK_CLK_FALL_UPDATE_RAISE_SAMPLE | 时钟沿采样模式 |
ALINK_BIT_WIDE_TYPE | enum | ALINK_LEN_16BIT | 位宽 16bit |
ALINK_SCLK_PER_FRAME_TYPE | enum | ALINK_FRAME_64SCLK | 每帧 SCLK 数 |
ALINK_SR_DEFAULT | enum | ALINK_SR_32000 | 链路采样率 |
ALINK_MAX_CHANNEL | int | 4 | 最大通道数 |
ALINK_MCLK_IO | IO | IO_PORTA_02 | 主时钟引脚 |
存储与 USB 配置(app_config.h)
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
VM_SFC_ENABLE | 0/1 | ENABLE | VM 数据区放 SFC Flash |
SYS_MEMORY_SELECT | enum | USE_NEW_VM | VM 版本选择(新/旧) |
TFG_EXT_FLASH_EN | 0/1 | 依 RUN_APP_CUSTOM | 外挂 Flash 使能 |
TFG_SD_EN | 0/1 | 依 RUN_APP_CUSTOM | SD 卡使能 |
TFG_SPI_HW_NUM | int | 1 | SPI 硬件编号 |
TFG_SPI_WORK_MODE | enum | SPI_MODE_BIDIR_1BIT | SPI 工作模式(双向 1bit) |
TFG_DEV_UPGRADE_SUPPORT | 0/1 | ENABLE | 设备升级总开关 |
TFG_UPGRADE_FILE_NAME | string | "/update.ufw" | 升级固件路径 |
TCFG_PC_ENABLE | 0/1 | ENABLE | PC 模块(声卡)使能 |
TCFG_UDISK_ENABLE | 0/1 | ENABLE | U 盘模块使能 |
TCFG_USB_PORT_CHARGE | 0/1 | DISABLE | USB 口充电使能 |
TCFG_OTG_USB_DEV_EN | bit | BIT(0) | USB 设备口选择(USB0) |
USB_DEVICE_CLASS_CONFIG | 位或 | AUDIO|HID|MASSSTORAGE | USB 枚举 Class 组合 |
运行期常量配置(app_config.c)
| 符号 | 类型 | 默认值 | 说明 |
|---|---|---|---|
IRQ_DECODER_IP | int | 0 | 解码器中断优先级(最高) |
IRQ_AUDIO_IP | int | 5 | 音频中断优先级 |
IRQ_USB_IP | int | 4 | USB 中断优先级 |
IRQ_BT_TIMEBASE_IP | int | 6 | BT 时基中断优先级 |
config_usbslave_ctl_mic | bool | 1 | USB 从机麦克风控制使能 |
config_decoder_auto_mutex | bool | 1 | 解码器自动互斥 |
config_rtc_enable | int | 依 RTC_EN | RTC 使能(推导常量) |
config_no_osc_enable | u8 | 0 | 无晶振模式使能 |
CONFIG_UPDATE_STORAGE_DEV_EN | int | 1 | 本地存储升级通道 |
CONFIG_UPDATE_TESTBOX_UART_EN | int | 1 | 测试盒 UART 升级通道 |
CONFIG_UPDATE_APP_OTA_EN | int | 1 | App OTA 升级通道 |
CONFIG_UPDATE_TESTBOX_BLE_EN | int | 1 | 测试盒 BLE 升级通道 |
libs_debug | char | TRUE | 日志打印总开关 |
API 参考(config.h)
log_init(x)
初始化调试串口(debug_uart_init(x))。UART_DEBUG 未定义时展开为空操作。
参数: x — UART 配置(波特率等,由 TCFG_UART_* 宏驱动)
说明: 由 UART_DEBUG → DEBUG_EN 条件编译控制是否真实生效。
log_uninit()
关闭调试串口(uart_uninit())。未定义 DEBUG_EN 时为空操作。
log_u8hex(x)
以十六进制输出单字节(put_u8hex(x)),用于调试打印二进制数据。
IO_DEBUG_0(i, x) / IO_DEBUG_1(i, x) / IO_DEBUG_TOGGLE(i, x)
直接操作 GPIO 寄存器拉低/拉高/翻转指定 IO,用于示波器测时序。i 为端口号(如 A),x 为位号(如 5 → PA05)。宏内部自动将 IO 方向置为输出。
_OTP_CONST_
条件常量修饰符:D_IS_FLASH_SYSTEM 定义为空(数据可直接驻留 Flash),否则展开为 const。用于 OTP/Flash 启动系统节省 RAM。
失败模式、边界情况与并发
配置错误导致的失败模式
| 失败模式 | 触发场景 | 表现与排查 |
|---|---|---|
| 引脚冲突 | 多个模块宏绑定同一 IO(如 ALINK_MCLK_IO 与按键 IO 复用) | 外设工作异常/时序错乱;交叉核对各 IO_PORTx_yy 宏 |
| 时钟超频/欠频 | TCFG_PLL_SEL/TCFG_PLL_DIV 组合超出芯片规格 | 系统挂死、USB 枚举失败;检查 TCFG_SYS_PLL_CLK 与分频乘积 |
| 中断优先级冲突 | 两个实时模块同优先级抢占 | 音频卡顿/数据丢失;参考 app_config.c 注释的“使用到/未使用到”分区调整 |
| 存储介质缺失 | 使能 TFG_SD_EN/TFG_EXT_FLASH_EN 但硬件未贴片 | 启动卡在存储初始化;用 log_tag_const_*_FLASH 日志定位 |
| 升级通道误开 | CONFIG_UPDATE_* 全开且固件无对应源 | 启动时逐个轮询升级源造成延时;按方案裁剪通道 |
| USB Class 冲突 | 同时使能 TCFG_CFG_TOOL_ENABLE 与 CONFIG_APP_OTA_EN | USB_DEVICE_CLASS_CONFIG 按优先级取第一个分支,行为不确定;避免同时开启 |
边界情况
RUN_APP_CUSTOM派生依赖:TFG_EXT_FLASH_EN、TFG_SD_EN、LED_5X7都依赖RUN_APP_CUSTOM。若构建系统未传入该宏,#if RUN_APP_CUSTOM按 0 处理,全部走默认分支——未定义即关闭是 C 预处理器的隐性默认,换方案时最容易踩坑;NO_CONFIG_PORT (-1)哨兵:用于表示“无配置引脚”的端口场景,与合法 IO 编号区分,避免把 -1 当真实引脚操作;- 注释掉 RX 口:
TCFG_UART_RX_PORT被注释而非置空,说明该方案仅需 TX 单线打印,若需双向通信须同时恢复 RX 定义; - 枚举越界:
CHARGE_*、VDDIOM_VOL_*、ALINK_*等枚举值必须取自有定义范围,误写数值可能被驱动静默忽略。
并发与一致性
- 配置均为编译期/链接期固化,运行期无并发读写问题——这既是优势(无竞态、无缓存一致性问题)也是约束(无法热更新);
- 中断优先级常量在系统启动早期由库读取并配置 NVIC,此后不可变;多中断并发时,优先级编排(见
IRQ_*_IP)直接决定音频/解码/USB 的抢占时序,配置错误表现为偶发音频断流或 USB 丢包; libs_debug与各log_tag_const_*位于.LOG_TAG_CONST专用段,日志打印在中断上下文中只读该符号,无写入路径,天然线程安全。
性能与运维考虑
- cache 路数权衡:
CPU_USE_CACHE_WAY_NUMBER增大提升代码执行性能但减少 RAM(cache RAM 与普通 RAM 互斥),mbox_flash 取 4 路上限;调试 RAM 不足时可降为 2; - ICACHE RAM 复用:
ICACHE_RAM_TO_RAM_ENABLE可将 ICACHE RAM 挪用为普通 RAM(mbox_flash 默认关闭,预留 8192 字节档位),是“性能换容量”的最终手段; - 日志段开销:
log_tag_const_*即使值为 0 也占用.LOG_TAG_CONST段空间,量产固件应把不必要模块置 0 以压缩日志段; - 升级轮询延时:四条
CONFIG_UPDATE_*通道全开时,启动需依次探测存储/UART/OTA/BLE 升级源,量产方案建议只保留实际使用的通道以减少启动时间; - 编译耦合:任何配置修改都会触发大范围重编译(
app_config.h被几乎所有模块包含),建议修改集中在功能分支提交前,避免频繁全量构建。
扩展点
- 新增板级方案:复制 mbox_flash 目录结构,通过构建系统传入新的
RUN_APP_*宏(如RUN_APP_CUSTOM),在app_config.h中按该宏条件分支配置存储/USB/UI——现有文件已示范#if RUN_APP_CUSTOM双分支写法; - 新增日志模块标签:在
app_config.c仿照log_tag_const_*_XXX格式添加模块标签,并在该模块源码中引用,即可获得独立日志开关; - 新增升级通道:仿照
CONFIG_UPDATE_*增加const int符号并配套轮询逻辑,通道使能与TFG_DEV_UPGRADE_SUPPORT总开关分层管理; - 新增外设:在
app_config.h增加TCFG_*引脚/使能宏,驱动层以#if引用,保持“配置集中、消费分散”的既有模式; - VM 版本切换:
SYS_MEMORY_SELECT可在新/旧 VM 实现间切换(USE_NEW_VM/USE_OLD_VM),用于兼容历史数据格式。
测试与验证
- 配置的正确性主要由板级冒烟测试验证:时钟(
TCFG_SYS_PLL_CLK对应实际主频)、UART 打印(TCFG_UART_*)、按键(KEY_*)、USB 枚举(USB_DEVICE_CLASS_CONFIG对应的设备形态)、升级通道(CONFIG_UPDATE_*)均可在开机日志中直接观测; - 日志系统本身即测试工具:
log_tag_const_*_FLASH、_SPI1、_USB等模块标签可在不重编库的前提下单独打开(置 1),用于驱动层问题定位; IO_DEBUG_*宏为硬件时序测量提供打点能力,配合示波器验证中断响应与 DMA 时序;- 在线 EQ 配置工具(
TCFG_CFG_TOOL_ENABLE)开启后可对音频通路做量产前的调音验证,验证完成后必须关闭(其 USB Class 分支与 OTA 分支互斥)。