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 ~ 31bitSPI_FLASH_SendByte((SectorAddr & 0xFF000000) >> 24);// 发送擦除地址16 ~ 23bitSPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);// 发送擦除地址8 ~ 15bitSPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);// 发送擦除地址0 ~ 7bitSPI_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]bitSPI_FLASH_SendByte((WriteAddr & 0xFF000000) >> 24);// 发送写入地址[16,23]bitSPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);// 发送写入地址[8,15]bitSPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);// 发送写入地址[0,7]bitSPI_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]bitSPI_FLASH_SendByte((ReadAddr & 0xFF000000) >> 24);// 发送读取地址[16,23]bitSPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);// 发送读取地址[8,15]bitSPI_FLASH_SendByte((ReadAddr & 0xFF00) >> 8);// 发送读取地址[0,7]bitSPI_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、实验现象