这个字符设备系列,主要借助较容易上手的字符设备实例,去讲解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超时执行之后,就会自动释放。