本篇主要介绍ESP32的ADC功能,ESP32有两个ADC模块,分别为ADC1/ADC2;每个ADC具有8个通道。
在WiFi在使用时,ADC2的使用受到一些限制,实际应用场景中一般只使用ADC1即可。
GPIO | Analog Function | RTC GPIO | Comments |
---|---|---|---|
GPIO0 | ADC2_CH1 | RTC_GPIO11 | Strapping pin |
GPIO1 | TXD | ||
GPIO2 | ADC2_CH2 | RTC_GPIO12 | Strapping pin |
GPIO3 | RXD | ||
GPIO4 | ADC2_CH0 | RTC_GPIO10 | |
GPIO5 | Strapping pin | ||
GPIO6 | SPI0/1 | ||
GPIO7 | SPI0/1 | ||
GPIO8 | SPI0/1 | ||
GPIO9 | SPI0/1 | ||
GPIO10 | SPI0/1 | ||
GPIO11 | SPI0/1 | ||
GPIO12 | ADC2_CH5 | RTC_GPIO15 | Strapping pin; JTAG |
GPIO13 | ADC2_CH4 | RTC_GPIO14 | JTAG |
GPIO14 | ADC2_CH6 | RTC_GPIO16 | JTAG |
GPIO15 | ADC2_CH3 | RTC_GPIO13 | Strapping pin; JTAG |
GPIO16 | SPI0/1 | ||
GPIO17 | SPI0/1 | ||
GPIO18 | |||
GPIO19 | |||
GPIO21 | |||
GPIO22 | |||
GPIO23 | |||
GPIO25 | ADC2_CH8 | RTC_GPIO6 | |
GPIO26 | ADC2_CH9 | RTC_GPIO7 | |
GPIO27 | ADC2_CH7 | RTC_GPIO17 | |
GPIO32 | ADC1_CH4 | RTC_GPIO9 | |
GPIO33 | ADC1_CH5 | RTC_GPIO8 | |
GPIO34 | ADC1_CH6 | RTC_GPIO4 | GPI |
GPIO35 | ADC1_CH7 | RTC_GPIO5 | GPI |
GPIO36 | ADC1_CH0 | RTC_GPIO0 | GPI |
GPIO37 | ADC1_CH1 | RTC_GPIO1 | GPI |
GPIO38 | ADC1_CH2 | RTC_GPIO2 | GPI |
GPIO39 | ADC1_CH3 | RTC_GPIO3 | GPI |
从上表我们可以看到ADC1可以使用的IO引脚为GPIO32-GPIO39总共8个通道。
ESP32默认的参考电压是1.1V(每个芯片间有差异,非精准),所以只能测量0-1.1V的电压;
为了能够测量更大量程的电压,需要使用其衰减配置;每个通道都可以单独配置
- /**
- * @brief ADC attenuation parameter. Different parameters determine the range of the ADC. See ``adc1_config_channel_atten``.
- */
- typedef enum {
- ADC_ATTEN_DB_0 = 0, ///<No input attenumation, ADC can measure up to approx. 800 mV
- ADC_ATTEN_DB_2_5 = 1, ///<The input voltage of ADC will be attenuated extending the range of measurement by about 2.5 dB (1.33 x)
- ADC_ATTEN_DB_6 = 2, ///<The input voltage of ADC will be attenuated extending the range of measurement by about 6 dB (2 x)
- ADC_ATTEN_DB_11 = 3, ///<The input voltage of ADC will be attenuated extending the range of measurement by about 11 dB (3.55 x)
- } adc_atten_t;
每个配置推荐的输入范围
Attenuation | Measurable input voltage range |
---|---|
| 100 mV ~ 950 mV |
| 100 mV ~ 1250 mV |
| 150 mV ~ 1750 mV |
| 150 mV ~ 2450 mV |
1.在输入引脚接一个100nF的陶瓷电容
2.多次采样取平均
不同芯片的参考电压不一样(1000mV - 1200mV),所以建议进行硬件校准
- /**
- * @brief Type of calibration value used in characterization
- */
- 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_EFUSE_TP_FIT = 3, /**< Characterization based on Two Point values and fitting curve coefficients stored in eFuse */
- ESP_ADC_CAL_VAL_MAX,
- ESP_ADC_CAL_VAL_NOT_SUPPORTED = ESP_ADC_CAL_VAL_MAX,
- } esp_adc_cal_value_t;
ESP32支持ESP_ADC_CAL_VAL_EFUSE_TP/ESP_ADC_CAL_VAL_EFUSE_VREF
ESP32S2支持ESP_ADC_CAL_VAL_EFUSE_TP
ESP32S3支持ESP_ADC_CAL_VAL_EFUSE_TP_FIT
ESP_ADC_CAL_VAL_EFUSE_TP根据EFUSE里面两个采样值进行校准(150mV/850mV)
ESP_ADC_CAL_VAL_EFUSE_VREF根据EFUSE里面的参考电压校准
ESP_ADC_CAL_VAL_EFUSE_TP_FIT根据EFUSE里面两个采样点和特性曲线校准
有些ESP32模组出厂时已经做了校准,我们可以通过工具查看
- $espefuse.py -p /dev/cu.SLAB_USBtoUART adc_info
- Connecting......
- Detecting chip type... Unsupported detection protocol, switching and trying again...
- Connecting......
- Detecting chip type... ESP32
- espefuse.py v4.1
-
- === Run "adc_info" command ===
- ADC VRef calibration: 1128mV
通过espefuse.py adc_info命令我们可以看到我的ESP32模组默认支持VRef校验。
如果不支持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
1.检查是否支持指定的校准方式
esp_err_t esp_adc_cal_check_efuse(esp_adc_cal_value_t value_type);
2.设置采样量化位数
esp_err_t adc1_config_width(adc_bits_width_t width_bit);
3.设置指定通道的衰减
esp_err_t adc1_config_channel_atten(adc1_channel_t channel, adc_atten_t atten);
4.获取指定通道校准特征值
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);
5.获取指定通道的采样值
int adc1_get_raw(adc1_channel_t channel);
6.根据校准特征值获取实际电压值
uint32_t esp_adc_cal_raw_to_voltage(uint32_t adc_reading, const esp_adc_cal_characteristics_t *chars);
- /* ADC1 Example
- This example code is in the Public Domain (or CC0 licensed, at your option.)
- Unless required by applicable law or agreed to in writing, this
- software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, either express or implied.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "driver/gpio.h"
- #include "driver/adc.h"
- #include "esp_adc_cal.h"
-
- #define DEFAULT_VREF 1100 //Use adc2_vref_to_gpio() to obtain a better estimate
- #define NO_OF_SAMPLES 64 //Multisampling
-
- static esp_adc_cal_characteristics_t *adc_chars;
- #if CONFIG_IDF_TARGET_ESP32
- static const adc_channel_t channel = ADC_CHANNEL_6; //GPIO34 if ADC1, GPIO14 if ADC2
- static const adc_bits_width_t width = ADC_WIDTH_BIT_12;
- #elif CONFIG_IDF_TARGET_ESP32S2
- static const adc_channel_t channel = ADC_CHANNEL_6; // GPIO7 if ADC1, GPIO17 if ADC2
- static const adc_bits_width_t width = ADC_WIDTH_BIT_13;
- #endif
- static const adc_atten_t atten = ADC_ATTEN_DB_0;
- static const adc_unit_t unit = ADC_UNIT_1;
-
-
- 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));
- }
- }
以上,基本就讲完了ADC1的使用,点赞收藏不可少