使用STM32 HAL库驱动W25Q16 SPI,实现FATFS文件系统和USB虚拟U盘功能

概述

使用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>&copy; 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)
  {
    ;
  }
}

注意事项

  1. STORAGE_BLK_NBR 表示扇区数量
    STORAGE_BLK_SIZ 表示扇区大小
    电脑上的U盘容量跟这两个参数密切相关 容量=扇区数量*扇区大小
    这两个参数大小也要跟 USER_ioctl() 函数中一致
  2. 由于前128个扇区用于flash存取数据,使用再使用过程中,数据存放的地址不要超过0x80000(128*4096),如果超过了,就GG了,如果不想存取数据,所有空间全部用作虚拟U盘,可以将Offsee_U_Pan_addr改成0就行了。
  3. 虚拟U盘第一次上电需要格式化,格式化的大小就是STORAGE_BLK_NBR×STORAGE_BLK_SIZ,我这个不知道出现什么问题空间少了0.25MB,
    理想情况下,格式化时显示1.5MB

结果

1.

2.
在电脑上新建 test2.txt 文件 内容:test2 file is created on PC
然后串口打印读取内容

完毕

祝大家身体健康

作者:嵌入式小白白

物联沃分享整理
物联沃-IOTWORD物联网 » 使用STM32 HAL库驱动W25Q16 SPI,实现FATFS文件系统和USB虚拟U盘功能

发表评论