cancel
Showing results for 
Search instead for 
Did you mean: 

f_write fails the 2nd time its called

cloudyrowly
Associate

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!

1 ACCEPTED SOLUTION

Accepted Solutions

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

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

View solution in original post

2 REPLIES 2

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

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

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