開(kāi)工了,開(kāi)工了,新年新氣象,軟件和硬件SPI方式都有,可以參考
一、SPI的通信原理
SPI的通信原理很簡(jiǎn)單,它以主從方式工作,這種模式通常有一個(gè)主設(shè)備和一個(gè)或多個(gè)從設(shè)備,需要至少4根線,事實(shí)上3根也可以(單向傳輸時(shí))。也是所有基于SPI的設(shè)備共有的,它們是SDI(數(shù)據(jù)輸入),SDO(數(shù)據(jù)輸出),SCK(時(shí)鐘),CS(片選)。
- SDO – 主設(shè)備數(shù)據(jù)輸出,從設(shè)備數(shù)據(jù)輸入 對(duì)應(yīng)MOSI master output slave input
- SDI – 主設(shè)備數(shù)據(jù)輸入,從設(shè)備數(shù)據(jù)輸出 對(duì)應(yīng)MISO master input slave output
- SCLK – 時(shí)鐘信號(hào),由主設(shè)備產(chǎn)生
- CS – 從設(shè)備使能信號(hào),由主設(shè)備控制
CS: 其中CS是控制芯片是否被選中的,也就是說(shuō)只有片選信號(hào)為預(yù)先規(guī)定的使能信號(hào)時(shí)(高電位或低電位),對(duì)此芯片的操作才有效,這就允許在同一總線上連接多個(gè)SPI設(shè)備成為可能。
SDI/SDO/SCLK: 通訊是通過(guò)數(shù)據(jù)交換完成的,這里先要知道SPI是串行通訊協(xié)議,也就是說(shuō)數(shù)據(jù)是一位一位的傳輸?shù)?。這就是SCK時(shí)鐘線存在的原因,由SCK提供時(shí)鐘脈沖,SDI,SDO則基于此脈沖完成數(shù)據(jù)傳輸。數(shù)據(jù)輸出通過(guò) SDO線,數(shù)據(jù)在時(shí)鐘上升沿或下降沿時(shí)改變,在緊接著的下降沿或上升沿被讀取。完成一位數(shù)據(jù)傳輸,輸入也使用同樣原理。這樣,在至少8次時(shí)鐘信號(hào)的改變(上沿和下沿為一次),就可以完成8位數(shù)據(jù)的傳輸。
要注意的是,SCK信號(hào)線只由主設(shè)備控制,從設(shè)備不能控制信號(hào)線。同樣,在一個(gè)基于SPI的設(shè)備中,至少有一個(gè)主控設(shè)備。
這樣傳輸?shù)奶攸c(diǎn):這樣的傳輸方式有一個(gè)優(yōu)點(diǎn),與普通的串行通訊不同,普通的串行通訊一次連續(xù)傳送至少8位數(shù)據(jù),而SPI允許數(shù)據(jù)一位一位的傳送,甚至允許暫停,因?yàn)镾CK時(shí)鐘線由主控設(shè)備控制,當(dāng)沒(méi)有時(shí)鐘跳變時(shí),從設(shè)備不采集或傳送數(shù)據(jù),也就是說(shuō),主設(shè)備通過(guò)對(duì)SCK時(shí)鐘線的控制可以完成對(duì)通訊的控制。SPI還是一個(gè)數(shù)據(jù)交換協(xié)議:因?yàn)镾PI的數(shù)據(jù)輸入和輸出線獨(dú)立,所以允許同時(shí)完成數(shù)據(jù)的輸入和輸出。不同的SPI設(shè)備的實(shí)現(xiàn)方式不盡相同,主要是數(shù)據(jù)改變和采集的時(shí)間不同,在時(shí)鐘信號(hào)上沿或下沿采集有不同定義,具體請(qǐng)參考相關(guān)器件的文檔。
在點(diǎn)對(duì)點(diǎn)的通信中,SPI接口不需要進(jìn)行尋址操作,且為全雙工通信,顯得簡(jiǎn)單高效。在多個(gè)從設(shè)備的系統(tǒng)中,每個(gè)從設(shè)備需要獨(dú)立的使能信號(hào),硬件上比I2C系統(tǒng)要稍微復(fù)雜一些。
最后,SPI接口的一個(gè)缺點(diǎn):沒(méi)有指定的流控制,沒(méi)有應(yīng)答機(jī)制確認(rèn)是否接收到數(shù)據(jù)。
再看看 SPI 通訊的通訊時(shí)序,見(jiàn)圖 SPI 通訊時(shí)序。
二、SPI的通信模式
經(jīng)常忘記SPI4種工作模式,學(xué)了忘了,現(xiàn)在記下方便以后查閱。在芯片資料上極性和相位一般表示為CPOL(Clock POLarity)和CPHA(Clock PHAse), 極性和相位組合成4種工作模式。
CPOL CPHA
MODE0 0 0
MODE1 0 1
MODE2 1 0
MODE3 1 1
CPOL: SPI空閑時(shí)的時(shí)鐘信號(hào)電平(1:高電平, 0:低電平)CPHA: SPI在時(shí)鐘第幾個(gè)邊沿采樣(1:第二個(gè)邊沿開(kāi)始, 0:第一個(gè)邊沿開(kāi)始)MODE0和MODE3最常用。
我們結(jié)合實(shí)例分析SPI模塊通信,采用W25Q64,看手冊(cè)如何選擇通信模式
我們只能采用模式0和3,這是spi讀數(shù)據(jù)
/*
*********************************************************************************************************
* 函 數(shù) 名: bsp_spiRead1
* 功能說(shuō)明: 從SPI總線接收8個(gè)bit數(shù)據(jù)。 SCK上升沿采集數(shù)據(jù), SCK空閑時(shí)為高電平
* 形 參: 無(wú)
* 返 回 值: 無(wú)
*********************************************************************************************************
*/
uint8_t bsp_spiRead1(void)
{
#ifdef SOFT_SPI /* 軟件SPI */
uint8_t i;
uint8_t read = 0;
for (i = 0; i < 8; i++)
{
SCK_0();
bsp_spiDelay();
read = read << 1;
if (MISO_IS_HIGH())
{
read++;
}
SCK_1();
bsp_spiDelay();
}
return read;
#endif
#ifdef HARD_SPI /* 硬件SPI */
uint8_t read;
/* 等待發(fā)送緩沖區(qū)空 */
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
/* 發(fā)送一個(gè)字節(jié) */
SPI_I2S_SendData(SPI1, 0);
/* 等待數(shù)據(jù)接收完畢 */
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
/* 讀取接收到的數(shù)據(jù) */
read = SPI_I2S_ReceiveData(SPI1);
/* 返回讀到的數(shù)據(jù) */
return read;
#endif
}
這是寫數(shù)據(jù)
/*
*********************************************************************************************************
* 函 數(shù) 名: bsp_spiWrite0
* 功能說(shuō)明: 向SPI總線發(fā)送一個(gè)字節(jié)。SCK上升沿采集數(shù)據(jù), SCK空閑時(shí)為低電平。
* 形 參: 無(wú)
* 返 回 值: 無(wú)
*********************************************************************************************************
*/
void bsp_spiWrite0(uint8_t _ucByte)
{
#ifdef SOFT_SPI /* 軟件SPI */
uint8_t i;
for(i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
MOSI_1();
}
else
{
MOSI_0();
}
bsp_spiDelay();
SCK_1();
_ucByte <<= 1;
bsp_spiDelay();
SCK_0();
}
bsp_spiDelay();
#endif
#ifdef HARD_SPI /* 硬件SPI */
/* 等待發(fā)送緩沖區(qū)空 */
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
/* 發(fā)送一個(gè)字節(jié) */
SPI_I2S_SendData(SPI1, _ucByte);
/* 等待數(shù)據(jù)接收完畢 */
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
/* 讀取接收到的數(shù)據(jù) */
SPI_I2S_ReceiveData(SPI1);
#endif
}
/*
*********************************************************************************************************
* 函 數(shù) 名: bsp_spiRead0
* 功能說(shuō)明: 從SPI總線接收8個(gè)bit數(shù)據(jù)。 SCK上升沿采集數(shù)據(jù), SCK空閑時(shí)為低電平。
* 形 參: 無(wú)
* 返 回 值: 讀到的數(shù)據(jù)
*********************************************************************************************************
*/
uint8_t bsp_spiRead0(void)
{
#ifdef SOFT_SPI /* 軟件SPI */
uint8_t i;
uint8_t read = 0;
for (i = 0; i < 8; i++)
{
read = read<<1;
if (MISO_IS_HIGH())
{
read++;
}
SCK_1();
bsp_spiDelay();
SCK_0();
bsp_spiDelay();
}
return read;
#endif
#ifdef HARD_SPI /* 硬件SPI */
uint8_t read;
/* 等待發(fā)送緩沖區(qū)空 */
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
/* 發(fā)送一個(gè)字節(jié) */
SPI_I2S_SendData(SPI1, 0);
/* 等待數(shù)據(jù)接收完畢 */
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
/* 讀取接收到的數(shù)據(jù) */
read = SPI_I2S_ReceiveData(SPI1);
/* 返回讀到的數(shù)據(jù) */
return read;
#endif
}
具體資料在配套資料