原创 使用Cubemx移植FatFs到stm32

2018-7-5 16:32 3380 16 4 分类: MCU/ 嵌入式 文集: stm32

排版有问题请看这里

在大型的存储器中,没有文件系统是万万不可行的,你不可能每次要打开一个文件都要从头到尾扫描一遍存储器,几兆的小存储器还好,几G甚至几T的存储器就根本没办法这么做了。而且有了文件系统也可以方便的管理使用各类文件。

这一次使用Cubemx生成FatFs的初始化代码,然后做最后的移植工作。本人使用的是stm32f767的野火的板子。

介绍一下FafFs

系统架构

FatFs是一种中间层,可以屏蔽硬件的差异,移植起来非常方便

移植需要注意的地方

你需要提供FatFs需要的底层I/O函数,需要的函数如下表,但是并不是全部函数都需要,你只需要提供必需的disk_status disk_initialize disk_read 和你需要的就可以。


开始移植配置Cubemx

因为我准备的是W25Q128上的FatFs,CubeMX上面没有,所以要选择user-defined


接下来是在Configuration里面配置FatFs,因为需要读取的文件是中文的,所以要配置为中文,然后为以后可能还有别的存储器预留空位,所以配置VOLUMES为3


还有就是在project setting里面把stack设置的大点


此外就是W25Q128的基本配置,这些就不提供了,网上有,可以参考微雪课堂里的配置。

接下来就是生成代码了

补完底层I/O函数

打开user_disko.c这个文件,可以看到CubeMX自动生成的函数原型。由于我需要读写磁盘,所以我需要补全这些所有的函数USER_initialize USER_status USER_read USER_write USER_ioctl

遇到问题和解决问题
f_mount返回FR_DISK_ERR

这个问题困扰了我好几个小时,我从f_mount->get_ldnumber用printf找了差不多1个小时的BUG,发现找不到问题,突然间发现我f_mount还调用了一个函数find_volume,而且就算这里出现了问题,就换方向一路追踪find_volume->check_fs->move_window->disk_read,发现是disk_read返回的错误,看到这我就傻眼了,觉得不可能啊,因为我之前读取写入flash都是没有问题的。

第二天脑袋比较清醒了,首先先测试初始化是否成功,在读取成功flash id后确定了初始化成功。

然后再来看看disk_read,它使用的是我提供的一个全局的disk io驱动来读取disk,结构体类型如下


  1. typedef struct

  2. {

  3.  uint8_t                 is_initialized[_VOLUMES];

  4.  const Diskio_drvTypeDef *drv[_VOLUMES];

  5.  uint8_t                 lun[_VOLUMES];

  6.  volatile uint8_t        nbr;

  7. }Disk_drvTypeDef;


所以我回到了userdiskio.c查看我的USERread函数


  1. DRESULT USER_read (

  2.    BYTE pdrv,      /* Physical drive nmuber to identify the drive */

  3.    BYTE *buff,     /* Data buffer to store read data */

  4.    DWORD sector,   /* Sector address in LBA */

  5.    UINT count      /* Number of sectors to read */

  6. )

  7. {

  8.  /* USER CODE BEGIN READ */

  9.  FLASH_DEBUG_FUNC();

  10.  DRESULT res = RES_ERROR;

  11.    if ((Stat & STA_NOINIT))

  12.    {

  13.        res = RES_NOTRDY;

  14.    }

  15.  else

  16.  {

  17.    sector+=1536;//扇区偏移,外部Flash文件系统空间放在外部Flash后面6M空间

  18.    res = BSP_QSPI_Read(buff, sector <<12, count<<12);

  19.  }

  20.    return res;  

  21.  /* USER CODE END READ */

  22. }


我在USER_read的else括号中加了一个printf,结果发现调用f_mount时,居然没有进入这个else语句,然后我开始怀疑这个Stat有问题,所以我把if的条件去掉了,直接执行sector+=1536;res = BSP_QSPI_Read(buff, sector <<12, count<<12);这条语句,发现FatFs挂载成功。

很激动,原来问题在于Stat的状态没有更新,因为Stat一开始定义的时候是定义为STA_NOINIT


  1. /* Disk status */

  2. static volatile DSTATUS Stat = STA_NOINIT;


接下来就是查找disk的状态没有更新的原因。回到一开始找到返回错误信息的函数find_volume,它的执行过程相当于get_ldnumber->disk_initialize->check_fs...一开始就是到了check_fs这一步的时候报错,停止程序的,我们可以发现,这个过程并没有调用到用户定义的USER_status或FatFs定义的disk_status来检查disk的状态更新。

所以我在初始化中加入了检查disk的状态的代码


  1. DSTATUS USER_initialize (

  2.    BYTE pdrv           /* Physical drive nmuber to identify the drive */

  3. )

  4. {

  5.  /* USER CODE BEGIN INIT */

  6.  Stat = USER_status(pdrv);

  7.  return BSP_QSPI_Init(); /* Flash的初始化 */

  8.  /* USER CODE END INIT */

  9. }


效果很好,disk的状态更新成功,f_mount挂载正常。

f_open返回FR_INVALID_NAME

好不容易挂载成功了,但是读写还是失败,第一反应是可能在编码上出错了,因为我的工程没有包括cc936.c这个文件,而野火官方的工程却包括了,我怀疑是不是这一点导致无法读取中文,从而FR_INVALID_NAME。

但是我现在还不知道FatFs是怎么调用cc936.c这个文件的,只能先从f_open跟踪进去看看。

f_open->follow_path,打LOG发现是follow_path中的create_name返回的FR_INVALID_NAME

进入create_name找到返回FR_INVALID_NAME的语句


  1. ...

  2. mem_set(sfn, ' ', 11);

  3. ...

  4. for (;;;) {

  5.    ...

  6.    if (c == '.' || i >= ni) {        /* End of body or over size? */

  7.            if (ni == 11 || c != '.') return FR_INVALID_NAME;   /* Over size or invalid dot */

  8.            i = 8; ni = 11;             /* Goto extension */

  9.            continue;

  10.    }

  11.    ...

  12. }


由于对于文件名一开始只分配了11个空间,但是我的文件名却超过了11个空间,所以导致了这个问题,在我将我的文件名减少后,问题解决。

文件名长度才11有点短,但是可以通过修改支持长文件名来支持。


在支持长文件名后,读写长文件名没有问题了



可以改进的地方

本工程没有加入RTC的支持,所以没有去实现get_fattime,因此不支持多磁盘,想支持多磁盘需要实现get_fattime

本工程已上传github
https://github.com/greedyhao/stm32



———— / END / ————


文章评论0条评论)

登录后参与讨论
我要评论
0
16
关闭 站长推荐上一条 /2 下一条