十七、【ESP32全栈开发指南:ADC接口使用】

「一、概述」
ESP32集成两个12位逐次逼近型模数转换器(SARADC),共支持18个模拟输入通道:

  • 「ADC1」:8通道(GPIO32-GPIO39)

  • 「ADC2」:10通道(GPIO0、GPIO2、GPIO4、GPIO12-GPIO15、GPIO25-GPIO27)

1.1 ADC限制

  • 「ADC2与Wi-Fi冲突」:
    ADC2在Wi-Fi驱动启用时不可用,需在未启动Wi-Fi时使用。

  • 「硬件限制」:

    • 绑带引脚限制(GPIO 0/2/15)

    • ESP32 DevKitC:GPIO0被编程电路占用

    • ESP-WROVER-KIT:GPIO 0/2/4/15因外设连接不可用

1.2 ADC采样模式

模式

适用场景

单次采样模式

低频采样(如传感器读取)

连续采样模式

高频采样(需DMA支持)


「二、API说明」
关键头文件:

  • driver/adc.h
  • driver/adc_common.h
  • esp_adc_cal.h

1. 「位宽配置 – adc1_config_width()」

esp_err_t adc1_config_width(adc_bits_width_t width);
  • 「功能」:设置ADC1的采样分辨率(9~12位)

  • 「参数」:

    typedef enum {
        ADC_WIDTH_BIT_9  = 0,  // 9位(量程0~511)
        ADC_WIDTH_BIT_10 = 1,  // 10位(0~1023)
        ADC_WIDTH_BIT_11 = 2,  // 11位(0~2047)
        ADC_WIDTH_BIT_12 = 3   // 12位(0~4095,推荐)
    } adc_bits_width_t;
  • 「注意」:分辨率越高精度越高,但转换时间稍长;ADC2需在每次读取时单独设置位宽。


2. 「衰减配置 – adc1_config_channel_atten()」

esp_err_t adc1_config_channel_atten(adc1_channel_t channel, adc_atten_t atten);
  • 「功能」:配置指定通道的输入电压衰减倍数,扩展测量范围

  • 「衰减等级与量程对照」:

    衰减枚举

    量程(VDD=3.3V)

    适用场景

    ADC_ATTEN_DB_0

    0~0.8V

    精密传感器(如光敏)

    ADC_ATTEN_DB_2_5

    0~1.1V

    低电压信号

    ADC_ATTEN_DB_6

    0~1.35V

    中等电压

    ADC_ATTEN_DB_11

    0~2.6V

    电池电压(需分压)

  • 「硬件设计关键」:超出0.8V必须使用衰减,否则可能损坏ADC模块。


3. 「原始值读取 – adc1_get_raw()」

int adc1_get_raw(adc1_channel_t channel);
  • 「返回值」:12位原始数据(0~4095),需配合校准转换为实际电压

  • 「阻塞特性」:单次采样约10μs,适用低频场景(如温度采集)。


三、ADC校准核心API

1. 「校准初始化 – esp_adc_cal_characterize()」

esp_adc_cal_value_t esp_adc_cal_characterize(
    adc_unit_t unit,
    adc_atten_t atten,
    adc_bits_width_t bitwidth,
    uint32_t default_vref,
    esp_adc_cal_characteristics_t *chars
)
;
  • 「作用」:生成ADC电压特征曲线,补偿芯片间Vref差异(±100mV)

  • 「校准源优先级」:

    1. eFuse存储的两点校准值(工厂校准)

    2. eFuse的Vref参考电压

    3. 用户提供的default_vref(默认1100mV)

  • 「输出校验」:

    if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
        printf("使用eFuse两点校准\n");
    } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
        printf("使用eFuse Vref校准\n");
    } else {
        printf("使用默认Vref\n");  // 精度最低
    }

2. 「原始值转电压 – esp_adc_cal_raw_to_voltage()」

uint32_t esp_adc_cal_raw_to_voltage(
    uint32_t adc_reading,
    const esp_adc_cal_characteristics_t *chars
)
;
  • 「输入」:adc1_get_raw()的原始值 + 校准结构体

  • 「输出」:实际电压值(单位mV)

  • 「示例」:

    uint32_t voltage = esp_adc_cal_raw_to_voltage(raw_val, &adc_chars);
    printf("电压值: %dmV\n", voltage);  // 输出如 "电压值: 1234mV"

四、专用功能API

1. 「ADC2冲突处理 – adc2_get_raw()」

esp_err_t adc2_get_raw(adc2_channel_t channel, adc_bits_width_t width, int *raw_out);
  • 「Wi-Fi冲突」:返回ESP_ERR_INVALID_STATE表示被Wi-Fi占用

  • 「正确用法」:

    int raw_val;
    esp_err_t ret = adc2_get_raw(ADC2_CHANNEL_7, ADC_WIDTH_BIT_12, &raw_val);
    if (ret == ESP_ERR_INVALID_STATE) {
        // 需延迟重试或关闭Wi-Fi
    }

2. 「内部霍尔传感器 – hall_sensor_read()」

int hall_sensor_read(void);
  • 「硬件要求」:自动占用GPIO36(SVP)和GPIO39(SVN),「不可复用」
  • 「示例」:

    adc1_config_width(ADC_WIDTH_BIT_12);  // 需先配置位宽
    int hall_val = hall_sensor_read();    // 返回值受外部磁场影响

3. 「参考电压输出 – adc_vref_to_gpio()」

esp_err_t adc_vref_to_gpio(adc_unit_t unit, gpio_num_t gpio);
  • 「功能」:将内部Vref输出至GPIO(如GPIO25),方便万用表实测校准

  • 「使用场景」:当eFuse无校准值且需要高精度时,手动测量真实Vref。


五、关键实践技巧

  1. 「噪声抑制」

    • 输入引脚并联100nF陶瓷电容

    • 软件多次采样取平均(推荐64次):

      uint32_t sum = 0;
      for (int i=0; i<64; i++) {
          sum += adc1_get_raw(channel);
      }
      int avg = sum >> 6;  // 等效除以64
  2. 「分压电路设计」
    锂电池检测典型电路:

    VBAT+ ─ 20kΩ ──┬── 10kΩ ── GND
                    │
                    └─ 100nF ── GND
                    │
                    └─ GPIO35 (ADC1_CH7)
    • 分压比 = R2/(R1+R2) = 1/3,适配3.7V电池

    • 电容滤除高频干扰

「六、编程流程」

3.1 配置参数

「1. 位宽配置」

typedef enum {  
    ADC_WIDTH_BIT_9  = 0,  // 9位精度  
    ADC_WIDTH_BIT_10 = 1,  // 10位精度  
    ADC_WIDTH_BIT_11 = 2,  // 11位精度  
    ADC_WIDTH_BIT_12 = 3   // 12位精度(默认)  
} adc_bits_width_t;  

「2. 衰减系数与量程」

衰减值

量程范围 (VDD=3.3V)

ADC_ATTEN_DB_0

0 ~ 0.8V

ADC_ATTEN_DB_2_5

0 ~ 1.1V

ADC_ATTEN_DB_6

0 ~ 1.35V

ADC_ATTEN_DB_11

0 ~ 2.6V

「3. 通道映射」

// ADC1通道与GPIO对应  
ADC1_CHANNEL_0 → GPIO36  
ADC1_CHANNEL_4 → GPIO32  // 注意索引顺序  

// ADC2通道示例  
ADC2_CHANNEL_0 → GPIO4  
ADC2_CHANNEL_9 → GPIO26  

「七、应用实例」

4.1 ADC1单次采样(GPIO35)

#include <driver/adc.h>  

void read_adc1() {  
    adc1_config_width(ADC_WIDTH_BIT_12);  
    adc1_config_channel_atten(ADC1_CHANNEL_7, ADC_ATTEN_DB_0);  
    int val = adc1_get_raw(ADC1_CHANNEL_7);  // 读取GPIO35  
    printf("ADC1 Value: %d\n", val);  
}  

4.2 ADC2单次采样(GPIO27)

#include <driver/adc.h>  

void read_adc2() {  
    int raw_val;  
    adc2_config_channel_atten(ADC2_CHANNEL_7, ADC_ATTEN_DB_11);  
    esp_err_t ret = adc2_get_raw(ADC2_CHANNEL_7, ADC_WIDTH_BIT_12, &raw_val);  

    if (ret == ESP_OK) {  
        printf("ADC2 Value: %d\n", raw_val);  
    } else if (ret == ESP_ERR_INVALID_STATE) {  
        printf("Error: Wi-Fi占用ADC2!\n");  // 处理冲突  
    }  
}  

4.3 ADC校准与电压转换

#include "esp_adc_cal.h"  

void adc_calibration() {  
    esp_adc_cal_characteristics_t adc_chars;  
    esp_adc_cal_value_t cal_type = esp_adc_cal_characterize(  
        ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &adc_chars  
    );  

    int raw = adc1_get_raw(ADC1_CHANNEL_5);  
    uint32_t voltage = esp_adc_cal_raw_to_voltage(raw, &adc_chars);  
    printf("Voltage: %dmV\n", voltage);  
}  

4.4 读取内部霍尔传感器

#include <driver/adc.h>  

void read_hall_sensor() {  
    adc1_config_width(ADC_WIDTH_BIT_12);  
    int hall_val = hall_sensor_read();  // 自动使用GPIO36/39  
    printf("Hall Effect: %d\n", hall_val);  
}  

「关键注意事项」

  1. ADC2与Wi-Fi冲突时返回ESP_ERR_INVALID_STATE
  2. 校准需在每次修改衰减/位宽后重新执行

  3. 霍尔传感器会占用ADC1的通道0和3(GPIO36/39)

❝

本文基于ESP-IDF v4.4+编写,完整示例见ESP-IDF示例目录:

  • examples/peripherals/adc
  • examples/peripherals/adc2

❞

发表评论