2. VxWorks看门狗API:wdCreate、wdStart、wdCancel、wdDelete函数详解与参数说明

好,咱们进入正题。看门狗定时器(Watchdog Timer)在嵌入式系统里,说白了就是一个「保命符」。系统跑飞了?死锁了?任务卡死了?看门狗会帮你把系统拉回来。VxWorks 提供了一套简洁的 API 来管理看门狗,今天我就把这四个核心函数掰开揉碎讲清楚。

2.1 wdCreate —— 创建看门狗定时器

这个函数用来创建一个看门狗定时器对象。原型很简单:

WDOG_ID wdCreate(void)

返回一个 WDOG_ID 类型的句柄。如果创建失败,返回 NULL。

参数说明: 无参数。

注意事项:

  • 每个看门狗定时器会占用系统资源(内存、定时器槽位)。
  • 创建的数量受系统配置限制,默认通常支持几十个。
  • 创建成功后,看门狗处于「未启动」状态。
我的经验: 我个人习惯在系统初始化阶段就把所有看门狗一次性创建好。别在任务运行中频繁创建删除,容易造成内存碎片。我曾经在一个项目中,因为动态创建看门狗导致系统运行几天后莫名其妙挂掉,查了两天才发现是内存碎片惹的祸。

2.2 wdStart —— 启动看门狗定时器

创建完看门狗,得让它跑起来。wdStart 就是干这个的:

STATUS wdStart
    (
    WDOG_ID  wdogId,    /* 看门狗ID */
    int      delay,     /* 延迟时间,单位:tick */
    FUNCPTR  routine,   /* 超时回调函数 */
    int      parameter  /* 传给回调函数的参数 */
    )

参数详解:

参数 说明 典型值/注意事项
wdogId 由 wdCreate 返回的句柄 必须有效,否则返回 ERROR
delay 超时时间(tick数) 最小为 1 tick。0 表示立即触发
routine 超时后执行的回调函数 在系统时钟中断上下文中执行!
parameter 传递给回调函数的参数 通常传一个整数或指针
⚠️ 重要警告: 回调函数是在中断上下文中执行的!这意味着:
  • 不能调用可能阻塞的函数(如 semTake、taskDelay)
  • 不能进行浮点运算(除非使能了浮点中断支持)
  • 执行时间要尽可能短,否则会影响系统实时性
我曾经见过一个同事在回调里调用了 printf,结果系统直接死机。嗯,这个坑我踩过,你别再踩了。

2.3 wdCancel —— 取消看门狗定时器

有时候看门狗启动了,但条件满足了,不需要它再触发。这时候用 wdCancel:

STATUS wdCancel
    (
    WDOG_ID  wdogId     /* 看门狗ID */
    )

返回值:

  • OK:取消成功
  • ERROR:取消失败(比如看门狗已经触发过了)

使用场景:

  • 任务正常完成,不需要看门狗干预了
  • 重新设置超时时间前,先取消旧的
  • 系统进入休眠/低功耗模式前
避坑指南: 我曾经在项目中遇到一个问题:看门狗已经触发了,但代码还在调用 wdCancel。结果返回 ERROR,但程序没检查返回值,继续往下走,导致逻辑混乱。记住:一定要检查 wdCancel 的返回值! 如果返回 ERROR,说明看门狗已经触发或不存在,需要做相应处理。

2.4 wdDelete —— 删除看门狗定时器

看门狗不用了,记得删除,释放资源:

STATUS wdDelete
    (
    WDOG_ID  wdogId     /* 看门狗ID */
    )

注意事项:

  • 删除前,看门狗必须处于「未启动」状态
  • 如果看门狗正在运行,需要先调用 wdCancel
  • 删除后,WDOG_ID 变为无效,不能再使用
我的习惯: 我一般会在删除前先调用 wdCancel,即使我知道它可能已经停止了。为什么?因为安全。你想想看,如果某个任务意外重启了看门狗,而你直接删除,系统可能会崩溃。先取消再删除,双重保险。

2.5 完整使用示例

光说不练假把式。我给你写一个完整的示例,展示这四个函数怎么配合使用:

/* 看门狗回调函数 */
void myWatchdogRoutine(int param)
{
    /* 注意:这是在中断上下文中! */
    logMsg("Watchdog timeout! param=%d\n", param, 0,0,0,0,0);
    
    /* 这里可以做紧急处理,比如重启任务 */
    taskRestart((int)param);
}

/* 某个任务中使用看门狗 */
void myTask(void)
{
    WDOG_ID wdId;
    int myTaskId = taskIdSelf();
    
    /* 1. 创建看门狗 */
    wdId = wdCreate();
    if (wdId == NULL)
    {
        logMsg("wdCreate failed!\n", 0,0,0,0,0,0);
        return;
    }
    
    /* 2. 启动看门狗,5秒超时(假设系统tick=100Hz) */
    if (wdStart(wdId, 500, myWatchdogRoutine, myTaskId) != OK)
    {
        logMsg("wdStart failed!\n", 0,0,0,0,0,0);
        wdDelete(wdId);
        return;
    }
    
    /* 3. 执行主要工作 */
    while (1)
    {
        /* 做一些需要监控的操作 */
        if (workDone())
        {
            /* 工作完成,取消看门狗 */
            wdCancel(wdId);
            break;
        }
        taskDelay(1);  /* 让出CPU */
    }
    
    /* 4. 清理 */
    wdDelete(wdId);
}
核心要点总结:
  • 创建 → 启动 → 取消/触发 → 删除,这是标准生命周期
  • 回调函数在中断上下文,别干重活
  • 一定要检查返回值,尤其是 wdCancel 和 wdStart
  • 删除前先取消,养成好习惯

好了,这四个函数就讲完了。说白了,看门狗 API 本身不复杂,复杂的是怎么用好它。下一章我会讲看门狗在系统可靠性设计中的实战技巧,包括多级看门狗、喂狗策略这些。到时候我会分享一些我在实际项目中踩过的坑,保证让你少走弯路。