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

这几天又回到了驱动程序学习的轨道上来。还是从简单的硬件开始一步步的来学习驱动。使用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);







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值