【HM】STM32F407 HAL库 SPI读写FLASH W25Q128
W25Q128芯片简介
查看数据手册W25Q128芯片的SPI总线工作模式0支持(0,0)和3(1,1)
SPI不同工作模式CPOL和CPHA
Mode 0 : Clock Polarity (CPOL) = 0 and, Clock Phase (CPHA) = 0
Mode 1 : CPOL = 0 and, CPHA = 1
Mode 2 : CPOL = 1 and, CPHA = 0
Mode 3 : CPOL = 1 and, CPHA = 1
配置SYS和时钟
https://blog.csdn.net/hmxm6/article/details/136636793
配置SPI
Mode:全双工主机模式
NSS:不使用片选
Prescaler (for Baud Rate):波特率分配因子
CPOL:时钟极性
CPHA:时钟相位
![image-20240315153020384](https://img-blog.csdnimg.cn/direct/49e264ec718640ed83ca644c71cd4d16.png#pic_center)
根据硬件电路配置SPI引脚
![image-20240315153647090](https://img-blog.csdnimg.cn/direct/8b78ed3d3d5849bab85521cfa4be9e7f.png#pic_center)
![image-20240315153629461](https://img-blog.csdnimg.cn/direct/bf553ab1158d41e7b187d3b50f16c9f6.png#pic_center)
![](https://img-blog.csdnimg.cn/direct/3c524b24676e4094b44be0f36d8c096e.png#pic_center)
测试程序
头文件
#ifndef __BSP_SPI_FLASH_H
#define __BSP_SPI_FLASH_H
#include "stm32f4xx.h"
#include "stdio.h"
/**************命令定义-开头*************/
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg 0x05
#define W25X_WriteStatusReg 0x01
#define W25X_ReadData 0x03
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#define W25X_BlockErase 0xD8
#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_Flag 0x01 /* Write In Progress (WIP) flag */
#define Dummy_Byte 0xFF
/**************命令定义-结尾*************/
#define SPI_FLASH_PageSize 256
#define SPI_FLASH_PerWritePageSize 256
// FLASH初始化
void SPI_FLASH_Init(SPI_HandleTypeDef hspi1);
// 读写数据
uint8_t SPI_FLASH_ReadWrite(uint8_t txdata);
// 读取ID
uint16_t SPI_FLASH_ReadManufactDeviceID(void);
uint16_t SPI_FLASH_ReadDeviceID(void);
uint32_t SPI_FLASH_ReadJedecDeviceID(void);
// 写使能
void SPI_FLASH_WriteEnable(void);
// 进入掉电模式
void SPI_Flash_PowerDown(void);
// 唤醒
void SPI_Flash_WAKEUP(void);
// 等待WIP(BUSY)标志被置0,即等待到FLASH内部数据写入完毕
void SPI_FLASH_WaitForWriteEnd(void);
// 对FLASH写入数据,调用本函数写入数据前需要先擦除扇区
void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
// 对FLASH按页写入数据,调用本函数写入数据前需要先擦除扇区
void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
// 读取FLASH数据
void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);
// 擦除FLASH扇区
void SPI_FLASH_SectorErase(uint32_t SectorAddr);
// 擦除FLASH扇区,整片擦除
void SPI_FLASH_BulkErase(void);
// 片选拉低
void SPI_FLASH_CS_LOW(void);
// 片选拉高
void SPI_FLASH_CS_HIGH(void);
#endif
片选信号函数&初始化函数
#include "bsp_spi_flash.h"
SPI_HandleTypeDef spiFlashHandle;
void SPI_FLASH_Init(SPI_HandleTypeDef hspi1)
{
spiFlashHandle = hspi1;
SPI_FLASH_CS_HIGH();
}
void SPI_FLASH_CS_LOW(void)
{
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_RESET);
}
void SPI_FLASH_CS_HIGH(void)
{
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_SET);
}
SPI最底层读写函数
uint8_t SPI_FLASH_ReadWrite(uint8_t txdata)
{
uint8_t rxdata;
HAL_SPI_TransmitReceive(&spiFlashHandle, &txdata, &rxdata, 1, 1000);
return rxdata; /* 返回收到的数据 */
}
读取芯片各种ID函数
uint16_t SPI_FLASH_ReadManufactDeviceID(void)
{
uint16_t deviceid;
SPI_FLASH_CS_LOW();
SPI_FLASH_ReadWrite(W25X_ManufactDeviceID); /* 发送读 ID 命令 */
SPI_FLASH_ReadWrite(0); /* 写入一个字节 */
SPI_FLASH_ReadWrite(0);
SPI_FLASH_ReadWrite(0);
deviceid = SPI_FLASH_ReadWrite(0xFF) << 8; /* 读取高8位字节 */
deviceid |= SPI_FLASH_ReadWrite(0xFF); /* 读取低8位字节 */
SPI_FLASH_CS_HIGH();
return deviceid;
}
uint16_t SPI_FLASH_ReadDeviceID(void)
{
uint32_t Temp = 0;
SPI_FLASH_CS_LOW();
SPI_FLASH_ReadWrite(W25X_DeviceID);
SPI_FLASH_ReadWrite(0);
SPI_FLASH_ReadWrite(0);
SPI_FLASH_ReadWrite(0);
Temp = SPI_FLASH_ReadWrite(0);
SPI_FLASH_CS_HIGH();
return Temp;
}
uint32_t SPI_FLASH_ReadJedecDeviceID(void)
{
uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
SPI_FLASH_CS_LOW();
SPI_FLASH_ReadWrite(W25X_JedecDeviceID);
Temp0 = SPI_FLASH_ReadWrite(0);
Temp1 = SPI_FLASH_ReadWrite(0);
Temp2 = SPI_FLASH_ReadWrite(0);
Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
return Temp;
}
写使能&掉电&唤醒
void SPI_FLASH_WriteEnable(void)
{
SPI_FLASH_CS_LOW();
/* 发送写使能命令*/
SPI_FLASH_ReadWrite(W25X_WriteEnable);
SPI_FLASH_CS_HIGH();
}
void SPI_Flash_PowerDown(void)
{
SPI_FLASH_CS_LOW();
/* 发送 掉电 命令 */
SPI_FLASH_ReadWrite(W25X_PowerDown);
SPI_FLASH_CS_HIGH();
}
void SPI_Flash_WAKEUP(void)
{
SPI_FLASH_CS_LOW();
/* 发上 上电 命令 */
SPI_FLASH_ReadWrite(W25X_ReleasePowerDown);
SPI_FLASH_CS_HIGH();
}
Flash擦除函数
void SPI_FLASH_SectorErase(uint32_t SectorAddr)
{
/* 发送FLASH写使能命令 */
SPI_FLASH_WriteEnable();
SPI_FLASH_WaitForWriteEnd();
SPI_FLASH_CS_LOW();
/* 发送扇区擦除指令*/
SPI_FLASH_ReadWrite(W25X_SectorErase);
/*发送擦除扇区地址的高位*/
SPI_FLASH_ReadWrite((SectorAddr & 0xFF0000) >> 16);
/* 发送擦除扇区地址的中位 */
SPI_FLASH_ReadWrite((SectorAddr & 0xFF00) >> 8);
/* 发送擦除扇区地址的低位 */
SPI_FLASH_ReadWrite(SectorAddr & 0xFF);
SPI_FLASH_CS_HIGH();
/* 等待擦除完毕*/
SPI_FLASH_WaitForWriteEnd();
}
void SPI_FLASH_BulkErase(void)
{
/* 发送FLASH写使能命令 */
SPI_FLASH_WriteEnable();
SPI_FLASH_CS_LOW();
/* 发送整块擦除指令*/
SPI_FLASH_ReadWrite(W25X_ChipErase);
SPI_FLASH_CS_HIGH();
/* 等待擦除完毕*/
SPI_FLASH_WaitForWriteEnd();
}
等待到FLASH内部数据写入完毕
void SPI_FLASH_WaitForWriteEnd(void)
{
uint8_t FLASH_Status = 0;
SPI_FLASH_CS_LOW();
/* 发送 读状态寄存器 命令 */
SPI_FLASH_ReadWrite(W25X_ReadStatusReg);
int SPITimeout = 1000;
/* 若FLASH忙碌,则等待 */
do
{
/* 读取FLASH芯片的状态寄存器 */
FLASH_Status = SPI_FLASH_ReadWrite(Dummy_Byte);
{
if((SPITimeout--) == 0)
{
//SPI_TIMEOUT_UserCallback(4);
return;
}
}
}
while ((FLASH_Status & WIP_Flag) == SET); /* 正在写入标志 */
SPI_FLASH_CS_HIGH();
}
按页写数据
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_ReadWrite(W25X_PageProgram);
/*发送写地址的高位*/
SPI_FLASH_ReadWrite((WriteAddr & 0xFF0000) >> 16);
/*发送写地址的中位*/
SPI_FLASH_ReadWrite((WriteAddr & 0xFF00) >> 8);
/*发送写地址的低位*/
SPI_FLASH_ReadWrite(WriteAddr & 0xFF);
if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
{
NumByteToWrite = SPI_FLASH_PerWritePageSize;
printf("SPI:SPI_FLASH_PageWrite too large!\r\n");
}
/* 写入数据*/
while (NumByteToWrite--)
{
/* 发送当前要写入的字节数据 */
SPI_FLASH_ReadWrite(*pBuffer);
/* 指向下一字节数据 */
pBuffer++;
}
/* 停止信号 FLASH: CS 高电平 */
SPI_FLASH_CS_HIGH();
/* 等待写入完毕*/
SPI_FLASH_WaitForWriteEnd();
}
写数据
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);
}
else /*当前页剩余的count个位置能写完NumOfSingle个数据*/
{
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
}
}
else /* NumByteToWrite > SPI_FLASH_PageSize */
{
/*地址不对齐多出的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);
}
}
}
}
读取数据
void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
SPI_FLASH_CS_LOW();
/* 发送 读 指令 */
SPI_FLASH_ReadWrite(W25X_ReadData);
/* 发送 读 地址高位 */
SPI_FLASH_ReadWrite((ReadAddr & 0xFF0000) >> 16);
/* 发送 读 地址中位 */
SPI_FLASH_ReadWrite((ReadAddr& 0xFF00) >> 8);
/* 发送 读 地址低位 */
SPI_FLASH_ReadWrite(ReadAddr & 0xFF);
/* 读取数据 */
while (NumByteToRead--)
{
/* 读取一个字节*/
*pBuffer = SPI_FLASH_ReadWrite(Dummy_Byte);
/* 指向下一个字节缓冲区 */
pBuffer++;
}
SPI_FLASH_CS_HIGH();
}