Linux驱动开发之三----按键驱动(Tiny6410)

Linux内核版本:2.6.38

编译器:arm-linux-gcc-4.5.1


Tiny6410中默认给出的内核是支持按键驱动的,要想加载自己的按键驱动,不能使用它提供的内核,需要重新配置内核,并编译生成新的内核,最后再烧写到开发板NAND FLASH上。修改的方法:make menuconfig->device driver->char->把buttons有关的*去掉,也就是说不编译它。然后将自己的驱动加载到内核测试即可.

有了操作系统后,对于中断的处理比较简单,因为操作系统已经为你搭建好了中断体系结构,并且对它进行了初始化,而普通的程序员要做的事情就是编写用户中断处理程序,并调用内核提供给用户的接口来注册中断,这样就OK了,至于什么使能中断、中断清理啊,这些都由操作系统来完成。在ARM体系结构中,Linux使用irq_desc结构体来描述对应的中断,这个结构体在文件”include/linux/irqdesc.h“定义,比较重要的字段有:

	struct irq_chip		*chip;/*底层的硬件访问*/
	irq_flow_handler_t	handle_irq;/*当前中断的处理函数入口*/
	struct irqaction	*action;	/* IRQ action list 用户提供的中断处理函数链表*/
	unsigned int		status;		/* IRQ status 当前中断状态*/
	const char		*name;/*中断名称*/

每个中断号对应一个这样的结构体,irqaction是用户中断处理函数链表结构体,比较重要的字段如下:(在include/linux/interrupt.h中定义)

struct irqaction {
	irq_handler_t handler;//中断处理函数
	unsigned long flags;//标志位,是否要共享中断,触发方式:电平或者是边沿触发*/
	void *dev_id;/*传递给中断处理函数handler的参数*/
	struct irqaction *next;
	int irq;//中断号
	const char *name;/*用户注册的中断名称,使用cat /proc/interrupt 查看到的名称*/
} 
irq_chip是底层的硬件访问函数接口,例如屏蔽中断、使能中断等,该结构体在include/linux/irq.h中定义


按键驱动中我们需要做的是编写中断处理程序,并注册到Linux中断框架中去,依然使用标准的字符设备驱动的写法来编写,具体代码如下:

/*
 *
 *	buttons驱动程序TIny6410
 *	使用标准字符设备驱动编写方法
 *	make完成后根据打印到终端的输出,创建字符设备
 *	格式如下:
 *	mknod /dev/buttons c 主设备号 0
 *	目前存在一些问题,只能驱动按键K1~K4,不能驱动K5~K7,原因是request_irq函数调用失败
 *	另外,使用ctrl+c中断后再次加载驱动会失败,原因不清楚
 *	Author:jefby
 *	Email:jef199006@gmail.com
 *
 */
#include <linux/module.h>//MODULE_LICENSE,MODULE_AUTHOR
#include <linux/init.h>//module_init/module_exit


#include <linux/fs.h>//file_operations
#include <asm/io.h>//ioread32,iowrite32
#include <linux/cdev.h>//cdev
#include <mach/map.h>//定义了S3C64XX_VA_GPIO
#include <mach/regs-gpio.h>//定义了gpio-bank-n中使用的S3C64XX_GPN_BASE
#include <mach/gpio-bank-n.h>//定义了GPNCON
#include <mach/gpio-bank-l.h>//定义了GPNCON
#include <linux/wait.h>//wait_event_interruptible(wait_queue_head_t q,int condition);
//wake_up_interruptible(struct wait_queue **q)
#include <linux/sched.h>//request_irq,free_irq
#include <asm/uaccess.h>//copy_to_user
#include <linux/irq.h>//IRQ_TYPE_EDGE_FALLING
#include <linux/interrupt.h>//request_irq,free_irq

MODULE_AUTHOR("jefby");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("Tiny 6410 buttons with interrupt");

//buttons IRQ描述符
struct buttons_irq_desc{
	int irq;//中断号
	unsigned long flags;//中断标志号,是否可共享
	char *name;//中断名称,使用cat /proc/interrupt
};
//irq描述符的初始化,用来指定所用的外部中断引脚以及中断触发方式、名字
static struct buttons_irq_desc buttons_irqs[] = {
	{IRQ_EINT(0),IRQ_TYPE_EDGE_RISING,"KEY1"},//KEY1
	{IRQ_EINT(1),IRQ_TYPE_EDGE_RISING,"KEY2"},//KEY2	
	{IRQ_EINT(2),IRQ_TYPE_EDGE_RISING,"KEY3"},//KEY3	
	{IRQ_EINT(3),IRQ_TYPE_EDGE_RISING,"KEY4"},//KEY4	
/*	{IRQ_EINT(4),IRQ_TYPE_EDGE_RISING,"KEY5"},//KEY4	
	{IRQ_EINT(5),IRQ_TYPE_EDGE_RISING,"KEY6"},//KEY4	
	{IRQ_EINT(19),IRQ_TYPE_EDGE_FALLING,"KEY7"},//KEY4	
	{IRQ_EINT(20),IRQ_TYPE_EDGE_FALLING,"KEY8"},//KEY4	
*/	
};

//声明一个按键的等待队列
static DECLARE_WAIT_QUEUE_HEAD(buttons_waitq);
//指示是否有按键被按下
static volatile int ev_press = 0;
//按键设备的主设备号
static int buttons_major = 0;
//设备号
dev_t dev;
//字符设备
struct cdev * buttons_cdev;
//按下次数
static volatile int press_cnt[]={0,0,0,0};

//中断处理程序,记录按键按下的次数,并置标志位为1,唤醒等待队列上等待的进程
static irqreturn_t buttons_interrupt(int irq,void *dev_id)
{
	volatile int *press_cnt = (volatile int *)dev_id;

	*press_cnt = *press_cnt + 1;//按键计数值加1
	ev_press = 1;//设置标志位
	wake_up_interruptible(&buttons_waitq);

	return IRQ_RETVAL(IRQ_HANDLED);
}

//设备打开操作,主要完成BUTTONS所对应的GPIO的初始化,注册用户中断处理函数
int  buttons_open(struct inode *inode,struct file *filp)
{
	int i;
	int err;
	unsigned val;

	/*设置buttons对应的GPIO管脚*/
	val = ioread32(S3C64XX_GPNCON);
	val = (val & ~(0xFF)) | (0xaa);//设置GPIO 0~5为Ext interrupt[0~3]输出
	iowrite32(val,S3C64XX_GPNCON);
/*
	val = ioread32(S3C64XX_GPLCON1);
	val = (val & ~(0xFF<<12)) | (0x33);
	iowrite32(val,S3C64XX_GPLCON1);
*/
	/*注册中断处理例程*/
	for(i=0;i<sizeof(buttons_irqs)/sizeof(buttons_irqs[0]);++i){
		err = request_irq(buttons_irqs[i].irq,buttons_interrupt,buttons_irqs[i].flags,buttons_irqs[i].name,(void*)&press_cnt[i]);
		if(err)
			break;
	}
	if(err){
		printk("buttons_open functions err.\n");
		i--;
		for(;i>=0;--i)
			free_irq(buttons_irqs[i].irq,(void*)&press_cnt[i]);
		return -EBUSY;
	}
	return 0;
}
//按键读若没有键被按下,则使进程休眠;若有按键被按下,则拷贝数据到用户空间,然后清零
int buttons_read(struct file *filp, char __user *buf, size_t len, loff_t * pos)
{
	unsigned long err;
	wait_event_interruptible(buttons_waitq,ev_press);//如果ev_press==0,则进程在队列buttons_waitq队列上休眠,直到ev_press==1
	ev_press = 0;//此时ev_press==1,清除ev_press
	err = copy_to_user(buf,(const void *)press_cnt,min(sizeof(press_cnt),len));//将press_cnt的值拷贝到用户空间
	memset((void*)press_cnt,0,sizeof(press_cnt));//初始化press_cnt为0
	return err ? -EFAULT : 0;

}
//主要是卸载用户中断处理程序
int buttons_close(struct inode *inode,struct file *filp)
{
	int i;
	for(i=0;i<sizeof(buttons_irqs)/sizeof(buttons_irqs[0]);++i)
		free_irq(buttons_irqs[i].irq,(void*)&press_cnt);
	return 0;
}


static struct file_operations buttons_fops = {
	.owner = THIS_MODULE,
	.read = buttons_read,
	.release = buttons_close,
	.open = buttons_open,
};
/*
	模块初始化:
		1.申请设备号,默认使用动态分配的方法
		2.申请并初始化cdev结构
		3.将cdev注册到内核
*/
static int module_buttons_init(void)
{
	
	int result;
	printk("Tiny6410 buttons module init.\n");	
	if(buttons_major){
		dev = MKDEV(buttons_major,0);
		result = register_chrdev_region(dev,1,"buttons");
	}else{
		result = alloc_chrdev_region(&dev,0,1,"buttons");
		buttons_major = MAJOR(dev);
	}
	if(result < 0){
		printk(KERN_WARNING "buttons : can't get major %d\n",buttons_major);
	}

	printk("buttons major is %d",buttons_major);
	buttons_cdev = cdev_alloc();
	buttons_cdev ->ops = &buttons_fops;
	cdev_init(buttons_cdev,&buttons_fops);
	cdev_add(buttons_cdev,dev,1);
	printk("buttons add ok.\n");
	return 0;
}

static void module_buttons_exit(void)
{
	cdev_del(buttons_cdev);
	unregister_chrdev_region(dev,1);
	printk("Tiny6410 buttons module exit");
}

module_init(module_buttons_init);
module_exit(module_buttons_exit);

Makefile的编写参考上一篇博文,基本语法一样,另外测试的APP程序如下:

/*
 *
 *
 *	buttons的用户程序:配合驱动程序使用
 *	Author:jefby
 *	Email:jef199006@gmail.com
 *
 *
 * */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>


int main(int argc,char **argv)
{
	int i;
	int ret;
	int fd;
	int press_cnt[4];
	//打开设备
	fd = open("/dev/buttons",0);
	if(fd < 0){
		printf("can't open /dev/buttons\n");
		return -1;
	}
	while(1){//循环读取,若read函数执行前无按键被按下,则此时进程处于休眠状态.
		ret = read(fd,press_cnt,sizeof(press_cnt));
		if(ret < 0){
			printf("read err.\n");
			continue;
		}
		for(i=0;i<sizeof(press_cnt)/sizeof(press_cnt[0]);++i){//打印
			if(press_cnt[i])
				printf("K%d has been pressed %d times!\n",i+1,press_cnt[i]);
		}
	}
}

编译,挂载到开发板上可以看到运行截图如下:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值