硬件
单片机:STM32F405RGT6
外部Flash芯片:W25Q128
引脚说明
设置好RCC和SYS后接下来配置:
SPI设置
CS引脚配置
USB设置
USB驱动设置
这里是时钟配置
设置堆栈大小
然后生成代码
外部FLASH驱动文件
新建一个 W25qxx.h 头文件
// W25qxx.h
#ifndef __SPI_FLASH_H
#define __SPI_FLASH_H
#include "stm32f4xx.h"
#include "stm32f4xx_hal_spi.h"
#include "stm32f4xx_hal.h"
/* ----------------------------------------------------------------*/
#define SPI_FLASH_REBUILD 0 //
#define SPI_FLASH_SECTOR_SIZE 4096 //
#define SPI_FLASH_START_SECTOR 256 * 4 //
#define SPI_FLASH_SECTOR_COUNT 256 //
// 使用SPI3
#define FLASH_SPI_Handle hspi3
// W25Qxx系列芯片的ID
// W25Q80 ID 0xEF13
// W25Q16 ID 0xEF14
// W25Q32 ID 0xEF15
// W25Q64 ID 0xEF16
// W25Q128 ID 0xEF17
// W25Q256 ID 0xEF18
// #define W25Q80 0xEF13
#define W25Q16 0xEF14
#define W25Q32 0xEF15
#define W25Q64 0xEF16
#define W25Q128 0xEF17
// #define W25Q256 0xEF18
#define FLASH_ID W25Q128
// SPI字段
extern SPI_HandleTypeDef FLASH_SPI_Handle;
extern uint16_t W25qxx_TYPE;
// 设置CS引脚 高低电平 宏定义
#define W25QXX_CS_1 HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET)
#define W25QXX_CS_0 HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET)
/* ----------------------------------------------------------------*/
//
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg1 0x05
#define W25X_ReadStatusReg2 0x35
#define W25X_ReadStatusReg3 0x15
#define W25X_WriteStatusReg1 0x01
#define W25X_WriteStatusReg2 0x31
#define W25X_WriteStatusReg3 0x11
#define W25X_ReadData 0x03
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#define W25X_BlockErase 0xD8
#define W25X_SectorErase 0x20
#define W25X_ChipErase 0xC7
#define W25X_PowerDown 0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID 0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID 0x9F
#define W25X_Enable4ByteAddr 0xB7
#define W25X_Exit4ByteAddr 0xE9
void W25qxx_Init(void);
uint16_t W25qxx_ReadID(void);
void W25qxx_WAKEUP(void);
void SPI_FLASH_SectorErase(uint32_t SectorAddr);
void SPI_FLASH_BufferWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void SPI_FLASH_BufferRead(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);
void SPI_FLASH_PageWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
#endif /* __SPI_FLASH_H */
新建一个 W25qxx.c 文件
#include "./W25qxx/W25qxx.h"
#include "stm32f4xx_hal_gpio.h"
#include "stdio.h"
// #include "board.h"
uint16_t W25qxx_TYPE = FLASH_ID;
extern SPI_HandleTypeDef FLASH_SPI_Handle;
uint8_t W25qxx_ReadSR(uint8_t regno); //
void W25qxx_4ByteAddr_Enable(void); //
void W25qxx_Write_SR(uint8_t regno, uint8_t sr); //
void W25qxx_Write_Enable(void); //
void W25qxx_Write_Disable(void); //
void W25qxx_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void W25qxx_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead); //
void W25qxx_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite); //
void W25qxx_Erase_Chip(void); //
void W25qxx_Erase_Sector(uint32_t Dst_Addr); //
void W25qxx_Wait_Busy(void); //
void W25qxx_PowerDown(void); //
// SPI 读写字节
uint8_t SPI_ReadWriteByte(uint8_t TxData)
{
uint8_t Rxdata;
HAL_SPI_TransmitReceive(&FLASH_SPI_Handle, &TxData, &Rxdata, 1, 1000);
return Rxdata;
}
// SPI设置速度
void SPI2_SetSpeed(uint8_t SPI_BaudRatePrescaler)
{
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
__HAL_SPI_DISABLE(&FLASH_SPI_Handle);
FLASH_SPI_Handle.Instance->CR1 &= 0xFFC7;
FLASH_SPI_Handle.Instance->CR1 |= SPI_BaudRatePrescaler;
__HAL_SPI_ENABLE(&FLASH_SPI_Handle);
}
// 初始化引脚
void W25qxx_Init(void)
{
uint8_t temp;
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOD_CLK_ENABLE(); //
// PD2
GPIO_Initure.Pin = GPIO_PIN_2; // PD2
GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP; // 设置推挽
GPIO_Initure.Pull = GPIO_PULLUP; // 设置上拉
GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH; // 设置高速
W25QXX_CS_1; // 默认高电平
SPI2_SetSpeed(SPI_BAUDRATEPRESCALER_2); //
W25qxx_TYPE = W25qxx_ReadID(); // 读取FLASH芯片的ID
if (W25qxx_TYPE == FLASH_ID) //
{
temp = W25qxx_ReadSR(3);
if ((temp & 0x01) == 0)
{
W25QXX_CS_0;
SPI_ReadWriteByte(W25X_Enable4ByteAddr);
W25QXX_CS_1;
}
}
}
//
uint8_t W25qxx_ReadSR(uint8_t regno)
{
uint8_t byte = 0, command = 0;
switch (regno)
{
case 1:
command = W25X_ReadStatusReg1;
break;
case 2:
command = W25X_ReadStatusReg2;
break;
case 3:
command = W25X_ReadStatusReg3;
break;
default:
command = W25X_ReadStatusReg1;
break;
}
W25QXX_CS_0; //
SPI_ReadWriteByte(command); //
byte = SPI_ReadWriteByte(0xff); //
W25QXX_CS_1; //
return byte;
}
//
void W25qxx_Write_SR(uint8_t regno, uint8_t sr)
{
uint8_t command = 0;
switch (regno)
{
case 1:
command = W25X_WriteStatusReg1; //
break;
case 2:
command = W25X_WriteStatusReg2; //
break;
case 3:
command = W25X_WriteStatusReg3; //
break;
default:
command = W25X_WriteStatusReg1;
break;
}
W25QXX_CS_0; //
SPI_ReadWriteByte(command); //
SPI_ReadWriteByte(sr); //
W25QXX_CS_1; //
}
// W25QXX
void W25qxx_Write_Enable(void)
{
W25QXX_CS_0; //
SPI_ReadWriteByte(W25X_WriteEnable); //
W25QXX_CS_1; //
}
// W25QXX
void W25qxx_Write_Disable(void)
{
W25QXX_CS_0; //
SPI_ReadWriteByte(W25X_WriteDisable); //
W25QXX_CS_1; //
}
// 读取 W25Qxx系列芯片的ID
// W25Q80 ID 0xEF13
// W25Q16 ID 0xEF14
// W25Q32 ID 0xEF15
// W25Q64 ID 0xEF16
// W25Q128 ID 0xEF17
// W25Q256 ID 0xEF18
uint16_t W25qxx_ReadID(void)
{
uint16_t Temp = 0;
W25QXX_CS_0;
SPI_ReadWriteByte(0x90);
SPI_ReadWriteByte(0x00);
SPI_ReadWriteByte(0x00);
SPI_ReadWriteByte(0x00);
Temp |= SPI_ReadWriteByte(0xFF) << 8;
Temp |= SPI_ReadWriteByte(0xFF);
W25QXX_CS_1;
return Temp;
}
//
void W25qxx_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
uint16_t i;
W25QXX_CS_0; //
SPI_ReadWriteByte(W25X_ReadData); //
// if(W25qxx_TYPE==FLASH_ID) //W25Q256ĻַΪ4ֽڵģҪ8λ
// {
// SPI_ReadWriteByte((uint8_t)((ReadAddr)>>24));
// }
SPI_ReadWriteByte((uint8_t)((ReadAddr) >> 16)); // 24bitַ
SPI_ReadWriteByte((uint8_t)((ReadAddr) >> 8));
SPI_ReadWriteByte((uint8_t)ReadAddr);
for (i = 0; i < NumByteToRead; i++)
{
pBuffer[i] = SPI_ReadWriteByte(0xFF); // ѭ
}
W25QXX_CS_1;
}
/**
* @brief FLASH
* @param pBuffer洢ݵָ
* @param ReadAddrȡַ
* @param NumByteToReadȡݳ
* @retval
*/
void SPI_FLASH_BufferRead(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
W25QXX_CS_0;
SPI_ReadWriteByte(W25X_ReadData);
SPI_ReadWriteByte((ReadAddr & 0xFF0000) >> 16);
SPI_ReadWriteByte((ReadAddr & 0xFF00) >> 8);
SPI_ReadWriteByte(ReadAddr & 0xFF);
while (NumByteToRead--)
{
*pBuffer = SPI_ReadWriteByte(0xFF);
pBuffer++;
}
W25QXX_CS_1;
}
#define WIP_Flag 0x01
/**
* @brief ȴWIP(BUSY)־0ȴFLASHڲд
* @param none
* @retval none
*/
void SPI_FLASH_WaitForWriteEnd(void)
{
uint8_t FLASH_Status = 0;
W25QXX_CS_0;
SPI_ReadWriteByte(W25X_ReadStatusReg1);
do
{
FLASH_Status = SPI_ReadWriteByte(0xFF);
} while ((FLASH_Status & WIP_Flag) == SET);
W25QXX_CS_1;
}
/**
* @brief FLASH
* @param SectorAddrҪַ
* @retval
*/
void SPI_FLASH_SectorErase(uint32_t SectorAddr)
{
W25qxx_Write_Enable();
SPI_FLASH_WaitForWriteEnd();
W25QXX_CS_0;
SPI_ReadWriteByte(W25X_SectorErase);
SPI_ReadWriteByte((uint8_t)((SectorAddr) >> 16)); // 24bitַ
SPI_ReadWriteByte((uint8_t)((SectorAddr) >> 8));
SPI_ReadWriteByte(SectorAddr & 0xFF);
W25QXX_CS_1;
SPI_FLASH_WaitForWriteEnd();
}
#define SPI_FLASH_PageSize 256
#define SPI_FLASH_PerWritePageSize 256
/**
* @brief FLASHҳдݣñдǰҪȲ
* @param pBufferҪдݵָ
* @param WriteAddrдַ
* @param NumByteToWriteдݳȣСڵSPI_FLASH_PerWritePageSize
* @retval
*/
void SPI_FLASH_PageWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
W25qxx_Write_Enable();
W25QXX_CS_0;
SPI_ReadWriteByte(W25X_PageProgram);
SPI_ReadWriteByte((WriteAddr & 0xFF0000) >> 16);
SPI_ReadWriteByte((WriteAddr & 0xFF00) >> 8);
SPI_ReadWriteByte(WriteAddr & 0xFF);
if (NumByteToWrite > SPI_FLASH_PageSize)
{
NumByteToWrite = SPI_FLASH_PerWritePageSize;
}
while (NumByteToWrite--)
{
SPI_ReadWriteByte(*pBuffer);
pBuffer++;
}
W25QXX_CS_1;
SPI_FLASH_WaitForWriteEnd();
}
//
void W25qxx_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
uint16_t i;
W25qxx_Write_Enable();
W25QXX_CS_0;
SPI_ReadWriteByte(W25X_PageProgram);
// if(W25qxx_TYPE==FLASH_ID)
// {
// SPI_ReadWriteByte((uint8_t)((WriteAddr)>>24));
// }
SPI_ReadWriteByte((uint8_t)((WriteAddr) >> 16)); // 24bitַ
SPI_ReadWriteByte((uint8_t)((WriteAddr) >> 8));
SPI_ReadWriteByte((uint8_t)WriteAddr);
for (i = 0; i < NumByteToWrite; i++)
SPI_ReadWriteByte(pBuffer[i]); //
W25QXX_CS_1; //
SPI_FLASH_WaitForWriteEnd(); //
}
/**
* @brief FLASHдݣñдǰҪȲ
* @param pBufferҪдݵָ
* @param WriteAddrдַ
* @param NumByteToWriteдݳ
* @retval
*/
void SPI_FLASH_BufferWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
Addr = WriteAddr % SPI_FLASH_PageSize;
count = SPI_FLASH_PageSize - Addr;
NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
if (Addr == 0)
{
if (NumOfPage == 0)
{
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
}
else
{
while (NumOfPage--)
{
SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
WriteAddr += SPI_FLASH_PageSize;
pBuffer += SPI_FLASH_PageSize;
}
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
}
}
else
{
if (NumOfPage == 0)
{
if (NumOfSingle > count)
{
temp = NumOfSingle - count;
SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
WriteAddr += count;
pBuffer += count;
SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
}
else
{
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
}
}
else
{
NumByteToWrite -= count;
NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
WriteAddr += count;
pBuffer += count;
while (NumOfPage--)
{
SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
WriteAddr += SPI_FLASH_PageSize;
pBuffer += SPI_FLASH_PageSize;
}
if (NumOfSingle != 0)
{
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
}
}
}
}
//
void W25qxx_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
uint16_t pageremain;
pageremain = 256 - WriteAddr % 256; // ҳʣֽ
if (NumByteToWrite <= pageremain)
pageremain = NumByteToWrite; // 256ֽ
while (1)
{
W25qxx_Write_Page(pBuffer, WriteAddr, pageremain);
if (NumByteToWrite == pageremain)
break;
else
{
pBuffer += pageremain;
WriteAddr += pageremain;
NumByteToWrite -= pageremain;
if (NumByteToWrite > 256)
pageremain = 256;
else
pageremain = NumByteToWrite;
}
};
}
//
uint8_t W25qxx_BUFFER[4096];
void W25qxx_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
uint32_t secpos;
uint16_t secoff;
uint16_t secremain;
uint16_t i;
uint8_t *W25qxx_BUF;
W25qxx_BUF = W25qxx_BUFFER;
secpos = WriteAddr / 4096; //
secoff = WriteAddr % 4096; //
secremain = 4096 - secoff; //
if (NumByteToWrite <= secremain)
secremain = NumByteToWrite; //
while (1)
{
W25qxx_Read(W25qxx_BUF, secpos * 4096, 4096); //
for (i = 0; i < secremain; i++) //
{
if (W25qxx_BUF[secoff + i] != 0xFF)
break; //
}
if (i < secremain) //
{
W25qxx_Erase_Sector(secpos); //
for (i = 0; i < secremain; i++) //
{
W25qxx_BUF[i + secoff] = pBuffer[i];
}
W25qxx_Write_NoCheck(W25qxx_BUF, secpos * 4096, 4096); //
}
else
W25qxx_Write_NoCheck(pBuffer, WriteAddr, secremain); //
if (NumByteToWrite == secremain)
break; //
else //
{
secpos++; //
secoff = 0; //
pBuffer += secremain; //
WriteAddr += secremain; //
NumByteToWrite -= secremain; //
if (NumByteToWrite > 4096)
secremain = 4096; //
else
secremain = NumByteToWrite; //
}
};
}
//
void W25qxx_Erase_Chip(void)
{
W25qxx_Write_Enable(); // SET WEL
W25qxx_Wait_Busy();
W25QXX_CS_0; //
SPI_ReadWriteByte(W25X_ChipErase); //
W25QXX_CS_1; //
W25qxx_Wait_Busy(); //
}
//
void W25qxx_Erase_Sector(uint32_t Dst_Addr)
{
Dst_Addr *= 4096;
W25qxx_Write_Enable(); // SET WEL
W25qxx_Wait_Busy();
W25QXX_CS_0; //
SPI_ReadWriteByte(W25X_SectorErase); //
if (W25qxx_TYPE == FLASH_ID) //
{
SPI_ReadWriteByte((uint8_t)((Dst_Addr) >> 24));
}
SPI_ReadWriteByte((uint8_t)((Dst_Addr) >> 16)); // 24bitַ
SPI_ReadWriteByte((uint8_t)((Dst_Addr) >> 8));
SPI_ReadWriteByte((uint8_t)Dst_Addr);
W25QXX_CS_1; //
W25qxx_Wait_Busy(); //
}
// ȴ
void W25qxx_Wait_Busy(void)
{
while ((W25qxx_ReadSR(1) & 0x01) == 0x01)
; // BUSY
}
// ģʽ
void W25qxx_PowerDown(void)
{
W25QXX_CS_0; //
SPI_ReadWriteByte(W25X_PowerDown); //
W25QXX_CS_1; //
// delay_us(3); //
}
//
void W25qxx_WAKEUP(void)
{
W25QXX_CS_0; //
SPI_ReadWriteByte(W25X_ReleasePowerDown); //
W25QXX_CS_1; //
// delay_us(3); //
}
将以上俩文件都放在一个文件里(W25qxx)
然后引入到项目中
打开 usbd_storage_if.c 文件
引入外部FLASH驱动头文件
#include "./W25qxx/W25qxx.h"
修改这里的宏定义(每次使用CubeMx生成代码后,这里都需要再重新设置一次)
#define STORAGE_LUN_NBR 1
#define STORAGE_BLK_NBR 4096 // 256*16扇区=16MByte 更改FLASH大小更改这里就可以
#define STORAGE_BLK_SIZ 4096 // 每个扇区4096Byte
修改以下函数内的内容
int8_t STORAGE_Init_FS(uint8_t lun)
{
/* USER CODE BEGIN 2 */
// UNUSED(lun);
W25qxx_Init();
return (USBD_OK);
/* USER CODE END 2 */
}
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 */
}
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);
blk_addr += SPI_FLASH_START_SECTOR;
SPI_FLASH_BufferRead(buf, blk_addr * SPI_FLASH_SECTOR_SIZE, blk_len * SPI_FLASH_SECTOR_SIZE);
return (USBD_OK);
/* USER CODE END 6 */
}
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);
uint32_t write_addr;
blk_addr += SPI_FLASH_START_SECTOR;
write_addr = blk_addr * SPI_FLASH_SECTOR_SIZE;
SPI_FLASH_SectorErase(write_addr);
SPI_FLASH_BufferWrite((uint8_t *)buf, write_addr, blk_len * SPI_FLASH_SECTOR_SIZE);
return (USBD_OK);
/* USER CODE END 7 */
}
修改后完整的代码(usbd_storage_if.c)
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : usbd_storage_if.c
* @version : v1.0_Cube
* @brief : Memory management layer.
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usbd_storage_if.h"
/* USER CODE BEGIN INCLUDE */
#include "./W25qxx/W25qxx.h"
/* USER CODE END INCLUDE */
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
/* USER CODE END PV */
/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
* @brief Usb device.
* @{
*/
/** @defgroup USBD_STORAGE
* @brief Usb mass storage device module
* @{
*/
/** @defgroup USBD_STORAGE_Private_TypesDefinitions
* @brief Private types.
* @{
*/
/* USER CODE BEGIN PRIVATE_TYPES */
/* USER CODE END PRIVATE_TYPES */
/**
* @}
*/
/** @defgroup USBD_STORAGE_Private_Defines
* @brief Private defines.
* @{
*/
// #define STORAGE_LUN_NBR 1
// #define STORAGE_BLK_NBR 0x10000
// #define STORAGE_BLK_SIZ 0x200
/* USER CODE BEGIN PRIVATE_DEFINES */
#define STORAGE_LUN_NBR 1
#define STORAGE_BLK_NBR 4096
#define STORAGE_BLK_SIZ 4096
/* USER CODE END PRIVATE_DEFINES */
/**
* @}
*/
/** @defgroup USBD_STORAGE_Private_Macros
* @brief Private macros.
* @{
*/
/* USER CODE BEGIN PRIVATE_MACRO */
/* USER CODE END PRIVATE_MACRO */
/**
* @}
*/
/** @defgroup USBD_STORAGE_Private_Variables
* @brief Private variables.
* @{
*/
/* USER CODE BEGIN INQUIRY_DATA_FS */
/** USB Mass storage Standard Inquiry Data. */
const int8_t STORAGE_Inquirydata_FS[] = {
/* 36 */
/* LUN 0 */
0x00,
0x80,
0x02,
0x02,
(STANDARD_INQUIRY_DATA_LEN - 5),
0x00,
0x00,
0x00,
'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Manufacturer : 8 bytes */
'P', 'r', 'o', 'd', 'u', 'c', 't', ' ', /* Product : 16 Bytes */
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
'0', '.', '0', '1' /* Version : 4 Bytes */
};
/* USER CODE END INQUIRY_DATA_FS */
/* USER CODE BEGIN PRIVATE_VARIABLES */
/* USER CODE END PRIVATE_VARIABLES */
/**
* @}
*/
/** @defgroup USBD_STORAGE_Exported_Variables
* @brief Public variables.
* @{
*/
extern USBD_HandleTypeDef hUsbDeviceFS;
/* USER CODE BEGIN EXPORTED_VARIABLES */
/* USER CODE END EXPORTED_VARIABLES */
/**
* @}
*/
/** @defgroup USBD_STORAGE_Private_FunctionPrototypes
* @brief Private functions declaration.
* @{
*/
static int8_t STORAGE_Init_FS(uint8_t lun);
static int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size);
static int8_t STORAGE_IsReady_FS(uint8_t lun);
static int8_t STORAGE_IsWriteProtected_FS(uint8_t lun);
static int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
static int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
static int8_t STORAGE_GetMaxLun_FS(void);
/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */
/**
* @}
*/
USBD_StorageTypeDef USBD_Storage_Interface_fops_FS =
{
STORAGE_Init_FS,
STORAGE_GetCapacity_FS,
STORAGE_IsReady_FS,
STORAGE_IsWriteProtected_FS,
STORAGE_Read_FS,
STORAGE_Write_FS,
STORAGE_GetMaxLun_FS,
(int8_t *)STORAGE_Inquirydata_FS};
/* Private functions ---------------------------------------------------------*/
/**
* @brief Initializes 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);
W25qxx_Init();
return (USBD_OK);
/* 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 */
// uint16_t flash_ID;
// flash_ID = W25QXX_ReadID();
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);
blk_addr += SPI_FLASH_START_SECTOR;
SPI_FLASH_BufferRead(buf, blk_addr * SPI_FLASH_SECTOR_SIZE, blk_len * SPI_FLASH_SECTOR_SIZE);
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);
uint32_t write_addr;
blk_addr += SPI_FLASH_START_SECTOR;
write_addr = blk_addr * SPI_FLASH_SECTOR_SIZE;
SPI_FLASH_SectorErase(write_addr);
SPI_FLASH_BufferWrite((uint8_t *)buf, write_addr, blk_len * SPI_FLASH_SECTOR_SIZE);
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 */
}
/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
/**
* @}
*/
/**
* @}
*/
最后编译烧录
测试
第一次插入电脑会提示格式化,格式化以后就会变成U盘了,往里边丢文件测试完成