您的位置:首页 > 运维架构 > Linux

【Linux设备驱动】TQ2440按键驱动程序设计

2016-06-03 14:38 447 查看
这几天又回到了驱动程序学习的轨道上来。还是从简单的硬件开始一步步的来学习驱动。使用TQ2440开发板上的按键来设计按键驱动程序。

★关于按键的结构体

按键的结构体主要有:对应的中断号,中断触发方式,按键名称

struct button_irq_desc
{
int irq; //中断号
unsigned long flags;//中断触发方式
char *name;//中断名称
};

struct button_irq_desc button[4] =
{
{IRQ_EINT1,IRQF_TRIGGER_FALLING,"KEY1"},
{IRQ_EINT4,IRQF_TRIGGER_FALLING,"KEY2"},
{IRQ_EINT2,IRQF_TRIGGER_FALLING,"KEY3"},
{IRQ_EINT0,IRQF_TRIGGER_FALLING,"KEY4"},
};
★关于等待队列

在本驱动程序中,等待队列可以休眠或者唤醒进程。首先要定义和初始化一个等待队列,使用宏定义

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

另外还有其他定义初始化等待队列的方式:

定义等待队列button_waitq:wait_queue_head_t  button_waitq;

初始化等待队列button_waitq:init_waitqueue_head(&button_wait);

休眠进程:

wait_event(queue,condition);

condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_UNINTERRUPTIBLE模式的睡眠,并挂在queue参数所指定的等待队列上。

wait_event_interrupt(queue,condition);

condition(布尔表达式)为真时,立即返回 ;否则让进程进入TASK_INTERRUPTIBLE模式睡眠,并挂在queue参数所指定的等待队列上。

wait_event_killable(queue,condition);

condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_KILLABLE的睡眠,并挂在queue参数所指定的等待队列上。

★关于模块初始化与退出函数

◇模块初始化函数实现的步骤

1)注册设备号

2)注册字符设备驱动

3)创建类实现自动创建设备文件

button_class = class_create(THIS_MODULE, "Button_char_driver");
if(IS_ERR(button_class))
{
printk("failed ^^^^^^^^^");
return -1;
}
device_create(button_class,NULL, dev,NULL,"Button_char_driver");
◇模块退出函数实现步骤

退出模块就是把上面完成的任务全部注销掉

cdev_del(&button_cdev);
device_destroy(button_class, MKDEV(Button_major, 0));
class_destroy(button_class);
unregister_chrdev_region(MKDEV(Button_major, 0), 1);
★关于Button_open函数

此函数内主要实现了中断函数的注册以及释放

err=request_irq(button[i].irq, Button_interrupt, button[i].flags, button[i].name, (void *)&Button_press[i]);
第一个参数为中断号;第二个参数为中断函数;第三个参数为中断触发标志;第四个参数为中断名;第五个参数为不固定的传入的值(在这里表示存储按键被按下的次数)

对于第三个参数中断触发标志有这么几种触发标志:低电平触发、高电平触发、上升沿触发、下降沿触发等。定义触发方式的头文件在include/linux/interrupt.h下

#define IRQF_TRIGGER_NONE	0x00000000
#define IRQF_TRIGGER_RISING	0x00000001
#define IRQF_TRIGGER_FALLING	0x00000002
#define IRQF_TRIGGER_HIGH	0x00000004
#define IRQF_TRIGGER_LOW	0x00000008
#define IRQF_TRIGGER_MASK	(IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE	0x00000010
free_irq(button[i].irq, (void *)&Button_press[i]);

释放中断函数

★关于Button_read函数

此函数的作用是记录按键陪按下的次数,然后将这个记录从内核空间复制到用户空间

static int Button_read(struct file *file,char __user *buff,size_t count,loff_t *offp)
{
printk("Button_read \n");
#if 1
unsigned long err;
printk("process is stopped\n");
wait_event_interruptible(button_waitq, en_press);
en_press = 0;
err = copy_to_user(buff, (const void *)Button_press, min(sizeof(Button_press),count));
//将内核空间数据拷贝到用户空间
printk("copy_to_user return value = %u\n",err);
memset((void *)Button_press,0,sizeof(Button_press));
return err ? -EFAULT : 0;
#endif
}
如果按键没有被按下,调用此驱动函数的进程就会被wait_event_interruptible阻塞,因为这时候en_press=0满足被阻塞的条件;只有当按键被按下,中断产生en_press=1,唤醒进程,执行下面的程序。

copy_to_user是将内核空间的内容复制到用户空间;对应的程序是copy_from_user是将用户空间的内容复制到内核空间。

★关于中断处理函数

static irqreturn_t Button_interrupt(int irq,void *dev_id)
{
volatile int *press_cnt = (volatile int *)dev_id;

*press_cnt = *press_cnt +1;
en_press = 1;
wake_up_interruptible(&button_waitq);
return IRQ_RETVAL(IRQ_HANDLED);
}
中断处理函数主要是这个返回值,总共有三种

 enum irqreturn

 @IRQ_NONE interrupt was not from this device

 @IRQ_HANDLED interrupt was handled by this device

 @IRQ_WAKE_THREAD handler requests to wake the handler thread

enum irqreturn {
IRQ_NONE,
IRQ_HANDLED,
IRQ_WAKE_THREAD,
};


关于代码

#include<linux/module.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<asm/irq.h> //关于中断号的头文件
#include<linux/interrupt.h>
#include<linux/errno.h>
#include<asm/uaccess.h> //copy_to_user copy_from_user

#define BUTTON_MAJOR 0
static int Button_major = 0;
static int en_press = 0;

struct cdev button_cdev;

struct button_irq_desc { int irq; //中断号 unsigned long flags;//中断触发方式 char *name;//中断名称 }; struct button_irq_desc button[4] = { {IRQ_EINT1,IRQF_TRIGGER_FALLING,"KEY1"}, {IRQ_EINT4,IRQF_TRIGGER_FALLING,"KEY2"}, {IRQ_EINT2,IRQF_TRIGGER_FALLING,"KEY3"}, {IRQ_EINT0,IRQF_TRIGGER_FALLING,"KEY4"}, };
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

static volatile int Button_press[]= {0,0,0,0};

/**中断处理函数**/
static irqreturn_t Button_interrupt(int irq,void *dev_id) { volatile int *press_cnt = (volatile int *)dev_id; *press_cnt = *press_cnt +1; en_press = 1; wake_up_interruptible(&button_waitq); return IRQ_RETVAL(IRQ_HANDLED); }

static int Button_open(struct inode *inode, struct file *file)
{
int i,err;
for(i = 0;i < 4;i++)
{
err=request_irq(button[i].irq, Button_interrupt, button[i].flags, button[i].name, (void *)&Button_press[i]);
if(err)
break;
}

if(err)
{
for(;i>0;i--)
free_irq(button[i].irq, (void *)&Button_press[i]);
return -EBUSY;
}
return 0;
}

static int Button_read(struct file *file,char __user *buff,size_t count,loff_t *offp) { printk("Button_read \n"); #if 1 unsigned long err; printk("process is stopped\n"); wait_event_interruptible(button_waitq, en_press); en_press = 0; err = copy_to_user(buff, (const void *)Button_press, min(sizeof(Button_press),count)); //将内核空间数据拷贝到用户空间 printk("copy_to_user return value = %u\n",err); memset((void *)Button_press,0,sizeof(Button_press)); return err ? -EFAULT : 0; #endif }

static int Button_close(struct inode *inode,struct file *file)
{
int i;
for(i = 0;i < 4;i++)
{
free_irq(button[i].irq, (void *)&Button_press[i]);
}
return 0;
}

struct file_operations char_operation =
{
.owner = THIS_MODULE,
.open = Button_open,
.read = Button_read,
.release = Button_close,
};

static struct class *button_class;

static int __init button_init(void)
{
int result,err;

dev_t dev = MKDEV(BUTTON_MAJOR, 0);
if(BUTTON_MAJOR)
{

result = register_chrdev_region(dev, 1, "Button");
}
else
{
result = alloc_chrdev_region(&dev, 0, 1, "Button");
Button_major = MAJOR(dev);
}
if(result < 0)
return result;

cdev_init(&button_cdev, &char_operation);
button_cdev.owner = THIS_MODULE;
err = cdev_add(&button_cdev,dev, 1);
if(err)
printk(KERN_NOTICE "Error %d adding globalmem",err);

#if 1

button_class = class_create(THIS_MODULE, "Button_char_driver"); if(IS_ERR(button_class)) { printk("failed ^^^^^^^^^"); return -1; } device_create(button_class,NULL, dev,NULL,"Button_char_driver");
#endif
return 0;
}

static void __exit button_exit(void)
{
cdev_del(&button_cdev); device_destroy(button_class, MKDEV(Button_major, 0)); class_destroy(button_class); unregister_chrdev_region(MKDEV(Button_major, 0), 1);

}

MODULE_LICENSE("Dual BSD/GPL");

module_init(button_init);
module_exit(button_exit);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: