跳转至

GPIO基础

一、GPIO 的一些基础概念

1.1 GPIO 是什么

GPIO 的全称是 general purpose input and output。

相比 UART,I2C, SPI 这类专用管脚, GPIO 是没什么特定用途,比较万能的芯片管脚。

GPIO 的最大特性就是使用很灵活,可以通过编程,实现很丰富的应用。

同时,GPIO 的灵活性,又比较难以理解,对初学者来说,掌握透彻有一定的难度。

但是,GPIO 是嵌入式开发的基础,不理解 GPIO 的各种基本概念,不熟练掌握 GPIO, 不透彻理解 GPIO, 就无法真正理解嵌入式的系统。

1.2 理解 GPIO 的基础知识

要理解 GPIO 的基本概念,要先知道欧姆定律,就是:

电流 = 电压/电阻 = U / R

同时,还需要知道,硬件设备的功耗大小,就是指这个设备的功率是大还是小:

功率 = U*I = U²/R

在我们的物联网系统里面,电压一般是 1V 到 5V 之间, 是属于低压系统。

我们把典型的电压看做 3V。

在我们这个物联网系统里面, 功率如果是达到 1 瓦,那就是不得了的,是极大的功耗了,是不正常的。

我们的物联网设备,比如我们的模组,平时待机的功率,长连接状态下, 大家经常说是 1 毫安以内,功率就是在 3 毫瓦左右的;

在深度休眠,是 3 微安左右,功率就是 10 微瓦。

即使是在正常工作状态,我们平时的电流,也就是在 50mA 左右,功率就是 150 毫瓦;

只有在刚开机,会有瞬时电流,不到 1.5A 的样子,这时候功率就是 4.5 瓦,但是这个高功率,持续时间只有不到 1 毫秒,是非常短时间的脉冲,不影响整个设备的平均功率是毫瓦级别的。

为什么一定要强调功率的概念呢?

是要给大家建立一个认知, 在我们这个低功耗的系统里面,电压是常量,不大会变的,要么是 3V 左右,要么是 1.8V,就这两个常用电压,一旦在设备启动的时候设置好,整个设备运行过程中,就不大会变了。

那么,影响功耗的因素是什么呢?

对,是电阻。

我们要做到 1 毫瓦左右的功耗,一般会用到多少欧姆的电阻呢?

从 功率 = 电压 ² / 电阻,这个简单的公式,可以知道,

电阻 = 电压的平方/功率,

3V 电压的平方,除以 1 毫瓦, 电阻就是

9V²/1 毫瓦 = 9V² / 1V*1 毫安 = 9V / 1 毫安,大约就是 9000 欧姆, 属于一般简称为 9KΩ。

大家要记住, 在我们的系统里面,为什么我们的电路,动不动就用 10KΩ 的电阻,20KΩ 的电阻,几乎不用 10Ω 的电阻。

你会看到,我们模组的 BOM 里面,电阻都是 4.7K,10K, 20K,100K,要么是 0Ω, 是用来做导线或者断路的。

你是看不到 10 欧姆,20 欧姆这样的电阻的。

就是因为,我们是低功耗系统, 平时大部分时间待机的功耗, 是只有毫瓦级别的。

在需要大脉冲搜网的时候, 是通过发射放大器,不改变电压,不改变电阻,单纯的加大电流实现的, 是让供电系统,也就是电池,或者是外部电源,给一个大电流, 通过

P = U*I

来实现的大功率。

但是这种大功率,是瞬时的,不是常态。

我们这个系统的常态, 是通过

P = U² / R

来计算的功率。

讲了这么多,就是想跟大家讲明白,我们的系统, 由于电压是 1-5V 之间的一个基本恒定的值, 所以,我们系统里面的电阻, 是 KΩ 级别的电阻。

由于这个 KΩ 级别电阻的存在, 我们系统里面的正常工作的电流,基本上都是毫安级别的。

这点非常非常重要,对理解 GPIO 非常重要!

1.3 MOS,MOSFET,N_MOS, P_MOS, CMOS

MOS - Metal-Oxide-Semiconductor,金属氧化物半导体,是一个通用的称呼,可以泛指半导体元器件;

真正用在半导体芯片的,是一种称为 MOSFET 的结构。 MOSFET 是 MOS 的升级版,更加实用。

MOSFET 的工作原理,是通过给栅极加载不同的电压,控制不同的电流。

MOSFET 分为两种,一种是 N 型,一种是 P 型,对应不同的电流流向,他们分别称为 N_MOS 和 P_MOS。

这样的两种类型,形成了半导体的二进制世界。

MOSFET 的工作原理参考下面几个图:

我们经常听到的 CMOS 半导体晶圆,就是由很多个 MOSFET 组成的。

一个典型的 12 英寸的半导体晶圆,可以包含几十亿个 MOSFET。

大家记住这些名词,不懂细节没关系。

只需要知道, N 型的 MOSFET 和 P 型的 MOSFET,是典型的芯片的基本组成单元,分别是对应不同的电流方向就可以了。

P 代表 Positive, 正极;

N 代表 Negative, 负极。

后续只要说是 N_MOS,就是指输出低电平的 MOSFET,

只要说 P_POS,就说是高电平的 MOSFET。

1.4 GPIO 驱动信号的能力是什么意思?

我们经常看到文档里面写,GPIO 驱动能力强,驱动能力弱。

驱动能力强, 经常是指,对外提供的电流可以几十个毫安;

驱动能力弱,是指是指,对外提供的电流,只能几个毫安。

GPIO 驱动信号的能力,是指 GPIO 对外部的电路,提供电流的输出能有多大。

GPIO 为啥需要对外输出电流呢?

因为我们的模组是主控,GPIO 接一个 LED 灯,这个 LED 灯,有两个引脚,一个正极,一个负极, LED 灯在正极和负极中间,负极是接地的,负极是 0V。

LED 灯如果通过有电流, 这个灯就能亮。

如果电流太小, 灯就不亮了。

所以, GPIO 接到 LED 灯的正极,GPIO 的电压(平时成为 GPIO 电平,其实就是电压), 电压是 0V 左右的话,LED 的正极跟负极,都是 0V,所以 LED 两边的电压就是 0V, 看 I = U / R 这个公式 , 就没有电流, 灯不亮;

这时候,把 GPIO 电压设置为 3V, LED 正极就是 3V,LED 两边的电压差就是 3V, 仍然是看 I = U / R 这个公式, LED 灯的电阻是不变的, 所以,LED 灯就有电流通过了,灯就亮了。

所以,你看到, 接到 LED 灯的 GPIO,必须要有能对外输出电流的能力, 如果没有输出电流的能力的话, 即使 LED 两边的电压是 3V,电阻是1K欧姆,按照公式, LED通过的电流是 3/1000 = 3mA 的电流。

但是,如果 GPIO 没有能力提供 3mA 的电流, 这时候就抓瞎了,灯还是亮不了。

所以说,GPIO 的驱动信号的能力,就是对外输出电流的能力。

驱动信号的能力,就是指能够对外输出电流能力有多少的意思。

GPIO 驱动信号的能力,是指 GPIO 在输出模式下,对外提供电流的能力。

而输入模式,是指不对外输出电流,仅仅检测 GPIO 的电压是高电压还是低电压。

在输入模式下,检测 GPIO 的电压水平,只需要 nA 级别的电流就可以做到了。

1.5 什么叫做 GPIO 的高阻态?

什么是 GPIO 的高阻态?

理解了什么是高阻态,才能理解 GPIO 的输入模式和输出模式的差异。

GPIO 的高阻态的意思是,GPIO 引脚和自己所在的主控系统之间,有一个非常大的电阻,这个电阻达到兆欧,近似于是断路的状态。

GPIO 的高阻态,可以理解为,GPIO 引脚和所在主控的其他电路之间, 是近似断路的,所以 GPIO 上不会有较大的电流。

而高阻态,又不是真正断路的状态,因为真正断路的话,主控系统就无法判断 GPIO 的电平是高还是低了。

GPIO 的高阻态有两个含义:

(1)由于跟主控的其他电路之间是近似断路状态,所以是无法给 GPIO 输出电流的,所以该 GPIO 就不具备驱动信号的能力了,所以也就不是输出状态;

(2)由于是跟其他主控电路近似断路状态,所以该 GPIO 接的外部电路,也无法影响到主控的其他电路,只需要判断该 GPIO 的电平是高还是低就可以了。

所以说,在 GPIO 是高阻态的状态下,GPIO 只能是输入模式或者空闲模式,不可能是输出模式。

我们通常所说的设置 GPIO 为输入模式,可以把 GPIO 设置为高阻态,也可以设置上拉下拉,让 GPIO 有确定电压。

如果是把 GPIO 设置为输出模式, 就不可能是高阻态,因为输出模式是要对外提供电流的驱动能力的。

在高阻态模式下,GPIO 的电流是极小极小的,是纳安级别的;

在非高阻态模式下, GPIO 的电流,取决于关联的阻值,所以非高阻态的电流一定比高阻态下的电流大。

GPIO 的高阻态,是 GPIO 真正休息的状态。

进入高阻态,意味着 GPIO 不对外输出电流,不驱动外部信号;

高阻态的 GPIO 的状态,基本上完全无效的状态,软件是获取不到 GPIO 的高低电平的。

GPIO 的高阻态,也是最省电的。

在 GPIO 什么都不做,也不需要 GPIO 的状态的时候,一般都会让 GPIO 进入高阻态。

我们的 780 系列的模组, 在进入深度休眠省电的时候,大部分 GPIO 只能进入高阻态,只有少部分 AGPIO 是软件可以设置的,这就是为了极致省电的目的而做的设计,因为只有 GPIO 的高阻态才是最省电的。

1.6 什么叫做 GPIO 的输入模式?输出模式?中断模式?

1.6.1 GPIO 的输出模式

GPIO 的输出模式, 是指 GPIO 具备对外输出电流的模式。

在 GPIO 的输出模式下,主控系统和 GPIO 之间,是通路的,主控系统和 GPIO 引脚之间的电阻,是 KΩ 级别的,是可以提供毫安级的电流的。

这个毫安级的电流, 是可以跟 GPIO 的外部电路连通的。

在 GPIO 电平为高,也就是 GPIO 电压为 3V 的时候,GPIO 对于外接的电路(参考 LED 的例子),是输出电流的,GPIO 作为正极,外接电路的 GND 作为负极;

在 GPIO 电平为低,也就是 GPIO 电压为 0V 的时候,GPIO 对于外接的电路(同样参考 LED 例子),是吸收电流的,GPIO 作为负极,是 GND, 外接电路的正极是 3V,电流从外接电路向 GPIO 流入,同样是形成了电流回路,形成了电流驱动信号的能力。

所以,在 GPIO 的输出模式下,GPIO 电平无论是高还是低,都有驱动信号的能力。

并且在 GPIO 的输出模式下,由于主控系统和 GPIO 引脚之间的电阻是 KΩ 级别,所以是一个电流在毫安级别的电路回路,从而使得电流能够正常的流通,达到驱动信号的目的。

1.6.2 GPIO 的输入模式

GPIO 的输入模式,是指通过主控芯片内部的逻辑电路(这个逻辑电路比较复杂,这里不讲了,知道有这回事就行),读取 GPIO 脚的电平是高还是低,从而获取外部电路对主控芯片的信号。

外部电路通过外部的上拉或者下拉电阻,改变 GPIO 的电压值,GPIO 所在的主动 CPU 通过这种按照时间轴持续的读取变化的高低电平,就获取了多个 0 和 1 交替的数值,从而达到外部设备和自己交换数据的目的。

在输入模式下,有三种初始模式:

(1)GPIO 进入高阻态

GPIO 在高阻态状态下,初始电平不确定,主控CPU也不在意初始电平是高还是低。

外部电路通过上拉上拉改变GPIO电平,实现通信目的。

高阻态模式下,只要外部做了上拉或者下拉处理,也是可以设置为输入中断模式,可以上升沿,也可以下降沿触发,也可以双标沿触发。

(2)GPIO 通过上拉电阻设置初始电平为高;

初始电平为高的话,外部电路把GPIO拉低,就传递了一个bit给到了主控CPU.

(3)GPIO 通过下拉电阻设置初始电平为低。

初始电平为低的话,外部电路把GPIO拉高,就传递了一个bit给到了主控CPU.

在输入模式下, 没有产生毫安级电流的回路,主控系统监测 GPIO 的电压值,只需要纳安级别的电流就可以实现了。

1.6.3 GPIO 的中断模式

  CPU 都有中断处理单元,用来处理高优先级的事件,并且短时间就能处理完的动作。

  当初始电压从低电平改变为高电平这个事件发生的时候,把这个电平改变的事件, 和主控CPU的中断处理机制做绑定,只要有电平改变这个事件发生,就让主控CPU处理一个短时间的动作,这就是输入模式的中断机制,称为 GPIO 的中断模式。

   GPIO 的中断模式,是GPIO 输入模式的延伸模式,是GPIO 输入模式的一个应用。

   按照电平的变化方向,有三种中断触发方式:

(1)初始低电平,变为高电平,从低到高的事件,称为上升沿触发,LuatOS 有个常量:gpio.RISING;

(2)初始高电平,变为低电平,从高到低的事件,称为下降沿触发,LuatOS 有个常量:gpio.FALLING;

(3)无论是从高到底,还是从低到高,都触发,称为双边沿触发,LuatOS 有个常量:gpio.BOTH。

1.7 推挽输出和开漏输出

推挽输出和开漏输出, 是 GPIO 输出模式的两种方式。

推挽输出是指, 使用 N 型 MOS 输出低电平, 用 P 型 MOS 输出高电平,不会有高阻态存在。所以无论是低电平还是高电平,都有一定的电流能力。

开漏输出是指, 只有 N 型 MOS, 没有 P 型 MOS,所以导通状态下只会输出低电平,非导通状态下就是高阻态。

既然推挽输出,可以输出高电平,也能输出低电平,已经是可以满足 GPIO 的输出模式了,为什么还需要开漏输出呢?

这是因为,如果多个设备,每个设备都有主控 CPU, 他们会共用一个信号线,比如 I2C,一个主设备,10 个从设备, 都会接到同一个 SCL 时钟线,也都会接到同一个 SDA 数据线。

SCL 时钟线和 SDA 数据线,都是用 GPIO 通过输出模式来实现的。

这时候,因为多个设备都接入了同一根线,并且设备的引脚只有高电平和低电平两种状态,无论是高还是低,都可能形成电流。

如果一个设备是高电平,另一个设备是低电平, 就很容易形成回路, 如果这个回路的电阻比较小的话,造成短路或者是漏电流很严重。

解决这个问题的方法,就是增加开漏输出模式。

开漏输出模式只有 N_MOS, 没有 P_MOS, 所以该引脚的两个状态分别是低电平和高阻态:

当 NMOS 导通的时候,就是低电平;

当 NMOS 关闭的时候,就是高阻态,接近短路状态,不会形成回路。

所以在设备不做通信的时候,把管脚设置为高阻态,就不会造成电流回路;

在设备工作的时候, 由设备的外部上拉电阻根据通信协议的需要,把引脚拉高进行通信,一旦通信完毕,马上就进入高阻态(这个动作叫做释放信号线),这样就保证了系统的安全。

这里要强调是,只有 GPIO 作为多个管脚协作通信的时候,才会发生这种情况,比如 I2C, SPI,CAN,485,这样的多管脚协作,才会出现多个设备接入到同一根信号线的情况。

如果 GPIO 仅仅是一根线工作, 不跟其他的 GPIO 协作,是用不到开漏输出的,只需要推挽输出,就足够了。

1.8 为什么会有内部上拉和内部下拉?为什么会有外部上拉和外部下拉?

1.8.1 什么是内部上拉和内部下拉

内部上拉是指,在主芯片内部,在 GPIO 和 电源正极(也就是 VCC) 之间,接一个几十 KΩ 的电阻,使得 GPIO 的电压,接近正极电压;

内部下拉是指,在主芯片内部,在 GPIO 和 GND 之间,接一个几十 KΩ 的电阻,使得 GPIO 的电压,接近 0V。

1.8.2 什么是外部上拉和外部下拉

外部上拉是指,在主芯片外部,在 GPIO 和 电源正极(也就是 VCC) 之间,接一个不大于 10KΩ 的电阻,使得 GPIO 的电压,接近正极电压;

外部下拉是指,在主芯片外部,在 GPIO 和 GND 之间,接一个不大于 10KΩ 的电阻,使得 GPIO 的电压,接近 0V。

1.8.3 内部上下拉和外部上下拉的关系

外部上拉电阻的阻值,要明显小于内部下拉电阻的阻值;

外部下拉电阻的阻值,要明显小于内部上拉电阻的阻值。

这是由于分压原理, 外部上拉和内部下拉电阻是串联的,外部上拉阻值小的话,GPIO 的电压会更接近于外部的正极电源电压。

同理,外部下拉和内部上拉电阻也是串联的,外部下拉阻值更小的话,GPIO 的电压会更接近于外部的接地电压 0V;

1.8.4 GPIO 输出模式的上拉和下拉

仅仅考虑单个 GPIO 自己工作,没有多个 GPIO 协作的时候,GPIO 的输出模式只有推挽输出, 这时候,是不需要内部上拉下拉,也不需要外部上拉下拉。

GPIO 只通过内部的 NMOS 导通还是 PMOS 导通, 向外界输出高电平和低电平。

所以,在软件配置的时候,如果 GPIO 配置为了输出模式, 是不需要配置内部上拉和下拉的

在硬件实现的时候,有一点需要注意:

由于从开机上电到推挽输出生效,可能会有一个很短的时间差,在这个时间差内,GPIO 的电平可能是不受控的,如果 GPIO 外接有负载的话,比如外接一个继电器使能脚的话,可能会错误的启动负载,造成设备故障。

为了避免这个问题, 会在外部加一个上拉或者下拉电阻, 在开机状态下,也能把GPIO电平置为和推挽输出一致的电平。这时候GPIO不具备电流输出能力,只是电平符合预期,不会造成设备工作故障。 等到推挽输出生效的时候,才能够真正具有正确的电平和足够的信号驱动能力,也就是输出电流的能力。

这一点,是硬件设计上是最容易出现"失误"的地方,

比如你需要控制一个继电器的开关,在模组上电时不可控的话,有可能造成的时 220V 市电不受控的导通,后果不堪设想。

1.8.5 GPIO 输入模式的上拉和下拉

GPIO 的输入模式下,如果是设置为高阻态,就没有内部的上拉和下拉,优点是省电,缺点是初始的 GPIO 输入电平是随机的;

为了设置一个默认的确定的电平,通常会设置上拉或者下拉电阻,让GPIO电平有个确认的初始值。

当内部启用内部上拉, GPIO 电平默认为高电平,而外部电路用下拉电阻把 GPIO 拉低,并且外部下拉电阻阻值比内部上拉电阻低的时候, 根据分压原理, GPIO 的电压就会贴近于 0V, 从而实现外部拉低的目的;

反过来,当内部启用内部下拉,GPIO 电平默认为低电平,而外部电路用上拉电阻把 GPO 拉高,并且外部上拉电阻阻值比内部下拉阻值更小的时候,根据分压原理,GPIO 的电压就会贴近于高电平,实现实现外部拉高的目的。

二、GPIO 输出模式的常见场景

2.1 LED 灯

LED 灯的驱动有三种方式:

(1)直接用 GPIO 驱动 LED 灯,GPIO 直连 LED 灯,并且串联一个限流电阻,这种方式是要求 GPIO 有足够的驱动能力,也就是能提供足够的电流;

(2)通过三极管的开关作用,用 GPIO 连接三极管的基极,用来控制三极管的放大开关,通过外部电源对 LED 供电;

(3)通过 PWM 对 LED 供电,方便调整 LED 亮度。

以上的三种方式,都是利用了 GPIO 的输出模式实现对 LED 灯的控制。

2.2 LCD 背光开关控制

LCD 的背光的开关,可以通过三极管来控制。

LCD 背光的供电,有单独的外部电源,不通过 GPIO 来提供驱动电流。

三极管的基极,连接 GPIO, 可以实现对三极管的断路和通路的控制,从而实现对 LCD 背光的开关控制。

2.3 继电器使能开关

继电器的供电,有外部的电源,不通过GPIO来提供驱动电流。

继电器的开关, 通过三极管来控制。

三极管的基极,连接 GPIO, 可以实现对三极管的断路和通路的控制,从而实现对继电器的开关控制。

2.4 直流电机的使能开关

直流电机的供电,有外部的电源,不通过GPIO来提供驱动电流。

直流电机的开关, 通过三极管来控制。

三极管的基极,连接 GPIO, 可以实现对三极管的断路和通路的控制,从而实现对直流电机的开关控制。

2.5 PWM 模拟

通过输出高电平的占空比,模拟 PWM, 来驱动 LED,电机,音频喇叭等。

2.6 输出稳定的时钟信号

GPIO 输出模式下,保持恒定的频率,持续输出方波,可以外部设置获得一个同步信号源,作为时钟的参考。

I2C 的 SCK 时钟信号线,就是这么实现的。

时钟信号线,是用来给通信的双方,提供一个时序的参考,方便解析出来有效的信号。

三、GPIO 输入模式的常见场景

3.1 检测电池是否在位

GPIO 设置为输入模式,并且外部连接一个 20KΩ 的对地电阻,同时,GPIO 在跟电池连接器的正极相连,并串联一个 1KΩ 的限流电阻。

当电池未在位时,电池连接器正极和GPIO之间没有通路,GPIO是通过20KΩ对地下拉,所以输入的电平是低电平;

当电池在位时,电池连接器跟GPIO导通,通过1KΩ的上拉电阻拉高到了高电平,GPIO就检测到了高电平的输入。

3.2 检测 SIM 卡是否在位

GPIO 设置为输入模式,并且连接 SIM 卡座的 SIM 检测脚,并接一个上拉电阻,默认输入的电平为高。

当SIM卡未在位时,输入的电平是高电平;

当SIM卡在位时,通过SIM卡座的CD(card detect)引脚接地,输入的电平变为低电平。

3.3 读取温湿度传感器的值

温湿度传感器,通过 GPIO 的输入模式,通过实现协商好的时序,不断的通过高电平和低电平的变换,向主控 CPU 输入不同的 1 和 0,实现对温湿度数值的读取。

四、GPIO 中断模式的常见场景

4.1 按键

GPIO 设置为输入中断模式,初始状态设置为内部上拉高电平,并通过一个按键开关,再串联一个下拉电阻接地。

当按键没有按下时,GPIO 接地是断路状态,输入是高电平;

当按键按下时,GPIO 接地是通路状态,输入是低电平。

中断设置为上升沿中断,可以监测到按键抬起;

中断设置为下降沿中断,可以监测到按键按下;

中断设置为双边沿中断,可以监测到所有按键抬起和按下的动作。

4.2 充电器插入

GPIO 设置为输入中断模式,初始状态设置为内部下拉低电平,并通过一个按键开关,再串联一个上拉电阻接充电器的正极。

当充电器没有插入时,GPIO 接充电器的正极是断路状态,输入是低电平;

当充电器插入时,GPIO 接正极是通路状态,输入是高电平。

中断设置为上升沿中断,可以监测到充电器插入;

中断设置为下降沿中断,可以监测到充电器拔出;

中断设置为双边沿中断,可以监测到所有充电器插入和拔出的动作。

4.3 红外传感器状态变化提醒

GPIO 设置为输入中断模式,初始状态设置为内部下拉低电平,并通过一个开关,再串联一个上拉电阻接到红外传感器电池的正极。

红外传感器监测到红外反射信号时,会把开关连通,使得 GPIO 的输入电平变为高电平。

中断设置为上升沿中断,可以监测到所红外传感器遇到红外反射的事件。