Why are my ADC channels doubled up when reading them via I2S?
Posted: Thu Jul 04, 2019 1:52 am
This question relates to issue #3686. I am trying to use the I2S direct-from-ADC functionality, operating in stereo. I'm using the Wemos Lolin D32, which uses the ESP32-WROOM-32, developing on Ubuntu 18.04.
My IDF version is v3.3-beta2, but it requires hacking on the IDF a bit to expose the necessary functions (I have also tried this by manipulating the registers directly); I'll detail that below.
My issue is, no matter what I do, I see the ADC data with doubled up or skipped channels. I have code that extracts the channel from the ADC data that appears from an I2S read like this:
And the result I see in my serial log is:
The configuration is fairly simple:
As I mentioned, I have to make some changes to IDF to use the ADC pattern table: expose the functions adc_set_i2s_data_len() and adc_set_i2s_data_pattern() and comment out the _i2s_adc_mode_recover() call in i2s_adc_enable(). The resulting code is available in my fork.
The full project, including build config, is on Gitlab. It can be built just by checking it out and doing "idf.py build".
I am totally unable to figure out what's wrong. I have checked through both my code and the IDF libs to make sure the registers are being set correctly. I have read the technical reference manual front to back. I've tried it with and without the queue, with and without the DMA clearing. I've used an oscilloscope to verify that the WS signal is indeed running at 24kHz (although that shouldn't affect the channel order).
It would be a huge help to me even just to have someone else try out my example project and tell me whether they see the same thing on their hardware. But if you can see a mistake that I've made here, or have any ideas for more debugging, please let me know.
My IDF version is v3.3-beta2, but it requires hacking on the IDF a bit to expose the necessary functions (I have also tried this by manipulating the registers directly); I'll detail that below.
My issue is, no matter what I do, I see the ADC data with doubled up or skipped channels. I have code that extracts the channel from the ADC data that appears from an I2S read like this:
Code: Select all
static void show_some_data(uint16_t * data)
{
const unsigned rows = 8;
for (unsigned row = 0; row < rows; row++)
{
ESP_LOGI(
TAG,
"CH: %d %d %d %d %d %d %d %d",
(data[8*row + 0] & 0xF000) >> 12,
(data[8*row + 1] & 0xF000) >> 12,
(data[8*row + 2] & 0xF000) >> 12,
(data[8*row + 3] & 0xF000) >> 12,
(data[8*row + 4] & 0xF000) >> 12,
(data[8*row + 5] & 0xF000) >> 12,
(data[8*row + 6] & 0xF000) >> 12,
(data[8*row + 7] & 0xF000) >> 12
);
}
}
Code: Select all
I (324) I2S: PLL_D2: Req RATE: 24000, real rate: 1515.000, BITS: 16, CLKM: 55, BCK: 60, MCLK: 55.556, SCLK: 48480.000000, diva: 64, divb: 35
I (334) i2s-test: CH: 6 6 6 6 6 6 0 0
I (334) i2s-test: CH: 0 0 0 0 0 0 0 0
I (344) i2s-test: CH: 0 0 0 0 0 0 0 0
I (344) i2s-test: CH: 0 0 0 0 0 0 0 0
I (354) i2s-test: CH: 0 0 0 0 0 0 0 0
I (354) i2s-test: CH: 0 0 0 0 0 0 0 0
I (364) i2s-test: CH: 0 0 0 0 0 0 0 0
I (364) i2s-test: CH: 0 0 0 0 0 0 0 0
I (364) i2s-test: CH: 5 5 6 6 5 5 6 6
I (374) i2s-test: CH: 5 5 6 6 5 5 6 6
I (374) i2s-test: CH: 5 5 6 6 5 5 6 6
I (384) i2s-test: CH: 5 5 6 6 5 5 6 6
I (384) i2s-test: CH: 5 5 6 6 5 5 6 6
I (394) i2s-test: CH: 5 5 6 6 5 5 6 6
I (394) i2s-test: CH: 5 5 6 6 5 5 6 6
I (394) i2s-test: CH: 5 5 6 6 5 5 6 6
I (404) i2s-test: CH: 6 6 5 5 6 6 5 5
Code: Select all
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
.sample_rate = SAMPLE_RATE, // 24kHz
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.communication_format = I2S_COMM_FORMAT_I2S,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 2,
.dma_buf_len = NUM_SAMPLES, // 64
.use_apll = 0,
.fixed_mclk = 0
};
i2s_driver_install(i2s_num, &i2s_config, 4, i2s_queue);
i2s_set_clk(i2s_num, SAMPLE_RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO);
i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_6);
adc_set_i2s_data_pattern(ADC_UNIT_1, 0, ADC_CHANNEL_6, ADC_WIDTH_BIT_12, ADC_ATTEN_DB_11);
adc_set_i2s_data_pattern(ADC_UNIT_1, 1, ADC_CHANNEL_5, ADC_WIDTH_BIT_12, ADC_ATTEN_DB_11);
adc_set_i2s_data_len(ADC_UNIT_1, 2);
The full project, including build config, is on Gitlab. It can be built just by checking it out and doing "idf.py build".
I am totally unable to figure out what's wrong. I have checked through both my code and the IDF libs to make sure the registers are being set correctly. I have read the technical reference manual front to back. I've tried it with and without the queue, with and without the DMA clearing. I've used an oscilloscope to verify that the WS signal is indeed running at 24kHz (although that shouldn't affect the channel order).
It would be a huge help to me even just to have someone else try out my example project and tell me whether they see the same thing on their hardware. But if you can see a mistake that I've made here, or have any ideas for more debugging, please let me know.