3. ESP32基础与外设:GPIO、I2C、SPI、UART、ADC、PWM等常用接口,以及传感器数据采集方法
在TinyML工作流中,模型训练只是第一步,真正的挑战在于将量化后的模型部署到资源受限的微控制器上,并让它与真实世界交互。ESP32作为性价比极高的双核MCU,集成了Wi-Fi和蓝牙,且拥有丰富的外设接口,是TinyML原型验证的理想平台。本章将系统梳理ESP32的六大核心外设——GPIO、I2C、SPI、UART、ADC、PWM,重点讲解它们在传感器数据采集链路中的角色、配置方法、时序要点以及常见陷阱,为后续在ESP32上运行TensorFlow Lite Micro(TFLM)推理引擎打下坚实的硬件基础。
上图展示了ESP32在传感器数据采集中的核心位置。所有外设最终都将数据汇聚到SoC内部的总线矩阵,经过DMA或CPU搬运后进入内存,供TFLM推理引擎处理。理解这条链路上的每个环节——从传感器模拟信号到数字特征张量——是部署TinyML模型的前提。
3.1 GPIO:数字世界的“开关”与“眼睛”
核心原理
GPIO(General Purpose Input/Output)是ESP32最基础的外设。每个GPIO引脚可配置为输入、输出、开漏或复用功能。在TinyML场景中,GPIO常用于读取数字传感器(如人体红外PIR、霍尔开关)、控制LED指示灯或触发摄像头快门。ESP32的GPIO支持上升沿/下降沿中断,且可从深度睡眠中唤醒,这对电池供电的TinyML设备至关重要。
配置与操作流程
- 引脚分配:查阅ESP32 DevKitC引脚图,避开Strapping引脚(GPIO0、GPIO2、GPIO5、GPIO12、GPIO15),这些引脚在启动时影响下载模式或Flash电压。
- 初始化:使用
gpio_config_t结构体设置模式、上拉/下拉、中断类型。 - 读写操作:调用
gpio_set_level()和gpio_get_level()。 - 中断服务:注册ISR,注意ISR中只做标记,具体处理放在主循环或任务中。
// GPIO 初始化示例(ESP-IDF v5.x)
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << GPIO_NUM_4),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_POSEDGE
};
gpio_config(&io_conf);
gpio_install_isr_service(0);
gpio_isr_handler_add(GPIO_NUM_4, gpio_isr_handler, NULL);
案例拆解:PIR人体感应触发推理
假设我们要做一个“人靠近时自动进行图像分类”的智能门锁。PIR传感器输出高电平表示有人。我们将PIR OUT接GPIO4,LED接GPIO2(注意GPIO2为Strapping引脚,但上电时为高电平,若LED阳极接GPIO2、阴极接地,则上电瞬间LED会闪一下,可接受)。在ISR中置位g_trigger_flag,主循环检测到标志后执行TFLM推理。实测中,PIR的“锁存时间”约2秒,需在代码中做去抖(如连续读取3次均为高电平才确认)。
3.2 I2C:多传感器共享总线
核心原理
I2C(Inter-Integrated Circuit)使用两根线(SDA数据、SCL时钟)连接最多127个设备,每个设备有7位地址。ESP32的I2C控制器支持主机和从机模式,时钟频率标准100kHz、快速400kHz。在TinyML中,I2C是连接温湿度传感器(SHT30)、气压计(BMP280)、IMU(MPU6050)的首选,因为它占用引脚少,且支持多设备挂载。
关键参数与配置
| 参数 | 典型值 | 说明 |
|---|---|---|
| SCL频率 | 100kHz / 400kHz | 线缆较长时降频 |
| 上拉电阻 | 4.7kΩ - 10kΩ | ESP32内部有弱上拉,但建议外部加 |
| 超时时间 | 50ms | 防止总线死锁 |
| 地址位宽 | 7位 | 部分传感器支持10位 |
操作流程与排障
- 扫描总线:使用
i2c_scanner例程确认设备地址。 - 初始化驱动:
i2c_master_init()设置端口、SDA/SCL引脚、时钟。 - 读写时序:注意寄存器地址的字节序(大端居多)。
- 错误处理:检查
ESP_FAIL返回值,常见错误为NACK(设备未应答)或总线繁忙。
3.3 SPI:高速数据搬运通道
核心原理
SPI(Serial Peripheral Interface)采用主从模式,四线制:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。ESP32有4个SPI控制器(SPI0/SPI1用于Flash,SPI2/SPI3可自由使用)。SPI的传输速率可达40MHz以上,适合高吞吐量的传感器,如摄像头(OV2640)、SD卡、LCD显示屏。在TinyML中,SPI常用于将预处理后的图像数据快速搬运到内存。
模式选择与DMA
SPI有4种模式(CPOL时钟极性、CPHA时钟相位)。大多数传感器使用模式0(CPOL=0, CPHA=0)或模式3。ESP32的SPI驱动支持DMA传输,可大幅降低CPU占用。配置时需指定DMA通道(如SPI_DMA_CH_AUTO)。
// SPI 总线初始化(主机模式)
spi_bus_config_t buscfg = {
.mosi_io_num = 23,
.miso_io_num = 19,
.sclk_io_num = 18,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4096
};
spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
案例拆解:读取MAX31865热电偶数据
在工业温度监控场景中,MAX31865通过SPI输出24位数据。我们配置SPI时钟2MHz(线缆较长时降低),CS接GPIO5。读取流程:拉低CS→发送寄存器地址(0x00)→读取4字节(含故障状态)→拉高CS。实测中,若MISO数据全为1,说明传感器未接或线路断开;若数据跳变异常,检查SCLK是否受干扰,建议在SCLK和MISO上加22pF电容滤波。
ets_delay_us(1)。
3.4 UART:异步串行通信与调试利器
核心原理
UART(Universal Asynchronous Receiver/Transmitter)只需TX和RX两根线,无需时钟线,通过波特率约定采样时刻。ESP32有3个UART控制器,支持最高5Mbps。UART在TinyML中扮演两个角色:一是与PC通信输出推理结果(日志),二是连接串口型传感器(如部分GPS模块、激光雷达)。
配置要点
- 波特率:115200是调试默认值,但高波特率(如921600)可加快固件下载。
- 数据位/停止位/校验:通常8N1(8数据位、无校验、1停止位)。
- 流控:若传感器数据量大,可启用硬件流控(RTS/CTS)。
- 缓冲区:设置环形缓冲区大小,避免数据丢失。
// UART 配置示例
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};
uart_param_config(UART_NUM_1, &uart_config);
uart_driver_install(UART_NUM_1, 1024 * 2, 0, 0, NULL, 0);
常见误区
很多开发者直接用printf()输出调试信息,但在TinyML推理循环中,printf()会阻塞CPU且耗时较长。建议使用ESP_LOGI()(异步日志)或先格式化到缓冲区再一次性输出。另外,UART的RX引脚默认有上拉,若外部设备为开漏输出,需注意电平匹配。
3.5 ADC:模拟世界的数字化入口
核心原理
ESP32的ADC(Analog-to-Digital Converter)为12位逐次逼近型,支持衰减(0dB、2.5dB、6dB、11dB),对应输入范围约0-1.1V、0-1.5V、0-2.2V、0-3.3V。ADC2与Wi-Fi共用,当Wi-Fi开启时ADC2不可用,因此传感器应优先接ADC1(GPIO32-GPIO39)。
校准与非线性
ESP32的ADC存在较大的非线性(尤其在低压端),官方提供esp_adc_cal库进行两点校准或查表校准。在TinyML中,若传感器输出为模拟信号(如麦克风MAX4466、光敏电阻),必须做校准,否则模型输入分布偏移会导致推理精度下降。
| 衰减档位 | 量程(mV) | 推荐场景 |
|---|---|---|
| 0dB | 0-1100 | 高精度小信号 |
| 2.5dB | 0-1500 | 电池电压监测 |
| 6dB | 0-2200 | 通用传感器 |
| 11dB | 0-3300 | 3.3V供电传感器 |
操作流程
- 选择通道:确认ADC1的GPIO编号。
- 配置衰减:根据传感器输出范围选择。
- 多次采样取平均:建议连续采样16次取平均,降低噪声。
- 应用校准:调用
esp_adc_cal_get_voltage()转换为实际电压。
Lux = (Vcc - Vout) / (R * K)估算照度。实测中,发现电压在1.2V附近波动较大,原因是室内50Hz灯光频闪。解决方案:采样周期设为20ms的整数倍(如100ms),并做滑动平均滤波。
3.6 PWM:模拟输出与执行器控制
核心原理
PWM(Pulse Width Modulation)通过改变占空比模拟模拟电压。ESP32的LEDC控制器提供16个通道,可分组绑定到任意GPIO,支持高达40MHz的载波频率。在TinyML应用中,PWM常用于控制电机转速、LED亮度或加热器功率,作为推理结果的执行器。
配置与使用
// LEDC 初始化(50Hz 舵机控制)
ledc_timer_config_t timer_conf = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = LEDC_TIMER_0,
.duty_resolution = LEDC_TIMER_13_BIT,
.freq_hz = 50,
.clk_cfg = LEDC_AUTO_CLK
};
ledc_timer_config(&timer_conf);
ledc_channel_config_t channel_conf = {
.gpio_num = 18,
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LEDC_CHANNEL_0,
.timer_sel = LEDC_TIMER_0,
.duty = 0,
.hpoint = 0
};
ledc_channel_config(&channel_conf);
精度与抖动
13位分辨率意味着占空比步进为1/8192,对于舵机控制足够。但若PWM频率过高(如1MHz),分辨率会下降。在电机控制中,建议频率在1kHz-20kHz之间,避免 audible noise。另外,LEDC的占空比更新是异步的,若需要精确相位同步,需使用ledc_update_duty()。
3.7 传感器数据采集综合实战:环境监测节点
我们设计一个完整的TinyML环境监测节点,采集温度(I2C)、光照(ADC)、运动(GPIO中断),并通过UART输出推理结果。
硬件连接:
- SHT30 → I2C0 (SDA=21, SCL=22)
- LDR分压 → GPIO34 (ADC1_CH6)
- PIR → GPIO4 (中断输入)
- LED → GPIO2 (PWM调光)
软件架构:
- FreeRTOS任务1:每2秒读取SHT30和LDR,计算温湿度和光照,存入全局结构体。
- FreeRTOS任务2:等待PIR中断标志,触发TFLM模型推理(如“是否有人”分类)。
- UART任务:每5秒输出一次所有传感器数据及推理结果。
关键代码片段:
// 读取SHT30(简化版)
uint8_t data[6] = {0};
i2c_master_write_read_device(I2C_NUM_0, 0x44, cmd, 2, data, 6, 100);
uint16_t temp_raw = (data[0] << 8) | data[1];
float temp = -45 + 175 * (temp_raw / 65535.0f);
实测数据与调优:初始测试发现LDR读数波动±5%,原因是ADC参考电压随温度漂移。解决:在PCB上增加一个2.5V基准电压源(如TL431)接到另一个ADC通道,用比值法消除漂移。另外,PIR中断频繁触发(每30秒一次),导致CPU唤醒频繁,功耗增加。优化:在ISR中增加软件去抖(记录时间戳,若小于5秒则忽略)。
3.8 常见误区与总结
| 误区 | 后果 | 正确做法 |
|---|---|---|
| 所有GPIO都当普通IO用 | 启动失败或下载异常 | 避开Strapping引脚,查阅数据手册 |
| I2C不加上拉电阻 | 通信不稳定,偶发NACK | 外部加4.7kΩ上拉至VCC |
| SPI速率越高越好 | 信号完整性差,数据错位 | 根据线缆长度降频,加端接电阻 |
| UART只用printf调试 | 阻塞CPU,影响推理实时性 | 使用异步日志或DMA传输 |
| ADC不校准直接读 | 电压偏差达10%以上 | 使用esp_adc_cal库进行两点校准 |
| PWM频率随意设置 | 电机啸叫或LED闪烁 | 根据负载选择合适频率 |
本章从ESP32的六大外设出发,详细阐述了它们在TinyML传感器数据采集链路中的角色和用法。GPIO负责数字事件触发,I2C和SPI承担多传感器数据搬运,UART提供调试和外部通信,ADC将模拟世界数字化,PWM则驱动执行器完成闭环控制。掌握这些接口的时序特性、配置方法和排障技巧,是确保TFLM模型在ESP32上稳定运行的基础。下一阶段,我们将进入TensorFlow Lite Micro的集成与部署,届时这些外设采集的数据将直接作为模型的输入张量。