STM32 使用SPI读写FLASH(W25Q64型号)
文章目錄
- 手冊中的指令
- 讀取Flash ID函數
- 讀取Device ID
- 扇區擦除
- 對Flash寫操作
- 整頁寫
- 不固定數據量
- 讀數據
手冊中的指令
帶括號的字節參數,方向為 FLASH 向主機傳輸,即命令響應。
不帶括號的則為主機向 FLASH 傳輸;
“A0~A23” 指 FLASH 芯片內部存儲器組織的地址;
“M0~M7” 為廠商號( MANUFACTURER ID); “ID0-ID15”為 FLASH 芯片的ID;
“dummy”指該處可為任意數據;
“D0~D7” 為 FLASH 內部存儲矩陣的內容
固定的廠商編號(M7-M0)和不同類型 FLASH 芯片獨有的編號(ID15-ID0) 手冊中提供的值如下:
0x9F緊跟指令編碼的三個字節“(M7-M0)”、“(ID15-ID8)”及“(ID7-ID0)” 時序圖如下:
主機首先通過 MOSI 線向 FLASH 芯片發送第一個字節數據為“9F h” ,當 FLASH 芯片收到該數據后,它會解讀成主機向它發送了“JEDEC 指令”,然后它就作出該命令的響應: 通過 MISO 線把它的廠商 ID(M7-M0)及芯片類型(ID15-0)發送給主機,主機接收到指令響應后可進行校驗。 常見的作用是主機端通過讀取設備 ID 來測試硬件是否連接正常,或用于識別設備
SPI使用的主要讀寫函數,發送一個字節的數據
u8 SPI_FLASH_SendByte(u8 byte) {SPITimeout = SPIT_FLAG_TIMEOUT;//等待發送緩沖區為空,TXEwhile (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);}//把要寫入的數據寫到發送緩沖區SPI_I2S_SendData(FLASH_SPIx , byte);//設置超時時間SPITimeout = SPIT_FLAG_TIMEOUT;//等待接收緩沖區非空 RXNEwhile (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET){if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);}//從緩沖區中讀取數據return SPI_I2S_ReceiveData(FLASH_SPIx ); }讀取Flash ID函數
發送命令碼后需要讀三個字節
函數實現如下:用于驗證讀出的是否是0XEF4017(手冊中)
讀取Device ID
中間有三個dumpy 在變成的時候也需要空出來
函數實現如下
只要向 FLASH 芯片發送了讀狀態寄存器的指令, FLASH 芯片就會持續向主機返回最新的狀態寄存器內容,直到收到 SPI 通訊的停止信號。
//寫使能命令 void SPI_FLASH_WriteEnable(void) {SPI_FLASH_CS_LOW();SPI_FLASH_SendByte(0x06);SPI_FLASH_CS_HIGH(); } #define WIP_Flag 0x01 //寫等待停止信號 void SPI_FLASH_WaitForWriteEnd(void) {u8 FLASH_Status = 0;SPI_FLASH_CS_LOW();SPI_FLASH_SendByte(0x05);//ReadStatusReg do{FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte); }while ((FLASH_Status & WIP_Flag) == SET);SPI_FLASH_CS_HIGH(); }扇區擦除
FLASH 存儲器的特性決定了它只能把原來為“ 1”的數據位改寫成“0”,而原來為“0”的數據位不能直接改寫為“1”。所以這里涉及到數據“擦除”的概念,在寫入前,必須要對目標存儲矩陣進行擦除操作,把矩陣中的數據位擦除為“1”,在數據寫入的
時候,如果要存儲數據“1”,那就不修改存儲矩陣 ,在要存儲數據“0”時,才更改該位。
FLASH 芯片的最小擦除單位為扇區(Sector),而一個塊(Block)包含 16 個扇區。如下所示
刪除4K扇區,程序源碼如下:
//讀寫統一地址 #define FLASH_WriteAddress 0x00000 #define FLASH_ReadAddress FLASH_WriteAddress #define FLASH_SectorToErase FLASH_WriteAddressvoid SPI_FLASH_SectorErase(u32 SectorAddr) {//需要開寫使能SPI_FLASH_WriteEnable();SPI_FLASH_WaitForWriteEnd();SPI_FLASH_CS_LOW();SPI_FLASH_SendByte(0x20);//三字節地址需要分三次發送,先高位字節,再中間,后低SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);SPI_FLASH_SendByte(SectorAddr & 0xFF);SPI_FLASH_CS_HIGH();SPI_FLASH_WaitForWriteEnd(); }對Flash寫操作
整頁寫
void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) {//寫使能SPI_FLASH_WriteEnable();SPI_FLASH_CS_LOW();SPI_FLASH_SendByte(0x02);SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);SPI_FLASH_SendByte(WriteAddr & 0xFF);//錯誤處理if(NumByteToWrite > SPI_FLASH_PerWritePageSize){NumByteToWrite = SPI_FLASH_PerWritePageSize;FLASH_ERROR("SPI_FLASH_PageWrite too large!"); }while (NumByteToWrite--){SPI_FLASH_SendByte(*pBuffer);pBuffer++;}SPI_FLASH_CS_HIGH();SPI_FLASH_WaitForWriteEnd(); }不固定數據量
Flash 數據寫入示意圖
數據寫入流程圖
程序源碼
#define SPI_FLASH_PageSize 256//pBuffer 寫地址指針;WriteAddr寫地址;NumByteToWrite寫長度 //需要判斷WriteAddr寫地址是否在首地址還是中間地址 //NumByteToWrite寫大小,是整頁還是非整頁 void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) {u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;//取模,檢查有幾頁Addr = WriteAddr % SPI_FLASH_PageSize;//差count 個數值,剛好對齊到頁地址count = SPI_FLASH_PageSize - Addr;//整數頁NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;//不滿一頁的字節數NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;//Addr=0 剛好按頁對齊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);}}//地址不對齊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 能寫完NumByteToWrite{SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);}}else /* NumByteToWrite > SPI_FLASH_PageSize */{//地址不對齊多出的count分開處理,重新計算頁數NumByteToWrite -= count;NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;//先寫完count個數據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);}}} }讀數據
//pBuffer讀到的緩沖區; ReadAddr 讀取Flash地址;NumByteToRead 讀數據量 void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead) {SPI_FLASH_CS_LOW();SPI_FLASH_SendByte(0x03);SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);SPI_FLASH_SendByte(ReadAddr & 0xFF);while (NumByteToRead--){*pBuffer = SPI_FLASH_SendByte(Dummy_Byte);pBuffer++;}SPI_FLASH_CS_HIGH(); }參考資料:
W25Q64 datasheet
總結
以上是生活随笔為你收集整理的STM32 使用SPI读写FLASH(W25Q64型号)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android menu菜单 实现点击后
- 下一篇: 硬件知识:USB3.0和USB2.0的区