S3C2416裸機(jī)開發(fā)系列十七_(dá)GCC下Fatfs的移植
掃描二維碼
隨時(shí)隨地手機(jī)看文章
對(duì)于固態(tài)存儲(chǔ)器,其存儲(chǔ)容量可以很大,往往需要一款文件系統(tǒng)對(duì)存儲(chǔ)器用戶數(shù)據(jù)進(jìn)行組織文件的管理。它對(duì)文件存儲(chǔ)器空間進(jìn)行組織和分配,負(fù)責(zé)文件的存儲(chǔ)并對(duì)存入的文件進(jìn)行保護(hù)和檢索。在嵌入式系統(tǒng)中,往往需要采用windows兼容的文件系統(tǒng),像相機(jī)的照片、視頻監(jiān)控、語音產(chǎn)品等,很多都需要從windows計(jì)算機(jī)上提取資源或在windows計(jì)算機(jī)上進(jìn)一步處理。Fatfs由于其開源免費(fèi),支持fat32,受到了廣泛的應(yīng)用,筆者此處就s3c2416移植Fatfs,對(duì)sd卡進(jìn)行讀寫訪問作一個(gè)簡(jiǎn)單的介紹。
1. Fatfs概述Fatfs是由日本工程師ChaN所編寫的fat文件系統(tǒng)模塊,從06年發(fā)布第一個(gè)Fatfs版本開始,作者就從未停止維護(hù)和更新。Fatfs的編寫遵循ANSI C,并且完全與磁盤I/O層分開。它不依賴于硬件架構(gòu),代碼和工作區(qū)占用空間小,使之可以嵌入到各個(gè)低成本的微控制器中,如AVR、8051、PIC、ARM、Z80、68K等。
2. 代碼準(zhǔn)備Fatfs源碼,請(qǐng)讀者自行從Fatfs官網(wǎng)http://elm-chan.org/fsw/ff/00index_e.html下載最新的源碼。
s3c2416啟動(dòng)代碼工程,啟動(dòng)代碼是s3c2416/50/51這系列arm9芯片在運(yùn)行用戶c代碼main函數(shù)之前必須先運(yùn)行的代碼,啟動(dòng)代碼支持sd、Nand啟動(dòng),為用戶設(shè)置系統(tǒng)時(shí)鐘,初始化內(nèi)存,自動(dòng)識(shí)別啟動(dòng)設(shè)備并搬移代碼到RAM,MMU映射,中斷管理等,用戶只需專注于用c開發(fā)其它功能函數(shù)即可。關(guān)于啟動(dòng)代碼以及啟動(dòng)代碼的實(shí)現(xiàn)過程,筆者前面章節(jié)有非常詳細(xì)的介紹。此處以GCC下移植Fatfs為講解,下載”GCC啟動(dòng)代碼工程應(yīng)用實(shí)例”中的啟動(dòng)代碼源碼即可。如果在MDK下開發(fā),下載”MDK啟動(dòng)代碼工程應(yīng)用實(shí)例”中的啟動(dòng)代碼源碼。
用戶代碼,用c開發(fā)的所有功能代碼,其中,用戶代碼入口為main()函數(shù),在這里需要實(shí)現(xiàn)sd卡驅(qū)動(dòng)模塊等。
3. 工程搭建在linux操作系統(tǒng)下任一路徑下新建一個(gè)Fatfs_GCC的工程目錄,該目錄下新建Fatfs目錄,下載Fatfs最新源碼并解壓,把src目錄內(nèi)容全部拷貝到Fatfs目錄下。
把啟用代碼目錄start_code拷貝到Fatfs_GCC目錄下,這部分代碼無需任何的修改。并保留其中的Makefile這些文件,按照Makefile的模板添加各個(gè)目錄的Makefile。GCC啟動(dòng)代碼下的工程管理Makefile提取自u(píng)boot,可以方便地增加源代碼以及代碼目錄。
在Fatfs_GCC目錄下新建apps目錄,用來保存應(yīng)用相關(guān)的源碼以及各個(gè)模塊驅(qū)動(dòng)。
4. Fatfs移植Fatfs模塊完全獨(dú)立于磁盤I/O層,因此底層磁盤I/O訪問并不屬于Fatfs的模塊部分,用戶必須自己實(shí)現(xiàn)這部分用來訪問存儲(chǔ)設(shè)備。通常在diskio.c中實(shí)現(xiàn)這六個(gè)函數(shù)disk_initialize()、disk_status()、disk_read()、disk_wirte()、disk_ioctl()、get_fattime()即可。如果使能了OS相關(guān)的特性,則還需額外實(shí)現(xiàn)進(jìn)程/內(nèi)存函數(shù)。sd卡底層驅(qū)動(dòng)實(shí)現(xiàn)在前面的章節(jié)有詳細(xì)的介紹,此處直接在Fatfs移植接口中調(diào)用sd驅(qū)動(dòng)模塊中的相關(guān)函數(shù)。
4.1. disk_initialize函數(shù)初始化存儲(chǔ)設(shè)備,若設(shè)備初始化成功,應(yīng)清除STA_NOINIT這個(gè)標(biāo)志返回。若初始化不成功,應(yīng)置位STA_NOINIT標(biāo)志再返回。如果在初始化時(shí),未檢測(cè)到卡,可設(shè)置STA_NODISK標(biāo)志表明無卡,檢測(cè)到寫保護(hù),可設(shè)置STA_PROTECT標(biāo)志表明寫保護(hù)。
static DSTATUS State = STA_NOINIT;
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber (0..) */
)
{
if (pdrv != 0) {
return STA_NOINIT; // 僅支持driver0
}
if (!Hsmmc_Init()) { // 調(diào)用sd卡初始化
State &= ~STA_NOINIT; // 初始化成功
} else {
State |= STA_NOINIT;
}
return State;
}
4.2. disk_status函數(shù)獲取設(shè)備的狀態(tài),返回STA_NOINIT、STA_NODISK、STA_PROTECT這三個(gè)標(biāo)志的組合。磁盤設(shè)備的狀態(tài)隨時(shí)都可能發(fā)生變化,例如初始化后的sd卡在某一時(shí)刻被拔出,此時(shí)無卡,F(xiàn)atfs通過disk_status函數(shù)重新獲知STA_NODISK無卡這一標(biāo)志。
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber (0..) */
)
{
if (pdrv != 0) {
return STA_NOINIT; // 僅支持driver0
}
return State;
}
4.3. disk_read函數(shù)讀取扇區(qū),F(xiàn)atfs通過該函數(shù)從磁盤某一扇區(qū)地址開始獲取一塊或多塊扇區(qū)的數(shù)據(jù),F(xiàn)atfs最多支持一次性讀寫128個(gè)扇區(qū)的數(shù)據(jù),通常磁盤都支持多塊讀、多塊寫,并且這樣的讀寫性能遠(yuǎn)遠(yuǎn)好于分單塊的讀寫。
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address (LBA) */
UINT count /* Number of sectors to read (1..128) */
)
{
if (pdrv || !count) {
return RES_PARERR;
}
if (State & STA_NOINIT) {
return RES_NOTRDY;
}
if (!Hsmmc_ReadBlock(buff, sector,count)) {
return RES_OK; // 讀取成功
} else {
return RES_ERROR; // 讀取出錯(cuò)
}
}
4.4. disk_wirte函數(shù)寫扇區(qū),F(xiàn)atfs通過該函數(shù)從磁盤某一扇區(qū)地址開始寫入一塊或多塊扇區(qū)的數(shù)據(jù)。如果只讀(_FS_READONLY == 1),可以不實(shí)現(xiàn)該函數(shù)。
#if _USE_WRITE
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber (0..) */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address (LBA) */
UINT count /* Number of sectors to write (1..128) */
)
{
if (pdrv || !count) {
return RES_PARERR;
}
if (State & STA_NOINIT) {
return RES_NOTRDY;
}
if (State & STA_PROTECT) {
return RES_WRPRT;
}
if (!Hsmmc_WriteBlock((unsignedchar *)buff, sector, count)) {
return RES_OK; // 寫成功
} else {
return RES_ERROR; // 寫錯(cuò)誤
}
}
#endif
4.5. disk_ioctl函數(shù)控制設(shè)備相關(guān)的功能,F(xiàn)atfs使用5個(gè)設(shè)備獨(dú)立的命令控制/獲取設(shè)備特定的功能。
CTRL_SYNC:寫同步,在關(guān)閉文件等操作時(shí),如果磁盤I/O口層使用了寫緩存,那么通知磁盤I/O口層把寫緩存中的數(shù)據(jù)寫回到磁盤中。對(duì)于沒有寫緩存,即每次disk_write均寫入到磁盤中,無需處理該命令,只需返回RES_OK即可。在可寫時(shí)_FS_READONLY == 0,該命令才會(huì)被使用。
GET_SECTOR_COUNT:獲取磁盤的總扇區(qū)數(shù),在用f_mkfs()格式化文件系統(tǒng),f_fdisk對(duì)磁盤分區(qū)時(shí)均會(huì)使用這個(gè)命令來獲取磁盤的總扇區(qū)數(shù),對(duì)于sd卡,通過CSD獲取卡容量信息。在支持格式化文件系統(tǒng)或多分區(qū)的情況下(_USE_MKFS == 1 或 _MULTI_PARTITION== 1),該命令才會(huì)被使用。
GET_SECTOR_SIZE:獲取磁盤一個(gè)扇區(qū)的字節(jié)數(shù),有效值為512、1024、2048或4096。對(duì)于大部分的系統(tǒng),所有內(nèi)存卡,硬盤,通常返回扇區(qū)大小為512字節(jié),但對(duì)于flash,一頁可能為512字節(jié),也可能為1k字節(jié),2k字節(jié),4k字節(jié),需要根據(jù)具體的flash頁大小進(jìn)行配置。
GET_BLOCK_SIZE:以扇區(qū)為單位獲取擦除塊的大小。在用f_mkfs()格式化文件系統(tǒng)時(shí),用來使數(shù)據(jù)區(qū)對(duì)齊到擦除塊。例如,第一個(gè)擦除塊往往用來保存系統(tǒng)信息等,真正的數(shù)據(jù)在第二個(gè)擦除塊位置開始存放。該命令并不重要,可直接返回1表明1個(gè)扇區(qū)對(duì)齊。此處與原作者移植例程保持一致,對(duì)sd2.0版本卡,返回AU(可分配單元)的大小,sd1.0版本卡,返回擦除塊大小。在_USE_MKFS == 1時(shí),該命令才被使用。
CTRL_ERASE_SECTOR:擦除某一段扇區(qū),對(duì)于flash,都是要先擦除才能正確寫入,對(duì)于nor、nand flash,某一個(gè)文件不再使用時(shí)(例如刪除或被覆蓋),先發(fā)出這個(gè)命令強(qiáng)制設(shè)備擦除這個(gè)文件所在的空間區(qū)域,之后在disk_write無需再對(duì)flash進(jìn)行擦除操作,因?yàn)槊看挝募辉偈褂脮r(shí),都已經(jīng)先擦除了這部分。對(duì)于sd卡等,這個(gè)命令沒有任何用處,因?yàn)閟d卡接收到塊寫命令均是先擦除再寫。如果再開啟這個(gè)命令_USE_ERASE == 1,相當(dāng)重復(fù)擦除。對(duì)于sd卡,建議設(shè)置_USE_ERASE == 0,不使用這個(gè)命令。
#if _USE_IOCTL
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
unsigned char CSD[16];
unsigned charSdState[64];
unsigned int c_size,c_size_multi, read_bl_len, sector_size, au_size;
DRESULT Result =RES_ERROR;
if (pd