您的位置:首页 > 运维架构 > Linux

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;

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,屏幕显示相关信息。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐