arduino-esp32 icon indicating copy to clipboard operation
arduino-esp32 copied to clipboard

I2S 24bit - Incorrect data output

Open tcpiinterface opened this issue 3 years ago • 8 comments

Board

ESP32 Dev Module (esp32dev)

Device Description

Inputs from I2S: PCM1802 IC PCM1808 IC

Outputs to I2S: PCM5102A ADAU1401 DSP IC ADAU1701 DSP IC NONE just used module and logic analizer.

Note: One at time, tested with all ICs, also with no external connections.

Hardware Configuration

GPIO 25: I2S_DATA GPIO 26: I2S_BLCK GPIO 27: I2S_LRCK GPIO 3: I2S_MCLK ( Only for ESP as master )

Version

v2.0.4

IDE Name

Platformio

Operating System

Windows 10 Pro

Flash frequency

40 Mhz

PSRAM enabled

no

Upload speed

115200

Description

I2S wont works using 24bit ( 44.1khz or 48khz ).

I had done all tests, see bellow.

Filled buffer ( left/right channels ) with the folowing pattern: "spaces to better undertand" 8bit: 0x01 02 16bit: 0x01 02 03 04 24bit: 0x01 02 03 04 05 06 32bit: 0x01 02 03 04 05 06 07 08

All cases, LRCK/WS is ok, toggle at specified bit count BCK is ok, toggle many times as specified bit count DATA is ok, have all bits as specified bit count, but wrong values at 24 bits.

Data Byte value:

  1. 44.1k/48k - 8bit ERROR! All values for both channels are 0x02, Expected: 01 02 ( just testing )

  2. 44.1k/48k - 16bit Correct! output pattern is like expected.

  3. 44.1k/48k - 24bit ERROR! pattern is ( please note, i2s sent bytes inverted order from buffer ): Output: 01 02 03 - 03 04 05 - 05 06 01 - 01 02 03 - 03 04 05 - 05 06 01 - 01 02 03 - 03 04 05 - 05 06 01 - 01 02 03 Expected: 01 02 03 - 04 05 06 - 01 02 03 - 04 05 06 - 01 02 03 - 04 05 06 - 01 02 03 - 04 05 06 - 01 02 03 - 04 05 06

    Looks like last byte has not been incremented its buffer position, causing repeated value at first byte of each channel. Maybe just needs increment buffer position to solve this problem.

  4. 44.1k/48k - /32bit Correct! output pattern is like expected.

I cant find source code that write to i2s driver, just find headers, not implementation, i will try more, maybe someone can tell what filename is it.

Please note! Checked with logic analizer.

PS: Tested with all framework versions, same result.

24bit is very important for use with DSPs

48k 16 bits, OK! My Remote Image

48k 24 bits, ERROR! My Remote Image

48k 32 bits, OK! My Remote Image

platformio.ini

[env:ESP32-4M]
framework = arduino
platform = espressif32
board = esp32dev

monitor_port = COM7
monitor_speed = 115200
monitor_filters = 
	esp32_exception_decoder
	colorize

upload_speed = 921600
upload_port = COM7
upload_protocol = esptool

build_type = debug
build_flags = -DCORE_DEBUG_LEVEL=5

Sketch

#include <WiFi.h>
#include "driver/i2s.h"

#define I2S_DATA            25 //
#define I2S_BLCK            26 // 
#define I2S_LRCK            27 //
#define I2S_MCLK	       3 // 

QueueHandle_t i2sQueueHandle;
TaskHandle_t i2sOutputTaskHandle = NULL;

i2s_config_t i2s_config;
i2s_pin_config_t i2s_pin_config;

// Just change that to test
const unsigned char bitsPerSample = I2S_BITS_PER_SAMPLE_24BIT;
const unsigned long sampleRate = 48000;

// stereo samples for single DMA write is:
// 32 bits audio is 128: 1024 total bytes 
// 24 bits audio si 170: 1020 total bytes
// 16 bits audio is 256: 1024 total bytes
unsigned long samples = int( 1024 / ((bitsPerSample/8)*2) );

unsigned char bytes = (bitsPerSample/8)*2;

// Buffer like struct array is not dynamic for hold bitspersample data
// So used bytes array instead, this prevents headcache.
unsigned char buffer[2048];

constexpr char hexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7',
                           '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

void i2sOutputTask(void* pvParameters) {
    size_t bytesWritten;

    // Install driver
    i2s_driver_install(I2S_NUM_0, &i2s_config, 4, &i2sQueueHandle );
    i2s_set_pin(I2S_NUM_0, &i2s_pin_config);

    //if(ESP_OK != i2s_set_clk(I2S_NUM_0, sampleRate, I2S_BITS_PER_SAMPLE_24BIT, I2S_CHANNEL_STEREO)){}

    //i2s_check_set_mclk(I2S_NUM_0, I2S_MCLK );
    i2s_zero_dma_buffer(I2S_NUM_0);

    for(;;) { 
        // wait for some data to be requested
        i2s_event_t evt;
        if (xQueueReceive(i2sQueueHandle, &evt, portMAX_DELAY) == pdPASS) {

            if (evt.type == I2S_EVENT_TX_DONE) {
                i2s_write( I2S_NUM_0, (uint8_t *)buffer, 
                    (samples*bytes), &bytesWritten, portMAX_DELAY
                );

                if ( bytesWritten != (samples*bytes) ){
                    Serial.println( "Not all bytes were written to I2S");
                }
                //i2s_write_expand( I2S_NUM_0, (uint8_t *)buffer, (samples*bytes), 24, 24, &bytesWritten, portMAX_DELAY ); 
            }
        }
    }
}

void setup() {
    Serial.begin( 115200 );

    // Set pins
    i2s_pin_config.bck_io_num = I2S_BLCK;
    i2s_pin_config.ws_io_num = I2S_LRCK;
    i2s_pin_config.data_out_num = I2S_DATA;
    i2s_pin_config.data_in_num = I2S_PIN_NO_CHANGE;

    // set config
    i2s_config.mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX);
    i2s_config.sample_rate = (i2s_bits_per_sample_t)sampleRate;
    i2s_config.bits_per_sample = (i2s_bits_per_sample_t)bitsPerSample;
    i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
    i2s_config.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S;
    i2s_config.intr_alloc_flags = 0;
    i2s_config.dma_buf_count = 6;
    i2s_config.dma_buf_len = int( 1024/ ((bitsPerSample/8)*2 ) );
    i2s_config.use_apll = false;  
    i2s_config.fixed_mclk = 12288;
    i2s_config.tx_desc_auto_clear = false;

    // fill frames bytes with same byte values, for use with logic analizer only
    // so dont expect audio, all bytes on right to 0x01 and bytes on left to 0x02  
    for ( int i = 0; i < samples; i++ ) {
        // Left
        switch ( bitsPerSample ) {
            case I2S_BITS_PER_SAMPLE_8BIT:
                // rigth
                buffer[(i*bytes)] = 1;

                // left
                buffer[(i*bytes)+1] = 2;
                break;

            case I2S_BITS_PER_SAMPLE_16BIT:
                // rigth 
                buffer[(i*bytes)] = 4;
                buffer[(i*bytes)+1] = 3;

                // left
                buffer[(i*bytes)+2] = 2;
                buffer[(i*bytes)+3] = 1;
                break;

            case I2S_BITS_PER_SAMPLE_24BIT:
                // rigth
                buffer[(i*bytes)] = 6;
                buffer[(i*bytes)+1] = 5;
                buffer[(i*bytes)+2] = 4;

                // left 
                buffer[(i*bytes)+3] = 3;
                buffer[(i*bytes)+4] = 2;
                buffer[(i*bytes)+5] = 1;
                break;

            case I2S_BITS_PER_SAMPLE_32BIT:
                // right
                buffer[(i*bytes)] = 8;
                buffer[(i*bytes)+1] = 7;
                buffer[(i*bytes)+2] = 6;      
                buffer[(i*bytes)+3] = 5;

                // left
                buffer[(i*bytes)+4] = 4;
                buffer[(i*bytes)+5] = 3;
                buffer[(i*bytes)+6] = 2;
                buffer[(i*bytes)+7] = 1;
                break;

            default:
                Serial.printf("Unknown bits per sample: %d\n", bitsPerSample );
        }
    }

    Serial.println( "" );
    Serial.printf( "Bits per Sample: %d, Samples: %d, Bytes: %d, Total: %d\n", 
        bitsPerSample, samples, bytes, (samples*bytes)
    );

    // will print only first 4 samples ( stereo )
    for ( int i = 0; i < (4*(bytes)); ++i) {
        Serial.print( hexmap[(buffer[i] & 0xF0) >> 4] );
        Serial.print( hexmap[buffer[i] & 0x0F] );
        Serial.print( " " );
        if ( ((i+1) % ( bytes/2) == 0 ) && ( i+1 != (4*(bytes)) )) { Serial.print( "- " ); }
    }

    xTaskCreatePinnedToCore(
        i2sOutputTask,            //Function to implement the task 
        "i2sOutputTask",          //Name of the task
        5000,                     //Stack size in words 
        NULL,                     //Task input parameter 
        0,                        //Priority of the task 
        &i2sOutputTaskHandle,     //Task handle.
        1                         //Core where the task should run 
    );
   
}

void loop() {
    delay(5);
}

Debug Message

No debug messages available.

Other Steps to Reproduce

No response

I have checked existing issues, online documentation and the Troubleshooting Guide

  • [X] I confirm I have checked existing issues, online documentation and Troubleshooting guide.

tcpiinterface avatar Jul 20 '22 03:07 tcpiinterface

Documentation Examples Source

Thanks for your repply...

but this sources files link you provided does not exists at my hard drive, also build logs does not includes it, i have successfuly build using Arduino ide 1.8.19 and 2.0.0.rc8, also using Platformio, so i believe this files is not included/needed.

tcpiinterface avatar Jul 20 '22 07:07 tcpiinterface

Please help with triage, @PilnyTomas.

VojtechBartoska avatar Jul 20 '22 07:07 VojtechBartoska

Yeah, it's a mess, I tried some workaround which should move around bytes before sending to prevent this issue. Try using I2S lib as described in Arduino docs https://docs.arduino.cc/learn/built-in-libraries/i2s The workaround can be seen here .

PilnyTomas avatar Jul 25 '22 11:07 PilnyTomas

Hi PilnyTomas,

I have tried this, but missing I2S.h with standard arduino ( 1.8.9 or 2.0 )

Also i have tried to install it from arduino Library Manager ( same as before, no "I2S.h" file )

Also i have tried download it from https://github.com/espressif/arduino-esp32/tree/5f427c998a5eff55abf163a4fbeab17fff06e348/libraries/I2S and manually placed it into arduino library folder, on this case, there is errors when compile.

I2S.cpp:143:58: error: 'I2S_COMM_FORMAT_STAND_I2S' is not a member of 'esp_i2s' .communication_format = (esp_i2s::i2s_comm_format_t)(esp_i2s::I2S_COMM_FORMAT_STAND_I2S),

i have not tried it with Platformio.

Any sugestions?

tcpiinterface avatar Jul 25 '22 19:07 tcpiinterface

arduino ( 1.8.9 or 2.0 )

I suppose those are Arduino IDE versions? If yes then those do not matter. What matters is the Arduino-esp32 core. Please go to your board manager and update: Tools > Board > Boards Manager > then search for esp32 - you should see only 1 entry - update it to currently latest version (2.0.4). The I2S is there since 2.0.3 so if you have older version it will not work.

PilnyTomas avatar Jul 26 '22 06:07 PilnyTomas

Hi @PilnyTomas

As requested, i have tryed with Arduino SDK 2.0.4 - v4.4.1-472-gc9140caf8c

Same results as before, except that 8 bit works fine this time.

My Remote Image

// **This wont works!!! missing data output**
// Also tryed with one at time: PIN_I2S_SD and PIN_I2S_SD_OUT
//#define PIN_I2S_SD 25
//#define PIN_I2S_SD_OUT 25
//#define PIN_I2S_SCK 26
//#define PIN_I2S_FS 27

#include <I2S.h>

i2s_mode_t mode = I2S_PHILIPS_MODE;
const int sampleRate = 48000;
const int bps = 24;

unsigned char sample8[2]  = { 1,2 };
unsigned char sample16[4] = { 1,2,3,4 };
unsigned char sample24[6] = { 1,2,3,4,5,6 };
unsigned char sample32[8] = { 1,2,3,4,5,6,7,8 };

void setup() {
  Serial.begin(115200);
  Serial.printf("I2S test, build with %s", ESP.getSdkVersion() );

  I2S.setDataPin(25);       // this works, but i need simplex mode
  //I2S.setDataOutPin(25);  **// this wont works! there is no output**
  I2S.setSckPin(26);
  I2S.setFsPin(27);

  // start I2S at the sample rate with 16-bits per sample
  if (!I2S.begin(mode, sampleRate, bps)) {
    Serial.println("Failed to initialize I2S!");
    while (1); // do nothing
  }
}

void loop() {
  switch ( bps ) {
      case 8:  // **Works! NOT SAME as before**, this time correct output, values: 0x01 and 0x02
        I2S.write( sample8, 2 );
        break;

      case 16:  // **Works! SAME as before**, correct output, values: 0x04 03 and 0x02 01
        I2S.write( sample16, 4 );
        break;

      // Same error as before.
      case 24:  // **Wont Works! SAME as before**, but **wrong values**: 0x02 01 06 and 0x06 05 04
        I2S.write( sample24, 6 );
        break;

      case 32:  // **Works! SAME as before**, correct output, values: 0x08 07 06 05 and 0x04 03 02 01
        I2S.write( sample32, 8 );
        break;
    };
}

tcpiinterface avatar Jul 26 '22 21:07 tcpiinterface

For 24 bit sample please use uint32_t with LSB padding: uint32_t sample24[2] = {0x03020100, 0x06050400}; This should be read on the logic analyzer as 01 02 03 04 05 06 or uint32_t sample24[2] = {0x01020300, 0x04050600}; For output 03 02 01 06 05 04

The IDF i2s driver just works this way. I could add to the workaround function some tweaking to take 24bit samples as an array of bytes...

PilnyTomas avatar Jul 27 '22 09:07 PilnyTomas