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文件系統html

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

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

  • STM32_USB-FS_Device_Driver設備驅動層:該層負責管理與USB-FS_Device外設和USB標準協議的直接通訊。 STM32_USBFS_Device_Driver符合USB 2.0規範,而且與標準STM32標準外設庫分離。
  • 應用接口層:該層爲用戶提供了固件庫和最終應用程序之間的完整接口。
    下表給出了庫文件的說明:
    在這裏插入圖片描述
    3、移植步驟
    一、刪除沒必要要的文件
    清理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
    在這裏插入圖片描述
    二、將整理好的庫文件添加到工程
    在這裏插入圖片描述
    因爲官方庫的程序是針對官方開發板編寫的,咱們從中拷貝的代碼在編譯後存在一些錯誤,下面對此進行修改。

三、修改「platform_config.h」
platform_config.h中定義了官方開發板的型號,因此刪除相關的宏定義並添加一些頭文件electron

/* 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****/

四、修改「hw_config.c」
修改USB中斷、時鐘等相關函數,刪除與LED相關的函數ide

/* 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****/

五、修改「mass_mal.c」
庫中原文件是對SD進行操做,這裏將對應函數修改成操做FLASHsvg

/* 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****/

六、修改「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****/

七、在「stm32f10x_it.c」中添加USB中斷服務函數
將以前「stm32_it.c」中的中斷函數複製過來,並添加與USB相關的頭文件oop

/** ****************************************************************************** * @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****/

八、編寫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**********************/

4、移植測試結果
將USB線接到電腦上,顯示「U盤」驅動器即表示移植成功,能夠操做U盤的方式進行文件建立和更改,就是速度感人。
在這裏插入圖片描述ui

END