-
Notifications
You must be signed in to change notification settings - Fork 7.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ESP32 ADC DMA sampling rate different than configured sampling frequency with ESP-IDF 5.0 (IDFGH-9225) #10612
Comments
I've just found the exact same thing. I've arrived at my calculation of 1.2195 independently so it seem there is a real bug here. Interestingly the data captured does seem complete. I fed a sine wave in and it looks correct when plotted. There must be a bug in the clock configuration for the ADC. |
Relevant code is here: /**
* For esp32s2 and later chips
* - Set ADC digital controller clock division factor. The clock is divided from `APLL` or `APB` clock.
* Expression: controller_clk = APLL/APB * (div_num + div_a / div_b + 1).
* - Enable clock and select clock source for ADC digital controller.
* For esp32, use I2S clock
*/
static void adc_hal_digi_sample_freq_config(adc_hal_dma_ctx_t *hal, uint32_t freq)
{
#if !CONFIG_IDF_TARGET_ESP32
uint32_t interval = APB_CLK_FREQ / (ADC_LL_CLKM_DIV_NUM_DEFAULT + ADC_LL_CLKM_DIV_A_DEFAULT / ADC_LL_CLKM_DIV_B_DEFAULT + 1) / 2 / freq;
//set sample interval
adc_ll_digi_set_trigger_interval(interval);
//Here we set the clock divider factor to make the digital clock to 5M Hz
adc_ll_digi_controller_clk_div(ADC_LL_CLKM_DIV_NUM_DEFAULT, ADC_LL_CLKM_DIV_B_DEFAULT, ADC_LL_CLKM_DIV_A_DEFAULT);
adc_ll_digi_clk_sel(0); //use APB
#else
i2s_ll_rx_clk_set_src(hal->dev, I2S_CLK_SRC_DEFAULT); /*!< Clock from PLL_D2_CLK(160M)*/
uint32_t bclk_div = 16;
uint32_t bclk = freq * 2;
uint32_t mclk = bclk * bclk_div;
uint32_t mclk_div = I2S_BASE_CLK / mclk;
i2s_ll_rx_set_mclk(hal->dev, I2S_BASE_CLK, mclk, mclk_div);
i2s_ll_rx_set_bck_div_num(hal->dev, bclk_div);
#endif
} |
6 months have passed since I've last posted something related to the ongoing ADC continuous mode problems here #8874. I've re-visited the issue now and I still have no clue how this driver is supposed to behave/work... I have taken the initial example from this issue and removed code for "non-S3" chips. I've setup the ADC to take 100.000 samples @ 10kHz. My highly advanced math skills tell me that it should take roughly 10s to take those, yet the loop which collects it finishes within 2.5s. So I'm not off by a factor of 1.22 but a factor of 4. The ESP-IDF version I'm using is v5.2-dev-350-g56123c52aa Any ideas, suggestions...? #include <esp_adc/adc_continuous.h>
#include <esp_task.h>
#include <esp_timer.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <array>
#include <cstdio>
static constexpr auto max_number_of_samples{100'000uz};
static constexpr auto sampling_frequency{10u * 1000u};
static constexpr auto expected_time{static_cast<double>(max_number_of_samples) /
static_cast<double>(sampling_frequency)};
adc_continuous_handle_t adc1_handle{};
std::array<uint8_t, 256uz> result{};
// Times how long ADC takes to take "max_number_of_samples" at configured
// sampling frequency
void task_function(void*) {
uint32_t ret_num{};
size_t number_of_samples{};
// Start ADC
int64_t previous_us{esp_timer_get_time()};
ESP_ERROR_CHECK(adc_continuous_start(adc1_handle));
for (;;) {
// Read samples
if (adc_continuous_read(
adc1_handle, data(result), size(result), &ret_num, 0u) != ESP_OK)
continue;
// Sum all, once done print time it took to measure them
number_of_samples += ret_num;
if (number_of_samples < max_number_of_samples) continue;
number_of_samples = 0uz;
// Convert time it took from us to s (10^6)
auto const actual_us{esp_timer_get_time()};
auto const delta_time{(actual_us - previous_us) / 1e6};
printf("Taking %zu samples @ %uHz took %fs instead of %fs\n",
max_number_of_samples,
sampling_frequency,
delta_time,
expected_time);
// Stop ADC and read till empty (might print error message that ADC is
// already stopped since print before takes long)
adc_continuous_stop(adc1_handle);
while (adc_continuous_read(
adc1_handle, data(result), size(result), &ret_num, 0u) == ESP_OK)
;
// Restart ADC
previous_us = esp_timer_get_time();
ESP_ERROR_CHECK(adc_continuous_start(adc1_handle));
}
}
extern "C" void app_main() {
// Init ADC in continuous mode
adc_continuous_handle_cfg_t adc_config{
.max_store_buf_size = size(result) * SOC_ADC_DIGI_RESULT_BYTES * 8u,
.conv_frame_size = size(result),
};
ESP_ERROR_CHECK(adc_continuous_new_handle(&adc_config, &adc1_handle));
std::array patterns{
adc_digi_pattern_config_t{
.atten = ADC_ATTEN_DB_0,
.channel = ADC_CHANNEL_0,
.unit = ADC_UNIT_1,
.bit_width = ADC_BITWIDTH_12,
},
adc_digi_pattern_config_t{
.atten = ADC_ATTEN_DB_0,
.channel = ADC_CHANNEL_0,
.unit = ADC_UNIT_1,
.bit_width = ADC_BITWIDTH_12,
},
};
adc_continuous_config_t dig_cfg{
.pattern_num = size(patterns),
.adc_pattern = data(patterns),
.sample_freq_hz = sampling_frequency,
.conv_mode = ADC_CONV_SINGLE_UNIT_1,
.format = ADC_DIGI_OUTPUT_FORMAT_TYPE2,
};
ESP_ERROR_CHECK(adc_continuous_config(adc1_handle, &dig_cfg));
// Create a max priority task on core 1
xTaskCreatePinnedToCore(
task_function, NULL, 4096uz, NULL, ESP_TASK_PRIO_MAX, NULL, 1);
for (;;)
vTaskDelay(pdMS_TO_TICKS(1000u));
} I've also created a zip which contains this example: /edit |
@higaski it looks like you are configuring two patterns with the same channel. |
I'm aware of that, but since I didn't care about the conversion results anyhow it didn't matter. I've just written that snippet to estimate the sampling frequency. |
Could it be causing the sampling rate to be doubled? |
No definitely not. The number of channels defined in the pattern does not have any effect on the sampling rate, at least not in the ESP-IDF version I've been using. I've fixed my stupid mistake and updated the example: Using the high resolution timer I can hardly find any discrepancy between the set sampling rate and the actual one. When taking 100.000 samples @ 10kHz the error measured this way is ~ <0.03% which is good enough for my use-cases. |
Hi, I'm also facing the problem, that the real sampling freqeuncy is about 0.8 times the configured sampling frequency. Is there still no solution for this? |
Has anyone from the IDF team even seen this yet? Hello out there!? This is a pretty serious bug. |
The issue seems to be platform dependent. I've run your code on an ESP32-S3 and the error rate there is nowhere even near that high. =~=~=~=~=~=~=~=~=~=~=~= PuTTY log 2023.05.10 12:22:44 =~=~=~=~=~=~=~=~=~=~=~=
Time: 76863611, samples per second: 40250
Time: 77863611, samples per second: 40250
Time: 78863611, samples per second: 40250
Time: 79863611, samples per second: 40250
Time: 80863611, samples per second: 40500 |
Yes, I can confirm that I have tried the original code I submitted in bug report on |
Could this PR fix the issue? #11411 |
I am facing similar issues with my ESP32-S3 and IDF 5.0.2 #include <esp_log.h>
#include <esp_attr.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_adc/adc_continuous.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
constexpr uint32_t ADC_TEST_FREQ_HZ{20 * 1000};
constexpr uint32_t CONVERSIONS_IN_ONE_CALLBACK{1};
constexpr uint32_t MEASUREMENT_DURATION_SECS{10};
static volatile DRAM_ATTR uint32_t successfulSamples{0};
static volatile DRAM_ATTR uint32_t errors{0};
adc_continuous_handle_t handle{nullptr};
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(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, "Start ADC with %luHz and %lu samples per callback and %lu seconds measurement time", ADC_TEST_FREQ_HZ, CONVERSIONS_IN_ONE_CALLBACK, MEASUREMENT_DURATION_SECS);
adc_continuous_start(handle);
vTaskDelay(pdMS_TO_TICKS(MEASUREMENT_DURATION_SECS*1000));
adc_continuous_stop(handle);
ESP_LOGI(TAG, "Stop ADC: successful:%lu (expecting: %lu!), errors %lu", successfulSamples, MEASUREMENT_DURATION_SECS*ADC_TEST_FREQ_HZ, errors);
while (true)
{
ESP_LOGI(TAG, "Heap %6lu", esp_get_free_heap_size());
vTaskDelay(pdMS_TO_TICKS(10000));
}
} The relevant log is
So, instead of 200,000 samples in 10 seconds, I get 176,266. Regards, Klaus |
@klaus-liebler that result is strange because higaski said that it works when using an ESP32-S3 derivate. The only difference I can see is that you are using IDF 5.0.2 and higaski is using IDF 5.2. Can you switch to the IDF 5.2 and doublecheck whether your code works with that version? Maybe @higaski can also try it with IDF 5.0.2 to have a better evidence? |
I'm getting 0.18 to 0.19% error with 5.0.2 |
Thanks @klaus-liebler and @higaski, but this effect seems to be a different problem. In case of using the ESP32 changing the amount of callbacks does not have any impact of the factor of 0.8. |
@Tom-Lichtinghagen , do you recommend to file another issue for this or is this just beyond the limits of the architecture? |
Hi, switching to the ESP32-S3 solved the problem regarding the sample rate deviation. But now I'm facing the problem, that the pool overflow interrupt is triggered although I read out the pool fast enough. Does anyone has face this problem also? Another question from my side is if the conversion frame size and the maximal pool buffer size refer to all configured adc channels? That would imply that the time between two conversion done interrupts would depend on the number of used adc channel (with the conditional that the conversion frame size is the same). But tests did not show the behavior. |
FWIW – on ESP32 with ESP-IDF v5.1.1 the the sampling rate is still incorrect. When used to record audio through an analog input, it plays back at noticeably a higher pitch than the input. |
Hi ! I am confronted to this issue it seems, with indeed, my requested frequency (44100 Hz) divided by ~1.22 on ESP32. From what I can see, the ADC continuous driver uses the i2s DMA, which is configured where @felixcollins mentioned in #10612 (comment) According to the esp32 TRM:
(Page 310, 12.3 The Clock of I2S Module) But in the initialization code, of the The DAC continuous driver has a Did anyone successfully got the correct frequency with PLL_D2_CLK(160M) ? |
There is no noticeable improvement when using APLL as clock source. But I was able to confirm that the I2S clocks are correctly configured to closely match the requested sampling frequency. In my test program, the interrupt routine is also called correctly:
But still, the effective sampling frequency is 36082 Hz instead of 44100. (/1.22) In v4.4, with a similar program (the I will be looking into the DMA configuration next. It could be misconfigured and ends up timeout-ing sometimes. |
I pushed a simple fix, which is more a workaround, but can help for now. Next, I'd like to analyze why those 2 samples are lost in continuous mode. |
Now, will Espressif investigate a proper fix for this!? It seems like @cazou has found an important clue. |
This is definitely an issue on the ESP32. The same code on esp32-H2 (or others as mentioned by other people) yields the correct frequency. All the other ones have it deactivated by default (and the API only uses the default value):
Also, the ESP32 is the only one using the i2s DMA. Some use SPI or GDMA. There is no GDMA on ESP32 but there is a SPI DMA, maybe using that one would work better, but I don't know if that is possible. It would be nice to have an investigation on espressif side, at least to confirm that it is an issue on the ESP32 and that it won't be solved. Maybe @mythbuster5 could shine some light on this ? |
Found something Setting SYSCON.saradc_ctrl2.meas_num_limit=0; after starting DMA, I'm getting exact sample rates - as far as clock dividers allow. Before: ADC values seem to be stable. I'll let it run for a day. The question is now: Where is the Bug? Maybe in chip documentation? And in all code derived from that? BTW: With APLL there are only 0 values from adc. Maybe clock is not routed to it. |
Indeed ! because of I'm also getting way better results this way. So, as a workaround if people have this issue:
And add this after the call to
The ESP_LOGI seems necessary for some reason, maybe a delay issue. You may still miss 2 samples at the beginning though. I'll create a PR to force espressif to hopefully move a bit on this |
Just an Info for Arduino users: |
And Yes, there might be a bug on chip. |
My startup code looks like this now:
Seems to be safe. |
Answers checklist.
General issue report
Hello, I have modified the ESP-IDF examples/peripherals/adc/continuous_read/main/continuous_read_main.c as follows to check the samples generated per second
Getting the following output when the above example is run on ESP32-DevKit-V1 board:
As seen in the results the samples are generated at rate 65287 samples/second whereas configured sampling frequency is 80000. I have tried with different values of desired sample frequency. There is always a ratio of 1.22 between expected desired frequency to achieved sampling rates.
Please help me understand why there is a difference between achieved sample rate vs configured sampling frequency.
The text was updated successfully, but these errors were encountered: