linux按键驱动程序

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

一、定时器延时去抖

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

1.1内核定时器

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

  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按键驱动程序(二)的更多相关文章

  1. 14.linux按键驱动程序(一)

    按键驱动程序 本文学习主要包含按键硬件的实现.中断分层管理.按键定时器去抖.阻塞性驱动程序设计.这里面需要使用到混杂设备驱动和中断处理程序的内容. 一.创建按键混杂设备驱动模型 int key_ope ...

  2. LINUX按键驱动程序

    <<混杂设备驱动模型>> <混杂设设备的描述> <混在设备的概念> 在linux系统中,存在一类字符设备,他们拥有相同的主设备号(10),但是次设备号不 ...

  3. 在Linux下的中断方式读取按键驱动程序

    // 在Linux下的中断方式读取按键驱动程序 //包含外部中断 休眠 加入poll机制 // 采用异步通知的方式 // 驱动程序发 ---> app接收 (通过kill_fasync()发送) ...

  4. 转:Linux网卡驱动程序编写

    Linux网卡驱动程序编写 [摘自 LinuxAID] 工作需要写了我们公司一块网卡的Linux驱动程序.经历一个从无到有的过程,深感技术交流的重要.Linux作为挑战微软垄断的强有力武器,日益受到大 ...

  5. 【转】linux设备驱动程序中的阻塞机制

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275272.html 阻塞与非阻塞是设备访问的两种方式.在写阻塞与非阻塞的驱动程序时,经 ...

  6. 在Ubuntu上为Android系统编写Linux内核驱动程序(老罗学习笔记1)

    这里,我们不会为真实的硬件设备编写内核驱动程序.为了方便描述为Android系统编写内核驱动程序的过程,我们使用一个虚拟的硬件设备,这个设备只有一个4字节的寄存器,它可读可写.想起我们第一次学习程序语 ...

  7. 在Ubuntu上为Android系统内置C可执行程序测试Linux内核驱动程序(老罗学习笔记2)

    在前一篇文章中,我们介绍了如何在Ubuntu上为Android系统编写Linux内核驱动程序.在这个名为hello的Linux内核驱动程序中,创建三个不同的文件节点来供用户空间访问,分别是传统的设备文 ...

  8. 在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序(老罗学习笔记3)

    简单来说,硬件驱动程序一方面分布在Linux内核中,另一方面分布在用户空间的硬件抽象层中.接着,在Ubuntu上为Android系统编写Linux内核驱动程序(老罗学习笔记1)一文中举例子说明了如何在 ...

  9. 如何编写Linux设备驱动程序

    一.Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口.设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看 ...

随机推荐

  1. 【先定一个小目标】怎么解决mysql不允许远程连接的错误

    最近使用Navicat for MySQl访问远程mysql数据库,出现报错,显示“1130 - Host'xxx.xxx.xxx.xxx' is not allowed to connect to ...

  2. 什么是License

    许多混乱就始于你不知道License到底是什么,到底有什么含义.当你对你的产品使用License时,并不意味着你放弃了任何权利,你依然对其拥有原著作权.License只是授予他们于特定权利来使用你的产 ...

  3. mfc+vtk

    MFC中view类主要处理显示视图,doc类处理文档,mainframe主要为整个窗口的和工程的设置管理.由此,VTK与MFC联合编程时,需要主要的是数据操作,以及显示要很好的与MFC中的结构结合,做 ...

  4. XUnit - Shared Context between Tests

    原文 单元测试类通常都会有share setup和cleanup的相关代码.xUnit.net根据共享的范围提供了几种share setup和cleanup的方法. Constructor and D ...

  5. CentOS 6 部署GlusterFS

    首先需要关闭CentOS的防火墙和selinux,否则glusterfs将可能无法正常工作. /etc/init.d/iptables status 会得到一系列信息,说明防火墙开着. /etc/in ...

  6. informatica读取FTP文件

    以下为一个完整的informatica读取ftp文件,并导入到系统中. 第一步: 通过shell脚本下载压缩包文件 /server/infa_shared/crm_prod/shell/ftpFrom ...

  7. Set集合的使用

    #include<iostream> #include<set> using namespace std; typedef struct { int i,j; char s; ...

  8. BZOJ2292——【POJ Challenge 】永远挑战

    1.题意:dijkstra模板题,存点模板 #include <queue> #include <cstdio> #include <cstdlib> #inclu ...

  9. TJpgDec使用说明

    TJpgDec模块应用说明 [TOC] 怎么使用 首先,你应该构建和运行如下所示示例程序.这是一个典型的使用TJpgDec模块,它有助于调试和缩小问题. 解码会话分为两个阶段.第一阶段是分析JPEG图 ...

  10. Ubuntu14.04安装配置web/ftp/tftp/dns服务器

    目录: 1.安装ftp服务器vsftpd --基于tcp,需要帐号密码 2.安装tftp服务器tftpd-hpa,tftp-hpa --udp 3.web服务器--使用Apache2+Mysql+PH ...