本文来自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中文文档的更多相关文章

  1. Phoenix综述(史上最全Phoenix中文文档)

    个人主页:http://www.linbingdong.com 简书地址:http://www.jianshu.com/users/6cb45a00b49c/latest_articles 网上关于P ...

  2. Chart.js中文文档-雷达图

    雷达图或蛛网图(Radar chart) 简介 A radar chart is a way of showing multiple data points and the variation bet ...

  3. Knockout中文开发指南(完整版API中文文档) 目录索引

    a, .tree li > span { padding: 4pt; border-radius: 4px; } .tree li a { color:#46cfb0; text-decorat ...

  4. ReactNative官方中文文档0.21

    整理了一份ReactNative0.21中文文档,提供给需要的reactnative爱好者.ReactNative0.21中文文档.chm  百度盘下载:ReactNative0.21中文文档 来源: ...

  5. java中文文档官方下载

    一直在寻找它,今天无意之间终于发现它了! http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/overview-summa ...

  6. Spring中文文档

    前一段时间翻译了Jetty的一部分文档,感觉对阅读英文没有大的提高(*^-^*),毕竟Jetty的受众面还是比较小的,而且翻译过程中发现Jetty的文档写的不是很好,所以呢翻译的兴趣慢慢就不大了,只能 ...

  7. jQuery 3.1 API中文文档

    jQuery 3.1 API中文文档 一.核心 1.1 核心函数 jQuery([selector,[context]]) 接收一个包含 CSS 选择器的字符串,然后用这个字符串去匹配一组元素. jQ ...

  8. jQuery EasyUI API 中文文档 - ComboGrid 组合表格

    jQuery EasyUI API 中文文档 - ComboGrid 组合表格,需要的朋友可以参考下. 扩展自 $.fn.combo.defaults 和 $.fn.datagrid.defaults ...

  9. jQuery EasyUI API 中文文档 - ValidateBox验证框

    jQuery EasyUI API 中文文档 - ValidateBox验证框,使用jQuery EasyUI的朋友可以参考下.   用 $.fn.validatebox.defaults 重写了 d ...

随机推荐

  1. 问题:com.alibaba.dubbo.rpc.RpcException: Failed to invoke ......

    个人解决流程: 一看到这个问题,下意识想到了是dubbo远程连接的问题,可能是dubbo本身的问题,于是在虚拟机上另外一台dubbo能正常脸上的服务器上重新尝试,还是报相同的错误,并且在dubbo-a ...

  2. conda 安装虚拟环境 fastai

    一.conda常用命令 1.创建一个虚拟环境env_name,后面跟的是创建这个环境时,同时安装的软件包 conda create -n env_name python=3.6 2.通过克隆创建一个环 ...

  3. Lambda学习总结(三)--方法引用

    一.方法引用 1.1 方法引用含义 在学习了 Lambda 表达式之后,我们通常会使用 Lambda 表达式来创建匿名方法.但有的时候我们仅仅是需要调用一个已存在的方法.如下示例: @Function ...

  4. MongoDB学习笔记之文档

    #向集合中插入文档有两种方式(insert.save) db.col.insert({title: 'MongoDB 教程', description: 'MongoDB 是一个 Nosql 数据库' ...

  5. 2017 CVTE Windows开发一面 3.7

    下午3点接到了个广州打过来的电话,电话面试总体时间比较短,35分钟. 考察内容: 1.讲实习: 因人而异,将了之前公司做的项目,刚好和面的岗位匹配,面试官听完之后还不忘垂壁一下他们的产品. 2.C#事 ...

  6. 本机的IP地址无法打开(Vue项目)

    1, 首先找到使用vue脚手架建立项目config文件中的index.js文件2, 修改dev里面的host属性值:改成 host: ‘0.0.0.0’3, 最后在局域网下,使用自己的ip进行连接,同 ...

  7. MySql触发器简介

    MySQL 数据库中触发器是一个特殊的存储过程,不同的是执行存储过程要使用 CALL 语句来调用,而触发器的执行不需要使用 CALL 语句来调用,也不需要手工启动,只要一个预定义的事件发生就会被 My ...

  8. python入门导读

    很多培训机构宣称py是人工智能必备的编程语言,打着速成的旗号来引诱学者学习python.事实却并不是这样的,万丈高台平地起,不论你想从事怎样的编程工作,都是从最基本的编程技巧开始的:Python并不适 ...

  9. PHP入门(五)

    一.超级全局变量 超级全局变量在PHP 4.1.0之后被启用, 是PHP系统中自带的变量,在一个脚本的全部作用域中都可用. PHP中预定义了几个超级全局变量(superglobals) ,这意味着它们 ...

  10. 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 ...