ESP32S3基于espidf ADC使用
- 官方在线文档介绍模数转换器:
https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32s3/api-reference/peripherals/adc_oneshot.html
- 🔖espidf版本:
v5.4
- 模数转换器 (ADC)转换方式:
- 模数转换器 (ADC) 单次转换模式驱动
- 模数转换器 (ADC) 连续转换模式驱动
- 模数转换器 (ADC) 带校准驱动
- ESP32-S3 功能框图
ESP32S3 ADC介绍
ESP32-S3 内置了两个 12 位的 SAR ADC,可测量最多来自 20 个管脚的模拟信号。
- SAR ADC概图
主要特性
-
支持 12 位采样分辨率
• 支持采集最多 20 个管脚上的模拟电压 -
RTC ADC1/2 控制器:
– 支持单次转换
– 支持在低功耗模式下工作,如 Deep-sleep
– 可由 ULP 协处理器配置 -
DIG ADC1 控制器:
– 配有多通道扫描控制模块,支持多通道扫描模式
– 提供模式控制模块,支持单 SAR ADC 采样模式
– 在多通道扫描模式下,支持自定义扫描通道顺序
– 提供两个滤波器,滤波系数可配
– 支持阈值监控,采样值大于设置的高阈值或小于设置的低阈值将产生中断
– 支持 DMA。 -
SAR ADC 的主要元件与连接情况见图
-
SAR ADC 模块主要包括以下部分:
• SAR ADC1:可对 10 个通道进行电压检测;
• SAR ADC2:可对 10 个通道进行电压检测; -
SAR ADC 的信号输入
ADC 转换和衰减
SAR ADC 转换模拟信号时,转换分辨率(12 位)电压范围为 0 mV ~ Vref。其中,Vref 为 SAR ADC 内部参考电
压,出厂设定为 1100 mV。因此,转换结果 (data) 可以使用以下公式转换成模拟电压输出 Vdata:
- ADC 经硬件校准和 软件校准后的结果如表下表 所示。如需更高的精度,可选用其他方法自行校准。
- ✨在使用ADC测量模拟输入电压时,被测电压信号,阈值最好不要超过3V。不然精度会比上表所述低。
- 针对esp32型号,只有在未启动Wi-Fi 驱动程序下,才能使用 ADC2。
- esp32S3 SAR ADC2的数字控制器无法工作
ESP32-S3 SAR ADC2 的数字控制器(即DIGADC2控制器)可能收到错误的采样启动信号,导致控制器进入无法工作的状态。
- 无变通方法。建议使用RTC控制器来控制SAR ADC2。
🔰编译器不同版本和ADC驱动程序差异
- 在当前所使用的
v5.4
版本下,所调用的ADC驱动头文件:
adc_cali.h
adc_cali_scheme.h
adc_continuous.h
adc_oneshot.h
- 在旧版本中,所包含的头文件:
#include "driver/adc.h"
当前版本(v5.4)下,部分旧版本的API程序还是可以使用,可能在未来某个版本中会全部移除。有些API已经不支持了,仍继续使用的话,程序运行可能会报错。并且编译时会报警告信息:
#warning "legacy adc driver is deprecated, please migrate to use esp_adc/adc_oneshot.h and esp_adc/adc_continuous.h for oneshot mode and continuous mode drivers respectively"
📘数转换器 (ADC) 单次转换模式驱动
- 所需包含的头文件:
#include "esp_adc/adc_oneshot.h"
- ADC 驱动代码
- 模数转换器 (ADC) 单次转换模式驱动可以参考:
v5.4\esp-idf\examples\peripherals\adc\oneshot_read
#include <stdio.h>
#include "esp_adc/adc_oneshot.h"
#define ADC1_CHANNEL ADC_CHANNEL_6 // 使用 ADC1 通道 6 (GPIO 6)
#define ADC_ATTEN ADC_ATTEN_DB_12 // 衰减值
#define ADC_BITWIDTH ADC_BITWIDTH_12 // 位宽
void app_main(void) {
// 初始化 ADC 单元
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = ADC_UNIT_1, // 使用 ADC1
};
ESP_ERROR_CHECK(adc_oneshot_unit_init(&init_config, &adc1_handle));
// 配置 ADC 通道
adc_oneshot_chan_cfg_t channel_config = {
.atten = ADC_ATTEN, // 衰减值
.bitwidth = ADC_BITWIDTH, // 位宽
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, ADC1_CHANNEL, &channel_config));
// 读取 ADC 值
while (1) {
int adc_raw_value = 0;
ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, ADC1_CHANNEL, &adc_raw_value));
printf("ADC Raw Value: %d\n", adc_raw_value);
// 延时 1 秒
vTaskDelay(pdMS_TO_TICKS(1000));
}
// 释放资源(此代码不会执行到此处,仅为示例)
ESP_ERROR_CHECK(adc_oneshot_unit_deinit(adc1_handle));
}
📗模数转换器 (ADC) 校准驱动
#include <stdio.h>
#include "driver/gpio.h"
#include "esp_adc/adc_continuous.h"
#include "esp_adc/adc_oneshot.h"
//#include "esp_adc/adc_oneshot_unit.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_err.h"
#define TAG "ADC_CAL"
#define DEFAULT_VREF 1100 // 默认内部参考电压 (mV)
#define NO_OF_SAMPLES 64 // 采样次数
#define ADC1_CHANNEL ADC_CHANNEL_6 // 使用 ADC1 通道 6 (GPIO 6)
#define ADC_ATTEN ADC_ATTEN_DB_12 // 衰减值
#define ADC_BITWIDTH ADC_BITWIDTH_12 // 位宽
void app_main(void) {
// 初始化 ADC 单元
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = ADC_UNIT_1, // 使用 ADC1
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config, &adc1_handle));
// 配置 ADC 通道
adc_oneshot_chan_cfg_t channel_config = {
.atten = ADC_ATTEN, // 衰减值
.bitwidth = ADC_BITWIDTH, // 位宽
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, ADC1_CHANNEL, &channel_config));
// 初始化校准方案
adc_cali_handle_t adc_cali_handle = NULL;
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = ADC_UNIT_1,
.atten = ADC_ATTEN,
.bitwidth = ADC_BITWIDTH,
};
esp_err_t ret = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle);
if (ret == ESP_OK) {
printf("Calibration scheme created successfully\n");
} else {
printf("Failed to create calibration scheme\n");
return;
}
while (1) {
int adc_raw_value = 0;
ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, ADC1_CHANNEL, &adc_raw_value));
printf("ADC Raw Value: %d\n", adc_raw_value);
// 将原始值转换为电压
int voltage = 0;
if (adc_cali_raw_to_voltage(adc_cali_handle, adc_raw_value, &voltage) == ESP_OK) {
printf("ADC Raw Value: %d\tVoltage: %dmV\n", adc_raw_value, voltage);
} else {
printf("Failed to convert raw value to voltage\n");
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
// 释放校准方案资源
// ESP_ERROR_CHECK(adc_cali_delete_scheme(adc_cali_handle));
// 释放 ADC 单元资源(此代码不会执行到此处,仅为示例)
// ESP_ERROR_CHECK(adc_oneshot_unit_deinit(adc1_handle));
}
📒模数转换器 (ADC) 连续转换模式驱动
- 可以参考例程:
v5.4\esp-idf\examples\peripherals\adc\continuous_read
- ✨连续转换模式需要注意,输出数据格式,只能选择类型2:
ADC_DIGI_OUTPUT_FORMAT_TYPE2
,否则,使用类型1,烧录后会报错:
单通道转换模式和连续多通道转换模式共用驱动实现程序
#include <stdio.h>
#include "driver/gpio.h"
#include "esp_adc/adc_continuous.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_err.h"
#define TAG "ADC_CAL"
#define DEFAULT_VREF 1100 // 默认内部参考电压 (mV)
#define NO_OF_SAMPLES 64 // 采样次数
#define ADC1_CHANNEL ADC_CHANNEL_6 // 使用 ADC1 通道 6 (GPIO 6)
#define ADC_ATTEN ADC_ATTEN_DB_12 // 衰减值
#define ADC_BITWIDTH ADC_BITWIDTH_12 // 位宽
#define ADC_CONV_MODE ADC_CONV_SINGLE_UNIT_1 // 使用 ADC1 单通道模式
#define DMA_BUFFER_SIZE 8 // DMA 缓冲区大小
#define SAMPLE_RATE 10000 // 采样率 (Hz)
//#define oneshot 1 //oneshot模式启用
#define continuous 1 //连续转换模式启用
void app_main(void) {
#ifdef oneshot
// 初始化 ADC 单元
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = ADC_UNIT_1, // 使用 ADC1
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config, &adc1_handle));
// 初始化 ADC 单元
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_unit_init_cfg_t init_config = {
.unit_id = ADC_UNIT_1, // 使用 ADC1
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config, &adc1_handle));
// 配置 ADC 通道
adc_oneshot_chan_cfg_t channel_config = {
.atten = ADC_ATTEN, // 衰减值
.bitwidth = ADC_BITWIDTH, // 位宽
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, ADC1_CHANNEL, &channel_config));
#elif continuous
// 初始化 ADC 连续模式句柄
adc_continuous_handle_t adc_handle = NULL;
adc_continuous_handle_cfg_t handle_cfg = {
.max_store_buf_size = DMA_BUFFER_SIZE, // DMA 缓冲区大小
.conv_frame_size = DMA_BUFFER_SIZE, // 每帧数据大小
};
ESP_ERROR_CHECK(adc_continuous_new_handle(&handle_cfg, &adc_handle));
// 配置 ADC 通道
adc_digi_pattern_config_t adc_pattern_ch[2] = {
{
.atten = ADC_ATTEN,
.channel = ADC_CHANNEL_6,
.unit = ADC_UNIT_1,
.bit_width = ADC_BITWIDTH,
},
{
.atten = ADC_ATTEN,
.channel = ADC_CHANNEL_7,
.unit = ADC_UNIT_1,
.bit_width = ADC_BITWIDTH,
},
};
// 配置 ADC 连续模式
adc_continuous_config_t adc_config = {
.pattern_num = 2, // 使用 2 个通道
.adc_pattern = adc_pattern_ch,
.sample_freq_hz = SAMPLE_RATE, // 采样率
.conv_mode = ADC_CONV_MODE, // 转换模式
.format = ADC_DIGI_OUTPUT_FORMAT_TYPE2, // 输出格式
};
ESP_ERROR_CHECK(adc_continuous_config(adc_handle, &adc_config));
// 启动连续采样
ESP_ERROR_CHECK(adc_continuous_start(adc_handle));
ESP_LOGI(TAG, "ADC continuous mode started");
// 读取采样数据
uint8_t buffer[DMA_BUFFER_SIZE] = {0};
#endif
while (1) {
#ifdef oneshot
int adc_raw_value = 0;
ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, ADC1_CHANNEL, &adc_raw_value));
printf("ADC Raw Value: %d\n", adc_raw_value);
// 将原始值转换为电压
int voltage = 0;
if (adc_cali_raw_to_voltage(adc_cali_handle, adc_raw_value, &voltage) == ESP_OK) {
printf("ADC Raw Value: %d\tVoltage: %dmV\n", adc_raw_value, voltage);
} else {
printf("Failed to convert raw value to voltage\n");
}
#elif continuous
// 读取采样数据
uint32_t ret_num = 0;
esp_err_t ret = adc_continuous_read(adc_handle, buffer, DMA_BUFFER_SIZE, &ret_num, 0);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "Read %"PRIu32" bytes of data", ret_num);
// 处理采样数据(例如打印或存储)
// for (int i = 0; i < ret_num; i += 2) { // 假设每个采样点占 2 字节
// uint16_t adc_value = (buffer[i + 1] << 8) | buffer[i];
// printf("ADC Value: %d\n", adc_value);
// }
// 解析 TYPE2 格式的数据
for (int i = 0; i < ret_num; i += 2) { // 每个采样点占 2 字节
uint16_t adc_value = (buffer[i + 1] << 8) | buffer[i];
uint8_t unit = (adc_value >> 12) & 0x0F; // 提取单元标识
uint16_t raw_data = adc_value & 0x0FFF; // 提取 12 位原始数据
printf("Unit: %d, Raw Data: %d\n", unit, raw_data);
}
} else {
ESP_LOGE(TAG, "Failed to read data: %d", ret);
}
#endif
vTaskDelay(pdMS_TO_TICKS(1000));
}
// 单采样释放校准方案资源
// ESP_ERROR_CHECK(adc_cali_delete_scheme(adc_cali_handle));
// 释放 ADC 单元资源(此代码不会执行到此处,仅为示例)
// ESP_ERROR_CHECK(adc_oneshot_unit_deinit(adc1_handle));
// 连续采样停止采样并释放资源
// ESP_ERROR_CHECK(adc_continuous_stop(adc_handle));
// ESP_ERROR_CHECK(adc_continuous_deinit(adc_handle));
// ESP_LOGI(TAG, "ADC continuous mode stopped");
}
- 🔖CMakeLists.txt:
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
REQUIRES driver esp_adc)