linux驱动之中断方式获取键值
------------------------------------------------------------------------------------------------------------------------------------------------------
回想在单片机下的中断处理
- 分辨是哪个中断
- 调用处理函数
- 清中断
1、中断框架
trap_int 中构造
trap_int函数就是一些跳转指令
b...
b...
b vector_irq + stubs_offset ;vector_irq是链接地址 stubs_offset 是偏移地址
vector_irq:
- 保存被中断的现场
- asm_do_IRQ
- 恢复现场
- 。
。。
asm_do_IRQ:
对中断的处理。
request_irq 注冊中断
request_irq(irq,handle,irqflags,devname,dev_id)
1、分配irqaction 结构
2、setup_irq(irq,action)
0、分配一个irqaction 结构
a、在irq_desc[irq] -> action
b、desc -> chip ->settype
c、desc -> chip -> startup /enable
irq | 要申请的硬件中断号 | + |
handle | 是向系统登记的中断处理函数 | 是一个回调函数,主哦功能短发生时,系统将调用这个函数,并将dev_id传递给它 |
irqflags | 是中断处理属性 |
free_irq 卸载中断
free_irq(irq, dev_id)
1、出链
2、禁止中断
2、代码编写
1、申请IRQ
这里我要特殊说明下:(向我这样的小白在使用request_irq这个函数时遇到了一个问题就是 IRQ_EINT1、IRQ_EINT4、IRQ_EINT2、IRQ_EINT0这四个宏找不到,终于原因找到了 。我配置eclipse的时候内核树的路径有些小问题。
irqs.h这个文件是在 /linux-2.6.30.4/arch/arm/mach-s3c2410/include 这个路径下。我以为这个在mach-s3c2440也会有这个路径呢。事实上不然。另一些引脚的定义也在2410的路径里面,这里要注意一下 )。
request_irq(IRQ_EINT1,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key1",&pins_desc[0]); request_irq(IRQ_EINT4,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key2",&pins_desc[1]); request_irq(IRQ_EINT2,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key3",&pins_desc[2]); request_irq(IRQ_EINT0,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key4",&pins_desc[3]);
这里的pins_desc 是一个结构体数组
struct pin_desc pins_desc[4] = { {S3C2410_GPF1,0x01}, {S3C2410_GPF4,0x02}, {S3C2410_GPF2,0x03}, {S3C2410_GPF0,0x04}, };
这个数组里面包括了这四个中断引脚和该引脚的键值。
结构体原型是这种:
struct pin_desc{ unsigned int pin; unsigned int key_value; };
2、写中断处理函数
static irqreturn_t buttons_irq(int irq,void *dev_id) { struct pin_desc * pindesc = (struct pin_desc *) dev_id; unsigned int pinval; pinval = s3c2410_gpio_getpin(pindesc -> pin); if(pinval)//松开 { keyval = 0x80|pindesc->key_value; } else { keyval = pindesc->key_value; } ev_press =1;//中断发生 wake_up_interruptible(&button_wait_q); printk("button is pressed : %d ",irq); return IRQ_HANDLED; }说明:
pindesc :这个结构体会得知是哪个按键按下了
s3c2410_gpio_getpin:这个函数是内核提供的函数,能够获得当前引脚的高低电平
ev_press:中断事件标志。中断服务程序将他置1,read函数将他置0
wake_up_interruptible(&button_wait_q); 会唤醒休眠的进程,唤醒注冊到等待队列上的进程,当中 button_wait_q是通过例如以下方式获得的:
static DECLARE_WAIT_QUEUE_HEAD(button_wait_q); 生成一个等待队列头wait_queue_head_t,名字为 button_wait_q
3、改动read函数
ssize_t button_dev_read(struct file *file,char __user *buf,size_t size,loff_t *ppos) { if(size !=1) { return -EINVAL; } /*假设没有按键动作发生 就休眠*/ wait_event_interruptible(button_wait_q,ev_press); /*假设有按键动作发生,直接返回*/ copy_to_user(buf,&keyval,1); ev_press = 0; return 0; }
当中:
wait_event_interruptible(button_wait_q,ev_press); 假设ev_press是假的话。就会休眠
ev_press =0; 每次读完,都将中断事件标志清零。
4、填充file_operations结构体
static struct file_operations button_sdv_fops = { .owner = THIS_MODULE, .open = button_dev_open, .read = button_dev_read, .release = button_dev_close, };
当中button_dev_close函数为:
int button_dev_close(struct inode* inode ,struct file *file) { free_irq(IRQ_EINT1,&pins_desc[0]); free_irq(IRQ_EINT4,&pins_desc[1]); free_irq(IRQ_EINT2,&pins_desc[2]); free_irq(IRQ_EINT0,&pins_desc[3]); return 0; }
free_irq为释放中断。当中须要两个參数。
本代码在上一篇按键的博文基础上改动。
完整的驱动代码:
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <asm/io.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <mach/regs-gpio.h> #include <mach/irqs.h>//这个在/opt/EmbedSky/linux-2.6.30.4/arch/arm/mach-s3c2410/include/mach 路径 #include <linux/interrupt.h> MODULE_LICENSE("Dual BSD/GPL"); static struct class *buttondrv_class; static struct class_devices *buttondrv_class_dev; /* */ static DECLARE_WAIT_QUEUE_HEAD(button_wait_q); /*中断事件标志,中断服务程序将他置1,read函数将他置0*/ static volatile int ev_press =0; volatile unsigned long *gpfcon = NULL; volatile unsigned long *gpfdat = NULL; static unsigned keyval; struct pin_desc{ unsigned int pin; unsigned int key_value; }; /*按键按下时是:0x01 0x02 0x03 0x04*/ /*按键松开时是:0x81 0x82 0x83 0x84*/ struct pin_desc pins_desc[4] = { {S3C2410_GPF1,0x01}, {S3C2410_GPF4,0x02}, {S3C2410_GPF2,0x03}, {S3C2410_GPF0,0x04}, }; /* * 确定按键值 */ static irqreturn_t buttons_irq(int irq,void *dev_id) { struct pin_desc * pindesc = (struct pin_desc *) dev_id; unsigned int pinval; pinval = s3c2410_gpio_getpin(pindesc -> pin); if(pinval)//松开 { keyval = 0x80|pindesc->key_value; } else { keyval = pindesc->key_value; } ev_press =1;//中断发生 wake_up_interruptible(&button_wait_q); printk("button is pressed : %d ",irq); return IRQ_HANDLED; } static int button_dev_open(struct inode *inode ,struct file* file) { //配置按键的引脚 GPF0,1,2,4为输入引脚 request_irq(IRQ_EINT1,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key1",&pins_desc[0]); request_irq(IRQ_EINT4,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key2",&pins_desc[1]); request_irq(IRQ_EINT2,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key3",&pins_desc[2]); request_irq(IRQ_EINT0,buttons_irq, IRQ_TYPE_EDGE_BOTH,"key4",&pins_desc[3]); return 0; } ssize_t button_dev_read(struct file *file,char __user *buf,size_t size,loff_t *ppos) { if(size !=1) { return -EINVAL; } /*假设没有按键动作发生 就休眠*/ wait_event_interruptible(button_wait_q,ev_press); /*假设有按键动作发生,直接返回*/ copy_to_user(buf,&keyval,1); ev_press = 0; return 0; } int button_dev_close(struct inode* inode ,struct file *file) { free_irq(IRQ_EINT1,&pins_desc[0]); free_irq(IRQ_EINT4,&pins_desc[1]); free_irq(IRQ_EINT2,&pins_desc[2]); free_irq(IRQ_EINT0,&pins_desc[3]); return 0; } static struct file_operations button_sdv_fops = { .owner = THIS_MODULE, .open = button_dev_open, .read = button_dev_read, .release = button_dev_close, }; int major; static int button_dev_init(void)//入口函数 { major = register_chrdev(0,"button_drv",&button_sdv_fops); buttondrv_class = class_create(THIS_MODULE,"button_drv"); if(IS_ERR(buttondrv_class)) return PTR_ERR(buttondrv_class); buttondrv_class_dev= device_create(buttondrv_class,NULL,MKDEV(major,0),NULL,"wq_button"); if(unlikely(IS_ERR(buttondrv_class_dev))) return PTR_ERR(buttondrv_class_dev); /*映射物理地址*/ gpfcon = (volatile unsigned long *) ioremap(0x56000050 ,16); gpfdat = gpfcon + 1; return 0; } static void button_dev_exit(void) { unregister_chrdev(major,"button_drv"); device_unregister(buttondrv_class_dev); class_destroy(buttondrv_class); iounmap(gpfcon); } module_init(button_dev_init); module_exit(button_dev_exit);
測试代码:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> /* * wq_device <dev> <on|off> */ int main(int argc, char **argv) { int cnt=0; int fd; unsigned char key_val; fd = open("/dev/wq_button", O_RDWR); if(fd<0) { printf("can't open "); } while(1) { read(fd,&key_val,1); printf("key_val = 0x%x ",key_val); } return 0; }