gpio 订阅
GPIO(英语:General-purpose input/output),通用型之输入输出的简称,功能类似8051的P0—P3,其接脚可以供使用者由程控自由使用,PIN脚依现实考量可作为通用输入(GPI)或通用输出(GPO)或通用输入与输出(GPIO),如当clk generator, chip select等。既然一个引脚可以用于输入、输出或其他特殊功能,那么一定有寄存器用来选择这些功能。对于输入,一定可以通过读取某个寄存器来确定引脚电位的高低;对于输出,一定可以通过写入某个寄存器来让这个引脚输出高电位或者低电位;对于其他特殊功能,则有另外的寄存器来控制它们。 展开全文
GPIO(英语:General-purpose input/output),通用型之输入输出的简称,功能类似8051的P0—P3,其接脚可以供使用者由程控自由使用,PIN脚依现实考量可作为通用输入(GPI)或通用输出(GPO)或通用输入与输出(GPIO),如当clk generator, chip select等。既然一个引脚可以用于输入、输出或其他特殊功能,那么一定有寄存器用来选择这些功能。对于输入,一定可以通过读取某个寄存器来确定引脚电位的高低;对于输出,一定可以通过写入某个寄存器来让这个引脚输出高电位或者低电位;对于其他特殊功能,则有另外的寄存器来控制它们。
信息
外文名
GPIO
功    耗
大约1μA
特殊功能
GPxDAT 寄存器
分    类
硬件/嵌入开发驱动开发/核心开发
中文名
通用输入/输出口
全    称
General Purpose Input Output
优    点
小封装
gpio优点
GPIO的优点(端口扩展器) 低功耗:GPIO具有更低的功率损耗(大约1μA,μC的工作电流则为100μA)。集成IIC从机接口:GPIO内置IIC从机接口,即使在待机模式下也能够全速工作。小封装:GPIO器件提供最小的封装尺寸 ― 3mm x 3mm QFN!低成本:您不用为没有使用的功能买单。快速上市:不需要编写额外的代码、文档,不需要任何维护工作。灵活的灯光控制:内置多路高分辨率的PWM输出。可预先确定响应时间:缩短或确定外部事件与中断之间的响应时间。更好的灯光效果:匹配的电流输出确保均匀的显示亮度。布线简单:仅需使用2条就可以组成IIC总线或3条组成SPI总线。与ARM 的几组GPIO引脚,功能相似,GPxCON 控制引脚功能,GPxDAT用于读写引脚数据。另外,GPxUP用于确定是否使用上拉电阻。 x为A,B,,H/J,GPAUP 没有上拉电阻。
收起全文
精华内容
下载资源
问答
  • android平台通过GPIO口控制LED灯
  • Linux内核GPIO子系统分析

    千次阅读 多人点赞 2020-08-23 10:18:31
    Linux内核的GPIO子系统通过gpiolib来实现,gpiolib始于2.6.24版本,这里是gpiolib的初始提交信息,这里是gpiolib的初始代码。 下面是子系统的架构图: gpiolib向上为使用gpio的设备驱动程序提供了统一的接口,向下为...

    概述

    Linux内核的GPIO子系统通过gpiolib来实现,gpiolib始于2.6.24版本,这里是gpiolib的初始提交信息,这里是gpiolib的初始代码

    下面是子系统的架构图:
    在这里插入图片描述
    gpiolib向上为使用gpio的设备驱动程序提供了统一的接口,向下为SoC的gpio控制器提供注册到系统的接口。

    gpiolib为驱动程序提供的服务包括:

    • 系统中GPIO信息的管理,比如有多少个GPIO,每个GPIO的编号是什么等;
    • GPIO的申请、释放;
    • IO的输入、输出方向的设置;IO电平的输出或者输入设置;以及GPIO与中断号的相互转换;
    • DTS中关于GPIO相关的配置信息的解析;
    • gpio系统与sysfs文件系统的交互;
    • gpio系统与debugfs文件系统的交互等。

    gpiolib为SoC芯片的GPIO控制器提供的服务包括:

    • 将GPIO控制器抽象为gpio_chip,并提供接口将gpio_chip注册到系统中;
    • gpio_chip抽象了关于GPIO执行申请、释放、方向设置、IO电平输出等接口,特定SoC芯片的GPIO控制器驱动程序需要实现这些接口,从而使设备驱动程序可以正常使用gpio;

    3.12.0版本之后,社区对于GPIO子系统进行了重构,内核对于gpio的管理从基于gpio num的方式,修改为基于“opaque handlers”的方式,下面是gpiolib重构时代码提交信息

    • gpio works with integers, whereas gpiod operates on opaque handlers
      which cannot be forged or used before proper acquisition
    • gpiod get/set functions are aware of the active low state of a GPIO
    • gpio consumers should now include <linux/gpio/consumer.h> to access
      the new interface, whereas chips drivers will use
      <linux/gpio/driver.h>

    这段提交信息中,说到了两种gpiolib子系统的工作原理,以及重构后,gpio消费者通过“<include/linux/gpio/consumer.h>”使用新版的gpio配置接口,gpio控制器芯片通过“<include/linux/gpio/driver.h>”使用新版的gpio控制器配置接口。

    为了兼容之前的gpio相关的设备驱动,内核保留了重构之前的gpio接口,接口声明位于”<include/linux/gpio.h>“,该文件的开头的注释部分也说明了该文件中定义的接口已经被遗弃,该文件的作用是为了兼容之前的设备驱动程序, 对于新的驱动程序请使用“<incldue/linux/gpio/consumer.h>”中的接口。

    This is the LEGACY GPIO bulk include file, including legacy APIs. It is
    used for GPIO drivers still referencing the global GPIO numberspace,
    and should not be included in new code.
    
    If you're implementing a GPIO driver, only include <linux/gpio/driver.h>
    If you're implementing a GPIO consumer, only include <linux/gpio/consumer.h>
    
    see Documentation/driver-api/gpio/legacy.rst。
    

    架构实现

    gpiolib通过gpio_chip结构抽象了所有对于GPIO的操作,gpio chip驱动程序,实现这些抽象接口,并将gpio_chip注册到gpiolib子系统中。gpio驱动程序首先通过gpio申请接口,申请成功后,返回gpio的句柄,之后,通过这个句柄完成对于gpio的各种操作。下图表示gpio驱动程序如何通过gpiolib框架完成对于特定gpiochip的访问控制的。
    在这里插入图片描述
    gpiolib子系统经过多年的发展,其对于gpio的管理方式产生了两种不同的机制,一种是基于gpio num的方式,另一种是基于gpio_desc的描述符形式。前一种机制由于种种问题,现在已经被废弃,但为了向后兼容,内核对于该机制进行了保留,但对于新的gpio驱动程序,内核强烈建议使用新版的机制。

    下面的章节会介绍在编写gpio相关的驱动程序时常用到的数据结构和接口,其分为两部分:旧架构和新架构。旧架构主要讲述gpiolib重构之前的使用方式;新架构主要讲述gpiolib重构之后的使用方式。每部分都会分为四个小部分:数据结构、APIs、示例。本文所介绍的内核代码版本为5.8

    旧架构

    需要声明的是,下面所说的gpio操作机制,已经在3.12.0版本的内核之后,被列为历史遗迹,在新的gpio相关驱动中不推荐使用。include/linux/gpio.h在4.17.0版本中,再次强调该文件中的接口已经成为遗产
    对于gpio驱动,使用<linux/gpio/consumer.h>中的接口,对于gpio chip驱动,使用<linux/gpio/driver.h>中的接口。

    This is the LEGACY GPIO bulk include file, including legacy APIs. It is
    used for GPIO drivers still referencing the global GPIO numberspace,
    and should not be included in new code.
    
    If you're implementing a GPIO driver, only include <linux/gpio/driver.h>
    If you're implementing a GPIO consumer, only include <linux/gpio/consumer.h>
    

    内核需要配置CONFIG_GPIOLIB选项,用来启用gpiolib子系统。

    数据结构

    对于gpio相关的驱动来说,没有比较重要的数据结构,所有关于gpio的操作都时基于一个gpio num,这里就不涉及数据结构了。

    APIs

    当内核启用CONFIG_GPIOLIB之后,驱动程序就可以使用gpio相关的接口完成gpio相关的操作。对于gpio相关的操作一般分为下面几类:

    获取gpio num

    	int of_get_named_gpio_flags(struct device_node *np,
    										const char *list_name, int index, enum of_gpio_flags *flags);
    
    	int of_get_gpio_flags(struct device_node *np, int index,enum of_gpio_flags *flags);
    	
    	int of_get_named_gpio(struct device_node *np, const char *propname, int index);
    
    	int of_get_gpio(struct device_node *np, int index);
    

    上面是获取设备节点中gpio相关配置信息的api函数。下面举一个例子,来说明一下各个api的使用方式。

    假如,有一个驱动程序gpio-drv,其dts配置信息如下:

    	gpio-dmeo{
    		compatible = "gpio-drv";
            pinctrl-names = "default";
            pinctrl-0 = <&pinctrl_gpio0>;
    
    		gpios = <0
    		         &gpio1 1 2
    		         0
    		         &gpio2 3 4>;
    	}
    

    上面的设备dts配置中的,gpios字段定义了驱动程序使用到的gpios,其包括四个gpio,其中,索引为0、2的gpio没有配置,下面我们分别使用上面提到的api获取一下gpios的配置信息。

    比如,我们想获得第二个gpio的配置信息,我们可以使用如下方式:

    	of_get_named_gpio_flags(np, "gpios", 1, &flags); //注意,gpio的索引值从0开始,gpios对应于dts中的gpios
    	
    	of_get_gpio_flags(np, 1, &flags); //注意,该api只适用于存在单个gpio配置信息的情况下,例如,本地中只有gpios一个gpios配置信息。
    
    	of_get_named_gpio(np, "gpios", 1);
    
    	of_get_gpio(np, 1);//此api与of_get_gpio_flags的注意事项一样。
    

    获取gpio数量

    	int of_gpio_named_count(struct device_node *np, const char* propname);
    
    	int of_gpio_count(struct device_node *np);
    

    这两个api用于获取设备驱动相关的gpios的数量,例如,上一节的dts配置,

    	of_gpio_named_count(np, "gpios");//返回4
    	of_gpio_count(np);//返回4
    

    申请/释放gpio num

    有了gpio num之后,在正式使用gpio之前需要申请,使用之后还需要对其还需要释放。

    	int gpio_request(unsigned gpio, const char *label);
    	int gpio_request_one(unsigned gpio,
    					unsigned long flags, const char *label);
    
    	int gpio_request_array(const struct gpio *array, size_t num);
    	void gpio_free(unsigned gpio);
    	void gpio_free_array(const struct gpio *array, size_t num);
    	
    	int devm_gpio_request(struct device *dev, unsigned gpio,
    			    const char *label);
    
    	int devm_gpio_request_one(struct device *dev, unsigned gpio,
    				unsigned long flags, const char *label);
    
    	void devm_gpio_free(struct device *dev, unsigned int gpio);
    

    这里需要注意就是, 如果申请成功,返回值为0,反之失败,可以通过errno分析失败的原因。

    设置gpio方向

    gpio可以设置为输出或者输入,具体的设置api如下:

    	int gpio_direction_input(unsigned gpio);
    	int gpio_direction_output(unsigned gpio, int value);
    

    设置gpio输出值

    gpio如果配置成输出模式,那么可以通过如下api控制gpio的输出值。

    	void gpio_set_value(unsigned gpio, int value);
    

    获取gpio当前值

    gpio如果配置成输入模式,那么可以通过如下api读取gpio的值。

    	int gpio_get_value(unsigned gpio);
    
    注意,gpio为输出模式时,也能通过该API获取当前IO值。
    

    获取gpio对应的irq num

    一般情况下,每个gpio都可以作为单独的中断源,每个gpio num对应一个具体的irq num,通过下面的api可以将一个gpio转换为irq。

    	int gpio_to_irq(unsigned gpio);
    

    同时,使用irq_to_gpio可以将irq转换为对应的gpio num。

    	int irq_to_gpio(unsigned irq);
    

    示例

    假如,有一个驱动程序gpio-drv,其dts配置信息如下:

    	gpio-dmeo{
    		compatible = "gpio-drv";
            pinctrl-names = "default";
            pinctrl-0 = <&pinctrl_gpio0>;
    
    		gpios = <0
    		         &gpio1 1 2
    		         0
    		         &gpio2 3 4>;
    	}
    

    下面是gpio-drv使用gpios中定义的gpio代码。

    	static int gpio_drv_probe(struct platform_device *pdev)
    	{
    		int gpio1, gpio3;
                                                       
    	    struct device *dev = &pdev->dev;
    	    struct device_node *node = pdev->dev.of_node;
    		
    		gpio1 = of_get_named_gpio_flags(node, "gpios", 1, NULL);
    
    		if (gpio1 < 0) {
    			dev_err(dev, "get gpios failed, index = %d.", 1);
    			return -EINVAL;
    		}
    		gpio3 = of_get_named_gpio_flags(node, "gpios", 3, NULL);
    		if (gpio3 < 0) {
    			dev_err(dev, "get gpios failed, index = %d.", 3);
    			return -EINVAL;
    		}
    
    		if (devm_gpio_request(gpio1) < 0) {
    			dev_err(dev, "get gpios failed, gpio1.");
    			return -EINVAL;
    		}
    
    		if (devm_gpio_request(gpio3) < 0) {
    			dev_err(dev, "get gpios failed, gpio3.");
    			return -EINVAL;
    		}
    
    		... 
    		
    		gpio_direction_output(gpio1, 0);
    
    		gpio_direction_input(gpio3);
    
    		...
    
    		gpio_set_value(gpio1, 1);
    		gpio_get_value(gpio3);
    	}
    

    新架构

    gpio的新架构的所有接口都是基于gpio_desc的,这避免了直接使用gpio num的诸多问题。内核文档consumer.rst详细描述了重构后的gpio子系统的使用方式。接口的前缀由gpio*改为了gpiod*。内核头文件<linux/gpio/consumer.h>描述用于编写gpio相关驱动所需要的接口和数据结构定义。

    内核文档intro.rst描述了GPIO在内核中的接口定义,对于新、旧架构都做了介绍。同时,文中还提到了GPIO的基本含义,以及几个通用的gpio的相关属性,比如,Active-High、Active-Low,以及Open Drain、Open Souce。

    内核文档board.rst描述了几种如何将GPIO分配给设备的方式,比如,Device Treee、ACPI、Platform Data等,本文主要涉及的是Device Tree,其他两种方式不是主流的方式。

    内核文档driver.rst描述了如何基于最新的架构编写gpio chip的驱动程序。内核头文件<linux/gpio/driver.h>描述了用于gpio chip驱动相关的接口和数据结构的定义。

    内核文档drivers-on-gpio.rst描述了内核中与GPIO有关的子系统,谈到了如何在内核或者用户层,关联和使用这些子系统。

    内核文档using-gpio.rst描述了gpio子系统为用户空间提供的标准ABI接口,以及如何基于libgpiod库完成用户间GPIO应用开发的方式。

    上面各个文档讲述的都十分的详细,而且都会配套相应的示例,对于想深入学习GPIO子系统的小伙伴们都是十分有帮助的。

    数据结构

    对于编写gpio驱动来说,一般涉及到的数据结构就是struct gpio_desc,它唯一代表一个gpio line,其抽象了gpio相关的硬件特性,使得驱动开发者可以不用关于具体的gpio chip就能很好的实现gpio相关的控制。

    这是struct gpio_desc的定义:

    	struct gpio_desc {
    			struct gpio_device	*gdev;
    			unsigned long		flags;
    			/* flag symbols are bit numbers */
    			#define FLAG_REQUESTED	0
    			#define FLAG_IS_OUT	1
    			#define FLAG_EXPORT	2	/* protected by sysfs_lock */
    			#define FLAG_SYSFS	3	/* exported via /sys/class/gpio/control */
    			#define FLAG_ACTIVE_LOW	6	/* value has active low */
    			#define FLAG_OPEN_DRAIN	7	/* Gpio is open drain type */
    			#define FLAG_OPEN_SOURCE 8	/* Gpio is open source type */
    			#define FLAG_USED_AS_IRQ 9	/* GPIO is connected to an IRQ */
    			#define FLAG_IRQ_IS_ENABLED 10	/* GPIO is connected to an enabled IRQ */
    			#define FLAG_IS_HOGGED	11	/* GPIO is hogged */
    			#define FLAG_TRANSITORY 12	/* GPIO may lose value in sleep or reset */
    			#define FLAG_PULL_UP    13	/* GPIO has pull up enabled */
    			#define FLAG_PULL_DOWN  14	/* GPIO has pull down enabled */
    			#define FLAG_BIAS_DISABLE    15	/* GPIO has pull disabled */
    			
    			/* Connection label */
    			const char		*label;
    			/* Name of the GPIO */
    			const char		*name;
    	};
    
    • gdev :抽象了具体的gpio chip设备。
    • flags:表示gpio的属性,下面定义了具体的属性值,比如,open_drain、open_source、pull_up、pull_down、bias_disabel等。
    • label:定义了gpio的标签名,一般用作device tree相关处理接口的参数。
    • name : 定义了gpio 的名字。

    gpio驱动在使用gpio之前,首先需要获得gpio_desc描述符,才能继续操作gpio。

    APIs

    获得/处置gpio

    如果设备只需要一个gpio,那么可以通过gpiod_get获得对应的gpio_desc。

    struct gpio_desc *gpiod_get(struct device *dev, const char *con_id,
    		    enum gpiod_flags flags);
    

    如果设备同时定义了多个gpio(比如用于LED数码显示的设备驱动),那么,在获取gpio时,需要额外增加一个index参数用于区分。

    struct gpio_desc *gpiod_get_index(struct device *dev,
    				  const char *con_id, unsigned int idx,
    				  enum gpiod_flags flags);
    

    对于flags用于初始化gpio的方向和初始值,其定义如下:

    • GPIOD_ASIS或0:表示对gpio不进行初始化,之后必须设置gpio的方向才能使用该gpio。
    • GPIOD_IN:初始化该gpio作为输入模式。
    • GPIOD_OUT_LOW :初始化该gpio作为输出模式,并且输出低电平。
    • GPIOD_OUT_HIGH :初始化该gpio作为输出模式,并且输出高电平。
    • GPIOD_OUT_LOW_OPEN_DRAIN:初始化该gpio为输出模式,默认输出低电平,并且其电气特性为开漏输出。
    • GPIOD_OUT_HIGH_OPEN_DRAIN:初始化该gpio为输出模式,默认输出高电平,并且其电气特性为开漏输出。

    最后两个gpio配置一般应用在电气连接必须是开漏的场景,比如I2C的SDA和SCL。

    所有接口成功返回gpio_desc,失败返回可以通过IS_ERR检测的错误信息(注意:绝不会返回NULL)。错误信息-ENOENT 表示没有为该设备分配请求的gpio,其他的错误表示gpio可以获得,但是,过程中存在某些错误,这对于进一步的调试非常有帮助。

    struct gpio_descs *gpiod_get_array(struct device *dev,
    				   const char *con_id,
    				   enum gpiod_flags flags);
    

    对于配置了多个gpios的设备,可以通过上面的函数一次获得,结构struct gpio_descs的定义如下:

    struct gpio_descs {
    	struct gpio_array *info;
    	unsigned int ndescs;
    	struct gpio_desc *desc[];
    }
    

    desc指向gpio的gpio_desc数组,这可以加速关于gpio的访问速度。

    可以使用下面的函数释放gpio_desc的使用权。

    void gpiod_put(struct gpio_desc *desc);
    void gpiod_put_array(struct gpio_descs *descs);
    

    注意:

    • gpio_desc在调用完gpiod_put之后,禁止再次使用该gpio_desc。

    • struct gpio_descs *descs必须要使用gpiod_put_array进行释放,不能单独使用gpiod_put释放每个gpio desc。

        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_descs *devm_gpiod_get_array(struct device *dev,
        					const char *con_id,
        					enum gpiod_flags flags);
      
        void devm_gpiod_put(struct device *dev, struct gpio_desc *desc);
      
        void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs);
      

    上面提供了devm版本的gpio访问接口。

    为了更好的理解上述接口的使用方式,这里举了一个具体的设备实例。

    foo_device {
    	compatible = "acme,foo";
    	...
    	led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */
    		    <&gpio 16 GPIO_ACTIVE_HIGH>, /* green */
    		    <&gpio 17 GPIO_ACTIVE_HIGH>; /* blue */
    
    	power-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
    };
    

    设备foo_device定义了两个gpio功能属性,led-gpios和power-gpios。gpio 15、16、17用于led-gpios属性,gpio 1用于power-gpios属性。获取这些gpios的方式如下:

    struct gpio_desc *red, *green, *blue, *power;
    
    red   = gpiod_get_index(dev, "led", 0, GPIOD_OUT_HIGH);
    green = gpiod_get_index(dev, "led", 1, GPIOD_OUT_HIGH);
    blue  = gpiod_get_index(dev, "led", 2, GPIOD_OUT_HIGH);
    
    power = gpiod_get(dev, "power", GPIOD_OUT_HIGH);
    

    注意,index和con_id参数与foo_device属性的对应关系。

    设置方向

    使用gpio之前必须首先设置gpio的方向,如果在gpiod_get*()的flags参数已经指定了方向,可以不再进行方向的设置。

    int gpiod_direction_input(struct gpio_desc *desc);
    int gpiod_direction_output(struct gpio_desc *desc, int value);
    

    两个函数,成功返回0,失败返回负数。必须仔细检查两个函数的返回值,因为get/set函数不会返回错误码。必须在进程上下文使用这些函数,对于spinlock-safe的GPIOs可以在系统启动的早期(没有创建响应的进程)使用。

    对于配成输出的gpio,value会作为初始化进行输出。这对于系统初始化时是有帮助的,可以去除gpio上的毛刺。

    驱动程序可以通过下面的函数查询gpio的方向:

    int gpiod_get_direction(const struct gpio_desc *desc);
    

    返回值,0表示输出,1表示输入,否则会返回错误码。

    必须注意的是,gpio不会有默认的方向,对没有初始化方向的gpio进行操作是未定义的行为

    spinlock-safe的gpio访问

    所谓的spinlock-safe的gpio访问说的是,驱动程序在对这些gpio的访问时不会睡眠,因为在使用spinlock的场景中是不允许睡眠发生的,所以,对于这些gpio的访问是spinlock-sfae的,换句话说,可以在不允许发生睡眠的上下文中使用这些gpios,比如中断上下文。

    对于GPIO控制器,可以通过内存访问指令进行直接的读写,所以,这些附属于GPIO控制器的GPIOs一般都是spinlock-safe的gpios。可以在原子上下文中使用如下函数访问gpios。

    int gpiod_get_value(const struct gpio_desc *desc);
    void gpiod_set_value(struct gpio_desc *desc, int value);
    

    注意:

    • vlaue:value是布尔类型,0表示低电平,非零表示高电平。
    • 对于方向为输出的gpio,通过gpiod_get_value可以得到gpio当前值,即为当前gpio引脚上的真实电平状态。实际情况是,可能得到的值与输出的值不匹配,可能的原因是gpio被配置成开漏输出或者输出存在延时。
    • get/set不会返回错误码,因为“无效的gpio”应该提前通过gpiod_direcion_*函数进行检查出来。
    • 不是所有的平台都支持读取输出模式下的gpio值,此时,对于gpio的读取永远返回0,比如,imx6ul平台的gpio,需要配置SPIN寄存器才能读取输出模式下的gpio值。

    gpios与IRQs之间的映射

    gpio引脚一般都可以作为中断源,可以使用如下函数将gpio转换为对应的IRQ num。

    int gpiod_to_irq(const struct gpio_desc *desc);
    

    如果成功返回irq num,否则返回负的错误码,这可能是由于gpio不能作为IRQ中断使用。注意,gpio作为中断源使用时,必须被配置成输入模式。gpiod_to_irq不会睡眠。

    使用gpiod_to_irq成功获取到irq num之后,可以做为irq_request或者free_irq的参数使用。

    与旧gpio子系统通信

    内核中仍然有一些子系统在使用基于gpio num的gpio访问方式,为了能够与之兼容,内核提供了两种架构直接的转化函数。

    int desc_to_gpio(const struct gpio_desc *desc);
    struct gpio_desc *gpio_to_desc(unsigned gpio);
    
    • desc_to_gpio返回的gpio在desc释放之前都是可用的。
    • 传递给gpio_desc的gpio必须是通过gpio_requst*获得的。
    • 由另一个函数,释放由其中一个函数返回gpio的行为是不允许和未定义的。

    示例

    这里以dht11的驱动程序为例,简要说一下,如何使用gpiod_*函数族。

    dht11是比较常用的温湿度采集模块,其通信方式为单总线(one-wire bus)方式,即,CPU通过一个GPIO就可以实现与之通信。

    下面是dht11的device tree配置信息。

    	humidity_sensor {
    		compatible = "dht11";
    		gpios = <&gpio0 6 0>;
    }
    

    dht11_probe对gpio进行申请和配置。

    static int dht11_probe(struct platform_device *pdev) 
    {
    	... ...
    
    	dht11->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN);
    	if (IS_ERR(dht11->gpiod))
    		return PTR_ERR(dht11->gpiod);
    
    	... ...
    
    	dht11->irq = gpiod_to_irq(dht11->gpiod);
    	if (dht11->irq < 0) {
    		dev_err(dev, "GPIO %d has no interrupt\n", desc_to_gpio(dht11->gpiod));
    		return -EINVAL;
    	}
    	... ... 
    }
    

    dht11_read_raw对gpio的方向和值进行配置。

    static int dht11_read_raw(struct iio_dev *iio_dev,
    		  const struct iio_chan_spec *chan,
    		int *val, int *val2, long m) {
    	... ...
    
    	ret = gpiod_direction_output(dht11->gpiod, 0);
    	if (ret)
    		goto err;
    	... ...
    
    	ret = gpiod_direction_input(dht11->gpiod);
    	if (ret)
    		goto err;
    	... ...
    }
    

    dht11_handle_irq中处理了gpio的状态。

    static irqreturn_t dht11_handle_irq(int irq, void *data)
    {
    	... ...
    
    	dht11->edges[dht11->num_edges++].value =
    					gpiod_get_value(dht11->gpiod);
    
    	... ...
    }
    

    总结

    好了,上文主要分析了GPIO新旧架构之间的区别和联系,目前,社区明确要求在新的驱动程序中使用新的架构去进行开发。实际开发时,我们需要首先确定当前的内核版本是否支持GPIO新架构接口,然后,再选择具体的GPIO使用方式。后面的文章会继续分析GPIO子系统关于gpio chip的部分。

    展开全文
  • GPIO简介

    万次阅读 多人点赞 2021-01-20 10:39:51
    一、GPIO是什么 GPIO(general porpose intput output):通用输入输出端口的简称。可以通过软件控制其输出和输入。stm32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通信,控制以及数据采集的功能。 二、STM32...

    目录

     

    一、GPIO是什么

    二、STM32引脚分类

    三、GPIO内部结构

    四、GPIO的工作模式

    4.1、输入模式(模拟、上拉、下拉、浮空)

    4.2、输出模式(推挽/ / 开漏)

    4.3、复用功能(推挽/ / 开漏)

    4.4、模拟输入输出(上下拉无影响)


    一、GPIO是什么

    GPIO(general porpose intput output):通用输入输出端口的简称。可以通过软件控制其输出和输入。stm32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通信,控制以及数据采集的功能。

    二、STM32引脚分类

    1. 电源管脚: 引脚图中的 VDD、VSS、VREF+、VREF-、VSSA、VDDA 等都属于电源引脚。
    2. 晶振管脚:引脚图中的 PC14、PC15 和 OSC_IN、OSC_OUT 都属于晶振引脚,不过它们还可以作为普通引脚使用。
    3. 复位管脚: 引脚图中的 NRST 属于复位引脚,不做其他功能使用。
    4. 下载引脚:引脚图中的 PA13、PA14、PA15、PB3 和 PB4 属于 JTAG 或SW 下载引脚。不过它们还可以作为普通引脚或者特殊功能使用,具体的功能可以查看芯片数据手册,里面都会有附加功能说明。当然,STM32 的串口功能引脚也是可以作为下载引脚使用。
    5. T BOOT  引脚:引脚图中的 BOOT0 和 PB2(BOOT1)属于 BOOT 引脚,PB2 还可以作为普通管脚使用。在 STM32 启动中会有模式选择,其中就是依靠着 BOOT0和 BOOT1 的电平来决定。
    6. GPIO管脚:引脚图中的 PA、PB、PC、PD 等均属于 GPIO 引脚。从引脚图可以看出,GPIO 占用了 STM32 芯片大部分的引脚。并且每一个端口都有 16 个引脚,比如 PA 端口,它有 PA0-PA15。其他的 PB、PC 等端口是一样的。

    三、GPIO内部结构

    图中最右端 I/O 端口是STM32 芯片的引脚,其它部分都在 STM32 芯片内部

    (1)、保护二极管

    • 引脚内部加上这两个保护二级管可以防止引脚外部过高或过低的电压输入。
    • 当引脚电压高于 VDD_FT 或 VDD 时,上方的二极管导通吸收这个高电压。
    • 当引脚电压低于 VSS 时,下方的二极管导通,防止不正常电压引入芯片导致芯片烧毁。

    (2)、上下拉电阻

    • 上拉和下拉电阻上都有一个开关,通过配置上下拉电阻开关,可以控制引脚的默认状态电平。
    • 当开启上拉时引脚默认电压为高电平,
    • 开启下拉时,引脚默认电压为低电平,这样就可以消除引脚不定状态的影响。
    • 将上拉和下拉的开关都关断,这种状态我们称为浮空模式,一旦配置成这个模式,引脚的电压是不确定的,如果用万用表测量此模式下管脚电压时会发现只有 1 点几伏,而且还不时改变,所以一般情况下我们都会给引脚设置成上拉或者下拉模式,使它有一个默认状态。
    • STM32 上下拉及浮空模式的配置是通过GPIOx_CRL 和 GPIOx_CRH 寄存器控制的。
    • STM32 内部的上拉其实是一个弱上拉,也就是说通过此上拉电阻输出的电流很小,如果想要输出一个大电流,那么就需要外接上拉电阻了

    (3)、P-MOS 和 和 S N-MOS 

    • GPIO 引脚经过两个保护二极管后就分成两路,
    • 上面一路是“输入模式”,下面一路是“输出模式”。
    • 输出模式,线路经过一个由 P-MOS 和 N-MOS管组成的单元电路,这让 GPIO 引脚具有了推挽和开漏两种输出模式。
    • 推挽输出模式,是根据 P-MOS 和 N-MOS 管的工作方式命名的。
    • 在该结构单元输入一个高电平时,P-MOS 管导通,N-MOS 管截止,对外输出高电平(3.3V)。
    • 在该单元输入一个低电平时,P-MOS 管截止,N-MOS 管导通,对外输出低电平(0V)。
    • 如果当切换输入高低电平时,两个 MOS 管将轮流导通,一个负责灌电流(电流输出到负载),一个负责拉电流(负载电流流向芯片),使其负载能力和开关速度都比普通的方式有很大的提高。下图为推挽输出模式的等效电路。

    • 在开漏输出模式时,不论输入是高电平还是低电平,P-MOS 管总处于关闭状态。
    • 当给这个单元电路输入低电平时,N-MOS 管导通,输出即为低电平。
    • 当输入高电平时,N-MOS 管截止,这个时候引脚状态既不是高电平,又不是低电平,我们称之为高阻态。
    • 如果想让引脚输出高电平,那么引脚必须外接一个上拉电阻,由上拉电阻提供高电平。开漏输出模式等效电路图如下图所示。

    •   在开漏输出模式中还有一个特点,引脚具有“线与”关系。即多个开漏输出模式的引脚接在一起,只要有一个引脚为低电平,其他所有管脚都为低电平,即把所有引脚连接在一起的这条总线拉低了。
    • 只有当所有引脚输出高阻态时这条总线的电平才由上拉电阻的 VDD 决定。如果 VDD 连接的是 3.3V,那么引脚输出的就是 3.3V,如果 VDD 连接的是 5V,那么引脚输出的就是 5V。因此如果想要让 STM32 管脚输出 5V,可以选择开漏输出模式,然后在外接上拉电阻的电源 VDD 选择 5V 即可,前提是这个 STM32 引脚是容忍 5V 的。开漏输出模式一般应用在 I2C、SMBUS 通讯等需要“线与”功能的总线电路中。还可以用在电平不匹配的场合中,就如上面说的输出 5V 一样。
    • 推挽输出模式一般应用在输出电平为 0-3.3V 而且需要高速切换开关状态的场合。除了必须要用开漏输出模式的场合,我们一般选择推挽输出模式。要配置引脚是开漏输出还是推挽输出模式可以使用GPIOx_CRL 和 GPIOx_CRH 寄存器。

    (4)、输出数据寄存器

    双 MOS 管结构电路的输入信号,是由 GPIO“输出数据寄存器GPIOx_ODR”提供的,因此我们通过修改输出数据寄存器的值就可以修改 GPIO 引脚的输出电平。而“置位/复位寄存器 GPIOx_BSRR”可以通过修改输出数据寄存器的值从而影响电路的输出。

    (5)、复用功能输出

    由于 STM32 的 GPIO 引脚具有第二功能,因此当使用复用功能的时候,也就是通过其他外设复用功能输出信号与 GPIO 数据寄存器一起连接到双 MOS 管电路的输入,其中梯形结构是用来选择使用复用功能还是普通 IO 口功能。例如我们使用 USART 串口通讯时,需要用到某个 GPIO 引脚作为通讯发送引脚,这个时候就可以把该 GPIO 引脚配置成 USART 串口复用功能,由串口外设控制该引脚,发送数据。

    (6)、输入数据寄存器

    输入数据寄存器是由 IO 口经过上下拉电阻、施密特触发器引入。当信号经过触发器,模拟信号将变为数字信号 0 或 1,然后存储在输入数据寄存器中,通过读取输入数据寄存器 GPIOx_IDR 就可以知道 IO 口的电平状态。

    (7)、复用功能输入

    此模式与复用功能输出类似。在复用功能输入模式时,GPIO 引脚的信号传输到 STM32 其他片上外设,由该外设读取引脚的状态。同样,如我们使用 USART 串口通讯时,需要用到某个 GPIO 引脚作为通讯接收引脚,这个时候就可以把该 GPIO 引脚配置成 USART 串口复用功能,使 USART 可以通过该通讯引脚的接收远端数据。

    (8)、模拟输入输出

    当 GPIO 引脚用于 ADC 采集电压的输入通道时,用作“模拟输入”功能,此时信号是不经过施密特触发器的,因为经过施密特触发器后信号只有 0、1 两种状态,ADC 外设要采集到原始的模拟信号,信号源输入必须在施密特触发器之前。类似地,当 GPIO 引脚用于 DAC 作为模拟电压输出通道时,此时作为“模拟输出”功能, DAC 的模拟信号输出就不经过双 MOS 管结构了,模拟信号直接通过管脚输出。

    四、GPIO的工作模式

    通过 GPIO 内部的结构关系,决定了 GPIO 可以配置成以下几种模式。

    4.1、输入模式(模拟、上拉、下拉、浮空)

    • 在输入模式时,施密特触发器打开,输出被禁止。
    • 可通过输入数据寄存器GPIOx_IDR 读取 I/O 状态。
    • 输入模式可以配置为模拟、上拉、下拉以及浮空模式。上拉和下拉输入很好理解,默认的电平由上拉或者下拉决定。
    • 浮空输入的电平是不确定的,完全由外部的输入决定,一般接按键的时候可以使用这个模式。
    • 模拟输入则用于 ADC 采集。

    4.2、输出模式(推挽/ / 开漏)

    • 在输出模式中,推挽模式时双 MOS 管以推挽方式工作,输出数据寄存器GPIOx_ODR 可控制 I/O 输出高低电平。
    • 开漏模式时,只有 N-MOS 管工作,输出数据寄存器可控制 I/O 输出高阻态或低电平。
    • 输出速度可配置,有2MHz\25MHz\50MHz 的选项。此处的输出速度即 I/O 支持的高低电平状态最高切换频率,支持的频率越高,功耗越大,如果功耗要求不严格,把速度设置成最大即可。
    • 在输出模式时,施密特触发器是打开的,即输入可用,通过输入数据寄存器 GPIOx_IDR 可读取 I/O 的实际状态。

    4.3、复用功能(推挽/ / 开漏)

    复用功能模式中,输出使能,输出速度可配置,可工作在开漏及推挽模式,但是输出信号源于其它外设,输出数据寄存器 GPIOx_ODR 无效;输入可用,通过输入数据寄存器可获取 I/O 实际状态,但一般直接用外设的寄存器来获取该数据信号。

    4.4、模拟输入输出(上下拉无影响)

    模拟输入输出模式中,双 MOS 管结构被关闭,施密特触发器停用,上/下拉也被禁止。其它外设通过模拟通道进行输入输出。通过对 GPIO 寄存器写入不同的参数,就可以改变 GPIO 的应用模式,再强调一下,要了解具体寄存器时一定要查阅《STM32F1xx 参考手册》中对应外设的寄存器说明。在 GPIO 外设中,通过设置“端口配置寄存器 GPIOx_CRL 和 GPIOx_CRH”可配置 GPIO 的工作模式和输出速度。CRH 控制端口的高八位,CRL 控制端口的低八位。

    展开全文
  • GPIO

    千次阅读 2014-06-19 15:28:44
    什么是GPIO? =============== GPIO----“通用目的输入/输出端口”----是一个灵活的软件控制的数字信号。许多种类的芯片都会提供,嵌入式linux开发者和硬件定制者会对此比较熟悉。每个GPIO提供一位与特定的...
    什么是GPIO?
    ===============
    GPIO----“通用目的输入/输出端口”----是一个灵活的软件控制的数字信号。许多种类的芯片都会提供,嵌入式linux开发者和硬件定制者会对此比较熟悉。每个GPIO提供一位与特定的管脚(或是“球”,BGA(Ball Grid Array)封装下)相连。单板电路图会显示外部硬件与GPIOs的连接关系。GPIO驱动可写成通用的,便于单板setup代码可以将这些管脚配置数据传递给驱动。

    SOC处理器非常依赖于GPIO。某些情况下,普通管脚可以被配置为GPIO。大多数芯片至少拥有几组类似的GPIO。可编程逻辑器件(如FPGAs)可以很容易提供GPIO。一些多功能芯片,如电源管理、声音解码等经常具有一些这样的管脚来弥补SOC芯片上面管脚的不足。同样,也存在一些GPIO扩展芯片,连接用于I2C或是SPI串行总线。多数PC南桥拥有几组GPIO兼容的管脚(仅有BIOS固件知道如何使用它们)。


    不同系统间的GPIO的确切作用不同。通用常有下面几种:
    ----输出值可写(高=1,低=0)。一些芯片也可以选择驱动这些值的方式,以便支持“线-或”或类似方案(开漏信号线)。

    ----输入值可读(1,0)。一些芯片支持输出管脚回读,这在线或的情况下非常有用(以支持双向信号线)。GPIO控制器可能具有一个输入防故障/防反跳逻辑,有时还会有软件控制。
    ----输入经常被用作中断信号,通常是边沿触发,但也有可能是电平触发。这些中断可以配置为系统唤醒事件,从而将系统从低功耗模式唤醒。

    ----一个GPIO经常被配置为输入/输出双向,根据不同的产品单板需求,但也存在单向的情况。

    ----大多是GPIO可以在获取到spinlock自旋锁时访问,但那些通过串行总线访问的通常不能如此操作(休眠的原因)。一些系统中会同时存在这两种形式的GPIO。

    ----在一个给定单板上,每个GPIO用于一个特定的目的,如监控MMC/SD卡的插入/移除,检查卡写保护状态,驱动LED,配置发送器,串行总线位拆,触发一个硬件看门狗,触发一个开关之类的。


    GPIO约定
    ================

    注意,它被称为一个约定,因为你不是必须要遵守它。如果你不使用此种方式操作,也不会遭到任何惩罚。存在一些可移植性不是关键的应用场景:GPIO经常用作板级胶合逻辑,这些逻辑甚至在单板的不同版本之间都会改变,且不能用在那些连接不同的单板上。只有极少通用的标准功能可以是可移植的。其余的特性是平台特有的,且对于胶合逻辑是关键(且危险)的。

    另外,这不需要任何实现框架,只是一个接口。一个平台可以将它实现为一个简单的inline函数来存取芯片寄存器,另一个可能通过一个多个不同GPIO控制器的抽象委托实现。
    (有一些可选的代码支持这种实施策略,这在本文后面会讲到,但担任客户的GPIO接口驱动不要关心他是如何实现的。)

    也就是说,如果平台支持约定,驱动应当尽可能使用它。平台必须在Kconfig中声明GENERIC_GPIO来支持,并且提供一个<asm/gpio.h>文件。那些不能离开标准GPIO调用的驱动应该具有一个依赖于GENERIC_GPIO的条目。要使GPIO调用有效,无论是“真实代码”或是作为“optimized-away stubs”,驱动需要使用包含文件:
    #include <linux/gpio.h>

    如果你坚持此约定,这样别的开发者理解你的代码会比较容易且可以帮助维护它。

    注意:在那些需要的平台上,这些操作包括I/0操作间隔(barriers);驱动不需要显式添加它们。

    标识GPIO
    -----------------
    GPIO使用一个无符号整型数进行标识,范围0到MAX_INT。那些保留的“negative”(负)数用于其他的目的,如标识信号为“在此单板上无效”,或是指出错误。那些不涉及到基本硬件操作的代码将这些整型数视为不透明的。

    平台定义了它们如何使用这些接口,并且通常为每个GPIO线使用#define宏定义符号,以便单板的启动代码与相关设计直接保持一致。与此相反,驱动应该只使用从setup代码传递给他们的GPIO号码,使用platform_data来保存单板特定的管脚配置数据(与其它所需的单板特定数据一起)。这避免了移植问题。

    例如:一个平台给GPIOs使用号码32-159,同时另一个使用0-63支持一个GPIO控制器集合,64-79支持另一个类型的GPIO控制器,且在一个特定的单板上80-95支持一个FPGA。号码不必是连续的,这些单板也可以使用2000-2063来标识一组用于I2CGPIO扩展

    如果你想要使用一个无效的GPIO号码初始化一个结构体,使用一些负数(可以为“-EINVAL”),它将永远不会有效。为了测试来自这样一个结构的这样一个号码是否能够引用一个GPIO,你需要使用:
    int gpio_is_valid(int number);

    一个无效的号码将被调用(可以是申请或释放GPIO)拒绝。别的号码也可能被拒绝。例如,一个号码可能是有效的,但在给定的单板上临时未使用。

    平台是否支持多个GPIO控制器是平台特定实现的关键,同样是否支持GPIO号码空间的“空洞”,是否支持在运行时增加新控制器也是关键。这些关键会影响多个事情,包括相邻的GPIO号码是否都有效。


    使用GPIOs
    -----------

    要使用GPIO,系统首先要分配一个GPIO,使用gpio_request() 为系统分配一个GPIO。


    接下来要做的一件事是标示GPIO的方向,通常在使用GPIO建立一个platform_device时(位于单板的setup代码中):
    /* set as input or output, returning 0 or negative errno */
    int gpio_direction_input(unsigned gpio);
    int gpio_direction_output(unsigned gpio, int value);


    返回0标示成功,或是一个负的errno错误码。它应该被检查,因为get/set调用没有错误返回,且可能会有错误配置。你通常应该在线程上下文中使用这些调用。虽然如此,对于spinlock-safe的GPIO,在tasking使能之前使用也是可以的,作为一个早期的单板建立。

    对于输出GPIO,value参数提供了初始输出值。这有助于避免系统启动过程中的信号干扰。

    为了与GPIO早期的接口兼容,设置一个GPIO的方向,隐性要求申请GPIO。这个兼容性从可选的gpiolib架构中移除了。


    如果GPIO号码无效或是指定的GPIO不能使用对应模式操作的话,设置方向会失败。依靠boot固件设置好GPIO的方向通常不是一个好主意,因为boot的功能可能没有通过验证(除了boot linux)。(类似的,单板setup代码可能需要将管脚复用为一个GPIO,和配置为合适的上拉/下拉。)


    Spinlock-Safe GPIO访问
    -------------------------

    大多数GPIO控制器可以使用内存读写指令访问。它们不需要休眠,且可以从内部硬件中断处理(非线程)和类似的上下文环境安全完成。

    使用下列调用访问这些GPIO,此时gpio_cansleep将总是返回错误
    /* GPIO INPUT:  return zero or nonzero */
    int gpio_get_value(unsigned gpio);

    /* GPIO OUTPUT */
    void gpio_set_value(unsigned gpio, int value);


    其中,value是一个布尔型参数,零表示低,非零表示高。当读一个输出管脚的值时,返回的值应该是在管脚上看到的值...这并不总是与指定输出值相匹配的,因为存在开漏信号和输出延迟问题。

    get/set调用没有错误返回,因为“无效GPIO”应该已经由gpio_direction_*()提早报告了。虽然如此,并非所有的平台都可以读取输出管脚的值,那些不能读的应该总是返回零。同时,对那些可能导致睡眠的GPIO使用这些接口是一个错误。

    平台的特定实现被鼓励优化这两个调用以获取GPIO值。在那些GPIO号码是常量的情况下,它们通常只需一对指令(读或写一个硬件寄存器)访问,且不需要spinlock。这样的优化可以使位拆分应用更有效率(在时间和空间上)(相比较于花费一堆指令在子例程调用来说)。


    可能睡眠的GPIO访问
    --------------------------

    一些GPIO控制器必须使用基于消息的总线如I2C和SPI来进行访问。读写这些GPIO的命令需要等待到达发送队列的开始和获取它的响应。这需要休眠,且不能从内部中断处理函数中完成。

    对于这种GPIO调用gpio_cansleep接口将返回非零值(需要一个有效的GPIO号码,并已经提前使用gpio_request进行分配)
    int gpio_cansleep(unsigned gpio);

    为了访问这些GPIO,一个不同的访问函数集被定义
    /* 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需要一个可能睡眠的上下文,例如一个线程级别中断处理程序,并且这些访问函数必须代替那些没有cansleep()后缀的spinlock-safe的函数。

    除了这些访问函数可能休眠,且对那些不能从硬件中断处理函数中访问的GPIO起作用外,这些调用与那些spinlock-safe的调用效果一致

      ** IN ADDITION ** calls to setup and configure such GPIOs must be made
    from contexts which may sleep, since they may need to access the GPIO
    controller chip too:  (These setup calls are usually made from board
    setup or driver probe/teardown code, so this is an easy constraint.)
    附加的调用(用于建立和配置这样的GPIO)必须出自可以休眠的上下文,因为它们可能需要访问GPIO控制器芯片:(这些setup调用经常出自单板setup或是驱动probe/teardown(拆卸)代码,所以这是一个简单(无关紧要)的限制。)

    gpio_direction_input()
    gpio_direction_output()
    gpio_request()

    ##  gpio_request_one()
    ## gpio_request_array()
    ##  gpio_free_array()

    gpio_free()
    gpio_set_debounce()




    主张和释放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号会导致gpio_request()失败,同样申请一个已经被主张的也会出错。gpio_request()的返回值必须被检查。通常应该在一个任务上下文中调用此函数,虽然如此,对于spinlock-safe GPIO来讲,在使能tasking之前申请GPIO是可以的(作为早期单板setup的一部分)。

    这些调用服务于两个基本目的。一个是为了诊断目的标识实际使用GPIO的信号,系统可能有几百个GPIO,但在一个给定的单板上常常只有几组处于使用状态。另一个是为了捕获冲突、标示错误。当两个或多个驱动错误地认为它们独占此信号或是一些东西错误的认为移除驱动是安全的,这时需要管理一个信号用于管理活动状态。既是说,申请一个GPIO的过程可以为这种锁服务。

    一些平台也使用GPIO用于功耗管理,如关掉不使用的芯片部分和更简单的门控不使用的时钟。

    对于那些使用pinctrl子系统的管脚的GPIO,子系统应该被通知它们的用途。一个gpiolib驱动的.request()操作可能调用pinctrl_request_gpio(),且一个gpiolib驱动的.free()操作可能调用pinctrl_free_gpio()。pinctrl子系统允许一个pinctrl_request_gpio()在一个管脚或是管脚组被一个设备拥有时成功,为了复用目的。

    任意支持管脚复用硬件的编程需要路由GPIO信号到适当的管脚。这些应该在一个GPIO驱动的.direction_input()或是.direction_output()操作中发生,且发生在任意一个输出GPIO值setup之后。这允许从一个管脚的特殊功能到GPIO的无障碍集成。当使用一个GPIO来实现一个关于一个非GPIO硬件模块驱动一个典型信号的工作区(变通方案)时,这时常会被需求。

    一些平台允许一些或所有的GPIO信号被路由到不同的管脚。同样的,GPIO或管脚的别的方面可能需要配置,如上来/下拉。平台软件应该安排所有的这些细节优先于gpio_request()之前配置。例如,使用pinctrl子系统的映射表,这样GPIO使用者就不需要知道那些细节。

    同样注意:在你释放GPIO之前需要停止使用它。

    考虑到大多数场景中GPIO在它们被声明后实际已经被正确配置,定义了3各附加的调用:
    /* request a single GPIO, with initial configuration specified by
     * 'flags', identical to gpio_request() wrt other arguments and
     * return value
     */
    int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);

    /* request multiple GPIOs in a single call
     */
    int gpio_request_array(struct gpio *array, size_t num);

    /* release multiple GPIOs in a single call
     */
    void gpio_free_array(struct gpio *array, size_t num);


    其中,flags参数当前可以指定为下列属性:
    * GPIOF_DIR_IN - 配置方向为输入
    * GPIOF_DIR_OUT - 配置方向为输出

    * GPIOF_INIT_LOW - 作为输出,设置初始值为低
    * GPIOF_INIT_HIGH - 作为输出,设置初始值为高
    * GPIOF_OPEN_DRAIN - GPIO管脚是开漏极形式
    * GPIOF_OPEN_SOURCE - GPIO管脚是开源极形式


    由于GPIOF_INIT_*仅仅当配置为输出时有效,所以有效组合为
    * GPIOF_IN - 配置为输入
    * GPIOF_OUT_INIT_LOW - 配置为输出,初始为低电平
    * GPIOF_OUT_INIT_HIGH - 配置为输入,初始为高电平


    当设置flag为GPIOF_OPEN_DRAIN时,它将假设管脚是开漏极方式。这类管脚在输出模式将不会被驱动为1。这样的管脚需要连接上拉。通过使能此flag,当它在输出模式被要求设置为1时,gpio lib将使得方向为输入以使得管脚变高。输出模式下,管脚输出值0以驱动电平为低。

    当设置flag为GPIOF_OPEN_SOURCE,它假设管脚时开源极类型。这种管脚在输出模式不能驱动为0。此种管脚需要下拉。通过使能这个flag,当管脚要求输出1时,gpio lib将使得方向变为输入以使得管脚变低。管脚在输出模式驱动1为高

    未来,这些flag可以被扩展以支持更多的特性。

    此外,为了简化多个GPIO的声明/释放,引入了gpio结构来压缩这3个域
    struct gpio {
    unsigned gpio;
    unsigned long flags;
    const char *label;
    };


    一个典型用法的例子如下:
    static struct gpio leds_gpios[] = {
    { 32, GPIOF_OUT_INIT_HIGH, "Power LED" }, /* default to ON */
    { 33, GPIOF_OUT_INIT_LOW,  "Green LED" }, /* default to OFF */
    { 34, GPIOF_OUT_INIT_LOW,  "Red LED"   }, /* default to OFF */
    { 35, GPIOF_OUT_INIT_LOW,  "Blue LED"  }, /* default to OFF */
    { ... },
    };

    err = gpio_request_one(31, GPIOF_IN, "Reset Button");
    if (err)
    ...

    err = gpio_request_array(leds_gpios, ARRAY_SIZE(leds_gpios));
    if (err)
    ...

    gpio_free_array(leds_gpios, ARRAY_SIZE(leds_gpios));



    GPIO映射到中断
    --------------------

    GPIO号码是无符号整型数,同样中断号码也是这样。这些构成了两个逻辑区别的名字空间(GPIO0无须对应使用中断0)。你可以在它们之间使用以下调用进行映射:
    /* 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不能用于中断。)使用一个未setup的GPIO号码作为输出调用gpio_direction_input()或是使用一个不是来源于gpio_to_irq()的中断号是一个未核对的错误,


    这两个映射调用会在单个增加或减少上有耗费。它们不能休眠。

    gpio_to_irq()的返回值(非错误)可以传递给request_irq()或free_irq()。它们经常被保存到对应platform设备的IRQ resource中,这使用单板特定的初始化函数完成。注意,中断触发选项是中断接口的一部分,例如IRQF_TRIGGER_FALLING,作为系统唤醒能力。

    irq_to_gpio()的返回值(非错误)通常用于gpio_get_value(),例如,为了在中断被边沿触发时 初始化和更新驱动状态。注意,一些platform不支持反转映射,所以你应该避免使用它。


    仿真开漏信号
    ----------------------------

    有时共享信号需要使用开漏信号,它只有低信号电平是实际驱动的。(此术语用于COMS晶体管,开集电极用于TTL。)一个上拉电阻引出高电平信号。这有时称为“线与”,或是事实上更多的,来自负逻辑观点(low=true)这是一个“线或”。

    一个常见的开漏信号的例子是一个共享的低电平激活中断线。同样,双向数据总线信号有时也使用开漏信号

    一些GPIO控制器直接支持开漏输出,更多的不支持。当你需要开漏信号但你的硬件不直接支持时,一个常见的方法你可以使用任意的即可用于输入也可以用于输出的GPIO来模拟它
     LOW: gpio_direction_output(gpio, 0) ... this drives the signal
    and overrides the pullup.

     HIGH: gpio_direction_input(gpio) ... this turns off the output,
    so the pullup (or some other device) controls the signal.


    如果你正在驱动信号为高,但是 gpio_get_value(gpio)报告了一个低值(经过适当的上升时间准备),你应该知道可能是某些别的部件驱动了这个共享信号为低。这是不必要的错误。作为一个常见的例子,这是I2C时钟拉伸的方式:一个从部件需要一个低速时钟延迟了SCK的上升沿们,且I2C主设备因此调整了它的信号频率。


    这些约定节省了什么?
    ===============================

    这些约定节省的一个大方面是关于管脚复用,因为这是高度芯片相关且不可移植的。一个平台可能不需要显式的管脚复用,另一个可能只有两个选项用于任意给定的管脚,另一个可能每个管脚有八个选择,一个可能能够路由一个给定的GPIO到多个管脚中的一个。(是的,这些例子在今天的linux上都可以找到)

    在一些平台上配置和上拉/下拉的使能与多路复用相关。并不是所有的平台都支持或是以同样的方式支持它们,任意一个给定的单板可能使用外部上拉(或下拉)以便片上ons不能被使用。(当一个电路需要5千欧姆,片上的100K欧姆电阻不能作用。)同样的,驱动能力(2mA 对 20mA)和电压(1.8V 对 3.3V)是平台特定的,与模型一样在配置的管脚和GPIO之间一一对应

    还有别的系统特定的机制此处并没有提到,如上文提及的抗干扰和线或输出。
    硬件可能按组读写GPIO,但是那通常是单独配置的:对于那些共享同一个bank的GPIO。
    (GPIO通常16或32个为一组,一个给定的SOC系统一般拥有几个这样的BANK。)
    一些系统可以从输出GPIO管脚触发中断,或是从一个没作为GPIO管理的管脚上读值。依赖于这种机制的代码将是不可移植的。

    GPIO动态定义并不是当前的标准,例如,作为配置一个单板附加的GPIO扩展器的边界效应。


    GPIO系统结构(可选)
    =======================================

    如前面提醒的一样,一个可选的实现结构使得平台支持不同种类的GPIO控制器使用同一个编程接口变得简单。这个结构称为gpiolib。

    作为一个调试目的,如果debugfs有效,一个/sys/kernel/debug/gpio文件在那里将被找到。它列出了所有的通过这个结构注册的GPIO控制器,和GPIO当前的使用状态。

    Controller Drivers: gpio_chip
    控制器驱动:gpio_chip
    -----------------------------

    在这个架构中,每个GPIO控制器被封装为一个“gpio_chip”结构体,此结构体中包含了每个控制器的通用信息:

     --确定GPIO方向的方法
     --存取GPIO值的方法
     --声明方法是否休眠的flag
     --可选的debugfs dump方法(展现附加的状态如上拉配置等)
     --用于诊断目的的标签

    每个实例也有自己的私有数据,可能来自device.platform_data:它的第一个GPIO和它暴露几个GPIO.

    实现一个gpio_chip的代码应该支持多个控制器的实例,可能使用驱动模型。代码会配置每个gpio_chip并且执行gpiochip_add()。移除一个GPIO控制器是少见的,使用gpio_remove()移除一个不再有效的GPIO控制器。

    大多数时候,一个gpio_chip是一个实例独有的结构,它的一些状态值不暴露给GPIO接口,如编址、电源管理等等。编码解码器之类的芯片会有复杂的非GPIO状态。

    所有的debugfs dump 方式通常应该忽略那些未作为GPIO请求的信号。他们可以使用gpiochip_is_requested(),此函数返回与GPIO相关的label或是NULL。


    平台支持
    ----------------

    为了支持这个结构,一个平台的Kconfig需要选择ARCH_REQUIRE_GPIOLIB或是ARCH_WANT_OPTIONAL_GPIOLIB之一,且安排的它的<asm/gpio.h>包含<asm-generic/gpio.h>并且定义3个函数gpio_get_value(), gpio_set_value(), 和 gpio_cansleep()。

    他也可以提供一个自定义的值:ARCH_NR_GPIOS,以便能更好的反映平台实际使用的GPIO数目,并不浪费静态区域空间。(它应该计数 内建/SOC GPIO和GPIO扩展器扩展的数目)

    ARCH_REQUIRE_GPIOLIB意味着此平台上gpiolib代码将永久编译进内核

    ARCH_WANT_OPTIONAL_GPIOLIB意味着gpiolib代码默认是关闭的,用于可以使能它并且将它可选的编译进内核。

    如果这些选项都未被选上,平台不能通过GPIO-lib支持GPIO,这些代码也不能被用户使能。

    那些函数琐细的实现可以直接使用架构代码,它们经常通过gpio_chip分配:
      #define gpio_get_value __gpio_get_value
      #define gpio_set_value __gpio_set_value
      #define gpio_cansleep __gpio_cansleep


    爱好者实现可以代替定义他们使用内联函数,使用逻辑优化存取特定的基于SOC的GPIO。例如,如果 引用的GPIO是常数“12”,getting或setting它的值可能只需要2个或3个指令,且从不休眠。如果这样一个优化是不可能的话,这些调用实现必须委托给架构代码,它会耗费至少几十个指令。为了位拆型I/O,这些指令的节约是有相当大的意义的。

    对于SOC来说,平台特定的代码为每个bank的片上GPIO定义和注册了gpio_chip实例。那些GPIO应该被编号和打上标签以匹配芯片厂商文档,且直接匹配单板设计图。他们可以从零开始一直到平台特定的限制。这些GPIO通常集成到单板初始化过程中以使得它们总是有效的,从arch_initcall()到更早,它们总是可以为中断服务。


    板级支持
    -------------

    对于外部GPIO控制器(如I2C或SPI扩展)、ASIC、多功能器件、FPGA或是CPLD,通常单板私有代码例程注册控制器器件且确定它们的驱动使用什么GPIO号来调用gpiochip_add。它们的号码经常在平台特定GPIO之后开始。

    例如,单板setup代码可以创建结构标示芯片想要暴露的GPIO的范围,且使用platform_data传递它们到每个GPIO扩展器芯片。这样芯片驱动的probe()历程可以传递这些数据到gpiochip_add()。

    初始化顺序是很重要的。例如当一个依赖于基于I2C的GPIO的设备,它的probe()例程应该仅能在GPIO有效后调用。这意味着设备不能在GPIO可以工作之前注册。一个解决这样依赖的方法是在板级特定代码中,对于这种gpio_chip控制器来提供setup()和teardown()回调,这些板级特定的回调将注册设备一旦所有的需要资源有效时,并且在GPIO控制器无效时将它们移除。


    用户空间的Sysfs接口(可选)
    ========================================

    使用gpiolib实现结构的平台可以选择为GPIO配置一个sysfs用户接口。这与debugfs接口不同,因为它提供了覆盖GPIO方向和值的控制而不只是显示一个gpio状态信息摘要。另外,它可以在产品系统中提供而不需要调试支持。

    为系统给出对应的硬件文档,用户空间可以知道例如GPIO#23控制着保护线,用于保护flash中的boot区域。系统升级程序可能需要临时移除这个保护,首先引入一个GPIO,然后改变它的输出状态,接下来在重新使能写保护之前升级代码。通常用法中,GPIO#23将不会被触碰,并且内核也不需知道它的信息。

    同样依靠一个合适的硬件文档,在一些系统用户空间,GPIO可以被用于决定那些内核并不关心的系统配置数据。对于一些任务,简单用户空间GPIO驱动是系统真正需要的

    注意,针对通用“LED和按钮”的标准内核驱动存在对应的GPIO任务“leds-gpio”和“gpio-keys”。使用它们代替直接与GPIO通话,它们集成在内核架构比你的用户态代码可能更好。

    Paths in Sysfs
    Sysfs路径
    --------------
    There are three kinds of entry in /sys/class/gpio:
    /sys/class/gpio有3个入口条目:
       - 控制接口用于用户空间获取GPIO控制
       - GPIO自己
       - GPIO控制器(“gpio_chip”实例)

    这是对于标准文件的补充,包括“device”符号

    控制接口是只写的:
        /sys/class/gpio/

        
    “export” ————通过写GPIO的号码到此文件,用户空间可以要求内核导出一个GPIO的控制到用户空间

    例如:“echo 19 > export”将创建一个GPIO #19的“gpio19”节点(假设内核代码未申请此GPIO号)。

    “unexport”————与“export”效果相反

    例如:"echo 19 > unexport"将移除一个由“export”文件导出的“gpio19”节点。

    GPIO信号拥有如/sys/class/gpio/gpio42/(对应于GPIO#42)的路径,并且具有下列读写属性:
        /sys/class/gpio/gpioN/

    “direction”————读为“in”或是“out”。这个值通常可写。写“out”默认初始化此值为低。为了确定无障碍操作,值“low”和“high”可以被写入以配置GPIO的输出初始化值。

    注意这个属性“将不存在”如果内核不支持改变一个GPIO的方向,或者它不能被内核代码导出(不能显式的允许用户空间来重新配置GPIO的选项。)

    “value”—————读作“0”(低)或“1”(高)。如果GPIO被配置为一个输出,这个值可写;任何非零值均被视为高。
    如果管脚可以被配置为中断产生中断管脚,且如果它已经被配置为产生中断(参考“edge”描述),你可以poll(2)此文件并且当中断触发时poll(2)将返回。如果你使用了poll(2),设置POLLPRI和POLLERR事件。如果你使用select(2),在exceptfds中设置文件描述符。在poll(2)返回之后,有两个选择一是lseek(2)到sysfs文件的开始且读新的值,另一个是关闭文件且重打开它来读取新的值。(为何这样设置?)

    “edge”————读作“none”、“rising”、“falling”或是“both”。写这些字符串以选择边沿信号,他将使得“value”文件上的poll(2)操作返回。
    这个文件只在管脚可以配置为中断产生输入管脚时存在。

    “active_low”————读为0(false)或1(true)。写任何非零值都会反转读或写的值。目前和后来的poll(2)支持经由edge属性配置为“rising”或“falling”上升沿或下降沿将遵循这个设置。

    GPIO控制器具有如/sys/class/gpio/gpiochip42/(针对控制器,实现GPIO开始于#42)的路径,且具有下列制度属性:
        /sys/class/gpio/gpiochipN/

        
    “base”————与N相等,是第一个被此芯片管理的GPIO
        
    “label”————提供用于诊断(并不总是独一无二的)
        
    “ngpio”————管理的GPIO数(N到N+ngpio-1)

    大多数情况下,单板文档应该提供GPIO的使用目的。虽然如此这些号码并非总是固定的,一个子板上的GPIO可能与基础板使用的不同。此种情况下,你可能需要使用gpiochip节点(可能与设计结合)来为每个信号决定正确的GPIO号码。


    从内核代码中导出
    --------------------------

    内核代码可以显式管理那些使用gpio_request()申请的GPIO的导出
    /* export the GPIO to userspace */
    int gpio_export(unsigned gpio, bool direction_may_change);

    /* reverse gpio_export() */
    void gpio_unexport();

    /* create a sysfs link to an exported GPIO node */
    int gpio_export_link(struct device *dev, const char *name,
    unsigned gpio)

    /* change the polarity of a GPIO node in sysfs */
    int gpio_sysfs_set_active_low(unsigned gpio, int value);


    一个内核驱动申请一个GPIO后,它可以使用gpio_export()使得sysfs接口有效。驱动可以控制信号方向是否可以改变。这使得驱动可以防止用户空间代码不小心冲击重要的系统状态。

    明确的exporting有助于调试(使得一些实验更简单),或是提供一个总是可以使用的接口,适合于bsp文档。

    GPIO被导出后,gpio_export_link()允许在sysfs的任何地方创建GPIO sysfs节点的符号链接。驱动可以用此在它们自己设备sysfs目录下提供指定名字的接口(链接到GPIO节点)

    驱动可以使用gpio_sysfs_set_active_low()隐藏GPIO在用户空间和单板之间的线极性不同。这仅影响sysfs接口。极性变换可以在gpio_export()之前和之后完成,并且前面使能的poll(2) (支持上升沿或下降沿事件)将被重新配置为遵循此设置。
    展开全文
  • 主板GPIO控制软件

    2014-11-20 15:51:34
    本文描述了如何操作工业计算机主板GPIO端口的编程实例
  • GPIO 使用 — Rockchip

    千次阅读 2020-02-09 13:44:10
    昨天跟同事调试GPIO,用的就是Rockchip的板子,我们想把一个gpio口设置成阻塞状态,刚开始一直没有成功,然后我问了连总,连总是我原来的同事,是一个大牛,他跟我说...

           

    昨天跟同事调试GPIO,用的就是Rockchip的板子,我们想把一个gpio口设置成阻塞状态,刚开始一直没有成功,然后我问了连总,连总是我原来的同事,是一个大牛,他跟我说,只要把gpio口设置成输入状态,不要上拉,不要下拉,就可以设置成阻塞态了。

    我确实这样设置了,但是没有效果,我们是直接在代码里面设置的,后来我们的同事在dts里面尝试修改了下,结果成功了。

    看这个宏的意思也就是没有上拉,没有下拉的意思,应该是代码上做了判断。所以我想到代码里面去看看这个宏有什么作用,可惜的是,这个也是在dts里面定义的。

    定义如下:

    然后,我料定,bias-disable 这个一定是在代码里面定义的,于是,我搜索了代码里面的这个字符串。

    所以上面的设置实际上是跑到了这里去执行

    感兴趣的同学可以自己去piconf-generic.c里面去一探究竟。

                   

    然后我网上搜到了一个文章,是说明rockchip gpio的,虽然简单,但是我觉得对于开发有参考意义,推荐给各位义士。


       

                   

    使用的开发板~                 

       

           

           

       

       

                   

    AIO-3399J采用RK3399六核(A72x2+A53x4)64位处理器,主频高达2.0GHz,集成四核Mali-T864 GPU,性能优异。

       

             

             

                 

    简介¶

    GPIO, 全称 General-Purpose Input/Output(通用输入输出),是一种软件运行期间能够动态配置和控制的通用引脚。RK3399有5组GPIO bank:GPIO0~GPIO4,每组又以 A0~A7, B0~B7, C0~C7, D0~D7 作为编号区分(不是所有 bank 都有全部编号,例如 GPIO4 就只有 C0~C7, D0~D2)。所有的GPIO在上电后的初始状态都是输入模式,可以通过软件设为上拉或下拉,也可以设置为中断脚,驱动强度都是可编程的。每个 GPIO 口除了通用输入输出功能外,还可能有其它复用功能,例如 GPIO2_A2,可以利用成以下功能:

    • GPIO2_A2

    • CIF_D2

    每个 GPIO 口的驱动电流、上下拉和重置后的初始状态都不尽相同,详细情况请参考《RK3399 规格书》中的 “Chapter 10 GPIO” 一章。RK3399 的 GPIO 驱动是在以下 pinctrl 文件中实现的:

    kernel/drivers/pinctrl/pinctrl-rockchip.c
    

    其核心是填充 GPIO bank 的方法和参数,并调用 gpiochip_add 注册到内核中。

    AIO-3399J开发板为方便用户开发使用,并没有引出了通用的GPIO口,但是可以将其他接口用作gpio,

    例如LED扩展接口,在不用的情况可以将其当作一般的gpio口使用,其对应引脚如下图:

    本文以TP_RST(GPIO0_B4)和LCD_RST(GPIO4_D5)这两个通用GPIO口为例写了一份简单操作GPIO口的驱动,在SDK的路径为:

    kernel/drivers/gpio/gpio-firefly.c
    

    以下就以该驱动为例介绍GPIO的操作。

    输入输出¶

    首先在DTS文件中增加驱动的资源描述:

    kernel/arch/arm64/boot/dts/rockchip/rk3399-firefly-demo.dtsi
    gpio_demo: gpio_demo {
        status = "okay";
        compatible = "firefly,rk3399-gpio";
        firefly-gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>;          /* GPIO0_B4 */
        firefly-irq-gpio = <&gpio4 29 IRQ_TYPE_EDGE_RISING>;  /* GPIO4_D5 */
    };
    

    这里定义了一个脚作为一般的输出输入口:

    AIO-3399J的dts对引脚的描述与Firefly-RK3288有所区别,GPIO0_B4被描述为:<&gpio0 12 GPIO_ACTIVE_HIGH>,这里的12来源于:8+4=12,其中8是因为GPIO0_B4是属于GPIO0的B组,如果是A组的话则为0,如果是C组则为16,如果是D组则为24,以此递推,而4是因为B4后面的4。GPIO_ACTIVE_HIGH表示高电平有效,如果想要低电平有效,可以改为:GPIO_ACTIVE_LOW,这个属性将被驱动所读取。

    然后在probe函数中对DTS所添加的资源进行解析,代码如下:

    static int firefly_gpio_probe(struct platform_device *pdev)
    {
        int ret; int gpio; enum of_gpio_flags flag;
        struct firefly_gpio_info *gpio_info;
        struct device_node *firefly_gpio_node = pdev->dev.of_node;
    
    
        printk("Firefly GPIO Test Program Probe\n");
        gpio_info = devm_kzalloc(&pdev->dev,sizeof(struct firefly_gpio_info *), GFP_KERNEL);
        if (!gpio_info) {
            return -ENOMEM;
        }
        gpio = of_get_named_gpio_flags(firefly_gpio_node, "firefly-gpio", 0, &flag);
        if (!gpio_is_valid(gpio)) {
            printk("firefly-gpio: %d is invalid\n", gpio); return -ENODEV;
        }
        if (gpio_request(gpio, "firefly-gpio")) {
            printk("gpio %d request failed!\n", gpio);
            gpio_free(gpio);
            return -ENODEV;
        }
        gpio_info->firefly_gpio = gpio;
        gpio_info->gpio_enable_value = (flag == OF_GPIO_ACTIVE_LOW) ? 0:1;
        gpio_direction_output(gpio_info->firefly_gpio, gpio_info->gpio_enable_value);
        printk("Firefly gpio putout\n");
        ......
    }
    

    of_get_named_gpio_flags 从设备树中读取 firefly-gpio 和 firefly-irq-gpio 的 GPIO 配置编号和标志,gpio_is_valid 判断该 GPIO 编号是否有效,gpio_request 则申请占用该 GPIO。如果初始化过程出错,需要调用 gpio_free 来释放之前申请过且成功的 GPIO 。在驱动中调用 gpio_direction_output 就可以设置输出高还是低电平,这里默认输出从DTS获取得到的有效电平GPIO_ACTIVE_HIGH,即为高电平,如果驱动正常工作,可以用万用表测得对应的引脚应该为高电平。实际中如果要读出 GPIO,需要先设置成输入模式,然后再读取值:

    int val;
    gpio_direction_input(your_gpio);
    val = gpio_get_value(your_gpio);
    

    下面是常用的 GPIO API 定义:

    #include <linux/gpio.h>
    #include <linux/of_gpio.h>
    
    
    enum of_gpio_flags {
         OF_GPIO_ACTIVE_LOW = 0x1,
    };
    int of_get_named_gpio_flags(struct device_node *np, const char *propname,
    int index, enum of_gpio_flags *flags);
    int gpio_is_valid(int gpio);
    int gpio_request(unsigned gpio, const char *label);
    void gpio_free(unsigned gpio);
    int gpio_direction_input(int gpio);
    int gpio_direction_output(int gpio, int v);
    

    中断¶

    在Firefly的例子程序中还包含了一个中断引脚,GPIO口的中断使用与GPIO的输入输出类似,首先在DTS文件中增加驱动的资源描述:

    kernel/arch/arm64/boot/dts/rockchip/rk3399-firefly-port.dtsi
    gpio {
        compatible = "firefly-gpio";
        firefly-irq-gpio = <&gpio4 29 IRQ_TYPE_EDGE_RISING>;  /* GPIO4_D5 */
    };
    

    IRQ_TYPE_EDGE_RISING表示中断由上升沿触发,当该引脚接收到上升沿信号时可以触发中断函数。这里还可以配置成如下:

    IRQ_TYPE_NONE         //默认值,无定义中断触发类型
    IRQ_TYPE_EDGE_RISING  //上升沿触发
    IRQ_TYPE_EDGE_FALLING //下降沿触发
    IRQ_TYPE_EDGE_BOTH    //上升沿和下降沿都触发
    IRQ_TYPE_LEVEL_HIGH   //高电平触发
    IRQ_TYPE_LEVEL_LOW    //低电平触发
    

    然后在probe函数中对DTS所添加的资源进行解析,再做中断的注册申请,代码如下:

    static int firefly_gpio_probe(struct platform_device *pdev)
    {
        int ret; int gpio; enum of_gpio_flags flag;
        struct firefly_gpio_info *gpio_info;
        struct device_node *firefly_gpio_node = pdev->dev.of_node;
        ......
        gpio_info->firefly_irq_gpio = gpio;
        gpio_info->firefly_irq_mode = flag;
        gpio_info->firefly_irq = gpio_to_irq(gpio_info->firefly_irq_gpio);
        if (gpio_info->firefly_irq) {
            if (gpio_request(gpio, "firefly-irq-gpio")) {
               printk("gpio %d request failed!\n", gpio); gpio_free(gpio); return IRQ_NONE;
            }
            ret = request_irq(gpio_info->firefly_irq, firefly_gpio_irq, flag, "firefly-gpio", gpio_info);
            if (ret != 0) free_irq(gpio_info->firefly_irq, gpio_info);
                dev_err(&pdev->dev, "Failed to request IRQ: %d\n", ret);
        }
        return 0;
    }
    static irqreturn_t firefly_gpio_irq(int irq, void *dev_id) //中断函数
    {
        printk("Enter firefly gpio irq test program!\n"); return IRQ_HANDLED;
    }
    

    调用gpio_to_irq把GPIO的PIN值转换为相应的IRQ值,调用gpio_request申请占用该IO口,调用request_irq申请中断,如果失败要调用free_irq释放,该函数中gpio_info-firefly_irq是要申请的硬件中断号,firefly_gpio_irq是中断函数,gpio_info->firefly_irq_mode是中断处理的属性,”firefly-gpio”是设备驱动程序名称,gpio_info是该设备的device结构,在注册共享中断时会用到。

    复用¶

    如何定义 GPIO 有哪些功能可以复用,在运行时又如何切换功能呢?以 I2C4 为例作简单的介绍。

    查规格表可知,I2C4_SDA 与 I2C4_SCL 的功能定义如下:

    Pad#                   func0           func1
    I2C4_SDA/GPIO1_B3   gpio1b3   i2c4_sda
    I2C4_SCL/GPIO1_B4   gpio1b4   i2c4_scl
    

    在 kernel/arch/arm64/boot/dts/rockchip/rk3399.dtsi 里有:

    i2c4: i2c@ff3d0000 {
        compatible = "rockchip,rk3399-i2c";
        reg = <0x0 0xff3d0000 0x0 0x1000>;
        clocks = <&pmucru SCLK_I2C4_PMU>, <&pmucru   PCLK_I2C4_PMU>;
        clock-names = "i2c", "pclk";
        interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH 0>;
        pinctrl-names = "default", "gpio";
        pinctrl-0 = <&i2c4_xfer>;
        pinctrl-1 = <&i2c4_gpio>;   //此处源码未添加
        #address-cells = <1>;
        #size-cells = <0>;
        status = "disabled";
    };
    

    此处,跟复用控制相关的是 pinctrl- 开头的属性:

    • pinctrl-names 定义了状态名称列表:default (i2c 功能) 和 gpio 两种状态。

    • pinctrl-0 定义了状态 0 (即 default)时需要设置的 pinctrl: &i2c4_xfer

    • pinctrl-1 定义了状态 1 (即 gpio)时需要设置的 pinctrl: &i2c4_gpio

    这些 pinctrl 在kernel/arch/arm64/boot/dts/rockchip/rk3399.dtsi中这样定义:

    pinctrl: pinctrl {
        compatible = "rockchip,rk3399-pinctrl";
        rockchip,grf = <&grf>;
        rockchip,pmu = <&pmugrf>;
        #address-cells = <0x2>;
        #size-cells = <0x2>;
        ranges;
        i2c4 {
        i2c4_xfer: i2c4-xfer {
            rockchip,pins = <1 12 RK_FUNC_1 &pcfg_pull_none>, <1 11 RK_FUNC_1 &pcfg_pull_none>;
        };
        i2c4_gpio: i2c4-gpio {
            rockchip,pins = <1 12 RK_FUNC_GPIO &pcfg_pull_none>, <1 11 RK_FUNC_GPIO &pcfg_pull_none>;
        };
    };
    

    RK_FUNC_1,RK_FUNC_GPIO 的定义在 kernel/include/dt-bindings/pinctrl/rk.h 中:

     #define RK_FUNC_GPIO    0
     #define RK_FUNC_1   1
     #define RK_FUNC_2   2
     #define RK_FUNC_3   3
     #define RK_FUNC_4   4
     #define RK_FUNC_5   5
     #define RK_FUNC_6   6
     #define RK_FUNC_7   7
    

    另外,像”1 11”,”1 12”这样的值是有编码规则的,编码方式与上一小节”输入输出”描述的一样,”1 11”代表GPIO1_B3,”1 12”代表GPIO1_B4。

    在复用时,如果选择了 “default” (即 i2c 功能),系统会应用 i2c4_xfer 这个 pinctrl,最终将 GPIO1_B3 和 GPIO1_B4 两个针脚切换成对应的 i2c 功能;而如果选择了 “gpio” ,系统会应用 i2c4_gpio 这个 pinctrl,将 GPIO1_B3 和 GPIO1_B4 两个针脚还原为 GPIO 功能。

    我们看看 i2c 的驱动程序 kernel/drivers/i2c/busses/i2c-rockchip.c 是如何切换复用功能的:

    static int rockchip_i2c_probe(struct platform_device *pdev)
    {
        struct rockchip_i2c *i2c = NULL; struct resource *res;
        struct device_node *np = pdev->dev.of_node; int ret;//
        ...
        i2c->sda_gpio = of_get_gpio(np, 0);
        if (!gpio_is_valid(i2c->sda_gpio)) {
            dev_err(&pdev->dev, "sda gpio is invalid\n");
            return -EINVAL;
        }
        ret = devm_gpio_request(&pdev->dev, i2c->sda_gpio, dev_name(&i2c->adap.dev));
        if (ret) {
            dev_err(&pdev->dev, "failed to request sda gpio\n");
            return ret;
        }
        i2c->scl_gpio = of_get_gpio(np, 1);
        if (!gpio_is_valid(i2c->scl_gpio)) {
            dev_err(&pdev->dev, "scl gpio is invalid\n");
            return -EINVAL;
        }
        ret = devm_gpio_request(&pdev->dev, i2c->scl_gpio, dev_name(&i2c->adap.dev));
        if (ret) {
            dev_err(&pdev->dev, "failed to request scl gpio\n");
            return ret;
        }
        i2c->gpio_state = pinctrl_lookup_state(i2c->dev->pins->p, "gpio");
        if (IS_ERR(i2c->gpio_state)) {
            dev_err(&pdev->dev, "no gpio pinctrl state\n");
            return PTR_ERR(i2c->gpio_state);
        }
        pinctrl_select_state(i2c->dev->pins->p, i2c->gpio_state);
        gpio_direction_input(i2c->sda_gpio);
        gpio_direction_input(i2c->scl_gpio);
        pinctrl_select_state(i2c->dev->pins->p, i2c->dev->pins->default_state);
        // ...
    }
    

    首先是调用 of_get_gpio 取出设备树中 i2c4 结点的 gpios 属于所定义的两个 gpio:

    gpios = <&gpio1 GPIO_B3 GPIO_ACTIVE_LOW>, <&gpio1 GPIO_B4 GPIO_ACTIVE_LOW>;
    
    
    

    然后是调用 devm_gpio_request 来申请 gpio,接着是调用 pinctrl_lookup_state 来查找 “gpio” 状态,而默认状态 “default” 已经由框架保存到 i2c->dev-pins->default_state 中了。

    最后调用 pinctrl_select_state 来选择是 “default” 还是 “gpio” 功能。

    下面是常用的复用 API 定义:

    #include <linux/pinctrl/consumer.h> 
    struct device {
        //...
        #ifdef CONFIG_PINCTRL
        struct dev_pin_info  *pins;
        #endif
        //...
    }; 
    struct dev_pin_info {
        struct pinctrl *p;
        struct pinctrl_state *default_state;
        #ifdef CONFIG_PM
        struct pinctrl_state *sleep_state;
        struct pinctrl_state *idle_state;
        #endif
    }; 
    struct pinctrl_state * pinctrl_lookup_state(struct pinctrl *p, const char *name); 
    int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state
    
     *s);
    

    IO-Domain¶

    在复杂的片上系统(SOC)中,设计者一般会将系统的供电分为多个独立的block,这称作电源域(Power Domain),这样做有很多好处,例如:

    • 在IO-Domain的DTS节点统一配置电压域,不需要每个驱动都去配置一次,便于管理;

    • 依照的是Upstream的做法,以后如果需要Upstream比较方便;

    • IO-Domain的驱动支持运行过程中动态调整电压域,例如PMIC的某个Regulator可以1.8v和3.3v的动态切换,一旦Regulator电压发生改变,会通知IO-Domain驱动去重新设置电压域。

    AIO-3399J原理图上的 Power Domain Map 表以及配置如下表所示:

    通过RK3399 SDK的原理图可以看到bt656-supply 的电压域连接的是vcc18_dvp, vcc_io是从PMIC RK808的VLDO1出来的;在DTS里面可以找到vcc1v8_dvp, 将bt656-supply = <&vcc18_dvp>。其他路的配置也类似,需要注意的是如果这里是其他PMIC,所用的Regulator也不一样,具体以实际电路情况为标准。

    调试方法¶

    IO指令¶

    GPIO调试有一个很好用的工具,那就是IO指令,AIO-3399J的Android系统默认已经内置了IO指令,使用IO指令可以实时读取或写入每个IO口的状态,这里简单介绍IO指令的使用。首先查看 io 指令的帮助:

    #io --help
    Unknown option: ?
    Raw memory i/o utility - $Revision: 1.5 $
    
    
    io -v -1|2|4 -r|w [-l <len>] [-f <file>] <addr> [<value>]
    
    
       -v         Verbose, asks for confirmation
       -1|2|4     Sets memory access size in bytes (default byte)
       -l <len>   Length in bytes of area to access (defaults to
                  one access, or whole file length)
       -r|w       Read from or Write to memory (default read)
       -f <file>  File to write on memory read, or
                  to read on memory write
       <addr>     The memory address to access
       <val>      The value to write (implies -w)
    
    
    Examples:
       io 0x1000                  Reads one byte from 0x1000
       io 0x1000 0x12             Writes 0x12 to location 0x1000
       io -2 -l 8 0x1000          Reads 8 words from 0x1000
       io -r -f dmp -l 100 200    Reads 100 bytes from addr 200 to file
       io -w -f img 0x10000       Writes the whole of file to memory
    
    
    Note access size (-1|2|4) does not apply to file based accesses.
    

    从帮助上可以看出,如果要读或者写一个寄存器,可以用:

    io -4 -r 0x1000 //读从0x1000起的4位寄存器的值
    io -4 -w 0x1000 //写从0x1000起的4位寄存器的值
    

    使用示例:

    • 查看GPIO1_B3引脚的复用情况

    • 从主控的datasheet查到GPIO1对应寄存器基地址为:0xff320000

    • 从主控的datasheet查到GPIO1B_IOMUX的偏移量为:0x00014

    • GPIO1_B3的iomux寄存器地址为:基址(Operational Base) + 偏移量(offset)=0xff320000+0x00014=0xff320014

    • 用以下指令查看GPIO1_B3的复用情况:

    # io -4 -r 0xff320014
    ff320014:  0000816a
    
    • 从datasheet查到[7:6]:

    gpio1b3_sel 
    GPIO1B[3] iomux select  
    2'b00: gpio 
    2'b01: i2c4sensor_sda 
    2'b10: reserved 
    2'b11: reserved
    

    因此可以确定该GPIO被复用为 i2c4sensor_sda。

    • 如果想复用为GPIO,可以使用以下指令设置:

    # io -4 -w 0xff320014 0x0000812a
    

    GPIO调试接口¶

    Debugfs文件系统目的是为开发人员提供更多内核数据,方便调试。这里GPIO的调试也可以用Debugfs文件系统,获得更多的内核信息。GPIO在Debugfs文件系统中的接口为 /sys/kernel/debug/gpio,可以这样读取该接口的信息:

    # cat /sys/kernel/debug/gpio
    GPIOs 0-31, platform/pinctrl, gpio0:
     gpio-2   (                    |vcc3v3_3g           ) out hi    
     gpio-4   (                    |bt_default_wake_host) in  lo    
     gpio-5   (                    |power               ) in  hi    
     gpio-9   (                    |bt_default_reset    ) out lo    
     gpio-10  (                    |reset               ) out lo    
     gpio-13  (                    |?                   ) out lo    
     
    GPIOs 32-63, platform/pinctrl, gpio1:
     gpio-32  (                    |vcc5v0_host         ) out hi    
     gpio-34  (                    |int-n               ) in  hi    
     gpio-35  (                    |vbus-5v             ) out lo    
     gpio-45  (                    |pmic-hold-gpio      ) out hi    
     gpio-49  (                    |vcc3v3_pcie         ) out hi    
     gpio-54  (                    |mpu6500             ) out hi    
     gpio-56  (                    |pmic-stby-gpio      ) out hi    
     
    GPIOs 64-95, platform/pinctrl, gpio2:
     gpio-83  (                    |bt_default_rts      ) in  hi    
     gpio-90  (                    |bt_default_wake     ) in  lo    
     gpio-91  (                    |?                   ) out hi    
     
    GPIOs 96-127, platform/pinctrl, gpio3:
     gpio-111 (                    |mdio-reset          ) out hi    
     
    GPIOs 128-159, platform/pinctrl, gpio4:
     gpio-149 (                    |hp-con-gpio         ) out lo
    

    从读取到的信息中可以知道,内核把GPIO当前的状态都列出来了,以GPIO0组为例,gpio-2(GPIO0_A2)作为3G模块的电源控制脚(vcc3v3_3g),输出高电平(out hi)。

    FAQs¶

    Q1: 如何将PIN的MUX值切换为一般的GPIO?¶

    A1: 当使用GPIO request时候,会将该PIN的MUX值强制切换为GPIO,所以使用该pin脚为GPIO功能的时候确保该pin脚没有被其他模块所使用。

    Q2: 为什么我用IO指令读出来的值都是0x00000000?¶

    A2: 如果用IO命令读某个GPIO的寄存器,读出来的值异常,如 0x00000000或0xffffffff等,请确认该GPIO的CLK是不是被关了,GPIO的CLK是由CRU控制,可以通过读取datasheet下面CRU_CLKGATE_CON* 寄存器来查到CLK是否开启,如果没有开启可以用io命令设置对应的寄存器,从而打开对应的CLK,打开CLK之后应该就可以读到正确的寄存器值了。

    Q3: 测量到PIN脚的电压不对应该怎么查?¶

    A3: 测量该PIN脚的电压不对时,如果排除了外部因素,可以确认下该pin所在的io电压源是否正确,以及IO-Domain配置是否正确。

    Q4: gpio_set_value()与gpio_direction_output()有什么区别?¶

    A4: 如果使用该GPIO时,不会动态的切换输入输出,建议在开始时就设置好GPIO 输出方向,后面拉高拉低时使用gpio_set_value()接口,而不建议使用gpio_direction_output(), 因为gpio_direction_output接口里面有mutex锁,对中断上下文调用会有错误异常,且相比 gpio_set_value,gpio_direction_output 所做事情更多,浪费。

             


    扫码或长按关注

    回复「 篮球的大肚子」进入技术群聊

    关注公众号回复「1024」获取学习资料

    展开全文
  • maixpy k210 GPIO操作

    千次阅读 2020-11-21 08:54:30
    from Maix import GPIO from Maix import FPIOA from fpioa_manager import fm from board import board_info # see board/readme.md to config your sipeed's hardware. print(board_info.LED_R) print(board_info...
  • GPIO_init()函数初始化详解

    千次阅读 2021-06-23 10:39:43
    目录 1.GPIO_init()函数初始化示例 1.1 GPIO_InitTypeDef 1.2 GPIO_Init(GPIOB, &GPIO_GPIO_InitStructure) 1.3 GPIO_InitStructure.GPIO_Pin 1.4 GPIO_InitStructure.GPIO_Mode 1.5 GPIO_InitStructure.GPIO_Speed ...
  • ESP32学习笔记(2)——GPIO接口使用

    千次阅读 2021-04-14 10:08:17
    IO_MUX、RTC IO_MUX 和 GPIO 交换矩阵用于将信号从外设传输至 GPIO pad。这些模块共同组成了芯片的 IO 控制。 注意:其中 GPIO 34-­39 仅用作输入管脚,其他的既可以作为输入又可以作为输出管脚。 GPIO6-11通常用于...
  • 关于 GPIO_Init(GPIOF,&GPIO_InitStructure);的理解

    千次阅读 多人点赞 2020-07-07 17:44:56
    至此对程序段一的○1解析可以做一个总结: 该行定义一个结构体类型的变量GPIO_InitStructure,并且该结构体有3个成员,分别为GPIO_Pin、GPIO_Speed和GPIO_Mode,并且GPIO_Pin表示GPIO设备引脚GPIO_Speed表示GPIO...
  • 全志A20关于GPIO的配置及使用方法

    热门讨论 2014-11-10 14:19:25
    全志A20关于GPIO的配置及使用方法,实用,简洁
  • 先看: 嵌入式linux下操作GPIO 在嵌入式设备中对GPIO的操作是最基本的操作。一般的做法是写一个单独驱动程序,网上大多数的例子都是...其实Linux下面有一个通用的GPIO操作接口,那就是我要介绍的 “/sys/class/...
  • GPIO介绍

    万次阅读 多人点赞 2018-08-09 11:52:58
    GPIO的一些函数说明:...库函数GPIO中定义GPIO引脚名称,传输速率,引脚模式设置相应的结构体 @brief This file contains all the functions prototypes for the GPIO   * firmwa...
  • 【STM32】STM32F4 GPIO八种模式及工作原理详解

    万次阅读 多人点赞 2019-08-12 10:13:27
    1 GPIO简介 GPIO,即通用I/O(输入/输出)端口,是STM32可控制的引脚。STM32芯片的GPIO引脚与外部设备连接起来,可实现与外部通讯、控制外部硬件或者采集外部硬件数据的功能。 STM32F407有7组IO。分别为GPIOA~GPIOG,...
  • STM32F103GPIO-LED点灯

    千次阅读 2021-04-14 11:47:59
    8.1 关于GPIO GPIO(General-Purpose IO ports,通用输入/输出接口),用于感知外界信号(输入模式)和控制外部设备(输出模式),如图 6.1.1 所示的STM32F103ZET6芯片四周的细引脚就是GPIO。 在嵌入式开发中,经常...
  • 树莓派GPIO控制

    万次阅读 多人点赞 2018-07-15 12:21:35
    树莓派GPIO控制 陈拓chentuo@ms.xab.ac.cn 2018.06.09/2018.06.10 0. 概述 本文介绍树莓派 Zero W的GPIO控制,并用LED看效果。也适宜于树莓派3B+。 0.1 树莓派GPIO编号方式 功能物理引脚 从左到右,从上到下:...
  • gpio-key驱动分析

    千次阅读 2018-09-29 09:30:40
    文章目录前言测试平台架构DTS配置基本数据结构设备注册设备probe流程设备资源解析按键注册...Linux内核中的gpio-keys.c(driver/input/keyboard/gpio-keys.c)统一了所有关于按键的驱动实现方式。其良好的代码架构可...
  • Linux GPIO 驱动 (gpiolib)

    万次阅读 多人点赞 2019-08-04 23:27:42
    2.1 gpio_chip 结构 2.2 gpio_desc 结构 2.3 gpio_device 结构 3、Gpiolib 对接芯片底层 3.1、注册 GPIO 资源(gpiochip_add) 3.2、gpiochip_add_data_with_key 4、Gpiolib 为其他驱动提供的 APIs 4.1、gp.....
  • 华大单片机GPIO配置

    万次阅读 2020-06-05 23:57:14
    //u8PA1Stat 为PA1端口输入电平状态 获得IO口输出值 原型 boolean_t Gpio_ReadOutputIO(en_gpio_port_t enPort, en_gpio_pin_t enPin) 举例 u8PA1Stat = Gpio_ReadOutputIO(GpioPortA ,GpioPin1); //u8PA1Stat 为PA...
  • Linux下控制GPIO的三种方法

    千次阅读 2020-11-02 22:44:00
    控制GPIO的三种方式应用空间控制 /sys/class/gpio/ 下的驱动驱动空间调用的GPIO接口驱动空间通过ioremap映射I/O口寄存器 前言: 以下三种方式,可借助设备树进行GPIO引脚的定义: 定义该引脚为 IO 引脚,屏蔽其...
  • 树莓派笔记(三) 使用 RPi.GPIO 模块

    千次阅读 2020-10-07 14:22:23
    目录树莓派笔记(三) 使用 RPi.GPIO 模块RPi.GPIO引脚简介引脚编号引脚图引脚设置指定引脚编号系统配置通道释放引脚输出输入上拉/下拉电阻轮询输入中断和边检检测线程回调开关防抖 树莓派笔记(三) 使用 RPi.GPIO ...
  • gpio-sunxi驱动应用gpio口终端操作

    千次阅读 2018-06-29 13:32:46
    内核里gpiolib可以操作gpio口,但基本只能作输入或输出功能。而全志的gpio控制器除这两种功能外,还提供了其它功能,如配置gpio的上下拉,输出电流等级大小等。所以全志提供了类似gpiolib但可以有更多功能的驱动(但...
  • HAL库配置GPIO

    千次阅读 2020-05-04 10:43:11
    HAL库配置GPIOHAL库与标准库不同的特点:使用`CubeMX`配置GPIO底层参数:总结HAL库中GPIO的相关功能:GPIO的寄存器:总结 HAL库与标准库不同的特点: 标准库中初始化外设使用结构体变量+Init函数实现,结构体变量...
  • linux GPIO子系统

    千次阅读 2019-01-19 17:36:57
    linux GPIO子系统 linux中有各种各样的子模块系统,最简单的应该就是GPIO子系统了。GPIO(General Purpose Input Output),通俗的说就是输入输出管脚了,控制它状态,就是GPIO子系统了,滑稽脸.jpg。 一直都说,...
  • GPIO接口解析

    千次阅读 2018-12-07 14:10:21
    转自 ...   本文提供了一个linux下访问GPIO的约定的概述。 这些调用使用gpio_* 命名前缀。没有别的调用会使用这个前缀或是相关的__gpio_*前缀。   什么是GPIO? =============== GPIO--...
  • 04-Linux设备树系列-GPIO驱动实践

    千次阅读 2018-09-17 14:48:39
    2. GPIO编程模式 2.1 编程接口 2.2 DTS配置 2.3 GPIO驱动程序 2.3 GPIO测试程序 3. GPIO子系统的变化 1. 前言 GPIO驱动开发可能算是Linux内核设备驱动开发中最为简单、最常见的一个方向,对于开发板的按键...
  • 树莓派的GPIO端口详解

    万次阅读 多人点赞 2018-10-27 16:24:31
    首先上一张端口图 ...GPIO是个比较重要的概念,用户可以通过GPIO口和硬件进行数据交互(如UART),控制硬件工作(如LED、蜂鸣器等),读取硬件的工作状态信号(如中断信号)等。GPIO口的使用非常广泛。掌握了...
  • Nvidia Xavier GPIO 输入输出 中断 PWM

    千次阅读 2019-11-13 21:51:52
    文章目录前言Jetson.GPIO安装可用引脚点亮LEDGPIO输出示例GPIO输入示例GPIO EventGPIO InterruptPWM微信公众号 前言 Nvidia Jetson AGX Xavier 硬件相关 这篇讲到Xavier的40Pin扩展口和树莓派的40Pin扩展口类似, 本...
  • RPI.GPIO Python使用手册

    千次阅读 2018-12-21 12:55:25
    RPiGPIO模块基本使用 导入模块 引脚编号方式 设置一个通道 输入 输出 设置多个通道的输出 清空 GPIO 输入 上拉或者下拉电阻 测试输入轮询 中断与边沿检测 线程回调 开关去...
  • gpio-android-api

    热门讨论 2012-06-12 09:23:03
    android GPIO开发,包括GPIO口的读写,gpio驱动开发部分,应用部分JNI层,以及Java source code ,助你对GPIO操作完全入门

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 138,200
精华内容 55,280
关键字:

gpio