SPI協議簡述 SPI,是英語Serial Peripheral interface的縮寫,顧名思義就是串列外圍設備介面。由Motorola首創。SPI介面主要應用在 EEPROM,FLASH,實時時鐘,AD轉換器,還有數字信號處理器和數字信號解碼器之間。SPI,是一種高速的,全雙工,同步的通信匯流排 ...
SPI協議簡述
SPI,是英語Serial Peripheral interface的縮寫,顧名思義就是串列外圍設備介面。由Motorola首創。SPI介面主要應用在 EEPROM,FLASH,實時時鐘,AD轉換器,還有數字信號處理器和數字信號解碼器之間。SPI,是一種高速的,全雙工,同步的通信匯流排。
優缺點:
- 協議簡單,相對數據速率高。
- 占用的Pin口較多
- 沒有指定的流控制,沒有應答機制確認是否接收到數據。
SPI的通信原理很簡單,它以主從方式工作,這種模式通常有一個主設備和一個或多個從設備,需要至少4根線,事實上3根也可以(單向傳輸時)。也是所有基於SPI的設備共有的,它們是SDI,SDO,SCK,CS。
- SDO – 主設備數據輸出,從設備數據輸入
- SDI – 主設備數據輸入,從設備數據輸出
- SCK – 時鐘信號,由主設備產生
- CS – 從設備使能信號,由主設備控制
CS: 其中CS是控制晶元是否被選中的,也就是說只有片選信號為預先規定的使能信號時(高電位或低電位),對此晶元的操作才有效,這就允許在同一匯流排上連接多個SPI設備成為可能。
SCK:SCK為時鐘信號線,主要控制時序。相當於整個SPI協議是以SCK為準進行的。因此SCK的控制在每次發送中只能在主機的控制下進行,從機不可控制。
SDI/SDO: 通訊是通過數據交換完成的,這裡先要知道SPI是串列通訊協議,也就是說數據是一位一位的傳輸的。SDO為主機發送,從機接收;SDI為主機接受,從機發送。
參考網站:http://dlnware.com/theory/SPI-Bus
SPI的四種模式
模式 | CPOL | CPHA |
MODE0 | 0 | 0 |
MODE1 | 0 | 1 |
MODE2 | 1 | 0 |
MODE3 | 1 | 1 |
參考網站:http://dlnware.com/theory/SPI-Transfer-Modes
在這四種模式中,我們常用MODE0和MODE2。因為它便於操作。我便是使用的MODE2模式。這四種模式的區別在參考網站中有詳細的描述,這裡便不再贅述。
在MODE2模式下。時鐘在空閑時始終置1,每產生一次下降沿便會發送1 bit 數據。大家可能已經想到,SPI協議可以在八位沒有發出送完的情況下停止發送。
這裡我跑了下示波器。
從圖中清晰可見8個下降沿,時鐘在空閑時始終置1。
其餘的三個模式以此類推。
軟體模擬
我使用的單片機為STC89C52,內部沒有SPI的資源,因此需要自己進行軟體模擬。
利用串口中斷,首先利用電腦A得串口助手發送的數據存入SBUF,再將SBUF的值通過SPI的SDO發送給從機的SDI接收,並存入從機的SBUF,顯示在電腦B的串口助手上。
目的:電腦A發送數據,如:AB,電腦B可接收到AB。
如圖:
PS:在此項目中CS(片選)可以不用。
代碼
# include <reg52.h>//頭文件 # include <intrins.h>//頭文件 # define uchar unsigned char # define uint unsigned int sbit SCK = P1^0;//位定義時鐘 //sbit CS = P1^1;//位定義片選(使能) 此項目可以不使用 sbit SDI = P1^2;//位定義Input sbit SDO = P1^3;//位定義Output /*-----函數聲明-----*/ void delay5us(); void SpiSend(uchar dat1); uchar SpiReceive(); void UARTInit(); /*-----主函數-----*/ void main() { UARTInit(); while(1) { SBUF = SpiReceive();// 迴圈接收數據 } ;//空語句 } /*-----5微秒延時函數-----*/ void delay5us() { _nop_(); } /*-----CPHA=0;CPOL=1 模式2-----*/ /*-----SPI發送函數-----*/ /*-----上升沿發送-----*/ void SpiSend(uchar dat1) { uchar i; for (i=0; i<8; ++i)//8bit,一位一位寫 { SCK = 0; if (dat1 & 0x80)//判斷當前最高位為1還是0 { SDO = 1; } else { SDO = 0; } SCK = 1;//上升沿發送數據 dat1 <<= 1; delay5us(); } } /*-----SPI接收函數-----*/ /*-----下降沿接收-----*/ uchar SpiReceive() { uchar i, dat0; dat0 = 0x00;//dat0初始化 for (i=0; i<8; ++i)//8bit,一位一位讀 { dat0 <<= 1; while(SCK == 1); while(SCK == 0);//等待下降沿,下降沿讀取數據 dat0 |= SDI; } return (dat0);//收到數據(返回值)dat0 } /*-----串口(中斷)初始化-----*/ void UARTInit() { EA = 1;//開啟總中斷 ES = 1;//打開串口中斷 SM0 = 0;SM1 = 1;//串口工作方式1,8位UART波特率可變 REN = 1;//串口允許接收 TR1 = 1;//啟動定時器1 TMOD |= 0X20;//定時器1,工作模式2 8位自動重裝 TH1 =0XFD; TL1 =0XFD;//設置波特率9600 } /*-----串口中斷服務函數-----*/ void UART() interrupt 4 { if (RI)//判斷是否接收完成 { RI = 0;//軟體清零 SpiSend(SBUF); // 轉發接收到的數據 } if (TI)//判斷是否發送完成 { TI = 0;//軟體清零 } }
PS:SDI和SDO需交叉連接。
總結
- 在發送數據時,時鐘僅由發送端(主機)控制;
- SPI四種模式,只需將主從機同步一種模式即可;
- SCK,SDI,SDO,CS四個引腳由自己定義即可。