代码借鉴自野火骄阳(STM32F407)电机开发板例程中的“14-SPI—读写串行FLASH(W25Q128)”。有以下区别:

1. 芯片由STM32F407换为了STM32F405;

2. SPI Flash由W25Q128换为了W25Q64;

3. 通讯由SPI1改为了SPI2;

4. 模式由Mode 3改为了Mode 0。

相关代码如下:

spi.c

/**
  ******************************************************************************
  * File Name          : SPI.c
  * Description        : This file provides code for the configuration
  *                      of the SPI instances.
  ******************************************************************************
  * @attention
  *
  * <h2><center>© Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "spi.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

SPI_HandleTypeDef hspi2;

/* SPI2 init function */
void MX_SPI2_Init(void)
{

  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }

}

void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(spiHandle->Instance==SPI2)
  {
  /* USER CODE BEGIN SPI2_MspInit 0 */

  /* USER CODE END SPI2_MspInit 0 */
    /* SPI2 clock enable */
    __HAL_RCC_SPI2_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**SPI2 GPIO Configuration
    PB13     ------> SPI2_SCK
    PB14     ------> SPI2_MISO
    PB15     ------> SPI2_MOSI
    */
    GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* USER CODE BEGIN SPI2_MspInit 1 */

  /* USER CODE END SPI2_MspInit 1 */
  }
}

void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{

  if(spiHandle->Instance==SPI2)
  {
  /* USER CODE BEGIN SPI2_MspDeInit 0 */

  /* USER CODE END SPI2_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_SPI2_CLK_DISABLE();

    /**SPI2 GPIO Configuration
    PB13     ------> SPI2_SCK
    PB14     ------> SPI2_MISO
    PB15     ------> SPI2_MOSI
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15);

  /* USER CODE BEGIN SPI2_MspDeInit 1 */

  /* USER CODE END SPI2_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

spi.h

/**
  ******************************************************************************
  * File Name          : SPI.h
  * Description        : This file provides code for the configuration
  *                      of the SPI instances.
  ******************************************************************************
  * @attention
  *
  * <h2><center>© Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __spi_H
#define __spi_H
#ifdef __cplusplus
 extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

extern SPI_HandleTypeDef hspi2;

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_SPI2_Init(void);

/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif
#endif /*__ spi_H */

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

bsp_spi_flash.c

#include "bsp_spi_flash.h"


static __IO uint32_t  SPITimeout = SPIT_LONG_TIMEOUT;   

static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);


void SPI_FLASH_Init(void)
{
	__HAL_SPI_ENABLE(&hspi2);
}

/*
 * @brief  擦除FLASH扇区
 * @param  SectorAddr:要擦除的扇区地址
 * @retval 无
 */
void SPI_FLASH_SectorErase(uint32_t SectorAddr)
{
	//发送FLASH写使能命令
	SPI_FLASH_WriteEnable();
	SPI_FLASH_WaitForWriteEnd();

	//擦除扇区
	//选择FLASH: CS低电平
	SPI_FLASH_CS_LOW();

	//发送扇区擦除指令
	SPI_FLASH_SendByte(W25X_SECTOR_ERASE);
	//发送擦除扇区地址的高位
	SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
	//发送擦除扇区地址的中位
	SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
	//发送擦除扇区地址的低位
	SPI_FLASH_SendByte(SectorAddr & 0xFF);

	//停止信号 FLASH: CS 高电平
	SPI_FLASH_CS_HIGH();

	//等待擦除完毕
	SPI_FLASH_WaitForWriteEnd();
}

/*
 * @brief  擦除FLASH扇区,整片擦除
 * @param  无
 * @retval 无
 */
void SPI_FLASH_BulkErase(void)
{
	//发送FLASH写使能命令
	SPI_FLASH_WriteEnable();

	//整块Erase
	//选择FLASH: CS低电平
	SPI_FLASH_CS_LOW();

	//发送整块擦除指令
	SPI_FLASH_SendByte(W25X_CHIP_ERASE);

	//停止信号 FLASH: CS 高电平
	SPI_FLASH_CS_HIGH();

	//等待擦除完毕
	SPI_FLASH_WaitForWriteEnd();
}

/*
 * @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)
{
	//发送FLASH写使能命令
	SPI_FLASH_WriteEnable();

	//选择FLASH: CS低电平
	SPI_FLASH_CS_LOW();

	//写页写指令
	SPI_FLASH_SendByte(W25X_PAGE_PROGRAM); //#define W25X_PAGE_PROGRAM	0x02
	//发送写地址的高位
	SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
	//发送写地址的中位
	SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
	//发送写地址的低位
	SPI_FLASH_SendByte(WriteAddr & 0xFF);

	if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
	{
		NumByteToWrite = SPI_FLASH_PerWritePageSize;
		FLASH_ERROR("SPI_FLASH_PageWrite too large!");
	}

	//写入数据
	while(NumByteToWrite--)
	{
		//发送当前要写入的字节数据
		//SPI_FLASH_SendByte(*pBuffer);
		//指向下一字节数据
		//pBuffer++;
		SPI_FLASH_SendByte(*pBuffer++);
	}

	//停止信号 FLASH: CS 高电平
	SPI_FLASH_CS_HIGH();

	//等待写入完毕
	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;

	//mod运算求余,若writeAddr是SPI_FLASH_PageSize整数倍,运算结果Addr值为0
	Addr = WriteAddr % SPI_FLASH_PageSize;

	//差count个数据值,刚好可以对齐到页地址
	count = SPI_FLASH_PageSize - Addr;
	//计算出要写多少整数页
	NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
	//mod运算求余,计算出剩余不满一页的字节数
	NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

	//Addr=0,则WriteAddr 刚好按页对齐 aligned
	if(Addr == 0) 
	{
		//NumByteToWrite < SPI_FLASH_PageSize
		if(NumOfPage == 0)
			SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
		else //NumByteToWrite > SPI_FLASH_PageSize
		{
			//先把整数页都写了
			while(NumOfPage--)
			{
				SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
				WriteAddr += SPI_FLASH_PageSize;
				pBuffer += SPI_FLASH_PageSize;
			}
			//若有多余的不满一页的数据,把它写完
			SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
		}
	}
	//若地址与 SPI_FLASH_PageSize 不对齐
	else 
	{
		//NumByteToWrite < SPI_FLASH_PageSize
		if(NumOfPage == 0)
		{
			//当前页剩余的count个位置比NumOfSingle小,写不完
			if(NumOfSingle > count)
			{
				temp = NumOfSingle - count;
				//先写满当前页
				SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
				WriteAddr += count;
				pBuffer += count;
				//再写剩余的数据
				SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
			}
			//当前页剩余的count个位置能写完NumOfSingle个数据
			else				
				SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
		}
		//NumByteToWrite > SPI_FLASH_PageSize
		else
		{
			//地址不对齐多出的count分开处理,不加入这个运算
			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);
		}
	}
}

/*
 * @brief  读取FLASH ID
 * @param 	无
 * @retval FLASH ID
 */
uint32_t SPI_FLASH_ReadID(void)
{
	uint32_t temp = 0, temp0 = 0, temp1 = 0, temp2 = 0;

	//开始通讯:CS低电平
	SPI_FLASH_CS_LOW();

	//发送JEDEC指令,读取ID
	SPI_FLASH_SendByte(W25X_JEDEC_DEVICE_ID); //#define W25X_JEDEC_DEVICE_ID	0x9F

	//读取一个字节数据
	temp0 = SPI_FLASH_SendByte(Dummy_Byte);

	//读取一个字节数据
	temp1 = SPI_FLASH_SendByte(Dummy_Byte);

	//读取一个字节数据
	temp2 = SPI_FLASH_SendByte(Dummy_Byte);

	//停止通讯:CS高电平
	SPI_FLASH_CS_HIGH();

	//把数据组合起来,作为函数的返回值
	temp = (temp0 << 16) | (temp1 << 8) | temp2;

	return temp;
}

/*
 * @brief  读取FLASH Device ID
 * @param 	无
 * @retval FLASH Device ID
 */
uint32_t SPI_FLASH_ReadDeviceID(void)
{
	uint32_t temp = 0;

	//Select the FLASH: Chip Select low
	SPI_FLASH_CS_LOW();
	//HAL_Delay(100);

	//Send "RDID " instruction
	SPI_FLASH_SendByte(W25X_DEVICE_ID); //#define W25X_DEVICE_ID	0xAB
	SPI_FLASH_SendByte(Dummy_Byte);
	SPI_FLASH_SendByte(Dummy_Byte);
	SPI_FLASH_SendByte(Dummy_Byte);

	//Read a byte from the FLASH
	temp = SPI_FLASH_SendByte(Dummy_Byte);

	//Deselect the FLASH: Chip Select high
	SPI_FLASH_CS_HIGH();

	return temp;
}

/*
 * @brief  读取FLASH数据
 * @param 	pBuffer,存储读出数据的指针
 * @param   ReadAddr,读取地址
 * @param   NumByteToRead,读取数据长度
 * @retval 无
 */
void SPI_FLASH_BufferRead(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
	//选择FLASH: CS低电平
	SPI_FLASH_CS_LOW();

	//发送 读 指令
	SPI_FLASH_SendByte(W25X_READ_DATA);

	//发送 读 地址高位
	SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
	//发送 读 地址中位
	SPI_FLASH_SendByte((ReadAddr & 0xFF00) >> 8);
	//发送 读 地址低位
	SPI_FLASH_SendByte(ReadAddr & 0xFF);

	//读取数据
	while(NumByteToRead--)
	{
		//读取一个字节
		//*pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
		//指向下一个字节缓冲区
		//pBuffer++;
		*pBuffer++ = SPI_FLASH_SendByte(Dummy_Byte);
	}

	//停止信号 FLASH: CS 高电平
	SPI_FLASH_CS_HIGH();
}

/*******************************************************************************
* Function Name  : SPI_FLASH_StartReadSequence
* Description    : 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.
* Input          : - ReadAddr : FLASH's internal address to read from.
* Output         : None
* Return         : None
*******************************************************************************/
void SPI_FLASH_StartReadSequence(uint32_t ReadAddr)
{
	//Select the FLASH: Chip Select low
	SPI_FLASH_CS_LOW();

	//Send "Read from Memory " instruction
	SPI_FLASH_SendByte(W25X_READ_DATA);

	//Send the 24-bit address of the address to read from
	//Send ReadAddr high nibble address byte
	SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
	//Send ReadAddr medium nibble address byte
	SPI_FLASH_SendByte((ReadAddr & 0xFF00) >> 8);
	//Send ReadAddr low nibble address byte
	SPI_FLASH_SendByte(ReadAddr & 0xFF);
}

/*
 * @brief  使用SPI读取一个字节的数据
 * @param  无
 * @retval 返回接收到的数据
 */
uint8_t SPI_FLASH_ReadByte(void)
{
	return (SPI_FLASH_SendByte(Dummy_Byte));
}

/**-----------------------------------------------------------------
  * @函数名 SPI_FLASH_SendByte
  * @功能   通过SPI总线发送一个字节数据(顺便接收一个字节数据)
  *         Sends a byte through the SPI interface and return the byte
  *         received from the SPI bus.
  * @参数   要写入的一个字节数据
  * @返回值 在发数据时,MISO信号线上接收的一个字节
***----------------------------------------------------------------*/
uint8_t SPI_FLASH_SendByte(uint8_t TxData)
{
	uint8_t Rxdata;

	HAL_SPI_TransmitReceive(&hspi2, &TxData, &Rxdata, 1, 1000);

	return Rxdata;
}

/*
 * @brief  使用SPI发送一个字节的数据
 * @param  byte:要发送的数据
 * @retval 返回接收到的数据
 */
/*uint8_t SPI_FLASH_SendByte(uint8_t byte)
{
	SPITimeout = SPIT_FLAG_TIMEOUT;

	//等待发送缓冲区为空,TXE事件
	while (__HAL_SPI_GET_FLAG( &hspi2, SPI_FLAG_TXE ) == RESET)
	{
		if((SPITimeout--) == 0)
			return SPI_TIMEOUT_UserCallback(0);
	}

	//写入数据寄存器,把要写入的数据写入发送缓冲区
	WRITE_REG(hspi2.Instance->DR, byte);

	SPITimeout = SPIT_FLAG_TIMEOUT;

	//等待接收缓冲区非空,RXNE事件
	while (__HAL_SPI_GET_FLAG( &hspi2, SPI_FLAG_RXNE ) == RESET)
	{
		if((SPITimeout--) == 0)
			return SPI_TIMEOUT_UserCallback(1);
	}

	//读取数据寄存器,获取接收缓冲区数据
	return READ_REG(hspi2.Instance->DR);
}*/

/*******************************************************************************
* Function Name  : SPI_FLASH_SendHalfWord
* Description    : Sends a Half Word through the SPI interface and return the
*                  Half Word received from the SPI bus.
* Input          : Half Word : Half Word to send.
* Output         : None
* Return         : The value of the received Half Word.
*******************************************************************************/
uint16_t SPI_FLASH_SendHalfWord(uint16_t HalfWord)
{
	SPITimeout = SPIT_FLAG_TIMEOUT;

	//Loop while DR register in not emplty
	while (__HAL_SPI_GET_FLAG( &hspi2,  SPI_FLAG_TXE ) == RESET)
	{
		if((SPITimeout--) == 0)
			return SPI_TIMEOUT_UserCallback(2);
	}

	//Send Half Word through the SPIx peripheral
	WRITE_REG(hspi2.Instance->DR, HalfWord);

	SPITimeout = SPIT_FLAG_TIMEOUT;

	//Wait to receive a Half Word
	while (__HAL_SPI_GET_FLAG( &hspi2, SPI_FLAG_RXNE ) == RESET)
	{
		if((SPITimeout--) == 0)
			return SPI_TIMEOUT_UserCallback(3);
	}

	//Return the Half Word read from the SPI bus
	return READ_REG(hspi2.Instance->DR);
}

/*
 * @brief  向FLASH发送 写使能 命令
 * @param  none
 * @retval none
 */
void SPI_FLASH_WriteEnable(void)
{
	//通讯开始:CS低
	SPI_FLASH_CS_LOW();

	//发送写使能命令
	SPI_FLASH_SendByte(W25X_WRITE_ENABLE);

	//通讯结束:CS高
	SPI_FLASH_CS_HIGH();
}

/*
 * @brief  向FLASH发送 写禁止 命令
 * @param  none
 * @retval none
 */
void SPI_FLASH_WriteDisable(void)
{
	//通讯开始:CS低
	SPI_FLASH_CS_LOW();

	//发送写禁止命令
	SPI_FLASH_SendByte(W25X_WRITE_DISABLE);

	//通讯结束:CS高
	SPI_FLASH_CS_HIGH();
}

/*
 * @brief  等待WIP(BUSY)标志被置0,即等待到FLASH内部数据写入完毕
 * @param  none
 * @retval none
 */
void SPI_FLASH_WaitForWriteEnd(void)
{
	uint8_t flash_Status = 0;

	//选择 FLASH: CS 低
	SPI_FLASH_CS_LOW();

	//发送 读状态寄存器 命令
	SPI_FLASH_SendByte(W25X_READ_STATUS_REG);

	SPITimeout = SPIT_FLAG_TIMEOUT;

	//若FLASH忙碌,则等待
	do
	{
		//读取FLASH芯片的状态寄存器
		flash_Status = SPI_FLASH_SendByte(Dummy_Byte);

		if((SPITimeout--) == 0)
		{
			SPI_TIMEOUT_UserCallback(4);
			return;
		}
	}
	while((flash_Status & WIP_Flag) == SET); //正在写入标志

	//停止信号  FLASH: CS 高
	SPI_FLASH_CS_HIGH();
}

//进入掉电模式
void SPI_Flash_PowerDown(void)
{
	//选择 FLASH: CS 低
	SPI_FLASH_CS_LOW();

	//发送 掉电 命令
	SPI_FLASH_SendByte(W25X_POWER_DOWN);

	//停止信号  FLASH: CS 高
	SPI_FLASH_CS_HIGH();
}

//唤醒
void SPI_Flash_WAKEUP(void)
{
	//选择 FLASH: CS 低
	SPI_FLASH_CS_LOW();

	//发上 上电 命令
	SPI_FLASH_SendByte(W25X_RELEASE_POWER_DOWN);

	//停止信号 FLASH: CS 高
	SPI_FLASH_CS_HIGH(); //等待TRES1
}

/*
 * @brief  等待超时回调函数
 * @param  None.
 * @retval None.
 */
static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
	//等待超时后的处理,输出错误信息
	FLASH_ERROR("SPI 等待超时! errorCode = %d", errorCode);

	return 0;
}

bsp_spi_flash.h

#ifndef __SPI_FLASH_H
#define __SPI_FLASH_H

#ifdef __cplusplus
 extern "C" {
#endif

#include "main.h"
#include "spi.h"
#include "usart.h"

/* Private typedef ----------------------------------------------------------- */
//#define  sFLASH_ID	0xEF3015 //W25X16
//#define  sFLASH_ID	0xEF4015 //W25Q16
#define  sFLASH_ID		0xEF4017 //W25Q64
//#define  sFLASH_ID	0xEF4018 //W25Q128

//#define SPI_FLASH_PageSize            4096
#define SPI_FLASH_PageSize              256
#define SPI_FLASH_PerWritePageSize      256

/* Private define ------------------------------------------------------------*/
/* 命令定义-开头*******************************/
#define W25X_WRITE_ENABLE		0x06
#define W25X_WRITE_DISABLE		0x04
#define W25X_READ_STATUS_REG	0x05
#define W25X_WRITE_STATUS_REG	0x01
#define W25X_READ_DATA			0x03
#define W25X_FAST_READ_DATA		0x0B
#define W25X_FAST_READ_DUAL		0x3B
#define W25X_FAST_READ_QUAD		0x6B
#define W25X_PAGE_PROGRAM		0x02
#define W25X_BLOCK_ERASE		0xD8
#define W25X_SECTOR_ERASE		0x20
#define W25X_CHIP_ERASE			0xC7
#define W25X_POWER_DOWN			0xB9
#define W25X_RELEASE_POWER_DOWN	0xAB
#define W25X_DEVICE_ID			0xAB
#define W25X_MANUFACTURER_ID	0x90 
#define W25X_JEDEC_DEVICE_ID	0x9F 

#define WIP_Flag	0x01 //Write In Progress (WIP) flag
#define Dummy_Byte	0xFF
/* 命令定义-结尾*******************************/

//CS(NSS)引脚
#define FLASH_CS_PIN                     GPIO_PIN_12               
#define FLASH_CS_GPIO_PORT               GPIOB

//设置为高电平
#define SPI_FLASH_CS_HIGH()		HAL_GPIO_WritePin(FLASH_CS_GPIO_PORT, FLASH_CS_PIN, GPIO_PIN_SET);
//输出低电平
#define SPI_FLASH_CS_LOW()		HAL_GPIO_WritePin(FLASH_CS_GPIO_PORT, FLASH_CS_PIN, GPIO_PIN_RESET);

/*SPI接口定义-结尾****************************/

/* 等待超时时间 */
#define SPIT_FLAG_TIMEOUT	((uint32_t)0x1000)
#define SPIT_LONG_TIMEOUT	((uint32_t)(10 * SPIT_FLAG_TIMEOUT))

/*信息输出*/
#define FLASH_DEBUG_ON	1

#define FLASH_INFO(fmt,arg...)		printf("<<-FLASH-INFO->> "fmt"\n",##arg)
#define FLASH_ERROR(fmt,arg...)		printf("<<-FLASH-ERROR->> "fmt"\n",##arg)
#define FLASH_DEBUG(fmt, arg...)          do{\
											if(FLASH_DEBUG_ON)\
											printf("<<-FLASH-DEBUG->> [%d]"fmt"\n", __LINE__, ##arg);\
                                          } while(0)

void SPI_FLASH_Init(void);
void SPI_FLASH_SectorErase(uint32_t SectorAddr);
void SPI_FLASH_BulkErase(void);
void SPI_FLASH_PageWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
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);
uint32_t SPI_FLASH_ReadID(void);
uint32_t SPI_FLASH_ReadDeviceID(void);
void SPI_FLASH_StartReadSequence(uint32_t ReadAddr);
void SPI_Flash_PowerDown(void);
void SPI_Flash_WAKEUP(void);
uint8_t SPI_FLASH_ReadByte(void);
uint8_t SPI_FLASH_SendByte(uint8_t byte);
uint16_t SPI_FLASH_SendHalfWord(uint16_t HalfWord);
void SPI_FLASH_WriteEnable(void);
void SPI_FLASH_WriteDisable(void);
void SPI_FLASH_WaitForWriteEnd(void);

#ifdef __cplusplus
}
#endif
#endif /* __SPI_FLASH_H */

注:如果各操作出现失败的情况,可以调整void SPI_FLASH_WaitForWriteEnd(void)中的SPITimeout,由原来的SPIT_FLAG_TIMEOUT调整为SPIT_LONG_TIMEOUT,即10倍的SPIT_FLAG_TIMEOUT(见bsp_spi_flash.h中相应的宏定义)。