cancel
Showing results for 
Search instead for 
Did you mean: 

SD Card sector read delay

aa.1
Associate

I am making an stm32 driven music player that reads from a text file containing binary PCM data. (not the right way to do it i know but it gets the job done) I did a test by doing an f_read 1 byte at a time at the sample frequency (44100) to see if the performance is adequate. It works fine most of the time but whenever i read the 512th byte (going into a new sector) the read time jumps from sub-milliseconds to roughly 2 ms which introduces a noticable delay in the audio (chrunching the number results in a 2 ms delay nearly every 11 ms). This is the case whether i read 1, 512 or 1024 bytes and occurs every time i read across sectors. I wanted to know if this is a feature of the sd specification or is it something Im doing wrong. I tracked the cause of the delay to HAL_SD_ReadBlocks function (as expected) if it is of any help. Is there a method of mitigating the 2 ms delay altogether or should I just reduce its occurence by reading as much data as i can and hope that it doesn't get heard? I am using this board fyi: https://github.com/mcauser/BLACK_F407ZG

My main.c (stripped to make it fit):

	FATFS myFATFS;
	FIL myFILE;
	UINT testByte;
	uint8_t buffer[512];
	const double sampling_freq = 44100;
	uint32_t sampling_period_us = 0;
	uint64_t system_clock = 0;
	uint64_t micros_start = 0;
	double m_total = 0;
	char myPATH[] = "1khz441K.TXT\0";
 
	uint64_t microseconds(void){
	return system_clock*1000 + ((SysTick->LOAD - SysTick->VAL)/168); 
	}
int main(void)
{
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SDIO_SD_Init();
  MX_USART1_UART_Init();
  MX_FATFS_Init();
  MX_DAC_Init();
  MX_RTC_Init();
  /* USER CODE BEGIN 2 */
	SysTick_Config(SystemCoreClock/1000);//set systick up
	HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
	HAL_DAC_Start(&hdac, DAC_CHANNEL_2);
	sampling_period_us = (uint32_t)((1/sampling_freq)*1000000);
	if(f_mount(&myFATFS, SDPath, 1)==FR_OK){
		f_open(&myFILE, myPATH, FA_READ | FA_OPEN_ALWAYS);
	}
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
 
  /* USER CODE BEGIN WHILE */
  while (1)
  {
 
    /* USER CODE END WHILE */
//		char myPATH[] = "WRITE1.TXT\0";
//		f_open(&myFILE, myPATH, FA_WRITE | FA_CREATE_ALWAYS);
//		if(f_open(&myFILE, myPATH, FA_WRITE | FA_CREATE_ALWAYS)==FR_OK){
//			micros_start = microseconds();
//			write_result = f_write(&myFILE, buffer, sizeof(buffer), &testByte);
//			m_total = ((double)(microseconds()-micros_start))/1000;
//			f_close(&myFILE);
//			HAL_Delay(1000);
//		}
		micros_start = microseconds();
		f_read(&myFILE, buffer, 1, &testByte);
		HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_8B_R, buffer[0]);
		while((microseconds() - micros_start) < sampling_period_us);
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

Last note: My main objective is to use an sd card as a black box for a drone project i have been working on and this is more of a getting used to the sd peripheral project for me. The drone's loop runs at 500hz so a delay of 2ms is absolutely unacceptable for my taste (Thats why i don't see uart as a propper solution). I tried out the write performance in some of the commented out code up above. Unfortunately same results. Thanks in advance to anyone who is willing to help.

2 REPLIES 2

This is not how to approach the problem.

Reading 1 byte for f_read() has a lot of overhead.

Reading one sector is also the slowest method possible due to the inherent command-response behavior of a slow MCU in the card itself.

What you need to do in the loop is read much larger blocks, that constitute half a buffer, using a TIM and DMA to feed the DAC at the appropriate pacing. You fill in inactive half of the buffer based on the HT and TC interrupts from the DMA.

You can likely read 16 sectors in a similar length of time.

You need to move beyond the simple linear programming model so you can do useful work concurrently as different tasks wait for slower​ hardware to deliver data.

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

Thank you for your input. Ok so realistically the only way to get rid of that occasional 2 ms delay would be to use a dma for sdio. If i use the blocking mode of the sdio peripheral i need any and all f_write functions to take less than 100 us. Can writing larger chunks of data provide such a time saving? If not I suppose dma for sd would solve the issue?