一、環(huán)境介紹
STM32程序開發(fā)IDE: keil5
STM32程序風(fēng)格:? 采用寄存器方式開發(fā),注釋齊全,執(zhí)行效率高,方便移植
硬件包含:??一塊STM32F103ZET6系統(tǒng)板、一個(gè)SPI接口的SD卡卡槽模塊、一張SD卡
工程完整源碼下載地址:??https://download.csdn.net/download/xiaolong1126626497/19687693
這篇文章主要演示FATFS文件系統(tǒng)如何移植到自己的工程,并完成文件的讀寫。
因?yàn)镾D卡采用的是SPI模擬時(shí)序,所以,其他單片機(jī)一樣可以照著移植,代碼都可以復(fù)制粘貼的。
二、FATFS文件系統(tǒng)介紹
2.1 FATFS簡(jiǎn)介
FatFs 是一種完全免費(fèi)開源的 FAT 文件系統(tǒng)模塊,專門為小型的嵌入式系統(tǒng)而設(shè)計(jì)。它完全用標(biāo)準(zhǔn)C 語(yǔ)言編寫,所以具有良好的硬件平臺(tái)獨(dú)立性,可以移植到 8051、 PIC、 AVR、 SH、 Z80、 H8、 ARM 等系列單片機(jī)上而只需做簡(jiǎn)單的修改。它支持 FATl2、 FATl6 和 FAT32,支持多個(gè)存儲(chǔ)媒介;有獨(dú)立的緩沖區(qū),可以對(duì)多個(gè)文件進(jìn)行讀/寫,并特別對(duì) 8 位單片機(jī)和 16 位單片機(jī)做了優(yōu)化。
2.2 特點(diǎn)
- ?Windows兼容的FAT文件系統(tǒng)
- 不依賴于平臺(tái),易于移植
- 代碼和工作區(qū)占用空間非常小
- ?多種配置選項(xiàng)
- 多卷(物理驅(qū)動(dòng)器和分區(qū))
- ?多ANSI/OEM代碼頁(yè),包括DBCS
- 在ANSI/OEM或Unicode中長(zhǎng)文件名的支持
- RTOS的支持
- 多扇區(qū)大小的支持
- 只讀,最少API,I/O緩沖區(qū)等等
2.3 移植性
fatfs模塊是ANSI C(C89)編寫的。 沒有平臺(tái)的依賴, 編譯器只要符合ANSI C標(biāo)準(zhǔn)就可以編譯。
fatf模塊假設(shè)大小的字符/短/長(zhǎng)8/16/32位和int是16或32位。 這些數(shù)據(jù)類型在integer.h文件中定義。這些數(shù)據(jù)類型在大多數(shù)的編譯器中定義都符合要求。 如果現(xiàn)有的定義與編譯器有任何沖突發(fā)生時(shí),需要自己解決。
2.4 源碼下載
下載地址:http://elm-chan.org/fsw/ff/00index_e.html
FATFS有兩個(gè)版本,一個(gè)大版本,一個(gè)小版本。小版本主要用于8位機(jī)(內(nèi)存小)使用。
下載圖:
2.5 FATFS源碼文件介紹
將下載的源碼解壓后可以得到兩個(gè)文件夾: doc 和 src。 doc 里面主要是對(duì) FATFS 的介紹(離線文檔—英文和日文),而 src 里面才是我們需要的源碼。
其中,與平臺(tái)無(wú)關(guān)的是:
ffconf.h???? FATFS配置文件
ff.h??????? 應(yīng)用層頭文件 ff.c??????? 應(yīng)用層源文件 diskio.h??? 硬件層頭文件 interger.h?? 數(shù)據(jù)類型定義頭文件 option???? 可選的外部功能(比如支持中文等) |
與平臺(tái)相關(guān)的代碼:
diskio.c???? 底層接口文件(需要用戶提供)
FATFS 模塊在移植的時(shí)候,我們一般只需要修改 2 個(gè)文件,即 ffconf.h 和 diskio.c。
FATFS模塊的所有配置項(xiàng)都是存放在 ffconf.h 里面,我們可以通過配置里面的一些選項(xiàng),來(lái)滿足自己的需求。
FATFS最頂層是應(yīng)用層,使用者無(wú)需理會(huì) FATFS 的內(nèi)部結(jié)構(gòu)和復(fù)雜的 FAT 協(xié)議,只需要調(diào)用FATFS 模塊提供給用戶的一系列應(yīng)用接口函數(shù),如 f_open, f_read, f_write 和 f_close 等,就可以像在 PC 上讀/寫文件那樣簡(jiǎn)單。
中間層 FATFS 模塊, 實(shí)現(xiàn)了 FAT 文件讀/寫協(xié)議。 FATFS 模塊提供的是 ff.c 和 ff.h。除非有必要,使用者一般不用修改,使用時(shí)將頭文件直接包含進(jìn)去即可。
需要我們編寫移植代碼的是 FATFS 模塊提供的底層接口,它包括存儲(chǔ)媒介讀/寫接口 ( disk、I/O) 和供給文件創(chuàng)建修改時(shí)間的實(shí)時(shí)時(shí)鐘。
三、 移植FATFS文件系統(tǒng)
移植之前,首先得準(zhǔn)備一個(gè)能正常編譯的工程,并且工程里有SD卡的驅(qū)動(dòng)代碼,提供了讀寫扇區(qū)這些函數(shù)才能進(jìn)行FATFS文件系統(tǒng)的正常移植。
關(guān)于如何編寫SD卡驅(qū)動(dòng),SD卡的時(shí)序介紹、命令介紹等知識(shí)點(diǎn)下篇文章再講解。這篇文章重點(diǎn)是FATFS文件系統(tǒng)的移植過程。
3.1? 新建工程
FATFS文件系統(tǒng)源碼下載下來(lái),解壓之后,移植修改的步驟如下:
打開KEIL工程,添加FATFS文件源碼:
加入.h文件主要是方便配。cc936.c 用于支持中文。
3.2? 修改diskio.c文件
注釋掉現(xiàn)在不需要的用到的文件,因?yàn)槲覀儸F(xiàn)在用的是SD卡,與USB,ATA,MMC卡沒關(guān)系。
并加入一個(gè)新的宏 :
#define? SD? 0
定義SD卡的物理驅(qū)動(dòng)器號(hào)為0。
修改 disk_status函數(shù),該函數(shù)主要是用來(lái)獲取磁盤狀態(tài)。現(xiàn)在未用到,可以直接函數(shù)體內(nèi)代碼刪除。
修改截圖:
代碼示例:
#include "diskio.h" ??????? ? /* fatf底層API */
#include "sd.h"??????? ??????? ????? /* SD卡驅(qū)動(dòng)頭文件? */ /* 定義每個(gè)驅(qū)動(dòng)器的物理驅(qū)動(dòng)器號(hào)*/ #define SD??? 0 /*-----------------------------------------------------------------------*/ /* 獲取設(shè)備(磁盤)狀態(tài)???????????????????????????????????????????????????? */ /*-----------------------------------------------------------------------*/ DSTATUS disk_status ( BYTE pdrv???? ??????? /* 物理驅(qū)動(dòng)識(shí)別 */ ) { return 0;? //該函數(shù)現(xiàn)在無(wú)需用到,直接返回0 } |
修改disk_initialize函數(shù),添加SD卡的初始化,其他不用到的代碼直接刪掉,該函數(shù)成功返回0,失敗返回1。
修改截圖:
代碼示例:
/*-----------------------------------------------------------------------*/
/* 初始化磁盤驅(qū)動(dòng)??????????????? ????????????????????????????????????????*/ /*-----------------------------------------------------------------------*/ DSTATUS disk_initialize ( BYTE pdrv???? ??????? ??????? ??????? /* 物理驅(qū)動(dòng)識(shí)別 */ ) { DSTATUS stat; int result; switch (pdrv) { case SD :??????????? //選擇SD卡 stat=SD_Init();?? //初始化SD卡-用戶自己提供 } if(stat)return STA_NOINIT;? //磁盤未初始化 return 0; //初始化成功 } |
修改disk_read函數(shù),加入SD卡讀任意扇區(qū)的函數(shù)(需要用戶自己提供),其他不用到的選項(xiàng)可以刪掉。
修改代碼如下:
/*-----------------------------------------------------------------------*/
/* 讀扇區(qū)??????????????? ????????????????????????????????????????????????*/ /*-----------------------------------------------------------------------*/ DRESULT disk_read ( BYTE pdrv,??? ??????? /* 物理驅(qū)動(dòng)編號(hào) - 范圍0-9*/ BYTE *buff,?? ??????? /* 數(shù)據(jù)緩沖區(qū)存儲(chǔ)讀取數(shù)據(jù) */ DWORD sector,? /* 扇區(qū)地址*/ UINT count???? ??????? /* 需要讀取的扇區(qū)數(shù)*/ ) { DRESULT res; int result; switch (pdrv) { case SD: res=SD_Read_Data((u8*)buff,sector,count);? //讀SD扇區(qū)函數(shù)--用戶提供 return res; //在此處可以判錯(cuò)誤 } return RES_PARERR;? //無(wú)效參數(shù) } |
修改disk_write 函數(shù),添加寫扇區(qū)函數(shù):
代碼:
/*-----------------------------------------------------------------------*/
/* 寫扇區(qū)??????????????????????????????????????????????????????????????? */ /*-----------------------------------------------------------------------*/ #if _USE_WRITE DRESULT disk_write ( BYTE pdrv,??? ??????? ??????? ? /* 物理驅(qū)動(dòng)號(hào)*/ const BYTE *buff, ?????? /* 要寫入數(shù)據(jù)的首地址 */ DWORD sector,????? ??????? ?? /* 扇區(qū)地址 */ UINT count???? ??????? ??????? ? ?/* 扇區(qū)數(shù)量*/ ) { DRESULT res; int result; switch (pdrv) { case SD: res=SD_Write_Data((u8*)buff,sector,count); //寫入扇區(qū) return res; } return RES_PARERR;? //無(wú)效參數(shù) } #endif |
修改disk_ioctl 函數(shù),填充ioctl命令功能。這些功能是標(biāo)準(zhǔn)的命令,在diskio.h有定義。
代碼如下:
/*-----------------------------------------------------------------------*/
/* 其他函數(shù)??????????????????? ??????????????????????????*/ /*-----------------------------------------------------------------------*/ #if _USE_IOCTL DRESULT disk_ioctl ( BYTE pdrv,??? ??????? /* 物理驅(qū)動(dòng)號(hào) */ BYTE cmd,???? ??????? ? /* 控制碼? */ void *buff?????? ??????? /* 發(fā)送/接收數(shù)據(jù)緩沖區(qū)地址 */ ) { DRESULT res; int result; switch (pdrv) { case SD: switch(cmd) { case CTRL_SYNC:???? ?//等待寫過程 SD_CS(0);????????? //選中SD卡 if(SD_Wait_Ready())result = RES_ERROR;/*等待卡準(zhǔn)備好*/ else res = RES_OK;???? //成功 SD_CS(1);??????????? //釋放SD卡 break; case GET_SECTOR_SIZE://獲取扇區(qū)大小 *(DWORD*)buff = 512; res = RES_OK;???? //成功 break; case GET_BLOCK_SIZE:??? //獲取塊大小 *(WORD*)buff = 8;????? //塊大小(扇區(qū)為單位),一塊等于8個(gè)扇區(qū) res = RES_OK; break; case GET_SECTOR_COUNT: //獲取總扇區(qū)數(shù)量 *(DWORD*)buff = SD_Get_Sector_Count(); res = RES_OK; break; default:? //命令錯(cuò)誤 res = RES_PARERR; break; } return res; } return RES_PARERR;? //返回狀態(tài) } |
diskio.c 文件修改完整代碼:
/*-----------------------------------------------------------------------*/
/* 低級(jí)別磁盤I / O模塊框架fatf(C)ChaN)2014 *存儲(chǔ)控制模塊fatf模塊定義了一個(gè)API。????? */ /*-----------------------------------------------------------------------*/ #include "diskio.h" ??????? ? /* fatf底層API */ #include "sd.h"??????? ??????? ????? /* SD卡驅(qū)動(dòng)頭文件 ?*/ /* 定義每個(gè)驅(qū)動(dòng)器的物理驅(qū)動(dòng)器號(hào)*/ #define SD??? 0 /*-----------------------------------------------------------------------*/ /* 獲取設(shè)備(磁盤)狀態(tài)?????????????????????????????????????????????? ??????*/ /*-----------------------------------------------------------------------*/ DSTATUS disk_status ( BYTE pdrv???? ??????? /* 物理驅(qū)動(dòng)識(shí)別 */ ) { return 0;? //該函數(shù)現(xiàn)在無(wú)需用到,直接返回0 } /*-----------------------------------------------------------------------*/ /* 初始化磁盤驅(qū)動(dòng)??????? ????????????????????????????????????????????????*/ /*-----------------------------------------------------------------------*/ DSTATUS disk_initialize ( BYTE pdrv???? ??????? ??????? ??????? /* 物理驅(qū)動(dòng)識(shí)別 */ ) { DSTATUS stat; int result; switch (pdrv) { case SD :?? ????????//選擇SD卡 stat=SD_Init();?? //初始化SD卡-用戶自己提供 } if(stat)return STA_NOINIT;? //磁盤未初始化 return 0; //初始化成功 } /*-----------------------------------------------------------------------*/ /* 讀扇區(qū)????????????????????????????????????????????????????????? ??????*/ /*-----------------------------------------------------------------------*/ DRESULT disk_read ( BYTE pdrv,??? ??????? /* 物理驅(qū)動(dòng)編號(hào) - 范圍0-9*/ BYTE *buff,?? ??????? /* 數(shù)據(jù)緩沖區(qū)存儲(chǔ)讀取數(shù)據(jù) */ DWORD sector,????? /* 扇區(qū)地址*/ UINT count???? ??????? /* 需要讀取的扇區(qū)數(shù)*/ ) { DRESULT res; int result; switch (pdrv) { case SD: res=SD_Read_Data((u8*)buff,sector,count);? //讀SD扇區(qū)函數(shù)--用戶提供 return res; //在此處可以判錯(cuò)誤 } return RES_PARERR;? //無(wú)效參數(shù) } /*-----------------------------------------------------------------------*/ /* 寫扇區(qū)????????????????????? ??????????????????????????????????????????*/ /*-----------------------------------------------------------------------*/ #if _USE_WRITE DRESULT disk_write ( BYTE pdrv,??? ??????? ??????? ? /* 物理驅(qū)動(dòng)號(hào)*/ const BYTE *buff, /* 要寫入數(shù)據(jù)的首地址 */ DWORD sector,????? ??????? ? /* 扇區(qū)地址 */ UINT count???? ??????? ??????? ? /* 扇區(qū)數(shù)量*/ ) { DRESULT res; int result; switch (pdrv) { case SD: res=SD_Write_Data((u8*)buff,sector,count); //寫入扇區(qū) return res; } return RES_PARERR;? //無(wú)效參數(shù) } #endif /*-----------------------------------------------------------------------*/ /* 其他函數(shù)????????????????????????????????????????????? */ /*-----------------------------------------------------------------------*/ #if _USE_IOCTL DRESULT disk_ioctl ( BYTE pdrv,??? ??????? /* 物理驅(qū)動(dòng)號(hào) */ BYTE cmd,???? ??????? ? /* 控制碼? */ void *buff?????? ??????? /* 發(fā)送/接收數(shù)據(jù)緩沖區(qū)地址 */ ) { DRESULT res; int result; switch (pdrv) { case SD: switch(cmd) { case CTRL_SYNC:????? //等待寫過程 SD_CS(0);????????? //選中SD卡 if(SD_Wait_Ready())result = RES_ERROR;/*等待卡準(zhǔn)備好*/ else res = RES_OK;???? //成功 SD_CS(1);????????? //釋放SD卡 break; case GET_SECTOR_SIZE://獲取扇區(qū)大小 *(DWORD*)buff = 512; res = RES_OK;???? //成功 break; case GET_BLOCK_SIZE:? //獲取塊大小 *(WORD*)buff = 8;????? //塊大小--一塊等于8個(gè)扇區(qū) res = RES_OK; break; case GET_SECTOR_COUNT: //獲取總扇區(qū)數(shù)量 *(DWORD*)buff = SD_Get_Sector_Count(); res = RES_OK; break; default:? //命令錯(cuò)誤 res = RES_PARERR; break; } return res; } return RES_PARERR;? //返回狀態(tài) } #endif //返回FATFS時(shí)間 //獲得時(shí)間 DWORD get_fattime (void) { return (DWORD)(2017-1980)<<25|??? //年 7<<21|??? //月 27<<16|??? //日 12<<11|??? //時(shí) 13<<5|??? //分 14;??? //秒 } /* Return Value Currnet local time is returned with packed into a DWORD value. The bit field is as follows: bit31:25 Year origin from the 1980 (0..127) bit24:21 Month (1..12) bit20:16 Day of the month(1..31) bit15:11 Hour (0..23) bit10:5 Minute (0..59) bit4:0 Second / 2 (0..29) */ |
3.3?修改ffconf.h文件
需要注意的一些宏配置:
#define _CODE_PAGE? 936?? //采用中文GBK編碼?????? (64行)
#define??? _USE_LFN???? 3???? //動(dòng)態(tài)的堆上工作???????????? (93行)
#define??? _MAX_LFN?? 255?? /*_USE_LFN選項(xiàng)開關(guān)LFN(長(zhǎng)文件名)特性。
#define _VOLUMES????? 1???? /* 支持的磁盤數(shù)量(邏輯驅(qū)動(dòng)器)。 */?? (142行)
#define??? _MIN_SS??????? ??????? 512????????????????????????????????? (165行)
#define??? _MAX_SS????? ??????? 512?? /*這些選項(xiàng)配置支持扇區(qū)大小的范圍。(512,1024, 4096*/
#define _FS_NORTC???? ??? 0??? /*啟用RTC時(shí)間功能*/?? (202行)
#define _NORTC_MON ? ??1
#define _NORTC_MDAY???? 1
#define _NORTC_YEAR?????? 2015 //年
/*需要實(shí)現(xiàn):get_fattime()函數(shù)*/
ffconf.h 文件源碼(講解):
/*---------------------------------------------------------------------------/
/? FatFs - FAT文件系統(tǒng)模塊配置文件? R0.11a (C)ChaN, 2015 /---------------------------------------------------------------------------*/ #define _FFCONF 64180?????? /* 版本識(shí)別*/ /*---------------------------------------------------------------------------/ / 功能配置 /---------------------------------------------------------------------------*/ #define _FS_READONLY???? 0 /* 這個(gè)選項(xiàng)開關(guān)只讀配置。(0:讀/寫或1:只讀) /只讀配置刪除編寫API函數(shù),f_write(),f_sync(), / f_unlink(),f_mkdir(),f_chmod(),f_rename(),f_truncate(),f_getfree() /寫和可選的功能. */ #define _FS_MINIMIZE??????? 0 /*此選項(xiàng)定義刪除一些基本的API函數(shù)極小化水平。 / / 0:所有基本功能都是激活的。 / 1:f_stat(),f_getfree(),f_unlink(),f_mkdir(),f_chmod(),f_utime(), / f_truncate()和f_rename()函數(shù)刪除。 / 2:f_opendir(),f_readdir()和f_closedir()中除了1。 / 3:f_lseek()函數(shù)刪除除了2。*/ #define??? _USE_STRFUNC? 1 /*這個(gè)選項(xiàng)開關(guān)字符串函數(shù),f_gets(),f_putc(),f_puts()和 / f_printf()。 / / 0:禁用字符串函數(shù)。 / 1:啟用沒有LF-CRLF轉(zhuǎn)換。 / 2:啟用LF-CRLF(回車換行)轉(zhuǎn)換。*/ #define _USE_FIND????? ??????? 0 /*這個(gè)選項(xiàng)開關(guān)過濾目錄讀取特性和相關(guān)功能, / f_findfirst()和f_findnext()。(0:禁用或1:啟用)*/ #define??? _USE_MKFS ??????? 1 /* 這個(gè)選項(xiàng)開關(guān)f_mkfs()函數(shù)。(0:禁用或1:啟用) */ #define??? _USE_FASTSEEK 1 /* 這個(gè)選項(xiàng)開關(guān)快速尋求功能。(0:禁用或1:啟用) */ #define _USE_LABEL?? ??????? 1 /* 磁盤卷標(biāo)這個(gè)選項(xiàng)開關(guān)功能,f_getlabel()和f_setlabel()。 /(0:禁用或1:啟用) */ #define??? _USE_FORWARD 0 /* 這個(gè)選項(xiàng)開關(guān)f_forward()函數(shù)。(0:禁用或1:啟用) /啟用它,也_FS_TINY需要設(shè)置為1. */ /*---------------------------------------------------------------------------/ / 語(yǔ)言環(huán)境和名稱空間配置 /---------------------------------------------------------------------------*/ #define _CODE_PAGE? 936? //采用中文GBK編碼 /* 這個(gè)選項(xiàng)指定OEM代碼頁(yè)在目標(biāo)系統(tǒng)上使用。 /不正確的代碼頁(yè)的設(shè)置會(huì)導(dǎo)致文件打開失敗. / /?? 1?? - ASCII (沒有擴(kuò)展字符。Non-LFN cfg。只有) /?? 437 - U.S. /?? 720 - 阿拉伯語(yǔ) /?? 737 - 希臘語(yǔ); /?? 771 - 阿富汗 /?? 775 - 波羅的海 /?? 850 - 拉丁1 /?? 852 - 拉丁2 /?? 855 - 西里爾字母 /?? 857 - 土耳其語(yǔ) /?? 860 - 葡萄牙語(yǔ) / ??861 - 冰島語(yǔ) /?? 862 - 希伯來(lái)人 /?? 863 - 加拿大法語(yǔ) /?? 864 - 阿拉伯語(yǔ) /?? 865 - 日耳曼民族的 /?? 866 - 俄語(yǔ) /?? 869 - 希臘 2 /?? 932 - 日本人 (DBCS) /?? 936 - 簡(jiǎn)體中文(DBCS) /?? 949 - 韓國(guó)人 (DBCS) /?? 950 - 繁體中文(DBCS) */ #define??? _USE_LFN???? 3 //動(dòng)態(tài)的堆上工作 #define??? _MAX_LFN?? 255 /*_USE_LFN選項(xiàng)開關(guān)LFN(長(zhǎng)文件名)特性。 / / 0:禁用LFN特性。_MAX_LFN沒有影響。 / 1:啟用LFN BSS靜態(tài)工作緩沖區(qū)。總是不是線程安全的。 / 2:啟用LFN與動(dòng)態(tài)緩沖棧上的工作。 / 3:使LFN與動(dòng)態(tài)緩沖區(qū)在堆上工作。 / /? 當(dāng)啟用LFN(長(zhǎng)文件名)特性,Unicode(選項(xiàng)/ unicode.c)必須處理功能 /被添加到項(xiàng)目中。LFN工作緩沖區(qū)占用(_MAX_LFN + 1)* 2字節(jié)。 /當(dāng)使用堆棧緩沖區(qū),照顧堆棧溢出。當(dāng)使用堆 /工作緩沖區(qū)內(nèi)存,內(nèi)存管理功能,ff_memalloc()和 / ff_memfree(),必須添加到項(xiàng)目中。 */ #define??? _LFN_UNICODE?? 0 /* 這個(gè)選項(xiàng)開關(guān)字符編碼的API。(0:ANSI / OEM或1:Unicode) 路徑名/使用Unicode字符串,并設(shè)置_LFN_UNICODE啟用LFN特性 /1。這個(gè)選項(xiàng)也會(huì)影響行為的字符串的I / O功能。 */ #define _STRF_ENCODE???? 3 /* 當(dāng)_LFN(長(zhǎng)文件名)_UNICODE是1,這個(gè)選項(xiàng)選擇文件的字符編碼 /通過字符串讀取/寫入I /O功能,f_gets(),f_putc(),f_puts和f_printf(). / /? 0: ANSI/OEM /? 1: UTF-16LE /? 2: UTF-16BE /? 3: UTF-8 / / 當(dāng)_LFN_UNICODE = 0時(shí),該選項(xiàng)沒有影響。*/ #define _FS_RPATH???? 0 /* 這個(gè)選項(xiàng)配置相對(duì)路徑的功能。 / / 0:禁用相對(duì)路徑特性和刪除相關(guān)功能。 / 1:啟用相對(duì)路徑特性。f_chdir()和f_chdrive()是可用的。 / 2:f_getcwd()函數(shù)可用除了1?! ? /注意,目錄項(xiàng)讀通過f_readdir()這個(gè)選項(xiàng)。 */ /*---------------------------------------------------------------------------/ / 驅(qū)動(dòng)/卷配置 /---------------------------------------------------------------------------*/ #define _VOLUMES????? 1 /* 支持的磁盤數(shù)量(邏輯驅(qū)動(dòng)器)。 */ #define _STR_VOLUME_ID 0 #define _VOLUME_STRS??????? "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3" /* STR_VOLUME_ID選項(xiàng)開關(guān)卷ID字符串功能。 /當(dāng)_STR_VOLUME_ID設(shè)置為1時(shí),也可以使用預(yù)先定義的字符串在路徑名稱/數(shù)量。 為每個(gè)_VOLUME_STRS定義驅(qū)動(dòng)ID字符串 /邏輯驅(qū)動(dòng)器。條目的數(shù)量必須等于_VOLUMES。有效字符 /驅(qū)動(dòng)ID字符串:a - z和0 - 9。*/ #define??? _MULTI_PARTITION? 0 /* 這個(gè)選項(xiàng)開關(guān)多分區(qū)的特性。在默認(rèn)情況下(0),每個(gè)邏輯驅(qū)動(dòng)器 /號(hào)綁定到相同的物理驅(qū)動(dòng)器號(hào) /物理驅(qū)動(dòng)器將被安裝。當(dāng)啟用分區(qū)特性(1), /每個(gè)邏輯驅(qū)動(dòng)器號(hào)是綁定到任意物理驅(qū)動(dòng)器和分區(qū) /中列出VolToPart[]。還f_fdisk()函數(shù)可用. */ #define??? _MIN_SS??????? ??????? 512 #define??? _MAX_SS????? ??????? 512 /* 這些選項(xiàng)配置支持扇區(qū)大小的范圍。(512,1024, / 2048或4096)總是為大多數(shù)系統(tǒng)設(shè)置兩個(gè)512,卡和所有類型的內(nèi)存 /硬盤。但是可能需要更大的值為車載閃存和一些 /類型的光學(xué)媒體。當(dāng)_MAX_SS大于_MIN_SS,fatf配置 /變量扇區(qū)大小和GET_SECTOR_SIZE命令必須執(zhí)行 disk_ioctl()函數(shù). */ #define??? _USE_TRIM? 0 /* 這個(gè)選項(xiàng)開關(guān)ATA-TRIM特性。(0:禁用或1:啟用) /啟用削減特性,也應(yīng)該實(shí)現(xiàn)CTRL_TRIM命令 / disk_ioctl()函數(shù)。*/ #define _FS_NOFSINFO?????? 0 /* 如果你需要知道正確的自由空間體積FAT32,設(shè)置一些0 /選項(xiàng),f_getfree()函數(shù)在第一次后體積將迫使山 /全脂肪掃描。位1控制使用的集群數(shù)量分配?! ? / bit0 = 0:使用免費(fèi)的集群計(jì)算FSINFO如果可用。 / bit0 = 1:不相信自由FSINFO集群計(jì)算。 / bit1 = 0:最后使用集群可用FSINFO如果數(shù)量分配。 / bit1 = 1:不相信最后分配FSINFO集群數(shù)量. */ /*---------------------------------------------------------------------------/ / 系統(tǒng)配置列表 /---------------------------------------------------------------------------*/ #define??? _FS_TINY????? 0 /* 這個(gè)選項(xiàng)開關(guān)小緩沖區(qū)配置。(0:正?;?:小) /小配置,文件對(duì)象的大小(FIL)_MAX_SS減少字節(jié)。而不是私人部門從文件對(duì)象,緩沖了 /公共部門緩沖文件系統(tǒng)中的對(duì)象(fatf)是用于該文件 /數(shù)據(jù)傳輸. */ #define _FS_NORTC???? 0 #define _NORTC_MON 1 #define _NORTC_MDAY???? 1 #define _NORTC_YEAR?????? 2015 //年 /* _FS_NORTC選項(xiàng)開關(guān)時(shí)間戳的特性。如果系統(tǒng)沒有/ RTC函數(shù)或不需要有效的時(shí)間戳,_FS_NORTC 1設(shè)置為禁用/ 時(shí)間戳的特性。所有對(duì)象修改fatf將有一個(gè)固定的時(shí)間戳。/ 固定的時(shí)間定義為_NORTC_MON _NORTC_MDAY _NORTC_YEAR。 /當(dāng)啟用時(shí)間戳特性(_FS_NORTC = = 0),需要實(shí)現(xiàn)get_fattime()函數(shù)?! ? 添加到項(xiàng)目RTC讀當(dāng)前時(shí)間形式。_NORTC_MON, / _NORTC_MDAY和_NORTC_YEAR沒有效果。 /這些選項(xiàng)沒有影響只讀配置(_FS_READONLY = = 1)。 */ #define??? _FS_LOCK???? 0 /* _FS_LOCK選項(xiàng)開關(guān)控制復(fù)制的文件打開的文件鎖定功能 /和非法操作打開對(duì)象。這個(gè)選項(xiàng)_FS_READONLY時(shí)必須是0 /是1?! ? / 0:禁用文件鎖定功能。為了避免體積腐敗、應(yīng)用程序 /應(yīng)該避免非法打開,刪除和重命名的開放對(duì)象。 / > 0:啟用文件鎖定功能。值定義了多少文件/子目錄 可以同時(shí)打開的/文件鎖的控制之下。注意,這個(gè)文件獨(dú)立于re-entrancy /鎖功能。 */ #define _FS_REENTRANT?? 0 #define _FS_TIMEOUT ??????? 1000 #define??? _SYNC_t ??????? ??????? HANDLE /* _FS_REENTRANT選項(xiàng)開關(guān)re-entrancy fatf的(線程安全) /模塊本身。注意,不管這個(gè)選項(xiàng),文件訪問不同 /體積始終是凹角和音量控制功能,f_mount(),f_mkfs() /和f_fdisk()函數(shù),總是不凹角。只有文件/目錄的訪問 /相同的體積是這個(gè)功能的控制。 / / 0:禁用re-entrancy。_FS_TIMEOUT和_SYNC_t沒有效果。 / 1:啟用re-entrancy。還提供用戶同步處理程序, / ff_req_grant(),ff_rel_grant(),ff_del_syncobj()和ff_cre_syncobj() /函數(shù),必須添加到項(xiàng)目中。樣品中可用 /選項(xiàng) / syscall.c。 / /? _FS_TIMEOUT定義超時(shí)時(shí)間單位的滴答聲。 / _SYNC_t定義了O / S依賴同步對(duì)象類型。例如處理、ID、OS_EVENT * / SemaphoreHandle_t等. .O / S的頭文件定義需要 /包括在ff.c的范圍。 */ #define _WORD_ACCESS??? 0 /* _WORD_ACCESS選項(xiàng)是一個(gè)只有依賴于平臺(tái)的選擇。 它定義了這個(gè)詞/訪問方法是用來(lái)體積上的數(shù)據(jù)。 / / 0:逐字節(jié)的訪問??偸羌嫒菟衅脚_(tái)。 / 1:詞的訪問。不要選擇這個(gè),除非在下列條件。 / / *地址對(duì)齊內(nèi)存訪問總是允許所有指令。 / *字節(jié)順序的記憶是低位優(yōu)先。 / /如果是這樣的情況,_WORD_ACCESS也可以減少代碼的大小設(shè)置為1。 /下表顯示允許設(shè)置某種類型的處理器。 / /? ARM7TDMI?? 0?? *2????????? ColdFire ??0??? *1???????? V850E????? 0??? *2 /? Cortex-M3? 0?? *3????????? Z80??????? 0/1???????????? V850ES???? 0/1 /? Cortex-M0? 0?? *2????????? x86??????? 0/1???????????? TLCS-870?? 0/1 /? AVR??????? 0/1???????? ????RX600(LE)? 0/1???????????? TLCS-900?? 0/1 /? AVR32????? 0?? *1????????? RL78?????? 0??? *2???????? R32C?????? 0??? *2 /? PIC18????? 0/1???????????? SH-2?????? 0??? *1??????? ?M16C?????? 0/1 /? PIC24????? 0?? *2????????? H8S??????? 0??? *1???????? MSP430???? 0??? *2 /? PIC32????? 0?? *1????????? H8/300H??? 0??? *1???????? 8051?????? 0/1 / / * 1:高位優(yōu)先?! ? * 2:不支持不連續(xù)的內(nèi)存訪問?! ? * 3:一些編譯器生成LDM(邏輯磁盤管理器 ) / STM mem_cpy(內(nèi)存拷貝)函數(shù)。 */ |
3.4?實(shí)現(xiàn)動(dòng)態(tài)內(nèi)存分配函數(shù)與時(shí)間函數(shù)
ff.h文件有動(dòng)態(tài)內(nèi)存的釋放,動(dòng)態(tài)內(nèi)存申請(qǐng),時(shí)間獲取函數(shù)接口。
在diskio.c文件實(shí)現(xiàn)函數(shù)功能:
代碼實(shí)現(xiàn)如下:
//動(dòng)態(tài)內(nèi)存分配
void* ff_memalloc (UINT msize)? ??????? ??????? ??? /* 分配內(nèi)存塊 */ { return (void*)malloc(msize); //分配空間 } //動(dòng)態(tài)內(nèi)存釋放 void ff_memfree (void* mblock)?? ??????? ??????? ??? /* 空閑內(nèi)存塊 */ { free(mblock);????????????? //釋放空間 } //返回FATFS時(shí)間 //獲得時(shí)間 DWORD get_fattime (void) { //Get_RTC_Timer(); //獲取一次RTC時(shí)間 return (RTC_Timer.year-1980)<<25|?? //年 RTC_Timer.month<<21|? //月 RTC_Timer.day<<16|??? //日 RTC_Timer.hour<<11|?? //時(shí) RTC_Timer.minute<<5|? //分 RTC_Timer.sec;??????? //秒 } /* Return Value Currnet local time is returned with packed into a DWORD value. The bit field is as follows: bit31:25 Year origin from the 1980 (0..127) bit24:21 Month (1..12) bit20:16 Day of the month(1..31) bit15:11 Hour (0..23) bit10:5 Minute (0..59) bit4:0 Second / 2 (0..29) */ |
3.5 修改堆棧空間
完成了上述的修改,還需要修改堆??臻g,因?yàn)殚L(zhǎng)文件支持需要占用堆空間。
修改STM32啟動(dòng)文件如下:
3.6?編譯工程測(cè)試
修改完畢之后,給開發(fā)板插上SD卡,調(diào)用API函數(shù)在SD卡創(chuàng)建一個(gè)文件,并寫入數(shù)據(jù),測(cè)試是否成功:
#include "ff.h"
FATFS fs;? // 用戶定義的文件系統(tǒng)結(jié)構(gòu)體 FIL? file;? // 用戶定義的文件系統(tǒng)結(jié)構(gòu)體 u8 buff[]="123 知識(shí)??!"; int main(void) { u32 data;??????????????? //檢測(cè)SD卡容量 u8 i,res; LED_Init();????????????? //LED燈初始化 Delay_Init(); KEY_Init(); USART1_Init(72,115200); USART2_Init(36,115200); FLASH_Init(); Set_Font_addr(); //字庫(kù)地址初始化 FSMC_SRAM_Init(); LCD_Init(); RTC_Init();???? //RTC時(shí)鐘初始化 while(SD_Init()) ???//檢測(cè)不到SD卡,SD相關(guān)硬件初始化 { i=!i; LCD_ShowString(60,150,200,16,16,"SD Card Error!? Please Check SD Card!!",0xf800); Delay_ms(500); LED1(i)//DS0閃爍 } f_mount(&fs,"0",1);? // 注冊(cè)工作區(qū),驅(qū)動(dòng)器號(hào) 0,初始化后其他函數(shù)可使用里面的參數(shù) printf("注冊(cè)工作區(qū)!n"); if(f_mkfs("0",0,4096))? //格式化SD卡 { printf("格式化失?。?!n"); } else { printf("格式化成功??!n"); } res = f_open(&file, "/file.c", FA_OPEN_ALWAYS | FA_READ | FA_WRITE); if(res==0) { printf("文件創(chuàng)建成功??!n"); } else { printf("文件創(chuàng)建失?。?!n"); } res =f_write(&file,buff,strlen((const char*)buff),&data); if(res==0) { printf("數(shù)據(jù)寫入成功!!n"); } else { printf("數(shù)據(jù)寫入失敗?。"); } printf("成功寫入%d字節(jié)數(shù)據(jù)n",data); f_close(&file);? //關(guān)閉文件 //_FS_RPATH while(1) { Delay_ms(1000); LED1(1); Delay_ms(500); LED1(0); } } |