FAT32 f_write FR_DISK_ERR (Transmit FIFO underrun)

1、问题描述

使用FAT32 f_write 多次执行写操作时,会报FR_DISK_ERR错误,而且是刚开始写不报错,写几次后会一直报错。

设断点跟踪到HAL_SD_WriteBlocks中,在调用SDMMC_CmdWriteMultiBlock时,会报SDMMC_ERROR_TX_UNDERRUN,意思 是Transmit FIFO underrun

2、原因分析

如下图所示,SDMMC开始写操作时,首先要将数据写入FIFO,然后再将FIFO中的数据取出,通过Data path写入到SD卡。因为时钟频率比较快(最高可达20MHz),因此往FIFO里装填数据必须非常快,否则后面从FIFO取数时就会取不到数,报出Transmit FIFO underrun错误。

可能原因1:

    可能是通过APB2总线往FIFO装填数时,发生了中断,导致总线被占用或其他资源被占用,从而无法及时完成FIFO的装填,而后面从FIFO取数的频率又非常快,导致无法取到数。

可能原因2:

    SD卡写的时候出现了坏块,导致写不成功,在写失败时需要重试一下。

可能原因3:

    写速度太快,导致FIFO来不及装填,产生错误,需要降速。

3、解决办法之禁用中断

在调用f_write之前调用  __disable_irq()  接口关闭中断,写操作完成后调用  __enable_irq()接口启用中断:

__disable_irq();

if(FR_OK  ==f_write())

{ }

else

{}

__disable_irq() ;

优点:简单

缺点:在写操作期间CPU无法响应中断

4、解决办法之写重试

重写bsp_driver_sd.c文件中的BSP_SD_WriteBlocks函数是_weak 函数,可以在自己的源文件myfile.c中重写该函数,当写发生错误后,清除错误标记,再重试几次即可,在我的单片机上,一般第一次重试即可成功

uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout)

{

int i;

for (i = 1; i <= TY_SDIO_RETRY_MAX; i ++)

{

if (HAL_SD_WriteBlocks(&hsd, (uint8_t *)pData, WriteAddr, NumOfBlocks, Timeout) == HAL_OK)

{

if (i > 1)

printf("[INFO] SDIO writing succeeded: retry %d.\n", i);

return (MSD_OK); // Succeeded

}

else

{

printf("[ERROR] SDIO writing failure: retry %d, error code %#x, addr %#x, %u blocks.\n",

i, hsd.ErrorCode, WriteAddr, NumOfBlocks);

HAL_SD_Abort(&hsd); //clear error flag

}

}

return (MSD_ERROR);

}

优点:上层应用软件不需要更改,也不需要禁用中断

缺点:频率较高时(我的是30MHZ)时,经常会发生写重试

5、解决办法之降低写频率(降速)

更改sdio.c中的MX_SDIO_SD_Init函数,将hsd.Init.ClockDiv = 0改为hsd.Init.ClockDiv = 2,写频率会变成原来的1/2,在我的单片机上降频1/2之后,写错误将不再发生。

优点:非常简单;无需禁用中断;无需对上层软件进行修改。

缺点:降低了写速度

6、参考资料

1、https://blog.frankvh.com/2011/12/30/stm32f2xx-stm32f4xx-sdio-interface-part-2/

2、STM32F405/415 Reference manual

你可能感兴趣的:(FAT32 f_write FR_DISK_ERR (Transmit FIFO underrun))