如何在ISR中清除或阻止挂起中断?基于ATSAMD21的技术问询
解决ATSAMD21 ISR执行期间额外中断触发的问题
针对你遇到的ATSAMD21(Cortex-M0+)外部中断ISR重复触发问题——真实中断触发一次却导致两次ISR执行,核心原因是ISR执行期间数据引脚的额外下降沿触发了EIC(外部中断控制器)的中断请求,且之前的中断禁用/清除操作顺序错误,没有从源头阻断中断请求。
有效解决方案代码
假设触发引脚对应EXTINT2,正确的ISR实现需从**中断源头(EIC)**入手,按「禁用EIC引脚中断 → 清除EIC中断标志 → 清除NVIC挂起 → 执行业务逻辑 → 恢复EIC引脚中断」的顺序操作:
#include <samd21.h> void interrupt_cb(void) { // 1. 先禁用对应引脚的EIC中断(阻断中断源头) EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT2; __DSB(); // 确保寄存器写操作完成,防止编译器重排 // 2. 清除EIC的中断标志位(ATSAMD21需写1清除) EIC->INTFLAG.reg = EIC_INTFLAG_EXTINT2; __DSB(); // 3. 清除NVIC中的挂起中断,避免残留请求 NVIC_ClearPendingIRQ(EIC_IRQn); __ISB(); // 确保NVIC状态同步更新 // ------------------------------ // 4. 执行你的24位数据读取逻辑 // < 这里放你的位bang设备读取代码 > // ------------------------------ // 5. 重新启用对应引脚的EIC中断,准备响应下一次真实中断 EIC->INTENSET.reg = EIC_INTENSET_EXTINT2; __DSB(); }
为什么之前的操作无效?
你之前尝试的方法存在几个关键问题:
- 操作顺序错误:先禁用NVIC或全局中断,再处理EIC——NVIC只是中断分发器,EIC作为中断源仍会产生请求,导致NVIC挂起位被反复置位。必须先从EIC层面禁用引脚中断,阻断源头。
- 未清除EIC中断标志:ATSAMD21的EIC中断标志位(
INTFLAG)需要写1清除,若只清NVIC挂起位,EIC的未处理标志会持续向NVIC发送请求,导致挂起位再次被置位。 - 全局中断操作无效:Cortex-M0+进入ISR时会自动设置
PRIMASK屏蔽全局中断,退出时自动恢复,ISR内调用__disable_irq()不会改变最终中断状态,反而可能干扰正常流程。
额外注意事项
- 硬件滤波:如果数据引脚的额外下降沿由噪声导致,建议在引脚上添加100nF左右的滤波电容,从根源减少误触发。
- 寄存器volatile修饰:确保EIC相关寄存器的访问被编译器视为易变的,避免优化导致操作丢失(可直接使用SAM库提供的宏)。
- 调试验证:可在ISR开头和结尾添加GPIO翻转操作,配合示波器观察ISR执行次数,验证解决方案是否生效。
内容的提问来源于stack exchange,提问作者aulven
相关产品推荐
相关产品推荐

