Chinaunix首页 | 论坛 | 博客
  • 博客访问: 686002
  • 博文数量: 152
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1793
  • 用 户 组: 普通用户
  • 注册时间: 2013-09-12 12:26
个人简介

相信自己,只有不想做的,没有做不到的。

文章分类

全部博文(152)

文章存档

2021年(1)

2015年(2)

2014年(74)

2013年(75)

分类: LINUX

2013-12-18 23:33:48

要实现开发板的按键驱动,就要了解按键,当你按下的时候有时候会按一下,结果打印出几次结果,感觉就像是按了好多下,这是按钮的结果造成的,因为当你按下按键的时候会有齿波产生,也叫抖动,如何才能让开发板的按键,我们按一下只打印出一次结果,去抖呢? 做法很多,在这里来讲解一下使用内核定时器的机制来去除抖动。




s5pc100_button.c中
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include /*包括timer.h头文件*/


//#define  GPH0_1  0x

MODULE_LICENSE ("GPL");


int  buttons_major   =  250;
int  buttons_minor  =  0;
int  number_of_devices = 0;
struct cdev cdev;
dev_t dev = 0;


//按键的




//等待队列定义
wait_queue_head_t readq;
static int flags = 0; 
int n = 0;


//异步信号定义
struct fasync_struct *buttons_fasync_struct;
struct timer_list s_timer; /*设备要使用的定时器*/




static char pin_desc[6] = {1,2,3,4,6,7};


static unsigned char key_vals[6] = {1,2,3,4,5,6}; 






static int s5pc100_buttons_open(struct inode *inode, struct file *file)
{
return 0;
}




static int s5pc100_buttons_release(struct inode *inode, struct file *file)
{


return 0; 
}




//注册异步信号函数
static int s5pc100_buttons_fasync (int fd, struct file *file, int on)
{
return fasync_helper(fd, file, on, &buttons_fasync_struct);
}


//等待wake_up_interruptible(&readq)来唤醒wait_event_interruptible,唤醒后,当使用copy_to_user()的时候,发给应用程序的数据放在打开这个设备的文件描述符fd中,
就是通过kill_sysnc的SIGNIO告诉应用程序开始读数据,其实时候应用程序在非阻塞的状态下运行....这样就形成了异步通信机制,可以很大程度上的节约了cpu的开销

ssize_t s5pc100_buttons_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
if (count != 1)
return -EINVAL;


//等待被唤醒
wait_event_interruptible(readq,flags != 0);


if(copy_to_user(buf,key_vals+n,1))
return -EFAULT;


// printk(KERN_WARNING"key_vals = %d\n",key_vals[n]);
flags = 0;
return 1;




}



//定时器时间到后会把赋值的中断号判断是哪个中断事件,然后再唤醒读,kill_fasync的SIGIO告诉应用程序可以开始读取数据了
static void time_handler(unsigned long arg)
{
    switch(s_timer.data)
    {
    case IRQ_EINT(1):
        n = 0;
        break;
    case IRQ_EINT(2):
        n = 1;
        break;
    case IRQ_EINT(3):
        n = 2;
        break;
    case IRQ_EINT(4):
        n = 3;
        break;
    case IRQ_EINT(6):
        n = 4;
        break;
    case IRQ_EINT(7):
        n = 5;
    }


    //唤醒读
    flags = 1;
    wake_up_interruptible(&readq);


//判断是否有信号被定义, 有这读操作被唤醒,KILL——FASYNC 给应用层发送数据
if(buttons_fasync_struct)
kill_fasync(&buttons_fasync_struct, SIGIO, POLL_IN);


}


//当发生按键中断时,会调用中断处理函数,在中断处理函数中,给定时器赋值,在给开启定时器,时间到会唤起定时器注册函数time_handler()
static irqreturn_t my_handler(int irq, void *dev_id)
{
//给定时器初始化
s_timer.data = irq;
mod_timer(&s_timer,jiffies+5);
return IRQ_HANDLED; 
}

//字符设备操作符
struct file_operations buttons_fops = {
  .owner = THIS_MODULE,
  .open  = s5pc100_buttons_open,
  .release = s5pc100_buttons_release,
  .read = s5pc100_buttons_read,
 
//异步信号机制
.fasync = s5pc100_buttons_fasync,
};
 

//设备初始化函数
static void char_reg_setup_cdev(void)
{
int error,devno;
devno = MKDEV(buttons_major,buttons_minor); //获得设备号


cdev_init(&cdev,&buttons_fops);//初始化字符设备,获得设备操作符
cdev.owner = THIS_MODULE;
cdev.ops = &buttons_fops;
error = cdev_add(&cdev,devno,1); //加载字符设备


 if (error)
    printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);
}


static int __init buttons_init (void)
{
  int result;


  dev = MKDEV (buttons_major, buttons_minor); //获得设备号
  result = register_chrdev_region (dev, number_of_devices, "buttons");  //注册设备


  if (result<0) {
    printk (KERN_WARNING "hello: can't get major number %d\n", buttons_major);
    return result;
  }

  init_timer(&s_timer);  //初始化定时器
  s_timer.function = time_handler;  //注册定时器函数
  //激活定时器
  add_timer(&s_timer);


  char_reg_setup_cdev(); //初始化字符设备


//等待队列初始化
init_waitqueue_head(&readq);


//注册中断
 if( request_irq(IRQ_EINT(1),my_handler,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"KEY1",&pin_desc[0]))
printk(KERN_WARNING"error request_irq DMA irq\n");


 if( request_irq(IRQ_EINT(2),my_handler,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"KEY2",&pin_desc[1]))
printk(KERN_WARNING"error request_irq DMA irq\n");


 if( request_irq(IRQ_EINT(3),my_handler,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"KEY3",&pin_desc[2]))
printk(KERN_WARNING"error request_irq DMA irq\n");


  if( request_irq(IRQ_EINT(4),my_handler,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"KEY4",&pin_desc[3]))
printk(KERN_WARNING"error request_irq DMA irq\n");


 if( request_irq(IRQ_EINT(6),my_handler,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"KEY5",&pin_desc[4]))
printk(KERN_WARNING"error request_irq DMA irq\n");


 if( request_irq(IRQ_EINT(7),my_handler,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"KEY6",&pin_desc[5]))
printk(KERN_WARNING"error request_irq DMA irq\n");


  return 0;
}


static void __exit buttons_exit (void)
{
  dev_t devno = MKDEV (buttons_major, buttons_minor);
  free_irq(IRQ_EINT(1),&pin_desc[0]);
  free_irq(IRQ_EINT(2),&pin_desc[1]);
  free_irq(IRQ_EINT(3),&pin_desc[2]);
  free_irq(IRQ_EINT(4),&pin_desc[3]);
  free_irq(IRQ_EINT(6),&pin_desc[4]);
  free_irq(IRQ_EINT(7),&pin_desc[5]);
  cdev_del (&cdev);
  unregister_chrdev_region (devno, number_of_devices);

}

module_init (buttons_init);
module_exit (buttons_exit);



测试程序test.c
#include
#include
#include
#include
#include
#include
#include
#include

int key = 0;
int i = 0;
int fd;

#if 1
void sig_handler(int signo)
{
if(read(fd,&key,1) < 0) 
perror("open");


// printf("asd\n");
// printf("signo = %d ", signo);

printf("key = %d i = %d \n", key,++i);
}
#endif
int main(int argc, const char *argv[])
{
int key = 0;
int oflags;


fd = open("/dev/buttons",O_RDWR);
if(fd < 0)
{
perror("open");
return 0;
}
#if 1
printf("asigio\n");
signal(SIGIO, sig_handler);


//异步信号通知
fcntl(fd, F_SETOWN, getpid());
oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags|FASYNC);
#endif


while(1)
{
sleep(1000000);
#if 0
if(read(fd,&key,1) < 0) 
perror("open");
printf("key    =  %d\n",key);
#endif
}
close(fd);
return 0;
}

程序写到这里,为什么用定时器可以去抖动呢?
当按键发生中断是,之前的效果是可能发生调用2次或多次中断函数,但是现在让你按下后,第一次调用中断函数,开启定时器,第一个定时器为超时,第二个中断函数来了,这样就开启定时器,就这样,当你按下的时候回调用多次中断函数,但是没次调用中断函数都会开启定时器,所以只有当按下按键后的最后一个中断函数调用的定时器会超时,在会调用定时器函数,这样就可以读取时哪个按键发来的数据,唤醒读,异步机制告诉应用程序,数据已准备好,应用程序调用异步信号的机制非阻塞的方式捕捉SIGIO的到来,在调用信号处理函数,在信号处理函数中read(fd,buf,1);  对于不用的开发板的按键的结构不一样,要适当的设置定时器超时时间mod_timer(&s_timer,jiffies+5);

这样就可以很好的去除按键的抖动,这也是底层使用最多的一种去抖方法。


阅读(1090) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~