前言在嵌入式开发尤其是物联网、智慧网关场景中串口、SPI、I2C、ADC、USB是最核心的五大通信接口贯穿传感器采集、外设控制、数据传输全流程。本文针对ESP32芯片整理5篇独立实战文章每篇均包含原理极简解析纯C语言代码ESP-IDF接线图问题排查代码可直接复制编译适配嵌入式工程师开发习惯兼顾新手入门与实战落地。第一篇ESP32 串口通信实战UART—— 最基础的异步通信方案一、串口核心原理极简版串口UART通用异步收发传输器是嵌入式最基础的通信方式无需时钟线仅通过TX发送、RX接收两根信号线实现双向通信支持全双工适用于短距离、低速率数据传输如调试日志输出、与蓝牙模块/4G模块通信。核心参数必须匹配否则通信失败波特率数据传输速率常见9600、115200ESP32默认调试波特率、921600数据位通常8位8N1模式即8位数据位、1位停止位、无校验位校验位无校验最常用、奇校验、偶校验停止位1位默认、2位ESP32 内置3个UART接口UART0、UART1、UART2可灵活分配GPIO引脚支持中断接收、DMA传输满足不同场景需求。二、硬件准备ESP32-S3-N16R8 开发板核心主控USB-TTL 模块用于PC与ESP32串口通信杜邦线 4根VCC、GND、TX、RX面包板可选用于接线固定三、接线方案ESP32 ↔ USB-TTLESP32 引脚USB-TTL 模块引脚备注VCC5V或3.3V确保电压匹配避免烧毁模块GNDGND共地保证信号稳定关键GPIO18UART1_TXRXESP32发送 → USB-TTL接收交叉连接GPIO19UART1_RXTXESP32接收 → USB-TTL发送交叉连接提示ESP32的UART0默认用于调试TXGPIO1RXGPIO3建议使用UART1或UART2进行外设通信避免占用调试端口。四、纯C语言实战代码ESP-IDF实现功能ESP32通过UART1向PC发送数据同时接收PC发送的数据支持中断接收代码带英文注释可直接复制编译。#include driver/uart.h #include esp_log.h #include freertos/FreeRTOS.h #include freertos/task.h #include string.h static const char *TAG uart_demo; // 串口配置参数 #define UART_NUM UART_NUM_1 // 使用UART1 #define UART_TX_PIN 18 // TX引脚 #define UART_RX_PIN 19 // RX引脚 #define UART_BAUD_RATE 115200 // 波特率 #define UART_DATA_BITS UART_DATA_8_BITS // 8位数据位 #define UART_PARITY UART_PARITY_DISABLE // 无校验 #define UART_STOP_BITS UART_STOP_BITS_1 // 1位停止位 #define UART_BUFFER_SIZE 1024 // 接收缓冲区大小 // 串口初始化函数 void uart_init(void) { uart_config_t uart_config { .baud_rate UART_BAUD_RATE, .data_bits UART_DATA_BITS, .parity UART_PARITY, .stop_bits UART_STOP_BITS, .flow_ctrl UART_HW_FLOWCTRL_DISABLE, // 禁用流控 .source_clk UART_SCLK_APB // 时钟源 }; // 配置串口参数 uart_param_config(UART_NUM, uart_config); // 配置GPIO引脚 uart_set_pin(UART_NUM, UART_TX_PIN, UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); // 安装串口驱动启用接收中断 uart_driver_install(UART_NUM, UART_BUFFER_SIZE * 2, 0, 10, NULL, 0); ESP_LOGI(TAG, UART1 initialized successfully); } // 串口发送数据函数 esp_err_t uart_send_data(const char *data, size_t len) { if (data NULL || len 0) { ESP_LOGE(TAG, Invalid send data); return ESP_ERR_INVALID_ARG; } // 发送数据 int bytes_sent uart_write_bytes(UART_NUM, data, len); if (bytes_sent ! len) { ESP_LOGE(TAG, Send data failed, expected %d bytes, actual %d bytes, len, bytes_sent); return ESP_ERR_TIMEOUT; } return ESP_OK; } // 串口接收数据任务中断驱动非阻塞 void uart_receive_task(void *pvParameters) { uint8_t receive_buf[UART_BUFFER_SIZE]; while (1) { // 读取接收缓冲区数据非阻塞有数据则读取无数据则返回0 int len uart_read_bytes(UART_NUM, receive_buf, UART_BUFFER_SIZE, 10 / portTICK_PERIOD_MS); if (len 0) { // 给接收的数据添加结束符 receive_buf[len] \0; ESP_LOGI(TAG, Received data: %s (len: %d), receive_buf, len); // 回显数据将接收的数据发送回去 uart_send_data((char *)receive_buf, len); } vTaskDelay(pdMS_TO_TICKS(10)); } } void app_main(void) { // 初始化串口 uart_init(); // 创建串口接收任务 xTaskCreate(uart_receive_task, uart_receive_task, 4096, NULL, 5, NULL); // 主任务循环发送数据 int count 0; char send_buf[100]; while (1) { sprintf(send_buf, ESP32 UART Test, count: %d\r\n, count); uart_send_data(send_buf, strlen(send_buf)); vTaskDelay(pdMS_TO_TICKS(1000)); // 每秒发送一次 } }五、测试步骤与问题排查5.1 测试步骤按接线方案连接硬件将USB-TTL插入PC打开设备管理器确认COM口。将代码复制到ESP-IDF项目修改CMakeLists.txt确保编译通过。烧录固件打开串口监视器波特率115200、8N1、无流控。观察监视器每秒接收ESP32发送的计数数据在监视器发送框输入内容可收到ESP32回显的数据。5.2 常见问题问题1无数据接收/发送 → 检查TX/RX交叉连接是否正确、GND是否共地、波特率是否匹配。问题2数据乱码 → 波特率错误、电源不稳定更换5V电源、引脚接触不良。问题3中断接收无响应 → 检查串口驱动安装参数缓冲区大小、中断队列长度。六、实战拓展1. DMA传输当需要传输大量数据如传感器批量数据时启用UART DMA减少CPU占用修改uart_driver_install参数即可。2. 多设备通信通过不同UART接口连接多个外设如UART1连蓝牙模块、UART2连4G模块实现多设备协同。3. 调试日志重定向将ESP_LOGI等日志重定向到自定义UART替代默认调试端口方便项目调试。第二篇ESP32 SPI通信实战——高速同步通信适配外设扩展一、SPI核心原理极简版SPI串行外设接口是一种高速同步串行通信协议采用主从架构需4根核心引脚可简化为3根支持全双工、高速传输适用于短距离、高速率数据交互如连接SPI Flash、OLED屏幕、ADC芯片、WiFi模块。ESP32 内置4个SPI控制器SPI0~SPI3其中SPI0用于内部FlashSPI1~SPI3可用于外部外设支持主模式ESP32作为主设备和从模式ESP32作为从设备最高传输速率可达80MHz。核心引脚主模式SCLK时钟线主设备产生同步数据传输节奏MOSI主出从入主设备向从设备发送数据MISO主入从出从设备向主设备返回数据CS片选线主设备通过拉低CS引脚选中对应的从设备多从设备时必备二、硬件准备ESP32-S3-N16R8 开发板SPI OLED 128×64 屏幕从设备最常用的SPI外设杜邦线 6根VCC、GND、SCLK、MOSI、CS、DC面包板、3.3V电源可选说明OLED屏幕需额外DC引脚数据/命令选择属于SPI外设的扩展引脚不影响SPI核心通信。三、接线方案ESP32 ↔ SPI OLEDESP32 引脚SPI2OLED 引脚功能说明3.3VVCC给OLED供电请勿接5V避免烧毁GNDGND共地保证信号稳定GPIO18SCLKSCLSPI时钟线GPIO23MOSISDASPI数据发送线OLED仅接收数据无需MISOGPIO5CSCS片选线拉低选中OLEDGPIO2DCDC数据/命令选择0命令1数据四、纯C语言实战代码ESP-IDF实现功能初始化SPI控制器通过SPI向OLED屏幕发送指令和数据显示文字ESP32 SPI Test代码带英文注释适配ESP-IDF框架可直接复制使用。#include driver/spi_master.h #include esp_log.h #include freertos/FreeRTOS.h #include freertos/task.h #include driver/gpio.h static const char *TAG spi_oled_demo; // SPI 配置参数 #define SPI_NUM SPI2_HOST // 使用SPI2 #define SPI_MOSI_PIN 23 // MOSI引脚 #define SPI_SCLK_PIN 18 // SCLK引脚 #define SPI_CS_PIN 5 // CS引脚片选 #define SPI_DC_PIN 2 // OLED DC引脚数据/命令 #define SPI_BAUD_RATE 10000000 // 10MHz 传输速率 #define SPI_MODE 0 // SPI模式0~3默认0 // OLED 相关定义 #define OLED_CMD 0 // 发送命令 #define OLED_DATA 1 // 发送数据 spi_device_handle_t spi_oled_handle; // SPI设备句柄 // OLED 发送命令/数据函数 void oled_send(uint8_t type, uint8_t data) { // 控制DC引脚区分命令和数据 gpio_set_level(SPI_DC_PIN, type); // 片选拉低选中OLED gpio_set_level(SPI_CS_PIN, 0); // SPI发送数据 spi_transaction_t t { .length 8, // 数据长度bit .tx_buffer data, // 发送缓冲区 .rx_buffer NULL, // 不接收数据 .flags SPI_TRANS_USE_TXDATA // 使用tx_data传输 }; spi_device_transmit(spi_oled_handle, t); // 片选拉高结束通信 gpio_set_level(SPI_CS_PIN, 1); } // OLED 初始化函数 void oled_init(void) { // 初始化DC和CS引脚输出模式 gpio_config_t io_conf { .pin_bit_mask (1ULL SPI_DC_PIN) | (1ULL SPI_CS_PIN), .mode GPIO_MODE_OUTPUT, .pull_up_en GPIO_PULLUP_DISABLE, .pull_down_en GPIO_PULLDOWN_DISABLE, .intr_type GPIO_INTR_DISABLE }; gpio_config(io_conf); // 初始状态CS拉高未选中DC拉低默认命令 gpio_set_level(SPI_CS_PIN, 1); gpio_set_level(SPI_DC_PIN, 0); // OLED 初始化指令通用128×64 OLED指令 oled_send(OLED_CMD, 0xAE); // 关闭显示 oled_send(OLED_CMD, 0x00); // 设置列地址低4位 oled_send(OLED_CMD, 0x10); // 设置列地址高4位 oled_send(OLED_CMD, 0x40); // 设置显示起始行 oled_send(OLED_CMD, 0xB0); // 设置页地址 oled_send(OLED_CMD, 0x81); // 对比度调节 oled_send(OLED_CMD, 0xFF); // 最大对比度 oled_send(OLED_CMD, 0xA1); // 左右反置 oled_send(OLED_CMD, 0xA6); // 正常显示0xA7为反显 oled_send(OLED_CMD, 0xA8); // 设置驱动路数 oled_send(OLED_CMD, 0x3F); // 64路驱动 oled_send(OLED_CMD, 0xC8); // 上下反置 oled_send(OLED_CMD, 0xD3); // 设置显示偏移 oled_send(OLED_CMD, 0x00); // 无偏移 oled_send(OLED_CMD, 0xD5); // 设置震荡频率 oled_send(OLED_CMD, 0x80); // 默认频率 oled_send(OLED_CMD, 0xD9); // 设置预充电周期 oled_send(OLED_CMD, 0xF1); // 预充电周期 oled_send(OLED_CMD, 0xDA); // 设置COM引脚配置 oled_send(OLED_CMD, 0x12); // COM引脚配置 oled_send(OLED_CMD, 0xDB); // 设置VCOMH电压 oled_send(OLED_CMD, 0x40); // VCOMH电压 oled_send(OLED_CMD, 0xAF); // 开启显示 ESP_LOGI(TAG, OLED initialized successfully); } // SPI 初始化函数 void spi_init(void) { // SPI 总线配置 spi_bus_config_t bus_cfg { .mosi_io_num SPI_MOSI_PIN, .miso_io_num -1, // OLED无需接收数据MISO设为-1 .sclk_io_num SPI_SCLK_PIN, .quadwp_io_num -1, .quadhd_io_num -1, .max_transfer_sz 4096 // 最大传输长度 }; // 初始化SPI总线 esp_err_t err spi_bus_initialize(SPI_NUM, bus_cfg, SPI_DMA_CH_AUTO); if (err ! ESP_OK) { ESP_LOGE(TAG, SPI bus initialize failed: %s, esp_err_to_name(err)); return; } // SPI 设备配置OLED作为从设备 spi_device_interface_config_t dev_cfg { .clock_speed_hz SPI_BAUD_RATE, // 传输速率 .mode SPI_MODE, // SPI模式 .spics_io_num -1, // CS手动控制不使用SPI自动CS .queue_size 10, // 传输队列大小 .flags SPI_DEVICE_NO_DUMMY // 不使用虚拟数据 }; // 添加OLED设备到SPI总线 err spi_bus_add_device(SPI_NUM, dev_cfg, spi_oled_handle); if (err ! ESP_OK) { ESP_LOGE(TAG, Add SPI device failed: %s, esp_err_to_name(err)); spi_bus_free(SPI_NUM); return; } ESP_LOGI(TAG, SPI initialized successfully); } // OLED 显示单个字符8×16点阵 void oled_show_char(uint8_t x, uint8_t y, uint8_t ch) { uint8_t i, j; ch - 0x20; // 偏移量ASCII码从0x20开始 // 设置列地址x oled_send(OLED_CMD, 0x00 (x 0x0F)); oled_send(OLED_CMD, 0x10 (x 4)); // 设置页地址y oled_send(OLED_CMD, 0xB0 y); // 发送字符点阵数据8×16共16字节 for (i 0; i 16; i) { oled_send(OLED_DATA, 0x00); // 左半部分可替换为点阵数据 oled_send(OLED_DATA, 0xFF); // 右半部分示例实际需替换为字符点阵 } } // OLED 显示字符串 void oled_show_string(uint8_t x, uint8_t y, const char *str) { uint8_t i 0; while (str[i] ! \0) { oled_show_char(x i*8, y, str[i]); i; } } void app_main(void) { // 初始化SPI spi_init(); // 初始化OLED oled_init(); // 显示字符串 oled_show_string(20, 3, ESP32 SPI Test); // 主循环空循环保持程序运行 while (1) { vTaskDelay(pdMS_TO_TICKS(1000)); } }五、测试步骤与问题排查5.1 测试步骤按接线方案连接ESP32与OLED屏幕确保引脚接触良好。将代码复制到ESP-IDF项目编译并烧录固件。上电后OLED屏幕显示ESP32 SPI Test说明SPI通信正常。5.2 常见问题问题1OLED无显示 → 检查VCC/GND供电、DC/CS引脚接线、OLED初始化指令是否正确。问题2显示乱码 → SPI传输速率过高降低到10MHz以下、SPI模式不匹配、字符点阵数据错误。问题3SPI传输失败 → 检查SPI总线初始化参数、设备添加是否成功查看ESP_LOG日志排查错误。六、实战拓展1. 多从设备通信增加CS引脚如GPIO15、GPIO21分别连接多个SPI外设通过切换CS引脚选中不同设备。2. DMA传输启用SPI DMA实现大量数据如图片、传感器批量数据高速传输减少CPU占用。3. 从模式配置将ESP32配置为SPI从设备接收其他主设备如STM32发送的数据实现多芯片协同。第三篇ESP32 I2C通信实战——两线制同步通信适配传感器一、I2C核心原理极简版I2C集成电路总线是一种两线制同步串行通信协议由飞利浦公司开发仅需SDA数据线和SCL时钟线两根引脚即可实现多主多从通信支持半双工传输适用于短距离、低速率、多设备通信场景如连接温湿度传感器、EEPROM、OLED屏幕。ESP32 内置2个I2C控制器I2C0、I2C1支持主模式和从模式最高传输速率可达1MHz快速模式每个I2C总线上可连接多个从设备通过从设备地址区分7位或10位地址。核心特点两线制SDA数据线、SCL时钟线布线简单节省GPIO引脚。多从设备同一总线可连接多个从设备通过地址区分无需额外片选线。硬件应答从设备接收数据后会发送应答信号主设备可确认数据传输是否成功。二、硬件准备ESP32-S3-N16R8 开发板BH1750 光照传感器I2C从设备常用传感器杜邦线 4根VCC、GND、SDA、SCL10KΩ 上拉电阻 2个可选增强信号稳定性长距离通信必备面包板、3.3V电源提示BH1750 传感器工作电压为3.3V请勿接5VSDA和SCL引脚建议接10KΩ上拉电阻到3.3V避免信号干扰。三、接线方案ESP32 ↔ BH1750ESP32 引脚I2C1BH1750 引脚功能说明3.3VVCC给BH1750供电GNDGND共地保证信号稳定GPIO21SDASDAI2C数据线双向GPIO22SCLSCLI2C时钟线主设备产生说明BH1750 有两个地址0x23和0x5C通过ADDR引脚控制接GND为0x23接VCC为0x5C本文默认使用0x23。四、纯C语言实战代码ESP-IDF实现功能初始化I2C控制器通过I2C与BH1750传感器通信读取光照强度数据单位lx打印到串口代码带英文注释可直接复制编译。#include driver/i2c.h #include esp_log.h #include freertos/FreeRTOS.h #include freertos/task.h #include stdio.h static const char *TAG i2c_bh1750_demo; // I2C 配置参数 #define I2C_NUM I2C_NUM_1 // 使用I2C1 #define I2C_SDA_PIN 21 // SDA引脚 #define I2C_SCL_PIN 22 // SCL引脚 #define I2C_BAUD_RATE 100000 // 100KHz标准模式 #define I2C_TIMEOUT_MS 1000 // 超时时间ms // BH1750 配置参数 #define BH1750_ADDR 0x23 // BH1750 从设备地址ADDR接GND #define BH1750_POWER_ON 0x01 // 上电指令 #define BH1750_RESET 0x07 // 重置指令 #define BH1750_CONT_H 0x10 // 连续高分辨率模式1lx120ms采样 // I2C 初始化函数 void i2c_init(void) { i2c_config_t i2c_config { .mode I2C_MODE_MASTER, // 主模式 .sda_io_num I2C_SDA_PIN, // SDA引脚 .sda_pullup_en GPIO_PULLUP_ENABLE, // 启用SDA上拉 .scl_io_num I2C_SCL_PIN, // SCL引脚 .scl_pullup_en GPIO_PULLUP_ENABLE, // 启用SCL上拉 .master.clk_speed I2C_BAUD_RATE // 时钟速率 }; // 配置I2C参数 i2c_param_config(I2C_NUM, i2c_config); // 安装I2C驱动 esp_err_t err i2c_driver_install(I2C_NUM, I2C_MODE_MASTER, 0, 0, 0); if (err ! ESP_OK) { ESP_LOGE(TAG, I2C driver install failed: %s, esp_err_to_name(err)); } else { ESP_LOGI(TAG, I2C initialized successfully); } } // I2C 发送数据函数主设备向从设备发送数据 esp_err_t i2c_send_data(uint8_t addr, uint8_t *data, size_t len) { if (data NULL || len 0) { ESP_LOGE(TAG, Invalid send data); return ESP_ERR_INVALID_ARG; } // I2C 传输结构体 i2c_cmd_handle_t cmd i2c_cmd_link_create(); i2c_master_start(cmd); // 起始信号 i2c_master_write_byte(cmd, (addr 1) | I2C_MASTER_WRITE, true); // 从设备地址写标志 i2c_master_write(cmd, data, len, true); // 发送数据等待应答 i2c_master_stop(cmd); // 停止信号 // 执行传输 esp_err_t err i2c_master_cmd_begin(I2C_NUM, cmd, I2C_TIMEOUT_MS / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); // 释放传输链路 if (err ! ESP_OK) { ESP_LOGE(TAG, I2C send data failed: %s, esp_err_to_name(err)); } return err; } // I2C 接收数据函数主设备从从设备接收数据 esp_err_t i2c_receive_data(uint8_t addr, uint8_t *data, size_t len) { if (data NULL || len 0) { ESP_LOGE(TAG, Invalid receive data buffer); return ESP_ERR_INVALID_ARG; } i2c_cmd_handle_t cmd i2c_cmd_link_create(); i2c_master_start(cmd); // 起始信号 i2c_master_write_byte(cmd, (addr 1) | I2C_MASTER_READ, true); // 从设备地址读标志 // 接收数据最后一个字节无需应答 if (len 1) { i2c_master_read(cmd, data, len - 1, I2C_MASTER_ACK); // 前n-1个字节发送应答 } i2c_master_read_byte(cmd, data[len - 1], I2C_MASTER_NACK); // 最后1个字节不发送应答 i2c_master_stop(cmd); // 停止信号 // 执行传输 esp_err_t err i2c_master_cmd_begin(I2C_NUM, cmd, I2C_TIMEOUT_MS / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); // 释放传输链路 if (err ! ESP_OK) { ESP_LOGE(TAG, I2C receive data failed: %s, esp_err_to_name(err)); } return err; } // BH1750 初始化函数 void bh1750_init(void) { uint8_t cmd BH1750_POWER_ON; i2c_send_data(BH1750_ADDR, cmd, 1); // 上电 vTaskDelay(pdMS_TO_TICKS(10)); // 等待上电稳定 cmd BH1750_RESET; i2c_send_data(BH1750_ADDR, cmd, 1); // 重置 vTaskDelay(pdMS_TO_TICKS(10)); cmd BH1750_CONT_H; i2c_send_data(BH1750_ADDR, cmd, 1); // 配置为连续高分辨率模式 vTaskDelay(pdMS_TO_TICKS(120)); // 等待首次采样完成120ms ESP_LOGI(TAG, BH1750 initialized successfully); } // 读取BH1750 光照强度数据单位lx float bh1750_read_light(void) { uint8_t data[2]; // 读取2字节数据BH1750输出为16位数据 esp_err_t err i2c_receive_data(BH1750_ADDR, data, 2); if (err ! ESP_OK) { return -1.0f; // 读取失败返回错误值 } // 转换数据BH1750数据格式高8位低8位分辨率1lx uint16_t light_raw (data[0] 8) | data[1]; float light_lx light_raw / 1.2f; // 转换为实际光照强度 return light_lx; } void app_main(void) { // 初始化I2C i2c_init(); // 初始化BH1750 bh1750_init(); // 主任务循环读取光照强度打印到串口 while (1) { float light bh1750_read_light(); if (light 0) { ESP_LOGI(TAG, Light intensity: %.1f lx, light); } else { ESP_LOGE(TAG, Read light intensity failed); } vTaskDelay(pdMS_TO_TICKS(1000)); // 每秒读取一次 } }五、测试步骤与问题排查5.1 测试步骤按接线方案连接ESP32与BH1750传感器若布线较长在SDA、SCL引脚接10KΩ上拉电阻。将代码复制到ESP-IDF项目编译并烧录固件。打开串口监视器波特率115200观察日志每秒会打印一次光照强度数据遮挡/照射传感器数据会随之变化。5.2 常见问题问题1读取数据失败 → 检查传感器地址是否正确ADDR引脚接线、SDA/SCL引脚接线是否反接、上拉电阻是否添加。问题2数据波动过大 → 环境光照不稳定、传感器接触不良、I2C总线存在干扰添加屏蔽线。问题3I2C初始化失败 → 引脚被其他外设占用、I2C驱动安装参数错误查看ESP_LOG日志排查。六、实战拓展1. 多从设备通信在同一I2C总线上连接多个I2C设备如BH1750光照传感器AHT10温湿度传感器通过不同设备地址区分实现多传感器数据采集。2. 从模式配置将ESP32配置为I2C从设备接收其他主设备如STM32的指令实现数据交互。3. 高速模式将I2C传输速率提升到400KHz快速模式适用于需要更快传输速率的场景如EEPROM读写。第四篇ESP32 ADC实战——模拟信号采集适配传感器数据读取一、ADC核心原理极简版ADC模数转换器的核心功能是将模拟信号连续变化的电压信号转换为数字信号离散的二进制数据供单片机处理。在嵌入式开发中ADC常用于采集传感器的模拟信号如温度、湿度、压力、光照等是连接模拟世界与数字世界的关键模块。ESP32-S3 内置2个12位ADC控制器ADC1、ADC2支持多达20个ADC通道采样范围0~3.3V默认采样速率最高可达1MSPS每秒100万次采样支持单次采样、连续采样、DMA采样可满足不同场景的采集需求。核心参数分辨率12位默认可配置为9~12位分辨率越高采集精度越高12位分辨率可区分4096个等级。采样范围0~3.3V默认可通过分压电路扩展到更高电压如0~5V、0~12V。采样速率可配置最高1MSPS速率越高CPU占用越高。注意ESP32的ADC引脚不能直接输入超过3.3V的电压否则会烧毁芯片若需采集超过3.3V的信号需添加分压电路。二、硬件准备ESP32-S3-N16R8 开发板电位器10KΩ模拟可变模拟信号杜邦线 3根VCC、GND、信号输出面包板、3.3V电源说明用电位器模拟传感器的模拟信号转动电位器输出电压会在0~3.3V之间变化ADC采集该电压并转换为数字信号。三、接线方案ESP32 ↔ 电位器ESP32 引脚ADC1电位器引脚功能说明3.3V一端给电位器供电GND另一端共地GPIO36ADC1_CH0中间引脚采集电位器输出的模拟电压信号说明ESP32-S3的ADC1通道CH0~CH7可自由使用ADC2通道部分引脚与WiFi功能冲突若使用WiFi建议优先使用ADC1。四、纯C语言实战代码ESP-IDF实现功能初始化ADC1通道单次采样电位器输出的模拟电压将采集到的数字信号转换为实际电压值打印到串口代码带英文注释可直接复制编译。#include driver/adc.h #include esp_log.h #include freertos/FreeRTOS.h #include freertos/task.h #include stdio.h static const char *TAG adc_demo; // ADC 配置参数 #define ADC_UNIT ADC_UNIT_1 // 使用ADC1 #define ADC_CHANNEL ADC_CHANNEL_0 // 使用ADC1_CH0GPIO36 #define ADC_WIDTH ADC_WIDTH_BIT_12 // 12位分辨率 #define ADC_ATTEN ADC_ATTEN_DB_11 // 衰减11dB采样范围0~3.3V #define ADC_SAMPLE_CNT 10 // 采样次数取平均值提高精度 // ADC 初始化函数 void adc_init(void) { // ADC 通道配置 adc_config_t adc_config { .bit_width ADC_WIDTH, // 分辨率 .atten ADC_ATTEN, // 衰减决定采样范围 .unit ADC_UNIT // ADC单元 }; // 配置ADC通道 adc_channel_config(ADC_UNIT, adc_config, ADC_CHANNEL); // 校准ADC提高采集精度可选 esp_err_t err adc_calibrate(ADC_UNIT, ADC_CHANNEL); if (err ! ESP_OK) { ESP_LOGW(TAG, ADC calibration failed: %s, esp_err_to_name(err)); } else { ESP_LOGI(TAG, ADC calibrated successfully); } ESP_LOGI(TAG, ADC initialized successfully); } // ADC 单次采样函数返回采样的数字值 uint16_t adc_single_sample(void) { uint16_t adc_raw 0; // 单次采样 esp_err_t err adc_read(ADC_UNIT, ADC_CHANNEL, adc_raw); if (err ! ESP_OK) { ESP_LOGE(TAG, ADC read failed: %s, esp_err_to_name(err)); return 0; } return adc_raw; } // ADC 多次采样取平均值提高采集精度 uint16_t adc_average_sample(uint8_t sample_cnt) { uint32_t adc_sum 0; for (uint8_t i 0; i sample_cnt; i) { adc_sum adc_single_sample(); vTaskDelay(pdMS_TO_TICKS(10)); // 每次采样间隔10ms } return adc_sum / sample_cnt; } // 数字值转换为实际电压值0~3.3V float adc_raw_to_voltage(uint16_t adc_raw) { // 12位分辨率最大值4095对应3.3V return (float)adc_raw * 3.3f / 4095.0f; } void app_main(void) { // 初始化ADC adc_init(); // 主任务循环采样打印电压值 while (1) { // 多次采样取平均值 uint16_t adc_raw adc_average_sample(ADC_SAMPLE_CNT); // 转换为电压值 float voltage adc_raw_to_voltage(adc_raw); // 打印结果 ESP_LOGI(TAG, ADC Raw: %d, Voltage: %.2f V, adc_raw, voltage); vTaskDelay(pdMS_TO_TICKS(500)); // 每500ms采样一次 } }五、测试步骤与问题排查5.1 测试步骤按接线方案连接ESP32与电位器确保电位器中间引脚连接到GPIO36。将代码复制到ESP-IDF项目编译并烧录固件。打开串口监视器波特率115200转动电位器观察日志ADC原始值和电压值会随之变化原始值0~4095电压值0~3.3V。5.2 常见问题问题1采样值不变 → 电位器接线错误、ADC通道配置错误、引脚接触不良。问题2采样值波动过大 → 未进行多次采样取平均值、环境干扰添加滤波电容、ADC未校准。问题3采样值超出范围 → 衰减配置错误如未配置衰减采样范围仅0~1.1V、输入电压超过3.3V。六、实战拓展1. 连续采样与DMA启用ADC连续采样和DMA传输实现高速、批量数据采集减少CPU占用适用于高频采集场景如音频采集。2. 分压电路扩展通过两个电阻组成分压电路将0~5V、0~12V等高压信号转换为0~3.3V实现高压信号采集。3. 传感器采集将电位器替换为模拟传感器