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、實驗現象
?