模数转换器集成于芯片,支持测量特定模拟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)
//-------------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)函数进行初始化。
//-------------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, ///
0dB(不衰减):满量程电压——1.2V
2.5dB(-2.5dB衰减):满量程电压——1.5V
6dB(-6dB衰减):满量程电压——2.0V
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的实例、通道和配置结构体。
//-------------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
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()函数将原始的结果转换为电压值