2024-05-19 04:15 PM
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:
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:
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!
Solved! Go to Solution.
2024-05-19 04:48 PM
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
2024-05-19 04:48 PM
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
2024-05-20 02:07 AM
It turned out to be a memory alignment issue in the filtering operations before the f_write. Thanks