概述
目前测到三个问题:
一、ADC DMA采样频率sample_freq_hz取值范围611-83333,虽然可以达到83333,但是只能是在while循环里面不停采样才可以,如果想要隔一段时间采样一次则不行,假如隔一段时间使用adc_digi_read_bytes读取40byte数据,结果经常会返回ESP_ERR_INVALID_STATE,且经常读取的数据量少于40byte。
所以ADC DMA采样方式(大约是15us)虽然比单次采样(大约是35us)速度要快,但是不能间隔采样,只能连续不停采样。
二、ADC DMA采样精度太烂,粗略使用还行,要想精确不能用。
三、ADC DMA和WIFI STA模式冲突
adc_digi_read_bytes执行的时候,如果正好wifi sta在初始化或者在连接ap,那么100%会超时,返回值ESP_ERR_TIMEOUT。而且后期无法恢复,只能调用下列函数才可以恢复。
adc_digi_stop();
adc_digi_start();
新解决方案:升级idf到4.4.3
具体见下面代码
代码:
main.c
/* Handwrite Board
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_http_client.h"
#include "driver/gpio.h"
#include "nvs_flash.h"
#include "esp_err.h"
#include "esp_event.h"
#include "esp_wifi.h"
//local variable
static const char* TAG = "Main";
TaskHandle_t wifi_sta_ap_task_handle = NULL;
TaskHandle_t rmt_task_handle = NULL;
//extern method
extern void HWB_Wifi_Init_Sta_And_Ap();
extern void HWB_RMT_Task(void *Parameters);
void HWB_Wifi_Sta_Ap_Task(void *Parameters)
{
HWB_Wifi_Init_Sta_And_Ap();
}
void app_main(void)
{
/* Print chip information */
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
printf("This is %s chip with %d CPU core(s), WiFi%s%s, ",
CONFIG_IDF_TARGET,
chip_info.cores,
(chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
printf("silicon revision %d, ", chip_info.revision);
printf("%dMB %s flashn", spi_flash_get_chip_size() / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
ESP_ERROR_CHECK(nvs_flash_erase());//test
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
//创建业务处理线程,启动wifi sta
xTaskCreate(HWB_Wifi_Sta_Ap_Task, "HWB_Wifi_Sta_Ap_Task", (4*1024), NULL, 1, &wifi_sta_ap_task_handle);
xTaskCreate(HWB_RMT_Task, "HWB_RMT_Task", (4*1024), NULL, 20, &rmt_task_handle);
}
rmt.c
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/rmt.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"
#include <sys/time.h>
//extern variable
extern xQueueHandle adc_data_queue;
//extern method
//local variable
static const char *TAG = "example";
#define TIMES 40
#define GET_UNIT(x) ((x>>3) & 0x1)
#define ADC_RESULT_BYTE 4
#define ADC_CONV_LIMIT_EN 0
#define ADC_CONV_MODE ADC_CONV_SINGLE_UNIT_1
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2
#define RMT_TX_U102_29_CHANNEL RMT_CHANNEL_0
#define RMT_TX_U102_29_GPIO 4
#define RMT_TX_U102_16_CHANNEL RMT_CHANNEL_1
#define RMT_TX_U102_16_GPIO 6
#define RMT_TX_U102_15_CHANNEL RMT_CHANNEL_2
#define RMT_TX_U102_15_GPIO 7
#define ADC_DMA_MODE 1
#define DEFAULT_VREF 1100 //Use adc2_vref_to_gpio() to obtain a better estimate
static esp_adc_cal_characteristics_t *adc_chars;
static const adc_bits_width_t width = ADC_WIDTH_BIT_12;
static const adc_atten_t atten = ADC_ATTEN_DB_11;
#if ADC_DMA_MODE
static uint16_t adc1_chan_mask = BIT(4);
static uint16_t adc2_chan_mask = 0;
static adc_channel_t channel1[1] = {ADC1_CHANNEL_4};
#endif
/*
* Prepare a raw table with a message in the Morse code
*
* The message is "ESP" : . ... .--.
*
* The table structure represents the RMT item structure:
* {duration, level, duration, level}
*
*/
static const rmt_item32_t morse_esp_type1[] = {
{{{ 10, 0, 60, 1 }}},
{{{ 20, 0, 10, 0 }}},
};
static const rmt_item32_t morse_esp2[] = {
{{{ 100, 1, 180, 0 }}},
};
// static const rmt_item32_t morse_esp3[] = {
// {{{ 100, 1, 60, 0 }}},
// };
extern int THRESHOLD_VALUE;
static void continuous_adc_init(uint16_t adc1_chan_mask, uint16_t adc2_chan_mask, adc_channel_t *channel, uint8_t channel_num)
{
printf("continuous_adc_init %d,%dn",adc1_chan_mask,adc2_chan_mask);
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 = 83333,
.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;
printf("adc_pattern[%d].atten is :%xn", i, adc_pattern[i].atten);
printf("adc_pattern[%d].channel is :%xn", i, adc_pattern[i].channel);
printf("adc_pattern[%d].unit is :%xn", i, adc_pattern[i].unit);
printf("adc_pattern[%d].bit_width is :%d,%dn", i, adc_pattern[i].bit_width,SOC_ADC_DIGI_MAX_BITWIDTH);
}
dig_cfg.adc_pattern = adc_pattern;
ESP_ERROR_CHECK(adc_digi_controller_configure(&dig_cfg));
}
static void check_efuse(void)
{
//Check if TP is burned into eFuse
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) {
printf("eFuse Two Point: Supportedn");
} else {
printf("eFuse Two Point: NOT supportedn");
}
//Check Vref is burned into eFuse
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) {
printf("eFuse Vref: Supportedn");
} else {
printf("eFuse Vref: NOT supportedn");
}
}
static void print_char_val_type(esp_adc_cal_value_t val_type)
{
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
printf("Characterized using Two Point Valuen");
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
printf("Characterized using eFuse Vrefn");
} else {
printf("Characterized using Default Vrefn");
}
}
/*
* Initialize the RMT Tx channel
*/
static void rmt_tx_init(void)
{
printf("run rmt_tx_init");
rmt_config_t config1 = RMT_DEFAULT_CONFIG_TX(RMT_TX_U102_29_GPIO, RMT_TX_U102_29_CHANNEL);
config1.flags = RMT_CHANNEL_FLAGS_INVERT_SIG;
config1.tx_config.carrier_en = true;
config1.tx_config.carrier_duty_percent = 50;
config1.tx_config.carrier_freq_hz = 500000;
config1.clk_div = 80;
rmt_config_t config2 = RMT_DEFAULT_CONFIG_TX(RMT_TX_U102_16_GPIO, RMT_TX_U102_16_CHANNEL);
config2.tx_config.carrier_en = false;
config2.tx_config.carrier_duty_percent = 50;
config2.tx_config.carrier_freq_hz = 500000;
config2.clk_div = 80;
rmt_config_t config3 = RMT_DEFAULT_CONFIG_TX(RMT_TX_U102_15_GPIO, RMT_TX_U102_15_CHANNEL);
config3.tx_config.carrier_en = false;
config3.tx_config.carrier_duty_percent = 50;
config3.tx_config.carrier_freq_hz = 500000;
config3.clk_div = 80;
ESP_ERROR_CHECK(rmt_config_self(&config1, true)); /*20221011 YB added new API to set open Drain for TX or not*/
ESP_ERROR_CHECK(rmt_config(&config2));
ESP_ERROR_CHECK(rmt_config(&config3));
ESP_ERROR_CHECK(rmt_driver_install(config1.channel, 0, 0));
ESP_ERROR_CHECK(rmt_driver_install(config2.channel, 0, 0));
ESP_ERROR_CHECK(rmt_driver_install(config3.channel, 0, 0));
ESP_ERROR_CHECK(rmt_add_channel_to_group(RMT_TX_U102_29_CHANNEL));
ESP_ERROR_CHECK(rmt_add_channel_to_group(RMT_TX_U102_16_CHANNEL));
ESP_ERROR_CHECK(rmt_add_channel_to_group(RMT_TX_U102_15_CHANNEL));
}
void HWB_RMT_Task(void *Parameters)
{
printf("Configuring transmitter");
uint32_t ret_num = 0;
uint8_t result[TIMES] = {0};
memset(result, 0xcc, TIMES);
//Check if Two Point or Vref are burned into eFuse
check_efuse();
//Characterize ADC
adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t));
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(ADC_UNIT_1, atten, width, DEFAULT_VREF, adc_chars);
print_char_val_type(val_type);
#if ADC_DMA_MODE
continuous_adc_init(adc1_chan_mask, adc2_chan_mask, channel1, sizeof(channel1) / sizeof(adc_channel_t));
adc_digi_start();
#endif
rmt_tx_init();
while (1) {
ESP_ERROR_CHECK(rmt_write_items(RMT_TX_U102_15_CHANNEL, morse_esp2, sizeof(morse_esp2) / sizeof(morse_esp2[0]), false));
ESP_ERROR_CHECK(rmt_write_items(RMT_TX_U102_29_CHANNEL, morse_esp_type1, sizeof(morse_esp_type1) / sizeof(morse_esp_type1[0]), false));
ESP_ERROR_CHECK(rmt_write_items(RMT_TX_U102_16_CHANNEL, morse_esp_type1, sizeof(morse_esp_type1) / sizeof(morse_esp_type1[0]), false));
rmt_wait_tx_done(RMT_TX_U102_16_CHANNEL,10);
#if ADC_DMA_MODE
esp_err_t ret = adc_digi_read_bytes(result, TIMES, &ret_num, 10/*ADC_MAX_DELAY*/);
uint32_t max_adc_val = 0;
if (ret == ESP_OK || ret == ESP_ERR_INVALID_STATE) {
for (int i = 0; i < ret_num; i += ADC_RESULT_BYTE) {
adc_digi_output_data_t *p = (void*)&result[i];
if (p->type2.unit == 0 && p->type2.channel == 4) {
max_adc_val = (max_adc_val > p->type2.data ? max_adc_val : p->type2.data);
printf("Unit: %d,Channel: %d, Value: %dn", p->type2.unit+1, p->type2.channel, p->type2.data);
}
}
}else if(ret == ESP_ERR_TIMEOUT){
printf("ESP_ERR_TIMEOUTn");
// adc_digi_stop();
// adc_digi_start();
}
#endif
rmt_wait_tx_done(RMT_TX_U102_15_CHANNEL,10);
}
}
wifi_ap_sta.c
/* WiFi station Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#define HWB_WIFI_AP_SSID "1TMT_HWB"
#define HWB_WIFI_AP_PASSWORD "12345678" //最小长度是8,最大长度是64
#define HWB_WIFI_AP_CHANNEL 1 //范围1-13,默认值1
#define HWB_WIFI_AP_MAX_STA_CONN 1
#define HWB_WIFI_STA_MAXIMUM_RETRY 5
//local variable
static const char *TAG = "HWB_Wifi_Sta_Ap";
//local method
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
printf("WIFI_EVENT_STA_START");
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
printf("WIFI_EVENT_STA_DISCONNECTED");
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
printf("WIFI_EVENT_STA_CONNECTED");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
printf("IP_EVENT_STA_GOT_IP");
}else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) {
printf("WIFI_EVENT_AP_STACONNECTED");
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STADISCONNECTED) {
printf("WIFI_EVENT_AP_STADISCONNECTED");
}
}
void HWB_Wifi_Init_Sta_And_Ap()
{
printf("HWB_Wifi_Init_Sta_And_Ap start");
ESP_ERROR_CHECK(esp_netif_init());//创建一个 LwIP(轻量级的 TCP/IP 协议栈)核心任务,并初始化 LwIP 相关工作
//创建一个系统事件任务,并初始化应用程序事件的回调函数。
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();//创建带有 TCP/IP 堆栈的默认网络接口实例绑定 AP
//WIFI 驱动程序初始化配置时建议使用 IDF 默认的参数进行配置,WIFI_INIT_CONFIG_DEFAULT是一个宏,
//这个宏的作用就是对结构体wifi_init_config_t进行初始化
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
//创建 WIFI 驱动程序任务并初始化 WIFI 驱动程序,这个API必须第一个调用,才能调用wifi的其他API
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
//向system event loop注册event handler
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
//wifi_config_t是配置WIFI的集合union。
//wifi_ap_config_t ap 用于配置AP
//wifi_sta_config_t sta 用于配置STA
//WIFI STA Config
wifi_config_t wifi_config_sta;
memset(&wifi_config_sta, 0, sizeof(wifi_config_sta));
wifi_config_sta.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
if (strlen((char *)wifi_config_sta.sta.password) == 0) {
printf("authmode WIFI_AUTH_OPEN");
wifi_config_sta.sta.threshold.authmode = WIFI_AUTH_OPEN;
}
//WIFI AP Config
wifi_config_t wifi_config_ap = {
.ap = {
.ssid = HWB_WIFI_AP_SSID,
.ssid_len = strlen(HWB_WIFI_AP_SSID),
.channel = HWB_WIFI_AP_CHANNEL,
.password = HWB_WIFI_AP_PASSWORD,
.max_connection = HWB_WIFI_AP_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA_WPA2_PSK
},
};
if (strlen(HWB_WIFI_AP_PASSWORD) == 0) {
wifi_config_ap.ap.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA) );//配置WIFI的工作模式为AP-STA,即AP 和STA共存模式;
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config_sta) );//对wifi的STA模式进行配置
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config_ap) );//对wifi的AP模式进行配置
ESP_ERROR_CHECK(esp_wifi_start() );//启动WIFI
printf("HWB_Wifi_Init_Sta_And_Ap finished.");
while(1){
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
最后
以上就是粗犷小土豆为你收集整理的ESP32S3 ADC DMA使用记录(坑记录)(大牛欢迎给出建议)的全部内容,希望文章能够帮你解决ESP32S3 ADC DMA使用记录(坑记录)(大牛欢迎给出建议)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复