文章对应视频的第12课,第5、6、7、8节。

在这之前还有查询方式的驱动编写,中断方式的驱动编写,这篇文章中暂时没有这些类容。但这篇文章是以这些为基础写的,前面的内容有空补上。

按键驱动——按下按键,打印键值:

目录

  1. 概要
  2. poll机制
  3. 异步通知
  4. 同步互斥阻塞
  5. 定时器防抖

概要:

查询方式: 12-3
缺点:占用CPU99%的资源。
中断方式:12-4
缺点:调用read函数后如果没有按键按下,该函数永远不会结束,一直在等待按键按下。
优点:使用到了休眠机制,占用cpu资源极少。
poll机制: 12-5
优点:可以设置超时时间,来结束read函数。
缺点:需要主动去调用read函数来获取按键值。
同步互斥阻塞:12-7
优点:引入了原子操作、信号量,使得按键驱动同一时间只能有一个应用使用
异步通知:12-6
优点:当有按键按下时,可以主动通知应用程序,这样就不不需要应用程序主动调用read函数来获取按键值。
定时器防抖: 12-8
引入了系统中断xxx,每隔10ms xxx变量将自动加1。

poll机制:

目标:测试程序调用poll,如果5秒内有按键按下,则返回键值,如果没有按键按下则退出程序。
/********/
poll机制:
步骤:
/********/
一、将third全部替换成tourth。
二、添加poll机制需要的头文件:
#include <linux/poll.h>
三、在file_operations结构中增加poll函数:
.poll = forth_drv_poll,
四、编写poll函数:
static unsigned forth_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;
}
完整测试程序请看下面:polldrv

/**************************************************************/
/**** polldrv ***************************************/
/**************************************************************/
#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 *forthdrv_class;
static struct class_device *forthdrv_class_dev; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中断事件标志, 中断服务程序将它置1,forth_drv_read将它清0 */
static volatile int ev_press = ; 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[] = {
{S3C2410_GPF0, 0x01},
{S3C2410_GPF2, 0x02},
{S3C2410_GPG3, 0x03},
{S3C2410_GPG11, 0x04},
}; /*
* 确定按键值
*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
struct pin_desc * pindesc = (struct pin_desc *)dev_id;
unsigned int pinval; pinval = s3c2410_gpio_getpin(pindesc->pin); if (pinval)
{
/* 松开 */
key_val = 0x80 | pindesc->key_val;
}
else
{
/* 按下 */
key_val = pindesc->key_val;
} ev_press = ; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ return IRQ_RETVAL(IRQ_HANDLED);
} static int forth_drv_open(struct inode *inode, struct file *file)
{
/* 配置GPF0,2为输入引脚 */
/* 配置GPG3,11为输入引脚 */
request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[]);
request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[]);
request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[]);
request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[]); return ;
} ssize_t forth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
if (size != )
return -EINVAL; /* 如果没有按键动作, 休眠 */
wait_event_interruptible(button_waitq, ev_press); /* 如果有按键动作, 返回键值 */
copy_to_user(buf, &key_val, );
ev_press = ; return ;
} int forth_drv_close(struct inode *inode, struct file *file)
{
free_irq(IRQ_EINT0, &pins_desc[]);
free_irq(IRQ_EINT2, &pins_desc[]);
free_irq(IRQ_EINT11, &pins_desc[]);
free_irq(IRQ_EINT19, &pins_desc[]);
return ;
} static unsigned forth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = ;
poll_wait(file, &button_waitq, wait); // 不会立即休眠 if (ev_press)
mask |= POLLIN | POLLRDNORM; return mask;
} static struct file_operations sencod_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = forth_drv_open,
.read = forth_drv_read,
.release = forth_drv_close,
.poll = forth_drv_poll,
}; int major;
static int forth_drv_init(void)
{
major = register_chrdev(, "forth_drv", &sencod_drv_fops); forthdrv_class = class_create(THIS_MODULE, "forth_drv"); forthdrv_class_dev = class_device_create(forthdrv_class, NULL, MKDEV(major, ), NULL, "buttons"); /* /dev/buttons */ return ;
} static void forth_drv_exit(void)
{
unregister_chrdev(major, "forth_drv");
class_device_unregister(forthdrv_class_dev);
class_destroy(forthdrv_class);
return ;
} module_init(forth_drv_init); module_exit(forth_drv_exit); MODULE_LICENSE("GPL");

polldrv

测试程序:
1.在thirtest中修改,增加头文件 #include <poll.h>
2.在虚拟机中输入 man poll ,查看poll 的用法。
3.使用此函数调用驱动程序中的poll函数:int poll(struct pollfd *fds, nfds_t nfds, int timeout);
返回值: 0表示有超时。
参数一:pollfd结构,用来保存需要查询的文件、参数。不太懂。
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
参数二:查询的文件数,fds为一个结构数组,存放需要查询的文件,本程序中为1,
参数三:超时时间,单位毫秒 5000ms=5s
4.定义上述函数的返回值、第一个参数struct pollfd *fds,并设置pollfd结构:
int ret; //存放返回值
struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN; //该事件的含义是:There is data to read.(有要读取的数据)
5.调用poll:
ret = poll(fds, 1, 5000);
6.判断是否有超时,并打印相关数据:
if (ret == 0)
{
printf("time out\n");
}
else
{
read(fd, &key_val, 1);
printf("key_val = 0x%x\n", key_val);
}
完整测试程序请看下面:polltest

/**************************************************************/
/**** polltest ***************************************/
/**************************************************************/ #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h> /* forthdrvtest
*/
int main(int argc, char **argv)
{
int fd;
unsigned char key_val;
int ret; struct pollfd fds[]; fd = open("/dev/buttons", O_RDWR);
if (fd < )
{
printf("can't open!\n");
} fds[].fd = fd;
fds[].events = POLLIN;
while ()
{
ret = poll(fds, , );
if (ret == )
{
printf("time out\n");
}
else
{
read(fd, &key_val, );
printf("key_val = 0x%x\n", key_val);
}
} return ;
}

polltest

异步通知:
目标:当按键按下时,驱动程序通知应用程序,
1、应用程序注册信号处理函数。
2、谁发、发给谁:驱动发信号给应用程序。
3、应用告诉驱动应用的PID是多少。
4、怎么发;kill_fasyn
/**************************/
异步通知 信号(signal):
步骤:
/**************************/
发信号简介:
kill -9 PID(进程号)可以杀死某个进程
发送方 内容 接收方
写一个进程通信的例子:
用 man signal 查看signal怎么使用
#include <signal.h>
void my_signal_fun(int signum)
{
static int cnt = 0 ;
printf("signal = %d , %d time\n" , signum , ++cnt);
}

int main(int argc , char **argv)
{
signal(SIGUSR1, my_signal_fun);
while (1)
{
sleep(1000);
}
return 0;
}

编译:arm-linux-gcc -o signal signal.c
./signal //运行
PS //查看进程号
kill -USR1 xxx
kill -10 xxx

异步通知的要点:
1.注册信号处理函数
2.谁发
3.发给谁
4.怎么发
转化成代码:
1、应用程序注册信号处理函数。
2、谁发、发给谁:驱动发信号给应用程序。
3、应用程序告诉驱动应用程序的PID是多少。
4、怎么发:kill_fasync

驱动编写:
1、在forth_drv的基础上编写,将forth替换成fifth。
2、在file_operations结构中增加一个函数:
.fasync = fifth_drv_fasync,
3、定义fasync_struct结构体
static struct fasync_struct *button_async;
4、实现该函数,当应用程序调用fcntl(fd,F_SETFL, oflags | FASYNC );时,内核就得到应用程序的PID。
当标志位发生改变时此函数将被调用,初始化fasync_struct结构体,该结构体中有PID信息。(好像是这样的,不明白)
static int fifth_drv_fasync (int fd, struct file *filp, int on)
{
printk("driver: fifth_drv_fasync\n");
return fasync_helper (fd, filp, on, &button_async);
}
5、在中断处理函数中发送信号给应用程序:
kill_fasync (&button_async, SIGIO, POLL_IN);

完整代码请看下面:fifth_drv.c

/**************************************************************/
/**** fifth_drv.c: ***************************************/
/**************************************************************/
#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 *fifthdrv_class;
static struct class_device *fifthdrv_class_dev; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中断事件标志, 中断服务程序将它置1,fifth_drv_read将它清0 */
static volatile int ev_press = ; 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[] = {
{S3C2410_GPF0, 0x01},
{S3C2410_GPF2, 0x02},
{S3C2410_GPG3, 0x03},
{S3C2410_GPG11, 0x04},
}; /*
* 确定按键值
*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
struct pin_desc * pindesc = (struct pin_desc *)dev_id;
unsigned int pinval; pinval = s3c2410_gpio_getpin(pindesc->pin); if (pinval)
{
/* 松开 */
key_val = 0x80 | pindesc->key_val;
}
else
{
/* 按下 */
key_val = pindesc->key_val;
} ev_press = ; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ kill_fasync (&button_async, SIGIO, POLL_IN); return IRQ_RETVAL(IRQ_HANDLED);
} static int fifth_drv_open(struct inode *inode, struct file *file)
{
/* 配置GPF0,2为输入引脚 */
/* 配置GPG3,11为输入引脚 */
request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[]);
request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[]);
request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[]);
request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[]); return ;
} ssize_t fifth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
if (size != )
return -EINVAL; /* 如果没有按键动作, 休眠 */
wait_event_interruptible(button_waitq, ev_press); /* 如果有按键动作, 返回键值 */
copy_to_user(buf, &key_val, );
ev_press = ; return ;
} int fifth_drv_close(struct inode *inode, struct file *file)
{
free_irq(IRQ_EINT0, &pins_desc[]);
free_irq(IRQ_EINT2, &pins_desc[]);
free_irq(IRQ_EINT11, &pins_desc[]);
free_irq(IRQ_EINT19, &pins_desc[]);
return ;
} static unsigned fifth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = ;
poll_wait(file, &button_waitq, wait); // 不会立即休眠 if (ev_press)
mask |= POLLIN | POLLRDNORM; return mask;
} static int fifth_drv_fasync (int fd, struct file *filp, int on)
{
printk("driver: fifth_drv_fasync\n");
return fasync_helper (fd, filp, on, &button_async);
} static struct file_operations sencod_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = fifth_drv_open,
.read = fifth_drv_read,
.release = fifth_drv_close,
.poll = fifth_drv_poll,
.fasync = fifth_drv_fasync,
}; int major;
static int fifth_drv_init(void)
{
major = register_chrdev(, "fifth_drv", &sencod_drv_fops); fifthdrv_class = class_create(THIS_MODULE, "fifth_drv"); fifthdrv_class_dev = class_device_create(fifthdrv_class, NULL, MKDEV(major, ), NULL, "buttons"); /* /dev/buttons */ return ;
} static void fifth_drv_exit(void)
{
unregister_chrdev(major, "fifth_drv");
class_device_unregister(fifthdrv_class_dev);
class_destroy(fifthdrv_class);
return ;
} module_init(fifth_drv_init);
module_exit(fifth_drv_exit);
MODULE_LICENSE("GPL");

fifth_drv.c

测试程序请看下面:fifth_test.c

/**************************************************************/
/**** fifth_test.c: ***************************************/
/**************************************************************/ #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> /* fifthdrvtest
*/
int fd;
void my_signal_fun(int signum)
{
unsigned char key_val;
read(fd, &key_val, );
printf("key_val: 0x%x\n", key_val);
} int main(int argc, char **argv)
{
unsigned char key_val;
int ret;
int Oflags; signal(SIGIO, my_signal_fun); fd = open("/dev/buttons", O_RDWR);
if (fd < )
{
printf("can't open!\n");
} fcntl(fd, F_SETOWN, getpid()); Oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, Oflags | FASYNC); while ()
{
sleep();
}
return ;
}
//关于 fcntl 可以参考: http://www.cnblogs.com/lonelycatcher/archive/2011/12/22/2297349.html
//我没看明白

fifth_test.c

原子操作、信号量
/********************/
同步互斥阻塞:12-7:
原子操作、信号量
步骤:
/********************/
原子相关:
原子操作的相关函数:
atomic_t v=ATOMIC_INIT(1); //定义原子变量v并初始化为1
atomic_read(atomic_t *v); //返回原子变量的值/
void atomic_inc(&v); //自增1
void atomic_dec(&v); //自减1
atomic_dec_and_test(&v); //自减操作后测试其指是否为0,为0返回true,否则返回false

1、添加一个一个原子变量:atomic_t canopen=ATOMIC_INIT(1);
2、在open函数中增加一段判断代码:
if (!atomic_dec_and_test(&canopen))
{
atomic_inc(&canopen);
return -EBUSY; //当已经有应用程序打开该文件了,就返回忙
}
3、在close 中把变量加回来:
void atomic_inc(&canopen);

信号量相关:
信号量(semaphore):
用于保护临界区的一种常用方法,只有得到信号得信号量的进程才能执行临界区代码。
当获取不到信号量是,进程进入休眠等待状态。
信号量的相关函数:
定义信号量:struct semaphore sem;
初始化信号量:
void sema_init(struct semaphore * sem, int val);
void init_MUTEX(struct semaphore * sem);
static DECLARE_MUTEX(button_lock); //定义互斥锁
获得信号量:
void down(struct semaphore * sem)
void down_interruptible(struct semaphore * sem)
void down_trylock(struct semaphore * sem)

释放信号量:
void up(struct semaphore *sem)

步骤和原子操作一样,都是定义一个信号量、在open中申请信号量、在close中释放
使用信号量与使用原子操作的区别在于,
当使用原子操作时,第二次申请失败后程序将退出,
而使用信号量的时候第二次申请失败时,进程将被挂起,等待其他程序释放该信号量,
当有其他程序释放该信号量时,此程序将被唤醒。

阻塞与非阻塞:
当应用程序选择阻塞方式访问驱动时,应用申请不到信号量将等待其他程序释放信号量
当应用程序选择非阻塞方式访问驱动时,应用申请不到信号量或没有按键触发时将直接退出,不会去等待信号量与按键触发
1、在open函数中增加判断,判断传入的参数是阻塞还是非阻塞:
if (file->f_flags & O_NONBLOCK)
{
if (down_trylock(&button_lock))
return -EBUSY;
}
else
{
/* 获取信号量 */
down(&button_lock);
}
2、在read函数中也增加判断:
if (file->f_flags & O_NONBLOCK)
{
if (!ev_press)
return -EAGAIN;
}
else
{
/* 如果没有按键动作, 休眠 */
wait_event_interruptible(button_waitq, ev_press);
}

完整代码请看下面:sixth_drv.c

/**************************************************************/
/**** sixth_drv.c: ***********************************/
/**************************************************************/
#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 *sixthdrv_class;
static struct class_device *sixthdrv_class_dev; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中断事件标志, 中断服务程序将它置1,sixth_drv_read将它清0 */
static volatile int ev_press = ; 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[] = {
{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 irqreturn_t buttons_irq(int irq, void *dev_id)
{
struct pin_desc * pindesc = (struct pin_desc *)dev_id;
unsigned int pinval; pinval = s3c2410_gpio_getpin(pindesc->pin); if (pinval)
{
/* 松开 */
key_val = 0x80 | pindesc->key_val;
}
else
{
/* 按下 */
key_val = pindesc->key_val;
} ev_press = ; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ kill_fasync (&button_async, SIGIO, POLL_IN); return IRQ_RETVAL(IRQ_HANDLED);
} static int sixth_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[]);
request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[]);
request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[]);
request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[]); return ;
} ssize_t sixth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
if (size != )
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, );
ev_press = ; return ;
} int sixth_drv_close(struct inode *inode, struct file *file)
{
//atomic_inc(&canopen);
free_irq(IRQ_EINT0, &pins_desc[]);
free_irq(IRQ_EINT2, &pins_desc[]);
free_irq(IRQ_EINT11, &pins_desc[]);
free_irq(IRQ_EINT19, &pins_desc[]);
up(&button_lock);
return ;
} static unsigned sixth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = ;
poll_wait(file, &button_waitq, wait); // 不会立即休眠 if (ev_press)
mask |= POLLIN | POLLRDNORM; return mask;
} static int sixth_drv_fasync (int fd, struct file *filp, int on)
{
printk("driver: sixth_drv_fasync\n");
return fasync_helper (fd, filp, on, &button_async);
} static struct file_operations sencod_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = sixth_drv_open,
.read = sixth_drv_read,
.release = sixth_drv_close,
.poll = sixth_drv_poll,
.fasync = sixth_drv_fasync,
}; int major;
static int sixth_drv_init(void)
{
major = register_chrdev(, "sixth_drv", &sencod_drv_fops); sixthdrv_class = class_create(THIS_MODULE, "sixth_drv"); sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, ), NULL, "buttons"); /* /dev/buttons */ return ;
} static void sixth_drv_exit(void)
{
unregister_chrdev(major, "sixth_drv");
class_device_unregister(sixthdrv_class_dev);
class_destroy(sixthdrv_class);
return ;
} module_init(sixth_drv_init); module_exit(sixth_drv_exit); MODULE_LICENSE("GPL");

sixth_drv.c

测试程序:
测试程序中打开驱动文件是可以传入一个阻塞非阻塞的参数,驱动程序中将会对其判断
非阻塞: fd = open("/dev/buttons", O_RDWR );
阻塞: fd = open("/dev/buttons", O_RDWR | O_NONBLOCK);
完整代码请看下面:sixth_test.c

/**************************************************************/
/**** sixth_test.c: ***************************************/
/**************************************************************/ #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, );
printf("key_val: 0x%x\n", key_val);
} int main(int argc, char **argv)
{
unsigned char key_val;
int ret;
int Oflags; //signal(SIGIO, my_signal_fun); fd = open("/dev/buttons", O_RDWR | O_NONBLOCK);
if (fd < )
{
printf("can't open!\n");
return -;
} //fcntl(fd, F_SETOWN, getpid()); //Oflags = fcntl(fd, F_GETFL); //fcntl(fd, F_SETFL, Oflags | FASYNC); while ()
{
ret = read(fd, &key_val, );
printf("key_val: 0x%x, ret = %d\n", key_val, ret);
sleep();
} return ;
}

sixth_test.c

定时器防抖
/********************/
定时器防抖 12-8
/********************/
定时器两要素:
1、超时时间。2、超时处理函数
步骤:
1、定义一个timer:static struct timer_list buttons_timer;
2、在init函数中初始化timer,设置timer:
init_timer(&buttons_timer); //初始化
buttons_timer.function = buttons_timer_function; //超时处理函数
//buttons_timer.expires = 0;
add_timer(&buttons_timer); //将定时器告诉内核
3、在中断处理函数中增加:
/* 10ms后启动定时器 */
irq_pd = (struct pin_desc *)dev_id;
mod_timer(&buttons_timer, jiffies+HZ/100); //设置超时时间,如果抖动了,该函数将被重新调用,即重新设置超时时间
//jiffies为系统时钟中断,每隔10ms将自增1。可以 cat /proc/interrupts查看到一个Timer Tick
4、并将原来的irq函数里的内容放到超时处理函数中。
5、因为init函数中,设置timer是没有设置超时时间,即默认超时时间为0,即设置好后将立即调用超时函数,所以需要在超时函数中增加一个判断:
if (!pindesc)
return;
//pindesc在irq中设置了,当没有发生中断,而进入该函数,就直接返回。

完整代码请看下面:buttuns_alldrv.c

/**************************************************************/
/**** buttuns_alldrv.c: ***************************************/
/**************************************************************/
#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 *sixthdrv_class;
static struct class_device *sixthdrv_class_dev; static struct timer_list buttons_timer; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中断事件标志, 中断服务程序将它置1,sixth_drv_read将它清0 */
static volatile int ev_press = ; 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[] = {
{S3C2410_GPF0, 0x01},
{S3C2410_GPF2, 0x02},
{S3C2410_GPG3, 0x03},
{S3C2410_GPG11, 0x04},
}; static struct pin_desc *irq_pd; //static atomic_t canopen = ATOMIC_INIT(1); //定义原子变量并初始化为1 static DECLARE_MUTEX(button_lock); //定义互斥锁 /*
* 确定按键值
*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
/* 10ms后启动定时器 */
irq_pd = (struct pin_desc *)dev_id;
mod_timer(&buttons_timer, jiffies+HZ/);
return IRQ_RETVAL(IRQ_HANDLED);
} static int sixth_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[]);
request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[]);
request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[]);
request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[]); return ;
} ssize_t sixth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
if (size != )
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, );
ev_press = ; return ;
} int sixth_drv_close(struct inode *inode, struct file *file)
{
//atomic_inc(&canopen);
free_irq(IRQ_EINT0, &pins_desc[]);
free_irq(IRQ_EINT2, &pins_desc[]);
free_irq(IRQ_EINT11, &pins_desc[]);
free_irq(IRQ_EINT19, &pins_desc[]);
up(&button_lock);
return ;
} static unsigned sixth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = ;
poll_wait(file, &button_waitq, wait); // 不会立即休眠 if (ev_press)
mask |= POLLIN | POLLRDNORM; return mask;
} static int sixth_drv_fasync (int fd, struct file *filp, int on)
{
printk("driver: sixth_drv_fasync\n");
return fasync_helper (fd, filp, on, &button_async);
} static struct file_operations sencod_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = sixth_drv_open,
.read = sixth_drv_read,
.release = sixth_drv_close,
.poll = sixth_drv_poll,
.fasync = sixth_drv_fasync,
}; int major; 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 = ; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ kill_fasync (&button_async, SIGIO, POLL_IN);
} static int sixth_drv_init(void)
{
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;
//buttons_timer.expires = 0;
add_timer(&buttons_timer); major = register_chrdev(, "sixth_drv", &sencod_drv_fops); sixthdrv_class = class_create(THIS_MODULE, "sixth_drv"); sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, ), NULL, "buttons"); /* /dev/buttons */ return ;
} static void sixth_drv_exit(void)
{
unregister_chrdev(major, "sixth_drv");
class_device_unregister(sixthdrv_class_dev);
class_destroy(sixthdrv_class);
return ;
} module_init(sixth_drv_init);
module_exit(sixth_drv_exit);
MODULE_LICENSE("GPL");

buttuns_alldrv.c

代码都是韦老师的,自己有好几处都不是很明白。我是纯新手,里面肯定有好多地方理解错了,暂时先这样写。如果看到我写错了,希望各位千万要指出来,感谢!

入门级的按键驱动——按键驱动笔记之poll机制-异步通知-同步互斥阻塞-定时器防抖的更多相关文章

  1. Linux驱动之异步OR同步,阻塞OR非阻塞概念介绍

    链接:https://www.zhihu.com/question/19732473/answer/20851256 1.同步与异步同步和异步关注的是消息通信机制 (synchronous commu ...

  2. Node.js学习笔记(6)--异步变同步

    说明(2017-5-3 14:59:03): 1. 异步变同步: var fs = require("fs"); var documents = []; fs.readdir(&q ...

  3. linux设备驱动归纳总结(三):7.异步通知fasync【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-62725.html linux设备驱动归纳总结(三):7.异步通知fasync xxxxxxxxxxx ...

  4. 【Linux开发】linux设备驱动归纳总结(三):7.异步通知fasync

    linux设备驱动归纳总结(三):7.异步通知fasync xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  5. 嵌入式Linux驱动学习之路(十二)按键驱动-poll机制

    实现的功能是在读取按键信息的时候,如果没有产生按键,则程序休眠在read函数中,利用poll机制,可以在没有退出的情况下让程序自动退出. 下面的程序就是在读取按键信息的时候,如果5000ms内没有按键 ...

  6. 字符设备驱动(六)按键poll机制

    title: 字符设备驱动(六)按键poll机制 tags: linux date: 2018-11-23 18:57:40 toc: true --- 字符设备驱动(六)按键poll机制 引入 在字 ...

  7. Linux 进程间通信 --- 信号通信 --- signal --- signal(SIGINT, my_func); --- 按键驱动异步通知(转)

    信号  ( signal ) 机制是 UNIX 系统中最为古老的进程间通信机制,很多条件可以产生一个信号. 信号的产生: 1,当用户按下某些按键时,产生信号. 2,硬件异常产生信号:除数为 0 ,无效 ...

  8. linux字符驱动之poll机制按键驱动

    在上一节中,我们讲解了如何自动创建设备节点,实现一个中断方式的按键驱动.虽然中断式的驱动,效率是蛮高的,但是大家有没有发现,应用程序的死循环里的读函数是一直在读的:在实际的应用场所里,有没有那么一种情 ...

  9. 集齐所有机制的按键控制LED驱动

    内核版本:linux2.6.22.6 硬件平台:JZ2440 驱动源码 final_key.c : #include <linux/module.h> #include <linux ...

随机推荐

  1. Java实现注册时发送激活邮件+激活

    最近从项目分离出来的注册邮箱激活功能,整理一下,方便下次使用 1.RegisterController.java package com.app.web.controller; import java ...

  2. 【转】PHP调试开发工具你认识多少?

    来源:PHP100中文网(http://www.php100.com/html/itnews/PHPxinwen/2009/0902/3257.html) PHP现在已经是使用最为广泛的开源服务器端脚 ...

  3. [转]Dcloud App离线本地存储方案

    原文地址:http://ask.dcloud.net.cn/article/166 HTML5+的离线本地存储有如下多种方案:HTML5标准方案:cookie.localstorage.session ...

  4. CSS3文本温故

    1.CSS早期属性,分为三大类:字体.颜色和文本: 2.CSS文本类型有11个属性: 注:还有一个颜色属性:color,主要用来设置文本颜色 3.CSS3文本阴影属性:text-shadow语法:te ...

  5. Android之手机向导以及设置中心模块的开发

    当我们使用的新的软件的时候,我们首先需要教用户如何使用我们的软件,当用户学习完使用教程,下次再登录的时候,我们应该直接跳到我们的功能界面,下面我来展示一下我学习视频做的效果图:手机防盗这个功能模块就是 ...

  6. Android 常用抓包工具介绍之Charles

    ➠更多技术干货请戳:听云博客 Charles是一款抓包修改工具,相比起TcpDump,charles具有界面简单直观,易于上手,数据请求控制容易,修改简单,抓取数据的开始暂停方便等等优势!前面介绍了如 ...

  7. 优化MySchool数据库(存储过程)

    什么是“存储过程”: ---- 数据库中,用于存储“业务逻辑”的技术!(T-SQL代码当做数据一样保存到数据可) 语法 : [if exists(select * from sysobjects wh ...

  8. mac PHP配置

    apache默认路径配置方法 apache的配置   apache已经自带了,只需如下三个命令就可以了. 开启apache服务 sudo apachectl start 停止apache服务 sudo ...

  9. python 判断学期与学年

    9,10,11,12,1 第一学期 2,3,4,5,6,7 第二学期 其中8月份放假,暂且放入第一学期.因为大部分学校都选在8月底开学 import datetime def getXNandXQ() ...

  10. 小试SQL SERVER 2014 加密备份

            数据库加密: http://blog.csdn.net/u012992506/article/details/25283035   create master key encrypti ...