这个字符设备系列,主要借助较容易上手的字符设备实例,去讲解linux的一些机制,以及驱动中比较常用到的中断、定时器、信号量等一些知识,由于本人自身的知识有限,对于arm的架构体系不太了解,这里,一般这里只讲,如何去用,对于一些原理性的东西不会深究,以后的文章会慢慢的加深。
想想我们当初玩51单片机的时候,那时候按键防抖是一个硬件、软件都需要处理的地方。软件一般就是加延时检测判断。当然,这里我们也可以用到定时器的这个机制,做按键驱动,这里主要还是以按键为例,但不是讲的按键防抖。
1. 定时器的一些概念:
节拍率(HZ):它是通过静态预处理定义的,在系统启动时,按照HZ值对硬件进行设置。体系结构不同,HZ值不同。
jiffies:全局变量jiffies用来记录自系统启动以来产生的节拍的总数。启动时内核将它设为0。系统运行时间以秒为单位,等于jiffies/HZ。
定时器:
它是管理内核流逝的时间的基础。内核常需要推后执行某些代码,也就是说,不在当前时间执行。定时器使用简单,一般做一些初始化工作,设置一个超时时间,指定超时发生后执行的函数,然后激活定时器就可以。指定的函数将在定时器到期时自动执行。执行结束后,定时器自行撤销。所以,定时器不断的创建和撤销,而且它的运行次数不受限制。
备注:部分neir来自于《linux内核设计与实现》
2. 驱动demo:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/irq.h> #include <linux/input.h> #include <linux/platform_device.h> #include <linux/miscdevice.h> #include <mach/gpio.h> #include <linux/io.h> #include <mach/hardware.h> #include <linux/delay.h> #include <asm/irq.h> #include <asm/uaccess.h> #include <linux/interrupt.h> #include <linux/gpio.h> #include <linux/wait.h> #include <linux/sched.h> #include <plat/gpio-core.h> #include <plat/gpio-cfg.h> #include <plat/gpio-cfg-helpers.h> static struct class *buttondrv_class; static struct device *buttondrv_class_dev; int major; volatile unsigned long *GPCCON; volatile unsigned long *GPCDAT; //static DECLARE_WAIT_QUEUE_HEAD(button_waitq); static struct timer_list button_timer; static unsigned char key_val; static volatile int ev_press = 0; struct pin_desc{ unsigned int pin; unsigned int key_val; }; struct pin_desc pins_desc[2] = { {S5PV210_GPH3(7), 0x01}, }; struct pins_desc *irq_temp; static irqreturn_t buttons_irq(int irq, void *dev_id) { printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>buttons_irq\n"); irq_temp = (struct pin_desc *)dev_id; mod_timer(&button_timer, jiffies+HZ/100); return IRQ_RETVAL(IRQ_HANDLED); } static int button_drv_open(struct inode *inode, struct file *file) { printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_open\n"); int ret=-1; s3c_gpio_setpull(S5PV210_GPH3(7), S3C_GPIO_PULL_NONE); ret = request_threaded_irq(gpio_to_irq(S5PV210_GPH3(7)), NULL, buttons_irq, IRQF_TRIGGER_RISING, "s2", &pins_desc[0]); printk("ret=%d irq=%d >>>>>>>>>>>>>>>>>>>>>>>>>\n ",ret,gpio_to_irq(S5PV210_GPH3(7))); return 0; } int button_drv_close(struct inode *inode, struct file *file) { free_irq(gpio_to_irq(S5PV210_GPH3(7)), &pins_desc[0]); return 0; } static int button_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_read\n"); if (count != 1) return -EINVAL; // wait_event_interruptible(button_waitq, ev_press); copy_to_user(buf, &key_val, 1); key_val=0; ev_press = 0; return 1; } static void button_timer_function(unsigned long data) { printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_timer_function\n"); struct pin_desc *pindesc = irq_temp; unsigned int pinval; pinval = gpio_get_value(pindesc->pin); printk("irq >>>>>>>>>>>>>>>>>>>>>>>>>>>>pinval =%d \n",pinval); if (pinval) { key_val = 0x80 | pindesc->key_val; } else { key_val = pindesc->key_val; } ev_press = 1; // wake_up_interruptible(&button_waitq); } static struct file_operations button_drv_fops = { .owner = THIS_MODULE, .open = button_drv_open, .read = button_drv_read, .release = button_drv_close, }; unsigned long test=0; static int button_drv_init(void){ printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_init\n"); button_timer.function = button_timer_function; button_timer.data=test; init_timer(&button_timer); add_timer(&button_timer); // setup_timer(&button_timer,button_timer_function,test); //before 4 method instead of the one GPCCON = (volatile unsigned long *)ioremap(0xE0200C60, 8); GPCDAT= GPCCON + 1; if (!GPCCON) { return -EIO; } major = register_chrdev(0, "button_drv", &button_drv_fops); buttondrv_class = class_create(THIS_MODULE, "buttondrv"); buttondrv_class_dev = device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); return 0; } static void button_drv_exit(void){ printk(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>button_drv_exit\n"); unregister_chrdev(major, "button_drv"); device_unregister(buttondrv_class_dev); class_destroy(buttondrv_class); iounmap(GPCCON); } module_init(button_drv_init); module_exit(button_drv_exit); MODULE_LICENSE("GPL");
2.1 代码简单解析:
定时器操作的步骤:
1.定义一个timer_list结构体:static struct timer_list button_timer;
2.定时器超时调用的函数:button_timer.function = button_timer_function;
给定时器操作函数传的参数:button_timer.data=test;
根据该定义该timer_list的参数初始化定时器:init_timer(&button_timer);
激活定时器:add_timer(&button_timer);
注:setup_timer(&button_timer,button_timer_function,test); 这个可代替上面的2步。
3. 在中断处理函数中,mod_timer(&button_timer, jiffies+HZ/100); //10ms
注:动态定时器不需要手动释放,mod_timer超时执行之后,就会自动释放。
版权声明:本文为博主原创文章,未经博主允许不得转载。