In this Blog I am giving a little bit of background on how the sampling of analog signals has been implemented in my Arduino audo-tools library. My initial approach was based on the Blog from Ivan Voras using timers and interrupts.

But there is a much better way by using the extended ESP32 I2S functionality: You can use this to sample an analog signal (e.g. from a microphone) at very high speeds and I finally used this approach in my ADC class.

Here is how you set it up using the ESP32 API:

#include "esp_a2dp_api.h"
#include "driver/i2s.h"
#include "freertos/queue.h"
#include "driver/adc.h"

// public config parameters
int sample_rate = 44100;
int dma_buf_count = 10;
int dma_buf_len = 512;
bool use_apll = false;

const i2s_port_t i2s_num = I2S_NUM_0; // Analog input only supports 0!

// select the analog pin e.g 32
adc_unit_t unit = ADC_UNIT_1;
adc1_channel_t channel = ADC1_GPIO32_CHANNEL;

// I2S config
i2s_config_t i2s_config = {
  .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
  .sample_rate = sample_rate,
  .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
  .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
  .communication_format = I2S_COMM_FORMAT_I2S_LSB,
  .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
  .dma_buf_count = dma_buf_count,
  .dma_buf_len = dma_buf_len,
  .use_apll = use_apll,
  .tx_desc_auto_clear = false,
  .fixed_mclk = 0};

// setup config
if (i2s_driver_install(i2s_num, &i2s_config, 0, NULL)!=ESP_OK){
    ESP_LOGE(ADC_TAG, "%s - %s", __func__, "i2s_driver_install");
}      

//init ADC pad
if (i2s_set_adc_mode(unit, channel)!=ESP_OK) {
    ESP_LOGE(ADC_TAG, "%s - %s", __func__, "i2s_set_adc_mode");
}

// enable the ADC
if (i2s_adc_enable(i2s_num)!=ESP_OK) {
    ESP_LOGE(ADC_TAG, "%s - %s", __func__, "i2s_adc_enable");
}

And finally you can just use i2s_read() in a loop to get the data:

size_t size_read;
i2s_read(i2s_num, dest, size_bytes, &size_read, ticks_to_wait);

Pretty simple! – but it is even simpler with my API:

ADC adc;
const int32_t max_buffer_len = 512;
int16_t buffer[max_buffer_len][2];

auto config = adc.defaultConfig());
// default pin is 34 - change it to 32 
config.setPin(32);
adc.begin(config);

// and finally read the data 
size_t len = adc.read(buffer, max_buffer_len); 


2 Comments

mark donners · 8. May 2021 at 22:45

How did you deal with the buffers I2S only returning data in the first half …last half of buffer is always empty

Leave a Reply

Your email address will not be published.