使用ATK-NANO STM32F411的开发板,实现外部flash挂载虚拟文件系统。
本文最终工程下载
参考基于 RT-Thread Studio 的 SPI 驱动开发文档,基于自己使用的芯片,创建空工程,实现hello world。
虽然rt-thread将hal库的接口驱动都放在了C:\RT-ThreadStudio\download\rt-thread-sdk\rt-thread-src\v4.0.2\bsp\stm32\libraries\HAL_Drivers
目录下,但是该目录下的spi驱动最后更新日志都是2018年的,而且其中还有关于dma相关的配置;对于上手flash挂载虚拟文件系统,我们是不需要这些东西的。
所以可以直接在本地drivers
目录下创建相应的drv_spi.c
和drv_spi.h
的空文件,再去github复制drv_spi.c和drv_spi.h中的内容到本地。
根据硬件设计原理图,知道使用的是SPI2接口:
在文件drivers/board.c
中,添加使用SPI2的宏定义#define BSP_USING_SPI2
;
在文件drivers/stm32f4xx_hal_conf.h
中,解注释#define HAL_SPI_MODULE_ENABLED
以启用spi相关的函数。
此时编译应该是没有错误的。
在drv_spi.c
最后面,添加使用cubeMX生成的驱动代码:
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hspi->Instance == SPI2)
{
__HAL_RCC_SPI2_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_13 |GPIO_PIN_14 | GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
}
int rt_hw_spi_init(void)
{
rt_hw_spi_bus_register(SPI2, "spi2");
return 0;
}
INIT_BOARD_EXPORT(rt_hw_spi_init);
注意!!!!
这里的HAL_SPI_MspInit
强烈建议根据自己的硬件电路设计从CubeMX生成!!!
因为对于使用标准库在STM32F103进行开发的时候(一般不使用重映射),我们习惯修改相应的引脚、GPIO时钟、总线时钟、总线名这些,一般就修改完了,但转到HAL库,我们极有可能会忽视一个变量:GPIO_InitStruct.Alternate
。
举个例子,我这里使用的是SPI2(PB13、PB14、PB15),如果你用的是使用的SPI3(PB3、PB4、PB5),那么除了需要修改引脚编号,把所有的SPI2改成SPI3,GPIO_InitStruct.Alternate
也从GPIO_AF5_SPI2
改成了GPIO_AF5_SPI3
,但查看其宏定义,我们使用的还是AF5的功能!
而PB3、PB4、PB5的AF5复用实际上是SPI1,而不是SPI3!!!
参考数据手册,可以看到:
所以此时从代码整体上来看,我们会觉得没有问题,但实际上在SPI3总线上读不出来数据(因为引脚功能没复用到SPI3上去),虽然复用到了SPI1总线上,但由于没开SPI1的时钟,所以SPI1上也抓不到波形。
驱动配置完成后,就是软件层面的注册。
新建dfs_port.c
文件,添加初始化相关的内容:
#include
#include
#include "drv_spi.h"
static int rt_hw_spi_flash_init()
{
__HAL_RCC_GPIOB_CLK_ENABLE();
rt_hw_spi_device_attach("spi2", "spi20", GPIOB, GPIO_PIN_12);
if (NULL == rt_sfud_flash_probe("W25Q16", "spi20"))
{
rt_kprintf("spi flash probe failed!\n");
}
}
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
上述函数中rt_hw_spi_device_attach
实现了spi20设备的注册(即我们可以通过对spi20这个名字访问W25Q16设备),而rt_sfud_flash_probe
实现了W25Q16设备的解析(通过读取sfud信息,知道了它是一个spi flash设备,知道了它的容量,擦写大小那些),并把它挂载成一个块设备(Block Device),可直接以块为单位进行数据读写。
然后就是挂载文件管理系统:
static int mnt_init()
{
//mkfs("elm", "W25Q16");
if (dfs_mount("W25Q16", "/", "elm", 0, 0) == 0)
{
//rt_kprintf("dfs mount ok\n");
}
else
{
rt_kprintf("dfs mount failed\n");
}
}
INIT_ENV_EXPORT(mnt_init);
对于一个存储设备,肯定有相应的文件系统,比如windows用的ntfs,linux使用的ext4,同时还有一个方比较小巧适合嵌入式设备使用的FAT32文件系统,这里的dfs_mount("spi20", "/", "elm", 0, 0)
就实现了文件系统的挂载,其中elm代表的就是FAT32文件系统。
挂载文件系统的过程中,可能会出现以下提示:
The sector size of device is greater than the sector size of FAT.
这是因为实际设备中扇区的大小比文件系统的大小还要大。
在工程目录\rt-thread\components\drivers\spi\sfud\inc\sfud_flash_def.h
中,SFUD_FLASH_CHIP_TABLE
定义了使用的W25Q16设备
的扇区大小为4096(这个table是根据数据手册写的,所以不能改这里),那么需要修改工程中关于文件系统的参数:
如果还是格式化失败,可能是因为存储器设备没有FAT32文件系统的描述信息,可以使用mkfs("elm", "W25Q16");
将设备重新格式化成FAT32格式。
此时可以使用ls指令查看文件系统下的文件,使用echo往文件写入字符串,使用cat查看文件内容:
至此,文件系统挂载完成。