在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

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐