【ESP32-IDF】基础外设开发3:ADC(IDF5.x新版驱动)
本文介绍了ESP32的ADC功能,重点分析了其硬件架构和工作原理。ESP32的ADC系统由模拟前端和两套控制器组成,支持低功耗和高性能两种工作模式。RTC ADC控制器适用于低频信号采集,可在低功耗模式下运行;数字ADC控制器则支持高速采样和DMA传输。文章还详细说明了ADC引脚分配、类型定义及常用API,并特别指出ADC2与Wi-Fi模块的资源冲突问题。这些内容为开发者使用ESP32进行模拟信号
系列文章目录
持续更新…
前言
在嵌入式系统中,模拟信号的采集是日常任务之一,尤其在物联网设备、传感器应用等领域,如何将模拟信号转换为数字信号进行处理至关重要。ESP32 提供了丰富的 ADC(模数转换器)功能,能够通过多个输入通道,采集传感器信号并转换为数字值。本篇博文将详细介绍 ESP32 的 ADC 功能,讲解其硬件原理、常见配置及 API 用法,帮助开发者更好地进行模拟信号的采集和处理。
参考文档:ESP32-S3技术参考手册;ESP32-S3编程指南
一、ADC概述
ESP32 的 ADC 系统由模拟前端与两套控制器组成,前端统一完成信号采样与逐次逼近转换,控制器则决定采样时序、触发方式、数据缓存及搬运策略。两条通路共享同一物理 ADC 核心,但分属不同电源域:RTC ADC 控制器位于 RTC 低功耗域,支持在 CPU 深睡或频繁唤醒情况下进行低速测量,适合与 ULP 协处理器配合实现定期检测;数字 ADC 控制器位于数字高性能域,支持高速采样、DMA 传输、多通道扫描以及 I2S 总线输出,适用于波形分析和音频级数据采集。
SAR ADC 概图
SAR ADC 的功能概况
1.RTC ADC控制器
该控制器常驻 RTC 域,可在低功耗模式下维持运行,并能通过软件触发、RTC 状态机信号或 ULP 协处理器指令启动采样,测量结果保存在 RTC 寄存器中供读取。它的典型特性是功耗低、延迟可控,适用于环境传感器、电池电压监测等低频任务。虽然本身不直接支持连续扫描或 DMA,但可以借助 ULP 定期触发实现多通道轮询。IDF 的 oneshot 驱动在 ESP32-S3 上即以 RTC 控制器为时钟源,支持衰减(0/2.5/6/11 dB)和位宽(9~12 位)配置,并可利用 eFuse 校准数据将码值换算成 mV 级电压。
RTC SAR ADC 的功能概况
2.数字 ADC控制器
该控制器运行在数字域,面向高吞吐场景,可配置单通道、扫描或交替模式采集。内部扫描表最多支持 16 条规则(包含通道号、衰减、位宽等),按表顺序轮询采样,并将每个样本与通道号、单元号等元信息打包输出。数据可经 I2S 总线或 DMA 直接搬运至内存,实现高速批量采集。IDF 的 continuous 驱动对接该通路,输出结构化帧(如 adc_digi_output_data_t),便于在环形缓冲中解析和处理。数字 ADC 控制器还优化了触发与交错采样机制,支持同时采集 ADC1 与 ADC2,提升带宽和采样均匀性。
3.ADC引脚
ESP32 的 ADC 引脚固定映射至通道,不可通过 GPIO 矩阵更改。ADC1 提供 8 个通道(GPIO3239),ADC2 提供 10 个通道(GPIO0、2、4、1215、25~27)。需注意 ADC2 与 Wi-Fi 模块共享硬件资源,在 Wi-Fi 工作期间普通应用无法使用 ADC2;在使用 ADC2 时,需通过 esp_wifi_stop() 停止 Wi-Fi 功能,确保 ADC2 可用。,因此对高速连续采样或双单元模式需提前规划资源冲突。选择通道时应结合衰减档位和位宽配置,并参考数据手册中的“满量程电压”曲线,确保量程与分辨率满足需求。
二、ADC类型定义及常用API
ESP32-S3 ADC类型定义
//=============================================================================
// ESP32-S/ESP32-S3 ADC 相关类型定义
// 收录自 adc_common.h / adc.h / adc_types.h
// 说明:通道与 GPIO 为固定映射;ADC2 可能与 Wi-Fi 共用,注意仲裁与冲突。
//=============================================================================
#include <stdint.h>
#include <stdbool.h>
//=============================================================================
// 1) ADC 单元类型
//=============================================================================
typedef enum {
ADC_UNIT_1 = 1, // SAR ADC1
ADC_UNIT_2 = 2, // SAR ADC2
ADC_UNIT_BOTH = 3, // 同时使用 ADC1 与 ADC2(数字控制器/连续采样)
ADC_UNIT_ALTER = 7, // ADC1 与 ADC2 交替采样
ADC_UNIT_MAX
} adc_unit_t;
//=============================================================================
// 2) 通用通道类型(逻辑通道号)
// 注:具体到 ADC1/ADC2 的映射见下文 adc1_channel_t / adc2_channel_t
//=============================================================================
typedef enum {
ADC_CHANNEL_0 = 0,
ADC_CHANNEL_1,
ADC_CHANNEL_2,
ADC_CHANNEL_3,
ADC_CHANNEL_4,
ADC_CHANNEL_5,
ADC_CHANNEL_6,
ADC_CHANNEL_7,
ADC_CHANNEL_8,
ADC_CHANNEL_9,
ADC_CHANNEL_MAX
} adc_channel_t;
//=============================================================================
// 3) ADC1 通道类型(与 GPIO 的固定对应,目标不同映射不同)
// * 经典 ESP32: CH0~7 分别映射 GPIO36~39, 32~35
// * ESP32-S2/S3: CH0~7 -> GPIO1~8;部分芯片还包含 CH8/CH9 -> GPIO9/10
//=============================================================================
typedef enum {
ADC1_CHANNEL_0 = 0,
ADC1_CHANNEL_1,
ADC1_CHANNEL_2,
ADC1_CHANNEL_3,
ADC1_CHANNEL_4,
ADC1_CHANNEL_5,
ADC1_CHANNEL_6,
ADC1_CHANNEL_7,
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
ADC1_CHANNEL_8,
ADC1_CHANNEL_9,
#endif
ADC1_CHANNEL_MAX
} adc1_channel_t;
//=============================================================================
// 4) ADC2 通道类型(与 GPIO 的固定对应;常与 Wi-Fi 共用,需仲裁/避让)
// * 经典 ESP32: CH0~9 -> GPIO4,0,2,15,13,12,14,27,25,26
// * ESP32-S2/S3: CH0~9 -> GPIO11~20(参考目标芯片引脚图)
//=============================================================================
typedef enum {
ADC2_CHANNEL_0 = 0,
ADC2_CHANNEL_1,
ADC2_CHANNEL_2,
ADC2_CHANNEL_3,
ADC2_CHANNEL_4,
ADC2_CHANNEL_5,
ADC2_CHANNEL_6,
ADC2_CHANNEL_7,
ADC2_CHANNEL_8,
ADC2_CHANNEL_9,
ADC2_CHANNEL_MAX
} adc2_channel_t;
//=============================================================================
// 5) 衰减系数(影响满量程电压 / 量程,需要配合标定)
//=============================================================================
typedef enum {
ADC_ATTEN_DB_0 = 0, // 约 0~0.8V
ADC_ATTEN_DB_2_5, // 约 0~1.1V
ADC_ATTEN_DB_6, // 约 0~1.35V
ADC_ATTEN_DB_11, // 约 0~2.6V
ADC_ATTEN_MAX
} adc_atten_t;
//=============================================================================
// 6) I2S 数据源选择(数字控制器经 DMA 输出到 I2S 时的数据来源)
//=============================================================================
typedef enum {
ADC_I2S_DATA_SRC_IO_SIG = 0, // GPIO 模拟矩阵信号
ADC_I2S_DATA_SRC_ADC, // 来自 ADC
ADC_I2S_DATA_SRC_MAX
} adc_i2s_source_t;
//=============================================================================
// 7) 位宽(采样有效位数)
// 典型默认 12bit;部分目标支持 9/10/11bit 配置;S2 支持 13bit
//=============================================================================
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;
//=============================================================================
// 8) 数字控制器转换模式(连续采样 / DMA 扫描模式)
//=============================================================================
typedef enum {
ADC_CONV_SINGLE_UNIT_1 = 1, // 仅 ADC1
ADC_CONV_SINGLE_UNIT_2, // 仅 ADC2
ADC_CONV_BOTH_UNIT, // 同步:ADC1+ADC2 同时采样
ADC_CONV_ALTER_UNIT = 7 // 交替:ADC1 与 ADC2 轮流采样
} adc_digi_convert_mode_t;
//=============================================================================
// 9) 数字控制器「转换规则表项」(pattern)
// 指定每一步采样的 (atten/bit_width/channel/unit),最多 16 项轮询
//=============================================================================
typedef struct {
uint8_t atten : 2; // adc_atten_t
uint8_t bit_width : 2; // adc_bits_width_t(某些目标保留)
uint8_t channel : 4; // 0~9
uint8_t unit : 1; // 0: ADC1, 1: ADC2(Type2 格式需要)
} adc_digi_pattern_table_t;
//=============================================================================
// 10) 数字控制器输出格式(写入 DMA 的单样本数据位宽组织)
//=============================================================================
typedef enum {
ADC_DIGI_FORMAT_12BIT = 0, // 12 位数据格式
ADC_DIGI_FORMAT_11BIT // 11 位数据格式(带 unit 等元信息)
} adc_digi_output_format_t;
//=============================================================================
// 11) 数字控制器输出数据(DMA 缓冲中单样本的解析结构)
// Type1:12bit 数据 + 4bit 通道
// Type2:11bit 数据 + 4bit 通道 + 1bit 单元(ADC1/ADC2)
//=============================================================================
typedef union {
struct { // 与 ADC_DIGI_FORMAT_12BIT 对应
uint16_t data : 12;
uint16_t channel : 4;
} type1;
struct { // 与 ADC_DIGI_FORMAT_11BIT 对应(S2/S3 常用)
uint16_t data : 11;
uint16_t channel : 4;
uint16_t unit : 1; // 0:ADC1 1:ADC2
} type2;
uint16_t val;
} adc_digi_output_data_t;
//=============================================================================
// 12) 数字控制器时钟配置(连续采样的采样时钟源与分频)
// 采样时钟 = (APLL 或 APB) / (div_num + div_a/div_b + 1)
//=============================================================================
typedef struct {
bool use_apll; // true: APLL;false: APB
uint32_t div_num; // 1~255
uint32_t div_b; // 1~63
uint32_t div_a; // 0~63
} adc_digi_clk_t;
//=============================================================================
// 13) 数字控制器总配置(DMA 模式)
// 启动后按 pattern 表循环扫描;可设置转换次数限制/格式/时钟等
//=============================================================================
typedef struct {
uint32_t conv_limit_en; // 使能采样次数限制
uint32_t conv_limit_num; // 限制次数(1~255)
uint32_t adc_pattern_len; // pattern 项个数(<=16)
const adc_digi_pattern_table_t *adc_pattern; // pattern 表
adc_digi_convert_mode_t conv_mode; // 转换模式
adc_digi_output_format_t format; // 输出格式(11/12bit)
adc_digi_clk_t dig_clk; // 采样时钟
} adc_digi_config_t;
//=============================================================================
// 14) ADC2 仲裁模式(当 RTC 控制器 / 数字控制器 / Wi-Fi 同时请求 ADC2)
//=============================================================================
typedef enum {
ADC_ARB_MODE_SHIELD = 0, // 屏蔽一种客户端
ADC_ARB_MODE_FIX, // 固定优先级
ADC_ARB_MODE_LOOP // 循环轮转优先级
} adc_arbiter_mode_t;
// 仲裁器配置(仅 ADC2)
typedef struct {
adc_arbiter_mode_t mode; // 工作模式(仅 ADC2)
uint8_t rtc_pri; // RTC 控制器优先级(0~2,数值大优先)
uint8_t dig_pri; // 数字控制器优先级(0~2)
uint8_t pwdet_pri; // Wi-Fi 功率检测优先级(0~2)
} adc_arbiter_t;
//=============================================================================
// 15) 数字控制器中断类型(DMA 模式)
//=============================================================================
typedef enum {
ADC_DIGI_INTR_MASK_MONITOR = 0x1, // 监视器触发
ADC_DIGI_INTR_MASK_MEAS_DONE= 0x2, // 一次测量完成
ADC_DIGI_INTR_MASK_ALL = 0x3
} adc_digi_intr_t;
//=============================================================================
// 16) 数字控制器滤波器索引(可对若干通道使能 IIR 滤波)
//=============================================================================
typedef enum {
ADC_DIGI_FILTER_IDX0 = 0,
ADC_DIGI_FILTER_IDX1,
ADC_DIGI_FILTER_IDX_MAX
} adc_digi_filter_idx_t;
// 滤波模式(IIR 一阶,2/4/8/16/64 分母系数,数值越大平滑越强)
typedef enum {
ADC_DIGI_FILTER_IIR_2 = 0, // 系数 2
ADC_DIGI_FILTER_IIR_4, // 系数 4
ADC_DIGI_FILTER_IIR_8, // 系数 8
ADC_DIGI_FILTER_IIR_16, // 系数 16
ADC_DIGI_FILTER_IIR_64, // 系数 64
ADC_DIGI_FILTER_IIR_MAX
} adc_digi_filter_mode_t;
// 滤波器配置(绑定到某个单元/通道)
typedef struct {
adc_unit_t adc_unit; // ADC1/ADC2
adc_channel_t channel; // 目标通道
adc_digi_filter_mode_t mode; // IIR 模式
} adc_digi_filter_t;
//=============================================================================
// 17) 数字控制器监视器(阈值比较器)
//=============================================================================
typedef enum {
ADC_DIGI_MONITOR_IDX0 = 0,
ADC_DIGI_MONITOR_IDX1,
ADC_DIGI_MONITOR_IDX_MAX
} adc_digi_monitor_idx_t;
typedef enum {
ADC_DIGI_MONITOR_HIGH = 0, // 大于阈值触发
ADC_DIGI_MONITOR_LOW, // 小于阈值触发
ADC_DIGI_MONITOR_MAX
} adc_digi_monitor_mode_t;
// 监视器配置(越界即触发中断)
typedef struct {
adc_unit_t adc_unit; // ADC1/ADC2
adc_channel_t channel; // 目标通道
adc_digi_monitor_mode_t mode; // 触发模式
uint32_t threshold; // 阈值(按当前输出位宽)
} adc_digi_monitor_t;
//=============================================================================
// 18) I2S 编码格式(ADC->DMA 搬运到 I2S 的打包方式,对应 11/12bit)
//=============================================================================
typedef enum {
ADC_ENCODE_12BIT = 0, // [15:12] 通道,[11:0] 数据
ADC_ENCODE_11BIT // [15] 单元,[14:11] 通道,[10:0] 数据
} adc_i2s_encode_t;
//=============================================================================
// 备注:本文件只罗列“类型定义”。实际使用还需配套 IDF 驱动:
// - 单次采样:adc_oneshot_*(RTC 控制器通路,适合低功耗/低频)
// - 连续采样:adc_continuous_* / adc_digi_*(数字控制器+DMA,适合多通道/高吞吐)
// - 若使用 ADC2 且启用 Wi-Fi,请结合 adc_arbiter_* 做优先级或避让规划。
//=============================================================================
ADC常用API
//=============================================================================
// ESP32-S3 ADC 常用 API 列表
//=============================================================================
// 初始化 ADC 单次采样单元(ADC1 或 ADC2)
// 参数:
// - init_cfg:指向 adc_oneshot_unit_init_cfg_t 结构体的指针,
// 该结构体包含初始化配置,如 ADC 单元 ID 和 ULP 模式。
// - unit_handle:指向 adc_oneshot_unit_handle_t 类型的指针,用于返回创建的 ADC 单元句柄。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_NO_MEM:内存不足。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
esp_err_t adc_oneshot_new_unit(const adc_oneshot_unit_init_cfg_t *init_cfg, adc_oneshot_unit_handle_t *unit_handle);
// 删除 ADC 单次采样单元
// 参数:
// - unit_handle:要删除的 ADC 单元句柄。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
esp_err_t adc_oneshot_del_unit(adc_oneshot_unit_handle_t unit_handle);
// 配置 ADC 通道属性(衰减、位宽等)
// 参数:
// - unit_handle:要配置的 ADC 单元句柄。
// - chan_cfg:指向 adc_oneshot_chan_cfg_t 结构体的指针,
// 该结构体包含通道配置,如衰减和位宽。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
esp_err_t adc_oneshot_config_channel(adc_oneshot_unit_handle_t unit_handle, const adc_oneshot_chan_cfg_t *chan_cfg);
// 读取 ADC 通道的原始数值
// 参数:
// - unit_handle:要读取的 ADC 单元句柄。
// - channel:要读取的 ADC 通道。
// - raw_value:指向 int 类型的指针,用于返回读取到的原始值。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
// - ESP_ERR_TIMEOUT:操作超时。
esp_err_t adc_oneshot_read(adc_oneshot_unit_handle_t unit_handle, adc_channel_t channel, int *raw_value);
// 初始化 ADC 连续采样句柄
// 参数:
// - cfg:指向 adc_continuous_handle_cfg_t 结构体的指针,
// 该结构体包含连续采样配置,如最大存储缓冲区大小。
// - handle:指向 adc_continuous_handle_t 类型的指针,用于返回创建的 ADC 连续采样句柄。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_NO_MEM:内存不足。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
esp_err_t adc_continuous_new_handle(const adc_continuous_handle_cfg_t *cfg, adc_continuous_handle_t *handle);
// 删除 ADC 连续采样句柄
// 参数:
// - handle:要删除的 ADC 连续采样句柄。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
esp_err_t adc_continuous_deinit(adc_continuous_handle_t handle);
// 配置 ADC 连续采样通道属性
// 参数:
// - handle:要配置的 ADC 连续采样句柄。
// - chan_cfg:指向 adc_continuous_chan_cfg_t 结构体的指针,
// 该结构体包含通道配置,如衰减和位宽。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
esp_err_t adc_continuous_config_channel(adc_continuous_handle_t handle, const adc_continuous_chan_cfg_t *chan_cfg);
// 启动 ADC 连续采样
// 参数:
// - handle:要启动的 ADC 连续采样句柄。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
esp_err_t adc_continuous_start(adc_continuous_handle_t handle);
// 停止 ADC 连续采样
// 参数:
// - handle:要停止的 ADC 连续采样句柄。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
esp_err_t adc_continuous_stop(adc_continuous_handle_t handle);
// 读取 ADC 连续采样结果
// 参数:
// - handle:要读取的 ADC 连续采样句柄。
// - buffer:指向存储读取数据的缓冲区。
// - size:缓冲区的大小。
// - bytes_read:指向 size_t 类型的指针,用于返回实际读取的字节数。
// - ticks_to_wait:等待时间,单位为系统节拍。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
// - ESP_ERR_TIMEOUT:操作超时。
// - ESP_ERR_NO_MEM:内存不足。
esp_err_t adc_continuous_read(adc_continuous_handle_t handle, void *buffer, size_t size, size_t *bytes_read, TickType_t ticks_to_wait);
// 获取 ADC 校准方案(线性拟合)
// 参数:
// - config:指向 adc_cali_line_fitting_config_t 结构体的指针,
// 该结构体包含校准配置,如衰减和位宽。
// - handle:指向 adc_cali_handle_t 类型的指针,用于返回创建的校准句柄。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_NO_MEM:内存不足。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
esp_err_t adc_cali_create_scheme_line_fitting(const adc_cali_line_fitting_config_t *config, adc_cali_handle_t *handle);
// 删除 ADC 校准方案
// 参数:
// - handle:要删除的校准句柄。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
esp_err_t adc_cali_delete_scheme(adc_cali_handle_t handle);
// 将 ADC 原始值转换为电压(单位:mV)
// 参数:
// - raw_value:ADC 读取到的原始值。
// - chars:指向 esp_adc_cal_characteristics_t 结构体的指针,
// 该结构体包含校准特性,如 ADC 宽度和衰减。
// - voltage:指向 uint32_t 类型的指针,用于返回转换后的电压值。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
// - ESP_ERR_TIMEOUT:操作超时。
esp_err_t esp_adc_cal_raw_to_voltage(int raw_value, const esp_adc_cal_characteristics_t *chars, uint32_t *voltage);
// 获取 ADC 校准特性结构体
// 参数:
// - config:指向 esp_adc_cal_characterize_config_t 结构体的指针,
// 该结构体包含校准配置,如 ADC 宽度和衰减。
// - chars:指向 esp_adc_cal_characteristics_t 结构体的指针,
// 用于返回校准特性。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
// - ESP_ERR_TIMEOUT:操作超时。
esp_err_t esp_adc_cal_characterize(const esp_adc_cal_characterize_config_t *config, esp_adc_cal_characteristics_t *chars);
// 检查 ADC 校准方案支持情况
// 参数:
// - scheme:要检查的校准方案。
// - is_supported:指向 bool 类型的指针,用于返回是否支持该方案。
// 返回值:
// - ESP_OK:成功。
// - ESP_ERR_INVALID_ARG:参数无效。
// - ESP_ERR_INVALID_STATE:当前状态不允许操作。
// - ESP_ERR_TIMEOUT:操作超时。
esp_err_t adc_cali_check_scheme(adc_cali_scheme_t scheme, bool *is_supported);
三、ADC示例程序(ESP32-S3)
ADC 单次采样模式示例
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_adc/adc_oneshot.h"
static adc_oneshot_unit_handle_t adc_handle;
void ADC_Oneshot_Task(void *pvParameter)
{
int raw_value;
while (1)
{
// 读取 ADC1 通道 9 的原始值
esp_err_t ret = adc_oneshot_read(adc_handle, ADC_CHANNEL_9, &raw_value);
if (ret == ESP_OK)
{
ESP_LOGI("ADC", "ADC1_CH9 raw=%d", raw_value);
}
else
{
ESP_LOGW("ADC", "ADC read failed");
}
vTaskDelay(pdMS_TO_TICKS(1000)); // 每秒读取一次
}
}
void ADC_Init()
{
// 初始化 ADC1 单次采样单元
adc_oneshot_unit_init_cfg_t init_cfg = {
.unit_id = ADC_UNIT_1,
.ulp_mode = ADC_ULP_MODE_DISABLE,
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_cfg, &adc_handle));
// 配置 ADC1 通道 9 的属性(衰减、位宽等)
adc_oneshot_chan_cfg_t chan_cfg = {
.atten = ADC_ATTEN_DB_11, // 约 0~2.6V
.bitwidth = ADC_WIDTH_BIT_12, // 12 位分辨率
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, ADC_CHANNEL_9, &chan_cfg));
}
void app_main(void)
{
ADC_Init();
xTaskCreatePinnedToCore(ADC_Oneshot_Task, "ADC Oneshot Task", 2048, NULL, 5, NULL, 0);
}
初始化 ADC 单次采样单元:
使用 adc_oneshot_new_unit() 创建 ADC 单次采样单元句柄。
配置 adc_oneshot_unit_init_cfg_t 结构体,指定 ADC 单元 ID 和 ULP 模式。
配置 ADC 通道属性:使用 adc_oneshot_config_channel() 配置 ADC 通道的衰减和位宽。在此示例中,选择 ADC1 的通道 9,衰减设置为 11 dB(约 0~2.6V),位宽设置为 12 位。
读取 ADC 数据:在任务中使用 adc_oneshot_read() 函数读取 ADC 通道的原始值。读取成功后,通过日志输出原始数据。
任务调度:使用 FreeRTOS 的 xTaskCreatePinnedToCore() 创建任务,定期读取 ADC 数据。
ADC 连续采样模式(DMA 模式)示例
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_adc/adc_continuous.h"
static adc_continuous_handle_t adc_handle;
void ADC_Continuous_Task(void *pvParameter)
{
uint8_t buf[256];
while (1)
{
uint32_t len = 0;
if (adc_continuous_read(adc_handle, buf, sizeof(buf), &len, 20 / portTICK_PERIOD_MS) == ESP_OK)
{
uint32_t samples = len / 4;
for (uint32_t i = 0; i < len; i += 4)
{
uint16_t raw = (buf[i] | (buf[i + 1] << 8)) & 0x0FFF; // 12位有效
uint8_t meta = buf[i + 2];
uint8_t ch = meta & 0x0F;
// unit 固定为 ADC1,可忽略 (meta >> 4)
if (i < 16)
{ // 仅打印前 4 个样本减少日志
ESP_LOGI("ADC", "ADC1_CH%d raw=%u", ch, raw);
}
}
// ESP_LOGI("ADC", "frame bytes=%lu samples=%lu", (unsigned long)len, (unsigned long)samples);
}
else
{
ESP_LOGW("ADC", "No data");
}
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
void ADC_Init()
{
// 仅使用 ADC1 连续模式
adc_digi_pattern_config_t pattern[1] = {
{.atten = ADC_ATTEN_DB_11,
.channel = ADC_CHANNEL_9, // 选择的 ADC1 通道 (GPIO 对应请查 pin 表)
.unit = ADC_UNIT_1,
.bit_width = ADC_BITWIDTH_12}};
adc_continuous_handle_cfg_t handle_cfg = {
.max_store_buf_size = 1024,
.conv_frame_size = 256, // 必须是 pattern_num * 单样本字节(4) 的整数倍;这里 256 OK
};
ESP_ERROR_CHECK(adc_continuous_new_handle(&handle_cfg, &adc_handle));
adc_continuous_config_t adc_cfg = {
.sample_freq_hz = 10000, // 10 kHz
.conv_mode = ADC_CONV_SINGLE_UNIT_1, // 只支持 ADC1
.format = ADC_DIGI_OUTPUT_FORMAT_TYPE2,
.pattern_num = 1,
.adc_pattern = pattern,
};
ESP_ERROR_CHECK(adc_continuous_config(adc_handle, &adc_cfg));
ESP_ERROR_CHECK(adc_continuous_start(adc_handle));
}
void app_main(void)
{
ADC_Init();
xTaskCreatePinnedToCore(ADC_Continuous_Task, "ADC Continuous Task", 4096, NULL, 5, NULL, 0);
}
adc_continuous_read() 函数将从硬件缓冲区读取 ADC 转换结果,并将其存储到用户提供的缓冲区中。每个转换结果包含多个字节,具体格式取决于配置的输出格式。在示例代码中,使用了 ADC_DIGI_OUTPUT_FORMAT_TYPE2 格式,该格式的每个转换结果由 4 个字节组成,具体结构如下:
字节 0 和 1:12 位原始数据(低字节在前,高字节在后),高 4 位为零。
字节 2:包含通道号和 ADC 单元信息。
低 4 位表示通道号(0~9)。
高 4 位表示 ADC 单元(0:ADC1,1:ADC2)。
字节 3:未使用,可忽略。
adc_continuous_read() 函数返回的数据是原始的 ADC 数值。要将其转换为实际电压值,需要根据配置的衰减和位宽进行计算。例如,使用以下公式:
Vout = Dout * Vmax / Dmax
Vout:输出电压(单位:伏特)。
Dout:ADC 原始数字值。
Vmax:最大可测输入模拟电压,取决于配置的衰减。
Dmax:ADC 的最大输出数字值,等于 2^bit_width - 1,其中 bit_width 是配置的位宽。
总结
本文详细介绍了 ESP32 的 ADC 功能,包括硬件原理、常见配置和 API 用法,帮助开发者更高效地采集和处理模拟信号。ESP32 提供了单次采样和连续采样模式,支持多通道配置和高频采样。通过相关 API,开发者可以灵活配置采样参数,并准确读取和转换模拟信号为数字值。希望本文对 ESP32 开发者有所帮助,欢迎讨论与反馈。
更多推荐



所有评论(0)