为您推荐:
精华内容
最热下载
问答
  • 5星
    8KB qq_45569373 2021-06-23 21:23:26
  • 5星
    4.47MB lunzilx 2021-03-24 08:51:16
  • 5星
    218.92MB xiaolong1126626497 2021-08-03 15:21:12
  • 5星
    139KB qq_20232875 2021-01-08 15:29:56
  • 5星
    12KB u014627020 2020-11-24 18:16:41
  • 来源:公众号【鱼鹰谈单片机】作者:鱼鹰OspreyID :emOsprey本篇笔记主要介绍 STM32 相关的知识点,毕竟之后的 CDC 教程是用 STM32开发的。为了写这一篇,鱼...

    来源:公众号【鱼鹰谈单片机】

    作者:鱼鹰Osprey

    ID   :emOsprey

    本篇笔记主要介绍 STM32 相关的知识点,毕竟之后的 CDC 教程是用 STM32开发的。

    为了写这一篇,鱼鹰把STM32中文参考手册USB相关的从头到尾看了一遍,虽然以前就已经看过了,但这次看,收获又是不同。

    不过限于篇幅,鱼鹰不会面面俱到,只介绍和 CDC 相关的一些东西。

    要完成 USB 模拟串口(CDC)的实验,STM32 手册是必须细细阅读的,不然代码里面很多操作你是无法看懂的。

    其实理解了前面的一些东西,你会发现 STM32 中的 USB 知识和前面的大同小异,毕竟开发芯片的厂家也是按照 USB 标准来实现的,不会差到哪里去。

    硬件基础

    首先,STM32F103 使用 PA11(USBDM,D-)和PA12(USBDP,D+)完成数据的收发。但看过前面章节的道友应该知道,全速 USB 在 D+ 引脚是需要有一个上拉电阻的,同时两根数据线需要各自串联一个 22 Ω的电阻。

    这就是你需要的硬件基础,如果说你的开发板有 USB 接口,但是没有这些条件,那么你的 USB 接口只能用于供电,无法进行数据传输。

    当然,STM32F103的速度为全速 12 Mbit,换算成字节为 1.5 MB,除去 USB 协议的开销(令牌、打包等),大概能达到 1 MB/s 速度。

    鱼鹰在测试给各位道友的 CDC 例程发现只能达到100 KB 左右,原以为是主机没有及时发送令牌包导致带宽很低,后来发现 USB 设备发出的数据包只有几个字节,而不是最大包 64B,才知道是发送的数据太少了,后来增加发送的数据量(一次往缓冲多写几百个字节),带宽达到了 400~700KB,但离 1MB 还差了点。

    通过逻辑分析仪查看才知道,主机发送 IN令牌包时,设备有可能还没准备好,浪费了带宽,不过在看 STM32 资料中发现,对于批量传输(CDC使用批量传输),可以使用双缓冲提高传输量,估计用了双缓冲,传输速率能达到 1MB/s,比串口的 115200 Bit/s 快的多,也稳定的多,毕竟人家可是自带了 CRC 校验和数据重传功能的。

    软件基础

    现在看一看 STM32F103 的USB有哪些功能

    第一点,支持 USB2.0 全速,而不是2.0高速 480Mbit/s。

    有 1~8 个(双向)端点,这是能完成组合设备的基础,按照 CDC + DAP 组合设备来说,一共需要 1(控制传输)+ 2(CDC)+1(HID) = 4 个端点的,更不要说再模拟一个 U盘了。

    CRC、NRZI编解码,这个可以让你不必关心每一位是什么情况,你只需要处理底层给你的字节数据即可。

    支持双缓冲,最大程度的利用 USB的带宽。

    支持USB挂起和恢复操作,其实还支持设备远程唤醒操作,即由设备发起唤醒请求(比如鼠标移动后唤醒设备)。

    后面有一个注意点,就是 USB 和 CAN 共用 512 字节的缓存,也就是说同一时刻只能有一个外设可以工作,当然你可以通过软件在不同时刻使用不同的外设。

    可以看看 USB 设备框图,了解一下 USB 是由哪些结构组成的。

    为了实现 USB 通信,有以下基础步骤需要完成:

    1、打开 Port A 的外设时钟(PA11和PA12)

    2、打开 USB 时钟(其实还需要设置 USB 时钟频率,一般 SystemInit 会替你完成,当USB时钟打开后, PA11 和 PA12 引脚由 USB 接管,不归 GPIO 控制)。

    3、打开相应中断(一共有三个中断)

    低优先级中断是我们主要关注的,因为USB枚举过程就在这个中断完成,所以这个中断必须开启,其他两个就看需求了。

    4、配置 USB 寄存器,使 USB可以正常工作。

    5、之后所有的操作都在低优先级中断进行(包括复位、枚举、SOF检测等)。

    以上步骤具体可以看鱼鹰提供的例程实现,不再多说。

    USB 寄存器

    USB 中有三类寄存器:端点寄存器、通用寄存器、缓冲区描述表,再加上和描述表对应的缓冲区(数据收发缓存区,USB所有的数据传输都首先要经过这里),我们要做的就是在合适的时候对这些寄存器进行相应的操作即可。

    地址 0x 0x4000 5C00 开始为端点寄存器,因为有8个(双向)端点,所以有8个寄存器管理。

    之后的寄存器为通用寄存器,用于管理整个 USB 模块的,具体可查看参考手册。

    以上寄存器有些位很特殊,比如可能写0有效,写 1 无效,所以有如下要求:

    所以以往的读-改-写不能在这里使用,不然你这边读回了0,但是硬件修改了变成1,如果往回写 0 ,那么就把硬件设置的1 清除了,肯定会有影响,所以针对这种位,需要对不操作的位设置为 1 ,这样就不会意外修改了。

    还有可能写1翻转,写0无效,这时你会发现代码中使用异或(^)来设置需要的位,非常巧妙。

    总之,在学习 USB 过程中,可以锻炼你的位操作能力。

    上述两类寄存器在参考手册其实是比较详尽的,但缓冲区描述表(描述表的作用就是描述端点发送和接收缓存区的地址和大小)就显得晦涩难懂了,所以这里详细说一下缓冲区描述表(以下表述可能有问题,需要各位自行验证)。

    首先,描述表的地址在0x4000 6000,也就是说前面所说的 512 Byte 的基地址。但是按照参考手册中的描述来看,这个空间大小应该是 512 Byte * 2,这是因为 USB模块寻址采用 16 位寻址的,而应用程序使用 32位寻址,也就是说,按照我们的软件角度,空间分布应该是这样的:

    低地址的两个字节可以被我们访问(有颜色部分),高地址的两个字节不可访问(但是按照双缓冲描述来看,好像可以访问到,以后在验证一下)。

    所以地址范围应该有 1 KB的空间,但只有一半是可以使用的。

    还有一点就是这块空间不仅用于存放 USB 传输的数据,还用来存放缓存区描述表,这个缓冲区描述表可以在这块空间的任何一个位置(上图在缓冲区的最开始位置),只要满足 8 字节对齐即可,毕竟一个端点需要 16 字节记录(这里可能会感到疑惑,为什么一个端点16字节,但却是 8 字节对齐,这就是 16 位 和 32 访问的区别,在USB寄存器中,USB模块通过 16 位访问,所以寄存器里面的值都是按照 16 位来保存偏移的)。

    这个表的基地址存放在 USB_BTABLE 寄存器中,一般设置为 0,表示这个表放在上述空间的开始处。

    根据需要,依次安排描述表。比如 CDC 有三个端点,前 16 个字节安排端点 0,负责描述发送缓存区的地址和大小,接收缓存区的地址和大小(防止接收时溢出)

    端点 1 和端点 2 供 CDC 使用,占用32 字节。所以前48字节被描述表占用了,剩下的(1024 – 48)/ 2 就是数据缓冲区了。比如将端点 0 的发送缓冲区地址指向 0x18(相对地址0x4000 6000偏移,16位访问),大小为 64字节,端点0的接收缓存区指向 0x58(寄存器USB_ADDR0_RX写入的值,16位访问),大小为 64 字节(注意这里的值为 16位寻址,即 USB 模块的寻址,和应用层32位寻址不同,两者之间需要转化)。

    按理应该像上面分布空间的,但实际上你会发现分布如下:

    那么是否可以将端点 0 的缓存地址安排在 0x40006030 位置(即USB_ADDR0_TX值为 0x18 而不是上图的0x30呢),而不是 0x40006060 呢,这样就不会浪费那些空间了。

    因为这个改动会较大,感兴趣的可以尝试一下。

    当USB 模块写入端点 0 的数据时,首先根据 USB_BTABLE 的值找到描述表的位置,然后再根据描述表第一个表项的 USB_ADDR0_RX 找到接收缓冲区的地址,最后写入数据(写入过程中会判断是否超出限制,防止破坏其他缓冲区,这个通过 USB_COUNT0_Rx 判断),

    当应用程序进行读取上述地址的数据时,因为采用了 32 位访问,所以对USB_BTABLE 和 USB_ADDR0_RX偏移地址x2,这样就可以找到我们需要的缓存地址,从而读取到主机发给设备的数据,然后进行相应的处理。

    设备发送同理。

    具体实现可参考鱼鹰给出的源代码。

    推荐阅读:

    嵌入式系统优先级详解

    KEIL 调试经验总结

    线程CPU使用率到底该如何计算?

    许久以后,你会感谢自己写的异常处理代码

    终极串口接收方式,极致效率

    延时功能进化论(合集)

    如何写一个健壮且高效的串口接收程序?

    打了多年的单片机调试断点到底应该怎么设置?| 颠覆认知

    -THE END-


    如果对你有帮助,记得转发分享哦

    微信公众号「鱼鹰谈单片机」

    每周一更单片机知识

    长按后前往图中包含的公众号关注

    鱼鹰,一个被嵌入式耽误的畅销书作家

    个人微信「EmbeddedOsprey」

    长按后打开对方的名片关注

    展开全文
    weixin_42876465 2020-09-01 20:02:00
  • 所用的芯片是STM32F105RB,注意必须要有OTG的功能的芯片才可以的单板驱动USB向其内部读写数据,没有OTG功能只能做从机我理解为可以和电脑连接后STM32内部flash做为U盘. 从图上看我们知道只有F105/107和F2/4的...

     

    这几天需要往U盘里读写数据,需要升级单片机和向USB里写入测量数据。所用的芯片是STM32F105RB,注意必须要有OTG的功能的芯片才可以的单板驱动USB向其内部读写数据,没有OTG功能只能做从机我理解为可以和电脑连接后STM32内部flash做为U盘.

    从图上看我们知道只有F105/107和F2/4的单片机才具有OTG功能,而且F4还有FS/HS可以选择。

      说明: OTG_FS为 full speed 全速接口,   OTG_HS为 high speed 高速接口,对于全速接口就是我们一般使用最多的USB接口形式了,而高速的USB接口,一般需要配合USB的外部联合使用,但是在不使用外部PHY的情况下,也可以当做全速接口使用(这是在调试的时候,因为硬件已经连接成这样了,没办法最后百度才知道可以这样使用的,而我最终的使用方式也是用HS的接口,当FS使用)

      USB芯片也分为Controller部分和PHY部分。Controller部分主要实现USB的协议和控制。内部逻辑主要有MAC层、CSR层和FIFO控制层,还有其他低功耗管理之类层次。MAC实现按USB协议进行数据包打包和解包,并把数据按照UTMI总线格式发送给PHY(USB3.0为PIPE)。CSR层进行寄存器控制,软件对USB芯片的控制就是通过CSR寄存器,这部分和CPU进行交互访问,主要作为Slave通过AXI或者AHB进行交互。FIFO控制层主要是和DDR进行数据交互,控制USB从DDR搬运数据的通道,主要作为Master通过AXI/AHB进行交互。PHY部分功能主要实现并转串的功能,把UTMI或者PIPE口的并行数据转换成串行数据,再通过差分数据线输出到芯片外部。 

    一个关于PHY文章:https://blog.csdn.net/weiwei_xiaoyu/article/details/53347956
      一般来说,如果usb phy封装在芯片内,基本采用UTMI+的接口。不封装到芯片内的一般采用ULPI接口,这样可以降低pin的数量。 
      关于STM32芯片内嵌的OTG FS控制器、OTG HS控制器、OTG FS PHY具体见芯片手册

    画原理图时一定要注意。如果只是需要FS的功能画到hs端口还能补救,需要HS功能画到FS就不可以实现了

    注意芯片图上FS/HS接口是定义好的,我使用FS实现STM32做HOST(主机) 实现读写功能。

    看一下网上下载的驱动

    STSW-STM32046: 主要是针对STM32F105/7, STM32F2 and STM32F4 USB on-the-go Host and device library,对应的说明文档为UM1021。下载地址为https://www.st.com/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32046.html,该版本的驱动最新版本为2.2.0。下文就是以该驱动为例。
    STSW-STM32121: 主要是针对STM32F10x, STM32L1xx and STM32F3xx USB full speed device library,对应的说明文档为UM0424。 
    下载地址为https://www.st.com/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32121.html,该版本的驱动最新版本为4.0.1。STSW-STM32092: 主要是针对STM32F0x2xx USB FS device library,对应的说明为UM1717。下载地址为https://www.st.com/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32092.html,该版本驱动的最新版本为1.0.0。这里需要注意,该驱动是针对STM32F0x2xx的,但是可以非常方便的移植到STM32F0xx系列得MCU中,需要注意的是,需要根据具体芯片的时钟,修改驱动的usb_conf.h和usb_bsp.c。
     

    我们确定自己要用的功能。

    先说一下USB功能驱动函数的分类。

    USB只有两个功能:1,做驱动device相当于做从机,2,做主机即HOST。

    重点在Libraries目录中。其中的USB OTG是USB Device和USB Host 的基础。在实际使用,USB OTG是USB Device和USB Host 的底层驱动。(在一开始学习时,还以为每部分都可以独立使用!!)。 

    参考文章 :https://blog.csdn.net/zcshoucsdn/article/details/78944536,做主机即HOST

    https://blog.csdn.net/ZCShouCSDN/article/details/78936456  ,做驱动device相当于做从机

     

    如果是要做驱动 device相当于做从机 就是OTG+device

    如果是坐主机就是OTG+host,

    开始移植

    个人建议打开库里的例程复制粘贴。

    我的移植比较简单。

    简单来说USB_HOST文件夹下的文件是不需要修改的只要我们从库里找到这些文件添加进来就好了,我把我的头文件c文件夹给大家看一下。

    这些文件不需要修改,直接复制进来就好了。注意不使用USB_device时是不需要调用的。我这个是一开始移植不懂都拉进来实际程序中也没有用到,删除减少代码大小

    接下来是六个放在我们主函数中的文件

    介绍一下,usb_bsp.c定义了我们USB的IO口和电源。C文件需要修改,H文件不动。

    usb_conf.h,

    usbh_conf.h, 这两个函数都是配置函数头文件。一定要修改

    usbh_usr.c/.h,这里面都是usb的回调函数和中断函数,我在里面添加了和FATFS的接口函数。即对U盘的操作函数。

    接下来因为我们要操作U盘因为U盘是FAT的储存介质。很自然要用到FATFS文件

    FATFS库下载地址:http://elm-chan.org/fsw/ff/00index_e.html 

    接下来移植fatfs文件这个官网上有很多解释我就不多写了

    程序里要添加的文件

    src文件夹下是我们的文件

    option文件夹下都是字库

    到此全部移植完成。

    修改我们之前提到的文件

    /**
      ******************************************************************************
      * @file    usb_bsp.c
      * @author  MCD Application Team
      * @version V1.2.0
      * @date    09-November-2015
      * @brief   This file implements the board support package for the USB host library
      ******************************************************************************
      * @attention
      *
      * <h2><center>&copy; COPYRIGHT 2012 STMicroelectronics</center></h2>
      *
      * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
      * You may not use this file except in compliance with the License.
      * You may obtain a copy of the License at:
      *
      *        http://www.st.com/software_license_agreement_liberty_v2
      *
      * Unless required by applicable law or agreed to in writing, software 
      * distributed under the License is distributed on an "AS IS" BASIS, 
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      * See the License for the specific language governing permissions and
      * limitations under the License.
      *
      ******************************************************************************
      */ 
    
    /* Includes ------------------------------------------------------------------*/
    
    #include "usb_bsp.h"
    #include "stm32f10x.h"
    #include "delay.h"
    #include "stm32f10x_tim.h"
    /** @addtogroup USBH_USER
    * @{
    */
    
    /** @defgroup USB_BSP
      * @brief This file is responsible to offer board support package
      * @{
      */ 
    
    /** @defgroup USB_BSP_Private_Defines
      * @{
      */ 
    #define USE_ACCURATE_TIME                  0
    #define TIM_MSEC_DELAY                     0x01
    #define TIM_USEC_DELAY                     0x02
    //#define HOST_OVRCURR_PORT                  GPIOE
    //#define HOST_OVRCURR_LINE                  GPIO_Pin_1
    //#define HOST_OVRCURR_PORT_SOURCE           GPIO_PortSourceGPIOE
    //#define HOST_OVRCURR_PIN_SOURCE            GPIO_PinSource1
    //#define HOST_OVRCURR_PORT_RCC              RCC_APB2Periph_GPIOE
    //#define HOST_OVRCURR_EXTI_LINE             EXTI_Line1
    //#define HOST_OVRCURR_IRQn                  EXTI1_IRQn 
    
    
     #define HOST_POWERSW_PORT_RCC             RCC_APB2Periph_GPIOA
     #define HOST_POWERSW_PORT                 GPIOA
     #define HOST_POWERSW_VBUS                 GPIO_Pin_15
    
    //#define HOST_SOF_OUTPUT_RCC                RCC_APB2Periph_GPIOA
    //#define HOST_SOF_PORT                      GPIOA
    //#define HOST_SOF_SIGNAL                    GPIO_Pin_8
    
    /**
      * @}
      */ 
    
    
    /** @defgroup USB_BSP_Private_TypesDefinitions
      * @{
      */ 
    /**
      * @}
      */ 
    
    
    
    /** @defgroup USB_BSP_Private_Macros
      * @{
      */ 
    /**
      * @}
      */ 
    
    /** @defgroup USBH_BSP_Private_Variables
      * @{
      */ 
    ErrorStatus HSEStartUpStatus;
    
    
    #ifdef USE_ACCURATE_TIME 
    
    __IO uint32_t BSP_delay = 0;
    #endif
    /**
      * @}
      */ 
    
    /** @defgroup USBH_BSP_Private_FunctionPrototypes
      * @{
      */ 
    
    #ifdef USE_ACCURATE_TIME 
    static void BSP_SetTime(uint8_t Unit);
    static void BSP_Delay(uint32_t nTime,uint8_t Unit);
    static void USB_OTG_BSP_TimeInit ( void );
    #endif
    /**
      * @}
      */ 
    
    /** @defgroup USB_BSP_Private_Functions
      * @{
      */ 
    
    /**
      * @brief  USB_OTG_BSP_Init
      *         Initializes BSP configurations
      * @param  None
      * @retval None
      */
    
    void USB_OTG_BSP_Init(USB_OTG_CORE_HANDLE *pdev)
    {
    
      GPIO_InitTypeDef GPIO_InitStructure;
    	
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);  
      RCC_OTGFSCLKConfig(RCC_OTGFSCLKSource_PLLVCO_Div3);
      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_OTG_FS, ENABLE) ;
      
      
      /* Configure SOF ID DM DP Pins GPIO_Pin_8|*/
      GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_11 | 
                                    GPIO_Pin_12;
      
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP ;
      GPIO_Init(GPIOA, &GPIO_InitStructure);  
     
    
      
      /* Configure  VBUS Pin */
    //  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    //  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
    //  GPIO_Init(GPIOC, &GPIO_InitStructure);    
    //    
    
    
     
    
      /* Initialize Timer for delay function */
     USB_OTG_BSP_TimeInit();   
    }
    /**
      * @brief  USB_OTG_BSP_EnableInterrupt
      *         Configures USB Global interrupt
      * @param  None
      * @retval None
      */
    void USB_OTG_BSP_EnableInterrupt(USB_OTG_CORE_HANDLE *pdev)
    {
      NVIC_InitTypeDef NVIC_InitStructure; 
      
    //  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
      NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_IRQn;  
    
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_Init(&NVIC_InitStructure);  
     
    }
    
    /**
      * @brief  BSP_Drive_VBUS
      *         Drives the Vbus signal through IO
      * @param  state : VBUS states
      * @retval None
      */
    
    void USB_OTG_BSP_DriveVBUS(USB_OTG_CORE_HANDLE *pdev, uint8_t state)
    {
      /*
      On-chip 5 V VBUS generation is not supported. For this reason, a charge pump 
      or, if 5 V are available on the application board, a basic power switch, must 
      be added externally to drive the 5 V VBUS line. The external charge pump can 
      be driven by any GPIO output. When the application decides to power on VBUS 
      using the chosen GPIO, it must also set the port power bit in the host port 
      control and status register (PPWR bit in OTG_FS_HPRT).
      
      Bit 12 PPWR: Port power
      The application uses this field to control power to this port, and the core 
      clears this bit on an overcurrent condition.
      */
    //这里一定要注意,U盘是要单独供电的一般要5V1A电源,在硬件设计时会做一个开关电源。注意要根据自己的电源使能管教配置,根据你是高低电平使能修改,否则有可能导致你的U盘插上没有反应。
      
      if (0 == state)
      { 
        /* DISABLE is needed on output of the Power Switch */
        GPIO_ResetBits(HOST_POWERSW_PORT, HOST_POWERSW_VBUS);
      }
      else
      { GPIO_SetBits(HOST_POWERSW_PORT, HOST_POWERSW_VBUS);
        /*ENABLE the Power Switch by driving the Enable LOW */
       
      }
    }
    
    /**
      * @brief  USB_OTG_BSP_ConfigVBUS
      *         Configures the IO for the Vbus and OverCurrent
      * @param  None
      * @retval None
      */
    
    void  USB_OTG_BSP_ConfigVBUS(USB_OTG_CORE_HANDLE *pdev)
    {
    
      
      GPIO_InitTypeDef GPIO_InitStructure; 
      
    
      RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA , ENABLE);  
    	
      GPIO_InitStructure.GPIO_Pin = HOST_POWERSW_VBUS;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
     
      GPIO_Init(HOST_POWERSW_PORT,&GPIO_InitStructure);
    
    
      /* By Default, DISABLE is needed on output of the Power Switch */
      GPIO_SetBits(HOST_POWERSW_PORT, HOST_POWERSW_VBUS);
      
      USB_OTG_BSP_mDelay(200);   /* Delay is need for stabilising the Vbus Low 
      in Reset Condition, when Vbus=1 and Reset-button is pressed by user */
     
    }
    
    /**
      * @brief  USB_OTG_BSP_TimeInit
      *         Initializes delay unit using Timer2
      * @param  None
      * @retval None
      */
    static void USB_OTG_BSP_TimeInit ( void )
    {
    #ifdef USE_ACCURATE_TIME   
      NVIC_InitTypeDef NVIC_InitStructure;
      
      /* Set the Vector Table base address at 0x08000000 */
      NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x00);
      
      /* Configure the Priority Group to 2 bits */
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
      
      /* Enable the TIM2 global Interrupt */
      NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      
      NVIC_Init(&NVIC_InitStructure);
      
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);  
    #endif  
    }
    
    /**
      * @brief  USB_OTG_BSP_uDelay
      *         This function provides delay time in micro sec
      * @param  usec : Value of delay required in micro sec
      * @retval None
      */
    void USB_OTG_BSP_uDelay (const uint32_t usec)
    {
      
    	Delay_us(usec);
      
    }
    
    
    /**
      * @brief  USB_OTG_BSP_mDelay
      *          This function provides delay time in milli sec
      * @param  msec : Value of delay required in milli sec
      * @retval None
      */
    void USB_OTG_BSP_mDelay (const uint32_t msec)
    { 
    	Delay_ms(msec);  
    
    }
    
    
    /**
      * @brief  USB_OTG_BSP_TimerIRQ
      *         Time base IRQ
      * @param  None
      * @retval None
      */
    
    void USB_OTG_BSP_TimerIRQ (void)
    {
    #ifdef USE_ACCURATE_TIME 
        
      if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
      {
       TIM_ClearITPendingBit(TIM2, TIM_IT_Update);	
        if (BSP_delay > 0x00)
        { 
          BSP_delay--;
        }
        else
        {
          TIM_Cmd(TIM2,DISABLE);
        }
      }
    #endif  
    } 
    
    /**
      * @brief  BSP_Delay
      *         Delay routine based on TIM2
      * @param  nTime : Delay Time 
      * @param  unit : Delay Time unit : mili sec / micro sec
      * @retval None
      */
    static void BSP_Delay(uint32_t nTime, uint8_t unit)
    {
      
      BSP_delay = nTime;
      BSP_SetTime(unit);  
      while(BSP_delay != 0);
      TIM_Cmd(TIM2,DISABLE);
    }
    
    /**
      * @brief  BSP_SetTime
      *         Configures TIM2 for delay routine based on TIM2
      * @param  unit : msec /usec
      * @retval None
      */
    static void BSP_SetTime(uint8_t unit)
    {
      TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
      
      TIM_Cmd(TIM2,DISABLE);
      TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE); 
      
      
      if(unit == TIM_USEC_DELAY)
      {  
        TIM_TimeBaseStructure.TIM_Period = 11;
      }
      else if(unit == TIM_MSEC_DELAY)
      {
        TIM_TimeBaseStructure.TIM_Period = 11999;
      }
      TIM_TimeBaseStructure.TIM_Prescaler = 5;
      TIM_TimeBaseStructure.TIM_ClockDivision = 0;
      TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
      
      TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
      TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
      
      TIM_ARRPreloadConfig(TIM2, ENABLE);
      
      /* TIM IT enable */
      TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
      
      /* TIM2 enable counter */ 
      TIM_Cmd(TIM2, ENABLE);  
    } 
    
    
    
    /**
    * @}
    */ 
    
    /**
    * @}
    */ 
    
    /**
    * @}
    */
    
    /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
    
    /**
      ******************************************************************************
      * @file    usbh_usr.c
      * @author  MCD Application Team
      * @version V1.2.0
      * @date    09-November-2015
      * @brief   This file includes the usb host library user callbacks
      ******************************************************************************
      * @attention
      *
      * <h2><center>&copy; COPYRIGHT 2015 STMicroelectronics</center></h2>
      *
      * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
      * You may not use this file except in compliance with the License.
      * You may obtain a copy of the License at:
      *
      *        http://www.st.com/software_license_agreement_liberty_v2
      *
      * Unless required by applicable law or agreed to in writing, software 
      * distributed under the License is distributed on an "AS IS" BASIS, 
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      * See the License for the specific language governing permissions and
      * limitations under the License.
      *
      ******************************************************************************
      */
    
    /* Includes ------------------------------------------------------------------*/
    
    
    #include "usbh_usr.h"
    #include "usb_hcd_int.h"
    #include "stm32f10x.h"
    #include "ff.h"       /* FATFS */
    #include "my_printf.h"
    
    
    系统配置结构体//
    
    extern USB_OTG_CORE_HANDLE    USB_OTG_Core;
    extern USBH_HOST              USB_Host;
    
    /** @defgroup USBH_USR_Private_Variables
    * @{
    */ 
    uint8_t USBH_USR_ApplicationState = USH_USR_FS_INIT;
    
    
    FATFS fatfs;
    FIL file;
    static u8 AppState; 
    
    
    
    /*  Points to the DEVICE_PROP structure of current device */
    /*  The purpose of this register is to speed up the execution */
    
    USBH_Usr_cb_TypeDef USR_cb =
    {
      USBH_USR_Init,
      USBH_USR_DeInit,
      USBH_USR_DeviceAttached,
      USBH_USR_ResetDevice,
      USBH_USR_DeviceDisconnected,
      USBH_USR_OverCurrentDetected,
      USBH_USR_DeviceSpeedDetected,
      USBH_USR_Device_DescAvailable,
      USBH_USR_DeviceAddressAssigned,
      USBH_USR_Configuration_DescAvailable,
      USBH_USR_Manufacturer_String,
      USBH_USR_Product_String,
      USBH_USR_SerialNum_String,
      USBH_USR_EnumerationDone,
      USBH_USR_UserInput,
      USBH_USR_MSC_Application,
      USBH_USR_DeviceNotSupported,
      USBH_USR_UnrecoveredError
        
    };
    
    
    
    
    /** @defgroup USBH_USR_Private_Functions
    * @{
    */ 
    
    
    void OTG_FS_IRQHandler(void)
    {
      	USBH_OTG_ISR_Handler(&USB_OTG_Core);
    } 
    
    /**
    * @brief  USBH_USR_Init 
    *         USB HOST 初始化 
    * @param  None
    * @retval None
    */
    void USBH_USR_Init(void)
    {
    	Debug_Com_printf("USB OTG HS MSC Host\r\n");
    	Debug_Com_printf("> USB Host library started.\r\n");
    	Debug_Com_printf("  USB Host Library v2.1.0\r\n\r\n");
    }
    
    /**
    * @brief  USBH_USR_DeviceAttached 
    *         //检测到U盘插入
    * @param  None
    * @retval None
    */
    void USBH_USR_DeviceAttached(void)
    {
    	Debug_Com_printf("检测到USB设备插入!\r\n");
    }
    
    //获取U盘状态
    //返回值:0,U盘未就绪
    //      1,就绪
    
    uint8_t USBH_UDISK_Status(void)
    {
    	return HCD_IsDeviceConnected(&USB_OTG_Core);//返回U盘状态
    }
    
    
    /**
    * @brief  USBH_USR_UnrecoveredError
    * @param  None
    * @retval None
    */
    void USBH_USR_UnrecoveredError (void)
    {
      
    }
    
    
    /**
    * @brief  USBH_DisconnectEvent
    *         //检测到U盘拔出
    * @param  None
    * @retval Status
    */
    void USBH_USR_DeviceDisconnected (void)
    {
      Debug_Com_printf("检测到USB设备拔出!\r\n");
    }
    /**
    * @brief  USBH_USR_ResetUSBDevice 
       //复位从机
    * @param  None
    * @retval None
    */
    void USBH_USR_ResetDevice(void)
    {
    		Debug_Com_printf("复位设备...\r\n");
      /* callback for USB-Reset */
    }
    
    
    /**
    * @brief  USBH_USR_DeviceSpeedDetected 
    *         //检测到从机速度
                //DeviceSpeed:从机速度(0,1,2 / 其他)
    * @param  Device speed
    * @retval None
    */
    void USBH_USR_DeviceSpeedDetected(uint8_t DeviceSpeed)
    {
      	if(DeviceSpeed==HPRT0_PRTSPD_HIGH_SPEED)
    	{
    		Debug_Com_printf("高速(HS)USB设备!\r\n");
     	}  
    	else if(DeviceSpeed==HPRT0_PRTSPD_FULL_SPEED)
    	{
    		Debug_Com_printf("全速(FS)USB设备!\r\n"); 
    	}
    	else if(DeviceSpeed==HPRT0_PRTSPD_LOW_SPEED)
    	{
    		Debug_Com_printf("低速(LS)USB设备!\r\n");  
    	}
    	else
    	{
    		Debug_Com_printf("设备错误!\r\n");  
    	}
    }
    
    /**
    * @brief  USBH_USR_Device_DescAvailable 
    *        //检测到从机的描述符
    /          /DeviceDesc:设备描述符指针
    * @param  device descriptor
    * @retval None
    */
    void USBH_USR_Device_DescAvailable(void *DeviceDesc)
    { 
    	USBH_DevDesc_TypeDef *hs;
    	hs=DeviceDesc;   
    	Debug_Com_printf("VID: %04Xh\r\n" , (uint32_t)(*hs).idVendor); 
    	Debug_Com_printf("PID: %04Xh\r\n" , (uint32_t)(*hs).idProduct); 
      
    }
    
    /**
    * @brief  USBH_USR_DeviceAddressAssigned 
    *        //从机地址分配成功
    * @param  None
    * @retval None
    */
    void USBH_USR_DeviceAddressAssigned(void)
    {
      	Debug_Com_printf("从机地址分配成功!\r\n");   
    }
    
    
    /**
    * @brief  USBH_USR_Conf_Desc 
    *         //配置描述符获有效
    * @param  Configuration descriptor
    * @retval None
    */
    void USBH_USR_Configuration_DescAvailable(USBH_CfgDesc_TypeDef * cfgDesc,
                                              USBH_InterfaceDesc_TypeDef *itfDesc,
                                              USBH_EpDesc_TypeDef *epDesc)
    {
    	USBH_InterfaceDesc_TypeDef *id; 
    	id = itfDesc;   
    	if((*id).bInterfaceClass==0x08)
    	{
    		Debug_Com_printf("可移动存储器设备!\r\n"); 
    	}else if((*id).bInterfaceClass==0x03)
    	{
    		Debug_Com_printf("HID 设备!\r\n"); 
    	}    
    }
    
    /**
    * @brief  USBH_USR_Manufacturer_String 
    *         //获取到设备Manufacturer String
    * @param  Manufacturer String 
    * @retval None
    */
    void USBH_USR_Manufacturer_String(void *ManufacturerString)
    {
     Debug_Com_printf("Manufacturer: %s\r\n",(char *)ManufacturerString);
    }
    
    /**
    * @brief  USBH_USR_Product_String 
    *         //获取到设备Product String 
    * @param  Product String
    * @retval None
    */
    void USBH_USR_Product_String(void *ProductString)
    {
    		Debug_Com_printf("Product: %s\r\n",(char *)ProductString); 
    //  LCD_UsrLog("Product : %s\n", (char *)ProductString);  
    }
    
    /**
    * @brief  USBH_USR_SerialNum_String 
    *       //获取到设备SerialNum String 
    * @param  SerialNum_String 
    * @retval None
    */
    void USBH_USR_SerialNum_String(void *SerialNumString)
    {
    Debug_Com_printf("Serial Number: %s\r\n",(char *)SerialNumString);  
    	
    } 
    
    
    
    /**
    * @brief  EnumerationDone 
    *       //设备USB枚举完成
    * @param  None
    * @retval None
    */
    void USBH_USR_EnumerationDone(void)
    {
      
    	Debug_Com_printf("设备枚举完成!\r\n\r\n"); 
      
    } 
    
    
    /**
    * @brief  USBH_USR_DeviceNotSupported
    *      //无法识别的USB设备
    * @param  None
    * @retval None
    */
    void USBH_USR_DeviceNotSupported(void)
    {
    		Debug_Com_printf("无法识别的USB设备!\r\n\r\n"); 
    //  LCD_ErrLog ("No registered class for this device. \n\r");
    }  
    
    
    /**
    * @brief  USBH_USR_UserInput
    *        //等待用户输入按键,执行下一步操作
    * @param  None
    * @retval USBH_USR_Status : User response for key button
    */
    USBH_USR_Status USBH_USR_UserInput(void)
    {
    		Debug_Com_printf("跳过用户确认步骤!\r\n");
    	return USBH_USR_RESP_OK;
    }  
    
    /**
    * @brief  USBH_USR_OverCurrentDetected
    *        //USB接口电流过载
    * @param  None
    * @retval Status
    */
    void USBH_USR_OverCurrentDetected (void)
    {
    		Debug_Com_printf("端口电流过大!!!\r\n");
    //  LCD_ErrLog ("Overcurrent detected.");
    }
    
    
    
    
    
    u8  text_buffer[50] = "hospital,bednumber,name,age,sex,\n1,2,3,4,5,\n";
    
    
    
    
    u8 USH_User_App(void)
    { 
    	FATFS fs[_VOLUMES];//逻辑磁盘工作区. 
    	u8 f_res;
      FIL filescr1;	
      UINT br;
      	f_res=f_mount(&fs[0],"0:",1); 	//重新挂载U盘
    	  
       f_res=f_mkdir("0:DATA");//新建文件夹,其中DATA是文件夹名称
    	  f_res=f_open(&filescr1,"0:20190522.csv", FA_CREATE_ALWAYS|FA_WRITE);
    	 
       	f_res= f_write(&filescr1, text_buffer, sizeof(text_buffer), &br);
    	
    	//在新建的文件夹下创建新的txt文本文件新建完成之后要记得关闭该文件,否则就会出错。
       f_res = f_close(&filescr1);
    	Debug_Com_printf("写入完成\r\n");
    	while(HCD_IsDeviceConnected(&USB_OTG_Core))//设备连接成功
    	{	
      
    
    	}
    
     	f_mount(0,"35:",1); 	//卸载U盘
      Debug_Com_printf("USB已卸载\r\n");
    	return 1;
    } 
    
    
    
    
    /**
    * @brief  USBH_USR_MSC_Application 
    *       //USB HOST MSC类用户应用程序
    * @param  None
    * @retval Status
    */
    int USBH_USR_MSC_Application(void)
    {
     	u8 res=0;
      	switch(AppState)
      	{
        	case USH_USR_FS_INIT://初始化文件系统 
       			Debug_Com_printf("开始执行用户程序!!!\r\n");
    			  AppState=1;
          		break;
        	case 1 :	//执行USB OTG 测试主程序
    				
    			
    			res=USH_User_App(); //用户主程序
         		res=0;
    			if(res)AppState=USH_USR_FS_INIT;
          		break;
        	default:break;
      	} 
    	return res;
    }
    
    
    /* @brief  USBH_USR_DeInit
    *         Deinit User state and associated variables
    * @param  None
    * @retval None
    */
    void USBH_USR_DeInit(void)
    {
      USBH_USR_ApplicationState = USH_USR_FS_INIT;
    }
    
    
    
    //用户定义函数,实现fatfs diskio的接口函数 
    
    
    
    //读U盘
    //buf:读数据缓存区
    //sector:扇区地址
    //cnt:扇区个数	
    //返回值:错误状态;0,正常;其他,错误代码;		 
    u8 USBH_UDISK_Read(u8* buf,u32 sector,u32 cnt)
    {
    	u8 res=1;
    	if(HCD_IsDeviceConnected(&USB_OTG_Core)&&AppState==USH_USR_FS_TEST)//连接还存在,且是APP测试状态
    	{  		    
    		do
    		{
    			res=USBH_MSC_Read10(&USB_OTG_Core,buf,sector,512*cnt);
    			USBH_MSC_HandleBOTXfer(&USB_OTG_Core ,&USB_Host);		      
    			if(!HCD_IsDeviceConnected(&USB_OTG_Core))
    			{
    				res=1;//读写错误
    				Debug_Com_printf("读写错误!!!\r\n");
    				break;
    			};   
    		}while(res==USBH_MSC_BUSY);
    	}else res=1;		  
    	if(res==USBH_MSC_OK)res=0;	
    	return res;
    }
    
    //写U盘
    //buf:写数据缓存区
    //sector:扇区地址
    //cnt:扇区个数	
    //返回值:错误状态;0,正常;其他,错误代码;		 
    u8 USBH_UDISK_Write(u8* buf,u32 sector,u32 cnt)
    {
    	u8 res=1;
    	if(HCD_IsDeviceConnected(&USB_OTG_Core)&&AppState==USH_USR_FS_TEST)//连接还存在,且是APP测试状态
    	{  		    
    		do
    		{
    			res=USBH_MSC_Write10(&USB_OTG_Core,buf,sector,512*cnt); 
    			USBH_MSC_HandleBOTXfer(&USB_OTG_Core ,&USB_Host);		      
    			if(!HCD_IsDeviceConnected(&USB_OTG_Core))
    			{
    				res=1;//读写错误
    				Debug_Com_printf("读写错误!!!\r\n");
    				break;
    			};   
    		}while(res==USBH_MSC_BUSY);
    	}else res=1;		  
    	if(res==USBH_MSC_OK)res=0;	
    	return res;
    }
    
    
    /**
    * @}
    */ 
    
    /**
    * @}
    */ 
    
    /**
    * @}
    */
    
    /**
    * @}
    */
    
    /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
    
    
    /**
      ******************************************************************************
      * @file    usbh_usr.h
      * @author  MCD Application Team
      * @version V1.2.0
      * @date    09-November-2015
      * @brief   Header file for usbh_usr.c
      ******************************************************************************
      * @attention
      *
      * <h2><center>&copy; COPYRIGHT 2015 STMicroelectronics</center></h2>
      *
      * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
      * You may not use this file except in compliance with the License.
      * You may obtain a copy of the License at:
      *
      *        http://www.st.com/software_license_agreement_liberty_v2
      *
      * Unless required by applicable law or agreed to in writing, software 
      * distributed under the License is distributed on an "AS IS" BASIS, 
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      * See the License for the specific language governing permissions and
      * limitations under the License.
      *
      ******************************************************************************
      */ 
    
    /* Define to prevent recursive inclusion -------------------------------------*/
    #ifndef __USH_USR_H__
    #define __USH_USR_H__
    
    /* Includes ------------------------------------------------------------------*/
    #include "ff.h"
    #include "usbh_core.h"
    #include "usb_conf.h"
    #include <stdio.h>
    #include "usbh_msc_core.h"
    
    /** @addtogroup USBH_USER
      * @{
      */
    
    /** @addtogroup USBH_MSC_DEMO_USER_CALLBACKS
      * @{
      */
    
    /** @defgroup USBH_USR
      * @brief This file is the Header file for usbh_usr.c
      * @{
      */ 
    
    
    /** @defgroup USBH_USR_Exported_Types
      * @{
      */ 
    
    
    extern  USBH_Usr_cb_TypeDef USR_cb;
    
    
    
    /**
      * @}
      */ 
    
    
    
    /** @defgroup USBH_USR_Exported_Defines
      * @{
      */ 
    /* State Machine for the USBH_USR_ApplicationState */
    #define USH_USR_FS_INIT       0
    
    #define USH_USR_FS_TEST   		1  
    #define USH_USR_FS_READLIST   1
    #define USH_USR_FS_WRITEFILE  2
    #define USH_USR_FS_DRAW       3
    /**
      * @}
      */ 
    
    /** @defgroup USBH_USR_Exported_Macros
      * @{
      */ 
    /**
      * @}
      */ 
    
    /** @defgroup USBH_USR_Exported_Variables
      * @{
      */ 
    extern  uint8_t USBH_USR_ApplicationState ;
    /**
      * @}
      */ 
    
    /** @defgroup USBH_USR_Exported_FunctionsPrototype
      * @{
      */ 
    void USBH_USR_ApplicationSelected(void);
    void USBH_USR_Init(void);
    void USBH_USR_DeInit(void);
    void USBH_USR_DeviceAttached(void);
    void USBH_USR_ResetDevice(void);
    void USBH_USR_DeviceDisconnected (void);
    void USBH_USR_OverCurrentDetected (void);
    void USBH_USR_DeviceSpeedDetected(uint8_t DeviceSpeed); 
    void USBH_USR_Device_DescAvailable(void *);
    void USBH_USR_DeviceAddressAssigned(void);
    void USBH_USR_Configuration_DescAvailable(USBH_CfgDesc_TypeDef * cfgDesc,
                                              USBH_InterfaceDesc_TypeDef *itfDesc,
                                              USBH_EpDesc_TypeDef *epDesc);
    void USBH_USR_Manufacturer_String(void *);
    void USBH_USR_Product_String(void *);
    void USBH_USR_SerialNum_String(void *);
    void USBH_USR_EnumerationDone(void);
    USBH_USR_Status USBH_USR_UserInput(void);
    void USBH_USR_DeInit(void);
    void USBH_USR_DeviceNotSupported(void);
    void USBH_USR_UnrecoveredError(void);
    int USBH_USR_MSC_Application(void);
    
    uint8_t USBH_UDISK_Status(void);
    u8 USBH_UDISK_Write(u8* buf,u32 sector,u32 cnt);
    u8 USBH_UDISK_Read(u8* buf,u32 sector,u32 cnt);
    /**
      * @}
      */ 
    
    #endif /*__USH_USR_H__*/
    
    /**
      * @}
      */ 
    
    /**
      * @}
      */ 
    
    /**
      * @}
      */ 
    
    /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
    
    

    注意一点这个C文件我是这样修改的。

    /**
      ******************************************************************************
      * @file    usbh_conf_template
      * @author  MCD Application Team
      * @version V2.2.0
      * @date    09-November-2015
      * @brief   General USB Host library configuration
      ******************************************************************************
      * @attention
      *
      * <h2><center>&copy; COPYRIGHT 2015 STMicroelectronics</center></h2>
      *
      * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
      * You may not use this file except in compliance with the License.
      * You may obtain a copy of the License at:
      *
      *        http://www.st.com/software_license_agreement_liberty_v2
      *
      * Unless required by applicable law or agreed to in writing, software 
      * distributed under the License is distributed on an "AS IS" BASIS, 
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      * See the License for the specific language governing permissions and
      * limitations under the License.
      *
      ******************************************************************************
      */
    
    /* Define to prevent recursive inclusion -------------------------------------*/
    #ifndef __USBH_CONF__H__
    #define __USBH_CONF__H__
    
    /* Includes ------------------------------------------------------------------*/
    
    /** @addtogroup USBH_OTG_DRIVER
      * @{
      */
      
    /** @defgroup USBH_CONF
      * @brief usb otg low level driver configuration file
      * @{
      */ 
    
    /** @defgroup USBH_CONF_Exported_Defines
      * @{
      */ 
    
    #define USBH_MAX_NUM_ENDPOINTS                2
    #define USBH_MAX_NUM_INTERFACES               2
    
    
    #ifdef USE_USB_OTG_FS 
    #define USBH_MSC_MPS_SIZE                 0x40
    #else
    #define USBH_MSC_MPS_SIZE                 0x200
    #endif
    
    ///检测U盘大小的问价默认是2 一定要修改否则无法通过枚举过程
    
    #define USBH_MAX_DATA_BUFFER              0x400///
    /**
      * @}
      */ 
    
    
    /** @defgroup USBH_CONF_Exported_Types
      * @{
      */ 
    /**
      * @}
      */ 
    
    
    /** @defgroup USBH_CONF_Exported_Macros
      * @{
      */ 
    /**
      * @}
      */ 
    
    /** @defgroup USBH_CONF_Exported_Variables
      * @{
      */ 
    /**
      * @}
      */ 
    
    /** @defgroup USBH_CONF_Exported_FunctionsPrototype
      * @{
      */ 
    /**
      * @}
      */ 
    
    
    #endif //__USBH_CONF__H__
    
    
    /**
      * @}
      */ 
    
    /**
      * @}
      */ 
    /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
    
    
    /**
      ******************************************************************************
      * @file    usb_conf.h
      * @author  MCD Application Team
      * @version V2.2.0
      * @date    09-November-2015
      * @brief   General low level driver configuration
      ******************************************************************************
      * @attention
      *
      * <h2><center>&copy; COPYRIGHT 2015 STMicroelectronics</center></h2>
      *
      * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
      * You may not use this file except in compliance with the License.
      * You may obtain a copy of the License at:
      *
      *        http://www.st.com/software_license_agreement_liberty_v2
      *
      * Unless required by applicable law or agreed to in writing, software 
      * distributed under the License is distributed on an "AS IS" BASIS, 
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      * See the License for the specific language governing permissions and
      * limitations under the License.
      *
      ******************************************************************************
      */
    
    /* Define to prevent recursive inclusion -------------------------------------*/
    #ifndef __USB_CONF__H__
    #define __USB_CONF__H__
    
    /* Includes ------------------------------------------------------------------*/
    
    
    /** @addtogroup USB_OTG_DRIVER
      * @{
      */
      
    /** @defgroup USB_CONF
      * @brief USB low level driver configuration file
      * @{
      */ 
    
    /** @defgroup USB_CONF_Exported_Defines
      * @{
      */ 
    
    /* USB Core and PHY interface configuration.
       Tip: To avoid modifying these defines each time you need to change the USB
            configuration, you can declare the needed define in your toolchain
            compiler preprocessor.
       */
    /****************** USB OTG FS PHY CONFIGURATION *******************************
    *  The USB OTG FS Core supports one on-chip Full Speed PHY.
    *  
    *  The USE_EMBEDDED_PHY symbol is defined in the project compiler preprocessor 
    *  when FS core is used.
    *******************************************************************************/
    
    
    
    //一定要在 Option for Target ->c++->define 中添加  ,USE_USB_OTG_FS
    
    
    #ifdef USE_USB_OTG_FS 
     #define USB_OTG_FS_CORE
    #endif
    
    /****************** USB OTG HS PHY CONFIGURATION *******************************
    *  The USB OTG HS Core supports two PHY interfaces:
    *   (i)  An ULPI interface for the external High Speed PHY: the USB HS Core will 
    *        operate in High speed mode
    *   (ii) An on-chip Full Speed PHY: the USB HS Core will operate in Full speed mode
    *
    *  You can select the PHY to be used using one of these two defines:
    *   (i)  USE_ULPI_PHY: if the USB OTG HS Core is to be used in High speed mode 
    *   (ii) USE_EMBEDDED_PHY: if the USB OTG HS Core is to be used in Full speed mode
    *
    *  Notes: 
    *   - The USE_ULPI_PHY symbol is defined in the project compiler preprocessor as 
    *     default PHY when HS core is used.
    *   - On STM322xG-EVAL and STM324xG-EVAL boards, only configuration(i) is available.
    *     Configuration (ii) need a different hardware, for more details refer to your
    *     STM32 device datasheet.
    *******************************************************************************/
    //#ifndef USE_USB_OTG_HS
    // //#define USE_USB_OTG_HS
    //#endif /* USE_USB_OTG_HS */
    
    //#ifndef USE_ULPI_PHY
    // //#define USE_ULPI_PHY
    //#endif /* USE_ULPI_PHY */
    
    //#ifndef USE_EMBEDDED_PHY
    // //#define USE_EMBEDDED_PHY
    //#endif /* USE_EMBEDDED_PHY */
    
    //#ifdef USE_USB_OTG_HS 
    // #define USB_OTG_HS_CORE
    //#endif
    
    /*******************************************************************************
    *                      FIFO Size Configuration in Device mode
    *  
    *  (i) Receive data FIFO size = RAM for setup packets + 
    *                   OUT endpoint control information +
    *                   data OUT packets + miscellaneous
    *      Space = ONE 32-bits words
    *     --> RAM for setup packets = 10 spaces
    *        (n is the nbr of CTRL EPs the device core supports) 
    *     --> OUT EP CTRL info      = 1 space
    *        (one space for status information written to the FIFO along with each 
    *        received packet)
    *     --> data OUT packets      = (Largest Packet Size / 4) + 1 spaces 
    *        (MINIMUM to receive packets)
    *     --> OR data OUT packets  = at least 2*(Largest Packet Size / 4) + 1 spaces 
    *        (if high-bandwidth EP is enabled or multiple isochronous EPs)
    *     --> miscellaneous = 1 space per OUT EP
    *        (one space for transfer complete status information also pushed to the 
    *        FIFO with each endpoint's last packet)
    *
    *  (ii)MINIMUM RAM space required for each IN EP Tx FIFO = MAX packet size for 
    *       that particular IN EP. More space allocated in the IN EP Tx FIFO results
    *       in a better performance on the USB and can hide latencies on the AHB.
    *
    *  (iii) TXn min size = 16 words. (n  : Transmit FIFO index)
    *   (iv) When a TxFIFO is not used, the Configuration should be as follows: 
    *       case 1 :  n > m    and Txn is not used    (n,m  : Transmit FIFO indexes)
    *       --> Txm can use the space allocated for Txn.
    *       case2  :  n < m    and Txn is not used    (n,m  : Transmit FIFO indexes)
    *       --> Txn should be configured with the minimum space of 16 words
    *  (v) The FIFO is used optimally when used TxFIFOs are allocated in the top 
    *       of the FIFO.Ex: use EP1 and EP2 as IN instead of EP1 and EP3 as IN ones.
    *******************************************************************************/
    
    /*******************************************************************************
    *                     FIFO Size Configuration in Host mode
    *  
    *  (i) Receive data FIFO size = (Largest Packet Size / 4) + 1 or 
    *                             2x (Largest Packet Size / 4) + 1,  If a 
    *                             high-bandwidth channel or multiple isochronous 
    *                             channels are enabled
    *
    *  (ii) For the host nonperiodic Transmit FIFO is the largest maximum packet size 
    *      for all supported nonperiodic OUT channels. Typically, a space 
    *      corresponding to two Largest Packet Size is recommended.
    *
    *  (iii) The minimum amount of RAM required for Host periodic Transmit FIFO is 
    *        the largest maximum packet size for all supported periodic OUT channels.
    *        If there is at least one High Bandwidth Isochronous OUT endpoint, 
    *        then the space must be at least two times the maximum packet size for 
    *        that channel.
    *******************************************************************************/
     
    /****************** USB OTG HS CONFIGURATION不使用 **********************************/
    #ifdef USB_OTG_HS_CORE
     #define RX_FIFO_HS_SIZE                          512
     #define TX0_FIFO_HS_SIZE                         512
     #define TX1_FIFO_HS_SIZE                         512
     #define TX2_FIFO_HS_SIZE                          0
     #define TX3_FIFO_HS_SIZE                          0
     #define TX4_FIFO_HS_SIZE                          0
     #define TX5_FIFO_HS_SIZE                          0
     #define TXH_NP_HS_FIFOSIZ                         96
     #define TXH_P_HS_FIFOSIZ                          96
    
    // #define USB_OTG_HS_LOW_PWR_MGMT_SUPPORT
    // #define USB_OTG_HS_SOF_OUTPUT_ENABLED
    
    // #define USB_OTG_INTERNAL_VBUS_ENABLED
    
    
    // #define USB_OTG_EXTERNAL_VBUS_ENABLED
    
     #ifdef USE_ULPI_PHY
      #define USB_OTG_ULPI_PHY_ENABLED
     #endif
     #ifdef USE_EMBEDDED_PHY
       #define USB_OTG_EMBEDDED_PHY_ENABLED
     #endif
     #define USB_OTG_HS_INTERNAL_DMA_ENABLED
     #define USB_OTG_HS_DEDICATED_EP1_ENABLED
    #endif
    
    /****************** USB OTG FS CONFIGURATION 我用的USB HOST模式 **********************************/
    #ifdef USB_OTG_FS_CORE
     #define RX_FIFO_FS_SIZE                          128
     #define TX0_FIFO_FS_SIZE                          64
     #define TX1_FIFO_FS_SIZE                         128
     #define TX2_FIFO_FS_SIZE                          0
     #define TX3_FIFO_FS_SIZE                          0
     #define TXH_NP_FS_FIFOSIZ                         96
     #define TXH_P_FS_FIFOSIZ                          96
    
    // #define USB_OTG_FS_LOW_PWR_MGMT_SUPPORT
    // #define USB_OTG_FS_SOF_OUTPUT_ENABLED
    #endif
    
    /****************** USB OTG MISC CONFIGURATION ********************************/
    //#define VBUS_SENSING_ENABLED
    
    /****************** USB OTG MODE CONFIGURATION ********************************/
    #define USE_HOST_MODE
    //#define USE_DEVICE_MODE
    //#define USE_OTG_MODE
    
    #ifndef USB_OTG_FS_CORE
     #ifndef USB_OTG_HS_CORE
        #error  "USB_OTG_HS_CORE or USB_OTG_FS_CORE should be defined"
     #endif
    #endif
    
    #ifndef USE_DEVICE_MODE
     #ifndef USE_HOST_MODE
        #error  "USE_DEVICE_MODE or USE_HOST_MODE should be defined"
     #endif
    #endif
    
    #ifndef USE_USB_OTG_HS
     #ifndef USE_USB_OTG_FS
        #error  "USE_USB_OTG_HS or USE_USB_OTG_FS should be defined"
     #endif
    #else //USE_USB_OTG_HS
     #ifndef USE_ULPI_PHY
      #ifndef USE_EMBEDDED_PHY
         #error  "USE_ULPI_PHY or USE_EMBEDDED_PHY should be defined"
      #endif
     #endif
    #endif
    
    /****************** C Compilers dependant keywords ****************************/
    /* In HS mode and when the DMA is used, all variables and data structures dealing
       with the DMA during the transaction process should be 4-bytes aligned */    
    #ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
      #if defined   (__GNUC__)        /* GNU Compiler */
        #define __ALIGN_END    __attribute__ ((aligned (4)))
        #define __ALIGN_BEGIN         
      #else                           
        #define __ALIGN_END
        #if defined   (__CC_ARM)      /* ARM Compiler */
          #define __ALIGN_BEGIN    __align(4)  
        #elif defined (__ICCARM__)    /* IAR Compiler */
          #define __ALIGN_BEGIN 
        #elif defined  (__TASKING__)  /* TASKING Compiler */
          #define __ALIGN_BEGIN    __align(4) 
        #endif /* __CC_ARM */  
      #endif /* __GNUC__ */ 
    #else
      #define __ALIGN_BEGIN
      #define __ALIGN_END   
    #endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */
    
    /* __packed keyword used to decrease the data type alignment to 1-byte */
    #if defined (__CC_ARM)         /* ARM Compiler */
      #define __packed    __packed
    #elif defined (__ICCARM__)     /* IAR Compiler */
      #define __packed    __packed
    #elif defined   ( __GNUC__ )   /* GNU Compiler */                        
      #define __packed    __attribute__ ((__packed__))
    #elif defined   (__TASKING__)  /* TASKING Compiler */
      #define __packed    __unaligned
    #endif /* __CC_ARM */
    
    /**
      * @}
      */ 
    
    
    /** @defgroup USB_CONF_Exported_Types
      * @{
      */ 
    /**
      * @}
      */ 
    
    
    /** @defgroup USB_CONF_Exported_Macros
      * @{
      */ 
    /**
      * @}
      */ 
    
    /** @defgroup USB_CONF_Exported_Variables
      * @{
      */ 
    /**
      * @}
      */ 
    
    /** @defgroup USB_CONF_Exported_FunctionsPrototype
      * @{
      */ 
    /**
      * @}
      */ 
    
    
    #endif //__USB_CONF__H__
    
    
    /**
      * @}
      */ 
    
    /**
      * @}
      */ 
    /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
    
    

    修改dikio.c

    /*-----------------------------------------------------------------------*/
    /* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2016        */
    /*-----------------------------------------------------------------------*/
    /* If a working storage control module is available, it should be        */
    /* attached to the FatFs via a glue function rather than modifying it.   */
    /* This is an example of glue functions to attach various exsisting      */
    /* storage control modules to the FatFs module with a defined API.       */
    /*-----------------------------------------------------------------------*/
    #include "diskio.h"			/* FatFs lower layer API */
    
    //#include "ftl.h"	 
    #include "usbh_usr.h" 
    
    
    //V1.1 20160124
    //新增对U盘的支持							  
    // 
    
    //#define SD_CARD	 3  			//SD卡,卷标为0
    //#define EX_FLASH 	1			//外部spi flash,卷标为1
    //#define EX_NAND  	2			//外部nand flash,卷标为2
    
    #define USB_DISK	0			//U盘,卷标为0
    
    //对于W25Q256
    //前25M字节给fatfs用,25M字节后,用于存放字库,字库占用6.01M.	剩余部分,给客户自己用	 
    #define FLASH_SECTOR_SIZE 	512	
    #define FLASH_SECTOR_COUNT 	1024*25*2	//W25Q256,前25M字节给FATFS占用	
    #define FLASH_BLOCK_SIZE   	8     		//每个BLOCK有8个扇区		
      
     
    //获得磁盘状态
    DSTATUS disk_status (
    	BYTE pdrv		/* Physical drive nmuber to identify the drive */
    )
    { 
    	return RES_OK;
    }  
    //初始化磁盘
    DSTATUS disk_initialize (
    	BYTE pdrv				/* Physical drive nmuber to identify the drive */
    )
    {
    	u8 res=0;	    
    	switch(pdrv)
    	{
    
    		case USB_DISK:		//U盘  
    	  		res=!USBH_UDISK_Status();//U盘连接成功,则返回1.否则返回0	
    			break;
    		default:
    			res=1; 
    	}		 
    	if(res)return  STA_NOINIT;
    	else return 0; //初始化成功 
    } 
    //读扇区
    //pdrv:磁盘编号0~9
    //*buff:数据接收缓冲首地址
    //sector:扇区地址
    //count:需要读取的扇区数
    DRESULT disk_read (
    	BYTE pdrv,		/* Physical drive nmuber to identify the drive */
    	BYTE *buff,		/* Data buffer to store read data */
    	DWORD sector,	/* Sector address in LBA */
    	UINT count		/* Number of sectors to read */
    )
    {
    	u8 res=0; 
        if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误		 	 
    	switch(pdrv)
    	{
    		  case USB_DISK:	//U盘    
    			res=USBH_UDISK_Read(buff,sector,count);  								    
    			break;
    		default:
    			res=1; 
    	}
       //处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值
        if(res==0x00)return RES_OK;	 
        else return RES_ERROR;	   
    }
    //写扇区
    //pdrv:磁盘编号0~9
    //*buff:发送数据首地址
    //sector:扇区地址
    //count:需要写入的扇区数
    DRESULT disk_write (
    	BYTE pdrv,			/* Physical drive nmuber to identify the drive */
    	const BYTE *buff,	/* Data to be written */
    	DWORD sector,		/* Sector address in LBA */
    	UINT count			/* Number of sectors to write */
    )
    {
    	u8 res=0;  
        if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误		 	 
    	switch(pdrv)
    	{
    		
    		case USB_DISK:	//U盘 
    			res=USBH_UDISK_Write((u8*)buff,sector,count); 
    			break;
    		default:
    			res=1; 
    	}
        //处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值
        if(res == 0x00)return RES_OK;	 
        else return RES_ERROR;	
    }
    //其他表参数的获得
    //pdrv:磁盘编号0~9
    //ctrl:控制代码
    //*buff:发送/接收缓冲区指针
    DRESULT disk_ioctl (
    	BYTE pdrv,		/* Physical drive nmuber (0..) */
    	BYTE cmd,		/* Control code */
    	void *buff		/* Buffer to send/receive control data */
    )
    {
    DRESULT res;						  			     
    	 if(pdrv==USB_DISK)	//U盘
    	{
    	    switch(cmd)
    	    {
    		    case CTRL_SYNC:
    				res = RES_OK; 
    		        break;	 
    		    case GET_SECTOR_SIZE:
    		        *(WORD*)buff=512;
    		        res = RES_OK;
    		        break;	 
    		    case GET_BLOCK_SIZE:
    		        *(WORD*)buff=512;
    		        res = RES_OK;
    		        break;	 
    		    case GET_SECTOR_COUNT:
    		        *(DWORD*)buff=USBH_MSC_Param.MSCapacity;
    		        res = RES_OK;
    		        break;
    		    default:
    		        res = RES_PARERR;
    		        break;
    	    }		
    	}else res=RES_ERROR;//其他的不支持
        return res;
    }
    //获得时间
    //User defined function to give a current time to fatfs module      */
    //31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */                                                                                                                                                                                                                                          
    //15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */                                                                                                                                                                                                                                                
    DWORD get_fattime (void)
    {				 
    	return 0;
    }			 
    //动态分配内存
    void *ff_memalloc (UINT size)			
    {
    //	return (void*)mymalloc(SRAMIN,size);
    }
    //释放内存
    void ff_memfree (void* mf)		 
    {
    //	myfree(SRAMIN,mf);
    }
    
    
    
    
    
    
    
    
    
    

    最后我的ffconf配置

    /*---------------------------------------------------------------------------/
    /  FatFs - FAT file system module configuration file  R0.12  (C)ChaN, 2016
    /---------------------------------------------------------------------------*/
    
    #define _FFCONF 88100	/* Revision ID */
    
    /*---------------------------------------------------------------------------/
    / Function Configurations
    /---------------------------------------------------------------------------*/
    
    #define _FS_READONLY	0
    /* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
    /  Read-only configuration removes writing API functions, f_write(), f_sync(),
    /  f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
    /  and optional writing functions as well. */
    
    
    #define _FS_MINIMIZE	0
    /* This option defines minimization level to remove some basic API functions.
    /
    /   0: All basic functions are enabled.
    /   1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
    /      are removed.
    /   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
    /   3: f_lseek() function is removed in addition to 2. */
    
    
    #define	_USE_STRFUNC	1
    /* This option switches string functions, f_gets(), f_putc(), f_puts() and
    /  f_printf().
    /
    /  0: Disable string functions.
    /  1: Enable without LF-CRLF conversion.
    /  2: Enable with LF-CRLF conversion. */
    
    
    #define _USE_FIND		0
    /* This option switches filtered directory read functions, f_findfirst() and
    /  f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
    
    
    #define	_USE_MKFS		1
    /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
    
    
    #define	_USE_FASTSEEK	1
    /* This option switches fast seek function. (0:Disable or 1:Enable) */
    
    
    #define	_USE_EXPAND		0
    /* This option switches f_expand function. (0:Disable or 1:Enable) */
    
    
    #define _USE_CHMOD		0
    /* This option switches attribute manipulation functions, f_chmod() and f_utime().
    /  (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */
    
    
    #define _USE_LABEL		1
    /* This option switches volume label functions, f_getlabel() and f_setlabel().
    /  (0:Disable or 1:Enable) */
    
    
    #define	_USE_FORWARD	0
    /* This option switches f_forward() function. (0:Disable or 1:Enable)
    /  To enable it, also _FS_TINY need to be 1. */
    
    
    /*---------------------------------------------------------------------------/
    / Locale and Namespace Configurations
    /---------------------------------------------------------------------------*/
    
    #define _CODE_PAGE	437	
    /* This option specifies the OEM code page to be used on the target system.
    /  Incorrect setting of the code page can cause a file open failure.
    /
    /   1   - ASCII (No extended character. Non-LFN cfg. only)
    /   437 - U.S.
    /   720 - Arabic
    /   737 - Greek
    /   771 - KBL
    /   775 - Baltic
    /   850 - Latin 1
    /   852 - Latin 2
    /   855 - Cyrillic
    /   857 - Turkish
    /   860 - Portuguese
    /   861 - Icelandic
    /   862 - Hebrew
    /   863 - Canadian French
    /   864 - Arabic
    /   865 - Nordic
    /   866 - Russian
    /   869 - Greek 2
    /   932 - Japanese (DBCS)
    /   936 - Simplified Chinese (DBCS)
    /   949 - Korean (DBCS)
    /   950 - Traditional Chinese (DBCS)
    */
    
    
    #define	_USE_LFN	1
    #define	_MAX_LFN	255
    /* The _USE_LFN switches the support of long file name (LFN).
    /
    /   0: Disable support of LFN. _MAX_LFN has no effect.
    /   1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
    /   2: Enable LFN with dynamic working buffer on the STACK.
    /   3: Enable LFN with dynamic working buffer on the HEAP.
    /
    /  To enable the LFN, Unicode handling functions (option/unicode.c) must be added
    /  to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and
    /  additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255.
    /  It should be set 255 to support full featured LFN operations.
    /  When use stack for the working buffer, take care on stack overflow. When use heap
    /  memory for the working buffer, memory management functions, ff_memalloc() and
    /  ff_memfree(), must be added to the project. */
    
    
    #define	_LFN_UNICODE	0
    /* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode)
    /  To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1.
    /  This option also affects behavior of string I/O functions. */
    
    
    #define _STRF_ENCODE	0
    /* When _LFN_UNICODE == 1, this option selects the character encoding on the file to
    /  be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
    /
    /  0: ANSI/OEM
    /  1: UTF-16LE
    /  2: UTF-16BE
    /  3: UTF-8
    /
    /  This option has no effect when _LFN_UNICODE == 0. */
    
    
    #define _FS_RPATH	0
    /* This option configures support of relative path.
    /
    /   0: Disable relative path and remove related functions.
    /   1: Enable relative path. f_chdir() and f_chdrive() are available.
    /   2: f_getcwd() function is available in addition to 1.
    */
    
    
    /*---------------------------------------------------------------------------/
    / Drive/Volume Configurations
    /---------------------------------------------------------------------------*/
    
    #define _VOLUMES	4//支持4个磁盘
    /* Number of volumes (logical drives) to be used. */
    
    
    #define _STR_VOLUME_ID	0
    #define _VOLUME_STRS	"RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
    /* _STR_VOLUME_ID switches string support of volume ID.
    /  When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
    /  number in the path name. _VOLUME_STRS defines the drive ID strings for each
    /  logical drives. Number of items must be equal to _VOLUMES. Valid characters for
    /  the drive ID strings are: A-Z and 0-9. */
    
    
    #define	_MULTI_PARTITION	0
    /* This option switches support of multi-partition on a physical drive.
    /  By default (0), each logical drive number is bound to the same physical drive
    /  number and only an FAT volume found on the physical drive will be mounted.
    /  When multi-partition is enabled (1), each logical drive number can be bound to
    /  arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
    /  funciton will be available. */
    
    
    #define	_MIN_SS		512
    #define	_MAX_SS		512
    /* These options configure the range of sector size to be supported. (512, 1024,
    /  2048 or 4096) Always set both 512 for most systems, all type of memory cards and
    /  harddisk. But a larger value may be required for on-board flash memory and some
    /  type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
    /  to variable sector size and GET_SECTOR_SIZE command must be implemented to the
    /  disk_ioctl() function. */
    
    
    #define	_USE_TRIM	0
    /* This option switches support of ATA-TRIM. (0:Disable or 1:Enable)
    /  To enable Trim function, also CTRL_TRIM command should be implemented to the
    /  disk_ioctl() function. */
    
    
    #define _FS_NOFSINFO	0
    /* If you need to know correct free space on the FAT32 volume, set bit 0 of this
    /  option, and f_getfree() function at first time after volume mount will force
    /  a full FAT scan. Bit 1 controls the use of last allocated cluster number.
    /
    /  bit0=0: Use free cluster count in the FSINFO if available.
    /  bit0=1: Do not trust free cluster count in the FSINFO.
    /  bit1=0: Use last allocated cluster number in the FSINFO if available.
    /  bit1=1: Do not trust last allocated cluster number in the FSINFO.
    */
    
    
    
    /*---------------------------------------------------------------------------/
    / System Configurations
    /---------------------------------------------------------------------------*/
    
    #define	_FS_TINY	0
    /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
    /  At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS bytes.
    /  Instead of private sector buffer eliminated from the file object, common sector
    /  buffer in the file system object (FATFS) is used for the file data transfer. */
    
    
    #define _FS_EXFAT	1
    /* This option switches support of exFAT file system in addition to the traditional
    /  FAT file system. (0:Disable or 1:Enable) To enable exFAT, also LFN must be enabled.
    /  Note that enabling exFAT discards C89 compatibility. */
    
    
    #define _FS_NORTC	0
    #define _NORTC_MON	3
    #define _NORTC_MDAY	1
    #define _NORTC_YEAR	2016
    /* The option _FS_NORTC switches timestamp functiton. If the system does not have
    /  any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable
    /  the timestamp function. All objects modified by FatFs will have a fixed timestamp
    /  defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time.
    /  To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be
    /  added to the project to get current time form real-time clock. _NORTC_MON,
    /  _NORTC_MDAY and _NORTC_YEAR have no effect. 
    /  These options have no effect at read-only configuration (_FS_READONLY = 1). */
    
    
    #define	_FS_LOCK	0
    /* The option _FS_LOCK switches file lock function to control duplicated file open
    /  and illegal operation to open objects. This option must be 0 when _FS_READONLY
    /  is 1.
    /
    /  0:  Disable file lock function. To avoid volume corruption, application program
    /      should avoid illegal open, remove and rename to the open objects.
    /  >0: Enable file lock function. The value defines how many files/sub-directories
    /      can be opened simultaneously under file lock control. Note that the file
    /      lock control is independent of re-entrancy. */
    
    
    #define _FS_REENTRANT	0
    #define _FS_TIMEOUT		1000
    #define	_SYNC_t			HANDLE
    /* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
    /  module itself. Note that regardless of this option, file access to different
    /  volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
    /  and f_fdisk() function, are always not re-entrant. Only file/directory access
    /  to the same volume is under control of this function.
    /
    /   0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
    /   1: Enable re-entrancy. Also user provided synchronization handlers,
    /      ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
    /      function, must be added to the project. Samples are available in
    /      option/syscall.c.
    /
    /  The _FS_TIMEOUT defines timeout period in unit of time tick.
    /  The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
    /  SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be
    /  included somewhere in the scope of ff.c. */
    
    
    /*--- End of configuration options ---*/
    

    注意我没有支持中文,所以创建的名字,目录写的数据是不能写中文的否则会乱码,最后po一下实现的图片

    展开全文
    qq_36305492 2019-05-22 11:28:41
  • 在这一篇中,我们就来讨论如何在STM32上实现USB主机读写U盘文件的方法。 1、应用概述   在我们的产品上有这样一个需求,希望通过大容量的U盘存取数据。我们来分析一下这个需求的具体内容。   首先在硬件上我们...

      在项目应用中,经常会有对外交换数据的需求。USB接口读写U盘无疑是一种颇为方便的选择。在这一篇中,我们就来讨论如何在STM32上实现USB主机读写U盘文件的方法。

    1、应用概述

      在我们的产品上有这样一个需求,希望通过大容量的U盘存取数据。我们来分析一下这个需求的具体内容。

      首先在硬件上我们需要有相应的USB端口,这一点在产品设计时就已经考虑并实现,所以硬件方面我们就不再过多的表述。

      其次我们需要为这个USB的硬件接口编写驱动,就是实现USB端口的底层操作,如IO配置、终端处理等,这是移植的主要工作。

      再者我们需要移植面向大容量存储的USB Host库,这也是我们需要做的工作之一。其实USB主机库本身并不需要我们做什么,但我们要清楚它需要我们提供些什么。

      最后我们还要实现一个应用层操作,它将用于实现我们的读写数据的最终目标。在开发过程中这其实是重点工作,但在这篇文章中他不是重点。我们主要是要实现USB主机库面向大容量存储设备的移植问题。

    2、USB库的移植

      在这个产品中使用的是STM32F407作为控制单元,所以我们使用STM32的USB主机库来实现。接下来就看一看STM32的USB主机库移植问题。我们使用STM32CubeMX来实现USB的相关配置。

      第一步,我们来配置USB的连接端口,我们连接中找到USB_OTG_FS,在右侧的选项中选择Host_Only,如下图所示:

      然后在Middleware中找到USB_HOST,并在右侧“Class for FS IP”中选择“Mass Storage Host Class”配置为大容量存储设备。如下图所示:

      接着在Middleware中找到FATFS,并在右侧选中“USB Disk”。下方的参数中,将CODE_PAGE设置为简体中文。具体如下图所示:

      最后需要在终端中将USB的全局中断选中,并设定中断级别,具体如下图所示:

      中断必须打开,中断级别可以根据需要设置。中断会监测USB的状态,所以中断不打开,USB库无法工作。完成上述配置后就可以生成源码了,然后在此基础上进一步开发。

      对于STM32的USB主机库的移植有两个文件是必须要写的,就是usbh_conf.c和usbh_conf.h文件。这两个文件文件实现USB的基础配置,以及库需要调用的基础函数。在我们使用STM32CubeMX来配置和建立项目时,usbh_conf.c和usbh_conf.h文件也一并生成好了,不需要我们再去单独编写这些平台相关的函数。

    3、应用实现

      因为使用STM32CubeMX来配置和建立项目的缘故,使得USB主机库的移植非常简单,但我们还需要编写应用层的代码。应用层代码主要实现两个方面的内容:一是编写主机库需要回调的获取USB状态的函数;二是轮询处理主机库中的USBH_Process函数以及我们需要处理的工作。

      首先来看应用处理回调函数。这个函数是主机库所要求的,用于处理与用户应用相关的操作,这个函数的原型如下:

      void (*pUsrFunc)(USBH_HandleTypeDef *phost, uint8_t id)

      如果我们的操作比较简单,我们可以在这个函数中直接完成,在测试时比骄傲方便,在复杂一点的应用中不建议这么做。在这里我们实现这个函数如下:

    /* USB应用处理回调函数 */
    static void USBH_UserProcess (USBH_HandleTypeDef *phost, uint8_t id)
    {
      
      switch(id)
      {
      case HOST_USER_SELECT_CONFIGURATION:break;case HOST_USER_DISCONNECTION:
    ​    Appli_state = APPLICATION_DISCONNECT;break;case HOST_USER_CLASS_ACTIVE:
    ​    Appli_state = APPLICATION_READY;break;case HOST_USER_CONNECTION:
    ​    Appli_state = APPLICATION_START;break;default:break;
      }
    }
    

      我们还需要轮询USBH_Process函数并处理我们的应用任务,如读写文件操作等。我们在这一函数中,先调用USBH_Process函数,然后根据当前的状态来决定应处理的工作。在这里,我们希望在USB检测到U盘准备好后对齐进行读写操作,所以我们实现如下:

    /* USB通讯数据处理 */
    void McUsbDataProcess(void)
    {
      /* USB 主机过程:应在主循环中调用,运行主机协议栈 */
      USBH_Process(&hUsbHostFS);
      
      switch(Appli_state)
      {
      case APPLICATION_START:{break;}
      case APPLICATION_READY:{MSC_Application();
    ​      Appli_state = APPLICATION_IDLE;break;}
      case APPLICATION_DISCONNECT:{break;}
      case APPLICATION_IDLE:{break;}
      default:{break;}
      }
    }
    

      我们实现USB主机库的移植,我们尝试往U盘写一个名为STM32.txt的文件,经测试是成功的,具体如下图:

      上图中,我们创建了一个名为STM32.txt的文本文件,我们进一步像文件中写入一定的字符。我们写入一句话,其结果如下:

      至此,我们可以确定我们的USB主机库移植及大容量存储设备的操作应该是正确的。

    4、小结

      在usbh_conf.c和usbh_conf.h文件的实现中已经将硬件接口对象HCD_HandleTypeDef已经连接到了USB主机对象USBH_HandleTypeDef上,所以在应用层面就需要操作USBH_HandleTypeDef对象了。

      在移植测试过程中,我们发现了一个现象。如果在USB HOST库完成软硬件初始化之前就插入U盘,则系统不能完成初始化。等到初始化完成之后插入U盘则操作正常。

    欢迎关注:

    展开全文
    foxclever 2021-08-21 22:24:31
  • 4星
    7.24MB gaofto 2016-01-19 11:33:10
  • 5星
    5.9MB liyun_can 2014-04-02 13:11:26
  • STM32学习】基于STM32USB储存设备 1. 实现动机 最近打算做一下lvgl在stm32上的移植,考虑到lvgl可能会用到一些图片资源,这样一来文件系统就很有必要了,于是想在手头的一块小板子上实现一下FatFs和USB Mass ...

    1. 实现动机

    最近打算做一下lvgl在stm32上的移植,考虑到lvgl可能会用到一些图片资源,

    这样一来文件系统就很有必要了,于是想在手头的一块小板子上实现一下FatFs和USB Mass Storage Class,

    利用板上的FLASH芯片存储文件。其实大部分的工作都是由STM32CubeMX这个软件实现的。


    2. 硬件平台

    这里使用的是WeAct Studio的STM32F411CEU6核心板,长这个样子:

    f411核心板

    下面还有两张实物图, 这是正面。USB接口是TypeC的,

    有一个电源指示灯和用户LED,有两个按钮分别是BOOT和RESET引脚。

    f411核心板实物图1

    这是背面,可以看见上面焊接了一个SPIFLASH,

    具体型号是W25Q128,这个是要自己焊的,买来的时候默认没有。

    f411核心板实物图2

    这个芯片有512KB的片上FLASH,以及128KB的ram,主频最高100MHz。

    但是考虑到要使用USB外设(48MHz),于是将主频设为96MHz。

    硬件的介绍就这么多了,接下来看看软件上的配置。


    3. STM32CubeMX配置

    3.1 配置时钟

    打开CubeMX后选择对应的芯片之后,首先将RCC设置好,外部晶振的频率一定要和实际硬件一致,这里是25MHz,不然会出问题。

    主频设为96MHz

    f411核心板实物图2时钟配置

    3.2 配置SPI1 + DMA

    接下来要配置SPI1接口,这个就要按照实际硬件连接来配置了,打开原理图,找到SPIFLASH连接的部分。

    GPIO功能
    PA4FLASH片选
    PA5SCK
    PA7MOSI
    PA6MISO
    f411核心板外部FLASH连接

    按照上述引脚顺序配置SPI1即可。

    然后在DMA settings中添加SPI_TX和SPI_RX,优先级设为中等:

    SPI DMA SETTINGS

    3.3 配置USB

    要实现USB储存设备那必须要使能USB接口才行,于是在CubeMX中选择USB_OTG_FSMode设为Device_Only即可。然后还要在Middleware中选择USB_DEVICE
    接着在Class For FS IP 中选择Mass Storage Class。最后不要忘了勾选上FATFSMode选择User-defined

    3.4 配置中断

    整个工程中要用到的中断有SPI1收发中断,DMA中断以及USB中断,注意USB的中断优先级不能太高,否则SPI数据传输可能会失败! 本例中USB中断抢占优先级设置为1,其他都是0;

    这些都完成后直接生成工程即可。


    4. 添加W25QXX驱动

    代码移植自正点原子的W25QXX例程,使用HAL库,SPI+DMA传输以提高速度,下面是代码:

    文件名:w25qxx.c

    #include "w25qxx.h"
    //
    //本程序只供学习使用,未经作者许可,不得用于其它任何用途
    //ALIENTEK STM32F103开发板
    //W25QXX驱动代码
    //正点原子@ALIENTEK
    //技术论坛:www.openedv.com
    //创建日期:2017/5/30
    //版本:V1.0
    //版权所有,盗版必究。
    //Copyright(C) 广州市星翼电子科技有限公司 2014-2024
    //All rights reserved
    //
    
    uint16_t W25QXX_TYPE = W25Q64; //默认是W25Q128
    
    //4Kbytes为一个Sector
    //16个扇区为1个Block
    //W25Q256
    //容量为32M字节,共有512个Block,8192个Sector
    
    //初始化SPI FLASH的IO口
    void W25QXX_Init(void)
    {
    	uint8_t temp;
    	W25QXX_CS(1); //SPI FLASH不选中
    	// SPI1_Init();		   			        //已经在main.c中初始化
    	W25QXX_TYPE = W25QXX_ReadID(); //读取FLASH ID.
    	if (W25QXX_TYPE == W25Q128)	   //SPI FLASH为W25Q256
    	{
    		temp = W25QXX_ReadSR(3); //读取状态寄存器3,判断地址模式
    		if ((temp & 0X01) == 0)	 //如果不是4字节地址模式,则进入4字节地址模式
    		{
    			W25QXX_CS(0);						  //选中
    			SPI1_SEND_BYTE(W25X_Enable4ByteAddr); //发送进入4字节地址模式指令
    			uint8_t data = W25X_Enable4ByteAddr;
    			HAL_SPI_Transmit(&hspi1, &data, 1, 1000);
    			W25QXX_CS(1); //取消片选
    		}
    	}
    }
    
    //读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器
    //状态寄存器1:
    //BIT7  6   5   4   3   2   1   0
    //SPR   RV  TB BP2 BP1 BP0 WEL BUSY
    //SPR:默认0,状态寄存器保护位,配合WP使用
    //TB,BP2,BP1,BP0:FLASH区域写保护设置
    //WEL:写使能锁定
    //BUSY:忙标记位(1,忙;0,空闲)
    //默认:0x00
    //状态寄存器2:
    //BIT7  6   5   4   3   2   1   0
    //SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
    //状态寄存器3:
    //BIT7      6    5    4   3   2   1   0
    //HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
    //regno:状态寄存器号,范:1~3
    //返回值:状态寄存器值
    uint8_t W25QXX_ReadSR(uint8_t regno)
    {
    	uint8_t byte = 0, command = 0;
    	switch (regno)
    	{
    	case 1:
    		command = W25X_ReadStatusReg1; //读状态寄存器1指令
    		break;
    	case 2:
    		command = W25X_ReadStatusReg2; //读状态寄存器2指令
    		break;
    	case 3:
    		command = W25X_ReadStatusReg3; //读状态寄存器3指令
    		break;
    	default:
    		command = W25X_ReadStatusReg1;
    		break;
    	}
    	W25QXX_CS(0);							 //使能器件
    	SPI1_SEND_BYTE(command);				 //发送读取状态寄存器命令
    	HAL_SPI_Receive(&hspi1, &byte, 1, 1000); //读取一个字节
    
    	W25QXX_CS(1); //取消片选
    	return byte;
    }
    //写W25QXX状态寄存器
    void W25QXX_Write_SR(uint8_t regno, uint8_t sr)
    {
    	uint8_t command = 0;
    	switch (regno)
    	{
    	case 1:
    		command = W25X_WriteStatusReg1; //写状态寄存器1指令
    		break;
    	case 2:
    		command = W25X_WriteStatusReg2; //写状态寄存器2指令
    		break;
    	case 3:
    		command = W25X_WriteStatusReg3; //写状态寄存器3指令
    		break;
    	default:
    		command = W25X_WriteStatusReg1;
    		break;
    	}
    	W25QXX_CS(0);			 //使能器件
    	SPI1_SEND_BYTE(command); //发送写取状态寄存器命令
    	SPI1_SEND_BYTE(sr);		 //写入一个字节
    	W25QXX_CS(1);			 //取消片选
    }
    //W25QXX写使能
    //将WEL置位
    void W25QXX_Write_Enable(void)
    {
    	W25QXX_CS(0);					  //使能器件
    	SPI1_SEND_BYTE(W25X_WriteEnable); //发送写使能
    	W25QXX_CS(1);					  //取消片选
    }
    //W25QXX写禁止
    //将WEL清零
    void W25QXX_Write_Disable(void)
    {
    	W25QXX_CS(0);					   //使能器件
    	SPI1_SEND_BYTE(W25X_WriteDisable); //发送写禁止指令
    	W25QXX_CS(1);					   //取消片选
    }
    
    //读取芯片ID
    //返回值如下:
    //0XEF13,表示芯片型号为W25Q80
    //0XEF14,表示芯片型号为W25Q16
    //0XEF15,表示芯片型号为W25Q32
    //0XEF16,表示芯片型号为W25Q64
    //0XEF17,表示芯片型号为W25Q128
    //0XEF18,表示芯片型号为W25Q256
    uint16_t W25QXX_ReadID(void)
    {
    	uint16_t Temp = 0;
    	uint8_t byte = 0;
    	W25QXX_CS(0);
    	SPI1_SEND_BYTE(0x90); //发送读取ID命令
    	SPI1_SEND_BYTE(0x00);
    	SPI1_SEND_BYTE(0x00);
    	SPI1_SEND_BYTE(0x00);
    
    	HAL_SPI_Receive(&hspi1, &byte, 1, 1000);
    	Temp |= byte;
    	HAL_SPI_Receive(&hspi1, &byte, 1, 1000);
    	Temp |= byte;
    	W25QXX_CS(1);
    	return Temp;
    }
    //读取SPI FLASH
    //在指定地址开始读取指定长度的数据
    //pBuffer:数据存储区
    //ReadAddr:开始读取的地址(24bit)
    //NumByteToRead:要读取的字节数(最大65535)
    void W25QXX_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
    {
    	W25QXX_CS(0);				   //使能器件
    	SPI1_SEND_BYTE(W25X_ReadData); //发送读取命令
    	if (W25QXX_TYPE == W25Q256)	   //如果是W25Q256的话地址为4字节的,要发送最高8位
    	{
    		SPI1_SEND_BYTE((uint8_t)((ReadAddr) >> 24));
    	}
    	SPI1_SEND_BYTE((uint8_t)((ReadAddr) >> 16)); //发送24bit地址
    	SPI1_SEND_BYTE((uint8_t)((ReadAddr) >> 8));
    	SPI1_SEND_BYTE((uint8_t)ReadAddr);
    	// HAL_SPI_Receive(&hspi1, pBuffer, NumByteToRead, 1000); //读数	
    	HAL_SPI_Receive_DMA(&hspi1, pBuffer, NumByteToRead); //读数	
    	uint16_t count = 10000; 
    	while ((HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_BUSY_RX)&&count)
    	{
    		count --;
    	}
    
    	W25QXX_CS(1);
    }
    //SPI在一页(0~65535)内写入少于256个字节的数据
    //在指定地址开始写入最大256字节的数据
    //pBuffer:数据存储区
    //WriteAddr:开始写入的地址(24bit)
    //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
    void W25QXX_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
    {
    	// uint16_t i;
    	W25QXX_Write_Enable();			  //SET WEL
    	W25QXX_CS(0);					  //使能器件
    	SPI1_SEND_BYTE(W25X_PageProgram); //发送写页命令
    	if (W25QXX_TYPE == W25Q256)		  //如果是W25Q256的话地址为4字节的,要发送最高8位
    	{
    		SPI1_SEND_BYTE((uint8_t)((WriteAddr) >> 24));
    	}
    	SPI1_SEND_BYTE((uint8_t)((WriteAddr) >> 16)); //发送24bit地址
    	SPI1_SEND_BYTE((uint8_t)((WriteAddr) >> 8));
    	SPI1_SEND_BYTE((uint8_t)WriteAddr);
    	// HAL_SPI_Transmit(&hspi1, pBuffer, NumByteToWrite, 1000);
    	HAL_SPI_Transmit_DMA(&hspi1, pBuffer, NumByteToWrite);
    	uint16_t count = 10000; 
    	while ((HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_BUSY_TX)&&count)
    	{
    		count --;
    	}
    	
    		// for (i = 0; i < NumByteToWrite; i++)
    		// 	SPI1_SEND_BYTE(pBuffer[i]); //循环写数
    
    	W25QXX_CS(1);					//取消片选
    	W25QXX_Wait_Busy();				//等待写入结束
    }
    //无检验写SPI FLASH
    //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
    //具有自动换页功能
    //在指定地址开始写入指定长度的数据,但是要确保地址不越界!
    //pBuffer:数据存储区
    //WriteAddr:开始写入的地址(24bit)
    //NumByteToWrite:要写入的字节数(最大65535)
    //CHECK OK
    void W25QXX_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
    {
    	uint16_t pageremain;
    	pageremain = 256 - WriteAddr % 256; //单页剩余的字节数
    	if (NumByteToWrite <= pageremain)
    		pageremain = NumByteToWrite; //不大于256个字节
    	while (1)
    	{
    		W25QXX_Write_Page(pBuffer, WriteAddr, pageremain);
    		if (NumByteToWrite == pageremain)
    			break; //写入结束了
    		else	   //NumByteToWrite>pageremain
    		{
    			pBuffer += pageremain;
    			WriteAddr += pageremain;
    
    			NumByteToWrite -= pageremain; //减去已经写入了的字节数
    			if (NumByteToWrite > 256)
    				pageremain = 256; //一次可以写入256个字节
    			else
    				pageremain = NumByteToWrite; //不够256个字节了
    		}
    	};
    }
    //写SPI FLASH
    //在指定地址开始写入指定长度的数据
    //该函数带擦除操作!
    //pBuffer:数据存储区
    //WriteAddr:开始写入的地址(24bit)
    //NumByteToWrite:要写入的字节数(最大65535)
    uint8_t W25QXX_BUFFER[4096];
    void W25QXX_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
    {
    	uint32_t secpos;
    	uint16_t secoff;
    	uint16_t secremain;
    	uint16_t i;
    	uint8_t *W25QXX_BUF;
    	W25QXX_BUF = W25QXX_BUFFER;
    	secpos = WriteAddr / 4096; //扇区地址
    	secoff = WriteAddr % 4096; //在扇区内的偏移
    	secremain = 4096 - secoff; //扇区剩余空间大小
    	//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
    	HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    
    	if (NumByteToWrite <= secremain)
    		secremain = NumByteToWrite; //不大于4096个字节
    	while (1)
    	{
    		W25QXX_Read(W25QXX_BUF, secpos * 4096, 4096); //读出整个扇区的内容
    		for (i = 0; i < secremain; i++)				  //校验数据
    		{
    			if (W25QXX_BUF[secoff + i] != 0XFF)
    				break; //需要擦除
    		}
    		if (i < secremain) //需要擦除
    		{
    			W25QXX_Erase_Sector(secpos);	//擦除这个扇区
    			for (i = 0; i < secremain; i++) //复制
    			{
    				W25QXX_BUF[i + secoff] = pBuffer[i];
    			}
    			W25QXX_Write_NoCheck(W25QXX_BUF, secpos * 4096, 4096); //写入整个扇区
    		}
    		else
    			W25QXX_Write_NoCheck(pBuffer, WriteAddr, secremain); //写已经擦除了的,直接写入扇区剩余区间.
    		if (NumByteToWrite == secremain)
    			break; //写入结束了
    		else	   //写入未结束
    		{
    			secpos++;	//扇区地址增1
    			secoff = 0; //偏移位置为0
    
    			pBuffer += secremain;		 //指针偏移
    			WriteAddr += secremain;		 //写地址偏移
    			NumByteToWrite -= secremain; //字节数递减
    			if (NumByteToWrite > 4096)
    				secremain = 4096; //下一个扇区还是写不完
    			else
    				secremain = NumByteToWrite; //下一个扇区可以写完了
    		}
    	};
    }
    //擦除整个芯片
    //等待时间超长...
    void W25QXX_Erase_Chip(void)
    {
    	W25QXX_Write_Enable(); //SET WEL
    	W25QXX_Wait_Busy();
    	W25QXX_CS(0);					//使能器件
    	SPI1_SEND_BYTE(W25X_ChipErase); //发送片擦除命令
    	W25QXX_CS(1);					//取消片选
    	W25QXX_Wait_Busy();				//等待芯片擦除结束
    }
    //擦除一个扇区
    //Dst_Addr:扇区地址 根据实际容量设置
    //擦除一个扇区的最少时间:150ms
    void W25QXX_Erase_Sector(uint32_t Dst_Addr)
    {
    	//监视falsh擦除情况,测试用
    	//printf("fe:%x\r\n",Dst_Addr);
    	Dst_Addr *= 4096;
    	W25QXX_Write_Enable(); //SET WEL
    	W25QXX_Wait_Busy();
    	W25QXX_CS(0);					  //使能器件
    	SPI1_SEND_BYTE(W25X_SectorErase); //发送扇区擦除指令
    	if (W25QXX_TYPE == W25Q256)		  //如果是W25Q256的话地址为4字节的,要发送最高8位
    	{
    		SPI1_SEND_BYTE((uint8_t)((Dst_Addr) >> 24));
    	}
    	SPI1_SEND_BYTE((uint8_t)((Dst_Addr) >> 16)); //发送24bit地址
    	SPI1_SEND_BYTE((uint8_t)((Dst_Addr) >> 8));
    	SPI1_SEND_BYTE((uint8_t)Dst_Addr);
    	W25QXX_CS(1);		//取消片选
    	W25QXX_Wait_Busy(); //等待擦除完成
    }
    //等待空闲
    void W25QXX_Wait_Busy(void)
    {
    	while ((W25QXX_ReadSR(1) & 0x01) == 0x01)
    		; // 等待BUSY位清空
    }
    //进入掉电模式
    void W25QXX_PowerDown(void)
    {
    	W25QXX_CS(0);					//使能器件
    	SPI1_SEND_BYTE(W25X_PowerDown); //发送掉电命令
    	W25QXX_CS(1);					//取消片选
    	HAL_Delay(3);					//等待TPD
    }
    //唤醒
    void W25QXX_WAKEUP(void)
    {
    	W25QXX_CS(0);						   //使能器件
    	SPI1_SEND_BYTE(W25X_ReleasePowerDown); //  send W25X_PowerDown command 0xAB
    	W25QXX_CS(1);						   //取消片选
    	HAL_Delay(3);						   //等待TRES1
    }
    
    

    文件名:w25qxx.h

    #ifndef __W25QXX_H
    #define __W25QXX_H
    
    #include <stdint.h>
    #include "stm32f4xx_hal.h"
    #include "main.h"
    //	 
    //本程序只供学习使用,未经作者许可,不得用于其它任何用途
    //ALIENTEK STM32F103开发板
    //W25QXX驱动代码	   
    //正点原子@ALIENTEK
    //技术论坛:www.openedv.com
    //创建日期:2017/5/30
    //版本:V1.0
    //版权所有,盗版必究。
    //Copyright(C) 广州市星翼电子科技有限公司 2014-2024
    //All rights reserved									  
    // 	
    
    //W25X系列/Q系列芯片列表	   
    //W25Q80  ID  0XEF13
    //W25Q16  ID  0XEF14
    //W25Q32  ID  0XEF15
    //W25Q64  ID  0XEF16	
    //W25Q128 ID  0XEF17	
    //W25Q256 ID  0XEF18
    #define W25Q80 	0XEF13 	
    #define W25Q16 	0XEF14
    #define W25Q32 	0XEF15
    #define W25Q64 	0XEF16
    #define W25Q128	0XEF17
    #define W25Q256 0XEF18
    
    extern uint16_t W25QXX_TYPE;					//定义W25QXX芯片型号		   
    
    // #define	W25QXX_CS 		HAL_GPIO_TogglePin(NULL, NULL)  		//W25QXX的片选信号
    
    #define W25QXX_CS(state)    HAL_GPIO_WritePin(F_CS_GPIO_Port, F_CS_Pin, (GPIO_PinState)state)
    #define SPI1_SEND_BYTE(byte) \
    do{ \
        uint8_t tmp = byte;\
        HAL_SPI_Transmit(&hspi1, &tmp, 1, 1000);\
    }while(0);
    // 
    //指令表
    #define W25X_WriteEnable		0x06 
    #define W25X_WriteDisable		0x04 
    #define W25X_ReadStatusReg1		0x05 
    #define W25X_ReadStatusReg2		0x35 
    #define W25X_ReadStatusReg3		0x15 
    #define W25X_WriteStatusReg1    0x01 
    #define W25X_WriteStatusReg2    0x31 
    #define W25X_WriteStatusReg3    0x11 
    #define W25X_ReadData			0x03 
    #define W25X_FastReadData		0x0B 
    #define W25X_FastReadDual		0x3B 
    #define W25X_PageProgram		0x02 
    #define W25X_BlockErase			0xD8 
    #define W25X_SectorErase		0x20 
    #define W25X_ChipErase			0xC7 
    #define W25X_PowerDown			0xB9 
    #define W25X_ReleasePowerDown	0xAB 
    #define W25X_DeviceID			0xAB 
    #define W25X_ManufactDeviceID	0x90 
    #define W25X_JedecDeviceID		0x9F 
    #define W25X_Enable4ByteAddr    0xB7
    #define W25X_Exit4ByteAddr      0xE9
    
    void W25QXX_Init(void);
    uint16_t  W25QXX_ReadID(void);  	    		//读取FLASH ID
    uint8_t W25QXX_ReadSR(uint8_t regno);             //读取状态寄存器 
    void W25QXX_4ByteAddr_Enable(void);     //使能4字节地址模式
    void W25QXX_Write_SR(uint8_t regno,uint8_t sr);   //写状态寄存器
    void W25QXX_Write_Enable(void);  		//写使能 
    void W25QXX_Write_Disable(void);		//写保护
    void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
    void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead);   //读取flash
    void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//写入flash
    void W25QXX_Erase_Chip(void);    	  	//整片擦除
    void W25QXX_Erase_Sector(uint32_t Dst_Addr);	//扇区擦除
    void W25QXX_Wait_Busy(void);           	//等待空闲
    void W25QXX_PowerDown(void);        	//进入掉电模式
    void W25QXX_WAKEUP(void);				//唤醒
    
    #endif
    
    

    5. 修改usbd_storage_if.c代码

    本文件提供了一个接口,只要实现了存储设备的读写扇区操作就可以启用USB存储设备功能了,在本例中只需要将W25QXX_Read和W25QXX_Write填到对应的位置即可,十分简便。

    文件名:USB_DEVICE\App\usbd_storage_if.c

    /* USER CODE BEGIN Header */
    /**
      ******************************************************************************
      * @file           : usbd_storage_if.c
      * @version        : v1.0_Cube
      * @brief          : Memory management layer.
      ******************************************************************************
      * @attention
      *
      * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
      * All rights reserved.</center></h2>
      *
      * This software component is licensed by ST under Ultimate Liberty license
      * SLA0044, the "License"; You may not use this file except in compliance with
      * the License. You may obtain a copy of the License at:
      *                             www.st.com/SLA0044
      *
      ******************************************************************************
      */
    /* USER CODE END Header */
    
    /* Includes ------------------------------------------------------------------*/
    #include "usbd_storage_if.h"
    
    /* USER CODE BEGIN INCLUDE */
    #include "w25qxx.h"
    /* USER CODE END INCLUDE */
    
    /* Private typedef -----------------------------------------------------------*/
    /* Private define ------------------------------------------------------------*/
    /* Private macro -------------------------------------------------------------*/
    
    /* USER CODE BEGIN PV */
    /* Private variables ---------------------------------------------------------*/
    
    /* USER CODE END PV */
    
    /** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
      * @brief Usb device.
      * @{
      */
    
    /** @defgroup USBD_STORAGE
      * @brief Usb mass storage device module
      * @{
      */
    
    /** @defgroup USBD_STORAGE_Private_TypesDefinitions
      * @brief Private types.
      * @{
      */
    
    /* USER CODE BEGIN PRIVATE_TYPES */
    
    /* USER CODE END PRIVATE_TYPES */
    
    /**
      * @}
      */
    
    /** @defgroup USBD_STORAGE_Private_Defines
      * @brief Private defines.
      * @{
      */
    
    #define STORAGE_LUN_NBR                  1
    #define STORAGE_BLK_NBR                  0x10000
    #define STORAGE_BLK_SIZ                  0x200
    
    /* USER CODE BEGIN PRIVATE_DEFINES */
    #define USER_STORAGE_LUN_NBR                  1
    #define USER_STORAGE_BLK_NBR                  0x8000
    #define USER_STORAGE_BLK_SIZ                  0x200
    /* USER CODE END PRIVATE_DEFINES */
    
    /**
      * @}
      */
    
    /** @defgroup USBD_STORAGE_Private_Macros
      * @brief Private macros.
      * @{
      */
    
    /* USER CODE BEGIN PRIVATE_MACRO */
    
    /* USER CODE END PRIVATE_MACRO */
    
    /**
      * @}
      */
    
    /** @defgroup USBD_STORAGE_Private_Variables
      * @brief Private variables.
      * @{
      */
    
    /* USER CODE BEGIN INQUIRY_DATA_FS */
    /** USB Mass storage Standard Inquiry Data. */
    const int8_t STORAGE_Inquirydata_FS[] = {/* 36 */
    
      /* LUN 0 */
      0x00,
      0x80,
      0x02,
      0x02,
      (STANDARD_INQUIRY_DATA_LEN - 5),
      0x00,
      0x00,
      0x00,
      'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer : 8 bytes */
      'P', 'r', 'o', 'd', 'u', 'c', 't', ' ', /* Product      : 16 Bytes */
      ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
      '0', '.', '0' ,'1'                      /* Version      : 4 Bytes */
    };
    /* USER CODE END INQUIRY_DATA_FS */
    
    /* USER CODE BEGIN PRIVATE_VARIABLES */
    
    /* USER CODE END PRIVATE_VARIABLES */
    
    /**
      * @}
      */
    
    /** @defgroup USBD_STORAGE_Exported_Variables
      * @brief Public variables.
      * @{
      */
    
    extern USBD_HandleTypeDef hUsbDeviceFS;
    
    /* USER CODE BEGIN EXPORTED_VARIABLES */
    
    /* USER CODE END EXPORTED_VARIABLES */
    
    /**
      * @}
      */
    
    /** @defgroup USBD_STORAGE_Private_FunctionPrototypes
      * @brief Private functions declaration.
      * @{
      */
    
    static int8_t STORAGE_Init_FS(uint8_t lun);
    static int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size);
    static int8_t STORAGE_IsReady_FS(uint8_t lun);
    static int8_t STORAGE_IsWriteProtected_FS(uint8_t lun);
    static int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
    static int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
    static int8_t STORAGE_GetMaxLun_FS(void);
    
    /* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
    
    /* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */
    
    /**
      * @}
      */
    
    USBD_StorageTypeDef USBD_Storage_Interface_fops_FS =
    {
      STORAGE_Init_FS,
      STORAGE_GetCapacity_FS,
      STORAGE_IsReady_FS,
      STORAGE_IsWriteProtected_FS,
      STORAGE_Read_FS,
      STORAGE_Write_FS,
      STORAGE_GetMaxLun_FS,
      (int8_t *)STORAGE_Inquirydata_FS
    };
    
    /* Private functions ---------------------------------------------------------*/
    /**
      * @brief  Initializes over USB FS IP
      * @param  lun:
      * @retval USBD_OK if all operations are OK else USBD_FAIL
      */
    int8_t STORAGE_Init_FS(uint8_t lun)
    {
      /* USER CODE BEGIN 2 */
      return (USBD_OK);
      /* USER CODE END 2 */
    }
    
    /**
      * @brief  .
      * @param  lun: .
      * @param  block_num: .
      * @param  block_size: .
      * @retval USBD_OK if all operations are OK else USBD_FAIL
      */
    int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
    {
      /* USER CODE BEGIN 3 */
      *block_num  = USER_STORAGE_BLK_NBR;
      *block_size = USER_STORAGE_BLK_SIZ;
      return (USBD_OK);
      /* USER CODE END 3 */
    }
    
    /**
      * @brief  .
      * @param  lun: .
      * @retval USBD_OK if all operations are OK else USBD_FAIL
      */
    int8_t STORAGE_IsReady_FS(uint8_t lun)
    {
      /* USER CODE BEGIN 4 */
      return (USBD_OK);
      /* USER CODE END 4 */
    }
    
    /**
      * @brief  .
      * @param  lun: .
      * @retval USBD_OK if all operations are OK else USBD_FAIL
      */
    int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)
    {
      /* USER CODE BEGIN 5 */
      return (USBD_OK);
      /* USER CODE END 5 */
    }
    
    /**
      * @brief  .
      * @param  lun: .
      * @retval USBD_OK if all operations are OK else USBD_FAIL
      */
    int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
    {
      /* USER CODE BEGIN 6 */
      W25QXX_Read(buf, blk_addr * USER_STORAGE_BLK_SIZ, blk_len * USER_STORAGE_BLK_SIZ);
      return (USBD_OK);
      /* USER CODE END 6 */
    }
    
    /**
      * @brief  .
      * @param  lun: .
      * @retval USBD_OK if all operations are OK else USBD_FAIL
      */
    int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
    {
      /* USER CODE BEGIN 7 */
      W25QXX_Write(buf, blk_addr * USER_STORAGE_BLK_SIZ, blk_len * USER_STORAGE_BLK_SIZ);
      return (USBD_OK);
      /* USER CODE END 7 */
    }
    
    /**
      * @brief  .
      * @param  None
      * @retval .
      */
    int8_t STORAGE_GetMaxLun_FS(void)
    {
      /* USER CODE BEGIN 8 */
      return (USER_STORAGE_LUN_NBR - 1);
      /* USER CODE END 8 */
    }
    
    /* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
    
    /* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
    
    /**
      * @}
      */
    
    /**
      * @}
      */
    
    /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
    
    

    其中

    #define USER_STORAGE_LUN_NBR 1
    #define USER_STORAGE_BLK_NBR 0x8000
    #define USER_STORAGE_BLK_SIZ 0x200

    这三个定义是为了防止CubeMX覆盖我们自己的代码而添加的。

    最后编译代码烧录到单片机


    6 效果

    就如果是第一次连接到电脑,系统应该会提示格式化磁盘,按照提示格式化为FAT32格式即可,至于为什么一定是FAT32,因为后面还要用到FATFS,而FATFS就是默认使用FAT32格式的。

    然后测试一下能不能拷文件进去:

    coping file

    这速度不敢恭维。。。但效果确实达到了

    读取文件就比较快了,大概900Kbps的样子,就不贴图了

    最后给出源码(百度网盘):

    链接:https://pan.baidu.com/s/137bI0tZSkYbcOlA4GdSDLw
    提取码:sync

    感谢您的阅读,期待下次再见🕊🕊🕊

    展开全文
    Slade99X 2021-07-20 18:18:06
  • weixin_39921504 2020-12-20 23:49:48
  • 5星
    10.04MB zhangyan3494 2013-12-31 09:47:12
  • weixin_44536527 2021-08-02 09:49:42
  • qq_35968965 2019-02-25 14:42:32
  • weixin_39769984 2020-11-08 11:15:59
  • qq_41830158 2021-11-10 13:26:58
  • asher__zhou 2020-05-17 11:22:51
  • 9.24MB weixin_38512659 2021-04-21 18:29:27
  • 9.52MB qq_31511067 2019-04-13 18:52:26
  • 5星
    50.67MB mobei1983 2020-12-05 13:40:38
  • ZLK1214 2017-12-31 15:53:32
  • asher__zhou 2020-04-16 13:06:16
  • USB_ABC 2020-08-17 21:59:41
  • qq_34254642 2021-01-04 08:47:37
  • qq_29559457 2021-02-07 15:16:51
  • weixin_31633071 2021-02-04 21:34:11
  • Simpson_ 2021-02-02 10:46:42
  • 4星
    23.99MB lz_kwok 2017-10-09 22:04:24
  • Chen_ry 2020-12-25 16:03:43
  • 4星
    1.5MB chenbingjy 2015-07-11 12:39:29
  • 502KB weixin_42099814 2021-07-01 18:45:00
  • ai5945fei 2021-11-16 15:06:31
  • 5星
    4.67MB zoujiao6609 2018-08-16 17:10:59
  • 7.06MB qq_24835087 2018-08-27 10:26:16

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,938
精华内容 1,175
关键字:

usb写入stm32