1、FATFS 簡介
FatFs Module是一種完全免費開源的FAT檔案系統子產品,專門為小型的嵌入式系統而設計。
完全用标準C語言編寫,是以具有良好的硬體平台立性,隻需要簡單修改就可以移植到8051、PIC、AVR、SH、Z80、H8、ARM等系列嵌入式裝置。
支援FATl2、FATl6和FAT32,支援多個存儲媒介、有獨立的緩沖區,可以對多個檔案進行讀/寫,并特别對8位單片機和16位單片機做了優化。
2、移植準備
1)FATFS源代碼的擷取,可以到官網下載下傳:http://elm-chan.org/fsw/ff/00index_e.html 最新版本是R0.09版本;
2)解壓檔案會得到兩個檔案夾,一個是doc檔案夾,這裡是FATFS的一些使用文檔和說明,以後在檔案程式設計的時候可以檢視該文檔。
另一個是src檔案夾,裡面就是我們所要的源檔案。
3)建立一個STM32的工程,為友善調試,我們應重載printf()底層函數實作序列槽列印輸出,可以參考已經建立好的printf()列印輸出工程。
3、移植
1)在已經建立好的工程目錄User檔案夾下建立兩個檔案夾,FATFS_V0.09和SPI_SD_Card,FATFS_V0.09用于存放FATFS源檔案,SPI_SD_Card用于存放SPI的驅動檔案。
2)将ff.c添加到工程檔案夾中,并建立diskio.c檔案,在diskio.c檔案中實作五個函數:
DSTATUS disk_initialize (BYTE);//SD卡的初始化
DSTATUS disk_status (BYTE);//擷取SD卡的狀态,這裡可以不用管
DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE);//從SD卡讀取資料
DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);//将資料寫入SD卡,若該檔案系統為隻讀檔案系統則不用實作該函數
DRESULT disk_ioctl (BYTE, BYTE, void*);//擷取SD卡檔案系統相關資訊
3)函數實作
FATFS初始化函數:
/* Physical drive nmuber (0..) */
DSTATUS disk_initialize ( BYTE drv )
{
switch (drv)
{
case 0 :
return RES_OK;
case 1 :
return RES_OK;
case 2 :
return RES_OK;
case 3 :
return RES_OK;
default:
return STA_NOINIT;
}
}
FATFS狀态擷取函數:
/* Physical drive nmuber (0..) */
DSTATUS disk_status ( BYTE drv )
{
switch (drv)
{
case 0 :
return RES_OK;
case 1 :
return RES_OK;
case 2 :
return RES_OK;
default:
return STA_NOINIT;
}
}
FATFS底層讀資料函數:
DRESULT disk_read (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to read (1..255) */
)
{
if( !count )
{
return RES_PARERR; /* count不能等于0,否則傳回參數錯誤 */
}
switch (drv)
{
case 0:
if(count==1) /* 1個sector的讀操作 */
{
return RES_OK;
}
else /* 多個sector的讀操作 */
{
return RES_OK;
}
case 1:
if(count==1) /* 1個sector的讀操作 */
{
return RES_OK;
}
else /* 多個sector的讀操作 */
{
return RES_OK;
}
default:
return RES_ERROR;
}
}
FATFS底層寫資料函數:
DRESULT disk_write (
BYTE drv, /* Physical drive nmuber (0..) */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to write (1..255) */
)
{
if( !count )
{
return RES_PARERR; /* count不能等于0,否則傳回參數錯誤 */
}
switch (drv)
{
case 0:
if(count==1) /* 1個sector的寫操作 */
{
return RES_OK;
}
else /* 多個sector的寫操作 */
{
return RES_OK;
}
case 1:
if(count==1) /* 1個sector的寫操作 */
{
return RES_OK;
}
else /* 多個sector的寫操作 */
{
return RES_OK;
}
default:return RES_ERROR;
}
}
FATFS磁盤控制函數:
DRESULT disk_ioctl (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE ctrl, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
if (drv==0)
{
switch (ctrl)
{
case CTRL_SYNC :
return RES_OK;
case GET_SECTOR_COUNT :
return RES_OK;
case GET_BLOCK_SIZE :
return RES_OK;
case CTRL_POWER :
break;
case CTRL_LOCK :
break;
case CTRL_EJECT :
break;
/* MMC/SDC command */
case MMC_GET_TYPE :
break;
case MMC_GET_CSD :
break;
case MMC_GET_CID :
break;
case MMC_GET_OCR :
break;
case MMC_GET_SDSTAT :
break;
}
}else if(drv==1){
switch (ctrl)
{
case CTRL_SYNC :
return RES_OK;
case GET_SECTOR_COUNT :
return RES_OK;
case GET_SECTOR_SIZE :
return RES_OK;
case GET_BLOCK_SIZE :
return RES_OK;
case CTRL_POWER :
break;
case CTRL_LOCK :
break;
case CTRL_EJECT :
break;
/* MMC/SDC command */
case MMC_GET_TYPE :
break;
case MMC_GET_CSD :
break;
case MMC_GET_CID :
break;
case MMC_GET_OCR :
break;
case MMC_GET_SDSTAT :
break;
}
}
else{
return RES_PARERR;
}
return RES_PARERR;
}
以上函數都隻是實作一個架構,并沒有做實際的事情,下一步就需要把操作SD卡的程式填充在這個架構裡面。
4)實作 disk_initialize() 函數
該函數在挂載檔案系統的時候會被調用,主要是實作讀寫SD卡前對SD卡進行初始化,根據SD卡的傳輸協定,我們按照如下步驟初始化SD卡:
a、判斷SD卡是否插入,可以通過檢查SD卡卡座的CD腳電平進行判斷,一般插入卡後該引腳會變成低電平;
b、稍微延時一段時間後發送至少74個時鐘給SD卡;
c、發送CMD0指令給SD卡,直到SD卡傳回0x01為止,這裡可以循環多次發送;
程式如下:
/* Start send CMD0 till return 0x01 means in IDLE state */
for(retry=0; retry<0xFFF; retry++)
{
r1 = MSD0_send_command(CMD0, 0, 0x95);
if(r1 == 0x01)
{
retry = 0;
break;
}
}
d、發送CMD8擷取卡的類型,不同類型的卡其初始化方式有所不同;
e、根據卡的類型對卡進行初始化。具體初始化方式可以參考附件程式;
注:在初始化SD卡之前應該初始化SPI接口和相關的管腳。
實作後的程式如下:
DSTATUS disk_initialize (
BYTE drv /* Physical drive nmuber (0..) */
)
{
int Status;
switch (drv)
{
case 0 :
Status = MSD0_Init();
if(Status==0){
return RES_OK;
}else{
return STA_NOINIT;
}
case 1 :
return RES_OK;
case 2 :
return RES_OK;
case 3 :
return RES_OK;
default:
return STA_NOINIT;
}
}
MSD0_Init()函數在SPI_MSD0_Driver.c檔案中實作。
5)實作disk_read()函數
該函數是讀取SD卡扇區資料的函數,根據SD卡資料傳輸協定可知有讀取單扇區和讀取多扇區兩種操作模式,為提高讀檔案的速度應該實作讀取多扇區函數。
實作後的程式如下:
DRESULT disk_read (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to read (1..255) */
)
{
int Status;
if( !count )
{
return RES_PARERR; /* count不能等于0,否則傳回參數錯誤 */
}
switch (drv)
{
case 0:
if(count==1) /* 1個sector的讀操作 */
{
Status = MSD0_ReadSingleBlock( sector ,buff );
if(Status == 0){
return RES_OK;
}else{
return RES_ERROR;
}
}
else /* 多個sector的讀操作 */
{
Status = MSD0_ReadMultiBlock( sector , buff ,count);
if(Status == 0){
return RES_OK;
}else{
return RES_ERROR;
}
}
case 1:
if(count==1) /* 1個sector的讀操作 */
{
return RES_OK;
}
else /* 多個sector的讀操作 */
{
return RES_OK;
}
default:
return RES_ERROR;
}
}
MSD0_ReadSingleBlock()和MSD0_ReadMultiBlock()函數都是SD卡操作的底層函數,我們在SPI_MSD0_Driver.c檔案中實作。
6)實作disk_write()函數
該函數主要實作對SD卡進行寫資料操作,和讀資料操作一樣也分單塊寫和多塊寫,建議實作多塊寫的方式,這樣可以提高寫資料速度。
實作後的程式如下:
DRESULT disk_write (
BYTE drv, /* Physical drive nmuber (0..) */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to write (1..255) */
)
{
int Status;
if( !count )
{
return RES_PARERR; /* count不能等于0,否則傳回參數錯誤 */
}
switch (drv)
{
case 0:
if(count==1) /* 1個sector的寫操作 */
{
Status = MSD0_WriteSingleBlock( sector , (uint8_t *)(&buff[0]) );
if(Status == 0){
return RES_OK;
}else{
return RES_ERROR;
}
}
else /* 多個sector的寫操作 */
{
Status = MSD0_WriteMultiBlock( sector , (uint8_t *)(&buff[0]) , count );
if(Status == 0){
return RES_OK;
}else{
return RES_ERROR;
}
}
case 1:
if(count==1) /* 1個sector的寫操作 */
{
return RES_OK;
}
else /* 多個sector的寫操作 */
{
return RES_OK;
}
default:return RES_ERROR;
}
}
MSD0_WriteSingleBlock()和MSD0_WriteMultiBlock()函數都是SD卡操作的底層函數,我們在SPI_MSD0_Driver.c檔案中實作。
7)實作disk_ioctl()函數
該函數在磁盤格式化、擷取檔案系統資訊等操作時會被調用。
實作後的程式如下:
DRESULT disk_ioctl (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE ctrl, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
if (drv==0)
{
MSD0_GetCardInfo(&SD0_CardInfo);
switch (ctrl)
{
case CTRL_SYNC :
return RES_OK;
case GET_SECTOR_COUNT :
*(DWORD*)buff = SD0_CardInfo.Capacity/SD0_CardInfo.BlockSize;
return RES_OK;
case GET_BLOCK_SIZE :
*(WORD*)buff = SD0_CardInfo.BlockSize;
return RES_OK;
case CTRL_POWER :
break;
case CTRL_LOCK :
break;
case CTRL_EJECT :
break;
/* MMC/SDC command */
case MMC_GET_TYPE :
break;
case MMC_GET_CSD :
break;
case MMC_GET_CID :
break;
case MMC_GET_OCR :
break;
case MMC_GET_SDSTAT :
break;
}
}else if(drv==1){
switch (ctrl)
{
case CTRL_SYNC :
return RES_OK;
case GET_SECTOR_COUNT :
return RES_OK;
case GET_SECTOR_SIZE :
return RES_OK;
case GET_BLOCK_SIZE :
return RES_OK;
case CTRL_POWER :
break;
case CTRL_LOCK :
break;
case CTRL_EJECT :
break;
/* MMC/SDC command */
case MMC_GET_TYPE :
break;
case MMC_GET_CSD :
break;
case MMC_GET_CID :
break;
case MMC_GET_OCR :
break;
case MMC_GET_SDSTAT :
break;
}
}
else{
return RES_PARERR;
}
return RES_PARERR;
}
MSD0_GetCardInfo()函數也在SPI_MSD0_Driver.c檔案中實作,其中SD0_CardInfo為PMSD_CARDINFO類型的全局變量,它在SPI_MSD0_Driver.h檔案中被定義。
8)到此diskio.c這個檔案中的所有函數就已經實作,下一步就是實作SPI_MSD0_Driver.c檔案中的相關函數。
4、檔案系統測試
1)測試寫檔案
測試代碼如下:
printf("write file test......\n\r");
res = f_open(&fdst, "0:/test.txt", FA_CREATE_ALWAYS | FA_WRITE);
if(res != FR_OK){
printf("open file error : %d\n\r",res);
}else{
res = f_write(&fdst, textFileBuffer, sizeof(textFileBuffer), &bw); /* Write it to the dst file */
if(res == FR_OK){
printf("write data ok! %d\n\r",bw);
}else{
printf("write data error : %d\n\r",res);
}
/*close file */
f_close(&fdst);
}
注意:成功打開檔案後一定要調用f_close()函數,否則資料無法寫入SD卡中。
2)測試讀檔案
printf("read file test......\n\r");
res = f_open(&fsrc, "0:/test.txt", FA_OPEN_EXISTING | FA_READ);
if(res != FR_OK){
printf("open file error : %d\n\r",res);
}else{
res = f_read(&fsrc, buffer, sizeof(textFileBuffer), &br); /* Read a chunk of src file */
if(res==FR_OK){
printf("read data num : %d\n\r",br);
printf("%s\n\r",buffer);
}else{
printf("read file error : %d\n\r",res);
}
/*close file */
f_close(&fsrc);
}
refer:
http://bbs.21ic.com/icview-883729-1-1.html