前言

前面是如何操作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. sprint3总结 && sprint4计划

    sprint3总结 在一周时间里,逻辑部分顺利的将数据库,查词,UI部分连接到一起.并且各部分也针对新的要求做出了一些修改,目前数据库和查词alpha版已经完成,UI部分还需要一些美化,逻辑部分也还需 ...

  2. Python - 利用词云wordcloud,jieba和中国地图制作四大名著的热词图

    热词图很酷炫,也非常适合热点事件,抓住重点,以图文结合的方式表现出来,很有冲击力.下面这段代码是制作热词图的,用到了以下技术: jieba,把文本分词 wordcloud,制作热图 chardet,辨 ...

  3. Jmeter接口测试、性能测试详细介绍

    下面主要就是讲一下Jmeter工具的用法,用法非常简单,比起loadrunner不知道简单多少,并且开源免费~~ 1.接口简介 接口定义 接口: 就是数据交互的入口和出口,是一套标准规范. 接口(硬件 ...

  4. PHP函数:fopen

    fopen()  - 打开文件或者 URL. 注意:array_key_exists() 仅仅搜索第一维的键. 多维数组里嵌套的键不会被搜索到. 说明: fopen ( string $filenam ...

  5. Linux下nginx自启动配置

    1.在linux系统的/etc/init.d/目录下创建nginx文件 vim /etc/init.d/nginx 在脚本中添加一下命令(内容主要参考官方文档) #!/bin/sh # # nginx ...

  6. CVE-2019-0193 Apache solr velocity模块漏洞

    Solr简单介绍 Solr是建立在Apache Lucene ™之上的一个流行.快速.开放源代码的企业搜索平台. Solr具有高度的可靠性,可伸缩性和容错能力,可提供分布式索引,复制和负载平衡查询,自 ...

  7. selemiun 下拉菜单、复选框、弹框定位识别

    一.下拉菜单识别 对下拉框的操作,主要是通过Select 类里面的方法来实现的,所以需要new 一个Select 对象(org.openqa.selenium.support.ui.Select)来进 ...

  8. 005.Ansible de palybook简单使用

    一 Ansible Playbook简介 ansbile-playbook是一系列ansible命令的集合,利用yaml 语言编写.playbook命令根据自上而下的顺序依次执行.同时,playboo ...

  9. JS在线代码编辑器多种方案monaco-editor,vue-monaco-editor

    前言 JavaScript在线代码编辑器. 需要代码提示,关键字高亮,能够格式化代码.(不需要在线运行) 简简单单的需求. 方案一: Monaco-editor 简介:微软的开源项目,开源中国上面的在 ...

  10. Phaser都不懂,还学什么多线程

    前面的文章中我们讲到了CyclicBarrier.CountDownLatch的使用,这里再回顾一下CountDownLatch主要用在一个线程等待多个线程执行完毕的情况,而CyclicBarrier ...