cancel
Showing results for 
Search instead for 
Did you mean: 

FATFS f_open issue when text file reaches 64kB

evanfahy
Associate

Hi all,

I'm using an STM32F413 to write to an uSD card via DMA over SDIO. I'm using the FATFS (R0.12c). 

 
Up until now, I've been writing to the SD card by firstly mounting, opening the file, write, write, write... then closing and unmounting. I'm writing to the SD 30 times at 0.5Hz an unfixed/irregular amount of data (approx 54 chars) in sequential order in a for loop that repeats 30 times. After each successful write I call: 

 

f_sync(&SDFile);​

 

 
The issue I am seeing is that over time, consistently 20.5 minutes of 0.5Hz writing, the SD_read (called by f_sync) function gives me a FR_DISK_ERR. I also see that if I mount, open, write, close, open, write, close... etc. I am seeing this issue again f_open is called. I have some error catching implemented that I unmount, mount and attempt to write on each failed f_open. The significance of the 20 minutes is that after this time has elapsed, I can see that the text file is 64kB in size.
 
I've stepped through the code and found that when BSP_SD_ReadBlocks_DMA get's called by DRESULT SD_read, it gets stuck in a timeout loop. I shortened SD_TIMEOUT, eventually it times out giving me FR_DISK_ERR.
 
Has anybody seen the an issue with this BSP_SD_ReadBlocks_DMA function when the file object becomes larger than 63.9kB? I've seen a few forums of people talking about cache management for DMA but I'm not sure how to implement this. Any tips? I can provide more code but here is what I've implemented so far:
 
Note: ENABLE_SCRATCH_BUFFER and ENABLE_SD_DMA_CACHE_MAINTENANCE not defined.
 

 

DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
  DRESULT res = RES_ERROR;
  uint32_t timeout;
#if defined(ENABLE_SCRATCH_BUFFER)
  uint8_t ret;
#endif
#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
  uint32_t alignedAddr;
#endif

  /*
  * ensure the SDCard is ready for a new operation
  */

  if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0)
  {
    return res;
  }

#if defined(ENABLE_SCRATCH_BUFFER)
  if (!((uint32_t)buff & 0x3))
  {
#endif
    if(BSP_SD_ReadBlocks_DMA((uint32_t*)buff,
                             (uint32_t) (sector),
                             count) == MSD_OK)
    {
      ReadStatus = 0;
      /* Wait that the reading process is completed or a timeout occurs */
      timeout = HAL_GetTick();
      while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
      {
      }
      /* in case of a timeout return error */
      if (ReadStatus == 0)
      {
        res = RES_ERROR;
      }
      else
      {
        ReadStatus = 0;
        timeout = HAL_GetTick();

        while((HAL_GetTick() - timeout) < SD_TIMEOUT)
        {
          if (BSP_SD_GetCardState() == SD_TRANSFER_OK)
          {
            res = RES_OK;
#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
            /*
            the SCB_InvalidateDCache_by_Addr() requires a 32-Byte aligned address,
            adjust the address and the D-Cache size to invalidate accordingly.
            */
            alignedAddr = (uint32_t)buff & ~0x1F;
            SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr));
#endif
            break;
          }
        }
      }
    }
#if defined(ENABLE_SCRATCH_BUFFER)
  }
    else
    {
      /* Slow path, fetch each sector a part and memcpy to destination buffer */
      int i;

      for (i = 0; i < count; i++) {
        ret = BSP_SD_ReadBlocks_DMA((uint32_t*)scratch, (uint32_t)sector++, 1);
        if (ret == MSD_OK) {
          /* wait until the read is successful or a timeout occurs */

          timeout = HAL_GetTick();
          while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT))
          {
          }
          if (ReadStatus == 0)
          {
            res = RES_ERROR;
            break;
          }
          ReadStatus = 0;

#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1)
          /*
          *
          * invalidate the scratch buffer before the next read to get the actual data instead of the cached one
          */
          SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, BLOCKSIZE);
#endif
          memcpy(buff, scratch, BLOCKSIZE);
          buff += BLOCKSIZE;
        }
        else
        {
          break;
        }
      }

      if ((i == count) && (ret == MSD_OK))
        res = RES_OK;
    }
#endif

  return res;
}

 

 
 
Kind regards,
Evan
1 REPLY 1
evanfahy
Associate

Just adding some additional MCU configuration settings:

 

evanfahy_0-1723654274949.png

 

 

 

/**
 * @brief SDIO Initialization Function
 * @PAram None
 * @retval None
 */
static void MX_SDIO_SD_Init(void)
{

	/* USER CODE BEGIN SDIO_Init 0 */

	/* USER CODE END SDIO_Init 0 */

	/* USER CODE BEGIN SDIO_Init 1 */
	//ToDo: Ensure hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
	/* USER CODE END SDIO_Init 1 */
	hsd.Instance = SDIO;
	hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
	hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
	hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
	hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
	hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
	hsd.Init.ClockDiv = 6;
	/* USER CODE BEGIN SDIO_Init 2 */

	/* USER CODE END SDIO_Init 2 */

}

 

 

I'm using the SDIO peripheral in 4 bit wide mode but I found that initialising as 1 bit wide works and reverting back to 4-bit works:

https://community.st.com/t5/stm32cubemx-mcus/sdcard-does-not-work-with-4bits-wide-bus/td-p/602607