需求:将W25Q128部分空间搭载文件系统,并虚拟成U盘可通过USB连接至电脑实现文件拷贝
硬件:STM32F103ZET6 + W25Q128
说明:默认W25Q128已调通,可搭载文件系统
RT-Thread:W25Q128虚拟U盘并搭载文件系统
RT-Thread:STM32F407虚拟U盘,无法识别拔出问题解决方案
1、W25Q128分区
使用fal组件给spi flash 分区(如何分区见fal介绍),分区表如下,调试时usb_disk暂分4M空间
注:每个分区的起始地址需要是1024的整数倍
2、在env中使能USB
使用menuconfig打开配置环境
3、开启大容量存储设备
保存配置后,更新、编译工程,会发现工程多了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;
rt_hw_spi_device_attach(SPI_BUS_NAME, W25Q_SPI_DEVICE_NAME, GPIOB, GPIO_PIN_12);
if (rt_sfud_flash_probe(W25Q_FLASH_NAME,"spi20"))
{
rt_kprintf("[D/w25q128]rt sfud flash probe success!
");
}
else
{
rt_kprintf("[E/w25q128]rt sfud flash probe failed!
");
return RT_ERROR;
}
if (rt_device_find("W25Q128") != RT_NULL)
{
rt_kprintf("[D/w25q128]W25Q128 successfully mounted to bus spi2
");
}
else
{
rt_kprintf("[E/w25q128]find W25Q128 failed!
");
goto NEXT_STEP;
}
fal_init();
blk_dev = (struct fal_blk_device *)fal_blk_device_create(USB_DISK_DEV_NAME);
FLASH_MOUNT:
if(dfs_mount(USB_DISK_DEV_NAME, "/", "elm", 0, 0) == 0)
{
rt_kprintf("[D/w25q128]elm fs mount to '/' success.
");
}
else
{
rt_kprintf("[E/w25q128]elm fs mount to '/' failed!
");
if(dfs_mkfs("elm", USB_DISK_DEV_NAME) == 0)
rt_kprintf("[D/w25q128]make elm fs success.
");
goto FLASH_MOUNT;
}
if(statfs("/", &elm_stat) == 0)
rt_kprintf("[D/w25q128]elm fs block size: %d, total blocks: %d, free blocks: %d.
", 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:
if(dfs_mount("usb_disk", "/", "elm", 0, 0) == 0)
{
rt_kprintf("[D/w25q128]elmfs mount to usb_disk '/' success.
");
}
else
{
rt_kprintf("[E/w25q128]elmfs mount to usb_disk '/' failed!
");
if(dfs_mkfs("elm", "usb_disk") == 0)
{
rt_kprintf("[D/w25q128]make elmfs success.
");
}
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!
");
}
else
{
rt_kprintf("[E/w25q128]elmfs unmount failed!
");
}
}
}
7、如何解除文件系统占用?
连接到电脑后,应立即卸载文件系统,再使能U盘功能。调用过程如下:
到此即实现了U盘和文件系统的自由切换
原作者:Panyh220213