Chinaunix首页 | 论坛 | 博客
  • 博客访问: 265123
  • 博文数量: 25
  • 博客积分: 329
  • 博客等级: 一等列兵
  • 技术积分: 1380
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-24 09:43
文章分类

全部博文(25)

文章存档

2014年(4)

2013年(12)

2012年(9)

分类: LINUX

2012-12-29 16:25:57

buttons.c
/********************************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int major;
static struct timer_list  button_timer;  // 定义定时器链表,作用是延时消抖
static struct pins_desc * pin_pd = NULL;
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);  // 定义并初始化等待队列
static volatile int ev_press = 0;
static struct class *bottons_class;
static unsigned int keynum;
struct pins_desc {       // 定义一个引脚描述的结构
 int irq;         // 中断号
 int pin;         // 引脚
 int keyval;      // 定义键值
 char *name;      // 名字
 
};
static struct pins_desc pins_desc[6] = {
 {IRQ_EINT8,   S3C2410_GPG(0),   1, "S1"},
 {IRQ_EINT11, S3C2410_GPG(3),  2, "S2"},
 {IRQ_EINT13, S3C2410_GPG(5),  3, "S3"},
 {IRQ_EINT14, S3C2410_GPG(6),  4, "S4"},
 {IRQ_EINT15, S3C2410_GPG(7),  5, "S5"},
 {IRQ_EINT19, S3C2410_GPG(11), 6, "S6"},
};
// 当按键按下时,产生中断,进入中断函数,在中断函数中设置定时器的超时时间。
static irqreturn_t bottons_handler(int irq, void *dev_id)
{
 pin_pd = (struct pins_desc *)dev_id;
 // 设置定时器的超时时间为10ms,用于按键的消抖
 mod_timer(&button_timer, jiffies + HZ/100);
 
 return IRQ_RETVAL(IRQ_HANDLED);
}
// 当定时器超时后,调用该函数
static void buttons_fun(unsigned long data)
{
 struct pins_desc * desc =pin_pd;
 int key;
 // 当添加后定时器第一次超时后,调用该超时函数,但是此时,按键并未按下,并未产生中断,排除该情况
 if(!pin_pd)
  return;
 
 // 获取引脚的值,判断是上升沿,还是下降沿
 key = s3c2410_gpio_getpin(desc->pin);
 if(key)
  keynum = 0x80 | desc->keyval;
 else
  keynum =  desc->keyval;
  
 // 当有按键按下时,唤醒睡眠状态为TASK_INTERRUPTIBLE的进程,实现按下一次,想用户空间发一次数据。
 wake_up_interruptible(&button_waitq);
 
 ev_press = 1;
}
static int bottons_open(struct inode *inode, struct file *filp)

 
 int i;
 /* 注册中断 */
 for(i = 0; i < 6; i++)
  request_irq(pins_desc[i].irq, bottons_handler, IRQ_TYPE_EDGE_BOTH, pins_desc[i].name, &pins_desc[i]);
  //                终端号       中断处理函数        中断触发方式         中断的名字    传入中断处理函数的参数(使用该参数可以区分共享中断的)
 return 0; 
}
// 用户空间使用read系统调用时,调用驱动中的该函数
static ssize_t bottons_read(struct file *filep, char __user *buf, size_t size, loff_t *opps)
{
 
 // 当ev_press为真时,立刻返回,否则进程进入TASK_INTERRUPTIBLE的模式睡眠,并挂载指定的等待队列上
 wait_event_interruptible(button_waitq, ev_press);
 
 // 设置ev_press,使进程进入下一次阻塞
 ev_press = 0;
 
 // 将数据返回到用户空间
 copy_to_user(buf, &keynum, 1);
 
 return 1;
}
// 当用户空间使用poll或者select系统调用时,调用驱动的该函数
static unsigned int bottons_poll(struct file *file, struct poll_table_struct *wait)
{
 int mask = 0;
 
 // 将进程添加到等待队列上,
 poll_wait( file, &button_waitq, wait);
 // 如果有数据可读,返回掩码
 if(ev_press)
   mask |= POLLIN | POLLRDNORM;
 
 return mask;
}
// 函数操作集结果体
static struct file_operations bottons_fops= {
 .owner = THIS_MODULE,
 .open   = bottons_open,
 .read   = bottons_read,
 .poll   = bottons_poll,
};
static __init int bottons_init(void)
{
 /*
  * 注册字符设备驱动,register_chrdev函数的第一个参数为零时,表示由系统自动分配主设备号,
  * 分配的主设备好通过函数的返回值返回给major变量,
  * 当第一个参数不为零的是,表示指定主设备号
  */
 major = register_chrdev(0, "bottons", &bottons_fops); 
 
 // 创建一个类
 bottons_class = class_create(THIS_MODULE, "KEY");
 // 在类下面创建一个设备,通过busybox的mdev机制在/dev目录下创建/dev/bottons的设备节点 
 device_create(bottons_class, NULL,  MKDEV(major, 0), NULL, "bottons");
 // 初始化定时器
 init_timer(&button_timer);
 // 设置定时器的超时函数
 button_timer.function = buttons_fun;
 // 向内核注册定时器
 add_timer(&button_timer);
 
 return 0;
}
 
static __exit void bottons_exit(void)
{
 int i;
 // 释放中断
 for(i = 0; i < 6; i++)
  free_irq(pins_desc[i].irq, &pins_desc[i]);
 // 注销字符设备驱动
 unregister_chrdev(MKDEV(major, 0),"bottons");
 // 删除设备文件
 device_destroy(bottons_class, MKDEV(major, 0));
 // 删除类
 class_destroy(bottons_class);
}

module_init(bottons_init);
module_exit(bottons_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("");
 
/********************************************************************************************/
 
 
buttonstest_poll.c
/********************************************************************************************/
#include
#include
#include
#include
#include

int main(int argc,char **argv)
{
 int fd;
 int res = 0;
 
 unsigned int  keynum = 0;
 
 struct pollfd fds[1];
 
 
 fd = open("/dev/bottons",O_RDWR);
 if(fd<0)
 {
  printf("cannot open !\n");
  return -1;
 }
 fds[0].fd        = fd;
 fds[0].events = POLLIN; // 有数据可读
 while(1)
 {
  // int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  res = poll(fds, 1, 5000); 
  if( 0 == res)
   printf("time out!\n");
  else
  {
   read(fd, &keynum, 1);
   printf("keyval = 0x%x\n", keynum);
  }
 }
    
 close(fd);
  
 return 0;
}
/********************************************************************************************/
 
buttons_select.c
/********************************************************************************************/
 
#include
#include
#include
#include
#include

int main(int argc,char **argv)
{
 int fd;
 int res = 0;
 unsigned int  keynum = 0;
 fd_set set;
 
 fd = open("/dev/bottons",O_RDWR);
 if(fd<0)
 {
  printf("cannot open !\n");
  return -1;
 }
 
 static struct timeval time = {
 .tv_sec = 5,
 .tv_usec = 0,
 };
 
 while(1)
 {
  
  /*
  * int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
                  参数:
                   nfds:    文件描述符的范围,比检测的最大文件描述符大1
                   readfds: 被读监控的文件描述符集
                   writefds:被写监控的文件描述符集
                   exceptfds:被异常监控的文件描述符集
                   timeout:取不同的值,该调用有不同的表示
                    timeout值为0,不管是否有文件满足要求,都立刻返回,无文件满足要求返回0,有文件满足要求返回一个正值
                    timeout值为NULL,select将阻塞进程,直到文件满足要求
                    timeout值为正整数,就是等待的最长时间,既select在timeout时间内阻塞进程
                   
           返回值:
   Select调用返回时,返回值有如下情况:
   1.正常情况下返回满足要求的文件描述符个数;
   2.经过了timeout等待后仍无文件满足要求,返回值为0;
   3.如果select被某个信号中断,它将返回-1并设置errno为EINTR。
   4.如果出错,返回-1并设置相应的errno
    使用方法:
  1.将要监控的文件添加到文件描述符集
  2.调用Select开始监控
  3.判断文件是否发生变化
  系统提供了4个宏对描述符集进行操作:
  #include
  void FD_SET(int fd, fd_set *fdset)
  void FD_CLR(int fd, fd_set *fdset)
  void FD_ZERO(fd_set *fdset)
  void FD_ISSET(int fd, fd_set *fdset)
  宏FD_SET将文件描述符fd添加到文件描述符集fdset中;
  宏FD_CLR从文件描述符集fdset中清除文件描述符fd;
  宏FD_ZERO清空文件描述符集fdset;
  在调用select后使用FD_ISSET来检测文件描述符集fdset中的文件
  fd发生了变化。
  
  例子:
  FD_ZERO(&fds); //清空集合
  FD_SET(fd1,&fds); //设置描述符
  FD_SET(fd2,&fds); //设置描述符
  maxfdp=fd1+1;//描述符最大值加1,假设fd1>fd2
  switch(select(maxfdp,&fds,NULL,NULL,&timeout))
  case -1: exit(-1);break; //select错误,退出程序
  case 0:break;
  default:
  if(FD_ISSET(fd1,&fds)) //测试fd1是否可读
  */
  
  FD_SET(fd, &set);
  res = select(fd+1, &set, NULL, NULL, &time);
  if( 0 == res)
   printf("time out!\n");
  else
  {
   read(fd, &keynum, 1);
   printf("keyval = 0x%x\n", keynum);
  }
  
  time.tv_sec = 5; //必须从新设置超时时间
 }
    
 close(fd);
  
 return 0;
}
 
/********************************************************************************************/
 
 
 
阅读(2004) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~