背景:項目中使用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()是動态增加存儲,也就是說不是每次切換讀寫裝置都要連結一次。而是系統增加磁盤時連結,拔出磁盤時斷開連結。實作動态磁盤管理的目的。
- 經過以下修改,完美運作
- 系統識别到NOR FLASH和U盤時,FATFS_LinkDriver(),并挂載
- 以後每次插入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);
}