当前位置: 首页 > 工具软件 > check_diskio > 使用案例 >

Fatfs文件系统二次f_open返回值为FR_DISK_ERR的问题

谭高峯
2023-12-01

背景:项目中使用ST的fatfs驱动,驱动U盘和NOR FLASH
目的:系统往NOR FLASH写日志,插入U盘后导出日志到U盘中
问题现象:每次进去拷贝函数bCopyOneLogFile(),打开pacCurOutFile文件对象时都会报FR_DISK_ERR错误。
分析过程:

  • 经查阅,FR_DISK_ERR是磁盘IO读写错误。故给USBH_read,USBH_write 返回错误处都加断点,进行调试。可是出现FR_DISK_ERR时未进USB读写断点。

  • 很明显不是磁盘IO读写错误,若是磁盘IO读写错误。为什么第一次调用bCopyOneLogFile()时未出现?又为何没进USBH_read,USBH_write 返回错误断点?

  • 遇事不决查手册

    dm00105259-developing-applications-on-stm32cube-with-fatfs-stmicroelectronics-zh.pdf

  • 手册中说FATFS_LinkDriver()是动态增加存储,也就是说不是每次切换读写设备都要链接一次。而是系统增加磁盘时链接,拔出磁盘时断开链接。实现动态磁盘管理的目的。

  • 经过以下修改,完美运行

    1. 系统识别到NOR FLASH和U盘时,FATFS_LinkDriver(),并挂载
    2. 以后每次插入U盘 ,FATFS_LinkDriver(),并挂载。U盘拔出时FATFS_UnLinkDriver(acUSBPath);并且取消挂载

USB磁盘IO读写代码

/**
 * @brief  Reads Sector(s)
 * @param  lun : lun id
 * @param  *buff: Data buffer to store read data
 * @param  sector: Sector address (LBA)
 * @param  count: Number of sectors to read (1..128)
 * @retval DRESULT: Operation result
 */
DRESULT USBH_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
  BYTE status = USBH_OK;
  usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;

  if (!count)
  {
    return RES_PARERR;
  }

  if (Stat & STA_NOINIT)
  {
    return RES_NOTRDY;
  }

  if (udev->host.connect_status)
  {
    do
    {
      status = usbh_msc_read(&usb_host_msc, lun, sector, buff, count);

      if (!udev->host.connect_status)
      {
        return RES_ERROR;
      }
    } while (status == USBH_BUSY);
  }

  if (status == USBH_OK)
  {
    return RES_OK;
  }

  return RES_ERROR;
}
/**
 * @brief  Writes Sector(s)
 * @param  lun : lun id
 * @param  *buff: Data to be written
 * @param  sector: Sector address (LBA)
 * @param  count: Number of sectors to write (1..128)
 * @retval DRESULT: Operation result
 */
#if _USE_WRITE == 1
DRESULT USBH_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
{
  BYTE status = USBH_OK;
  usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;

  if (!count)
  {
    return RES_PARERR;
  }

  if (Stat & STA_NOINIT)
  {
    return RES_NOTRDY;
  }

  if (Stat & STA_PROTECT)
  {
    return RES_WRPRT;
  }

  if (udev->host.connect_status)
  {
    do
    {
      status = usbh_msc_write(&usb_host_msc, lun, sector, (BYTE *)buff, count);

      if (!udev->host.connect_status)
      {
        return RES_ERROR;
      }
    } while (status == USBH_BUSY);
  }

  if (status == USBH_OK)
  {
    return RES_OK;
  }

  return RES_ERROR;
}
#endif /* _USE_WRITE == 1 */

修改前USB拷贝部分

BOOLEAN LOG_bCopy2USB(void)
{
   UNSIGNED32 dwLogSize = 0;  /**< 日志大小*/
   UINT dwReadedByteNum = 0;  /**< 实际读取字节*/
   UINT dwWrittenByteNum = 0; /**< 实际写入字节*/
   FRESULT eRet = FR_OK;
   UNSIGNED8 i = 0;

   for (i = 0; i < LOG_FILE_NUM; i++)
   {
      /**< 打开2个拷贝文件*/
      if (FATFS_LinkDriver(&SNORDISK_Driver, acFlashPath) == 0)
      {
         eRet = f_open(&sLogFile, (TCHAR const *)&acLogFilePath[i][0], FA_READ);
         if (FR_OK != eRet)
         {
            return FALSE;
         }
         dwLogSize = f_size(&sLogFile);
         dwLogSize = dwLogSize;
      }
      else
      {
         return FALSE;
      }
      if (FATFS_LinkDriver(&USBH_Driver, acUSBPath) == 0)
      {
         eRet = f_open(&sOutFile, (TCHAR const *)&acOutFilePath[i][0], FA_OPEN_ALWAYS | FA_WRITE);
         if (FR_OK != eRet)
         {
            /**< 打开失败取消U盘挂载*/
            eRet = f_mount(NULL, acUSBPath, 0);
            return FALSE;
         }
      }
      else
      {
         /**< 链接失败取消U盘挂载*/
         eRet = f_mount(NULL, acUSBPath, 0);
         return FALSE;
      }
      /**< 进行拷贝*/
      while (FR_OK == eRet)
      {
         if (FATFS_LinkDriver(&SNORDISK_Driver, acFlashPath) == 0)
         {
            eRet = f_read(&sLogFile, acCopyBuff, (UINT)sizeof(acCopyBuff), &dwReadedByteNum);
            if (FR_OK != eRet || dwReadedByteNum == 0)
            {
               break;
            }
         }
         else
         {
            break;
         }

         if (FATFS_LinkDriver(&USBH_Driver, acUSBPath) == 0)
         {
            eRet = f_write(&sOutFile, acCopyBuff, dwReadedByteNum, (void *)&dwWrittenByteNum);
            if (FR_OK != eRet || dwWrittenByteNum < dwReadedByteNum)
            {
               break;
            }
         }
         else
         {
            break;
         }
      }
      /**< 关闭文件*/
      eRet = f_close(&sOutFile);
      eRet = f_close(&sLogFile);
   }
   /**< 拷贝完成取消U盘挂载*/
   eRet = f_mount(NULL, acUSBPath, 0);

   return eRet == FR_OK ? TRUE : FALSE;
}

修改后USB拷贝部分

/**
 * @brief 拷贝一个日志文件
 *
 * @param [in] void
 * @return 是否成功
 * @retval
 * @retval
 *
 * @see
 * @note
 * @warning
 */
static BOOLEAN bCopyOneLogFile(void)
{
   UINT dwReadedByteNum = 0;  /**< 实际读取字节*/
   UINT dwWrittenByteNum = 0; /**< 实际写入字节*/
   FRESULT eRet = FR_OK;      /**< 便于调试,不要优化*/
   BOOLEAN bRet = FALSE;

   /**< 打开2个拷贝文件*/

   eRet = f_open(&sLogFile, (TCHAR const *)pacCurLogFile, FA_OPEN_ALWAYS | FA_READ);
   if (FR_OK != eRet)
   {
      return bRet;
   }

   eRet = f_open(&sOutFile, (TCHAR const *)pacCurOutFile, FA_OPEN_ALWAYS | FA_WRITE);
   if (FR_OK != eRet)
   {
      eRet = f_close(&sLogFile);
      return bRet;
   }

   /**< 进行拷贝*/
   while (FR_OK == eRet)
   {

      eRet = f_read(&sLogFile, acCopyBuff, (UINT)sizeof(acCopyBuff), &dwReadedByteNum);
      if (FR_OK != eRet)
      {
         break;
      }
      else if (FR_OK == eRet && dwReadedByteNum == 0)
      {
         // 说明拷贝完成了
         bRet = TRUE;
         break;
      }

      eRet = f_write(&sOutFile, acCopyBuff, dwReadedByteNum, (void *)&dwWrittenByteNum);
      eRet = f_sync(&sLogFile);
      if (FR_OK != eRet)
      {
         break;
      }
      else if (FR_OK == eRet && dwWrittenByteNum < dwReadedByteNum)
      {
         // 说明文件写不下了
         bRet = TRUE;
         break;
      }
   }
   /**< 关闭文件*/

   eRet = f_close(&sLogFile);
   eRet = f_close(&sOutFile);
   return bRet;
}
/**
 * @brief 导出日志到U盘
 *
 * @param [in] void
 * @return 拷贝日志结果
 * @retval TRUE:成功
 * @retval FALSE:失败
 *
 * @see
 * @note
 * @warning
 */
BOOLEAN LOG_bCopy2USB(void)
{
   BOOLEAN bRet = FALSE;
   FRESULT eRet = FR_OK; /**< 便于调试,不要优化*/

   bRet = bCopyOneLogFile();
   if (bRet)
   {
      vSwitchCurLogFile(); // 切换日志对象
      // vTaskDelay(pdMS_TO_TICKS(10));
      bRet = bCopyOneLogFile();
      vSwitchCurLogFile(); // 还原日志对象
   }
   /**< 无论拷贝是否完成取消U盘挂载*/
   eRet = f_mount(NULL, acUSBPath, 1);
   FATFS_UnLinkDriver(acUSBPath);

   return bRet && (FR_OK == eRet);
}
 类似资料: