在RT-Thread STM32F407平台下配置SPI flash为U盘

基于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软件包

image

 

 

 

 注意上面划红线的地方必需一致。

 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aping_cs_dn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值