How to use i2s_read and i2s_write simultaneously?

physiii
Posts: 23
Joined: Fri Nov 17, 2017 9:37 pm

How to use i2s_read and i2s_write simultaneously?

Postby physiii » Sun May 17, 2020 11:08 pm

I am building an electric conductivity sensor and need to generate a 100hz tone for excitation while reading the output of the voltage divider.

I'm using ESP-IDF v4.2-dev-1415-ga2263571b and started with the i2s_adc_dac example.

Generating the 100hz was straight forward however I can't continuously read from i2s channel.

The first time the program executes i2s_read, I see the correct adc data in i2s_read_buff however the data does not change the next time the program runs i2s_read. So the output looks like:

Code: Select all

======
ff 3f ff 3f ff 3f ff 3f
ff 3f ff 3f ff 3f ff 3f
ff 3f ff 3f ff 3f ff 3f
ff 3f ff 3f ff 3f ff 3f
======
======
ff 3f ff 3f ff 3f ff 3f
ff 3f ff 3f ff 3f ff 3f
ff 3f ff 3f ff 3f ff 3f
ff 3f ff 3f ff 3f ff 3f
======
======
ff 3f ff 3f ff 3f ff 3f
ff 3f ff 3f ff 3f ff 3f
ff 3f ff 3f ff 3f ff 3f
ff 3f ff 3f ff 3f ff 3f
======

This is how I'm reading:

Code: Select all

void i2s_read_task(void*arg)
{
    int i2s_read_len = EXAMPLE_I2S_READ_LEN;
    size_t bytes_read;
    char* i2s_read_buff = (char*) calloc(i2s_read_len, sizeof(char));
    i2s_adc_enable(EXAMPLE_I2S_NUM);

    while (1) {
	i2s_read(EXAMPLE_I2S_NUM, (char*) i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY);
	vTaskDelay(100 / portTICK_PERIOD_MS);
	example_disp_buf((uint8_t*) i2s_read_buff, 32);
    }
}
Any idea why I am not able to load new data into i2s_read_buff?

Here is the program file. I tried several different things but here you can see I'm using seperate tasks for i2s_write and i2s_read

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 "audio_example_file.h"
#include "esp_adc_cal.h"

#define V_REF   1100
#define EXAMPLE_I2S_NUM           (0)
#define EXAMPLE_I2S_SAMPLE_RATE   (6000)
#define EXAMPLE_I2S_SAMPLE_BITS   (16)
#define EXAMPLE_I2S_READ_LEN      (16 * 1024)
#define EXAMPLE_I2S_FORMAT        (I2S_CHANNEL_FMT_RIGHT_LEFT)
#define EXAMPLE_I2S_CHANNEL_NUM   ((EXAMPLE_I2S_FORMAT < I2S_CHANNEL_FMT_ONLY_RIGHT) ? (2) : (1))
#define I2S_ADC_UNIT              ADC_UNIT_1
#define I2S_ADC_CHANNEL           ADC1_CHANNEL_3

void example_i2s_init(void)
{
     int i2s_num = EXAMPLE_I2S_NUM;
     i2s_config_t i2s_config = {
        .mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN,
        .sample_rate =  EXAMPLE_I2S_SAMPLE_RATE,
        .bits_per_sample = EXAMPLE_I2S_SAMPLE_BITS,
        .communication_format = I2S_COMM_FORMAT_PCM,
        .channel_format = EXAMPLE_I2S_FORMAT,
        .intr_alloc_flags = 0,
        .dma_buf_count = 2,
        .dma_buf_len = 1024,
        .use_apll = 1,
     };
     //install and start i2s driver
     i2s_driver_install(i2s_num, &i2s_config, 0, NULL);
     //init DAC pad
     i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
     //init ADC pad
     i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL);
		 i2s_set_clk(EXAMPLE_I2S_NUM, 6000, EXAMPLE_I2S_SAMPLE_BITS, 1);;
}

void total_buf(uint8_t* buf, int length)
{
	uint32_t total = 0;
  for (int i = 0; i < length; i++) {
			total += buf[i];
  }
  printf("Total: %u\n", total);
}

void example_disp_buf(uint8_t* buf, int length)
{
    printf("======\n");
    for (int i = 0; i < length; i++) {
        printf("%02x ", buf[i]);
        if ((i + 1) % 8 == 0) {
            printf("\n");
        }
    }
    printf("======\n");
}

/**
 * @brief Scale data to 16bit/32bit for I2S DMA output.
 *        DAC can only output 8bit data value.
 *        I2S DMA will still send 16 bit or 32bit data, the highest 8bit contains DAC data.
 */
int example_i2s_dac_data_scale(uint8_t* d_buff, uint8_t* s_buff, uint32_t len)
{
    uint32_t j = 0;
#if (EXAMPLE_I2S_SAMPLE_BITS == 16)
    for (int i = 0; i < len; i++) {
        d_buff[j++] = 0;
        d_buff[j++] = s_buff[i];
    }
    return (len * 2);
#else
    for (int i = 0; i < len; i++) {
        d_buff[j++] = 0;
        d_buff[j++] = 0;
        d_buff[j++] = 0;
        d_buff[j++] = s_buff[i];
    }
    return (len * 4);
#endif
}

void i2s_read_task(void*arg)
{
    int i2s_read_len = EXAMPLE_I2S_READ_LEN;
    size_t bytes_read;
		char* i2s_read_buff = (char*) calloc(i2s_read_len, sizeof(char));
		i2s_adc_enable(EXAMPLE_I2S_NUM);

    while (1) {
			i2s_read(EXAMPLE_I2S_NUM, (char*) i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY);
			vTaskDelay(100 / portTICK_PERIOD_MS);
			example_disp_buf((uint8_t*) i2s_read_buff, 32);
    }
}

void i2s_write_task(void*arg)
{
    size_t bytes_written;
		int i2s_read_len = EXAMPLE_I2S_READ_LEN;
    uint8_t* i2s_write_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char));
		int tot_size = sizeof(audio_table);

    while (1) {
        int offset = 0;
        while (offset < tot_size) {
            int play_len = ((tot_size - offset) > (4 * 1024)) ? (4 * 1024) : (tot_size - offset);
            int i2s_wr_len = example_i2s_dac_data_scale(i2s_write_buff, (uint8_t*)(audio_table + offset), play_len);
            i2s_write(EXAMPLE_I2S_NUM, i2s_write_buff, i2s_wr_len, &bytes_written, portMAX_DELAY);
            offset += play_len;
				}

        vTaskDelay(100 / portTICK_PERIOD_MS);
    }
}

esp_err_t i2s_main(void)
{
    example_i2s_init();
    xTaskCreate(i2s_read_task, "i2s_read_task", 1024 * 2, NULL, 5, NULL);
		xTaskCreate(i2s_write_task, "i2s_write_task", 1024 * 2, NULL, 5, NULL);
    return ESP_OK;
}


ESP_Sprite
Posts: 9020
Joined: Thu Nov 26, 2015 4:08 am

Re: How to use i2s_read and i2s_write simultaneously?

Postby ESP_Sprite » Mon May 18, 2020 7:05 am

Not sure if that will solve your issue, but why the vTaskDelay? The i2s_read is blocking, it will return data whenever it has some, and the vTaskDelay will lead to buffer overruns.

physiii
Posts: 23
Joined: Fri Nov 17, 2017 9:37 pm

Re: How to use i2s_read and i2s_write simultaneously?

Postby physiii » Mon May 18, 2020 2:01 pm

That wasn't it but thank you for pointing it out. Running the file outside of it's parent program resolved the issue so I'll need to trace it down when integrating this portion of the program back in.

I may need to start another thread but my issue now is that I need to attenuate the signal read by i2s_read.

When shorting the DAC output to the ADC input all I saw was what looked like noise. So I ran a test by slowly moving up the voltage and notice the average voltage repeats every few hundred mV.

I hoped it was as easy as running

Code: Select all

adc1_config_channel_atten(I2S_ADC_CHANNEL,ADC_ATTEN_DB_11);
But that did not work nor did setting other attenuations .

Searching through esp idf files, I found this:

Code: Select all

    // enable adc sampling, ADC_WIDTH_BIT_12, ADC_ATTEN_DB_11 hard-coded in adc_i2s_mode_init
How do I attenuate the adc signal so I can spread the 12 bits across 0 to 3v3 range?

Who is online

Users browsing this forum: No registered users and 122 guests