Why are my ADC channels doubled up when reading them via I2S?

detlier
Posts: 15
Joined: Tue Nov 06, 2018 3:44 am

Why are my ADC channels doubled up when reading them via I2S?

Postby detlier » 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:

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
        );
    }
}
And the result I see in my serial log is:

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
The configuration is fairly simple:

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);
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.

felixcollins
Posts: 125
Joined: Fri May 24, 2019 2:02 am

Re: Why are my ADC channels doubled up when reading them via I2S?

Postby felixcollins » Wed Jun 01, 2022 5:07 am

Did you ever get this figured out?

felixcollins
Posts: 125
Joined: Fri May 24, 2019 2:02 am

Re: Why are my ADC channels doubled up when reading them via I2S?

Postby felixcollins » Wed Jun 01, 2022 11:02 pm

You could try...

Code: Select all

.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT

felixcollins
Posts: 125
Joined: Fri May 24, 2019 2:02 am

Re: Why are my ADC channels doubled up when reading them via I2S?

Postby felixcollins » Thu Jun 02, 2022 3:40 am

My code...

Code: Select all

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_spi_flash.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_partition.h"
#include "driver/i2s.h"
#include "driver/adc.h"
#include "driver/gpio.h"
#include "esp_adc_cal.h"
#include "esp_rom_sys.h"
#include <soc/syscon_struct.h>

static const char* TAG = "adc";

//i2s number
#define EXAMPLE_I2S_NUM           (0)
//i2s sample rate
#define EXAMPLE_I2S_SAMPLE_RATE   (17000)
//i2s data bits
#define EXAMPLE_I2S_SAMPLE_BITS   (16)
//I2S read buffer length
#define EXAMPLE_I2S_READ_LEN      (1700 * 4)
//I2S built-in ADC unit
#define I2S_ADC_UNIT              ADC_UNIT_1


void example_i2s_init(void)
{
	

	i2s_config_t i2s_config = {
		.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN,
		// I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN 
		 .sample_rate = EXAMPLE_I2S_SAMPLE_RATE * 2,
		.bits_per_sample = EXAMPLE_I2S_SAMPLE_BITS,
		.communication_format = I2S_COMM_FORMAT_STAND_MSB,
		.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
		.intr_alloc_flags = 0,
		.dma_buf_count = 4,
		.dma_buf_len = 1024,
		.use_apll = true,
	};
	//install and start i2s driver
	i2s_driver_install(EXAMPLE_I2S_NUM, &i2s_config, 0, NULL);
	ESP_ERROR_CHECK(i2s_set_adc_mode(I2S_ADC_UNIT, ADC1_CHANNEL_6));
	ESP_ERROR_CHECK(i2s_set_adc_mode(I2S_ADC_UNIT, ADC1_CHANNEL_7));
	
	SYSCON.saradc_ctrl2.max_meas_num = 255;
	SYSCON.saradc_ctrl2.meas_num_limit = 1;


}
void example_i2s_adc_dac(void)
{
	size_t bytes_read; //, bytes_written;
	//I2S_EVENT_RX_Q_OVF 
	char* i2s_read_buff = (char*) calloc(EXAMPLE_I2S_READ_LEN, sizeof(char));
	//i2s_event_t evt;
	while (1)
	{
		vTaskDelay(100);
		
		ESP_LOGI(TAG, "Starting");
		
		i2s_adc_enable(EXAMPLE_I2S_NUM);
		
		// Overwrite oattern register to get 2 channels scanning
		SYSCON.saradc_sar1_patt_tab[0] = 0x07F6F0F0F;
		SYSCON.saradc_ctrl.sar1_patt_len = 1;

		//rstb_wait: 8 sample_cycle: 2 standby_wait: 100 start_wait: 16 
		//ESP_LOGI(TAG, " rstb_wait: %u sample_cycle: %u standby_wait: %u start_wait: %u", SYSCON.saradc_fsm.rstb_wait, SYSCON.saradc_fsm.sample_cycle, SYSCON.saradc_fsm.standby_wait, SYSCON.saradc_fsm.start_wait);
		
		//read data from I2S bus, in this case, from ADC.
		i2s_read(EXAMPLE_I2S_NUM, (void*) i2s_read_buff, EXAMPLE_I2S_READ_LEN, &bytes_read, portMAX_DELAY);

		ESP_LOGI(TAG, "Stopping");

		i2s_adc_disable(EXAMPLE_I2S_NUM);

		vTaskDelay(100);

		for (size_t i = 0; i < EXAMPLE_I2S_READ_LEN; i += 2)
		{
			ESP_LOGI(TAG, " %1x  %u", ((i2s_read_buff[i + 1] & 0xf0) >> 4), (i2s_read_buff[i] | ((i2s_read_buff[i + 1] & 0x0f) << 8)));
		}
	}
}

esp_err_t app_main(void)
{
		example_i2s_init();
    esp_log_level_set("I2S", ESP_LOG_INFO);
		example_i2s_adc_dac();

    return ESP_OK;
}


Who is online

Users browsing this forum: No registered users and 71 guests