资讯详情

资讯详情

ESP-IDF ADC 连续模式驱动(DMA 模式)完全指南:从配置、事件回调到数据解析

ESP-IDF ADC 连续模式驱动DMA 模式完全指南从配置、事件回调到数据解析【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf本指南以 ESP-IDF 官方文档 adc_continuous.rst 为主体结合 esp_adc 组件源码与 continuous_read 示例系统讲解 ADC 连续模式驱动的工作原理、资源分配、采样配置、事件回调、原始数据读取与解析、硬件限制以及电源管理。读完本文你将能够在 ESP32 系列芯片上独立搭建一套基于 DMA 的高速、多通道 ADC 连续采样应用并正确处理缓冲溢出、IRAM 安全与线程安全等工程问题。说明ADC 连续模式适用于周期性或高频数据采集场景若只需按需执行单次转换请参阅 ADC Oneshot 模式驱动。驱动核心概念转换帧Conversion Frame与转换结果Conversion ResultADC 连续模式下硬件以固定节拍对一组模拟通道自动采样采样结果经由 DMA 高效搬运到内存。驱动将数据组织为转换帧转换帧Conversion Frame一个帧包含多个转换结果。帧大小以字节为单位通过adc_continuous_new_handle时传入的adc_continuous_handle_cfg_t::conv_frame_size配置。硬件每完成一帧触发一次中断即产生一次帧完成事件。转换结果Conversion Result一个结果由多个字节组成字节数由宏SOC_ADC_DIGI_RESULT_BYTES定义由 SoC 能力决定结构体为adc_digi_output_data_t包含 ADC 单元Unit、ADC 通道Channel和原始数据Raw Data。例如conv_frame_size 100表示一个转换帧包含100 / SOC_ADC_DIGI_RESULT_BYTES条转换结果。该帧-结果层级关系在头文件 adc_continuous.h 的 Driver Backgrounds 注释中也有完整描述。功能总览本驱动典型使用流程包含以下步骤本文后续各节逐一展开资源分配初始化驱动句柄及可选的 IIR 滤波器、监视器ADC 配置配置采样通道、衰减、位宽、采样频率与转换模式ADC 控制启动与停止连续转换注册事件回调挂接帧完成与缓冲池溢出回调读取转换结果从内部缓冲池取出原始数据并解析成结构化结果了解硬件限制、电源管理、IRAM 安全与线程安全约束。资源分配创建与销毁驱动句柄ADC 连续模式驱动基于 SAR ADC 模块实现不同 ESP 芯片拥有的独立 ADC 数量不同。通过配置结构体adc_continuous_handle_cfg_t并调用adc_continuous_new_handle创建句柄adc_continuous_handle_t handle NULL; adc_continuous_handle_cfg_t adc_config { .max_store_buf_size 1024, // 内部缓冲池最大容量单位字节 .conv_frame_size 256, // 转换帧大小单位字节需为 SOC_ADC_DIGI_DATA_BYTES_PER_CONV 的整数倍 }; ESP_ERROR_CHECK(adc_continuous_new_handle(adc_config, handle));各字段含义见 adc_continuous.hmax_store_buf_size内部缓冲池最大容量字节驱动将转换结果暂存于此conv_frame_size转换帧大小字节需为单次转换数据字节数的整数倍flags.flush_pool置位后缓冲池满时自动冲刷旧数据并写入新数据否则池满时新转换结果将被丢弃对应on_pool_ovf事件。adc_continuous_new_handle可能因参数非法、内存不足等原因失败。部分错误码具有特殊含义因芯片而异ESP32返回ESP_ERR_NOT_FOUND表示 I2S0 外设已被占用见下文硬件限制ESP32-S2返回ESP_ERR_NOT_FOUND表示 SPI3 外设已被占用支持 GDMA 的芯片返回ESP_ERR_NOT_FOUND表示没有空闲的 GDMA 通道。不再使用驱动时调用adc_continuous_deinit回收资源ESP_ERROR_CHECK(adc_continuous_deinit(handle));可选IIR 滤波器SOC_ADC_DIG_IIR_FILTER_SUPPORTED支持该能力的芯片在连续模式下可创建两个 IIR 滤波器。配置结构体adc_continuous_iir_filter_config_t包含unit要滤波的 ADC 单元channel要滤波的 ADC 通道coeff滤波系数。调用adc_new_continuous_iir_filter创建调用adc_del_continuous_iir_filter回收。在SOC_ADC_DIG_IIR_FILTER_UNIT_BINDED的芯片上滤波器按 ADC 单元绑定一旦使能该单元内所有已使能的通道都会被滤波。由于滤波结果依赖上一次滤波结果官方建议使用滤波功能时每个单元只使能一个通道避免滤波结果互相混叠。在不支持按单元绑定的芯片上若对同一通道使用两个滤波器仅第一个生效。可选监视器MonitorSOC_ADC_MONITOR_SUPPORTED监视器可在某工作通道上设置一个或两个阈值一旦转换结果越界硬件在每个采样循环都会触发中断。配置结构体adc_monitor_config_t见 adc_monitor.hadc_unit被监视通道所属的 ADC 单元channel被监视的通道h_threshold高阈值转换结果大于该值触发中断置 -1 表示不使用l_threshold低阈值转换结果小于该值触发中断置 -1 表示不使用。创建后通过以下 API 操作监视器adc_continuous_monitor_enable/adc_continuous_monitor_disable使能/禁用adc_continuous_monitor_register_event_callbacks注册越界回调on_over_high_thresh/on_below_low_threshadc_del_continuous_monitor删除监视器并释放资源。ESP32-S2 的硬件限制每个监视器只支持一个阈值每个 ADC 单元只支持一个监视器且连续模式驱动中某单元所有已使能通道都会被监视channel参数不生效。典型应用过零检测Zero-Crossing Detection。ADC 无法直接处理负输入信号需在测量前通过外部电路给原信号叠加 DC 偏置将其平移进 ADC 量程。例如叠加 1 V 偏置后-1 V ~ 1 V 的信号变为 0 V ~ 2 V。随后设置合适的上下阈值即可检测输入信号是否接近零点、识别信号相位变化// 初始化 ADC monitor 句柄 adc_monitor_handle_t adc_monitor_handle NULL; // 配置 ADC monitor adc_monitor_config_t zero_crossing_config { .adc_unit EXAMPLE_ADC_UNIT_1, // 指定要监视的 ADC 单元 .channel EXAMPLE_ADC_CHANNEL_0, // 指定要监视的 ADC 通道 .h_threshold 1100, // 高阈值靠近 DC 偏置可按需调整 .l_threshold 900, // 低阈值靠近 DC 偏置可按需调整 }; // 创建 ADC monitor ESP_ERROR_CHECK(adc_new_continuous_monitor(zero_crossing_config, adc_monitor_handle)); // 注册回调函数 adc_monitor_evt_cbs_t zero_crossing_cbs { .on_over_high_thresh example_on_exceed_high_thresh, .on_below_low_thresh example_on_below_low_thresh, }; ESP_ERROR_CHECK(adc_continuous_monitor_register_event_callbacks(adc_monitor_handle, zero_crossing_cbs, NULL)); // 使能 ADC monitor ESP_ERROR_CHECK(adc_continuous_monitor_enable(adc_monitor_handle)); // 禁用并删除 ADC monitor ESP_ERROR_CHECK(adc_continuous_monitor_disable(adc_monitor_handle)); ESP_ERROR_CHECK(adc_del_continuous_monitor(adc_monitor_handle));ADC 配置采样通道、衰减、位宽与频率驱动初始化后通过adc_continuous_config_t配置 ADC IO 以测量模拟信号pattern_num使用的 ADC 通道数量adc_pattern每个使用通道的配置列表sample_freq_hz期望的 ADC 采样频率Hz可用范围请参考soc/soc_caps.h中的 SoC 能力宏conv_mode连续转换模式adc_digi_convert_mode_tformat转换输出格式已标记为 deprecated详见 adc_continuous.h。每个通道的adc_digi_pattern_config_t按以下过程设置attenADC 衰减档位如ADC_ATTEN_DB_0、ADC_ATTEN_DB_12决定满量程量程参考芯片 TRM 的 On-Chip Sensor and Analog Signal Processing 章节channelIO 对应的 ADC 通道号unit该 IO 所属的 ADC 单元bit_width原始转换结果的位宽。注意通道号与 IO 的对应关系需查芯片 TRM也可调用adc_continuous_io_to_channel与adc_continuous_channel_to_io在 GPIO 与 ADC 通道之间互查。配置示例来自 continuous_read 示例adc_continuous_handle_t handle NULL; adc_continuous_handle_cfg_t adc_config { .max_store_buf_size 1024, .conv_frame_size EXAMPLE_READ_LEN, // 256 字节 }; ESP_ERROR_CHECK(adc_continuous_new_handle(adc_config, handle)); adc_continuous_config_t dig_cfg { .sample_freq_hz 20 * 1000, // 20 kHz 采样率 .conv_mode ADC_CONV_SINGLE_UNIT_1, // 仅使用 ADC1 的连续转换模式 }; adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] {0}; dig_cfg.pattern_num channel_num; // 使用通道数 for (int i 0; i channel_num; i) { adc_pattern[i].atten EXAMPLE_ADC_ATTEN; // ESP32 上为 ADC_ATTEN_DB_12 adc_pattern[i].channel channel[i] 0x7; adc_pattern[i].unit EXAMPLE_ADC_UNIT; // ADC_UNIT_1 adc_pattern[i].bit_width SOC_ADC_DIGI_MAX_BITWIDTH; // SoC 支持的最大位宽 } dig_cfg.adc_pattern adc_pattern; ESP_ERROR_CHECK(adc_continuous_config(handle, dig_cfg));调用adc_continuous_config使配置生效。该 API 可能返回ESP_ERR_INVALID_ARG参数非法若返回ESP_ERR_INVALID_STATE说明驱动已启动此刻不应调用本 API。配置完成后可通过adc_continuous_iir_filter_enable/disable与adc_continuous_monitor_enable/disable控制滤波器与监视器的开关。ADC 控制启动与停止调用adc_continuous_start后ADC 开始按配置对通道采样并生成转换结果调用adc_continuous_stop停止转换ESP_ERROR_CHECK(adc_continuous_start(handle)); // ... 采样与数据处理 ... ESP_ERROR_CHECK(adc_continuous_stop(handle));注册事件回调ISR 上下文通过adc_continuous_register_event_callbacks可将用户函数挂接到驱动 ISR。支持的回调定义于adc_continuous_evt_cbs_t见 adc_continuous.hon_conv_done一帧转换完成时触发on_pool_ovf内部缓冲池满时触发新转换结果将被丢弃。回调原型为adc_continuous_callback_t返回bool表示是否唤醒了高优先级任务。由于回调运行在 ISR 上下文内部严禁出现阻塞逻辑。调用时可通过user_data参数注册自定义上下文该指针会原样传给回调。static bool IRAM_ATTR s_conv_done_cb(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data) { BaseType_t mustYield pdFALSE; // 通知任务已积累足够转换数据 vTaskNotifyGiveFromISR(s_task_handle, mustYield); return (mustYield pdTRUE); } adc_continuous_evt_cbs_t cbs { .on_conv_done s_conv_done_cb, }; ESP_ERROR_CHECK(adc_continuous_register_event_callbacks(handle, cbs, NULL));该 API 可能因参数非法返回ESP_ERR_INVALID_ARG当使能CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE时该错误可能意味着回调函数未放入内部 RAM可查看错误日志返回ESP_ERR_INVALID_STATE表示驱动已启动此刻不能注册回调。取消注册只需将对应回调成员置 NULL 后再次调用。帧完成事件Conversion Done Event一帧转换完成时触发on_conv_done事件数据adc_continuous_evt_data_t包含conv_frame_buffer指向一帧转换结果缓冲区的指针size转换帧大小。注意conv_frame_buffer由驱动自行维护切勿释放这块内存。当使能CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE时注册的回调及其调用的函数都应放入 IRAM涉及的变量也应放在内部 RAM。缓冲池溢出事件Pool Overflow Event驱动内部用缓冲池保存转换结果。当池满时产生溢出事件此时驱动不会填充事件数据。通常原因是adc_continuous_read的读取速度远慢于 ADC 转换速度。读取转换结果与电压换算adc_continuous_start之后调用adc_continuous_read获取转换结果uint32_t ret_num 0; uint8_t result[EXAMPLE_READ_LEN] {0}; esp_err_t ret adc_continuous_read(handle, result, EXAMPLE_READ_LEN, ret_num, 0);adc_continuous_read每次尝试读取指定长度若池中实际数据少于请求长度函数仍会把现有数据搬到用户缓冲区需通过out_length判断实际搬移的字节数若池中尚无转换结果函数将阻塞timeout_ms毫秒等待至少一个结果超时仍无数据则返回ESP_ERR_TIMEOUT若转换结果已填满缓冲池新结果会丢失下次调用adc_continuous_read会返回ESP_ERR_INVALID_STATE通常意味着采样率高于任务处理速率。timeout_ms传ADC_MAX_DELAYUINT32_MAX可在支持的操作系统上无限阻塞等待。原始码值到电压的换算adc_continuous_read读出的是原始数据。可用如下公式估算电压Vout Dout * Vmax / Dmax (1)符号含义Vout数字输出结果代表电压DoutADC 原始数字读数Vmax最大可测输入模拟电压与 ADC 衰减档位相关参考芯片 TRMDmaxADC 原始数字读数最大值即2^bitwidth其中 bitwidth 为之前配置的adc_digi_pattern_config_t::bit_width推荐做法为进一步校准并将原始值转换为 mV 级电压请使用 ADC 校准 API如adc_cali_raw_to_voltage而不是直接解释原始码值。解析原始数据adc_continuous_parse_data原始数据需进一步解析才能得到可用的结构化结果。adc_continuous_parse_data将原始数据解析为adc_continuous_data_t数组长度对齐raw_data_size必须是SOC_ADC_DIGI_RESULT_BYTES的整数倍缓冲区大小确保raw_data缓冲区足以容纳raw_data_size字节数据解析前请确保parsed_data数组足够容纳raw_data_size / SOC_ADC_DIGI_RESULT_BYTES个样本函数内部带有完整的边界检查可防止缓冲区溢出与整数溢出见 adc_continuous.c。// 读取原始数据 uint32_t ret_num 0; esp_err_t ret adc_continuous_read(handle, result, EXAMPLE_READ_LEN, ret_num, 0); if (ret ESP_OK) { // 解析原始数据 adc_continuous_data_t parsed_data[ret_num / SOC_ADC_DIGI_RESULT_BYTES]; uint32_t num_parsed_samples 0; esp_err_t parse_ret adc_continuous_parse_data(handle, result, ret_num, parsed_data, num_parsed_samples); if (parse_ret ESP_OK) { for (int i 0; i num_parsed_samples; i) { if (parsed_data[i].valid) { ESP_LOGI(TAG, ADC%d, Channel: %d, Value: %PRIu32, parsed_data[i].unit 1, parsed_data[i].channel, parsed_data[i].raw_data); } } } }adc_continuous_data_t结构包含以下字段见 adc_continuous.hunitADC 单元ADC_UNIT_1或ADC_UNIT_2channelADC 通道号0-9raw_dataADC 原始数据值valid数据是否有效。一步到位adc_continuous_read_parse为简化流程adc_continuous_read_parse将读取 解析合并为一次调用由函数内部自动完成原始缓冲区的分配与释放用户只需提供解析结果数组并指定最大样本数// 使用 read parse 合并函数 adc_continuous_data_t parsed_data[64]; // 用户指定最大样本数 uint32_t num_samples 0; esp_err_t ret adc_continuous_read_parse(handle, parsed_data, 64, num_samples, 1000); if (ret ESP_OK) { for (int i 0; i num_samples; i) { if (parsed_data[i].valid) { ESP_LOGI(TAG, ADC%d, Channel: %d, Value: %PRIu32, parsed_data[i].unit 1, parsed_data[i].channel, parsed_data[i].raw_data); } } }硬件限制使用连续模式前必须了解以下硬件限制部分与芯片相关同一 ADC 单元同一时刻只能工作在一种模式下连续模式或单次模式adc_continuous_start已提供相应保护随机数发生器RNG以 ADC 为输入源之一连续模式工作时 RNG 产生的随机数随机性会下降ESP32-S31 除外支持差分输入SOC_ADC_DIFF_SUPPORTED的芯片单端模式下若使用 N 侧通道作为输入接口其原始数据极性反转。对于 -2 V ~ 2 V 的输入范围N 侧原始码值从 4393 递减至 0。请使用 ADC 校准 API 处理ESP32 / ESP32-S2ADC2 同时被 Wi-Fi 使用adc_continuous_start提供了 Wi-Fi 驱动与连续模式驱动之间的互斥保护ESP32连续模式驱动使用 I2S0 外设作为硬件 DMA FIFO若 I2S0 已被占用adc_continuous_new_handle返回ESP_ERR_NOT_FOUND另外 ESP32 DevKitC 因外部自动烧录电路无法使用 GPIO 0ESP-WROVER-KIT 因外部连接用途无法使用 GPIO 0、2、4、15ESP32-S2连续模式驱动使用 SPI3 外设作为硬件 DMA FIFO若 SPI3 已被占用adc_continuous_new_handle返回ESP_ERR_NOT_FOUNDESP32-C3 / ESP32-S3因硬件限制ADC2 的 DMA 功能不再支持获取转换结果观察到了不稳定结果详见对应芯片 Errata。如确需兼容可开启CONFIG_ADC_CONTINUOUS_FORCE_USE_ADC2_ON_C3_S3强制使用 ADC2见 Kconfig。电源管理启用电源管理CONFIG_PM_ENABLE后系统空闲时 APB 时钟频率可能被调整从而改变 ADC 连续转换行为。连续模式驱动通过获取ESP_PM_APB_FREQ_MAX类型的电源管理锁来阻止这一变化adc_continuous_start启动连续转换后获取该锁adc_continuous_stop停止后释放该锁。因此adc_continuous_start与adc_continuous_stop必须成对出现否则电源管理将失去作用。IRAM 安全所有 ADC 连续模式 API 本身不是IRAM 安全的Cache 被禁用时不应调用。开启 Kconfig 选项CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE定义于 Kconfig后驱动内部 ISR 处理器变为 IRAM 安全即使在 Cache 被禁用如 Flash 写操作期间时驱动仍能将转换结果保存到内部缓冲池。线程安全ADC 连续模式驱动 API不保证线程安全但驱动提供了共享硬件互斥保护如 ADC2 与 Wi-Fi 之间的互斥详见上文硬件限制一节。应用示例与运行验证peripherals/adc/continuous_read 示例 完整演示了本驱动的典型用法它使用 ADC1 的两个通道ESP32 上为 GPIO 对应通道 6、7其余目标为通道 2、3以 20 kHz 采样率、SOC_ADC_DIGI_MAX_BITWIDTH位宽工作在on_conv_done回调中通过vTaskNotifyGiveFromISR通知任务读取数据再配合adc_continuous_parse_data打印每个有效样本的单元、通道与原始值。构建、烧录与监视idf.py -p PORT flash monitor运行示例时串口监视器将输出类似如下的日志I (367) EXAMPLE: adc_pattern[0].atten is :0 I (377) EXAMPLE: adc_pattern[0].channel is :2 I (377) EXAMPLE: adc_pattern[0].unit is :0 I (417) TASK: ret is 0, ret_num is 256 I (427) EXAMPLE: Unit: ADC-1, Channel: 2, Value: 819 I (427) EXAMPLE: Unit: ADC-1, Channel: 3, Value: 7b9 I (437) EXAMPLE: Unit: ADC-1, Channel: 2, Value: 7ab ...示例配套的自动化测试 pytest_adc_continuous.py 可用于 CI 环境验证。驱动完整 API 列表参见组件头文件 adc_continuous.h底层实现可进一步研读 adc_continuous.c、adc_filter.c 与 adc_monitor.c。【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →