15.linux按键驱动程序(二)

时间:2023-03-09 04:26:16
15.linux按键驱动程序(二)

        linux按键驱动程序

  包含内容定时器延时去抖动,阻塞型设备驱动设计

一、定时器延时去抖

  按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,开关不会马上稳定地接通或断开。因而在闭合及断开的瞬间总是伴有一连串的抖动的。按键去抖动的方法主要有两种,一种是硬件电路去抖动;另一种就是软件延时去抖动。而延时又一般分为了两种,一种是for循环等待,另一种是定时器延时。在操作系统中,由于效率方面的原因,一使用定时器。

1.1内核定时器

15.linux按键驱动程序(二)

定时器的使用分为了四个步骤:

  1.定义定时器的变量,就是timer_list结构。

  2.要对结构进行初始化。Init_timer是系统自动运行的初始化函数,能初始化很大部分timer_list里面的成员。但是,超时函数是需要我们自己设置,就是function。

  3.使用add_timer函数注册定时器。

  4.mod_timer重启定时器。注意,定时器不是循环的,需要重复调用mod_timer函数。

Linux内核使用struct timer_list来描述一个定时器:

 struct timer_list{
struct list_head entry;
unsigned long expires;
void (*function)(unsigned long);
unsigned long data;
struct tvec_base *base;
};

  其中expires是定时器延时的时间,function函数是定时器超时后要做的事情。

1.2使用内核定时器

二、阻塞型驱动程序设计

  当一个设备无法立刻满足用户的读写请求时应当如何处理? 例如:调用read时,设备没有数据提供, 但以后可能会有;或者一个进程试图向设备写入数据,但是设备暂时没有准备好接收数据。当上述情况发生的时候,驱动程序应当(缺省地)阻塞进程,使它进入等待(睡眠)状态,直到请求可以得到满足。

2.1内核等待队列

  在实现阻塞驱动的过程中,也需要有一个“候车室”来安排被阻塞的进程“休息”,当唤醒它们的条件成熟时,则可以从“候车室”中将这些进程唤醒。而这个“候车室”就是等待队列。

2.2队列描述

1、定义等待队列

  wait_queue_head_t my_queue
2、初始化等待队列
  init_waitqueue_head(&my_queue)
3、定义+初始化等待队列
  DECLARE_WAIT_QUEUE_HEAD(my_queue)

4、进入等待队列,睡眠

4.1 wait_event(queue,condition)
  当condition(布尔表达式)为真时,立即返回;否则让进程
  进入TASK_UNINTERRUPTIBLE模式的睡眠,并挂在queue参数所指定的等待队列上。

4.2wait_event_interruptible(queue,condition)
  当condition(布尔表达式)为真时,立即返回;否则让

  进程进入TASK_INTERRUPTIBLE的睡眠,并挂在queue参数所指定的等待队列上。

4.3int wait_event_killable(queue, condition)

  当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_KILLABLE的睡眠,并挂在queue参数所指定的等待队列上。

5、从等待队列中唤醒进程
5.1 wake_up(wait_queue_t *q)
  从等待队列q中唤醒状态为TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE 的所有进程。

5.2 wake_up_interruptible(wait_queue_t *q)
  从等待队列q中唤醒状态为TASK_INTERRUPTIBLE 的进程

按键阻塞代码如下(也是关于按键所有的程序):

   #include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h> #include <linux/sched.h> #define GPNCON 0x7f008830
#define GPNDAT 0x7f008834 MODULE_LICENSE("GPL"); struct work_struct *work1;
unsigned int *gpio_dat;
struct timer_list key_timer; unsigned int key_num = ;
wait_queue_head_t key_q; void work1_func(struct work_struct *work)
{
//启动定时器 100毫秒超时=HZ/10,HZ=1秒。jiffies是系统当前时间
mod_timer(&key_timer,jiffies + (HZ/)); //启动定时器
} void key_timer_func(unsigned long data) //定时器超时函数
{
unsigned int key_val;
key_val = readw(gpio_dat) &0x01;
if(key_val==)
key_num = ;
key_val = readw(gpio_dat) &0x02;
if(key_val==)
key_num = ;
wake_up(&key_q);
} irqreturn_t key_int(int irp,void *dev_id) //中断处理函数
{
//3.提交下半部
schedule_work(work1);
return IRQ_HANDLED;
} void key_hw_init(void) //硬件初始化
{
unsigned int *gpio_config;
unsigned short data;
gpio_config = ioremap(GPNCON,); //动态映射虚拟地址
data = readw(gpio_config);
data &= ~0xfff;
data |= 0xaaa;
writew(data,gpio_config);
gpio_dat = ioremap(GPNDAT,); //将数据寄存器地址转化为虚拟地址
} int key_open(struct inode *node,struct file *filp)
{
return ;
} ssize_t key_read (struct file *filp, char __user *buf, size_t size, loff_t *pos)
{
wait_event(key_q,key_num); //进入睡眠 copy_to_user(buf,&key_num,); key_num = ;
return ;
} struct file_operations key_fops =
{
.open = key_open,
.read = key_read,
}; struct miscdevice key_miscdev =
{
.minor = , //次设备号
.name = "key",
.fops = &key_fops,
};
//驱动程序初始化
static int button_init(void)
{
misc_register(&key_miscdev); //1注册混杂设备
//2按键硬件初始化
key_hw_init();
request_irq(IRQ_EINT(),key_int,IRQF_TRIGGER_FALLING,"key",); //注册中断处理程序
request_irq(IRQ_EINT(),key_int,IRQF_TRIGGER_FALLING,"key",); //注册中断处理程序 //3创建工作1
work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
INIT_WORK(work1,work1_func); //4初始化内核定时器
init_timer(&key_timer);
key_timer.function = key_timer_func;
//注册定时器
add_timer(&key_timer);
//初始化等待队列
init_waitqueue_head(&key_q);
return ;
} static void button_exit(void)
{
misc_deregister(&key_miscdev); //注销混杂设备
//注销中断处理程序
free_irq(IRQ_EINT(),);
free_irq(IRQ_EINT(),);
} module_init(button_init);
module_exit(button_exit);

按键应用程序:

 #include <stdio.h>
#include <stdlib.h> int main()
{
int fd;
int key_num; //1.打开设备
fd = open("/dev/6410key",);
if(fd<)
printf("open device fail!\n"); //2.读取设备
read(fd,&key_num,);
printf("key is %d\n",key_num); //3.关闭设备
close(fd);
}

运行过程如下:

15.linux按键驱动程序(二)