天天看點

Fatfs檔案系統二次f_open傳回值為FR_DISK_ERR的問題

背景:項目中使用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);
}
           

繼續閱讀