Writing ADC continuous stream to I2S

Hello,
I'm trying to read an analog microphone and put the sound through I2S, with an ESP32 WROOM-32
I'm able to make both work separately, but not together.
My code is:

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "driver/i2s_std.h"
#include "esp_adc/adc_continuous.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"

adc_continuous_handle_t handle = NULL;
i2s_chan_handle_t tx_chan = NULL;

#define SAMPLE_RATE 16000
#define BUFFER_SIZE 160

static TaskHandle_t mic_task = NULL;

static bool IRAM_ATTR on_microphone_data_ready(adc_continuous_handle_t handle, const adc_continuous_evt_data_t *edata, void *user_data) {
  BaseType_t mustYield = pdFALSE;
  vTaskNotifyGiveFromISR(mic_task, &mustYield);
  return (mustYield == pdTRUE);
}

void init_microphone() {
  adc_continuous_handle_cfg_t adc_config = {
      .max_store_buf_size = 1024,
      .conv_frame_size = BUFFER_SIZE,
  };

  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_TYPE2,
      .pattern_num = 1,
  };

  adc_digi_pattern_config_t adc_pattern = {
      .atten = ADC_ATTEN_DB_11,
      .channel = ADC_CHANNEL_0,
      .unit = ADC_UNIT_2,
      .bit_width = ADC_BITWIDTH_12,
  };

  dig_cfg.adc_pattern = &adc_pattern;

  ESP_ERROR_CHECK(adc_continuous_config(handle, &dig_cfg));

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

void init_speaker() {
  i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
  chan_cfg.auto_clear = true;
  i2s_std_config_t std_cfg = {
      .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
      .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
      .gpio_cfg = {
          .mclk = I2S_GPIO_UNUSED,
          .bclk = 26,
          .ws = 25,
          .dout = 22,
          .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));
}

void app_main(void) {
  init_speaker();
  init_microphone();
  // ...
}

Which outpus:

E (293) adc_share_hw_ctrl: adc_apb_periph_free called, but `s_adc_digi_ctrlr_cnt == 0`

abort() was called at PC 0x400d9dab on core 0
--- 0x400d9dab: adc_apb_periph_free at /esp/esp-idf/components/esp_hw_support/adc_share_hw_ctrl.c:234

Am I missing something obvious here ? Like DAC not being able to work together with I2S ? Or maybe DMA config incorrect and both trying to access the same DMA resources ? I'm new to ESP32.

Thanks a lot for your help !

Full trace:

I (29) boot: ESP-IDF v5.5-dev-847-gcb3ac7429c 2nd stage bootloader
I (29) boot: compile time Jan  2 2025 17:03:17
I (29) boot: Multicore bootloader
I (32) boot: chip revision: v1.0
I (35) boot.esp32: SPI Speed      : 40MHz
I (38) boot.esp32: SPI Mode       : DIO
I (42) boot.esp32: SPI Flash Size : 2MB
I (46) boot: Enabling RNG early entropy source...
I (50) boot: Partition Table:
I (53) boot: ## Label            Usage          Type ST Offset   Length
I (59) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (66) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (72) boot:  2 factory          factory app      00 00 00010000 00177000
I (79) boot: End of partition table
I (82) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=0a138h ( 41272) map
I (103) esp_image: segment 1: paddr=0001a160 vaddr=3ff80000 size=0001ch (    28) load
I (104) esp_image: segment 2: paddr=0001a184 vaddr=3ffb0000 size=0227ch (  8828) load
I (111) esp_image: segment 3: paddr=0001c408 vaddr=40080000 size=03c10h ( 15376) load
I (121) esp_image: segment 4: paddr=00020020 vaddr=400d0020 size=15358h ( 86872) map
I (152) esp_image: segment 5: paddr=00035380 vaddr=40083c10 size=0a56ch ( 42348) load
I (176) boot: Loaded app from partition at offset 0x10000
I (176) boot: Disabling RNG early entropy source...
I (186) cpu_start: Multicore app
I (194) cpu_start: Pro cpu start user code
I (194) cpu_start: cpu freq: 160000000 Hz
I (195) app_init: Application information:
I (195) app_init: Project name:     project
I (200) app_init: App version:      b12afb3-dirty
I (204) app_init: Compile time:     Jan  2 2025 17:24:00
I (209) app_init: ELF file SHA256:  6901b825a...
I (213) app_init: ESP-IDF:          v5.5-dev-847-gcb3ac7429c
I (219) efuse_init: Min chip rev:     v0.0
I (223) efuse_init: Max chip rev:     v3.99
I (227) efuse_init: Chip rev:         v1.0
I (231) heap_init: Initializing. RAM available for dynamic allocation:
I (237) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (242) heap_init: At 3FFB2B08 len 0002D4F8 (181 KiB): DRAM
I (247) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (253) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (258) heap_init: At 4008E17C len 00011E84 (71 KiB): IRAM
I (264) spi_flash: detected chip: generic
I (267) spi_flash: flash io: dio
W (270) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (283) main_task: Started on CPU0
I (293) main_task: Calling app_main()
W (293) i2s_platform: i2s controller 0 has been occupied by i2s_driver
E (293) adc_share_hw_ctrl: adc_apb_periph_free called, but `s_adc_digi_ctrlr_cnt == 0`

abort() was called at PC 0x400d9dab on core 0
--- 0x400d9dab: adc_apb_periph_free at /esp/esp-idf/components/esp_hw_support/adc_share_hw_ctrl.c:234



Backtrace: 0x40081b11:0x3ffb3c50 0x400869bd:0x3ffb3c70 0x4008c8ed:0x3ffb3c90 0x400d9dab:0x3ffb3d00 0x400d4300:0x3ffb3d20 0x400d443e:0x3ffb3d50 0x400d3fae:0x3ffb3d90 0x400d40ca:0x3ffb3de0 0x400e4bf0:0x3ffb3e00 0x4008733d:0x3ffb3e30
--- 0x40081b11: panic_abort at /esp/esp-idf/components/esp_system/panic.c:454
0x400869bd: esp_system_abort at /esp/esp-idf/components/esp_system/port/esp_system_chip.c:92
0x4008c8ed: abort at /esp/esp-idf/components/newlib/src/abort.c:38
0x400d9dab: adc_apb_periph_free at /esp/esp-idf/components/esp_hw_support/adc_share_hw_ctrl.c:234
0x400d4300: adc_continuous_deinit at /esp/esp-idf/components/esp_adc/adc_continuous.c:436
0x400d443e: adc_continuous_new_handle at /esp/esp-idf/components/esp_adc/adc_continuous.c:249
0x400d3fae: init_microphone at /project/main/main.c:34
0x400d40ca: app_main at /project/main/main.c:88
0x400e4bf0: main_task at /esp/esp-idf/components/freertos/app_startup.c:208
0x4008733d: vPortTaskWrapper at /esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:139

Why not start with an I2S Microphone?

I just happened to read this. So, adc continuous will consume one of the I2S drivers available it seems.

It's strange though, I see different hardware limitations for esp32, esp32 s2 and esp32 s3:

The reason is I have 3 input / outputs. I have:

  • a speaker
  • a microphone
  • another audio input coming out of a radio

My speaker must be over i2s because I may have BT sound that can be played with A2DP.
My microphone is used to speak over BT through HFP.

I need to store to an SD card, at all times:

  • a file containing the speaker input
  • a file containing the speaker output
  • a file containing the radio input

I initially thought:

  • speaker over i2s
  • microphone over ADC
  • radio input over the second I2S available

That's why I'm trying to mix I2S and ADC.

From my understanding, the ESP32 has two I2S drivers, so you can only connect two audio input/output.

I don't care if the audio quality of the data stored on the SD card is of low quality (8kHz).

I could probably do:

  • speaker I2S
  • adc continuous for both speaker and radio input

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.