日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

STM32 使用SPI读写FLASH(W25Q64型号)

發布時間:2023/12/10 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32 使用SPI读写FLASH(W25Q64型号) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 手冊中的指令
    • 讀取Flash ID函數
    • 讀取Device ID
    • 扇區擦除
    • 對Flash寫操作
      • 整頁寫
      • 不固定數據量
    • 讀數據

手冊中的指令

  • 第一列代表指令名(寫使能,讀狀態,寫狀態,擦除)
  • 第二列代表指令碼,程序中使用此進行操作
  • 第三列至N列,
    帶括號的字節參數,方向為 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(手冊中)

    u32 SPI_FLASH_ReadID(void) {u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;//CS拉低,產生起始信號SPI_FLASH_CS_LOW();//發送0x9FSPI_FLASH_SendByte(0X9F);//讀取一個字節Temp0 = SPI_FLASH_SendByte(Dummy_Byte);//讀取一個字節Temp1 = SPI_FLASH_SendByte(Dummy_Byte);//讀取一個字節Temp2 = SPI_FLASH_SendByte(Dummy_Byte);//CS拉高,停止通訊SPI_FLASH_CS_HIGH();//計算出返回值Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;return Temp; }

    讀取Device ID

    中間有三個dumpy 在變成的時候也需要空出來

    函數實現如下

    u32 SPI_FLASH_ReadDeviceID(void) {u32 Temp = 0;SPI_FLASH_CS_LOW();/* Send "RDID " instruction */SPI_FLASH_SendByte(0xAB);//根據手冊命令需要Dummy_Byte三次SPI_FLASH_SendByte(Dummy_Byte);SPI_FLASH_SendByte(Dummy_Byte);SPI_FLASH_SendByte(Dummy_Byte);//在第四次時讀取IDTemp = SPI_FLASH_SendByte(Dummy_Byte);/* Deselect the FLASH: Chip Select high */SPI_FLASH_CS_HIGH();return Temp; }

    只要向 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型号)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。