需求:将W25Q128部分空间搭载文件系统,并虚拟成U盘可通过USB连接至电脑实现文件拷贝
硬件:STM32F103ZET6 + W25Q128
说明:默认W25Q128已调通,可搭载文件系统
RT-Thread:W25Q128虚拟U盘并搭载文件系统
RT-Thread:STM32F407虚拟U盘,无法识别拔出问题解决方案
- 1、W25Q128分区
使用fal组件给spi flash 分区(如何分区见fal介绍),分区表如下,调试时usb_disk暂分4M空间

注:每个分区的起始地址需要是1024的整数倍

保存配置后,更新、编译工程,会发现工程多了usb相关文件,如下图:

- 4、开启USB时钟
这一步注意一点USB时钟必须为48MHZ,使用STM32CubeMX配置


经过测试,事实上,上面两图中的USB以及USB DEVICE配置根本没用到,只需要配置好USB的时钟


- 5、工程修改
我这取消了函数stm_usbd_register()的自启动,把它放在了main函数里测试,

stm_usbd_register启动usbd线程大概流程如下:

编译工程无误后下载,运行程序得到如下所示:
可见usb_disk分区已被创建成Block Device且当前未被占用

连接USB后,再次list_device,如下所示,发现usb_disk分区被USB占用

且电脑成功识别到U盘,可在内创建文件


- 6、如何解除USB占用?
和电脑断开连接后,发现USB并未解除占用

分析中断函数发现



这里需要调用函数HAL_PCD_DisconnectCallback(hpcd),阅读代码发现该函数中已实现具体功能,流程如下图示:

注:第三步截图有误
最终会调用函数_function_disable(ufunction_t func)来解除占用

因此可以在解除占用后将文件系统挂载到usb_disk分区,如下图所示:

注:我的工程中将elm挂载到usb_disk分区的根目录,系统启动时手动调用函数 int rt_spi_w25q128_init(void);
//w25q128初始化
int rt_spi_w25q128_init(void)
{
struct fal_blk_device *blk_dev;
struct statfs elm_stat;
//step1:给spi2总线上注册0号从设备,片选PB12
rt_hw_spi_device_attach(SPI_BUS_NAME, W25Q_SPI_DEVICE_NAME, GPIOB, GPIO_PIN_12);
//step2:将W25Q128挂载到spi2总线上,设备名spi20
if (rt_sfud_flash_probe(W25Q_FLASH_NAME,"spi20"))
{
rt_kprintf("[D/w25q128]rt sfud flash probe success!\n");
}
else
{
rt_kprintf("[E/w25q128]rt sfud flash probe failed!\n");
return RT_ERROR;
}
//step3:验证spi驱动以及W25Q128驱动是否正常
if (rt_device_find("W25Q128") != RT_NULL)
{
rt_kprintf("[D/w25q128]W25Q128 successfully mounted to bus spi2 \n");
}
else
{
rt_kprintf("[E/w25q128]find W25Q128 failed!\n");
goto NEXT_STEP;
}
//step4:flash抽象层初始化,分区管理
fal_init();
//step5:创建块设备usb_disk
blk_dev = (struct fal_blk_device *)fal_blk_device_create(USB_DISK_DEV_NAME);
FLASH_MOUNT:
//挂载elm到usb_disk分区根目录 "/"
if(dfs_mount(USB_DISK_DEV_NAME, "/", "elm", 0, 0) == 0)//挂载elm文件系统到根目录
{
rt_kprintf("[D/w25q128]elm fs mount to '/' success.\n");
}
else
{
rt_kprintf("[E/w25q128]elm fs mount to '/' failed!\n");
//首次挂载文件系统需要将usb_disk分区格式化elm文件系统
if(dfs_mkfs("elm", USB_DISK_DEV_NAME) == 0)
rt_kprintf("[D/w25q128]make elm fs success.\n");
goto FLASH_MOUNT;
}
//获取文件系统信息
if(statfs("/", &elm_stat) == 0)
rt_kprintf("[D/w25q128]elm fs block size: %d, total blocks: %d, free blocks: %d.\n", elm_stat.f_bsize, elm_stat.f_blocks, elm_stat.f_bfree);
NEXT_STEP:
return RT_EOK;
}
为了方便操作,将文件系统挂载和卸载再次封装了一下
//usb_disk分区挂载文件系统
void usb_disk_mount(void)
{
if(rt_device_find("usb_disk") != RT_NULL)
{
FLASH_MOUNT:
/* mount elmfat file system to usb_disk */
if(dfs_mount("usb_disk", "/", "elm", 0, 0) == 0)//挂载文件系统到根目录
{
rt_kprintf("[D/w25q128]elmfs mount to usb_disk '/' success.\n");
}
else
{
rt_kprintf("[E/w25q128]elmfs mount to usb_disk '/' failed!\n");
/* make a elmfat format filesystem */
if(dfs_mkfs("elm", "usb_disk") == 0)//将usb_disk分区格式化elm文件系统
{
rt_kprintf("[D/w25q128]make elmfs success.\n");
}
goto FLASH_MOUNT;
}
}
}
//usb_disk分区卸载文件系统
void usb_disk_unmount(void)
{
if(rt_device_find("usb_disk") != RT_NULL)
{
/*卸载文件系统*/
if(dfs_unmount("/") == 0)
{
rt_kprintf("[D/w25q128]elmfs unmount success!\n");
}
else
{
rt_kprintf("[E/w25q128]elmfs unmount failed!\n");
}
}
}
- 7、如何解除文件系统占用?
连接到电脑后,应立即卸载文件系统,再使能U盘功能。调用过程如下:

到此即实现了U盘和文件系统的自由切换

学习到了😁
收藏!💪
不错,收藏
学习,收藏
大佬用的是哪个版本的代码 我的根本就没出显usb_disk设备 usbd出现了
好像是v4.0.3@zhj_rt
学会了
我利用romfs建立了/fat和/vdisk挂载点,usb虚拟u盘放在/vdisk下,现在可以显示usb slave device,但是插入PC后没反应,list_devie前后没变化,请问是哈情况?
可以发下你得源码地址,对比下看是啥问题吗
单独虚拟u盘今天也发现一个问题,U盘插入和拔出调用_function_enable/disable不正确:第一次插入时调用_function_enable,断开时并不调用_function_disenable,而是在第二次插入时调用_function_disenable,这是啥情况?