这里主要是仿照《嵌入式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次,没有很精确。如果以前没有按键被按下则进行休眠状态