当前位置:首页 > 工业控制 > 工业控制
[导读]看门狗不是“主循环里加一句喂狗”就完事。工业现场要的是两层能力:跑飞能硬复位恢复 + 复位后知道上次为什么死。只喂狗不记日志,设备天天重启却查不出根因;只记日志不喂狗,程序卡死就真卡死。正经做法是 IWDG 兜底 + WWDG 监控关键周期 + 复位标志溯源 + 故障日志落非易失区。


看门狗不是“主循环里加一句喂狗”就完事。工业现场要的是两层能力:跑飞能硬复位恢复 + 复位后知道上次为什么死。只喂狗不记日志,设备天天重启却查不出根因;只记日志不喂狗,程序卡死就真卡死。正经做法是 IWDG 兜底 + WWDG 监控关键周期 + 复位标志溯源 + 故障日志落非易失区。

一、IWDG 与 WWDG 分工

独立看门狗走 LSI 低速时钟,主时钟炸了它还在跑,适合做最后防线;只要在计数归零前喂就行。

窗口看门狗走 APB 时钟,必须在“窗口”内喂,太早太晚都复位,专门防“主循环没死但时序已经乱了”的伪活状态。

工业设备常见搭配:

IWDG:超时 1~2s,主调度任务喂

WWDG:关键控制任务周期监控,比如 10ms 控制环

外部 CPLD/看门狗芯片:MCU 彻底死机时硬复位

二、喂狗别放中断里

最典型误区:定时器中断里 IWDG_Reload()。主循环已经死循环,定时器中断还在跑,看门狗永远不触发。

正确是“系统级健康”才喂:

主循环/最低优先级任务喂 IWDG

各业务任务维护心跳计数

监控任务检查所有心跳,异常时不喂狗

typedef struct {

   const char*name;

   uint32_t tick;       // 每次正常跑自增

   uint32_t last_chk;

}task_hb_t;


task_hb_t hb[4];


void task_mark(int id){ hb[id].tick++; }


void watchdog_guard_1s(void){

   uint8_t alive=1;

   for(int i=0;i<4;i++){

       if(hb[i].tick == hb[i].last_chk){

           alive=0;

           log_fault("task stuck:", hb[i].name);

       }

       hb[i].last_chk = hb[i].tick;

   }

   if(alive) iwdg_feed();

   /* 不alive:不喂,IWDG超时复位 */

}

WWDG 可在 EWI 提前唤醒中断里存“临终现场”:当前任务ID、关键状态字,再让它在窗口外自然复位。

三、启动第一段:读复位标志

复位原因全在 RCC_CSR / SRS 里,上电第一时间读,读完清掉,不然下次叠加旧标志。

void boot_check_reset(void){

   uint32_t csr = RCC->CSR;

   uint8_t reason;

   if(csr & RCC_CSR_IWDGRSTF)      reason = RST_IWDG;

   else if(csr & RCC_CSR_WWDGRSTF) reason = RST_WWDG;

   else if(csr & RCC_CSR_SFTRSTF)  reason = RST_SOFT;

   else if(csr & RCC_CSR_PORRSTF)  reason = RST_POR;

   else                            reason = RST_PIN;


   fault_log_write(reason);     // 写备份区/EEPROM

   RCC->CSR |= RCC_CSR_RMVF;    // 清标志

}

读到 IWDG/WWDG 复位 → 上一次有任务卡死;读到 POR/BOR 频繁 → 先查电源别怪软件。

四、故障日志:RAM 环形 + 严重落盘

别每条 printf 写 Flash,擦写寿命和实时性都崩。架构:

正常日志进 RAM 环形缓冲

WARN/ERROR 以上才考虑持久化

看门狗复位、HardFault、掉电事件 → 写备份寄存器/EEPROM/FRAM

typedef struct {

   uint32_t magic;     // 0xFA55

   uint16_t seq;

   uint8_t  reason;

   uint8_t  task_id;

   uint32_t ts;

   uint16_t crc;

}fault_rec_t;


void fault_log_write(uint8_t reason){

   fault_rec_t r;

   r.magic=0xFA55;

   r.seq = nv_read16(ADDR_SEQ)+1;

   r.reason=reason;

   r.task_id = current_task_id();

   r.ts = rtc_ms();

   r.crc = crc16((uint8_t*)&r, sizeof(r)-2);

   nv_write(ADDR_FAULT_LAST, &r, sizeof(r));

   nv_write16(ADDR_SEQ, r.seq);

}

FRAM/MRAM 不用擦除,掉电瞬间几十微秒能写完;用 NOR Flash 必须靠掉电中断+储能电容短窗口提交。

五、分级自愈(可选)

不是所有超时都直接复位:

第一次任务看门狗超时 → 记日志、删任务重启

连续 N 次 → IWDG 不管了,系统复位

关键安全设备 → 先断输出再复位

void wdg_timeout_cb(int task_id){

   fault_log_write(RST_TASK_STUCK);

   if(++fault_cnt > MAX_RETRY){

       NVIC_SystemReset();

   }else{

       task_restart(task_id);

   }

}

六、落坑清单

中断里喂狗 → 主循环死循环被掩盖

不读 RCC_CSR → 永远不知道为啥重启

读完不清 RMVF → 标志叠加误判

每帧日志写 Flash → 寿命耗尽+卡业务

WWDG 窗口配错 → 正常喂狗也复位

LSI 精度±30%当精确计时 → 超时时间留余量

HardFault 里还死等串口 → 看门狗又超时嵌套

备份区不写 CRC → 读到半截脏数据

只开 IWDG 不监控任务 → 伪活发现不了

调试阶段忘关狗 → 单步必复位

七、落地顺序

开 IWDG,超时按最长任务周期 1.5~2 倍

主调度任务统一喂狗,中断不喂

各任务心跳计数,监控任务巡检

启动首行读复位标志、存备份区、清 RMVF

RAM 环形日志跑正常输出

严重故障(看门狗/HardFault/掉电)写 FRAM/EEPROM

关键任务加 WWDG 或任务看门狗分级重启

外部 CPLD 看门狗兜底 MCU 死机

工业看门狗的成熟形态不是“重启就行”,是重启能恢复 + 重启有记录。IWDG 兜住跑飞,WWDG/任务心跳兜住伪活,复位标志区分电源问题和软件卡死,故障日志落非易失区留 seq+crc——下次现场设备自己复位了,串口拉出来能看到“WWDG复位 / 任务=can_poll / 第37次”,不用靠猜。日志写盘频率压住、喂狗点放对地方,这两点守住,看门狗才是可靠性资产而不是掩盖 bug 的遮羞布。



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭