Skip to main content
Explorer
May 19, 2024
Solved

f_write fails the 2nd time its called

  • May 19, 2024
  • 1 reply
  • 1512 views

Board: STM32F411RE

Greetings,

I am trying to make a program where the mcu read an audio file on the SD card, apply a sound filter on it, and write the output to a different file. The program will split the audio file into small chunks and compute them one by one:

  1. Open the source file to read
  2. Open a new file to write the result
  3. Read a chunk of the source file
  4. Apply filter onto the chunk, the output is written into a distinct buffer (array)
  5. Write the buffer into the opened file
  6. Repeat 3-5 until the whole file is done (total chunk size = source file size)
  7. Close source file
  8. Close new file

I am facing a problem where f_write will trigger a HardFault interrupt handler when it is called a 2nd time. (Code is attached below.

The audio is in the format .wav, so I have adapted tinywav library by mhroth to work over SPI for fatfs:

  • For reading, I'm using: f_open(..., ..., FA_READ)
  • For writing, I'm using: f_open(..., ..., FA_WRITE | FA_CREATE_ALWAYS), I tried to add FA_OPEN_APPEND as well but result is the same.

 

I have tried making the buffer and FIL variable "static" in the hope that a pre-located and fixed memory will prevent the variables from being overwritten. I have also discovered f_sync() which "flushes the cached information of a writing file.", still doesn't work. I suspect it has something to do with memory/buffer/stack when writing, but from where it terminates, it seems more like a disk initialization problem because the disk_status is the last function that is executed. It is this part in the function validate() in ff.c which calls the function of disk_status of diskio.c and the interrupt handler gets called:

 

#else
		if (!(disk_status(obj->fs->drv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */
			res = FR_OK;
		}
#endif

 

 

The main function in main.c (the problem is on line 90), codes that are unrelated to file i/o have been replaced with "....":

 

void binaural_compute(int degrees) {
	// Turn on GREEN LED to indicate SD card is busy => do not unmount
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);

	// setup for audio file comprehension and format
	static TinyWav tw; // address to store read audio file
	FIL tw_fil;
	tw.fp = &tw_fil;
	int sample_rate;

	// Mount SD card
	static FATFS FatFs;
	FRESULT f_res;
	f_res = f_mount(&FatFs, "", 1); //1=mount now
	if (f_res != FR_OK) {
		print_f("f_mount error (%i)\r\n", f_res);
	 while(1);
	}

	//f_res = f_open(&tw.f, audio_file, FA_READ);
	//if (f_res != FR_OK) {
	//	print_f("f_mount error (%i)\r\n", f_res);
	//}

	....

	// build output file path
	char output_path[64] = "";
	strcat(output_path, output_folder);
	strcat(output_path, char_degrees);
	strcat(output_path, "_");
	strcat(output_path, "degrees_");
	strcat(output_path, audio_file);

	FIL f_file;
	UINT byteCheck;
	....

	// load audio file
	f_res = tinywav_open_read(&tw, audio_file, TW_SPLIT);

	int data_size = tw.h.Subchunk2Size / 8; // get # of elements of data block per channel
																				 // 8 = 4 * 2 (4 bytes/float * 2 channels
	sample_rate = tw.h.SampleRate; // get audio's sample rate
	int data_left = data_size;
	int iteration = ceil((data_size / 2)/CONVOLVE_BLOCK_SIZE);

	// prepare output file
	static TinyWav tw_out;
	FIL tw_out_fil;
	tw_out.fp = &tw_out_fil;
	tinywav_open_write(&tw_out,
	 NUM_CHANNELS,
	 sample_rate,
	 TW_FLOAT32, // the output samples will be 32-bit floats. TW_INT16 is also supported
	 TW_SPLIT, // the samples to be written will be provided by an array of pointer
								 // that points to different sub-arrays: [[L,L,L,L], [R,R,R,R]]
	 output_path // the output path
	);

	....

	// For audio write
	// array to store converted binaural sample
	static float sample_out[(NUM_CHANNELS * CONVOLVE_BLOCK_SIZE)];
	float* sample_out_ptrs[NUM_CHANNELS];
 ....

	// generate pointers to different channel section for both read and write
	for (int j = 0; j < NUM_CHANNELS; j++) {
		....
		sample_out_ptrs[j] = sample_out + (j * CONVOLVE_BLOCK_SIZE);
		....
	}

	for (int i = 0; i < iteration; i++) {
		int input_seq_length = data_left < CONVOLVE_BLOCK_SIZE ? data_left : CONVOLVE_BLOCK_SIZE;

		if(i == 0) { // 1st iteration (edge case) as there are no data to prepend
			tinywav_read_f(&tw, sample_ptrs, input_seq_length);
			....

		} else { // i > 0 => prepend data first and then convolve so convolution is continuous
			....

			tinywav_read_f(&tw, sample_ptrs_offset, input_seq_length);
			....

 }
 tinywav_write_f(&tw_out, sample_out_ptrs, input_seq_length);
	}
	tinywav_close_write(&tw_out);
	tinywav_close_read(&tw);
	// Turn off GREEN LED to indicate SD card is NOT busy
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);

	// Unmount SD
	f_mount(NULL, "", 0);
}

 

implementation of tinywav_write_f() in tinywav.c (line 31 is the problem):

 

​int tinywav_write_f(TinyWav *tw, void *f, int len) {
 
 if (tw == NULL || f == NULL || len < 0 || !tinywav_isOpen(tw)) {
 return -1;
 }
 
 FRESULT fresult;
 // 1. Bring samples into interleaved format
 // 2. write to disk
 
 switch (tw->sampFmt) {
 case TW_INT16: {
 .....
 }
 case TW_FLOAT32: {
 .....
 case TW_SPLIT: {
 const float **const x = (const float **const) f;
 for (int i = 0, k = 0; i < len; ++i) {
 for (int j = 0; j < tw->numChannels; ++j) {
 z[k++] = x[j][i];
 }
 }
 break;
 }
 default: return 0;
 }

 //size_t samples_written = fwrite(z, sizeof(float), tw->numChannels*len, tw->f);
 UINT samples_written = 0;
 fresult = f_write(tw->fp, z, (sizeof(float)*tw->numChannels*len), &samples_written);
 if (fresult != FR_OK) {
 return -1;
 }
 f_sync(tw->fp); // flush cached information of a writing file, minimize writing error
 size_t frames_written = samples_written / tw->numChannels;
 tw->totalFramesReadWritten += frames_written;
 return (int) frames_written;
 }
 default: return 0;
 }
}

 

This is my very first post, apologise if I missed any info, I will provide it should it be needed. Any help or guidance is appreciated. Thanks in advance!

    This topic has been closed for replies.
    Best answer by Tesla DeLorean

    Not sure which STM32 you're using.

    Top-level code probably not going to tell you much. Could be an alignment issue. Does behaviour change with optimization and without?

    Watch out for the scope here

    	FIL tw_out_fil;
    	tw_out.fp = &tw_out_fil;

    Have Hard Fault Handler provide some more detail.

    https://github.com/cturvey/RandomNinjaChef/blob/main/KeilHardFault.c

    1 reply

    Graduate II
    May 19, 2024

    Not sure which STM32 you're using.

    Top-level code probably not going to tell you much. Could be an alignment issue. Does behaviour change with optimization and without?

    Watch out for the scope here

    	FIL tw_out_fil;
    	tw_out.fp = &tw_out_fil;

    Have Hard Fault Handler provide some more detail.

    https://github.com/cturvey/RandomNinjaChef/blob/main/KeilHardFault.c

    Explorer
    May 20, 2024

    It turned out to be a memory alignment issue in the filtering operations before the f_write. Thanks