ESP32 内置了 2 个 12 位 SAR ADC(ADC1 和 ADC2),支持多个模拟输入通道,可用于采集外部模拟信号(如传感器电压、电位器等)。ADC 功能的实现需通过 ESP-IDF 提供的 API 配置通道参数(如衰减、分辨率)、读取原始值并转换为实际电压。以下是详细的实现步骤和代码示例。

一、ESP32 ADC 基础特性

  • 通道数量:ADC1 有 8 个通道(GPIO32~GPIO39),ADC2 有 10 个通道(GPIO0、GPIO2、GPIO4、GPIO12GPIO15、GPIO25GPIO27);
  • 分辨率:支持 9~12 位(默认 12 位,分辨率越高,精度越高但采样速度略慢);
  • 输入电压范围:通过“衰减(attenuation)”配置,默认 0dB 对应 0~1.1V,最高 11dB 可支持 0~3.9V(但 ESP32 GPIO 最大耐压 3.3V,实际输入需≤3.3V);
  • 限制:ADC2 与 Wi-Fi 共用硬件资源,启用 Wi-Fi 时可能无法使用 ADC2 通道(建议优先使用 ADC1 通道)。

二、ADC 配置核心步骤

  1. 选择通道:根据硬件连接选择 ADC1 或 ADC2 的通道(优先 ADC1 以避免 Wi-Fi 冲突);
  2. 配置衰减:根据输入电压范围设置衰减(如测量 0~3.3V 信号需用 11dB 衰减);
  3. 配置分辨率:设置采样分辨率(如 12 位,对应 0~4095 的原始值);
  4. 校准(可选但推荐):使用 ESP32 内置的校准功能,提高电压转换精度;
  5. 读取并转换:读取原始 ADC 值,通过校准参数转换为实际电压。

三、完整代码实现(基于 ESP-IDF v4.4+)

以下代码以“读取 ADC1 通道 0(GPIO36)的电压”为例,实现连续采样并打印结果,包含校准功能。

1. 头文件与宏定义

#include <stdio.h>
#include "esp_adc_cal.h"
#include "driver/adc.h"
#include "esp_log.h"

// 日志标签
static const char *TAG = "esp32_adc";

// ADC 配置参数
#define ADC_CHANNEL    ADC1_CHANNEL_0  // 选择 ADC1 通道 0(对应 GPIO36)
#define ADC_ATTEN      ADC_ATTEN_DB_11 // 衰减 11dB(支持 0~3.9V 输入,实际≤3.3V)
#define ADC_WIDTH      ADC_WIDTH_BIT_12 // 12 位分辨率(0~4095)

// 校准类型(优先使用 eFuse 校准,无则用默认参数)
#define DEFAULT_VREF   1100  // 默认参考电压(mV),若无 eFuse 校准则使用
static esp_adc_cal_characteristics_t adc_chars;

2. ADC 初始化函数

初始化 ADC 通道,配置衰减、分辨率,并进行校准:

static void adc_init(void) {
    // 检查是否支持 eFuse 校准(提高精度)
    esp_adc_cal_value_t val_type = esp_adc_cal_characterize(
        ADC_UNIT_1,          // ADC 单元(ADC1)
        ADC_ATTEN,           // 衰减配置
        ADC_WIDTH,           // 分辨率
        DEFAULT_VREF,        // 默认参考电压
        &adc_chars           // 校准参数结构体
    );
    // 打印校准类型
    if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
        ESP_LOGI(TAG, "使用 eFuse VREF 校准");
    } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
        ESP_LOGI(TAG, "使用 eFuse 两点校准");
    } else {
        ESP_LOGI(TAG, "使用默认参考电压(%dmV)", DEFAULT_VREF);
    }

    // 配置 ADC 通道(ADC1 通道 0)
    adc1_config_width(ADC_WIDTH);                  // 设置分辨率
    adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN); // 设置衰减
}

3. ADC 读取与电压转换函数

读取原始 ADC 值,并通过校准参数转换为实际电压(mV):

static uint32_t adc_read_voltage(void) {
    // 读取原始 ADC 值(多次采样取平均,减少噪声)
    uint32_t adc_raw = 0;
    const int sample_count = 10;  // 采样次数
    for (int i = 0; i < sample_count; i++) {
        adc_raw += adc1_get_raw(ADC_CHANNEL);  // 读取 ADC1 原始值
    }
    adc_raw /= sample_count;  // 平均值

    // 转换为电压(mV),使用校准后的参数
    uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_raw, &adc_chars);
    ESP_LOGI(TAG, "原始值:%d,电压:%dmV", adc_raw, voltage);
    return voltage;
}

4. 主函数调用

void app_main(void) {
    // 初始化 ADC
    adc_init();

    // 循环读取 ADC 值(间隔 1 秒)
    while (1) {
        adc_read_voltage();
        vTaskDelay(pdMS_TO_TICKS(1000));  // 延时 1 秒
    }
}

四、代码说明与关键细节

1. 通道与 GPIO 对应关系

ADC1 通道对应的 GPIO 如下(可直接使用):

ADC1 通道 GPIO 编号
ADC1_CHANNEL_0 GPIO36
ADC1_CHANNEL_1 GPIO37
ADC1_CHANNEL_2 GPIO38
ADC1_CHANNEL_3 GPIO39
ADC1_CHANNEL_4 GPIO32
ADC1_CHANNEL_5 GPIO33
ADC1_CHANNEL_6 GPIO34
ADC1_CHANNEL_7 GPIO35

2. 衰减与电压范围对应

根据输入信号电压选择衰减(确保输入≤3.3V):

衰减配置 理论测量范围(V) 实际建议范围(V)
ADC_ATTEN_DB_0 0 ~ 1.1 0 ~ 1.0
ADC_ATTEN_DB_2_5 0 ~ 1.5 0 ~ 1.4
ADC_ATTEN_DB_6 0 ~ 2.2 0 ~ 2.0
ADC_ATTEN_DB_11 0 ~ 3.9 0 ~ 3.3(GPIO 上限)

3. 校准的重要性

ESP32 的 ADC 存在一定非线性误差,通过 esp_adc_cal_characterize() 函数可利用芯片内部的 eFuse 校准数据(如 VREF 参考电压)优化转换精度:

  • 若芯片支持 eFuse 校准(多数新款 ESP32),会自动使用校准数据;
  • 若无 eFuse 数据,使用 DEFAULT_VREF(1100mV)作为参考,精度略低。

4. 噪声处理

ADC 采样易受电磁干扰,可通过以下方式降低噪声:

  • 多次采样取平均:如代码中使用 10 次采样平均值,平滑波动;
  • 硬件滤波:在输入引脚串联 100Ω 电阻 + 并联 100nF 电容,滤除高频噪声;
  • 避免强干扰源:远离 Wi-Fi 天线、电机等强干扰设备。

5. ADC2 与 Wi-Fi 的冲突处理

若需使用 ADC2 通道且同时启用 Wi-Fi,需在读取 ADC 前暂停 Wi-Fi,读取后恢复:

// ADC2 读取示例(需配合 Wi-Fi 暂停)
#include "esp_wifi.h"

uint32_t adc2_read_voltage(adc2_channel_t channel) {
    uint32_t adc_raw;
    // 暂停 Wi-Fi
    esp_wifi_pause();
    // 读取 ADC2 原始值
    adc2_get_raw(channel, ADC_WIDTH, &adc_raw);
    // 恢复 Wi-Fi
    esp_wifi_resume();
    // 转换为电压
    return esp_adc_cal_raw_to_voltage(adc_raw, &adc_chars);
}

五、常见问题与解决方案

  1. 读数为 0 或 4095(最大值)

    • 检查输入电压是否超出所选衰减的范围(如 0dB 衰减接 3.3V 会导致读数饱和);
    • 确认 GPIO 引脚与 ADC 通道对应正确。
  2. 读数波动大

    • 增加采样次数取平均;
    • 增加硬件滤波电路;
    • 远离干扰源。
  3. ADC2 读数失败(返回 -1)

    • 检查是否启用了 Wi-Fi,需暂停 Wi-Fi 后再读取;
    • 确认 ADC2 通道未被其他外设占用(如某些 GPIO 同时支持其他功能)。

总结

ESP32 实现 ADC 功能的核心是:配置通道参数(衰减、分辨率)→ 校准 → 读取并转换电压。通过优先使用 ADC1 通道、合理设置衰减、增加采样平均,可实现稳定可靠的模拟信号采集,适用于传感器数据读取、电压监测等场景。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐