[英]How to use the watchdog timer in a RTOS?
假设我在嵌入式环境中有一个协作调度程序。 我有很多进程在运行。 我想利用看门狗定时器,以便我可以检测进程何时因任何原因停止运行并重置处理器。
在没有RTOS的简单应用程序中,我总是会从主循环接触看门狗,这总是足够的。 但是,在这里,有许多进程可能会挂起。 什么是定期触摸看门狗定时器的清洁方法,同时确保每个过程都处于良好状态?
我想我可以为每个进程提供一个回调函数,这样它就可以让另一个监督所有进程的函数知道它仍然存在。 回调将传递一个参数,该参数将是任务唯一ID,因此监督者可以确定谁正在回叫。
一种常见的方法是将看门狗委托给特定任务(通常是最高优先级或最低优先级,每种方法的权衡/动机),然后让所有其他任务“检入”此任务。
这条路:
如果一个中断挂起(100%CPU),踢球者任务将不会运行,你重置
如果踢球任务挂起,则重置
如果另一个任务挂起,踢球者任务没有检查,踢球者任务没有踢WDG,你重置
现在有一些实施细节需要考虑。 有些人让每个任务在全局变量中设置自己的专用位(原子地); 踢球者任务以特定的速率检查这组位标志,并在每个人都签入时清除/重置(当然还有踢WDG。)我避开像瘟疫一样的全局变量并避免这种方法。 RTOS事件标志提供了一种更优雅的机制。
我通常将我的嵌入式系统设计为事件驱动系统。 在这种情况下,每个任务都会阻塞在一个特定的位置 - 在消息队列中。 所有任务(和ISR)通过发送事件/消息相互通信。 通过这种方式,你不必担心任务没有检查,因为它被阻塞在信号量“那里”(如果这没有意义,对不起,没有写更多我无法解释它更好)。
还有考虑因素 - 执行任务“自主”检查或者他们回复/响应来自踢球者任务的请求。 自治 - 例如,每秒一次,每个任务在其队列中接收一个事件“告诉踢球者任务你还活着”。 回复请求 - 一秒钟(或其他),踢球任务告诉每个人(通过队列)“检查时间” - 并最终每个任务运行其队列,获取请求并回复。 适用任务优先级,排队理论等的考虑因素。
有100种方法可以为这只猫提供皮肤,但是单个任务的基本原则是负责踢动WDG并让其他任务漏斗到踢球者任务的标准。
至少还有一个方面需要考虑 - 在这个问题的范围之外 - 并且处理中断。 如果ISR正在占用CPU,那么上面描述的方法将触发WDG复位(好),但是相反的情况如何 - 一个ISR(可悲地)变得偶然和无意中被禁用。 在许多情况下,这不会被捕获,并且您的系统仍然会启动WDG,但系统的一部分仍然瘫痪。 有趣的东西,这就是我喜欢嵌入式开发的原因。
一种解决方案:
这样,任何永远不会返回健康状态的线程都会停止看门狗任务,直到发生硬件看门狗超时。
在抢占式操作系统中,监视程序线程将是最低优先级或空闲线程。 在协作调度程序中,它应该在回调调用之间产生。
回调函数本身的设计取决于特定任务及其行为和周期性。 每个功能都可以根据任务的需要和特点进行定制。 高周期性的任务可能只是增加一个计数器,当调用回调时,该计数器设置为零。 如果计数器在输入时为零,则自上次看门狗检查后该任务没有计划。 具有低或非周期性行为的任务可能会对其调度加时间戳,如果任务尚未在某个指定时间段内调度,则回调可能会返回失败。 可以通过这种方式监视任务和中断处理程序。 此外,因为线程负责向看门狗注册,所以可能有一些线程根本没有注册。
传统方法是使用具有最低优先级的监视程序进程
PROCESS(watchdog, PRIORITY_LOWEST) { while(1){reset_timer(); sleep(1);} }
并且实际的硬件定时器可能每隔3或5秒重置一次CPU。
跟踪单个进程可以通过反向逻辑实现:每个进程都会设置一个计时器,其回调会向监视程序发送一个“停止”消息。 然后,每个进程都需要取消之前的计时器事件,并在“从队列接收事件/消息”循环中的某处设置一个新事件。
PROCESS(watchdog, PRIORITY_LOWEST) {
while(1) {
if (!messages_in_queue()) reset_timer();
sleep(1);
}
}
void wdg_callback(int event) {
msg = new Message();
send(&msg, watchdog);
};
PROCESS(foo, PRIORITY_HIGH) {
timer event=new Timer(1000, wdg_callback);
while (1) {
if (receive(msg, TIMEOUT)) {
// handle msg
} else { // TIMEOUT expired
cancel_event(event);
event = new Timer(1000,wdg_callback);
}
}
}
每个任务都应该有自己的模拟监视器。 只有当所有模拟的看门狗都没有超时时,真正的看门狗才会被高优先级的实时任务提供。
即:
void taskN_handler()
{
watchdog *wd = watchdog_create(100); /* Create an simulated watchdog with timeout of 100 ms */
/* Do init */
while (task1_should_run)
{
watchdog_feed(wd); /* feed it */
/* do stuff */
}
watchdog_destroy(wd); /* destroy when no longer necessary */
}
void watchdog_task_handler()
{
int i;
bool feed_flag = true;
while(1)
{
/* Check if any simulated watchdog has timeout */
for (i = 0; i < getNOfEnabledWatchdogs(); i++)
{
if (watchogHasTimeout(i)) {
feed_flag = false;
break;
}
}
if (feed_flag)
WatchdogFeedTheHardware();
task_sleep(10);
}
现在,可以说系统真的受到保护,不会冻结,甚至不会冻结,而且大多数情况下,没有不需要的看门狗触发器。
其他答案已经涵盖了您的问题,我只是建议您在旧程序中添加一些内容(不使用RTOS)。 不要无条件地从main()踢掉看门狗,有些ISR可能会卡住,但系统会在没有通知的情况下继续工作(Dan提到的问题也与RTOS有关)。
我一直在做的是关联主要和定时器中断,以便在定时器内对变量进行倒计时,直到它为零,并从主要我检查它是否为零,然后才给监视器供电。 当然,喂食后将变量返回初始值。 很简单,如果变量停止递减,则会得到重置。 如果主要停止为看门狗供电,则会重置。
这个概念很容易适用于已知的周期性事件,但它仍然比从主要事件做的更好。 另一个好处是,乱码不太可能导致看门狗,因为主要内部的看门狗馈送程序已经在一些狂野的循环中结束。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.