在RT-Thread STM32F407平台下配置SPI flash为U盘_stm32实现usb spi-程序员宅基地

技术标签: 嵌入式随笔  嵌入式硬件  

基于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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/aping_cs_dn/article/details/128947509

智能推荐

Word插件开发

创建一个新的 Office 插件项目:在 Visual Studio 中,选择"文件" -> “新建项目”,然后在模板中选择"Office/SharePoint",选择适当的 Office 插件项目模板,如 Word 插件、Excel 插件或 PowerPoint 插件。设计用户界面:在解决方案资源管理器中,打开你的插件项目,并在其中打开相应的 Office 文件(如 Word 文件、Excel 文件或 PowerPoint 文件)。你可以在 Office 应用中测试插件的功能,并在开发过程中进行调试。

便携式iv检测仪解析

在应用场景方面,便携式IV功率测试仪广泛应用于光伏电站的日常运维、光伏组件生产过程中的质量控制以及光伏项目的前期评估等环节。在光伏电站运维中,定期对光伏组件进行IV测试,可以及时发现性能下降或损坏的组件,为电站的运维提供有力支持。首先,从工作原理来看,光伏电站便携式IV功率测试仪通过模拟太阳光照射光伏组件,并测量组件在不同电压下的电流输出,从而绘制出IV曲线。此外,测试仪还可以计算光伏组件的功率输出、转换效率等参数,为用户提供全面的性能评估。

postgresql 索引之 hash_load_categories_hash postgres-程序员宅基地

文章浏览阅读3.6k次。os: ubuntu 16.04postgresql: 9.6.8ip 规划192.168.56.102 node2 postgresqlhelp create indexpostgres=# \h create indexCommand: CREATE INDEXDescription: define a new indexSyntax:CREATE [ UNIQUE ..._load_categories_hash postgres

face++实现人脸识别及人脸相似度对比_face++人脸识别 html5-程序员宅基地

文章浏览阅读4.8k次。使用face++,先获取key和secret下方是人脸识别,还添加了画出人脸轮廓的正方形下方是人脸识别,还添加了画出人脸轮廓的正方形 import requests#网络访问控件 from json import JSONDecoder#互联网数据交换标准格式 import cv2 as cv#图像处理控件 http_url ="https://a..._face++人脸识别 html5

desencrypt java md5_Java实现DES加密与解密,md5加密以及Java实现MD5加密解密类-程序员宅基地

文章浏览阅读322次。很多时候要对秘要进行持久化加密,此时的加密采用md5。采用对称加密的时候就采用DES方法了import java.io.IOException;import java.security.MessageDigest;import java.security.SecureRandom;import javax.crypto.Cipher;import javax.crypto.SecretKey;im..._java desencrypt.encrypt(pass)

BZOJ 2818 欧拉函数,线性筛_线性筛预处理质数表, 并求出欧拉函数, 预处理前缀和即可 bzoj2818boj-程序员宅基地

文章浏览阅读145次。题目链接:https://www.acwing.com/problem/content/description/222/给定整数N,求1<=x,y<=N且GCD(x,y)为素数的数对(x,y)有多少对。GCD(x,y)即求x,y的最大公约数。输入格式输入一个整数N输出格式输出一个整数,表示满足条件的数对数量。数据范围1≤N≤10^7输入样例:4..._线性筛预处理质数表, 并求出欧拉函数, 预处理前缀和即可 bzoj2818boj

随便推点

测试算法的性能(以选择排序为例)_算法性能测试-程序员宅基地

文章浏览阅读1.6k次。测试算法的性能 很多时候我们需要对算法的性能进行测试,最简单的方式是看算法在特定的数据集上的执行时间,简单的测试算法性能的函数实现见testSort()。【思想】:用clock_t计算某排序算法所需的时间,(endTime - startTime)/ CLOCKS_PER_SEC来表示执行了多少秒。【关于宏CLOCKS_PER_SEC】:以下摘自百度百科,“CLOCKS_PE_算法性能测试

Lane Detection_lanedetectionlite-程序员宅基地

文章浏览阅读1.2k次。fromhttps://towardsdatascience.com/finding-lane-lines-simple-pipeline-for-lane-detection-d02b62e7572bIdentifying lanes of the road is very common task that human driver performs. This is important ..._lanedetectionlite

【数据结构】静态表查找之顺序查找、二分查找、分块查找_读取表元是什么意思-程序员宅基地

文章浏览阅读4.1k次,点赞8次,收藏23次。​通过一定的方法找出与给定关键字相同的数据元素的过程叫做查找。也就是根据给定的某个值,在查找表中确定一个关键字等于给定值的记录或数据元素。_读取表元是什么意思

如何设置交易滑点?精确到tick 测算期货冲击成本(附源码)_滑点设置多少合适-程序员宅基地

文章浏览阅读8.3k次,点赞4次,收藏18次。我们在非撮合回测模式下,因为无法获知交易价格当时的真实盘口价差、挂单数量,常主观设定一个滑点均值,比如针对螺纹钢等合约,设置 1 跳,针对某些交易不活跃的品种,设置 2 跳。但是这种近乎拍脑袋的方法并不精确。我们今天尝试通过简单的辅助工具,实现尽可能接近准确的 tick 级别滑点设置,代码已写好,不用编程也可获得结果。_滑点设置多少合适

大数据技术之 Azkaban_azkaban要建立job之间的依赖关系需要使用-程序员宅基地

文章浏览阅读551次。尚硅谷大数据技术之 Azkaban—————————————————————————————更多 Java –大数据 –前端 –python 人工智能资料下载,可百度访问:尚硅谷官网尚硅谷大数据技术之 Azkaban(作者:尚硅谷大数据研发部)版本:V3.0一 概述1.1 什么是 AzkabanAzkaban 是由 Linkedin 公司推出的一个批量工作流任务调度器,主要用于在一个工作流内以一个特定的顺序运行一组工作和流程,它的配置是通过简单的 key:value 对的方式,通过配置中_azkaban要建立job之间的依赖关系需要使用

python批量修改文件编码格式,由utf-16 le 格式转为utf-8_utf16le转换utf8-程序员宅基地

文章浏览阅读5k次,点赞2次,收藏9次。#! python3# encoding: utf-8import osimport chardetdef strJudgeCode(str1): return chardet.detect(str1)"""def readFile(path): with open(path,'r',encoding='utf-16 le') as f: filecontent ..._utf16le转换utf8

推荐文章

热门文章

相关标签