I2S 24bit - Incorrect data output
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:
-
44.1k/48k - 8bit ERROR! All values for both channels are 0x02, Expected: 01 02 ( just testing )
-
44.1k/48k - 16bit Correct! output pattern is like expected.
-
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.
-
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!

48k 24 bits, ERROR!

48k 32 bits, OK!

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.
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.
Please help with triage, @PilnyTomas.
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 .
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?
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.
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.

// **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;
};
}
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...