一.看门狗原理
在产品化的嵌入式系统中,为了使系统在异常情况下能自动复位,一般都需要引入看门狗。
看门狗其实就是一个可以在一定时间内被复位的计数器。当看门狗启动后,计数器开始自动计数,经过一定时间,如果没有被复位,计数器溢出就会对CPU产生一个复位信号使系统重启(俗称“被狗咬”)。系统正常运行时,需要在看门狗允许的时间间隔内对看门狗计数器清零(俗称“喂狗”),不让复位信号产生。如果系统不出问题,程序保证按时“喂狗”,一旦程序跑飞,没有“喂狗”,系统“被咬”复位。
这就好比你的身边有一只时刻饥饿的狗,你不按时给它肉吃,它就会吃你的肉,你被吃了......挂了,投胎转世,彻底重新做人了。不幸的是来世你还得喂狗.
二.看门狗种类
在现在的嵌入式系统中主要可以分为两种类型的看门狗:
1、CPU内部自带的看门狗:此类看门狗一般是将一个芯片中的定时器来作为看门狗,通过程序的初始化,写入初值,设定溢出时间,并启动定时器。程序按时对定时器赋初值(或复位),以免被咬。这种看门狗是可以被禁用的(只要停止这个定时器即可),好比对那只要咬你的狗来个“葵花点穴手”。大部分CPU都内置看门狗,硬件原理可参考各芯片数据手册。
优点:可以通过程序改变溢出时间;可以随时禁用
缺点:需要初始化;如果程序在初始化、启动完成前跑飞或在禁用后跑飞,看门狗就无法复位系统,这样看门狗的作用就没有了,系统恢复能力降低。
2、独立的看门狗芯片:这种看门狗主要有一个用于喂狗的引脚(一般与CPU的GPIO相连)和一个复位引脚(与系统的RESET引脚相连),如果没有在一定时间内改变喂狗脚的电平,复位引脚就会改变状态复位CPU。此类看门狗一上电就开始工作,无法禁用。现在常用的芯片有:CAT705/CAT706、IMP706等等,溢出时间在1.6秒左右。
优点:无须配置,上电即用。无法禁用,系统必须按时喂狗,系统恢复能力高。
缺点:无法灵活配置溢出时间,无法禁用,灵活性降低。
三.DTS配置
wdt: watchdog@feaf0000 {
compatible = "snps,dw-wdt";
reg = <0x0 0xfeaf0000 0x0 0x100>;
clocks = <&cru TCLK_WDT0>, <&cru PCLK_WDT0>;
clock-names = "tclk", "pclk";
interrupts = <GIC_SPI 315 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
看门狗默认没有使能,首先需在 DTS 文件中打开 wdt 节点:
&wdt{
status = "okay";
};
四.看门狗的使用
watchdog 的驱动文件为 kernel-5.10/drivers/watchdog/dw_wdt.c
看门狗设备的查看:
ls /dev/watchdog* -l
可以通过 echo
命令来控制该设备:
# 写入任意内容(大写字母‘V’除外),开启看门狗,每 44 秒内需要写入一次(喂狗)
echo A > /dev/watchdog
# 开启看门狗,并且内核会每隔 22 秒自动喂一次狗
echo V > /dev/watchdog
也可以通过程序来控制看门狗,通过Android.mk
编译生成可执行文件,在机器启动后,adb push
到上面运行,代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#define WDIOC_SETTIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
#define WDIOC_GETTIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 7, int)
int main(void)
{
int timeout;
int fd;
int ret = 0;
fd = open("/dev/watchdog", O_WRONLY); //start watchdog
if (fd == -1) {
perror("watchdog");
exit(EXIT_FAILURE);
}
timeout = 22;
ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout); //set timeout
if (ret < 0) {
printf("ioctl WDIOC_SETTIMEOUT failed.\n");
goto fail;
}
ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout); //get timeout
if (ret < 0) {
printf("ioctl WDIOC_SETTIMEOUT failed.\n");
goto fail;
} else {
printf("timeout = %d\n", timeout);
}
while (1) {
ret = write(fd, "\0", 1); //feed the dog
if (ret != 1) {
ret = -1;
break;
}
printf("feed the dog\n");
sleep(10);
}
fail:
close(fd);
return ret;
}
代码说明:
1、内部看门狗在使用 open 函数打开后会立刻开始计时。
2、通过用 ioctl 来设置超时时间和获取超时时间。当没有设置超时时间时,驱动会应用默认请求的超时时间为 30 s。需要说明的是驱动最终设置的超时时间并不一定是应用层传输的时间或者驱动一开始设置的默认时间。驱动函数里有一个超时时间的列表,该列表中存放了 16 个超时时间(前 9 个是 0)。驱动会在超时时间列表中找到一个合适的时间作为最终 watchdog 设置的超时时间。
以下为超时时间的详细列表: