ESP32-S3 双核下 ADC 连续采样与 Wi-Fi 任务共存时采样抖动来源与缓解方法
在 ESP32-S3 上做音频采集、电能质量分析或振动监测时,很多开发者会遇到一个棘手问题:单独跑 ADC 连续采样时数据很稳,一旦开启 Wi-Fi,采样间隔就开始“呼吸”——时而准时,时而延迟几十甚至几百微秒。这种抖动(jitter)会直接破坏 FFT 相位、导致音频爆音或计量误差。本文从原理到代码,帮你定位并缓解这个问题。
一、抖动从哪里来?
ESP32-S3 是双核 Xtensa LX7,ADC 模块通过 SAR ADC 和 数字控制器(RTC/ DIG ADC) 工作。连续采样通常有两种方式:
-
软件轮询/定时器触发:CPU 读
adc1_get_raw(),受任务调度影响大。 - DMA 连续采样:ADC 按固定时钟写入内存,CPU 只处理数据,抖动最小。
当 Wi-Fi 开启后,抖动主要来自四个层面:
- Wi-Fi 任务抢占:Wi-Fi 协议栈运行在 CPU0(PRO_CPU),优先级高于普通应用任务,会打断 ADC 读取任务。
- 中断延迟:Wi-Fi 中断、蓝牙中断、定时器中断同时触发时,ADC 相关中断被推迟。
- Cache 与总线争用:Wi-Fi 收发数据经过 Cache 和 PSRAM,与 ADC DMA 争抢内部总线。
- 电源噪声:Wi-Fi 射频突发使电源纹波增大,影响 ADC 参考电压,表现为采样值抖动。
关键结论:纯软件轮询在 Wi-Fi 下几乎无法消除抖动;要稳定,必须用 DMA + 双核隔离 + 合理中断优先级。
二、配置步骤:让 ADC 采样与 Wi-Fi 和平共处
1. 使用 ADC DMA 连续采样模式
ESP-IDF 提供 adc_continuous 驱动,底层用 DMA,采样时钟由硬件产生,不受 CPU 调度影响。
#include "esp_adc/adc_continuous.h"
#define ADC_UNIT ADC_UNIT_1
#define ADC_CHANNEL ADC_CHANNEL_0
#define SAMPLE_RATE_HZ 16000
#define BUF_SIZE 1024
static adc_continuous_handle_t adc_handle;
void adc_dma_init(void)
{
adc_continuous_handle_cfg_t handle_cfg = {
.max_store_buf_size = BUF_SIZE * 4,
.conv_frame_size = BUF_SIZE,
};
ESP_ERROR_CHECK(adc_continuous_new_handle(&handle_cfg, &adc_handle));
adc_digi_pattern_config_t pattern = {
.atten = ADC_ATTEN_DB_12,
.channel = ADC_CHANNEL,
.unit = ADC_UNIT,
.bit_width = ADC_BITWIDTH_12,
};
adc_continuous_config_t dig_cfg = {
.sample_freq_hz = SAMPLE_RATE_HZ,
.conv_mode = ADC_CONV_SINGLE_UNIT_1,
.format = ADC_DIGI_OUTPUT_FORMAT_TYPE2,
.pattern_num = 1,
.adc_pattern = &pattern,
};
ESP_ERROR_CHECK(adc_continuous_config(adc_handle, &dig_cfg));
ESP_ERROR_CHECK(adc_continuous_start(adc_handle));
}
2. 双核任务绑定:ADC 处理放 CPU1
Wi-Fi 默认在 CPU0,把 ADC 数据搬运和处理任务固定到 CPU1,避免被 Wi-Fi 任务抢占。
void adc_reader_task(void *arg)
{
uint8_t buf[BUF_SIZE];
uint32_t ret_len;
while (1) {
esp_err_t ret = adc_continuous_read(adc_handle, buf, BUF_SIZE,
&ret_len, 100);
if (ret == ESP_OK) {
// 这里做滤波、FFT 或打包,尽量短小
process_adc_data(buf, ret_len);
}
}
}
// 创建任务时绑定到 CPU1
xTaskCreatePinnedToCore(adc_reader_task, "adc_reader", 4096,
NULL, 10, NULL, 1);
3. 中断优先级与 Wi-Fi 配置
- 将 ADC DMA 完成中断优先级设为中等(如 2),不要高于 Wi-Fi 中断(通常 1~3),避免破坏 Wi-Fi 实时性。
- 在
menuconfig中关闭不必要的 Wi-Fi 省电模式:Component config → Wi-Fi → WiFi sleep → None,减少射频突发引起的电源波动。 - 若使用 PSRAM,尽量把 ADC DMA 缓冲区放在内部 SRAM,减少总线争用。
// 将 DMA 缓冲区放到内部 RAM
static uint8_t adc_dma_buf[BUF_SIZE] __attribute__((aligned(4)));
三、完整最小示例(ADC DMA + Wi-Fi 共存)
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_adc/adc_continuous.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"
#define BUF_SIZE 1024
static adc_continuous_handle_t adc_handle;
static void adc_task(void *arg)
{
uint8_t buf[BUF_SIZE];
uint32_t out_len;
while (1) {
if (adc_continuous_read(adc_handle, buf, BUF_SIZE, &out_len, 100) == ESP_OK) {
// 仅做轻量处理,避免长时间占用 CPU1
volatile uint32_t sum = 0;
for (int i = 0; i < out_len; i++) sum += buf[i];
}
}
}
void app_main(void)
{
// 1. NVS + Wi-Fi 初始化
nvs_flash_init();
esp_netif_init();
esp_event_loop_create_default();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_start();
// 2. ADC DMA 初始化
adc_continuous_handle_cfg_t hcfg = {
.max_store_buf_size = BUF_SIZE * 4,
.conv_frame_size = BUF_SIZE,
};
adc_continuous_new_handle(&hcfg, &adc_handle);
adc_digi_pattern_config_t pat = {
.atten = ADC_ATTEN_DB_12, .channel = ADC_CHANNEL_0,
.unit = ADC_UNIT_1, .bit_width = ADC_BITWIDTH_12,
};
adc_continuous_config_t ccfg = {
.sample_freq_hz = 16000, .conv_mode = ADC_CONV_SINGLE_UNIT_1,
.format = ADC_DIGI_OUTPUT_FORMAT_TYPE2,
.pattern_num = 1, .adc_pattern = &pat,
};
adc_continuous_config(adc_handle, &ccfg);
adc_continuous_start(adc_handle);
// 3. 采样任务绑定 CPU1
xTaskCreatePinnedToCore(adc_task, "adc", 4096, NULL, 10, NULL, 1);
}
四、注意事项与实测建议
-
不要用
adc1_get_raw()做高速连续采样:它依赖软件触发,Wi-Fi 下抖动可达数百微秒。 - 采样率不要超过 ADC 实际能力:ESP32-S3 SAR ADC 在 12 位下最高约 83.3 kSPS,但多通道轮询会下降,建议单通道 ≤ 40 kSPS。
-
Wi-Fi 省电模式影响巨大:
WIFI_PS_MIN_MODEM或WIFI_PS_MAX_MODEM会引起周期性射频唤醒,建议关闭或改用WIFI_PS_NONE。 - 电源去耦:在 ADC 参考引脚和电源脚加 10 µF + 0.1 µF,Wi-Fi 突发时纹波可降低 30% 以上。
- 用 GPIO 翻转测抖动:在 ADC DMA 回调里翻转一个 GPIO,用示波器观察周期稳定性,比打印日志更可靠。
- 若仍不够稳:可外接专用 ADC(如 ADS131M08)通过 SPI/I2S 采样,ESP32-S3 只做数据搬运,彻底隔离 Wi-Fi 干扰。
通过 DMA 硬件采样、双核任务隔离、合理中断优先级和电源优化,ESP32-S3 在 Wi-Fi 全速收发时,ADC 采样抖动可从数百微秒降到几微秒量级,满足大多数音频和计量应用。