一、什么是看门狗定时器
看门狗是一个定时器(倒计时),有计时的功能。当看门狗的计数值减为0,会产生一个复位信号给处理器,这样处理器就会重启,软件就会重新运行。
二、看门狗定时器的作用
当系统在运行的过程中,由于受到干扰(雷击、电机运行、手机、汽车…)或者系统的错误,而产生了死机(程序跑飞)不能正常工作,看门狗可以让系统自动重启,重新开始运行代码,让系统恢复到正常工作的状态。
看门狗不是用来解决产品设计的bug。
三、S5PV210的看门狗
1、计时值是16bits的。
2、看门狗定时器有两个功能:
1)作为看门狗,可以产生复位信号(讲解)
2)作为一个普通的定时器,可以产生周期性的中断
四、S5PV210看门狗的原理(复位功能)
见看门狗框图
五、看门狗的寄存器
只需要理解WTCON和WTCNT两个寄存器。
六、解决三个问题
1)C语言的位操作运算
在处理器中,一般一个寄存器都是32bits的,每个位都有特殊的含义,通常配置寄存器的时候,不是配置寄存器的整个值,而是配置寄存器的某个位。
C位操作运算符:&、|、~、^、<<、>>
求:
1<<2 + 3 = 1<<(2+3)=32
例1:求一个字节数据中,有多少个位为1?
int byte_cnt(unsigned char data)
{
int count = 0;
for(int i=0;i<8;i++)
{
if(data & (1<<i))
count++;
}
return count;
}
例2:unsigned int data,将data的第10位置1,其他位保持不变。
data = data | (1<<10);
data |= (1<<10);
例3:unsigned int data,将data的第10位清0,其他位保持不变。
data = data & ~(1<<10);
data &= ~(1<<10);
例4:unsigned int data,将data的第10位取反,其他位保持不变。
data ^= (1<<10);
2)如何通过地址访问地址下的数据
在处理器中,每个寄存器都有唯一的地址,我们配置寄存器的时候,通常都是通过寄存器的地址来配置的。所以需要理解通过寄存器的地址如何访问地址下的内容。
例1:将0xaa66写入地址0x67a9
0x67a9 = 0xaa66;不对
unsigned short *p=NULL;//定义一个指向unsigned short数据的指针。思考:sizeof§\sizeof(*p)
p = (unsigned short *)0x67a9;
*p = 0xaa66;
或者:
*(unsigned short *)0x67a9 = 0xaa66
题目是有问题的。
例2:将0x12345678写入0x40000000地址。
答:
*(unsigned int *)0x40000000 = 0x12345678;
例3:读出0x40000000地址下int的数据给a;
答:
unsigned int a;
a = *(unsigned int *)0x40000000;
3)在驱动中,如何访问处理器的寄存器?
物理地址:硬件实际的地址,在原理图或者处理器的使用手册中,得到的都是物理地址,在裸机环境下,可以直接访问物理地址。
虚拟地址:操作系统使用的地址是虚拟地址,虚拟地址是对应到物理地址的。操作做系统不能直接使用物理地址,要使用物理地址对应的虚拟地址。
驱动程序是工作在Linux内核中的,是的地址是虚拟地址。
结论:我们需要知道WTCON和WTCNT这两个寄存器物理地址对应的虚拟地址,然后通过虚拟地址再访问寄存器。
七、如何得到物理地址对应的虚拟地址
//驱动的头文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#define WDT_START 1 //看门狗打开
#define WDT_STOP 2 //看门狗关闭
#define WDT_ALIVE 3 //看门狗喂狗
static void __iomem * WTBASE_VA;
static void __iomem * WTCON_VA; //WTCON的虚拟地址
static void __iomem * WTCNT_VA; //WTCNT的虚拟地址
//定义一个文件操作集--给应用程序提供接口
//给应用程序open()的接口
static int mini210_wdt_open(struct inode *inode, struct file *filp)
{
printk("mini210 wdt driver openning\n");
return 0;
}
//给应用程序read()的接口
static ssize_t mini210_wdt_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
char wdt_flag;
int ret,i;
if(len != 1)
return -EINVAL;//Invalid argument
ret = copy_from_user(&wdt_flag, buf, len);
if(ret != 0){
return -EFAULT;
}
switch(wdt_flag){
case WDT_START:
*(unsigned int *)WTCON_VA |= (1<<5); //enable wdt
break;
case WDT_STOP:
*(unsigned int *)WTCON_VA &= ~(1<<5); //disable wdt
break;
case WDT_ALIVE:
*(unsigned int *)WTCNT_VA = 26050;//计数值重新赋值
break;
default:
return -EINVAL;
}
return len;
}
//给应用程序close()的接口
static int mini210_wdt_release(struct inode *inode, struct file *filp)
{
printk("mini210 wdt driver closing\n");
return 0;
}
static struct file_operations mini210_wdt_fops = {
.owner = THIS_MODULE,
.open = mini210_wdt_open,
.write = mini210_wdt_write,
.release = mini210_wdt_release,
};
//定义一个混杂设备驱动模型
static struct miscdevice mini210_misc = {
.minor = MISC_DYNAMIC_MINOR,//内核自动分配次设备号
.name = "wdt_drv",//混杂设备的名字,也是设备文件的名字
.fops = &mini210_wdt_fops, //文件操作集
};
//看门狗驱动的安装函数
static int __init mini210_wdt_init(void)
{
//看门狗驱动的安装过程
int ret,i;
//得到虚拟地址对应的物理地址
WTBASE_VA = ioremap(0xE2700000, 16);
if(WTBASE_VA == NULL)
{
printk("ioremap failed\n");
return -EFAULT;
}
WTCON_VA = WTBASE_VA + 0x00;
WTCNT_VA = WTBASE_VA + 0x08;
printk("VA: %p,%p\n", WTCON_VA,WTCNT_VA);
//将混杂设备驱动模型注册到内核
ret = misc_register(&mini210_misc);
if(ret < 0){
printk("wdt misc register error\n");
goto misc_reg_err;
}
printk("mini210 wdt driver init....\n");
//初始化看门狗(不能让看门狗工作)
//prescaler value=199,disable wdt,division factor=128,enable reset
*(unsigned int *)WTCON_VA = (199<<8)+(3<<3)+(1<<0);//看门狗的计数频率:2605Hz
//设置看看门狗的计数值,确定看门狗的复位时间是10秒
*(unsigned int *)WTCNT_VA = 26050;// 26050/2605=10秒
return 0;
misc_reg_err:
iounmap(WTBASE_VA);
return ret;
}
//蜂鸣器驱动的卸载函数
static void __exit mini210_wdt_exit(void)
{
int i;
//蜂鸣器驱动的卸载过程
iounmap(WTBASE_VA);
misc_deregister(&mini210_misc);
printk("mini210 wdt driver exit....\n");
}
//模块的入口和出口
module_init(mini210_wdt_init);//#insmod wdt_drv.ko-->驱动的入口
module_exit(mini210_wdt_exit);//#rmmod wdt_drv-->驱动的出口
//模块的描述(驱动的描述)
MODULE_AUTHOR("bobeyfeng@163.com");
MODULE_DESCRIPTION("S5PV210 wdt Device Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("V1.0");
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define WDT_START 1 //看门狗打开
#define WDT_STOP 2 //看门狗关闭
#define WDT_ALIVE 3 //看门狗喂狗
/**开启看门狗,10秒内喂狗,测试看门狗不复位功能,过一段时间,关闭看门狗,系统不会复位**/
int main(void)
{
int fd_wdt,count=0;
unsigned char wdt_cmd;
fd_wdt = open("/dev/wdt_drv", O_RDWR);
if(fd_wdt == -1)
{
perror("open wdt error");
return -1;
}
wdt_cmd = WDT_START;
write(fd_wdt, &wdt_cmd , 1);//打开看门狗
pirntf("wdt is starting, please keep alive in 10 second\n");
while(1)
{
sleep(8);
wdt_cmd = WDT_ALIVE;
write(fd_wdt, &wdt_cmd , 1);//看门狗喂狗
printf("wdt keepalive \n");
count++;
if(count == 5)
break;
}
wdt_cmd = WDT_STOP;
write(fd_wdt, &wdt_cmd , 1);//关闭看门狗
while(1)
{
pirntf("wdt was stopped, no reset\n");
sleep(1);
}
close(fd_wdt);
return 0;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define WDT_START 1 //看门狗打开
#define WDT_STOP 2 //看门狗关闭
#define WDT_ALIVE 3 //看门狗喂狗
/**开启看门狗,10秒内没有喂狗,测试自动复位功能**/
int main(void)
{
int fd_wdt;
unsigned char wdt_cmd;
fd_wdt = open("/dev/wdt_drv", O_RDWR);
if(fd_wdt == -1)
{
perror("open wdt error");
return -1;
}
wdt_cmd = WDT_START;
write(fd_wdt, &wdt_cmd , 1);//打开看门狗
pirntf("wdt is starting, please keep alive in 10 second\n");
while(1)
{
for(i=9;i>0;i--)
{
printf("%d second\n",i);
sleep(1);
}
printf("wdt resetting \n");
}
close(fd_wdt);
return 0;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define WDT_START 1 //看门狗打开
#define WDT_STOP 2 //看门狗关闭
#define WDT_ALIVE 3 //看门狗喂狗
/**开启看门狗,10秒内喂狗,测试看门狗不复位功能**/
int main(void)
{
int fd_wdt;
unsigned char wdt_cmd;
fd_wdt = open("/dev/wdt_drv", O_RDWR);
if(fd_wdt == -1)
{
perror("open wdt error");
return -1;
}
wdt_cmd = WDT_START;
write(fd_wdt, &wdt_cmd , 1);//打开看门狗
pirntf("wdt is starting, please keep alive in 10 second\n");
while(1)
{
sleep(8);
wdt_cmd = WDT_ALIVE;
write(fd_wdt, &wdt_cmd , 1);//看门狗喂狗
printf("wdt keepalive \n");
}
close(fd_wdt);
return 0;
}