待机、软关机与空闲检测
本文档介绍 AD23N GP-MCU SDK 中与待机(IDLE)、软关机(Soft-off)和空闲检测(Idle Detection)相关的完整实现:工作模式切换机制、关机提示音播放与 sys_softoff() 调用链、基于 500ms 心跳计数的空闲检测,以及待机模式的消息处理循环。
Purpose and Scope
本页面覆盖「应用层模式管理」中与低功耗退出路径相关的三个核心能力:
- 软关机(Soft-off):收到
MSG_POWER_OFF后切换到SOFTOFF_MODE,播放关机提示音dir_notice/power_off.f1a,随后调用sys_softoff()进入系统软关机; - 空闲检测(Idle Detection):
app_powerdown_deal()通过 500ms 级计数判断系统是否空闲,连续空闲达到阈值后进入sys_power_down(-2)睡眠; - 待机模式(Idle Mode):
idle_app()作为空闲工作模式的消息循环,处理热键并响应MSG_CHANGE_WORK_MODE退出。
不覆盖的内容:底层电源管理 API 的具体实现(sys_power_down/sys_softoff 属于 asm/power/power_api.h 驱动的硬件层)、按键扫描与热键处理(见 hot_msg.c 的按键分发页面)、以及文件系统/解码器本身的实现。这些属于独立的目录条目。
Overview
在 AD23N 这类低成本 MCU 音频方案中,「待机」与「软关机」是用户可感知的两种低功耗退出形态,而「空闲检测」是它们背后的公共机制:
- 软关机(Soft-off):区别于硬断电,软关机先播放一段提示音(
power_off.f1a,F1A1 格式),播放结束后才真正调用sys_softoff()关闭系统。这是典型的消费电子交互设计——给用户听觉反馈后再断电。播放期间需要持续喂狗(wdt_clear()),并处理解码器结束消息。 - 空闲检测(Idle):系统主循环(
bsp_loop())周期性调用app_powerdown_deal(is_busy)。若系统繁忙则清零计数,否则累加;计数达到IDLE_CNT_MAX(=2,即 2×500ms = 1 秒)后,保存 DAC 采样率、关闭 DAC、关闭数码管显示,然后进入sys_power_down(-2)睡眠,唤醒后恢复 DAC。 - 待机模式(Idle Mode):
idle_app()是应用层的一个工作模式,负责在空闲状态下接收消息(热键、模式切换),并保持系统运行。它的退出由MSG_CHANGE_WORK_MODE触发。
三者共同构成「检测空闲 → 降功耗 → 可唤醒」的闭环:空闲检测决定何时进入 power-down;软关机则是带提示音的一次性退出;待机模式则是系统停留在低负载但未断电的状态。
Architecture
下图展示了工作模式分发、软关机、空闲检测与底层电源 API 之间的关系(节点名称与源码中的函数/文件一一对应):
flowchart TD
subgraph sg_App["应用层 app.c 主循环"]
MainLoop["while(1) 消息分发"]
ModeSoftoff["SOFTOFF_MODE → softoff_app()"]
ModeIdle["IDLE_MODE → idle_app()"]
ModeLoudspk["LOUDSPEAKER_MODE → loudspk_app()"]
end
subgraph sg_HotMsg["热键消息 hot_msg.c"]
HotMsg["MSG_POWER_OFF 处理"]
SetMode["work_mode = SOFTOFF_MODE<br/>post_msg(MSG_CHANGE_WORK_MODE)"]
end
subgraph sg_Softoff["软关机 softoff_mode.c"]
SoftoffApp["softoff_app()"]
Decoder["decoder_io(pfile, BIT_F1A1)"]
PlayEnd{"MSG_*_FILE_END?"}
SysSoftoff["sys_softoff()"]
Wdt["MSG_500MS → wdt_clear()"]
end
subgraph sg_IdleCheck["空闲检测 idle_check.c"]
PowerdownDeal["app_powerdown_deal(is_busy)"]
Cnt{"app_idle_cnt == IDLE_CNT_MAX?"}
DacOff["dac_power_off() + UI_init()"]
SysPowerDown["sys_power_down(-2)"]
DacOn["dac_power_on(sr, 0)"]
end
subgraph sg_IdleMode["待机模式 idle_mode.c"]
IdleApp["idle_app()"]
KeyTable["key_table_sel(idle_msg_filter)"]
HandleHotkey["ap_handle_hotkey(msg[0])"]
Exit["MSG_CHANGE_WORK_MODE → 退出"]
end
subgraph sg_Power["底层电源 API power_api.h"]
PowerApi["sys_softoff / sys_power_down"]
end
MainLoop -->|"case IDLE_MODE"| ModeIdle
MainLoop -->|"case SOFTOFF_MODE"| ModeSoftoff
HotMsg -->|"MSG_POWER_OFF"| SetMode
SetMode -->|"MSG_CHANGE_WORK_MODE"| MainLoop
SoftoffApp --> Decoder
SoftoffApp --> PlayEnd
PlayEnd -->|"是"| SysSoftoff
SoftoffApp --> Wdt
SysSoftoff --> PowerApi
PowerdownDeal --> Cnt
Cnt -->|"是(连续空闲 1s)"| DacOff
DacOff --> SysPowerDown
SysPowerDown --> DacOn
SysPowerDown --> PowerApi
IdleApp --> KeyTable
IdleApp --> HandleHotkey
IdleApp --> Exit
架构要点说明:
app.c的主循环是整个应用的中枢:它持有全局work_mode变量,根据消息MSG_CHANGE_WORK_MODE切换IDLE_MODE/SOFTOFF_MODE/LOUDSPEAKER_MODE等模式,并调用对应的*_app()函数(见 app.c)。模式函数各自拥有独立的while(1)消息循环,只有收到模式切换消息才返回,形成「一个模式一个循环」的结构。- 软关机路径是「先播放后断电」:
softoff_app()先初始化解码器、挂载文件系统、打开提示音文件,播放完成后才调用sys_softoff()。任何一步失败(挂载失败、打开失败、解码器创建失败)都会跳过播放直接执行__softoff_app_deal标签处的sys_softoff()。 - 空闲检测位于
common/idle_check.c,是一个无状态依赖的纯计数函数,由系统主循环周期调用;它不关心上层是哪种工作模式,只根据is_busy标志决定计数清零或累加。 - 待机模式
idle_app()本质是一个「最小消息循环」:不播放音频、不操作外设,只处理热键(通过ap_handle_hotkey)和模式切换。
软关机实现详解(softoff_app)
段链接指令与低功耗布局
softoff_mode.c 文件开头使用了一组 #pragma 段重定向指令,这是该文件最显著的工程特征:
#pragma bss_seg(".softoff_mode.data.bss")
#pragma data_seg(".softoff_mode.data")
#pragma const_seg(".softoff_mode.text.const")
#pragma code_seg(".softoff_mode.text")
#pragma str_literal_override(".softoff_mode.text.const")
Source: softoff_mode.c
设计意图:把软关机代码、只读数据、字符串常量全部放入独立的段(.softoff_mode.*)。这样链接脚本可以把这些段放置到特定的内存区域(通常是低功耗场景下仍保持供电的 RAM 区),保证系统进入深睡/软关机流程时,执行软关机所需的代码不会因为掉电区域失效而无法运行。这是 MCU 低功耗设计的常见做法——把「最后一段路」的代码隔离到保电内存。
主流程:播放关机提示音
void softoff_app(void)
{
int msg[2], err;
void *pfs = 0;
void *pfile = 0;
dec_obj *obj = 0;
decoder_init();
err = vfs_mount(&pfs, (void *)NULL, (void *)NULL);
if (err) {
log_info("fs_mount err : 0x%x\n", err);
goto __softoff_app_deal;
}
err = vfs_openbypath(pfs, &pfile, "dir_notice/power_off.f1a");
if (err) {
log_info("fs_openbypath err : 0x%x\n", err);
vfs_fs_close(&pfs);
goto __softoff_app_deal;
}
obj = decoder_io(pfile, BIT_F1A1, NULL, 0);
if (NULL == obj) {
log_info("fs_openbypath err : 0x%x\n", err);
vfs_file_close(&pfile);
vfs_fs_close(&pfs);
goto __softoff_app_deal;
}
Source: softoff_mode.c
流程分四步:
decoder_init():初始化音频解码器子系统;vfs_mount(&pfs, NULL, NULL):挂载默认文件系统(NOR Flash 上的 VFS)。失败则打印日志并直接跳转到关机电平;vfs_openbypath(pfs, &pfile, "dir_notice/power_off.f1a"):按路径打开关机提示音文件。注意路径是相对挂载点的dir_notice/目录,这提示方案中提示音文件统一存放在dir_notice目录下;打开失败需要先vfs_fs_close释放挂载句柄再跳转;decoder_io(pfile, BIT_F1A1, NULL, 0):用文件句柄创建解码器对象,格式标志为BIT_F1A1(F1A1 是杰理私有音频压缩格式,占用空间小、适合提示音)。创建失败时依次关闭文件与文件系统,再跳转。
消息循环:播放中喂狗与结束检测
while (1) {
err = get_msg(2, &msg[0]);
bsp_loop();
if (MSG_NO_ERROR != err) {
msg[0] = NO_MSG;
log_info("get msg err 0x%x\n", err);
}
switch (msg[0]) {
case MSG_F1A1_FILE_END:
case MSG_MP3_FILE_END:
case MSG_WAV_FILE_END:
decoder_stop(obj, NEED_WAIT);
__softoff_app_deal:
sys_softoff();
break;
case MSG_500MS:
wdt_clear();
break;
}
}
Source: softoff_mode.c
循环体遵循 SDK 统一的「取消息 → 跑 bsp_loop() → 分发」模式:
get_msg(2, &msg[0])阻塞等待最多 2 个消息字;出错时置为NO_MSG并记录日志;bsp_loop()每轮执行 BSP 级轮询(按键扫描、LED 刷新等底层服务);MSG_F1A1_FILE_END/MSG_MP3_FILE_END/MSG_WAV_FILE_END:解码器上报文件播放结束。之所以兼容三种结束消息,是因为 F1A1 解码器可能以不同消息名上报结束;统一处理确保提示音无论以何种格式解码都能触发关机。先decoder_stop(obj, NEED_WAIT)同步停止解码器,再落入__softoff_app_deal标签调用sys_softoff();MSG_500MS:每 500ms 的系统心跳消息。这里只做wdt_clear()喂狗——因为提示音播放可能持续数百毫秒到数秒,期间系统不进入 power-down,必须持续喂狗防止复位;同时喂狗也保证关机提示音能完整播完。
值得注意的是 __softoff_app_deal 标签被 goto 与 case 共用:既作为错误处理的跳转目标(挂载/打开/解码失败时),也作为正常播放结束后的收敛点。这种「单一出口」写法保证任何路径最终都只执行一次 sys_softoff(),不会出现多次关机或泄漏句柄的路径。
空闲检测实现详解(app_powerdown_deal)
500ms 计数式空闲判定
#define IDLE_CNT_MAX 2//CNT值不低于2,idle时间=IDLE_CNT_MAX*500ms
static u8 app_idle_cnt;
void app_powerdown_deal(u8 is_busy)
{
if (is_busy) {
app_idle_cnt = 0;
return;
}
app_idle_cnt++;
if (IDLE_CNT_MAX == app_idle_cnt) {
app_idle_cnt = 0;
u32 sr = dac_sr_read();
dac_power_off();
UI_init();//关闭数码管
sys_power_down(-2);//进入powerdown
dac_power_on(sr, 0);
}
}
Source: idle_check.c
算法与设计意图
- 忙则清零:
is_busy == 1表示系统正忙(播放、录音、IO 操作中),立刻把计数清零并返回。这是典型的「连续空闲」判定:只要中间出现一次繁忙,就重新计时,避免在播放间隙误入睡眠。 - 空闲累加:
is_busy == 0时app_idle_cnt++;计数达到IDLE_CNT_MAX(=2)时触发休眠。注释明确说明「CNT值不低于2」,原因是单次 500ms 空闲不足以判断系统稳定空闲,至少要连续 1 秒(2×500ms)才可信。 - 进入 power-down 的时序:
dac_sr_read()保存当前 DAC 采样率;dac_power_off()关闭 DAC——音频通路是主要功耗源;UI_init():注释为「关闭数码管」,即关闭显示外设(数码管/LED 驱动);sys_power_down(-2):进入内核级 power-down。参数-2表示定时唤醒间隔(-2 秒量级),配合注释「power_down定时唤醒时,睡眠时间不可超过看门狗唤醒时间的一半」——即系统会周期唤醒喂狗,防止看门狗复位;- 唤醒后
dac_power_on(sr, 0)以保存的采样率恢复 DAC,整个睡眠过程对上层音频状态透明。
该函数的调用点位于系统主循环(bsp_loop() 相关的周期任务),以 500ms 为节拍被周期调用,因此 IDLE_CNT_MAX 直接换算成秒。计数器是 static u8,非重入、无锁,只允许在主循环单线程上下文中调用——这也是它被放在 common/ 目录、以普通函数而非任务形式提供的原因。
待机模式实现详解(idle_app)
void idle_app(void)
{
u32 err;
int msg[2];
log_info("toy idle mode!\n");
key_table_sel((void *)idle_msg_filter);
while (1) {
err = get_msg(2, &msg[0]);
if (MSG_NO_ERROR != err) {
msg[0] = NO_MSG;
log_info("get msg err 0x%x\n", err);
}
bsp_loop();
if (NO_MSG == msg[0]) {
continue;
}
switch (msg[0]) {
case MSG_CHANGE_WORK_MODE:
goto __idle_exit;
default:
ap_handle_hotkey(msg[0]);
break;
}
}
__idle_exit:
key_table_sel(NULL);
}
Source: idle_mode.c
待机模式的设计要点:
- 按键表切换:进入时
key_table_sel((void *)idle_msg_filter)把按键解析表切换为待机专用的过滤表,退出时key_table_sel(NULL)还原。这样同一组物理按键在待机模式下被映射为不同的语义(例如长按开机/唤醒),而不是普通模式下的功能键——这是 SDK 按键框架的典型用法。 - 空消息直接跳过:
NO_MSG时不走分发直接continue,保证待机循环最小化 CPU 占用。 - 热键兜底:除
MSG_CHANGE_WORK_MODE外的所有消息统一交给ap_handle_hotkey(msg[0])处理——热键机制是全局快捷键,适合在待机态响应开机/唤醒键。 - 唯一退出条件:
MSG_CHANGE_WORK_MODE触发goto __idle_exit。该消息由模式管理器在work_mode被改写后发出(见 hot_msg.c),例如从待机切回主模式或进入软关机。
模式切换入口(app.c 与 hot_msg.c)
app.c 的主循环按 work_mode 分发到各模式函数;hot_msg.c 的电源键处理把 work_mode 置为 SOFTOFF_MODE 并广播 MSG_CHANGE_WORK_MODE:
case SOFTOFF_MODE:
softoff_app();
...
case IDLE_MODE:
idle_app();
Source: app.c
log_info("MSG_POWER_OFF\n");
work_mode = SOFTOFF_MODE;
post_msg(1, MSG_CHANGE_WORK_MODE);
Source: hot_msg.c
设计意图:work_mode 是全局变量,而各模式函数都阻塞在自己的消息循环里,因此模式切换必须通过「改标志 + 发消息」两步完成——post_msg(MSG_CHANGE_WORK_MODE) 唤醒当前模式循环,让它检查到 MSG_CHANGE_WORK_MODE 后返回,主循环再按新的 work_mode 进入下一模式。app.c 中 work_mode = IDLE_MODE 的初始化行被注释掉,说明待机并非默认启动模式,需要由上层逻辑显式切入。
Core Flow
软关机时序
sequenceDiagram
participant Key as 电源键/热键
participant Hot as hot_msg.c
participant App as app.c 主循环
participant Soft as softoff_app()
participant Dec as 解码器/文件系统
participant Pwr as power_api
Key->>Hot: MSG_POWER_OFF
Hot->>Hot: work_mode = SOFTOFF_MODE
Hot->>App: post_msg(MSG_CHANGE_WORK_MODE)
App->>App: 重新分发 → case SOFTOFF_MODE
App->>Soft: softoff_app()
Soft->>Dec: decoder_init / vfs_mount
Dec-->>Soft: 挂载成功
Soft->>Dec: vfs_openbypath("dir_notice/power_off.f1a")
Soft->>Dec: decoder_io(pfile, BIT_F1A1)
loop 播放提示音
Soft->>Soft: get_msg + bsp_loop
Dec-->>Soft: MSG_500MS → wdt_clear()
end
Dec-->>Soft: MSG_F1A1_FILE_END
Soft->>Dec: decoder_stop(obj, NEED_WAIT)
Soft->>Pwr: sys_softoff()
Note over Pwr: 进入软关机(掉电/深睡)
空闲检测 → power-down 时序
sequenceDiagram
participant Loop as 主循环/bsp_loop
participant Idle as app_powerdown_deal()
participant Dac as DAC
participant UI as 数码管
participant Pwr as sys_power_down()
Loop->>Idle: 周期调用(is_busy=0)
Idle->>Idle: app_idle_cnt++
Note over Idle: 连续 2 次空闲(1s)
Idle->>Dac: dac_sr_read() 保存采样率
Idle->>Dac: dac_power_off()
Idle->>UI: UI_init() 关闭数码管
Idle->>Pwr: sys_power_down(-2)
Pwr-->>Idle: 定时唤醒(≤看门狗一半)
Idle->>Dac: dac_power_on(sr, 0) 恢复
两条时序揭示了本页面的核心设计哲学:所有低功耗动作都从消息循环/主循环的单线程上下文发起,不引入独立低功耗任务,从而避免竞态;每个模式循环各自负责「喂狗保活」或「触发断电」的职责划分。
Configuration Options
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
IDLE_CNT_MAX | 宏(u8 计数上限) | 2 | 空闲判定阈值。实际空闲时间 = IDLE_CNT_MAX × 500ms。源码注释要求该值不低于 2,避免单次 500ms 空闲即误判 |
app_idle_cnt | static u8 | 0 | 空闲连续计数,仅在 app_powerdown_deal() 内部使用,不可外部修改 |
sys_power_down(-2) 参数 | 整型(秒) | -2 | power-down 定时唤醒周期。负号表示周期唤醒,睡眠时间不可超过看门狗唤醒时间的一半(见 idle_check.c 注释) |
| 提示音路径 | 字符串常量 | "dir_notice/power_off.f1a" | 软关机提示音在 VFS 中的相对路径,需预先烧录到文件系统 dir_notice/ 目录 |
| 提示音格式标志 | 宏 | BIT_F1A1 | 关机提示音使用杰理私有 F1A1 格式解码;结束消息兼容 MSG_F1A1_FILE_END / MSG_MP3_FILE_END / MSG_WAV_FILE_END |
| 段重定向 | pragma | .softoff_mode.* | 将软关机代码/数据/常量放入独立段,便于链接器放置到保电内存区(见 softoff_mode.c) |
work_mode 初始值 | 全局变量 | 非 IDLE(注释掉的默认) | 主循环 app.c 中 work_mode = IDLE_MODE 被注释,待机模式需显式切入 |
注:上述宏与常量均为编译期配置,运行时无动态配置入口;如需调整空闲时间或提示音文件,需修改源码后重新编译烧录。
API Reference
void softoff_app(void)
软关机模式入口,由 app.c 在 work_mode == SOFTOFF_MODE 时调用。
- 流程:初始化解码器 → 挂载 VFS → 打开并解码
dir_notice/power_off.f1a→ 消息循环等待播放结束 →sys_softoff()。 - 参数:无。
- 返回:无(正常路径永不返回,直接软关机;异常路径也收敛到
sys_softoff())。 - 消息:消费
MSG_F1A1_FILE_END、MSG_MP3_FILE_END、MSG_WAV_FILE_END、MSG_500MS;错误时置NO_MSG并记录日志。 - 定义见 softoff_mode.c。
void app_powerdown_deal(u8 is_busy)
空闲检测与 power-down 触发函数,由系统主循环以 500ms 节拍周期调用。
- 参数:
is_busy(u8)—1表示系统繁忙(清零计数并返回),0表示空闲(计数累加)。 - 返回:无。
- 副作用:连续空闲达
IDLE_CNT_MAX次后依次执行dac_sr_read()→dac_power_off()→UI_init()→sys_power_down(-2)→dac_power_on(sr, 0)。 - 约束:非重入,必须在主循环单线程上下文调用;power-down 唤醒周期不得超过看门狗唤醒时间的一半。
- 定义见 idle_check.c。
void idle_app(void)
待机模式入口,由 app.c 在 work_mode == IDLE_MODE 时调用。
- 流程:
key_table_sel(idle_msg_filter)切换按键表 → 消息循环 → 退出时key_table_sel(NULL)。 - 参数:无。
- 返回:仅在收到
MSG_CHANGE_WORK_MODE时返回(经goto __idle_exit)。 - 消息:
MSG_CHANGE_WORK_MODE(退出)、其余消息全部转发ap_handle_hotkey()。 - 定义见 idle_mode.c。
相关底层 API(由本页面调用,实现在电源驱动层)
| API | 作用 | 调用方 |
|---|---|---|
sys_softoff() | 系统软关机(断电/深睡) | softoff_app() |
sys_power_down(interval) | 进入 power-down,支持定时唤醒 | app_powerdown_deal() |
dac_sr_read() / dac_power_off() / dac_power_on(sr, 0) | DAC 采样率保存、关闭、恢复 | app_powerdown_deal() |
decoder_init() / decoder_io() / decoder_stop() | 解码器初始化、创建、停止 | softoff_app() |
vfs_mount() / vfs_openbypath() / vfs_fs_close() / vfs_file_close() | 文件系统挂载与文件操作 | softoff_app() |
Failure Modes、Edge Cases 与并发
软关机路径的失败降级
softoff_app() 对三个失败点做了显式降级处理,全部收敛到 __softoff_app_deal 标签:
vfs_mount失败(返回非 0):直接goto,跳过提示音播放立即软关机。此时文件系统不可用,提示音必然无法播放,继续等待只会卡死;vfs_openbypath失败(提示音文件缺失/损坏):先vfs_fs_close(&pfs)释放挂载,再跳转。若不释放句柄会造成资源泄漏;decoder_io返回 NULL(解码器创建失败):依次vfs_file_close与vfs_fs_close,再跳转。
每步失败都打印 log_info([low_pwr] 标签),便于现场定位是文件缺失还是解码器问题。注意第 3 步的错误日志误用了 err(文件打开的错误码)而非 decoder_io 的返回值,这是源码中的一处瑕疵——实际错误码不可信,应只把日志当作「已失败」标志。
消息循环的异常输入
get_msg 返回非 MSG_NO_ERROR 时,msg[0] 被强制置为 NO_MSG 并记录日志,防止未初始化栈数据进入 switch 造成未定义行为。switch 对未知消息默认不处理(无 default 分支),静默丢弃——软关机循环只关心播放结束与心跳两类消息。
看门狗与定时唤醒的矛盾约束
软关机播放期间,系统停留在正常模式而非 power-down,因此必须靠 MSG_500MS → wdt_clear() 持续喂狗;而空闲检测进入的 sys_power_down(-2) 则是周期唤醒喂狗。两类路径的公共约束是:任何睡眠周期都不得超过看门狗唤醒时间的一半(源码注释明确),否则系统会被看门狗复位而非正常待机。这是本页面最关键的运行时边界条件。
并发与重入
三个函数均为单线程消息循环上下文设计,无锁、无异步回调:
app_idle_cnt是static变量,仅app_powerdown_deal()访问,主循环周期调用天然串行化;softoff_app()/idle_app()内部while(1)独占执行,work_mode切换通过post_msg(MSG_CHANGE_WORK_MODE)跨循环通信,不存在数据竞争;- 唯一需要注意的共享资源是按键表
key_table_sel()——待机模式进入/退出时成对切换,若中途异常退出会破坏按键表状态,因此idle_app()只在MSG_CHANGE_WORK_MODE一个出口还原。
性能与运维注意事项
- 空闲检测开销:
app_powerdown_deal每次调用仅做一次比较与自增(繁忙路径直接返回),CPU 开销可忽略;它依赖主循环的 500ms 节拍,节拍精度即空闲判定精度。 - 软关机延迟:提示音完整播完才断电,播放时长由
power_off.f1a内容决定;若产品要求快速断电,可缩短提示音或直接去掉decoder_io步骤(但会丢失用户反馈)。 - 功耗路径:空闲 → 关闭 DAC → 关闭数码管 → power-down,是典型的多级降功耗顺序;DAC 与显示是 MCU 音频方案的两大功耗源,先关它们再睡 CPU。
- 可配置性:空闲时长(
IDLE_CNT_MAX)、提示音文件(路径字符串)、唤醒周期(sys_power_down参数)均为编译期常量,现场不可热调;量产前应通过测试确定IDLE_CNT_MAX与看门狗周期的匹配。 - 日志标签:软关机模块
[low_pwr]、空闲检测[sys_idle]、待机模式[normal],抓取低功耗问题时可按标签过滤日志。
Extension Points
- 更换/新增提示音:修改
vfs_openbypath的路径字符串,或扩展switch中的结束消息枚举;解码格式由BIT_F1A1标志控制,可改为BIT_MP3/BIT_WAV等。 - 调整空闲判定粒度:
IDLE_CNT_MAX与调用节拍(500ms)共同决定判定时长;若要支持「忙碌判定只看音频」,可在调用点传入更精确的is_busy标志。 - 待机按键语义扩展:
idle_msg_filter是待机模式的按键过滤表,可在按键表中新增映射,使待机态响应更多按键(例如短按显示时间、长按开机)。 - 新增低功耗档位:
app_powerdown_deal的睡眠分支目前固定sys_power_down(-2);如需多级睡眠(如先浅睡后深睡),可在此函数内按计数分档。
Related Links
- 应用主循环与工作模式(app.c):
work_mode分发与各模式入口。 - 热键与电源键处理(hot_msg.c):
MSG_POWER_OFF→SOFTOFF_MODE的切换源头。 - 软关机模块源码(softoff_mode.c):段布局、提示音播放与
sys_softoff()。 - 空闲检测源码(idle_check.c):500ms 计数与 power-down 触发。
- 待机模式源码(idle_mode.c):待机消息循环与按键表切换。
- 底层电源 API 实现位于
sdk/include_lib/cpu/asm/power/power_api.h(sys_softoff/sys_power_down的硬件实现,超出本页面范围,另见电源管理相关目录)。