14.linux按键驱动程序(一)
按键驱动程序
本文学习主要包含按键硬件的实现、中断分层管理、按键定时器去抖、阻塞性驱动程序设计。这里面需要使用到混杂设备驱动和中断处理程序的内容。
一、创建按键混杂设备驱动模型
int key_open(struct inode *node,struct file *filp)
{
return ;
}
struct file_operations key_fops =
{
.open = key_open,
};
struct miscdevice key_miscdev = {
.minor = , //次设备号
.name = "6410key", //设备名
.fops = &key_fops,
};
二、按键硬件的实现
首先是按键的初始化,按键的初始化可以选择在open函数,和模块的初始化函数当中完成硬件的初始化。下面我们是选择在模块的初始化函数进行按键的初始化。按键的初始化,主要涉及对GPIO的引脚的功能进行相应的设置。我用的是ok6410A开发板上面有六个按键,核心板原理图如下:

2.1硬件初始化

s3c6410芯片手册对GPNCON的IO功能定义如下因此对按键硬件的初始化如下:
#define GPNCON 0x7f008830
#define GPNDAT 0x7f008834 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,); //将数据寄存器地址转化为虚拟地址
}
2.2按键中断程序(暂时注册第一个按键)
来源参照:中断处理程序
#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h> #define GPNCON 0x7f008830
#define GPNDAT 0x7f008834 MODULE_LICENSE("GPL"); irqreturn_t key_init(int irp,void *dev_id)
{
//1.检测是否发生中断
//2.清除已经发生的按键中断 //3.打印按键值
printk(KERN_EMERG"key down!\n");
return ;
} 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 ;
}
struct file_operations key_fops =
{
.open = key_open,
}; struct miscdevice key_miscdev =
{
.minor = ,
.name = "key",
.fops = &key_fops,
}; static int button_init(void)
{
misc_register(&key_miscdev); //注册混杂设备
//按键硬件初始化
key_hw_init();
request_irq(IRQ_EINT(),key_init,IRQF_TRIGGER_FALLING,"key",); //注册中断处理程序
return ;
} static void button_exit(void)
{
misc_deregister(&key_miscdev); //注销混杂设备 //注销中断处理程序
free_irq(IRQ_EINT(),);
} module_init(button_init);
module_exit(button_exit);
这里需要注意上面代码特殊标记的内容:IRQ_EINT(0)中断号、IRQF_TRIGGER_FALLING标志的来源
标志来源:
按键中断的处理,对于按键而言,可以在按下的时候产生中断,也可以在弹起的时候产生中断。需要通过一个标志来指定:IRQF_TRIGGER_FALLING,这个是从高电平到低电平产生中断。下表是其他产生中断的方式(内核代码中搜索IRQF_TRIGGER_FALLING):

中断号:
就是request_irq函数的第一个参数。我们在内核代码中搜索irqs.h,找对应的板子的:

从上面的代码看到,IRQ_EINT0_3的中断号是32.系统留出了S3C_IRQ_OFFSET=32个中断号,这是给软中断的。所以中断号就是等于硬件编号加上偏移量。可以查看内核代码的entry-macro.S
三、中断分层管理

3.1中断嵌套
所谓的中断嵌套就是,当一种中断正在执行的时候,又产生了另外中断。可以是同类型的,也可以是不同类型的。
慢速中断:是指在进行中断处理的时候,中断的总开关是不关闭的。允许其他类型中断产生。
快速中断:当中断产生的时候,控制位的IF为被置1,别的中断被禁止发生。这样就会产生我们不想看到的情况:中断丢失。
3.2中断分层

上半部:当中断发生时,它进行相应地硬件读写,并“登记”该中断。通常由中断处理程序充当上半部。
下半部:在系统空闲的时候对上半部“登记”的中断进行后续处理。
3.3工作队列
工作队列是一种将任务推后执行的形式,他把推后的任务交由一个内核线程去执行。这样下半部会在进程上下文执行,它允许重新调度甚至睡眠。 每个被推后的任务叫做“工作”,由这些工作组成的队列称为工作队列
下图为3内核的处理器队列处理图:


3.3.1工作队列描述
Linux内核使用struct work_struct来描述一个工作队列:
struct workqueue_struct{
struct cpu_workqueue_struct *cpu_wq;
struct list_head list;
const char *name; /*workqueue name*/
int singlethread;
int freezeable; /* Freeze threads during suspend */
int rt;
};
3.3.2工作队列项
Linux内核使用struct work_struct来描述一个工作项:
struct work_struct{
atomic_long_t data;
struct list_headentry;
work_func_t func;
};
typedef void (*work_func_t)(struct work_struct *work);
3.3.3工作队列使用
step1. 创建工作队列
create_workqueue
step2. 创建工作
INIT_WORK
step3. 提交工作
queue_work
在大多数情况下, 驱动并不需要自己建立工作队列,只需定义工作, 然后将工作提交到内核已经定义好的工作队列keventd_wq中。
1. 提交工作到默认队列
schedule_work
按照工作队列修改按键程序:
#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> #define GPNCON 0x7f008830
MODULE_LICENSE("GPL");
struct work_struct *work1;
struct timer_list key_timer; void work1_func(struct work_struct *work)
{
printk(KERN_EMERG"key down!\n");
} irqreturn_t key_int(int irp,void *dev_id)
{
//3.提交下半部
schedule_work(work1);
return ;
} 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 ;
}
struct file_operations key_fops =
{
.open = key_open,
}; struct miscdevice key_miscdev =
{
.minor = ,
.name = "key",
.fops = &key_fops,
}; static int button_init(void)
{
misc_register(&key_miscdev); //注册混杂设备 //按键硬件初始化
key_hw_init();
request_irq(IRQ_EINT(),key_int,IRQF_TRIGGER_FALLING,"key",); //注册中断处理程序
//创建工作1
work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
INIT_WORK(work1,work1_func);
return ;
} static void button_exit(void)
{
misc_deregister(&key_miscdev); //注销混杂设备
//注销中断处理程序
free_irq(IRQ_EINT(),);
} module_init(button_init);
module_exit(button_exit);
接下来的内容在下一个文章里包括:定时器去抖和阻塞性驱动程序设计
14.linux按键驱动程序(一)的更多相关文章
- 15.linux按键驱动程序(二)
linux按键驱动程序 包含内容定时器延时去抖动,阻塞型设备驱动设计 一.定时器延时去抖 按键所用开关为机械弹性开关,当机械触点断开.闭合时,由于机械触点的弹性作用,开关不会马上稳定地接通或断开.因而 ...
- LINUX按键驱动程序
<<混杂设备驱动模型>> <混杂设设备的描述> <混在设备的概念> 在linux系统中,存在一类字符设备,他们拥有相同的主设备号(10),但是次设备号不 ...
- 在Linux下的中断方式读取按键驱动程序
// 在Linux下的中断方式读取按键驱动程序 //包含外部中断 休眠 加入poll机制 // 采用异步通知的方式 // 驱动程序发 ---> app接收 (通过kill_fasync()发送) ...
- linux设备驱动程序--gpio控制
gpio驱动程序 上一章节linux设备驱动程序--创建设备节点章节主要介绍了linux字符设备驱动程序的框架,从这一章节开始我们讲解各种外设的控制,包括gpio,i2c,dma等等,既然是外设,那就 ...
- linux设备驱动程序该添加哪些头文件以及驱动常用头文件介绍(转)
原文链接:http://blog.chinaunix.net/uid-22609852-id-3506475.html 驱动常用头文件介绍 #include <linux/***.h> 是 ...
- 转:Linux网卡驱动程序编写
Linux网卡驱动程序编写 [摘自 LinuxAID] 工作需要写了我们公司一块网卡的Linux驱动程序.经历一个从无到有的过程,深感技术交流的重要.Linux作为挑战微软垄断的强有力武器,日益受到大 ...
- Linux设备驱动程序学习之分配内存
内核为设备驱动提供了一个统一的内存管理接口,所以模块无需涉及分段和分页等问题. 我已经在第一个scull模块中使用了 kmalloc 和 kfree 来分配和释放内存空间. kmalloc 函数内幕 ...
- linux设备驱动程序_hello word 模块编译各种问题集锦
在看楼经典书籍<linux设备驱动程序>后,第一个程序就是编写一个hello word 模块. 原以为非常easy,真正弄起来,发现问题不少啊.前两天编过一次,因为没有记录,今天看的时候又 ...
- linux设备驱动程序——将驱动程序编译进内核
linux驱动程序--将驱动程序编译进内核 模块的加载 通常来说,在驱动模块的开发阶段,一般是将模块编译成.ko文件,再使用 sudo insmod module.ko 或者 depmod -a mo ...
随机推荐
- 【先定一个小目标】Windows下Redis的安装使用
Redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set ...
- IO例子
1.用字节读取一个文件,替换换行符,并打印 String fileName = "src/learnIO/Stream.java"; FileInputStream in = ne ...
- vmware workstation安装 Mosrosoft Runtime DLL安装程序未能完成安装
不要点确定.开始菜单运行输入'%temp%',在弹出的窗体中找到一个文件名中含'{132E3257-14F1-411A-BC6C-0CA32D3A9BC6}~setup'(不一定一样,反正就是第一行的 ...
- [Machine Learning] logistic函数和softmax函数
简单总结一下机器学习最常见的两个函数,一个是logistic函数,另一个是softmax函数,若有不足之处,希望大家可以帮忙指正.本文首先分别介绍logistic函数和softmax函数的定义和应用, ...
- easyUI中onLoadSuccess方法获取多个对象
后台将一个list中的对象循环放到map中,如下: @RequestMapping("/getSaleOrderDetailList") @ResponseBody public ...
- Linux学习之十--.Net Core环境搭建以及Nginx的搭建
一.Centos7下.Net Core 环境安装: 链接:https://www.microsoft.com/net/core#linuxcentos 按照步骤来: yum install libun ...
- libvirt 网络手册(二):桥接网络
原文:Bridged Network 在一个桥接网络里面,宿主机和虚拟机共享物理网卡.每一个虚拟机可以直接绑定任意可用的IPV4或IPV6局域网地址,就像一个物理机一样.桥接给libvirt网络提供最 ...
- ORACLE 11G EXPDP交互模式 interactive mode
expdp导出工具可以实现在导出任务执行过程中修改并行度.终止.监控任务功能. 做实验验证一下: [oracle@test dpdump]$ expdp system/password directo ...
- robotframework,selenium启动不了打不开浏览器的问题访问不了网页
由于最近发现咨询火狐浏览器打不开的问题比较多,现罗列几点解决办法. 1,由于selenium更新3.0的原因导致不在默认支持火狐浏览器,且支持的火狐浏览器大概在45以上的版本,所以很多都由于这个原因导 ...
- 项目vue2.0仿外卖APP(六)
goods 商品列表页开发 布局编写 除了商品之外还有购物车,还有个详情页,挺复杂的. 两栏布局:左侧固定宽度,右侧自适应,还是用flex. 因为内容可能会超过手机高度,超过就隐藏.左右两侧的内容是可 ...