基于STM32 + 超详细对新手全面解析讲解SPI协议(附源码)
前言
? ? ? ?本次我們學習一下STM32的一個基本外設 --- SPI,全程參考手冊講解,講述SPI的工作模式和作用,讓大家快速掌握和了解SPI通訊協議。本篇博客大部分是自己收集和整理,借鑒了很多大佬的圖片和知識點整理,如有侵權請聯系我刪除。(@小麥大叔,@Aspirant-GQ)
本次實驗板子使用的是正點原子精英版,芯片是STM32F103ZET6,需要資料可以@我拿取。
本博客內容原創,創作不易,轉載請注明
一 . 初步認識SPI
SPI 是英語 Serial Peripheral interface 的縮寫,顧名思義就是串行外圍設備接口,是一種高速的,全雙工,同步的通信總線。
可以同時發出和接收串行數據;可以當作主機或從機工作;提供頻率可編程時鐘;發送結束中斷標志;寫沖突保護;總線競爭保護等。
應用場所:SPI 接口主要應用在 EEPROM,FLASH,實時時鐘,AD 轉換器,還有數字信號處理器和數字信號解碼器之間。
支持最高的 SCK 時鐘頻率為 fpclk/2:
????????STM32F103 型號的芯片默認 fpclk1為 72MHz, fpclk2為 36MHz),完全支持 SPI 協議的 4 種模式,數據幀長度可設置為 8 位或 16 位,可設置數據 MSB 先行或 LSB 先行。
二 . SPI協議層講解
????????就像IIC、串口一樣,SPI也有其通信協議,我們一般按照分層的思想來學習SPI的協議,主要分為物理層和協議層。
1.物理層
??? 在點對點的通信中,SPI接口不需要進行尋址操作,在多個從器件的系統中,每個從器件需要獨立的使能信號。
?1.SPI的兩種接口方式:五線制和四線制
五線制:
| MOSI(DO) | 作為主器件時為數據輸出,作為從器件時為數據輸入 | 
| MISO(DI) | 作為主器件時為數據輸入,作為從器件是為數據輸出 | 
| SCLK | 時鐘信號線,由主器件產生 | 
| NSS(CS) | 從器件使能信號,由主器件控制,低電平有效 | 
| GND | 公共地線 | 
四線制:
| MOSI | 作為主器件時為數據輸出,作為從器件時為數據輸入 | 
| SCLK | 時鐘信號線,由主器件產生 | 
| NSS(CS) | 從器件使能信號,由主器件控制,低電平有效 | 
| GND | 公共地線 | 
2. SPI引腳說明(一般使用4條線通信)
1)MOSI 主設備數據輸出,從設備數據輸入:MOSI專門負責主機向從機傳輸數據
不同器件引腳的MOSI也可以是:SOMI,DIN,DI,SDI或SI(在主機端);
????????MOSI就是主機輸出/從機輸入,因為SPI是全雙工的通信總線,即主機和從機可以同時收發數據,這樣的話就需要倆條線同時分別負責:主->從和從->主這倆條傳輸線路。而MOSI就專門負責主機向從機傳輸數據。
????????數據輸出通過MOSI發出,數據在時鐘的上升沿或下降沿時改變,在緊接著的下降沿或上升沿被讀取。
2)MISO 主設備數據輸入,從設備數據輸出:MISO專門負責從機向主機傳輸數據
不同器件引腳的MOSI也可以是:MISO也可以是SIMO,DOUT,DO,SDO或SO(在主機端);
3)SCLK 時鐘信號,由主設備產生:
SCK提供時鐘脈沖,MOSI則基于這個時鐘脈沖完成數據的傳輸。
統一主機和從機之間的數據傳輸,只有在有效的時鐘信號下才能正常傳輸數據。
不同設備支持的最高傳輸頻率可能不一樣,在傳輸過程中傳輸頻率受限于低速的一方。
4)NSS(CS) 從設備片選信號,由主設備控制。
不同于IIC,如果和其他從設備通訊,不需要尋址操作,只需要通過片選信號就能和從機通信。
NSS是控制芯片是否被選中,也就是說只有片選信號為預先規定的使能信號(高電平或低電平),對芯片操作才有效。
從選擇(NSS)腳管理:
● 軟件NSS模式:可以通過設置SPI_CR1寄存器的SSM位來使能這種模式(見圖211)。在這種 模式下NSS引腳可以用作它用,而內部NSS信號電平可以通過寫SPI_CR1的SSI位來驅動
● 硬件NSS模式,分兩種情況:
─ NSS輸出被使能:當STM32F10xxx工作為主SPI,并且NSS輸出已經通過SPI_CR2寄存 器的SSOE位使能,這時NSS引腳被拉低,所有NSS引腳與這個主SPI的NSS引腳相連并 配置為硬件NSS的SPI設備,將自動變成從SPI設備。
─當一個SPI設備需要發送廣播數據,它必須拉低NSS信號,以通知所有其它的設備它是主 設備;如果它不能拉低NSS,這意味著總線上有另外一個主設備在通信,這時將產生一個 硬件失敗錯誤(Hard Fault)。
─ NSS輸出被關閉:允許操作于多主環境。
2 . 協議層
SPI協議層規定了傳輸過程中的起始信號和停止信號、數據有效性、時鐘同步、通訊模式。
所有的運作都是基于SCK時鐘線的,SCK對于SPI的作用就像心臟對于人體的作用,SCK為低電平就代表心臟停止跳動。
1)起始信號和停止信號:
????????SPI通訊的起始和停止由NSS信號線控制,當NSS為低電平時代表起始信號,當NSS為高電平時代表停止信號
2)數據有效性:
SPI中使用MOSI和MISO來進行全雙工傳輸數據,SCK來同步數據傳輸,即MOSI和MISO同時工作,在時鐘信號線SCK為有效時對MOSI、MISO數據線進行采樣,采到的信息即為傳輸的信息。
????????SPI中數據的采樣是在SCK的上升沿或下降沿時進行的。圖示模式中3和5部分就是對數據進行采樣的時刻,可以看出圖示中數據是在SCK的下降沿進行采樣的。MOSI和MISO的高低電平代表了1和0。
3)通訊模式:主要依靠總線空閑時SCK的時鐘狀態和數據采樣時刻來區別。
?4)SPI總線工作方式:?
??? SPI有四種傳輸方式:上升沿、下降沿、前沿、后沿。SPI只有主模式和從模式之分,沒有讀和寫的說法。
5)時鐘極性CPOL和時鐘相位CPHA:
SPI_CR寄存器的CPOL和CPHA位,能夠組合成四種可能的時序關系。
時鐘極性CPOL
| 時鐘極性(CPOL):決定空閑狀態時時鐘線電平狀態 | 
| 時鐘極性CPOL:CPOL是指NSS總線空閑時SCK的電平信號 | 
| CPOL = 0:串行時鐘線空閑狀態為低電平,下降沿采集數據 | 
| CPOL = 1:串行時鐘線空閑狀態為高電平,上升沿采集數據 | 
| 如果SCK為高電平,CPOL=1;SCK為低電平,CPOL=0 | 
時鐘相位CPHA
| 時鐘相位(CPHA):決定第一個數據的采集時刻 | 
| CPHA是指數據的采樣時刻,SCK的信號可以看作方波 | 
| CPHA = 0:在串行時鐘線的第一個跳變數據被采集 | 
| CPHA = 1:在串行時鐘線的第二個跳變數據被采集 | 
| CPHA=0時會在SCK的奇數邊沿采樣;CPHA=1時會在SCK的偶數邊沿采樣。 | 
從上面的表格我們可以看到,時鐘極性CPOL是控制空閑的時候SCK的電平變化的,時鐘相位CPHA是控制我們在那個電平邊沿采集數據的,由此我們可以得到時鐘不同的搭配來采集數據。
| CPOL = 0,CPHA = 0 | SCK串行時鐘線空閑狀態為低電平,在SCK的第一個跳變數據(上降沿)被采集 | 
| CPOL = 0,CPHA = 1 | SCK串行時鐘線空閑狀態為低電平,在SCK的第一個跳變數據(下降沿)被采集 | 
| CPOL = 1,CPHA = 0 | SCK串行時鐘線空閑狀態為高電平,在SCK的第二個跳變數據(下降沿)被采集 | 
| CPOL = 1,CPHA = 1 | SCK串行時鐘線空閑狀態為高電平,在SCK的第二個跳變數據(上降沿)被采集 | 
注意:
1. 在改變CPOL/CPHA位之前,必須清除SPE位將SPI禁止。
2. 主和從必須配置成相同的時序模式。
3. SCK的空閑狀態必須和SPI_CR1寄存器指定的極性一致(CPOL為’1’時,空閑時應上拉SCK為 高電平;CPOL為’0’時,空閑時應下拉SCK為低電平)。
6)SPI 數據傳輸:
?SPI通訊協議規定傳輸的數據為8位,傳輸順序是高位在前,低位在后。
SPI是一個數據交換協議,主機給從機發送一個信息,那么從機就會返回一個信息給主機。也就是說,當主機讀從機的時候,需要先發一個位的數據給從機,從機才會發一個位的數據回來給主機。
SPI是一個同步的數據總線,也就是說它是用單獨的數據線和一個單獨的時鐘信號來保證發送端和接收端的完美同步。
時鐘的作用就是告訴接收端在確切的時機對數據線上的信號進行采樣。
7)數據傳輸過程:
1)主機先將NSS信號拉低,這樣保證開始接收數據;
2)當接收端檢測到時鐘的邊沿信號時,它將立即讀取數據線上的信號,這樣就得到了一位數據(1bit);
3)主機發送到從機時:主機產生相應的時鐘信號,然后數據一位一位地將從MOSI信號線上進行發送到從機;
4)主機接收從機數據:如果從機需要將數據發送回主機,則主機將繼續生成預定數量的時鐘信號,并且從機會將數據通過MISO信號線發送;
注意:SPI是“全雙工”(具有單獨的發送和接收線路),因此可以在同一時間發送和接收數據,另外SPI的接收硬件可以是一個簡單的移位寄存器。這比異步串行通信所需的完整UART要簡單得多,并且更加便宜;
8)數據控制邏輯:
1)主機發送數據
| 當發送完一幀數據的時候,“狀態寄存器 SR”中的“TXE ”標志位”會被置 1,表示傳輸完一幀,發送緩沖區已空 | 
| 地址和數據總線會在相應的地址上取到要發送的數據,將數據放入發送緩沖區,當向外發送數據時,移位寄存器會以發送緩沖區為數據源,一位一位的將數據發送出去。 | 
2)主機接收數據
| 當接收完一幀數據的時候,“ RXNE“標志位”會被置 1,表示傳輸完一幀,接收緩沖區非空 | 
| 接收數據時,移位寄存器把數據線采樣到的數據一位一位的傳到接收緩沖區,再由總線讀取接收緩沖區中的數據。 | 
3)數據幀格式
| 通過配置“控制寄存器CR1”的“DFF為”可以控制數據幀格式為8位還是16位,即一次接收或發送數據的大小 | 
4)先行位
| 通過配置“控制寄存器CR1”的“LSBFIRST 位”可選擇 MSB(最高有效位) 先行還是 LSB(最低有效位) 先行 | 
3 . 多從機模式
第一種方法:多NSS
主機和從機通信都是通過NSS片選信號線來通信的,每一臺從機都是單獨和主機相連,每一條NSS線都是獨立的,主機想要和那一臺從機通信,就把相應的NSS數據線拉低電平,保持其他從機的為高電平,就能和對應的從機進行數據通信了。
如果同時將兩個NSS信號線拉低,則可能會出現亂碼,因為從機可能都試圖在同一條MISO線上傳輸數據,最終導致接收數據亂碼,所以最好一次只能和一個從機通信。
第二種方法:菊花鏈?
在數字通信世界中,在設備信號(總線信號或中斷信號)以串行的方式從一 個設備依次傳到下一個設備,不斷循環直到數據到達目標設備的方式被稱為菊花鏈。
1.菊花鏈的最大缺點是因為是信號串行傳輸,所以一旦數據鏈路中的某設備發生故障的時候,它下面優先級較低的設備就不可能得到服務了;
 2.另一方面,距離主機越遠的從機,獲得服務的優先級越低,所以需要安排好從機的優先級,并且設置總線檢測器,如果某個從機超時,則對該從機進行短路,防止單個從機損壞造成整個鏈路崩潰的情況;
SPI通訊的優缺點:
SPI通訊的優點:
- 全雙工串行通信;
- 高速數據傳輸速率。
- 簡單的軟件配置;
- 極其靈活的數據傳輸,不限于8位,它可以是任意大小的字;
-  非常簡單的硬件結構。從站不需要唯一地址(與I2C不同)。從機使用主機時鐘,不需要精密時鐘振蕩器/晶振(與UART不同)。不需要收發器(與CAN不同). 
SPI通訊的缺點:
- 沒有硬件從機應答信號(主機可能在不知情的情況下無處發送);
- 通常僅支持一個主設備;
- 需要更多的引腳(與I2C不同);
- 沒有定義硬件級別的錯誤檢查協議;
- 與RS-232和CAN總線相比,只能支持非常短的距離;
4.初始化代碼(參考正點原子)
因為速度函數沒有被封裝,需要我們用寄存器來給他封裝一個函數。
要初始化 SPI2,設置 SPI2 為主機模式,設置數據格式為 8 位,然設置 SCK 時鐘極性及采樣方式。并設置 SPI2 的時鐘頻率(最大 18Mhz),以及數據的格式(MSB 在前還是LSB 在前)。
#include "spi.h"void SPI2_Init(void) {GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );//PORTB時鐘使能 RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );//SPI2時鐘使能 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15復用推挽輸出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOBGPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //PB13/14/15上拉SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //設置SPI單向或者雙向的數據模式:SPI設置為雙線雙向全雙工SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //設置SPI工作模式:設置為主SPISPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //設置SPI的數據大小:SPI發送接收8位幀結構SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步時鐘的空閑狀態為高電平SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步時鐘的第二個跳變沿(上升或下降)數據被采樣SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信號由硬件(NSS管腳)還是軟件(使用SSI位)管理:內部NSS信號有SSI位控制SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;//定義波特率預分頻的值:波特率預分頻值為256SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定數據傳輸從MSB位還是LSB位開始:數據傳輸從MSB位開始SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值計算的多項式SPI_Init(SPI2, &SPI_InitStructure); //根據SPI_InitStruct中指定的參數初始化外設SPIx寄存器SPI_Cmd(SPI2, ENABLE); //使能SPI外設SPI2_ReadWriteByte(0xff);//啟動傳輸 } //SPI 速度設置函數 //SpeedSet: //SPI_BaudRatePrescaler_2 2分頻 //SPI_BaudRatePrescaler_8 8分頻 //SPI_BaudRatePrescaler_16 16分頻 //SPI_BaudRatePrescaler_256 256分頻 void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler) {assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));SPI2->CR1&=0XFFC7;SPI2->CR1|=SPI_BaudRatePrescaler; //設置SPI2速度 SPI_Cmd(SPI2,ENABLE); }1)設置 SPI 的通訊方向
這兩個模式的最大區別為 SPI 的 SCK 信號線的時序, SCK 的時序是由通訊中的主機產生的。若被配置為從機模式, STM32 的 SPI 外設將接受外來的 SCK 信號。
2)設置 SPI 工作在主機模式
3) SPI 通訊的數據幀
4)配置 SPI 的時鐘極性 CPOL 和時鐘相位 CPHA?
5)配置 NSS 引腳的使用模式(軟硬件SPI區別)
可以選擇為硬件模式(SPI_NSS_Hard )與軟件模式(SPI_NSS_Soft ),在硬件模式中的 SPI 片選信號由 SPI 硬件自動產生,而軟件模式則需要我們親自把相應的 GPIO 端口拉高或置低產生非片選和片選信號。實際中軟件模式應用比較多。
6)設置波特率分頻因子
7)設置數據位高低位
8)CRC值計算的多項式
(若我們使用 CRC 校驗時,就使用這個成員的參數(多項式),來計算 CRC 的值。)
9)其他庫函數
讀取寫入的字節代碼:
1)發送數據前要等待發送緩沖區為空,靠TXE標志判斷
2)開始的while循環是等待發送緩沖區為空,同時,等待接收緩沖區是否有數據,靠RXNE標志來判斷,把接收緩沖區的數據作為返回值返回
3)由于發送和接收是同時進行的,而且要接收一個數據時必須在有效的SCK下,而只有發送數據才能產生有效的SCK,所以接收數據的函數時在發送數據的函數的基礎上,將發送的數據設置為Dummy_Byte假數據來騙取有效的SCK。
//SPIx 讀寫一個字節 //TxData:要寫入的字節 //返回值:讀取到的字節 u8 SPI2_ReadWriteByte(u8 TxData) { u8 retry=0; while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //檢查指定的SPI標志位設置與否:發送緩存空標志位{retry++;if(retry>200)return 0;} SPI_I2S_SendData(SPI2, TxData); //通過外設SPIx發送一個數據retry=0;while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //檢查指定的SPI標志位設置與否:接受緩存非空標志位{retry++;if(retry>200)return 0;} return SPI_I2S_ReceiveData(SPI2); //返回通過SPIx最近接收的數據 }總結:
????????SPI的知識點實在太多了,不過配置的時候不是很麻煩,初始化相對來說還是比較簡單的,我們不用糾結他底層是什么樣的,我們保證我們可以使用就行,后期我會出OLED和flash的芯片SPI使用教程,大家如果對我的博客有疑問或者錯誤,可以@我修改,大家相互交流。
??點贊收藏關注博主,不定期分享單片機知識,互相學習交流。
總結
以上是生活随笔為你收集整理的基于STM32 + 超详细对新手全面解析讲解SPI协议(附源码)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 激光检测----激光原理简述
- 下一篇: alpha测试和beta测试
