本內容基于江協科技STM32視頻學習之后整理而得。
文章目錄
- 1. SPI(串行外設接口)通信
- 1.1 SPI通信簡介
- 1.2 硬件電路
- 1.3 移位示意圖
- 1.4 SPI時序基本單元
- 1.5 SPI時序
- 1.5.1 發送指令
- 1.5.2 指定地址寫
- 1.5.3 指定地址讀
- 2. W25Q64
- 2.1 W25Q64簡介
- 2.2 硬件電路
- 2.3 W25Q64框圖
- 2.4 Flash操作注意事項
- 2.5 手冊指令集
- 3. SPI外設簡介
- 3.1 SPI框圖
- 3.2 SPI基本結構
- 3.3 主模式全雙工連續傳輸
- 3.4 非連續傳輸
- 3.5 軟件 / 硬件波形對比
- 3.6 庫函數
1. SPI(串行外設接口)通信
1.1 SPI通信簡介
- SPI(Serial Peripheral Interface)是由Motorola公司開發的一種通用數據總線
- 四根通信線:SCK(Serial Clock)串行時鐘線、MOSI(Master Output Slave Input)主機輸出從機輸入、MISO(Master Input Slave Output)主機輸入從機輸出、SS(Slave Select)從機選擇
- 同步,全雙工(數據的發送和接收單獨用一條線)
- 支持總線掛載多設備(一主多從)
1.2 硬件電路
- 所有SPI設備的SCK、MOSI、MISO分別連在一起
- 主機另外引出多條SS控制線,分別接到各從機的SS引腳
- 輸出引腳配置為推挽輸出,輸入引腳配置為浮空或上拉輸入
- SCK時鐘線由主機控制,對主機來說,時鐘線為輸出,對所有從機來說,時鐘線為輸入;
- MOSI:主機是MO,主機輸出,從機輸入。數據通過MOSI線由主機輸出,從機輸入。
- MISO:主機輸入從機輸出。三個從機通過MISO輸出,主機通過MISO輸入。當從機的SS引腳是高電平時,它的MISO引腳,必須切換為高阻態,相當于引腳斷開,不輸出任何電平。這樣就可以防止,一條線有多個輸出,而導致的電平沖突問題。在SS為低電平時,MISO才允許變為推挽輸出。
- SS線低電平有效。
1.3 移位示意圖
- 移位寄存器有一個時鐘輸入端,SPI一般都是高位先行的,所以,每來一個時鐘,移位寄存器都會向左進行移位。移位寄存器的時鐘源是由主機提供的,這里叫做波特率發生器,它產生的時鐘驅動主機的移位寄存器進行移位。同時,這個時鐘也通過SCK引腳進行輸出,接到從機的移位寄存器里。
- 主機移位寄存器左邊移出去的數據通過MOSI引腳,輸入到從機移位寄存器的右邊。從機移位寄存器左邊移出去的數據,通過MISO引腳,輸入到主機移位寄存器的右邊。
- 波特率發生器時鐘的上升沿,所有移位寄存器向左移動一位,移出去的位放到引腳上。波特率發生器時鐘的下降沿,引腳上的位,采樣輸入到移位寄存器的最低位。
- SPI通信的基礎是交換一個字節,可以實現發送一個字節、接收一個字節、發送同時接收一個字節。
1.4 SPI時序基本單元
- 起始條件:SS從高電平切換到低電平
- 終止條件:SS從低電平切換到高電平
-
交換一個字節(模式0)
-
CPOL=0:空閑狀態時,SCK為低電平
-
CPHA=0:SCK第一個邊沿移入數據,第二個邊沿移出數據
- CPOL(Clock Polarity)時鐘極性
- CPHA(Clock Phase)時鐘相位:決定是第一個時鐘采樣移入還是第二個時鐘采樣移入,
-
交換一個字節(模式1)
-
CPOL=0:空閑狀態時,SCK為低電平
-
CPHA=1:SCK第一個邊沿移出數據,第二個邊沿移入數據
- SS為高電平時,MISO為高阻態。SS下降沿之后,從機的MISO被允許開啟輸出;SS上升沿之后,從機的MISO必須置回高阻態。
- SCK上升沿時,主機和從機同時移出數據,主機通過MOSI移出最高位,此時MOSI的電平就表示了主機要發送數據的B7;從機通過MISO移出最高位,MISO的電平就表示了從機要發送數據的B7,時鐘運行產生下降沿,此時主機和從機同時移入數據,也就是進行數據采樣,主機移出的B7進入從機移位寄存器的最低位,從機移出的B7進入主機移位寄存器的最低位。這樣,一個時鐘脈沖產生完畢,一個數據位傳輸完畢。當主機和從機完成了一個字節的數據交換后,如果主機只想交換一個字節,那這時可以置SS為高電平,結束通信。在SS的上升沿,MOSI還可以再變化一次,將MOSI置到一個默認的高電平或低電平,但MISO,從機必須置回高組態,如果主機的MISO為上拉輸入的話,則MISO引腳的電平就是默認的高電平,如果主機MISO為浮空輸入,則MISO引腳的電平不確定。
-
交換一個字節(模式2)
-
CPOL=1:空閑狀態時,SCK為高電平
-
CPHA=0:SCK第一個邊沿移入數據,第二個邊沿移出數據
- 交換一個字節(模式3)
- CPOL=1:空閑狀態時,SCK為高電平
- CPHA=1:SCK第一個邊沿移出數據,第二個邊沿移入數據
1.5 SPI時序
1.5.1 發送指令
- 發送指令
- 向SS指定的設備,發送指令(0x06)。0x06是寫使能指令,
SPI采用指令碼加讀寫數據的模型。SPI起始后,第一個交換發送給從機的數據一般叫做指令碼,在從機中,對應的會定義一個指令集,當需要發送什么指令時,就可以在起始后第一個字節,發送指令集里面的數據,這樣就能指導從機完成相應的功能了。不同的指令,可以有不同的數據個數。有的指令,只需要一個字節的指令碼就可以完成。而有的指令,后面就需要再跟要讀寫的數據。
1.5.2 指定地址寫
- 指定地址寫
- 向SS指定的設備,發送寫指令(0x02),隨后在指定地址(Address[23:0])下,寫入指定數據(Data)
1.5.3 指定地址讀
- 指定地址讀
- 向SS指定的設備,發送讀指令(0x03),隨后在指定地址(Address[23:0])下,讀取從機數據(Data)
2. W25Q64
2.1 W25Q64簡介
-
W25Qxx系列是一種低成本、小型化、使用簡單的非易失性存儲器,常應用于數據存儲、字庫存儲、固件程序存儲等場景
-
存儲介質:Nor Flash(閃存)
-
時鐘頻率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)
-
存儲容量(24位地址):
W25Q40: 4Mbit / 512KByte
W25Q80: 8Mbit / 1MByte
W25Q16: 16Mbit / 2MByte
W25Q32: 32Mbit / 4MByte
W25Q64: 64Mbit / 8MByte
W25Q128: 128Mbit / 16MByte
W25Q256: 256Mbit / 32MByte
2.2 硬件電路
- CS:低電平有效。
- WP:寫保護,配合內部的寄存器配置,可以實現硬件的寫保護。低電平有效,WP接低電平,保護住,不讓寫;WP接高電平,不保護,可以寫。
- C1:濾波;R1和D1電源指示燈,接通電源就亮。
2.3 W25Q64框圖
- 存儲器以字節為單位,每個字節都有唯一的地址。W25Q64的地址寬度是24位,3個字節。24位地址,最大尋址范圍是16MB,該芯片只有8MB,所以地址空間只用了一半。8MB的空間排到最后一個字節就是7F FF FF。
- 在整個空間中以64KB為一個基本單元,劃分為若干的塊Block,
8*1024/64=128
塊。每一塊再進行更細的劃分,以4KB進行劃分,分為16個扇區Sector。頁是256個字節,一個扇區是4KB,以256個字節劃分,得到4*1024/256=16頁。每一行為一頁,一頁內的地址變化,僅限于地址的最低一個字節。 - 控制邏輯就是整個芯片的管理員,控制邏輯左邊是SPI的通信引腳,這些引腳與主控芯片進行連接,主控芯片通過SPI協議,把指令和數據發給控制邏輯,控制邏輯就會自動去操作內部電路來完成想要的功能。
- 控制邏輯上面的狀態寄存器,比如芯片是否處于忙狀態、是否寫使能、是否寫保護等都可以在這個狀態寄存器里體現。
- 寫控制邏輯與外部的WP引腳相連,是配合WP引腳實現硬件寫保護的。
- 高電壓生成器:是配合Flash進行編程的,因為Flash是掉電不丟失的,所以需要一個高壓源。
- 頁地址鎖存/計數器、字節地址鎖存/計數器:用來指定地址的,
- 通過SPI總共發過來3個字節的地址。因為一頁是256字節,所以一頁內的字節地址就取決于最低一個字節,而高位的2個字節就對應的是頁地址。
- 所以發過來的3個字節的前2個字節會進到頁地址鎖存計數器里,最后一個字節會進到字節地址鎖存計數器里。頁地址通過寫保護和行解碼來選擇要操作哪一頁。字節地址通過這個列解碼和256字節頁緩存來進行指定字節的讀寫操作。又因為地址鎖存都是有一個計數器的,所以這個地址指針在讀寫之后可以自動加1,因此可以實現從指定地址開始,連續讀寫多個字節的目的。
256字節的頁緩存區是一個256字節的RAM存儲器,讀寫是通過這個RAM緩存區來進行的,寫入的數據會先放到緩存區里,然后在時序結束后,芯片再將緩存區的數據復制到對應的Flash里,進行永久保存。
為什么數據要先進入緩存區呢?是因為SPI寫入的頻率是非常高的,
2.4 Flash操作注意事項
寫入操作時:
- 寫入操作前,必須先進行寫使能
- 每個數據位只能由1改寫為0,不能由0改寫為1
- 寫入數據前必須先擦除,擦除后,所有數據位變為1
- 擦除必須按最小擦除單元(4096個字節)進行
- 連續寫入多字節時,最多寫入一頁(256字節(RAM緩存區))的數據,超過頁尾位置的數據,會回到頁首覆蓋寫入
- 寫入操作結束后,芯片進入忙狀態,不響應新的讀寫操作
讀取操作時:
- 直接調用讀取時序,無需使能,無需額外操作,沒有頁的限制,讀取操作結束后不會進入忙狀態,但不能在忙狀態時讀取
2.5 手冊指令集
- Write Enable 寫使能:06,先起始,再交換一個字節,第一個字節是發送方向,發送0x06指令
- Write Disable寫失能:04,起始,交換字節發送指令碼04,終止。
- Read Status Register-1 讀狀態寄存器1:05,起始,交換字節發送指令碼05,要讀數據,繼續交換字節,通過交換讀取一個字節,該字節就是狀態寄存器的S7-S0,S0是BUSY位,S1是WEL位,主要用來查看忙狀態的
- Page Program頁編程:02,就是寫數據,起始、交換字節發送指令02,然后繼續交換發送地址的23-16位、15-8位、7-0位,這三個字節用來指定地址,再之后,就可以寫入數據D7-D0,該數據寫入到剛才指定的地址下。如果繼續交換寫入的話,后續的字節就從起始地址開始依次存儲,
Sector Erase(4KB)扇區擦除:20,起始,交換字節發送指令20,之后再交換發送3個字節的地址,終止。發送之后,這個指定地址所在的扇區就會被整個擦除。 - JEDEC ID 讀取ID號:9F,起始,交換發送9F,隨后繼續交換讀取3個字節,終止。第一個字節是廠商ID,后兩個字節是設備ID,
- Read Data 讀取數據:03,起始,交換發送指令03,之后交換發送3個字節的地址,再之后交換讀取數據,該數據是3個字節地址下的數據。再繼續讀取,后面數據就是從指定地址開始依次讀存儲的數據。
3. SPI外設簡介
- STM32內部集成了硬件SPI收發電路,可以由硬件自動執行時鐘生成、數據收發等功能,減輕CPU的負擔
- 可配置8位/16位數據幀、高位先行/低位先行
- 時鐘頻率:fPCLK / 分頻系數 = fPCLK / (2, 4, 8, 16, 32, 64, 128, 256)
- APB2的PCLK是72MHz,APB1的PCLK是36MHz。
- 支持多主機模型、主或從操作
- 可精簡為半雙工/單工通信
- 支持DMA
- 兼容I2S協議
- STM32F103C8T6 硬件SPI資源:SPI1(掛載在APB2)、SPI2(掛載在APB1)
3.1 SPI框圖
- 移位寄存器:右邊的數據低位一位一位地從MOSI移出去,
MISO的數據一位一位地移入到左邊的數據高位。 - LSBFIRST是控制低位先行還是高位先行,給0,先發送MSB即高位,給1,先發送LSB即低位
MOSI與MISO的交叉方框是主要用來進行主從模式引腳變換的。這個SPI外設可以做主機也可以做從機,做主機時,這個交叉就不用,MOSI為MO,主機輸出;MISO為MI,主機輸入。STM32做從機時,MOSI為SI,從機輸入,走交叉路線輸入到移位寄存器,MISO為SO,從機輸出,也走交叉路線,移位寄存器輸出到MISO。 - 接收緩沖區就是接收數據寄存器RDR,發送緩沖區實際上就是發送數據寄存器TDR。TDR和RDR占用同一個地址,統一叫做DR。當要發送一系列數據流時,數據寫入發送緩沖區,當移位寄存器沒有數據移位時,發送緩沖區的數據會立刻轉入移位寄存器,開始移位。這個轉入時刻會置狀態寄存器的TXE為1,表示發送寄存器空。當檢查到TXE為1時,下一個數據,就可以提前寫入到TDR里了,一旦上個數據發完,下一個數據就可以立刻跟進,實現不間斷的連續傳輸。移位寄存器一旦有數據進來了,就會自動產生時鐘,將數據移出去,在移出去的過程中,MISO的數據也會移入,一旦數據移出完成,數據移入也完成。這時,移入的數據就會整體的從移位寄存器轉入到接收緩沖區RDR,這個時刻會置狀態寄存器的RXNE為1,表示接收寄存器非空。當檢查RXNE置1后,就要盡快把數據從RDR讀出來,在下一個數據到來之前,讀出RDR,可以實現連續接收。否則,如果下一個數據已經收到了,上一個數據還沒從RDR讀出來,那么RDR的數據就會被覆蓋,就不能實現連續的數據流了。
- 波特率發生器用來產生SCK時鐘的,內部主要是一個分頻器。
- CR1寄存器的三個位BR0、BR1、BR2,用來控制分頻系數。
- SPE是SPI使能,就是SPI_Cmd函數配置的位。
- BR配置波特率,就是SCK時鐘頻率。
- MSTR配置主從模式,1是主模式,0是從模式。
- CPOL和CPHA用來選擇SPI的4種模式。
- SR寄存器:TXE為發送寄存器空,RXNE為接收寄存器非空
- CR2寄存器:主要是使能位,如中斷使能、DMA使能
- NSS:SS就是從機選擇,低電平有效,所以這里前面加了個N。
3.2 SPI基本結構
3.3 主模式全雙工連續傳輸
示例使用SPI模式3。
- SCK默認是高電平,在第一個下降沿,MOSI和MISO移出數據。上升沿移入數據。
- SS置低電平開始時序,在剛開始時,TXE為1,表示TDR空,
- 可以寫入數據,指示就是軟件寫入0xF1至SPI_DR,0xF1就是要發送的第一個數據,寫入之后TDR(發送緩沖器)變為0xF1,同時TXE=0,此時,TDR是等候區,移位寄存器才是真正的發送區。等候區TDR中的0xF1就會立刻轉入移位寄存器,開始發送。轉入瞬間置TXE標志為1,表示發送寄存器空。然后移位寄存器有數據了,波形就自動開始生成 (MISO/MOSI(輸出)),數據F1的波形就開始產生了,在移位產生F1波形的同時,等候區TDR是空的,為了移位完成時,下一個數據能不間斷地跟隨,這里就要提早把下一個數據寫入到TDR里等著了,
- 寫入F1后,軟件等待TEX=1,然后寫入0xF2至SPI_DR。寫入之后,TDR的內容變成F2了。然后F1數據波形產生完畢后,F2轉入移位寄存器開始發送,這時TXE=1,就盡快把下一個數據F3放到TDR里等著(軟件等待TEX=1,然后寫入0xF3至SPI_DR).如果只想發送3個數據,F3轉入移位寄存器之后,TXE=1,就不需要再繼續寫入了。在最后一個TXE=1之后,還要繼續再等待一段時間,F3的波形才能完整發送完,等波形完全發送完之后,BUSY標志由硬件清除,這才表示波形發送完了。
- SPI是全雙工,發送的同時,還有接收。
- 在第一個字節發送完成后,第一個字節的接收也完成了。接收到的數據1是A1,這時移位寄存器的數據整體轉入RDR,RDR隨后存儲的就是A1。轉入的同時RXNE標志位也置1,表示收到數據了(軟件等到RXNE=1,然后從SPI_DR讀出A1),接收之后,軟件清除RXNE標志位,當下一個數據A2收到后,RXNE重新置1。當監測到RXNE=1時,就繼續讀出RDR,即第二個數據A2。在最后一個字節時序完全產生之后,數據3才能收到。一個字節波形收到后,移位寄存器的數據自動轉入RDR,會覆蓋原有的數據,所以讀取RDR要及時。
3.4 非連續傳輸
模式3,SCK默認高電平。如果你檢測到TXE=1了,TDR為空,就軟件寫入0xF1至SPI_DR,這時TDR的值變為F1,TXE變為0,目前移位寄存器也是空,F1會立刻轉入移位寄存器開始發送,波形產生,并且TXE置1,表示可以把下一個數據放在TDR里候著了。等到第一個字節時序結束,也就是第一個字節接收完成,這時接收的RXNE置1,先把第一個接收到的數據讀出來,之后再寫入下一個字節數據(軟件等待TXE=1,但是較晚寫入0xF2至SPI_DR),之后數據2開始發送,等先把接收的數據2收著,再繼續寫入數據3。數據3時序結束后,再接收數據3置換回來的數據。
1、 等待TXE=1;2. 寫入發送的數據至TDR,3. 等待RXNE=1,4. 讀取RDR接收的數據。之后交換第二個字節,重復該4步。
3.5 軟件 / 硬件波形對比
3.6 庫函數
// 恢復缺省配置
void SPI_I2S_DeInit(SPI_TypeDef* SPIx);
// 初始化
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
// 結構體變量初始化
void SPI_StructInit(SPI_InitTypeDef* SPI_InitStruct);
// 外設使能
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
// 中斷使能
void SPI_I2S_ITConfig(SPI_TypeDef* SPIx, uint8_t SPI_I2S_IT, FunctionalState NewState);
// DMA使能
void SPI_I2S_DMACmd(SPI_TypeDef* SPIx, uint16_t SPI_I2S_DMAReq, FunctionalState NewState);
// 寫DR數據寄存器
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
// 讀DR數據寄存器
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);// 可以獲取TXE和RXNE標志位的狀態
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);