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. 3MyBatis配置--深入浅出MyBatis技术原理与实践(笔记)

    XML 映射配置文件 configuration 配置 properties 属性 settings 设置 typeAliases 类型命名 typeHandlers 类型处理器 objectFact ...

  2. 出现 cannot be resolved or is not a field 错误

    删除R.Java文件,这时,系统会重新生成一个R.java; 删除java代码中的"import Android.R"文件. 重新导入正确的包.

  3. Entity framewok 如何实现多条记录作为一条取出, for xml path如何实现

    http://www.myexception.cn/linq/1288046.html Entity framewok 怎么实现多条记录作为一条取出, for xml path怎么实现News表:ID ...

  4. SpringMVC下Ajax请求的方法,@Responsebody如果返回的是布尔值,ajax不会接到任何回传数据

    SpringMVC框架下,如果用ajax向后台请求得方法如果使用@Responsebody返回布尔值的话,ajax得不到任何的回传数据. 但是如果返回String类型,就是正常的. 测试了下代码写得没 ...

  5. Linux C 收藏

    某招聘要求:熟悉高性能分布式网络服务端设计开发,熟悉epoll.多线程.异步IO.事件驱动等服务端技术: <UNIX环境高级编程(第3版)>apue.h等源码文件的编译安装 <UNI ...

  6. 无意之间发现的Servlet3.0新特性@WebServlet

    今天无意之间看到了一个注解,这个注解就是@WebServlet,@WebServlet 用于将一个类声明为 Servlet,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为 Se ...

  7. 使用ICSharpCode.SharpZipLib.Zip类库解压zip文件的方法

    public static bool ZipExtractFile(string zipFilePath,string targetPath) { FastZip fastZip = new Fast ...

  8. js 中的快速排序算法简单实现

    对于快速排序,最早是在c++中看到,它是利用指针来交换顺序,其实无论哪种语言,原理 和 思想都是一样,然而真正用起来的时候就特别容易忽略一些事实,导致实现失败.废话少说,下面用js实现一下快速排序: ...

  9. Best Time to Buy and Sell Stock1,2,3,4

    找到最低值和最高值 int maxProfit(vector<int>& prices) { ); ; ]; ;i<prices.size();i++) { profit=m ...

  10. oracle忘记密码

    1.输入命令: sqlplus /nolog ,进入oracle控制台,并输入 conn /as sysdba;以DBA角色进入. 2.连接成功后,输入"select username fr ...