前言

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

设备树

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

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

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

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

  1. static int gpio_parse_data(struct gpio_demo_device *di){
  2. int ret;
  3. struct gpio_platform_data *pdata;
  4. struct device *dev = di->dev;
  5. struct device_node *np = di->dev->of_node;
  6. pdata = devm_kzalloc(di->dev, sizeof(*pdata), GFP_KERNEL);
  7. if (!pdata) {
  8. return -ENOMEM;
  9. }
  10. di->pdata = pdata;
  11. // set default value for platform data
  12. pdata->mode = DEFAULT_MODE;
  13. pdata->poll_ms = DEFAULT_POLL_TIME * 1000;
  14. dev_info(dev,"parse platform data\n");
  15. ret = of_property_read_u32(np, "mode", &pdata->mode);
  16. if (ret < 0) {
  17. dev_err(dev, "can't get mode property\n");
  18. }
  19. ret = of_property_read_u32(np, "poll_time", &pdata->poll_ms);
  20. if (ret < 0) {
  21. dev_err(dev, "can't get poll_ms property\n");
  22. }
  23. pdata->gpio_index = of_get_named_gpio(np,"input-gpio", 0);
  24. if (pdata->gpio_index < 0) {
  25. dev_err(dev, "can't get input gpio\n");
  26. }
  27. // debug parse device tree data
  28. dev_info(dev, "Success:mode is %d\n", pdata->mode);
  29. dev_info(dev, "Success:gpio index is %d\n", pdata->gpio_index);
  30. return 0;
  31. }

两个结构体

gpio_platform_data

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

  1. struct gpio_platform_data {
  2. int mode;
  3. int count;
  4. int gpio_index;
  5. struct mutex mtx;
  6. int poll_ms;
  7. };

gpio_demo_device

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

  1. struct gpio_demo_device {
  2. struct platform_device *pdev;
  3. struct device *dev;
  4. struct gpio_platform_data *pdata;
  5. struct workqueue_struct *gpio_monitor_wq;
  6. struct delayed_work gpio_delay_work ;
  7. int gpio_irq;
  8. };

两种方式

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

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

轮询

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

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

  1. static void gpio_demo_work(struct work_struct *work) {
  2. struct gpio_demo_device *di = container_of(work,
  3. struct gpio_demo_device,
  4. gpio_delay_work.work);
  5. struct gpio_platform_data *padta = di->pdata;
  6. int gpio_index,value;
  7. //获取gpio索引号
  8. gpio_index = padta->gpio_index;
  9. if (!gpio_is_valid(gpio_index) ) {
  10. dev_err(di->dev, "gpio is not valid\n");
  11. goto end;
  12. }
  13. if ( (value = gpio_get_value(gpio_index) ) == 0) {
  14. dev_info(di->dev,"get value is %d\n",value);
  15. }else{
  16. dev_info(di->dev,"get value is %d\n",value);
  17. }
  18. end:
  19. queue_delayed_work(di->gpio_monitor_wq, &di->gpio_delay_work,
  20. msecs_to_jiffies(di->pdata->poll_ms));
  21. }

外部中断

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

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

  1. static int gpio_demo_init_interrupt(struct gpio_demo_device *di) {
  2. ...
  3. // 获取gpio上的中断号
  4. irq = gpio_to_irq(gpio_index);
  5. ...
  6. //申请中断
  7. ret = devm_request_irq(di->dev, irq, gpio_demo_isr,
  8. IRQF_TRIGGER_FALLING, //下降沿
  9. "gpio-demo-isr", //中断名称
  10. di);
  11. ...
  12. }

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

  1. static irqreturn_t gpio_demo_isr(int irq, void *dev_id)
  2. {
  3. struct gpio_demo_device *di = (struct gpio_demo_device *)dev_id;
  4. struct gpio_platform_data *pdata = di->pdata;
  5. BUG_ON(irq != gpio_to_irq(pdata->gpio_index));
  6. //TODO
  7. dev_info(di->dev, "%s\n", __func__);
  8. return IRQ_HANDLED;
  9. }

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

总结

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

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

附录

  1. #include <linux/module.h>
  2. #include <linux/init.h>
  3. #include <linux/platform_device.h>
  4. //API for libgpio
  5. #include <linux/gpio.h>
  6. //API for malloc
  7. #include <linux/slab.h>
  8. //API for device tree
  9. #include <linux/of_platform.h>
  10. #include <linux/of_gpio.h>
  11. #include <linux/of_device.h>
  12. //API for thread
  13. #include <linux/kthread.h>
  14. #include <linux/delay.h>
  15. #include <linux/mutex.h>
  16. //API for delaywork
  17. #include <linux/workqueue.h>
  18. #include <linux/interrupt.h>
  19. #include <linux/irq.h>
  20. #define TIMER_MS_COUNTS 1000
  21. // default value of dts
  22. #define DEFAULT_POLL_TIME 5
  23. #define DEFAULT_MODE 1
  24. struct gpio_platform_data {
  25. int mode;
  26. int count;
  27. int gpio_index;
  28. struct mutex mtx;
  29. int poll_ms;
  30. };
  31. struct gpio_demo_device {
  32. struct platform_device *pdev;
  33. struct device *dev;
  34. struct gpio_platform_data *pdata;
  35. struct workqueue_struct *gpio_monitor_wq;
  36. struct delayed_work gpio_delay_work ;
  37. int gpio_irq;
  38. };
  39. static int gpio_parse_data(struct gpio_demo_device *di){
  40. int ret;
  41. struct gpio_platform_data *pdata;
  42. struct device *dev = di->dev;
  43. struct device_node *np = di->dev->of_node;
  44. pdata = devm_kzalloc(di->dev, sizeof(*pdata), GFP_KERNEL);
  45. if (!pdata) {
  46. return -ENOMEM;
  47. }
  48. di->pdata = pdata;
  49. // set default value for platform data
  50. pdata->mode = DEFAULT_MODE;
  51. pdata->poll_ms = DEFAULT_POLL_TIME * 1000;
  52. dev_info(dev,"parse platform data\n");
  53. ret = of_property_read_u32(np, "mode", &pdata->mode);
  54. if (ret < 0) {
  55. dev_err(dev, "can't get mode property\n");
  56. }
  57. ret = of_property_read_u32(np, "poll_time", &pdata->poll_ms);
  58. if (ret < 0) {
  59. dev_err(dev, "can't get poll_ms property\n");
  60. }
  61. pdata->gpio_index = of_get_named_gpio(np,"input-gpio", 0);
  62. if (pdata->gpio_index < 0) {
  63. dev_err(dev, "can't get input gpio\n");
  64. }
  65. // debug parse device tree data
  66. dev_info(dev, "Success:mode is %d\n", pdata->mode);
  67. dev_info(dev, "Success:gpio index is %d\n", pdata->gpio_index);
  68. return 0;
  69. }
  70. static void gpio_demo_work(struct work_struct *work) {
  71. struct gpio_demo_device *di = container_of(work,
  72. struct gpio_demo_device,
  73. gpio_delay_work.work);
  74. struct gpio_platform_data *padta = di->pdata;
  75. int gpio_index,value;
  76. gpio_index = padta->gpio_index;
  77. if (!gpio_is_valid(gpio_index) ) {
  78. dev_err(di->dev, "gpio is not valid\n");
  79. goto end;
  80. }
  81. if ( (value = gpio_get_value(gpio_index) ) == 0) {
  82. dev_info(di->dev,"get value is %d\n",value);
  83. }else{
  84. dev_info(di->dev,"get value is %d\n",value);
  85. }
  86. end:
  87. queue_delayed_work(di->gpio_monitor_wq, &di->gpio_delay_work,
  88. msecs_to_jiffies(di->pdata->poll_ms));
  89. }
  90. static int gpio_demo_init_poll(struct gpio_demo_device *di) {
  91. dev_info(di->dev,"%s\n", __func__);
  92. di->gpio_monitor_wq = alloc_ordered_workqueue("%s",
  93. WQ_MEM_RECLAIM | WQ_FREEZABLE, "gpio-demo-wq");
  94. INIT_DELAYED_WORK(&di->gpio_delay_work, gpio_demo_work);
  95. queue_delayed_work(di->gpio_monitor_wq, &di->gpio_delay_work,
  96. msecs_to_jiffies(TIMER_MS_COUNTS * 5));
  97. return 0;
  98. }
  99. static irqreturn_t gpio_demo_isr(int irq, void *dev_id)
  100. {
  101. struct gpio_demo_device *di = (struct gpio_demo_device *)dev_id;
  102. struct gpio_platform_data *pdata = di->pdata;
  103. BUG_ON(irq != gpio_to_irq(pdata->gpio_index));
  104. dev_info(di->dev, "%s\n", __func__);
  105. //printk("%s\n",__func__);
  106. return IRQ_HANDLED;
  107. }
  108. static int gpio_demo_init_interrupt(struct gpio_demo_device *di) {
  109. int irq, ret;
  110. int gpio_index = di->pdata->gpio_index;
  111. dev_info(di->dev,"%s\n", __func__);
  112. if (!gpio_is_valid(gpio_index)){
  113. return -1;
  114. }
  115. irq = gpio_to_irq(gpio_index);
  116. if (irq < 0) {
  117. dev_err(di->dev, "Unable to get irq number for GPIO %d, error %d\n",
  118. gpio_index, irq);
  119. gpio_free(gpio_index);
  120. return -1;
  121. }
  122. ret = devm_request_irq(di->dev, irq, gpio_demo_isr,
  123. IRQF_TRIGGER_FALLING,
  124. "gpio-demo-isr",
  125. di);
  126. if (ret) {
  127. dev_err(di->dev, "Unable to claim irq %d; error %d\n",
  128. irq, ret);
  129. gpio_free(gpio_index);
  130. return -1;
  131. }
  132. return 0;
  133. }
  134. static int gpio_demo_probe(struct platform_device *pdev){
  135. int ret;
  136. struct gpio_demo_device *priv;
  137. struct device *dev = &pdev->dev;
  138. priv = devm_kzalloc(dev, sizeof(*priv) , GFP_KERNEL);
  139. if (!priv) {
  140. return -ENOMEM;
  141. }
  142. priv->dev = dev; //important
  143. ret = gpio_parse_data(priv);
  144. if (ret){
  145. dev_err(dev,"parse data failed\n");
  146. }
  147. platform_set_drvdata(pdev,priv);
  148. if (priv->pdata->mode == 0){
  149. gpio_demo_init_poll(priv);
  150. } else {
  151. gpio_demo_init_interrupt(priv);
  152. }
  153. return 0;
  154. }
  155. #ifdef CONFIG_OF
  156. static struct of_device_id gpio_demo_of_match[] = {
  157. { .compatible = "gpio-demo"},
  158. {},
  159. }
  160. MODULE_DEVICE_TABLE(of,gpio_demo_of_match);
  161. #else
  162. static struct of_device_id gpio_demo_of_match[] = {
  163. { },
  164. }
  165. #endif
  166. static struct platform_driver gpio_demo_driver = {
  167. .probe = gpio_demo_probe,
  168. .driver = {
  169. .name = "gpio-demo-device",
  170. .owner = THIS_MODULE,
  171. .of_match_table = of_match_ptr(gpio_demo_of_match),
  172. }
  173. };
  174. static int __init gpio_demo_init(void){
  175. return platform_driver_register(&gpio_demo_driver);
  176. }
  177. static void __exit gpio_demo_exit(void){
  178. platform_driver_unregister(&gpio_demo_driver);
  179. }
  180. late_initcall(gpio_demo_init);
  181. module_exit(gpio_demo_exit);
  182. MODULE_LICENSE("GPL");
  183. MODULE_DESCRIPTION("Gpio demo Driver");
  184. 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. 泛型方法或泛型类中的方法是内部调用、PInvoke 或是在 COM 导入类中定义的。

    泛型基类中引用Api函数定义时static extern,在子类中会提示: 未处理TypeLoadException 泛型方法或泛型类中的方法是内部调用.PInvoke 或是在 COM 导入类中定义的 ...

  2. Problem L. World Cup

    题目大意:有A,B,C,D四个队伍,两两之间有一个比赛,假如A和B比赛,如果平局,各加一分,如果说A胜,给A加3分,不给B加分,B胜同理 给出A,B,C,D,的得分,判断形成这种局面有多少种方式. 思 ...

  3. Java数组 —— 八大排序

    (请观看本人博文--<详解 普通数组 -- Arrays类 与 浅克隆>) 在本人<数据结构与算法>专栏的讲解中,本人讲解了如何去实现数组的八大排序. 但是,在讲解的过程中,我 ...

  4. 4. git log的常见用法

    git log ======见https://blog.csdn.net/daguanjia11/article/details/73823617 +++++++++++++++++++++++ 使用 ...

  5. SVM家族(一)

    SVM家族简史 故事要从20世纪50年代说起,1957年,一个叫做感知器的模型被提出, 1963年, Vapnikand Chervonenkis, 提出了最大间隔分类器,SVM诞生了. 1992年, ...

  6. Mac下打开 kvm

    mac 下打开 kvm ,需要装这个 https://www.xquartz.org/

  7. web自动化中pytest框架的使用(二)---参数化

    1.pytest--参数化 在测试用例的前面加上@pytest.mark.parametrize("参数名",列表数据) 参数名:用来接收每一项数据,并作为测试用例的参数 列表数据 ...

  8. 架构设计 | 分布式业务系统中,全局ID生成策略

    本文源码:GitHub·点这里 || GitEE·点这里 一.全局ID简介 在实际的开发中,几乎所有的业务场景产生的数据,都需要一个唯一ID作为核心标识,用来流程化管理.比如常见的: 订单:order ...

  9. xpath爬虫实战-爬取小说斗罗大陆第四部

    爬取思路 用到的第三方库文件 lxml,requests,fake_agent 用fake_agent里的UserAgent修饰爬虫 用requests进行基本的请求 用lxml进行html的分析 用 ...

  10. PHP使用token防止表单重复提交的方法

    本文实例讲述了PHP使用token防止表单重复提交的方法.分享给大家供大家参考,具体如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2 ...