这里主要是仿照《嵌入式Linux开发完全手册》上的例子写的,只是增加了别外两个按按键。在我的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; }
|
wait_event_interruptible首先会判断ev_press是否为0,如果为0才会令当前进程进入休眠,否则向下继续执行,它的第一个参数,button_waitq是一个等待的队列,在前面定义过,第二个参数ev_press用来表示中断是否已经发生,中断服务程序将它置1,如果ev_press为0,当前进程进入休眠,中断发生时中断处理函数buttons_interrupt会把它唤醒。将press_cnt数组的内容复制到用户空间,buff参数表示的缓冲区位于用户空间,使用copy_to_user向它赋值。
5.中断处理函数buttons_interrupt
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(&button_waitq); //唤醒休眠的进程 return IRQ_RETVAL (IRQ_HANDLED); }
|
buttons_interrupt函数被调用时,第一个参数irq表示发生的中断号,第二个参数dev_id就是前面使用request_irq注册中断时传入的“&pres_cnt[i]”.
将mini2440_buttons.c放到内核源码目录drivers/char下,在drivers/char目录下生成可加载模块mini2440_buttons.ko,把它放开开发板根文件系统的/lib/modules/2.6.22.6目录下,就可以使用"insmod mini2440_buttons"、“rmmod mini2440_buttons.ko”命令进行加载,卸载了。
6.测试程序
编写的测试程序buttons_test.c,编译后生成可执行文件,然后把它放到开发板根文件系统/usr/bin目录下。在开发板根文件系统中建立设备文件。
#mknod /dev/buttons c 232 0
|
然后使用“insmod mini2440_buttons”命令加载模块。执行完这条命令后可以看到在控制台中执行“cat /proc/devices”
[root@Frankzfz 2.6.32.2-FriendlyARM]$cat /proc/devices Character devices: 1 mem 2 pty 3 ttyp 4 /dev/vc/0 4 tty 5 /dev/tty 5 /dev/console 5 /dev/ptmx 7 vcs 10 misc 13 input 14 sound 29 fb 89 i2c 90 mtd 116 alsa 128 ptm 136 pts 153 spi 180 usb 189 usb_device 204 s3c2410_serial 232 buttons //主设备号 刚注册的设备名 252 hidraw 253 ttySDIO 254 rtc Block devices: 1 ramdisk 256 rfd 259 blkext 31 mtdblock 44 ftl 93 nftl 96 inftl 179 mmc
|
运行测试程序button_test后,/dev/buttons设备就会被打开,可以使用“cat /proc/interrupts”命令看到注册的6个中断了。
[root@Frankzfz /mnt]$cat /proc/interrupts CPU0 30: 157894 s3c S3C2410 Timer Tick 42: 0 s3c ohci_hcd:usb1 43: 8 s3c s3c2440-i2c 51: 9751 s3c-ext eth0 52: 3 s3c-ext KEY0 55: 1 s3c-ext KEY1 57: 2 s3c-ext KEY2 58: 15 s3c-ext KEY3 59: 75 s3c-ext KEY4 63: 10 s3c-ext KEY5 70: 848 s3c-uart0 s3c2440-uart 71: 1476 s3c-uart0 s3c2440-uart 83: 0 - s3c2410-wdt Err: 0
|
第一列表示中断号,第二列表示这个中断发生的次数,第三列的文字表示这个中断的硬件访问结构“struct irq_chip *chip”的名字。它在初始化中断体系结构时指定,第四列文字表示中断的名称,它在request_irq中指定。
测试程序buttons_test.c如下
#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; } // 这是个无限循环,进程有可能在read函数中休眠,当有按键被按下时,它才返回 while (1) { // 读出按键被按下的次数
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++) { // 如果被按下的次数不为0,打印出来
if (press_cnt[i]) } } close(fd); return 0; }
|
在运行button_test时,可以以后台运行在button_test &。在开发板上按下不同的按键可以在串口上看到以下的信息。
$K1 has been pressed 1 K2 has been pressed 1 K2 has been pressed 1 K3 has been pressed 1 K3 has been pressed 5 K3 has been pressed 2 K6 has been pressed 1 K4 has been pressed 1 K4 has been pressed 1 K4 has been pressed 1 K4 has been pressed 3 K4 has been pressed 7 K4 has been pressed 2 K4 has been pressed 5 K4 has been pressed 10 K4 has been pressed 5 K5 has been pressed 1 K5 has been pressed 1 K5 has been pressed 1 K5 has been pressed 1 [3] + Done(255) ./button_test
|
这只是一处简单的测试程序,当有时按下一次时,也可能出现说是按下了10次,没有很精确。如果以前没有按键被按下则进行休眠状态。
阅读(224) | 评论(0) | 转发(0) |