1

【OK210试用体验】Fatfs的移植 - 在线问答 - 电子技术论坛 - 最好最受欢迎电子论坛! - 德赢Vwin官网

【OK210试用体验】Fatfs的移植

刘天 ( 楼主 ) 2015-8-5 21:15:43  只看该作者 倒序浏览
Fatfs的移植
象棋小子    1048272975
对于固态存储器,其存储容量可以很大,往往需要一款文件系统对存储器用户数据进行组织文件的管理。它对文件存储器空间进行组织和分配,负责文件的存储并对存入的文件进行保护和检索。在嵌入式系统中,往往需要采用windows兼容的文件系统,像相机的照片、视频监控、语音产品等,很多都需要从windows计算机上提取资源或在windows计算机上进一步处理。Fatfs由于其开源免费,支持fat32,受到了广泛的应用,笔者此处就S5PV210移植Fatfs,对sd卡进行读写访问作一个简单的介绍。
1. Fatfs概述
Fatfs是由日本工程师ChaN所编写的fat文件系统模块,从06年发布第一个Fatfs版本开始,作者就从未停止维护和更新。Fatfs的编写遵循ANSI C,并且完全与磁盘I/O层分开。它不依赖于硬件架构,代码和工作区占用空间小,使之可以嵌入到各个低成本的微控制器中,如AVR、8051、PIC、ARM、Z80、68K等。由于sd卡一般使用fat32文件系统,在使用到sd卡的系统中移植Fatfs,将很好地实现对sd卡文件的管理。
2. Fatfs移植
Fatfs模块完全独立于磁盘I/O层,因此底层磁盘I/O访问并不属于Fatfs的模块部分,用户必须自己实现这部分用来访问存储设备。通常在diskio.c中实现这六个函数disk_initialize()、disk_status()、disk_read()、disk_wirte()、disk_ioctl()、get_fattime()即可。如果使能了OS相关的特性,则还需额外实现进程/内存函数。sd卡底层驱动实现在前面的章节有详细的介绍,此处直接在Fatfs移植接口中调用sd驱动模块中的相关函数。
2.1. disk_initialize函数
初始化存储设备,若设备初始化成功,应清除STA_NOINIT这个标志返回。若初始化不成功,应置位STA_NOINIT标志再返回。如果在初始化时,未检测到卡,可设置STA_NODISK标志表明无卡,检测到写保护,可设置STA_PROTECT标志表明写保护。
static DSTATUS State = STA_NOINIT;
DSTATUS disk_initialize (
    BYTE pdrv               /* Physical drive nmuber (0..) */
)
{
    if (pdrv != 0) {
        return STA_NOINIT; // 仅支持driver0
    }
   
    if (!Hsmmc_Init()) { // 调用sd卡初始化
        State &= ~STA_NOINIT; // 初始化成功
    } else {
        State |= STA_NOINIT;
    }
    return State;
}
2.2. disk_status函数
获取设备的状态,返回STA_NOINIT、STA_NODISK、STA_PROTECT这三个标志的组合。磁盘设备的状态随时都可能发生变化,例如初始化后的sd卡在某一时刻被拔出,此时无卡,Fatfs通过disk_status函数重新获知STA_NODISK无卡这一标志。
DSTATUS disk_status (
    BYTE pdrv       /* Physical drive nmuber (0..) */
)
{
    if (pdrv != 0) {
        return STA_NOINIT; // 仅支持driver0
    }
    return State;
}
2.3. disk_read函数
读取扇区,Fatfs通过该函数从磁盘某一扇区地址开始获取一块或多块扇区的数据,Fatfs最多支持一次性读写128个扇区的数据,通常磁盘都支持多块读、多块写,并且这样的读写性能远远好于分单块的读写。
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; // 读取出错
    }
}
2.4. disk_wirte函数
写扇区,Fatfs通过该函数从磁盘某一扇区地址开始写入一块或多块扇区的数据。如果只读(_FS_READONLY == 1),可以不实现该函数。
#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; // 写错误
    }
}
#endif
2.5. disk_ioctl函数
控制设备相关的功能,Fatfs使用5个设备独立的命令控制/获取设备特定的功能。
CTRL_SYNC:写同步,在关闭文件等操作时,如果磁盘I/O口层使用了写缓存,那么通知磁盘I/O口层把写缓存中的数据写回到磁盘中。对于没有写缓存,即每次disk_write均写入到磁盘中,无需处理该命令,只需返回RES_OK即可。在可写时_FS_READONLY == 0,该命令才会被使用。
GET_SECTOR_COUNT:获取磁盘的总扇区数,在用f_mkfs()格式化文件系统,f_fdisk对磁盘分区时均会使用这个命令来获取磁盘的总扇区数,对于sd卡,通过CSD获取卡容量信息。在支持格式化文件系统或多分区的情况下(_USE_MKFS == 1 或 _MULTI_PARTITION== 1),该命令才会被使用。
GET_SECTOR_SIZE:获取磁盘一个扇区的字节数,有效值为512、1024、2048或4096。对于大部分的系统,所有内存卡,硬盘,通常返回扇区大小为512字节,但对于flash,一页可能为512字节,也可能为1k字节,2k字节,4k字节,需要根据具体的flash页大小进行配置。
GET_BLOCK_SIZE:以扇区为单位获取擦除块的大小。在用f_mkfs()格式化文件系统时,用来使数据区对齐到擦除块。例如,第一个擦除块往往用来保存系统信息等,真正的数据在第二个擦除块位置开始存放。该命令并不重要,可直接返回1表明1个扇区对齐。此处与原作者移植例程保持一致,对sd2.0版本卡,返回AU(可分配单元)的大小,sd1.0版本卡,返回擦除块大小。在_USE_MKFS == 1时,该命令才被使用。
CTRL_TRIM:表明一个扇区或多个扇区中的数据不再有效,可以被擦除。对于sd卡可以不实现此命令,在_USE_MKFS==1并且_USE_TRIM == 1,该命令才可能被使用。
#if _USE_IOCTL
DRESULT disk_ioctl (
    BYTE pdrv,      /* Physical drive nmuber (0..) */
    BYTE cmd,       /* Control code */
    void *buff      /* Buffer to send/receive control data */
)
{
uint8_t CSD[16];
uint8_t SdState[64];
uint32_t c_size, c_size_multi, read_bl_len,sector_size, au_size;   
DRESULT Result = RES_ERROR;
   
if (pdrv) {
    returnRES_PARERR;
}
if (State & STA_NOINIT) {
    returnRES_NOTRDY;
}
switch (cmd) {
case CTRL_SYNC:
    Result =RES_OK; // 写sd卡驱动函数确保了写完才返回,已写同步了
    break;
case GET_SECTOR_COUNT: /* Get drive capacity in unit ofsector (DWORD) */
    if(!Hsmmc_Get_CSD(CSD)) {
    if((CSD[15]>>6) == 1) { // CSD v2.00->SDHC卡
        c_size =((CSD[8]&0x3f) << 16) + (CSD[7]<<8) + CSD[6];
// 卡容量为字节(c_size+1)*512Kbyte,以1扇区512 byte字,卡的扇区数为      
        *(DWORD*)buff = (c_size+1) << 10;
    } else { // CSDv1.0->sd V1.x, sd v2.00 standard
    read_bl_len =CSD[10] & 0xf; // [83:80]
    c_size_multi= ((CSD[6] & 0x3) << 1) + ((CSD[5] & 0x80) >> 7);
    c_size =((WORD)(CSD[9]&0x3) << 10) + ((WORD)CSD[8]<<2) +(CSD[7]>>6);
// 卡容量为字节(c_size+1)*2^(c_size_multi+2)*2^(read_bl_len)
// 以1扇区 512 byte计,卡扇区数为
    *(DWORD*)buff = (c_size + 1)<<((c_size_multi + 2 +(read_bl_len-9));
    }
    Result =RES_OK;            
    }
    break;
   
case GET_BLOCK_SIZE: /* Get erase block size in unitof sector (DWORD) */
    if(!Hsmmc_Get_CSD(CSD)) {
    if ((CSD[15]>>6)== 1) { // CSD v2.00, SDHC卡
// v2.00扇区大小等信息在CSD中不再有效,需从sd state获取这些信息
    if(!Hsmmc_GetSdState(SdState)) {
        au_size =(SdState[53] >> 4); // Allocation Unit(AU)[431:428]
// 1au单位为16k,转化为512字节扇区数
        *(DWORD*)buff = 16UL << au_size;   
        Result = RES_OK;                    
    }           
    } else {
// CSD v1.0,sd version 1.x, sd version 2.00 standard
    sector_size =((CSD[5] & 0x3f) << 1) + (CSD[4] >> 7); // [45:39]
// 擦除块大小为扇区大小*块长度(512为单位)
    *(DWORD*)buff = (sector_size + 1) << ((CSD[2] >> 6) - 1);
    Result =RES_OK;               
    }   
    }
    break;  
        
case CTRL_TRIM:
/* Erase a block of sectors (used when CTRL_TRIM == 1)*/
    if(!Hsmmc_EraseBlock(((DWORD *)buff)[0], ((DWORD *)buff)[1])) {
        Result =RES_OK;
    }
    break;  
        
default:
    break;
}
return Result;
}
#endif
2.6. get_fattime函数
用来获取当前RTC的时间,从1980年开始计算时间戳,用来记录文件的创建时间,修改时间等。如果觉得文件的时间记录无关紧要,可以定义_FS_NORTC为1,返回固定的时间戳。在只读系统中(_FS_READONLY == 1),该函数不会被调用。
#if _USE_WRITE
DWORD get_fattime()
{
    RTC_Time Time;
    RTC_GetTime(&Time);         
    return (((Time.Year - 1980)<< 25) | (Time.Month << 21) |
            (Time.Day << 16) |(Time.Hour << 11) |
            (Time.Min << 5) |(Time.Sec << 1));
}
#endif
至此Fatfs模块磁盘底层IO调用接口全部移植完成。
3. 标准IO操作
对于嵌入式开发来说,所有的io操作均是面对特定设备,编译器的标准c库是无法统一实现的,应由用户去实现。在arm编译器中,是使用一种半主机的模式,当用户使用了标准io库函数时,默认情况下,编译器是通过一组定义好的软中断来把io应用请求传送至运行调试器的主机,如用printf和scanf来使用主机的屏幕以及键盘,需要jtag的支持。一旦目标板脱离主机,需单独运行时,在半主机模式下是无法运行,因为io软中断请求无法得到处理。我们此处先对所使用到的io库函数进行重定向,如把输入、输出、错误流重定向到串口,把文件操作fopen、fwrite、fread、fclose等重定向到对sd卡的文件操作。在Retarget.c中我们实现标准io库函数的重定向。
#define MAX_FILES   8
int FileHandle[MAX_FILES];
int __open(const char * filename, int mode)
{
int handle;
FRESULT Res;
FIL *file;
BYTE Mode = 0;
if (mode & _LLIO_CREAT) {
    // Create afile if it doesn't exists.
    Mode |=FA_OPEN_ALWAYS;
}
switch (mode & _LLIO_RDWRMASK) {
case _LLIO_RDONLY:
// The file should be opened for read only.
    Mode |=FA_READ;
    break;
case _LLIO_WRONLY:
    // The fileshould be opened for write only.
    Mode |=FA_WRITE;
    break;
case _LLIO_RDWR:
    // The fileshould be opened for both reads and writes.
    Mode |=FA_READ | FA_WRITE;
    break;
default:
    return -1;
}
  
for (handle=0; handle
    if(FileHandle[handle] == 0) {
        break;
    }
}
if (handle == MAX_FILES) {
    return -1;
}
file = (FIL *)malloc(sizeof(FIL));
if (file == NULL) {
    return -1;
}
Res =  f_open(file,filename, Mode);
if (Res == FR_OK) {
    if ((mode& _LLIO_CREAT) && (mode & _LLIO_APPEND)) {
        // Appendto the existing file.
        Res =(int)f_lseek(file, file->fsize);
        if (Res!= FR_OK) {
            f_close(file);
            free((FIL *)file);
            return -1;
        }
    }
    FileHandle[handle]= (int)file;
    return(handle+3); // skip STDIN, STDOUT, STDERR handles
} else {      
    free((FIL*)file);
    return -1;   
}  
}
int __close(int handle)
{
FRESULT Res;
FIL *file;
   
if (handle < 3) {
    return (0);
}
file = (FIL *)(FileHandle[handle-3]);
Res = f_close(file);
free(file);
FileHandle[handle-3] = 0;
if (Res == FR_OK) {
    return 0;
}
return -1;
}
size_t __write(int handle, const unsigned char *buffer, size_t size)
{
int32_t i;
FRESULT Res;
UINT ByteWrite;
FIL *file;
// Check for the command to flush all handles
if (handle == -1) {
    return 0;
}
if (buffer == 0) {
    return 0;
}
   
if ((handle == _LLIO_STDOUT) || (handle ==_LLIO_STDERR)) {
    for (i=0;i
        if(*buffer == 'n') {
            Uart_PutChar('r');
        }
        Uart_PutChar(*buffer++);
    }
    return size;
} else {
#if !(_FS_READONLY)
    file = (FIL*)(FileHandle[handle-3]);
    Res =f_write(file, buffer, size, &ByteWrite);
    if (Res ==FR_OK) {
        returnByteWrite;
    }
#endif  
}
return _LLIO_ERROR;
}
size_t __read(int handle, unsigned char * buffer,size_t size)
{
int32_t i;
FRESULT Res;
UINT ByteRead;
FIL *file;
int8_t c;
   
if (handle ==-1) {
    return 0;
}
if (handle == _LLIO_STDIN) {
    for (i=0;i
        c = (int8_t)Uart_WaitChar();
        if (c < 0) {
            return i;
        }
        *buffer++ = c;
    }
    return size;
} else {
    file = (FIL*)(FileHandle[handle-3]);
    Res =f_read(file, buffer, size, &ByteRead);
    if (Res ==FR_OK) {
        returnByteRead;
    }
    return -1;      
}
}
long __lseek(int handle, long offset, int whence)
{
FRESULT Res;   
FIL *file;
if (handle < 3) {
    return (0);
}
  
#if _FS_MINIMIZE < 3
file = (FIL *)(FileHandle[handle-3]);
switch (whence) {
case SEEK_SET:
    Res =f_lseek(file, offset);
    break;
case SEEK_CUR:
    Res =f_lseek(file, file->fptr + offset);
    break;
case SEEK_END:
    Res =f_lseek(file, file->fsize + offset);
    break;
default:
    return -1;
}
   
if (Res == FR_OK) {
    returnfile->fptr; // return the current file position
}
#endif
return (-1);
}
4. 应用测试
移植好Fatfs后,即可在main中采用标准的c库文件io操作函数实现对sd卡读写文件的操作。此处以从sd卡把Bootloader.bin代码自动更新到Nand flash为例说明Fatfs的应用。Bootloader.bin必须是裸机或者uboot经过SdBoot工具转换后适用于Nand的烧录代码。
int UpdateToNand(void)
{
uint8_t *pBuffer;   
FILE *handle;
int ByteRead;   
handle = fopen("Bootloader.bin","rb");
if (handle == NULL) {
    printf("Cannot find Bootloader.binn");
    return -1;
}
pBuffer = (uint8_t *)malloc(1024*1024);
if (pBuffer == NULL) {
    return -1;
}   
NandBoot_Init();
while (1) {
    ByteRead =fread(pBuffer, 1, 1024*1024, handle);
    if (ByteRead== 0) {
        printf("Read file errorn");
        fclose(handle);
        free(pBuffer);
        return -1;
    }
    NandBoot_WriteSkipBad(0,pBuffer, ByteRead);
    if (ByteRead< 1024*1024) {
        fclose(handle);
        free(pBuffer);
        return 0;
    }
}
}
void main(void)
{   
uint8_t Command;
FATFS fs;   
FILE *handle;
int ByteRead;
char Text[1025];   
RTC_Time Time = {
    2015, 7, 20,22, 00, 0, 1
};  
   
Gpio_Init();   
Uart_Init();
RTC_Init(&Time);
f_mount(&fs, "" , 0);   
while (1) {
printf("1: Update Bootloader.bin tonandn");
printf("2: View Update.logn");
Command = Uart_WaitChar();
if (Command == '1') {
if (UpdateToNand() == 0) {
    printf("Updatecompletedn");
    // 记录更新时间
    handle =fopen("Update.log", "a+");
    if (handle !=NULL) {
    RTC_GetTime(&Time);
    fprintf(handle,"Time: %4d/%02d/%02d %02d:%02d:%02drn", Time.Year,
            Time.Month,Time.Day, Time.Hour, Time.Min, Time.Sec);
    fclose(handle);
    }
}
} else if (Command == '2') {
    // 浏览更新log
    handle =fopen("Update.log", "rb");
    if (handle ==NULL) {
        printf("Cannot find Update.logn");
        continue;
    }
    while (1) {
        ByteRead= fread(Text, 1, 1024, handle);
        if(ByteRead == 0) {
            printf("Readfile errorn");
            fclose(handle);
            continue;
        }
        if(ByteRead < 1024) {
            Text[ByteRead]= 0;
        } else {
            Text[1024] = 0;
        }
        printf("%s",Text);
        if(ByteRead < 1024) {
            fclose(handle);
            break;
        }
    }
}
printf("n");      
}
}
5. 附录
OK210_IAR_Fatfs.rar,Fatfs在IAR下的移植工程,包括S5PV210 Bootloader、Fatfs源码、以及相应的sd卡驱动。
OK210_IAR_Fatfs.rar (791.71 KB, 下载次数: 17)

0个回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则


关闭

站长推荐上一条 /6 下一条

小黑屋|手机版|Archiver|德赢Vwin官网 ( 湘ICP备2023018690号 )

GMT+8, 2024-12-21 12:48 , Processed in 0.654031 second(s), Total 63, Slave 43 queries .

Powered by 德赢Vwin官网 网

© 2015 bbs.elecfans.com

微信扫描
快速回复 返回顶部 返回列表