当前位置:首页 » 《随便一记》 » 正文

Linux高级应用(十四)看门狗的驱动设计_人生当苦无妨

26 人参与  2021年12月21日 16:49  分类 : 《随便一记》  评论

点击全文阅读


一、什么是看门狗定时器
看门狗是一个定时器(倒计时),有计时的功能。当看门狗的计数值减为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;	
}



点击全文阅读


本文链接:http://zhangshiyu.com/post/31879.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1