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); }
View more

 

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; } }
View more

 

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