驱动程序:

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;
}