图24.3.1.1 ADC实验程序流程图
24.3.2 ADC函数解析
ESP-IDF提供了一套API来配置ADC。要使用此功能,需要导入必要的头文件:
#include " esp_adc_cal.h"
#include " driver/adc"
接下来,作者将介绍一些常用的ESP32-S3中的ADC函数,这些函数的描述及其作用如下:
1,配置ADC
该函数用于配置ADC各项参数,其函数原型如下所示:
esp_err_t adc_digi_controller_configure(const adc_digi_configuration_t *config); 该函数的形参描述,如下表所示:
| |
| 指向ADC配置结构体的指针 需自行定义,并根据ADC的配置参数填充结构体中的成员变量 |
表24.3.2.1 函数adc_digi_controller_configure()形参描述
返回值:ESP_OK表示配置成功。其他表示配置失败。
该函数使用adc_digi_pattern_config_t以及adc_digi_configuration_t类型的结构体变量传入,该结构体的定义如下所示:
| 成员变量 | |
adc_digi_pattern_config_t | atten: 配置ADC衰减程度,adc_atten_t结构体为索引 | ADC_ATTEN_DB_0,输入电压0-1.1V ADC_ATTEN_DB_2_5,输入电压0-1.5V ADC_ATTEN_DB_6,输入电压0-2.2V ADC_ATTEN_DB_11,输入电压0-3.3V |
channel: 配置ADC通道,adc1_channel_t结构体为索引 | ADC1_CHANNEL_0 , ADC1_CHANNEL_1, ADC1_CHANNEL_2, ADC1_CHANNEL_3, ADC1_CHANNEL_4, ADC1_CHANNEL_5, ADC1_CHANNEL_6, ADC1_CHANNEL_7, |
unit: 配置ADC单元,adc_unit_t结构体为索引 | |
bit_width: 配置ADC位宽,adc_bitwidth_t结构体为索引 | ADC_BITWIDTH_DEFAULT ADC_BITWIDTH_9 ADC_BITWIDTH_10 ADC_BITWIDTH_11 ADC_BITWIDTH_12 ADC_BITWIDTH_13 |
表24.3.2.2 adc_digi_pattern_config_t结构体参数值描述
| 成员变量 | |
| adc_pattern: 配置将要使用的每个ADC参数 | 无,该参数为结构体指针,当adc_digi_pattern_config_t结构体成员配置完成后,以指针的方式将参数传递至adc_pattern这一成员中 |
表24.3.2.3 adc_digi_configuration_t结构体参数值描述
完成上述结构体参数配置之后,可以将结构传递给 adc_digi_controller_configure () 函数,用以实例化ADC。
2,读取ADC原始数据
该函数用于读取ADC原始数据,其函数原型如下所示:
int adc1_get_raw(adc1_channel_t channel);
该函数的形参描述,如下表所示:
表24.3.2.4 函数adc1_get_raw()形参描述
返回值:其他值表示ADC原始数值,-1表示读取失败。
24.3.3 ADC驱动解析
在IDF版14_adc例程中,作者在14_adc \components\BSP路径下新增了一个ADC文件夹,分别用于存放adc.c、adc.h这两个文件。其中,adc.h文件负责声明ADC相关的函数和变量,而adc.c文件则实现了ADC的驱动代码。下面,我们将详细解析这两个文件的实现内容。
1,adc.h文件
/* ADC采集通道定义 */
#define ADC_ADCX_CHY ADC1_CHANNEL_7
2,adc.c文件
/**
* @retval 无
*/
void adc_init(void)
{
adc_digi_pattern_config_t adc1_digi_pattern_config; /* ADC1配置句柄 */
/* ADC1初始化句柄 */
adc_digi_configuration_t adc1_init_config;
/* 配置ADC1 */
adc1_digi_pattern_config.atten = ADC_ATTEN_DB_11; /* 配置ADC衰减程度 */
adc1_digi_pattern_config.channel = ADC_ADCX_CHY; /* 配置ADC通道 */
adc1_digi_pattern_config.unit = ADC_UNIT_1; /* 配置ADC单元 */
adc1_digi_pattern_config.bit_width = ADC_BITWIDTH_12; /* 配置ADC位宽 */
/* 配置将要使用的每个ADC参数 */
adc1_init_config.adc_pattern = &adc1_digi_pattern_config;
adc_digi_controller_configure(&adc1_init_config); /* 配置ADC1 */
}
从上面的代码中可以看出,ADC的初始化函数中,配置了 ADC1通道7。
ADC驱动中,获取ADC转换结果的函数,如下所示:
/**
* @brief 获取ADC转换且进行均值滤波后的结果
* @param ch : 通道号, 0~9
* @param times : 获取次数
* @retval 通道ch的times次转换结果平均值
*/
uint32_t adc_get_result_average(uint32_t ch, uint32_t times)
{
uint32_t temp_val = 0;
uint8_t t;
for (t = 0; t < times; t++) /* 获取times次数据 */
{
temp_val += adc1_get_raw(ch);
vTaskDelay(5);
}
return temp_val / times; /* 返回平均值 */
}
以上函数用于获取ADC转换结果的函数,函数adc_get_result_averagr()则是多次调用adc1_get_raw()获取多次ADC的转换结果,然后进行均值滤波。
24.3.4 CMakeLists.txt文件
打开本实验BSP下的CMakeLists.txt文件,其内容如下所示:
set(src_dirs
ADC
IIC
LCD
LED
SPI
XL9555)
set(include_dirs
ADC
IIC
LCD
LED
SPI
XL9555)
set(requires
driver
esp_adc)
idf_component_register(SRC_DIRS ${src_dirs}
INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})
component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
上述的红色ADC驱动以及esp_adc依赖库需要由开发者自行添加,以确保ADC驱动能够顺利集成到构建系统中。这一步骤是必不可少的,它确保了ADC驱动的正确性和可用性,为后续的开发工作提供了坚实的基础。
24.3.5 实验应用代码
打开main/main.c文件,该文件定义了工程入口函数,名为app_main。该函数代码如下。
i2c_obj_t i2c0_master;
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
uint16_t adcdata;
float voltage;
esp_err_t ret;
ret = nvs_flash_init(); /* 初始化NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES
|| ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
led_init(); /* 初始化LED */
i2c0_master = iic_init(I2C_NUM_0); /* 初始化IIC0 */
spi2_init(); /* 初始化SPI2 */
xl9555_init(i2c0_master); /* 初始化XL9555 */
lcd_init(); /* 初始化LCD */
adc_init(); /* 初始化ADC */
lcd_show_string(30, 50, 200, 16, 16, "ESP32", RED);
lcd_show_string(30, 70, 200, 16, 16, "ADC TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(30, 110, 200, 16, 16, "ADC1_CH7_VAL:", BLUE);
lcd_show_string(30, 130, 200, 16, 16, "ADC1_CH7_VOL:0.000V", BLUE);
while(1)
{
adcdata = adc_get_result_average(ADC_ADCX_CHY, 10);
/* 显示ADC采样后的原始值 */
lcd_show_xnum(134, 110, adcdata, 5, 16, 0, BLUE);
voltage = (float)adcdata * (3.3 / 4096);/* 获取计算后的带小数的实际电压值 */
adcdata = voltage; /* 赋值整数部分给adcx变量 */
lcd_show_xnum(134, 130, adcdata, 1, 16, 0, BLUE);/* 显示电压值的整数部分 */
voltage -= adcdata; /* 把已经显示的整数部分去掉,留下小数部分 */
voltage *= 1000; /* 小数部分乘以1000 */
lcd_show_xnum(150, 130, voltage, 3, 16, 0x80, BLUE); /* 显示小数部分 */
LED_TOGGLE(); /* LED翻转函数 */
vTaskDelay(100);
}
}
从上面的代码中可以看出,在进行完包括ADC的所有初始化工作后,便不断地获取ADC1通道7进行10次转换后经过均值滤波后的结果,并将该原始值显示在LCD上,同时还通过该电压的原始值计算出了电压的模拟量,并在LCD上进行显示。
24.4 下载验证
在完成编译和烧录操作后,可以看到LCD上实时刷新显示着ADC1通道7(IO8引脚)采集到电压的数字量和模拟量,此时可以通过杜邦线给IO8引脚接入不同的电压值(注意共地,且输入电压不能超过3.3V,否则可能损坏开发板),可以看到LCD上显示的电压数字量和模拟量也随之改变。