我认为这是“vi vs. emacs”类型的问题之一,但无论如何我都会问,因为我想听听别人的意见。
在嵌入式系统中,微控制器通常具有硬件定时器外设,为软件定时器子系统提供定时基础。该子系统允许开发人员创建任意数量(受系统资源约束)的定时器数量,这些定时器可用于生成和管理系统中的事件。通常管理软件定时器的方式是硬件定时器设置为以固定间隔(或者有时仅在下一个活动定时器到期时)生成。在中断处理程序中,调用回调函数来执行特定于该计时器的操作。与往常一样,这些回调例程应该非常短,因为它们在中断上下文中运行。
假设我创建一个每1ms触发一次的计时器,其回调例程需要100us才能执行,这是系统中唯一感兴趣的事情。定时器子系统应该何时安排下一次处理此软件定时器?它应该是中断发生后的1ms还是回调完成后的1ms?
为了让事情变得更有趣,硬件开发人员说,并说在某些操作模式下,CPU速度需要降低到最大值的20%以节省电量。现在回调例程需要500us而不是100us,但计时器的间隔仍然是1ms。假设在此待机模式下,回调中增加的延迟对系统没有负面影响。同样,计时器子系统何时应该安排下一次处理此软件时间? T + 1ms或T + 500us + 1ms?
或许在这两种情况下都应该拆分差异并安排在T +(execution_time / 2)+ 1ms?
答案 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运行几次,直到它“赶上”。如果它落后太多,它试图“追赶”的尝试将是有限的。