I2S Record to SDCard using FATFs

jerome
Posts: 37
Joined: Wed Jan 23, 2019 2:28 pm

Re: I2S Record to SDCard using FATFs

Postby jerome » Mon Mar 11, 2019 5:57 pm

Thanks for your answer.
Does this mean recording audio to SDCard using FATFs is not possible ?
Using an oscilloscope, the minimum time I measure to continuously write blocs of 16128 bytes to SD is about 50ms to 70ms (depending on the SD Card) as follow :

Code: Select all

void mytask()
{
     while(1)
     {
             // wait trigger to start recording
             ...

             do
             {
                   // toggle one IO to check with scope
                   ...
                   fwrite(recorded 16128 bytes from I2S);
                   // delay 1ms to avoid watchdog error                   
                   vTaskDelay(1 / portTICK_RATE_MS);

             } while (!trigger to stop recording);
     }
}
On the other side, filling one bloc of 16128 bytes from I2S @ 24/96K/Stereo is about 17ms.
According to the above, even if using huge circular buffers, recording is not possible.

Few questions :

1) Do you think using a faster SDCard could help ? I already tried with 2 different models SDHC
2) Is there anything to setup in make menuconfig that could improve fwrite() ? BTW I'm using long file names for FAT.
3) What is the most efficient between writing blocks of 512 bytes VS blocks of 16K bytes ?

I'm stuck on this, any idea would be greatly appreciated.

User avatar
fly135
Posts: 606
Joined: Wed Jan 03, 2018 8:33 pm
Location: Orlando, FL

Re: I2S Record to SDCard using FATFs

Postby fly135 » Mon Mar 11, 2019 6:00 pm

jerome wrote:
Mon Mar 11, 2019 5:57 pm
I'm stuck on this, any idea would be greatly appreciated.
Take a look at this post....

viewtopic.php?f=13&t=3787

jerome
Posts: 37
Joined: Wed Jan 23, 2019 2:28 pm

Re: I2S Record to SDCard using FATFs

Postby jerome » Wed Mar 13, 2019 9:37 am

I found my issue I think.
On the ESP32 I was using large circular buffers, stored in external SPI RAM (128 buffers of 16KB each allocated with calloc()), sending those buffers to fwrite() were causing problems, seems like the SPI RAM access is very slow ?
I checked using only 2 buffers located into the ESP32 internal RAM (declared as global var buffers[2][16384], and I did not longer observe SDcard hanging for several hundreds of milliseconds.
Will further investigate on this, but 24/96/stereo seems to work fine now.

Can anybody confirm storing buffers in external SPI cannot work for continuously writing to SDCard at a high data rate (16KB each 25ms) ?
Thanks

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

Re: I2S Record to SDCard using FATFs

Postby ESP_Sprite » Thu Mar 14, 2019 8:55 am

FWIW, external SPI ram, when used as a ringbuffer, is theoretially limited to 40MByte/second at 80MHz or 20MByte/s at 40MHz. As you are reading and writing about 1MByte/sec (total 2MByte/sec), I don't think the SPI RAM per se is the issue... however, the core can't do anything else while it's waiting for SPI RAM. You may be better off restructuring your code so it runs on two cores; if one blocks on a SPI RAM access, the other can still do useful work.

jerome
Posts: 37
Joined: Wed Jan 23, 2019 2:28 pm

Re: I2S Record to SDCard using FATFs

Postby jerome » Thu Mar 14, 2019 3:51 pm

It seems it is related to fread/fwrite functions.
When the arguments to those functions are buffers allocated with calloc, sdcard (or FATFs) hangs.
On the other side, when using static buffers declared with buffer[16384], everything works fine, no more hangs.

Nobody else observed this issue ?

Menu config is configured as shown in the screenshot, anything that may cause such a issue ?
Thanks
spiRAM.png
spiRAM.png (62.41 KiB) Viewed 8881 times
spiRAM2.png
spiRAM2.png (27.98 KiB) Viewed 8880 times

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

Re: I2S Record to SDCard using FATFs

Postby ESP_Sprite » Fri Mar 15, 2019 2:22 pm

If you allocate buffers that large using calloc, it will also allocate them in PSRAM, meaning the data has to go through another round-trip through the SPI bus. If you allocate them statically, they normally will be allocated in internal memory, which is much faster. That may be the root (and if not, at least a confounding factor) of your problem. Note that you can specify where you want to allocate your buffers by using heap_alloc_caps().

SinanAkkoyun
Posts: 1
Joined: Tue Jun 02, 2020 2:31 pm

Re: I2S Record to SDCard using FATFs

Postby SinanAkkoyun » Tue Jun 02, 2020 5:13 pm

Hello,
can someone please provide working code? I tried for weeks recording from an I2S MEMS microphone and writing it to an SD card.

My code "works", but the output WAV is wayy to sped up, it sounds like chipmunk noises and is very quiet.
What do I do wrong? Can someone just give me their working code, outputting into a 16 bit linear PCM WAV file?
Thank you

Here are code snippets, that should give an overview over the issue, hopefully:

Code: Select all

void I2S_Init(i2s_mode_t MODE, i2s_bits_per_sample_t BPS) {
	i2s_config_t i2s_config = {
		.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),// | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN),
		.sample_rate = SAMPLE_RATE,
		.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
		.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,//I2S_CHANNEL_FMT_RIGHT_LEFT,
		.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
		.intr_alloc_flags = 0,
		.dma_buf_count = 16,
		.dma_buf_len = 60
	};
	if (MODE == I2S_MODE_RX || MODE == I2S_MODE_TX) {
		//Serial.println("using I2S_MODE");
		i2s_pin_config_t pin_config;
		pin_config.bck_io_num = PIN_I2S_BCLK;
		pin_config.ws_io_num = PIN_I2S_LRC;
		if (MODE == I2S_MODE_RX) {
			pin_config.data_out_num = I2S_PIN_NO_CHANGE;
			pin_config.data_in_num = PIN_I2S_DIN;
		} else if (MODE == I2S_MODE_TX) {
			pin_config.data_out_num = PIN_I2S_DOUT;
			pin_config.data_in_num = I2S_PIN_NO_CHANGE;
		}
		
		
		esp_err_t err;

		err = i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
		if (err != ESP_OK) {
			ESP_LOGI(TAG, "Failed installing driver: %d\n", err);
			while (true) ;
		}
		err = i2s_set_pin(I2S_NUM_0, &pin_config);
		if (err != ESP_OK) {
			ESP_LOGI(TAG, "Failed setting pin: %d\n", err);
			while (true) ;
		}
		err = i2s_set_clk(I2S_NUM_0, SAMPLE_RATE, I2S_BITS_PER_SAMPLE_32BIT, I2S_CHANNEL_MONO);
		if (err != ESP_OK) {
			ESP_LOGI(TAG, "Failed setting clk: %d\n", err);
			while (true) ;
		}
		
		ESP_LOGI(TAG, "lel");
	} else if (MODE == I2S_MODE_DAC_BUILT_IN || MODE == I2S_MODE_ADC_BUILT_IN) {
		//Serial.println("using ADC_builtin");
		i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
		// GPIO36, VP
		i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_0);  
	}
}

Code: Select all

void CreateWavHeader(char* header, int waveDataSize) {
	header[0] = 'R';
	header[1] = 'I';
	header[2] = 'F';
	header[3] = 'F';
	unsigned int fileSizeMinus8 = waveDataSize + 44 - 8;
	header[4] = (char)(fileSizeMinus8 & 0xFF);
	header[5] = (char)((fileSizeMinus8 >> 8) & 0xFF);
	header[6] = (char)((fileSizeMinus8 >> 16) & 0xFF);
	header[7] = (char)((fileSizeMinus8 >> 24) & 0xFF);
	header[8] = 'W';
	header[9] = 'A';
	header[10] = 'V';
	header[11] = 'E';
	header[12] = 'f';
	header[13] = 'm';
	header[14] = 't';
	header[15] = ' ';
	header[16] = 0x10;   // linear PCM
	header[17] = 0x00;
	header[18] = 0x00;
	header[19] = 0x00;
	header[20] = 0x01;   // linear PCM
	header[21] = 0x00;
	header[22] = 0x01;   // monoral
	header[23] = 0x00;
	header[24] = 0x44;   // sampling rate 44100
	header[25] = 0xAC;
	header[26] = 0x00;
	header[27] = 0x00;
	header[28] = 0x88;   // Byte/sec = 44100x2x1 = 88200
	header[29] = 0x58;
	header[30] = 0x01;
	header[31] = 0x00;
	header[32] = 0x02;   // 16bit monoral
	header[33] = 0x00;
	header[34] = 0x10;   // 16bit
	header[35] = 0x00;
	header[36] = 'd';
	header[37] = 'a';
	header[38] = 't';
	header[39] = 'a';
	header[40] = (char)(waveDataSize & 0xFF);
	header[41] = (char)((waveDataSize >> 8) & 0xFF);
	header[42] = (char)((waveDataSize >> 16) & 0xFF);
	header[43] = (char)((waveDataSize >> 24) & 0xFF);
}

Code: Select all

CreateWavHeader(header, waveDataSize);
	
    // Use POSIX and C standard library functions to work with files.
    // First create a file.
    ESP_LOGI(TAG, "Opening file");
    FILE* f = fopen(filename, "w");
    if (f == NULL) {
        ESP_LOGE(TAG, "Failed to open file for writing");
        return;
    }
    fwrite(header, 1, headerSize, f);
	
	I2S_Init(I2S_MODE, I2S_BITS_PER_SAMPLE_32BIT);
	
	for (int j = 0; j < waveDataSize / numPartWavData; ++j) {
		//i2s_read(I2S_NUM_0, communicationData, numCommunicationData, bytesRead, portMAX_DELAY);
		size_t bytes_read = 0;
		//while (bytes_read == 0) {
			i2s_read(I2S_NUM_0, (void*) buf, buf_len, &bytes_read, portMAX_DELAY);
			//example_disp_buf((uint32_t*) buf, 64);
		//}
		
		//ESP_LOGI(TAG, "bytes = %i", (int)bytes_read);
		
		for (int i = 0; i < buf_len / 8; ++i) {
			partWavData[2*i] = buf[8*i + 2];
			partWavData[2*i + 1] = buf[8*i + 3];
		}
		fwrite(partWavData, 1, numPartWavData, f);
		
		//fwrite(buf, 1, buf_len, f);
	}
    
	fclose(f);
    ESP_LOGI(TAG, "File written");
    
Here is the audio file generated:
https://github.com/SinanAkkoyun/tmp/raw ... SOUND8.WAV

Please help me!

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

Re: I2S Record to SDCard using FATFs

Postby ESP_Sprite » Wed Jun 03, 2020 8:13 am

Code: Select all

for (int i = 0; i < buf_len / 8; ++i) {
			partWavData[2*i] = buf[8*i + 2];
			partWavData[2*i + 1] = buf[8*i + 3];
		}
Why 8?

Who is online

Users browsing this forum: Baidu [Spider], Bing [Bot] and 97 guests