单片机睡眠竞赛条件


11

给定一个运行以下代码的微控制器:

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
    }
}

这是实际问题还是理论问题?
AndrejaKo

从理论上讲,您使用一个计时器,该计时器每隔(输入可接受的值)毫秒唤醒您一次,然后在不需要执行任何操作时返回睡眠状态。
Grady Player

1
我将interrupt_flag作为int,并在每次发生中断时将其递增。然后更改if(has_flag)while (interrupts_count),然后进入睡眠状态。但是,退出while循环后可能会发生中断。如果这是一个问题,那么在中断本身中进行处理吗?
angelatlarge

1
好吧,这取决于您正在运行的微控制器。如果它是ATmega328,则可以在中断上禁用睡眠模式,因此,如果您描述的竞争条件发生,那么睡眠功能将被覆盖,再次循环返回,然后您将进行处理中断时间短。但是,使用计时器以等于或小于您的最大延迟的时间间隔唤醒也将是一个很好的解决方案
Kvegaoro

1
@TRISAbits:在PIC 18x上,我在答案中描述的方法很好用(这是使用该部件时的正常设计)。
2013年

Answers:


9

对于这种情况,通常会有某种硬件支持。例如,AVR的sei启用中断指令将延迟启用,直到完成以下指令。使用它可以做到:

forever,
   interrupts off;
   if has_flag,
      interrupts on;
      process interrupt;
   else,
      interrupts-on-and-sleep;    # won't be interrupted
   end
end

在这种情况下,本例中将错过的中断将一直持续到处理器完成其睡眠序列为止。


好答案!您提供的算法实际上可以在AVR上很好地工作。谢谢你的建议。
TRISAbits 2013年

3

在许多微控制器上,除了能够启用或禁用特定的中断原因(通常在中断控制器模块内)之外,CPU内核中还有一个主标志,用于确定是否将接受中断请求。如果中断请求到达内核,无论内核是否愿意接受它,许多微控制器都会退出睡眠模式。

在这样的设计上,实现可靠睡眠行为的一种简单方法是让主循环检查清除标志,然后检查是否知道处理器应该唤醒的任何原因。在这段时间内发生的任何中断都可能影响这些原因中的任何一个,都应该设置该标志。如果主循环没有找到任何保持清醒的原因,并且未设置该标志,则主循环应禁用中断并再次检查该标志[也许在执行了两次NOP指令后,如果有可能中断正在等待处理在执行与以下指令相关的操作数提取之后,可能会在禁用中断指令期间进行处理]。如果仍未设置该标志,请进入睡眠状态。

在这种情况下,在主循环禁用中断之前发生的中断将在最终测试之前设置标志。在睡眠指令之前太晚而无法处理的中断将阻止处理器进入睡眠状态。两种情况都很好。

退出时睡眠有时是一个很好的模型,但并非所有应用程序都“适合”它。例如,具有节能LCD的设备最容易用如下代码编写:

void select_view_user(int default_user)
{
  int current_user;
  int ch;
  current_user = default_user;
  do
  {
    lcd_printf(0, "User %d");
    lcd_printf(1, ...whatever... );
    get_key();
    if (key_hit(KEY_UP)) {current_user = (current_user + 1) % MAX_USERS};
    if (key_hit(KEY_DOWN)) {current_user = (current_user + MAX_USERS-1) % MAX_USERS};
    if (key_hit(KEY_ENTER)) view_user(current_user);
  } while(!key_hit(KEY_EXIT | KEY_TIMEOUT));
}

如果没有按下任何按钮,并且没有其他任何事情发生,则没有理由系统在执行该get_key方法期间不应该进入睡眠状态。尽管有可能使按键触发中断并通过状态机管理所有用户界面交互,但上述代码通常是处理小型微控制器典型的高度模式用户界面流程的最合乎逻辑的方法。


感谢supercat的出色回答。禁用中断然后进入休眠状态是一个绝妙的解决方案,只要内核可以从任何中断源中唤醒,无论全局中断位是否被设置/清除。我看了一下PIC18中断硬件原理图,该解决方案可以正常工作。
TRISAbits 2013年

1

对微控制器编程以在中断时唤醒。

具体细节将根据您所使用的麦克风而有所不同。

然后修改main()例程:

int main()
{
    while(1)
    {
        sleep();
        process(); //process the interrupt
    }
}

1
问题中假设了唤醒唤醒架构。我认为您的答案不能解决问题。
angelatlarge

@angelatlarge点被接受。我添加了一个我认为有帮助的代码示例。
jwygralak67 2013年

@ jwygralak67:感谢您的建议,但是您提供的代码仅将问题移到了process()例程中,该例程现在必须在执行process()主体之前测试中断是否发生。
TRISAbits

2
如果没有发生中断,为什么我们要醒着?
JRobert

1
@JRobert:我们可能会从上一个中断中醒来,完成process()例程,当我们完成if(has_flag)测试并且就在睡眠之前时,我们又得到了另一个中断,这导致了我在题。
TRISAbits
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.