基于RT-Thread:W25Q128虚拟U盘与文件系自由切换
winUSB设备的开发方法
记录下SPI Flash U盘实现过程中踩过的坑,与您分享。
前提条件是,需要先将SPI Flash 配置到elm fal文件系统,并挂载成功。如下图
然后开始配置USB
1,在CubeMX,选择SUB_OTG_FS
2 选择USB Device
3,确认USB时钟为48MHz
4,生成代码,然后打开生成的工程,如下
【1】将工程中void SystemClock_Config(void)的代码更新到RT-thread 平台board.c相同的函数内替换掉原有代码。
【2】将生成的USB 引脚初始化程序复制到board.c 内。
void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(pcdHandle->Instance==USB_OTG_FS)
{
/* USER CODE BEGIN USB_OTG_FS_MspInit 0 */
/* USER CODE END USB_OTG_FS_MspInit 0 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USB_OTG_FS GPIO Configuration
PA11 ------> USB_OTG_FS_DM
PA12 ------> USB_OTG_FS_DP
*/
GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Peripheral clock enable */
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
/* Peripheral interrupt init */
HAL_NVIC_SetPriority(OTG_FS_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(OTG_FS_IRQn);
/* USER CODE BEGIN USB_OTG_FS_MspInit 1 */
/* USER CODE END USB_OTG_FS_MspInit 1 */
}
}
void HAL_PCD_MspDeInit(PCD_HandleTypeDef* pcdHandle)
{
if(pcdHandle->Instance==USB_OTG_FS)
{
/* USER CODE BEGIN USB_OTG_FS_MspDeInit 0 */
/* USER CODE END USB_OTG_FS_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USB_OTG_FS_CLK_DISABLE();
/**USB_OTG_FS GPIO Configuration
PA11 ------> USB_OTG_FS_DM
PA12 ------> USB_OTG_FS_DP
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);
/* Peripheral interrupt Deinit*/
HAL_NVIC_DisableIRQ(OTG_FS_IRQn);
/* USER CODE BEGIN USB_OTG_FS_MspDeInit 1 */
/* USER CODE END USB_OTG_FS_MspDeInit 1 */
}
}
5,配置RT-Thread平台代码
【1】在RT-thread studio中打开RT-Thread Settings,然后在右上角的搜索栏内输入USB
【2】选择使用USB,设置如下
注意,磁盘名是指SPI Flash 文件系统分区时指定的名称,将来USB组件需要通过该名称查找注册的设备进行关联
系统挂载分区时需要用到此名字
6,打开board/Kconfig,加入如下代码后保存。
config BSP_USING_USBD
bool "Enable OTGFS as USB device"
select RT_USING_USB_DEVICE
default n
config BSP_USBD_EP_ISOC
bool
default n
depends on BSP_USING_USBD
config BSP_USING_STM32_SDIO
bool "Enable SDIO"
select RT_USING_SDIO
select PKG_USING_STM32_SDIO
default n
help
BSP_USING_STM32_SDIO use drv_sdio_adapter.c,and
BSP_USING_SDIO use drv_sdio.c
config BSP_USING_ON_CHIP_FLASH
bool "Enable On Chip FLASH (LittleFS)"
select BSP_USING_FS
select RT_USING_DFS
select RT_USING_DFS_ROMFS
select RT_USING_MTD_NOR
select PKG_USING_FAL
select FAL_USING_AUTO_INIT
select FAL_PART_HAS_TABLE_CFG
select PKG_USING_LITTLEFS
default n
7,在RT-Thread Settings中开启USBD功能选项
8,保存RT-Thread Settings更新配置后,重新编译
硬件上确保USB线序正确,D+线配置成上拉或者加上拉电阻。然后重新在程序,将此USB连线插入电脑,几秒钟后显示如下
然后打开U盘,新建文件text.txt,然后操作如下
保存后关闭文件,拔出U盘
在finsh命令端口操作如下
说明新建文件已经成功保存到SPI flash内。
但如果想要将Flash的一个分区挂载到USB上虚拟成U盘,怎么办?经过摸索证明,是可以做得到的。
9, 更改配置,这里我使用了TinyUSB软件包
注意上面划红线的地方必需一致。
10,然后保存配置,然后下载,运行后如下
11,问题是,虽然能保存文件了,但是不能rt-thread和USB之间自由切换,无法在上位机PC上存文件,然后在rt-thread系统里读文件。因为我USB只有D+ 和 D- 可用,Vbus 和 FS_ID已被占用,在Tiny官网也没找到有用答案,只能修改源码来解决问题。
【1】在打开TinyUSB-v0.13.0\rt-thread\port\msc_device_port.c文件,定位到文件开头部分,加入如下定义:
static bool ejected = false;
//static rt_device_t flash_device;
rt_device_t usb_flash_dev = RT_NULL;
uint8_t flag_usb_unplugged = 0;
uint8_t flag_usb_plugged = 0;
static struct rt_device_blk_geometry blk_geom;
其中usb_flash_dev不是必需的,因为和避免spi_flash_sfud.c文件中的变量重名而改。定义了两个标志变量,然后向下到tud_msc_test_unit_ready_cb()内,if (ejected)和if (usb_flash_dev == NULL)之间加入下面代码
if (ejected)
{
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
return false;
}
if (!flag_usb_plugged)
{
flag_usb_plugged = 1;
extern void unmount_from_rtt(void);
unmount_from_rtt();
}
if (usb_flash_dev == NULL)
{
usb_flash_dev = rt_device_find(PKG_TINYUSB_DEVICE_MSC_NAME);
}
【2】定位到TinyUSB-v0.13.0\src\device\usbd.c的void tud_task (void)内,case DCD_EVENT_SUSPEND处,在if ( _usbd_dev.connected )下面如下下面代码:
case DCD_EVENT_SUSPEND:
// NOTE: When plugging/unplugging device, the D+/D- state are unstable and
// can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ), which result in a series of event
// e.g suspend -> resume -> unplug/plug. Skip suspend/resume if not connected
if ( _usbd_dev.connected )
{
extern uint8_t flag_usb_unplugged;
flag_usb_unplugged = 1;
TU_LOG2(": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en);
if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en);
}else
{
TU_LOG2(" Skipped\r\n");
}
break;
【3】定位到项目\applications\main.c循环体内,加入如下代码:
while (1)
{
if (flag_usb_unplugged)
{
if (usb_flash_dev != RT_NULL )//&& usb_flash_dev->open_flag != 0
{
extern void dcd_event_unpluged_self (uint8_t rhport, bool in_isr);
dcd_event_unpluged_self(0, true);
rt_device_close(usb_flash_dev);
//usb_flash_dev = RT_NULL;
extern void mount_to_rtt(void);
mount_to_rtt();
}
flag_usb_plugged = 0;
flag_usb_unplugged = 0;
}
rt_thread_mdelay(500);
// //LOG_D("Hello RT-Thread!");
}
【4】定位到TinyUSB-v0.13.0\src\device\usbd.c的dcd_event_bus_signal()和dcd_event_bus_reset()之间,加入dcd_event_unpluged_self()函数,代码如下:
void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = eid };
dcd_event_handler(&event, in_isr);
}
void dcd_event_unpluged_self (uint8_t rhport, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED };
event.bus_reset.speed = 0;
dcd_event_handler(&event, in_isr);
}
void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_BUS_RESET };
event.bus_reset.speed = speed;
dcd_event_handler(&event, in_isr);
}
【5】新建两个函数用于挂载和卸载
#if defined (RT_USB_DEVICE_MSTORAGE) || defined (PKG_TINYUSB_DEVICE_MSC)
void mount_to_rtt(void)
{
rt_device_t dev = rt_device_find(SFLASH_PARTITION_NAME);
if (dev != RT_NULL)//&& usb_device->open_flag != 0
{
//rt_device_close(dev);
//rt_kprintf("close usb device ok!\n");
if (dfs_mount(SFLASH_PARTITION_NAME, SFLASH_FILESYSTEM_PATH, SFLASH_FILESYSTEM_TYPE, 0, 0) == 0) //"/"
{
LOG_I("Mount to /sf ok!\n");
}
}
else
{
LOG_E("Mount to /sf failed!\n");
}
}
//MSH_CMD_EXPORT( mount_to_rtt, mount to rtt);
void unmount_from_rtt(void)
{
char *fullpath = NULL;
fullpath = dfs_normalize_path(NULL, SFLASH_FILESYSTEM_PATH);//"/"
rt_device_t dev = rt_device_find(SFLASH_PARTITION_NAME);
if (dev != RT_NULL && dev->open_flag != 0)
{
rt_device_close(dev);
LOG_I("Unmount from /sf ok!\n");
if (dfs_unmount(fullpath) == RT_EOK)
{
LOG_I("Usb disk unmount ok!\n");
}
else
{
LOG_E("Usb disk unmount failed!\n");
}
}
else
{
LOG_E("Unmount from /sf failed!\n");
}
}
//MSH_CMD_EXPORT(unmount_from_rtt, unmount from rtt);
//INIT_APP_EXPORT(mount_to_usb);
#endif
12,修改后重新编译下载,OK