STM32F1移植USB庫實現外部FLASH模擬U盤功能

在STM32F1上移植FatFs文件系統後,實現了對FLASH數據的讀寫管理,但還不夠直觀,如果能像U盤一樣在電腦上直接操作FLASH的文件數據,進行一些參數的預設和修改等功能,可以提供更好的用戶體驗。本文主要介紹STM32的USB庫的移植方法,不討論USB通訊的原理。
ST公司針對不同內核的芯片,提供了對應的USB庫及例程,如下圖:
在這裏插入圖片描述
STSW-STM32121官網下載地址:https://www.st.com/en/embedded-software/stsw-stm32121.html
STM32 USB-FS-Device開發套件的用戶手冊(UM0424)官網下載地址:https://www.st.com/resource/en/user_manual/cd00158241-stm32-usb-fs-device-development-kit-stmicroelectronics.pdf
採用的基礎工程是我上一篇文章介紹的移植FatFs後的工程,文章鏈接:STM32F1移植FATFS文件系統

一、移植準備
從官網下載STSW-STM32121庫,目前的最新版本是STM32_USB-FS-Device_Lib_V4.1.0
解壓後包含如下文件夾:
在這裏插入圖片描述
將Libraries文件夾中的STM32_USB-FS-Device_Driver文件夾
在這裏插入圖片描述在這裏插入圖片描述
和Projects文件夾中的Mass_Storage文件夾拷貝至Keil工程。
在這裏插入圖片描述在這裏插入圖片描述
官方庫中給出了官方評估板的程序示例,官方評估板對應的芯片型號如下圖,在移植時僅需要保留inc和src文件夾中的內容。
在這裏插入圖片描述

二、庫文件說明
USB固件庫包含許多文件,下圖顯示了典型的USB應用中不同組件與USB-FS-Device庫之間的關係。
在這裏插入圖片描述
上圖表明USB-FS-Device庫被分成兩層:

  • STM32_USB-FS_Device_Driver設備驅動層:該層負責管理與USB-FS_Device外設和USB標準協議的直接通信。 STM32_USBFS_Device_Driver符合USB 2.0規範,並且與標準STM32標準外設庫分離。
  • 應用接口層:該層爲用戶提供了固件庫和最終應用程序之間的完整接口。
    下表給出了庫文件的說明:
    在這裏插入圖片描述
    三、移植步驟
    1、刪除不必要的文件
    清理Mass_Storage文件夾下的inc文件夾內的文件,刪除
  • fsmc_nand.h,nand_if.h(用以配置NAND FLASH的頭文件,這裏用不到);
  • stm32_it.h(STM32中斷頭文件,基礎工程已存在)
  • stm32f10x_conf.h,stm32f30x_conf.h,stm32f37x_conf.h,stm32l1xx_conf.h(不同主控芯片的配置頭文件,在基礎工程內已存在,所以不使用這些)。
    在這裏插入圖片描述
    清理Mass_Storage文件夾下的文件夾內的文件,刪除:
  • fsmc_nand.c,nand_if.c
  • main.c(基礎工程已有主函數,可以用來參考USB設備初始化的程序步驟)
  • stm32_it.c(管理中斷的文件,基礎工程已存在,可用來參考USB中斷函數的實現)
  • system_stm32f10x.c,system_stm32f30x.c,system_stm32f37x.c,system_stm32l1xx.c
    在這裏插入圖片描述
    2、將整理好的庫文件添加到工程
    在這裏插入圖片描述
    由於官方庫的程序是針對官方開發板編寫的,我們從中拷貝的代碼在編譯後存在一些錯誤,下面對此進行修改。

3、修改“platform_config.h”
platform_config.h中定義了官方開發板的型號,所以刪除相關的宏定義並添加一些頭文件

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __PLATFORM_CONFIG_H
#define __PLATFORM_CONFIG_H

/* Includes ------------------------------------------------------------------*/
/* 註釋掉關於官方開發板的宏定義,並添加一些頭文件-----------------------------*/
#include "stm32f10x.h"
#include <stdio.h>
#include "./usart/bsp_usart.h"
#include "./flash/bsp_spi_flash.h"

//#if defined (USE_STM32L152_EVAL)
//  #include "stm32l152_eval.h"
//  #include "stm32l152_eval_spi_sd.h"

//#elif defined (USE_STM32L152D_EVAL)
//  #include "stm32l152d_eval.h"
//  #include "stm32l152d_eval_sdio_sd.h"

//#elif defined (USE_STM3210E_EVAL)
//  #include "stm3210e_eval_sdio_sd.h"
//  #include "stm3210e_eval.h"
//  #include "fsmc_nand.h"
//  #include "nand_if.h"

//#elif defined (USE_STM3210B_EVAL)
//  #include "stm3210b_eval.h"
//  #include "stm3210b_eval_spi_sd.h"

//#elif defined (USE_STM32373C_EVAL)
// #include "stm32373c_eval.h"
// #include "stm32373c_eval_spi_sd.h"

//#elif defined (USE_STM32303C_EVAL)
// #include "stm32303c_eval.h"
// #include "stm32303c_eval_spi_sd.h"

//#else
// #error "Missing define: Evaluation board (ie. USE_STM3210E_EVAL)"
//#endif


/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/

/* Define if Low power mode is enabled; it allows entering the device into 
   STOP mode following USB Suspend event, and wakes up after the USB wakeup
   event is received. */
//#define USB_LOW_PWR_MGMT_SUPPORT

/*Unique Devices IDs register set */
/* 唯一設備ID寄存器集,本人的開發板用的是stm32f103 */

//#if defined(STM32L1XX_MD) || defined(STM32L1XX_HD) || defined(STM32L1XX_MD_PLUS) 

//#define         ID1          (0x1FF80050)
//#define         ID2          (0x1FF80054)
//#define         ID3          (0x1FF80064)

//#elif defined (STM32F37X) || defined(STM32F303xC) || defined(STM32F303xE)

//#define         ID1          (0x1FFFF7AC)
//#define         ID2          (0x1FFFF7B0)
//#define         ID3          (0x1FFFF7B4)

//#else /*STM32F1x*/

#define         ID1          (0x1FFFF7E8)
#define         ID2          (0x1FFFF7EC)
#define         ID3          (0x1FFFF7F0)

//#endif

//#define RCC_AHBPeriph_ALLGPIO                 (RCC_AHBPeriph_GPIOA \
//                                              | RCC_AHBPeriph_GPIOB \
//                                              | RCC_AHBPeriph_GPIOC \
//                                              | RCC_AHBPeriph_GPIOD \
//                                              | RCC_AHBPeriph_GPIOE \
//                                              | RCC_AHBPeriph_GPIOF )    
     
/* Define the STM32F10x hardware depending on the used evaluation board */
/*定義開發板上的USB_DISCONNECT端口連接到芯片的GPIO引腳,本人
  的開發板沒有使用USB_DISCONNECT端口,所以註釋掉這段宏定義 */
//#ifdef USE_STM3210B_EVAL

//  #define USB_DISCONNECT                    GPIOD  
//  #define USB_DISCONNECT_PIN                GPIO_Pin_9
//  #define RCC_APB2Periph_GPIO_DISCONNECT    RCC_APB2Periph_GPIOD

//  #define RCC_APB2Periph_ALLGPIO              (RCC_APB2Periph_GPIOA \
//                                               | RCC_APB2Periph_GPIOB \
//                                               | RCC_APB2Periph_GPIOC \
//                                               | RCC_APB2Periph_GPIOD \
//                                               | RCC_APB2Periph_GPIOE )
//#elif defined (USE_STM3210E_EVAL)

//  #define USB_DISCONNECT                    GPIOB  
//  #define USB_DISCONNECT_PIN                GPIO_Pin_14
//  #define RCC_APB2Periph_GPIO_DISCONNECT    RCC_APB2Periph_GPIOB

//  #define RCC_APB2Periph_ALLGPIO              (RCC_APB2Periph_GPIOA \
//                                               | RCC_APB2Periph_GPIOB \
//                                               | RCC_APB2Periph_GPIOC \
//                                               | RCC_APB2Periph_GPIOD \
//                                               | RCC_APB2Periph_GPIOE )
//#elif defined (USE_STM32L152_EVAL)
// /* 
//   For STM32L15xx devices it is possible to use the internal USB pullup
//   controlled by register SYSCFG_PMC (refer to RM0038 reference manual for
//   more details).
//   It is also possible to use external pullup (and disable the internal pullup)
//   by setting the define USB_USE_EXTERNAL_PULLUP in file platform_config.h
//   and configuring the right pin to be used for the external pull up configuration.
//   To have more details on how to use an external pull up, please refer to 
//   STM3210E-EVAL evaluation board manuals.
//   */
// /* Uncomment the following define to use an external pull up instead of the 
//    integrated STM32L15xx internal pull up. In this case make sure to set up
//    correctly the external required hardware and the GPIO defines below.*/
///* #define USB_USE_EXTERNAL_PULLUP */

// #if !defined(USB_USE_EXTERNAL_PULLUP)
//  #define STM32L15_USB_CONNECT                SYSCFG_USBPuCmd(ENABLE)
//  #define STM32L15_USB_DISCONNECT             SYSCFG_USBPuCmd(DISABLE)

// #elif defined(USB_USE_EXTERNAL_PULLUP)
//  /* PA0 is chosen just as illustrating example, you should modify the defines
//    below according to your hardware configuration. */ 
//  #define USB_DISCONNECT                      GPIOA
//  #define USB_DISCONNECT_PIN                  GPIO_Pin_0
//  #define RCC_AHBPeriph_GPIO_DISCONNECT       RCC_AHBPeriph_GPIOA
//  #define STM32L15_USB_CONNECT                GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN)
//  #define STM32L15_USB_DISCONNECT             GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN)
// #endif /* USB_USE_EXTERNAL_PULLUP */

//#elif defined (USE_STM32L152D_EVAL)

//  #define USB_DISCONNECT                    GPIOE 
//  #define USB_DISCONNECT_PIN                GPIO_Pin_6
//  #define RCC_AHBPeriph_GPIO_DISCONNECT    RCC_AHBPeriph_GPIOE

//#elif defined (USE_STM32373C_EVAL)

//  #define USB_DISCONNECT                      GPIOC  
//  #define USB_DISCONNECT_PIN                  GPIO_Pin_5
//  #define RCC_AHBPeriph_GPIO_DISCONNECT       RCC_AHBPeriph_GPIOC
// 
//#elif defined (USE_STM32303C_EVAL)

//  #define USB_DISCONNECT                      GPIOB  
//  #define USB_DISCONNECT_PIN                  GPIO_Pin_8
//  #define RCC_AHBPeriph_GPIO_DISCONNECT       RCC_AHBPeriph_GPIOB
//#endif /* USE_STM3210B_EVAL */

/* Exported macro ------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */

#endif /* __PLATFORM_CONFIG_H */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

4、修改“hw_config.c”
修改USB中斷、時鐘等相關函數,刪除與LED相關的函數

/* Includes ------------------------------------------------------------------*/

#include "hw_config.h"

//#include "stm32_it.h" //將其替換爲自己的中斷頭文件
#include "stm32f10x_it.h"

#include "mass_mal.h"
#include "usb_desc.h"
#include "usb_pwr.h"
#include "usb_lib.h"


/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
ErrorStatus HSEStartUpStatus;
EXTI_InitTypeDef EXTI_InitStructure;

/* Extern variables ----------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
static void IntToUnicode (uint32_t value , uint8_t *pbuf , uint8_t len);

/* Private functions ---------------------------------------------------------*/

/*******************************************************************************
* Function Name  : Set_System
* Description    : Configures Main system clocks & power
* Input          : None.
* Return         : None.
*******************************************************************************/
//刪除原來的Set_System()函數,重新編寫,本人開發板上的USB DM接PA11,DP接PA12
void Set_System(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure; 
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  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);
  /********************************************/
  /*          初始化中間層接口                */
  /********************************************/
  
  /* MAL configuration */
  MAL_Config();
}

//void Set_System(void)
//{
//  GPIO_InitTypeDef  GPIO_InitStructure;  
//  /*!< At this stage the microcontroller clock setting is already configured, 
//       this is done through SystemInit() function which is called from startup
//       file (startup_stm32xxx.s) before to branch to application main.
//       To reconfigure the default setting of SystemInit() function, refer to
//       system_stm32xxx.c file
//     */ 
//  
//#if defined(STM32L1XX_MD) || defined(STM32L1XX_HD) || defined(STM32F37X) || defined(STM32F303xC)  || defined(STM32F303xE)

//  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

//#else /* defined(STM32F10X_HD) || defined(STM32F10X_MD) defined(STM32F10X_XL)*/
//  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//#endif

//  /********************************************/
//  /*  Configure USB DM/DP pins                */
//  /********************************************/
//  
//#if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)
//  
//  /* Configure USB DM/DP pin. This is optional, and maintained only for user guidance.
//  For the STM32L products there is no need to configure the PA12/PA11 pins couple 
//  as Alternate Function */
//  
//  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
//  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz;
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
//  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
//  GPIO_Init(GPIOA, &GPIO_InitStructure);
//  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
//  
//  /* Enable all GPIOs Clock*/
//  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ALLGPIO, ENABLE);
//  
//#elif defined(STM32F10X_HD) || defined(STM32F10X_MD)  || defined(STM32F10X_XL)
//  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);
//  
//  /* Enable all GPIOs Clock*/
//  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ALLGPIO, ENABLE);

//#else /* defined(STM32F37X) || defined(STM32F303xC) */
//  
//  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
//  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
//  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
//  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
//  GPIO_Init(GPIOA, &GPIO_InitStructure);
//    
//  /*SET PA11,12 for USB: USB_DM,DP*/
//  GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_14);
//  GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_14);
//    
//  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ALLGPIO, ENABLE);
//    
//#endif 
//  
//  /********************************************/
//  /* Enable the USB PULL UP                   */
//  /********************************************/
//#if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)  
//  
//  /* Enable integrated STM32L15xx internal pull up 
//  Enable the SYSCFG module clock*/
//  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//  
//#elif defined(STM32F10X_HD) || defined(STM32F10X_MD)  || defined(STM32F10X_XL)
//  
//  /* USB_DISCONNECT used as USB pull-up */
//  GPIO_InitStructure.GPIO_Pin = USB_DISCONNECT_PIN;
//  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
//  GPIO_Init(USB_DISCONNECT, &GPIO_InitStructure);
//  
//  /* Enable the USB disconnect GPIO clock */
//  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_DISCONNECT, ENABLE);
//  
//#else  /* defined(STM32F37X) || defined(STM32F303xC) */
//  
//  /* Enable the USB disconnect GPIO clock */
//  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIO_DISCONNECT, ENABLE);

//  /* USB_DISCONNECT used as USB pull-up */
//  GPIO_InitStructure.GPIO_Pin = USB_DISCONNECT_PIN;
//  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
//  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
//  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
//  GPIO_Init(USB_DISCONNECT, &GPIO_InitStructure);
//  
//#endif
//  
//#ifdef USB_LOW_PWR_MGMT_SUPPORT
//  
//  /**********************************************************************/
//  /*  Configure the EXTI line 18 connected internally to the USB IP     */
//  /**********************************************************************/
//  
//  EXTI_ClearITPendingBit(EXTI_Line18);
//  EXTI_InitStructure.EXTI_Line = EXTI_Line18;
//  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
//  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
//  EXTI_Init(&EXTI_InitStructure);
//  
//#endif  /* USB_LOW_PWR_MGMT_SUPPORT */
//  

//  /********************************************/
//  /*      Init the media interface            */
//  /********************************************/
//  
//  /* MAL configuration */
//  MAL_Config();
// 
//}

/*******************************************************************************
* Function Name  : Set_USBClock
* Description    : Configures USB Clock input (48MHz)
* Input          : None.
* Return         : None.
*******************************************************************************/
//設置USB時鐘
void Set_USBClock(void)
{
  /* Select USBCLK source */
  RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
  
  /* Enable the USB clock */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
}
//void Set_USBClock(void)
//{
//#if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)
//  /* Enable USB clock */
//  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
//  
//#else
//  /* Select USBCLK source */
//  RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
//  
//  /* Enable the USB clock */
//  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
//#endif /* STM32L1XX_XD */
//}

/*******************************************************************************
* Function Name  : Leave_LowPowerMode
* Description    : Restores system clocks and power while exiting suspend mode
* Input          : None.
* Return         : None.
*******************************************************************************/
void Leave_LowPowerMode(void)
{
  DEVICE_INFO *pInfo = &Device_Info;

  /* Set the device state to the correct state */
  if (pInfo->Current_Configuration != 0)
  {
    /* Device configured */
    bDeviceState = CONFIGURED;
  }
  else
  {
    bDeviceState = ATTACHED;
  }
  /*Enable SystemCoreClock*/
  SystemInit(); 
}

/*******************************************************************************
* Function Name  : USB_Interrupts_Config
* Description    : Configures the USB interrupts
* Input          : None.
* Return         : None.
*******************************************************************************/
//USB中斷設置
void USB_Interrupts_Config(void)
{
	NVIC_InitTypeDef NVIC_InitStructure; 
  
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);  
	
	NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  NVIC_InitStructure.NVIC_IRQChannel = USB_HP_CAN1_TX_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}
//void USB_Interrupts_Config(void)
//{
//  NVIC_InitTypeDef NVIC_InitStructure; 
//  
//  /* 2 bit for pre-emption priority, 2 bits for subpriority */
//  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  
//  
//#if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS)
//  NVIC_InitStructure.NVIC_IRQChannel = USB_LP_IRQn;
//  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
//  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
//  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//  NVIC_Init(&NVIC_InitStructure);
//  
//    /* Enable the USB Wake-up interrupt */
//  NVIC_InitStructure.NVIC_IRQChannel = USB_FS_WKUP_IRQn;
//  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//  NVIC_Init(&NVIC_InitStructure);
//  
//#elif defined(STM32F37X)
//  /* Enable the USB interrupt */
//  NVIC_InitStructure.NVIC_IRQChannel = USB_LP_IRQn;
//  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
//  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
//  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//  NVIC_Init(&NVIC_InitStructure);
//  
//  /* Enable the USB Wake-up interrupt */
//  NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;
//  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//  NVIC_Init(&NVIC_InitStructure);
//  
//#else
//  NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
//  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
//  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
//  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//  NVIC_Init(&NVIC_InitStructure);
//  
//    /* Enable the USB Wake-up interrupt */
//  NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;
//  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//  NVIC_Init(&NVIC_InitStructure);
//#endif /* STM32L1XX_XD */

//  
//#if defined(STM32F10X_HD) || defined(STM32F10X_XL) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS) 
//  NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;
//  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
//  NVIC_Init(&NVIC_InitStructure);
//  NVIC_InitStructure.NVIC_IRQChannel = SD_SDIO_DMA_IRQn;
//  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//  NVIC_Init(&NVIC_InitStructure);
//#endif /* STM32L1XX_MD */
// 
//}

//沒有LED,所以刪除相關的代碼
/*******************************************************************************
* Function Name  : Led_Config
* Description    : configure the Read/Write LEDs.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
//void Led_Config(void)
//{
//  /* Configure the LEDs */
//  STM_EVAL_LEDInit(LED1);
//  STM_EVAL_LEDInit(LED2);  
//  STM_EVAL_LEDInit(LED3);
//  STM_EVAL_LEDInit(LED4);  
//}

/*******************************************************************************
* Function Name  : Led_RW_ON
* Description    : Turn ON the Read/Write LEDs.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
//void Led_RW_ON(void)
//{
//  STM_EVAL_LEDOn(LED3);
//}

/*******************************************************************************
* Function Name  : Led_RW_OFF
* Description    : Turn off the Read/Write LEDs.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
//void Led_RW_OFF(void)
//{
//  STM_EVAL_LEDOff(LED3);
//}
/*******************************************************************************
* Function Name  : USB_Configured_LED
* Description    : Turn ON the Read/Write LEDs.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
//void USB_Configured_LED(void)
//{
//  STM_EVAL_LEDOn(LED1);
//}

/*******************************************************************************
* Function Name  : USB_NotConfigured_LED
* Description    : Turn off the Read/Write LEDs.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
//void USB_NotConfigured_LED(void)
//{
//  STM_EVAL_LEDOff(LED1);
//}

/*******************************************************************************
* Function Name  : USB_Cable_Config
* Description    : Software Connection/Disconnection of USB Cable.
* Input          : None.
* Return         : Status
*******************************************************************************/
void USB_Cable_Config (FunctionalState NewState)
{

#if defined(STM32L1XX_MD)
  if (NewState != DISABLE)
  {
    STM32L15_USB_CONNECT;
  }
  else
  {
    STM32L15_USB_DISCONNECT;
  }  
 
#elif defined(STM32L1XX_HD) || defined(STM32L1XX_MD_PLUS)
  if (NewState != DISABLE)
  {
    GPIO_ResetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
    SYSCFG_USBPuCmd(ENABLE);
  }
  else
  {
    GPIO_SetBits(USB_DISCONNECT, USB_DISCONNECT_PIN);
    SYSCFG_USBPuCmd(DISABLE);
  }
#endif /* STM32L1XX_MD */
}

/*******************************************************************************
* Function Name  : Get_SerialNum.
* Description    : Create the serial number string descriptor.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void Get_SerialNum(void)
{
  uint32_t Device_Serial0, Device_Serial1, Device_Serial2;

  Device_Serial0 = *(uint32_t*)ID1;
  Device_Serial1 = *(uint32_t*)ID2;
  Device_Serial2 = *(uint32_t*)ID3;

  Device_Serial0 += Device_Serial2;

  if (Device_Serial0 != 0)
  {
    IntToUnicode (Device_Serial0, &MASS_StringSerial[2] , 8);
    IntToUnicode (Device_Serial1, &MASS_StringSerial[18], 4);
  }
}

/*******************************************************************************
* Function Name  : HexToChar.
* Description    : Convert Hex 32Bits value into char.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
static void IntToUnicode (uint32_t value , uint8_t *pbuf , uint8_t len)
{
  uint8_t idx = 0;
  
  for( idx = 0 ; idx < len ; idx ++)
  {
    if( ((value >> 28)) < 0xA )
    {
      pbuf[ 2* idx] = (value >> 28) + '0';
    }
    else
    {
      pbuf[2* idx] = (value >> 28) + 'A' - 10; 
    }
    
    value = value << 4;
    
    pbuf[ 2* idx + 1] = 0;
  }
}

/*******************************************************************************
* Function Name  : MAL_Config
* Description    : MAL_layer configuration
* Input          : None.
* Return         : None.
*******************************************************************************/
//中間層初始化函數
void MAL_Config(void)
{
  MAL_Init(0);
}
//void MAL_Config(void)
//{
//  MAL_Init(0);

//#if defined(STM32F10X_HD) || defined(STM32F10X_XL)
//  /* Enable the FSMC Clock */
//  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE);
//  MAL_Init(1);
//#endif /* STM32F10X_HD | STM32F10X_XL */
//}

#if !defined (USE_STM32L152_EVAL) 
/*******************************************************************************
* Function Name  : USB_Disconnect_Config
* Description    : Disconnect pin configuration
* Input          : None.
* Return         : None.
*******************************************************************************/
//沒有使用USB_Disconnect,所以刪除
//void USB_Disconnect_Config(void)
//{
//  GPIO_InitTypeDef GPIO_InitStructure;
//#if defined (USE_STM3210B_EVAL) || defined (USE_STM3210E_EVAL)
//  /* Enable USB_DISCONNECT GPIO clock */
//  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_DISCONNECT, ENABLE);

//  /* USB_DISCONNECT_PIN used as USB pull-up */
//  GPIO_InitStructure.GPIO_Pin = USB_DISCONNECT_PIN;
//  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
//#else
//	/* Enable the USB disconnect GPIO clock */
//  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIO_DISCONNECT, ENABLE);
//  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ALLGPIO, ENABLE);
//  
//  /* USB_DISCONNECT used as USB pull-up */
//  GPIO_InitStructure.GPIO_Pin = USB_DISCONNECT_PIN;
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
//  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
//  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
//  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

//#endif
//  GPIO_Init(USB_DISCONNECT, &GPIO_InitStructure);
//}
#endif /* USE_STM3210B_EVAL or USE_STM3210E_EVAL */


/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

5、修改“mass_mal.c”
庫中原文件是對SD進行操作,這裏將對應函數修改爲操作FLASH

/* Includes ------------------------------------------------------------------*/
#include "platform_config.h"
#include "mass_mal.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint32_t Mass_Memory_Size[2];
uint32_t Mass_Block_Size[2];
uint32_t Mass_Block_Count[2];
__IO uint32_t Status = 0;

#if defined(USE_STM3210E_EVAL) || defined(USE_STM32L152D_EVAL)
SD_CardInfo mSDCardInfo;
#endif

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/*******************************************************************************
* Function Name  : MAL_Init
* Description    : Initializes the Media on the STM32
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
//本文使用外部FLASH,所以改寫該函數
uint16_t MAL_Init(uint8_t lun)
{
  uint16_t status = MAL_OK;

  switch (lun)
  {
    case 0:
			SPI_FLASH_Init();
			if(SPI_FLASH_ReadID()==sFLASH_ID)
			{
				//printf("FLASH初始化成功\n");
				status = MAL_OK;
			}
			else
				status = MAL_FAIL;
      break;
    default:
      return MAL_FAIL;
	}
  return status;
}
//uint16_t MAL_Init(uint8_t lun)
//{
//  uint16_t status = MAL_OK;

//  switch (lun)
//  {
//    case 0:
//      Status = SD_Init();
//      break;
//#ifdef USE_STM3210E_EVAL
//    case 1:
//      NAND_Init();
//      break;
//#endif
//    default:
//      return MAL_FAIL;
//  }
//  return status;
//}

/*******************************************************************************
* Function Name  : MAL_Write
* Description    : Write sectors
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
//按SPI_FLASH的寫操作改寫函數
uint16_t MAL_Write(uint8_t lun, uint32_t Memory_Offset, uint32_t *Writebuff, uint16_t Transfer_Length)
{
	switch (lun)
  {
    case 0:
			SPI_FLASH_SectorErase(Memory_Offset);
			SPI_FLASH_BufferWrite((uint8_t *)Writebuff,Memory_Offset,Transfer_Length);
      break;
    default:
      return MAL_FAIL;
  }
	  return MAL_OK;
}
//uint16_t MAL_Write(uint8_t lun, uint32_t Memory_Offset, uint32_t *Writebuff, uint16_t Transfer_Length)
//{

//  switch (lun)
//  {
//    case 0:
//    Status = SD_WriteMultiBlocks((uint8_t*)Writebuff, Memory_Offset, Transfer_Length,1);
//#if defined(USE_STM3210E_EVAL) || defined(USE_STM32L152D_EVAL)
//    Status = SD_WaitWriteOperation();  
//    while(SD_GetStatus() != SD_TRANSFER_OK);
//      if ( Status != SD_OK )
//      {
//        return MAL_FAIL;
//      }      
//#endif /* USE_STM3210E_EVAL ||USE_STM32L152D_EVAL*/      
//      break;
//#ifdef USE_STM3210E_EVAL
//    case 1:
//      NAND_Write(Memory_Offset, Writebuff, Transfer_Length);
//      break;
//#endif /* USE_STM3210E_EVAL */  
//    default:
//      return MAL_FAIL;
//  }
//  return MAL_OK;
//}

/*******************************************************************************
* Function Name  : MAL_Read
* Description    : Read sectors
* Input          : None
* Output         : None
* Return         : Buffer pointer
*******************************************************************************/
//按SPI_FLASH的讀操作改寫函數
uint16_t MAL_Read(uint8_t lun, uint32_t Memory_Offset, uint32_t *Readbuff, uint16_t Transfer_Length)
{
	switch (lun)
  {
    case 0:
			SPI_FLASH_BufferRead((uint8_t *)Readbuff, Memory_Offset, Transfer_Length);
      break;
    default:
      return MAL_FAIL;
  }
	  return MAL_OK;
}
//uint16_t MAL_Read(uint8_t lun, uint32_t Memory_Offset, uint32_t *Readbuff, uint16_t Transfer_Length)
//{

//  switch (lun)
//  {
//    case 0:

//      SD_ReadMultiBlocks((uint8_t*)Readbuff, Memory_Offset, Transfer_Length, 1);
//#if defined(USE_STM3210E_EVAL) || defined(USE_STM32L152D_EVAL)
//      Status = SD_WaitReadOperation();
//      while(SD_GetStatus() != SD_TRANSFER_OK)
//      {
//      }
//      
//      if ( Status != SD_OK )
//      {
//        return MAL_FAIL;
//      }
//#endif /* USE_STM3210E_EVAL */      
//      break;
//#ifdef USE_STM3210E_EVAL
//    case 1:
//      NAND_Read(Memory_Offset, Readbuff, Transfer_Length);
//      ;
//      break;
//#endif
//    default:
//      return MAL_FAIL;
//  }
//  return MAL_OK;
//}

/*******************************************************************************
* Function Name  : MAL_GetStatus
* Description    : Get status
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
//獲取SPI_FLASH狀態
uint16_t MAL_GetStatus (uint8_t lun)
{
  if (lun == 0)
  {
    SPI_FLASH_Init();
		if(SPI_FLASH_ReadID()==sFLASH_ID)
    {
			//一個扇區爲4096,一個塊有8個扇區,W25Q128共有512個塊
			Mass_Block_Size[0]  = 4096;
			Mass_Block_Count[0] = 4096;
			Mass_Memory_Size[0] = Mass_Block_Size[0]*Mass_Block_Count[0];
      return MAL_OK;
    }
  }
	return MAL_FAIL;
}
//uint16_t MAL_GetStatus (uint8_t lun)
//{
//#ifdef USE_STM3210E_EVAL
//  NAND_IDTypeDef NAND_ID;
//  uint32_t DeviceSizeMul = 0, NumberOfBlocks = 0;
//#else
//#if !defined(USE_STM32L152D_EVAL)
//  SD_CSD SD_csdata;
//#endif
//  uint32_t DeviceSizeMul = 0;
//#endif /* USE_STM3210E_EVAL */

//#ifdef USE_STM32L152D_EVAL

//  uint32_t NumberOfBlocks = 0;
//#endif

//  if (lun == 0)
//  {
//#if defined (USE_STM3210E_EVAL)  || defined(USE_STM32L152D_EVAL)
//    if (SD_Init() == SD_OK)
//    {
//      SD_GetCardInfo(&mSDCardInfo);
//      SD_SelectDeselect((uint32_t) (mSDCardInfo.RCA << 16));
//      DeviceSizeMul = (mSDCardInfo.SD_csd.DeviceSizeMul + 2);

//      if(mSDCardInfo.CardType == SDIO_HIGH_CAPACITY_SD_CARD)
//      {
//        Mass_Block_Count[0] = (mSDCardInfo.SD_csd.DeviceSize + 1) * 1024;
//      }
//      else
//      {
//        NumberOfBlocks  = ((1 << (mSDCardInfo.SD_csd.RdBlockLen)) / 512);
//        Mass_Block_Count[0] = ((mSDCardInfo.SD_csd.DeviceSize + 1) * (1 << DeviceSizeMul) << (NumberOfBlocks/2));
//      }
//      Mass_Block_Size[0]  = 512;

//      Status = SD_SelectDeselect((uint32_t) (mSDCardInfo.RCA << 16)); 
//      Status = SD_EnableWideBusOperation(SDIO_BusWide_4b); 
//      if ( Status != SD_OK )
//      {
//        return MAL_FAIL;
//      }
//     
//#else

//    uint32_t temp_block_mul = 0;
//    SD_GetCSDRegister(&SD_csdata);
//    DeviceSizeMul = SD_csdata.DeviceSizeMul + 2;
//    temp_block_mul = (1 << SD_csdata.RdBlockLen)/ 512;
//    Mass_Block_Count[0] = ((SD_csdata.DeviceSize + 1) * (1 << (DeviceSizeMul))) * temp_block_mul;
//    Mass_Block_Size[0] = 512;
//    Mass_Memory_Size[0] = (Mass_Block_Count[0] * Mass_Block_Size[0]);
//#endif /* USE_STM3210E_EVAL */
//      Mass_Memory_Size[0] = Mass_Block_Count[0] * Mass_Block_Size[0];
//      STM_EVAL_LEDOn(LED2);
//      return MAL_OK;

//#if defined (USE_STM3210E_EVAL) || defined(USE_STM32L152D_EVAL)
//    }
//#endif /* USE_STM3210E_EVAL */
//  }
//#ifdef USE_STM3210E_EVAL
//  else
//  {
//    FSMC_NAND_ReadID(&NAND_ID);
//    if (NAND_ID.Device_ID != 0 )
//    {
//      /* only one zone is used */
//      Mass_Block_Count[1] = NAND_ZONE_SIZE * NAND_BLOCK_SIZE * NAND_MAX_ZONE ;
//      Mass_Block_Size[1]  = NAND_PAGE_SIZE;
//      Mass_Memory_Size[1] = (Mass_Block_Count[1] * Mass_Block_Size[1]);
//      return MAL_OK;
//    }
//  }
//#endif /* USE_STM3210E_EVAL */
//  STM_EVAL_LEDOn(LED2);
//  return MAL_FAIL;
//}

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

6、修改“memory.c”與“usb_prop.c”
由於在“hw_config.c”中刪除了與LED相關的函數,所以將這兩個文件中調用LED的函數刪除,同時修改memory.c中的數據緩衝區大小

/**
  ******************************************************************************
  * @file    memory.c
  * @author  MCD Application Team
  * @version V4.1.0
  * @date    26-May-2017
  * @brief   Memory management layer
  ******************************************************************************
*/

/* Includes ------------------------------------------------------------------*/

#include "memory.h"
#include "usb_scsi.h"
#include "usb_bot.h"
#include "usb_regs.h"
#include "usb_mem.h"
#include "usb_conf.h"
#include "hw_config.h"
#include "mass_mal.h"
#include "usb_lib.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
__IO uint32_t Block_Read_count = 0;
__IO uint32_t Block_offset;
__IO uint32_t Counter = 0;
uint32_t  Idx;
//修改數據緩衝區大小
uint32_t Data_Buffer[BULK_MAX_PACKET_SIZE * 64]; /* 4096 bytes*/
//uint32_t Data_Buffer[BULK_MAX_PACKET_SIZE * 2]; /* 512 bytes*/

uint8_t TransferState = TXFR_IDLE;
/* Extern variables ----------------------------------------------------------*/
extern uint8_t Bulk_Data_Buff[BULK_MAX_PACKET_SIZE];  /* data buffer*/
extern uint16_t Data_Len;
extern uint8_t Bot_State;
extern Bulk_Only_CBW CBW;
extern Bulk_Only_CSW CSW;
extern uint32_t Mass_Memory_Size[2];
extern uint32_t Mass_Block_Size[2];

/* Private function prototypes -----------------------------------------------*/
/* Extern function prototypes ------------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/*******************************************************************************
* Function Name  : Read_Memory
* Description    : Handle the Read operation from the microSD card.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void Read_Memory(uint8_t lun, uint32_t Memory_Offset, uint32_t Transfer_Length)
{
  static uint32_t Offset, Length;

  if (TransferState == TXFR_IDLE )
  {
    Offset = Memory_Offset * Mass_Block_Size[lun];
    Length = Transfer_Length * Mass_Block_Size[lun];
    TransferState = TXFR_ONGOING;
  }

  if (TransferState == TXFR_ONGOING )
  {
    if (!Block_Read_count)
    {
      MAL_Read(lun ,
               Offset ,
               Data_Buffer,
               Mass_Block_Size[lun]);

      USB_SIL_Write(EP1_IN, (uint8_t *)Data_Buffer, BULK_MAX_PACKET_SIZE);

      Block_Read_count = Mass_Block_Size[lun] - BULK_MAX_PACKET_SIZE;
      Block_offset = BULK_MAX_PACKET_SIZE;
    }
    else
    {
      USB_SIL_Write(EP1_IN, (uint8_t *)Data_Buffer + Block_offset, BULK_MAX_PACKET_SIZE);

      Block_Read_count -= BULK_MAX_PACKET_SIZE;
      Block_offset += BULK_MAX_PACKET_SIZE;
    }

    SetEPTxCount(ENDP1, BULK_MAX_PACKET_SIZE);
    SetEPTxStatus(ENDP1, EP_TX_VALID);  
    Offset += BULK_MAX_PACKET_SIZE;
    Length -= BULK_MAX_PACKET_SIZE;

    CSW.dDataResidue -= BULK_MAX_PACKET_SIZE;
//    Led_RW_ON();
  }
  if (Length == 0)
  {
    Block_Read_count = 0;
    Block_offset = 0;
    Offset = 0;
    Bot_State = BOT_DATA_IN_LAST;
    TransferState = TXFR_IDLE;
//    Led_RW_OFF();
  }
}

/*******************************************************************************
* Function Name  : Write_Memory
* Description    : Handle the Write operation to the microSD card.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void Write_Memory (uint8_t lun, uint32_t Memory_Offset, uint32_t Transfer_Length)
{

  static uint32_t W_Offset, W_Length;

  uint32_t temp =  Counter + 64;

  if (TransferState == TXFR_IDLE )
  {
    W_Offset = Memory_Offset * Mass_Block_Size[lun];
    W_Length = Transfer_Length * Mass_Block_Size[lun];
    TransferState = TXFR_ONGOING;
  }

  if (TransferState == TXFR_ONGOING )
  {

    for (Idx = 0 ; Counter < temp; Counter++)
    {
      *((uint8_t *)Data_Buffer + Counter) = Bulk_Data_Buff[Idx++];
    }

    W_Offset += Data_Len;
    W_Length -= Data_Len;

    if (!(W_Length % Mass_Block_Size[lun]))
    {
      Counter = 0;
      MAL_Write(lun ,
                W_Offset - Mass_Block_Size[lun],
                Data_Buffer,
                Mass_Block_Size[lun]);
    }

    CSW.dDataResidue -= Data_Len;
    SetEPRxStatus(ENDP2, EP_RX_VALID); /* enable the next transaction*/   
//    Led_RW_ON();
  }

  if ((W_Length == 0) || (Bot_State == BOT_CSW_Send))
  {
    Counter = 0;
    Set_CSW (CSW_CMD_PASSED, SEND_CSW_ENABLE);
    TransferState = TXFR_IDLE;
//    Led_RW_OFF();
  }
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/**
  ******************************************************************************
  * @file    usb_prop.c
  * @author  MCD Application Team
  * @version V4.1.0
  * @date    26-May-2017
  * @brief   All processing related to Mass Storage Demo
  ******************************************************************************
*/

/* Includes ------------------------------------------------------------------*/
#include "hw_config.h"
#include "usb_lib.h"
#include "usb_conf.h"
#include "usb_desc.h"
#include "usb_pwr.h"
#include "usb_bot.h"
#include "memory.h"
#include "mass_mal.h"
#include "usb_prop.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
#if defined (USE_STM3210E_EVAL)
uint32_t Max_Lun = 1;
#else
uint32_t Max_Lun = 0;
#endif

DEVICE Device_Table =
  {
    EP_NUM,
    1
  };

DEVICE_PROP Device_Property =
  {
    MASS_init,
    MASS_Reset,
    MASS_Status_In,
    MASS_Status_Out,
    MASS_Data_Setup,
    MASS_NoData_Setup,
    MASS_Get_Interface_Setting,
    MASS_GetDeviceDescriptor,
    MASS_GetConfigDescriptor,
    MASS_GetStringDescriptor,
    0,
    0x40 /*MAX PACKET SIZE*/
  };

USER_STANDARD_REQUESTS User_Standard_Requests =
  {
    Mass_Storage_GetConfiguration,
    Mass_Storage_SetConfiguration,
    Mass_Storage_GetInterface,
    Mass_Storage_SetInterface,
    Mass_Storage_GetStatus,
    Mass_Storage_ClearFeature,
    Mass_Storage_SetEndPointFeature,
    Mass_Storage_SetDeviceFeature,
    Mass_Storage_SetDeviceAddress
  };

ONE_DESCRIPTOR Device_Descriptor =
  {
    (uint8_t*)MASS_DeviceDescriptor,
    MASS_SIZ_DEVICE_DESC
  };

ONE_DESCRIPTOR Config_Descriptor =
  {
    (uint8_t*)MASS_ConfigDescriptor,
    MASS_SIZ_CONFIG_DESC
  };

ONE_DESCRIPTOR String_Descriptor[5] =
  {
    {(uint8_t*)MASS_StringLangID, MASS_SIZ_STRING_LANGID},
    {(uint8_t*)MASS_StringVendor, MASS_SIZ_STRING_VENDOR},
    {(uint8_t*)MASS_StringProduct, MASS_SIZ_STRING_PRODUCT},
    {(uint8_t*)MASS_StringSerial, MASS_SIZ_STRING_SERIAL},
    {(uint8_t*)MASS_StringInterface, MASS_SIZ_STRING_INTERFACE},
  };

/* Extern variables ----------------------------------------------------------*/
extern unsigned char Bot_State;
extern Bulk_Only_CBW CBW;

/* Private function prototypes -----------------------------------------------*/
/* Extern function prototypes ------------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/*******************************************************************************
* Function Name  : MASS_init
* Description    : Mass Storage init routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void MASS_init()
{
  /* Update the serial number string descriptor with the data from the unique
  ID*/
  Get_SerialNum();

  pInformation->Current_Configuration = 0;

  /* Connect the device */
  PowerOn();

  /* Perform basic device initialization operations */
  USB_SIL_Init();

  bDeviceState = UNCONNECTED;
}

/*******************************************************************************
* Function Name  : MASS_Reset
* Description    : Mass Storage reset routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void MASS_Reset()
{
  /* Set the device as not configured */
  Device_Info.Current_Configuration = 0;

  /* Current Feature initialization */
  pInformation->Current_Feature = MASS_ConfigDescriptor[7];

  SetBTABLE(BTABLE_ADDRESS);

  /* Initialize Endpoint 0 */
  SetEPType(ENDP0, EP_CONTROL);
  SetEPTxStatus(ENDP0, EP_TX_NAK);
  SetEPRxAddr(ENDP0, ENDP0_RXADDR);
  SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
  SetEPTxAddr(ENDP0, ENDP0_TXADDR);
  Clear_Status_Out(ENDP0);
  SetEPRxValid(ENDP0);

  /* Initialize Endpoint 1 */
  SetEPType(ENDP1, EP_BULK);
  SetEPTxAddr(ENDP1, ENDP1_TXADDR);
  SetEPTxStatus(ENDP1, EP_TX_NAK);
  SetEPRxStatus(ENDP1, EP_RX_DIS);

  /* Initialize Endpoint 2 */
  SetEPType(ENDP2, EP_BULK);
  SetEPRxAddr(ENDP2, ENDP2_RXADDR);
  SetEPRxCount(ENDP2, Device_Property.MaxPacketSize);
  SetEPRxStatus(ENDP2, EP_RX_VALID);
  SetEPTxStatus(ENDP2, EP_TX_DIS);


  SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
  SetEPRxValid(ENDP0);

  /* Set the device to response on default address */
  SetDeviceAddress(0);

  bDeviceState = ATTACHED;

  CBW.dSignature = BOT_CBW_SIGNATURE;
  Bot_State = BOT_IDLE;
// 註釋LED
//  USB_NotConfigured_LED();
}

/*******************************************************************************
* Function Name  : Mass_Storage_SetConfiguration
* Description    : Handle the SetConfiguration request.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void Mass_Storage_SetConfiguration(void)
{
  if (pInformation->Current_Configuration != 0)
  {
    /* Device configured */
    bDeviceState = CONFIGURED;
   
    ClearDTOG_TX(ENDP1);
    ClearDTOG_RX(ENDP2);

    Bot_State = BOT_IDLE; /* set the Bot state machine to the IDLE state */
  }
}

/*******************************************************************************
* Function Name  : Mass_Storage_ClearFeature
* Description    : Handle the ClearFeature request.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void Mass_Storage_ClearFeature(void)
{
  /* when the host send a CBW with invalid signature or invalid length the two
     Endpoints (IN & OUT) shall stall until receiving a Mass Storage Reset     */
  if (CBW.dSignature != BOT_CBW_SIGNATURE)
    Bot_Abort(BOTH_DIR);
}

/*******************************************************************************
* Function Name  : Mass_Storage_SetConfiguration.
* Description    : Update the device state to addressed.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void Mass_Storage_SetDeviceAddress (void)
{
  bDeviceState = ADDRESSED;
}
/*******************************************************************************
* Function Name  : MASS_Status_In
* Description    : Mass Storage Status IN routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void MASS_Status_In(void)
{
  return;
}

/*******************************************************************************
* Function Name  : MASS_Status_Out
* Description    : Mass Storage Status OUT routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void MASS_Status_Out(void)
{
  return;
}

/*******************************************************************************
* Function Name  : MASS_Data_Setup.
* Description    : Handle the data class specific requests..
* Input          : RequestNo.
* Output         : None.
* Return         : RESULT.
*******************************************************************************/
RESULT MASS_Data_Setup(uint8_t RequestNo)
{
  uint8_t    *(*CopyRoutine)(uint16_t);

  CopyRoutine = NULL;
  if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
      && (RequestNo == GET_MAX_LUN) && (pInformation->USBwValue == 0)
      && (pInformation->USBwIndex == 0) && (pInformation->USBwLength == 0x01))
  {
    CopyRoutine = Get_Max_Lun;
  }
  else
  {
    return USB_UNSUPPORT;
  }

  if (CopyRoutine == NULL)
  {
    return USB_UNSUPPORT;
  }

  pInformation->Ctrl_Info.CopyData = CopyRoutine;
  pInformation->Ctrl_Info.Usb_wOffset = 0;
  (*CopyRoutine)(0);

  return USB_SUCCESS;

}

/*******************************************************************************
* Function Name  : MASS_NoData_Setup.
* Description    : Handle the no data class specific requests.
* Input          : RequestNo.
* Output         : None.
* Return         : RESULT.
*******************************************************************************/
RESULT MASS_NoData_Setup(uint8_t RequestNo)
{
  if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
      && (RequestNo == MASS_STORAGE_RESET) && (pInformation->USBwValue == 0)
      && (pInformation->USBwIndex == 0) && (pInformation->USBwLength == 0x00))
  {
    /* Initialize Endpoint 1 */
    ClearDTOG_TX(ENDP1);

    /* Initialize Endpoint 2 */
    ClearDTOG_RX(ENDP2);

    /*initialize the CBW signature to enable the clear feature*/
    CBW.dSignature = BOT_CBW_SIGNATURE;
    Bot_State = BOT_IDLE;

    return USB_SUCCESS;
  }
  return USB_UNSUPPORT;
}

/*******************************************************************************
* Function Name  : MASS_Get_Interface_Setting
* Description    : Test the interface and the alternate setting according to the
*                  supported one.
* Input          : uint8_t Interface, uint8_t AlternateSetting.
* Output         : None.
* Return         : RESULT.
*******************************************************************************/
RESULT MASS_Get_Interface_Setting(uint8_t Interface, uint8_t AlternateSetting)
{
  if (AlternateSetting > 0)
  {
    return USB_UNSUPPORT;/* in this application we don't have AlternateSetting*/
  }
  else if (Interface > 0)
  {
    return USB_UNSUPPORT;/*in this application we have only 1 interfaces*/
  }
  return USB_SUCCESS;
}

/*******************************************************************************
* Function Name  : MASS_GetDeviceDescriptor
* Description    : Get the device descriptor.
* Input          : uint16_t Length.
* Output         : None.
* Return         : None.
*******************************************************************************/
uint8_t *MASS_GetDeviceDescriptor(uint16_t Length)
{
  return Standard_GetDescriptorData(Length, &Device_Descriptor );
}

/*******************************************************************************
* Function Name  : MASS_GetConfigDescriptor
* Description    : Get the configuration descriptor.
* Input          : uint16_t Length.
* Output         : None.
* Return         : None.
*******************************************************************************/
uint8_t *MASS_GetConfigDescriptor(uint16_t Length)
{
  return Standard_GetDescriptorData(Length, &Config_Descriptor );
}

/*******************************************************************************
* Function Name  : MASS_GetStringDescriptor
* Description    : Get the string descriptors according to the needed index.
* Input          : uint16_t Length.
* Output         : None.
* Return         : None.
*******************************************************************************/
uint8_t *MASS_GetStringDescriptor(uint16_t Length)
{
  uint8_t wValue0 = pInformation->USBwValue0;

  if (wValue0 >= 5)
  {
    return NULL;
  }
  else
  {
    return Standard_GetDescriptorData(Length, &String_Descriptor[wValue0]);
  }
}

/*******************************************************************************
* Function Name  : Get_Max_Lun
* Description    : Handle the Get Max Lun request.
* Input          : uint16_t Length.
* Output         : None.
* Return         : None.
*******************************************************************************/
uint8_t *Get_Max_Lun(uint16_t Length)
{
  if (Length == 0)
  {
    pInformation->Ctrl_Info.Usb_wLength = LUN_DATA_LENGTH;
    return 0;
  }
  else
  {
    return((uint8_t*)(&Max_Lun));
  }
}

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

7、在“stm32f10x_it.c”中添加USB中斷服務函數
將之前“stm32_it.c”中的中斷函數複製過來,並添加與USB相關的頭文件

/**
  ******************************************************************************
  * @file    Project/STM32F10x_StdPeriph_Template/stm32f10x_it.c 
  * @author  MCD Application Team
  * @version V3.5.0
  * @date    08-April-2011
  * @brief   Main Interrupt Service Routines.
  *          This file provides template for all exceptions handler and 
  *          peripherals interrupt service routine.
  ******************************************************************************
*/

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_it.h"

//與USB相關的頭文件
#include "hw_config.h"
#include "usb_lib.h"
#include "usb_istr.h"
#include "usb_pwr.h"

/** @addtogroup STM32F10x_StdPeriph_Template
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/******************************************************************************/
/*            Cortex-M3 Processor Exceptions Handlers                         */
/******************************************************************************/

/**
  * @brief  This function handles NMI exception.
  * @param  None
  * @retval None
  */
void NMI_Handler(void)
{
}

/**
  * @brief  This function handles Hard Fault exception.
  * @param  None
  * @retval None
  */
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Memory Manage exception.
  * @param  None
  * @retval None
  */
void MemManage_Handler(void)
{
  /* Go to infinite loop when Memory Manage exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Bus Fault exception.
  * @param  None
  * @retval None
  */
void BusFault_Handler(void)
{
  /* Go to infinite loop when Bus Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Usage Fault exception.
  * @param  None
  * @retval None
  */
void UsageFault_Handler(void)
{
  /* Go to infinite loop when Usage Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles SVCall exception.
  * @param  None
  * @retval None
  */
void SVC_Handler(void)
{
}

/**
  * @brief  This function handles Debug Monitor exception.
  * @param  None
  * @retval None
  */
void DebugMon_Handler(void)
{
}

/**
  * @brief  This function handles PendSVC exception.
  * @param  None
  * @retval None
  */
void PendSV_Handler(void)
{
}

/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
}

/******************************************************************************/
/*                 STM32F10x Peripherals Interrupt Handlers                   */
/*  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the  */
/*  available peripheral interrupt handler's name please refer to the startup */
/*  file (startup_stm32f10x_xx.s).                                            */
/******************************************************************************/

/**
  * @brief  This function handles PPP interrupt request.
  * @param  None
  * @retval None
  */
/*void PPP_IRQHandler(void)
{
}*/

/**
  * @}
  */ 


//與USB相關的中斷
/*******************************************************************************
* Function Name  : USB_HP_CAN1_TX_IRQHandler
* Description    : This function handles USB High Priority or CAN TX interrupts requests
*                  requests.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USB_HP_CAN1_TX_IRQHandler(void)
{
  CTR_HP();
}

/*******************************************************************************
* Function Name  : USB_IRQHandler
* Description    : This function handles USB Low Priority interrupts
*                  requests.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
#if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)|| defined(STM32L1XX_MD_PLUS) || defined(STM32F37X)
void USB_LP_IRQHandler(void)
#else
void USB_LP_CAN1_RX0_IRQHandler(void)
#endif
{
  USB_Istr();
}

/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

8、編寫main.c

/**
  ******************************************************************************
  * @file    main.c
  * @author  Asher
  * @version V1.0
  * @date    2020-xx-xx
  * @brief   華邦 16M串行flash測試“FATFS文件系統”和“模擬U盤”功能
  ******************************************************************************
  * @attention
  *
  * 說明:將數據寫入指定文件,如果文件不存在則創建後寫入
  *       如果文件存在則將文件內容從串口輸出。
  *       開啓USB模擬U盤功能。
  *
  ******************************************************************************
  */ 
#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./flash/bsp_spi_flash.h"
#include "ff.h"

#include "hw_config.h" 
#include "usb_lib.h"
#include "usb_pwr.h"

FATFS fs;													/* FatFs文件系統對象 */
FIL fnew;													/* 文件對象 */
FRESULT res_flash;                /* 文件操作結果 */
FILINFO fno;
UINT fnum;            					  /* 文件成功讀寫數量 */
BYTE ReadBuffer[1024]={0};        /* 讀緩衝區 */
BYTE WriteBuffer[] = "FatFs文件系統測試數據\r\n";  /* 寫緩衝區數據 */

BYTE work[FF_MAX_SS];

//初始化USB函數
void USB_MSC_Configuration(void)
{
	Set_System();
	Set_USBClock();
  USB_Interrupts_Config();
  USB_Init();
}

int main(void)
{	
	/* 初始化調試串口,一般爲串口 */
	USART_Config();	
		
  printf("****** FATFS文件系統+USB模擬U盤實驗 ******\r\n");
  
	//在外部SPI Flash掛載文件系統,文件系統掛載時會對SPI設備初始化
	//初始化函數調用流程如下
	//f_mount()->find_volume()->disk_initialize->SPI_FLASH_Init()
	res_flash = f_mount(&fs,"0:",1);
	
/*----------------------- 格式化測試 -----------------*/  
	/* 如果沒有文件系統就格式化創建創建文件系統 */
	if(res_flash == FR_NO_FILESYSTEM)
	{
		printf("》FLASH還沒有文件系統,即將進行格式化...\r\n");
    /* 格式化 */
		res_flash = f_mkfs("0:",0,work,sizeof work);			
		
		if(res_flash == FR_OK)
		{
			printf("》FLASH已成功格式化文件系統。\r\n");

      /* 格式化後,先取消掛載 */
			res_flash = f_mount(NULL,"0:",1);			
      /* 重新掛載	*/			
			res_flash = f_mount(&fs,"0:",1);
		}
		else
		{
			printf("《《格式化失敗。(%d)》》\r\n",res_flash);
			while(1);
		}
	}
  else if(res_flash!=FR_OK)
  {
    printf("!!外部Flash掛載文件系統失敗。(%d)\r\n",res_flash);
    printf("!!可能原因:SPI Flash初始化不成功。\r\n");
		while(1);
  }
  else
  {
    printf("》文件系統掛載成功,可以進行讀寫測試\r\n");
  }
	
  
/*------------------------- 程序功能測試 ---------------------*/
	printf("\r\n****** 即將進行功能測試... ******\r\n");
	res_flash=f_stat("0:測試文件.txt",&fno);	//判斷文件是否存在
	if(res_flash == FR_OK)
	{
		printf("\r\n測試文件.txt已存在,執行讀操作!\r\n");
		res_flash = f_open(&fnew, "0:測試文件.txt",FA_READ );
		if(res_flash == FR_OK)
		{
			printf("》打開文件成功。\r\n");
			res_flash = f_read(&fnew, ReadBuffer, sizeof(ReadBuffer), &fnum); 
			if(res_flash==FR_OK)
			{
				printf("》文件讀取成功,讀到字節數據:%d\r\n",fnum);
				printf("》讀取得的文件數據爲:\r\n%s \r\n", ReadBuffer);	
			}
			else
			{
				printf("!!文件讀取失敗:(%d)\n",res_flash);
			}		
		}
		else
		{
			printf("!!打開文件失敗。\r\n");
		}
	}
	else
	{
		printf("\r\n測試文件.txt不存在,創建文件並執行寫操作!\r\n");
		res_flash = f_open(&fnew, "0:測試文件.txt",FA_CREATE_NEW | FA_WRITE );
		if ( res_flash == FR_OK )
		{
			printf("》創建測試文件.txt文件成功,向文件寫入數據。\r\n");
			/* 將指定存儲區內容寫入到文件內 */
			res_flash=f_write(&fnew,WriteBuffer,sizeof(WriteBuffer),&fnum);
			if(res_flash==FR_OK)
			{
				printf("》文件寫入成功,寫入字節數據:%d\n",fnum);
				printf("》向文件寫入的數據爲:\r\n%s\r\n",WriteBuffer);
			}
			else
			{
				printf("!!文件寫入失敗:(%d)\n",res_flash);
			}    
			/* 不再讀寫,關閉文件 */
			f_close(&fnew);
		}
		else
		{	
			printf("!!打開/創建文件失敗。\r\n");
		}
	}
	
	//USB模擬U盤初始化
	USB_MSC_Configuration();
	while (bDeviceState != CONFIGURED);	 //等待配置完成
	printf("\r\n 外部FLASH模擬U盤功能已可用! \r\n");
	
  /* 操作完成,停機 */
	while(1)
	{
	}
}

/*********************************************END OF FILE**********************/

四、移植測試結果
將USB線接到電腦上,顯示“U盤”驅動器即表示移植成功,可以操作U盤的方式進行文件創建和更改,就是速度感人。
在這裏插入圖片描述

END

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章