ESP32 ADC 应用相关参数解析

此篇博客用来解读 ESP32 ADC 相关参数用法以及常见问题整理。

1 概述

通过查阅《ESP32 Datasheet》ADC 编程指南 可了解到 ESP32 集成了 2 个 12-bit SAR ADC,共支持 18 个模拟通道输入。接下来将会一一解读 ADC 各项参数。

注:2 个模数转换器指的是 ADC1 和 ADC2,12 bit 即为 ADC 的最高精度 2^12 = 4096。精度可配置 12 位、11 位、10 位、9 位多种分辨率,参见 《ESP32 技术参考手册》 的第 583 页。

2 ADC 各项参数解读

2.1 ADC 精度

ESP32 ADC 的最高精度为 12 bit,即为 2^12 = 4096。对应程序如下:

adc_bits_width_t width = ADC_WIDTH_BIT_12;
typedef enum {
    ADC_WIDTH_BIT_9  = 0, /*!< ADC capture width is 9Bit. Only ESP32 is supported. */
    ADC_WIDTH_BIT_10 = 1, /*!< ADC capture width is 10Bit. Only ESP32 is supported. */
    ADC_WIDTH_BIT_11 = 2, /*!< ADC capture width is 11Bit. Only ESP32 is supported. */
    ADC_WIDTH_BIT_12 = 3, /*!< ADC capture width is 12Bit. Only ESP32 is supported. */
#if !CONFIG_IDF_TARGET_ESP32
    ADC_WIDTH_BIT_13 = 4, /*!< ADC capture width is 13Bit. Only ESP32-S2 is supported. */
#endif
    ADC_WIDTH_MAX,
} adc_bits_width_t;

注:精度可配置 12 位、11 位、10 位、9 位多种分辨率,参见 《ESP32 技术参考手册》 的第 583 页。

2.2 ADC 通道

ESP32 一共有 18 个通道,可测量来自 18 个管脚的模拟信号。分别为:

  • ADC1:8 个通道,GPIO32~GPIO39
  • ADC2:10 个通道,GPIO0、GPIO2、GPIO4、GPIO12-GPIO15、GPIO25-GPIO27

在使用过程中有以下三点需要注意:

  • ADC1 的电源打开时,GPIO36 和 GPIO39 的数字输入会被拉低约 80 ns。使用 ADC1 的 Channel 0(GPIO36)和 Channel 3(GPIO39)时,不要将任何其他的东西接在这两个管脚上也不要改变他们的配置,否则可能会影响低频信号的测量值
  • 由于 GPIO0、GPIO2、GPIO15 为 Strapping 管脚,因此不能自由使用
  • Wi-Fi 驱动程序使用 ADC2。因此,只能在未启动 Wi-Fi 驱动程序时使用 ADC2。但 ADC2 可与 蓝牙同时使用

2.3 ADC 特性

通过查询 《ESP32 技术参考手册》,可以发现下图:

在这里插入图片描述
同时可以总结出以下特性:

  • 采用 2 个 SAR ADC,可支持同时采样与转换
  • 采用 5 个专用 ADC 控制器:RTC ADC1 CTRL、RTC ADC2 CTRL、DIG ADC1 CTRL、DIG ADC2 CTRL,及 PWDET CTRL。可支持不同应用场景(比如,高性能、低功耗,或功率检测和峰值检测)
  • 支持 18 个模拟输入管脚
  • 1 个内部电压 vdd33 通道、2 个 pa_pkdet 通道(ADC2 控制器支持)
  • 可配置 12 位、11 位、10 位、9 位多种分辨率
  • 支持 DMA(ADC1 控制器支持)
  • 支持多通道扫描模式(2 个控制器支持)
  • 支持 Deep-sleep 模式运行(2 个控制器支持)
  • 支持 ULP 协处理器控制(2 个控制器支持)

5 个专用 ADC 控制器的具体使用范围如下:
在这里插入图片描述
同时 DNL 和 INL 也是较为关心的 ADC 特性,如下:
在这里插入图片描述

2.4 ADC 工作模式

ESP32 的 ADC 模式目前有以下两种:

  • ADC 单次读取模式:适用于低频采样,采样频率要求 1 kHz
  • ADC 连续(DMA)模式:适用于高频连续采样,以 mKz 为单位的频率,可参考 I2S_ADC_DAC 例程

2.5 ADC 衰减配置

通过配置 ADC 的衰减可以确定 ADC 的读数范围,同时 ADC 的衰减配置是按通道进行的,具体需要配置参数 adc_atten_t,此衰减配置参数有 4 个等级,如下:

  • ADC_ATTEN_DB_0 = 0:满量程电压 1100 mV
  • ADC_ATTEN_DB_2_5 = 1:满量程电压 1500 mV
  • ADC_ATTEN_DB_6 = 2:满量程电压 2200 mV
  • ADC_ATTEN_DB_11 = 3:满量程电压 3900 mV

满量程电压也可以参考 ESP-IDF 里的这段注释,不同衰减等级的 ADC 推荐量程可以参考下图:
在这里插入图片描述

为了获得最大的测量精度,请使用这些推荐范围内的 ADC 测量电压。默认 ADC 满量程电压为 1100 mv,要读取更高的电压(最高为引脚的最大电压,通常为 3.3 V),则要求将该通道的 ADC 衰减参数设为 > 0dB 。在使用 11 dB 衰减参数时,最大测量电压受 VDD_A(模拟电源 2.3 V ∼ 3.6 V,由于 ESP32 供电一般是 3.3V ,即 VDD_A 最高也是 3.3V)的限制,而不是满量程电压。

注 :ADC 未连接任何信号的引脚的读数是随机的。

2.5 ADC 校准方式

ESP32 有以下三种校准方式:

  • 两点校准:两点校准的 ADC 读数分别为 150 mv 和 850 mv ,这些值应由用户测量并刻录到 eFuse 的 BLOCK3 中

  • eFuse Vref 校准:代表真实的 ADC 参考电压,在工厂校准期间会测量此值并将其刻录到 eFuse BLOCK0 中,是默认使用的 ADC 校准方式

  • Default Vref 校准:在表征过程中提供的 ADC 参考电压。如果 “两点校准” 或 "eFuse Vref " 值不可用,将使用 Default Vref。对应的参数定义为 #define DEFAULT_VREF 1100

上述三种方式对应的结构体如下:

typedef enum {
    ESP_ADC_CAL_VAL_EFUSE_VREF = 0,         /**< Characterization based on reference voltage stored in eFuse*/
    ESP_ADC_CAL_VAL_EFUSE_TP = 1,           /**< Characterization based on Two Point values stored in eFuse*/
    ESP_ADC_CAL_VAL_DEFAULT_VREF = 2,       /**< Characterization based on default reference voltage*/
    ESP_ADC_CAL_VAL_MAX
} esp_adc_cal_value_t;

3 ADC 常见问题整理


Q1. 使用 adc 例程,采集电压值发现采集低电压的时候误差会稍微偏大

[Ans] 默认的 ADC 电压是 0 db 衰减,为了获得最大的精度,请使用 ADC 校准 API 并测量这些建议范围内的电压(默认为 100 mV ~950 mV )。如下图,对于大电压的测量修改 ADC 的衰减配置参数,超过 3.3 V 的测量电压建议进行分压测量。请参考 ADC 的衰减配置参数


Q2. ADC1 的单通道采样频率是多大?

[Ans] ADC1 单通道的采样频率要求是 1KHz ,可参见 adc_digi_config_t 说明。


Q3. 如何擦除(修改)出厂刻录的 eFuse Vref 参考电压,而写入自定义的 “两点校准” 的参考电压?

[Ans] 芯片默认出厂烧录的 ADC eFuse_vref 无法擦除,但可以不使用,可在 menuconfig -> Component config -> ADC Calibration 配置选项中进行关闭,如下:
在这里插入图片描述

注:eFuse Vref ADC 参考电压和 “两点校准”的 ADC 参考电压可共存,若在 menuconfig 中将两种校准方式都开启,且 eFuse 中都写入了 eFuse Vref 和 “两点校准” 的 ADC 参考电压,以 “两点校准”的参考电压为准。


Q4. 如何写 “两点校准”值到 Efuse Block3 中?

[Ans] 可参考以下三篇文档:

来进行操作,举例:需要将 ADC 校准值写成二进制文件,然后烧录到 eFuse BLK3 的第 12 bytes,在终端输入以下指令即可:

espefuse.py -p /dev/ttyUSB0 burn_block_data --offset 12 BLK3 ADC_new2.bin
espefuse.py -p /dev/ttyUSB0 burn_efuse BLK3_PART_RESERVE 1

Q5. 修改 esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_12Bit, DEFAULT_VREF, adc_chars) 的参数 DEFAULT_VREF 发现 ADC 测量精度效果改善并不明显?

[Ans] DEFAULT_VREF 为表征过程中提供的 ADC 参考电压,默认是 1100 mV,不允许自定义修改。如果两点校准或 eFuse Vref 值不可用,将使用 Default Vref。ESP32 的出厂默认参考电压为 eFuse Vref ,存储在 eFuse 中,为 1100 mv

注:仅在 ESP32 中,如果无法使用 eFuse Vref 和 “两点校准”的值,则设置该参数作为参考电压有效。如果 eFuse 中没有刻录 eFuse_vref 或 在 menuconfig 中关闭了默认的 ADC 校准方式的情况下,可使用 Default Vref 。当 eFuse 中已经刻录 eFuse_vref 值时, 软件方式修改此参数并不能改变默认 ADC 参考电压值 。可参考 ADC 文档


Q6. 是否可以修改 eFuse Vref 值来提升精度?或者有什么其他的方案可以提高 ADC 采集电压的精度吗?

[Ans] ADC 默认的最高采样精度为 12 bit , 即 2^12 =4096 ,无法做到更高的采样精度。可以尝试减小 ADC 采样误差来 “提升精度”,方法如下:

  • 通过修改 esp_adc_cal_value_t 校准值的类型来修改 ADC 的参考电压
  • 通过在 efuse 中刻录 ADC 的 Vref 为两点校准值来提高测量精度。可参见 Calibration Values

Q7. 当没有给 ADC 采集引脚电压的时候,默认会采集 284mV 的电压,实际检测输入端口只有几十 mv , 这个是否有解决方法呢?

[Ans] 当采集引脚没有给电压(或悬空)的时候,ADC 采集的值并不准确(会在 0 到 3.3 V 随机)。如果想避免这种随机值,可以进行上下拉。


Q8. 使用 0-1 V 的 ADC 输入电压时为什么在 0~200 mV 之间的 ADC 采集值都是 284 mV?

[Ans] ADC 默认使用的衰减参数是 0 dbm,建议的 ADC 测量电压范围是 100 mv~950 mV;在实际使用中,建议最低测量电压不低于 300 mV 。


Q9. ADC 的测量误差有多大?有效测量范围是?

[Ans] 默认情况下,芯片 ADC 之间的测量差异会有 ±6%。ESP-IDF 提供了对 ADC1 的多种 校准方法。使用 (默认的)eFuse Vref 参考值校准后的结果如下图所示。用户如需要更高的精度可选用其他方法自行校准。
在这里插入图片描述


Q10. 如何验证是否刻录 eFuse Vref 参考电压?

[Ans] 可通过 esptool 工具,使用如下命令进行查询:

espefuse.py --port /dev/ttyUSB0 adc_info

此时如果有 eFuse Vref ,则返回的结果如下:

ADC VRef calibration: 1093 mV

如果没有 eFuse Vref ,则返回的结果如下:

ADC VRef calibration: None (1100 mV nominal)

如果刻录的是两点校准参数,则返回的结果类似于:

ADC VRef calibration: 1149 mV
ADC readings stored in efuse BLK3:
ADC1 Low reading  (150 mV): 306
ADC1 High reading (850 mV): 3153
ADC2 Low reading  (150 mV): 389
ADC2 High reading (850 mV): 3206

在 ADC 单次读取的应用程序模式下,会先检查 eFuse 是否已经刻录 ADC 参考电压值。


Q11. ESP32 的 ADC DMA 模式的最高采样率是多大?

[Ans] ESP32 的 ADC DMA 模式只支持 Dig ADC (ADC 数字信号控制器),故 ADC DMA 只支持模拟信号转数字信号的应用。以 I2S_ADC_DAC 例程为例,所以 ADC DMA 模式的最高采样率取决于 I2S 的采样率 。

static void check_efuse(void) { #if CONFIG_IDF_TARGET_ESP32 //Check if TP is burned into eFuse if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) { printf("eFuse Two Point: Supported\n"); } else { printf("eFuse Two Point: NOT supported\n"); } //Check Vref is burned into eFuse if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) { printf("eFuse Vref: Supported\n"); } else { printf("eFuse Vref: NOT supported\n"); } #elif CONFIG_IDF_TARGET_ESP32S2 if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) { printf("eFuse Two Point: Supported\n"); } else { printf("Cannot retrieve eFuse Two Point calibration values. Default calibration values will be used.\n"); } #else #error "This example is configured for ESP32/ESP32S2." #endif } static void print_char_val_type(esp_adc_cal_value_t val_type) { if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { printf("Characterized using Two Point Value\n"); } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { printf("Characterized using eFuse Vref\n"); } else { printf("Characterized using Default Vref\n"); } } void app_main(void) { //Check if Two Point or Vref are burned into eFuse check_efuse(); //Configure ADC if (unit == ADC_UNIT_1) { adc1_config_width(width); adc1_config_channel_atten(channel, atten); } else { adc2_config_channel_atten((adc2_channel_t)channel, atten); } //Characterize ADC adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t)); esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_chars); print_char_val_type(val_type); //Continuously sample ADC1 while (1) { uint32_t adc_reading = 0; //Multisampling for (int i = 0; i < NO_OF_SAMPLES; i++) { if (unit == ADC_UNIT_1) { adc_reading += adc1_get_raw((adc1_channel_t)channel); } else { int raw; adc2_get_raw((adc2_channel_t)channel, width, &raw); adc_reading += raw; } } adc_reading /= NO_OF_SAMPLES; //Convert adc_reading to voltage in mV uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_chars); printf("Raw: %d\tVoltage: %dmV\n", adc_reading, voltage); vTaskDelay(pdMS_TO_TICKS(1000)); } }
06-09
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值