您的位置:首页 > 其它

定时器防抖动

2016-05-18 22:30 295 查看
/*功能:使用定时器防抖动

*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*/

<
9b8a
br />

/* 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()用来取得目前进程的进程识别码,许多程序利用取到的

此值来建立临时文件,以避免临时文件相同带来的问题。*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: