【问题标题】:Clearing interrupt necessary inside an ISR? (for Atmega644p uC)在 ISR 中清除中断是必要的吗? (对于 Atmega644p uC)
【发布时间】:2022-11-24 02:09:00
【问题描述】:

当中断服务程序正在执行时,是否需要清除全局中断(例如使用 cli(); 命令)以防止另一个 ISR 被执行或排队?

例如,如果正在执行外部中断 INT0,并且在执行时将再次触发同一外部中断。该中断是否会在第一个中断完成后排队等待执行?

如果在当前中断期间执行以下代码,是否会阻止中断排队,或者我是否需要清除中断队列寄存器?

ISR(someISR_vect){
  cli();
  some code...
  sei();
}

【问题讨论】:

    标签: interrupt interrupt-handling atmega


    【解决方案1】:

    TL;DR:不,您不需要手动执行此操作。 AVR 硬件已经为您禁用了中断,编译器不会在您的处理程序执行时重新启用中断(默认情况下)。但是,当您的处理程序正在执行时触发的其他中断不会排队,而是会丢失。


    引用 this page 的“嵌套中断”部分(它没有锚点,CTRL + F“嵌套”):

    AVR 硬件在进入中断向量之前清除 SREG 中的全局中断标志。因此,通常中断将在处理程序内保持禁用状态,直到处理程序退出,此时 RETI 指令(由编译器发出,作为中断处理程序的正常函数尾声的一部分)最终将重新启用进一步的中断。

    ISR() 宏定义了一个具有特殊名称的函数,并在 AVR 固件的中断表中生成对该函数的调用。此外,定义的函数可能设置了 attributes。您感兴趣的两个属性是interruptsignal。两者都将函数声明为中断处理程序,这导致 return 语句被替换为 reti 指令(从中断返回),并且还导致编译器生成“适合在中断处理程序中使用的函数进入和退出序列”。

    不同之处在于interrupt自动生成seicli指令导致中断启用在你的处理程序执行期间,基本上覆盖了硬件设置的默认值。另一方面,signal 不会那样做。

    剩下要做的唯一一件事就是检查ISR() 默认设置了哪一个。通过查阅github上的avr-libc代码,具体是interrupt.h文件可以看出,ISR()宏默认指定了__signal__属性(根据this是同一个属性)。同时,可以作为参数传递给ISR()的宏ISR_BLOCKblank。同时,interrupt属性由ISR_NOBLOCKfew lines below指定。据此,您的中断处理程序的默认属性是signal,它不会在您的处理程序执行时重新启用中断。您可以通过查看使用和不使用 ISR_NOBLOCK 设置编译的中断向量的反汇编来验证这一点,以查看是否生成了 seicli 指令。

    【讨论】:

    • 我对 AVR 不是很熟悉,但是当全局中断标志清除时到达的中断真的被丢弃了吗?这适用于所有中断,还是仅适用于正在处理的中断?我很难想象如果中断会像那样随机丢失,您将如何设计任何类型的可靠系统。