陈拓 2022/06/24-2022/06/24
1. 概述
- 此示例显示如何配置ADC1并读取连接到GPIO引脚的电压。
- 引脚功能
在本例中,我们使用默认的ADC_UNIT_1,我们电池供电的应用中将ESP32开发板的电源连接到GPIO34,以监测电池电压。如果在应用程序中选择了其他ADC单元,则需要更改GPIO引脚(请参阅《ESP32技术参考手册》第4.11章)。
ESP32有2个12位的ADC,共18个通道。要注意的是ADC2和wifi共用引脚,wifi的优先级更高,所以ADC2只有在WIFI模块不用的情况下才能使用。
ESP32的18个ADC通道如下:
ADC1有8个通道:GPIO32 - GPIO39
ADC2有10个通道:GPIO0、GPIO2、GPIO4、GPIO12-GPIO15、GOIO25-GPIO27
每个ADC单元支持两种工作模式,单次采样模式和连续采样(DMA)模式。单次采样模式适用于低频采样操作,连续采样模式适用于高频连续采样动作。
本例为单次采样模式。
https://gitee.com/EspressifSystems/esp-idf/tree/master/examples/peripherals/adc/single_read/adc
?
2. 开发环境
《用乐鑫国内Gitee镜像搭建ESP32开发环境》
https://zhuanlan.zhihu.com/p/348106034
https://blog.csdn.net/chentuo2000/article/details/113424934?spm=1001.2014.3001.5501
3. 修改代码
将官方例子项目复制到ESP-IDF开发工具之外:
cd ~/esp
cp -r ~/esp/esp-idf/examples/peripherals/adc/single_read/adc ~/esp/
cd adc
tree
?
不同的衰减倍数对应不同的检测电压范围。
ADC默认满量程电压为1.1V。要读取更高的电压(最高为引脚最大电压,通常为3.3V),需要将该ADC通道的信号衰减设置为>0dB。
当VDD_A为3.3V时:
-- 0dB衰减(ADC_ATTEN_0)满量程电压为1.1V
-- 2.5dB衰减(ADC_ATTEN_DB_2_5)满量程电压为1.5V
-- 6dB衰减(ADC_ATTEN_6)满量程电压为2.2V
-- 11dB衰减(ADC_ATTEN_11)满量程电压为3.9 V(见以下注释)
注释:
满量程电压是对应于最大读数的电压(取决于ADC1配置的位宽度,该值为:4095表示12位,2047表示11位,1023表示10位,511表示9位。)
注释:
在11dB衰减时,最大电压受VDD_A限制,而非满量程电压。
由于ADC特性,可在以下近似电压范围内获得最准确的结果:
-- 0dB衰减(ADC_ATTEN_DB_0) 100~950 mV
-- 2.5dB衰减(ADC_ATTEN_DB_2_5) 100~1250 mV
-- 6dB衰减(ADC_ATTEN_DB_6) 150~1750 mV
-- 11dB衰减(ADC_ATTEN_DB_11) 150~2450 mV
为了2450mV以上电压测量更准确我们使用了
《ESP32在电池供电时用ULP监测电池电压》
https://zhuanlan.zhihu.com/p/523358887
https://blog.csdn.net/chentuo2000/article/details/125094853?spm=1001.2014.3001.5502
一文中相关的校正代码。
ADC1是带有出厂校准的,所以计算结果有eFuse Vref: Supported
adc1_example_main.c
/* 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 // 多重采样次数
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_11;
static const adc_unit_t unit = ADC_UNIT_1;
// voltage to raw data struct
typedef struct {
float vol;
uint32_t raw;
} vol_to_raw_t;
/* Set thresholds, approx. 2.00V - 3.30V */
#define VAL_TO_RAW_NUM 14
static const vol_to_raw_t g_v0l_to_raw[VAL_TO_RAW_NUM] = {
{2.00, 3123},
{2.10, 3207},
{2.20, 3301},
{2.27, 3361},
{2.35, 3434},
{2.43, 3515},
{2.52, 3598},
{2.60, 3682},
{2.68, 3745},
{2.76, 3825},
{2.80, 3867},
{2.90, 3891},
{3.00, 3986},
{3.30, 4095},
};
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");
}
}
static float ulp_adc_raw_to_vol(uint32_t adc_raw)
{
if (adc_raw < g_v0l_to_raw[0].raw
|| adc_raw > g_v0l_to_raw[VAL_TO_RAW_NUM - 1].raw) {
return 0;
}
float ret_vol = 0;
for (int i = 0; i < VAL_TO_RAW_NUM - 1; i++) {
if (g_v0l_to_raw[i].raw <= adc_raw && g_v0l_to_raw[i + 1].raw >= adc_raw) {
uint32_t raw_dif = g_v0l_to_raw[i + 1].raw - g_v0l_to_raw[i].raw;
float vol_dif = g_v0l_to_raw[i + 1].vol - g_v0l_to_raw[i].vol;
ret_vol = g_v0l_to_raw[i].vol + (float)(adc_raw - g_v0l_to_raw[i].raw) / (float)raw_dif * vol_dif;
break;
}
}
return ret_vol;
}
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 = 0;
if(adc_reading < 3123) { // 如果电压小于2.00, 3123
voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_chars);
} else {
float voltage_f = ulp_adc_raw_to_vol(adc_reading) * 1000;
voltage = (uint32_t)voltage_f;
}
printf("Raw: %d\tVoltage: %dmV\n", adc_reading, voltage);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
4. 构建项目
get_idf
idf.py set-target esp32
idf.py menuconfig
1) 将闪存设置为4MB
?
保存,退出。
idf.py build
查看USB转串口的COM口号:
?
烧写:
idf.py -p /dev/ttyS3 -b 115200 flash
idf.py monitor -p /dev/ttyS3
(Ctrl+]可以退出监视器程序)
?
|