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的主要内容,如果未能解决你的问题,请参考以下文章