Watchdog做过项目的同学应该不会陌生,主要是启动程序守护作用,程序跑飞后能够让MCU重新复位,让设备进入正常工作模式,通常所说的看门狗,普通的看门狗喂狗的周期是1.6S,只要在1.6S的时间内,让看门狗的喂狗电平进行反转一次,看门狗内部的计数器就会清0,如果超过1.6S周期没有反转喂狗信号,则会发出一个下降沿的电平给MCU的RST引脚,MCU从而复位。
普通单片机的操作是这样的,如果换成了ARM系统,1.6S周期对于Linux来说,启动时间是不够的,先从bootstrap->uboot->kernel->文件系统->应用程序;这个过程下来大概要10S时间,已经算是非常快了。
我接触的Linux系统方案有以下两种:
1、看门狗的Rst引脚和ARM的Rst脚之间加一个使能芯片,就像是一个开关一样,默认是关闭的,复位信号过不来,系统起来后,应用程序把开关打开,让复位信号导通;
缺点就是无法保证复位信号是不是导通的,也不能很清晰的知道硬件的开关是否正常。
2、ARM的外侧加一个单片机用来喂狗,ARM与单片机使用串口通讯,在系统启动之前喂狗权在单片机上面,系统启动后双机能够正常通讯喂狗权交给ARM。
缺点成本较高,通讯复杂。
3、基于以上两种呢,都不是很优的方案,后面找了一款超长时间的比较器,也能输出RST信号,用作看门狗比较好,并且还能输出提醒喂狗信号,ARM端可以做是否焊接看门狗芯片的逻辑;
下面主要介绍一下Linux下的字符驱动编写:
上期有小伙伴提了疑问,为什么IO的配置不放在设备树里面呢,你把IO配置还写在字符驱动的头文件当中是不是很Low,感觉不是很方便啊,工作和项目中难道是这么用的吗?
设备树里面放对应字符驱动IO配置当然没有问题,我现在目前的字符驱动都是模块驱动,就是不用更改内核的情况下可以随意更改驱动,就像是个应用程序一样,编译好的文件放入文件系统中,然后使用命令加载就可以了,如果IO的配置放在设备树里面,管脚有改动的话还要重新烧录设备树问题,根本做不到远程升级字符驱动,不利于应付工作中的突发需求。
关注微信公众号,回复“看门狗字符驱动”,下载源代码。
字符驱动的模型还是套用gpio系统;
1、模块的入口函数
module_init(at91_watdog_init);
module_exit(at91_watdog_cleanup);
MODULE_DESCRIPTION ("AT91 watdog detect DRIVER");
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR ("Jack");
MODULE_LICENSE ("GPL");
调用insmod和rmmod的时候会调用这两个函数;
2、创建init和cleanup函数
定义初始化信息,喂狗信号和捕捉提醒喂狗信号
struct watdog_pin
{
ulong wdgfree_pin; //
ulong wdgdete_pin; //
};
struct St_watdog
{
dev_t dev_major;
int sys_irq;
int hard_irq;
atomic_t open_count;
int detect_flag;// 断电检查标志
struct watdog_pin st_pin;
struct mutex watdog_mudex;//互斥锁
spinlock_t watdog_spinlock;
wait_queue_head_t watdog_poll_head;
struct cdev watdog_dev;//
struct class *watdog_class;
struct device *watdog_device;
};
3、注册及注销驱动流程
static int __init at91_watdog_init(void)
{
int retval = -1;
dev_t dev_id;
// 分配一个结构
pwdg = (struct St_watdog *)kzalloc(sizeof (struct St_watdog),GFP_KERNEL);
if(!pwdg)
{
printk("%s:kzalloc failded from:%s\n", __func__,WATCHDOG_NAME);
return -ENOMEM;
}
//需要减去偏移量
pwdg->st_pin.wdgfree_pin = WATCHDOG_PIN;
pwdg->st_pin.wdgdete_pin = WATDETECT_PIN;
mutex_init(&(pwdg->watdog_mudex));
spin_lock_init(&(pwdg->watdog_spinlock));
//init_timer
init_waitqueue_head(&pwdg->watdog_poll_head);
pwdg->detect_flag =0;
//分配主设备号
if (watdog_major) {
dev_id = MKDEV(watdog_major, 0);
retval = register_chrdev_region(dev_id, MAX_WATDOG_NB,
WATCHDOG_NAME);
} else {
retval = alloc_chrdev_region(&dev_id, 0, MAX_WATDOG_NB,
WATCHDOG_NAME);
watdog_major = MAJOR(dev_id);
}
if(retval<0)
{
printk("watdog: register error!\n");
goto error_malloc;
}
else
printk("watdog:watdog_major:%d\n",watdog_major);
cdev_init(&(pwdg->watdog_dev), &at91_watdog_fops);
retval = cdev_add(&(pwdg->watdog_dev),dev_id,MAX_WATDOG_NB);
if(retval<0)
{
printk("watdog:cdev_add failed!!!\n");
goto error_reg;
}
//创建类
pwdg->watdog_class = class_create(THIS_MODULE,WATCHDOG_NAME);
if(!pwdg->watdog_class)
{
printk("watdog:class_create error!\n");
goto error_cdev;
}
pwdg->watdog_device= device_create(pwdg->watdog_class, NULL,
MKDEV(watdog_major, 0),
NULL, WATCHDOG_NAME);
if(!pwdg->watdog_device)
{
printk("watdog:device_create error!\n");
goto error_class;
}
return 0;
error_class:
class_destroy(pwdg->watdog_class);
error_cdev:
cdev_del(&(pwdg->watdog_dev));
error_reg:
unregister_chrdev_region(dev_id,MAX_WATDOG_NB);
error_malloc:
kfree(pwdg);
return retval;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void __exit at91_watdog_cleanup(void)
{
dev_t dev_id;
dev_id = MKDEV(watdog_major, 0);
if(pwdg->watdog_device)
{
device_destroy(pwdg->watdog_class,MKDEV(watdog_major, 0));
}
if(pwdg->watdog_class)
{
class_destroy(pwdg->watdog_class);
}
// del cdev
cdev_del(&(pwdg->watdog_dev));
if(watdog_major)
{
unregister_chrdev_region(dev_id,MAX_WATDOG_NB);
}
//mutex destory
mutex_destroy(&pwdg->watdog_mudex);
//释放内存
kfree(pwdg);
printk("watdog:mod is cleanup.\n");
}
4、驱动的操作方法,这里面用到了epoll,用于捕捉信号,比如按键、YX信号等,应用层可以使用select这种非阻塞的方式读取信号,减少CPU的开销,让系统更加的丝滑。
static struct file_operations at91_watdog_fops={
.owner = THIS_MODULE,
.llseek = NULL,
.read = NULL,
.write = NULL,
.unlocked_ioctl = at91_watdog_ioctl,
.open = at91_watdog_open,
.poll = at91_watdog_poll,
.release = at91_watdog_close,
};
static long at91_watdog_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{
int ret = 0;
struct St_watdog *pwdg;
pwdg = (struct St_watdog *)filp->private_data;
//检查命令的有效性
if ((_IOC_TYPE(cmd) != WDG_MAGIC_NB) || (_IOC_NR(cmd) > WDG_IOC_MAXNR))
{
printk("%s:CMD error\n",__func__);
return -EINVAL;
}
//判断空间是否可访问
if(_IOC_DIR(cmd) & _IOC_READ)
ret = !access_ok(VERIFY_WRITE,(void *)args,_IOC_SIZE(cmd));
else if(_IOC_DIR(cmd) & _IOC_WRITE)
ret = !access_ok(VERIFY_READ,(void *)args,_IOC_SIZE(cmd));
if(ret)
return -EFAULT;
//互斥锁
if(mutex_lock_interruptible(&(pwdg->watdog_mudex)))
{
return -ERESTARTSYS;
}
switch(cmd)
{
case WATCHDOG_OUTPUT_LOW:
gpio_set_value(pwdg->st_pin.wdgfree_pin,0);
break;
case WATCHDOG_OUTPUT_HIGH:
gpio_set_value(pwdg->st_pin.wdgfree_pin,1);
break;
default:
printk("watdog:error cmd %d.\n", cmd);
mutex_unlock(&(pwdg->watdog_mudex));
return -EINVAL;
}
mutex_unlock(&(pwdg->watdog_mudex));
return ret;
}
static irqreturn_t at91_watdog_isr(int irq, void *dev_id)
{
struct St_watdog *pwdg = (struct St_watdog *)dev_id;
spin_lock(&pwdg->watdog_spinlock);
if(irq == pwdg->sys_irq)
{
pwdg->detect_flag = 1;//可读
wake_up_interruptible(&pwdg->watdog_poll_head);
}
spin_unlock(&pwdg->watdog_spinlock);
return IRQ_HANDLED;
}
static int at91_watdog_open(struct inode *inode, struct file *filp)
{
struct St_watdog *pwdg;
int ret=0;
pwdg = container_of(inode->i_cdev, struct St_watdog,watdog_dev);
filp->private_data = (void *)pwdg;
if(atomic_read(&(pwdg->open_count))>0)
{
printk("at91_watdog_open:busy!!!\n");
return -EAGAIN;
}
else
{
atomic_add(1,&(pwdg->open_count));
}
#if 0
at91_set_GPIO_periph(pwdg->st_pin.wdgfree_pin, 1); /* wdg_free */
at91_set_GPIO_periph(pwdg->st_pin.wdgdete_pin, 1);
at91_set_deglitch(pwdg->st_pin.wdgdete_pin, 1);
#endif
ret = gpio_request(pwdg->st_pin.wdgfree_pin, "wdgfree_pin");
if (ret < 0) {
printk("watchdog wdgfree_pin %ld\n", pwdg->st_pin.wdgfree_pin);
}
gpio_direction_output(pwdg->st_pin.wdgfree_pin, 1);
gpio_set_value(pwdg->st_pin.wdgfree_pin, 1);
//获取gpio描述符号
/* watdog_gpio_des = gpio_to_desc(pwdg->st_pin.wdgdete_pin);
if(!watdog_gpio_des)
{
printk("watdog:get watdog_gpio_des failed!!\n");
return -ENODEV;
}
else
{
pwdg->sys_irq = gpiod_to_irq(watdog_gpio_des);
printk("watdog:gpio_irq:%d\n",pwdg->sys_irq);
}
*/
pwdg->sys_irq = gpio_to_irq(pwdg->st_pin.wdgdete_pin);
irq_set_status_flags(pwdg->sys_irq, IRQ_NOAUTOEN);
ret = request_irq(pwdg->sys_irq,at91_watdog_isr,IRQ_TYPE_EDGE_BOTH,WATCHDOG_NAME,pwdg);
if(ret)
{
printk("watdog:request_irq error:%d\n",pwdg->sys_irq);
return ret;
}
enable_irq(pwdg->sys_irq);
return nonseekable_open(inode,filp);
}
static int at91_watdog_close(struct inode *inode, struct file *filp)
{
struct St_watdog *pwdg = (struct St_watdog *)filp->private_data;
atomic_sub(1,&(pwdg->open_count));
disable_irq(pwdg->sys_irq);
free_irq(pwdg->sys_irq,pwdg);
gpio_free(pwdg->st_pin.wdgdete_pin);
gpio_free(pwdg->st_pin.wdgfree_pin);
return 0;
}
unsigned int at91_watdog_poll(struct file *filp,struct poll_table_struct *poll_table)
{
unsigned int mask = 0;
unsigned long flags=0;
struct St_watdog *pwdg;
pwdg = (struct St_watdog *)filp->private_data;
poll_wait(filp, &pwdg->watdog_poll_head, poll_table);
spin_lock_irqsave(&pwdg->watdog_spinlock,flags);
if (pwdg->detect_flag)
{
pwdg->detect_flag = 0;
mask |= (POLLIN | POLLRDNORM);
}
spin_unlock_irqrestore(&pwdg->watdog_spinlock,flags);
return mask;
}
应用程序的编写
int main()
{
int iCnt=0;
int value =0;
int inum = 0;
int idetect = 0;
fd_set client_fd_set;
struct timeval tv;
int fd = 0;
for(;;)
{
fd = open("/dev/"WATCHDOG_NAME, 0);
if (fd < 0)
{
printf("watdg:failed to open watdg.\n");
return 0;
}
else
{
printf("watdg:open watdg sucess!!!\n");
}
tv.tv_sec = 2;
tv.tv_usec = 0;//10ms
FD_ZERO(&client_fd_set);
FD_SET(fd, &client_fd_set);
int ret;
ret = select(fd + 1, &client_fd_set, NULL, NULL, &tv);
if(ret<0)
{
perror("select error\n");
exit(0);
}
else if(0==ret)
{
printf("watdg:select time out\n");
}
else
{
printf("watdg:catch watdg single\n");
idetect = 1;
}
inum ++;
// if ((inum > 10000000 ) && idetect)
if ( idetect)
{
inum = 0;
idetect = 0;
usleep(500000);
ioctl(fd,WATCHDOG_OUTPUT_LOW, 0);
usleep(500000);
ioctl(fd,WATCHDOG_OUTPUT_HIGH, 0);
}
close(fd);
}
close(fd);
}
更多linux知识点推荐:
[Linux字符驱动] LED点灯试验
[Linux 驱动]模块加载RTX8025驱动
[linux kernel] 内核下RX8025对接系统时钟
[linux kernel]内核启动阶段控制IO口时序输出
[职场吐槽]如何缓解焦虑
[linux kernel] 内核下ksz8081驱动调试
[linux kernel] 内核下ksz9031驱动调试
[linux kernel]内核图形化裁剪配置
[linux kernel]内核移植过程记录
[linux kernel] 内核启动流程梳理
[linux 底层]u-boot EMMC驱动
[linux 底层]u-boot图形化裁剪配置
[Linux 底层]U-boot ksz9031网络驱动调试
[Linux 底层]U-boot调试命令使用技巧
[Linux 底层]U-boot编译移植
[Linux 底层]U-boot烧录脚本介绍SecureCRT
[Linux 底层]bootstrap移植裁剪及编译
[Linux 底层] 平台软件分层介绍
[Linux 驱动] RS485测试程序编写
[Linux 驱动] CAN测试程序编写
推荐阅读:
芯片手册解读 | Linux底层 | 职场吐槽 | C语言视频
关注微信公众号,回复“看门狗字符驱动”,下载源代码。