- BASEPRI 屏蔽 = 完全不唤醒 :WFI 的唤醒条件是「中断优先级高于当前执行优先级 且 高于 BASEPRI 阈值」。只要中断优先级数值 ≥ BASEPRI 值(5 ≥ 2),既不唤醒也不执行 ISR,CPU 就一直停在 WFI。
- PRIMASK 置位 = 会唤醒但暂不执行 :ARM 明确规定,用
CPSID i(__disable_irq())关中断时,只要来的是"若 PRIMASK 未置位就能抢占"的中断,处理器仍会退出 WFI,只是不进 ISR、保持 pending,等开中断后再响应。
这也是为什么"关总中断反而能唤醒、进临界区反而醒不来"。FreeRTOS 的 taskENTER_CRITICAL() 走的是 BASEPRI 路线,不是 PRIMASK。
另外 AC7843x 的 VLPS 定义是"CPU 进入深度休眠,支持部分模块中断唤醒"------唤醒仍然经过 NVIC 中断路径,所以必然受 BASEPRI/PRIMASK 约束,不存在"绕过 NVIC 的独立引脚唤醒通路"这一侥幸。
一句话定性
现象(优先级数值 5 不唤醒、改成 <2 就能唤醒)几乎可以直接锁定为:进入 VLPS 执行 WFI 时,BASEPRI 仍被 FreeRTOS 临界区设置为阈值 2(0x20),优先级 5(0x50)的 GPIO 中断处于"被屏蔽"状态,而 ARMv7-M 中被 BASEPRI 屏蔽的中断是唤醒不了 WFI 的------不是 GPIO 配置问题,也不是边沿没检测到(示波器已经证明了边沿存在)。
先厘清两个容易混的机制
- BASEPRI 屏蔽 = 完全不唤醒 :WFI 的唤醒条件是「中断优先级高于当前执行优先级 且 高于 BASEPRI 阈值」。只要中断优先级数值 ≥ BASEPRI 值(5 ≥ 2),既不唤醒也不执行 ISR,CPU 就一直停在 WFI。
- PRIMASK 置位 = 会唤醒但暂不执行 :ARM 明确规定,用
CPSID i(__disable_irq())关中断时,只要来的是"若 PRIMASK 未置位就能抢占"的中断,处理器仍会退出 WFI,只是不进 ISR、保持 pending,等开中断后再响应。
这也是为什么"关总中断反而能唤醒、进临界区反而醒不来"。FreeRTOS 的 taskENTER_CRITICAL() 走的是 BASEPRI 路线,不是 PRIMASK。
另外 AC7843x 的 VLPS 定义是"CPU 进入深度休眠,支持部分模块中断唤醒"------唤醒仍然经过 NVIC 中断路径,所以必然受 BASEPRI/PRIMASK 约束,不存在"绕过 NVIC 的独立引脚唤醒通路"这一侥幸。
可能造成该问题的原因(按可能性排序)
1. 带 BASEPRI 进入 WFI(最可能)
在 taskENTER_CRITICAL() / portENTER_CRITICAL() / portSET_INTERRUPT_MASK() 未退出、BASEPRI 仍为 0x20 的情况下调用了进 VLPS 的函数并 WFI。优先级 5 ≥ 阈值 2 → 不唤醒;改成 1 或 0 → 解除屏蔽 → 正常唤醒。现象完全吻合。
2. Tickless Idle 路径里 BASEPRI 未清零
vPortSuppressTicksAndSleep() 默认实现用 portDISABLE_INTERRUPTS()(BASEPRI,而非 PRIMASK)后在临界区内 __WFI();你自己实现 configPRE_SLEEP_PROCESSING() / configPRE_SLEEP_PROCESSING() 进 VLPS 时,如果没在这个宏里把 BASEPRI 清 0,唤醒源同样被屏蔽。
3. BASEPRI 残留
- 在 ISR 里调用进低功耗代码,用了
taskENTER_CRITICAL_FROM_ISR()但保存/恢复的 BASEPRI 值错误; uxCriticalNesting计数异常,退出临界区时没有把 BASEPRI 写成 0;- 应用层或 SDK 的
PMU_EnterVLPS()一类函数内部自己__set_BASEPRI()/ 关中断且退出后不恢复。
4. 优先级分组不是"全抢占位"
FreeRTOS 要求 AIRCR.PRIGROUP 配成 4 位全部为抢占优先级(等价 NVIC_PRIORITYGROUP_4)。若存在子优先级位,BASEPRI 只比较 group priority 部分,5 与 2 的屏蔽判定会跟你的设计意图对不上。
5. 优先级位数 / 移位不匹配
configPRIO_BITS 与实际 __NVIC_PRIO_BITS 不一致,或把"未移位的 2"直接当 configMAX_SYSCALL_INTERRUPT_PRIORITY 写进 BASEPRI(0x02 的高 4 位为 0,等于不屏蔽;真正表示阈值 2 的是 0x20)。重复移位则会把阈值抬到异常范围。
6. 实际是"唤醒了又睡回去"
CPU 退出过 WFI(如 PRIMASK 场景),但中断未被执行、唤醒标志未置位,主循环判断"无事发生"又进了 VLPS,从外部看像没醒。需要区分这两种情况。
7. 次要但需排除
- SCR.SLEEPONEXIT=1(ISR 返回立即再睡)或 SLEEPDEEP/VLPS 请求位配错;
- 进 VLPS 前中断标志清理顺序错误(应先清 NVIC pending 再清外设标志,顺序反了会丢边沿,但通常表现为"进不去"或"立即退出");
- 改优先级时改到的 IRQn 与实际引脚所属中断线不是一个(多引脚共用 IRQ);
- WFI 前后缺
__DSB(); __ISB();,屏障缺失导致时序竞态。