wfi进入之前没有清除basepri, 导致中断优先级不够,wfi 退不出来

  • 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();,屏障缺失导致时序竞态。
相关推荐
听风说ml18 小时前
socket编程 【C语言】
c语言·udp·tcp·socket编程
学逆向的19 小时前
C 局部静态变量
c语言·开发语言·网络安全
Zwawa1 天前
从 printf 到曲线:把数据送上 VOFA+
c语言·单片机·嵌入式
二十七日秋1 天前
算法刷题笔记「Day 1|四道 OJ 入门题里的深坑」
c语言·汇编·数据结构·c++·笔记·python·算法
abc light1 天前
ARM 裸机开发(1)——ARM 体系结构
c语言·arm开发
Navigator_Z1 天前
LeetCode //C - 1309. Decrypt String from Alphabet to Integer Mapping
c语言·算法·leetcode
爱聊灵光的维C1 天前
C语言初阶:关于函数设计的详细讲解和注意事项
c语言·开发语言·青少年编程·蓝桥杯·学习方法
wuminyu1 天前
JVM规范学习总结:class执行性能差异
java·linux·c语言·jvm·c++
C++ 老炮儿的技术栈1 天前
Linux 动态库踩坑:明明重新编译了 .so,调用函数返回值却一直不变(libfjrobot.so 版本号异常排查实录)
linux·c语言·网络·c++·visual studio
小向是个Der2 天前
嵌入式进阶——MCU上电后的动作
c语言·单片机·mcu·学习·链接脚本