Skip to content
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

ADC DMA interrupt function gets stucked over time using esp32s3 (IDFGH-14178) #14978

Closed
3 tasks done
plamendc opened this issue Dec 4, 2024 · 4 comments
Closed
3 tasks done
Labels
Awaiting Response awaiting a response from the author Resolution: NA Issue resolution is unavailable Status: Done Issue is done internally

Comments

@plamendc
Copy link

plamendc commented Dec 4, 2024

Answers checklist.

  • I have read the documentation ESP-IDF Programming Guide and the issue is not addressed there.
  • I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
  • I have searched the issue tracker for a similar issue and not found a similar issue.

General issue report

IDF version.

v5.3.1

Espressif SoC revision.

ESP32-S3

Operating System used.

Windows

How did you build your project?

VS Code IDE

What is the implementation?

The code uses ADC2 for oneshot measurements of two input signals and ADC1 continuous mode for taking periodic samples of another two input signals. I have implemented a counter that counts the number of invokes of the interrupt function.

What is the expected behaviour?

The code must consistently invoke the interrupt function adc_dma_callback() throughout the device's operation. The counter should be constantly increasing.

What is the actual behavior?

The code enters the adc_dma_callback() a random number of times and after that it just never calls it again. The counter gets stucked at a different value everytime I reset the device.

Current code implementation?

#define EXAMPLE_READ_LEN 4
#define CIRCULAR_BUFFER_SIZE 16

#define TEMP_INPUT_PIN ADC_CHANNEL_7
#define RSSI_INPUT_PIN ADC_CHANNEL_8
#define ADC_WIDTH ADC_WIDTH_BIT_12
#define ADC_ATTEN ADC_ATTEN_DB_11

#define ADC_CONV_MODE ADC_CONV_SINGLE_UNIT_1
#define ADC_OUTPUT_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2

Std_ReturnType continuous_adc_init(adc_continuous_handle_t *out_handle)
{
  Std_ReturnType ret = E_OK;

  adc_continuous_handle_t handle = NULL;
  adc_continuous_handle_cfg_t adc_config = {
      .max_store_buf_size = CIRCULAR_BUFFER_SIZE,
      .conv_frame_size = EXAMPLE_READ_LEN,
  };

  ret |= adc_continuous_new_handle(&adc_config, &handle);

  adc_continuous_config_t dig_cfg = {
      .sample_freq_hz = 5 * 1000, // 20 kHz sampling frequency
      .conv_mode = ADC_CONV_MODE,
      .format = ADC_OUTPUT_TYPE,
      .pattern_num = 2,
  };
  adc_digi_pattern_config_t adc_pattern[2] = {
    {
          .atten = ADC_ATTEN,
          .bit_width = ADC_WIDTH,
          .channel = TEMP_INPUT_PIN,
          .unit = ADC_UNIT_1,
      },
      {
          .atten = ADC_ATTEN,
          .bit_width = ADC_WIDTH,
          .channel = RSSI_INPUT_PIN,
          .unit = ADC_UNIT_1,
      }
  };
  dig_cfg.adc_pattern = adc_pattern;
  
  ret |= adc_continuous_config(handle, &dig_cfg);

  adc_continuous_evt_cbs_t cbs = {
    .on_conv_done = adc_dma_callback,
  };

  ret |= adc_continuous_register_event_callbacks(handle, &cbs, NULL);
  ret |= adc_continuous_start(handle);

  *out_handle = handle;

  return ret;
}

static void IRAM_ATTR adc_dma_callback(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *event_data, void *user_data)
{
    uint8_t result[EXAMPLE_READ_LEN] = {0};
    size_t bytes_read = 0;

    counter++;

    esp_err_t ret = adc_continuous_read(handle, result, EXAMPLE_READ_LEN, &bytes_read, 1);
    if (ret == ESP_OK && bytes_read > 0)
    {
        for (size_t i = 0; i < bytes_read; i += SOC_ADC_DIGI_RESULT_BYTES)
        {
            adc_digi_output_data_t *p = (void *)&result[i];
            if (p->type2.unit == ADC_UNIT_1 && (p->type2.channel == TEMP_INPUT_PIN || p->type2.channel == RSSI_INPUT_PIN))
            {
                uint32_t adc_reading = p->type2.data;
                uint32_t voltage = 0;
                adc_cali_raw_to_voltage(adc1_cali_handle, adc_reading, &voltage);
                if(p->type2.channel == TEMP_INPUT_PIN){
                  circular_buffer_temp[buffer_index_temp] = voltage;
                  buffer_index_temp = (buffer_index_temp + 1) % CIRCULAR_BUFFER_SIZE;
                }
                else{
                  circular_buffer_rssi[buffer_index_rssi] = voltage;
                  buffer_index_rssi = (buffer_index_rssi + 1) % CIRCULAR_BUFFER_SIZE;
                }
            }
        }
    }
    else
    {
        ESP_LOGE(TAG, "ADC read error: %s", esp_err_to_name(ret));
    }
}

Similar behaviour?

I had the same issue using the v5.0.7, however, I found an issue in the adc_continuous.c file. There I have modified the adc_continuous_new_handle() function the following way:

gdma_strategy_config_t strategy_config = {
    .auto_update_desc = true,
    .owner_check = false
};

The owner_check used to be true. This fixed my issue. However, in the 5.3.1 version, the implementation is modified and I wasn't able to resolve the issue. Please explain how I can solve this problem in a similar manner?

@espressif-bot espressif-bot added the Status: Opened Issue is new label Dec 4, 2024
@github-actions github-actions bot changed the title ADC DMA interrupt function gets stucked over time using esp32s3 ADC DMA interrupt function gets stucked over time using esp32s3 (IDFGH-14178) Dec 4, 2024
@Bruce297
Copy link
Collaborator

Bruce297 commented Dec 6, 2024

Hi, you can try to set ADC_CONV_MODE to ADC_CONV_BOTH_UNIT to see if it works

@Alvin1Zhang Alvin1Zhang added the Awaiting Response awaiting a response from the author label Dec 10, 2024
@lukasfischer83
Copy link

I am not sure, if that causes your issue, but internally adc_continuous_read() uses xRingbufferReceiveUpTo() to get the data from the internal ADC Continuous ring buffer. There is also a function xRingbufferReceiveUpToFromISR() which makes me believe, that you shouldn't use adc_continuous_read() from within the ISR. The callback that you registered however is called from within the adc_dma_in_suc_eof_callback(), which is effectively the ISR of the GDMA end of frame interrupt.
Also inside the adc_continuous_read(), ESP_RETURN_ON_FALSE() macro uses the serial console to output log messages, which is also not working from within the ISR in case of an error.
If you really rely on low latency to process your ADC data, you could copy the whole esp_adc component to your local components and rewrite the adc_continuous_read() function to suit your needs. Potentially you could even modify the adc_dma_in_suc_eof_callback() in order to get rid of the internal freertos ringbuffer completely and access data directly from the dma buffers that you can get from the *event_data argument.

@Bruce297
Copy link
Collaborator

Bruce297 commented Dec 30, 2024

I am not sure, if that causes your issue, but internally adc_continuous_read() uses xRingbufferReceiveUpTo() to get the data from the internal ADC Continuous ring buffer. There is also a function xRingbufferReceiveUpToFromISR() which makes me believe, that you shouldn't use adc_continuous_read() from within the ISR. The callback that you registered however is called from within the adc_dma_in_suc_eof_callback(), which is effectively the ISR of the GDMA end of frame interrupt. Also inside the adc_continuous_read(), ESP_RETURN_ON_FALSE() macro uses the serial console to output log messages, which is also not working from within the ISR in case of an error. If you really rely on low latency to process your ADC data, you could copy the whole esp_adc component to your local components and rewrite the adc_continuous_read() function to suit your needs. Potentially you could even modify the adc_dma_in_suc_eof_callback() in order to get rid of the internal freertos ringbuffer completely and access data directly from the dma buffers that you can get from the *event_data argument.

You're right, we should not use adc_continuous_read() from within the ISR.

@Alvin1Zhang
Copy link
Collaborator

Thanks for reporting, feel free to reopen.

@espressif-bot espressif-bot added Status: Done Issue is done internally Resolution: NA Issue resolution is unavailable and removed Status: Opened Issue is new labels Mar 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting Response awaiting a response from the author Resolution: NA Issue resolution is unavailable Status: Done Issue is done internally
Projects
None yet
Development

No branches or pull requests

5 participants