I2S 24bit Slave

StijnVM
Posts: 4
Joined: Mon Jul 30, 2018 7:31 pm

I2S 24bit Slave

Postby StijnVM » Sun Aug 05, 2018 4:18 pm

Hello!

I already reported the problem I'm having in the ESP-IDF subforum but this may be a better place.
See https://esp32.com/viewtopic.php?f=13&t=6582 for more detail but in a nutshell:

Trying to interface the ESP32 with a USB to I2S bridge which can only be configured as an I2S master and instead of using a continuous clock, outputs bursts of 24 clockpulses for each sample. ESP32 should only output data right now.

So I'm trying to use the ESP32 I2S in 24 bit slave mode but was seeing strange shifting behaviour in the audio data and now I've reduced the setup to 2 ESP32's to check if the problem could be the bursty clock. However, I'm still seeing some odd behaviour.

The code I'm using on the slave device:

Code: Select all

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s.h"
#include "esp_system.h"
#include <math.h>

#define I2S_NUM         (0)

static void setup_testblock()
{
   unsigned int n_blocks = 40;
    int *samples_data = malloc(8*n_blocks);
       size_t i2s_bytes_write = 0;

    unsigned int i;
    for(i = 0; i < n_blocks; i++)
    {
            samples_data[i*2] = -1431655681;          //0b101010101010101010101010(11111111) last byte should be dropped
            samples_data[i*2 + 1] = -286331137;         //0b111011101110111011101110(11111111) last byte should be dropped
    }

    i2s_write(I2S_NUM, samples_data, n_blocks*4, &i2s_bytes_write, 100);
    //i2s_write_expand(I2S_NUM, samples_data, ((bits+8)/16)*SAMPLE_PER_CYCLE*4, 16, 16, &i2s_bytes_write, 10);

    free(samples_data);
}
void app_main()
{
    i2s_config_t i2s_config = {
        .mode = I2S_MODE_SLAVE | I2S_MODE_TX,                                  // Only TX
        .sample_rate = 48000,
        .bits_per_sample = 24,
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,                           //2-channels
        .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
        .dma_buf_count = 2,
        .dma_buf_len = 30,
        .use_apll = true,
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1                               //Interrupt level 1
    };
    i2s_pin_config_t pin_config = {
        .bck_io_num = 26,
        .ws_io_num = 22,
        .data_out_num = 25,
        .data_in_num = -1                                                       //Not used
    };
    i2s_driver_install(0, &i2s_config, 0, NULL);
    i2s_set_pin(0, &pin_config);

    setup_testblock();
    while (1)
    {
        vTaskDelay(5000/portTICK_RATE_MS);
    }

}
If I understand correctly, it basically always writes the same pattern for left and right to the DMA buffers. The I2S peripheral then loops this data as output.
This works fine if I set both the master and slave to 32 bit. 0b10101010101010101010101011111111 and 0b11101110111011101110111011111111 is repeated. However, when I set the ESP's to 24 bit, the slave should drop the final byte (all the 1's). This happens most of the time.. but quite often, 2 bytes of zeroes are inserted, the data is shifted but then recovers on the falling LRClock.
See https://drive.google.com/file/d/1l0-Akl ... gBeJC/view and https://drive.google.com/file/d/1UeaQfL ... my2rn/view for scope screenshots.
Top is LRclock and bottom is Data.
I believe this problem is related to the problem I'm having with the USB-I2S bridge.

Since the testprogram is just sitting in the while(1) loop, I think the problem lies deeper than the software and may not be easy to solve..
Can anyone please confirm this behaviour? Any suggestions?

Thanks!

crespum
Posts: 3
Joined: Thu Nov 29, 2018 6:21 pm

Re: I2S 24bit Slave

Postby crespum » Fri Mar 22, 2019 8:19 am

Hi @StijnVM. I'm running into a similar issue. Have you ever found a solution for it?

SebastiaoRocha
Posts: 1
Joined: Tue Jul 19, 2022 5:11 pm

Re: I2S 24bit Slave

Postby SebastiaoRocha » Wed Jul 20, 2022 12:22 am

Hi, i can confirm that there is a bug at ESP32 I2S 24Bit.

I had done all tests, see bellow.

Filled buffer ( left/right channels ) with the folowing pattern:
"spaces to better undertand"
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

Data Byte value:
1) 44.1k/48k - 8bit ERROR! All values for both channels outputs value 0x02 ( just for test, bad quality )

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 is replicated to next one, ignoring buffer sequence, so its adding one byte before each sample channel.

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, if i found maybe i can fix it, but it may takes longer.

So, anyone with knowledge can fix it faster, as it looks like a mistake at byte index.

Please note, chacked with logic analizer.
  1. #include <WiFi.h>
  2. #include "driver/i2s.h"
  3.  
  4. #define I2S_DATA            25 //
  5. #define I2S_BLCK            26 //
  6. #define I2S_LRCK            27 //
  7. #define I2S_MCLK           3 //
  8.  
  9. QueueHandle_t i2sQueueHandle;
  10. TaskHandle_t i2sOutputTaskHandle = NULL;
  11.  
  12. i2s_config_t i2s_config;
  13. i2s_pin_config_t i2s_pin_config;
  14.  
  15. // Just change that to test
  16. const unsigned char bitsPerSample = I2S_BITS_PER_SAMPLE_24BIT;
  17. const unsigned long sampleRate = 48000;
  18.  
  19. // hardcoded only for this test, max stereo samples for single DMA write is:
  20. // 32 bits audio is 128: 1024 total bytes
  21. // 24 bits audio si 170: 1020 total bytes
  22. // 16 bits audio is 256: 1024 total bytes
  23. unsigned long samples = int( 1024 / ((bitsPerSample/8)*2) );
  24.  
  25. unsigned char bytes = (bitsPerSample/8)*2;
  26.  
  27. // Buffer like struct array is not dynamic for hold bitspersample data
  28. // So used bytes array instead, this prevents headcache.
  29. unsigned char buffer[2048];
  30.  
  31. constexpr char hexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7',
  32.                            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  33.  
  34. void i2sOutputTask(void* pvParameters) {
  35.     size_t bytesWritten;
  36.  
  37.     // Install driver
  38.     i2s_driver_install(I2S_NUM_0, &i2s_config, 4, &i2sQueueHandle );
  39.     i2s_set_pin(I2S_NUM_0, &i2s_pin_config);
  40.  
  41.     //if(ESP_OK != i2s_set_clk(I2S_NUM_0, sampleRate, I2S_BITS_PER_SAMPLE_24BIT, I2S_CHANNEL_STEREO)){}
  42.  
  43.     //i2s_check_set_mclk(I2S_NUM_0, I2S_MCLK );
  44.     i2s_zero_dma_buffer(I2S_NUM_0);
  45.  
  46.     for(;;) {
  47.         // wait for some data to be requested
  48.         i2s_event_t evt;
  49.         if (xQueueReceive(i2sQueueHandle, &evt, portMAX_DELAY) == pdPASS) {
  50.  
  51.             if (evt.type == I2S_EVENT_TX_DONE) {
  52.                 i2s_write( I2S_NUM_0, (uint8_t *)buffer,
  53.                     (samples*bytes), &bytesWritten, portMAX_DELAY
  54.                 );
  55.  
  56.                 if ( bytesWritten != (samples*bytes) ){
  57.                     Serial.println( "Not all bytes were written to I2S");
  58.                 }
  59.                 //i2s_write_expand( I2S_NUM_0, (uint8_t *)buffer, (samples*bytes), 24, 24, &bytesWritten, portMAX_DELAY );
  60.             }
  61.         }
  62.     }
  63. }
  64.  
  65. void setup() {
  66.     Serial.begin( 115200 );
  67.  
  68.     // Set pins
  69.     i2s_pin_config.bck_io_num = I2S_BLCK;
  70.     i2s_pin_config.ws_io_num = I2S_LRCK;
  71.     i2s_pin_config.data_out_num = I2S_DATA;
  72.     i2s_pin_config.data_in_num = I2S_PIN_NO_CHANGE;
  73.  
  74.     // set config
  75.     i2s_config.mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX);
  76.     i2s_config.sample_rate = (i2s_bits_per_sample_t)sampleRate;
  77.     i2s_config.bits_per_sample = (i2s_bits_per_sample_t)bitsPerSample;
  78.     i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
  79.     i2s_config.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S;
  80.     i2s_config.intr_alloc_flags = 0;
  81.     i2s_config.dma_buf_count = 6;
  82.     i2s_config.dma_buf_len = int( 1024/ ((bitsPerSample/8)*2 ) );
  83.     i2s_config.use_apll = false;  
  84.     i2s_config.fixed_mclk = 12288;
  85.     i2s_config.tx_desc_auto_clear = false;
  86.  
  87.     // fill frames bytes with same byte value, for use with logic analizer only
  88.     // so dont expect audio, all bytes on right to 0x01 and bytes on left to 0x02  
  89.     for ( int i = 0; i < samples; i++ ) {
  90.         // Left
  91.         switch ( bitsPerSample ) {
  92.             case I2S_BITS_PER_SAMPLE_8BIT:
  93.                 // rigth
  94.                 buffer[(i*bytes)] = 1;
  95.  
  96.                 // left
  97.                 buffer[(i*bytes)+1] = 2;
  98.                 break;
  99.  
  100.             case I2S_BITS_PER_SAMPLE_16BIT:
  101.                 // rigth
  102.                 buffer[(i*bytes)] = 4;
  103.                 buffer[(i*bytes)+1] = 3;
  104.  
  105.                 // left
  106.                 buffer[(i*bytes)+2] = 2;
  107.                 buffer[(i*bytes)+3] = 1;
  108.                 break;
  109.  
  110.             case I2S_BITS_PER_SAMPLE_24BIT:
  111.                 // rigth
  112.                 buffer[(i*bytes)] = 6;
  113.                 buffer[(i*bytes)+1] = 5;
  114.                 buffer[(i*bytes)+2] = 4;
  115.  
  116.                 // left
  117.                 buffer[(i*bytes)+3] = 3;
  118.                 buffer[(i*bytes)+4] = 2;
  119.                 buffer[(i*bytes)+5] = 1;
  120.                 break;
  121.  
  122.             case I2S_BITS_PER_SAMPLE_32BIT:
  123.                 // right
  124.                 buffer[(i*bytes)] = 8;
  125.                 buffer[(i*bytes)+1] = 7;
  126.                 buffer[(i*bytes)+2] = 6;      
  127.                 buffer[(i*bytes)+3] = 5;
  128.  
  129.                 // left
  130.                 buffer[(i*bytes)+4] = 4;
  131.                 buffer[(i*bytes)+5] = 3;
  132.                 buffer[(i*bytes)+6] = 2;
  133.                 buffer[(i*bytes)+7] = 1;
  134.                 break;
  135.  
  136.             default:
  137.                 Serial.printf("Unknown bits per sample: %d\n", bitsPerSample );
  138.         }
  139.     }
  140.  
  141.     Serial.println( "" );
  142.     Serial.printf( "Bits per Sample: %d, Samples: %d, Bytes: %d, Total: %d\n",
  143.         bitsPerSample, samples, bytes, (samples*bytes)
  144.     );
  145.  
  146.     // will print only first 4 samples ( stereo )
  147.     for ( int i = 0; i < (4*(bytes)); ++i) {
  148.         Serial.print( hexmap[(buffer[i] & 0xF0) >> 4] );
  149.         Serial.print( hexmap[buffer[i] & 0x0F] );
  150.         Serial.print( " " );
  151.         if ( ((i+1) % ( bytes/2) == 0 ) && ( i+1 != (4*(bytes)) )) { Serial.print( "- " ); }
  152.     }
  153.  
  154.     xTaskCreatePinnedToCore(
  155.         i2sOutputTask,            //Function to implement the task
  156.         "i2sOutputTask",          //Name of the task
  157.         5000,                     //Stack size in words
  158.         NULL,                     //Task input parameter
  159.         0,                        //Priority of the task
  160.         &i2sOutputTaskHandle,     //Task handle.
  161.         1                         //Core where the task should run
  162.     );
  163.    
  164. }
  165.  
  166. void loop() {
  167.     delay(5);
  168. }

philippe_44
Posts: 26
Joined: Thu May 23, 2019 3:05 pm

Re: I2S 24bit Slave

Postby philippe_44 » Sat Aug 27, 2022 7:15 pm

Have a look
------------------
It’s similar when the data is 32-bit width, but take care when using 8-bit and 24-bit data width. For 8-bit width, the written buffer should still using uint16_t (i.e. align with 2 bytes), and only the high 8 bits will be valid, the low 8 bits are dropped, and for 24-bit width, the buffer is supposed to use uint32_t (i.e. align with 4 bytes), and only the high 24 bits valid, the low 8 bits are dropped.

Another point is that, for the 8-bit and 16-bit mono mode, the real data on the line are swapped. To get the correct sequence, the writting buffer need to swap the data every two bytes.
-------------

Who is online

Users browsing this forum: No registered users and 12 guests