STM32---SPI(DMA)通信的總結(jié)(庫函數(shù)操作)
本文主要由7項內(nèi)容介紹SPI并會在最后附上測試源碼供參考:
1.SPI的通信協(xié)議
2.SPI通信初始化(以STM32為從機,LPC1114為主機介紹)
3.SPI的讀寫函數(shù)
4.SPI的中斷配置
5.SPI的SMA操作
6.測試源碼
7.易出現(xiàn)的問題及原因和解決方法
一、SPI的通信協(xié)議
SPI(Serial Peripheral Interface)是一種串行同步通訊協(xié)議,由一個主設備和一個或多個從設備組成,主設備啟動一個與從設備的同步通訊,從而完成數(shù)據(jù)的交換。SPI接口一般由4根線組成,CS片選信號(有的單片機上也稱為NSS),SCLK時鐘信號線,MISO數(shù)據(jù)線(主機輸入從機輸出),MOSI數(shù)據(jù)線(主機輸出從機輸入),CS決定了唯一的與主設備通信的從設備,如沒有CS信號,則只能存在一個從設備,主設備通過產(chǎn)生移位時鐘信號來發(fā)起通訊。通訊時主機的數(shù)據(jù)由MISO輸入,由MOSI輸出,輸入的數(shù)據(jù)在時鐘的上升或下降沿被采樣,輸出數(shù)據(jù)在緊接著的下降或上升沿被發(fā)出(具體由SPI的時鐘相位和極性的設置而決定)。
二、以STM32為例介紹SPI通信
1.STM32f103帶有3個SPI模塊其特性如下:
2SPI初始化
初始化SPI主要是對SPI要使用到的引腳以及SPI通信協(xié)議中時鐘相位和極性進行設置,其實STM32的工程師已經(jīng)幫我們做好了這些工作,調(diào)用庫函數(shù),根據(jù)自己的需要來修改其中的參量來完成自己的配置即可,主要的配置是如下幾項:
l引腳的配置
SPI1的SCLK, MISO ,MOSI分別是PA5,PA6,PA7引腳,這幾個引腳的模式都配置成GPIO_Mode_AF_PP復用推挽輸出(關于GPIO的8種工作模式如不清楚請自己百度,在此不解釋),如果是單主單從,CS引腳可以不配置,都設置成軟件模式即可。
l通信參數(shù)的設置
1.SPI_Direction_2Lines_FullDuplex把SPI設置成全雙工通信;
2.在SPI_Mode里設置你的模式(主機或者從機),
3.SPI_DataSize是來設置數(shù)據(jù)傳輸?shù)膸袷降腟PI_DataSize_8b是指8位數(shù)據(jù)幀格式,也可以設置為SPI_DataSize_16b,即16位幀格式
4.SPI_CPOL和SPI_CPHA是兩個很重要的參數(shù),是設置SPI通信時鐘的極性和相位的,一共有四種模式
在庫函數(shù)中CPOL有兩個值SPI_CPOL_High(=1)和SPI_CPOL_Low ( =0).
CPHA有兩個值SPI_CPHA_1Edge (=0)和SPI_CPHA_2Edge(=1)
CPOL表示時鐘在空閑狀態(tài)的極性是高電平還是低電平,而CPHA則表示數(shù)據(jù)是在什么時刻被采樣的,手冊中如下:
我的程序中主、從機的這兩位設置的相同都是設置成1,即空閑時時鐘是高電平,數(shù)據(jù)在第二個時鐘沿被采樣,實驗顯示數(shù)據(jù)收發(fā)都正常。
(要特別注意極性和相位的設置否則,數(shù)據(jù)傳輸會出現(xiàn)錯位的現(xiàn)象)
一般主從機的這兩個位要設置的一樣,但是網(wǎng)上也有人說不能設置成一樣的,在后文中我對主從機極性和相位的配置的16種情況都做了測試,結(jié)果見下文。
下圖很好的描述了4種模式下的時序狀況
引用網(wǎng)友的一句話::
“SPI主模塊和與之通信的外設備時鐘相位和極性應該一致。個人理解這句話有2層意思:其一,主設備SPI時鐘和極性的配置應該由外設的從設備來決定;其二,二者的配置應該保持一致,即主設備的SDO同從設備的SDO配置一致,主設備的SDI同從設備的SDI配置一致。因為主從設備是在SCLK的控制下,同時發(fā)送和接收數(shù)據(jù),并通過2個雙向移位寄存器來交換數(shù)據(jù)?!?/p>
5.SPI_BaudRatePrescaler波特率的設置
這在主機模式中,這一位的設置直接決定了通信的傳輸速率,而從機的設置不會影響數(shù)據(jù)傳輸?shù)乃俾剩謨灾杏羞@樣一句話:
(實際測試中發(fā)現(xiàn):當STM32作為從機時,它對波特率的設置會影響數(shù)據(jù)的通信,特別是在大數(shù)據(jù)兩傳輸時,當主機SPI時鐘設置為15M時,STM32從機如果是2分頻即18M則會在多次傳輸時出現(xiàn)錯誤,我記得在資料中看到過有前輩的經(jīng)驗貼說SPI從機的時鐘設置不能高于SPI主機的時鐘設置,雖然理論上從機的時鐘設置不影響SPI通信,但是在試驗中我也驗證,當STM32從機時鐘設為4分頻9M,低于15M時,通信就不會出現(xiàn)問題。所以SPI從機波特率的設置最好低于SPI主機波特率的設置。)
6.SPI_FirstBit這一位是設置首先傳輸?shù)母咦止?jié)還是低字節(jié)
SPI_FirstBit_MSB是先傳輸高字節(jié),SPI_FirstBit_LSB是先傳輸?shù)妥止?jié)
注意在初始化函數(shù)里還有兩項重要的內(nèi)容就是在初始化之前先使能SPI的時鐘和在初始化配置完成后使能SPI。
(………..初始化配置……………)
三、SPI的讀寫函數(shù)
SPI有一個16位的數(shù)據(jù)寄存器SPI_DR,它對應兩個緩沖區(qū),1個發(fā)送緩沖區(qū),1個接收緩沖區(qū),當在控制寄存器里SPI_CR1里對DFF位設置數(shù)據(jù)幀格式為8位時,發(fā)送和接收只用到SPI_DR[7:0]這8位,15-8位被強制為0,幀格式設置成16位時全用。
讀寫過程在手冊中是這樣描述的:
簡而言之,
發(fā)送時,可以通過檢測SPI_SR中的TXE位,當數(shù)據(jù)寄存器里有數(shù)據(jù)時,TXE位是0,當數(shù)據(jù)全部從數(shù)據(jù)寄存器的發(fā)送緩沖區(qū)傳輸?shù)揭莆患拇嫫鲿rTXE位被置1,這時候可以再往數(shù)據(jù)寄存器里寫入數(shù)據(jù)??梢酝ㄟ^
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)來檢測。
SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)是庫函數(shù),可以檢測SPI的一些狀態(tài)位。
接收時,可以通過檢測SPI_SR中的RXNE位,當數(shù)據(jù)寄存器里有數(shù)據(jù)時,RXNE位是0,當數(shù)據(jù)全部從數(shù)據(jù)寄存器的接收緩沖區(qū)傳輸?shù)揭莆患拇嫫鲿rRXNE位被置1,這時候可以從數(shù)據(jù)寄存器里讀出數(shù)據(jù)??梢酝ㄟ^
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);來檢測。源程序如下,
SPI讀寫一個字節(jié),讀寫一體
當能成功發(fā)送和接收一個字節(jié)時,發(fā)送數(shù)組數(shù)據(jù)就變的簡單了,只需要一個for循環(huán),和指針變量的遞增即可。以下僅為參考:
(有一點特別注意,從機數(shù)據(jù)傳輸時要依賴主機的時鐘,所以主機在接收從機發(fā)送的數(shù)據(jù)時要往從機發(fā)送啞巴字節(jié),這個字節(jié)可以自己定義0xff,0xfe等什么字節(jié)都可以)
讀寫分開的函數(shù):
void SPI_Ecah_Buffer_Send(u8* pBuffer, u16 NumByteToRead)
{
for(int i = 0; i < NumByteToRead; i++)
{
SPI_Conmunication_SendByte(*pBuffer);
pBuffer++;
}
}
void SPI_Buffer_Receive(u8* pBuffer, u16 NumByteToRead)
{
while (NumByteToRead--)
{
*pBuffer = SPI_Conmunication_SendByte (Dummy_Byte);
pBuffer++;
}
}
讀寫一體的函數(shù)
void SPI_Ecah_Buffer_Send(u8* str , u8* pBuffer, u16 NumByteToRead)
{
for(int i = 0; i < NumByteToRead; i++)
{
*str = SPI_Conmunication_SendByte(*pBuffer);
pBuffer++;
str++;
}
}
四、SPI的中斷配置
在SPI的SPI_CR2中可以配置,STM32的SPI的通信一共有8個中斷其中最常用的是如下4個。
TXEIE:發(fā)送緩沖區(qū)空中斷使能
在發(fā)送過程中,數(shù)據(jù)全部從數(shù)據(jù)寄存器的發(fā)送緩沖區(qū)傳輸?shù)揭莆患拇嫫鲿rTXE位被置1這時如果使能了TXEIE就會觸發(fā)發(fā)送完成的中斷請求。在中斷服務函數(shù)里可以做你想做的事情,也可以用一個標志位,在外面完成相應的操作。
(使用中斷時要特別注意,及時的清除中斷標志,為下一次能夠觸發(fā)中斷做準備。而清除中斷的操作可以放在中斷服務函數(shù)中,或者其他你認為何時的地方。)
RXNEIE:接收緩沖區(qū)非空中斷使能
接收同發(fā)送。
TXDMAEN:發(fā)送緩沖區(qū)DMA使能
RXDMAEN:接收緩沖區(qū)DMA使能
手冊中有這樣一句話,“不能同時設置TXEIE和TXDMAEN”這一點要特別注意。也就是說如果你在SPI的通信中不用DMA則使能TXEIE的中斷,禁能TXDMAEN的中斷,如果在SPI中使用DMA傳輸,則禁能TXEIE的中斷,只使能TXDMAEN的中斷。
五、SPI的DMA操作
DMA(Direct Memory Access)直接內(nèi)存存取,直接存儲器存取用來提供在外