ESP32学习笔记(八)——ADC
摘要:本文介绍了基于ESP32S3的ADC开发流程,包括连续转换和单次转换两种模式。主要内容有:1. ADC基本概念和工作原理;2. 连续转换模式的配置方法,包含初始化、回调函数设置和结果处理;3. 单次转换模式的实现步骤;4. 具体工程实践,包括文件结构、电路连接和代码实现。通过VSCode+ESP-IDF开发环境,实现了对两个ADC通道(IO4和IO5)的数据采集,并以500ms间隔输出采样值
注:使用VSCode中的ESP-IDF插件进行开发,笔记基于B站UP主艾谷科技的视频教程
ESP32S3+ESPIDF入门教程 程序纯手打 手把手教学 中文字幕 持续更新中_哔哩哔哩_bilibili
目录
(3).在components中的CMakeList文件中添加MYADC以及esp_adc文件路径
(3).在components中的CMakeList文件中添加MYADC以及esp_adc文件路径
一、信号简介
- 信号分类:模拟信号、数字信号
- 模拟信号是一段连续的信号,可以是某一范围内的任意值
- 数字信号是一段离散的信号,只有0和1


二、ADC简介
- ADC(Analog-Digital Converter)模拟-数字转换器
- ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁
- 2个12位逐次逼近型ADC
- 输入电压范围:0~3.3V,转换结果范围:0~4095
- ESP32-S3ADC资源:SAR ADC1、SAR ADC2,共20路输入通道 (SAR是逐次逼近型的简写)。但芯片手册中指出ADC2不能正常使用
- 分辨率越高所能表示的电压就越精细
- 在ESP32中输入电压的范围是可以改变的,要实现电压范围的调节就需要进行电压衰减(如下图所示)

- 芯片会根据电压衰减系数将电压进行对应的衰减

ADC转化过程简介
- 分为四个步骤:采样、保持、量化和编码

三、ADC工作流程
- 总控制器负责下发命令包括:衰减系数、转换使能、指定通道以及转换顺序
- 芯片会根据转换表中的顺序对外部多个引脚的模拟电压依次进行转换


ADC通道有对应的引脚,配置ADC通道的时候注意引脚的对应关系

四、ADC的配置流程
- 连续转换:

adc_continuous_new_handle(const adc_continuous_handle_cfg_t *hdl_config, adc_continuous_handle_t *ret_handle);
- hdl_config:结构体指针,需要创建新的结构体,并引出结构体成员
- ret_handle:ADC句柄,单独创建
adc_continuous_handle_cfg_t adc_continuous_structure = {
.conv_frame_size = , //配置转换帧的大小,单位是字节,本例程中使用来两个通道,每个通道转换的结果是4字节,总共是8字节
.max_store_buf_size = , //配置转换结果的最大值,即转换结果最大能存储多少字节
};
adc_digi_pattern_config_t 使用该结构体,定义通道信息,本例程中包含两个通道,需要配置为数组形式
adc_digi_pattern_config_t adc_digi_arr[] = {
{
.atten = , //配置ADC的衰减系数,衰减系数配置见下图
.bit_width = , //ADC的分辨率位宽,一般配置为12位
.channel = , //配置ADC通道
.unit = , //选择ADC,S3型号的芯片只能使用ADC1
},
{
.atten = ,
.bit_width = ,
.channel = ,
.unit = ,
},
};

adc_continuous_config(adc_continuous_handle_t handle, const adc_continuous_config_t *config);
- handle:ADC句柄
- config:结构体指针,建立新的结构体,引出所有结构体成员
adc_continuous_config_t adc_copeizhintinuous_cfg ={
.adc_pattern = , //将上面的adc_digi_pattern_config_t结构体中结构体名称传入
.conv_mode = , //配置ADC DMA转换模式,可以配置为ADC1、ADC2单独转换,也可配置为循环转换和同时转换,此处配置为ADC1单独转换。
.format = , //转换结果的输出形式,格式二与上图中所讲的转换帧格式相同
.pattern_num = , //需要转换的ADC通道,本例程中有两个通道,此处填入2
.sample_freq_hz = , //最大配置为2MHz,最小配置为20kHz
};
adc_continuous_register_event_callbacks(adc_continuous_handle_t handle, const adc_continuous_evt_cbs_t *cbs, void *user_data);
- handle:ADC函数句柄
- cbs:结构体指针,建立新的结构体,引出所有结构体成员
- user_data:
adc_continuous_evt_cbs_t adc_evt_structure = {
.on_conv_done = continuous_callback, //配置回调函数的触发类型,生成一个转换帧即触发一次回调函数,还有一个是on_pool_ovf,当缓冲区满才触发一次回调函数。跳转后查看回调函数的形式,此处填入回调函数的函数名称
};
adc_continuous_start(adc_continuous_handle_t handle);
- handle:ADC句柄
- 单次转换:使用时候需要包含头文件 "esp_adc/adc_oneshot.h"

adc_oneshot_new_unit(const adc_oneshot_unit_init_cfg_t *init_config, adc_oneshot_unit_handle_t *ret_unit);
- init_config:结构体指针,创建新的结构体,引出所有结构体成员
- ret_unit:ADC句柄
adc_oneshot_unit_init_cfg_t oneshot_structure = {
.clk_src = , //选择时钟源
.ulp_mode = , //配置ADC控制器由哪一个低功耗协处理器控制,一般不使用低功耗,直接选择ADC_ULP_MODE_DISABLE参数
.unit_id = , //选择ADC单元,S3型号的芯片只能使用ADC1
};
adc_oneshot_config_channel(adc_oneshot_unit_handle_t handle, adc_channel_t channel, const adc_oneshot_chan_cfg_t *config);
handle:ADC句柄,直接传入上面定义好的句柄
channel:ADC通道
config:结构体,创建新的结构体,引出所有结构体成员
adc_oneshot_chan_cfg_t oneshot_chan_stureture = {
.atten = , //配置电压衰减系数,选择0~3.3V对应的衰减系数
.bitwidth = , //配置ADC分辨率位宽,一般选择ADC_BITWIDTH_12
};
注:配置多个通道需要多次引用“adc_oneshot_config_channel”函数
adc_oneshot_read(adc_oneshot_unit_handle_t handle, adc_channel_t chan, int *out_raw)
- handle:ADC句柄,使用之前的定义的句柄,该句柄需要在myadc.h中使用extern关键字将句柄声明为外部可调用类型
- chan:选择ADC通道
- out_raw:存放转换结果,需要另外定义相关变量,并在此处使用&引出
注:多个通道需要单独多次调用该函数
五、建立工程项目——ADC连续转换
(1).复制工程文件夹
复制LED的工程文件夹,并改名为ADCContinuous,在components文件夹下创建新的文件夹并取名myadc,在myadc文件夹中创建myadc.c、myadc.h文件。
(2).在VSCode中打开新的工程文件夹
打开工程文件后,先点击垃圾桶按钮清理之前编译程序产生的中间文件,清理完成后再进行编译。
(3).在components中的CMakeList文件中添加MYADC以及esp_adc文件路径

(4).编写myadc.c文件
#include "myadc.h"
#include "hal/adc_types.h" //使用ADC时包含该头文件
#include "esp_adc/adc_continuous.h" //连续转换ADC需要包含此头文件
adc_continuous_handle_t adc_continuous_handle;
uint8_t *data_value;
uint16_t adc_value_1;
uint16_t adc_value_2;
bool continuous_callback(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data)
{
data_value = edata->conv_frame_buffer;
if (edata->size == 8)
{
adc_value_1 = ((data_value[1] & 0x0F) << 8) | data_value[0];
adc_value_2 = ((data_value[5] & 0x0F) << 8) | data_value[4];
return true;
}
return false;
}
void adc_init(void)
{
adc_continuous_handle_cfg_t adc_continuous_structure = {
.conv_frame_size = 8,
.max_store_buf_size = 1024,
};
adc_continuous_new_handle(&adc_continuous_structure, &adc_continuous_handle);
adc_digi_pattern_config_t adc_digi_arr[] = {
{
.atten = ADC_ATTEN_DB_11,
.bit_width = ADC_BITWIDTH_12,
.channel = ADC_CHANNEL_3,
.unit = ADC_UNIT_1,
},
{
.atten = ADC_ATTEN_DB_11,
.bit_width = ADC_BITWIDTH_12,
.channel = ADC_CHANNEL_4,
.unit = ADC_UNIT_1,
},
};
adc_continuous_config_t adc_continuous_cfg ={
.adc_pattern = adc_digi_arr,
.conv_mode = ADC_CONV_SINGLE_UNIT_1,
.format = ADC_DIGI_OUTPUT_FORMAT_TYPE2,
.pattern_num = 2,
.sample_freq_hz = 20000,
};
adc_continuous_config(adc_continuous_handle, &adc_continuous_cfg);
adc_continuous_evt_cbs_t adc_evt_structure = {
.on_conv_done = continuous_callback,
};
adc_continuous_register_event_callbacks(adc_continuous_handle, &adc_evt_structure, NULL);
adc_continuous_start(adc_continuous_handle);
}
(5).编写myadc.h文件
#ifndef __MYADC_H_
#define __MYADC_H_
#include <stdint.h> //使用uint16_t类型的数据,需要包含该头文件
extern uint16_t adc_value_1;
extern uint16_t adc_value_2;
void adc_init(void);
#endif
(6).编写main.c文件
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "myadc.h"
void app_main(void)
{
adc_init();
while(1)
{
printf("adc_value_1:%d\r\n",adc_value_1);
printf("adc_value_2:%d\r\n",adc_value_2);
vTaskDelay(500);
}
}
(7).编译
(8).连接电路

注:RL、RP可选为任意的支持ADC的传感器,将传感器的AO引脚连接至IO4、IO5。
(9).下载并运行
每个500ms在窗口打印一次两个通道的数据
六、建立工程项目——ADC连续转换
(1).复制工程文件夹
复制LED的工程文件夹,并改名为ADCOneshot,在components文件夹下创建新的文件夹并取名myadc,在myadc文件夹中创建myadc.c、myadc.h文件。
(2).在VSCode中打开新的工程文件夹
打开工程文件后,先点击垃圾桶按钮清理之前编译程序产生的中间文件,清理完成后再进行编译。
(3).在components中的CMakeList文件中添加MYADC以及esp_adc文件路径

(4).编写myadc.c文件
#include "myadc.h"
#include "driver/adc.h"
#include "esp_adc/adc_oneshot.h"
adc_oneshot_unit_handle_t adc_uint_handle;
void adc_init(void)
{
adc_oneshot_unit_init_cfg_t oneshot_structure = {
.clk_src = ADC_RTC_CLK_SRC_DEFAULT,
.ulp_mode = ADC_ULP_MODE_DISABLE,
.unit_id = ADC_UNIT_1,
};
adc_oneshot_new_unit(&oneshot_structure, &adc_uint_handle);
adc_oneshot_chan_cfg_t oneshot_chan_stureture = {
.atten = ADC_ATTEN_DB_11,
.bitwidth = ADC_BITWIDTH_12,
};
adc_oneshot_config_channel(adc_uint_handle, ADC_CHANNEL_3, &oneshot_chan_stureture);
adc_oneshot_config_channel(adc_uint_handle, ADC_CHANNEL_4, &oneshot_chan_stureture);
}
(5).编写myadc.h文件
#ifndef __MYADC_H_
#define __MYADC_H_
#include "esp_adc/adc_oneshot.h"
extern adc_oneshot_unit_handle_t adc_uint_handle;
void adc_init(void);
#endif
(6).编写main.c文件
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc.h"
#include "driver/gpio.h"
#include "myadc.h"
int adc_value_rl = 0;
int adc_value_rp = 0;
void app_main(void)
{
lcd_init();
adc_init();
while(1)
{
uint8_t adc_value[2] = {0};
adc_oneshot_read(adc_uint_handle, ADC_CHANNEL_3, &adc_value_rl);
adc_oneshot_read(adc_uint_handle, ADC_CHANNEL_4, &adc_value_rp);
printf("adc_value_1:%d\r\n",adc_value_rl);
printf("adc_value_2:%d\r\n",adc_value_rp);
vTaskDelay(500);
}
}
(7).编译
(8).连接电路

注:RL、RP可选为任意的支持ADC的传感器,将传感器的AO引脚连接至IO4、IO5。
(9).下载并运行
每个500ms在窗口打印一次两个通道的数据
更多推荐



所有评论(0)