细说STM32单片机USBD_MSC_SPI Flash虚拟U盘接口项目创建及编程方法
在USBD中间件众多的IP类中,可以把MCU中片外的SPI FLASH创建成虚拟的U盘,并在电脑端自动识别、驱动U盘,然后通过接口操作U盘。
本文旨在演示如何把MCU片外的SPI FLASH创建成一个虚拟U盘,然后通过U盘接口创建文件、读写文件。
继续使用旺宝红龙开发板STM32F407ZGT6 KIT V1.0,使用STM32CubeIDE 1.19.0。

为了方便阅读,减少公共部分的描述,阅读本文需要参考本文作者写的其他文章,参考文章:
细说STM32单片机USBD_MSC_FlashInChip虚拟U盘接口项目创建及编程方法-CSDN博客 https://wenchm.blog.csdn.net/article/details/153679144?spm=1011.2415.3001.5331
一、项目配置
1、RCC、SYS、CodeGenerator、NVIC、USB_OTG_FS、USART6
与参考文章相同。
2、SPI2


3、中间件USB_DEVICE


4、GPIO

5、LinkSetting

二、软件设计
配置完毕后,选择自动生成生成。
需要手动重写USBD_MSC的接口文件usbd_storage_if.c和USB的底层驱动文件usb_device.c。这两个文件都在USB_DEVICE\App下。
1、usbd_storage_if.c
/**
******************************************************************************
* @file : usbd_storage_if.c
* @version : v1.0_Cube
* @brief : Memory management layer.
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "usbd_storage_if.h"
/** @defgroup USBD_STORAGE_Private_Defines
* @brief Private defines.
* @{
*/
#define STORAGE_LUN_NBR 1
#define STORAGE_BLK_NBR 0x10000
#define STORAGE_BLK_SIZ 0x200
/* USER CODE BEGIN PRIVATE_DEFINES */
/* W25Q16,16Mbit,2Mbytes,Total flash size used to USBD MSC
* 256bytes/page
* 4KB/sector*16sector=64KB/BLOCK
* 64KB/BLOCK*32BLOCK=2048KB=2Mbytes
*/
#ifdef STORAGE_BLK_NBR
#undef STORAGE_BLK_NBR
#define STORAGE_BLK_NBR 0x200 //16*32=512 sectors
#endif
#ifdef STORAGE_BLK_SIZ
#undef STORAGE_BLK_SIZ
#define STORAGE_BLK_SIZ 0x1000 //4096
#endif
/* USER CODE END PRIVATE_DEFINES */
/**
* @}
*/
/* USER CODE BEGIN INQUIRY_DATA_FS */
/** USB Mass storage Standard Inquiry Data. */
const int8_t STORAGE_Inquirydata_FS[] = {/* 36 */
/* LUN 0 */
0x00,
0x80,
0x02,
0x02,
(STANDARD_INQUIRY_DATA_LEN - 5),
0x00,
0x00,
0x00,
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer : 8 bytes */
'P', 'r', 'o', 'd', 'u', 'c', 't', ' ', /* Product : 16 Bytes */
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
'0', '.', '0' ,'1' /* Version : 4 Bytes */
};
/** @defgroup USBD_STORAGE_Exported_Variables
* @brief Public variables.
* @{
*/
extern USBD_HandleTypeDef hUsbDeviceFS;
/**
* @}
*/
/** @defgroup USBD_STORAGE_Private_FunctionPrototypes
* @brief Private functions declaration.
* @{
*/
static int8_t STORAGE_Init_FS(uint8_t lun);
static int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size);
static int8_t STORAGE_IsReady_FS(uint8_t lun);
static int8_t STORAGE_IsWriteProtected_FS(uint8_t lun);
static int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
static int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
static int8_t STORAGE_GetMaxLun_FS(void);
/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
extern uint16_t W25Qxx_ReadID(void);
extern uint8_t W25Qxx_ReadSR(uint8_t reg);
extern int W25Qxx_Read(uint8_t* buffer, uint32_t start_addr, uint16_t nbytes);
extern void W25Qxx_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */
/**
* @}
*/
USBD_StorageTypeDef USBD_Storage_Interface_fops_FS =
{
STORAGE_Init_FS,
STORAGE_GetCapacity_FS,
STORAGE_IsReady_FS,
STORAGE_IsWriteProtected_FS,
STORAGE_Read_FS,
STORAGE_Write_FS,
STORAGE_GetMaxLun_FS,
(int8_t *)STORAGE_Inquirydata_FS
};
/* Private functions ---------------------------------------------------------*/
/**
* @brief Initializes the storage unit (medium) over USB FS IP
* @param lun: Logical unit number.
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_Init_FS(uint8_t lun)
{
/* USER CODE BEGIN 2 */
// UNUSED(lun);
if(W25Qxx_ReadID())
{
return (USBD_OK);
}
return (USBD_FAIL);
/* USER CODE END 2 */
}
/**
* @brief Returns the medium capacity.
* @param lun: Logical unit number.
* @param block_num: Number of total block number.
* @param block_size: Block size.
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
/* USER CODE BEGIN 3 */
// UNUSED(lun);
*block_num = STORAGE_BLK_NBR;
*block_size = STORAGE_BLK_SIZ;
return (USBD_OK);
/* USER CODE END 3 */
}
/**
* @brief Checks whether the medium is ready.
* @param lun: Logical unit number.
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_IsReady_FS(uint8_t lun)
{
/* USER CODE BEGIN 4 */
// UNUSED(lun);
return W25Qxx_ReadSR(1);
// return (USBD_OK);
/* USER CODE END 4 */
}
/**
* @brief Checks whether the medium is write protected.
* @param lun: Logical unit number.
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)
{
/* USER CODE BEGIN 5 */
// UNUSED(lun);
return (USBD_OK);
/* USER CODE END 5 */
}
/**
* @brief Reads data from the medium.
* @param lun: Logical unit number.
* @param buf: data buffer.
* @param blk_addr: Logical block address.
* @param blk_len: Blocks number.
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 6 */
// UNUSED(lun);
// UNUSED(buf);
// UNUSED(blk_addr);
// UNUSED(blk_len);
W25Qxx_Read(buf, blk_addr * STORAGE_BLK_SIZ, blk_len * STORAGE_BLK_SIZ);
return (USBD_OK);
/* USER CODE END 6 */
}
/**
* @brief Writes data into the medium.
* @param lun: Logical unit number.
* @param buf: data buffer.
* @param blk_addr: Logical block address.
* @param blk_len: Blocks number.
* @retval USBD_OK if all operations are OK else USBD_FAIL
*/
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 7 */
// UNUSED(lun);
// UNUSED(buf);
// UNUSED(blk_addr);
// UNUSED(blk_len);
W25Qxx_Write(buf, blk_addr * STORAGE_BLK_SIZ, blk_len * STORAGE_BLK_SIZ);
return (USBD_OK);
/* USER CODE END 7 */
}
/**
* @brief Returns the Max Supported LUNs.
* @param None
* @retval Lun(s) number.
*/
int8_t STORAGE_GetMaxLun_FS(void)
{
/* USER CODE BEGIN 8 */
return (STORAGE_LUN_NBR - 1);
/* USER CODE END 8 */
}
开发板上的FLASH规格为W25Q16,2M字节,接口程序把2M字节全部规划为USBD设备。
2、usb_device.c
/**
******************************************************************************
* @file : usb_device.c
* @version : v1.0_Cube
* @brief : This file implements the USB Device
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "usb_device.h"
#include "usbd_core.h"
#include "usbd_desc.h"
#include "usbd_msc.h"
#include "usbd_storage_if.h"
/* USB Device Core handle declaration. */
USBD_HandleTypeDef hUsbDeviceFS;
/**
* Init USB device Library, add supported class and start the library
* @retval None
*/
void MX_USB_DEVICE_Init(void)
{
/* Init Device Library, add supported class and start the library. */
if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK)
{
Error_Handler();
}
if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_MSC) != USBD_OK)
{
Error_Handler();
}
if (USBD_MSC_RegisterStorage(&hUsbDeviceFS, &USBD_Storage_Interface_fops_FS) != USBD_OK)
{
Error_Handler();
}
if (USBD_Start(&hUsbDeviceFS) != USBD_OK)
{
Error_Handler();
}
}
此处,不需要重写。当启用FATFS的时候,就需要重写此函数了。
3、main.c
还要声明和定义一些函数,用于查询Flash、读写Flash的函数。这些函数可以单独声明和定义,也可以定义在main,c里。
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "usart.h"
#include "usb_device.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
// flash specification
#define W25Q80 0XEF13
#define W25Q16 0XEF14
#define W25Q32 0XEF15
#define W25Qxx 0XEF16
#define W25Q128 0XEF17
#define W25Q256 0XEF18
// instruction set, comes from DATASHEET.
// not each specification have such below instruction.
#define W25Qxx_WriteEnable 0x06
#define W25Qxx_WriteDisable 0x04
#define W25Qxx_ReadStatusReg1 0x05
#define W25Qxx_ReadStatusReg2 0x35
#define W25Qxx_ReadStatusReg3 0x15
#define W25Qxx_WriteStatusReg1 0x01
#define W25Qxx_WriteStatusReg2 0x31
#define W25Qxx_WriteStatusReg3 0x11
#define W25Qxx_ReadData 0x03
#define W25Qxx_FastReadData 0x0B
#define W25Qxx_FastReadDual 0x3B
#define W25Qxx_PageProgram 0x02
#define W25Qxx_BlockErase 0xD8
#define W25Qxx_SectorErase 0x20
#define W25Qxx_ChipErase 0xC7
#define W25Qxx_PowerDown 0xB9
#define W25Qxx_ReleasePowerDown 0xAB
#define W25Qxx_DeviceID 0xAB
#define W25Qxx_ManufactDeviceID 0x90
#define W25Qxx_JedecDeviceID 0x9F
#define W25Qxx_Enable4ByteAddr 0xB7
#define W25Qxx_Exit4ByteAddr 0xE9
/* USER CODE END PD */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
void W25Qxx_Init(void);
uint16_t W25Qxx_ReadID(void); //read FLASH_ID
uint8_t W25Qxx_ReadSR(uint8_t regno); //read status Register
void W25Qxx_Write_Enable(void); //write enable
void W25Qxx_Write_Disable(void); //write protect
void W25Qxx_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
void W25Qxx_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead); //read flash
void W25Qxx_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//write flash
void W25Qxx_Erase_Sector(uint32_t Dst_Addr); //sector erase
void W25Qxx_Wait_Busy(void); //wait for Idle
uint8_t SPI_ReadWriteByte(uint8_t TxData);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/**
* @brief read W25Qxx ID
* @param void
* @retval uint16_t Temp:
*/
uint16_t W25Qxx_TYPE; //define W25Qxx type
uint16_t W25Qxx_ReadID(void)
{
uint16_t Temp = 0;
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_RESET);// enable CS,Low level active.
// SPI_ReadWriteByte(0x90); //sent read ID cmd
SPI_ReadWriteByte(W25Qxx_ManufactDeviceID); //BYTE1=90h,instruction code
SPI_ReadWriteByte(0x00); //BYTE2
SPI_ReadWriteByte(0x00); //BYTE3
SPI_ReadWriteByte(0x00); //BYTE4,return 00h
Temp|=SPI_ReadWriteByte(0xFF)<<8; //BYTE5,return efh,high 8bit
Temp|=SPI_ReadWriteByte(0xFF); //BYTE6,return 14h
W25Qxx_TYPE=Temp;
printf("FLASH SPECIFICATION IS :%x\r\n",W25Qxx_TYPE); //test
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_SET); // disable CS
return Temp;
}
/**
* @brief W25Qxx Read SR
* SR1:
* BIT7 6 5 4 3 2 1 0
* SPR RV TB BP2 BP1 BP0 WEL BUSY
* SPR:default 0,SR protection bit, to be used with WP
* TB,BP2,BP1,BP0:FLASH region write protection settings
* WEL: write enable lock
* BUSY:busy flag(1,busy;0,idle)
* default:0x00
* SR2:
* BIT7 6 5 4 3 2 1 0
* SUS CMP LB3 LB2 LB1 (R) QE SRP1
* SR3:
* BIT7 6 5 4 3 2 1 0
* HOLD/RST DRV1 DRV0 (R) (R) WPS ADP ADS
*
* @param regno:SR1~3
* @retval SR value
*/
uint8_t W25Qxx_ReadSR(uint8_t regno)
{
uint8_t byte=0,command=0;
switch(regno)
{
case 1:
command=W25Qxx_ReadStatusReg1; // read SR1,0x05
break;
case 2:
command=W25Qxx_ReadStatusReg2; // read SR2
break;
case 3:
command=W25Qxx_ReadStatusReg3; // read SR3
break;
default:
command=W25Qxx_ReadStatusReg1;
break;
}
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_RESET);// enable CS
SPI_ReadWriteByte(command); // BYTE1=05h,sent read SR CMD
byte=SPI_ReadWriteByte(0Xff); // BYTE2,return SR1
printf("FLASH SR1 :%d\r\n",byte); // test
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_SET); // disable CS
return byte;
}
/**
* @brief W25Qxx write enable
* set WEL bit
* @param void
* @retval void
*/
void W25Qxx_Write_Enable(void)
{
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_RESET); //enable CS
SPI_ReadWriteByte(W25Qxx_WriteEnable); //sent write enable CMD
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_SET); //disable CS
}
/**
* @brief W25Qxx write disable
* reset WEL bit
* @param void
* @retval void
*/
void W25Qxx_Write_Disable(void)
{
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_RESET); // disable CS
SPI_ReadWriteByte(W25Qxx_WriteDisable); // sent write disable CMD
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_SET); // enable CS
}
/**
* @brief read SPI FLASH
* Read data of specified length from the specified address.
* @param pBuffer:Data storage area
* ReadAddr:Starting address(24bit)
* NumByteToRead:Number of bytes to be read(max 65535)
* @retval void
*/
void W25Qxx_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)
{
uint16_t i;
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_RESET); // enable CS
SPI_ReadWriteByte(W25Qxx_ReadData); // sent read CMD
if(W25Qxx_TYPE==W25Q256) // if W25Q256 addr is 4 bytes,sent up to 8 bit.
{
SPI_ReadWriteByte((uint8_t)((ReadAddr)>>24));
}
SPI_ReadWriteByte((uint8_t)((ReadAddr)>>16)); // sent 24bit addr
SPI_ReadWriteByte((uint8_t)((ReadAddr)>>8));
SPI_ReadWriteByte((uint8_t)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
pBuffer[i]=SPI_ReadWriteByte(0XFF); // Circular reading
}
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_SET); // disable CS
}
/**
* @brief W25Qxx write one page
* SPI writes less than 256 bytes of data within one page (0~65535)
* Write up to 256 bytes of data starting at the specified address
* @param pBuffer:Data storage area
* WriteAddr:Starting address(24bit)
* NumByteToWrite:The bytes to be written(max 256),The size should not exceed the remaining bytes of the page.
* @retval void
*/
void W25Qxx_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint16_t i;
W25Qxx_Write_Enable(); // SET WEL
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_RESET); // enable CS
SPI_ReadWriteByte(W25Qxx_PageProgram); // sent write page CMD
if(W25Qxx_TYPE==W25Q256) // If W25Q256, the address is 4 bytes, and the highest 8 bits need to be sent.
{
SPI_ReadWriteByte((uint8_t)((WriteAddr)>>24));
}
SPI_ReadWriteByte((uint8_t)((WriteAddr)>>16)); // sent 24bit addr
SPI_ReadWriteByte((uint8_t)((WriteAddr)>>8));
SPI_ReadWriteByte((uint8_t)WriteAddr);
for(i=0;i<NumByteToWrite;i++)SPI_ReadWriteByte(pBuffer[i]); // Circular writing
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_SET); // disable CS
W25Qxx_Wait_Busy(); // Waiting for write completion
}
/**
* @brief write SPI FLASH with no check
* The data in the address range to be written must be all 0XFF,
* otherwise the data written at non-0XFF will fail.
* with the function to turn page automatically.
* Start writing the specified length of data at the specified address.
* @param pBuffer:data buffer
* WriteAddr:start write add(24bit)
* NumByteToWrite:bytes to be written (max 65535)
* @retval void
*/
void W25Qxx_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint16_t pageremain;
pageremain=256-WriteAddr%256; // The bytes remaining on a single page
if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite; // No more than 256 bytes
while(1)
{
W25Qxx_Write_Page(pBuffer,WriteAddr,pageremain);
if(NumByteToWrite==pageremain)break; // Finished writing.
else //NumByteToWrite>page remain
{
pBuffer+=pageremain;
WriteAddr+=pageremain;
NumByteToWrite-=pageremain; // Subtract the bytes that have already been written
if(NumByteToWrite>256)pageremain=256; // 256 bytes can be written at a time
else pageremain=NumByteToWrite; // Not enough for 256 bytes
}
};
}
/**
* @brief write SPI FLASH
* Start writing the specified length of data at the specified address,
* include the function with erase operation.
* @param pBuffer:data buffer
* WriteAddr:start write add(24bit)
* NumByteToWrite:bytes to be written (max 65535)
* @retval void
*/
uint8_t W25Qxx_BUFFER[4096];
void W25Qxx_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{
uint32_t secpos;
uint16_t secoff;
uint16_t secremain;
uint16_t i;
uint8_t* W25Qxx_BUF;
W25Qxx_BUF=W25Qxx_BUFFER;
secpos=WriteAddr/4096; // sector address
secoff=WriteAddr%4096; // offset within the sector
secremain=4096-secoff; // sector remaining space
// printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite); // test
if(NumByteToWrite<=secremain)secremain=NumByteToWrite; // No more than 4096 bytes
while(1)
{
W25Qxx_Read(W25Qxx_BUF,secpos*4096,4096); // Read out the content of the entire sector;
for(i=0;i<secremain;i++) // Check data
{
if(W25Qxx_BUF[secoff+i]!=0XFF)break; // need to be wiped out
}
if(i<secremain) // need to be erase
{
W25Qxx_Erase_Sector(secpos); // erase this sector
for(i=0;i<secremain;i++) // copy
{
W25Qxx_BUF[i+secoff]=pBuffer[i];
}
W25Qxx_Write_NoCheck(W25Qxx_BUF,secpos*4096,4096); // write cover sector
}else W25Qxx_Write_NoCheck(pBuffer,WriteAddr,secremain);// Write the already erased, write directly into the remaining interval of the sector.
if(NumByteToWrite==secremain)break; // Written up
else // Unfinished;
{
secpos++; // Sector address increment 1
secoff=0; // The offset is 0
pBuffer+=secremain; // pointer offset
WriteAddr+=secremain; // Write address offset
NumByteToWrite-=secremain; // Decreasing number of bytes
if(NumByteToWrite>4096)secremain=4096; // The next sector is still not finished;
else secremain=NumByteToWrite; // The next sector can be written now.
}
};
}
/**
* @brief Erase a sector
* @param Dst_Addr:Sector address, set according to actual capacity.
* Minimum time to erase a sector is 150ms.
* @retval void
*/
void W25Qxx_Erase_Sector(uint32_t Dst_Addr)
{
// printf("fe:%x\r\n",Dst_Addr); // monitor flash erasing, used for test
Dst_Addr*=4096;
W25Qxx_Write_Enable(); // SET WEL
W25Qxx_Wait_Busy();
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_RESET);
SPI_ReadWriteByte(W25Qxx_SectorErase); // Send erase sector CMD
if(W25Qxx_TYPE==W25Q256) // If W25Q256, the address is 4 bytes, the highest 8 bits need to be sent.
{
SPI_ReadWriteByte((uint8_t)((Dst_Addr)>>24));
}
SPI_ReadWriteByte((uint8_t)((Dst_Addr)>>16)); // sent 24bit addr
SPI_ReadWriteByte((uint8_t)((Dst_Addr)>>8));
SPI_ReadWriteByte((uint8_t)Dst_Addr);
HAL_GPIO_WritePin(W25Qxx_CHIP_SELECT_GPIO_Port, W25Qxx_CHIP_SELECT_Pin, GPIO_PIN_SET);
W25Qxx_Wait_Busy(); // Waiting for erase completion
}
/**
* @brief Wait for idle
* @param void
* @retval void
*/
void W25Qxx_Wait_Busy(void)
{
while((W25Qxx_ReadSR(1)&0x01)==0x01); // Wait for BUSY bit to clear
}
/**
* @brief write CMD into flash and then return a value,in block mode
* either a byte is written or a byte is returned.
* @param TxData: bytes written
* @retval Rxdata: bytes to be return
*/
uint8_t SPI_ReadWriteByte(uint8_t TxData)
{
uint8_t Rxdata;
HAL_SPI_TransmitReceive(&hspi2,&TxData,&Rxdata,1, 1000);
return Rxdata; // Return the received data
}
/* USER CODE END 0 */
// 省略此后IDE自动生成成的代码
/* USER CODE BEGIN 4 */
int __io_putchar(int ch)
{
HAL_UART_Transmit(&huart6,(uint8_t*)&ch,1,0xFFFF);
return ch;
}
/* USER CODE END 4 */
FLASH读写函数中调用的HAL_SPI_TransmiteRecieve()采用阻塞模式。即并不弃用SPI的DMA模式。
这些自定义的函数,是全局的,可以被其他函数调用,进行Flash诸多操作,这一点本文并不多言。
三、下载与运行
程序编译下载后,自动生成一个U盘设备,电脑能自动识别和驱动,2M的Flash系统自动格式化为1.68M的USB设备,



完整的代码托管于GitHub:
GitHub - wenchm/Demo15_8_USBD_MSC_SPIFlash: creat a USBD MSC use SPI flash https://github.com/wenchm/Demo15_8_USBD_MSC_SPIFlash
更多推荐



所有评论(0)