13327806566

新闻资讯

南京古河软件有限公司是一家专注于数字建筑运维系统开发和应用的公司,以开放、合作和共赢为经营理念。我们致力于应用BIM(建筑信息模型)、IBMS(智能建筑管理系统)和IOT(物联网)技术,打造数字建筑的创新运维系统。作为行业的先驱者,我们的主要产品是基于IBMS的解决方案。我们深入关注智能建筑系统集成行业的动态,并积极推动先进技术如BIM、物联网和大数据在数字建筑行业的应用。我们的IBMS系统结合了BIM和物联网技术,为建筑运维提供全面、智能的管理和监控。

全部新闻 公司资讯 行业资讯

古河网关新增GPIO转MQTT协议

1、什么是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,配置发送器,串行总线位拆,触发一个硬件看门狗,触发一个开关之类的。

2、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 {

  unsignedgpio;

  unsigned longflags;

  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主设备因此调整了它的信号频率。

3、这些约定节省了什么?

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

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

  还有别的系统特定的机制此处并没有提到,如上文提及的抗干扰和线或输出。

  硬件可能按组读写GPIO,但是那通常是单独配置的:对于那些共享同一个bank的GPIO。

  (GPIO通常16或32个为一组,一个给定的SOC系统一般拥有几个这样的BANK。)

  一些系统可以从输出GPIO管脚触发中断,或是从一个没作为GPIO管理的管脚上读值。依赖于这种机制的代码将是不可移植的。

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

  

  

 

 

南京古河软件有限公司版权所有.  Copyright©2006-2017 Guhe Software All Rights Reserved.      苏ICP备08012350号




微信咨询
微信咨询
                   扫码加微信咨询
电话咨询
咨询专线:13327806566