C 如何在RTOS中使用看门狗定时器?

C 如何在RTOS中使用看门狗定时器?,c,embedded,rtos,watchdog,C,Embedded,Rtos,Watchdog,假设我在嵌入式环境中有一个协作调度器。我有许多进程正在运行。我想利用看门狗定时器,这样我就可以检测进程何时因任何原因停止运行,并重置处理器 在没有RTO的简单应用程序中,我总是从主循环触摸看门狗,这总是足够的。然而,这里有许多进程可能会挂起。在确保每个过程处于良好状态的同时,定期触摸看门狗定时器的清洁方法是什么 我在想,我可以为每个进程提供一个回调函数,这样它就可以让另一个监视所有进程的函数知道它还活着。回调将传递一个参数,该参数将是任务的唯一id,以便监督者可以确定谁在回调 传统的方法是让看门

假设我在嵌入式环境中有一个协作调度器。我有许多进程正在运行。我想利用看门狗定时器,这样我就可以检测进程何时因任何原因停止运行,并重置处理器

在没有RTO的简单应用程序中,我总是从主循环触摸看门狗,这总是足够的。然而,这里有许多进程可能会挂起。在确保每个过程处于良好状态的同时,定期触摸看门狗定时器的清洁方法是什么


我在想,我可以为每个进程提供一个回调函数,这样它就可以让另一个监视所有进程的函数知道它还活着。回调将传递一个参数,该参数将是任务的唯一id,以便监督者可以确定谁在回调

传统的方法是让看门狗进程具有尽可能低的优先级

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

一种常见的方法是将看门狗踢腿授权给特定任务(通常是最高优先级或最低优先级,每种方法的权衡/动机),然后让所有其他任务“签入”该任务

这样:

  • 如果中断被挂起(100%CPU),kicker任务将不会运行,您需要重置

  • 如果kicker任务挂起,则重置

  • 如果另一个任务被挂起,kicker任务未看到签入,kicker任务不触发WDG,则重置

现在当然要考虑具体的实施细节。有些人让每个任务在全局变量中(原子地)设置自己的专用位;kicker任务以特定的速率检查这组位标志,并在每个人都签入时清除/重置(当然还有踢WDG)。我避免像瘟疫一样的全局性,避免这种方法。RTOS事件标志提供了一种更为优雅的类似机制

我通常将嵌入式系统设计为事件驱动系统。在这种情况下,每个任务在消息队列上的一个特定位置阻塞。所有任务(和ISR)通过发送事件/消息相互通信。这样,您就不必担心任务没有签入,因为它被“那边”的信号灯阻塞了(如果这没有意义,对不起,如果没有写更多的话,我无法更好地解释它)

另外还有一个考虑因素-任务是否“自动”签入,或者是否答复/响应踢球者任务的请求。自治-例如,每秒钟一次,每个任务在其队列中接收一个事件“告诉kicker任务您仍然活着”。回复请求-kicker tasks每隔一秒钟(或任何时间)告诉每个人(通过队列)“报到时间”-最终每个任务都运行其队列,获得请求和回复。应考虑任务优先级、排队理论等

有100种方法可以剥这只猫的皮,但是一个任务的基本原则是负责踢WDG,并让其他任务进入踢者任务,这是相当标准的

至少有另外一个方面需要考虑——在这个问题的范围之外,那就是处理中断。如果ISR占用CPU,我上面描述的方法将触发WDG重置(好),但是如果情况相反,ISR(不幸)意外地被禁用,又会怎样呢。在许多情况下,这将不会被捕获,并且您的系统仍将启动WDG,但您的系统的一部分已瘫痪。有趣的东西,这就是我喜欢嵌入式开发的原因。

一种解决方案模式:

  • 希望被检查的每个线程都显式地向看门狗线程注册其回调,看门狗线程维护一个此类回调的列表
  • 当看门狗被调度时,它可以迭代已注册任务的列表
  • 每个回调本身都会被迭代调用,直到返回正常状态
  • 在列表的末尾,硬件看门狗被踢了
这样,任何永远不会返回正常状态的线程都将暂停看门狗任务,直到硬件看门狗超时

在抢占式操作系统中,看门狗线程将是最低优先级或空闲线程。在协作调度器中,它应该在两次回调调用之间产生


回调函数本身的设计取决于特定任务及其行为和周期。每个功能都可以根据任务的需要和特点进行定制。高周期性的任务可能只是增加一个计数器,当调用回调时,计数器被设置为零。如果输入时计数器为零,则自上次看门狗检查以来任务未计划。具有低或非周期行为的任务可能会在其调度上加上时间戳,如果任务未在指定的时间段内调度,则回调可能会返回失败。任务和中断处理程序都可以通过这种方式进行监视。此外,由于线程负责向看门狗注册,因此可能有一些线程根本不注册。

每个任务都应该有自己的模拟看门狗。只有在所有模拟看门狗都没有超时的情况下,真正的看门狗才由高优先级的实时任务馈送

i、 e:

void taskN_handler()
{
watchdog*wd=watchdog_create(100);/*创建一个超时为100毫秒的模拟看门狗*/
/*开始*/
while(task1\u应该运行)
{
看门狗喂食(wd);/*喂食它*/
/*做事*/
}
看门狗销毁(wd);/*不再需要时销毁*/
}
无效看门狗任务
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);
}