STM32F1之SPI通信·軟件SPI代碼編寫-CSDN博客
目錄
1.? W25Qxx系列簡介
2.? W25Q64硬件電路
?3.??W25Q64框圖
4.? Flash操作注意事項
5.? 代碼編寫
5.1? 初始化
5.2??W25Q64讀取ID號
5.3??W25Q64寫使能
5.4??W25Q64等待忙
5.5??W25Q64頁編程
5.6??W25Q64扇區擦除(4KB)
5.7??W25Q64讀取數據
1.? W25Qxx系列簡介
????????W25Qxx系列是一種低成本、小型化、使用簡單的非易失性存儲器,常應用于數據存儲、字庫存儲、固件程序存儲等場景。
非易失性存儲器(Non-Volatile Memory, NVM)和易失性存儲器(Volatile Memory)的主要區別在于電源斷電后的數據保存能力:
非易失性存儲器(NVM):
- 數據持久性:即使在斷電的情況下,數據也能保持不丟失。
- 常見類型:閃存(如USB閃存盤、固態硬盤)、硬盤驅動器(HDD)、只讀存儲器(ROM)、磁帶等。
- 應用場景:適用于需要長期保存數據的場合,如計算機硬盤、手機存儲、固態硬盤等。
易失性存儲器(Volatile Memory):
- 數據持久性:在斷電時,存儲在其中的數據會丟失。
- 常見類型:隨機存取存儲器(RAM),如動態隨機存取存儲器(DRAM)和靜態隨機存取存儲器(SRAM)。
- 應用場景:適用于需要高速訪問數據但不需要長期保存的場合,如計算機的主內存(RAM)等。
存儲介質:Nor Flash(閃存)
時鐘頻率: 80MHz ;
? ? ? ? ? ? ? ? ? ?160MHz (Dual SPI)(雙重SPI);
? ? ? ? ? ? ? ? ? ?320MHz (Quad SPI)(四重SPI)。
存儲容量(24位地址):
W25Q40:?? ? ?4Mbit / 512KByte ?? ?
W25Q80:?? ? ?8Mbit / 1MByte ?? ?
W25Q16:?? ? ?16Mbit / 2MByte ?? ?
W25Q32:?? ? ?32Mbit / 4MByte ?? ?
W25Q64:?? ? ?64Mbit / 8MByte ?? ?
W25Q128: ?128Mbit / 16MByte ?? ?
W25Q256: ?256Mbit / 32MByte
2.? W25Q64硬件電路
引腳 | 功能 |
VCC、GND | 電源(2.7V~3.6V) |
CS(SS) | SPI片選 |
CLK(SCK) | SPI時鐘 |
DI(MOSI) | SPI主機輸出從機輸入 |
DO(MISO) | SPI主機輸入從機輸出 |
WP | 寫保護 |
HOLD | 數據保持 |
?3.??W25Q64框圖
4.? Flash操作注意事項
寫入操作時:
寫入操作前,必須先進行寫使能;
每個數據位只能由1改寫為0,不能由0改寫為1;
寫入數據前必須先擦除,擦除后,所有數據位變為1;
擦除必須按最小擦除單元進行 連續寫入多字節時,最多寫入一頁的數據,超過頁尾位置的數據,會回到頁首覆蓋寫入;
寫入操作結束后,芯片進入忙狀態,不響應新的讀寫操作。
讀取操作時:
直接調用讀取時序,無需使能,無需額外操作,沒有頁的限制,讀取操作結束后不會進入忙狀態,但不能在忙狀態時讀取
5.? 代碼編寫
5.1? 初始化
? ? ? ? 首先初始化SPI,可見本文頂部往期回顧鏈接,初始化SPI:
void W25Q64_Init(void)
{MySPI_Init(); //先初始化底層的SPI
}
5.2??W25Q64讀取ID號
? ? ? ? 對于下圖規格書,我們現將其進行宏定義:
#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H#define W25Q64_WRITE_ENABLE 0x06
#define W25Q64_WRITE_DISABLE 0x04
#define W25Q64_READ_STATUS_REGISTER_1 0x05
#define W25Q64_READ_STATUS_REGISTER_2 0x35
#define W25Q64_WRITE_STATUS_REGISTER 0x01
#define W25Q64_PAGE_PROGRAM 0x02
#define W25Q64_QUAD_PAGE_PROGRAM 0x32
#define W25Q64_BLOCK_ERASE_64KB 0xD8
#define W25Q64_BLOCK_ERASE_32KB 0x52
#define W25Q64_SECTOR_ERASE_4KB 0x20
#define W25Q64_CHIP_ERASE 0xC7
#define W25Q64_ERASE_SUSPEND 0x75
#define W25Q64_ERASE_RESUME 0x7A
#define W25Q64_POWER_DOWN 0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE 0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET 0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID 0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID 0x90
#define W25Q64_READ_UNIQUE_ID 0x4B
#define W25Q64_JEDEC_ID 0x9F
#define W25Q64_READ_DATA 0x03
#define W25Q64_FAST_READ 0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT 0x3B
#define W25Q64_FAST_READ_DUAL_IO 0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT 0x6B
#define W25Q64_FAST_READ_QUAD_IO 0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO 0xE3#define W25Q64_DUMMY_BYTE 0xFF#endif
? ? ? ? 找到其規格書,找到其框住的部分,可以看到其獲取ID的指令為9F,并且需要接收到3個字節,我們可以將其流程看做:起始、先交換發送指令9F、隨后連續交換接收3個字節、停止。
對于上面三個字節的解讀:
????????第一個字節是廠商的ID,表示是哪個廠家生產的。
? ? ? ? 第二個字節是設備ID,設備ID高八位,表示存儲器類型。
????????第三個字節也是設備ID,設備ID低八位,表示容量。
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_JEDEC_ID); //交換發送讀取ID的指令*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //交換接收MID,通過輸出參數返回,其中括號內的數據可以為任何數,通常我們使用0xFF,目的是將有用的數據置換過來。*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //交換接收DID高8位,其內數據同上*DID <<= 8; //高8位移到高位*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE); //或上交換接收DID的低8位,通過輸出參數返回,其內數據同上,MySPI_Stop(); //SPI終止
}
????????? 其中,MID 工廠ID,使用輸出參數的形式返回,DID 設備ID,使用輸出參數的形式返回。
5.3??W25Q64寫使能
void W25Q64_WriteEnable(void)
{MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_WRITE_ENABLE); //交換發送寫使能的指令MySPI_Stop(); //SPI終止
}
5.4??W25Q64等待忙
void W25Q64_WaitBusy(void)
{uint32_t Timeout;MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1); //交換發送讀狀態寄存器1的指令Timeout = 100000; //給定超時計數時間while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01) //循環等待忙標志位{Timeout --; //等待時,計數值自減if (Timeout == 0) //自減到0后,等待超時{/*超時的錯誤處理代碼,可以添加到此處*/break; //跳出等待,不等了}}MySPI_Stop(); //SPI終止
}
5.5??W25Q64頁編程
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{uint16_t i;W25Q64_WriteEnable(); //寫使能MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_PAGE_PROGRAM); //交換發送頁編程的指令MySPI_SwapByte(Address >> 16); //交換發送地址23~16位MySPI_SwapByte(Address >> 8); //交換發送地址15~8位MySPI_SwapByte(Address); //交換發送地址7~0位for (i = 0; i < Count; i ++) //循環Count次{MySPI_SwapByte(DataArray[i]); //依次在起始地址后寫入數據}MySPI_Stop(); //SPI終止W25Q64_WaitBusy(); //等待忙
}
5.6??W25Q64扇區擦除(4KB)
void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable(); //寫使能MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB); //交換發送扇區擦除的指令MySPI_SwapByte(Address >> 16); //交換發送地址23~16位MySPI_SwapByte(Address >> 8); //交換發送地址15~8位MySPI_SwapByte(Address); //交換發送地址7~0位MySPI_Stop(); //SPI終止W25Q64_WaitBusy(); //等待忙
}
5.7??W25Q64讀取數據
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{uint32_t i;MySPI_Start(); //SPI起始MySPI_SwapByte(W25Q64_READ_DATA); //交換發送讀取數據的指令MySPI_SwapByte(Address >> 16); //交換發送地址23~16位MySPI_SwapByte(Address >> 8); //交換發送地址15~8位MySPI_SwapByte(Address); //交換發送地址7~0位for (i = 0; i < Count; i ++) //循環Count次{DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //依次在起始地址后讀取數據}MySPI_Stop(); //SPI終止
}