I2S ADC Read - erroneous values for some input voltage levels
Posted: Thu Jun 23, 2022 1:07 pm
Hello. I have an application where an ESP32-WROOM-32D is reading ADC values via I2S at 10kHz continuously. It works great throughout the ADC input range except for a few regions where it seems to read the same value regardless of the input voltage.
It looks like the ADC is reading correctly, but the I2S values are incorrect. Sample values of one such region below. I've tried many different I2S handling schemes including this example (https://github.com/espressif/arduino-esp32/pull/2302), but same results. This happens on many devices and the problem regions are around 0x600, 0x4FF, 0x87F, and 0x8FF. Any ideas?
Example problem region:
InputV -> I2S val [analogRead()]
----------------------------------------------
1.34V -> 1438 [1438] <---I2S val is same as atomic read from analogRead(), good
1.35V -> 1455 [1455] <---I2S val is same as atomic read from analogRead(), good
1.36V -> 1536 [1462] <---I2S val is incorrect! Fills buffer with 0x600. Note, analogRead() value makes sense.
1.37V -> 1536 [1474] <---I2S val is incorrect! Fills buffer with 0x600. Note, analogRead() value makes sense.
1.38V -> 1536 [1487] <---I2S val is incorrect! Fills buffer with 0x600. Note, analogRead() value makes sense.
1.39V -> 1536 [1498] <---I2S val is incorrect! Fills buffer with 0x600. Note, analogRead() value makes sense.
1.40V -> 1536 [1509] <---I2S val is incorrect! Fills buffer with 0x600. Note, analogRead() value makes sense.
1.41V -> 1536 [1521] <---I2S val is incorrect! Fills buffer with 0x600. Note, analogRead() value makes sense.
1.42V -> 1540 [1535] <---I2S val is nearly the same as atomic read from analogRead(), probably good
1.43V -> 1551 [1551] <---I2S val is same as atomic read from analogRead(), good
1.44V -> 1566 [1566] <---I2S val is same as atomic read from analogRead(), good
It looks like the ADC is reading correctly, but the I2S values are incorrect. Sample values of one such region below. I've tried many different I2S handling schemes including this example (https://github.com/espressif/arduino-esp32/pull/2302), but same results. This happens on many devices and the problem regions are around 0x600, 0x4FF, 0x87F, and 0x8FF. Any ideas?
Example problem region:
InputV -> I2S val [analogRead()]
----------------------------------------------
1.34V -> 1438 [1438] <---I2S val is same as atomic read from analogRead(), good
1.35V -> 1455 [1455] <---I2S val is same as atomic read from analogRead(), good
1.36V -> 1536 [1462] <---I2S val is incorrect! Fills buffer with 0x600. Note, analogRead() value makes sense.
1.37V -> 1536 [1474] <---I2S val is incorrect! Fills buffer with 0x600. Note, analogRead() value makes sense.
1.38V -> 1536 [1487] <---I2S val is incorrect! Fills buffer with 0x600. Note, analogRead() value makes sense.
1.39V -> 1536 [1498] <---I2S val is incorrect! Fills buffer with 0x600. Note, analogRead() value makes sense.
1.40V -> 1536 [1509] <---I2S val is incorrect! Fills buffer with 0x600. Note, analogRead() value makes sense.
1.41V -> 1536 [1521] <---I2S val is incorrect! Fills buffer with 0x600. Note, analogRead() value makes sense.
1.42V -> 1540 [1535] <---I2S val is nearly the same as atomic read from analogRead(), probably good
1.43V -> 1551 [1551] <---I2S val is same as atomic read from analogRead(), good
1.44V -> 1566 [1566] <---I2S val is same as atomic read from analogRead(), good
Code: Select all
void adc_task(void *pvParameters)
{
int bytes_read,bytes_read_sum;
esp_err_t rc[ITERATIONS];
esp_err_t composite_rc;
int i;
rawADCValue=0;
// Give our application task time to make sure the relay is open and that our nonvolatile memory is read.
vTaskDelay(1000);
i=0;
do
{
InitADC();
// Overriding this until we can get back to the ADC part.
adc_ready_for_app=true;
while(adc_init_ok)
{
// Get each bank of current readings
bytes_read_sum=0;
for(i=0;i<ITERATIONS;i++)
{
rc[i]=i2s_read(I2S_NUM_0, (char*)&i2s_read_buff[i*(SIZEOF_BUF>>1)], SIZEOF_BUF , (size_t*)&bytes_read, portMAX_DELAY);
bytes_read_sum+=bytes_read;
}
// See if all bank reads are ok. composite_rc will end up with the last non-ok value.
composite_rc=ESP_OK;
for(i=0;i<ITERATIONS;i++)
if(rc[i]!=ESP_OK)
composite_rc=rc[i];
// If all bank readings were good, crunch the numbers.
if(composite_rc==ESP_OK)
{
i2s_stop(I2S_NUM_0);
i2s_adc_disable(I2S_NUM_0);
// send bytes_read to crunchNumbers()
crunchNumbers(bytes_read_sum);
memset((void*)i2s_read_buff,0,SIZEOF_BUF*ITERATIONS);
i2s_zero_dma_buffer(I2S_NUM_0);
i2s_start(I2S_NUM_0);
while(i2s_adc_enable(I2S_NUM_0) != ESP_OK)
{
Serial.printf("\n\rADC: i2c enable FAILED");
vTaskDelay(100);
}
}
vTaskDelay(1);
}
Serial.printf("\n\rADC: TASK ENDED!!");
vTaskDelete(NULL);
}
static void InitADC(void)
{
currentReading=0;
adc_init_ok=0;
adc_ready_for_app=false;
zeroCurrentVal=0;
// Configure I2S to do periodic ADC reads and store them in the DMA buffer.
// The benefit is that we can do ADC reads and store results without using
// the cpu core.
i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN);
i2s_config.sample_rate = I2S_SAMPLE_RATE; // The format of the signal using ADC_BUILT_IN
i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT; // is fixed at 12bit, stereo, MSB
i2s_config.channel_format = I2S_CHANNEL_FMT_ALL_LEFT;
i2s_config.communication_format = I2S_COMM_FORMAT_I2S_MSB;
i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1;
i2s_config.dma_buf_count = 8;
i2s_config.dma_buf_len = SIZEOF_BUF;
i2s_config.use_apll = false;
i2s_config.tx_desc_auto_clear = false;
i2s_config.fixed_mclk = 0;
if (adc_gpio_init(ADC_UNIT_1, ADC_CHANNEL_0) == ESP_OK)
{
// config adc atten and width.
// 12-bits (4095) is 3.3V
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_11db);
adc1_config_width(ADC_WIDTH_12Bit);
esp_adc_cal_characterize(ADC_UNIT_1,ADC_ATTEN_DB_11,ADC_WIDTH_12Bit,DEF_VREF, &adc1_cal);
adc_power_acquire();
// ALl operations must success
if (i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL) == ESP_OK)
{
Serial.printf("\n\rADC: driver installed successfully");
// I2S_CHANNEL_MONO has storing a single sample
if (i2s_set_clk(I2S_NUM_0, I2S_SAMPLE_RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO) == ESP_OK)
{
Serial.printf("\n\rADC: i2s clock set successfully");
// Setup I2S driver in ADC conversion mode
if (i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_0) == ESP_OK)
{
Serial.printf("\n\rADC: mode set successfully");
//? ADC_SAMPLE_START();
i2s_zero_dma_buffer(I2S_NUM_0);
if (i2s_adc_enable(I2S_NUM_0) == ESP_OK)
{
Serial.printf("\n\rADC: enabled successfully");
adc_init_ok=true;
}
else
Serial.printf("\n\rADC: i2c enable FAILED");
}
else
Serial.printf("\n\rADC: mode set FAILED");
}
else
Serial.printf("\n\rADC: i2s clock FAILED");
}
else
Serial.printf("\n\rADC: driver install FAILED");
}
else
Serial.printf("\n\rADC: gpio init FAILED");
}
static void crunchNumbers(int bytes_read)
{
int i;
float tempf1;
float acc;
int samples;
double long accl;
uint32_t reading;
int tempint;
int allZerosFlag=1;
// Nothing to do. No big deal.
if(bytes_read==0)
{
//Serial.printf("\n\rADC: no bytes");
return;
}
samples=bytes_read>>1;
accl=0;
for(i=0;i<samples;i++)
{
// Take reading
reading=(int)i2s_read_buff[i];
accl+=(reading*reading);
if(reading)
allZerosFlag=0;
}
accl/=samples;
tempf1=accl;
tempf1=sqrtf(accl);
// round it
tempint=tempf1;
if((tempf1-tempint)>0.5f)
tempint++;
// Stored for reference in calibration.
rawADCValue=tempint;
}