LinuxGPIO中文文档
本文来自Linux官方文档英文版,由于需要使用Linux的GPIO进行实验,我翻译了这篇文档。
本文档描述了GPIO框架的使用者接口。注意它描述了新的基于描述符的接口。
不推荐使用的基于整数的GPIO接口请参考gpio-legacy.txt。
获取和使用GPIO的函数可以通过include以下文件来获得:
#include <linux/gpio/consumer.h>
基于描述符的GPIO接口的所有函数都是以gpiod_为前缀。 gpio_前缀用于传统接口。内核中的其他函数不应该使用这些前缀。强烈建议不要使用legacy的接口函数,新的代码应该使用<linux/gpio/consumer.h>中的基于描述符的接口函数。
获取和释放GPIO
使用基于描述符的接口时,GPIO被作为一个描述符来使用,
必须通过调用gpiod_get()函数来获取该描述符。像许多其他内核子系统一样,gpiod_get()接收将使用GPIO的设备和所请求的GPIO将要使用的功能:
struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,
enum gpiod_flags flags)
如果通过一起使用多个GPIO来实现功能(例如,简单的LED显示数字的设备,可以指定一个额外的索引参数:
struct gpio_desc *gpiod_get_index(struct device *dev,
const char *con_id, unsigned int idx,
enum gpiod_flags flags)
有关DeviceTree情况中con_id参数的更详细说明请参阅Documentation/gpio/board.txt
flags参数用于可选地指定GPIO的方向和初始值,它的值可以是:
- GPIOD_ASIS或0表示根本不初始化GPIO。需要随后使用专门的函数设置方向
- GPIOD_IN初始化GPIO作为输入。
- GPIOD_OUT_LOW将GPIO初始化为输出,值为0。
- GPIOD_OUT_HIGH将GPIO初始化为输出,值为1。
- GPIOD_OUT_LOW_OPEN_DRAIN:与GPIOD_OUT_LOW相同,但强制以开漏的方式使用
- GPIOD_OUT_HIGH_OPEN_DRAIN:与GPIOD_OUT_HIGH相同,但强制以开漏的方式使用
\end{itemize}
最后两个标志用于必须开漏方式的情况,比如GPIO被用作I2C时,如果该GPIO尚未在映射(参见board.txt)中被配置为开漏方式,将被强制配置为开漏方式并给出WARNING。
这两个函数都返回有效的GPIO描述符或可被IS_ERR()检查的错误代码(它们永远不会返回NULL指针)。 返回-ENOENT只会发生在当且仅当没有为设备/功能/索引三元组成功分配GPIO的时候。其他错误代码用于已成功分配GPIO,但在试图获得它的时候发生了错误的情况:这可以用于区分错误原因是可选GPIO参数错误还是GPIO缺失这两种情况。
在允许GPIO不存在的情况下,可以使用gpiod_get_optional()和gpiod_get_index_optional()函数。这两个函数在没有成功分配到GPIO的时候返回NULL而不是-ENOENT。
struct gpio_desc *gpiod_get_optional(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
struct gpio_desc *gpiod_get_index_optional(struct device *dev,
const char *con_id,
unsigned int index,
enum gpiod_flags flags)
请注意,gpio_get*_optional()函数(及其变体)不同于其余gpiolib的API,当禁用gpiolib支持时它们也会返回NULL。这对驱动程序作者很有帮助,因为这代表他们不需要考虑-ENOSYS返回码这种特殊情况。但是,系统集成商应该注意在需要它的系统上启用gpiolib。
对于使用多个GPIO的函数,可以通过一次调用获得所有需要的GPIO:
struct gpio_descs * gpiod_get_array(struct device * dev,
const char * con_id,enum gpiod_flags flags)
此函数返回一个struct gpio_descs,其中包含一个描述符数组:
struct gpio_descs {
unsigned int ndescs;
struct gpio_desc *desc[];
}
如果没有GPIO被分配,则以下函数返回NULL而不是-ENOENT:
struct gpio_descs *gpiod_get_array_optional(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
这些函数的变体:
struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id,
enum gpiod_flags flags)
struct gpio_desc *devm_gpiod_get_index(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpiod_flags flags)
struct gpio_desc *devm_gpiod_get_optional(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
struct gpio_desc *devm_gpiod_get_index_optional(struct device *dev,
const char *con_id,
unsigned int index,
enum gpiod_flags flags)
struct gpio_descs *devm_gpiod_get_array(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
struct gpio_descs *devm_gpiod_get_array_optional(struct device *dev,
const char *con_id,
enum gpiod_flags flags)
可以使用gpiod_put()函数释放GPIO描述符:
void gpiod_put(struct gpio_desc *desc)
对于GPIO数组,可以使用此函数:
void gpiod_put_array(struct gpio_descs *descs)
在调用这些函数之后,严格禁止使用被释放的描述符。也不允许在使用gpiod_get_array()获取的数组中单独使用gpiod_put()释放描述符。
相关变体:
void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)
void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs)
使用GPIO
设置方向
设备驱动必须首先确定GPIO的方向。如果已经为gpiod_get * ()提供了nodirection设置标志,则可以通过调用gpiod_direction _*()函数之一来完成:
int gpiod_direction_input(struct gpio_desc *desc)
int gpiod_direction_output(struct gpio_desc *desc, int value)
成功返回值为零,否则返回值为负的错误代码。该返回值应该被检查,因为get/set调用不会返回错误,所以错误的配置是有可能的。您通常应该在任务上下文进行这些调用。但是,对于自旋锁安全(Spinlock-Safe)的GPIO,可以作为板级设置初期的一部分,在启用任务之前使用它们。
对于输出GPIO,提供的值将成为初始输出值。这有助于避免系统启动期间的信号故障。驱动程序还可以查询GPIO的当前方向:
int gpiod_get_direction(const struct gpio_desc *desc)
此函数返回0表示输出,1表示输入,或错误代码(如果出错)
要意识到GPIO没有默认的方向。因此,\emph{使用GPIO而不首先设置其方向是非法的,并且将导致未定义的行为!}
Spinlock-Safe的GPIO访问
大多数GPIO控制器可通过存储器读/写指令访问。对于不能睡眠,并且可以安全地从内部hard(非线程的)IRQ handler和类似的上下文中完成的操作(即原子操作中),使用以下调用来访问GPIO:
int gpiod_get_value(const struct gpio_desc *desc);
void gpiod_set_value(struct gpio_desc *desc, int value);
value为布尔值,零为低,非零为高。读取输出引脚的值时,返回的值应该是引脚上的值。由于包括开漏信号和输出延迟在内的问题,它并不总是匹配指定的输出值。
get/set调用不会返回错误,因为“无效的GPIO”应该在这之前就从gpiod_direction_*()中得知。但请注意,并非所有平台都可以读取输出引脚的值;对于那些不能读取的平台,函数永远返回零。另外,使用这些函数访问需要睡眠才能安全访问的GPIO(见下文)是错误的操作。
允许睡眠的GPIO访问
有些GPIO控制器必须使用基于消息的总线(如I2C或SPI)访问。读取或写入这些GPIO值的命令需要等待到达队列的头部以传输命令并获得其响应。
这样就需要允许睡眠,导致这类GPIO的访问不能在内部IRQ处理程序内(原子上下文)完成。
支持这种类型的GPIO的平台通过从以下调用返回非零来区别于其他GPIO:
int gpiod_cansleep(const struct gpio_desc *desc)
要访问此类GPIO,请使用另一组GPIO访问函数:
int gpiod_get_value_cansleep(const struct gpio_desc * desc)
void gpiod_set_value_cansleep(struct gpio_desc * desc,int value)
访问这样的GPIO需要一个可以休眠的上下文,例如一个threaded IRQ处理程序,并且必须使用上述访问函数而不是Spinlock-Safe的没有cansleep()后缀的访问函数。
除了可以睡眠,无法在hardIRQ处理程序访问的特点以外,这些调用与Spinlock-Safe的调用相同。
低有效和开漏语义
由于使用者不必关心物理线路级别,所有的gpiod_set_value_xxx()或 gpiod_set_array_value_xxx() 函数都以逻辑值操作。通过这种方式,它们会将低电平有效的性质考虑在内。这意味着它们会检查GPIO是否配置为低电平有效,如果是,它们会在物理线路电平被驱动之前调整传递的值。
这同样适用于开漏或开源输出:它们并不输出高电平(开漏)或低电平(开源),它们只是将输出切换到高阻抗值。使用者应该不需要关注。(有关的详细信息,请参阅driver.txt中关于开漏的细节。)
这样,所有gpiod_set_(array)_value_xxx()函数都将参数“value”解释为“asserted”(“1”)或“de-asserted”(“0” )。相应地驱动物理线路级别。
例如,如果设置了GPIO的低电平有效属性,并且gpiod_set_(array)_value_xxx()传递了“asserted”(“1”),则物理线路电平将被驱动为低电平。
总结:
函数(示例) | 线路属性 | 物理线路 |
---|---|---|
gpiod_set_raw_value(desc, 0); | - | 低电平 |
gpiod_set_raw_value(desc, 0); | - | 高电平 |
gpiod_set_value(desc, 0); | 默认(高电平有效) | 低电平 |
gpiod_set_value(desc, 1); | 默认(高电平有效) | 高电平 |
gpiod_set_value(desc, 0); | 低电平有效 | 高电平 |
gpiod_set_value(desc, 1); | 低电平有效 | 低电平 |
gpiod_set_value(desc, 0); | 开漏 | 低电平 |
gpiod_set_value(desc, 1); | 开漏 | 高阻态 |
gpiod_set_value(desc, 0); | 开漏 | 高阻态 |
gpiod_set_value(desc, 1); | 开漏 | 高电平 |
可以使用*set_raw /'get_raw函数覆盖这些语义,但应尽可能避免,尤其是系统无关的驱动程序,它们不需要关心实际的物理线路级别而是关心逻辑值。
访问原始GPIO值
对于的确需要管理GPIO线路物理状态的使用者,他们关心设备将实际接收到的值。
下面的一组调用忽略GPIO的低有效或开漏属性,并在原始线路状态上工作:
int gpiod_get_raw_value(const struct gpio_desc *desc)
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
还可以使用以下方法查询GPIO的低有效属性:
int gpiod_is_active_low(const struct gpio_desc *desc)
请注意,这些函数只能在使用者明白自己在做什么的情况下使用;驱动程序一般不应该关心线路物理状态或开漏语义。
使用单个函数调用访问多个GPIO
以下函数获取或设置GPIO数组的值:
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
int gpiod_get_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array)
void gpiod_set_raw_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array)
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array)
void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array)
数组可以是任意一组GPIO。如果相应的芯片驱动器支持,这些函数将尝试同时访问属于同一存储体或芯片的GPIO。在这种情况下,可以预期显著改善的性能。如果无法同时访问,GPIO将按顺序访问。
这些函数有三个参数:
- array_size - 数组元素的数量
- desc_array - GPIO描述符数组
- value_array - 存储GPIO值(get)的数组或要分配给GPIO的值数组(set)
可以使用gpiod_get_array()函数或其变体之一来获取描述符数组。如果该函数返回的描述符组与所需的GPIO组匹配,则只需使用gpiod_get_array()返回的struct gpio_descs即可访问这些GPIO:
struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
my_gpio_values);
也可以访问完全任意的描述符数组。可以使用gpiod_get()和gpiod_get_array()的任意组合来获得描述符。之后,必须先手动设置描述符数组才能将其传递给上述函数之一。
注意,为了获得最佳性能,属于同一芯片的GPIO应该在描述符数组中是连续的。
gpiod_get_array_value()及其变体成功时返回0,错误返回负数。请注意与gpiod_get_value()的区别,gpiod_get_value()在成功传递GPIO值时返回0或1。使用数组函数时,GPIO值存储在value_array中,而不是作为返回值传回。
GPIO映射到IRQ
GPIO行经常被使用作为IRQ。您可以使用以下调用获取与给定GPIO相对应的IRQ编号:
int gpiod_to_irq(const struct gpio_desc *desc)
如果映射不成功,它将返回IRQ编号或负的errno代码(很可能是因为该特定GPIO不能用作IRQ)。使用未使用gpiod_direction_input()设置为输入的GPIO,或者使用最初不是来自gpiod_to_irq()的IRQ编号,是错误的操作。 gpiod_to_irq()不允许休眠。
从gpiod_to_irq()返回的非错误值可以传递给request_irq()或free_irq()。它们通常通过特定于板的初始化代码存储到平台设备的IRQ资源中。注意,IRQ触发选项是IRQ接口的一部分,例如, IRQF_TRIGGER_FALLING,系统唤醒功能.
GPIO和ACPI
在ACPI系统上,GPIO由设备的_CRS配置对象列出的GpioIo()/ GpioInt()资源描述。这些资源不提供GPIO的连接ID(名称),因此有必要为此目的使用附加机制。
符合ACPI 5.1或更新版本的系统可能可以提供_DSD配置对象,它可以用于提供_CRS中的GpioIo()/ GpioInt()资源描述的特定GPIO的连接ID。如果是这种情况,它将由GPIO子系统自动处理。但是,如果不存在_DSD,则GpioIo()/ GpioInt()资源与GPIOconnection ID之间的映射需要由设备驱动程序提供。
有关详细信息,请参阅Documentation/acpi /gpio-properties.txt
使用旧版GPIO子系统进行交互
许多内核子系统仍使用传统的基于整数的GPIO接口。虽然强烈建议将它们升级到更安全的基于描述符的API,但以下两个函数允许您将GPIO描述符转换为GPIO整数命名空间,及其反向转换:
int desc_to_gpio(const struct gpio_desc *desc)
struct gpio_desc *gpio_to_desc(unsigned gpio)
只要没有释放GPIO描述符,就可以安全地使用desc_to_gpio()返回的GPIO号。同样,必须正确获取传递给gpio_to_desc()的GPIO号,并且只有在获取GPIO号后才能使用返回的GPIO描述符。
禁止使用不同类的API分别获取和释放GPIO,这是一个未进行检查的错误。
LinuxGPIO中文文档的更多相关文章
- Phoenix综述(史上最全Phoenix中文文档)
个人主页:http://www.linbingdong.com 简书地址:http://www.jianshu.com/users/6cb45a00b49c/latest_articles 网上关于P ...
- Chart.js中文文档-雷达图
雷达图或蛛网图(Radar chart) 简介 A radar chart is a way of showing multiple data points and the variation bet ...
- Knockout中文开发指南(完整版API中文文档) 目录索引
a, .tree li > span { padding: 4pt; border-radius: 4px; } .tree li a { color:#46cfb0; text-decorat ...
- ReactNative官方中文文档0.21
整理了一份ReactNative0.21中文文档,提供给需要的reactnative爱好者.ReactNative0.21中文文档.chm 百度盘下载:ReactNative0.21中文文档 来源: ...
- java中文文档官方下载
一直在寻找它,今天无意之间终于发现它了! http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/overview-summa ...
- Spring中文文档
前一段时间翻译了Jetty的一部分文档,感觉对阅读英文没有大的提高(*^-^*),毕竟Jetty的受众面还是比较小的,而且翻译过程中发现Jetty的文档写的不是很好,所以呢翻译的兴趣慢慢就不大了,只能 ...
- jQuery 3.1 API中文文档
jQuery 3.1 API中文文档 一.核心 1.1 核心函数 jQuery([selector,[context]]) 接收一个包含 CSS 选择器的字符串,然后用这个字符串去匹配一组元素. jQ ...
- jQuery EasyUI API 中文文档 - ComboGrid 组合表格
jQuery EasyUI API 中文文档 - ComboGrid 组合表格,需要的朋友可以参考下. 扩展自 $.fn.combo.defaults 和 $.fn.datagrid.defaults ...
- jQuery EasyUI API 中文文档 - ValidateBox验证框
jQuery EasyUI API 中文文档 - ValidateBox验证框,使用jQuery EasyUI的朋友可以参考下. 用 $.fn.validatebox.defaults 重写了 d ...
随机推荐
- 问题:com.alibaba.dubbo.rpc.RpcException: Failed to invoke ......
个人解决流程: 一看到这个问题,下意识想到了是dubbo远程连接的问题,可能是dubbo本身的问题,于是在虚拟机上另外一台dubbo能正常脸上的服务器上重新尝试,还是报相同的错误,并且在dubbo-a ...
- conda 安装虚拟环境 fastai
一.conda常用命令 1.创建一个虚拟环境env_name,后面跟的是创建这个环境时,同时安装的软件包 conda create -n env_name python=3.6 2.通过克隆创建一个环 ...
- Lambda学习总结(三)--方法引用
一.方法引用 1.1 方法引用含义 在学习了 Lambda 表达式之后,我们通常会使用 Lambda 表达式来创建匿名方法.但有的时候我们仅仅是需要调用一个已存在的方法.如下示例: @Function ...
- MongoDB学习笔记之文档
#向集合中插入文档有两种方式(insert.save) db.col.insert({title: 'MongoDB 教程', description: 'MongoDB 是一个 Nosql 数据库' ...
- 2017 CVTE Windows开发一面 3.7
下午3点接到了个广州打过来的电话,电话面试总体时间比较短,35分钟. 考察内容: 1.讲实习: 因人而异,将了之前公司做的项目,刚好和面的岗位匹配,面试官听完之后还不忘垂壁一下他们的产品. 2.C#事 ...
- 本机的IP地址无法打开(Vue项目)
1, 首先找到使用vue脚手架建立项目config文件中的index.js文件2, 修改dev里面的host属性值:改成 host: ‘0.0.0.0’3, 最后在局域网下,使用自己的ip进行连接,同 ...
- MySql触发器简介
MySQL 数据库中触发器是一个特殊的存储过程,不同的是执行存储过程要使用 CALL 语句来调用,而触发器的执行不需要使用 CALL 语句来调用,也不需要手工启动,只要一个预定义的事件发生就会被 My ...
- python入门导读
很多培训机构宣称py是人工智能必备的编程语言,打着速成的旗号来引诱学者学习python.事实却并不是这样的,万丈高台平地起,不论你想从事怎样的编程工作,都是从最基本的编程技巧开始的:Python并不适 ...
- PHP入门(五)
一.超级全局变量 超级全局变量在PHP 4.1.0之后被启用, 是PHP系统中自带的变量,在一个脚本的全部作用域中都可用. PHP中预定义了几个超级全局变量(superglobals) ,这意味着它们 ...
- Leetcode练习题21. Merge Two Sorted Lists
题目描述(easy) Merge Two Sorted Lists Merge two sorted linked lists and return it as a new list. The new ...