给定一个运行以下代码的微控制器:
volatile bool has_flag = false;
void interrupt(void) //called when an interrupt is received
{
clear_interrupt_flag(); //clear interrupt flag
has_flag = true; //signal that we have an interrupt to process
}
int main()
{
while(1)
{
if(has_flag) //if we had an interrupt
{
has_flag = false; //clear the interrupt flag
process(); //process the interrupt
}
else
sleep(); //place the micro to sleep
}
}
假设if(has_flag)
条件评估为假,我们将执行睡眠指令。 右我们执行睡眠指令之前,我们接收中断。离开中断后,我们执行睡眠指令。
此执行顺序是不理想的,因为:
- 微控制器进入睡眠状态,而不是唤醒并调用
process()
。 - 如果此后未接收到中断,则微控制器可能永远不会唤醒。
- 呼叫
process()
被推迟到下一个中断为止。
如何编写代码以防止发生这种竞争情况?
编辑
某些ATMega等微控制器具有休眠使能位,可以防止这种情况的发生(感谢Kvegaoro指出这一点)。JRoberts提供了一个示例实现来例证这种行为。
其他微控制器(如PIC18s)没有此位,问题仍然存在。但是,这些微控制器的设计使得无论是否设置了全局中断允许位,中断仍可以唤醒内核(感谢您的超级猫指出)。对于此类架构,解决方案是在进入睡眠之前禁用全局中断。如果恰好在执行睡眠指令之前触发了中断,则不会执行中断处理程序,内核将唤醒,并且一旦重新启用全局中断,就会执行中断处理程序。在伪代码中,实现如下所示:
int main()
{
while(1)
{
//clear global interrupt enable bit.
//if the flag tested below is not set, then we enter
//sleep with the global interrupt bit cleared, which is
//the intended behavior.
disable_global_interrupts();
if(has_flag) //if we had an interrupt
{
has_flag = false; //clear the interrupt flag
enable_global_interrupts(); //set global interrupt enable bit.
process(); //process the interrupt
}
else
sleep(); //place the micro to sleep
}
}
interrupt_flag
作为int
,并在每次发生中断时将其递增。然后更改if(has_flag)
为while (interrupts_count)
,然后进入睡眠状态。但是,退出while循环后可能会发生中断。如果这是一个问题,那么在中断本身中进行处理吗?