ESP32ADC采样率配置(基于ESP-IDF)
最近要使用ESP32进行ADC采样,需要对ESP32的ADC采样率进行设置,查阅后发现网上这方面的资料非常少,所以把配置过程写下来以供大家参考
文章目录
- 一、ESP32的ADC外设
- 二、示例代码修改
- 三、ADC采样率获取
- 四、采样率配置
- 五、实验验证
- 六、可能出现的问题
一、ESP32的ADC外设
打开ESP32的技术规格书第34页,可见ESP32具有2个12位的逐次逼近型ADC,他有RTC和DIG两个控制器,其中RTC控制器最大采样率为200KSPS,DIG控制器为2MSPS,如果我们需要采样频率较高的信号,就必须使用DIG控制器。
打开ESP32的技术参考手册第577页找到DIG控制器,可见我们为了追求最大的采样率,应该考虑使用DMA配合ADC使用。
二、示例代码修改
打开ESP-IDF的官方ADC_DMA示例程序,这里我用的是VSCODE+ESP-IDF(v4.4)插件,由于我使用的是ESP32芯片,所以我将所有用于适配其他芯片部分的代码删除,删除后的示例代码如下:
#include <string.h>
#include <stdio.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/adc.h"#define TIMES 1000 //一次的采样点数*2
#define GET_UNIT(x) ((x >> 3) & 0x1)#if CONFIG_IDF_TARGET_ESP32
#define ADC_RESULT_BYTE 2
#define ADC_CONV_LIMIT_EN 1 // For ESP32, this should always be set to 1
#define ADC_CONV_MODE ADC_CONV_SINGLE_UNIT_1 // ESP32 only supports ADC1 DMA mode
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE1
#endif#if CONFIG_IDF_TARGET_ESP32
static uint16_t adc1_chan_mask = BIT(7);
static uint16_t adc2_chan_mask = 0;
static adc_channel_t channel[1] = {ADC1_CHANNEL_7}; //设置ADC引脚
#endifstatic uint32_t ret_num = 0;
static uint8_t result[TIMES] = {0}; //采样结果存放数组static const char *TAG = "{adc}";
int countNum;static void continuous_adc_init(uint16_t adc1_chan_mask, uint16_t
adc2_chan_mask, adc_channel_t *channel, uint8_t channel_num)
{adc_digi_init_config_t adc_dma_config = {.max_store_buf_size = 1024,.conv_num_each_intr = TIMES,.adc1_chan_mask = adc1_chan_mask,.adc2_chan_mask = adc2_chan_mask,};ESP_ERROR_CHECK(adc_digi_initialize(&adc_dma_config));adc_digi_configuration_t dig_cfg = {.conv_limit_en = ADC_CONV_LIMIT_EN,.conv_limit_num = 250,.sample_freq_hz = 100000, //采样率.conv_mode = ADC_CONV_MODE,.format = ADC_OUTPUT_TYPE,};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++){uint8_t unit = GET_UNIT(channel[i]);uint8_t ch = channel[i] & 0x7;adc_pattern[i].atten = ADC_ATTEN_DB_11; //衰减倍率adc_pattern[i].channel = ch;adc_pattern[i].unit = unit;adc_pattern[i].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH; //采样位数// ESP_LOGI(TAG, "adc_pattern[%d].atten is :%x", i, adc_pattern[i].atten);// ESP_LOGI(TAG, "adc_pattern[%d].channel is :%x", i, adc_pattern[i].channel);// ESP_LOGI(TAG, "adc_pattern[%d].unit is :%x", i, adc_pattern[i].unit);}dig_cfg.adc_pattern = adc_pattern;ESP_ERROR_CHECK(adc_digi_controller_configure(&dig_cfg));
}#if !CONFIG_IDF_TARGET_ESP32
static bool check_valid_data(const adc_digi_output_data_t *data)
{const unsigned int unit = data->type2.unit;if (unit > 2)return false;if (data->type2.channel >= SOC_ADC_CHANNEL_NUM(unit))return false;return true;
}
#endifvoid app_main(void)
{memset(result, 0xcc, TIMES);continuous_adc_init(adc1_chan_mask, adc2_chan_mask, channel, sizeof(channel) / sizeof(adc_channel_t));adc_digi_start();while(1){adc_digi_read_bytes(result, TIMES, &ret_num, ADC_MAX_DELAY);for (int i = 0; i < ret_num; i += ADC_RESULT_BYTE){adc_digi_output_data_t *p = (void *)&result[i];// ESP_LOGI(TAG, "%d", p->type1.data);// printf("{value}%d\n", p->type1.data);// vTaskDelay(10/ portTICK_PERIOD_MS);}}}
三、ADC采样率获取
首先ADC的采样率等于单位时间采样的点数,所以需要定义一个变量为countNum,这个变量用于累计单位时间的采样点的个数,把countNum放到ADC采样部分,每完成一次ADC采样就加一,使用freertos中的任务创建函数xTaskCreate(countTask, “countTask”, 1024 * 10, NULL, 2, NULL);,在这个函数中,使用死循环,每间隔一秒打印一次countNum的值,因为这个任务与ADC采样并行运行,所以countNum即为ADC的采样率,代码如下
void countTask(void *param)//创建获取采样率任务
{while (1){vTaskDelay(1000 / portTICK_PERIOD_MS);printf("{count}%d\n", countNum);countNum = 0;}
}
adc_digi_read_bytes(result, TIMES, &ret_num, ADC_MAX_DELAY);//ADC采样数据读取for (int i = 0; i < ret_num; i += ADC_RESULT_BYTE){adc_digi_output_data_t *p = (void *)&result[i];value[i] = p->type1.data;// ESP_LOGI(TAG, "%d", p->type1.data);// printf("{value}%d\n", p->type1.data);countNum++;//采样率计数// vTaskDelay(10/ portTICK_PERIOD_MS);}
四、采样率配置
对ESP32ADC的配置实际上就是对ADC的DIG控制器的配置,在adc.h中可以找到adc_digi_init_config_t和adc_digi_configuration_t结构体的定义,修改这些结构体中的成员就可以改变ADC的相关参数。
typedef struct adc_digi_init_config_s {uint32_t max_store_buf_size; ///< Max length of the converted data that driver can store before they are processed.uint32_t conv_num_each_intr; ///< Bytes of data that can be converted in 1 interrupt.uint32_t adc1_chan_mask; ///< Channel list of ADC1 to be initialized.uint32_t adc2_chan_mask; ///< Channel list of ADC2 to be initialized.
} adc_digi_init_config_t;
typedef struct {bool conv_limit_en; ///< To limit ADC conversion times. Conversion stops after finishing `conv_limit_num` times conversionuint32_t conv_limit_num; ///< Set the upper limit of the number of ADC conversion triggers. Range: 1 ~ 255.uint32_t pattern_num; ///< Number of ADC channels that will be usedadc_digi_pattern_config_t *adc_pattern; ///< List of configs for each ADC channel that will be useduint32_t sample_freq_hz; /*!< The expected ADC sampling frequency in Hz. Range: 611Hz ~ 83333HzFs = Fd / interval / 2Fs: sampling frequency;Fd: digital controller frequency, no larger than 5M for better performanceinterval: interval between 2 measurement trigger signal, the smallest interval should not be smaller than the ADC measurement period, the largest interval should not be larger than 4095 */adc_digi_convert_mode_t conv_mode; ///< ADC DMA conversion mode, see `adc_digi_convert_mode_t`.adc_digi_output_format_t format; ///< ADC DMA conversion output format, see `adc_digi_output_format_t`.
} adc_digi_configuration_t;
我在初始化代码中做出了如下修改
(1)增大max_store_buf_size使得ADC一次可以采集更多的点数
(2)增加TIMES的值,方便观察现象
(3)修改sample_freq_hz,即修改采样率,最大可到达2000000hz
adc_digi_init_config_t adc_dma_config = {.max_store_buf_size = 4096,//最大采样缓存.conv_num_each_intr = TIMES,.adc1_chan_mask = adc1_chan_mask,.adc2_chan_mask = adc2_chan_mask,};
adc_digi_configuration_t dig_cfg = {.conv_limit_en = ADC_CONV_LIMIT_EN,.conv_limit_num = 250,.sample_freq_hz = 100000, //采样率.conv_mode = ADC_CONV_MODE,.format = ADC_OUTPUT_TYPE,};
最后附上完整代码
#include <string.h>
#include <stdio.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/adc.h"#define TIMES 2000 //一次的采样点数*2
#define GET_UNIT(x) ((x >> 3) & 0x1)#if CONFIG_IDF_TARGET_ESP32
#define ADC_RESULT_BYTE 2
#define ADC_CONV_LIMIT_EN 1 // For ESP32, this should always be set to 1
#define ADC_CONV_MODE ADC_CONV_SINGLE_UNIT_1 // ESP32 only supports ADC1 DMA mode
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE1
#endif#if CONFIG_IDF_TARGET_ESP32
static uint16_t adc1_chan_mask = BIT(7);
static uint16_t adc2_chan_mask = 0;
static adc_channel_t channel[1] = {ADC1_CHANNEL_7}; //设置ADC引脚
#endifstatic uint32_t ret_num = 0;
static uint8_t result[TIMES] = {0}; //采样结果存放数组
static uint16_t value[2000] = {0};static const char *TAG = "{adc}";
int countNum;void countTask(void *param)
{while (1){vTaskDelay(1000 / portTICK_PERIOD_MS);printf("{count}%d\n", countNum);// printf("{value}%d\n",p->type1.data);countNum = 0;}
}static void continuous_adc_init(uint16_t adc1_chan_mask, uint16_t adc2_chan_mask, adc_channel_t *channel, uint8_t channel_num)
{adc_digi_init_config_t adc_dma_config = {.max_store_buf_size = 4096,//最大采样缓存.conv_num_each_intr = TIMES,.adc1_chan_mask = adc1_chan_mask,.adc2_chan_mask = adc2_chan_mask,};ESP_ERROR_CHECK(adc_digi_initialize(&adc_dma_config));adc_digi_configuration_t dig_cfg = {.conv_limit_en = ADC_CONV_LIMIT_EN,.conv_limit_num = 250,.sample_freq_hz = 100000, //采样率.conv_mode = ADC_CONV_MODE,.format = ADC_OUTPUT_TYPE,};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++){uint8_t unit = GET_UNIT(channel[i]);uint8_t ch = channel[i] & 0x7;adc_pattern[i].atten = ADC_ATTEN_DB_11; //衰减倍率adc_pattern[i].channel = ch;adc_pattern[i].unit = unit;adc_pattern[i].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH; //采样位数// ESP_LOGI(TAG, "adc_pattern[%d].atten is :%x", i, adc_pattern[i].atten);// ESP_LOGI(TAG, "adc_pattern[%d].channel is :%x", i, adc_pattern[i].channel);// ESP_LOGI(TAG, "adc_pattern[%d].unit is :%x", i, adc_pattern[i].unit);}dig_cfg.adc_pattern = adc_pattern;ESP_ERROR_CHECK(adc_digi_controller_configure(&dig_cfg));
}#if !CONFIG_IDF_TARGET_ESP32
static bool check_valid_data(const adc_digi_output_data_t *data)
{const unsigned int unit = data->type2.unit;if (unit > 2)return false;if (data->type2.channel >= SOC_ADC_CHANNEL_NUM(unit))return false;return true;
}
#endifvoid app_main(void)
{xTaskCreate(countTask, "countTask", 1024 * 10, NULL, 2, NULL); //创建任务memset(result, 0xcc, TIMES);continuous_adc_init(adc1_chan_mask, adc2_chan_mask, channel, sizeof(channel) / sizeof(adc_channel_t));adc_digi_start();adc_digi_read_bytes(result, TIMES, &ret_num, ADC_MAX_DELAY);for (int i = 0; i < ret_num; i += ADC_RESULT_BYTE){adc_digi_output_data_t *p = (void *)&result[i];value[i] = p->type1.data;// ESP_LOGI(TAG, "%d", p->type1.data);// printf("{value}%d\n", p->type1.data);countNum++;// vTaskDelay(10/ portTICK_PERIOD_MS);}for (int i = 0; i < ret_num; i += ADC_RESULT_BYTE){printf("{value}%d\n", value[i]);}}
五、实验验证
首先将采样率配置为1000000hz,即sample_freq_hz = 100000,将esp32与信号发生器连接,信号发生器设置频率为1kHz,一次采样点数为1000,按下复位键开始一次采样1000个点,存入数组中,接着将数组中的值输出至串口绘图软件观察
可见esp32采样到了10个波形,这说明adc的采样率已经配置为了100000hz
六、可能出现的问题
(1)不能将串口输出与ADC采集放在同一个循环中,否则ADC采样率会被串口发送程序拉低,必须独立开。
(2)对于ESP32来说,最好先关闭看门狗,防止ADC读取数据时重启。
(3)数组建立时前面必须加上static,否则单片机内存会爆炸。
(4)rtc控制器与dig控制器最大的区别在于采样率,dig可以配合DMA达到非常高的采样率,但是无法将采样率配置的很低,rtc控制器则相反,可以根据需要使用不同的控制器。
(5)如果将sample_freq_hz = 200000,会发现countNum并不是200000,这是正常现象,因为freertos的多任务系统会降低采样率,但得益于ESP32的240MHz主频,影响一般不大。
(6)ADC2无法和WiFi同时使用。
ESP32ADC采样率配置(基于ESP-IDF)相关推荐
- CISCO CME:配置基于硬件的电话会议
如果一台配置了CME的Cisco路由器安装了PVDM模块,那就可配置基于硬件的电话会议,以获得诸如MeetMe或cBarge等功能. 1.voice-card命令引用了dspfarm,因此可能会有不止 ...
- 为ASP.NET MVC配置基于Active Directory的表单认证方式
为ASP.NET MVC配置基于Active Directory的表单认证方式 最近一直在研究基于Active Directory的表单认证方式,同时也在关注ASP.NET MVC的情况,同时也在应用 ...
- 配置基于Devstack的嵌套KVM虚拟化
本文为minxihou的翻译文章,转载请注明出处Bob Hou: http://blog.csdn.net/minxihou JmilkFan:minxihou的技术博文方向是 算法&Open ...
- java opencv 开发环境_Java + opencv学习:在Eclipse下配置基于Java的OpenCV开发环境
最近研究OpenCV想用java进行开发,因此研究了一下怎么在Eclipse中配置基于java的Opencv. 第一步:先到OpenCV官网下载你想要的版本,假设使用的是2.4.6版本.这里附上下载地 ...
- 使用apache配置基于IP地址的虚拟主机
使用apache配置基于IP地址的虚拟主机 第一步:设置多个IP地址 这里设置两个IP地址:192.168.1.5 和 192.168.1.8 第二步:在 httpd.conf 文件中加入如下内内容 ...
- 配置基于python的VIM环境
配置基于python的VIM环境 安装插件管理工具 为防止过多插件管理的麻烦,首先安装vim的插件管理工具Vundle.vundle本身的github软件已经有相关的中文文档,地址如下: vundle ...
- RHEL下安装配置基于2台服务器的MYSQL集群
一.介绍 ======== 这篇文档旨在介绍如何在RHEL下安装配置基于2台服务器的MySQL集群.并且实现任意一台服务器出现问题或宕机时MySQL依然能够继续运行. 注意! 虽然这是基于2台服务器的 ...
- NGINX配置基于Node.js服务的负载均衡服务器
NGINX配置基于Node.js服务的负载均衡服务器 本部署指南说明了如何使用NGINX开源和NGINX Plus在Node.js应用程序服务器池之间平衡HTTP和HTTPS通信.本指南中的详细说明适 ...
- 5.7 并行复制配置 基于GTID 搭建中从 基于GTID的备份与恢复,同步中断处理
5.7 并行复制配置 基于GTID 搭建中从 基于GTID的备份与恢复,同步中断处理 这个文章包含三个部分 1:gtid的多线程复制 2:同步中断处理 3:GTID的备份与恢复 下面文字相关的东西 大 ...
最新文章
- iPhone 应用开发:音频播放
- tomcat端口号被占用
- Cannot load libmkl_avx.so or libmkl_def.so
- springboot:banner.txt
- Hibernate + MySQL中文乱码问题
- 使用pg_resetwal时空穿梭找回“幽灵”元组
- jquery --- 网页选项卡
- linux c通过文件描述符以及write和read方法对文件进行读写
- 域名解析服务之DNS查询类型
- 【最新版】Java速成路线(急于找工作!)
- 互联网“高薪榜”曝光,物联网将接棒?
- WordPress技术资讯博客模板
- (72)FPGA模块调用(VHDL调用Verilog)
- 独立完成一个城市选择组件(阿里前端题目,内附知识点、思路)
- Echarts 柱形图最全详解
- hadoop HDFS 流式传输及JAVA API实现代码
- 计算机想ping一下网络,怎么ping网速(详细教您ping网络的方法)
- 《BPF( 伯克利数据包过滤器 ) Performance Tools》 第二章 技术背景
- Excel 序号自动增长,变更
- STM8L051的硬件I2C调试