四个按键分别接在GPN11, GPL8, GPL9, GPL10,本驱动对应的测试程序test.c可以分别检测这四个按钮的按下和释放状态。
本驱动改写自友善官方驱动,仅供学习参考之用。
驱动程序源码:
/**********************************************
基于Tiny6410 + linux-2.6.38 的按键中断驱动程序。
**********************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>
#define DEVICE_NAME "buttons"
struct button_irq_desc {
int irq;
int number;
char *name;
};
static struct button_irq_desc button_irqs [] = {
{IRQ_EINT(11), 0, "KEY0"}, //IRQ_EINT(11) 对应的管脚是 GPN11
{IRQ_EINT(16), 1, "KEY1"}, //IRQ_EINT(16) 对应的管脚是 GPL8
{IRQ_EINT(17), 2, "KEY2"}, //IRQ_EINT(17) 对应的管脚是 GPL9
{IRQ_EINT(18), 3, "KEY3"} //IRQ_EINT(18) 对应的管脚是 GPL10
};
static volatile char key_values [] = {'r', 'r', 'r', 'r'}; //初始状态都是released状态'r',当有按键按下时变为pressed状态'p'。
/*****************************************************
DECLARE_WAIT_QUEUE_HEAD(name)是一个与等待队列有关的宏
展开为:
wait_queue_head_t name={
.lock=__SPIN_LOCK_UNILOCKED(name.lock),
.task_list={&(name), task_list, &(name).task_list}
}
其中wait_queue_head_t的定义为:
typedef struct __wait_queue_head wait_queue_head_t;
struct __wait_queue_head{
spinlock_t lock;
struct list_head task_list;
};
仅供参考!
******************************************************/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int action_flag = 0; //事件监测,用于在read操作中判断是否有按键按下
static irqreturn_t buttons_interrupt(int irq, void *dev_id) //中断处理函数,结构是固定的。
{
struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
int down;
int number;
unsigned tmp;
unsigned excp=0;
number = button_irqs->number;
switch(number) {
case 0:
tmp = readl(S3C64XX_GPNDAT);
down = (tmp & (1 << 11)); //GPN11
break;
case 1:
tmp = readl(S3C64XX_GPLDAT);
down = (tmp & (1 << 8)); //GPL8
break;
case 2:
tmp = readl(S3C64XX_GPLDAT);
down = (tmp & (1 << 9)); //GPL9
break;
case 3:
tmp = readl(S3C64XX_GPLDAT);
down = (tmp & (1 << 10)); //GPL10
break;
default:
excp=1;
down = 0;
}
if(excp==0){ //此处主要是为了防止其他不可预测的误操作。
if(down==0){
key_values[number]='p';
}else{
key_values[number]='r';
}
action_flag = 1;
wake_up_interruptible(&button_waitq);
}
return IRQ_RETVAL(IRQ_HANDLED);
}
static int s3c64xx_buttons_open(struct inode *inode, struct file *file)
{
int i;
int err = 0;
for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
if (button_irqs[i].irq < 0) {
continue;
}
/***********************************************************************************************************************************
Linux中中断的申请
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);
其中:
irq表示要申请的中断号;
handler表示中断服务程序入口地址;
flags表示中断处理的属性,控制上升沿触发、下降沿触发什么的;
name是设备描述,表示那一个设备在使用这个中断;
dev一般是指向设备数据结构的指针,在调用中断处理程序的时候它就会传递给中断处理程序的void *dev,如果中断没有被共享,dev可以设置为NULL。
*************************************************************************************************************************************/
err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
button_irqs[i].name, (void *)&button_irqs[i]);
if (err)
break;
}
if (err) {
i--;
for (; i >= 0; i--) {
if (button_irqs[i].irq < 0) {
continue;
}
disable_irq(button_irqs[i].irq);
free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
}
return -EBUSY;
}
for(i=0; i<sizeof(key_values)/sizeof(key_values[0]); i++)
{
key_values[i]='r';
}
action_flag = 0;
return 0;
}
static int s3c64xx_buttons_close(struct inode *inode, struct file *file)
{
int i;
for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
if (button_irqs[i].irq < 0) {
continue;
}
/*****************************************
释放中断:
void free_irq(unsigned int irq, void *dev_id);
******************************************/
free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
}
return 0;
}
static int s3c64xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
unsigned long err;
if (!action_flag) {
if (filp->f_flags & O_NONBLOCK) //判断设备文件是否以非阻塞模式打开,若是,则马上返回
return -EAGAIN;
else
wait_event_interruptible(button_waitq, action_flag); //等待中断发生。
}
action_flag = 0;
err = copy_to_user((void *)buff, (const void *)(&key_values), min(sizeof(key_values), count));
return err ? -EFAULT : min(sizeof(key_values), count);
}
static unsigned int s3c64xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait);
if (action_flag)
mask |= POLLIN | POLLRDNORM;
return mask;
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.open = s3c64xx_buttons_open,
.release = s3c64xx_buttons_close,
.read = s3c64xx_buttons_read,
.poll = s3c64xx_buttons_poll,
};
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
static int __init dev_init(void)
{
int ret;
/******************************************************************************
(misc device)杂项设备也是在嵌入式系统中用得比较多的一种设备驱动。在Linux内核的
include/linux目录下有Miscdevice.h文件,要把自己定义的misc device从设备定义在这里。
其实是因为这些字符设备不符合预先确定的字符设备的范畴,所有这些设备采用主编号10,
一起归于misc device, 其实misc_register就是主编号10调用register_chrdev()的。
也就是说,misc设备其实也就是特殊的字符设备,可自动生成设备节点。
*******************************************************************************/
ret = misc_register(&misc); //系统会自动在/dev/下生成buttons设备文件
printk (DEVICE_NAME"\tinitialized\n");
return ret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc);
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FlyCloud Inc.");
驱动程序Makefile文件:
obj-m :=buttons.o
KERNELDIR :=/Tiny6410/kernel/linux-2.6.38/
PWD :=$(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *.ko *.mod.* *.symvers
.PHONY: all clean
测试程序源码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#define DEVICE_NAME "/dev/buttons"
int main(int argc, char* argv[])
{
int fd;
fd = open(DEVICE_NAME, O_RDWR);
if(fd < 0)
{
printf("Open %s failed!\n", DEVICE_NAME);
exit(-1);
}
unsigned char key_values[4];
unsigned char history_values[4];
int i=0;
for(i=0;i<4;i++){
history_values[i]='r';
}
for( ; ; )
{
int i=read(fd, key_values, sizeof(key_values));
if(i!=-1){
for(i=0;i<4;i++){
if(history_values[i]=='r' && key_values[i]=='p'){
printf("按键 %d 被按下!\n", i);
}
if(history_values[i]=='p' && key_values[i]=='r'){
printf("按键 %d 被释放!\n", i);
}
history_values[i]=key_values[i];
}
}
}
close(fd);
return 0;
}
测试效果截图:
(------------仅供参考,仅供参考-----------)