【ESP32学习笔记】#外设篇#(1)模数转换器(ADC)

一、简介

1、官方文档

Analog to Digital Converter


2、功能概况

ESP32 内置了 2 个 12 位的 SAR ADC,由 5 个专用转换器控制器管理,可测量来自 18 个管脚的模拟信号,还可测量 vdd33 等内部信号。
在这里插入图片描述
在这里插入图片描述

5 个专用 ADC 控制器:
在这里插入图片描述
PWDET 专门用于 PWDET / PKDET (功率检测和峰值监测)。


3、主要特性

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

4、ADC 控制器类型

(1)RTC SAR ADC 控制器

RTC 电源域中的 SAR ADC 控制器(RTC ADC1 CTRL 和 RTC ADC2 CTRL)可在低频状态下提供最小功耗ADC测量。

  • 可在 Deep-sleep 模式下对通道进行周期性检测。Deep-sleep 模式下,ULP 协处理器是唯一的触发器。
  • 可按一定顺序对通道进行连续性扫描。尽管控制器无法支持连续性扫描或 DMA,但 ULP 协处理器可协助实现这部分功能。
    在这里插入图片描述

(2)DIG SAR ADC 控制器

与 RTC SAR ADC 控制器相比,DIG SAR ADC 控制器的性能和吞吐均实现了一定优化,具备以下特点:

  • 高性能。时钟更快,因此采样速率实现了大幅提升。
  • 支持多通道扫描模式。每个 SAR ADC的测量规则可见样式表。扫描模式可配置为单通道模式、双通道模式或交替模式。
  • 扫描可由软件或 I2S 总线发起。
  • 支持DMA。扫描完成即发生中断。
    在这里插入图片描述

5、ADC管脚信息

  • ADC1:8通道:GPIO32 - GPIO39
  • ADC2:10个通道:GPIO0、GPIO2、GPIO4、GPIO12-GPIO15、GOIO25-GPIO27
+----------+--------------------+-------------+-------------+
|    ADC   |  	  channel		|	 ESP32 	  |   ESP32-S2	|	
|          |     	    		|			  |				|
+==========+====================+=============+=============+
|          |    ADC1_CHANNEL_0	|    GPIO36   |    GPIO36   |
|          +--------------------+-------------+-------------+
|          |	ADC1_CHANNEL_1	|    GPIO37   |    GPIO37   |
|          +--------------------+-------------+-------------+
|          |    ADC1_CHANNEL_2	|    GPIO38   |    GPIO38   |
|          +--------------------+-------------+-------------+
|          |	ADC1_CHANNEL_3	|    GPIO39   |    GPIO39   |
|   ADC1   +--------------------+-------------+-------------+
|          |    ADC1_CHANNEL_4	|    GPIO32   |    GPIO32   |
|          +--------------------+-------------+-------------+
|          |	ADC1_CHANNEL_5	|    GPIO33   |    GPIO33   |
|          +--------------------+-------------+-------------+
|          |    ADC1_CHANNEL_6	|    GPIO34   |    GPIO34   |
|          +--------------------+-------------+-------------+
|          |	ADC1_CHANNEL_7	|    GPIO35   |    GPIO35   |
+----------+--------------------+-------------+-------------+
|          |	ADC2_CHANNEL_0	|    GPIO4    |    GPIO11   |
|          +--------------------+-------------+-------------+
|          |	ADC2_CHANNEL_1	|    GPIO0    |    GPIO12   |
|          +--------------------+-------------+-------------+
|          |    ADC2_CHANNEL_2	|    GPIO2    |    GPIO13   |
|          +--------------------+-------------+-------------+
|          |	ADC2_CHANNEL_3	|    GPIO15   |    GPIO14	|
|          +--------------------+-------------+-------------+
|          |    ADC2_CHANNEL_4	|    GPIO13   |    GPIO15   |
|   ADC2   +--------------------+-------------+-------------+
|          |	ADC2_CHANNEL_5	|    GPIO12   |    GPIO16   |
|          +--------------------+-------------+-------------+
|          |    ADC2_CHANNEL_6	|    GPIO14   |    GPIO17   |
|          +--------------------+-------------+-------------+
|          |	ADC2_CHANNEL_7	|    GPIO27   |    GPIO18   |
|          +--------------------+-------------+-------------+
|          |	ADC2_CHANNEL_8	|    GPIO25   |    GPIO19   |
|          +--------------------+-------------+-------------+
|          |	ADC2_CHANNEL_9	|    GPIO26   |    GPIO20   |
+----------+--------------------+-------------+-------------+

表格中的 管脚 # 即代表ADC通道值。例如管脚 # 0 代表 ADC_CHANNEL_0
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6、测量范围与精度

在这里插入图片描述

  • 使用 eFuse Vref 参考值校准后的结果如上表所示。
  • 芯片之间的测量差异会有 ±6%。
  • Atten=3 时,测量值大于 3000(电压值约为 2450 mV)之后,精度会降低。
  • 使用过滤器多次采样或计算平均值可以获得更好的 DNL 结果。
+----------+-------------+-----------------+
|          | attenuation | suggested range |
|    SoC   |     (dB)    |      (mV)       |
+==========+=============+=================+
|          |       0     |    100 ~  950   |
|          +-------------+-----------------+
|          |       2.5   |    100 ~ 1250   |
|   ESP32  +-------------+-----------------+
|          |       6     |    150 ~ 1750   |
|          +-------------+-----------------+
|          |      11     |    150 ~ 2450   |
+----------+-------------+-----------------+
|          |       0     |      0 ~  750   |
|          +-------------+-----------------+
|          |       2.5   |      0 ~ 1050   |
| ESP32-S2 +-------------+-----------------+
|          |       6     |      0 ~ 1300   |
|          +-------------+-----------------+
|          |      11     |      0 ~ 2500   |
+----------+-------------+-----------------+

7、硬件设计注意事项

使用 ADC 功能时,建议靠近管脚添加 0.1 µF 的对地滤波电容。

  • 在初始化 SARADC1 或 SARADC2 或霍尔传感器的时候,会在 SENSOR_VP/SENSOR_VN PAD 的内部产
    生一个持续约 80 ns 的输入毛刺。
  • 优先推荐使用 SENSOR_VPSENSOR_VN 作为 ADC 使用。
  • 如果设计中使用了 SENSOR_VP/SENSOR_VN 作为 GPIO,且同时使用了其他 ADC,则需要额外的软件处
    理,避开该毛刺。
  • 目前 ADC2 不支持与 Wi-Fi 同时使用,优先推荐使用 ADC1。
  • 目前不支持高精度 ADC。SENSOR_VP 和 SENSOR_CAPP 及 SENSOR_VN 和 SENSOR_CAPN 之间的两
    个采样电容 270 pF 可删除。之后这 4 个管脚可当做普通的 ADC 或是 GPIO 使用。
  • ADC 的输入电压建议不超过 2450 mV,推荐在 100 ~ 950 mV 间,以获得更高的校准精度。具体请参考
    ESP32 技术规格书》中 ADC 章节。

参考 :ESP32硬件设计指南

扫描二维码关注公众号,回复: 13557850 查看本文章

二、配置参数及API

1、API函数

API 描述
adc.h
adc_set_i2s_data_source 设置 I2S 数据源
adc_i2s_mode_init 初始化 I2S ADC 模式
hall_sensor_read 读取霍尔传感器
adc_common.h
adc_power_on 启用 ADC
adc_power_off 关闭ADC
adc_power_acquire 增加 ADC 模块的使用计数器
adc_power_release 减少 ADC 模块的使用计数器
adc_gpio_init 初始化 ADC 引脚
adc1_pad_get_io_num 获取特定 ADC1 通道的 GPIO
adc1_config_channel_atten 在 ADC1 上设置特定通道的衰减,并配置其关联的 GPIO 引脚复用器
adc1_config_width 配置 ADC1 捕捉宽度,同时使能 ADC1 的输出反相
adc1_get_raw 从单个通道获取 ADC1 读数
adc_set_data_inv 设置 ADC 数据反转
adc_set_clk_div 设置 ADC 源时钟
adc_set_data_width 配置 ADC 捕获宽度
adc1_ulp_enable 将 ADC1 配置为可供 ULP 使用
adc2_pad_get_io_num 获取特定 ADC2 通道的 GPIO 编号
adc2_config_channel_atten 配置ADC2特定通道的衰减
adc2_get_raw 在单个通道上读取 ADC2
adc_vref_to_gpio 将 ADC1 或 ADC2 的参考电压输出到 adc2_channe_t的 IO
adc2_vref_to_gpio 将 ADC2 参考电压输出到 adc2_channe_t 的 IO
adc_digi_init ADC 数字控制器初始化
adc_digi_deinit ADC 数字控制器去初始化
adc_digi_controller_config 设置数字控制器

2、宏及结构体、枚举参数

(1)ADC 衰减参数
不同的参数决定了 ADC 的范围。
枚举 adc_atten_t
示例配置:

adc1_config_channel_atten(ADC1_CHANNEL_0,ADC_ATTEN_DB_0);
描述
ADC_ATTEN_DB_0= 0 无输入衰减,ADC可测量高达约800毫伏。
ADC_ATTEN_DB_2_5= 1 ADC的输入电压将衰减,将测量范围扩大到大约1100毫伏。
ADC_ATTEN_DB_6= 2 ADC的输入电压将衰减,将测量范围扩大到大约1350毫伏。
ADC_ATTEN_DB_11= 3 ADC的输入电压将被衰减,将测量范围扩大到大约2600毫伏。

(2)ADC分辨率
不同的参数决定了 ADC 的精度。
枚举 adc_bits_width_t
示例配置:

adc1_config_width(ADC_WIDTH_BIT_12);
描述
ADC_WIDTH_BIT_9= 0 ADC捕获宽度为9位。
ADC_WIDTH_BIT_10= 1 ADC捕获宽度为10位。
ADC_WIDTH_BIT_11= 2 ADC捕获宽度为11位。
ADC_WIDTH_BIT_12= 3 ADC捕获宽度为12位。

三、官方示例参考:

ADC1/ADC2 单次读取示例:

#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; // ADC分辨率
#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_11;    // ADC 衰减参数。不同的参数决定了 ADC 的范围。
static const adc_unit_t unit = ADC_UNIT_1;

static void check_efuse(void)
{
    
    
#if CONFIG_IDF_TARGET_ESP32
    // 检查ADC校准值是否烧录到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);   // 在特定衰减下表征ADC
    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));
    }
}

ADC2 单次读取示例:

#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "driver/dac.h"
#include "esp_system.h"

#define DAC_EXAMPLE_CHANNEL     CONFIG_EXAMPLE_DAC_CHANNEL
#define ADC2_EXAMPLE_CHANNEL    CONFIG_EXAMPLE_ADC2_CHANNEL

#if CONFIG_IDF_TARGET_ESP32
static const adc_bits_width_t width = ADC_WIDTH_BIT_12;
#elif CONFIG_IDF_TARGET_ESP32S2
static const adc_bits_width_t width = ADC_WIDTH_BIT_13;
#endif

void app_main(void)
{
    
    
    uint8_t output_data=0;
    int     read_raw;
    esp_err_t r;

    gpio_num_t adc_gpio_num, dac_gpio_num;

    r = adc2_pad_get_io_num( ADC2_EXAMPLE_CHANNEL, &adc_gpio_num );
    assert( r == ESP_OK );
    r = dac_pad_get_io_num( DAC_EXAMPLE_CHANNEL, &dac_gpio_num );
    assert( r == ESP_OK );

    printf("ADC2 channel %d @ GPIO %d, DAC channel %d @ GPIO %d.\n", ADC2_EXAMPLE_CHANNEL, adc_gpio_num,
                DAC_EXAMPLE_CHANNEL + 1, dac_gpio_num );

    dac_output_enable( DAC_EXAMPLE_CHANNEL );

    //be sure to do the init before using adc2. 
    printf("adc2_init...\n");
    adc2_config_channel_atten( ADC2_EXAMPLE_CHANNEL, ADC_ATTEN_11db );

    vTaskDelay(2 * portTICK_PERIOD_MS);

    printf("start conversion.\n");
    while(1) {
    
    
        dac_output_voltage( DAC_EXAMPLE_CHANNEL, output_data++ );
        r = adc2_get_raw( ADC2_EXAMPLE_CHANNEL, width, &read_raw);
        if ( r == ESP_OK ) {
    
    
            printf("%d: %d\n", output_data, read_raw );
        } else if ( r == ESP_ERR_INVALID_STATE ) {
    
    
            printf("%s: ADC2 not initialized yet.\n", esp_err_to_name(r));
        } else if ( r == ESP_ERR_TIMEOUT ) {
    
    
            //This can not happen in this example. But if WiFi is in use, such error code could be returned.
            printf("%s: ADC2 is in use by Wi-Fi.\n", esp_err_to_name(r));
        } else {
    
    
            printf("%s\n", esp_err_to_name(r));
        }

        vTaskDelay( 2 * portTICK_PERIOD_MS );
    }
}

猜你喜欢

转载自blog.csdn.net/p1279030826/article/details/118894193