GPIOLIB框架下的GPIO驱动
一、GPIOLIB 简介
GPIO(通用目的输入/输出端口)是一种灵活的软件控制的数字信号。大多数的嵌入式 处理器都引出一组或多组的 GPIO,并且部分普通管脚通过配置可以复用为 GPIO。
利用可 编程逻辑器件,或总线(如 I2C、SPI)转 GPIO 芯片,也可以扩展系统的 GPIO。不管是何种 GPIO,GPIOLIB 为内核和用户层都提供了标准的操作方法。
GPIOLIB 的接口十分简洁。在 GPIOLIB,所有的 GPIO 都是用整形的 GPIO 编号标识。 只要获得要操作 GPIO 的编号,就可以调用 GPIOLIB 提供的方法操作 GPIO。
二、GPIOLIB 的内核接口
GPIOLIB 的内核接口是指:若某些 GPIO 在 GPIOLIB 框架下被驱动后,GPIOLIB 为内 核的其它代码操作该 GPIO 而提供的标准接口。
1. GPIO 的申请和释放
GPIO 在使用前,必须先调用 gpio_request()函数申请 GPIO:
int gpio_request(unsigned gpio, const char *label);
该函数的 gpio 参数为 GPIO 编号;label 参数为 GPIO 的标识字符串,可以随意设定。 若该函数调用成功,将返回 0 值;否则返回非 0 值。gpio_request()函数调用失败的原因可能为 GPIO 的编号不存在,或在其它地方已经申请了该 GPIO 编号而还没有释放。
当 GPIO 使用完成后,应当调用 gpio_free()函数释放 GPIO:
void gpio_free(unsigned gpio);
2. GPIO 的输出控制
在操作 GPIO 输出信号前,需要调用 gpio_direction_output()函数把 GPIO 设置为输出方向:
int gpio_direction_output(unsigned gpio, int value);
把 GPIO 设置为输出方向后,参数 value 为默认的输出电平:1 为高电平;0 为低电平。
GPIO 被设置为输出方向后,就可以调用 gpio_set_value()函数控制 GPIO 输出高电平或 低电平:
void gpio_set_value(unsigned gpio, int value);
该函数的 value 参数可取值为:1 为高电平;0 为低电平。
3. GPIO 的输入控制
当需要从 GPIO 读取输入电平状态前,需要调用 gpio_direction_input()函数设置 GPIO 为 输入方向:
int gpio_direction_input(unsigned gpio);
在 GPIO 被设置为输入方向后,就可以调用 gpio_get_value()函数读取 GPIO 的输入电平 状态:
int gpio_get_value(unsigned gpio);
该函数的返回值为 GPIO 的输入电平状态:1 为高电平;0 为低电平。
4. GPIO 的中断映射
大多数的嵌入式处理器的 GPIO 引脚在被设置为输入方向后,可以用于外部中断信号的 输入。这些中断号和 GPIO 编号通常有对应关系,因此 GPIOLIB 为这些 GPIO 提供了 gpio_to_irq()函数用于通过 GPIO 编号而获得该 GPIO 中断号:
int gpio_to_irq(unsigned gpio);
gpio_to_irq()函数调用完成后,返回 GPIO 中断号。
由于并不是所有的 GPIO 都可以作为外部中断信号输入端口,所以 gpio_to_irq()函数不 是对所有的 GPIO 都强制实现的。
三、GPIOLIB的实现方法
大部分的嵌入式处理器的 GPIO 都是分组的。以 i.MX28 系列的处理器为例,所有 GPIO 被分为 5 组,每组 GPIO 数量从 20 到 32 不等。之所以把 GPIO 分组,是因为每组 GPIO 的 操作寄存器是相同或相近的。若 GPIO 是用可编程逻辑器件或总线(如 I2C、SPI 等)转 GPIO 扩展的,也需要按实现情况对其 GPIO 分组。GPIOLIB 对系统的所有 GPIO 统一编号,而每 组的 GPIO 编号都是连续的。
GPIOLIB 对每组 GPIO 都用一个 gpio_chip 对象来实现其驱动,其定义如所示:
1 struct gpio_chip { 2 const char *label; 3 struct device *dev; 4 struct module *owner; 5 struct list_head list; 6 7 int (*request)(struct gpio_chip *chip, unsigned offset); 9 void (*free)(struct gpio_chip *chip, unsigned offset); 11 int (*get_direction)(struct gpio_chip *chip, unsigned offset); 13 int (*direction_input)(struct gpio_chip *chip, unsigned offset); 15 int (*direction_output)(struct gpio_chip *chip, unsigned offset, int value); 17 int (*get)(struct gpio_chip *chip, unsigned offset); 19 void (*set)(struct gpio_chip *chip, unsigned offset, int value); 21 int (*set_debounce)(struct gpio_chip *chip, unsigned offset, unsigned debounce); 25 int (*to_irq)(struct gpio_chip *chip, unsigned offset); 28 void (*dbg_show)(struct seq_file *s, struct gpio_chip *chip); 30 int base; 31 u16 ngpio; 32 struct gpio_desc *desc; 33 const char *const *names; 34 bool can_sleep; 35 bool exported; 36 37 #if defined(CONFIG_OF_GPIO) 38 /* 39 * If CONFIG_OF is enabled, then all GPIO controllers described in the 40 * device tree automatically may have an OF translation 41 */ 42 struct device_node *of_node; 43 int of_gpio_n_cells; 44 int (*of_xlate)(struct gpio_chip *gc, 45 const struct of_phandle_args *gpiospec, u32 *flags); 46 #endif 47 #ifdef CONFIG_PINCTRL 48 /* 49 * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally 50 * describe the actual pin range which they serve in an SoC. This 51 * information would be used by pinctrl subsystem to configure 52 * corresponding pins for gpio usage. 53 */ 54 struct list_head pin_ranges; 55 #endif 56 };
下面介绍 gpio_chip 的部分成员:
base 该组 GPIO 的起始值。
ngpio 该组 GPIO 的数量。
owner 该成员表示所有者,一般设置为 THIS_MODULE。
request 在对该组的 GPIO 调用 gpio_request()函数时,该成员指向的实现函数将被调用。 在该成员指向的实现函数中,通常需要执行指定 GPIO 的初始化操作。
在实现函数中都是用索引值来区别组内的 GPIO。索引值是指组内的某一 GPIO 编号相 对于该组 GPIO 起始值(base)的偏移量,例如,组内第 1 个 GPIO 的索引值为 0、第 2 个 GPIO 的索引值为 1„„ 实现函数的 offset 参数为要操作 GPIO 的索引值(以下相同)。
free 在对该组的 GPIO 调用 gpio_free()函数时,该成员指向的实现函数将被调用。在该 成员的实现函数中,通常需要执行指定 GPIO 硬件资源的释放操作。
direction_input 在对该组的 GPIO 调用 gpio_direction_input()函数时,该成员指向的实 现函数将被调用。在该成员的实现函数中,需要把指定的 GPIO 设置为输入方向。
get 在对该组的 GPIO 调用 gpio_get_value()函数时,该成员指向的实现函数将被调用。 在该成员的实现函数中,需要返回指定 GPIO 的电平输入状态。
direction_output 在对该组的 GPIO 调用 gpio_direction_output()函数时,该成员指向的实 现函数将被调用。在该成员的实现函数中,需要把指定的 GPIO 设置为输出方向。
set 在对该组的 GPIO 调用 gpio_set_value()函数时,该成员指向的实现函数将被调用。 在该成员的实现函数中,需要把指定的 GPIO 设置为指定的电平输出状态。
当 GPIO 控制器初始化完成后,就可以调用 gpiochip_add()函数注册到内核:
int gpiochip_add(struct gpio_chip *chip);
该函数调用成功后,将返回 0 值;否则将返回非 0 值。
在给一组 GPIO 安排编号时,注意不要和其它 GPIO 组的编号有重叠,否则会造成注册GPIO 控制器的出错。
对于每种处理器平台,其最大 GPIO 编号值都由 ARCH_NR_GPIOS 宏设定的。在gpiolib.c中有引用
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
ARCH_NR_GPIOS 宏的值会限制系统的 GPIO 数量。当需要为系统添加 GPIO 而 受到该值的制约时,解决办法时是把该值改成足够大,然后重新编译内核,并把新的内核固 件烧写到目标机。
当需要把 GPIO 控制器从内核注销时,可以调用 gpiochip_remove()函数:
int __must_check gpiochip_remove(struct gpio_chip *chip);
该函数也需要检查返回值:0 值为注销成功;非 0 值为注销失败。注销失败的原因可能 是有 GPIO 正被使用。
具体实现,以IMX283为例,其源码是\drivers\gpio\gpio-mxs.c文件。
四、驱动测试
给 gpio_chip 结构体的 base = 160; ngpio = 2;
编译好驱动之后, 在/sys/class/gpio/目录可看到新添加的控制器
进入 gpiochip160 目录,可以看到新添加的 GPIO 控制器的属性文件
在 base 属性文件中可以看到该控制器的 GPIO 始起值;在 ngpio 属性文件中可以看到该 控制器的 GPIO 数量
在/sys/class/gpio/export 中,可以导出 160 和 161 的 GPIO
gpio160 目录包含了 GPIO3_4 的控制属性文件
这时 direction 属性文件的默认值为输入
输入下面命令,在 direction 属性文件设置 GPIO 为输出工作状态
# echo out >direction
这时在 value 属性文件分别设置 1 和 0 值,在 GPIO 分别输出高电平和低电平
五、在 C 程序中操作 GPIO
使用系统调用实现 GPIO 输入输出操作时,首先需要使用 export 属性文件导出 GPIO:
可以调用 write 函数向 direction 设备写入方向 in/out 字符串,将 GPIO 设置为输入(输 出):
GPIO 设置为输入时使用 read 系统调用读取 value 属性文件,就可以读取 GPIO 电平值。 GPIO 设置为输出时,使用 write 系统调用向 value 属性文件写入 0 或 1 字符串,就可以设置 GPIO 电平值:
注: Linux代码摘取的是3.14版本
原文地址:https://www.cnblogs.com/-tbd-/p/12918485.html
- LCA 最近公共祖先
- RMQ问题(线段树算法,ST算法优化)
- 统计0到n之间1的个数[数学,动态规划dp](经典,详解)
- Selenium2+python自动化40-cookie相关操作
- 【干货】PyTorch实例:用ResNet进行交通标志分类
- 2017年浙江理工大学程序设计竞赛校赛 题解&源码(A.水, D. 简单贪心 ,E.数论,I 暴力)
- Selenium2+python自动化41-绕过验证码(add_cookie)
- C语言求最小公倍数和最大公约数三种算法(经典)
- Selenium2+python自动化47-判断弹出框存在(alert_is_present)
- Free Pascal初次体验(有亮点哦)
- HDU 1312 Red and Black(DFS,板子题,详解,零基础教你代码实现DFS)
- Selenium2+python自动化48-登录方法(参数化)
- 51Nod 1003 阶乘后面0的数量(数学,思维题)
- 如何查看某个用户指定时间段的ABAP开发记录
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- R语言入门之t检验(t test)
- R语言入门之饼图
- 加速Spark编译
- 自定义方便kubectl中pods的管理
- R语言入门之切尾均值(trimmed mean)与绝对中位差(median absolute deviation,mad)
- 盘一盘 Python 特别篇 20 - SciPy 稀疏矩阵
- Spark 3.0.0-SNAPSHOT Access Kerberized HDFS
- Spark Nightly Builds
- R语言入门之基本统计量
- K8S 生态周报| Istio 已修复导致 Pod 崩溃的 bug
- Spark Kubernetes 的源码分析系列 - submit
- 如何交互可视化 Roam Research 局部笔记网络?
- Spark Kubernetes 的源码分析系列 - features
- Spark Kubernetes 的源码分析系列 - scheduler
- 简易Ramdisk 镜像制作(基于Centos7+)