ADC with I2S: increasing throughput to 2 MHz
Posted: Tue May 26, 2020 9:45 am
Hi there,
I am running high frequency ADC sampling through I2S which is loosely based on this example from the arduino-esp32 github: https://github.com/espressif/arduino-es ... eq_ADC.ino
The goal is to sample a single channel (which is excited by a pure sine wave) and compute FFT's from packets which are 512 samples long.
I have a single task running on core 1 to accomplish this, which is given below (simplified). One cycle of the while loop takes approximately 330us to complete (if the buffers are filled by DMA so the i2s_read function doesn't have to wait). So I know the maximum attainable loop frequency is approximately 3000 Hz.
And the i2sInit() function is setup like this:
The basics of this are working fine, but I am trying to increase throughput of the ADC. Currently, the maximum stable sample frequency appears to be 1.3 MHz, whereas 2 MSPS is advertised as maximum reachable. I determine this with the loop counter in my main while loop. Above 1.3-1.35 MHz sample rate the execution rate of my loop starts to drop severely instead of increase. Furthermore, the FFT is no longer accurately determining the frequency of the sinewave, indicating issues with sampling.
It should be noted that the true sample rate is already half of the configured sample rate, because the ESP samples two channels simultaneously (refered to as LEFT and RIGHT). Which is only usefull if you sample more than one source. At 1.024MHz sampling rate with 512 samples per reading, I am getting a steady state loop frequency of 992Hz, which is just below the expected (1.024/2)/512 = 1000Hz.
I have read in other threads, that the SAR ADC cannot sample continuously and needs two clock cycles to restart every 256 samples by default. This would explain my lower than expected sampling perfectly, i.e. 256/258*1000=992. Futhermore, turning restarts off with SYSCON.saradc_ctrl2.meas_num_limit = 0; (which is unadvised and indeed gives poorer data) gives me the exact 1000 Hz. (include "soc/syscon_reg" and "soc/syscon_struct.h").
So back to my main problem, what could be limiting my maximum attainable sampling rate?
I am running high frequency ADC sampling through I2S which is loosely based on this example from the arduino-esp32 github: https://github.com/espressif/arduino-es ... eq_ADC.ino
The goal is to sample a single channel (which is excited by a pure sine wave) and compute FFT's from packets which are 512 samples long.
I have a single task running on core 1 to accomplish this, which is given below (simplified). One cycle of the while loop takes approximately 330us to complete (if the buffers are filled by DMA so the i2s_read function doesn't have to wait). So I know the maximum attainable loop frequency is approximately 3000 Hz.
Code: Select all
void FFT_compute(void *pvParameters) {
i2sInit(); // Initialize the I2S peripheral
size_t bytes_read;
uint16_t buffer[512] = {0};
unsigned long t_start = millis();
unsigned long fft_loop_cntr = 0;
while(1){
fft_loop_cntr++;
//(I2S port, destination adress, data size in bytes, bytes read counter, RTOS ticks to wait)
i2s_read(I2S_NUM_0, &buffer, sizeof(buffer), &bytes_read, portMAX_DELAY);
//compute FFT of sampled data
compute_FFT(&buffer);
if(fft_loop_cntr % 5000 == 100){
Serial.print("FFT loop frequency[Hz]: ");
Serial.println((1000*fft_loop_cntr)/(millis()-t_start));
}
}
}
Code: Select all
void i2sInit()
{
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
.sample_rate = samplingFrequency, // The format of the signal using ADC_BUILT_IN
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // is fixed at 12bit, stereo, MSB
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 32, //number of DMA buffers
.dma_buf_len = 64, //number of samples (in bytes)
.use_apll = false, //no Audio PLL
.tx_desc_auto_clear = false,
.fixed_mclk = 0
};
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_adc_mode(ADC_UNIT_1, ADC_INPUT);
SYSCON.saradc_ctrl2.sar1_inv = 1; //SAR ADC samples are inverted by default
SYSCON.saradc_ctrl.sar1_patt_len = 0; //Use only the first entry of the pattern table
delay(1000); //required for stability of ADC
i2s_adc_enable(I2S_NUM_0);
delay(1000); //required for stability of ADC
SYSCON.saradc_sar1_patt_tab[0] = 0x5C0F0F0F; //mask would be nicer, but seems to be unstable
}
It should be noted that the true sample rate is already half of the configured sample rate, because the ESP samples two channels simultaneously (refered to as LEFT and RIGHT). Which is only usefull if you sample more than one source. At 1.024MHz sampling rate with 512 samples per reading, I am getting a steady state loop frequency of 992Hz, which is just below the expected (1.024/2)/512 = 1000Hz.
I have read in other threads, that the SAR ADC cannot sample continuously and needs two clock cycles to restart every 256 samples by default. This would explain my lower than expected sampling perfectly, i.e. 256/258*1000=992. Futhermore, turning restarts off with SYSCON.saradc_ctrl2.meas_num_limit = 0; (which is unadvised and indeed gives poorer data) gives me the exact 1000 Hz. (include "soc/syscon_reg" and "soc/syscon_struct.h").
So back to my main problem, what could be limiting my maximum attainable sampling rate?
- Are the DMA or FIFO buffers limiting my throughput? Which registers can I check for this?
- Could it be RTOS related? For instance, not enough time available to read out the buffers causing them to waste data? Personally I wouldn't expect this because the task seems to be quick enough and is the only one running.
- Anything you can think of, help me to look in the right direction is welcome.