Linux内核文档:GPIO接口
linux中gpio的判断方式

linux中gpio的判断方式Linux中的GPIO(General Purpose Input/Output)是一种通用输入/输出接口,允许用户通过控制电平来与外部电子设备进行通信。
在本文中,我们将探讨如何通过Linux中的GPIO判断外部设备的状态。
GPIO的判断方式主要通过读取相应的GPIO引脚的电平状态来实现。
在Linux中,可以通过/sys/class/gpio目录下的文件进行GPIO的控制和读取。
首先,我们需要将GPIO引脚导出为可控制的文件,可以通过以下命令完成:```echo [GPIO编号] > /sys/class/gpio/export```其中,[GPIO编号]代表需要操作的GPIO引脚的编号。
导出后,系统会在/sys/class/gpio目录下生成相应的文件夹,包含了与该GPIO 引脚相关的文件。
接下来,我们可以通过读取/sys/class/gpio/gpio[GPIO编号]/value 文件来获取GPIO引脚的电平状态。
该文件中的内容为0或1,分别表示低电平和高电平。
我们可以使用类似如下的命令来读取GPIO 引脚的状态:```cat /sys/class/gpio/gpio[GPIO编号]/value```通过读取该文件的内容,我们可以判断GPIO引脚的电平状态,并根据需要进行相应的处理。
例如,可以根据GPIO引脚的状态来控制外部设备的开关,或者根据不同的状态执行不同的程序。
在使用完GPIO引脚后,为了释放相关资源,我们需要将其取消导出。
可以通过以下命令完成:```echo [GPIO编号] > /sys/class/gpio/unexport```总结一下,通过读取/sys/class/gpio/gpio[GPIO编号]/value文件,我们可以方便地判断Linux中的GPIO引脚的电平状态,进而实现与外部设备的通信和控制。
这种方式简单、灵活,并且可以与其他Linux命令和脚本结合使用,为嵌入式系统开发和物联网应用提供了便利。
linux gpio编号

linux gpio 编号
在 Linux 系统中,GPIO(通用输入输出)引脚使用不同的编号 方式,具体取决于所使用的硬件平台和驱动程序。以下是常见的两 种 GPIO 编号方式:
1. 基于内核编号(Kernel nபைடு நூலகம்mbering):这是最常用的 GPIO 编号 方式,在该方式下,每个 GPIO 引脚都有一个唯一的整数编号, 从 0 开始递增。可以通过查看 /sys/class/gpio 目录下的文 件 来 获 取 和 管 理 GPIO 引 脚 。 例 如 , GPIO0 的 文 件 路 径为 /sys/class/gpio/gpio0 。
2. 基于物理板编号(Board numbering):这种编号方式与硬件平 台的物理引脚布局直接对应。每个 GPIO 引脚都会被分配一个特 定的标识符,如"P9_12"或"GPIO1_28"等。可以通过查找对应的 文档或参考资料来获取特定硬件平台上的 GPIO 编号。
请注意,具体的 GPIO 编号方式可能因使用的硬件平台和操作系统版 本而异。在实际使用时,可以根据硬件平台和操作系统的要求选择适 合的 GPIO 编号方式,并参考相应的文档来确定正确的 GPIO 引脚编 号。
Linux中的gpio口使用方法

Linux中的gpio⼝使⽤⽅法Linux中的IO使⽤⽅法应该是新版本内核才有的⽅法。
请参考:./Documentation/gpio.txt⽂件提供的API:驱动需要包含 #include <linux/gpio.h>判断⼀个IO是否合法:int gpio_is_valid(int number);设置GPIO的⽅向,如果是输出同时设置电平:/* set as input or output, returning 0 or negative errno */int gpio_direction_input(unsigned gpio);int gpio_direction_output(unsigned gpio, int value);获取输⼊引脚的电平:/* GPIO INPUT: return zero or nonzero */int gpio_get_value(unsigned gpio);/* GPIO OUTPUT */void gpio_set_value(unsigned gpio, int value);int gpio_cansleep(unsigned gpio);To access such GPIOs, a different set of accessors is defined:/* GPIO INPUT: return zero or nonzero, might sleep */int gpio_get_value_cansleep(unsigned gpio);/* GPIO OUTPUT, might sleep */void gpio_set_value_cansleep(unsigned gpio, int value);获取⼀个GPIO并声明标签:/* request GPIO, returning 0 or negative errno.* non-null labels may be useful for diagnostics.*/int gpio_request(unsigned gpio, const char *label);/* release previously-claimed GPIO */void gpio_free(unsigned gpio);将GPIO映射为IRQ中断:/* map GPIO numbers to IRQ numbers */int gpio_to_irq(unsigned gpio);/* map IRQ numbers to GPIO numbers (avoid using this) */int irq_to_gpio(unsigned irq);设置GPIO IRQ中断类型:if (!sw->both_edges) {if (gpio_get_value(sw->gpio))set_irq_type(gpio_to_irq(sw->gpio), IRQ_TYPE_EDGE_FALLING);elseset_irq_type(gpio_to_irq(sw->gpio), IRQ_TYPE_EDGE_RISING);在驱动中使⽤延时函数mdelay,需要包含<linux/delay.h>⽂件。
linuxgpio口测试程序,gpio接口测试

linuxgpio⼝测试程序,gpio接⼝测试GPIO(General Purpose I/O Ports)意思为通⽤输⼊/输出端⼝,通俗地说,就是⼀些引脚,可以通过它们输出⾼低电平或者通过它们读⼊引脚的状态-是⾼电平或是低电平。
GPIO⼝⼀是个⽐较重要的概念,⽤户可以通过GPIO⼝和硬件进⾏数据交互(如UART),控制硬件⼯作(如LED、蜂鸣器等),读取硬件的⼯作状态信号(如中断信号)等。
GPIO⼝的使⽤⾮常⼴泛。
⼀、 设置硬件环境连接VS⼦板 从dm8168扩展出来的GPIO连接到了VS⼦板的J1接⼝⼆、配置编译linux内核1》 在linux-2.6.37-psp04.00.00.12/arch/arm/mach-omap2/ board-TI8168evm.c⽂件的558⾏添加TI816X_MUX(SC1_DATA, OMAP_MUX_MODE2),TI816X_MUX(SC0_DET, OMAP_MUX_MODE2),配置GP0[24] GP1[9]管脚为GPIO管脚2》 配置linux内核 使能Device Drivers –》 GPIO Support –》 /sys/class/gpio三、启动开发板四、创建GPIO节点echo 24 》 /sys/class/gpio/exportecho 41 》 /sys/class/gpio/export五、设置GPIO⽅向cd /sys/class/gpio/gpio24echo “out” 》 direcTIoncd /sys/class/gpio/gpio41echo “out” 》 direction六、改变GPIO值cd /sys/class/gpio/gpio24echo 1 》 valuecd /sys/class/gpio/gpio41echo 1 》 value使⽤万⽤表测量VS⼦板 –》JP1 –》1 或3管脚为⾼电平cd /sys/class/gpio/gpio24echo 0 》 valuecd /sys/class/gpio/gpio41echo 0 》 value使⽤万⽤表测量VS⼦板 –》JP1 –》1 或3管脚为低电平七、运⾏C测试程序运⾏gpio0-24-test 或gpio1-9-test测试例程,使⽤⽰波器可以看到频率为1HZ的⽅波⼀、Follow these steps for controlling the GPIO lines from sysfs entriesa. Export, which GPIO pin you want to control. Below steps are done with respect to GPIO30 (an example$ echo 30 》 /sys/class/gpio/exportb. Change the GPIO pin direction to in/out$ echo “out” 》 /sys/class/gpio/gpio30/direction$ echo “in” 》 /sys/class/gpio/gpio30/directionc. Changing the value$ echo 1 》 /sys/class/gpio/gpio30/value$ echo 0 》 /sys/class/gpio/gpio30/valued. Unexport the GPIO pin$ echo 30 》 /sys/class/gpio/unexportSysfs entries are created from 0 - 63 in case of DM816X because it has two GPIO banks 0 and 1.Note: GPIO‘s which are used already in the drivers can not be control from sysfs, unless untill driver export that particular pin.Run these commands for knowing what are the GPIO’s already requested in the drivers.$ mount -t debugfs debugfs /sys/kernel/debug$ cat /sys/kernel/debug/gpio。
Linux用户态设置GPIO控制

Linux⽤户态设置GPIO控制Linux ⽤户态设置GPIO控制linux内核提供了⼀套在⽤户态配置GPIO的接⼝,在/sys/class/gpio/⽬录下可以发现其中包含有两个⽂件export、unexport和若⼲gpiochipN类型⽂件夹export⽤于将指定编号的引脚导出,作为GPIO使⽤unexport⽤于将导出的GPIO删除掉gpiochipN当前芯⽚中包含的GPIO控制器GPIO使⽤⽅法添加设备接⼝GPIO167输⼊:echo 167 > export可以发现,⽬录下出现了gpio167,如果执⾏命令后没有反应,表⽰当前的GPIO已经⽤作其他的功能,例如作为IIC的引脚等删除设备接⼝GPIO167输⼊:echo 167 > unexport可以发现当前导出的接⼝被删除控制设备接⼝GPIO167输⼊:echo 167 > unexportdirection设置输出还是输⼊模式设置为输⼊:echo “in” > direction设置为输出:echo “out” > directionvalue输出时,控制⾼低电平;输⼊时,获取⾼低电平⾼电平:echo 1 > value低电平:echo 0 > valueedge控制中断触发模式,引脚被配置为中断后可以使⽤poll()函数监听引脚⾮中断引脚: echo “none” > edge上升沿触发:echo “rising” > edge下降沿触发:echo “falling” > edge边沿触发:echo “both” > edgegpiochipN⽬录⽤来管理和控制⼀组gpio端⼝的控制器base和N相同,表⽰控制器管理的最⼩的端⼝编号。
lable诊断使⽤的标志(并不总是唯⼀的)ngpio控制器管理的gpio端⼝数量(端⼝范围是:N ~ N+ngpio-1)⽤户态使⽤gpio监听中断⽐如我想监听PA7上的电平变化(也就是边沿触发),那么应该先向“/sys/class/gpio/gpio7/direction”写⼊“in”,然后向“/sys/class/gpio/gpio7/edge”写⼊“both”,然后对”/sys/class/gpio/gpio7/value”执⾏select/poll操作。
linux内核的gpiolib详解

linux内核的gpiolib详解#include <linux/init.h> // __init __exit#include <linux/module.h> // module_init module_exit#include <mach/regs-gpio.h>#include <mach/gpio-bank.h>#include <asm/io.h> //writel#include <mach/gpio.h>#include <linux/leds.h>#include <asm/string.h>#define X210_LED_OFF 1U#define X210_LED_ON 0Ustruct led_classdev cdev1;struct led_classdev cdev2;struct led_classdev cdev3;void s5pv210_led1_set(struct led_classdev *led_cdev,enum led_brightness brightness);void s5pv210_led2_set(struct led_classdev *led_cdev,enum led_brightness brightness);void s5pv210_led3_set(struct led_classdev *led_cdev,enum led_brightness brightness);static struct gpio x210_led_gpio[] ={{ S5PV210_GPJ0(3), GPIOF_OUT_INIT_HIGH, "LED1" }, /* default to OFF */{ S5PV210_GPJ0(4), GPIOF_OUT_INIT_HIGH, "LED2" }, /* default to OFF */{ S5PV210_GPJ0(5), GPIOF_OUT_INIT_HIGH, "LED3" } /* default to OFF */};void s5pv210_led1_set(struct led_classdev *led_cdev,enum led_brightness brightness){printk(KERN_INFO "s5pv210_led1_set successful %d\n",brightness);if(brightness == LED_OFF){gpio_set_value(x210_led_gpio[0].gpio,X210_LED_OFF);}else{gpio_set_value(x210_led_gpio[0].gpio,X210_LED_ON);}}void s5pv210_led2_set(struct led_classdev *led_cdev,enum led_brightness brightness){printk(KERN_INFO "s5pv210_led2_set successful %d\n",brightness);if(brightness == LED_OFF){gpio_set_value(x210_led_gpio[1].gpio,X210_LED_OFF);}else{gpio_set_value(x210_led_gpio[1].gpio,X210_LED_ON);}}void s5pv210_led3_set(struct led_classdev *led_cdev,enum led_brightness brightness){printk(KERN_INFO "s5pv210_led3_set successful %d\n",brightness);if(brightness == LED_OFF){gpio_set_value(x210_led_gpio[2].gpio,X210_LED_OFF);}else{gpio_set_value(x210_led_gpio[2].gpio,X210_LED_ON);}}static int __init s5pv210_led_init(void){int ret = -1;printk(KERN_INFO "s5pv210_led_init successful \n");cdev1.brightness_set = s5pv210_led1_set; = "led1";ret = led_classdev_register(NULL, &cdev1);if (ret < 0){printk(KERN_WARNING "led_classdev_register fail \n");goto reg_err1;}cdev2.brightness_set = s5pv210_led2_set; = "led2";ret = led_classdev_register(NULL, &cdev2);if (ret < 0){printk(KERN_WARNING "led_classdev_register fail \n");goto reg_err2;}cdev3.brightness_set = s5pv210_led3_set; = "led3";ret = led_classdev_register(NULL, &cdev3);if (ret < 0){printk(KERN_WARNING "led_classdev_register fail \n");goto reg_err3;}ret = gpio_request_array(x210_led_gpio, ARRAY_SIZE(x210_led_gpio));if (ret){goto gpio_err;}return0;gpio_err:led_classdev_unregister(&cdev3);reg_err3:led_classdev_unregister(&cdev2);reg_err2:led_classdev_unregister(&cdev1);reg_err1:return ret;}static void __exit s5pv210_led_exit(void){printk(KERN_INFO "s5pv210_led_exit successful \n");gpio_free_array(x210_led_gpio, ARRAY_SIZE(x210_led_gpio));led_classdev_unregister(&cdev1);led_classdev_unregister(&cdev2);led_classdev_unregister(&cdev3);}module_init(s5pv210_led_init);module_exit(s5pv210_led_exit);// MODULE_xxx这种宏作⽤是⽤来添加模块描述信息MODULE_LICENSE("GPL"); // 描述模块的许可证MODULE_AUTHOR("musk"); // 描述模块的作者MODULE_DESCRIPTION("x210 LED driver"); // 描述模块的介绍信息MODULE_ALIAS("led_driver"); // 描述模块的别名信息View Code⼀. 什么是gpiolib1.1. linux中从2.6.35以后就开始有gpiolib库了,gpiolib的作⽤是对所有的gpio实⾏统⼀管理,因为驱动在⼯作的时候,会出现好⼏个驱动共同使⽤同⼀个gpio的情况;这会造成混乱。
linux内核gpio用法

linux内核gpio用法Linux内核对GPIO的使用是非常广泛的,本文将会通过几个步骤来解释如何在Linux内核中使用GPIO,包括GPIO的基本知识、配置GPIO、读取GPIO值、设置GPIO值和释放GPIO资源。
一、GPIO的基本知识GPIO(General Purpose Input/Output)是一种通用的输入/输出接口,它可以与各种设备进行连接,比如传感器、开关、LED等。
在Linux内核中,GPIO 被抽象为一个特殊的设备,可以通过相应的驱动程序进行读写操作。
每个GPIO引脚都被赋予了一个唯一的数字编号,这个编号称为GPIO号。
在不同的硬件平台上,GPIO号可能不同,因此在使用GPIO之前,需要先了解具体的GPIO号对应关系。
二、配置GPIO在使用GPIO之前,需要先配置GPIO的功能和方向,可以通过以下步骤来实现。
步骤1:加载GPIO驱动程序在Linux内核中,GPIO驱动程序以模块的形式存在,首先需要加载相应的GPIO 驱动程序。
可以使用如下命令加载GPIO驱动程序:modprobe gpio步骤2:导出GPIO引脚在Linux内核中,GPIO引脚默认是不可用的,需要先导出GPIO引脚,才能使用。
可以使用如下命令导出GPIO引脚:echo GPIO号> /sys/class/gpio/export其中,GPIO号为需要导出的GPIO引脚的编号。
步骤3:配置GPIO方向GPIO引脚有输入和输出两种方向,需要根据实际需求选择。
可以使用如下命令配置GPIO方向:echo in/out > /sys/class/gpio/gpioGPIO号/direction其中,in表示输入方向,out表示输出方向。
三、读取GPIO值配置好GPIO方向后,就可以读取GPIO引脚的值了。
可以使用如下命令读取GPIO值:cat /sys/class/gpio/gpioGPIO号/value其中,GPIO号为需要读取值的GPIO引脚的编号。
基于rk3568的linux驱动开发——gpio知识点

基于rk3568的linux驱动开发——gpio知识点基于rk3568的Linux驱动开发——GPIO知识点一、引言GPIO(General Purpose Input/Output)通用输入/输出,是现代计算机系统中的一种常用接口,它可以根据需要配置为输入或输出。
通过GPIO 接口,我们可以与各种外设进行通信,如LED灯、按键、传感器等。
在基于Linux系统的嵌入式设备上开发驱动程序时,熟悉GPIO的使用是非常重要的一环。
本文将以RK3568芯片为例,详细介绍GPIO的相关知识点和在Linux驱动开发中的应用。
二、GPIO概述GPIO是系统中的一个基本的硬件资源,它可以通过软件的方式对其进行配置和控制。
在嵌入式设备中,通常将一部分GPIO引脚连接到外部可编程电路,以实现与外部设备的交互。
在Linux中,GPIO是以字符设备的形式存在,对应的设备驱动为"gpiolib"。
三、GPIO的驱动开发流程1. 导入头文件在驱动程序中,首先需要导入与GPIO相关的头文件。
对于基于RK3568芯片的开发,需要导入头文件"gpiolib.h"。
2. 分配GPIO资源在驱动程序中,需要使用到GPIO资源,如GPIO所在的GPIO Bank和GPIO Index等。
在RK3568芯片中,GPIO资源的分配是通过设备树(Device Tree)来进行的。
在设备树文件中,可以定义GPIO Bank和GPIO Index等信息,以及对应的GPIO方向(输入或输出)、电平(高电平或低电平)等属性。
在驱动程序中,可以通过设备树接口(Device Tree API)来获取这些GPIO资源。
3. GPIO的配置与控制在驱动程序中,首先要进行GPIO的初始化与配置。
可以通过函数"gpiod_get()"来打开指定的GPIO,并判断其是否有效。
如果成功打开GPIO,则可以使用函数"gpiod_direction_output()"或"gpiod_direction_input()"来设置GPIO的方向,分别作为输出或输入。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
原文:Documentation/gpio.txt翻译:tekkamanninja@翻译完成时间:2012年2月18日星期六V1.0目录什么是GPIO? (3)GPIO公约 (3)标识GPIO (3)使用 GPIO (4)访问自旋锁安全的GPIO (4)访问可能休眠的GPIO (5)声明和释放GPIO (5)GPIO映射到IRQ (7)模拟开漏信号 (7)GPIO实现者的框架 (可选) (8)控制器驱动: gpio_chip (8)平台支持 (8)板级支持 (9)用户空间的Sysfs接口(可选) (9)Sysfs中的路径 (9)从内核代码中导出 (10)本文档提供了一个在Linux下访问GPIO的公约概述。
这些函数以gpio_* 作为前缀。
其他的函数不允许使用这样的前缀或相关的__gpio_* 前缀。
什么是GPIO?"通用输入/输出口"(GPIO)是一个灵活的由软件控制的数字信号。
他们可由多种芯片提供,且对于从事嵌入式和定制硬件的Linux开发者来说是比较熟悉。
每个GPIO都代表一个连接到特定引脚或球栅阵列(BGA)封装中“球珠”的一个位。
电路板原理图显示了GPIO与外部硬件的连接关系。
驱动可以编写成通用代码,以使板级启动代码可传递引脚配置数据给驱动。
片上系统(SOC) 处理器对GPIO有很大的依赖。
在某些情况下,每个非专用引脚都可配置为GPIO,且大多数芯片都最少有一些GPIO。
可编程逻辑器件(类似FPGA) 可以方便地提供GPIO。
像电源管理和音频编解码器这样的多功能芯片经常留有一些这样的引脚来帮助那些引脚匮乏的SOC。
同时还有通过I2C或SPI串行总线连接的"GPIO扩展器"芯片。
大多数PC的南桥有一些拥有GPIO能力的引脚(只有BIOS固件才知道如何使用他们)。
GPIO的实际功能因系统而异。
通常的用法有:- 输出值可写(高电平=1, 低电平=0)。
一些芯片也有如何驱动这些值的选项,例如只允许输出一个值、支持“线与”及其他取值类似的模式(值得注意的是“开漏”信号)。
- 输入值可读(1、0)。
一些芯片支持引脚在配置为“输出”时回读,这对于类似“线与”的情况(以支持双向信号)是非常有用的。
GPIO 控制器可能有输入去毛刺/消抖逻辑,这有时需要软件控制。
- 输入通常可作为IRQ信号,一般是沿触发,但有时是电平触发。
这样的IRQ 可能配置为系统唤醒事件,以将系统从低功耗状态下唤醒。
- 通常一个GPIO根据不同产品电路板的需求,可以配置为输入或输出,也有仅支持单向的。
- 大部分GPIO 可以在持有自旋锁时访问,但是通常由串行总线扩展的GPIO不允许持有自旋锁。
但某些系统也支持这种类型。
对于给定的电路板,每个GPIO都用于某个特定的目的,如监控MMC/SD卡的插入/移除、检测卡的写保护状态、驱动LED、配置收发器、模拟串行总线、复位硬件看门狗、感知开关状态等等。
GPIO公约注意,这个叫做“公约”,因为这不是强制性的,不遵循这个公约是无伤大雅的,因为此时可移植性并不重要。
GPIO常用于板级特定的电路逻辑,甚至可能随着电路板的版本而改变,且不可能在不同走线的板子上使用。
仅有在很少的功能上才具有可移植性,其他功能是平台特定。
这也是由于“胶合”的逻辑造成的。
此外,这不需要任何的执行框架,只是一个接口。
某个平台可能通过一个简单的访问芯片寄存器的内联函数来实现它,其他平台可能通过委托一系列不同的GPIO控制器的抽象函数来实现它。
(有一些可选的代码能支持这种策略的实现,本文档后面会介绍,但作为GPIO接口的客户端驱动程序必须与它的实现无关。
)也就是说,如果在他们的平台上支持这个公约,驱动应该尽可能的使用它。
平台必须在Kconfig中声明对GENERIC_GPIO 的支持(布尔型true),并提供一个<asm/gpio.h> 文件。
那些调用标准GPIO函数的驱动应该在Kconfig 入口中声明依赖GENERIC_GPIO。
当驱动包含文件:#include <linux/gpio.h>GPIO函数是可用,无论是“真实代码”还是经优化过的语句。
如果你遵守这个公约,当你的代码完成后,对其他的开发者来说会更容易看懂和维护。
注意,这些操作包含所用平台的I/O 屏障代码,驱动无须显式地调用他们。
标识GPIOGPIO是通过无符号整型来标识的,范围是0到MAX_INT。
保留“负”数用于其他目的,例如标识信号“在这个板子上不可用”或指示错误。
未接触底层硬件的代码会忽略这些整数。
平台会定义这些整数的用法,且通常使用#define 来定义GPIO ,这样板级特定的启动代码可以直接关联相应的原理图。
相对来说,驱动应该仅使用启动代码传递过来的GPIO 编号,使用platform_data 保存板级特定引脚配置数据(同时还有其他须要的板级特定数据),避免可能出现的问题。
例如一个平台使用编号32-159 来标识GPIO,而在另一个平台使用编号0-63 标识一组GPIO 控制器,64-79 标识另一类GPIO 控制器, 且在一个含有FPGA的特定板子上使用80-95 。
编号不一定要连续,那些平台中,也可以使用编号2000-2063 来标识一个I2C接口的GPIO扩展器中的GPIO。
如果你要初始化一个带有无效GPIO编号的结构体,可以使用一些负编码(比如"-EINV AL"),那将永远不会是有效。
来测试这样一个结构体中的编号是否关联一个GPIO,你可使用以下断言:int gpio_is_valid(int number);如果编号不存在,则请求和释放GPIO的函数将拒绝执行相关操作(见下文)。
其他编号也可能被拒绝,比如一个编号可能存在,但暂时在给定的板子上不可用。
一个平台是否支持多个GPIO控制器是平台特定的实现问题,就像是否可以在GPIO编号空间中有“空洞”和是否可以在运行时添加新的控制器一样。
这些问题会影响其他事情,包括相邻的GPIO编号是否存在等。
使用 GPIO对于一个GPIO,系统应该做的第一件事情就是通过gpio_request() 函数分配他,见下文。
而接下来要做的是标识它的方向,这通常是在板级启动代码中为GPIO设置一个platform_device时做的。
/* 设置为输入或输出, 返回0 或负的错误代码*/int gpio_direction_input(unsigned gpio);int gpio_direction_output(unsigned gpio, int value);返回值为零代表成功,否则返回一个负的错误代码。
这个返回值需要检查,因为get/set(获取/设置)函数调用没法返回错误,且有可能是配置错误。
通常,你应该在进程上下文中调用这些函数。
然而,对于自旋锁安全的GPIO,在板子启动的早期、进程启动前使用他们也是可以的。
对于作为输出的GPIO,为其提供初始输出值,对于避免在系统启动期间出现信号毛刺是很有帮助的。
为了与传统的GPIO接口兼容, 在设置一个GPIO方向时,如果它还未被申请,则隐含了申请那个GPIO的操作(见下文)。
这种兼容性正在从可选的gpiolib框架中移除。
如果这个GPIO编码不存在或特定的GPIO不能用于那种模式,则方向设置可能失败。
依赖启动固件来正确地设置方向通常是一个坏主意,因为它可能除了启动Linux,并没有做更多的验证工作。
(类似地, 板子的启动代码可能需要将这个复用的引脚设置为GPIO,并正确地配置上拉/下拉电阻。
)访问自旋锁安全的GPIO大多数GPIO控制器可以通过内存读/写指令来访问。
这些指令不会休眠,可以安全地在硬(非线程)中断例程和类似的上下文中完成。
对于那些用gpio_cansleep()测试总是返回失败的GPIO(见下文),使用以下的函数访问:/* GPIO 输入:返回零或非零*/int gpio_get_value(unsigned gpio);/* GPIO 输入*/void gpio_set_value(unsigned gpio, int value);返回值是布尔值,零表示低电平,非零表示高电平。
当读取一个输出引脚的值时,返回值应该是引脚上的值。
这个值不总是和输出值相符,因为存在开漏输出信号和输出潜伏期的问题。
以上的get/set函数不会对早期已经通过gpio_direction_*()报告“无效的GPIO”返回错误。
此外,还需要注意的是并不是所有平台都可以从输出引脚中读取数据的,那些引脚也不总是返回零。
且对那些无法安全访问(可能会休眠)的GPIO(见下文)使用这些函数是错误的。
在GPIO编号(还有输出、值)为常数的情况下,鼓励通过平台特定的实现来优化这两个函数来访问GPIO值。
这种情况(读写一个硬件寄存器)下只需要几条指令是很正常的,且无须自旋锁。
这种优化函数比起那些在子程序上花费许多指令的函数可以使得模拟接口(译者注:例如GPIO模拟I2C、1-wire或SPI)的应用(在空间和时间上都)更具效率。
访问可能休眠的GPIO某些GPIO控制器必须通过基于总线(如I2C或SPI)的消息访问。
读或写这些GPIO值的命令需要等待其信息排到队首才发送命令,再获得其反馈。
期间需要休眠,这不能在IRQ例程(中断上下文)中执行。
支持此类GPIO的平台通过以下函数返回非零值来区分出这种GPIO。
(此函数需要一个之前通过gpio_request分配到的有效的GPIO编号):int gpio_cansleep(unsigned gpio);为了访问这种GPIO,内核定义了一套不同的函数:/* GPIO 输入:返回零或非零,可能会休眠*/int gpio_get_value_cansleep(unsigned gpio);/* GPIO 输入,可能会休眠*/void gpio_set_value_cansleep(unsigned gpio, int value);访问这样的GPIO需要一个允许休眠的上下文,例如线程IRQ处理例程,并用以上的访问函数替换那些没有cansleep()后缀的自旋锁安全访问函数。
除了这些访问函数可能休眠,且它们操作的GPIO不能在硬件IRQ处理例程中访问的事实,这些处理例程实际上和自旋锁安全的函数是一样的。
** 除此之外** 调用设置和配置此类GPIO的函数也必须在允许休眠的上下文中,因为它们可能也需要访问GPIO控制器芯片:(这些设置函数通常在板级启动代码或者驱动探测/断开代码中,所以这是一个容易满足的约束条件。
) gpio_direction_input()gpio_direction_output()gpio_request()## gpio_request_one()## gpio_request_array()## gpio_free_array()gpio_free()gpio_set_debounce()声明和释放GPIO为了有助于捕获系统配置错误,定义了两个函数。