前言

前面是如何操作GPIO进行输出,这里我重新实现了一个gpio的驱动,可以获取外部信号的输入。gpio-demo.c中已经包括检测一个gpio的信号,并且包含了中断和轮询两种方式,可以通过设备树里的mode属性进行选择。

设备树

本文检测的输入引脚是GPIO3_D0,具体的设备树如下所示;

gpio-demo {
compatible = "gpio-demo";
input-gpio = <&gpio3 RK_PD0 GPIO_ACTIVE_LOW>;
mode = <1>; // 0:poll 1:interrupt
poll_time = <1000>; //ms
};
  • compatible:设备兼容属性为gpio-demo,与后面的驱动代码中的

    gpio_demo_of_match[] = { { .compatible = "gpio-demo"}, {}, } 需要相同;
  • input-gpio:这个属性值通过of_get_named_gpio来获取;
  • mode:用于判断当前的工作模式是轮询还是中断;
  • poll_time:轮询模式下的周期,间隔多少毫秒会读取一次gpio的状态;

对于设备树的解析,单独封装了一个接口;

static int gpio_parse_data(struct gpio_demo_device *di){

	int ret;
struct gpio_platform_data *pdata;
struct device *dev = di->dev;
struct device_node *np = di->dev->of_node; pdata = devm_kzalloc(di->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
return -ENOMEM;
}
di->pdata = pdata;
// set default value for platform data
pdata->mode = DEFAULT_MODE;
pdata->poll_ms = DEFAULT_POLL_TIME * 1000; dev_info(dev,"parse platform data\n"); ret = of_property_read_u32(np, "mode", &pdata->mode);
if (ret < 0) {
dev_err(dev, "can't get mode property\n");
}
ret = of_property_read_u32(np, "poll_time", &pdata->poll_ms);
if (ret < 0) {
dev_err(dev, "can't get poll_ms property\n");
} pdata->gpio_index = of_get_named_gpio(np,"input-gpio", 0);
if (pdata->gpio_index < 0) {
dev_err(dev, "can't get input gpio\n");
}
// debug parse device tree data
dev_info(dev, "Success:mode is %d\n", pdata->mode);
dev_info(dev, "Success:gpio index is %d\n", pdata->gpio_index);
return 0;
}

两个结构体

gpio_platform_data

gpio_platform_data主要是对设备树中众多属性的封装;

struct gpio_platform_data {
int mode;
int count;
int gpio_index;
struct mutex mtx;
int poll_ms;
};

gpio_demo_device

gpio_demo_device是与设备驱动中相关资源的封装,包括工作队列等等;

struct gpio_demo_device {
struct platform_device *pdev;
struct device *dev;
struct gpio_platform_data *pdata;
struct workqueue_struct *gpio_monitor_wq;
struct delayed_work gpio_delay_work ;
int gpio_irq;
};

两种方式

在驱动的probe函数中,先通过gpio_parse_data解析设备树文件,从而获取mode属性的值:

  • 0gpio_demo_init_poll初始化进入轮询工作模式;
  • 1gpio_demo_init_interrupt初始化进入中断工作模式;
static int gpio_demo_probe(struct platform_device *pdev){
...
ret = gpio_parse_data(priv);
if (ret){
dev_err(dev,"parse data failed\n");
}
...
if (priv->pdata->mode == 0){
gpio_demo_init_poll(priv); //轮询
} else {
gpio_demo_init_interrupt(priv);//中断
}
}

轮询

在轮询工作模式下,已经通过gpio_demo_init_poll对工作队列进行初始化,之后,后启动运行gpio_demo_work任务,并在规定的调度时间内,重复检测运行这个任务。

通过gpio_get_value(gpio_index)读取GPIO3_D0上的电平状态,如果需要对边沿信号进行处理还需要做改动,本文只能对电平信号进行处理。

static void gpio_demo_work(struct work_struct *work) {

	struct gpio_demo_device *di = container_of(work,
struct gpio_demo_device,
gpio_delay_work.work); struct gpio_platform_data *padta = di->pdata;
int gpio_index,value;
//获取gpio索引号
gpio_index = padta->gpio_index;
if (!gpio_is_valid(gpio_index) ) {
dev_err(di->dev, "gpio is not valid\n");
goto end;
}
if ( (value = gpio_get_value(gpio_index) ) == 0) {
dev_info(di->dev,"get value is %d\n",value);
}else{
dev_info(di->dev,"get value is %d\n",value);
}
end:
queue_delayed_work(di->gpio_monitor_wq, &di->gpio_delay_work,
msecs_to_jiffies(di->pdata->poll_ms));
}

外部中断

中断的申请和初始化在gpio_demo_init_interrupt函数中已经实现,如下所示;

通过gpio_to_irq接口获取相应GPIO上的软件中断号,然后通过devm_request_irq申请中断;

static int gpio_demo_init_interrupt(struct gpio_demo_device *di) {
...
// 获取gpio上的中断号
irq = gpio_to_irq(gpio_index);
...
//申请中断
ret = devm_request_irq(di->dev, irq, gpio_demo_isr,
IRQF_TRIGGER_FALLING, //下降沿
"gpio-demo-isr", //中断名称
di);
...
}

其中,每次外部发送一个下降沿信号,就会触发中断并进入gpio_demo_isr这个中断服务程序;下面来看一下这个gpio_demo_isr,在这里可以做一些我们想做的事情;

static irqreturn_t gpio_demo_isr(int irq, void *dev_id)
{
struct gpio_demo_device *di = (struct gpio_demo_device *)dev_id;
struct gpio_platform_data *pdata = di->pdata; BUG_ON(irq != gpio_to_irq(pdata->gpio_index));
//TODO
dev_info(di->dev, "%s\n", __func__);
return IRQ_HANDLED;
}

最终,我只在中断服务程序中打印了一下串口信息,方便验证。

总结

通过这次学习和总结,总体了解了以下几点;

  • 通过delayed_workGPIO进行轮询操作,后面会再深入学习一下;
  • 学习了对于GPIO上的中断申请,目前对于中断还是刚好够用的阶段,中断的篇幅较长,可以对其原理做一下学习,还有内核中中断的机制;
  • 学习了内核中读取设备树的几个接口;
  • 学习了platform设备驱动模型的框架;

附录

#include <linux/module.h>
#include <linux/init.h> #include <linux/platform_device.h>
//API for libgpio
#include <linux/gpio.h>
//API for malloc
#include <linux/slab.h>
//API for device tree
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
//API for thread
#include <linux/kthread.h> #include <linux/delay.h>
#include <linux/mutex.h>
//API for delaywork
#include <linux/workqueue.h> #include <linux/interrupt.h>
#include <linux/irq.h> #define TIMER_MS_COUNTS 1000 // default value of dts
#define DEFAULT_POLL_TIME 5
#define DEFAULT_MODE 1 struct gpio_platform_data {
int mode;
int count;
int gpio_index;
struct mutex mtx;
int poll_ms;
}; struct gpio_demo_device { struct platform_device *pdev;
struct device *dev;
struct gpio_platform_data *pdata;
struct workqueue_struct *gpio_monitor_wq;
struct delayed_work gpio_delay_work ;
int gpio_irq;
}; static int gpio_parse_data(struct gpio_demo_device *di){ int ret;
struct gpio_platform_data *pdata;
struct device *dev = di->dev;
struct device_node *np = di->dev->of_node; pdata = devm_kzalloc(di->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) {
return -ENOMEM;
}
di->pdata = pdata;
// set default value for platform data
pdata->mode = DEFAULT_MODE;
pdata->poll_ms = DEFAULT_POLL_TIME * 1000; dev_info(dev,"parse platform data\n"); ret = of_property_read_u32(np, "mode", &pdata->mode);
if (ret < 0) {
dev_err(dev, "can't get mode property\n");
}
ret = of_property_read_u32(np, "poll_time", &pdata->poll_ms);
if (ret < 0) {
dev_err(dev, "can't get poll_ms property\n");
} pdata->gpio_index = of_get_named_gpio(np,"input-gpio", 0);
if (pdata->gpio_index < 0) {
dev_err(dev, "can't get input gpio\n");
}
// debug parse device tree data
dev_info(dev, "Success:mode is %d\n", pdata->mode);
dev_info(dev, "Success:gpio index is %d\n", pdata->gpio_index);
return 0;
} static void gpio_demo_work(struct work_struct *work) { struct gpio_demo_device *di = container_of(work,
struct gpio_demo_device,
gpio_delay_work.work); struct gpio_platform_data *padta = di->pdata;
int gpio_index,value;
gpio_index = padta->gpio_index;
if (!gpio_is_valid(gpio_index) ) {
dev_err(di->dev, "gpio is not valid\n");
goto end;
}
if ( (value = gpio_get_value(gpio_index) ) == 0) {
dev_info(di->dev,"get value is %d\n",value);
}else{
dev_info(di->dev,"get value is %d\n",value);
}
end:
queue_delayed_work(di->gpio_monitor_wq, &di->gpio_delay_work,
msecs_to_jiffies(di->pdata->poll_ms));
} static int gpio_demo_init_poll(struct gpio_demo_device *di) { dev_info(di->dev,"%s\n", __func__); di->gpio_monitor_wq = alloc_ordered_workqueue("%s",
WQ_MEM_RECLAIM | WQ_FREEZABLE, "gpio-demo-wq"); INIT_DELAYED_WORK(&di->gpio_delay_work, gpio_demo_work);
queue_delayed_work(di->gpio_monitor_wq, &di->gpio_delay_work,
msecs_to_jiffies(TIMER_MS_COUNTS * 5)); return 0;
} static irqreturn_t gpio_demo_isr(int irq, void *dev_id)
{
struct gpio_demo_device *di = (struct gpio_demo_device *)dev_id;
struct gpio_platform_data *pdata = di->pdata; BUG_ON(irq != gpio_to_irq(pdata->gpio_index)); dev_info(di->dev, "%s\n", __func__);
//printk("%s\n",__func__);
return IRQ_HANDLED;
} static int gpio_demo_init_interrupt(struct gpio_demo_device *di) { int irq, ret;
int gpio_index = di->pdata->gpio_index;
dev_info(di->dev,"%s\n", __func__); if (!gpio_is_valid(gpio_index)){
return -1;
} irq = gpio_to_irq(gpio_index); if (irq < 0) {
dev_err(di->dev, "Unable to get irq number for GPIO %d, error %d\n",
gpio_index, irq);
gpio_free(gpio_index);
return -1;
}
ret = devm_request_irq(di->dev, irq, gpio_demo_isr,
IRQF_TRIGGER_FALLING,
"gpio-demo-isr",
di);
if (ret) {
dev_err(di->dev, "Unable to claim irq %d; error %d\n",
irq, ret);
gpio_free(gpio_index);
return -1;
} return 0;
} static int gpio_demo_probe(struct platform_device *pdev){ int ret;
struct gpio_demo_device *priv;
struct device *dev = &pdev->dev; priv = devm_kzalloc(dev, sizeof(*priv) , GFP_KERNEL); if (!priv) {
return -ENOMEM;
}
priv->dev = dev; //important ret = gpio_parse_data(priv);
if (ret){
dev_err(dev,"parse data failed\n");
} platform_set_drvdata(pdev,priv); if (priv->pdata->mode == 0){
gpio_demo_init_poll(priv);
} else {
gpio_demo_init_interrupt(priv);
}
return 0;
}
#ifdef CONFIG_OF
static struct of_device_id gpio_demo_of_match[] = {
{ .compatible = "gpio-demo"},
{},
} MODULE_DEVICE_TABLE(of,gpio_demo_of_match);
#else
static struct of_device_id gpio_demo_of_match[] = {
{ },
}
#endif static struct platform_driver gpio_demo_driver = {
.probe = gpio_demo_probe,
.driver = {
.name = "gpio-demo-device",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpio_demo_of_match),
}
}; static int __init gpio_demo_init(void){
return platform_driver_register(&gpio_demo_driver);
} static void __exit gpio_demo_exit(void){
platform_driver_unregister(&gpio_demo_driver);
} late_initcall(gpio_demo_init);
module_exit(gpio_demo_exit); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Gpio demo Driver");
MODULE_ALIAS("platform:gpio-demo");

Linux内核驱动学习(九)GPIO外部输入的处理的更多相关文章

  1. Linux内核驱动学习(八)GPIO驱动模拟输出PWM

    文章目录 前言 原理图 IO模拟输出PWM 设备树 驱动端 调试信息 实验结果 附录 前言 上一篇的学习中介绍了如何在用户空间直接操作GPIO,并写了一个脚本可以产生PWM.本篇的学习会将写一个驱动操 ...

  2. Linux内核驱动学习(六)GPIO之概览

    文章目录 前言 功能 如何使用 设备树 API 总结 前言 GPIO(General Purpose Input/Output)通用输入/输出接口,是十分灵活软件可编程的接口,功能强大,十分常用,SO ...

  3. linux内核驱动学习指南

    1. 参考链接 小白的博客 ONE_Tech 你为什么看不懂Linux内核驱动源码? 求教怎么学习linux内核驱动

  4. Linux内核驱动学习(二)添加自定义菜单到内核源码menuconfig

    文章目录 目标 drivers/Kconfig demo下的Kconfig 和 Makefile Kconfig Makefile demo_gpio.c 目标 Kernel:Linux 4.4 我编 ...

  5. Linux内核驱动学习(三)字符型设备驱动之初体验

    Linux字符型设备驱动之初体验 文章目录 Linux字符型设备驱动之初体验 前言 框架 字符型设备 程序实现 cdev kobj owner file_operations dev_t 设备注册过程 ...

  6. Linux内核驱动学习(七)应用层直接操作GPIO

    文章目录 简介 原理图 节点 设置为输出 设置为输入 映射关系 debugfs pwm demo 简介 前面通过libgpio的方式介绍了内核空间对GPIO进行操作的接口,其做了较好的封装,同时Lin ...

  7. Linux内核驱动学习(十)Input子系统详解

    文章目录 前言 框架 如何实现`input device` 设备驱动? 头文件 注册input_dev设备 上报按键值 dev->open()和dev->close() 其他事件类型,处理 ...

  8. Linux内核驱动学习(一)编写最简单Linux内核模块HelloWorld

    文章目录 准备工作 什么是内核模块 编写 hello.c 模块编译 相关指令 测试结果 模块加载 模块卸载 准备工作 在进行以下操作前,首先我准备了一台电脑,并且安装了虚拟机,系统是Ubuntu16. ...

  9. Linux内核驱动学习(四)Platform设备驱动模型

    Linux platform设备驱动模型 文章目录 Linux platform设备驱动模型 前言 框架 设备与驱动的分离 设备(device) 驱动(driver) 匹配(match) 参考 前言 ...

随机推荐

  1. stand up meeting 12/9/2015

    part 组员 今日工作 工作耗时/h 明日计划 工作耗时/h UI 冯晓云  --------------    --  -----------  -- PDF Reader 朱玉影 SDK终于差不 ...

  2. STL迭代器的使用

    STL的迭代器听起来怪吓人的,其实并不是什么高深的东西,说白了就是定义了一个指向STL的指针.. 对于没个STIL都可以定义 set,,vector ,,map,,,string 定义: set< ...

  3. I - Red and Black DFS

    There is a rectangular room, covered with square tiles. Each tile is colored either red or black. A ...

  4. search(6)- elastic4s-CRUD

    如果我们把ES作为某种数据库来使用的话,必须熟练掌握ES的CRUD操作.在这之前先更正一下上篇中关于检查索引是否存在的方法:elastic4s的具体调用如下: //删除索引 val rspExists ...

  5. Maven+JSP+Servlet+JDBC+Redis+Mysql实现的黑马旅游网

    项目简介 项目来源于:https://gitee.com/haoshunyu/travel 本系统是基于Maven+JSP+Servlet+JdbcTemplate+Redis+Mysql实现的旅游网 ...

  6. 22-Java-Hibernate框架(二)

    Hibernate的了解.Hibernate的搭建.Hibernate的基本使用流程等内容请阅读21-Java-Hibernate(一) 五.Hibernate的Query查询接口(重中之重) 1.H ...

  7. 干货!Python中字符串、列表、元祖、字典,集合之间的转换

    一.字符串的转化 1.字符串转换成列表 字符串转换成list 的时候,str可以作为迭代对象,直接放入:也可以使用split对字符串进行切割.然后返回list s = '1a1b1c' print(l ...

  8. 使用Reactor响应式编程

    介绍 响应式编程 响应式编程不同于我们熟悉的命令式编程,我们熟悉的命令式编程即代码就是一行接一行的指令,按照它们的顺序一次一条地出现.一个任务被执行,程序就需要等到它执行完了,才能执行下一个任务.每一 ...

  9. beego rel/reverse

    用户可以发布多个文章 对用户来说是一对多 对文章来说是多对一 用户是主表 文章是用户的从表 rel用在从表中,给主表结构体设置主键,也就是文章表对应用户表的外键 reverse用在主表中,指定主表与从 ...

  10. JVM原理与深度调优(三)

    jvm垃圾收集算法 1.引用计数算法每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收.此方法简单,无法解决对象相互循环引用的问题.还有一个问题是如何解决精准计 ...