搜索
bottom↓
回复: 0

《I.MX6U嵌入式Linux C应用编程指南》第二十八章 看门狗应用编程

[复制链接]

出0入234汤圆

发表于 2021-8-31 18:09:04 | 显示全部楼层 |阅读模式
1)实验平台:正点原子i.MX6ULL Linux阿尔法开发板
2)  章节摘自【正点原子】I.MX6U嵌入式Linux C应用编程指南
3)购买链接:https://item.taobao.com/item.htm?&id=603672744434
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyz-i.mx6ull.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子Linux技术交流群:1027879335
1.png

2.jpg


3.png


第二十八章 看门狗应用编程

       在产品化的嵌入式系统中,为了使系统在异常情况下能自动复位,一般都需要引入看门狗。看门狗其实就是一个可以在一定时间内被复位的计数器。当看门狗启动后,计数器开始自动计数,经过一定时间,如果没有被复位,计数器溢出就会对CPU产生一个复位信号使系统重启(俗称“被狗咬”)。系统正常运行时,需要在看门狗允许的时间间隔内对看门狗计数器清零(俗称“喂狗”),不让复位信号产生。如果系统不出问题,程序保证按时“喂狗”,一旦程序跑飞,没有“喂狗”,系统“被咬”复位。
       本章我们来学习应用层如何控制看门狗外设,譬如打开看门狗、关闭看门狗以及喂狗等操作。
       本章将会讨论如下主题内容。
       看门狗应用编程介绍;
       编写程序控制看门狗外设。

1.1看门狗应用编程介绍
       前面已经说到,看门狗其实就是一个可以在一定时间内被复位/重置的计数器,一般叫做看门狗计时器(或看门狗定时器);如果在规定时间内没有复位看门狗计时器,计数器溢出则会对CPU产生一个复位信号使系统重启,当然有些看门狗也可以只产生中断信号而不会使系统复位。
       I.MX6UL/I.MX6ULL SoC集成了两个看门狗定时器(WDOG):WDOG1和WDOG2;WDOG2用于安全目的,而WDOG1则是一个普通的看门狗,支持产生中断信号以及复位CPU。
       Linux系统中所注册的看门狗外设,都会在/dev/目录下生成对应的设备节点(设备文件),设备节点名称通常为watchdogX(X表示一个数字编号0、1、2、3等),譬如/dev/watchdog0、/dev/watchdog1等,通过这些设备节点可以控制看门狗外设。
看门狗应用编程689.png

图 28.1.1 watchdog0和watchdog设备节点

       这个watchdog0其实就是I.MX6U的WDOG1所对应的设备节点,从图中可知,除了/dev/watchdog0之外,还有一个watchdog设备节点,这个设备节点的名称没有后面的数字编号,这个又是什么意思呢?因为系统中可能注册了多个看门狗设备,/dev/watchdog设备节点则代表系统默认的看门狗设备;通常这指的就是watchdog0,所以,上图中,/dev/watchdog其实就等于/dev/watchdog0,也就意味着它俩代表的是同一个硬件外设。
       应用层控制看门狗其实非常简单,通过ioctl()函数即可做到!接下来笔者向大家进行介绍。
       首先在我们的应用程序中,需要包含头文件<linux/watchdog.h>头文件,该头文件中定义了一些ioctl指令宏,每一个不同的指令宏表示向设备请求不同的操作,如下所示:
  1. #define        WDIOC_GETSUPPORT                _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
  2. #define        WDIOC_GETSTATUS                        _IOR(WATCHDOG_IOCTL_BASE, 1, int)
  3. #define        WDIOC_GETBOOTSTATUS        _IOR(WATCHDOG_IOCTL_BASE, 2, int)
  4. #define        WDIOC_GETTEMP                        _IOR(WATCHDOG_IOCTL_BASE, 3, int)
  5. #define        WDIOC_SETOPTIONS                        _IOR(WATCHDOG_IOCTL_BASE, 4, int)
  6. #define        WDIOC_KEEPALIVE                        _IOR(WATCHDOG_IOCTL_BASE, 5, int)
  7. #define        WDIOC_SETTIMEOUT              _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
  8. #define        WDIOC_GETTIMEOUT              _IOR(WATCHDOG_IOCTL_BASE, 7, int)
  9. #define        WDIOC_SETPRETIMEOUT                _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
  10. #define        WDIOC_GETPRETIMEOUT        _IOR(WATCHDOG_IOCTL_BASE, 9, int)
  11. #define        WDIOC_GETTIMELEFT                _IOR(WATCHDOG_IOCTL_BASE, 10, int)
复制代码

       比较常用指令包括:WDIOC_GETSUPPORT、WDIOC_SETOPTIONS、WDIOC_KEEPALIVE、WDIOC_SETTIMEOUT、WDIOC_GETTIMEOUT,说明如下:
1.png

表 28.1.1 看门狗常用指令

1.1.1打开设备
        首先在调用ioctl()函数之前,需要先打开看门狗设备得到文件描述符,如下所示:
  1. int fd;

  2. fd = open("/dev/watchdog", "O_RDWR");
  3. if (0 > fd)
  4.         fprintf(stderr, "open error: %s: %s\n", "/dev/watchdog", strerror(errno));
复制代码

1.1.2获取设备支持哪些功能:WDIOC_GETSUPPORT
       使用WDIOC_GETSUPPORT指令获取看门狗设备支持哪些功能,使用方式如下:
  1. ioctl(int fd, WDIOC_GETSUPPORT, struct watchdog_info *info);
复制代码

       使用WDIOC_GETSUPPORT指令可以获取设备的信息,调用ioctl()需要传入一个struct watchdog_info *指针,ioctl()会将获取到的数据写入到info指针所指向的对象中。struct watchdog_info结构体描述了看门狗设备的信息,我们来看看struct watchdog_info结构体的定义:
       示例代码 28.1.1 struct watchdog_info结构体
  1. struct watchdog_info {
  2.     __u32 options;                      /* Options the card/driver supports */
  3.     __u32 firmware_version;         /* Firmware version of the card */
  4.     __u8  identity[32];                 /* Identity of the board */
  5. };
复制代码

       options字段记录了设备支持哪些功能或选项;
       firmware_version字段记录了设备的固件版本号;
       identity字段则是一个描述性的字符串。
       我们重点关注的是options字段,该字段描述了设备支持哪些功能、选项,该字段的值如下(可以是以下任意一个值或多个值的位或关系):
  1. #define        WDIOF_OVERHEAT                0x0001        /* Reset due to CPU overheat */
  2. #define        WDIOF_FANFAULT                0x0002        /* Fan failed */
  3. #define        WDIOF_EXTERN1                0x0004        /* External relay 1 */
  4. #define        WDIOF_EXTERN2                0x0008        /* External relay 2 */
  5. #define        WDIOF_POWERUNDER        0x0010        /* Power bad/power fault */
  6. #define        WDIOF_CARDRESET                0x0020        /* Card previously reset the CPU */
  7. #define        WDIOF_POWEROVER                0x0040        /* Power over voltage */
  8. #define        WDIOF_SETTIMEOUT        0x0080  /* Set timeout (in seconds) */
  9. #define        WDIOF_MAGICCLOSE        0x0100        /* Supports magic close char */
  10. #define        WDIOF_PRETIMEOUT        0x0200  /* Pretimeout (in seconds), get/set */
  11. #define        WDIOF_ALARMONLY                0x0400        /* Watchdog triggers a management or other external alarm not a reboot */
  12. #define        WDIOF_KEEPALIVEPING        0x8000        /* Keep alive ping reply */
复制代码

       一般常见的值包括:WDIOF_SETTIMEOUT、WDIOF_KEEPALIVEPING;WDIOF_SETTIMEOUT表示设备支持设置超时时间;WDIOF_KEEPALIVEPING表示设备支持“喂狗”操作,也就是重置看门狗计时器。
       使用示例如下:
  1. struct watchdog_info info;

  2. if (0 > ioctl(fd, WDIOC_GETSUPPORT, &info)) {
  3.         fprintf(stderr, "ioctl error: WDIOC_GETSUPPORT: %s\n", strerror(errno));
  4.         return -1;
  5. }

  6. printf("identity: %s\n", info.identity);
  7. printf("version: %u\n", firmware_version);

  8. if (0 == (WDIOF_KEEPALIVEPING & info.options))
  9.         printf("设备不支持喂狗操作\n");
  10. if (0 == (WDIOF_SETTIMEOUT & info.options))
  11.         printf("设备不支持设置超时时间\n");
复制代码

1.1.3获取/设置超时时间:WDIOC_GETTIMEOUT、WDIOC_SETTIMEOUT
       使用WDIOC_GETTIMEOUT指令可获取设备当前设置的超时时间,使用方式如下:
  1. ioctl(int fd, WDIOC_GETTIMEOUT, int *timeout);
复制代码

        使用WDIOC_SETTIMEOUT指令可设置看门狗的超时时间,使用方式如下:
  1. ioctl(int fd, WDIOC_SETTIMEOUT, int *timeout);
复制代码

         超时时间是以秒为单位,设置超时时间时,不可超过其最大值、否则ioctl()调用将会失败,使用示例如下所示:
  1. int timeout;

  2. /* 获取超时时间 */
  3. if (0 > ioctl(fd, WDIOC_GETTIMEOUT, &timeout)) {
  4.         fprintf(stderr, "ioctl error: WDIOC_GETTIMEOUT: %s\n", strerror(errno));
  5.         return -1;
  6. }

  7. printf("current timeout: %ds\n", timeout);

  8. /* 设置超时时间 */
  9. timeout = 10;        //10秒钟
  10. if (0 > ioctl(fd, WDIOC_SETTIMEOUT, &timeout)) {
  11.         fprintf(stderr, "ioctl error: WDIOC_SETTIMEOUT: %s\n", strerror(errno));
  12.         return -1;
  13. }
复制代码

1.1.4开启/关闭看门狗:WDIOC_SETOPTIONS
         设置好超时时间之后,接着便可以开启看门狗计时了,使用WDIOC_SETOPTIONS指令可以开启看门狗计时或停止看门狗计时,使用方式如下:
  1. ioctl(int fd, WDIOC_SETOPTIONS, int *option);
复制代码

         option指针指向一个int类型变量,该变量可取值如下:
  1. #define        WDIOS_DISABLECARD        0x0001        /* Turn off the watchdog timer */
  2. #define        WDIOS_ENABLECARD        0x0002        /* Turn on the watchdog timer */
复制代码

         WDIOS_DISABLECARD表示停止看门狗计时,WDIOS_ENABLECARD则表示开启看门狗计时。
         使用示例如下所示:
  1. int option = WDIOS_ENABLECARD;        //开启
  2. //int option = WDIOS_DISABLECARD; //停止

  3. if (0 > ioctl(fd, WDIOC_SETOPTIONS, &option)) {
  4.         fprintf(stderr, "ioctl error: WDIOC_SETOPTIONS: %s\n", strerror(errno));
  5.         return -1;
  6. }
复制代码

       需要注意的是,当调用open()打开看门狗设备的时候,即使程序中没有开启看门狗计时器,当close()关闭设备时,看门狗会自动启动;所以,当打开设备之后,需要使用WDIOC_SETOPTIONS指令停止看门狗计时,等所有设置完成之后再开启看门狗计时器。
1.1.5喂狗:WDIOC_KEEPALIVE
       看门狗计时器启动之后,我们需要在超时之前,去“喂狗”,否则计时器溢出超时将会导致系统复位或产生一个中断信号,通过WDIOC_KEEPALIVE指令喂狗,使用方式如下:
  1. ioctl(int fd, WDIOC_KEEPALIVE, NULL);
复制代码

       使用示例如下:
  1. if (0 > ioctl(fd, WDIOC_KEEPALIVE, NULL)) {
  2.         fprintf(stderr, "ioctl error: WDIOC_KEEPALIVE: %s\n", strerror(errno));
  3. }
复制代码

1.2看门狗应用编程实战
       通过上小节介绍之后,我们已经知道了如何编写应用程序去控制看门狗外设了,本小节我们来编写一个简单地看门狗应用程序,示例代码如下所示:
       本例程源码对应的路径为:开发板光盘->11、Linux C应用编程例程源码->28_watchdog->watchdog_test.c。
       示例代码 28.2.1 看门狗测试程序
  1. /***************************************************************
  2. Copyright © ALIENTEK Co., Ltd. 1998-2021. All rights reserved.
  3. 文件名 : watchdog_test.c
  4. 作者 : 邓涛
  5. 版本 : V1.0
  6. 描述 : 看门狗应用程序示例代码
  7. 其他 : 无
  8. 论坛 : <a href="www.openedv.com" target="_blank">www.openedv.com</a>
  9. 日志 : 初版 V1.0 2021/7/14 邓涛创建
  10. ***************************************************************/

  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. #include <fcntl.h>
  16. #include <unistd.h>
  17. #include <sys/ioctl.h>
  18. #include <errno.h>
  19. #include <string.h>
  20. #include <linux/watchdog.h>

  21. #define  WDOG_DEV   "/dev/watchdog"

  22. int main(int argc, char *argv[])
  23. {
  24.     struct watchdog_info info;
  25.     int timeout;
  26.     int time;
  27.     int fd;
  28.     int op;

  29.     if (2 != argc) {
  30.         fprintf(stderr, "usage: %s <timeout>\n", argv[0]);
  31.         exit(EXIT_FAILURE);
  32.     }

  33.     /* 打开看门狗 */
  34.     fd = open(WDOG_DEV, O_RDWR);
  35.     if (0 > fd) {
  36.         fprintf(stderr, "open error: %s: %s\n", WDOG_DEV, strerror(errno));
  37.         exit(EXIT_FAILURE);
  38.     }

  39.     /* 打开之后看门狗计时器会开启、先停止它 */
  40.     op = WDIOS_DISABLECARD;
  41.     if (0 > ioctl(fd, WDIOC_SETOPTIONS, &op)) {
  42.         fprintf(stderr, "ioctl error: WDIOC_SETOPTIONS: %s\n", strerror(errno));
  43.         close(fd);
  44.         exit(EXIT_FAILURE);
  45.     }

  46.     timeout = atoi(argv[1]);
  47.     if (1 > timeout)
  48.         timeout = 1;

  49.     /* 设置超时时间 */
  50.     printf("timeout: %ds\n", timeout);
  51.     if (0 > ioctl(fd, WDIOC_SETTIMEOUT, &timeout)) {
  52.         fprintf(stderr, "ioctl error: WDIOC_SETTIMEOUT: %s\n", strerror(errno));
  53.         close(fd);
  54.         exit(EXIT_FAILURE);
  55.     }

  56.     /* 开启看门狗计时器 */
  57.     op = WDIOS_ENABLECARD;
  58.     if (0 > ioctl(fd, WDIOC_SETOPTIONS, &op)) {
  59.         fprintf(stderr, "ioctl error: WDIOC_SETOPTIONS: %s\n", strerror(errno));
  60.         close(fd);
  61.         exit(EXIT_FAILURE);
  62.     }

  63.     /* 喂狗 */
  64.     time = (timeout * 1000 - 100) * 1000;//喂狗时间设置us微秒、在超时时间到来前100ms喂狗
  65.     for ( ; ; ) {

  66.         usleep(time);
  67.         ioctl(fd, WDIOC_KEEPALIVE, NULL);
  68.     }
  69. }
复制代码

       示例代码很简单,首先打开看门狗设备,接着使用WDIOC_SETOPTIONS指令(op = WDIOS_DISABLECARD)先停止看门狗计时器;接着通过atoi获取到用户传入的超时时间,所以执行该测试程序的时候,需要传入一个参数作为看门狗超时时间。
       接着使用WDIOC_SETTIMEOUT指令设置超时时间,再使用WDIOC_SETOPTIONS指令(op = WDIOS_ENABLECARD)开启看门狗计时器,看门狗开始工作。接着我们需要在超时时间到来之前,去喂狗,喂狗之后,计时器重置,重新计时;不断地喂狗重置计时器、不让其超时、如果一旦超时系统将会复位重启。
编译示例代码:
看门狗应用编程8420.png

图 28.2.1 编译示例代码

       将编译得到的可执行文件拷贝到开发板Linux系统/home/root目录下,执行测试程序,譬如启动看门狗,设置超时时间为2秒钟,如下:
看门狗应用编程8549.png

图 28.2.2 开启看门狗2秒超时时间

       执行程序之后,开门狗计时器就已经启动了,程序中会不断的喂狗重置计时器,以保证程序不会重启。
       现在我们按Ctrl + C结束程序,结束程序意味着已经停止喂狗了、然后看门狗计时器并没有停止,这样将会导致计时器溢出、发生复位重启:
看门狗应用编程8728.png

图 28.2.3 计时器溢出系统重启

       当按Ctrl+C终止进程后,内核打印出“watchdog watchdog0: watchdog did not stop!”信息,表示看门狗计时器还正在计时、未停止。


阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-27 02:19

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表