stm32cube+fatfs+spi_flash(W25Q128)

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

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

介绍一下FafFs

系统架构

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

stm32cube+fatfs+spi_flash(W25Q128)_第1张图片

stm32cube+fatfs+spi_flash(W25Q128)_第2张图片

移植需要注意的地方

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

stm32cube+fatfs+spi_flash(W25Q128)_第3张图片

开始移植

配置Cubemx

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

stm32cube+fatfs+spi_flash(W25Q128)_第4张图片

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

stm32cube+fatfs+spi_flash(W25Q128)_第5张图片

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

stm32cube+fatfs+spi_flash(W25Q128)_第6张图片

此外就是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,结构体类型如下

typedef struct

{

 uint8_t                 is_initialized[_VOLUMES];

 const Diskio_drvTypeDef *drv[_VOLUMES];

 uint8_t                 lun[_VOLUMES];

 volatile uint8_t        nbr;

}Disk_drvTypeDef;

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

DRESULT USER_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 */

)

{

 /* USER CODE BEGIN READ */

 FLASH_DEBUG_FUNC();

 DRESULT res = RES_ERROR;

   if ((Stat & STA_NOINIT))

   {

       res = RES_NOTRDY;

   }

 else

 {

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

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

 }

   return res;  

 /* USER CODE END READ */

}

我在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

/* Disk status */

static volatile DSTATUS Stat = STA_NOINIT;

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

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

DSTATUS USER_initialize (

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

)

{

 /* USER CODE BEGIN INIT */

 Stat = USER_status(pdrv);

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

 /* USER CODE END INIT */

}

效果很好,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的语句

...

mem_set(sfn, ' ', 11);

...

for (;;;) {

   ...

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

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

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

           continue;

   }

   ...

}

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

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

stm32cube+fatfs+spi_flash(W25Q128)_第7张图片

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

stm32cube+fatfs+spi_flash(W25Q128)_第8张图片

可以改进的地方

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

本工程已上传github

https://github.com/greedyhao/stm32

———— / END / ————

 

你可能感兴趣的:(stm32)