应该重复发生的软件定时器何时触发它们之前的超时?

时间:2011-03-23 23:42:16

标签: embedded real-time

我认为这是“vi vs. emacs”类型的问题之一,但无论如何我都会问,因为我想听听别人的意见。

在嵌入式系统中,微控制器通常具有硬件定时器外设,为软件定时器子系统提供定时基础。该子系统允许开发人员创建任意数量(受系统资源约束)的定时器数量,这些定时器可用于生成和管理系统中的事件。通常管理软件定时器的方式是硬件定时器设置为以固定间隔(或者有时仅在下一个活动定时器到期时)生成。在中断处理程序中,调用回调函数来执行特定于该计时器的操作。与往常一样,这些回调例程应该非常短,因为它们在中断上下文中运行。

假设我创建一个每1ms触发一次的计时器,其回调例程需要100us才能执行,这是系统中唯一感兴趣的事情。定时器子系统应该何时安排下一次处理此软件定时器?它应该是中断发生后的1ms还是回调完成后的1ms?

为了让事情变得更有趣,硬件开发人员说,并说在某些操作模式下,CPU速度需要降低到最大值的20%以节省电量。现在回调例程需要500us而不是100us,但计时器的间隔仍然是1ms。假设在此待机模式下,回调中增加的延迟对系统没有负面影响。同样,计时器子系统何时应该安排下一次处理此软件时间? T + 1ms或T + 500us + 1ms?

或许在这两种情况下都应该拆分差异并安排在T +(execution_time / 2)+ 1ms?

4 个答案:

答案 0 :(得分:5)

在实时操作系统中,定时器和延迟都与系统节拍同步,因此如果事件处理少于一个定时器节拍,并且在定时器节拍边界上启动,则使用定时器之间没有调度差异或延迟。

另一方面,如果处理过多次,则需要一个定时器事件来确保确定性的无抖动时序。

在大多数情况下,确定性很重要或必不可少,并使系统行为更具可预测性。如果时间从处理结束开始递增,则处理的可变性(静态 - 通过代码更改或通过差异执行路径的运行时)可能导致变量行为和未经测试的极端情况,这些情况难以调试或可能导致系统故障。

答案 1 :(得分:3)

我会让硬件计时器每1ms发射一次。我从来没有听说过考虑过这么快的例程的硬件计时器。特别是因为每次软件更改时都必须重新计算。或者弄清楚当CPU改变时钟速度时该怎么做。或者弄清楚如果您决定升级/降级正在使用的CPU,该怎么办。

答案 2 :(得分:1)

在此时添加另外几个理由的共识答案(计时器应该每1ms发射一次):

  • 如果计时器每1ms触发一次,你真正想要的是执行间隔1ms,你可以在你的回调函数退出时重置计时器,从那一点开始1ms。

  • 但是,如果计时器在回调函数退出后1ms触发,并且你想要其他行为,那么你就会陷入困境。

此外,每1ms发射一次,硬件复杂程度要低得多。要做到这一点,它只会生成事件和重置,除了在设置点之外,没有从软件返回到计时器的反馈。如果定时器留有1ms的间隙,则需要有一些方法让软件向定时器发出信号,告知它正在退出回调。

你当然不应该“分裂差异”。这对于每个人来说都是错误的,如果有人想让它做其他事情,那么解决这个问题会更加讨厌。

答案 3 :(得分:0)

我倾向于让默认行为是按照实际上几乎一致的间隔开始例行程序,并且让一个迟到的例程尝试在一定范围内“赶上”。有时好的模式可能是这样的:

/* Assume 32,768Hz interrupt, and that we want foo() to execute 1024x/second */

  typedef unsigned short ui; /* Use whatever size int works out best */
  ui current_ticks;  /* 32768Hz ticks */

  ui next_scheduled_event;
  ui next_event;

void interrupt_handler(void)
{
  current_ticks++;
  ...
  if ((ui)(current_ticks - next_event)  EVENT_INTERVAL*EVENT_MAX_BACKLOG)  /* We're 32 ticks behind -- don't even try to catch up */
    {
      delta = EVENT_INTERVAL*EVENT_MAX_BACKLOG;
      next_scheduled_event = current_ticks - delta;
    }
    next_scheduled_event += EVENT_INTERVAL;
    next_event = next_scheduled_event;

    foo();

    /* See how much time there is before the next event */
    delta = (ui)(current_ticks - next_event - EVENT_MIN_SPACING);
    if (delta > 32768)
      next_event = current_ticks + EVENT_MIN_GAP;
  }

如果可以,此代码(未经测试)将以统一的速率运行foo(),但在执行之间将始终允许EVENT_MIN_SPACING。如果它有时无法以所需的速度运行,它将在执行之间使用EVENT_MIN_SPACING运行几次,直到它“赶上”。如果它落后太多,它试图“追赶”的尝试将是有限的。