本文来自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. HackIM web关writeup

    Web100 访问页面将看到下面的错误 在burp里使用request / response查看有没有什么不正常的地方.如下图所示,在返回的数据包里被设置了两次不同的PHPSESSID. 如果我把PH ...

  2. Runtime.getRuntime.exec()执行linux脚本导致程序卡死问题

    rumtime程序执行中出现卡住,执行成果达不到预期的标准.查看输出流以及错误流程是否内存占满了.开两个线程来运行输出流程和错误流程. rumtime运行windows脚本执行是要添加执行环境 cmd ...

  3. VMware:未能将管道连接到虚拟机, 所有的管道范例都在使用中

    问题描述:虚拟机下的Ubuntu系统长时间死机无法正常关机,用Windows任务管理器关闭VMware也关不掉,没办法,只能直接关电脑了...重新打开电脑,启动VMware,发现提示客户机已经处于打开 ...

  4. Zabbix报错:"Zabbix http poller processes more than 75% busy"的解决

    一.钉钉收到告警 主机名称:Zabbix服务端-172.28.18.75 IP地址 :127.0.0.1 告警时间:2019.10.22 13:34:39 告警信息:Zabbix http polle ...

  5. 「工具」三分钟了解一款思维导图工具:XMind Zen

    一款非常实用的商业思维导图软件,融合艺术与创造力.致力于高效的可视化思维,强调软件的跨平台使用,帮助用户提高生产效率. 相关信息 · 操作系统:macOS / Windows / Linux · 官方 ...

  6. @EqualsAndHashCode

    1.@Data注解包含了这些注解 * @see Getter * @see Setter * @see RequiredArgsConstructor * @see ToString * @see E ...

  7. HDU - 6396 Swordsman (单调性+贪心)

    题意:有n个怪物和k种属性,当且仅当你的每种属性都大于等于怪物的属性才可以击杀它,且击杀怪物可以提升你一定的属性值.求可击杀怪物的最大数量以最终的属性值. 这不就是银行家算法里的安全性检验么? 本题的 ...

  8. 【BZOJ1016】【Luogu P4208】 [JSOI2008]最小生成树计数 最小生成树,矩阵树定理

    蛮不错的一道题,遗憾就遗憾在数据范围会导致暴力轻松跑过. 最小生成树的两个性质: 不同的最小生成树,相同权值使用的边数一定相同. 不同的最小生成树,将其都去掉同一个权值的所有边,其连通性一致. 这样我 ...

  9. vue 中 弹幕的播放

    前言 最近在搞弹幕的问题,小程序上的和vue上的,不想使用插件,于是自己摸索了一下,其实包括 2中弹幕形式 有序和无序的 直接上代码吧 <!-- 弹幕 --> <template v ...

  10. vue2.0 移动端,下拉刷新,上拉加载更多 封装组件

    前言 在做移动端的避免不了 下拉刷新,上拉加载 直接上代码吧,哈哈 组件里: <template lang="html"> <div class="yo ...