Samples swapped for mono I2S driving built-in ESP32 DAC (v4.4.4)

danjulio
Posts: 15
Joined: Tue Feb 25, 2020 9:00 pm

Samples swapped for mono I2S driving built-in ESP32 DAC (v4.4.4)

Postby danjulio » Sat Mar 04, 2023 6:56 pm

I have a very simple ESP32 program (attached) that attempts to output a 500 Hz sine wave to the GPIO25 DAC output using I2S. I configure I2S for mono (one-channel) operation. The sine wave comes from a table-lookup. The sine wave table is 32 entries (for one period). I2S is configured for 16 kHz operation so 16000 / 32 = 500 Hz. A buffer is filled with an even number of table entries to pass repeatedly to i2s_write.

When I run this it appears that every two entries are swapped when they are output to the DAC as shown with the following oscilloscope trace from the DAC output.
scope_0.png
scope_0.png (25.82 KiB) Viewed 1763 times
When I swap every other two entries (with the compiler define SWAP_WORDS) the output looks correct.
scope_2.png
scope_2.png (24.93 KiB) Viewed 1763 times
What is going on? Am I doing something wrong? It is interesting to note that the output is correct if I configure I2S for two channel operation, enable both DAC outputs, and load each sample twice (one for each channel).

The code is as follows (entire IDF project attached - compiled with IDF v4.4.4)

Code: Select all

[Codebox=c file=i2s_dac.c]
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "driver/i2s.h"


/* Enable to swap 16-bit words as a kludge work-around */
//#define SWAP_WORDS

/* Prepare for 32 sample values */
#define NUM_SINE_SAMPLES 32

/* Sine Wave Sample */
const int16_t sine_data_frame[NUM_SINE_SAMPLES] = {
	  6392,  12539,  18204,  23169,  27244,  30272,  32137,  32767,  32137,
	 30272,  27244,  23169,  18204,  12539,   6392,      0,  -6393, -12540,
	-18205, -23170, -27245, -30273, -32138, -32767, -32138, -30273, -27245,
	-23170, -18205, -12540,  -6393,     -1,
};


esp_err_t app_main(void)
{
	int16_t i2s_write_buf[512];
	size_t bytes_to_write;
	size_t bytes_written;
	size_t samples_to_write;
	
	// configure i2s
	i2s_config_t i2s_config = {
        .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
        .sample_rate =  16000,
        .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
        .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
        .intr_alloc_flags = 0,
        .dma_buf_count = 2,
        .dma_buf_len = 512,
        .use_apll = 1,
        .tx_desc_auto_clear = 0,
	};
	
    // install and start i2s driver
    i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
    
    // init DAC pad
    i2s_set_dac_mode(I2S_DAC_CHANNEL_RIGHT_EN);  // RIGHT is DAC 1: GPIO25, LEFT is DAC 2: GPIO26
	
	// Load the sine wave into the buffer
	samples_to_write = 8 * NUM_SINE_SAMPLES;
	bytes_to_write = 2 * samples_to_write;
	
#ifdef SWAP_WORDS
	for (int i=0; i<samples_to_write; i+=2) {
		// Swap every 2 entries in the sine table when writing to the i2s buffer
		// WHY DOES THIS WORK???
		i2s_write_buf[i] = sine_data_frame[(i+1) % NUM_SINE_SAMPLES] + 0x8000;
		i2s_write_buf[i+1] = sine_data_frame[i % NUM_SINE_SAMPLES] + 0x8000;
	}
#else
	for (int i=0; i<samples_to_write; i++) {
		// Offset signed value by 0x8000 to make unsigned for single-ended DAC output
		i2s_write_buf[i] = sine_data_frame[i % NUM_SINE_SAMPLES] + 0x8000;
	}
#endif

	// Output the buffer forever
	while (1) {
		i2s_write(I2S_NUM_0, i2s_write_buf, bytes_to_write, &bytes_written, portMAX_DELAY);
		if (bytes_written != bytes_to_write) {
			printf("tried to write %d, actually wrote %d", bytes_to_write, bytes_written);
		}
	}
		
    return ESP_OK;
}
[/Codebox]
Attachments
i2s_dac.zip
(11.55 KiB) Downloaded 147 times

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

Re: Samples swapped for mono I2S driving built-in ESP32 DAC (v4.4.4)

Postby ESP_Sprite » Sun Mar 05, 2023 2:20 am

SWAP_WORDS is the right solution; the I2S hardware outputs the most significant word first while the rest of the ESP32 is little-endian. It's not ideal, and I think later chips (S2, S3 etc) have a way of flipping that, but the original ESP32 doesn't.

danjulio
Posts: 15
Joined: Tue Feb 25, 2020 9:00 pm

Re: Samples swapped for mono I2S driving built-in ESP32 DAC (v4.4.4)

Postby danjulio » Mon Mar 06, 2023 7:05 pm

@ESP_Sprite - thank you. Good to understand how the ESP32 works.

I have a related question. I also wrote a demo that attempts to echo an ADC input back to the DAC output using I2S. It configures I2S 0 for both input and output (ADC + DAC) @ 16 bits. Then it just spins in a loop reading a buffer from the ADC (i2s_read) and writing it to the DAC (i2s_write). It works, however on occasion it seems data output from the DAC is corrupted (I have checked the data being passed into i2s_write and it appears correct). I notice that you don't appear to support ADC/DAC as I2S sources in IDF v5.0. Are they supported in v4.4? Should I expect this to work or is there some problem and it won't really work?

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

Re: Samples swapped for mono I2S driving built-in ESP32 DAC (v4.4.4)

Postby ESP_Sprite » Wed Mar 08, 2023 1:00 am

I think that did not change because of hardware issues, but because we refactored the drivers; you're likely to find that functionality elsewhere. (E.g. for the ADCs, I think we have some 'periodic ADC' driver.)

HarishKalla
Posts: 1
Joined: Sat Feb 10, 2024 6:45 am

Re: Samples swapped for mono I2S driving built-in ESP32 DAC (v4.4.4)

Postby HarishKalla » Sat Feb 10, 2024 9:39 am

HI, I am a beginner in developing the I2S protocol. I used espressif IDE V2.12.0 to implement a program, but I could not find the option to configure the channel format as I2S_CHANNEL_FMT_ONLY_LEFT. How can I set the I2S peripheral to use only the left channel data? And please tell the procedure to store I2S audio output data in SD card without lost.
Thank you.

Who is online

Users browsing this forum: allacmc, Bing [Bot], Majestic-12 [Bot] and 43 guests