系统提示 // 建议使用电脑访问,桌面模式效果最佳

按键输入:去抖与上下拉电阻(Pull-Up/Pull-Down)

按键就是一个开关,那为什么还要配电阻?悬空的引脚会让芯片读到什么、为什么按一下会被读成五下,以及这两件事怎么解决。

年份

2026年8月

类型

note

分类

电子学

工具

电路设计 · 嵌入式开发

#电子学#输入设计#上拉电阻#按键去抖#电路保护

上拉电阻基础

文章: 电路中上拉、下拉电阻的作用及原理 - 知乎

按钮本质是什么?

看完这篇你会知道:为什么一个裸按键读不稳、为什么加一颗电阻就好了,以及什么时候还得让软件再确认一遍。

按钮就是两片金属触点,按下去就碰上。读它的是单片机(MCU),也就是跑你固件的那颗小芯片。MCU 看不见触点有没有碰上,它只能测自己某个引脚上的电压,这种用来读信号的引脚叫 GPIO。所以整个过程是:你按下,触点接通,引脚电压变化,固件再判断这是什么意思。

先记住两个词。低电平(LOW):引脚电压接近 0V,也就是地的电压。高电平(HIGH):引脚电压接近电源电压,多数板子是 3.3V,老一些的板子是 5V。

什么是悬空(Floating)?

最简单的接法是引脚、按键、地:GPIO → Button → GND。按下时引脚接到地,MCU 读到低电平。

问题出在松手之后。这时引脚谁也不挨着,没有任何东西固定它的电压。我们叫它悬空。悬空的引脚会跟着周围的电气噪声乱飘,固件就会看到根本没发生过的按键。所以按键松开时,得有个东西把引脚按在一个确定的电平上,这个东西就是电阻。

一个初学者容易犯的错误

很容易想到直接把引脚接到电源上:5V → GPIO → 按键 → GND。按键没按下时引脚是高电平,看起来是对的。

但一按下,你就用一根导线和一个开关把 5V 接到了地。这条路径上几乎没有电阻,能限流的只剩电源本身,路径上总有一个环节会发热烧掉。

还有第二个问题,而且在按下之前就已经存在了。3.3V 的引脚上加了 5V,超过了它能承受的范围。每个引脚内部都有一颗保护二极管,电压过高时它会导通,把电流倒灌回 3.3V 电源轨。

绝对不能这么接。

正确做法

5V → 10kΩ 电阻 → GPIO + 按键 → GND

正确的做法是把电阻串在电源和引脚之间。电阻的作用是限制电流的大小,10kΩ 就是一万欧姆。

  • 按键未按下:电阻把引脚接到 5V,引脚读到高电平。引脚自己几乎不吃电流,所以电阻上几乎没有压降。
  • 按键按下:按键把引脚接到地,引脚读到低电平。这时电阻决定了电流:5V / 10kΩ = 0.5mA,只有一颗指示灯 LED 的几分之一。同样的电路放在 3.3V 上是 0.33mA,本文后面统一用 3.3V。

电阻拉得弱,按键拉得强。按键松开时电阻赢,把引脚稳在高电平;按下时直接接地轻松压倒电阻。

上拉与下拉电阻

3.3V
│
10kΩ
│
●──── GPIO
│
│
按钮闭合
│
GND

电阻可以接在引脚的任意一侧,这就有了两个名字:

  • 上拉:电阻从电源接到引脚。默认高电平,按下低电平,这种叫低电平有效(Active Low)。
  • 下拉:电阻从引脚接到地。默认低电平,按下高电平,这种叫高电平有效(Active High)。

两者都提供一个默认电平,也都在限流,所以不会出现上一节那种直接短路。

上下拉电阻对比

选哪个取决于你的固件期待什么电平。某个引脚上电时不能处于有效状态,它低电平有效就用上拉,高电平有效就用下拉。

还有一种情况值得知道。有些芯片的输出只能把线拉低,拉不高,因为内部的输出管只朝一个方向工作。这种线上,上拉电阻是唯一能产生高电平的东西。

为什么不能用导线直连 GPIO 和 3.3V?

这和上面初学者的错误是同一个失效,只是换成 3.3V:按下按键就是把电源直接接到地,靠电阻来限流。

为什么常用 10kΩ?

阻值小,引脚被按得更牢,抗噪声更好,但耗电更多。阻值大,耗电少,但噪声更容易把电平带偏。10kΩ 是两者之间常用的折中。

3.3V 下它的电流是 3.3V / 10kΩ = 0.33mA,小到可以忽略。这个式子这样读:电压不变时,电阻越大,电流越小。

上拉还是下拉取决于这个引脚干什么用。上电时必须保持关闭的使能脚,高电平有效就用下拉,低电平有效就用上拉。电机驱动就是例子:引脚悬空时噪声可能让电机自己转起来。

绝大多数 MCU 的引脚内部也自带上拉,软件里打开就行。它很弱,通常几十 kΩ,短线够用,线一长就不太靠得住。

按键为什么会抖动?

真实的按键不是理想开关。它的触点是两片金属,按下去会互相撞击,撞上、弹开、再撞上,几毫秒后才稳定下来。你的固件在这几毫秒里可能已经跑了几千遍,每一次弹开在它看来都是一次独立的按键。于是一次按下会被数成五次。

按键去抖:一次按下为什么会被数成五次

去抖就是把这些抖动忽略掉,有两种做法。

软件去抖:固件发现引脚变化后,先等 20ms 左右,再读一次引脚。如果还是新状态,才认定这次是真的按下了。不花硬件成本,大多数按键够用。

硬件去抖:在引脚旁边加一颗电阻和一颗电容,把快速跳变抹平,固件只会看到一次干净的变化。电容能存一点电荷,所以电压不会瞬间跳过去。有些芯片的输入还带施密特触发器:一种把半高不低的电压干脆利落地推到某一侧的电路,不会来回晃。

多数产品两种都做:电容清掉快噪声,软件再确认一次。

完整电路,逐件拆开看

3.3V
│
10kΩ
│
GPIO────┼────100nF────GND
│
Button
│
GND
  • Button:用户输入
  • 10kΩ:上拉、防悬空、限流
  • 100nF 电容:硬件去抖、过滤干扰
  • GPIO:检测输入状态

工业设计师需要关注什么?

  • 结构设计:按键尺寸、行程、按压力、回弹速度、装配公差、长期磨损。
  • 环境可靠性:防水防尘(IP 等级)、ESD 静电保护、EMC 抗干扰。
  • 用户体验:单击、双击、长按、连续触发、状态反馈(触觉/视觉/声音)。
  • 产品寿命:按键额定寿命、机械疲劳、环境温度影响。

电视遥控器和电饭煲用的是同一套按键逻辑:用户输入 → 按钮 → GPIO → MCU → 执行功能。

核心总结

  • 核心电路:3.3V → 上拉电阻 → GPIO 节点 → 按钮 → GND。
  • 核心思想:电阻提供默认状态并限流,按钮改变电路状态,GPIO 读取结果。

常见疑问

为什么工业设计中普遍采用上拉?

MCU 内部上拉资源的默认优势

多数 MCU 的引脚里就自带上拉,一行代码就能打开,省一颗器件和一点板子面积。内部下拉没那么普遍:很多经典 8 位芯片,比如 AVR 和 8051,根本没有。

失效安全:断路究竟会发生什么

如果按键到板子的线断了,上拉电阻会把引脚拉回高电平,也就是“未按下”:断线不会被读成一次按下。

把电路反过来看。下拉电阻把引脚按在低电平,而在下拉方案里低电平同样是“未按下”,所以两种接法的断线都落在空闲状态上。

真正的区别在短路。上拉电路里,引脚对地短路会被读成“一直被按下”;下拉电路里,对电源短路才会。在车上,线束碰到车身(地)的概率远高于碰到电源。

接地作为公共参考点更可靠

地是整个电路板的 0V 参考,阻抗又极低,所以作为参考它比电源轨硬。

功耗与逻辑的天然匹配

按键没按下时完全不走电流,按住时的电流也极小。下拉方案按下时的电流一样大,所以两边都不省电。很多芯片还把低电平当作有效信号,比如复位脚。

与开漏输出和总线兼容

有些线是好几颗芯片共用的,I²C 就是例子:没有芯片会把它拉高,大家只负责拉低,由一颗电阻统一提供高电平。

什么情况下会特意选择下拉?

  • 引脚是高电平有效:有些复位脚和使能脚期待高电平。要让按键按下时输出高电平,就得用下拉。
  • 芯片只支持上升沿唤醒:部分超低功耗 MCU 只在引脚从低变高时唤醒,那就必须用下拉,按键接电源。

拓展阅读

内部上拉 vs 外部上拉

线短、环境干净就用内部上拉;线长或者噪声大,再加一颗外部 10kΩ。

软件消抖的状态机框架

等 20ms 只能处理单击。想要双击和长按,就得记住当前走到哪一步:

IDLE → 检测到按下 → DEBOUNCE_PRESS(等 20ms)→ PRESSED(执行功能,然后等释放)→ 检测到释放 → DEBOUNCE_RELEASE(等 20ms)→ 回到 IDLE。

这个结构能避免长按反复触发。

按键检测:轮询 vs 中断

轮询是主循环每转一圈读一次引脚:简单,但 CPU 一直忙着。

中断正好相反。引脚一变就调用一小段代码,叫中断服务程序,CPU 之前可以一直睡,所以电池供电的设备用它。很多实际产品两种都用:中断把系统叫醒,轮询负责消抖。

硬件消抖的量化设计

硬件去抖一般从 10kΩ 加 100nF 开始,这两颗器件决定了 1ms 的时间常数,比一次抖动长。用滤波的说法,截止频率是 1/(2πRC) ≈ 159Hz:读作每秒变化慢于约 160 次的信号能过去,更快的会被抹平。

安全与可靠性设计

如果固件误把引脚设成输出,按下按键就等于把它短路到地。在按键前串一颗 100Ω~1kΩ 的电阻可以限制故障电流(3.3V / 100Ω = 33mA)。取低端之前先查你手上芯片的单脚电流上限,有些芯片允许的更小。

外露的按键也是手指静电的入口,加一颗到地的保护二极管,静电在碰到芯片之前就有地方可去。

多按键与资源优化

一个按键占一个引脚,很快就用完了。常见的办法是矩阵键盘(按键共用行和列),或者电阻分压(一个引脚读到不同电压,对应不同按键)。