1、硬件电路
需要系统性的看一下W25Q256芯片手册
2、设置RCC,选择高速外部时钟HSE,时钟设置为180MHz
3、配置SPI
4、生成工程配置
5、相关代码
#define sFLASH_ID 0XEF4019 // W25Q256
#define SPI_FLASH_PageSize 256
#define SPI_FLASH_PerWritePageSize 256
#define W25Q256_WriteEnable 0x06 // 写使能指令
#define W25Q256_WriteDisable 0x04 // 写屏蔽指令
#define W25Q256_ReadStatusReg 0x05 // 读取状态寄存器1
#define W25Q256_WriteStatusReg 0x01 // 写入状态寄存器1
//#define W25Q256_ReadData 0x03 // 3字节模式读取数据指令
#define W25Q256_ReadData 0x13 // 4字节模式读取数据指令
#define W25Q256_FastReadData 0x0B //
#define W25Q256_FastReadDual 0x3B
#define W25Q256_PageProgram 0x02 // 页写入指令
#define W25Q256_BlockErase 0xD8
#define W25Q256_SectorErase 0x20
#define W25Q256_ChipErase 0xC7
#define W25Q256_PowerDown 0xB9
#define W25Q256_ReleasePowerDown 0xAB
#define W25Q256_DeviceID 0xAB
#define W25Q256_ManufactDeviceID 0x90
#define W25Q256_JedecDeviceID 0x9F
#define W25Q256_Enter4ByteMode 0xB7 // 4字节地址模式指令
#define W25Q256_ReadStatusRegister3 0x15
#define W25Q256_WriteStatusRegister3 0x11 //写状态寄存器3指令
#define WIP_Flag 0x01
#define Dummy_Byte 0xFF
#define FLASH_WriteAddress 0x00000
#define FLASH_ReadAddress FLASH_WriteAddress
#define FLASH_SectorToErase FLASH_WriteAddress
/* 获取缓冲区的长度 */
#define TxBufferSize1 (countof(TxBuffer1) - 1)
#define RxBufferSize1 (countof(TxBuffer1) - 1)
#define countof(a) (sizeof(a) / sizeof(*(a)))
#define BufferSize (countof(Tx_Buffer) - 1)
#define FLASH_WriteAddress 0x00000
#define FLASH_ReadAddress FLASH_WriteAddress
#define FLASH_SectorToErase FLASH_WriteAddress
/* 发送缓冲区初始化 */
extern uint8_t ReadBuff[4096];
extern uint8_t WriteBuff[4096];
/**
* @brief 拉低片选线
* @param 无
* @retval 无
*/
void SPI_FLASH_NSS_LOW(void)
{
HAL_GPIO_WritePin(SPI5_NSS_GPIO_Port, SPI5_NSS_Pin, GPIO_PIN_RESET);
}
/**
* @brief 拉高片选线
* @param 无
* @retval 无
*/
void SPI_FLASH_NSS_HIGH(void)
{
HAL_GPIO_WritePin(SPI5_NSS_GPIO_Port, SPI5_NSS_Pin, GPIO_PIN_SET);
}
/**
* @brief 获取 FLASH ID
* @param 无
* @retval FLASH ID
*/
uint32_t SPI_FLASH_ReadID(void)
{
uint32_t temp = 0;
uint32_t temp0 = 0;
uint32_t temp1 = 0;
uint32_t temp2 = 0;
// 拉低片选线,开始通信
SPI_FLASH_NSS_LOW();
// 发送获取W25Q256芯片ID指令
SPI_FLASH_SendByte(W25Q256_JedecDeviceID);
// 接收数据
temp0 = SPI_FLASH_ReadByte();
temp1 = SPI_FLASH_ReadByte();
temp2 = SPI_FLASH_ReadByte();
// 拉高片选线,结束通信
SPI_FLASH_NSS_HIGH();
temp = temp0 << 16 | temp1 << 8 | temp2;
return temp;
}
/**
* @brief 发送一个字节
* @param 无
* @retval 无
*/
void SPI_FLASH_SendByte(uint8_t ch)
{
HAL_SPI_Transmit(&hspi5, &ch, 1, 500);
}
/**
* @brief 发送n个字节
* @param pData:发送数据首地址
* @param data_number:发送数据个数(以字节为单位)
* @retval 无
*/
void SPI_FLASH_SendnByte(uint8_t *pData, uint32_t data_number)
{
HAL_SPI_Transmit(&hspi5, pData, data_number, 500);
}
/**
* @brief 接收一个字节
* @param 无
* @retval 接收的数据
*/
uint8_t SPI_FLASH_ReadByte(void)
{
uint8_t rxData = 0;
HAL_SPI_Receive(&hspi5, &rxData, 1, 500);
return rxData;
}
/**
* @brief 接收n个字节
* @param pData:接收数据首地址
* @param data_number:接收数据个数(以字节为单位)
* @retval 无
*/
void SPI_FLASH_ReadnByte(uint8_t *pData, uint32_t data_number)
{
HAL_SPI_Receive(&hspi5, pData, data_number, 500);
}
/**
* @brief 使能写命令
* @param 无
* @param 无
* @retval 无
*/
void SPI_FLASH_WriteEnable(void)
{
// 拉低片选线,开始通信
SPI_FLASH_NSS_LOW();
// 发送写使能指令
SPI_FLASH_SendByte(W25Q256_WriteEnable);
// 拉高片选线,结束通信
SPI_FLASH_NSS_HIGH();
}
/**
* @brief 等待写入、擦除等操作完成
* @param 无
* @param 无
* @retval 无
*/
void SPI_FLASH_WaitForWriteEnd(void)
{
uint8_t FLASH_Status = 0;
// 拉低片选线,开始通信
SPI_FLASH_NSS_LOW();
// 发送写状态寄存器1指令
SPI_FLASH_SendByte(W25Q256_ReadStatusReg);
do
{
// 获取写状态寄存器1的值并做判断。0:空闲、1:忙碌
FLASH_Status = SPI_FLASH_ReadByte();
} while (SET == (FLASH_Status & WIP_Flag));
// 拉高片选线,结束通信
SPI_FLASH_NSS_HIGH();
}
/**
* @brief 擦除扇区
* @param SectorAddr:擦除扇区首地址
* @retval 无
*/
void SPI_FLASH_SectorErase(uint32_t SectorAddr)
{
// uint8_t ADDR[4] = {0x00,0x00,0x00,0x00};
// 使能写命令
SPI_FLASH_WriteEnable();
// 拉低片选线,开始通信
SPI_FLASH_NSS_LOW();
// 发送擦除扇区命令
SPI_FLASH_SendByte(W25Q256_SectorErase);
// 发送擦除地址24 ~ 31bit
SPI_FLASH_SendByte((SectorAddr & 0xFF000000) >> 24);
// 发送擦除地址16 ~ 23bit
SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
// 发送擦除地址8 ~ 15bit
SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
// 发送擦除地址0 ~ 7bit
SPI_FLASH_SendByte(SectorAddr & 0xFF);
// 拉高片选线,结束通信
SPI_FLASH_NSS_HIGH();
// HAL_Delay(3000);
// 等待擦除操作结束
SPI_FLASH_WaitForOperaEnd();
}
/**
* @brief 配置4字节模式
* @param 无
* @retval 无
*/
void SPI_FLASH_FOUR_MODE(void)
{
uint8_t temp = 0;
// 使能写命令
SPI_FLASH_WriteEnable();
// 拉低片选线,开始通信
SPI_FLASH_NSS_LOW();
// 发送写状态寄存器3命令
SPI_FLASH_SendByte(W25Q256_WriteStatusRegister3);
// 发送要写的数据
SPI_FLASH_SendByte(0x02);
// 拉高片选线,结束通信
SPI_FLASH_NSS_HIGH();
// 拉低片选线,开始通信
SPI_FLASH_NSS_LOW();
// 发送读状态寄存器3命令
SPI_FLASH_SendByte(W25Q256_ReadStatusRegister3);
// 读取数据
temp = SPI_FLASH_ReadByte();
// 拉高片选线,结束通信
SPI_FLASH_NSS_HIGH();
if (1 == (0x02 & temp))
{
// 拉低片选线,开始通信
SPI_FLASH_NSS_LOW();
// 发送配置四字节模式指令
SPI_FLASH_SendByte(W25Q256_Enter4ByteMode);
// 拉高片选线,结束通信
SPI_FLASH_NSS_HIGH();
}
SPI_FLASH_WaitForOperaEnd();
}
/**
* @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)
{
// 使能写命令
SPI_FLASH_WriteEnable();
// 拉低片选线,开始通信
SPI_FLASH_NSS_LOW();
// 发送页写入指令
SPI_FLASH_SendByte(W25Q256_PageProgram);
// 发送写入地址[24,31]bit
SPI_FLASH_SendByte((WriteAddr & 0xFF000000) >> 24);
// 发送写入地址[16,23]bit
SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
// 发送写入地址[8,15]bit
SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
// 发送写入地址[0,7]bit
SPI_FLASH_SendByte(WriteAddr & 0xFF);
if (NumByteToWrite > SPI_FLASH_PerWritePageSize)
{
NumByteToWrite = SPI_FLASH_PerWritePageSize;
printf("256\r\n");
}
for (int i = 0; i < NumByteToWrite; i++)
{
SPI_FLASH_SendByte(pBuffer[i]);
}
// 拉高片选线,结束通信
SPI_FLASH_NSS_HIGH();
}
/**
* @brief FALSH不定量数据写入函数,调用本函数写入数据前需要先擦除扇区
* @param pBuffer:要写入数据的指针
* @param WriteAddr:写入数据地址
* @param NumByteToWrite:写入数据长度
* @retval 无
*/
void SPI_FLASH_BufferWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
uint8_t NumOfPage = 0;
uint8_t NumOfSingle = 0;
uint8_t Addr = 0;
uint8_t count = 0;
uint8_t 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 (0 == Addr)
{
/*NumByteToWrite < SPI_FLASH_PageSize*/
if (0 == NumOfPage)
{
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
}
else
{
/* 先把整数页都写了 */
for (int i = 0; i < NumOfPage; i++)
{
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
WriteAddr += SPI_FLASH_PageSize;
pBuffer += SPI_FLASH_PageSize;
}
/* 若有多余的不满一页的数据,把它写完 */
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
}
}
else /* 若地址与 SPI_FLASH_PageSize 不对齐 */
{
/*NumByteToWrite < SPI_FLASH_PageSize*/
if (0 == NumOfPage)
{
/* 当前页剩余的 count 个位置比 NumOfSingle 小,写不完 */
if (NumOfSingle > count)
{
temp = NumOfSingle - count;
/* 先写满当前页 */
SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
WriteAddr += count;
pBuffer += count;
/* 再写剩余的数据 */
SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
}
else /* 当前页剩余的 count 个位置能写完 NumOfSingle 个数据 */
{
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
}
}
else /*NumByteToWrite > SPI_FLASH_PageSize*/
{
/*先把距离页地址的count个数据减去,计算需要写的页数和NumOfSingle,然后写数据时先把原来减去的count个数据写入,写满当前页*/
/*再写剩余数据,即计算好的NumOfPage和NumOfSingle*/
NumByteToWrite -= count;
NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
WriteAddr += count;
pBuffer += count;
/* 把整数页都写了 */
for (int i = 0; i < NumOfPage; i++)
{
SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
WriteAddr += SPI_FLASH_PageSize;
pBuffer += SPI_FLASH_PageSize;
}
if (0 != NumOfSingle)
{
SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
}
}
}
}
/**
* @brief 读取FLASH数据
* @param pBuffer:存储读出数据的指针
* @param WriteAddr:读取地址
* @param NumByteToRead:读取数据长度
* @retval 无
*/
void SPI_FLASH_BufferRead(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
// 拉低片选线,开始通信
SPI_FLASH_NSS_LOW();
// 发送读取数据指令
SPI_FLASH_SendByte(W25Q256_ReadData);
// 发送读取地址[24,31]bit
SPI_FLASH_SendByte((ReadAddr & 0xFF000000) >> 24);
// 发送读取地址[16,23]bit
SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
// 发送读取地址[8,15]bit
SPI_FLASH_SendByte((ReadAddr & 0xFF00) >> 8);
// 发送读取地址[0,7]bit
SPI_FLASH_SendByte(ReadAddr & 0xFF);
// 读取数据
for (int i = 0; i < NumByteToRead; i++)
{
// 读取一个字节数据
pBuffer[i] = SPI_FLASH_ReadByte();
}
// 拉高片选线,结束通信
SPI_FLASH_NSS_HIGH();
SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
}
/**
* @brief 等待写入、擦除等操作结束
* @param none
* @param none
* @param none
* @retval none
*/
void SPI_FLASH_WaitForOperaEnd(void)
{
uint8_t FLASH_Status = 0;
// 拉低片选线,开始通信
SPI_FLASH_NSS_LOW();
// 发送读状态寄存器1指令
SPI_FLASH_SendByte(W25Q256_ReadStatusReg);
do
{
// 接收读取状态寄存器1寄存器内容
FLASH_Status = SPI_FLASH_ReadByte();
} while (SET == (FLASH_Status & WIP_Flag));
// 拉高片选线,结束通信
SPI_FLASH_NSS_HIGH();
}
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI5_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
// 进入4字节地址模式
SPI_FLASH_FOUR_MODE();
printf("W25Q256 SPI readwrite test!!!\r\n");
device_ID = SPI_FLASH_ReadID();
printf("device_ID = 0x%x\r\n", device_ID);
SPI_FLASH_SectorErase(0x00); // 擦除扇区数据
// 读取擦除后的数据
SPI_FLASH_BufferRead(ReadBuff, 0x00, 4096);
printf("*****************读取擦出后的数据*****************\r\n");
for (int i = 0; i < 4096; i++)
{
printf("ReadBuff[%d] = 0x%02x\t", i, ReadBuff[i]);
if (0 == (i + 1) % 8 && (i + 1) >= 8)
{
printf("\r\n");
}
}
for (int i = 0; i < 256; i++)
{
WriteBuff[i] = i;
}
SPI_FLASH_BufferWrite(WriteBuff, 0xFF, 256);
SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
// 读数据
SPI_FLASH_BufferRead(ReadBuff, 0xFF, 256);
SPI_FLASH_WaitForOperaEnd(); // 等待操作完成
printf("*****************读取写入后的数据*****************\r\n");
for (int i = 0; i < 256; i++)
{
printf("ReadBuff[%d] = 0x%02x\t", i, ReadBuff[i]);
if (0 == (i + 1) % 8 && (i + 1) >= 8)
{
printf("\r\n");
}
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
LED_TIME();
}
/* USER CODE END 3 */
}
6、实验现象