ESP-IDF保姆级入门26|ADC与DAC模拟接口全解:多通道采样/硬件校准/DMA传输/波形输出/抗干扰设计,掌握模拟信号精准采集与控制
专栏前言
上一篇我们掌握了CAN总线工业通信,实现了强干扰场景下的高可靠多设备组网,解决了设备间数字通信的问题。
但嵌入式系统最终要和物理世界交互,传感器的电压信号、温度电流模拟量、音频波形、模拟控制输出,都需要 模拟-数字转换器(ADC) 和 数字-模拟转换器(DAC) 作为桥梁。
很多新手做模拟采集总会遇到读数不准、漂移大、干扰多、输出带不动的问题,本质是不了解模拟外设的特性、校准方法和抗干扰设计。
ESP32集成了丰富的模拟外设:2路12位逐次逼近型ADC,支持最多20个通道、多种衰减量程、硬件校准、DMA连续采样;2路8位DAC,支持可调电压输出、DMA波形生成,配合定时器可实现任意波形输出。
本篇一次性讲透ESP-IDF下的ADC与DAC开发:
模拟接口核心原理 → ADC衰减与量程配置 → 硬件校准与精度优化 → 多通道单次采样实战 → DMA连续高频采样 → DAC电压输出与波形生成 → 抗干扰与可靠性设计 → 最佳实践与踩坑汇总
全程配可编译源码+架构图解,零基础也能实现工业级精准的模拟信号采集与输出。
下面是本篇完整的学习路线图:
一、模拟接口核心基础
1. 什么是ADC与DAC
- ADC(Analog to Digital Converter):将连续变化的模拟电压信号转换为离散的数字量,是采集传感器、电压、电流等模拟信号的核心入口。
- DAC(Digital to Analog Converter):将数字量转换为连续的模拟电压信号,用于模拟控制、波形生成、音量调节、基准输出等场景。
2. ESP32模拟外设资源总览
ESP32内置2个ADC控制器和2个DAC通道,各自特性与适用场景不同:
| 外设 | 分辨率 | 通道数 | 典型量程 | 最高采样率 | 核心说明 |
|---|---|---|---|---|---|
| ADC1 | 12位(可配置9/10/11位) | 8通道(GPIO32~GPIO39) | 0~3.3V(配衰减) | 200ksps | 精度稳定,支持DMA,与WiFi无冲突,优先选用 |
| ADC2 | 12位 | 10通道 | 0~3.3V(配衰减) | 200ksps | 与WiFi共享资源,WiFi启用时使用受限 |
| DAC1 | 8位 | 1通道(GPIO25) | 0 | - | 输出阻抗高,适合高阻负载,需缓冲后驱动功率电路 |
| DAC2 | 8位 | 1通道(GPIO26) | 0~VDD3P3 | - | 特性同DAC1 |
3. 关键概念
- 分辨率:数字量的位数,决定最小可分辨电压。12位ADC对应4096级,3.3V量程下约0.8mV/步;8位DAC对应256级,约13mV/步。
- 量程:可测量或输出的电压范围,ADC通过内部衰减器扩大量程。
- 采样率:每秒完成采样的次数,根据奈奎斯特定理,需至少为信号最高频率的2倍才能还原波形。
- 量化误差:模拟量数字化的固有误差,为±1LSB(最低有效位),分辨率越高误差越小。

【配图1:ESP32模拟外设架构与通道分布图】
配图说明:展示芯片内部模拟前端、ADC1/ADC2控制器、DMA控制器、DAC模块的连接关系;标注各通道对应的GPIO引脚,直观呈现模拟外设的硬件架构。
下面是 ESP32 模拟外设的整体架构图:
二、ADC详解与配置
1. 衰减配置与量程选择
ESP32的ADC输入并非直接支持0~3.3V满幅,内部集成了可编程衰减器,不同衰减对应不同的满量程电压,这是新手最容易踩的坑。
| 衰减配置 | 满量程电压 | 推荐线性区间 | 选型说明 |
|---|---|---|---|
| ADC_ATTEN_DB_0 | ~1.1V | 0~1.0V | 无衰减,精度最高,适合小信号精密采集 |
| ADC_ATTEN_DB_2_5 | ~1.5V | 0~1.4V | 2.5dB衰减,中小量程场景 |
| ADC_ATTEN_DB_6 | ~2.2V | 0~2.0V | 6dB衰减,中量程场景 |
| ADC_ATTEN_DB_11 | ~3.9V | 0~3.3V | 11dB衰减,最常用,覆盖3.3V全量程 |
工程提示:实际满量程受芯片供电电压影响,3.3V供电下11dB衰减最大可测约3.3V;ADC输入绝对不能超过3.6V,否则会永久损坏芯片。
2. 三种采样模式
- 单次采样:软件触发一次转换一次,适合低速、低频信号采集,代码简单、占用资源少。
- 连续采样:ADC自动连续转换,通过DMA搬运数据,适合高频波形采集,CPU占用极低。
- 扫描模式:自动依次转换多个配置好的通道,适合多传感器同时采集,统一调度。
三种采样模式的选择流程如下:
3. 硬件校准原理
ESP32的ADC存在固有偏移误差和增益误差,出厂时每个芯片的校准参数已烧录在eFuse中,可读取参数进行硬件校正,无需额外校准电路即可将精度从±5%提升到±1%左右。
- 两点校准:基于零点和满量程点的参数,同时修正偏移和增益误差。
- Vref校准:基于内部参考电压的校准值,修正增益误差。
4. 核心API
#include "driver/adc.h"
#include "esp_adc_cal.h"
// 配置ADC分辨率
esp_err_t adc1_config_width(adc_bits_width_t width);
// 配置指定通道的衰减
esp_err_t adc1_config_channel_atten(adc1_channel_t channel, adc_atten_t atten);
// 读取通道原始采样值
int adc1_get_raw(adc1_channel_t channel);
// 初始化校准特征,读取eFuse参数
esp_adc_cal_value_t esp_adc_cal_characterize(
adc_unit_t adc_num,
adc_atten_t atten,
adc_bits_width_t bit_width,
uint32_t default_vref,
esp_adc_cal_characteristics_t *chars
);
// 原始值转换为真实电压(单位:mV)
uint32_t esp_adc_cal_raw_to_voltage(uint32_t raw, const esp_adc_cal_characteristics_t *chars);

【配图2:ADC衰减量程与精度特性对比图】
配图说明:用条形图对比四种衰减的满量程电压;曲线展示不同量程下的线性度,标注推荐工作区间,直观呈现衰减配置的选型依据。
三、实战1:多通道ADC采集与硬件校准
实现功能:ADC1的两个通道采集模拟电压,使用eFuse硬件校准,多次采样取平均降噪,输出真实电压值。
完整可编译源码
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"
#include "esp_log.h"
#define TAG "ADC_DEMO"
#define ADC_UNIT ADC_UNIT_1
#define ADC_WIDTH ADC_WIDTH_BIT_12 // 12位分辨率
#define ADC_ATTEN ADC_ATTEN_DB_11 // 11dB衰减,覆盖0~3.3V
#define CHAN_1 ADC1_CHANNEL_6 // GPIO34
#define CHAN_2 ADC1_CHANNEL_7 // GPIO35
#define SAMPLE_CNT 16 // 单次平均采样次数
// 校准特征结构体
static esp_adc_cal_characteristics_t adc_chars;
// ADC初始化与硬件校准
static void adc_init_calibrate(void)
{
// 1. 配置分辨率
adc1_config_width(ADC_WIDTH);
// 2. 配置通道衰减
adc1_config_channel_atten(CHAN_1, ADC_ATTEN);
adc1_config_channel_atten(CHAN_2, ADC_ATTEN);
// 3. 读取eFuse校准参数
esp_adc_cal_value_t cal_type = esp_adc_cal_characterize(
ADC_UNIT,
ADC_ATTEN,
ADC_WIDTH,
1100, // 默认参考电压1100mV
&adc_chars
);
if(cal_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
ESP_LOGI(TAG, "校准方式:eFuse Vref");
} else if(cal_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
ESP_LOGI(TAG, "校准方式:eFuse两点校准");
} else {
ESP_LOGW(TAG, "无出厂校准,使用默认参考电压");
}
}
// 读取指定通道的校准后电压(单位:mV)
static uint32_t adc_read_voltage(adc1_channel_t chan)
{
uint32_t raw_sum = 0;
// 多次采样取平均,抑制随机噪声
for(int i = 0; i < SAMPLE_CNT; i++) {
raw_sum += adc1_get_raw(chan);
}
uint32_t raw_avg = raw_sum / SAMPLE_CNT;
// 转换为真实电压
return esp_adc_cal_raw_to_voltage(raw_avg, &adc_chars);
}
void adc_sample_task(void *pvParameters)
{
while(1) {
uint32_t v1 = adc_read_voltage(CHAN_1);
uint32_t v2 = adc_read_voltage(CHAN_2);
ESP_LOGI(TAG, "通道1:%4d mV | 通道2:%4d mV", v1, v2);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void app_main(void)
{
adc_init_calibrate();
xTaskCreate(adc_sample_task, "adc_task", 4096, NULL, 3, NULL);
ESP_LOGI(TAG, "ADC初始化与校准完成");
}
关键说明
- 16次采样取平均是工业级最常用的软件降噪方法,有效减小随机噪声,对采样速度影响极小。
- 优先使用ADC1,ADC2与WiFi共享资源,开启WiFi后会冲突,无法正常使用。
- 输入信号尽量落在推荐线性区间,接近满量程时线性度下降,误差增大。
多通道 ADC 采集与硬件校准的完整流程如下:
四、进阶:DMA连续高频采样
适合音频、振动、脉冲等高频波形采集场景,ADC自动连续转换,DMA硬件自动搬运数据到内存,全程无需CPU干预,效率极高。
工作原理
- ADC配置为连续转换模式,转换完成自动触发下一次采样
- DMA控制器自动将ADC数据寄存器的值搬运到RAM缓冲区
- 采用双缓冲区乒乓机制:处理缓冲区A的数据时,ADC继续往缓冲区B采样,交替切换,不丢失任何采样点
- 缓冲区满触发中断,通知CPU批量处理数据
DMA 双缓冲乒乓机制的工作流程如下:
适用场景
- 音频采集、声纹检测
- 振动信号分析、脉冲计数
- 高速传感器数据采集
- 示波器、逻辑分析仪类应用
工程提示:连续采样需配合合理的采样率和缓冲区大小,避免数据溢出和处理不及时。
五、DAC详解与波形输出
1. DAC核心特性
- 8位分辨率,共256级输出电压
- 输出范围:0 ~ VDD3P3 - 0.1V,3.3V供电下约0~3.2V
- 输出阻抗高(约25kΩ),不能直接驱动低阻负载,必须加运放缓冲
- 支持DMA连续输出,配合定时器可生成任意频率的波形
2. 核心API
#include "driver/dac.h"
// 使能DAC通道输出
esp_err_t dac_output_enable(dac_channel_t channel);
// 输出指定数字值,范围0~255
esp_err_t dac_output_voltage(dac_channel_t channel, uint8_t dac_value);
// 禁用DAC输出
esp_err_t dac_output_disable(dac_channel_t channel);
实战2:DAC电压输出与正弦波生成
实现功能:DAC1输出静态电压,预计算正弦波查找表,通过软件定时更新模拟连续波形输出。
#include <stdio.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/dac.h"
#include "esp_log.h"
#define TAG "DAC_DEMO"
#define DAC_CHAN DAC_CHANNEL_1 // GPIO25
#define SINE_POINTS 100 // 正弦波一个周期的点数
uint8_t sine_table[SINE_POINTS];
// 生成正弦波查找表
static void generate_sine_table(void)
{
for(int i = 0; i < SINE_POINTS; i++) {
float angle = 2 * 3.1415926f * i / SINE_POINTS;
// 正弦值归一化到0~1,再映射到0~255
float value = (sinf(angle) + 1.0f) / 2.0f;
sine_table[i] = (uint8_t)(value * 255.0f);
}
}
// 静态电压输出测试
static void dac_static_test(void)
{
dac_output_enable(DAC_CHAN);
dac_output_voltage(DAC_CHAN, 128); // 输出约1.65V(中间值)
ESP_LOGI(TAG, "输出静态电压:约1.65V");
}
// 连续正弦波输出
void dac_wave_task(void *pvParameters)
{
uint32_t idx = 0;
while(1) {
dac_output_voltage(DAC_CHAN, sine_table[idx]);
idx = (idx + 1) % SINE_POINTS;
// 延时决定波形频率,100点+100us延时约100Hz
esp_rom_delay_us(100);
}
}
void app_main(void)
{
generate_sine_table();
dac_static_test();
xTaskCreate(dac_wave_task, "dac_task", 4096, NULL, 4, NULL);
ESP_LOGI(TAG, "DAC波形输出启动");
}
关键说明
- 软件延时输出仅适合低频演示,高频连续波形需配合DMA+定时器实现,CPU占用更低、频率更精准。
- DAC输出必须加电压跟随器(运放缓冲)才能驱动低阻负载,直接带载会导致电压大幅偏低。
- 8位分辨率适合一般控制类场景,高精度需求需外接12位以上的独立DAC芯片。

【配图3:DAC波形生成与DMA传输流程图】
配图说明:展示查找表→DMA控制器→DAC→模拟输出的完整数据流,标注定时器触发、双缓冲乒乓操作,直观呈现连续波形生成的工作机制。
六、工程化:抗干扰与精度优化
1. 硬件抗干扰设计
- 电源滤波:模拟电源引脚就近加100nF去耦电容+10μF电解电容,滤除电源噪声。
- 输入滤波:ADC输入端加RC低通滤波,截止频率根据采样率选择,滤除高频干扰。
- 布线规范:模拟走线与数字走线分开,避免平行走线,减少串扰;模拟地与数字地单点共地,避免地环流。
- 过压保护:ADC输入端串联限流电阻+钳位二极管,防止过压损坏芯片。
- 输出缓冲:DAC输出加运放电压跟随器,降低输出阻抗,提高带载能力和稳定性。
2. 软件精度优化
- 多次平均:16/32次采样取平均,抑制随机噪声,性价比最高。
- 滑动滤波:一阶低通滤波算法,平滑慢变信号,消除突发干扰。
- 定期校准:系统空闲时执行零点和量程校准,修正温漂和时漂。
- 异常校验:对采样值做范围判断,超出合理范围判定为传感器故障或开路。
3. 可靠性设计
- 关键模拟参数增加超时检测,长时间无更新触发告警。
- ADC和DAC外设初始化后做自检,验证外设工作正常。
- 工业级场景增加外部基准电压源,替代内部基准,提升长期稳定性。
七、工程最佳实践
- ADC选型原则
- 优先使用ADC1,避免与WiFi冲突;必须用ADC2时要和WiFi分时复用。
- 在满足量程的前提下,尽量选小的衰减,精度更高。
- 采样率按信号最高频率的5~10倍设计,留足余量。
- 输入信号尽量控制在线性区间,不要接近满量程。
- DAC使用原则
- 静态控制场景直接写寄存器,动态波形用DMA输出。
- 必须加缓冲运放,禁止直接驱动低阻负载。
- 8位分辨率满足一般控制需求,高精度场景外接高位DAC。
- 通用设计规范
- 模拟电路远离高频数字电路、电源电路、功率电路。
- 敏感模拟信号用屏蔽线,屏蔽层单端接地。
- 量产产品增加校准工序,修正个体差异。
八、新手高频踩坑汇总
- ADC读数不准、偏差很大
- 没有配置衰减,默认0dB衰减只能测1.1V以内,超过就饱和顶格。
- 没有做硬件校准,芯片固有偏移和增益误差导致整体偏差。
- 电源噪声大,没有滤波,读数跳变严重。
- 输入信号超过量程,ADC饱和后读数保持最大值。
- ADC读数跳变、噪声大
- 没有加输入滤波,高频干扰串入采样通道。
- 采样次数太少,随机噪声没有被平均掉。
- 模拟电源和数字电源混布,电源噪声耦合到模拟通路。
- 走线太长,引入空间电磁干扰。
- DAC输出电压不对
- 直接带低阻负载,输出阻抗高导致电压被拉低。
- 供电电压不是标准3.3V,满量程随供电变化。
- 输出值超出0~255范围,数据溢出错误。
- 连续采样丢数据、错位
- 缓冲区太小,处理不及时导致新数据覆盖旧数据。
- 没有用双缓冲乒乓机制,处理数据时采样不停止。
- DMA优先级太低,被其他高优先级任务抢占。
- ADC2完全用不了
- 开启了WiFi,ADC2与WiFi共享资源,硬件冲突。
- 必须使用ADC2的场景,要在WiFi关闭时采样,采样完成再开WiFi。
下一篇预告(27篇)
ESP-IDF保姆级入门27|PWM与电机控制全解:LED PWM定时器、互补输出、死区控制、直流电机调速、步进电机驱动,掌握嵌入式电力电子控制基础,实现精准的电机驱动与功率控制!
更多推荐



所有评论(0)