SPI通信的基础知识
1 SPI物理層
SPI通信設(shè)備之間常用物理連接方式如下圖
?SPI通訊使用3條總線及片選線,3條總線分別為SCK、MOSI、MISO,片選線為CS。
?CS:從設(shè)備選擇信號線,常稱為片選信號線,也稱為NSS。當(dāng)有多個SPI從設(shè)備與SPI主機(jī)相連時,設(shè)備的其它信號線SCK、MOSI及MISO同時并聯(lián)到相同的SPI總線上,即無論有多少個從設(shè)備,都共同只使用這3條總線;而每個從設(shè)備都有獨(dú)立的這一條CS信號線,本信號線獨(dú)占主機(jī)的一個引腳,即有多少個從設(shè)備,就有多少條片選信號線。SPI協(xié)議中沒有設(shè)備地址,它使用CS信號線來尋址,當(dāng)主機(jī)要選擇從設(shè)備時,把該從設(shè)備的CS信號線設(shè)置為低電平,該從設(shè)備即被選中,即片選有效,接著主機(jī)開始與被選中的從設(shè)備進(jìn)行SPI通訊。所以SPI通訊以CS線置低電平為開始信號,以NSS線被拉高作為結(jié)束信號。
?SCK:時鐘信號線,用于通訊數(shù)據(jù)同步。它由通訊主機(jī)產(chǎn)生,決定了通訊的速率,不同的設(shè)備支持的最高時鐘頻率不一樣,如STM32的SPI時鐘頻率最大為fpclk/2,兩個設(shè)備之間通訊時,通訊速率受限于低速設(shè)備。
在NRF52832中
typedef enum
{
NRF_DRV_SPI_FREQ_125K = NRF_SPI_FREQ_125K, ///< 125 kbps.
NRF_DRV_SPI_FREQ_250K = NRF_SPI_FREQ_250K, ///< 250 kbps.
NRF_DRV_SPI_FREQ_500K = NRF_SPI_FREQ_500K, ///< 500 kbps.
NRF_DRV_SPI_FREQ_1M = NRF_SPI_FREQ_1M, ///< 1 Mbps.
NRF_DRV_SPI_FREQ_2M = NRF_SPI_FREQ_2M, ///< 2 Mbps.
NRF_DRV_SPI_FREQ_4M = NRF_SPI_FREQ_4M, ///< 4 Mbps.
NRF_DRV_SPI_FREQ_8M = NRF_SPI_FREQ_8M ///< 8 Mbps.
} nrf_drv_spi_frequency_t;
nrf_drv_spi_frequency_t可以用來配置SPI的通信速率。
?MOSI (Master Output, Slave Input)主設(shè)備輸出/從設(shè)備輸入引腳。主機(jī)的數(shù)據(jù)從這條信號線輸出,從機(jī)由這條信號線讀入主機(jī)發(fā)送的數(shù)據(jù),即這條線上數(shù)據(jù)的方向?yàn)橹鳈C(jī)到從機(jī)。
?MISO(Master Input,,Slave Output):主設(shè)備輸入/從設(shè)備輸出引腳。主機(jī)從這條信號線讀入數(shù)據(jù),從機(jī)的數(shù)據(jù)由這條信號線輸出到主機(jī),即在這條線上數(shù)據(jù)的方向?yàn)閺臋C(jī)到主機(jī)。
2 協(xié)議層
2.1 SPI基本通信過程
?這是一個主機(jī)的通訊時序。NSS、SCK、MOSI信號都由主機(jī)控制產(chǎn)生,而MISO的信號由從機(jī)產(chǎn)生,主機(jī)通過該信號線讀取從機(jī)的數(shù)據(jù)。MOSI與MISO的信號只在NSS為低電平的時候才有效,在SCK的每個時鐘周期MOSI和MISO傳輸一位數(shù)據(jù)。
?在上圖中的標(biāo)號1處,NSS信號線由高變低,是SPI通訊的起始信號。NSS是每個從機(jī)各自獨(dú)占的信號線,當(dāng)從機(jī)檢在自己的NSS線檢測到起始信號后,就知道自己被主機(jī)選中了,開始準(zhǔn)備與主機(jī)通訊。在圖中的標(biāo)號?處,NSS信號由低變高,是SPI通訊的停止信號,表示本次通訊結(jié)束,從機(jī)的選中狀態(tài)被取消。
?SPI使用MOSI及MISO信號線來傳輸數(shù)據(jù),使用SCK信號線進(jìn)行數(shù)據(jù)同步。MOSI及MISO數(shù)據(jù)線在SCK的每個時鐘周期傳輸一位數(shù)據(jù),且數(shù)據(jù)輸入輸出是同時進(jìn)行的。數(shù)據(jù)傳輸時,MSB先行或LSB先行并沒有作硬性規(guī)定,但要保證兩個SPI通訊設(shè)備之間使用同樣的協(xié)定,一般都會采用上圖中的MSB先行模式。
?觀察圖中的2,3,4,5標(biāo)號處,MOSI及MISO的數(shù)據(jù)在SCK的上升沿期間變化輸出,在SCK的下降沿時被采樣。即在SCK的下降沿時刻,MOSI及MISO的數(shù)據(jù)有效,高電平時表示數(shù)據(jù)“1”,為低電平時表示數(shù)據(jù)“0”。在其它時刻,數(shù)據(jù)無效,MOSI及MISO為下一次表示數(shù)據(jù)做準(zhǔn)備。
?SPI每次數(shù)據(jù)傳輸可以8位或16位為單位,每次傳輸?shù)膯挝粩?shù)不受限制。
3 SPI的模式
?上面講述的圖中的時序只是SPI中的其中一種通訊模式,SPI一共有四種通訊模式,它們的主要區(qū)別是總線空閑時SCK的時鐘狀態(tài)以及數(shù)據(jù)采樣時刻。為方便說明,在此引入“時鐘極性CPOL”和“時鐘相位CPHA”的概念。
?時鐘極性CPOL是指SPI通訊設(shè)備處于空閑狀態(tài)時,SCK信號線的電平信號(即SPI通訊開始前、 NSS線為高電平時SCK的狀態(tài))。CPOL=0時, SCK在空閑狀態(tài)時為低電平,CPOL=1時,則相反。
?時鐘相位CPHA是指數(shù)據(jù)的采樣的時刻,當(dāng)CPHA=0時,MOSI或MISO數(shù)據(jù)線上的信號將會在SCK時鐘線的“奇數(shù)邊沿”被采樣。當(dāng)CPHA=1時,數(shù)據(jù)線在SCK的“偶數(shù)邊沿”采樣。
?我們來分析這個CPHA=0的時序圖。首先,根據(jù)SCK在空閑狀態(tài)時的電平,分為兩種情況。SCK信號線在空閑狀態(tài)為低電平時,CPOL=0;空閑狀態(tài)為高電平時,CPOL=1。
?無論CPOL=0還是=1,因?yàn)槲覀兣渲玫臅r鐘相位CPHA=0,在圖中可以看到,采樣時刻都是在SCK的奇數(shù)邊沿。注意當(dāng)CPOL=0的時候,時鐘的奇數(shù)邊沿是上升沿,而CPOL=1的時候,時鐘的奇數(shù)邊沿是下降沿。所以SPI的采樣時刻不是由上升/下降沿決定的。MOSI和MISO數(shù)據(jù)線的有效信號在SCK的奇數(shù)邊沿保持不變,數(shù)據(jù)信號將在SCK奇數(shù)邊沿時被采樣,在非采樣時刻,MOSI和MISO的有效信號才發(fā)生切換。
?類似地,當(dāng)CPHA=1時,不受CPOL的影響,數(shù)據(jù)信號在SCK的偶數(shù)邊沿被采樣,見上圖。
?由CPOL及CPHA的不同狀態(tài),SPI分成了四種模式,見下表,主機(jī)與從機(jī)需要工作在相同的模式下才可以正常通訊,實(shí)際中采用較多的是“模式0”與“模式3”。
3 軟件模擬
模式0
void SPI_write_read_mode1(unsigned int n_Data)
{
unsigned char x = 0x00;
unsigned int data;
SPI_CS = 0;
delay_us(10);
for(x = 0x00;x < 8;x ++)
{
SPI_SCK = 0; //數(shù)據(jù)發(fā)送
if((n_Data & 0x80) == 0x80)
SPI_MOSI = 1;
else SPI_MOSI = 0;
n_Data = n_Data << 1;
SPI_SCK = 1; //數(shù)據(jù)接收
data<<=1;
if(SPI_MISO)
{data++;}
SPI_SCK = 0;
}
}
模式1
void SPI_write_read_mode2(unsigned int n_Data)
{
unsigned char x = 0x00;
unsigned int data;
SPI_CS = 0;
delay_us(10);
for(x = 0x00;x < 8;x ++)
{
SPI_SCK = 1; //數(shù)據(jù)發(fā)送
if((n_Data & 0x80) == 0x80)
SPI_MOSI = 1;
else SPI_MOSI = 0;
n_Data = n_Data << 1;
SPI_SCK = 0; //數(shù)據(jù)接收
data<<=1;
if(SPI_MISO)
{data++;}
SPI_SCK = 0;
}
}
模式2
void SPI_write_read_mode3(unsigned int n_Data)
{
unsigned char x = 0x00;
unsigned int data;
SPI_CS = 0;
delay_us(10);
for(x = 0x00;x < 8;x ++)
{
SPI_SCK = 1; //數(shù)據(jù)發(fā)送
if((n_Data & 0x80) == 0x80)
SPI_MOSI = 1;
else SPI_MOSI = 0;
n_Data = n_Data << 1;
SPI_SCK = 0; //數(shù)據(jù)接收
data<<=1;
if(SPI_MISO)
{data++;}
SPI_SCK = 0;
}
SPI_SCK = 1;
}
模式3
void SPI_write_read_mode4(unsigned int n_Data)
{
unsigned char x = 0x00;
unsigned int data;
SPI_CS = 0;
delay_us(10);
for(x = 0x00;x < 8;x ++)
{
SPI_SCK = 0; //數(shù)據(jù)發(fā)送
if((n_Data & 0x80) == 0x80)
SPI_MOSI = 1;
else SPI_MOSI = 0;
n_Data = n_Data << 1;
SPI_SCK = 1; //數(shù)據(jù)接收
data<<=1;
if(SPI_MISO)
{data++;}
SPI_SCK = 0;
}
SPI_SCK = 1;
}
參考資料:
1《STM32庫開發(fā)實(shí)戰(zhàn)指南-基于STM32F4》 劉火良,楊森編著 機(jī)械工業(yè)出版社
2 軟件模擬SPI部分的程序參考了公眾號“ Bernice堅(jiān)果丁” https://mp.weixin.qq.com/s/SqKvReXRXCrqIj-G78I9CA
總結(jié)
以上是生活随笔為你收集整理的SPI通信的基础知识的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言 · 选择排序
- 下一篇: C#中int和IntPtr相互转换