espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.72k stars 7.3k forks source link

ESP32-S3 ADC continuous timing error on 20kHz callback (IDFGH-10346) #11604

Open klaus-liebler opened 1 year ago

klaus-liebler commented 1 year ago

Answers checklist.

IDF version.

v5.0.2

Operating System used.

Windows

How did you build your project?

VS Code IDE

If you are using Windows, please specify command line type.

None

Development Kit.

Standard Dev. Board

Power Supply used.

USB

What is the expected behavior?

I need to sample a signal in constant intervals and react immediately, if a condition occurs. I have written a small test program to check the timing capabilities of the ESP32 architecture. As you can see in the code below, the ADC is setup in continuous mode with a single channel and a sampling rate of 20kHz. The callback shoud be invoked after each sample. That means, the callback shoud occur every 50us and there should be roughly 200k callbacks in a 10 seconds test interval.

What is the actual behavior?

Instead, there are 176545 callbacks and after four correctly timed samples (50us time interval) there is an outlier (86...89us time interval).

Steps to reproduce.

Compile and run my test program

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <esp_log.h>
#include <esp_attr.h>
#include <freertos/FreeRTOS.h>
#include <sdkconfig.h>
#include <freertos/task.h>
#include <driver/gptimer.h>
#include <esp_rom_sys.h>
#include <esp_adc/adc_continuous.h>
#include <wifi_sta.hh>
#include <wifi_softap.hh>
#include <esp_timer.h>

#include <esp_http_server.h>

#define TAG "MAIN"

#if (SOC_ADC_DIGI_RESULT_BYTES == 2)
#define ADC_TEST_OUTPUT_TYPE    ADC_DIGI_OUTPUT_FORMAT_TYPE1
#else
#define ADC_TEST_OUTPUT_TYPE    ADC_DIGI_OUTPUT_FORMAT_TYPE2
#endif

#define ADC_TEST_FREQ_HZ        (20 * 1000)
constexpr int CONVERSIONS_IN_ONE_CALLBACK{1};

static volatile DRAM_ATTR uint32_t successfulSamples{0};
static volatile DRAM_ATTR uint32_t errors{0};
adc_continuous_handle_t handle{nullptr};
constexpr size_t SIZE{1024};
static volatile DRAM_ATTR uint32_t timestamps[SIZE];
static volatile DRAM_ATTR size_t timestampIndex{0};
static volatile DRAM_ATTR int64_t previousTime{0};

extern "C" bool IRAM_ATTR NOINLINE_ATTR s_conv_done_cb(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data)
{
    if(timestampIndex<SIZE){
        int64_t now = esp_timer_get_time();
        timestamps[timestampIndex]=now-previousTime;
        previousTime=now;
        timestampIndex++;
    }
    if(edata->size==CONVERSIONS_IN_ONE_CALLBACK*SOC_ADC_DIGI_RESULT_BYTES){
        successfulSamples=successfulSamples+CONVERSIONS_IN_ONE_CALLBACK;
    }else{
        errors=errors+1;
    }
    return false;
}

extern "C" void app_main(void)
{

    adc_continuous_handle_cfg_t adc_config = {};

    adc_config.max_store_buf_size = 1024;
    adc_config.conv_frame_size = CONVERSIONS_IN_ONE_CALLBACK*SOC_ADC_DIGI_DATA_BYTES_PER_CONV;
    adc_continuous_new_handle(&adc_config, &handle);

    adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX];
    adc_pattern[0].atten = ADC_ATTEN_DB_0;
    adc_pattern[0].channel = ADC_CHANNEL_0;
    adc_pattern[0].unit = ADC_UNIT_1;
    adc_pattern[0].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH;

    adc_continuous_config_t dig_cfg = {};
    dig_cfg.sample_freq_hz = ADC_TEST_FREQ_HZ,
    dig_cfg.conv_mode = ADC_CONV_SINGLE_UNIT_1;
    dig_cfg.format = ADC_TEST_OUTPUT_TYPE;
    dig_cfg.adc_pattern = adc_pattern;
    dig_cfg.pattern_num = 1;
    adc_continuous_config(handle, &dig_cfg);

    adc_continuous_evt_cbs_t cbs = {};

    cbs.on_conv_done = s_conv_done_cb;

    adc_continuous_register_event_callbacks(handle, &cbs, nullptr);

    ESP_LOGI(TAG, "About to start ADC");
    previousTime=esp_timer_get_time();
    adc_continuous_start(handle);
    vTaskDelay(pdMS_TO_TICKS(10000));
    adc_continuous_stop(handle);
    ESP_LOGI(TAG, "About to stop ADC: successful:%lu, errors %lu", successfulSamples, errors);

    adc_continuous_deinit(handle);
    for(int i=0;i<SIZE;i+=8){
        printf("%lu;%lu;%lu;%lu;%lu;%lu;%lu;%lu;\n", timestamps[i+0], timestamps[i+1], timestamps[i+2], timestamps[i+3], timestamps[i+4], timestamps[i+5], timestamps[i+6], timestamps[i+7]);
    }

    while (true)
    {
        ESP_LOGI(TAG, "Heap %6lu", esp_get_free_heap_size());
        vTaskDelay(pdMS_TO_TICKS(10000));
    }

}

Debug Logs.

I (193) cpu_start: App version:      1
I (197) cpu_start: Compile time:     Jun  6 2023 16:13:45
I (203) cpu_start: ELF file SHA256:  700ef01caec23f99...
I (209) cpu_start: ESP-IDF:          v5.0.2
I (214) cpu_start: Min chip rev:     v0.0
I (219) cpu_start: Max chip rev:     v0.99
I (224) cpu_start: Chip rev:         v0.1
I (228) heap_init: Initializing. RAM available for dynamic allocation:
I (236) heap_init: At 3FC96260 len 000534B0 (333 KiB): DRAM
I (242) heap_init: At 3FCE9710 len 00005724 (21 KiB): STACK/DRAM
I (248) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (255) heap_init: At 600FE010 len 00001FF0 (7 KiB): RTCRAM
I (262) spi_flash: detected chip: generic
I (266) spi_flash: flash io: dio
W (269) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (284) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (304) gpio: GPIO[1]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (304) MAIN: About to start ADC
I (10314) MAIN: About to stop ADC: successful:176545, errors 0
292;50;50;50;50;88;50;50;
50;50;89;50;50;50;50;89;
50;50;50;50;89;50;50;50;
50;89;50;50;50;50;88;50;
50;50;50;89;50;50;50;50;
89;50;50;50;50;89;50;50;
50;50;89;50;50;50;50;88;
50;50;50;50;89;50;50;50;
50;89;50;50;50;50;89;50;
50;50;50;89;50;50;50;50;
88;50;50;50;50;89;50;50;
50;50;89;50;50;50;50;89;
50;50;50;50;89;50;50;50;
50;88;50;50;50;50;89;50;
50;50;50;89;50;50;50;50;
89;50;50;50;50;89;50;50;
50;50;88;50;50;50;50;89;
50;50;50;50;89;50;50;50;
50;89;50;50;50;50;89;50;
50;50;50;88;50;50;50;50;
89;50;50;50;50;89;50;50;
50;50;89;50;50;50;50;89;
50;50;50;50;88;50;50;50;
50;89;50;50;50;50;89;50;
50;50;50;89;50;50;50;50;
89;50;50;50;50;88;50;50;
50;50;89;50;50;50;50;89;
50;50;50;50;89;50;50;50;
50;89;50;50;50;50;88;50;
50;50;50;89;50;50;50;50;
89;50;50;50;50;89;50;50;
50;50;89;50;50;50;50;87;
48;50;50;50;85;50;50;50;
50;85;50;50;50;50;86;50;
50;50;50;85;50;50;50;50;
85;50;50;50;50;85;50;50;
50;50;85;50;50;50;50;86;
50;50;50;50;85;50;50;50;
50;85;50;50;50;50;85;50;
50;50;50;85;50;50;50;50;
86;50;50;50;50;85;50;50;
50;50;85;50;50;50;50;85;
50;50;50;50;85;50;50;50;
50;86;50;50;50;50;85;50;
50;50;50;85;50;50;50;50;
85;50;50;50;50;85;50;50;
50;50;86;50;50;50;50;85;
50;50;50;50;85;50;50;50;
50;85;50;50;50;50;85;50;
50;50;50;86;50;50;50;50;
85;50;50;50;50;85;50;50;
50;50;85;50;50;50;50;85;
50;50;50;50;86;50;50;50;
50;85;50;50;50;50;85;50;
50;50;50;85;50;50;50;50;
85;50;50;50;50;86;50;50;
50;50;85;50;50;50;50;85;
50;50;50;50;85;50;50;50;
50;85;50;50;50;50;86;50;
50;50;50;85;50;50;50;50;
85;50;50;50;50;85;50;50;
50;50;85;50;50;50;50;86;
50;50;50;50;85;50;50;50;
50;85;50;50;50;50;85;50;
50;50;50;85;50;50;50;50;
86;50;50;50;50;85;50;50;
50;50;85;50;50;50;50;85;
50;50;50;50;85;50;50;50;
50;86;50;50;50;50;85;50;
50;50;50;85;50;50;50;50;
85;50;50;50;50;85;50;50;
50;50;86;50;50;50;50;85;
50;50;50;50;85;50;50;50;
50;85;50;50;50;50;85;50;
50;50;50;86;50;50;50;50;
85;50;50;50;50;85;50;50;
50;50;85;50;50;50;50;85;
50;50;50;50;86;50;50;50;
50;85;50;50;50;50;85;50;
50;50;50;85;50;50;50;50;
85;50;50;50;50;86;50;50;
50;50;85;50;50;50;50;85;
50;50;50;50;85;50;50;50;
50;85;50;50;50;50;86;50;
50;50;50;85;50;50;50;50;
85;50;50;50;50;85;50;50;
50;50;85;50;50;50;50;86;
50;50;50;50;85;50;50;50;
50;85;50;50;50;50;85;50;
50;50;50;85;50;50;50;50;
86;50;50;50;50;85;50;50;
50;50;85;50;50;50;50;85;
50;50;50;50;85;50;50;50;
50;86;50;50;50;50;85;50;
50;50;50;85;50;50;50;50;
85;50;50;50;50;85;50;50;
50;50;86;50;50;50;50;85;
50;50;50;50;85;50;50;50;
50;85;50;50;50;50;85;50;
50;50;50;86;50;50;50;50;
85;50;50;50;50;85;50;50;
50;50;85;50;50;50;50;85;
50;50;50;50;86;50;50;50;
50;85;50;50;50;50;85;50;
50;50;50;85;50;50;50;50;
85;50;50;50;50;86;50;50;
50;50;85;50;50;50;50;85;
50;50;50;50;85;50;50;50;
50;85;50;50;50;50;86;50;
50;50;50;85;50;50;50;50;
85;50;50;50;50;85;50;50;
50;50;85;50;50;50;50;86;
50;50;50;50;85;50;50;50;
50;85;50;50;50;50;85;50;
50;50;50;85;50;50;50;50;
86;50;50;50;50;85;50;50;
50;50;85;50;50;50;50;85;
50;50;50;50;85;50;50;50;
50;86;50;50;50;50;85;50;
50;50;50;85;50;50;50;50;
85;50;50;50;50;85;50;50;
50;50;86;50;50;50;50;85;
50;50;50;50;85;50;50;50;
50;85;50;50;50;50;85;50;
50;50;50;86;50;50;50;50;
85;50;50;50;50;85;50;50;
50;50;85;50;50;50;50;85;
50;50;50;50;86;50;50;50;
I (10594) MAIN: Heap 382368

More Information.

The error rate changes depending on the callback frequency and the sample rate, see https://github.com/espressif/esp-idf/issues/10612#issuecomment-1572787379

5ami commented 1 year ago

This callback is an Interrupt generated by DMA (I am not sure how that works). what I would do to get an idea of the sample rate is to generate an interrupt on APB_SARADC_ADC1_DONE_INT_ENA which indicates a conversion DONE event on the ADC. (check TRM Register 39.72. APB_SARADC_INT_ENA_REG (0x005C) ).

5ami commented 1 year ago

I added this peace of code to make a level 1 interrupt with Interrupt source 65 (to generate an interrupt on APB_SARADC_ADC1_DONE_INT_ENA)

/*Setting up the Interrupt*/
/*APB_SARADC_ADC1_DONE_INT: Triggered when SAR ADC1 completes one data conversion.*/

REG_SET_BIT(APB_SARADC_INT_ENA_REG, APB_SARADC_ADC1_DONE_INT_ENA); 

xt_ints_off(1<<13);
intr_matrix_set(0, 65, 13);
xt_ints_on(1<<13);
xt_set_interrupt_handler(13,  my_int, NULL);

-I am using both ADC units (two channels from two different units). -I toggle a GPIO inside the interrupt handler.

No matter what sampling frequency I set, the GPIO is toggled at the same frequency. the frequency of the interrupt is so high that no other task can run.

this might indicate some issue with ADC itself (if my code is correct ofc haha). I will open a separate issue on this.

Continous ADC INT DONE