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