STM32F103RC,FATFS,w25x16移植笔记

来源:互联网 发布:ubuntu 移动宽带 编辑:程序博客网 时间:2024/06/06 00:25

这些天移植了FatFS文件系统到STM32F103RC板上,使用STM32的SPI_FLASH库,出现了一些问题现在记录下来。

接下来说一下移植的过程:

移植的最新版本为FatFS 0.12版本版本中主要包含以下几个文件:diskio.c diskio.h ff.c ff.h ffconf.h integer.h。
其中,ff相关的文件为FatFS的核心文件系统文件,diskio相关的文件为文件系统与底层硬件交互的接口文件,ffconf.h是用来配置文件系统配置的一些参数,integer.h是对不同平台变量类型的定义。

1.修改ffconf.h

_FS_READONLY         //文件系统只读_FS_MINIMIZE         //移除一些函数,定义最小文件系统_USE_STRFUNC         //允许使用一些字符串函数_USE_FIND            //开启一些查询函数_USE_MKFS            //f_mkfs,可以格式化Flash_USE_FASTSEEK        //开启一些快速定位函数_USE_EXPAND          //开启一些扩展函数_USE_CHMOD           //开启权限相关的函数_USE_LABEL           //开启卷标相关函数_USE_FORWARD         //开启使用数据流而不用缓冲区_CODE_PAGE           //代码页格式_USE_LFN             //开启长文件名模式_MAX_LFN             //设置最长文件名长度_LFN_UNICODE         //设置长文件名编码格式_STRF_ENCODE         //长命名文件中字符串编码格式_FS_RPATH            //支持目录更改_VOLUMES             //支持硬盘个数_STR_VOLUME_ID       //允许字符串模式的ID_VOLUME_STRS         //字符串模式的具体字符串_MULTI_PARTITION     //允许新建多个分区_MIN_SS              //所支持最小的扇区大小_MAX_SS              //所支持最大的扇区大小_USE_TRIM            //支持空闲区域回收_FS_NOFSINFO         //支持查询文件系统信息_FS_TINY             //支持微型文件系统_FS_EXFAT            //支持扩展性文件系统_FS_NORTC            //支持不使用RTC的文件系统_NORTC_MON           //不使用RTC时的月份_NORTC_MDAY          //不使用RTC时的日_NORTC_YEAR          //不使用RTC时的年_FS_LOCK             //支持文件上锁功能_FS_REENTRANT        //支持文件重入,多线程访问_FS_TIMEOUT          //重入模式下超时周期我的设置如下:#define _USE_MKFS       1#define _CODE_PAGE  936#define _VOLUMES    1#define _MIN_SS     512#define _MAX_SS     512其余均为0或默认值

2.diskio.c

disk_initialize 增加SPI_FLASH_Init()以及SPI_FLASH_ReadID()函数进行SPI_FLASH的初始化操作

DSTATUS disk_initialize (    BYTE pdrv               /* Physical drive nmuber to identify the drive */){    DSTATUS stat;    u32  FlashID;    switch (pdrv) {    case SPI_FLASH :        SPI_FLASH_Init();        FlashID = SPI_FLASH_ReadID();        stat = 0;        return stat;    }    return STA_NOINIT;}

disk_read 增加SPI_FLASH_BufferRead,该函数是从指定地址读取指定字节数。

DRESULT disk_read (    BYTE pdrv,      /* Physical drive nmuber to identify the drive */    BYTE *buff,     /* Data buffer to store read data */    DWORD sector,   /* Sector address in LBA */    UINT count      /* Number of sectors to read */){    DRESULT res;    switch (pdrv) {    case SPI_FLASH :        SPI_FLASH_BufferRead(buff,sector * SPI_FLASH_SECTOR,count * SPI_FLASH_SECTOR);        res = RES_OK;        return res;    }    return RES_PARERR;}

disk_write 增加相对应的Flash写入函数,Flash在写入的时候需要先擦除后写入。使用的Flash芯片为w25x16的16MB Flash芯片,此芯片的最小擦除单位为4k字节的一个扇区,因此在写入的时候,不考虑增加缓存的情况下即及时写入时,需要先将所写入的区域部分所占的扇区数据读取出来,并将对应的扇区擦除,然后将写入的区域与刚刚读取数据的区域合并后,再次写入对应的几个扇区中。

DRESULT disk_write (    BYTE pdrv,          /* Physical drive nmuber to identify the drive */    const BYTE *buff,   /* Data to be written */    DWORD sector,       /* Sector address in LBA */    UINT count          /* Number of sectors to write */){    DRESULT res;    UINT i = 0;    UINT SectorWriteTotal = 0;                        //写入数据总共占用的扇区个数    UINT SectorWriteLeft = 0;                         //写入数据占用剩余不到一个扇区的字节个数    UINT SectorPhyLocation = 0;                       //芯片上物理扇区的标号    u32 AddrStart = sector * SPI_FLASH_SECTOR;        //写入数据的起始地址    UINT WriteLeftBytes = count * SPI_FLASH_SECTOR;   //写入数据的剩余字节个数    UINT CurrenSectorLeft = 0;                        //当前扇区未被占用的剩余字节个数    UINT SectorPhyLocationAddr = 0;                   //芯片上物理扇区的地址    switch (pdrv) {    case SPI_FLASH :        SectorWriteTotal = count * SPI_FLASH_SECTOR / PHY_SECTOR_SIZE;         //计算当前需要写入的数据量需要多少个扇区 向下取证        SectorWriteLeft = count * SPI_FLASH_SECTOR % PHY_SECTOR_SIZE;          //计算不足一个扇区的字节个数        if(SectorWriteLeft > 0)                                                //若存在不足一个扇区的数据,则需要多写入一个扇区            SectorWriteTotal += 1;          SectorPhyLocation = sector * SPI_FLASH_SECTOR / PHY_SECTOR_SIZE;       //计算写入数据的起始位置对应到芯片上的第几个扇区        for(i = 0; i < SectorWriteTotal;i++)        {            SectorPhyLocationAddr = (SectorPhyLocation + i) * PHY_SECTOR_SIZE;          //下一个要操作的扇区物理地址            SPI_FLASH_BufferRead(EraseBuff,SectorPhyLocationAddr,PHY_SECTOR_SIZE);      //把当前扇区的所有内容读出来放入缓冲区            SPI_FLASH_SectorErase(SectorPhyLocationAddr);                               //将当前扇区的内容擦除            CurrenSectorLeft = PHY_SECTOR_SIZE;                                         //重置当前扇区剩余的字节个数为整个扇区的大小            if(AddrStart != SectorPhyLocationAddr)                                      //判断若要写入的地址与扇区的起始地址不重合,则证明扇区起始地址与要写入的地址间原本可能存在数据            {                SPI_FLASH_BufferWrite(EraseBuff,SectorPhyLocationAddr,(AddrStart - SectorPhyLocationAddr));    //将两地址间的有效数据反写                CurrenSectorLeft = CurrenSectorLeft - (AddrStart - SectorPhyLocationAddr);                     //当前扇区剩余字节数减去已写入的字节数量            }            if(WriteLeftBytes <= CurrenSectorLeft)                                       //判断当前扇区可以把剩下要写入的数据装下            {                SPI_FLASH_BufferWrite((u8 *)buff,AddrStart,WriteLeftBytes);              //则在起始位置写入剩下所有的数据                AddrStart += WriteLeftBytes;                                             //起始地址调整到末尾  (此时此操作可能无意义)                CurrenSectorLeft = CurrenSectorLeft - WriteLeftBytes;                    //扇区剩余字节减去写入数量                WriteLeftBytes = 0;                                                      //剩余写入数据量为0,写入完毕            }            else                                                                         //当前扇区只可以装入一部分要写入的数据            {                SPI_FLASH_BufferWrite((u8 *)buff,AddrStart,CurrenSectorLeft);             //将数据写满该扇区的剩余部分               AddrStart += CurrenSectorLeft;                                            //写入地址调整               WriteLeftBytes -= CurrenSectorLeft;                                       //剩余写入字节数调整               CurrenSectorLeft = 0;                                                     //当前扇区剩余字节数清0            }            if(CurrenSectorLeft > 0)                                                     //若当前扇区未被写满,则将剩余部分缓冲区反写回Flash                SPI_FLASH_BufferWrite(EraseBuff,AddrStart,CurrenSectorLeft);            }           res = RES_OK;        return res;    }    return RES_PARERR;}

disk_ioctl 至少增加GET_SECTOR_COUNT、GET_BLOCK_SIZE、CTRL_SYNC三个状态的返回值,GET_SECTOR_COUNT的返回值为按照读取扇区来算总的扇区个数,GET_BLOCK_SIZE的返回值为读取扇区的大小,CTRL_SYNC返回值为RES_OK即可。

DRESULT disk_ioctl (    BYTE pdrv,      /* Physical drive nmuber (0..) */    BYTE cmd,       /* Control code */    void *buff      /* Buffer to send/receive control data */){    DRESULT res;    switch (pdrv) {    case SPI_FLASH :        switch(cmd)        {        case GET_SECTOR_COUNT:              (*(DWORD *)buff) = 32768;              res = RES_OK;              break;        case GET_BLOCK_SIZE:              (*(DWORD *)buff) = SPI_FLASH_SECTOR;              res = RES_OK;              break;        case CTRL_SYNC:              res = RES_OK;              break;            }        return res;    }    return RES_PARERR;}

至此,文件系统移植完毕,调用的时候f_mkfs(“0:”,0,512);只需要调用一次,其他的测试如下

void FatfsTest(){    FATFS fs;    FIL fsrc;    BYTE buffer[4096];    FRESULT res;    UINT br, bw;    BYTE if_Erase = 0;    BYTE if_mkfs = 0;    SPI_FLASH_Init();    if(if_Erase)        SPI_FLASH_BulkErase();    res = f_mount(&fs,"0:",1);    if(res == FR_NO_FILESYSTEM)    {        f_mkfs("0:",0,512);        res = f_mount(&fs,"0:",1);    }    res = f_open(&fsrc, "0:src", FA_CREATE_ALWAYS | FA_WRITE | FA_READ);    res = f_write(&fsrc, "12345", 5, &bw);    res = f_close(&fsrc);    res = f_open(&fsrc, "0:src",  FA_WRITE | FA_READ);    res = f_read(&fsrc, buffer, sizeof(buffer), &br);}

至此移植完成。

移植中遇到的问题:

1.执行挂载f_mount时出现FR_NO_FILESYSTEM提示。原因为没有执行f_mkfs,整个flash没有文件系统格式化。

2.执行过f_mkfs,但是依然出现FR_NO_FILESYSTEM的提示。仔细查找跟踪代码后,发现disk_write(pdrv, fs->win, 0, 1)写入flash后立刻读出数据不一致,尤其是tbl[4] = sys; 写入错误会导致读出的时候找不到具体对应的文件系统,从而导致错误提示。原因是写入函数没有将原先的数据擦除,原先将该区域写入0,之后没有擦除则无法恢复。Flash的特性是1可以翻转写入0,但0不能写入1,只有擦除后,才可以正确。因此修改了disk_write,增加了先擦出再写入。

0 0
原创粉丝点击