数字输出/输入
1. 数字信号
数字信号是用离散的数值来表示信息的信号。最简单、最常见的数字信号是二进制数字信号,它只有两种状态。
在 ESP32 的 GPIO 控制中,主要使用这种二进制数字信号,就像房间的电灯开关,二进制数字信号在任何时刻都处于两种明确状态之一:
- 高电平 (HIGH): 代表逻辑上的 "1" 或 "真"。在 ESP32 开发板上,这通常意味着引脚输出接近 3.3V 的电压。
- 低电平 (LOW): 代表逻辑上的 "0" 或 "假"。在 ESP32 开发板上,这通常意味着引脚输出大约 0 伏特的电压,也就是连接到地 (GND)。
简单来说,数字信号就是用这两种电压状态来传递是 (HIGH) / 否 (LOW) 或者 1 / 0 这样的信息。
- 当 ESP32 输出一个数字信号时,它控制一个引脚变成高电平或低电平,就像是在“说话”,比如控制 LED 的开关。
- 当 ESP32 输入一个数字信号时,它检测一个引脚上是高电平还是低电平,就像是在“倾听”,比如检测按钮是否被按下。
HIGH 电平总是等于微控制器的工作电压:
- ESP32 的工作电压是 3.3V → HIGH = 3.3V
- Arduino Uno 的工作电压是 5V → HIGH = 5V
- 某些低功耗芯片工作电压是 1.8V → HIGH = 1.8V
因此,"HIGH"所代表的具体电压值取决于所使用的开发板。
2. 数字输出
本示例将使用 ESP32 开发板和 Arduino 环境,使一个外接 LED 闪烁。本示例将演示如何使用 Arduino IDE 控制 ESP32 开发板的数字输出。
2.1 搭建电路
需要使用的器件有:
- LED * 1
- 330Ω 电阻 * 1
- 面包板 * 1
- 导线
- ESP32 开发板
按照下面接线图连接电路:
ESP32-S3-Zero 引脚图

电路工作原理
让我们理解一下这个简单电路是如何工作的:
-
电流路径: 当 GPIO7 输出高电平 (3.3V) 时,电流从引脚流出 → 经过 330Ω 电阻 → 通过 LED → 回到 ESP32 的 GND 引脚,形成完整的电路回路。
-
电阻的作用: 330Ω 电阻是限流电阻
- 保护 LED:防止过大电流烧坏 LED
- 保护 ESP32:防止 GPIO 引脚输出过大电流
-
LED 极性:
- 长脚(阳极):连接电阻的另一端
- 短脚(阴极):连接 GND
- 接反了 LED 不会亮,但一般不会损坏
如果没有 330Ω 电阻,可以使用 220Ω-1kΩ 范围内的电阻替代。
2.2 代码
打开 Arduino IDE,运行下面的代码。
const int ledPin = 7; // LED 连接的引脚号
// setup 函数在开发板上电或复位后运行一次
void setup() {
// 将引脚初始化为输出模式
pinMode(ledPin, OUTPUT);
}
// loop 函数会永远反复运行
void loop() {
digitalWrite(ledPin, HIGH); // 点亮 LED(HIGH 表示高电平)
delay(1000); // 等待一秒
digitalWrite(ledPin, LOW); // 引脚输出低电平,关闭 LED
delay(1000); // 等待一秒
}
上传代码后,与 ESP32 开发板连接的 LED 将会亮起一秒,熄灭一秒,如此循环往复。
代码解析
-
const int ledPin = 7;
:- 使用
const int
定义一个常量,这样如果要换引脚,只需修改这一处。 const
表示这个值在程序运行期间不会改变。
- 使用
-
pinMode(ledPin, OUTPUT);
:pinMode()
函数的作用: 配置指定引脚的工作模式,需要在使用数字引脚前调用。- 第一个参数: 引脚编号
- 第二个参数: 模式类型
OUTPUT
:输出模式,ESP32 可以控制这个引脚输出高电平或低电平。
-
digitalWrite(ledPin, HIGH)
:digitalWrite()
函数的作用: 向输出引脚输出值- 第一个参数: 引脚编号
- 第二个参数: 要输出的电平
HIGH
:输出高电平(3.3V),LED 点亮。LOW
:输出低电平(0V),LED 熄灭。
-
delay(1000);
:delay()
函数的作用: 暂停程序执行指定的毫秒数- 1000 毫秒 = 1 秒。
- 在
delay()
期间,程序不执行其他操作。这意味着在delay()
期间,ESP32 不能响应其他事件,例如读取传感器或按钮。在更复杂的项目中,需要使用非阻塞的延时方法。
3. 数字输入
本例将通过 ESP32 开发板制作一个简单的按钮电路,通过读取按钮状态来学习数字输入的基本操作。
3.1 搭建电路
需要使用的器件有:
- 按钮 * 1
- 面包板 * 1
- 导线
- ESP32 开发板
- 10kΩ 电阻 * 1(可选,使用内部上拉时不需要)
如果按钮引脚既没有连接电源也没有连接地,它就处于"浮空 (Floating)"状态,读取的值是不确定的。上拉电阻确保在按钮未按下时引脚有明确的高电平状态。
ESP32-S3-Zero 引脚图
内部上拉电阻(推荐) | 外部上拉电阻 |
---|---|
![]() | ![]() |
连接方式: • 按钮一端 → GPIO8 • 按钮另一端 → GND 工作原理: • ESP32 内部上拉电阻将 GPIO8 拉到 HIGH(3.3V) • 按钮未按下:读取 HIGH • 按钮按下:读取 LOW 优点: • 节省元器件 • 接线简单 • 代码: pinMode(buttonPin, INPUT_PULLUP); | 连接方式: • 按钮一端 → GPIO8 • 按钮另一端 → GND • 10kΩ 电阻:3.3V ↔ GPIO8 工作原理: • 外部电阻将 GPIO8 拉到 HIGH(3.3V) • 按钮未按下:读取 HIGH • 按钮按下:读取 LOW 优点: • 上拉电流可控 • 通用性更好 • 代码: pinMode(buttonPin, INPUT); |
ESP32-S3-Zero 引脚图
3.2 代码
示例 1:读取按钮状态
const int buttonPin = 8; // 定义按钮连接的引脚
void setup() {
Serial.begin(9600); // 初始化串口通信,波特率 9600
while (!Serial) {} // 等待串口连接建立
pinMode(buttonPin, INPUT_PULLUP); // 设置按钮引脚为上拉输入模式
}
void loop() {
int buttonState = digitalRead(buttonPin); // 读取按钮当前状态
Serial.println(buttonState); // 串口输出按钮状态值
}
代码解释:
const int buttonPin = 8;
:定义按钮连接的引脚。Serial.begin(9600);
:初始化串口通信,波特率设置为 9600。pinMode(buttonPin, INPUT_PULLUP);
:- 将
buttonPin
设置为INPUT_PULLUP
模式。 INPUT_PULLUP
是一个特殊的输入模式,它会启用 ESP32 GPIO 引脚内部的上拉电阻。这样,当按钮未按下时,引脚会被拉到高电平;当按钮按下将引脚连接到 GND 时,引脚变为低电平。这避免了连接外部上拉电阻的需要。
- 将
int buttonState = digitalRead(buttonPin);
:digitalRead()
函数用于读取指定数字引脚的电平状态。- 它返回
HIGH
(通常是整数 1) 或LOW
(通常是整数 0)。 - 由于使用了
INPUT_PULLUP
:- 按钮未按下:
buttonState
为HIGH
。 - 按钮按下:
buttonState
为LOW
。
- 按钮未按下:
Serial.println()
:将括号内的内容打印到串口监视器,并换行。
运行结果:
将代码烧录到 ESP32 开发板后,打开串口监视器。按钮未按下时,串口监视器会持续显示"1";按下按钮时,显示"0"。你可以通过按压和释放按钮来观察这些状态的变化。
示例 2:记录按钮按下的次数
const int buttonPin = 8;
int lastButtonState = 1; // 上一次按钮状态
int currentButtonState; // 当前按钮状态
int count = 0; // 计数器
void setup() {
Serial.begin(9600); // 初始化串口通信
while (!Serial) {} // 等待串口连接
pinMode(buttonPin, INPUT_PULLUP); // 设置按钮引脚为上拉输入
}
void loop() {
currentButtonState = digitalRead(buttonPin); // 读取当前按钮状态
if (lastButtonState == HIGH && currentButtonState == LOW) {
// 按钮被按下
} else if (lastButtonState == LOW && currentButtonState == HIGH) {
// 按钮被释放
count = count + 1; // 计数器加 1
Serial.println(count); // 串口输出当前计数值
}
lastButtonState = currentButtonState; // 保存当前按钮状态
}
代码解释:
-
int lastButtonState = HIGH;
:由于我们使用INPUT_PULLUP
模式,按钮未按下时引脚为高电平,所以初始值设为HIGH
。 -
状态检测逻辑:
lastButtonState == HIGH && currentButtonState == LOW
:引脚由高电平到低电平,检测按钮刚被按下的瞬间lastButtonState == LOW && currentButtonState == HIGH
:引脚由低电平到高电平,检测按钮刚被释放的瞬间- 我们选择在按钮释放时计数
-
lastButtonState = currentButtonState;
:在每次循环结束时更新状态,为下一次比较做准备。
运行结果:
烧录代码后打开串口监视器。尝试多次按下按钮,你可能会发现计数器有时会增加 1,但有时会突然增加 2、3 或者更多。这就是按钮抖动 (Button Bouncing)。
机械按钮在按下或松开的瞬间,其内部的金属触点会发生微小的、快速的物理弹跳。这导致在人感觉只按了一次的几毫秒内,电路实际上快速地接通、断开了很多次。ESP32 的运行速度非常快,它能捕捉到每一次微小的通断,因此会错误地将其识别为多次按下按钮。
示例 3:记录按钮按下的次数(简单防抖)
一种简单的消除抖动的方法是在检测到一次按键后,加入一个短暂的延时,忽略掉后续的抖动信号。
const int buttonPin = 8;
int lastButtonState = 1; // 上一次按钮状态
int currentButtonState; // 当前按钮状态
int count = 0; // 计数器
void setup() {
Serial.begin(9600); // 初始化串口通信
while (!Serial) {} // 等待串口连接
pinMode(buttonPin, INPUT_PULLUP); // 设置按钮引脚为上拉输入
}
void loop() {
currentButtonState = digitalRead(buttonPin); // 读取当前按钮状态
if (lastButtonState == HIGH && currentButtonState == LOW) {
// 按钮被按下
} else if (lastButtonState == LOW && currentButtonState == HIGH) {
// 按钮被释放
count = count + 1; // 计数器加 1
Serial.println(count); // 串口输出当前计数值
delay(100); // 延时 100 毫秒防抖
}
lastButtonState = currentButtonState; // 保存当前按钮状态
}
运行结果:
和上面一样,每松开按钮一次,计数器加 1,并且因为加了 delay(100),每次按钮弹起只会计数一次,有效减少了误触发数量。你可以尝试快速多次按按钮,留意计数器增量是否与实际按动匹配。
代码解释:
- 在每次检测到由 LOW 变为 HIGH 的瞬间(即按钮松开),更新计数器,执行
count=count+1
和Serial.println(count)
; - 加入
delay(100)
; 暂停 100 毫秒,简单抑制因按钮弹跳带来的重复计数
4. 拓展练习
请尝试实现:当按钮按下时,LED 亮起。松开按钮时,LED 熄灭。
接线图:

代码:
const int ledPin = 7; // LED 连接的引脚号
const int buttonPin = 8; // 按钮连接的引脚号
int buttonState; // 存储按钮状态
void setup() {
pinMode(ledPin, OUTPUT); // 设置 LED 引脚为输出模式
pinMode(buttonPin, INPUT_PULLUP); // 设置按钮引脚为上拉输入模式
}
void loop() {
buttonState = digitalRead(buttonPin); // 读取按钮状态
if (buttonState == LOW) { // 按钮按下时(LOW)
digitalWrite(ledPin, HIGH); // 点亮 LED
} else { // 按钮未按下时(HIGH)
digitalWrite(ledPin, LOW); // 熄灭 LED
}
}
请尝试实现:按按钮一次,切换 LED 状态一次。
接线图:

代码:
const int ledPin = 7; // LED 连接的引脚号
const int buttonPin = 8; // 按钮连接的引脚号
int lastButtonState = HIGH; // 上一次按钮状态
int ledState = LOW; // LED 当前状态(LOW=灭,HIGH=亮)
int currentButtonState; // 当前按钮状态
void setup() {
pinMode(ledPin, OUTPUT); // 设置 LED 引脚为输出模式
pinMode(buttonPin, INPUT_PULLUP); // 设置按钮引脚为上拉输入模式
}
void loop() {
currentButtonState = digitalRead(buttonPin); // 读取当前按钮状态
// 检测按钮从按下变为松开的瞬间
if (lastButtonState == LOW && currentButtonState == HIGH) {
ledState = !ledState; // 切换 LED 状态(亮变灭,灭变亮)
digitalWrite(ledPin, ledState); // 应用新的 LED 状态
delay(100); // 防抖延时
}
lastButtonState = currentButtonState; // 保存当前状态,用于下次比较
}