数字输出与输入(GPIO)
ESP32 的通用输入输出引脚由 GPIO 矩阵统一管理,绝大多数引脚既可作输入也可作输出。Arduino 框架下使用 pinMode() 与 digitalWrite()、digitalRead() 控制,与经典 Arduino 用法一致。
const int LED = 2;
void setup() {
pinMode(LED, OUTPUT);
}
void loop() {
digitalWrite(LED, HIGH);
delay(500);
digitalWrite(LED, LOW);
delay(500);
}
读取按键等外部信号时,把引脚设为 INPUT_PULLUP(内部上拉),按键另一端接地,按下读到 LOW,松开读到 HIGH。
哪些引脚不能随便用
ESP32 共有 34 个物理 GPIO,但一部分有特殊用途,做普通 IO 前要留心:
- GPIO 34、35、36、39:仅输入,没有内部上拉下拉,适合接 ADC 或数字传感器,不能做输出
- GPIO 6-11:连接内部 Flash,动了会直接崩溃,不要使用
- GPIO 0、2、5、12、15:参与启动模式判断与上电电平检测,可用但外接电路不能影响上电时的默认电平,否则设备无法正常启动或进不了烧录模式
- GPIO 1、3:默认串口 TX/RX,调试期间建议保留
PWM 输出(LEDC)
ESP32 的 PWM 由 LEDC 外设(原本为驱动 LED 设计,共 16 个通道)产生,可输出独立频率与占空比的方波,常用于 LED 调光、电机调速和驱动舵机。
Arduino-ESP32 3.x 对 API 做了重要简化:不再手动分配通道,直接把引脚、频率、分辨率一并交给 ledcAttach(),之后用 ledcWrite() 按引脚写占空比。2.x 时代的 ledcSetup() 与 ledcAttachPin() 已废弃,网上大量旧教程还在用,照抄会编译报错。
const int LED = 2; // 任意支持输出的 GPIO
const int FREQ = 5000; // 5 kHz
const int RES = 8; // 8 位分辨率
void setup() {
ledcAttach(LED, FREQ, RES); // 自动分配通道
}
void loop() {
for (int duty = 0; duty <= 255; duty++) {
ledcWrite(LED, duty);
delay(5);
}
for (int duty = 255; duty >= 0; duty--) {
ledcWrite(LED, duty);
delay(5);
}
}
频率与分辨率的权衡
LEDC 的时钟源固定,频率和分辨率此消彼长:频率越高,可用的分辨率位数越少。常用组合:
- LED 调光:5 kHz、8 位(占空比 0-255)
- 舵机控制:50 Hz、16 位(脉宽精度约 0.5 微秒)
如果只需要简单调光,也可以直接用 analogWrite(pin, value),它内部走 LEDC,取值 0-255,与 AVR Arduino 习惯一致。
模拟输入(ADC)
ESP32 内置两个 12 位逐次逼近型 ADC(ADC1、ADC2),共 18 个输入通道,读数范围默认 0-4095,对应约 0-3.3 V。使用 analogRead(pin) 即可读取:
const int POT = 34; // 电位器接 GPIO34
void setup() {
Serial.begin(115200);
}
void loop() {
int raw = analogRead(POT);
float voltage = raw * 3.3 / 4095.0;
Serial.printf("raw=%d voltage=%.2fV\n", raw, voltage);
delay(200);
}
使用注意
- ADC2 与 WiFi 冲突:ADC2 的所有引脚在 WiFi 开启时不可用,WiFi 项目中的模拟输入一律选 ADC1 引脚(GPIO 32-39)
- 满量程并非精确 3.3 V:ESP32 的 ADC 非线性较明显,绝对精度约 ±3%,适合趋势判断(如电位器、光敏电阻),做精确电压测量需校准或外接 ADC
-
衰减设置:默认量程约 1.1 V,需用
analogSetPinAttenuation(pin, ADC_11db)把量程扩到约 3.3 V(新版核心多数引脚默认已是 11 dB,读满量程前建议显式设置一次)
动手实验:呼吸灯 + 旋钮调光
把前面内容串起来:电位器接 GPIO34(ADC1),LED 接 GPIO2,转动旋钮实时改变 LED 亮度。
const int POT = 34;
const int LED = 2;
void setup() {
ledcAttach(LED, 5000, 8);
}
void loop() {
int raw = analogRead(POT); // 0-4095
int duty = map(raw, 0, 4095, 0, 255);
ledcWrite(LED, duty);
delay(20);
}
烧录后转动电位器,LED 亮度应平滑跟随。若 LED 不亮,先检查引脚是否误用了 GPIO 6-11;若读数始终为 0 或 4095,检查电位器接线与衰减设置。
常见坑速查
- 编译报
ledcSetup was not declared:在用 2.x 旧 API,改用ledcAttach/ledcWrite - 引脚无输出:确认不是 GPIO 34-39(仅输入)或 GPIO 6-11(Flash 专用)
- 上电反复重启或进不了烧录模式:GPIO 0、2、12、15 上外接了影响启动电平的电路
- 开了 WiFi 后模拟读数异常:用了 ADC2 引脚,换 GPIO 32-39