这里主要是仿照《嵌入式Linux开发完全手册》 上的例子写的,下载见http://www.linuxidc.com/Linux/2011-01/31114.htm ,只是增加了别外两个按按键。在我的mini2440开发板上有6个按键。在上两篇文章中,主要分析了驱动中的整体的流程,现在来看一个具体的例子,是如何使用中断的。
1. 模块的初始化函数和卸载函数
/* 执行"insmod mini2440_buttons.ko"命令时就会调用这个函数*/ static int __init mini2440_buttons_init (void) { int ret; /*这里主要是注册设备驱动程序,参数为主设备号,如果BUTTON_MAJOR设为0,表示由内核自动分配主设备号,设备的名字,file_operations结构,操作主调和号为BUTTON_MAJOR的设备文件时,就会调用mini2440_buttons_fops中的相关成员函数*/ ret = register_chrdev(BUTTON_MAJOR,DEVICE_NAME,&mini2440_buttons_fops); if(ret < 0) { printk(DEVICE_NAME "can't register major number\n"); return ret ; } printk(DEVICE_NAME"initialized\n"); return 0; } /* 执行 rmmod mini2440_buttons.ko0 命令时就会调用这个函数 */ static void __exit mini2440_buttons_exit(void) {//卸载驱动程序 unregister_chrdev(BUTTON_MAJOR,DEVICE_NAME); } //指定驱动程序的初始化函数和卸载函数 module_init(mini2440_buttons_init); module_exit(mini2440_buttons_exit); |
下面这个结构体是每一个字符驱动程序都是要用到的。这里定义了应用程序可以使用的设备操作函数,只有在这个结构体中的函数,在应用程序中才可以使用,在下面的驱动程序中要实现下面的函数。
/* 这个结构是字符设备驱动程序的核心,当应用程序操作设备文件时所调用的open,read,write等函数,最终会调用这个结构中的对应函数*/ static struct file_operations mini2440_buttons_fops = { .owner = THIS_MODULE, //这是 个宏,指向编译模块时自动创建的_this_module变量 .open = mini2440_buttons_open, .release = mini2440_buttons_close, .read = mini2440_buttons_read, }; |
2. mini2440_buttons_open函数
在应用程序执行“open("/dev/buttons",..)"系统调用时,mini2440_buttons_open函数将被调用。这用来注册6个按键的中断处理程序
static int mini2440_buttons_open(struct inode *inode,struct file *file) { int i; int err; for (i=0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++) { //注册中断处理函数 一共六个 err = request_irq(button_irqs[i].irq,buttons_interrupt,button_irqs[i].flags,button_irqs[i].name,(void *)&press_cnt[i]); if (err) break; } if(err) //出错处理函数,如果出错释放已经注册的中断 { i--; for(;i>=0;i--) free_irq(button_irqs[i].irq,(void *)&press_cnt[i]); return -EBUSY; } return 0; } |
requst_irq函数执行成功后,这6个按键所用的GPIO引脚的功能被设为外部中断,触发方式为下降沿触发,中断处理函数为buttons_interrupt.最后一个参数“(void *)&press_cnt[i]”将在buttons_interrupt函数中用到,它用来 存储按键被按下的次数。参数button_irqs的定义如下:
struct button_irq_desc { int irq;//中断号 unsigned long flags; //中断标志,用来定义中断的触发方式 char *name; //中断名称 }; static struct button_irq_desc button_irqs[] = { //下面是按键对应的外部的中断号,触发方式,名称 {IRQ_EINT8,IRQF_TRIGGER_FALLING,"KEY0"}, {IRQ_EINT11,IRQF_TRIGGER_FALLING,"KEY1"}, {IRQ_EINT13,IRQF_TRIGGER_FALLING,"KEY2"}, {IRQ_EINT14,IRQF_TRIGGER_FALLING,"KEY3"}, {IRQ_EINT15,IRQF_TRIGGER_FALLING,"KEY4"}, {IRQ_EINT19,IRQF_TRIGGER_FALLING,"KEY5"}, }; |
3. mini2440_buttons_close函数
mini2440_buttons_close函数的作用是用来卸载6个按键的中断处理函数代码如下:
/* 应用程序对设备文件/dev/buttons执行close(...)时。就会调用mini2440_buttons_close函数*/ static int mini2440_buttons_close(struct inode *inode,struct file *file) { int i; for(i=0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++) { //释放已注册的函数 free_irq(button_irqs[i].irq,(void *)&press_cnt[i]); } return 0; } |
4. mini2440_buttons_read函数
中断处理函数会在press_cnt数组中记录按键被按下的次数。mini_buttons_read函数,首先判断是否按键再次按下,如果没有则休眠;否则读取press_cnt数组的数据,
/*等待队列: 当没有按键被按下时,如果有进程调用mini2440_buttons_read函数,它将休眠*/ static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /*中断事件标志,中断服务程序将它置1,mini2440_buttons_read将它清0*/ static volatile int ev_press = 0; /*应用程序对设备文件/dev/buttons执行read(...)时,就会调用mini2440_buttons_read函数*/ static int mini2440_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp) { unsigned long err; //如果ev_press等于0,休眠 wait_event_interruptible(button_waitq,ev_press); ev_press = 0;// 执行到这里是ev_press肯定是1,将它清0 //将按键状态复制给用户,并清0 err = copy_to_user(buff,(const void *)press_cnt,min(sizeof(press_cnt),count)); memset((void *)press_cnt,0,sizeof(press_cnt)); return err ? -EFAULT:0; } |