前言

前面是如何操作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-7

    weekend updates: 1.答题界面和结果界面的跳转和数据传输已全部完成. 2.答题界面完成简单的getRankingData API结果展示,答题时间,错误数目和错题题目的展示,点击题目可 ...

  2. jmeter if控制器使用

    if控制器有两种用法 1.不勾选“interpret condition as variable expression” 直接输入我们需要判断的表达式即可,判断表达式为真时,执行if控制器下的请求 2 ...

  3. Java 导出Excel xlsx、xls, CSV文件

    通用导出功能: 1.支持Excel xlsx.xls 2.支持CSV文件导出 3.数据库查询分页导出.内存导出 4.支持大批量数据导出 使用步骤如下 导入jar <dependency> ...

  4. Linux下jdk的安装和环境变量的配置

    Linux下jdk的安装和环境变量的配置 一.jdk的下载 方式一:在官网下载 http://www.oracle.com/technetwork/java/javase/downloads/inde ...

  5. VideoView--简单的设置全屏幕播放

    我说的最主要的是要在布局哪里设置一下,如: <com.example.mypalyer.fullScreen          android:id="@+id/videoView1& ...

  6. php正则匹配到字符串里面的a标签

    $cont = preg_replace('/<a href=\"(.*?)\".*?>(.*?)<\/a>/i','',$cont);

  7. 微信jssdk遇到的一些问题汇总

    1.用户手动去触发的接口可以直接调用比如wx.startRecord(); 但是写在页面加载完成里就无效,需要写在 wx.ready(function(){ wx.startRecord(); }); ...

  8. 2019-2020-1 20199329《Linux内核原理与分析》第七周作业

    <Linux内核原理与分析>第七周作业 一.本周内容概述: 对Linux系统如何创建一个新进程进行追踪 分析Linux内核创建一个新进程的过程 二.本周学习内容: 1.学习进程的描述 操作 ...

  9. Pascal 字符串

    Dancing with Strings http://baskent.edu.tr/~tkaracay/etudio/ders/prg/pascal/PasHTM1/pas/pasl1007.htm ...

  10. 同步类的基础AbstractQueuedSynchronizer(AQS)

    同步类的基础AbstractQueuedSynchronizer(AQS) 我们之前介绍了很多同步类,比如ReentrantLock,Semaphore, CountDownLatch, Reentr ...