全部博文(298)
分类: LINUX
2011-03-24 13:07:10
(11)mini2440之按键驱动
注:所以文章红色字体代表需要特别注意和有问题还未解决的地方,蓝色字体表示需要注意的地方
1.本文所介绍的程序平台
开发板:arm9-mini2440
虚拟机为:Red Hat Enterprise Linux 5
开发板上系统内核版本:linux-2.6.32.2
2.程序清单
本次实验程序为网络代码,本人作了改动和较为详细的注释,如有错误请指出。
内核维护了一个中断信号线的注册表,类似于 I/O 端口的注册表。模块在使用中断前要先请求一个中断通道(或者 IRQ中断请求),并在使用后释放它。所用的函数声明在
int request_irq(unsigned int irq, const char *dev_name, |
request_irq 的返回值: 0 指示成功,或返回一个负的错误码,如 -EBUSY 表示另一个驱动已经占用了你所请求的中断线。
函数的参数如下:
unsigned int irq :请求的中断号
irqreturn_t (*handler) :安装的处理函数指针。
unsigned long flags :一个与中断管理相关的位掩码选项。
const char *dev_name :传递给 request_irq 的字符串,用来在 /proc/interrupts 来显示中断的拥有者。
void *dev_id :用于共享中断信号线的指针。它是唯一的标识,在中断线空闲时可以使用它,驱动程序也可以用它来指向自己的私有数据区(来标识哪个设备产生中断)。若中断没有被共享,dev_id 可以设置为 NULL,但推荐用它指向设备的数据结构。
flags是中断处理的属性,若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版zhon已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的)
dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
flags 中可以设置的位如下:
SA_INTERRUPT :快速中断标志。快速中断处理例程运行在当前处理器禁止中断的状态下。
现在没有了 !!
SA_SHIRQ : 在设备间共享中断标志。
SA_SAMPLE_RANDOM :该位表示产生的中断能对 /dev/random 和 /dev/urandom 使用的熵池(entropy pool)有贡献。 读取这些设备会返回真正的随机数,从而有助于应用程序软件选择用于加密的安全密钥。 若设备以真正随机的周期产生中断,就应当设置这个标志。若设备中断是可预测的,这个标志不值得设置。可能被攻击者影响的设备不应当设置这个标志。更多信息看 drivers/char/random.c 的注释。
mini_button.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include "regs-gpio.h"
//#include "irqs.h"
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "buttons" /* 加载模式后,执行”cat /proc/devices”命令看到的设备名称 */
#define BUTTON_MAJOR 232 /* 主设备号 */
struct button_irq_desc {
int irq;//中断号
int pin;//引脚
int pin_setting;//引脚设置
int number;//编号
char *name; //名字
};
/* 用来指定按键所用的外部中断引脚及中断触发方式, 名字 ,初始化了各值*/
static struct button_irq_desc button_irqs [] = {
{IRQ_EINT8, S3C2410_GPG(0), S3C2410_GPG0_EINT8, 0, "KEY1"}, /* K1 */
{IRQ_EINT11, S3C2410_GPG(3), S3C2410_GPG3_EINT11, 1, "KEY2"}, /* K2 */
{IRQ_EINT13, S3C2410_GPG(5), S3C2410_GPG5_EINT13, 2, "KEY3"}, /* K3 */
{IRQ_EINT14, S3C2410_GPG(6), S3C2410_GPG6_EINT14, 3, "KEY4"}, /* K3 */
{IRQ_EINT15, S3C2410_GPG(7), S3C2410_GPG7_EINT15, 4, "KEY5"}, /* K3 */
{IRQ_EINT19, S3C2410_GPG(11), S3C2410_GPG11_EINT19, 5, "KEY6"}, /* K4 */
};
/* 记录按键的状态 */
static volatile int key_values [] = {0, 0, 0, 0, 0, 0};
/* 等待队列:
* 当没有按键被按下时,如果有进程调用mini2440_buttons_read函数,
* 它将休眠
*/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
/* 中断事件标志, 中断服务程序将它置1,mini2440_buttons_read将它清0 */
static volatile int ev_press = 0;
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
int up = s3c2410_gpio_getpin(button_irqs->pin);//获取引脚状态
if (up)//如果是释放按键
//键值
key_values[button_irqs->number] = (button_irqs->number + 1) + 0x80;
else////如果是按下按键
key_values[button_irqs->number] = (button_irqs->number + 1);
ev_press = 1; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
return IRQ_RETVAL(IRQ_HANDLED);
}
/* 应用程序对设备文件/dev/buttons执行open(...)时,
* 就会调用mini2440_buttons_open函数
*/
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++) {
// 注册中断处理函数
//设置引脚作用 为中断
s3c2410_gpio_cfgpin(button_irqs[i].pin,button_irqs[i].pin_setting);
err = request_irq(button_irqs[i].irq, buttons_interrupt, NULL,
button_irqs[i].name, (void *)&button_irqs[i]);
//set_irq_type(button_irqs[i].irq,IRQT_BOTHEDGE);
//双沿IRQT_BOTHEDGE 上升沿IRQ_TYPE_EDGE_RISING
set_irq_type(button_irqs[i].irq, IRQ_TYPE_EDGE_BOTH);
if (err)
break;
}
if (err) {
// 释放已经注册的中断
i--;
for (; i >= 0; i--) {
disable_irq(button_irqs[i].irq);//关闭中断
free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
}
return -EBUSY;
}
return 0;
}
/* 应用程序对设备文件/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++) {
// 释放已经注册的中断
disable_irq(button_irqs[i].irq);
free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
}
return 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;
if (!ev_press) {//如果中断没有发生 即ev_press等于0
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
else
/* 如果ev_press等于0,休眠 */
wait_event_interruptible(button_waitq, ev_press);
}
/* 执行到这里时,ev_press等于1,将它清0 */
ev_press = 0;
/* 将按键状态复制给用户,并清0 */
err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));
memset((void *)key_values, 0, sizeof(key_values));
return err ? -EFAULT : min(sizeof(key_values), count);
}
/**************************************************
* 当用户程序调用select函数时,本函数被调用
* 如果有按键数据,则select函数会立刻返回
* 如果没有按键数据,本函数使用poll_wait等待
**************************************************/
static unsigned int mini2440_buttons_poll(
struct file *file,
struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait);
if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
/* 这个结构是字符设备驱动程序的核心
* 当应用程序操作设备文件时所调用的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,
.poll = mini2440_buttons_poll,
};
/*
* 执行“insmod mini2440_buttons.ko”命令时就会调用这个函数
*/
static int __init mini2440_buttons_init(void)
{
int ret;
/* 注册字符设备驱动程序
* 参数为主设备号、设备名字、file_operations结构;
* 这样,主设备号就和具体的file_operations结构联系起来了,
* 操作主设备为BUTTON_MAJOR的设备文件时,就会调用mini2440_buttons_fops中的相关成员函数
* BUTTON_MAJOR可以设为0,表示由内核自动分配主设备号
*/
ret = register_chrdev(BUTTON_MAJOR, DEVICE_NAME, &mini2440_buttons_fops);
if (ret < 0) {
printk(DEVICE_NAME " can't register major number\n");
return ret;
}
//低版本才能用devfs_mk_cdev
// devfs_mk_cdev(MKDEV(BUTTON_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEVICE_NAME);
printk(DEVICE_NAME " initialized\n");
return 0;
}
/*
* 执行”rmmod mini2440_buttons.ko”命令时就会调用这个函数
*/
static void __exit mini2440_buttons_exit(void)
{
/* 卸载驱动程序 */
//低版本才能用devfs_mk_cdev
// devfs_remove(DEVICE_NAME);
unregister_chrdev(BUTTON_MAJOR, DEVICE_NAME);
}
/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(mini2440_buttons_init);
module_exit(mini2440_buttons_exit);
/* 描述驱动程序的一些信息,不是必须的 */
MODULE_AUTHOR(""); // 驱动程序的作者
MODULE_DESCRIPTION("S3C2410/S3C2440 BUTTON Driver"); // 一些描述信息
MODULE_LICENSE("GPL"); // 遵循的协议
button_app.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(void)
{
int i;
int buttons_fd;
int key_value[4];
fd_set rds;//可读集
int ret;
/*打开键盘设备文件*/
buttons_fd = open("/dev/buttons", 0);
if (buttons_fd < 0) {
perror("open device buttons");
exit(1);
}
for (;;) {
FD_ZERO(&rds);//清空集合
FD_SET(buttons_fd, &rds);//将文件描述符加入可读集
/*使用系统调用select检查是否能够从/dev/buttons设备读取数据*/
Select第一个参数为最大描述符
ret = select(buttons_fd + 1, &rds, NULL, NULL, NULL);
/*读取出错则退出程序*/
if (ret < 0) {
perror("select");
exit(1);
}
if (ret == 0) {
printf("Timeout.\n");
}
/*能够读取到数据*/
else if (FD_ISSET(buttons_fd, &rds)) {
/*开始读取键盘驱动发出的数据,注意key_value和键盘驱动中定义为一致的类型*/
int ret = read(buttons_fd, key_value, sizeof key_value);
if (ret != sizeof key_value) {
if (errno != EAGAIN)
perror("read buttons\n");
continue;
} else {
/*打印键值*/
for (i = 0;i < sizeof(key_value)/sizeof(key_value[0]); i++)
printf("K%d %s, key value = 0x%02x\n", i+1, (key_value[i] & 0x80) ? "released" : \
key_value[i] ? "pressed down" : "", \
key_value[i]);
}
}
}
close(buttons_fd);
return 0;
}
Arm平台按键实验:
[root@FriendlyARM test]# insmod mini_button.ko
buttons initialized
[root@FriendlyARM test]# cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
21 sg
29 fb
81 video4linux
89 i2c
90 mtd
116 alsa
128 ptm
136 pts
180 usb
188 ttyUSB
189 usb_device
204 s3c2410_serial
232 buttons
254 rtc
Block devices:
259 blkext
7 loop
8 sd
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
[root@FriendlyARM test]# mknod /dev/buttons c 232 0
[root@FriendlyARM test]# ./button_app
K1 , key value = 0x00
K2 , key value = 0x00
K3 pressed down, key value = 0x03
K4 , key value = 0x00
K1 , key value = 0x00
K2 , key value = 0x00
K3 released, key value = 0x83
K4 , key value = 0x00
K1 , key value = 0x00
K2 pressed down, key value = 0x02
K3 , key value = 0x00
K4 , key value = 0x00
K1 , key value = 0x00
K2 pressed down, key value = 0x02
K3 , key value = 0x00
K4 , key value = 0x00
K1 , key value = 0x00
K2 released, key value = 0x82
K3 , key value = 0x00
K4 , key value = 0x00
K1 , key value = 0x00
K2 , key value = 0x00
K3 , key value = 0x00
K4 pressed down, key value = 0x04
K1 , key value = 0x00
K2 , key value = 0x00
K3 , key value = 0x00
K4 pressed down, key value = 0x04
K1 , key value = 0x00
K2 , key value = 0x00
K3 , key value = 0x00
K4 pressed down, key value = 0x04
K1 , key value = 0x00
K2 , key value = 0x00
K3 , key value = 0x00
K4 pressed down, key value = 0x04
注意:按键有抖动,应该消抖处理