按钮没按,引脚为什么会乱跳?|单片机动画课 02

课程插图

上一课,我们让单片机通过输出引脚点亮 LED。这一课把方向反过来:让引脚观察外面的按钮。

先想一个问题:按钮松开时,线路断了,单片机是不是自然就读到 0?不一定。没有明确电平来源的输入,可能处于悬空状态。 今天不用接线,跟着三张动画看清这件事。

本文采用 Arduino Uno R3 的普通数字输入作例子。所有动图均为原创原理示意,非芯片仿真、非硬件实测;变化速度为教学而放慢。不同芯片的电压和门限不能直接照搬。

输入引脚读的是电压

digitalRead() 返回 HIGH 或 LOW,但外面的世界没有一个叫“1”的小球钻进引脚。芯片先面对实际电压,再按输入门限判断逻辑状态。

在第一课的 OUTPUT 模式下,输出级主动把引脚拉向高电平或低电平。换成 INPUT,输入端通常呈高阻态,只需要很小的电流就能感知外部信号。高阻意味着它不会像输出那样主动决定线路电压。

悬空输入没有确定的默认电平

看动图:按钮断开,上拉也没有接入,输入端的电压就缺少稳定的约束。附近的电信号、布线耦合和残留电荷,都可能影响它。

图里的读数来回变化,只是在演示一种可能结果。悬空不保证一定乱跳,更不会按固定节奏跳。 它也可能暂时读成同一个值;这仍不等于设计可靠。数字输入还有不能保证读成哪一种逻辑的电压区域,不能把每个中间电压都想成一个稳定的“半个 1”。

上拉:给松开的按钮一个默认值

现在加入一条路径:从逻辑电源 VCC,经过电阻,接到输入引脚。这就是上拉。

按钮松开和按下时,上拉电阻与接地路径的关系

按钮松开时,引脚经电阻连到电源,电压趋向高电平,读到 HIGH。按钮按下时,它把引脚连向 GND,电压降到低电平,读到 LOW。

电阻为什么不能省?因为按下时,电源到地之间也有一条通路。电阻限制这条通路的电流。这里是输入电路,不是在教你把一个主动输出 HIGH 的引脚直接短接到地。

图中用理想开关表示按钮,暂时忽略接触抖动;下一课会把这个被省略的细节放大。VCC 表示这块板对应的逻辑电源,GND 是共同的电压参考。换成 3.3V 芯片时,不能把这里的电源想当然地接成 5V。

为什么按下反而读到 0?

因为接线规定了这件事。我们选择“松开上拉、按下接地”,所以按下有效的电平就是 LOW。

从原始电平到按钮是否按下

看动画的两层文字:上面是电平,下面是程序解释出来的含义。“HIGH”不等于“发生了动作”,“LOW”也不等于“什么都没发生”。它们需要和接线一起理解。

Uno R3 支持通过下面的写法打开内部上拉。先看懂两行各自负责什么,不需要现在运行:

// setup() 中配置一次
pinMode(2, INPUT_PULLUP);
// loop() 中反复读取
bool pressed = (digitalRead(2) == LOW);

第一行决定引脚模式,第二行把电平转成“是否按下”的布尔值。内部上拉的阻值和可用引脚因芯片而异;某些芯片的部分引脚没有内部上拉。概念相通,不代表每块板的每个引脚都相同。

一个常见误区

“按钮没按,所以线路是断的,读数就应该为零。”这句话把没有连接和连接到低电平混为一谈。悬空输入没有可靠默认值;上拉输入有明确的默认高电平。

上拉也不负责把一次机械按压变得干干净净。它解决默认电平问题,消抖解决接触瞬间的多次跳变问题,这是两件事。

看完再猜

在本课接法中,按钮一直按住,反复调用 digitalRead(2),会得到一个 LOW,还是连续很多个 LOW?

答案是后者。读取的是此刻的状态,并不会自动把长按变成“一次按下事件”。下一课,我们就利用这个区别解释:为什么按一次,计数器却可能加了好几次。

资料:Arduino InputPullupSerial、Arduino 数字引脚说明。

发现更多好玩的开源项目

专注 Arduino、开源硬件、端侧 AI 和值得收藏的开发者工具。

课程插图

长按二维码关注「GitStar」

所有动画为教学原理示意,非芯片仿真、非硬件实测。网页发布:2026-09-21。