linux驱动开发之输入子系统编程(一)使用工作队列实现中断下半部
2018-01-31 16:14
921 查看
基本功能:使用工作队列实现中断下半部
在Linux内核中,可以通过工作队列来实现中断下半部。当中断发生时,将当前的进程加入到一个工作队列 struct work_struct中,加入到工作队列中的中断事件会通过队列的方式出队,得到处理。
新增功能:在linux内核中使用定时器消抖(工程需要)
1.linux内核使用 struct timer_list 结构对象来描述一个定时器
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;
unsigned long expires;
struct tvec_base *base;
2.linux内核中初始化一个定时器
//参数:timer—–struct timer_list结构对像
#define init_timer(timer)
3.linux内核中将一个定时器加入到内核
//参数:timer—–struct timer_list结构对像
void add_timer(struct timer_list *timer)
4.linux内核中开始启用定时器,
//参数1:timer—–struct timer_list结构对像
//参数2:expires—定时器的计数值
int mod_timer(struct timer_list *timer, unsigned long expires)
5.linux内核中使用 jiffies 变量来记录从系统启动到当前的计算值,表示为一个滴答数
6.linux内核中使用 HZ 代表 jiffies 1s中计数的次数, 通常为1000,既每次计数 1ms
input_simple.c程序
app_input.c程序
开发板型号:s5pv210
程序链接:https://pan.baidu.com/s/1jJhZmtw 密码:yy03
效果:
按下按键KEY_ESC,屏幕显示相关信息。
在Linux内核中,可以通过工作队列来实现中断下半部。当中断发生时,将当前的进程加入到一个工作队列 struct work_struct中,加入到工作队列中的中断事件会通过队列的方式出队,得到处理。
新增功能:在linux内核中使用定时器消抖(工程需要)
1.linux内核使用 struct timer_list 结构对象来描述一个定时器
struct timer_list {
/*
* All fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;
unsigned long expires;
struct tvec_base *base;
void (*function)(unsigned long); //当定时到达后,执行这个函数接口,有用户实现 unsigned long data; int slack;
#ifdef CONFIG_TIMER_STATS int start_pid; void *start_site; char start_comm[16]; #endif #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif };
2.linux内核中初始化一个定时器
//参数:timer—–struct timer_list结构对像
#define init_timer(timer)
3.linux内核中将一个定时器加入到内核
//参数:timer—–struct timer_list结构对像
void add_timer(struct timer_list *timer)
4.linux内核中开始启用定时器,
//参数1:timer—–struct timer_list结构对像
//参数2:expires—定时器的计数值
int mod_timer(struct timer_list *timer, unsigned long expires)
5.linux内核中使用 jiffies 变量来记录从系统启动到当前的计算值,表示为一个滴答数
6.linux内核中使用 HZ 代表 jiffies 1s中计数的次数, 通常为1000,既每次计数 1ms
input_simple.c程序
#include <linux/input.h> #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/gpio.h> #include <linux/workqueue.h> #include <linux/timer.h> #include <mach/gpio.h> #include <asm/irq.h> #include <asm/io.h> // //新增功能:在linux内核中使用定时器消抖 struct button_type { struct input_dev *button_dev; int irqno; struct work_struct work; struct timer_list buttons_timer;//定时器 }; struct button_type *button_input_dev; void Key_Dithering_timer(unsigned long arg) { if(gpio_get_value(S5PV210_GPH0(5))) { //上报数据 到 input handler input_report_key(button_input_dev->button_dev, KEY_ESC, 1); //同步数据到用户空间 input_sync(button_input_dev->button_dev); } else { //上报数据 到 input handler input_report_key(button_input_dev->button_dev, KEY_ESC, 0); //同步数据到用户空间 input_sync(button_input_dev->button_dev); } } void work_button_event(struct work_struct *work) { //linux内核中开始启用定时器 //参数1:timer-----struct timer_list结构对像 //参数2:expires---定时器的计数值 //linux内核中使用 jiffies 变量来记录从系统启动到当前的计算值,表示为一个滴答数 //linux内核中使用 HZ 代表 jiffies 1s中计数的次数, 通常为1000,既每次计数 1ms mod_timer(&button_input_dev->buttons_timer, jiffies+(HZ/50));//延时20ms } static irqreturn_t button_interrupt(int irq, void *dummy) { schedule_work(&button_input_dev->work);//把工作任务加入到全局队列 return IRQ_HANDLED; } static int __init button_init(void) { int error; // 1.实例化设备对象 button_input_dev = kzalloc(sizeof(struct button_type), GFP_KERNEL);//分配内存给结构体 if(IS_ERR(button_input_dev)) { printk(KERN_ERR"kzalloc struct button_type error!\n"); return -ENODEV; } // 2.申请 input 输入设备 button_input_dev->button_dev = input_allocate_device(); if (!button_input_dev->button_dev) { printk(KERN_ERR "Not enough memory\n"); error = -ENOMEM; goto err_free_zalloc; } // button_input_dev->button_dev->name = "KEY_ESC"; // 3.设置位表 button_input_dev->button_dev->evbit[0] |= BIT_MASK(EV_KEY); //支持按键事件 button_input_dev->button_dev->keybit[BIT_WORD(KEY_ESC)] |= BIT_MASK(KEY_ESC);//支持 KEY_ESC按键 button_input_dev->button_dev->keybit[BIT_WORD(KEY_UP)] |= BIT_MASK(KEY_UP);//支持 KEY_UP按键 // 4.将设备注册到input子系统中 error = input_register_device(button_input_dev->button_dev); if (error) { printk(KERN_ERR "Failed to register device\n"); goto err_free_dev; } // 5. 初始化硬件 button_input_dev->irqno = gpio_to_irq(S5PV210_GPH0(5)); if (request_irq(button_input_dev->irqno, button_interrupt, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "KEY_ESC", NULL)) { printk(KERN_ERR " Can't allocate irq %d\n", button_input_dev->irqno); error = -EBUSY; goto err_unregister_dev; } // 6.初始化工作队列 INIT_WORK(&button_input_dev->work, work_button_event); //初始化定时器 //参数:timer-----struct timer_list结构对象 init_timer(&button_input_dev->buttons_timer); button_input_dev->buttons_timer.function = Key_Dithering_timer; //将一个定时器加入到内核 add_timer(&button_input_dev->buttons_timer); return 0; err_unregister_dev: input_unregister_device(button_input_dev->button_dev); err_free_dev: input_free_device(button_input_dev->button_dev); err_free_zalloc: kfree(button_input_dev); return error; } static void __exit button_exit(void) { free_irq(button_input_dev->irqno, 0); input_unregister_device(button_input_dev->button_dev); input_free_device(button_input_dev->button_dev); kfree(button_input_dev); } module_init(button_init); module_exit(button_exit); MODULE_LICENSE("GPL");
app_input.c程序
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <linux/input.h> #include <unistd.h> int main(int argc,char ** argv) { int fd; int ret = 0; struct input_event button_event;//用户读取到输入设备的数据包 fd = open("/dev/event0", O_RDONLY, S_IRWXU); if(fd < 0) { perror("open filed!\n"); exit(1); } while(1) { ret = read(fd, &button_event, sizeof(struct input_event)); if(!ret) { printf("read button event filed!!\n"); exit(1); } if(button_event.type == EV_KEY)//表示是按键类型 { if(button_event.code == KEY_ESC)//哪一个按键 { if(button_event.value) { printf("---KEY_ESC---up---\n"); } else { printf("---KEY_ESC---down---\n"); } } } } return 0; }
开发板型号:s5pv210
程序链接:https://pan.baidu.com/s/1jJhZmtw 密码:yy03
效果:
按下按键KEY_ESC,屏幕显示相关信息。
相关文章推荐
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之工作队列详解
- linux 触摸屏驱动中断下半部实现-工作队列
- linux驱动开发--中断:工作者队列实现中断底半部
- Linux驱动开发十:按键中断之输入子系统
- Linux 驱动之中断下半部之工作队列
- Linux2.6内核--中断下半部实现方法 工作队列
- linux驱动开发--中断:tasklet实现中断底半部
- Linux驱动开发-中断分层机制_工作队列 笔记 7
- 【Linux开发】linux设备驱动归纳总结(六):3.中断的上半部和下半部——工作队列
- Linux2.6内核--中断下半部实现方法 工作队列
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之七姑八姨
- linux设备驱动--内核等待队列知识点---结合中断使用
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之tasklet详解
- 《Linux设备驱动开发详解》-- Linux中断处理底半部机制(tasklet、工作队列和软中断)
- linux 2.6 输入子系统 键盘驱动的实现
- linux 底层驱动中断分析和一般使用(顶半部与底半部)
- Linux2.6中断下半部分的三种实现机制---软中断/tasklet/工作队列
- Linux2.6中断下半部分的三种实现机制---工作队列
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之老大-软中断softirq
- linux驱动编程--工作队列浅析