一、概述
SPI = Serial Peripheral Interface,是串行外圍接口設備,是一種高速,全雙工,同步的通信總線。常規只占用四根線,節約了芯片管腳,PCB的布局省空間。
- 優點:
支持全雙工,push-pull的驅動性能相比open-drain信號完整性更好。
支持高速(100MHz以上)。
協議支持字節長不限於8bits,可根據應用特點靈活選擇消息字長
硬件連接簡單。
- 缺點:
相比I2C多兩根線。
沒有尋址機制,只能靠片選選擇不同設備。
沒有從設備接收ACK,主設備對於發送成功與否不得而知。
典型應用只支持單主控
相比RS232 RS485和CAN總線,SPI傳輸距離短
二、硬件結構
SPI總線定義兩個及以上設備間的數據通信,提供時鍾的設備為主設備Master,接收時鍾的設備為從設備Slave;
信號定義如下:
SCK :Serial Clock 串行時鍾
MOSI:Master Ouput,Slave Input 主發從收信號
MISO:Master Input,Slave Input主收從發信號
SS/CS:Slave Select片選信號
電路連接如下:
單個主設備和單個從設備:
單個主設備和多個從設備:
三、寄存器類型
摩托羅拉定義的SPI寄存器包括:
SPI Control Register 1 控制寄存器1
SPI Control Register 2 控制寄存器2
SPI Baud Rate Register 波特率寄存器
SPI Status Register(SPISR) 狀態寄存器(只讀 其余均可讀可寫)
SPI Data Register(SPIDR) 數據寄存器
通過往寄存器中寫入不同的值,設置SPI模塊的不同屬性
四、SPI傳輸模式
SPI通信有四種模式,簡單地講就是設置SCLK時鍾信號線的那種信號為有效信號
通過設置控制寄存器SPICR1中的CPOL和CPHA位,將SPI可以分成四種傳輸模式
時鍾極性CPOL,即Clock Polarity,決定時鍾空閑時狀態電平。對於SPI數據傳輸格式沒有顯著影響
CPOL = 0 ,表示當SCLK=0時處於空閑狀態,所以有效狀態就是SCLK處於高電平狀態時
CPOL = 1, 表示當SCLK=1時處於空閑狀態,所以有效狀態就是SCLK處於低電平狀態時
CPHA,即Clock Phase,定義SPI數據傳輸的兩種基本模式。
CPHA = 0 ,在時鍾的第一個跳變沿(上升沿或下降沿)進行數據采樣。,在第2個邊沿發送數據
CPHA = 1, 在時鍾的第二個跳變沿(上升沿或下降沿)進行數據采樣。,在第1個邊沿發送數據
四種模式如下圖所示:
先看第一列兩張圖(CPHA = 0),采樣發生在第一個時鍾跳變沿,即數據采樣發生在SCK奇數邊沿;
再看第二列兩張圖(CPHA = 1),采樣發生在第二個時鍾跳變沿,即數據采樣發生在SCK偶數邊沿。
MODE0:CPOL=0,CPHA=0:此時空閑態時,SCLK處於低電平,數據采樣是在第一個邊沿,也就是SCLK由低電平到高電平的跳變,所以數據采樣是在上升沿(准備數據),(發送數據)數據發送是在下沿
MODE1:CPOL=0,CPHA=1:此時空閑態時,SCLK處於低電平,數據發送是在第一個邊沿,也就是SCLK由低電平到高電平的跳變,所以數據采樣是在下降沿,數據發送是在上升沿
MODE2:CPOL=1,CPHA=0:此時空閑態時,SCLK處於高電平,數據采樣是在第一個邊沿,也就是SCLK由高電平到低電平的跳變,所以數據采樣是在下降沿,數據發送是在上升沿
MODE3:CPOL=1,CPHA=1:此時空閑態時,SCLK處於高電平,數據發送是在第一個邊沿,也就是SCLK由高電平到低電平的跳變,所以數據采樣是在上升沿,數據發送是在下降沿
五、讀寫操作
標准SPI讀寫為例
片選——讀指令——地址——數據讀出
片選——寫指令——地址——數據寫入
SPI通信協議
主從設備
必須使用相同的工作模式——SCLK、CPOL 和 CPHA,才能正常工作。如果有多個從設備
,並且它們使用了不同的工作模式,那么主設備
必須在讀寫不同從設備
時需要重新修改對應從設備的模式。以上SPI總線協議的主要內容。
是不是感覺,這就完了? SPI就是如此,他沒有規定最大傳輸速率,沒有地址方案,也沒規定通信應答機制,沒有規定流控制規則。
只要四根信號線連接正確,SPI模式相同,將CS/SS信號線拉低,即可以直接通信,一次一個字節的傳輸,讀寫數據同時操作,這就是SPI
一些通信控制都得通過SPI設備自行實現,SPI並不關心物理接口的電氣特性,例如信號的標准電壓。
STM32中SPI初始化配置
1.初始化GPIO口,配置相關引腳的復用功能,使能SPIx時鍾。調用函數:void GPIO_Init();
2.使能SPI時鍾總線:RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE)
3.配置SPI初始化的參數,設置SPI工作模式:SPI_Init(SPI1,&SPI_Initstructure)
4.使能SPI外設:SPI_Cmd(SPI1,ENABLE);
SPI配置設置
typedef struct { uint16_t SPI_Direction; /*!< 傳輸方向,兩向全雙工,單向接收等*/ uint16_t SPI_Mode; /*!< 模式選擇,確定主機還是從機 */ uint16_t SPI_DataSize; /*!< 數據大小,8位還是16位 */ uint16_t SPI_CPOL; /*!< 時鍾極性選擇 */ uint16_t SPI_CPHA; /*!< 時鍾相位選擇 */ uint16_t SPI_NSS; /*!< 片選是硬件還是軟件*/ uint16_t SPI_BaudRatePrescaler; /*!< 分頻系數 */ uint16_t SPI_FirstBit; /*!< 指定數據傳輸是從MSB還是LSB位開始的。MSB就是二進制第一位,LSB就是最后一位 */ uint16_t SPI_CRCPolynomial; /*!< CRC校驗 ,設置 CRC 校驗多項式,提高通信可靠性,大於 1 即可*/ }SPI_InitTypeDef;
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);//初始化GPIOB GPIO_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工作模式:設置為主SPI SPI_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; //定義波特率預分頻的值:波特率預分頻值為256 SPI_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);//啟動傳輸
}