系统基础服务
系统基础服务是 AD16N 通用音频 MCU SDK 中支撑整个应用程序运行的底层服务集合,涵盖系统启动引导(c_main)、电源管理(sys_power_init/sys_power_down)、看门狗(wdt_init/wdt_clear)、系统定时器(sys_timer_init)、异常分析与掩码调试(mask_init)以及时钟/PLL 配置(pll_sel)等基础能力。
Purpose and Scope
本页面向 AD16N SDK 的系统基础服务层,说明这些服务在应用启动与运行生命周期中的职责、调用顺序、实现机制与配置方式。内容以 sdk/apps/app/bsp/start/uc03/main.c(启动入口)与 sdk/apps/app/bsp/cpu/uc03/power_api.c(电源管理)为骨架,结合各应用模块(如 idle_check.c、music_play.c、rtc_mode.c)对基础服务的实际调用展开。
以下相关主题由其他页面覆盖,本页不展开:
- 音频解码/播放服务(
bsp/common/decoder、bsp/common/audio)→ 参见音频服务相关页面。 - 配置存储服务(
bsp/common/config/cfg_tools.c)→ 参见配置服务页面。 - 外设驱动(IO、UART、SD、LED 等)→ 参见外设驱动页面。
Overview
系统基础服务位于应用层与芯片硬件之间,是 app() 业务逻辑得以运行的先决条件。SDK 的启动入口 c_main(int cfg_addr) 在汇编启动代码之后被调用,它按严格的顺序完成:关闭并重新配置看门狗 → 初始化异常掩码与调试设施 → 中断系统初始化 → IO 初始化 → 日志(串口)初始化 → PLL/时钟配置 → 内存堆初始化 → 系统定时器初始化 → 电源管理初始化 → 业务系统初始化 → 进入应用主循环。
设计意图在于:让所有硬件级前置条件(时钟、电源、内存、中断、看门狗)在业务代码运行前被确定性地建立,从而保证 app() 中任意模块(播放、RTC、低功耗)都能直接使用这些服务而不必关心底层时序。同时,电源管理服务通过 sys_power_down(usec) 提供统一的低功耗入口,业务模块(如空闲检测、音乐暂停、RTC 模式)在关键路径上直接调用它以进入 powerdown。
Architecture
下图展示系统基础服务在启动流程与运行期中的位置及相互关系:
flowchart TD
subgraph sg_Startup["启动阶段 (c_main)"]
A["wdt_close / wdt_init(WDT_8S)"] --> B["mask_init_for_app<br/>(mask_init + sd_mask_init)"]
B --> C["irq_init / port_init"]
C --> D["log_init(TCFG_UART_BAUDRATE)"]
D --> E["pll_sel(TCFG_PLL_SEL, TCFG_PLL_DIV, TCFG_HSB_DIV)"]
E --> F["my_malloc_init"]
F --> G["sys_timer_init (SYS_TIMER_EN)"]
G --> H["sys_power_init"]
H --> I["system_init"]
I --> J["app() 业务主循环"]
end
subgraph sg_Runtime["运行期服务调用"]
J --> K["wdt_clear 周期喂狗"]
K --> L["sys_power_down(usec) 低功耗"]
end
subgraph sg_Modules["业务模块调用方"]
M["idle_check.c 空闲检测"]
N["music_play.c 音乐暂停"]
O["rtc_mode.c RTC 模式"]
end
M --> L
N --> L
O --> L
L --> P["power_api.c 电源管理"]
架构说明:
- 启动链路是单向依赖链:每一步都以前一步为前置条件。例如
sys_timer_init()依赖my_malloc_init()建立的堆,sys_power_init()依赖时钟与中断就绪,而system_init()/app()依赖上述全部服务。 - 看门狗在启动早期被关闭(
wdt_close())以允许慢速初始化,随后以wdt_init(WDT_8S)重新开启,业务主循环while(1)中必须周期wdt_clear(),否则系统复位。 - 电源管理服务是运行期的公共低功耗出口,多个业务模块在关屏/暂停/待机场景下调用
sys_power_down(-2)进入 powerdown,由power_api.c统一实现。 - 异常分析服务(
mask_init)在config_exception_enable使能时挂接exception_analyze回调,用于崩溃现场分析;SD 卡相关的sd_mask_init则为 SD 调试提供超时回调。
启动流程详解:c_main 引导序列
系统上电后,芯片复位向量跳转到汇编启动代码,最终调用应用入口 c_main(int cfg_addr)。它是系统基础服务在启动阶段的"总调度器",其实现位于 sdk/apps/app/bsp/start/uc03/main.c:
__attribute__((noreturn))
void c_main(int cfg_addr)
{
wdt_close();
mask_init_for_app();
irq_init();
local_irq_disable();
local_irq_enable();
//上电初始化所有IO
port_init();
log_init(TCFG_UART_BAUDRATE);
pll_sel(TCFG_PLL_SEL, TCFG_PLL_DIV, TCFG_HSB_DIV);
dump_clock_info();
debug_init();
wdt_init(WDT_8S);
P3_PINR_CON &= ~BIT(0); //关闭长按复位
/* gpio_clk_out(IO_PORTC_00, CLK_OUT_HSB); */
log_info("time & date %s %s \n OTP c_main\n", __TIME__, __DATE__);
my_malloc_init();
#if SYS_TIMER_EN
sys_timer_init();
#endif
power_reset_source_dump();
power_wakeup_reason_dump();
sys_power_init();
system_init();
app();
while (1) {
wdt_clear();
}
}
Source: main.c
逐步拆解该序列的设计意图:
wdt_close()— 启动早期关闭看门狗。原因:复位后看门狗可能处于使能状态,而后续初始化(尤其时钟切换、内存初始化)耗时较长,若继续计数会导致启动过程被意外复位。mask_init_for_app()— 建立异常分析设施(见下文"异常处理与掩码调试")。它根据config_exception_enable决定是否挂接exception_analyze回调,使崩溃时可输出分析信息。irq_init()+local_irq_disable/enable()— 初始化中断控制器并做一次关/开中断的屏障操作,确保后续初始化期间中断状态确定。port_init()— 上电初始化所有 IO 引脚,避免悬空引脚造成漏电或误触发。log_init(TCFG_UART_BAUDRATE)— 以配置的波特率初始化串口日志,此后log_info等输出才可用。pll_sel(TCFG_PLL_SEL, TCFG_PLL_DIV, TCFG_HSB_DIV)+dump_clock_info()— 选择 PLL 并配置 HSB 分频,随后打印时钟信息。时钟是后续所有外设与定时器的基础,必须先于电源/定时器初始化。debug_init()— 初始化调试辅助设施。wdt_init(WDT_8S)— 以 8 秒超时重新开启看门狗,此后代码必须周期性喂狗。P3_PINR_CON &= ~BIT(0)— 关闭长按复位功能,避免用户长按 IO 触发复位。my_malloc_init()— 初始化内存堆分配器,为sys_timer_init等需要动态内存的服务做准备。sys_timer_init()(受SYS_TIMER_EN宏控制) — 初始化系统定时器服务。power_reset_source_dump()/power_wakeup_reason_dump()— 打印复位源与唤醒原因,用于排查异常复位和低功耗唤醒路径。sys_power_init()— 初始化电源管理服务(见下节)。system_init()— 初始化业务系统(设备、事件等)。app()— 进入业务应用主体;返回后进入while(1)死循环,仅做wdt_clear()喂狗,防止系统复位。
电源管理服务(power_api.c)
电源管理是系统基础服务的核心运行期能力。sdk/apps/app/bsp/cpu/uc03/power_api.c 提供了两个关键 API:
void sys_power_init()
{
...
}
void sys_power_down(u32 usec)
{
...
}
Source: power_api.c
sys_power_init():在启动阶段完成电源模块初始化,使后续低功耗请求可用。它在c_main中被调用,且在power_reset_source_dump()/power_wakeup_reason_dump()之后——这样可以在初始化电源前先记录复位/唤醒现场。sys_power_down(u32 usec):统一的低功耗入口。参数usec为低功耗持续时间,业务代码以-2调用表示"进入 powerdown"(由调用方配合临界区使用,见下文示例)。
业务模块在关键低功耗路径上调用它,例如空闲检测模块:
UI_init();//关闭数码管
sys_power_down(-2);//进入powerdown
OS_EXIT_CRITICAL();
Source: idle_check.c
注意调用惯例:在临界区(OS_ENTER_CRITICAL)内执行 sys_power_down(-2),返回后调用 OS_EXIT_CRITICAL()。这是因为 powerdown 过程中系统时钟/中断被挂起,必须以临界区保护状态切换的原子性,防止唤醒瞬间的中断竞争。
看门狗服务
看门狗贯穿启动与运行期:
- 启动早期
wdt_close()关闭,避免初始化超时误复位; - 初始化完成前
wdt_init(WDT_8S)以 8 秒超时开启; - 业务主循环与耗时任务中周期调用
wdt_clear()喂狗。
在 c_main 中,主循环只做喂狗:
while (1) {
wdt_clear();
}
Source: main.c
设计意图:看门狗是系统"最后一道防线"——当业务逻辑死锁或异常导致主循环停滞时,8 秒后系统自动复位,配合 power_reset_source_dump() 的复位源打印可定位故障。SD 卡调试中还通过 sd_debug 回调在 E_SD_RECEIVE_DATA_TIMEOUT/E_SD_SEND_ACMD41_TIMEOUT 时提前 wdt_clear(),避免 SD 超时路径误触发复位:
static void sd_debug(u32 idx)
{
if (idx == E_SD_RECEIVE_DATA_TIMEOUT || idx == E_SD_SEND_ACMD41_TIMEOUT) {
wdt_clear();
}
...
}
Source: main.c
系统定时器服务
系统定时器由编译宏 SYS_TIMER_EN 控制,使能时在启动流程中调用:
#if SYS_TIMER_EN
sys_timer_init();
#endif
Source: main.c
它必须在 my_malloc_init()(内存堆就绪)之后、sys_power_init()(电源服务)之前初始化,因为后续业务系统(system_init/app)依赖定时器提供时间基准与调度能力。
异常处理与掩码调试
mask_init_for_app() 是调试服务在启动期的装配点:
void mask_init_for_app(void)
{
if (config_exception_enable) {
mask_init(exception_analyze, putchar, clk_get, device_status_emit);
} else {
mask_init(NULL, putchar, clk_get, device_status_emit);
}
//--------------------------
#if TFG_SD_EN
sd_mask_init(5, NULL, NULL, NULL, NULL, sd_debug);
#endif
}
Source: main.c
要点:
config_exception_enable使能时,mask_init挂接exception_analyze作为异常分析回调;关闭时传入NULL以减小代码体积/开销(量产裁剪场景)。putchar作为日志输出函数、clk_get提供时钟计数、device_status_emit上报设备状态——这些回调把异常分析、时间戳与设备状态输出串联起来,使崩溃现场可复现。sd_mask_init(5, NULL, NULL, NULL, NULL, sd_debug)在TFG_SD_EN使能时为 SD 子系统注册调试掩码,sd_debug负责超时场景下的喂狗与日志。
核心流程:启动与低功耗时序
sequenceDiagram
participant HW as 芯片复位/汇编启动
participant CM as c_main
participant WDT as 看门狗
participant PWR as power_api.c
participant SYS as system_init/app
HW->>CM: 跳转 c_main(cfg_addr)
CM->>WDT: wdt_close()
CM->>CM: mask_init_for_app() / irq_init() / port_init()
CM->>CM: log_init(TCFG_UART_BAUDRATE)
CM->>CM: pll_sel(...) + dump_clock_info()
CM->>WDT: wdt_init(WDT_8S)
CM->>CM: my_malloc_init() → sys_timer_init()
CM->>PWR: power_reset_source_dump() / power_wakeup_reason_dump()
CM->>PWR: sys_power_init()
CM->>SYS: system_init()
CM->>SYS: app() 业务主体
SYS-->>CM: 返回(不应发生)
CM->>WDT: while(1) wdt_clear() 周期喂狗
Note over SYS,PWR: 运行期低功耗路径
SYS->>PWR: sys_power_down(-2) [临界区内]
PWR-->>SYS: powerdown 返回(唤醒)
SYS->>SYS: OS_EXIT_CRITICAL()
时序要点:
- 启动阶段(上半部分)是确定性的线性序列,任何一步失败(如堆初始化失败、时钟配置错误)都会导致后续服务不可用;由于看门狗已开启,卡死会在 8 秒后触发复位。
- 运行期低功耗(下半部分)由业务模块主动触发:
sys_power_down(-2)必须在临界区内调用,唤醒返回后立即退出临界区。power_wakeup_reason_dump()使下次启动能够打印本次唤醒原因,形成"低功耗 → 唤醒 → 诊断"的闭环。
使用示例
示例一:在业务空闲检测中进入低功耗
UI_init();//关闭数码管
sys_power_down(-2);//进入powerdown
OS_EXIT_CRITICAL();
Source: idle_check.c
说明:空闲检测模块先关闭数码管显示(关闭功耗大户),随后在临界区内进入 powerdown;OS_EXIT_CRITICAL() 前的代码均在关中断保护下执行,保证低功耗状态的原子进入。
示例二:音乐暂停时进入低功耗
LED5X7_init();
sys_power_down(-2);
OS_EXIT_CRITICAL();
Source: music_play.c
说明:音乐播放模块在暂停场景先初始化 LED 显示(LED5X7),随后同样以 sys_power_down(-2) 进入 powerdown,遵循"先关显示、再进低功耗"的顺序以最大化省电效果。
示例三:RTC 模式下的低功耗
sys_power_down(-2);//进入powerdown
Source: rtc_mode.c
说明:RTC 模式(时钟显示待机)依赖系统定时器维持时间,进入 powerdown 后由 RTC/唤醒源恢复,属于该模式的核心节能路径。
配置选项
系统基础服务的行为主要由编译期宏与配置项控制,汇总如下:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
SYS_TIMER_EN | 宏 (0/1) | 依工程配置 | 是否使能系统定时器服务;使能时 c_main 调用 sys_timer_init() |
TCFG_UART_BAUDRATE | 数值 | 工程配置 | 日志串口波特率,传入 log_init() |
TCFG_PLL_SEL | 数值 | 工程配置 | PLL 选择参数,传入 pll_sel() |
TCFG_PLL_DIV | 数值 | 工程配置 | PLL 分频参数,传入 pll_sel() |
TCFG_HSB_DIV | 数值 | 工程配置 | HSB 总线分频参数,传入 pll_sel() |
WDT_8S | 枚举/宏 | 8 秒 | 看门狗超时周期,传入 wdt_init() |
config_exception_enable | 全局变量 | 依工程配置 | 使能时 mask_init 挂接 exception_analyze 异常分析回调 |
TFG_SD_EN | 宏 (0/1) | 依工程配置 | 使能 SD 子系统;使能时调用 sd_mask_init 注册调试掩码 |
其中时钟三项(TCFG_PLL_SEL/TCFG_PLL_DIV/TCFG_HSB_DIV)决定芯片工作频率,直接影响定时器精度与低功耗唤醒时序,属于"一次配置、全局生效"的关键项。
API 参考
c_main(int cfg_addr)
系统应用入口,由启动汇编代码调用,noreturn。负责按顺序装配全部系统基础服务后进入业务主循环。
参数:
cfg_addr(int):配置区地址,用于定位系统配置。
返回: 不返回(__attribute__((noreturn)))。
Source: main.c
mask_init_for_app(void)
装配异常分析与调试掩码服务。依据 config_exception_enable 决定是否注册 exception_analyze 回调;TFG_SD_EN 使能时注册 SD 调试掩码。
Source: main.c
sys_power_init(void)
初始化电源管理服务,使 sys_power_down 可用。在 c_main 的时钟/中断/内存初始化之后调用。
Source: power_api.c
sys_power_down(u32 usec)
进入低功耗状态。
参数:
usec(u32):低功耗持续时间;业务代码传入-2表示进入 powerdown。
调用约束: 应在临界区(OS_ENTER_CRITICAL)内调用,唤醒返回后调用 OS_EXIT_CRITICAL()。
Source: power_api.c
wdt_init(wdt_timeout_t timeout) / wdt_clear(void) / wdt_close(void)
看门狗三件套:wdt_init(WDT_8S) 以 8 秒超时开启;wdt_clear() 周期喂狗(主循环与 SD 超时路径);wdt_close() 启动早期关闭。
Source: main.c
sys_timer_init(void)
初始化系统定时器服务,仅在 SYS_TIMER_EN 使能时由 c_main 调用,需在 my_malloc_init() 之后。
Source: main.c
pll_sel(sel, div, hsb_div) / dump_clock_info(void)
配置系统时钟(PLL 选择与 HSB 分频)并打印时钟信息,必须在电源与定时器初始化之前完成。
Source: main.c
失败模式、边界情况与并发
基于源码证据,系统基础服务存在以下可观测的失败模式与边界行为:
看门狗复位
- 主循环停滞复位:
app()返回后主循环仅执行wdt_clear();任何阻塞超过WDT_8S(8 秒)的路径都会触发看门狗复位。SD 卡超时(E_SD_RECEIVE_DATA_TIMEOUT、E_SD_SEND_ACMD41_TIMEOUT)是已知的长时间阻塞路径,因此sd_debug回调在这些事件中提前喂狗,防止误复位。 - 诊断闭环:复位后下次启动通过
power_reset_source_dump()打印复位源,开发者可据此区分看门狗复位、上电复位与长按复位(P3_PINR_CON关闭了长按复位,因此该来源会被屏蔽)。
低功耗的并发/原子性约束
sys_power_down(-2)必须在OS_ENTER_CRITICAL/OS_EXIT_CRITICAL临界区内调用(见 idle_check/music_play 示例)。这是硬性约定:powerdown 期间时钟与中断被挂起,若不在临界区内,唤醒瞬间的中断处理可能与低功耗状态切换竞争,导致状态不一致或漏唤醒。- 所有业务模块(空闲检测、音乐暂停、RTC)遵循同一调用模式,说明该约束由调用方负责而非库内强制,扩展新低功耗场景时必须沿用此模式。
配置宏一致性
SYS_TIMER_EN、TFG_SD_EN、config_exception_enable等配置控制代码装配路径。若SYS_TIMER_EN关闭但业务代码仍依赖系统定时器,将出现未初始化服务的使用错误;同理TFG_SD_EN关闭时sd_mask_init分支被裁剪。修改配置后需全量重编译并回归验证启动序列。
启动序列的强顺序依赖
c_main 中各步骤顺序不可调换:log_init 先于 pll_sel 的日志输出、my_malloc_init 先于 sys_timer_init、时钟先于电源。任何新增基础服务都应在 system_init() 之前完成装配,否则业务代码可能在服务未就绪时访问它。
性能与运维要点
- 启动时序敏感:
wdt_close()→ 慢速初始化 →wdt_init(WDT_8S)的结构允许初始化阶段不受看门狗限制;但 8 秒窗口意味着system_init()/app()前的全部初始化必须在 8 秒内完成(正常情况下远小于此)。 - 低功耗收益顺序:业务模块先关闭显示(数码管/LED5X7)再
sys_power_down,说明显示是待机功耗的主要来源,新增省电路径应遵循"先关外设、再进低功耗"的顺序。 - 日志与诊断:
dump_clock_info()、power_reset_source_dump()、power_wakeup_reason_dump()在每次启动打印,是现场问题排查的第一手信息;量产裁剪时可关闭config_exception_enable以减小异常分析开销。
扩展点
系统基础服务通过以下方式支持扩展:
- 新增低功耗场景:复用
sys_power_down(-2)入口,按"临界区进入 → 关显示 → powerdown → 退出临界区"的既有模式编写,例如新增按键待机、定时待机场景。 - 新增启动阶段服务:在
c_main中system_init()之前插入初始化调用,并遵守依赖顺序(内存 → 时钟 → 定时器 → 电源)。 - 异常分析定制:通过
mask_init的回调参数(exception_analyze、putchar、clk_get、device_status_emit)替换日志输出、时钟计数与设备状态上报实现。 - SD/外设调试掩码:仿照
sd_mask_init(5, NULL, NULL, NULL, NULL, sd_debug)为其他外设注册调试回调,在超时路径上执行喂狗或日志。
测试覆盖说明
本次探索未在源码中发现针对系统基础服务(启动序列、电源、看门狗)的独立单元测试文件;其正确性主要通过以下机制保障:启动期诊断打印(复位源/唤醒原因/时钟信息)用于回归验证,以及看门狗作为运行期兜底防止静默故障。建议在新增启动阶段服务或修改电源路径时,通过复位源打印与低功耗唤醒日志进行手工回归。
Related Links
- 启动入口 main.c
- 电源管理 power_api.c
- 空闲检测低功耗调用 idle_check.c
- 音乐播放低功耗调用 music_play.c
- RTC 模式低功耗调用 rtc_mode.c
- 相关主题:音频服务、配置存储服务、外设驱动(见"Purpose and Scope"中的页面边界说明)