ESP32 GPIO 矩阵
ESP32 最独特的设计之一是 GPIO 矩阵(GPIO Matrix)。传统微控制器的外设引脚是固定绑定的(如 UART TX 只能在某几个特定引脚),而 ESP32 的 GPIO 矩阵允许几乎任意外设信号路由到任意 GPIO 引脚。这极大简化了 PCB 布线。
GPIO 关键概念词表
GPIO 配置与中断示例
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#define BTN_PIN GPIO_NUM_0 // 通常是 BOOT 按钮
#define LED_PIN GPIO_NUM_2
static QueueHandle_t gpio_evt_queue;
static const char *TAG = "GPIO";
/* ISR 必须标记 IRAM_ATTR,不得调用非 IRAM 函数 */
static void IRAM_ATTR gpio_isr_handler(void *arg)
{
uint32_t gpio_num = (uint32_t)arg;
/* 从 ISR 向队列发送,不阻塞 */
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
static void gpio_task(void *arg)
{
uint32_t gpio_num;
while (1) {
if (xQueueReceive(gpio_evt_queue, &gpio_num, portMAX_DELAY)) {
ESP_LOGI(TAG, "GPIO[%" PRIu32 "] 触发,当前电平: %d",
gpio_num, gpio_get_level(gpio_num));
/* 切换 LED */
static int led_state = 0;
led_state = !led_state;
gpio_set_level(LED_PIN, led_state);
}
}
}
void app_main(void)
{
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
/* 配置 LED 输出 */
gpio_config_t led_cfg = {
.pin_bit_mask = (1ULL << LED_PIN),
.mode = GPIO_MODE_OUTPUT,
};
gpio_config(&led_cfg);
/* 配置按钮输入,内部上拉,下降沿触发中断 */
gpio_config_t btn_cfg = {
.pin_bit_mask = (1ULL << BTN_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_NEGEDGE,
};
gpio_config(&btn_cfg);
/* 安装 GPIO 中断服务(只调用一次) */
gpio_install_isr_service(0);
gpio_isr_handler_add(BTN_PIN, gpio_isr_handler, (void *)BTN_PIN);
xTaskCreate(gpio_task, "gpio_task", 2048, NULL, 10, NULL);
}
ADC — 模数转换
ESP32 有两组 ADC:ADC1(8 个通道,GPIO 32-39)和 ADC2(10 个通道,GPIO 0/2/4/12-15/25-27)。ADC 将 0~3.3V 的模拟电压转换为 0~4095 的 12bit 数字值。
ESP32 的 ADC2 由 Wi-Fi 硬件共享使用。当 Wi-Fi 启用时,ADC2 完全不可用,调用会返回错误。如果你的项目需要 Wi-Fi,请将所有模拟传感器连接到 ADC1(GPIO 32-39)。
ADC 通道与 GPIO 对应关系
ADC 校准与读取
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "esp_log.h"
static const char *TAG = "ADC";
void app_main(void)
{
/* 1. 初始化 ADC 单次模式句柄 */
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = ADC_UNIT_1,
};
adc_oneshot_new_unit(&init_config, &adc1_handle);
/* 2. 配置通道(12bit,11dB 衰减 → 量程 0~3.3V)*/
adc_oneshot_chan_cfg_t chan_cfg = {
.bitwidth = ADC_BITWIDTH_DEFAULT, // 12bit
.atten = ADC_ATTEN_DB_11, // 0~3.3V 量程
};
adc_oneshot_config_channel(adc1_handle, ADC_CHANNEL_6, &chan_cfg); // GPIO34
/* 3. 创建校准方案(Curve Fitting 或 Line Fitting)*/
adc_cali_handle_t cali_handle = NULL;
adc_cali_curve_fitting_config_t cali_cfg = {
.unit_id = ADC_UNIT_1,
.chan = ADC_CHANNEL_6,
.atten = ADC_ATTEN_DB_11,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
adc_cali_create_scheme_curve_fitting(&cali_cfg, &cali_handle);
while (1) {
int raw = 0, voltage_mv = 0;
adc_oneshot_read(adc1_handle, ADC_CHANNEL_6, &raw);
adc_cali_raw_to_voltage(cali_handle, raw, &voltage_mv);
ESP_LOGI(TAG, "ADC 原始值: %d 电压: %d mV", raw, voltage_mv);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
ADC_ATTEN_DB_0:0~1.1V(最精准,适合低压传感器)
ADC_ATTEN_DB_2_5:0~1.5V
ADC_ATTEN_DB_6:0~2.2V
ADC_ATTEN_DB_11:0~3.3V(最常用,满量程)
ledc — LED 控制器 PWM
PWM(脉宽调制)通过快速切换 GPIO 高低电平,利用占空比(duty cycle)模拟模拟信号。ESP32 的 ledc(LED Control)模块提供最多 16 路 PWM,分为高速(HS,8路)和低速(LS,8路)两组,频率和精度可独立配置。
ledc 架构
呼吸灯完整示例
#include "driver/ledc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#define LED_PIN GPIO_NUM_2
#define LEDC_TIMER LEDC_TIMER_0
#define LEDC_MODE LEDC_LOW_SPEED_MODE
#define LEDC_CHANNEL LEDC_CHANNEL_0
#define LEDC_DUTY_RES LEDC_TIMER_13_BIT // 13位 = 0~8191
#define LEDC_FREQUENCY (5000) // 5kHz
static const char *TAG = "BREATH";
static void ledc_init(void)
{
/* 配置定时器 */
ledc_timer_config_t timer_cfg = {
.speed_mode = LEDC_MODE,
.duty_resolution = LEDC_DUTY_RES,
.timer_num = LEDC_TIMER,
.freq_hz = LEDC_FREQUENCY,
.clk_cfg = LEDC_AUTO_CLK,
};
ledc_timer_config(&timer_cfg);
/* 配置通道 */
ledc_channel_config_t channel_cfg = {
.speed_mode = LEDC_MODE,
.channel = LEDC_CHANNEL,
.timer_sel = LEDC_TIMER,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = LED_PIN,
.duty = 0,
.hpoint = 0,
};
ledc_channel_config(&channel_cfg);
/* 启用硬件 Fade 功能 */
ledc_fade_func_install(0);
}
void app_main(void)
{
ledc_init();
ESP_LOGI(TAG, "呼吸灯启动");
while (1) {
/* 渐亮:0 → 8191,耗时 1000ms,等待完成 */
ledc_set_fade_with_time(LEDC_MODE, LEDC_CHANNEL, 8191, 1000);
ledc_fade_start(LEDC_MODE, LEDC_CHANNEL, LEDC_FADE_WAIT_DONE);
/* 渐暗:8191 → 0,耗时 1000ms */
ledc_set_fade_with_time(LEDC_MODE, LEDC_CHANNEL, 0, 1000);
ledc_fade_start(LEDC_MODE, LEDC_CHANNEL, LEDC_FADE_WAIT_DONE);
}
}
触摸传感器(Touch Sensor)
ESP32 内置电容式触摸传感器,支持 10 个触摸通道(Touch0~Touch9,对应 GPIO 4/0/2/15/13/12/14/27/33/32)。通过检测 GPIO 上电容变化来感应触摸,无需外部器件。
#include "driver/touch_pad.h"
#include "esp_log.h"
#define TOUCH_CHANNEL TOUCH_PAD_NUM7 // GPIO27
#define TOUCH_THRESH 800 // 触摸阈值,需根据实际校准
static const char *TAG = "TOUCH";
void app_main(void)
{
touch_pad_init();
touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_1V);
touch_pad_config(TOUCH_CHANNEL, TOUCH_THRESH);
touch_pad_filter_start(10); // 10ms 滤波周期
while (1) {
uint16_t touch_val = 0;
touch_pad_read_filtered(TOUCH_CHANNEL, &touch_val);
ESP_LOGI(TAG, "Touch7 = %d %s",
touch_val, touch_val < TOUCH_THRESH ? "[TOUCHED]" : "");
vTaskDelay(pdMS_TO_TICKS(200));
}
}
触摸传感器的基线值受环境湿度、PCB 布线、探针长度等因素影响,代码中的阈值 800 仅为示例。正确做法是先运行程序读取无触摸时的基线值,再将阈值设置为基线值的约 70%(即触摸时读数下降 30% 触发)。