STM32F4 HAL库开发 -- SPI Flash

Posted 聚优致成

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32F4 HAL库开发 -- SPI Flash相关的知识,希望对你有一定的参考价值。

一、驱动

bsp_spi_flash.c

#include "THC_Board_include_h.h"

/* Private define ------------------------------------------------------------*/
#define W25X_WriteEnable                0x06        //写使能
#define W25X_WriteDisable               0x04        //写失能

#define W25X_ReadStatusReg_1            0x05        //读控制和状态寄存器
#define W25X_ReadStatusReg_2            0x35        //读控制和状态寄存器
#define W25X_ReadStatusReg_3            0x15        //读控制和状态寄存器

#define W25X_WriteStatusReg_1             0x01        //写控制和状态寄存器
#define W25X_WriteStatusReg_2             0x31        //写控制和状态寄存器
#define W25X_WriteStatusReg_3             0x11        //写控制和状态寄存器

#define W25X_ReadData                   0x03        //读数据
#define W25X_FastReadData               0x0B        //快速读数据
#define W25X_FastReadDual               0x3B 
#define W25X_PageProgram                0x02        //页编程
#define W25X_BlockErase                 0xD8        //64kB块擦除
#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 WIP_FlagMask                    0x01  /* Write In Progress (WIP) flag */

#define Dummy_Byte                      0xA5            //w25q80

//#define Dummy_Byte                      0xFF             //w25q128
//SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节



/**-----------------------------------------------------------------
  * @函数名 SPI_FLASH_SectorErase
  * @功能   擦除SPI FLASH一个扇区的驱动函数
  *         Erases the specified FLASH sector.
  * @参数   SectorAddr: 扇区地址 address of the sector to erase.
  * @返回值 无
***----------------------------------------------------------------*/
void SPI_FLASH_SectorErase(uint32_t SectorAddr)
{
    /* Send write enable instruction */
    SPI_FLASH_WriteEnable();

    /* Sector Erase */
    /* Select the FLASH: Chip Select low */
    SPI2_FLASH_CS_LOW();
    /* Send Sector Erase instruction */
    SPI_FLASH_SendByte(W25X_SectorErase);
    /* Send SectorAddr high nibble address byte */
    SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
    /* Send SectorAddr medium nibble address byte */
    SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
    /* Send SectorAddr low nibble address byte */
    SPI_FLASH_SendByte(SectorAddr & 0xFF);
    /* Deselect the FLASH: Chip Select high */
    SPI2_FLASH_CS_HIGH();

    /* Wait the end of Flash writing */
    SPI_FLASH_WaitForWriteEnd();
}

/**-----------------------------------------------------------------
  * @函数名 SPI_FLASH_ChipErase
  * @功能   擦除整个SPI FLASH的驱动函数
  *         Erases the entire FLASH.
  * @参数   无
  * @返回值 无
***----------------------------------------------------------------*/
void SPI_FLASH_ChipErase(void)
{
    /* Send write enable instruction */
    SPI_FLASH_WriteEnable();

    /* Bulk Erase */
    /* Select the FLASH: Chip Select low */
    SPI2_FLASH_CS_LOW();
    /* Send Bulk Erase instruction  */
    SPI_FLASH_SendByte(W25X_ChipErase);
    /* Deselect the FLASH: Chip Select high */
    SPI2_FLASH_CS_HIGH();

    /* Wait the end of Flash writing */
    SPI_FLASH_WaitForWriteEnd();
}

/**-----------------------------------------------------------------
  * @函数名 SPI_FLASH_PageWrite
  * @功能   单个写周期写入大于一个字节而小于等于一页大小的数据
  *         Writes more than one byte to the FLASH with a single 
  *         WRITE cycle(Page WRITE sequence). The number of byte 
  *         can't exceed the FLASH page size.
  * @参数   - pBuffer : 指向包含写入数据缓冲器的地址指针
  *             pointer to the buffer  containing the data to be
  *             written to the FLASH.
  *         - WriteAddr : flash的写入地址
  *             FLASH's internal address to write to.
  *         - NumByteToWrite : 写入的字节数
  *             number of bytes to write to the FLASH, must be
  *             equal or less than "SPI_FLASH_PageSize" value.
  * @返回值 无
***----------------------------------------------------------------*/
void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
    /* Enable the write access to the FLASH */
    SPI_FLASH_WriteEnable();

    /* Select the FLASH: Chip Select low */
    SPI2_FLASH_CS_LOW();
    /* Send "Write to Memory " instruction */
    SPI_FLASH_SendByte(W25X_PageProgram);
    /* Send WriteAddr high nibble address byte to write to */
    SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
    /* Send WriteAddr medium nibble address byte to write to */
    SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
    /* Send WriteAddr low nibble address byte to write to */
    SPI_FLASH_SendByte(WriteAddr & 0xFF);

    if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
    {
        NumByteToWrite = SPI_FLASH_PerWritePageSize;
    }

    /* while there is data to be written on the FLASH */
    while (NumByteToWrite--)
    {
        /* Send the current byte */
        SPI_FLASH_SendByte(*pBuffer);
        /* Point on the next byte to be written */
        pBuffer++;
    }

    /* Deselect the FLASH: Chip Select high */
    SPI2_FLASH_CS_HIGH();

    /* Wait the end of Flash writing */
    SPI_FLASH_WaitForWriteEnd();
}

/**-----------------------------------------------------------------
  * @函数名 SPI_FLASH_BufferWrite
  * @功能   向SPI FLASH写入一堆数据,写入的字节数可以大于一页容量
  *         Writes block of data to the FLASH. In this function,
  *         the number of WRITE cycles are reduced,
  *         using Page WRITE sequence.
  * @参数   - pBuffer : 指向包含写入数据缓冲器的地址指针
  *             pointer to the buffer  containing the data to be
  *             written to the FLASH.
  *         - WriteAddr : flash的写入地址
  *             FLASH's internal address to write to.
  *         - NumByteToWrite : 写入的字节数
  *             number of bytes to write to the FLASH.
  * @返回值 无
***----------------------------------------------------------------*/
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) /* WriteAddr is SPI_FLASH_PageSize aligned  */
    {
        if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
        {
            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);
        }
    }
    else /* WriteAddr is not SPI_FLASH_PageSize aligned  */
    {
        if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */
        {
            if (NumOfSingle > count) /* (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize */
            {
                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 > SPI_FLASH_PageSize */
        {
            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);
            }
        }
    }
}

/**-----------------------------------------------------------------
  * @函数名 SPI_FLASH_BufferRead
  * @功能   从SPI FLASH读出一段数据,写入的字节数可以大于一页容量
  *         Reads a block of data from the FLASH.
  * @参数   - pBuffer : 指向包含写入数据缓冲器的地址指针
  *             pointer to the buffer that receives the data read
  *             from the FLASH.
  *         - ReadAddr : flash的读起始地址
  *             FLASH's internal address to read from.
  *         - NumByteToWrite : 读出的字节数
  *             number of bytes to read from the FLASH.
  * @返回值 无
***----------------------------------------------------------------*/
void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
  /* Select the FLASH: Chip Select low */
  SPI2_FLASH_CS_LOW();

  /* Send "Read from Memory " instruction */
  SPI_FLASH_SendByte(W25X_ReadData);

  /* Send ReadAddr high nibble address byte to read from */
  SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  /* Send ReadAddr medium nibble address byte to read from */
  SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  /* Send ReadAddr low nibble address byte to read from */
  SPI_FLASH_SendByte(ReadAddr & 0xFF);

  while (NumByteToRead--) /* while there is data to be read */
  {
    /* Read a byte from the FLASH */
    *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
    /* Point to the next location where the byte read will be saved */
    pBuffer++;
  }

  /* Deselect the FLASH: Chip Select high */
  SPI2_FLASH_CS_HIGH();
}

/**-----------------------------------------------------------------
  * @函数名 SPI_FLASH_ReadID
  * @功能   读取SPI FLASH厂商ID和设备ID(设备ID包含类型和容量)
  *         Reads Manufacturer ID and two Device ID bytes
  * @参数   无
  * @返回值 24bit,高到底依次为厂商ID、类型和容量
***----------------------------------------------------------------*/
uint32_t SPI_FLASH_ReadID(void)
{
    uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

    /* Select the FLASH: Chip Select low */
    SPI2_FLASH_CS_LOW();

    /* Send "RDID " instruction */
    SPI_FLASH_SendByte(W25X_JedecDeviceID);

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

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

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

    /* Deselect the FLASH: Chip Select high */
    SPI2_FLASH_CS_HIGH();

    Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;

    return Temp;
}

/**-----------------------------------------------------------------
  * @函数名 SPI_FLASH_ReadDeviceID
  * @功能   读取SPI FLASH设备ID
  *         Read one Device ID bytes
  * @参数   无
  * @返回值 一个字节的Device ID
***----------------------------------------------------------------*/
uint32_t SPI_FLASH_ReadDeviceID(void)
{
    uint32_t Temp = 0;

    /* Select the FLASH: Chip Select low */
    SPI2_FLASH_CS_LOW();

    /* Send "RDID " instruction */
    SPI_FLASH_SendByte(W25X_DeviceID);
    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 */
    SPI2_FLASH_CS_HIGH();

    return Temp;
}

/**-----------------------------------------------------------------
  * @函数名 SPI_FLASH_StartReadSequence
  * @功能   发起一个读取SPI FLASH的访问,包括发送读命令和起始地址
  *         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.
  *         Read one Device ID bytes
  * @参数   ReadAddr FLASH的访问地址
  *                  FLASH's internal address to read from.
  * @返回值 无
***----------------------------------------------------------------*/
void SPI_FLASH_StartReadSequence(uint32_t ReadAddr)
{
    /* Select the FLASH: Chip Select low */
    SPI2_FLASH_CS_LOW();

    /* Send "Read from Memory " instruction */
    SPI_FLASH_SendByte(W25X_ReadData);

    /* 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);
}


/**-----------------------------------------------------------------
  * @函数名 SPI_FLASH_ReadByte
  * @功能   读取SPI FLASH的一个字节,未包含发送读命令和起始地址
  * @参数   无
  * @返回值 从SPI_FLASH读取的一个字节
***----------------------------------------------------------------*/
uint8_t SPI_FLASH_ReadByte(void)
{
    return (SPI_FLASH_SendByte(Dummy_Byte));
}


//uint8_t SPI2_ReadWriteByte(uint8_t TxData)
//{
//    uint8_t Rxdata;
//    HAL_SPI_TransmitReceive(&hspi2,&TxData,&Rxdata,1, 1000);       
// 	  return Rxdata;          		    //返回收到的数据		
//}

/**-----------------------------------------------------------------
  * @函数名 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;          		    //返回收到的数据		
}

/**-----------------------------------------------------------------
  * @函数名 SPI_FLASH_WriteEnable
  * @功能   SPI FLASH写使能
  *         Enables the write access to the FLASH.
  * @参数   无
  * @返回值 无
***----------------------------------------------------------------*/
void SPI_FLASH_WriteEnable(void)
{
  /* Select the FLASH: Chip Select low */
  SPI2_FLASH_CS_LOW();

  /* Send "Write Enable" instruction */
  SPI_FLASH_SendByte(W25X_WriteEnable);

  /* Deselect the FLASH: Chip Select high */
  SPI2_FLASH_CS_HIGH();
}

/**-----------------------------------------------------------------
  * @函数名 SPI_FLASH_WaitForWriteEnd
  * @功能   通过反复读取SPI FLASH的状态寄存器判断写入是否执行结束
  *         Polls the status of the Write In Progress (WIP) flag in the
  *         FLASH's status  register  and  loop  until write  opertaion
  *         has completed.
  * @参数   无
  * @返回值 无
***----------------------------------------------------------------*/
void SPI_FLASH_WaitForWriteEnd(void)
{
  uint8_t FLASH_Status = 0;

  /* Select the FLASH: Chip Select low */
  SPI2_FLASH_CS_LOW();

  /* Send "Read Status Register" instruction */
  SPI_FLASH_SendByte(W25X_ReadStatusReg_1);

  /* 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 */
    FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);

  }
  while ((FLASH_Status & WIP_FlagMask) == SET); /* Write in progress */

  /* Deselect the FLASH: Chip Select high */
  SPI2_FLASH_CS_HIGH();
}

/**-----------------------------------------------------------------
  * @函数名 SPI_Flash_PowerDown
  * @功能   SPI FLASH进入掉电模式(待机)
  * @参数   无
  * @返回值 无
***----------------------------------------------------------------*/
void SPI_Flash_PowerDown(void)   
{ 
  /* Select the FLASH: Chip Select low */
  SPI2_FLASH_CS_LOW();

  /* Send "Power Down" instruction */
  SPI_FLASH_SendByte(W25X_PowerDown);

  /* Deselect the FLASH: Chip Select high */
  SPI2_FLASH_CS_HIGH();
}  

/**-----------------------------------------------------------------
  * @函数名 SPI_Flash_WAKEUP
  * @功能   唤醒SPI FLASH
  * @参数   无
  * @返回值 无
***----------------------------------------------------------------*/
void SPI_Flash_WAKEUP(void)   
{
  /* Select the FLASH: Chip Select low */
  SPI2_FLASH_CS_LOW();

  /* Send "Power Down" instruction */
  SPI_FLASH_SendByte(W25X_ReleasePowerDown);

  /* Deselect the FLASH: Chip Select high */
  SPI2_FLASH_CS_HIGH();
}

bsp_spi_flash.h


#ifndef __FLASH_W25Q_H
#define __FLASH_W25Q_H	
#include "sys.h"	

#define  W25Q180_FLASH_ID            0xEF4018

#define SPI2_FLASH_CS_LOW()             HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, GPIO_PIN_RESET)
#define SPI2_FLASH_CS_HIGH()            HAL_GPIO_WritePin(SPI2_CS_GPIO_Port, SPI2_CS_Pin, GPIO_PIN_SET)
 
#define  W25Q80_FLASH_ID            0xEF4014

/* Private typedef -----------------------------------------------------------*/
#define SPI_FLASH_PageSize              256
#define SPI_FLASH_PerWritePageSize      256
#define	SPI_FLASH_SectorSize	(4096)			// 扇区大小(可擦除的最小单元)0x1000
#define	SPI_FLASH_BlockSize		(65536)			// 块大小0x10000

 
/*----- High layer function -----*/
void SPI_FLASH_Init(void);
void SPI_FLASH_SectorErase(uint32_t SectorAddr);
void SPI_FLASH_ChipErase(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);

/*----- Low layer function -----*/
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_WaitForWriteEnd(void);
void SPI_Flash_Read_Send(void);

extern uint32_t FLASH_WriteAddress;             //写地址
extern uint32_t FLASH_SectorToErase;            //擦除地址
extern uint32_t FLASH_ReadAddress;              //读地址
#endif  

二、实例

static u8 TestBuf[16] = {0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A};
#define	SystemSettings1_Time_Start									(0x000000)	

u32 THC_SPI_FLASH_TEST(void)
{
	u8 HexBuf[20] = { 0  };
	u8 i = 0, len = sizeof(TestBuf);
	u32 id = SPI_FLASH_ReadID();

	SPI_FLASH_SectorErase(SPI_FLASH_SectorSize);
	SPI_FLASH_BufferWrite(TestBuf, SystemSettings1_Time_Start, len);
	SPI_FLASH_BufferRead(HexBuf, SystemSettings1_Time_Start, len);
	for(i = 0; i < len; i++)
	{
		if(HexBuf[i] != TestBuf[i]) {
			break;
		}
	}

	if(i < len) {
		id = 0xFFFFFFFF;
	}
	SPI_FLASH_SectorErase(SPI_FLASH_SectorSize);
	return id;
}

三、问题

需要注意:
1、每次写入之前都要擦除,只能整个扇区的擦除。
2、第一次读取失败,网上的办法都试了还是不行。处理方法是初始化的时候读取ID。

以上是关于STM32F4 HAL库开发 -- SPI Flash的主要内容,如果未能解决你的问题,请参考以下文章

STM32F4 HAL库开发 -- SPI Flash

STM32F4 HAL库开发 -- SPI Flash

[STM32F4裸机]STM32F4HAL库开发

STM32F4 HAL库开发 -- 再识

STM32F4 HAL库开发 -- DMA

STM32F4 HAL SPI_Receive dma 只接收一次