当前位置: 首页 > news >正文

【ESP32-IDF笔记】07-ADC 配置和使用

配置环境

Visual Studio Code :版本1.98.2
ESP32:ESP32-S3
ESP-IDF:V5.4

ESP32 ADC介绍

ADC介绍

ESP32-S3 集成了两个 12 位 SAR ADC,共支持 20 个模拟通道输入。为了实现更低功耗,ESP32-S3 的 ULP 协处理器也可以在睡眠方式下测量电压,此时,可通过设置阈值或其他触发方式唤醒 CPU。
在这里插入图片描述

如图 39.3-1 所示,SAR ADC 由四个专用控制器控制,包括:

  • 一个数字控制器,支持高性能多通道扫描和 DMA 连续转换,包括:

    – DIG ADC1 控制器 (Digital ADC1 Controller)

  • 两个 RTC 控制器,支持在低功耗模式下工作和单次转换,包括:

    – RTC ADC1 控制器 (RTC ADC1 Controller)

    – RTC ADC2 控制器 (RTC ADC2 Controller)

  • 一个内部控制器,即 PWDET 控制器 (Power/Peak Detect Controller),用于 RF 功率检测。注意,此控制器仅供 RF 内部使用

注意

ADC2_CH… 模拟功能不能和 Wi-Fi 同时使用。

ESP32-S3 的 DIG ADC2 控制器无法正常工作,该章节中已删除其相关信息,详见 。ESP32-S3 系列芯片勘误表

管脚分配

SAR ADC 管脚通过 IO MUX 与 GPIO1 ~ GPIO20、RTC_GPIO1 ~ RTC_GPIO20、触摸传感器接口、UART 接口、SPI接口、以及 USB_D- 和 USB_D+ 管脚复用。

在这里插入图片描述

ADC 特性

SAR ADC 具有以下特性:

  • 四个控制器均配有独立的 ADC Reader 模块,见图 39.3-2

  • 支持 DIG ADC1 控制器和 RTC ADC1 控制器通过软件选择的方式获取 SAR ADC1 的控制权

  • 支持 RTC ADC2 控制器和 PWDET 控制器通过仲裁的方式按照指定的仲裁方式,轮流获取 SAR ADC2 的控制权

  • 支持 12 位采样分辨率

  • 支持采集最多 20 个管脚上的模拟电压

  • RTC ADC1/2 控制器:

    • 支持单次转换
    • 支持在低功耗模式下工作,如 Deep-sleep
    • 可由 ULP 协处理器配置
  • DIG ADC1 控制器:

    • 配有多通道扫描控制模块,支持多通道扫描模式
    • 提供模式控制模块,支持单 SAR ADC 采样模式
    • 在多通道扫描模式下,支持自定义扫描通道顺序
    • 提供两个滤波器,滤波系数可配
    • 支持阈值监控,采样值大于设置的高阈值或小于设置的低阈值将产生中断
    • 支持 DMA

• PWDET 控制器:用于 RF 功率检测(仅供内部使用

SAR ADC 架构

SAR ADC 的主要元件与连接情况见图 39.3-2。
在这里插入图片描述

  • —>:时钟信号
  • 在这里插入图片描述:时钟分频器、多路器、以及时钟作用的模块

如图 39.3-2 所示,SAR ADC 模块主要包括以下部分:

  • SAR ADC1:可对 10 个通道进行电压检测;

  • SAR ADC2:可对 10 个通道进行电压检测;

  • 时钟管理 (Clock Management):对时钟源进行选择和分频:

    • DIG ADC1 控制器时钟源:可选择 APB_CLK 或 PLL_D2_CLK;

    • DIG ADC1 控制器分频时钟:

      • DIGADC_SARCLK:SAR ADC1、SAR ADC2、Digital Reader1 的工作时钟;其中控制DIGADC_SARCLK 分频的 DIG_SAR_DIV 的分频系数至少是 2 分频,且 DIGADC_SARCLK 的频率不能超过 5 MHz,具体配置见 APB_SARADC_SAR_CLK_DIV;
      • DIGADC_CLK:DIG ADC FSM1 的工作时钟。
    • RTC ADC 控制器时钟源:RTC_FAST_CLK

    • RTC ADC 控制器分频时钟:

      • RTCADC_SARCLK:SAR ADC1、SAR ADC2、RTC Reader1 和 RTC Reader2 的工作时钟;其中控制 RTCADC_SARCLK 分频的 RTC_SAR_DIV 的分频系数至少是 2 分频,且 RTCADC_SARCLK 的频率不能超过 5 MHz;
  • 仲裁器 (ADC2_ARBIT):用于选择使用 SAR ADC2 的控制器,可以是 RTC ADC2 控制器或 PWDET 控制器。仲裁器不仅仲裁控制信号,还会根据授权的控制器选择 SAR ADC2 的时钟。

  • 定时器 (Timer):DIG ADC1 专用定时器,可发起采样使能信号。

  • DIG ADC FSM1:用于

    • 接收定时器提供的采样使能信号
    • 根据样式表生成 SAR ADC1 的采样配置
    • 驱动 Digital Reader1 模块读取 ADC 采样值
    • 并将采样值传输到滤波器、存储器等后续数据处理模块
  • Filter:滤波器 0 和滤波器 1,可对目标通道的采样数据进行滤波处理。

  • Threshold Monitor:阈值监控器 0 和阈值监控器 1。可在采样值大于设定的高阈值,或小于设定的低阈值时触发中断。

  • 模式控制模块 (MODE CNTL):用于过滤定时器触发的采样信号,支持单 SAR ADC 采样模式。

  • Digital Reader1:由 DIG ADC FSM1 驱动,读取 SAR ADC1 的数值。

  • RTC Controller1/2:提供采样使能信号,根据配置驱动 RTC Reader1/2 模块读取 SAR ADC1/2 的采样值,并存储采样数据。

  • RTC Reader1/2:由 RTC Controller1/2 驱动,读取 SAR ADC1/2 的数值。

本章节数据是在 ADC 外接 100 nF 电容、输入为 DC 信号、25 °C 环境温度、Wi-Fi 关闭条件下的测量结果。

ADC 转换和衰减

SAR ADC 转换模拟信号时,通过该函数获取的 ADC 转换结果为原始数据。可以使用以下公式,根据 ADC 原始结果计算电压:

V o u t = D o u t ∗ V m a x / D m a x Vout = Dout * Vmax / Dmax Vout=DoutVmax/Dmax

Vout数字输出结果,代表电压。
DoutADC 原始数字读取结果。
Vmax可测量的最大模拟输入电压,与 ADC 衰减相关
Dmax输出 ADC 原始数字读取结果的最大值,即 2^位宽,位宽即之前配置的 adc\_oneshot\_chan\_cfg\_t::bitwidth​。

如需转换大于 Vref 的电压,信号输入 SAR ADC 前可进行衰减。衰减可配置为 0 dB、2.5 dB、6 dB 和 12dB。

衰减输入范围
0 dB0~1.1V
2.5 dB0~2.5V
6 dB0~2.2V
12dB0~3.3V

单次转换模式

简介

模数转换器集成于芯片,支持测量特定模拟 IO 管脚的模拟信号。

ESP32-S3 有 {SOC_ADC_PERIPH_NUM} 个 ADC 单元,可以在以下场景使用:

  • 生成 ADC 单次转换结果
  • 生成连续 ADC 转换结果

使用方法

资源分配

ADC 单次转换模式驱动基于 ESP32-S3 SAR ADC 模块实现,不同的 ESP 芯片可能拥有不同数量的独立 ADC。对于单次转换模式驱动而言,ADC 实例以 adc_oneshot_unit_handle_t​ 表示。

请设置所需的初始配置结构体 adc_oneshot_unit_init_cfg_t​ 安装 ADC 实例,具体如下:

  • adc_oneshot_unit_init_cfg_t::unit_id​ 选择 ADC。请参阅 技术规格书,了解对应 ADC 的专用模拟 IO 管脚。
  • adc_oneshot_unit_init_cfg_t::clk_src​ 选择 ADC 的时钟源。设置为 0 时,驱动程序将使用默认时钟源,详情请参阅 adc_oneshot_clk_src_t​。
  • adc_oneshot_unit_init_cfg_t::ulp_mode​ 设置是否支持 ADC 在 ULP 模式下工作。

完成 ADC 初始配置后,使用已设置的初始配置结构体 adc_oneshot_unit_init_cfg_t​ 调用 adc_oneshot_new_unit()​。如果分配成功,该函数将返回 ADC 单元实例句柄。

该函数可能因参数无效、内存不足等原因返回错误代码。比如,当要分配的 ADC 实例已经注册时,该函数会返回 ESP_ERR_NOT_FOUND​ 错误。可用 ADC 数量可通过 SOC_ADC_PERIPH_NUM​ 查看。

如果不再需要先前创建的 ADC 单元实例,请调用 adc_oneshot_del_unit()​ 回收该实例,相关的硬件和软件资源也会回收。

在普通单次转换模式下创建 ADC 单元实例句柄
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_unit_init_cfg_t init_config1 = {.unit_id = ADC_UNIT_1,.ulp_mode = ADC_ULP_MODE_DISABLE,
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));
回收 ADC 单元实例
ESP_ERROR_CHECK(adc_oneshot_del_unit(adc1_handle));

配置 ADC 单元实例

创建 ADC 单元实例后,请设置 adc_oneshot_chan_cfg_t​ 配置 ADC IO 以测量模拟信号,具体如下:

  • adc_oneshot_chan_cfg_t::atten​,ADC 衰减。请参阅 技术规格书 > ADC 特性​。
  • adc_oneshot_chan_cfg_t::bitwidth​,原始转换结果的位宽。

备注

ADC IO 及其对应的 ADC 通道编号,请参阅 技术规格书。

此外,可以使用 adc_continuous_io_to_channel()​ 和 adc_continuous_channel_to_io()​ 了解 ADC 通道和 ADC IO。

为使以上设置生效,请使用上述配置结构体调用 adc_oneshot_config_channel(),并指定要配置的 ADC 通道。函数 adc_oneshot_config_channel() 支持多次调用,以配置不同的 ADC 通道。驱动程序将在内部保存每个通道的配置。

配置两个 ADC 通道
adc_oneshot_chan_cfg_t config = {.bitwidth = ADC_BITWIDTH_DEFAULT,.atten = ADC_ATTEN_DB_12,
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN0, &config));
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN1, &config));

读取转换结果

完成上述配置后,ADC 即可测量来自配置好的 ADC 通道的模拟信号。调用 adc_oneshot_read()​ 可以获取 ADC 通道的原始转换结果。

  • adc_oneshot_read()​ 可安全使用。ADC 由其他驱动程序/外设共享,请参阅 硬件限制。函数 adc_oneshot_read()​ 使用互斥锁,避免与其他函数同时使用硬件,因此该函数不应在 ISR 上下文中使用。当 ADC 由其他驱动程序/外设占用时,该函数可能出错,并返回 ESP_ERR_TIMEOUT​ 错误。此时,ADC 原始结果无效。

该函数可能因参数无效而调用失败。

通过该函数获取的 ADC 转换结果为原始数据。可以使用以下公式,根据 ADC 原始结果计算电压:

Vout = Dout * Vmax / Dmax       (1)

其中:

Vout数字输出结果,代表电压。
DoutADC 原始数字读取结果。
Vmax可测量的最大模拟输入电压,与 ADC 衰减相关,请参考 技术参考手册 > 片上传感器与模拟信号处理​。
Dmax输出 ADC 原始数字读取结果的最大值,即 2^位宽,位宽即之前配置的 adc_oneshot_chan_cfg_t::bitwidth​。

若需进一步校准,将 ADC 原始结果转换为以 mV 为单位的电压数据,请参ADC 校准章节。

读取原始结果

ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN0, &adc_raw[0][0]));
ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN0, adc_raw[0][0]);ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN1, &adc_raw[0][1]));
ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN1, adc_raw[0][1]);

硬件限制

  • 随机数生成器 (RNG) 以 ADC 为输入源。使用 ADC 单次转换模式驱动从 RNG 生成随机数时,随机性会减弱。
  • 一个 ADC 单元每次只能在一种操作模式下运行,可以是连续模式或单次模式。adc_oneshot_start()​ 提供了保护措施。
  • Wi-Fi 也使用 ADC2,adc_oneshot_read()​ 提供了 Wi-Fi 驱动与 ADC 单次转换模式驱动间的保护。

电源管理

启用电源管理,即启用 CONFIG_PM_ENABLE 时,系统在空闲状态下可能会调整系统时钟频率。然而,ADC 单次转换模式驱动以轮询例程运行,adc_oneshot_read()​ 会不断检查 CPU 是否完成读取,直到函数返回。在此期间,ADC 单次转换模式驱动程序所在的任务不会受阻塞。因此,在读取时时钟频率保持稳定。

IRAM 安全

flash 写入/擦除、OTA 等原因都可能导致 cache 禁用,此时,默认不应运行任何 ADC 单次转换模式驱动 API。如果在禁用 cache 时执行了 ADC 单次转换模式驱动 API,可能会出现类似 Illegal Instruction​ 或 Load/Store Prohibited​ 的错误。

线程安全

  • adc_oneshot_new_unit()
  • adc_oneshot_config_channel()
  • adc_oneshot_read()

上述函数均为线程安全,使用时,可以直接从不同的 RTOS 任务中调用以上函数,无需额外锁保护。

  • adc_oneshot_del_unit()​ 非线程安全。此外,与上文中线程安全的函数一起调用该函数时,可能导致线程安全函数的调用出错。

API 参考

头文件

头文件

#include "esp_adc/adc_oneshot.h"

组件:components/esp_adc/include/esp_adc/adc_oneshot.h

REQUIRES esp_adc

功能API

创建特定 ADC 单元的句柄
adc_oneshot_new_unit esp_err_t (const adc_oneshot_unit_init_cfg_t *init_config, adc_oneshot_unit_handle_t *ret_unit)

参数:

  • init_config[in] 驱动程序初始配置

  • ret_unit[out] ADC 单元手柄

返回:

  • ESP_OK:成功时

  • ESP_ERR_INVALID_ARG:参数无效

  • ESP_ERR_NO_MEM:无内存

  • ESP_ERR_NOT_FOUND:要申请的 ADC 外设已在使用

  • ESP_FAIL : 时钟源未正确初始化

备注

此 API 是线程安全的。有关更多详细信息,请参阅 ADC 编程指南

设置 ADC oneshot 模式所需的配置

esp_err_t adc_oneshot_config_channel(adc_oneshot_unit_handle_t handle、adc_channel_t channel、const adc_oneshot_chan_cfg_t *config)

参数:

  • handle[in] ADC 手柄

  • channel – 要配置的 [in] ADC 通道

  • config[in] ADC 配置

返回:

  • ESP_OK:成功时

  • ESP_ERR_INVALID_ARG:参数无效

获取一个 ADC 转换原始结果
esp_err_t adc_oneshot_read(adc_oneshot_unit_handle_t 句柄、adc_channel_t chan、int *out_raw)

参数:

  • handle[in] ADC 手柄

  • chan[in] ADC 通道

  • out_raw[输出] ADC 转换原始结果

返回:* ESP_OK:成功时

  • ESP_ERR_INVALID_ARG:参数无效
  • ESP_ERR_TIMEOUT:超时,ADC 结果无效

注意:

不能在 ISR(中断) 上下文中调用此 API

删除 ADC 单元手柄)
esp_err_t adc_oneshot_del_unit(adc_oneshot_unit_handle_t handle )

参数:

  • handle[in] ADC 手柄

返回:* ESP_OK:成功时

  • ESP_ERR_INVALID_ARG:参数无效
  • ESP_ERR_NOT_FOUND:要放弃的 ADC 外设未使用
从给定的 GPIO 编号获取 ADC 通道
esp_err_t adc_oneshot_io_to_channel(int io_num, adc_unit_t *const unit_id, adc_channel_t *const channel )

参数:

  • io_num[in] GPIO 编号

  • unit_id[out] ADC 单元

  • channel[out] ADC 通道

返回:

  • ESP_OK:成功时

  • ESP_ERR_INVALID_ARG:参数无效

  • ESP_ERR_NOT_FOUND: IO 不是有效的 ADC 焊盘

从给定的 ADC 通道获取 GPIO 编号
esp_err_t adc_oneshot_channel_to_io(adc_unit_t unit_id、adc_channel_t channel、int *const io_num)

参数:

  • unit_id[in] ADC 单元

  • channel[in] ADC 通道

  • io_num[out] GPIO 编号

  • - – ESP_OK:成功时

    • ESP_ERR_INVALID_ARG:参数无效
获取 ADC 校准结果的便捷功能
esp_err_t adc_oneshot_get_calibrated_result(adc_oneshot_unit_handle_t 句柄、adc_cali_handle_t cali_handle、adc_channel_t chan、int *cali_result)

这是一个多合一功能,它确实:

  • oneshot 读取 ADC 原始结果
  • 校准原始结果并将其转换为校准结果(以 mV 为单位)

参数:

  • handle[在] ADC oneshot 句柄中,您应该调用 adc_oneshot_new_unit() 来获取此句柄

  • cali_handle – 在 ADC 校准手柄,您应该在 adc_cali_scheme.h 中调用 adc_cali_create_scheme_x() 来创建手柄

  • chan[in] ADC 通道

  • cali_result[out] 校准的 ADC 结果(以 mV 为单位)

返回:

  • ESP_OK 来自 adc_oneshot_read() 和 adc_cali_raw_to_voltage() 的其他返回错误

连续转换模式

简介

ESP32-S3 芯片集成了模数转换器 (ADC),支持测量特定模拟 IO 管脚的模拟信号。此外,ADC 还支持直接内存访问 (DMA) 功能,高效获取 ADC 转换结果。

ESP32-S3 具有 两 个 ADC 单元,可应用于以下场景:

  • 生成单次 ADC 转换结果
  • 生成连续 ADC 转换结果

ADC 连续转换模式驱动概念

ADC 连续转换模式驱动由多个转换帧组成。

  • 转换帧:一个转换帧包含多个转换结果。转换帧大小以字节为单位,在 adc_continuous_new_handle()​ 中配置。
  • 转换结果:一个转换结果包含多个字节,即 SOC_ADC_DIGI_RESULT_BYTES​。转换结果的数据结构由 adc_digi_output_data_t​ 定义,包括 ADC 单元、ADC 通道以及原始数据。

在这里插入图片描述

使用步骤

资源分配

ADC 连续转换模式驱动基于 ESP32-S3 SAR ADC 模块实现,不同的 ESP 目标芯片可能拥有不同数量的独立 ADC。

请按照以下步骤设置配置结构体 adc_continuous_handle_cfg_t​,创建 ADC 连续转换模式驱动的句柄:

  • adc_continuous_handle_cfg_t::max_store_buf_size​:以字节为单位设置最大缓冲池的大小,驱动程序将 ADC 转换结果保存到该缓冲池中。缓冲池已满时,新的转换将丢失。

  • adc_continuous_handle_cfg_t::conv_frame_size​:以字节为单位设置 ADC 转换帧大小。

  • adc_continuous_handle_cfg_t::flags​:设置可以改变驱动程序行为的标志。

    • flush_pool​:缓冲池满时自动清空缓冲池。

完成以上 ADC 配置后,使用已设置的配置结构体 adc_continuous_handle_cfg_t​ 调用 adc_continuous_new_handle()​。该函数可能将在特定情况下返回错误值,如无效参数、内存不足等。

函数返回 ESP_ERR_NOT_FOUND​ 时,表明 GDMA 空闲通道不足。

如果不再使用 ADC 连续转换模式驱动,请调用 adc_continuous_deinit()​ 将驱动去初始化。

IIR 滤波器

ADC 连续转换模式下支持使用两个 IIR 滤波器。请设置 adc_continuous_iir_filter_config_t​ 结构体并调用 adc_new_continuous_iir_filter()​,以创建 ADC IIR 滤波器。

  • adc_digi_filter_config_t::unit​:ADC 单元。
  • adc_digi_filter_config_t::channel​:将进行滤波的 ADC 通道。
  • adc_digi_filter_config_t::coeff​:滤波器系数。

调用 adc_del_continuous_iir_filter()​ 可以回收滤波器。

备注

在一个 ADC 通道上同时使用两个滤波器时,只有第一个滤波器会生效。

监视器

当 ADC 在连续转换模式下运行时,支持使用 2 个监视器。你可以在运行中的 ADC 通道上设置一到两个监视器阈值,一旦转换结果超出阈值,监视器将在每个采样循环中触发中断。请设置 adc_monitor_config_t​,并调用 adc_new_continuous_monitor()​ 以创建 ADC 监视器。

  • adc_monitor_config_t::adc_unit​:配置要监视的 ADC 通道所属的 ADC 单元。
  • adc_monitor_config_t::channel​:要监视的 ADC 通道。
  • adc_monitor_config_t::h_threshold​:高阈值,转换结果大于此值将触发中断,如果不使用此阈值,则将其设置为 -1。
  • adc_monitor_config_t::l_threshold​:低阈值,转换结果小于此值将触发中断,如果不使用此阈值,则将其设置为 -1。

创建监视器后,可以使用以下 API 操作监视器,构建你的应用程序。

  • adc_continuous_monitor_enable()​:启用监视器。
  • adc_continuous_monitor_disable()​:禁用监视器.
  • adc_monitor_register_callbacks()​:注册用户回调函数,在 ADC 转换结果超出阈值时,执行相应操作。
  • adc_del_continuous_monitor()​:删除监视器,释放资源。
初始化 ADC 连续转换模式驱动
adc_continuous_handle_t handle = NULL;
adc_continuous_handle_cfg_t adc_config = {.max_store_buf_size = 1024,.conv_frame_size = 256,
};
ESP_ERROR_CHECK(adc_continuous_new_handle(&adc_config, &handle));
回收 ADC 单元
ESP_ERROR_CHECK(adc_continuous_deinit(handle));

配置 ADC

初始化 ADC 连续转换模式驱动后,设置 adc_continuous_config_t​ 配置 ADC IO,测量模拟信号:

  • adc_continuous_config_t::pattern_num​:要使用的 ADC 通道数量。
  • adc_continuous_config_t::adc_pattern​:每个要使用的 ADC 通道的配置列表,请参阅下文描述。
  • adc_continuous_config_t::sample_freq_hz​:期望的 ADC 采样频率,单位为 Hz。
  • adc_continuous_config_t::conv_mode​:连续转换模式。
  • adc_continuous_config_t::format​:转换模式结果的输出格式。

按照以下步骤设置 adc_digi_pattern_config_t​:

  • adc_digi_pattern_config_t::atten​:ADC 衰减。请参阅 技术规格书 中的 ADC 特性​ 章节。
  • adc_digi_pattern_config_t::channel​:IO 对应的 ADC 通道号,请参阅下文注意事项。
  • adc_digi_pattern_config_t::unit​:IO 所属的 ADC 单元。
  • adc_digi_pattern_config_t::bit_width​:原始转换结果的位宽。

备注

对于 IO 对应的 ADC 通道号,请参阅 技术参考手册 获取 ADC IO 管脚的详细信息。另外,可以使用 adc_continuous_io_to_channel()​ 和 adc_continuous_channel_to_io()​ 获取 ADC 通道和 ADC IO 的对应关系。

为使这些设置生效,请使用上述配置结构体,调用 adc_continuous_config()​。此 API 可能由于 ESP_ERR_INVALID_ARG​ 等原因返回错误。当它返回 ESP\_ERR\_INVALID\_STATE​ 时,意味着 ADC 连续转换模式驱动已经启动,此时不应调用此 API。

请参考 ADC 连续转换模式示例 peripherals/adc/continuous_read,查看相应配置代码。

请调用 adc_continuous_iir_filter_enable()​ 或 adc_continuous_iir_filter_disable()​,以启用或禁用 ADC IIR 滤波器。

请调用 adc_continuous_monitor_enable()​ 或 adc_continuous_monitor_disable()​,以启用或禁用 ADC 监视器。

ADC 控制

启动和停止

调用 adc_continuous_start()​,将使 ADC 开始从配置好的 ADC 通道测量模拟信号,并生成转换结果。

相反,调用 adc_continuous_stop()​ 则会停止 ADC 转换。

ESP_ERROR_CHECK(adc_continuous_stop(handle));

注册事件回调

调用 adc_continuous_register_event_callbacks()​,可以将自己的函数链接到驱动程序的 ISR 中。通过 adc_continuous_evt_cbs_t​ 可查看所有支持的事件回调。

  • adc_continuous_evt_cbs_t::on_conv_done​:当一个转换帧完成时,触发此事件。
  • adc_continuous_evt_cbs_t::on_pool_ovf​:当内部缓冲池已满时,触发此事件,新的转换结果将丢失。

由于上述回调函数在 ISR 中调用,请确保回调函数适合在 ISR 上下文中运行,且这些回调不应涉及阻塞逻辑。回调函数的原型在 adc_continuous_callback_t​ 中声明。

在调用 adc_continuous_register_event_callbacks()​ 时,还可以通过参数 user_data​ 注册自己的上下文,该用户数据将直接传递给回调函数。

此回调函数可能由于 ESP_ERR_INVALID_ARG​ 等原因返回错误。启用 CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE 时,如果回调函数失败并报错,可能是因为回调函数不在内部 RAM 中,请查看错误日志了解详情。此外,如果回调函数出现 ESP_ERR_INVALID_STATE​ 错误,表明 ADC 连续转换模式驱动已经启动,此时不应添加回调。

转换完成事件

当驱动程序完成一次转换后,会触发 adc_continuous_evt_cbs_t::on_conv_done​ 事件,并填充事件数据。事件数据包含一个指向转换帧缓冲区的指针,以及转换帧缓冲区大小。要了解事件数据结构,请参阅 adc_continuous_evt_data_t​。

注意:

  1. 数据缓冲区 adc_continuous_evt_data_t::conv_frame_buffer 由驱动程序本身维护,请勿释放此内存。
  2. 启用 Kconfig 选项 CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE 时,注册的回调函数以及回调函数中调用的函数应放置在 IRAM 中,涉及的变量也应放置在内部 RAM 中。
缓冲池溢出事件

ADC 连续转换模式驱动使用内部缓冲池保存转换结果,缓冲池满时将发生缓冲池溢出事件。此时,驱动程序不会继续填充事件数据。缓冲池溢出通常是因为调用 adc_continuous_read()​ 从池中读取数据的速度远低于 ADC 转换的速度。

读取转换结果

调用 adc_continuous_start()​ 启动 ADC 连续转换,调用 adc_continuous_read()​ 可以获取 ADC 通道的转换结果。注意提供缓冲区,获取原始结果。

函数 adc_continuous_read()​ 每次都会尝试以期望长度读取转换结果。

  • 调用 adc_continuous_read()​ 可以请求读取指定长度的转换结果。但有时实际可用的转换结果可能少于请求长度,此时,函数仍会将数据从内部池移动到你提供的缓冲区中。因此,请查看 out_length​ 的值,了解实际移动到缓冲区中的转换结果数量。
  • 如果内部池中没有生成转换结果,函数将会阻塞一段时间,即 timeout_ms​,直到转换结果生成。如果始终没有转换结果生成,函数将返回 ESP_ERR_TIMEOUT​。
  • 如果 ADC 连续转换生成的结果填满了内部池,新产生的结果将丢失。下次调用 adc_continuous_read()​ 时,将返回 ESP_ERR_INVALID_STATE​,提示此情况发生。

此 API 提供了一个读取所有 ADC 连续转换结果的机会。

API 参考

头文件

头文件

#include "esp_adc/adc_continuous.h"

组件:components/esp_adc/include/esp_adc/adc_continuous.h

REQUIRES esp_adc

功能函数

初始化 ADC continuous driver 并获取其句柄
esp_err_t adc_continuous_new_handle(const adc_continuous_handle_cfg_t *hdl_config, adc_continuous_handle_t *ret_handle)

参数:

  • hdl_config – [in] 指向 ADC 初始化配置的指针。指。adc_continuous_handle_cfg_t

  • ret_handle[out] ADC 连续模式驱动器手柄

返回:

  • ESP_ERR_INVALID_ARG 如果参数组合无效。

  • ESP_ERR_NOT_FOUND 未找到具有指定标志的空闲中断

  • ESP_ERR_NO_MEM 如果内存不足

  • ESP_OK 成功时

设置 ADC 连续模式所需的配置
esp_err_t adc_continuous_config(adc_continuous_handle_t handle 、const adc_continuous_config_t *config)

参数:

  • handle[in] ADC 连续模式驱动器手柄

  • config – [in] 请参阅 。adc_digi_config_t

返回:

  • ESP_ERR_INVALID_STATE:Driver state 无效,此时不应调用此 API

  • ESP_ERR_INVALID_ARG:如果参数组合无效。

  • ESP_OK:成功时

注册回调
esp_err_t adc_continuous_register_event_callbacks(adc_continuous_handle_t handle, const adc_continuous_evt_cbs_t *cbs, void *user_data)

参数:

  • handle[in] ADC 连续模式驱动器手柄

  • cbs[in] 回调函数组

  • user_data[in] 用户数据,将直接投递给回调函数

返回:

  • ESP_OK:成功时

  • ESP_ERR_INVALID_ARG:参数无效

  • ESP_ERR_INVALID_STATE:Driver state 无效,此时不应调用此 API

注意:

  1. 用户可以通过调用此函数并将结构体中待注销的回调成员设置为 NULL 来注销之前注册的回调。cbs
  2. 启用 CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE 后,回调本身及其调用的函数应放置在 IRAM 中。涉及的变量 (包括 ) 也应该在内部 RAM 中。user_data
  3. 仅当 ADC 连续模式驱动程序未启动时,才能调用此 API。检查返回值以了解这一点。
启动 ADC
esp_err_t adc_continuous_start(adc_continuous_handle_t handle )

在连续模式下启动 ADC。在此之后,硬件开始工作

参数:

  • handle[in] ADC 连续模式驱动器手柄

返回:

  • ESP_ERR_INVALID_STATE Driver 状态无效。

  • ESP_OK 成功时

在连续模式下从 ADC 读取字节
esp_err_t adc_continuous_read(adc_continuous_handle_t handle, uint8_t *buf, uint32_t length_max, uint32_t *out_length、uint32_t timeout_ms)

参数:

  • handle[in] ADC 连续模式驱动器手柄

  • buf – [out] 要从 ADC 读取的转换结果缓冲区。建议转换为 for 。请参阅此头文件中的小节以了解此概念。adc_digi_output_data_tADC Conversion ResultsDriver Backgrounds

  • length_max[in] 从 ADC 读取的 Conversion Results 的预期长度,以字节为单位。

  • out_length[out] 通过此 API 从 ADC 读取的 Conversion Results 的实际长度,以字节为单位。

  • timeout_ms[in] 等待通过此 API 获取数据的时间(以毫秒为单位)。

返回:

  • ESP_ERR_INVALID_STATE Driver 状态无效。通常这意味着 ADC 采样率比任务处理率快。

  • ESP_ERR_TIMEOUT作超时

  • ESP_OK 成功时

停止 ADC
esp_err_t adc_continuous_stop(adc_continuous_handle_t handle )

停止 ADC。在此之后,硬件将停止工作。

参数:

  • handle[in] ADC 连续模式驱动器手柄

返回:

  • ESP_ERR_INVALID_STATE Driver 状态无效。

  • ESP_OK 成功时

取消初始化
esp_err_t adc_continuous_deinit(adc_continuous_handle_t handle )

取消初始化 ADC continuous driver。

参数:

  • handle[in] ADC 连续模式驱动器手柄

返回:* ESP_ERR_INVALID_STATE Driver 状态无效。

  • ESP_OK 成功时
刷新驱动程序内部池
esp_err_t adc_continuous_flush_pool(adc_continuous_handle_t 手柄)

参数:

  • handle[in] ADC 连续模式驱动器手柄

返回:

  • ESP_ERR_INVALID_STATE Driver 状态无效,则应在 API 处于 init 状态时调用此 API

  • ESP_ERR_INVALID_ARG:参数无效

  • ESP_OK 成功时

注意

  1. 不应在 ISR 上下文中调用此 API
从给定的 GPIO 编号获取 ADC 通道
esp_err_t adc_continuous_io_to_channel(int io_num, adc_unit_t *const unit_id, adc_channel_t *const channel )

参数:

  • io_num[in] GPIO 编号

  • unit_id[out] ADC 单元

  • channel[out] ADC 通道

返回:

  • ESP_OK:成功时

  • ESP_ERR_INVALID_ARG:参数无效

  • ESP_ERR_NOT_FOUND: IO 不是有效的 ADC 焊盘

从给定的 ADC 通道获取 GPIO 编号
esp_err_t adc_continuous_channel_to_io(adc_unit_t unit_id、adc_channel_t 通道、int *const io_num)

参数:

  • unit_id[in] ADC 单元

  • channel[in] ADC 通道

  • io_num[out] GPIO 编号

返回:

  • ESP_OK:成功时
  • ESP_ERR_INVALID_ARG:参数无效

ADC 校准

简介

在 ESP32-S3 中,模数转换器 (ADC) 比较输入的模拟电压和参考电压,以确定每一位数字输出结果。ESP32-S3 设计的 ADC 参考电压为 1100 mV。然而,不同芯片的真实参考电压可能会略有变化,范围在 1000 mV 到 1200 mV 之间。本文介绍了 ADC 校准驱动程序,可以降低参考电压不同带来的影响,获取更准确的输出结果。

安装和使用ADC 校准驱动程序的基本步骤

创建校准方案

ADC 校准驱动程序会提供 ADC 校准方案。对于驱动程序来说,每个 ADC 校准方案对应一个 ADC 校准句柄 adc_cali_handle_t​。

使用 adc_cali_check_scheme()​ 可以查看芯片支持的校准方案。若已了解芯片支持的校准方案,可以跳过该步骤,直接调用对应函数创建校准方案句柄。

使用自定义 ADC 校准方案时,可以选择调整函数 adc_cali_check_scheme()​,或直接跳过该步骤,调用自定义函数创建校准方案句柄。

ADC 校准曲线拟合方案

ESP32-S3 支持 ADC_CALI_SCHEME_VER_CURVE_FITTING​ 方案。要创建此方案,请先根据以下配置选项,设置 adc_cali_curve_fitting_config_t​。

  • adc_cali_curve_fitting_config_t::unit_id​,表示 ADC 原始结果来自哪个 ADC 单元。
  • adc_cali_curve_fitting_config_t::chan​,此选项保留以供扩展。校准方案仅因衰减程度而异,与通道选择无关。
  • adc_cali_curve_fitting_config_t::atten​,表示 ADC 原始结果的衰减程度。
  • adc_cali_curve_fitting_config_t::bitwidth​,表示 ADC 原始结果的位宽。

设置完上述配置结构体后,请调用 adc_cali_create_scheme_curve_fitting()​ 创建曲线拟合方案句柄。 由于 ESP_ERR_INVALID_ARG​ 或 ESP_ERR_NO_MEM​ 等原因,该函数调用可能失败。函数返回 ESP_ERR_NOT_SUPPORTED​ 时,说明你的开发板没有烧录校准方案所需的 eFuse 位。

与 eFuse 相关的 ADC 校准故障

函数 adc_cali_create_scheme_curve_fitting()​ 返回 ESP_ERR_NOT_SUPPORTED​ 时,代表开发板上校准方案所需的 eFuse 位不正确。

ESP-IDF 提供的 ADC 校准方案基于芯片上某些与 ADC 校准相关的 eFuse 位的值。乐鑫模组已在出厂时完成烧录,无需用户额外烧录。

创建曲线拟合方案句柄
ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
adc_cali_curve_fitting_config_t cali_config = {.unit_id = unit,.atten = atten,.bitwidth = ADC_BITWIDTH_DEFAULT,
};
ESP_ERROR_CHECK(adc_cali_create_scheme_curve_fitting(&cali_config, &handle));

ADC 校准使用完毕后,请调用 adc_cali_delete_scheme_curve_fitting()​,删除校准方案句柄。

删除曲线拟合方案句柄
ESP_LOGI(TAG, "delete %s calibration scheme", "Curve Fitting");
ESP_ERROR_CHECK(adc_cali_delete_scheme_curve_fitting(handle));

备注

要使用自定义校准方案,可以通过提供创建函数,创建自己的校准方案句柄。请参阅 components/esp_adc/interface/adc_cali_interface.h​ 中的函数表 adc_cali_scheme_t​,了解 ESP ADC 校准接口。

结果转换

对驱动程序进行完上述配置和初始化工作后,可以调用 adc_cali_raw_to_voltage()​,将原始 ADC 结果转换为校准结果,校准结果以 mV 为单位。该函数可能因参数无效而调用失败。如果函数返回 ESP_ERR_INVALID_STATE​,说明校准方案尚未创建。因此你需要创建一个校准方案句柄,通过 adc_cali_check_scheme()​ 可以了解当前芯片支持的校准方案;你也可以提供自定义校准方案,创建对应的校准方案句柄。

获取电压
ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc_cali_handle, adc_raw[0][0], &voltage[0][0]));
ESP_LOGI(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN0, voltage[0][0]);

线程安全

驱动程序会确保工厂函数 esp_adc_cali_new_scheme()​ 的线程安全,使用时,可以直接从不同的 RTOS 任务中调用此类函数,无需额外锁保护。

其他以 adc_cali_handle_t​ 作为第一个位置参数的函数均非线程安全,在没有设置互斥锁保护的任务中,应避免从多个任务中调用这类函数。

减少噪声

ESP32-S3 ADC 对噪声敏感,可能导致 ADC 读数出现较大偏差。根据不同使用场景,要减少噪声影响,你可能需要将旁路电容(如 100 nF 陶瓷电容)连接到 ADC 使用的输入管脚。此外,也可以通过多次采样,进一步减轻噪声的影响。

API 参考

头文件包含

  • components/esp_adc/include/esp_adc/adc_cali.h
#include "esp_adc/adc_cali.h"

包含组件

REQUIRES esp_adc

函数功能

检查支持的 ADC 校准方案
esp_err_t adc_cali_check_scheme(adc_cali_scheme_ver_t *scheme_mask)

参数:

**scheme_mask** --  **[out]**  支持的 ADC 校准方案

返回:

  • ESP_OK:成功时

  • ESP_ERR_INVALID_ARG:参数无效

  • ESP_ERR_NOT_SUPPORTED:不支持校准方案

将 ADC 原始数据转换为校准电压
esp_err_t adc_cali_raw_to_voltage(adc_cali_handle_t 句柄、int raw、int *voltage)

参数:

  • 手柄[in] ADC 校准手柄

  • raw[in] ADC 原始数据

  • voltage - - [out] 校准的 ADC 电压(以 mV 为单位)

返回:* ESP_OK:成功时

  • ESP_ERR_INVALID_ARG:参数无效
  • ESP_ERR_INVALID_STATE:状态无效,方案未注册

应用示例

自定义文件

adc_config.h

#ifndef _ADC_CONFIG_H
#define _ADC_CONFIG_H
#ifdef __cplusplus
extern "C" {
#endif#include "esp_err.h"#include "esp_adc/adc_continuous.h"
#include "esp_adc/adc_oneshot.h"void adc_continuous_init(adc_channel_t *channel, uint8_t channel_num);      //ADC 连续采集模式初始化
uint32_t adc_continuous_get_raw_data(adc_channel_t channel);                //ADC 获取连续采集模式的原始数据  
uint16_t adc_continuous_get_voltage(adc_channel_t channel);                 //ADC 获取连续采集模式的电压
esp_err_t adc_start_continuous();                                           //ADC 开始连续采集数据
esp_err_t adc_stop_continuous();                                            //ADC 停止连续采集数据void adc_oneshot_init(adc_channel_t *channel, uint8_t channel_num);         //ADC 单次采集初始化
int adc_oneshot_get_raw_value(adc_channel_t channel);                       //ADC 获取单次采集模式的原始数据
uint16_t adc_oneshot_get_voltage(adc_channel_t channel);                    //ADC 获取单次采集模式的电压
#ifdef __cplusplus
}
#endif
#endif

adc_config.c

#include "adc_config.h"
#include "esp_log.h"
#include "esp_err.h"
#include <string.h>
#include <stdio.h>#define ADC_UNIT                            ADC_UNIT_1                      //使用ADC1的通道,注意wifi功能和ADC2采集不能同时使用
// ADC衰减             输入范围
// ADC_ATTEN_DB_0      0~1.1V
// ADC_ATTEN_DB_2_5    0~2.5V
// ADC_ATTEN_DB_6      0~2.2V
// ADC_ATTEN_DB_12     0~3.3V
#define ADC_ATTEN                           ADC_ATTEN_DB_12                 //ADC衰减 12dB,可测输入范围0~3.3V
#define ADC_BIT_WIDTH                       ADC_BITWIDTH_12                 //ADC数据位宽 12bit,2^12= 4096#define ADC_CONV_MODE                       ADC_CONV_SINGLE_UNIT_1          //ADC 循环模式,仅使用ADC1进行循环
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
#define ADC_OUTPUT_TYPE             ADC_DIGI_OUTPUT_FORMAT_TYPE1
#define ADC_GET_CHANNEL(p_data)     ((p_data)->type1.channel)           //获取通道号
#define ADC_GET_DATA(p_data)        ((p_data)->type1.data)              //获取adc原始数据
#else
#define ADC_OUTPUT_TYPE             ADC_DIGI_OUTPUT_FORMAT_TYPE2
#define ADC_GET_CHANNEL(p_data)     ((p_data)->type2.channel)           //获取通道号
#define ADC_GET_DATA(p_data)        ((p_data)->type2.data)              //获取adc原始数据
#endif#define ADC_CONV_SIZE                        2*16    //每帧采16组数据,可以自定义,//但要注意,>  2 * (ADC通道数num + 1) * m,//举例 通道数num=3, 则建议 ADC_CONV_SIZE = 2 * (3+1) * m   (m取非零正整数!)#define TAG "ADC->"static adc_continuous_handle_t adc_cont_handle=NULL;        //连续ADC句柄
/// @brief ADC 校准初始化
/// @param unit [in] ADC单元
/// @param channel [in] ADC通道
/// @param atten [in] ADC衰减
/// @param out_handle [out] ADC校准句柄
/// @return 是否成功
static bool adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)
{adc_cali_handle_t handle = NULL;esp_err_t ret = ESP_FAIL;bool calibrated = false;#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTEDif (!calibrated) {ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");adc_cali_curve_fitting_config_t cali_config = {.unit_id = unit,.chan = channel,.atten = atten,.bitwidth = ADC_BITWIDTH_DEFAULT,};ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);if (ret == ESP_OK) {calibrated = true;}}
#endif#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTEDif (!calibrated) {ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting");adc_cali_line_fitting_config_t cali_config = {.unit_id = unit,.atten = atten,.bitwidth = ADC_BITWIDTH_DEFAULT,};ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);if (ret == ESP_OK) {calibrated = true;}}
#endif*out_handle = handle;if (ret == ESP_OK) {ESP_LOGI(TAG, "Calibration Success");} else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");} else {ESP_LOGE(TAG, "Invalid arg or no memory");}return calibrated;
}/// @brief 连续采集一帧数据完成回调函数
/// @param handle 连续ADC句柄
/// @param edata 事件数据
/// @param user_data 用户数据
/// @return 
static bool IRAM_ATTR s_conv_done_cb(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data){esp_err_t ret;uint32_t ret_num = 0;static uint8_t buffer[ADC_CONV_SIZE] = {0};memset(buffer, 0xcc, ADC_CONV_SIZE);ret = adc_continuous_read(handle, buffer, ADC_CONV_SIZE, &ret_num, 0);if (ret == ESP_OK) {for (int i = 0; i < ret_num; i += 2) {  // 每个采样点占 2 字节adc_digi_output_data_t *p = (adc_digi_output_data_t*)&buffer[i];uint32_t chan_num = ADC_GET_CHANNEL(p);uint32_t raw_data = ADC_GET_DATA(p);     }return true ;}else return false ;
}
/// @brief adc 连续采集模式初始化
/// @param channel //需要采集的通道数组
/// @param channel_num // 通道个数
void adc_continuous_init(adc_channel_t *channel, uint8_t channel_num)
{ESP_LOGI(TAG,"continuous_adc_init ");adc_continuous_handle_cfg_t adc_config = {.max_store_buf_size = 512,         // DMA 缓冲区大小.conv_frame_size = ADC_CONV_SIZE, // 每帧数据大小};ESP_ERROR_CHECK(adc_continuous_new_handle(&adc_config, &adc_cont_handle));// 配置 ADC 通道adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0};    for (int i = 0; i < channel_num; i++) {adc_pattern[i].atten = ADC_ATTEN;           //ADC 衰减adc_pattern[i].channel = channel[i] & 0x7;  //通道号adc_pattern[i].unit = ADC_UNIT;             //ADC单元adc_pattern[i].bit_width = ADC_BIT_WIDTH;   //数据位宽ESP_LOGI(TAG, "adc_pattern[%d].atten is :%"PRIx8, i, adc_pattern[i].atten);ESP_LOGI(TAG, "adc_pattern[%d].channel is :%"PRIx8, i, adc_pattern[i].channel);ESP_LOGI(TAG, "adc_pattern[%d].unit is :%"PRIx8, i, adc_pattern[i].unit);}// 配置 ADC 连续模式adc_continuous_config_t dig_cfg = {.sample_freq_hz = 20 * 1000,            // 采样率.conv_mode = ADC_CONV_MODE,             // 转换模式.format = ADC_OUTPUT_TYPE,              // 输出格式 .pattern_num = channel_num,             // 通道个数.adc_pattern = adc_pattern,             //通道};ESP_ERROR_CHECK(adc_continuous_config(adc_cont_handle, &dig_cfg));//校准adc_cali_handle_t adc1_cali_chan0_handle = NULL;for (int i = 0; i < channel_num; i++){adc_calibration_init(ADC_UNIT, channel[i]& 0x7, ADC_ATTEN, &adc1_cali_chan0_handle);       //校准}#if 0       adc_continuous_evt_cbs_t cbs = {        .on_conv_done = s_conv_done_cb,         //一帧数据转换完成回调.on_pool_ovf = NULL,                    //DMA 缓冲满了回调};ESP_ERROR_CHECK(adc_continuous_register_event_callbacks(adc_cont_handle, &cbs, NULL));      //注册回调函数#endifESP_LOGI(TAG,"continuous_adc_init DONE! ");}
/// @brief ADC 开始连续采集数据
/// @return 
esp_err_t adc_start_continuous()
{return adc_continuous_start(adc_cont_handle);
}
/// @brief ADC 停止连续采集数据
/// @return 
esp_err_t adc_stop_continuous()
{return adc_continuous_stop(adc_cont_handle);
}
/// @brief ADC 获取连续采集模式的原始数据
/// @param channel 指定的通道
/// @return 一帧数据对应通道数据的平均值
uint32_t adc_continuous_get_raw_data(adc_channel_t channel)
{uint32_t raw_data_ave=0;uint16_t ave_cnt=0;uint32_t ret_num = 0;static uint8_t buffer[ADC_CONV_SIZE] = {0};memset(buffer, 0xcc, ADC_CONV_SIZE);esp_err_t ret = adc_continuous_read(adc_cont_handle, buffer, ADC_CONV_SIZE, &ret_num, 0);if (ret == ESP_OK) {ESP_LOGI(TAG, "ret_num is %"PRIu32" bytes" ,ret_num);// 解析 TYPE2 格式的数据for (int i = 0; i < ret_num; i += 2) {  // 每个采样点占 2 字节adc_digi_output_data_t *p = (adc_digi_output_data_t*)&buffer[i];uint32_t chan_num = ADC_GET_CHANNEL(p);if(chan_num==channel){ave_cnt++;raw_data_ave += ADC_GET_DATA(p);}      }}else {ESP_LOGE(TAG, "Failed to read data: %d", ret);}raw_data_ave = raw_data_ave / ave_cnt;      //求平均值ESP_LOGI(TAG,"channel: %d, Raw Ave Data: %ld", channel, raw_data_ave);return raw_data_ave;
}
/// @brief 获取ADC 获取连续采集模式的电压
/// @param channel 指定的通道
/// @return 电压值,单位mV
uint16_t adc_continuous_get_voltage(adc_channel_t channel)
{uint32_t raw_data = adc_continuous_get_raw_data(channel);uint16_t max_voltage=1;uint16_t max_bitwidth=1;uint16_t voltage = 0;if(ADC_BIT_WIDTH==ADC_BITWIDTH_DEFAULT){max_bitwidth = (1<<ADC_BITWIDTH_13)-1;}max_bitwidth = (1<<ADC_BIT_WIDTH)-1;switch (ADC_ATTEN){case ADC_ATTEN_DB_0:    max_voltage = 1100;break;case ADC_ATTEN_DB_2_5:  max_voltage = 2500;break;case ADC_ATTEN_DB_6:    max_voltage = 2200;break;case ADC_ATTEN_DB_12:   max_voltage = 3300;break;     default:break;            }ESP_LOGI(TAG,"max_voltage=%d,max_bitwidth%d",max_voltage,max_bitwidth);voltage = raw_data *max_voltage / max_bitwidth;ESP_LOGI(TAG,"voltage=%d mV",voltage);return voltage;
}adc_oneshot_unit_handle_t adc_oneshot_handle;    //ADC单次采集 句柄/// @brief ADC 单次采集初始化
/// @param channel 通道数组
/// @param channel_num 通道个数
void adc_oneshot_init(adc_channel_t *channel, uint8_t channel_num)
{    ESP_LOGI(TAG,"adc_oneshot_init start!");// 初始化 ADC 单元adc_oneshot_unit_init_cfg_t init_config = {.unit_id = ADC_UNIT,  // 使用 ADC1};ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config, &adc_oneshot_handle));// 配置 ADC 通道adc_oneshot_chan_cfg_t channel_config = {.atten = ADC_ATTEN,         // 衰减值.bitwidth = ADC_BIT_WIDTH,  //ADC 数据位宽};adc_cali_handle_t adc1_cali_chan0_handle = NULL;for (uint8_t i = 0; i < channel_num; i++){ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_oneshot_handle, channel[i]&0x07, &channel_config));adc_calibration_init(ADC_UNIT, channel[i]&0x07, ADC_ATTEN, &adc1_cali_chan0_handle);       //校准/* code */}ESP_LOGI(TAG,"adc_oneshot_init DONE! ");
}
/// @brief ADC 获取单次采集模式的原始数据
/// @param channel 指定的ADC通道
/// @return 原始数据
int adc_oneshot_get_raw_value(adc_channel_t channel)
{int adc_raw_value = 0;ESP_ERROR_CHECK(adc_oneshot_read(adc_oneshot_handle, channel, &adc_raw_value));ESP_LOGI(TAG,"ADC Raw Value: %d\n", adc_raw_value);return adc_raw_value;
}
/// @brief ADC 获取单次采集模式的电压
/// @param channel 指定的通道
/// @return 电压值,单位mV
uint16_t adc_oneshot_get_voltage(adc_channel_t channel)
{uint32_t raw_data = adc_oneshot_get_raw_value(channel);uint16_t max_voltage=1;uint16_t max_bitwidth=1;uint16_t voltage = 0;if(ADC_BIT_WIDTH==ADC_BITWIDTH_DEFAULT){max_bitwidth = (1<<ADC_BITWIDTH_13)-1;}max_bitwidth = (1<<ADC_BIT_WIDTH)-1;switch (ADC_ATTEN){case ADC_ATTEN_DB_0:    max_voltage = 1100;break;case ADC_ATTEN_DB_2_5:  max_voltage = 2500;break;case ADC_ATTEN_DB_6:    max_voltage = 2200;break;case ADC_ATTEN_DB_12:   max_voltage = 3300;break;     default:break;            }ESP_LOGI(TAG,"max_voltage=%d,max_bitwidth%d",max_voltage,max_bitwidth);voltage = raw_data *max_voltage / max_bitwidth;ESP_LOGI(TAG,"voltage=%d mV",voltage);return voltage;
}

app_main.c

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_err.h"///... 其他头文件
#include "adc_config.h"#define TAG "main"void app_main(void)
{///...  其他代码adc_channel_t channel[2] = {ADC_CHANNEL_2,ADC_CHANNEL_2};adc_continuous_init(channel,1);		// ADC 连续模式adc_start_continuous();				// 启动ADC连续模式// adc_oneshot_init(channel,1);		//ADC 单次模式vTaskDelay(1000 / portTICK_PERIOD_MS);      //等待系统稳定uint16_t adc_voltage=0;///...其他代码while(1){///...其他代码adc_voltage = adc_continuous_get_voltage(ADC_CHANNEL_2);		//采集连续// adc_voltage =  adc_oneshot_get_voltage(ADC_CHANNEL_2);			//单次采集ESP_LOGI(TAG,"voltage=%d mV",adc_voltage );vTaskDelay(500 / portTICK_PERIOD_MS);///...其他代码	}}

相关文章:

  • 移动端使用keep-alive将页面缓存和滚动缓存具体实现方法 - 详解
  • 程序员思维体操:TDD修炼手册
  • 激光雷达成为新时代「安全气囊」,禾赛推动智能车安全再进化
  • 网络socks 代理
  • 怎么减少tcp 的time_wait时间
  • Openharmony 和 HarmonyOS 区别?
  • 【架构】Armstrong公理系统通俗详解:数据库设计的基本法则
  • Linux实现网络计数器
  • 比较:AWS VPC peering与 AWS Transit Gateway
  • stm32之GPIO函数详解和上机实验
  • AI PPT创作原理解析:让你的演示文稿更智能
  • vue3中slot(插槽)的详细使用
  • SpringBoot入门实战(第六篇:项目接口-登录)
  • 数据结构初阶:二叉树(四)
  • 使用Python求解泊松方程
  • 静态存储区(Static Storage Area)的总结
  • 从零手写 RPC-version1
  • docker在windows下wsl存储路径的变更与数据迁移
  • 区块链技术在物联网中的应用:构建可信的智能世界
  • 从白平衡色温坐标系调整的角度消除硬件不一致性引起的偏色问题
  • 大家聊中国式现代化|彭羽:为国家试制度探新路,推进高水平对外开放
  • 人民日报:外卖平台应保障好骑手就业权益,消除后顾之忧
  • 郑庆华任同济大学党委书记
  • 金融监管总局:支持将上海打造成具有国际竞争力的再保险中心
  • 世界读书日|阅读在上海
  • 新增1839个!2024年度本科专业备案和审批结果,公布