驱动程序:
LED分别使用GPB5/6/7/8 六个按键分别使用GPG0、3、5、7、6、11、所用中断号是8、11、13、15、14、19
/*
程序说明:字符型按键驱动,按下按键1时候1号LED亮,按2按键LED2亮,依次,按5时候所有LED都亮,按6时候闪烁两下
*/
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h> //file_operations
#include <linux/poll.h> //poll设备方法
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/cdev.h>//include in cdev cdev_init
#include <linux/device.h>
static unsigned long led_table[]={
S3C2410_GPB(5),
S3C2410_GPB(6),
S3C2410_GPB(7),
S3C2410_GPB(8),
};
static unsigned long led_set_table[]={
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
S3C2410_GPIO_OUTPUT,
};
//用于保存设备的一些属性,这里用来自动创建/dev设备文件
struct button{
struct class *btn_class;
struct device *btn_device;
wait_queue_head_t button_queue;
};
struct button xsy_button;
struct cdev button; //按键设备
#define major 250 //主设备号
bool en_button=0; //表示是否有按键按下,为1表示有按键按下
//static int data_old[6]={1,1,1,1,1,1}; //刚开始六个按键寄存器中都是1,就是高电平
//static volatile char data_old[] = {'0', '0', '0', '0', '0', '0'};
static char data_old[] = {'1', '1', '1', '1', '1', '1'};
struct button_desc{
int irq;//中断号
int pin; //引脚
int pin_setting; //引脚设置
int num; //按键编号
char *name; //按键名称
};
static struct button_desc button_irqs[6]={
{IRQ_EINT8, S3C2410_GPG(0),S3C2410_GPG0_EINT8,0,"KEY1"},
{IRQ_EINT11,S3C2410_GPG(3),S3C2410_GPG3_EINT11,1,"KEY2"},
{IRQ_EINT13,S3C2410_GPG(5),S3C2410_GPG5_EINT13,2,"KEY3"},
{IRQ_EINT15,S3C2410_GPG(7),S3C2410_GPG7_EINT15,3,"KEY4"},
{IRQ_EINT14,S3C2410_GPG(6),S3C2410_GPG6_EINT14,4,"KEY5"},
{IRQ_EINT19,S3C2410_GPG(11),S3C2410_GPG11_EINT19,5,"KEY6"},
};
//中断处理函数
static irqreturn_t button_irq(int irq,void *dev_id)
{
struct button_desc *dev=(struct button_desc*)dev_id; //保存中断传进来的设备
int data,i;
char ch;
data = !s3c2410_gpio_getpin(dev->pin); //当对应设备的中断发生,该函数中的设备就是发生中断的设备,然后去读取该设备的引脚的值,当按下按键时候该引脚值为0,多以down=1
// printk(KERN_INFO"driver read data %d/n",data);
if(data==1)
{
ch='0';
if(dev->num < 4)
s3c2410_gpio_setpin(led_table[dev->num],0);
else if(dev->num ==4)
{
for(i=0;i<4;++i)
{
s3c2410_gpio_setpin(led_table[i],0); //按下第五个按键时候,所有灯都亮
}
}
else if (dev->num==5)
{
for(i=0;i<4;i+=2)
{
s3c2410_gpio_setpin(led_table[i],0); //按下第五个按键时候,所有灯都亮
}
}
}
if(data==0)
{
ch='1';
for(i=0;i<4;++i)
{
s3c2410_gpio_setpin(led_table[i],1); //按下第五个按键时候,所有灯都亮
}
}
// printk(KERN_INFO"ch = %c/n",ch);
if(ch!=data_old[dev->num])
{
data_old[dev->num] = ch;
// printk(KERN_INFO"data_old[%d] = %c/n",dev->num,ch);
en_button = 1;
wake_up_interruptible(&xsy_button.button_queue);
}
return IRQ_RETVAL(IRQ_HANDLED);//????不明白返回值是什么类型的
}
//open函数
static int btn_open(struct inode *inode,struct file *file)
{
//打开设备文件,调用该函数,要为中断做准备
int ret=0;
int i,j;
for(i=0;i<6;++i)
{
if(button_irqs[i].irq<0)
{
continue;
}
ret=request_irq(button_irqs[i].irq,button_irq,IRQ_TYPE_EDGE_BOTH,button_irqs[i].name,(void *)&button_irqs[i]);//分别为各个按键设备申请中断函数
if(ret<0) //中断注册失败
{
//讲已经注册的中断返还给系统
for(j=0;j<=i;++j)
{
if(button_irqs[j].irq<0)
continue;
disable_irq(button_irqs[j].irq);
free_irq(button_irqs[j].irq,(void*)&button_irqs[j]);
}
break;
}
}
return ret;
}
//读函数
static int btn_read(struct file *filp,char __user *buf,size_t count,loff_t *offp)
{
int ret;
if(!en_button)//没有按键按下
{
//检测是否设置了非阻塞标示
if(filp->f_flags&O_NONBLOCK)
{
return -EAGAIN;
}
else
{
wait_event_interruptible(xsy_button.button_queue,en_button); //没数据可读,并且没有设置非阻塞表示为,阻塞进程
}
}
ret=copy_to_user(buf,(const void *)data_old,(sizeof(data_old)>count)?count:sizeof(data_old));
//copy_to_user成功返回0,失败返回1
en_button=0; //按键按下标示清除
if(ret!=0)
return -1;
else
return (sizeof(data_old)>count)?count:sizeof(data_old);
}
static unsigned int btn_poll(struct file *filp,struct poll_table_struct *wait)
{
int mask=0;
poll_wait(filp,&xsy_button.button_queue,wait);
if(en_button)
mask |= POLLIN | POLLRDNORM;
return mask;
}
//释放函数
static int btn_close(struct inode *inode,struct file *file)
{
int i;
for(i=0;i<6;++i)
{
if(button_irqs[i].irq<0)
continue;
disable_irq(button_irqs[i].irq);
free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
}
printk(KERN_INFO"char device button close!/n");
return 0;
}
static struct file_operations btn_fops={
.owner=THIS_MODULE,
.open=btn_open,
.read=btn_read,
.release=btn_close,
.poll=btn_poll,
};
static int __init button_init(void)
{
int ret,i;
dev_t btn_no=MKDEV(major,0); //创建设备号
ret=register_chrdev_region(btn_no,1,"xsy-buttons"); //注册设备号
if(ret<0)
{
printk(KERN_INFO"xsy-buttons register failed!/n");
return ret;
}
cdev_init(&button,&btn_fops);
button.owner=THIS_MODULE;
button.ops=&btn_fops; //初始化设备
//向内核注册设备
cdev_add(&button,btn_no,1);
//自动向/dev生成 设备名
xsy_button.btn_class=class_create(THIS_MODULE,"xsy-class");
if(IS_ERR(xsy_button.btn_class))
{
printk(KERN_INFO"xsy_button.btn_class create failed/n");
goto err;
}
xsy_button.btn_device=device_create(xsy_button.btn_class,NULL,btn_no,NULL,"xsy-buttons");
init_waitqueue_head(&xsy_button.button_queue); //初始化等待队列
//初始化led引脚
for(i=0;i<4;++i)
{
s3c2410_gpio_cfgpin(led_table[i],led_set_table[i]);
s3c2410_gpio_setpin(led_table[i],1);
}
return ret;
err:
unregister_chrdev_region(btn_no,1);
return -1;
}
static void __exit button_exit(void)
{
device_unregister(xsy_button.btn_device);
class_destroy(xsy_button.btn_class);
unregister_chrdev_region(MKDEV(major,0),1);
printk(KERN_INFO"char device button has 卸载!/n");
}
module_init(button_init);
module_exit(button_exit);
MODULE_AUTHOR("ECJTU CERT XIESIYUAN");
MODULE_LICENSE("GPL");
应用程序
使用select多路轮询监控
/*
程序介绍:使用select轮询的按键读取
作者:谢思源
时间;2010.7.15
*/
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
int main()
{
int fd_button;
int ret,i;
char buf[6]={'1', '1', '1', '1', '1', '1'};
char buf_read[6]={'1', '1', '1', '1', '1', '1'};
if((fd_button=open("/dev/xsy-buttons",O_RDONLY))<0)
{
perror("open /dev/xys-buttons:");
return -1;
}
fd_set fset;
FD_ZERO(&fset);
FD_SET(fd_button,&fset);
struct timeval tv;
tv.tv_sec=2000;
tv.tv_usec=0; //定时20秒
while(1)
{
ret=select(fd_button+1,&fset,NULL,NULL,&tv);
if(ret<0)
{
perror("select:");
break;
}
else if(ret==0)
{
perror("after 20 seconds , time out!no button clicked!/n");
break;
}
else
{
if(FD_ISSET(fd_button,&fset)) //发现可读
{
if(read(fd_button,buf_read,6)!=6)
{
perror("read:");
break;
}
for(i=0;i<6;++i)
{
if(buf_read[i]!=buf[i])
{
// printf("buf_read[%d] is %c/n",i,buf_read[i]);
printf("%d button clicked %s!/n",i+1,buf_read[i]=='1'?"up":"down");
buf[i]=buf_read[i]; //保存读取出来的值,可以检测到/松手和按下
}
}
}
}
}
close(fd_button);
return 0;
}