定时器防抖动

/*功能:使用定时器防抖动
*2016年5月14日20:35:13
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>




static struct class *seventhdrv_class;
static struct class_device *seventhdrv_class_devs[4];


volatile unsigned long * gpfcon;
volatile unsigned long * gpfdat;


volatile unsigned long * gpgcon;
volatile unsigned long * gpgdat;




/*
*
确定按键值 s3c2410_gpio_getpin
*/


struct pin_desc{
unsigned int pin;
unsigned int key_val;
};


/*键值:按下时:0x01,0x02,0x03x0x04*/
/*键值:松开时:0x81,0x82,0x83,0x84*/




struct pin_desc pins_desc[4] = {
{S3C2410_GPF0,0x01},
{S3C2410_GPF2,0x01},
{S3C2410_GPG3,0x01},
{S3C2410_GPG11,0x01},


};


/*发生中断时的引脚描述*/
static struct pin_desc * irq_pd;
/*定义定时器结构体*/
static struct timer_list buttons_timer;


static unsigned char key_val;


//static int canopen = 1;  对应open函数中if方法
//atomic_t canopen = ATOMIC_INIT(1);     //定义原子变量v并初始化为0


static DECLARE_MUTEX(button_lock);     // 定义一个名称为button_lock的信号量,并将信号量初始化为1 。定义互斥锁 




static struct fasync_struct *button_async;


static DECLARE_WAIT_QUEUE_HEAD(button_waitq);


/* 中断事件标志, 中断服务程序将它置1,seventh_drv_read将它清0 */
static volatile int ev_press = 0;




static irqreturn_t buttons_irq(int irq, void *dev_id)
{
/* 记录下引脚描述 */
irq_pd = (struct pin_desc *)dev_id;
/* 10ms后启动定时器 ,定时器其实是在定时器链表中存放的,
每一个时钟中断都会检查链表中的哪个定时器时间到了,并执行相应的定时器处理函数*/
/*修改定时器的超时时间expires*/
mod_timer(&buttons_timer, jiffies+HZ/100);//HZ可以将理解为1秒
/*闹钟什么时候到,是基于jiffies这个值的,这个值是个全局变量,这个值每隔十毫秒就会有个系统时钟中断
在系统时钟中断里面,jiffies这个值就会累加 HZ是用来定义每一秒有几次timer interrupts。
Jiffies为Linux核心变数(unsigned long),它被用来记录系统自开机以来,已经过了多少tick。每发生一次
timer interrupt,Jiffies变数会被加一*/
/*
LINUX系统时钟频率是一个常数HZ来决定的,通常HZ=100,那么他的精度度就是10ms
(毫秒)。也就是说每10ms一次中断。所以一般来说Linux的精确度是10毫秒
*/

return IRQ_HANDLED;
}


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


#if  0
/*第一次执行open函数时,--canopen!=0不成立,所以执行if后面的函数
从第二次以后 --canopen!=0 都成立,所以会返回错误,即表示不能第二次打开
该方法有BUG,因为linux是多任务操作系统,在 执行--canopen != 0期间的取值时,
cpu可能会被切换出去,去执行另一个程序打开open函数后,再返回继续执行--canopen != 0
接下来的程序,结果是两个应用程序都成功打开了设备*/.

if(--canopen != 0)
{
canopen ++;
return -EBUSY;


}
#endif


/*获取信号量,如果是第一次执行open函数的话,就可以获得信号量,当第二个程序在执行open函数时,就无法获得信号量,
就会进入不可中断的休眠状态,只有第一个应用程序把button_lock这个信号量释放的时候,第二个应用程序才会被唤醒
继续执行。
down系列函数会使信号量的值减1
up会使信号量的值加1
调用down的进程不允许被中断,*/


/*对open函数传入的O_NONBLOCK标志位的处理,在file这个结构体中,这个结构是内核给我们提供的*/
if(file->f_flags & O_NONBLOCK)
{
/*如果打不开这个设备就会立刻返回错误*/
if(down_trylock(&button_lock))
return -EBUSY;


}
else
{

down(&button_lock);
}


#if 0
/*
int atomic_dec_and_test(atomic_t *v); //自减操作后测试其是否为0,为0则返回true,否则返回false。
atomic_t v = ATOMIC_INIT(0);     //定义原子变量v并初始化为0
atomic_read(atomic_t *v);        //返回原子变量的值
void atomic_inc(atomic_t *v);    //原子变量增加1
void atomic_dec(atomic_t *v);    //原子变量减少1
*/


if(!atomic_dec_and_test(&canopen))   /*被执行过一次之后,canopen变为0*/
{
atomic_inc(&canopen);
return -EBUSY;

}


#endif


/*配置GPF0,2  位输入引脚 */
/*配置GPG3,11为输入引脚 */
/*
IRQ_EINT0是在irqs.h中定义的
IRQT_BOTHEDGE是在s3c_irqext_type函数中找到,在irq.h中定义
*/


//向内核注册中断处理函数
request_irq(IRQ_EINT0,buttons_irq,IRQT_BOTHEDGE,"s2",&pins_desc[0]);
request_irq(IRQ_EINT2,buttons_irq,IRQT_BOTHEDGE,"s3",&pins_desc[1]);
request_irq(IRQ_EINT11,buttons_irq,IRQT_BOTHEDGE,"s4",&pins_desc[2]);
request_irq(IRQ_EINT19,buttons_irq,IRQT_BOTHEDGE,"s5",&pins_desc[3]);





return 0;
}










/*函数的参数是用户空间的函数的参数*/
static ssize_t seventh_drv_read(struct file *file, const __user char *buffer,size_t count, loff_t *ppos)
{
if(count != 1)
return -EINVAL;//返回错误值




if (file->f_flags & O_NONBLOCK)
{
/*判断有没有按键按下,没有按键按下的话*/
if (!ev_press)
return -EAGAIN;/*如果你连续做read操作而没有数据可读。此时程序不会阻塞起来等待数据准备就绪返回,
read函数会返回一个错误EAGAIN,提示你的应用程序现在没有数据可读请稍后再试,再次执行*/
}
else
{

/*如果没有按键按下:进入休眠(让出CPU,不返回)*/
wait_event_interruptible(button_waitq,ev_press);

}





/*如果休眠的程序被唤醒,则会从wait_event_interruptible继续往下执行*/




/*如果有按键动作发生,则直接返回键值*/
copy_to_user(buffer ,&key_val,1);
ev_press = 0;
return 1;





}






static void buttons_timer_function(unsigned long data)
{
/*利用设备号来传递引脚号和值*/
struct pin_desc * pindesc = irq_pd;
unsigned int pinval ;
if(!pindesc)
return;

pinval = s3c2410_gpio_getpin(pindesc->pin);//系统提供的读取引脚状态的函数


if(pinval)
{
/*按键松开*/
key_val = 0x80 | pindesc->key_val;

}
else
{
/*按键按下*/

key_val = pindesc->key_val;


}


ev_press = 1;                  /* 表示中断发生了 */


/*由kill_fasync函数发出SIGIO信号,发给谁,是在button_async这个结构中定义的
button_async这个结构要在seventh_drv_fasync函数中进行初始化
在内核里面一般向应用程序发出的是SIGIO信号,表示有数据可供读写*/
*/
kill_fasync(&button_async, SIGIO, POLL_IN);

    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */


}


/*入口函数*/
int major;
static int seventh_drv_init (void)
{
/*初始化定时器,没有设置超时时间,则超时时间就是0*/
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
/*告诉内核,启动定时器,当定时器的超时时间一到就会调用buttons_timer_function*/
/*当启动定时器到达第一个时钟中断时,jiffies>=0,则会立刻调用定时器处理函数,此时还没有按键产生,则要在
定时器处理函数中判断一下是否有中断*/
add_timer(&buttons_timer);

major = register_chrdev(0,"secod_drv",&seventh_drv_fops);


seventhdrv_class = class_create(THIS_MODULE, "secod_drv");//创建个类

seventhdrv_class_devs = class_device_create(seventhdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */



gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
gpfdat = gpfcon + 1;//指向0x56000054


gpgcon = (volatile unsigned long *)ioremap(0x56000060,16);
gpgdat = gpgcon + 1;//指向0x56000064


return 0;


}






/*出口函数*/
int major;
static int seventh_drv_exit (void)
{
unregister_chrdev(major,"secod_drv");

class_device_unregister(seventhdrv_class_devs);
 
class_destroy(seventhdrv_class);



iounmap(gpfcon);
iounmap(gpgcon);
return 0;


}


int seventh_drv_close (struct inode * inode, struct file * file)
{
/* canopen ++;是配合只能一次打开open函数?当open函数真正被打开后,
canopen的值为0,此时不能在被任何应用程序打开,当打开的open函数被关闭时canopen
的值变为1,以便下次再被应用程序打开*/


//canopen ++;  对应于open函数中的if语句


//atomic_inc(&canopen);
free_irq(IRQ_EINT0,&pins_desc[0]);
free_irq(IRQ_EINT2,&pins_desc[1]);
free_irq(IRQ_EINT11,&pins_desc[2]);
free_irq(IRQ_EINT19,&pins_desc[3]);
up(&button_lock);/*第一个程序运行完后,释放掉信号量*/
return 0;


}




/*如果seventh_drv_poll函数返回的是0,则当前进程会进入休眠状态,直到超时被唤醒,返回到用户态,
用户态的poll函数返回0,或者是seventh_drv_poll函数返回非0,返回到用户态*/
static unsigned int seventh_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
/*poll_wait函数是将当前进程挂到button_waitq队列中,不会使当前进程立即进入休眠
使当前进程进入休眠是在其他的内核中的函数中实现的*/
poll_wait(file, &button_waitq, wait); // 不会立即休眠


if (ev_press)
mask |= POLLIN | POLLRDNORM;


return mask;


}
f_owner
static int seventh_drv_fasync (int fd, struct file *filp, int on)
{
printk("driver: seventh_drv_fasync\n");
/*
fasync_helper函数初始化button_async这个结构体,初始化后,
kill_fasync(&button_async, SIGIO, POLL_IN);这个函数就能给应用程序发送信号了


*/
return fasync_helper (fd, filp, on, &button_async);
}




static struct file_operations seventh_drv_fops = {
    .owner   =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open    =   seventh_drv_open,    //表示将first_drv_open函数的地址赋给file_operations结构体中open函数,
.read = seventh_drv_read, //以后执行 first_drv_open函数时会自动调用file_operations结构体中的open函数
.release =  seventh_drv_close,//关闭设备的时候,释放open函数中注册中断处理函数的irq
.poll    =  seventh_drv_poll,
.fasync  =  seventh_drv_fasync,
};


/*修饰*/
modules_init(seventh_drv_init);
modules_exit(seventh_drv_exit);


MODULE_LICENSE("GPL");



//测试函数



/*功能:使用定时器防抖动
2016年5月14日20:35:35*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>




/*
阻塞操作
是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作
被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足
非阻塞操作
进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作.
*/




/* sixthdrvtest 
  */
int fd;


void my_signal_fun(int signum)
{
unsigned char key_val;
read(fd, &key_val, 1);
printf("key_val: 0x%x\n", key_val);
}


int main(int argc, char **argv)
{
unsigned char key_val;
int ret;
int Oflags;


/*使用signal系统调用给SIGIO这个信号挂接处理函数
在内核里面一般向应用程序发出的是SIGIO信号*/
signal(SIGIO, my_signal_fun);


/*默认为非阻塞 */
fd = open("/dev/buttons", O_RDWR );
if (fd < 0)
{
printf("can't open!\n");
return -1;
}

#if 0


/*buttons这个设备就是驱动程序*/
/*F_GETFL :读取文件状态标志。*/
/*可以用fcntl 函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志*/
/*F_SETFL 将文件状态标志设置为第三个参数的值 (取为整型值)。*/
/* fcntl的F_SETOWN指令设置当前进程为设备文件owner*/




/* fcntl(fd, F_SETOWN, getpid());告诉驱动程序当前PID进程号
*/
fcntl(fd, F_SETOWN, getpid());


/*读取fd的文件状态标志位*/
Oflags = fcntl(fd, F_GETFL); 


/* 
设置fd的文件状态标志位为Oflags | FASYNC  即异步通知
调用fcntl(fd, F_SETFL, Oflags | FASYNC);函数时,会进入驱动程序的file_operations结构体执行fasync函数


*/


/*
每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。
驱动中应该实现fasync()函数。


*/
fcntl(fd, F_SETFL, Oflags | FASYNC);


#endif


while (1)
{
/*判断返回值,非阻塞的话一读就会返回一个值,*/
ret = read(fd, &key_val, 1);
printf("key_val: 0x%x, ret = %d\n", key_val, ret);
}

return 0;
}




/*
fcnt1函数返回值说明
返回说明:   
成功执行时,对于不同的操作,有不同的返回值 
F_DUPFD: 新文件描述词 
F_GETFD:  标志值 
F_GETFL:  标志值 
F_GETOWN: 文件描述词属主 
F_GETSIG: 读写变得可行时将要发送的通告信号,或者0对于传统的SIGIO行为 


*/


/*
getpid()用来取得目前进程的进程识别码,许多程序利用取到的
此值来建立临时文件,以避免临时文件相同带来的问题。*/




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,使用定时器也是一种实现按键长按短按双击的方法。 具体实现方法可以参考以下步骤: 1. 初始化定时器。选择合适的定时器,并设置定时器的时钟源、计数模式、计数周期等参数。一般推荐使用定时器的计数模式为向上计数,并且设置一个合适的计数周期。 2. 初始化按键。选择合适的引脚作为按键输入,配置引脚的输入模式和上拉/下拉电阻。一般推荐使用上拉电阻,使按键默认为高电平。 3. 在定时器中断服务函数中实现按键状态的检测和处理。在定时器中断服务函数中,读取按键状态,并根据当前状态和前一次状态的变化,判断按键事件的类型。如果按键被按下,则记录按下时间;如果按键被释放,则记录释放时间,并根据时间间隔判断按键事件的类型。 4. 根据按键事件的类型,执行相应的操作。例如,长按事件可以用于开启或关闭某个功能;短按事件可以用于切换不同的模式;双击事件可以用于执行快速操作。 注意事项: 1. 在定时器中断服务函数中,需要注意防抖处理。对于按键输入信号,由于存在抖动现象,因此需要使用软件或硬件方式进行防抖处理,以确保检测到的按键状态是稳定的。 2. 在定时器中断服务函数中,需要注意按键状态的检测间隔。检测间隔过短会导致系统负载过高,检测间隔过长会影响按键检测的灵敏度,因此需要选择一个合适的检测间隔。 3. 在定时器中断服务函数中,需要注意定时器的溢出问题。如果定时器的计数周期比较短,容易出现定时器溢出的情况,需要进行相应的处理。 总之,使用定时器实现按键长按短按双击需要结合具体的硬件平台和软件环境进行综合考虑,根据实际需求选择合适的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值