概述
使用stm32F407驱动W25Q16,使用FATFS文件系统,USB虚拟优盘功能,W25Q16一共512个扇区,其中128作为flash存取相关数据,其他的384个扇区用作虚拟U盘使用
CubeMax配置过程
代码
W25Q16.c
/**
******************************************************************************
* @file stm32_eval_spi_flash.c
* @author MCD Application Team
* @version V4.5.0
* @date 07-March-2011
* @brief This file provides a set of functions needed to manage the SPI M25Pxxx
* FLASH memory mounted on STM32xx-EVAL board (refer to stm32_eval.h
* to know about the boards supporting this memory).
* It implements a high level communication layer for read and write
* from/to this memory. The needed STM32 hardware resources (SPI and
* GPIO) are defined in stm32xx_eval.h file, and the initialization is
* performed in sFLASH_LowLevel_Init() function declared in stm32xx_eval.c
* file.
* You can easily tailor this driver to any other development board,
* by just adapting the defines for hardware resources and
* sFLASH_LowLevel_Init() function.
*
* +-----------------------------------------------------------+
* | Pin assignment |
* +-----------------------------+---------------+-------------+
* | STM32 SPI Pins | sFLASH | Pin |
* +-----------------------------+---------------+-------------+
* | sFLASH_CS_PIN | ChipSelect(/S)| 1 |
* | sFLASH_SPI_MISO_PIN / MISO | DataOut(Q) | 2 |
* | | VCC | 3 (3.3 V)|
* | | GND | 4 (0 V) |
* | sFLASH_SPI_MOSI_PIN / MOSI | DataIn(D) | 5 |
* | sFLASH_SPI_SCK_PIN / SCLK | Clock(C) | 6 |
* | | VCC | 7 (3.3 V)|
* | | VCC | 8 (3.3 V)|
* +-----------------------------+---------------+-------------+
******************************************************************************
* @attention
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <h2><center>© COPYRIGHT 2011 STMicroelectronics</center></h2>
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
/** @addtogroup Utilities
* @{
*/
/** @addtogroup STM32_EVAL
* @{
*/
/** @addtogroup Common
* @{
*/
/** @addtogroup STM32_EVAL_SPI_FLASH
* @brief This file includes the M25Pxxx SPI FLASH driver of STM32-EVAL boards.
* @{
*/
/** @defgroup STM32_EVAL_SPI_FLASH_Private_Types
* @{
*/
/**
* @}
*/
/** @defgroup STM32_EVAL_SPI_FLASH_Private_Defines
* @{
*/
/**
* @}
*/
/** @defgroup STM32_EVAL_SPI_FLASH_Private_Macros
* @{
*/
/**
* @}
*/
/** @defgroup STM32_EVAL_SPI_FLASH_Private_Variables
* @{
*/
/**
* @}
*/
/** @defgroup STM32_EVAL_SPI_FLASH_Private_Function_Prototypes
* @{
*/
/**
* @}
*/
/** @defgroup STM32_EVAL_SPI_FLASH_Private_Functions
* @{
*/
/* USER CODE BEGIN 1 */
//SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
uint8_t SPI1_WriteReadData(uint8_t TxData)
{
uint8_t RxData = 0;
HAL_SPI_TransmitReceive(&hspi1, &TxData, &RxData, 1, 0xff);
return RxData;
}
/**
* @brief Initializes the peripherals used by the SPI FLASH driver.
* @param None
* @retval None
*/
void sFLASH_Init(void)
{
//MX_SPI1_Init();
}
/**
* @brief Erases the specified FLASH sector.
* @param SectorAddr: address of the sector to erase.
* @retval None
*/
void sFLASH_EraseSector(uint32_t SectorAddr)
{
/*!< Send write enable instruction */
sFLASH_WriteEnable();
/*!< Sector Erase */
/*!< Select the FLASH: Chip Select low */
sFLASH_CS_LOW();
/*!< Send Sector Erase instruction */
sFLASH_SendByte(sFLASH_CMD_SE);
/*!< Send SectorAddr high nibble address byte */
sFLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
/*!< Send SectorAddr medium nibble address byte */
sFLASH_SendByte((SectorAddr & 0xFF00) >> 8);
/*!< Send SectorAddr low nibble address byte */
sFLASH_SendByte(SectorAddr & 0xFF);
/*!< Deselect the FLASH: Chip Select high */
sFLASH_CS_HIGH();
/*!< Wait the end of Flash writing */
sFLASH_WaitForWriteEnd();
}
/**
* @brief Erases the entire FLASH.
* @param None
* @retval None
*/
void sFLASH_EraseBulk(void)
{
/*!< Send write enable instruction */
sFLASH_WriteEnable();
/*!< Bulk Erase */
/*!< Select the FLASH: Chip Select low */
sFLASH_CS_LOW();
/*!< Send Bulk Erase instruction */
sFLASH_SendByte(sFLASH_CMD_BE);
/*!< Deselect the FLASH: Chip Select high */
sFLASH_CS_HIGH();
/*!< Wait the end of Flash writing */
sFLASH_WaitForWriteEnd();
}
/**
* @brief Writes more than one byte to the FLASH with a single WRITE cycle
* (Page WRITE sequence).
* @note The number of byte can't exceed the FLASH page size.
* @param pBuffer: pointer to the buffer containing the data to be written
* to the FLASH.
* @param WriteAddr: FLASH's internal address to write to.
* @param NumByteToWrite: number of bytes to write to the FLASH, must be equal
* or less than "sFLASH_PAGESIZE" value.
* @retval None
*/
void sFLASH_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
/*!< Enable the write access to the FLASH */
sFLASH_WriteEnable();
/*!< Select the FLASH: Chip Select low */
sFLASH_CS_LOW();
/*!< Send "Write to Memory " instruction */
sFLASH_SendByte(sFLASH_CMD_WRITE);
/*!< Send WriteAddr high nibble address byte to write to */
sFLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
/*!< Send WriteAddr medium nibble address byte to write to */
sFLASH_SendByte((WriteAddr & 0xFF00) >> 8);
/*!< Send WriteAddr low nibble address byte to write to */
sFLASH_SendByte(WriteAddr & 0xFF);
/*!< while there is data to be written on the FLASH */
while (NumByteToWrite--)
{
/*!< Send the current byte */
sFLASH_SendByte(*pBuffer);
/*!< Point on the next byte to be written */
pBuffer++;
}
/*!< Deselect the FLASH: Chip Select high */
sFLASH_CS_HIGH();
/*!< Wait the end of Flash writing */
sFLASH_WaitForWriteEnd();
}
/*
\brief: 可跨页写数据(不考虑擦除,认为写入的地址都为0xFF)
\param: WriteAddr:要写入的地址
pBuffer:写入的数据
NumByteToWrite:数据的数量(字节数)
\retval: none
*/
void W25Q64_StepOverPageWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint32_t NumByteToWrite)
{
uint32_t addr_remain= sFLASH_PAGE_SIZE - WriteAddr%sFLASH_PAGE_SIZE;//当前页地址剩余
uint8_t *pData=pBuffer;
if(NumByteToWrite <= addr_remain)
{
addr_remain = NumByteToWrite;
}
while(1)
{
sFLASH_WritePage(pData,WriteAddr, addr_remain);
if(addr_remain == NumByteToWrite) break; //数据全部写入
pData += addr_remain; //数据地址偏移
WriteAddr += addr_remain; //地址偏移
NumByteToWrite -= addr_remain; //计算剩余数据
addr_remain = sFLASH_PAGE_SIZE; //写入一页数据
if(NumByteToWrite <= addr_remain) //计算当前页是否够写入剩余数据
{
addr_remain = NumByteToWrite;
}
}
}
/**
* @brief Writes block of data to the FLASH. In this function, the number of
* WRITE cycles are reduced, using Page WRITE sequence.
* @param pBuffer: pointer to the buffer containing the data to be written
* to the FLASH.
* @param WriteAddr: FLASH's internal address to write to.
* @param NumByteToWrite: number of bytes to write to the FLASH.
* @retval None
*/
uint8_t sector_data[sFLASH_SECTOR_SIZE];
void sFLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
#if 0
uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
Addr = WriteAddr % sFLASH_SPI_PAGESIZE;
count = sFLASH_SPI_PAGESIZE - Addr;
NumOfPage = NumByteToWrite / sFLASH_SPI_PAGESIZE;
NumOfSingle = NumByteToWrite % sFLASH_SPI_PAGESIZE;
if (Addr == 0) /*!< WriteAddr is sFLASH_PAGESIZE aligned */
{
if (NumOfPage == 0) /*!< NumByteToWrite < sFLASH_PAGESIZE */
{
sFLASH_WritePage(pBuffer, WriteAddr, NumByteToWrite);
}
else /*!< NumByteToWrite > sFLASH_PAGESIZE */
{
while (NumOfPage--)
{
sFLASH_WritePage(pBuffer, WriteAddr, sFLASH_SPI_PAGESIZE);
WriteAddr += sFLASH_SPI_PAGESIZE;
pBuffer += sFLASH_SPI_PAGESIZE;
}
sFLASH_WritePage(pBuffer, WriteAddr, NumOfSingle);
}
}
else /*!< WriteAddr is not sFLASH_PAGESIZE aligned */
{
if (NumOfPage == 0) /*!< NumByteToWrite < sFLASH_PAGESIZE */
{
if (NumOfSingle > count) /*!< (NumByteToWrite + WriteAddr) > sFLASH_PAGESIZE */
{
temp = NumOfSingle - count;
sFLASH_WritePage(pBuffer, WriteAddr, count);
WriteAddr += count;
pBuffer += count;
sFLASH_WritePage(pBuffer, WriteAddr, temp);
}
else
{
sFLASH_WritePage(pBuffer, WriteAddr, NumByteToWrite);
}
}
else /*!< NumByteToWrite > sFLASH_PAGESIZE */
{
NumByteToWrite -= count;
NumOfPage = NumByteToWrite / sFLASH_SPI_PAGESIZE;
NumOfSingle = NumByteToWrite % sFLASH_SPI_PAGESIZE;
sFLASH_WritePage(pBuffer, WriteAddr, count);
WriteAddr += count;
pBuffer += count;
while (NumOfPage--)
{
sFLASH_WritePage(pBuffer, WriteAddr, sFLASH_SPI_PAGESIZE);
WriteAddr += sFLASH_SPI_PAGESIZE;
pBuffer += sFLASH_SPI_PAGESIZE;
}
if (NumOfSingle != 0)
{
sFLASH_WritePage(pBuffer, WriteAddr, NumOfSingle);
}
}
}
#else
uint16_t sector_offset = WriteAddr%4096; //计算当前扇区的地址偏移
uint16_t sector_remain = 4096 - sector_offset; //计算当前扇区剩余
uint32_t sector_addr = WriteAddr - sector_offset; //计算当前扇区的起始地址
uint8_t *pData=pBuffer;
uint32_t i;
if(NumByteToWrite <= sector_remain)
{
sector_remain=(uint16_t )NumByteToWrite;
}
while(1)
{
sFLASH_ReadBuffer(sector_data,WriteAddr,sector_remain);//读取要写入地址的数据
for(i=0;i<sector_remain;i++)
{
if(sector_data[i]!=0xFF)
{
break;
}
}
if(i!=sector_remain)//判断是否需要擦除扇区
{
//擦除前保存当前扇区前一段数据
sFLASH_ReadBuffer(sector_data,WriteAddr,sector_remain);
//擦除前保存当前扇区后一段数据
sFLASH_ReadBuffer(sector_data+(sector_offset+sector_remain),WriteAddr + sector_remain,sFLASH_SECTOR_SIZE - (sector_offset+sector_remain));
sFLASH_EraseSector(sector_addr);//擦除扇区
//将要写入的数据插入缓冲区
for(i=0;i<sector_remain;i++)
{
sector_data[sector_offset+i]= pData[i];
}
W25Q64_StepOverPageWrite(sector_data,sector_addr,sFLASH_SECTOR_SIZE);
sector_offset = 0;
}
else
{
W25Q64_StepOverPageWrite(pData,WriteAddr,sector_remain);//向当前扇区写入数据
}
if(sector_remain == NumByteToWrite) break;//全部数据完全写入
pData += sector_remain; //数据地址偏移
WriteAddr += sector_remain; //flash地址偏移
sector_addr = WriteAddr; //当前扇区起始地址
NumByteToWrite -= sector_remain; //数据量减少
sector_remain = sFLASH_SECTOR_SIZE;//当前扇区剩余
if(NumByteToWrite <= sFLASH_SECTOR_SIZE)//计算当前扇区是否够写入剩余数据
{
sector_remain = NumByteToWrite;
}
}
#endif
}
/**
* @brief Reads a block of data from the FLASH.
* @param pBuffer: pointer to the buffer that receives the data read from the FLASH.
* @param ReadAddr: FLASH's internal address to read from.
* @param NumByteToRead: number of bytes to read from the FLASH.
* @retval None
*/
void sFLASH_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
/*!< Select the FLASH: Chip Select low */
sFLASH_CS_LOW();
/*!< Send "Read from Memory " instruction */
sFLASH_SendByte(sFLASH_CMD_READ);
/*!< Send ReadAddr high nibble address byte to read from */
sFLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
/*!< Send ReadAddr medium nibble address byte to read from */
sFLASH_SendByte((ReadAddr& 0xFF00) >> 8);
/*!< Send ReadAddr low nibble address byte to read from */
sFLASH_SendByte(ReadAddr & 0xFF);
while (NumByteToRead--) /*!< while there is data to be read */
{
/*!< Read a byte from the FLASH */
*pBuffer = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
/*!< Point to the next location where the byte read will be saved */
pBuffer++;
}
/*!< Deselect the FLASH: Chip Select high */
sFLASH_CS_HIGH();
}
/**
* @brief Reads FLASH identification.
* @param None
* @retval FLASH identification
*/
uint32_t sFLASH_ReadID(void)
{
uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
/*!< Select the FLASH: Chip Select low */
sFLASH_CS_LOW();
/*!< Send "RDID " instruction */
sFLASH_SendByte(0x9F);
/*!< Read a byte from the FLASH */
Temp0 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
/*!< Read a byte from the FLASH */
Temp1 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
/*!< Read a byte from the FLASH */
Temp2 = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
/*!< Deselect the FLASH: Chip Select high */
sFLASH_CS_HIGH();
Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
return Temp;
}
/**
* @brief Initiates a read data byte (READ) sequence from the Flash.
* This is done by driving the /CS line low to select the device, then the READ
* instruction is transmitted followed by 3 bytes address. This function exit
* and keep the /CS line low, so the Flash still being selected. With this
* technique the whole content of the Flash is read with a single READ instruction.
* @param ReadAddr: FLASH's internal address to read from.
* @retval None
*/
void sFLASH_StartReadSequence(uint32_t ReadAddr)
{
/*!< Select the FLASH: Chip Select low */
sFLASH_CS_LOW();
/*!< Send "Read from Memory " instruction */
sFLASH_SendByte(sFLASH_CMD_READ);
/*!< Send the 24-bit address of the address to read from -------------------*/
/*!< Send ReadAddr high nibble address byte */
sFLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
/*!< Send ReadAddr medium nibble address byte */
sFLASH_SendByte((ReadAddr& 0xFF00) >> 8);
/*!< Send ReadAddr low nibble address byte */
sFLASH_SendByte(ReadAddr & 0xFF);
}
/**
* @brief Reads a byte from the SPI Flash.
* @note This function must be used only if the Start_Read_Sequence function
* has been previously called.
* @param None
* @retval Byte Read from the SPI Flash.
*/
uint8_t sFLASH_ReadByte(void)
{
return (sFLASH_SendByte(sFLASH_DUMMY_BYTE));
}
/**
* @brief Sends a byte through the SPI interface and return the byte received
* from the SPI bus.
* @param byte: byte to send.
* @retval The value of the received byte.
*/
uint8_t sFLASH_SendByte(uint8_t byte)
{
return SPI1_WriteReadData(byte);
}
/**
* @brief Enables the write access to the FLASH.
* @param None
* @retval None
*/
void sFLASH_WriteEnable(void)
{
/*!< Select the FLASH: Chip Select low */
sFLASH_CS_LOW();
/*!< Send "Write Enable" instruction */
sFLASH_SendByte(sFLASH_CMD_WREN);
/*!< Deselect the FLASH: Chip Select high */
sFLASH_CS_HIGH();
}
/**
* @brief Polls the status of the Write In Progress (WIP) flag in the FLASH's
* status register and loop until write opertaion has completed.
* @param None
* @retval None
*/
void sFLASH_WaitForWriteEnd(void)
{
uint8_t flashstatus = 0;
/*!< Select the FLASH: Chip Select low */
sFLASH_CS_LOW();
/*!< Send "Read Status Register" instruction */
sFLASH_SendByte(sFLASH_CMD_RDSR);
/*!< Loop as long as the memory is busy with a write cycle */
do
{
/*!< Send a dummy byte to generate the clock needed by the FLASH
and put the value of the status register in FLASH_Status variable */
flashstatus = sFLASH_SendByte(sFLASH_DUMMY_BYTE);
}
while ((flashstatus & sFLASH_WIP_FLAG) == SET); /* Write in progress */
/*!< Deselect the FLASH: Chip Select high */
sFLASH_CS_HIGH();
}
#include "string.h"
uint8_t tx_buff1[]="flash start";
uint8_t tx_buff2[]="flash end";
uint8_t tx_buff3[]="Cross boundary testing";
uint8_t rx_buff1[20];
uint8_t rx_buff2[20];
uint8_t rx_buff3[40];
void flash_test(void)
{
//检测ID号
uint32_t flashID=0;
flashID=sFLASH_ReadID();
printf("\r\nflashID is %x\r\n",flashID);
if(flashID==sFLASH_W25Q16)
{
//一共512个扇区 flash数据存储位置 是在 1-128 个扇区之间 其他384个扇区被当做U盘使用
//起始地址是 0x00000000 结束地址是 0x7FFFF(128*4096-1)
//写数据
sFLASH_WriteBuffer(tx_buff1,0,strlen((char *)tx_buff1));
sFLASH_WriteBuffer(tx_buff2,0x7FFFF-strlen((char *)tx_buff2),strlen((char *)tx_buff2));
sFLASH_ReadBuffer(rx_buff1,0,strlen((char *)tx_buff1));
sFLASH_ReadBuffer(rx_buff2,0x7FFFF-strlen((char *)tx_buff2),strlen((char *)tx_buff2));
printf("rxbuff1 : %s\r\n",rx_buff1);
printf("rxbuff2 : %s\r\n",rx_buff2);
}
else printf("flash is erro!!");
}
void flash_test1(void)
{
//一共512个扇区 flash数据存储位置 是在 1-128 个扇区之间 其他384个扇区被当做U盘使用
//起始地址是 0x00000000 结束地址是 0x8FFFF(128*4096-1)
//写数据
sFLASH_WriteBuffer(tx_buff3,0x7FFFF,strlen((char *)tx_buff3));
sFLASH_ReadBuffer(rx_buff3,0x7FFFF,strlen((char *)tx_buff3));
printf("rxbuff3 : %s\r\n",rx_buff3);
}
/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
W25Q16.h
#ifndef _FLASH_H_
#define _FLASH_H_
#include "stm32f4xx_hal.h"
/**
* @brief M25P SPI Flash supported commands
*/
#define sFLASH_CMD_WRITE 0x02 /*!< Write to Memory instruction */
#define sFLASH_CMD_WRSR 0x01 /*!< Write Status Register instruction */
#define sFLASH_CMD_WREN 0x06 /*!< Write enable instruction */
#define sFLASH_CMD_READ 0x03 /*!< Read from Memory instruction */
#define sFLASH_CMD_RDSR 0x05 /*!< Read Status Register instruction */
#define sFLASH_CMD_RDID 0x9F /*!< Read identification */
#define sFLASH_CMD_SE 0x20 /*!< Sector Erase instruction */
#define sFLASH_CMD_BE 0xD8 /*!< Bulk Erase instruction */
#define sFLASH_WIP_FLAG 0x01 /*!< Write In Progress (WIP) flag */
#define sFLASH_DUMMY_BYTE 0xA5
#define sFLASH_SPI_PAGESIZE 0x100
#define sFLASH_W25Q16 0x207017
//W25Q16 is 2M
//1bytes is 8bit
#define sFLASH_BLOCK_COUNT 32 //32 block
#define sFLASH_SECTOR_COUNT (sFLASH_BLOCK_COUNT*16) //512 sector
#define sFLASH_PAGE_SIZE (256) //1 page is 256 bytes
#define sFLASH_SECTOR_SIZE (sFLASH_PAGE_SIZE*16) //1 sector is 4kb(4096 bytes)(16 page)
#define sFLASH_BLOCK_SIZE (sFLASH_SECTOR_SIZE*16) //1 block is 64kb(65536 bytes)(16 sector)
#define Offsee_U_Pan_addr (sFLASH_SECTOR_SIZE*128) //W25q16用作虚拟U盘的偏移量
#define Offsee_sector_num (Offsee_U_Pan_addr/sFLASH_SECTOR_SIZE)
//0x000000~0x1FFFFF
/**
* @}
*/
/** @defgroup STM32_EVAL_SPI_FLASH_Exported_Macros
* @{
*/
/**
* @brief Select sFLASH: Chip Select pin low
*/
#define sFLASH_CS_LOW() HAL_GPIO_WritePin(W25Q16_CS_GPIO_Port, W25Q16_CS_Pin, GPIO_PIN_RESET);
/**
* @brief Deselect sFLASH: Chip Select pin high
*/
#define sFLASH_CS_HIGH() HAL_GPIO_WritePin(W25Q16_CS_GPIO_Port, W25Q16_CS_Pin, GPIO_PIN_SET);
/**
* @}
*/
/** @defgroup STM32_EVAL_SPI_FLASH_Exported_Functions
* @{
*/
/**
* @brief High layer functions
*/
void sFLASH_Init(void);
void sFLASH_EraseSector(uint32_t SectorAddr);
void sFLASH_EraseBulk(void);
void sFLASH_WritePage(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void sFLASH_WriteBuffer(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
void sFLASH_ReadBuffer(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);
uint32_t sFLASH_ReadID(void);
void sFLASH_StartReadSequence(uint32_t ReadAddr);
/**
* @brief Low layer functions
*/
uint8_t sFLASH_ReadByte(void);
uint8_t sFLASH_SendByte(uint8_t byte);
uint16_t sFLASH_SendHalfWord(uint16_t HalfWord);
void sFLASH_WriteEnable(void);
void sFLASH_WaitForWriteEnd(void);
void flash_test(void);
void flash_test1(void);
#ifdef __cplusplus
}
#endif
#endif /* __STM32_EVAL_SPI_FLASH_H */
user_diskio.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file user_diskio.c
* @brief This file includes a diskio driver skeleton to be completed by the user.
******************************************************************************
* @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 */
#ifdef USE_OBSOLETE_USER_CODE_SECTION_0
/*
* Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0)
* To be suppressed in the future.
* Kept to ensure backward compatibility with previous CubeMx versions when
* migrating projects.
* User code previously added there should be copied in the new user sections before
* the section contents can be deleted.
*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
#endif
/* USER CODE BEGIN DECL */
/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include "ff_gen_drv.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Disk status */
static volatile DSTATUS Stat = STA_NOINIT;
FATFS fs;
FIL file; /* 文件对象 */
unsigned char work[4096] = {0};
FRESULT f_res; /* 文件操作结果 */
BYTE ReadBuffer[2000]={0}; /* 读缓存区 */
BYTE WriteBuffer[]= "test file is created on STM32"; /* 文件写入内容 */
extern char USERPath[4];
UINT bw;
void create_file(void)
{
f_res = f_open(&file, "0:test.txt", FA_OPEN_ALWAYS | FA_WRITE);
f_res = f_write(&file, WriteBuffer, sizeof(WriteBuffer), &bw);
f_res = f_close(&file);
}
void read_file(void)
{
f_res = f_open(&file, "0:test.txt", FA_READ);
f_res = f_read(&file, ReadBuffer, sizeof(ReadBuffer), &bw);
printf("test :%s\r\n", (const char *)ReadBuffer);
f_res = f_close(&file);
memset(ReadBuffer,0,sizeof(WriteBuffer));
f_res = f_open(&file, "0:test2.txt", FA_READ);
f_res = f_read(&file, ReadBuffer, sizeof(ReadBuffer), &bw);
printf("test2 :%s\r\n", (const char *)ReadBuffer);
f_res = f_close(&file);
}
//第一次运行文件系统,需要先注册文件系统和格式化
void FatfsTest(void)
{
f_res = f_mount(&fs, USERPath, 1);
if(f_res == FR_NO_FILESYSTEM) //如果没有文件系统就格式化创建创建文件系统
{
printf("no system so geshihua\r\n");
f_res=f_mkfs(USERPath, FM_FAT, 4096, work, sizeof(work)); //格式化
if(f_res == FR_OK)
{
f_res = f_mount(NULL,USERPath,1); //格式化后,先取消挂载
f_res = f_mount(&fs,USERPath,1); //重新挂载
}
}
if(f_res == FR_OK)
printf(" mount sucess!!! \r\n");
else
printf(" mount error : %d \r\n",f_res);
create_file(); //创建TXT文件并写"PZKKKKK666\n"
read_file(); //读取文件内容并放到ReadBuffer中
}
/* USER CODE END DECL */
/* Private function prototypes -----------------------------------------------*/
DSTATUS USER_initialize (BYTE pdrv);
DSTATUS USER_status (BYTE pdrv);
DRESULT USER_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count);
#if _USE_WRITE == 1
DRESULT USER_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count);
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
DRESULT USER_ioctl (BYTE pdrv, BYTE cmd, void *buff);
#endif /* _USE_IOCTL == 1 */
Diskio_drvTypeDef USER_Driver =
{
USER_initialize,
USER_status,
USER_read,
#if _USE_WRITE
USER_write,
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
USER_ioctl,
#endif /* _USE_IOCTL == 1 */
};
/* Private functions ---------------------------------------------------------*/
/**
* @brief Initializes a Drive
* @param pdrv: Physical drive number (0..)
* @retval DSTATUS: Operation status
*/
DSTATUS USER_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
/* USER CODE BEGIN INIT */
if(sFLASH_ReadID() == sFLASH_W25Q16)
{
Stat &= ~STA_NOINIT;
}
return Stat;
/* USER CODE END INIT */
}
/**
* @brief Gets Disk Status
* @param pdrv: Physical drive number (0..)
* @retval DSTATUS: Operation status
*/
DSTATUS USER_status (
BYTE pdrv /* Physical drive number to identify the drive */
)
{
/* USER CODE BEGIN STATUS */
Stat &= ~STA_NOINIT;
return Stat;
/* USER CODE END STATUS */
}
/**
* @brief Reads Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data buffer to store read data
* @param sector: Sector address (LBA)
* @param count: Number of sectors to read (1..128)
* @retval DRESULT: Operation result
*/
DRESULT USER_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to read */
)
{
/* USER CODE BEGIN READ */
for(uint8_t i=0;i<count;i++)
{
sFLASH_ReadBuffer(buff+i*sFLASH_SECTOR_SIZE, Offsee_U_Pan_addr+sector*sFLASH_SECTOR_SIZE+i*sFLASH_SECTOR_SIZE, sFLASH_SECTOR_SIZE);
}
return RES_OK;
/* USER CODE END READ */
}
/**
* @brief Writes Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data to be written
* @param sector: Sector address (LBA)
* @param count: Number of sectors to write (1..128)
* @retval DRESULT: Operation result
*/
#if _USE_WRITE == 1
DRESULT USER_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to write */
)
{
/* USER CODE BEGIN WRITE */
/* USER CODE HERE */
for(uint8_t i=0;i<count;i++)
{
sFLASH_WriteBuffer((u8*)(buff+i*sFLASH_SECTOR_SIZE), Offsee_U_Pan_addr+sector*sFLASH_SECTOR_SIZE+i*sFLASH_SECTOR_SIZE, sFLASH_SECTOR_SIZE);
}
return RES_OK;
/* USER CODE END WRITE */
}
#endif /* _USE_WRITE == 1 */
/**
* @brief I/O control operation
* @param pdrv: Physical drive number (0..)
* @param cmd: Control code
* @param *buff: Buffer to send/receive control data
* @retval DRESULT: Operation result
*/
#if _USE_IOCTL == 1
DRESULT USER_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
/* USER CODE BEGIN IOCTL */
DRESULT res = RES_ERROR;
if(pdrv != 0) return RES_PARERR;
switch(cmd)
{
case CTRL_SYNC:
res = RES_OK;
break;
case GET_SECTOR_COUNT:
*(DWORD*)buff = sFLASH_SECTOR_COUNT-Offsee_sector_num;
res = RES_OK;
break;
case GET_SECTOR_SIZE:
*(WORD*)buff = sFLASH_SECTOR_SIZE;
res = RES_OK;
break;
case GET_BLOCK_SIZE:
*(DWORD*)buff = sFLASH_BLOCK_SIZE;
res = RES_OK;
break;
default:
res = RES_PARERR;
break;
}
return res;
/* USER CODE END IOCTL */
}
#endif /* _USE_IOCTL == 1 */
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 "flash.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 */
#if 0
/* 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 */
#else
//#define STORAGE_BLK_NBR 扇区数量
//#define STORAGE_BLK_SIZ 扇区大小
#define STORAGE_LUN_NBR 1
#define STORAGE_BLK_NBR (sFLASH_SECTOR_COUNT-Offsee_sector_num)
#define STORAGE_BLK_SIZ sFLASH_SECTOR_SIZE
//U盘容量就是 扇区数量*扇区大小
#endif
/* 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);
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 */
UNUSED(lun);
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);
for(uint8_t i=0; i<blk_len; i++)
{
sFLASH_ReadBuffer(buf+i*STORAGE_BLK_SIZ, Offsee_U_Pan_addr+blk_addr*STORAGE_BLK_SIZ+i*STORAGE_BLK_SIZ, 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);
for(uint8_t i=0; i<blk_len; i++)
{
sFLASH_WriteBuffer(buf+i*STORAGE_BLK_SIZ, Offsee_U_Pan_addr+blk_addr*STORAGE_BLK_SIZ+i*STORAGE_BLK_SIZ, 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 */
}
/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
/**
* @}
*/
/**
* @}
*/
main.c
int main(void)
{
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_SPI1_Init();
MX_USB_DEVICE_Init();
MX_FATFS_Init();
/* USER CODE BEGIN 2 */
flash_test();
FatfsTest();
while (1)
{
;
}
}
注意事项
- STORAGE_BLK_NBR 表示扇区数量
STORAGE_BLK_SIZ 表示扇区大小
电脑上的U盘容量跟这两个参数密切相关 容量=扇区数量*扇区大小
这两个参数大小也要跟 USER_ioctl() 函数中一致 - 由于前128个扇区用于flash存取数据,使用再使用过程中,数据存放的地址不要超过0x80000(128*4096),如果超过了,就GG了,如果不想存取数据,所有空间全部用作虚拟U盘,可以将Offsee_U_Pan_addr改成0就行了。
- 虚拟U盘第一次上电需要格式化,格式化的大小就是STORAGE_BLK_NBR×STORAGE_BLK_SIZ,我这个不知道出现什么问题空间少了0.25MB,
理想情况下,格式化时显示1.5MB
结果
1.
2.
在电脑上新建 test2.txt 文件 内容:test2 file is created on PC
然后串口打印读取内容
完毕
祝大家身体健康