前面程序按键速度较快时出现按下打印几个相同值的情况,这个和机械的抖动有关,现在使用定时器去抖动
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
static struct class *sevendrv_class;
static struct class_device *sevendrv_class_dev;
volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;
static struct timer_list buttons_timer;/*第一步:定义一个定时器结构体buttons_timer*/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
/* 中断事件标志, 中断服务程序将它置1,seven_drv_read将它清0 */
static volatile int ev_press = 0;
static struct fasync_struct *button_async;
struct pin_desc{
unsigned int pin;
unsigned int key_val;
};
/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val;
struct pin_desc pins_desc[4] = {
{S3C2410_GPF0, 0x01},
{S3C2410_GPF2, 0x02},
{S3C2410_GPG3, 0x03},
{S3C2410_GPG11, 0x04},
};
//static atomic_t canopen = ATOMIC_INIT(1); //定义原子变量并初始化为1
static DECLARE_MUTEX(button_lock); //定义互斥锁
static struct pin_desc *irq_pd =NULL;
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
/*保存键值*/
irq_pd = (struct pin_desc *)dev_id;
/*第三步:修改定时器,消抖动,最后一次时调用超时函数
*buttons_timer_function
*修改定时器10ms,按键有抖动,而抖动时间肯定小于10ms,所以
*第一次抖动修改定时器,定时器延后抖动发生 的10ms
*第N-1次抖动修改定时器,定时器延后抖动发生 的10ms
*最后一次(第N次)抖动发生,定时器修改到抖动发生后10ms
*N次抖动后10ms,即N次的定时器超时时间10ms到后,调用定时器
*处理函数buttons_timer_function
*/
mod_timer(&buttons_timer, jiffies+HZ/100);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int seven_drv_open(struct inode *inode, struct file *file)
{
#if 0
if (!atomic_dec_and_test(&canopen))
{
atomic_inc(&canopen);
return -EBUSY;
}
#endif
if (file->f_flags & O_NONBLOCK)
{
if (down_trylock(&button_lock))
return -EBUSY;
}
else
{
/* 获取信号量 */
down(&button_lock);
}
/* 配置GPF0,2为输入引脚 */
/* 配置GPG3,11为输入引脚 */
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;
}
ssize_t seven_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
if (size != 1)
return -EINVAL;
if (file->f_flags & O_NONBLOCK)
{
if (!ev_press)
return -EAGAIN;
}
else
{
/* 如果没有按键动作, 休眠 */
wait_event_interruptible(button_waitq, ev_press);
}
/* 如果有按键动作, 返回键值 */
copy_to_user(buf, &key_val, 1);
ev_press = 0;
return 1;
}
int seven_drv_close(struct inode *inode, struct file *file)
{
//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;
}
static unsigned seven_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(file, &button_waitq, wait); // 不会立即休眠
if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
static int seven_drv_fasync (int fd, struct file *filp, int on)
{
printk("driver: seven_drv_fasync\n");
return fasync_helper (fd, filp, on, &button_async);
}
static struct file_operations sencod_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = seven_drv_open,
.read = seven_drv_read,
.release = seven_drv_close,
.poll = seven_drv_poll,
.fasync = seven_drv_fasync,
};
/*第四步:最后一次抖动的上升沿或下降沿10ms后调用buttons_timer_function*/
static void buttons_timer_function(unsigned date)
{
struct pin_desc * pindesc = irq_pd;
unsigned int pinval;
/*add_timer添加时就产生了定时器超时,所以需要判断*/
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; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
kill_fasync (&button_async, SIGIO, POLL_IN);
}
int major;
static int seven_drv_init(void)
{
/*第二步:初始化buttons_timer, 并启用定时器*/
/*buttons_timer为结构体双向链表,我只需要填充一个项,第一次将定时器加入链表,需要初始化整个链表*/
init_timer(&buttons_timer);
/*初始化定时器结构体成员function,function为定时器超时后调用的函数buttons_timer_function*/
buttons_timer.function = buttons_timer_function;
/*注册定时器并运行*/
add_timer(&buttons_timer);
major = register_chrdev(0, "seven_drv", &sencod_drv_fops);
sevendrv_class = class_create(THIS_MODULE, "seven_drv");
sevendrv_class_dev = class_device_create(sevendrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon + 1;
gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
gpgdat = gpgcon + 1;
return 0;
}
static void seven_drv_exit(void)
{
unregister_chrdev(major, "seven_drv");
class_device_unregister(sevendrv_class_dev);
class_destroy(sevendrv_class);
iounmap(gpfcon);
iounmap(gpgcon);
return 0;
}
module_init(seven_drv_init);
module_exit(seven_drv_exit);
MODULE_LICENSE("GPL");
mod_timer(&buttons_timer, jiffies+HZ/100);jiffies表示系统启动到现在的tick数,比如jiffies=50,过了一段时间jiffies=55则产生了55-50个系统定时器中断,现在HZ=100,即产生一个定时器中断,1个tick的时间产生了一个定时器中断,而tick为HZ的倒数,即1秒产生100个定时器中断,一个定时器中断为10ms
测试代码:
#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>
/* sevendrvtest
*/
int main(int argc, char **argv)
{
unsigned char key_val;
int ret,fd;
int Oflags;
//signal(SIGIO, my_signal_fun);
fd = open("/dev/buttons", O_RDWR );
if (fd < 0)
{
printf("can't open!\n");
return -1;
}
//fcntl(fd, F_SETOWN, getpid());
//Oflags = fcntl(fd, F_GETFL);
//fcntl(fd, F_SETFL, Oflags | FASYNC);
while (1)
{
ret = read(fd, &key_val, 1);
printf("key_val: 0x%x, ret = %d\n", key_val, ret);
//sleep(5);
}
测试结果:
# insmod seven_drv.ko
# lsmod
Module Size Used by Not tainted
seven_drv 4740 0
# ./sevendrvtest &
# ./sevendrvtest &
# ./sevendrvtest &
# ./sevendrvtest &
# ps
PID Uid VSZ Stat Command
1 0 3092 S init
2 0 SW< [kthreadd]
3 0 SWN [ksoftirqd/0]
4 0 SW< [watchdog/0]
5 0 SW< [events/0]
6 0 SW< [khelper]
55 0 SW< [kblockd/0]
56 0 SW< [ksuspend_usbd]
59 0 SW< [khubd]
61 0 SW< [kseriod]
73 0 SW [pdflush]
74 0 SW [pdflush]
75 0 SW< [kswapd0]
76 0 SW< [aio/0]
710 0 SW< [mtdblockd]
745 0 SW< [kmmcd]
762 0 SW< [rpciod/0]
770 0 3096 S -sh
776 0 1308 S ./sevendrvtest
777 0 1308 D ./sevendrvtest
778 0 1308 D ./sevendrvtest
779 0 1308 D ./sevendrvtest
780 0 3096 R ps
# key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
key_val: 0x1, ret = 1
key_val: 0x81, ret = 1
也可以用异步通知,驱动程序不变,测试源码改为:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
/*sevendrvtest
*/
int fd;
void my_signal_func(int signum)
{
unsigned char key_val;
// printf("signum = %d\n", signum);
read(fd, &key_val, 1);
printf("key_val = 0x%x\n", key_val);
}
int main(int argc, char **argv)
{
int O_FLAGS,ret=0;
unsigned char key_val=0;
signal( SIGIO, my_signal_func); /*安装信号处理函数*/
fd = open("/dev/buttons", O_RDWR);
if (fd < 0)
{
printf("can't open!, fd = %d\n", fd);
return -1;
}
fcntl( fd, F_SETOWN, getpid());
O_FLAGS = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, O_FLAGS|O_ASYNC);
while (1)
{
sleep(5); //屏蔽掉,因为read没有按键产生,six_drv_read会休眠
}
return 0;
}
测试结果:
# ps
PID Uid VSZ Stat Command
1 0 3092 S init
2 0 SW< [kthreadd]
3 0 SWN [ksoftirqd/0]
4 0 SW< [watchdog/0]
5 0 SW< [events/0]
6 0 SW< [khelper]
55 0 SW< [kblockd/0]
56 0 SW< [ksuspend_usbd]
59 0 SW< [khubd]
61 0 SW< [kseriod]
73 0 SW [pdflush]
74 0 SW [pdflush]
75 0 SW< [kswapd0]
76 0 SW< [aio/0]
710 0 SW< [mtdblockd]
745 0 SW< [kmmcd]
762 0 SW< [rpciod/0]
770 0 3096 S -sh
776 0 1312 S ./sevendrvtest
777 0 1308 D ./sevendrvtest
778 0 1308 D ./sevendrvtest
779 0 1308 D ./sevendrvtest
781 0 3096 R ps
# kill -9 776 777 778 779
#
[4] + Killed ./sevendrvtest
[3] + Killed ./sevendrvtest
[2] + Killed ./sevendrvtest
[1] + Killed ./sevendrvtest
#
# ./sevendrvtest &
# driver: sixth_drv_fasync
# ./sevendrvtest &
# ./sevendrvtest &
# ./sevendrvtest &
# ps
PID Uid VSZ Stat Command
1 0 3092 S init
2 0 SW< [kthreadd]
3 0 SWN [ksoftirqd/0]
4 0 SW< [watchdog/0]
5 0 SW< [events/0]
6 0 SW< [khelper]
55 0 SW< [kblockd/0]
56 0 SW< [ksuspend_usbd]
59 0 SW< [khubd]
61 0 SW< [kseriod]
73 0 SW [pdflush]
74 0 SW [pdflush]
75 0 SW< [kswapd0]
76 0 SW< [aio/0]
710 0 SW< [mtdblockd]
745 0 SW< [kmmcd]
762 0 SW< [rpciod/0]
770 0 3096 S -sh
782 0 1308 S ./sevendrvtest
783 0 1308 D ./sevendrvtest
784 0 1308 D ./sevendrvtest
785 0 1308 D ./sevendrvtest
786 0 3096 R ps
# key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x2
key_val = 0x82
key_val = 0x2
key_val = 0x82
key_val = 0x2
key_val = 0x82
key_val = 0x2
key_val = 0x82
key_val = 0x2
key_val = 0x82
key_val = 0x2
key_val = 0x82
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
key_val = 0x81
key_val = 0x1
计时5sS3C2410 Timer Tick由965663变为966585,966585-965663=922,可知大约5s ,1000此定时器中断,即HZ=200,而在本实验中在HZ上ctrl+鼠标左键,HZ=100,和内核的并不相同
# cat proc/interrupts
CPU0
16: 179 s3c-ext0 S2
18: 194 s3c-ext0 S3
30: 965663 s3c S3C2410 Timer Tick
32: 0 s3c s3c2410-lcd
33: 0 s3c s3c-mci
34: 0 s3c I2SSDI
35: 0 s3c I2SSDO
37: 12 s3c s3c-mci
42: 0 s3c ohci_hcd:usb1
43: 0 s3c s3c2440-i2c
51: 1613 s3c-ext eth0
55: 0 s3c-ext S4
60: 0 s3c-ext s3c-mci
63: 0 s3c-ext S5
70: 176 s3c-uart0 s3c2440-uart
71: 1067 s3c-uart0 s3c2440-uart
79: 20 s3c-adc s3c2410_action
80: 20332 s3c-adc s3c2410_action
83: 0 - s3c2410-wdt
Err: 0
# cat proc/interrupts
CPU0
16: 179 s3c-ext0 S2
18: 194 s3c-ext0 S3
30: 966585 s3c S3C2410 Timer Tick
32: 0 s3c s3c2410-lcd
33: 0 s3c s3c-mci
34: 0 s3c I2SSDI
35: 0 s3c I2SSDO
37: 12 s3c s3c-mci
42: 0 s3c ohci_hcd:usb1
43: 0 s3c s3c2440-i2c
51: 1623 s3c-ext eth0
55: 0 s3c-ext S4
60: 0 s3c-ext s3c-mci
63: 0 s3c-ext S5
70: 178 s3c-uart0 s3c2440-uart
71: 1104 s3c-uart0 s3c2440-uart
79: 20 s3c-adc s3c2410_action