ESP32-IDF官方例程解读——模数转换器 (ADC) 单次转换模式驱动

简介

模数转换器集成于芯片,支持测量特定模拟IO管脚的模拟信号。

ESP32有两个ADC单元,可以进行ADC单次转换和连续转换。

注意!!!使用WiFi时不能使用ADC2引脚。因此,如果您使用 Wi-Fi 并且无法从 ADC2 GPIO 获取值,您可以考虑改用 ADC1 GPIO。那应该可以解决您的问题。

ADC使用的IO引脚如下所示:

ADC1:

  • ADC1_CH0 (GPIO 36)
  • ADC1_CH1 (GPIO 37)
  • ADC1_CH2 (GPIO 38)
  • ADC1_CH3 (GPIO 39)
  • ADC1_CH4 (GPIO 32)
  • ADC1_CH5 (GPIO 33)
  • ADC1_CH6 (GPIO 34)
  • ADC1_CH7 (GPIO 35)

ADC2(不能与WIFI连用):

  • ADC2_CH0 (GPIO 4)
  • ADC2_CH1 (GPIO 0)
  • ADC2_CH2 (GPIO 2)
  • ADC2_CH3 (GPIO 15)
  • ADC2_CH4 (GPIO 13)
  • ADC2_CH5 (GPIO 12)
  • ADC2_CH6 (GPIO 14)
  • ADC2_CH7 (GPIO 27)
  • ADC2_CH8 (GPIO 25)
  • ADC2_CH9 (GPIO 26)

 程序解读

1.创建ADC单元实例

//-------------ADC1 Init---------------//
    adc_oneshot_unit_handle_t adc1_handle;
    adc_oneshot_unit_init_cfg_t init_config1 = {
        .unit_id = ADC_UNIT_1,
    };
    ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));

 对于单次转换模式驱动而言,ADC 实例以 adc_oneshot_unit_handle_t 表示。声明完成后需要配置结构体成员。

/**
 * @brief ADC oneshot driver initial configurations
 */
typedef struct {
    adc_unit_t unit_id;             ///< ADC unit
    adc_oneshot_clk_src_t clk_src;  ///< Clock source
    adc_ulp_mode_t ulp_mode;        ///< ADC controlled by ULP, see `adc_ulp_mode_t`
} adc_oneshot_unit_init_cfg_t;

成员变量1:unit_id。

用于选择ADC实例,就ESP32而言,是选择ADC1还是ADC2。

成员变量2:clk_src。

选择 ADC 的时钟源。设置为 0 时,驱动程序将使用默认时钟源。这里不设置,默认为0。

ADC的时钟为默认的SOC_MOD_CLK_RC_FAST。

成员变量3:ulp_mode。

超低功耗配置,这里没设置,默认为0,ADC_ULP_MODE_DISABLE。即无超低功耗。

在配置完结构体后,使用esp_err_t adc_oneshot_new_unit(const adc_oneshot_unit_init_cfg_t *init_config, adc_oneshot_unit_handle_t *ret_unit)函数进行初始化。

2.配置ADC单元实例

//-------------ADC1 Config---------------//
    adc_oneshot_chan_cfg_t config = {
        .bitwidth = ADC_BITWIDTH_DEFAULT,
        .atten = EXAMPLE_ADC_ATTEN,
    };
    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN0, &config));
    ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN1, &config));

配置结构体adc_oneshot_chan_cfg_t。

/**
 * @brief ADC channel configurations
 */
typedef struct {
    adc_atten_t atten;              ///< ADC attenuation
    adc_bitwidth_t bitwidth;        ///< ADC conversion result bits
} adc_oneshot_chan_cfg_t;

成员变量1:atten。

该变量为ADC衰减。ADC(模数转换器)的衰减(attenuation)通常指的是输入信号经过固定的电阻分压电路进行降压,以便将大于ADC输入范围的信号缩放到ADC可接受的范围内进行转换,即代表能测量的满量程电压。这里配置的为11dB。

/**
 * @brief ADC attenuation parameter. Different parameters determine the range of the ADC.
 */
typedef enum {
    ADC_ATTEN_DB_0   = 0,  ///
  1. 0dB(不衰减):满量程电压——1.2V

  2. 2.5dB(-2.5dB衰减):满量程电压——1.5V

  3. 6dB(-6dB衰减):满量程电压——2.0V

  4. 11dB(-11dB衰减):满量程电压——3.3V

成员变量2: adc_bitwidth_t

typedef enum {
    ADC_BITWIDTH_DEFAULT = 0, ///< Default ADC output bits, max supported width will be selected
    ADC_BITWIDTH_9  = 9,      ///< ADC output width is 9Bit
    ADC_BITWIDTH_10 = 10,     ///< ADC output width is 10Bit
    ADC_BITWIDTH_11 = 11,     ///< ADC output width is 11Bit
    ADC_BITWIDTH_12 = 12,     ///< ADC output width is 12Bit
    ADC_BITWIDTH_13 = 13,     ///< ADC output width is 13Bit
} adc_bitwidth_t;

原始转换结果的位宽。位宽是指ADC输出的二进制的位数,它影响测量的精度。这里是配置为默认的ADC宽度。

最后,使用esp_err_t adc_oneshot_config_channel(adc_oneshot_unit_handle_t handle, adc_channel_t channel, const adc_oneshot_chan_cfg_t *config)函数,传入ADCHandle的实例、通道和配置结构体。

3. ADC矫正初始化

//-------------ADC1 Calibration Init---------------//
    adc_cali_handle_t adc1_cali_chan0_handle = NULL;
    adc_cali_handle_t adc1_cali_chan1_handle = NULL;
    bool do_calibration1_chan0 = example_adc_calibration_init(ADC_UNIT_1, EXAMPLE_ADC1_CHAN0, EXAMPLE_ADC_ATTEN, &adc1_cali_chan0_handle);
    bool do_calibration1_chan1 = example_adc_calibration_init(ADC_UNIT_1, EXAMPLE_ADC1_CHAN1, EXAMPLE_ADC_ATTEN, &adc1_cali_chan1_handle);

使用自定义ADC矫正函数static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)

/*---------------------------------------------------------------
        ADC Calibration
---------------------------------------------------------------*/
static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)
{
    adc_cali_handle_t handle = NULL;
    esp_err_t ret = ESP_FAIL;
    bool calibrated = false;

#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
    if (!calibrated) {
        ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
        adc_cali_curve_fitting_config_t cali_config = {
            .unit_id = unit,
            .chan = channel,
            .atten = atten,
            .bitwidth = ADC_BITWIDTH_DEFAULT,
        };
        ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
        if (ret == ESP_OK) {
            calibrated = true;
        }
    }
#endif

#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
    if (!calibrated) {
        ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting");
        adc_cali_line_fitting_config_t cali_config = {
            .unit_id = unit,
            .atten = atten,
            .bitwidth = ADC_BITWIDTH_DEFAULT,
        };
        ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
        if (ret == ESP_OK) {
            calibrated = true;
        }
    }
#endif

    *out_handle = handle;
    if (ret == ESP_OK) {
        ESP_LOGI(TAG, "Calibration Success");
    } else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
        ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
    } else {
        ESP_LOGE(TAG, "Invalid arg or no memory");
    }

    return calibrated;
}

这里只讲解ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED以下部分。

1.先判断是否创建线性拟合方案

2.输出日志(非必要),配置adc_cali_line_fitting_config_t结构体

3.创建线性拟合方案句柄adc_cali_create_scheme_line_fitting()

4.将创建完成的句柄传递给形参out_handle,这里是指传递给实参adc1_cali_chan1_handle

4.读取数据并转化

ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN0, &adc_raw[0][0]));
ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN0, adc_raw[0][0]);
if (do_calibration1_chan0) {
        ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_chan0_handle, adc_raw[0][0], &voltage[0][0]));
        ESP_LOGI(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN0, voltage[0][0]);
}
vTaskDelay(pdMS_TO_TICKS(1000));

ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN1, &adc_raw[0][1]));
ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN1, adc_raw[0][1]);
if (do_calibration1_chan1) {
        ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_chan1_handle, adc_raw[0][1], &voltage[0][1]));
        ESP_LOGI(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN1, voltage[0][1]);
}
vTaskDelay(pdMS_TO_TICKS(1000));

在While循环内,

首先,调用 adc_oneshot_read() 可以获取 ADC 通道的原始转换结果。这里将ADC1通道0的原始转换结果传递给了adc_raw[0][0]

其次,使用if条件判断语句,判定ADC1通道0是否已经完成校验,若已经完成则使用adc_cali_raw_to_voltage()函数将原始的结果转换为电压值

你可能感兴趣的:(mcu,单片机)