原创文章,未经许可,禁止转载!

3 GPIO输入与下拉电阻

视频教程:https://www.bilibili.com/video/BV1fZ4y1P7Mw/

在这一章开始前,我们先介绍另外一种Debug的方法。还记得前面我们所讲到的 builtin led 吗?看下图 3-1

图 3-1

Builtin Led 有 2 和 16 2个数字,其实代表了 GPIO2 和 GPIO16 分别对应的2个 led,这里选择的作用就是让程序中 LED_BUILTIN 字段映射成 GPIO2 还是 GPIO16,在前面的程序里我们选择了 2,因此可以使用这个字段来控制 GPIO2 上的 led,也就是下图3-2中红框对应的引脚和led。而GPIO16 就是绿框对应的引脚和led,都是低电平有效,也就是低电平的时候led会亮起。

我们当然也可以不使用 LED_BUILTIN 字段,直接使用 GPIO 的编号来使用引脚。

这个功能非常有用的地方就是可以方便的观察引脚的电平情况,而不需要通过打日志的方式来查看,不管输入还是输出,只要引脚上是低电平,led都会亮起,这样Debug起来就会非常直观。

图 3-2

那么在上一个章节里面,我们分析了 Blink 程序,它主要用到了 GPIO 的输出功能。现在可以依照 Blink 来写一个 GPIO 输入功能的程序。想象如果我有一个开关,当我合上开关的时候,板载的 led 就会亮,断开开关的时候,led 就会熄灭。

首先需要选定一个 GPIO 来作为输入,这次就选 D0(GPIO16) 作为输入。另外要让开关的另一端连接 3.3v,当开关合下的时候,3.3v 就会通过开关输入到 GPIO16,然后我们可以用程序来读取它的状态。就像下图3-3,非常简单。

图 3-3

下面的内容我会使用一种面包板,如下图3-4,我特地拆了一个面包板给大家看的更清楚一些。图片分别是面包板的正面和反面。可以看到金属片连通的方式,这里就不多解释了,这个面包板就是为了方便连线使用的,对应的还有面包板用的线,大家往下可以看到。

图 3-4

现在到代码部分,读取输入的代码也非常简单,使用 digitalRead(16) 来读取引脚状态即可,完整的代码如下:

void setup() {
  // 设置 GPIO2 引脚为输出引脚,用于控制 led 的亮起和熄灭
  pinMode(2, OUTPUT); 

  // 设置 GPIO16 为输入引脚,用于读取开关输入
  pinMode(16, INPUT);
}

void loop() {
  if (digitalRead(16) == HIGH) {    // 如果 GPIO16 引脚为高电平则亮灯
    digitalWrite(2, LOW); // GPIO2 上的内置 led 是低电平亮灯
  } else {                          
    digitalWrite(2, HIGH); // 否则就设置 GPIO2 为高电平,灭灯
  }
}
不正常的情况

当开关 S1 合上的时候,D0 引脚得到一个高电平,digitalRead(16) == HIGH 成立,因此 led 亮起。但是实际情况是当我们给芯片通电,程序开始执行时,led 就已经亮起了,而且我这里按下开关灯也不会闪,这似乎和我们的预期完全不一样。是不是程序写的有毛病?

千万不要怀疑程序,程序一点毛病没有,这里就牵涉到一个重要的概念。

下拉电阻

看下图3-5,当开关合上的时候,开关的左右连通,这时 3.3v 就顺着导线进入 D0,没毛病。但是在开关断开的时候,开关的左端和右端断开了,这样 3.3v 到开关的右端就过不去了,开关的左端就变成悬空了,这时 D0 的输入到底读入的到底是一个什么值就变成玄学了,可能是低电平 LOW,可能是高电平 HIGH。

图 3-5

我现在的情况就是在开关断后 D0 读到的值还是高电平,因此 GPIO2 led 亮起,GPIO16 led 熄灭。并且在尝试合上开关以后,仍然是 3.3v 导入 D0 引脚,因此 D0 引脚始终是高电平,这也导致 LED 常亮。

注意,这种输入悬空状态是不确定的,我这里的表现是这样,但是有可能其他的情况下 D0 并不是低电平,这个状态是不稳定的。

这种情况就像我们平时写的程序没有初始化一样。我们只关心了开关合上的情况,但是并没有考虑开关断开的情况,因此设计里应该在开关断开的时候 D0 引脚上应该是低电平。那是不是把 GND 连在开关的左端就可以了呢?当然不行,这样的话当开关合下的时候 3.3v 和 GND 都会走到开关的左端,就会短路。

正确的姿势应该是在开关的左端和 GND 之间接一个阻值非常大的电阻,这个电阻就叫做下拉电阻,这样以来,当开关打开的时候,D0 就会通过这个下拉电阻获得一个 GND 上的低电平信号,而当开关合上的时候,3.3v 和 GND 对于 D0 的作用就相当与一个并联电路,而由于 GND 端上接了一个很大的电阻,因此这一端基本不会产生什么压降(电压与电阻成反比),这样 3.3v 的高电平就可以顺利的进入 D0 了,而断开开关后,由于下拉电阻的加持,D0 也迅速回归低电平。见下图3-6:

图 3-6

在Esp8266,通常使用的下啦电阻的阻值是10kΩ,具体怎么算的就不详细讲了,手册上可以查到。另外教大家一个看电阻的方法,这个电阻上面的三条线,依次是橙色,黑色和棕色,这就表示电阻的阻值是10kΩ的。下面来看下加了下拉电阻后的情况如何。

正常情况

现在的情况就正常了,当开关没有按下的时候 GPIO16 是低电平,对应的 led 亮起,当开关按下后,GPIO16 变为高电平,对应 led 熄灭,同时我们的程序收到了 GPIO16 上的高电平后将 GPIO2 置为低电平,对应的 GPIO2 上的 led 亮起。这样的调试方式是不是很方便?

这种将悬空端置为低电平的方式就叫做下拉,对应的英文名称叫 pull-down 或者 pull-low。对应的这个电阻就叫做下拉电阻,英文叫做 pull-down resistor。既然有下拉电阻,那么对应的应该就会有上拉电阻。

上拉电阻

和下拉电阻的概念类似,上拉电阻就是将悬空端拉成高电平,这样在开关断开的情况下 GPIO16 就会呈现高电平。接线方式如下图3-7,电阻的一端接在了3.3v 的位置,表示上拉,而开关的另一头接在了 GND 上,如果程序不改的话表现应该是反过来的,按下的时候 GPIO2 的灯熄灭,大家可以思考一下,这里就不多做解释了。

图 3-7

最后再给大家看一个输入端悬空的情况,我拔掉了上拉电阻,让输入在没有按下开关的时候悬空,可以发现 GPIO16 的 led 是微微亮的,这就说明引脚上既不是高电平也不是低电平,这种输入悬空的状态是完全不确定的,在这种情况下调试程序是非常错误的,大家一定要切记。

右下角的led的引脚电平不确定

《3 GPIO输入与下拉电阻》有1个想法

  1. Pingback: 关于上拉电阻和下拉电阻的说明 - 算法网

发表评论