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 continuous sample rate does not match I2S (IDFGH-14457) #15231

Open
3 tasks done
gempain opened this issue Jan 18, 2025 · 0 comments
Open
3 tasks done

ADC continuous sample rate does not match I2S (IDFGH-14457) #15231

gempain opened this issue Jan 18, 2025 · 0 comments
Labels
Status: Opened Issue is new

Comments

@gempain
Copy link

gempain commented Jan 18, 2025

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

Hello,

I sample an analog microphone with ADC continuous, and push the samples into an I2S output.

The ADC continuous docs mention that it consumes I2S_NUM_0, so I assume it is fully capable of producing correctly aligned samples. However, the following code does not produce correct sound. The pitch in the I2S output can be heard way too high and particularly too fast, as if the sample rates were not aligned. Lowering the I2S sample rate to 16kHz produces better result but yet the audio quality is still very poor as if the frames were not aligned.

I could not find an example in ESP IDF or ESP ADF that demonstrates the sampling of the continuous ADC is reliable.

Is the ESP32 even capable of sampling audio via ADC ? The reason for not using I2S for audio input is that I need to sample more than 3 microphones at the same time and the second I2S port I keep for a speaker.

Here is the code I use:

#include <stdio.h>
#include <string.h>

#include "driver/i2s_std.h"
#include "esp_adc/adc_continuous.h"
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "sdkconfig.h"

#define EXAMPLE_READ_LEN 256

i2s_chan_handle_t tx_chan = NULL;
static adc_channel_t channel[1] = {ADC_CHANNEL_7};

static TaskHandle_t s_task_handle;
static const char *TAG = "EXAMPLE";

static void i2s_init() {
  i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_1, I2S_ROLE_MASTER);
  chan_cfg.auto_clear = true;
  i2s_std_config_t std_cfg = {
      .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(20000),
      .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
      .gpio_cfg = {
          .mclk = I2S_GPIO_UNUSED,
          .bclk = 26,
          .ws = 25,
          .dout = 32,
          .din = I2S_GPIO_UNUSED,
          .invert_flags = {
              .mclk_inv = false,
              .bclk_inv = false,
              .ws_inv = false,
          },
      },
  };

  ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_chan, NULL));
  ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_chan, &std_cfg));
  ESP_ERROR_CHECK(i2s_channel_enable(tx_chan));
}

static bool IRAM_ATTR s_conv_done_cb(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data) {
  BaseType_t mustYield = pdFALSE;
  // Notify that ADC continuous driver has done enough number of conversions
  vTaskNotifyGiveFromISR(s_task_handle, &mustYield);

  return (mustYield == pdTRUE);
}

static void continuous_adc_init(adc_channel_t *channel, uint8_t channel_num, adc_continuous_handle_t *out_handle) {
  adc_continuous_handle_t handle = NULL;

  adc_continuous_handle_cfg_t adc_config = {
      .max_store_buf_size = 10 * 1024,
      .conv_frame_size = EXAMPLE_READ_LEN,
  };
  ESP_ERROR_CHECK(adc_continuous_new_handle(&adc_config, &handle));

  adc_continuous_config_t dig_cfg = {
      .sample_freq_hz = 20000,
      .conv_mode = ADC_CONV_SINGLE_UNIT_1,
      .format = ADC_DIGI_OUTPUT_FORMAT_TYPE1,
  };

  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++) {
    adc_pattern[i].atten = ADC_ATTEN_DB_0;
    adc_pattern[i].channel = channel[i] & 0x7;
    adc_pattern[i].unit = ADC_UNIT_1;
    adc_pattern[i].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH;

    ESP_LOGI(TAG, "adc_pattern[%d].atten is :%" PRIx8, i, adc_pattern[i].atten);
    ESP_LOGI(TAG, "adc_pattern[%d].channel is :%" PRIx8, i, adc_pattern[i].channel);
    ESP_LOGI(TAG, "adc_pattern[%d].unit is :%" PRIx8, i, adc_pattern[i].unit);
  }
  dig_cfg.adc_pattern = adc_pattern;
  ESP_ERROR_CHECK(adc_continuous_config(handle, &dig_cfg));

  *out_handle = handle;
}

void app_main(void) {
  i2s_init();

  esp_err_t ret;
  uint32_t ret_num = 0;
  size_t speaker_bytes_written;
  uint8_t result[EXAMPLE_READ_LEN] __attribute__((aligned(4))) = {0};
  uint8_t data[EXAMPLE_READ_LEN] = {0};  // Buffer for I2S data
  memset(result, 0xcc, EXAMPLE_READ_LEN);

  s_task_handle = xTaskGetCurrentTaskHandle();

  adc_continuous_handle_t handle = NULL;
  continuous_adc_init(channel, sizeof(channel) / sizeof(adc_channel_t), &handle);

  adc_continuous_evt_cbs_t cbs = {
      .on_conv_done = s_conv_done_cb,
  };
  ESP_ERROR_CHECK(adc_continuous_register_event_callbacks(handle, &cbs, NULL));
  ESP_ERROR_CHECK(adc_continuous_start(handle));

  while (1) {
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

    ret = adc_continuous_read(handle, result, EXAMPLE_READ_LEN, &ret_num, 0);
    if (ret != ESP_OK) {
      continue;
    }

    // Convert ADC samples to byte array for I2S
    for (int i = 0; i < ret_num; i += SOC_ADC_DIGI_RESULT_BYTES) {
      adc_digi_output_data_t *p = (adc_digi_output_data_t *)&result[i];
      uint16_t adc = p->type1.data;

      int16_t sample = ((int16_t)adc - 2048) << 4;  // Convert to signed 16-bit PCM

      // Split sample into two bytes and store in the data array
      data[i] = sample & 0xFF;             // Lower byte
      data[i + 1] = (sample >> 8) & 0xFF;  // Upper byte
    }

    // Write the byte array to the I2S speaker
    i2s_channel_write(tx_chan, data, ret_num, &speaker_bytes_written, portMAX_DELAY);

    // vTaskDelay(1);
  }
}

Wishing you the very best, and thanks in advance for pointing out whether ESP-IDF produces reliable and evenly distributed samples in ADC continuous mode.

@espressif-bot espressif-bot added the Status: Opened Issue is new label Jan 18, 2025
@github-actions github-actions bot changed the title ADC continuous sample rate does not match I2S ADC continuous sample rate does not match I2S (IDFGH-14457) Jan 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Opened Issue is new
Projects
None yet
Development

No branches or pull requests

2 participants