基于STM32的SPI基本介绍
STM32---SPI(DMA)通信的總結(庫函數操作)
本文主要由7項內容介紹SPI并會在最后附上測試源碼供參考:
1.?????SPI的通信協議
2.?????SPI通信初始化(以STM32為從機,LPC1114為主機介紹)
3.?????SPI的讀寫函數
4.?????SPI的中斷配置
5.?????SPI的SMA操作
6.?????測試源碼
7.?????易出現的問題及原因和解決方法
一、?????SPI的通信協議
SPI(Serial Peripheral Interface)是一種串行同步通訊協議,由一個主設備和一個或多個從設備組成,主設備啟動一個與從設備的同步通訊,從而完成數據的交換。SPI?接口一般由4根線組成,CS片選信號(有的單片機上也稱為NSS),SCLK時鐘信號線,MISO數據線(主機輸入從機輸出),MOSI數據線(主機輸出從機輸入),CS?決定了唯一的與主設備通信的從設備,如沒有CS?信號,則只能存在一個從設備,主設備通過產生移位時鐘信號來發起通訊。通訊時主機的數據由MISO輸入,由MOSI?輸出,輸入的數據在時鐘的上升或下降沿被采樣,輸出數據在緊接著的下降或上升沿被發出(具體由SPI的時鐘相位和極性的設置而決定)。
?
?
?
二、?????以STM32為例介紹SPI通信
1.?????STM32f103?帶有3個SPI模塊其特性如下:
?
2???????SPI?初始化
初始化SPI?主要是對SPI要使用到的引腳以及SPI通信協議中時鐘相位和極性進行設置,其實STM32的工程師已經幫我們做好了這些工作,調用庫函數,根據自己的需要來修改其中的參量來完成自己的配置即可,主要的配置是如下幾項:
l??引腳的配置
?SPI1的SCLK, MISO ,MOSI分別是PA5,PA6,PA7引腳,這幾個引腳的模式都配置成GPIO_Mode_AF_PP?復用推挽輸出(關于GPIO的8種工作模式如不清楚請自己百度,在此不解釋),如果是單主單從,CS引腳可以不配置,都設置成軟件模式即可。
?
l??通信參數的設置
?
?
1.?????SPI_Direction_2Lines_FullDuplex把SPI設置成全雙工通信;
2.?????在SPI_Mode?里設置你的模式(主機或者從機),
3.?????SPI_DataSize是來設置數據傳輸的幀格式的SPI_DataSize_8b是指8位數據幀格式,也可以設置為SPI_DataSize_16b,即16位幀格式
4.?????SPI_CPOL和SPI_CPHA是兩個很重要的參數,是設置SPI通信時鐘的極性和相位的,一共有四種模式
?
?
在庫函數中?CPOL有兩個值SPI_CPOL_High(=1)和SPI_CPOL_Low ( =0).
CPHA有兩個值SPI_CPHA_1Edge (=0)?和SPI_CPHA_2Edge(=1)
CPOL表示時鐘在空閑狀態的極性是高電平還是低電平,而CPHA則表示數據是在什么時刻被采樣的,手冊中如下:
?
?
我的程序中主、從機的這兩位設置的相同都是設置成1,即空閑時時鐘是高電平,數據在第二個時鐘沿被采樣,實驗顯示數據收發都正常。
(要特別注意極性和相位的設置否則,數據傳輸會出現錯位的現象)
一般主從機的這兩個位要設置的一樣,但是網上也有人說不能設置成一樣的,在后文中我對主從機極性和相位的配置的16種情況都做了測試,結果見下文。
下圖很好的描述了4種模式下的時序狀況
?
?
?
引用網友的一句話::
?
“?SPI主模塊和與之通信的外設備時鐘相位和極性應該一致。個人理解這句話有?2層意思:其一,主設備SPI時鐘和極性的配置應該由外設的從設備來決定;其二,二者的配置應該保持一致,即主設備的SDO同從設備的SDO配置一致,主設備的?SDI同從設備的SDI配置一致。因為主從設備是在SCLK的控制下,同時發送和接收數據,并通過2個雙向移位寄存器來交換數據。?”
5.?????SPI_BaudRatePrescaler??波特率的設置
這在主機模式中,這一位的設置直接決定了通信的傳輸速率,而從機的設置不會影響數據傳輸的速率,手冊中有這樣一句話:
?
?
(實際測試中發現:當STM32作為從機時,它對波特率的設置會影響數據的通信,特別是在大數據兩傳輸時,當主機SPI時鐘設置為15M時,STM32從機如果是2分頻即18M則會在多次傳輸時出現錯誤,我記得在資料中看到過有前輩的經驗貼說SPI從機的時鐘設置不能高于SPI主機的時鐘設置,雖然理論上從機的時鐘設置不影響SPI通信,但是在試驗中我也驗證,當STM32從機時鐘設為4分頻?9M,低于15M時,通信就不會出現問題。所以SPI從機波特率的設置最好低于SPI主機波特率的設置。)
6.?????SPI_FirstBit?這一位是設置首先傳輸的高字節還是低字節
SPI_FirstBit_MSB?是先傳輸高字節,SPI_FirstBit_LSB?是先傳輸低字節
注意在初始化函數里還有兩項重要的內容就是在初始化之前先使能SPI的時鐘和在初始化配置完成后使能SPI。
?
?
(………..初始化配置……………)
?
?
?
三、?????SPI的讀寫函數
SPI有一個16位的數據寄存器SPI_DR,它對應兩個緩沖區,1個發送緩沖區,1個接收緩沖區,當在控制寄存器里SPI_CR1里對DFF位設置數據幀格式為8位時,發送和接收只用到SPI_DR[7:0]這8位,15-8位被強制為0,幀格式設置成16位時全用。
?
讀寫過程在手冊中是這樣描述的:
?
?
簡而言之,
發送時,可以通過檢測SPI_SR中的TXE位,當數據寄存器里有數據時,TXE位是0,當數據全部從數據寄存器的發送緩沖區傳輸到移位寄存器時TXE位被置1,這時候可以再往數據寄存器里寫入數據。可以通過
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)?來檢測。
?SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)??是庫函數,可以檢測SPI的一些狀態位。
接收時,可以通過檢測SPI_SR中的RXNE位,當數據寄存器里有數據時,RXNE位是0,當數據全部從數據寄存器的接收緩沖區傳輸到移位寄存器時RXNE位被置1,這時候可以從數據寄存器里讀出數據。可以通過
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);???來檢測。源程序如下,
SPI?讀寫一個字節,讀寫一體
?
?
?
當能成功發送和接收一個字節時,發送數組數據就變的簡單了,只需要一個for循環,和指針變量的遞增即可。以下僅為參考:
(有一點特別注意,從機數據傳輸時要依賴主機的時鐘,所以主機在接收從機發送的數據時要往從機發送啞巴字節,這個字節可以自己定義?0xff,0xfe等什么字節都可以)
?
讀寫分開的函數:
?
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++;
???????}
}
讀寫一體的函數
?
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:發送緩沖區空中斷使能
在發送過程中,數據全部從數據寄存器的發送緩沖區傳輸到移位寄存器時TXE位被置1這時如果使能了TXEIE?就會觸發發送完成的中斷請求。在中斷服務函數里可以做你想做的事情,也可以用一個標志位,在外面完成相應的操作。
(使用中斷時要特別注意,及時的清除中斷標志,為下一次能夠觸發中斷做準備。而清除中斷的操作可以放在中斷服務函數中,或者其他你認為何時的地方。)
RXNEIE:接收緩沖區非空中斷使能
接收同發送。
TXDMAEN:發送緩沖區DMA使能
RXDMAEN:接收緩沖區DMA使能
?
手冊中有這樣一句話,“不能同時設置TXEIE和TXDMAEN”這一點要特別注意。也就是說如果你在SPI的通信中不用DMA則使能TXEIE的中斷,禁能TXDMAEN的中斷,如果在SPI中使用DMA傳輸,則禁能TXEIE?的中斷,只使能TXDMAEN?的中斷。
?
五、?????SPI的DMA操作
DMA(Direct Memory Access)直接內存存取,直接存儲器存取用來提供在外設和存儲器之間或者存儲器和存儲器之間的高速數據傳輸。無須CPU任何干預,通過DMA數據可以快速地移動。使用DMA最大的特點就是數據傳輸不經過CPU這就節省了CPU的資源,讓CPU能有更多的時間來做其他的事情。
SPI的DMA操作,就是在SPI->TXE為1時,會向對應的DMA通道發出請求,DMA通道會發出應答信號,SPI收到應答信號后撤銷請求信號,DMA撤銷應答信號,并把內存值裝入SPI_DR的發送緩區,SPI的傳送開始。
DMA
的初始化
?
?
DMA_PeripheralBaseAddr是值外設數據的地址,用SPI1故DMA外設地址對應的是SPI1_DR_Addr,
DMA_MemoryBaseAddr是內存地址,它的值可以使,你要發送的數據所存放的數組的名,因為數組名代表的是數組數據存放的首地址,在SPI-DMA的發送中可以理解為把DMATX[]數組里的數據傳送到SPI1_DR_Addr
DMA_DIR?是指數據傳輸的方向,其值發送時其值為DMA_DIR_PeripheralDST?即外設是目的地,方向是DMATX—> SPI1_DR_Addr,
在接受收時其值為DMA_DIR_PeripheralSRC,即外設是數據的來源,傳輸方向是?SPI1_DR_Addr—>用戶指定的數據存儲數組。
DMA_BufferSize?用來設置傳輸數據的個數,在STM32的DMA中其值的范圍是0—65536.
DMA_Mode?指?DMA的傳輸模式?DMA_Mode_Normal為正常工作模式
DMA_Mode_Circular?是循環工作模式,這里對循環模式的解釋我認為有位網友解釋的很不錯如下:
“循環的意思是指DMA的傳輸數量計數器會重置初值,由于DMA每傳一個數據,傳輸數量計數器減一,只有在傳輸數量計數器的值不為零時,才會響應請求。在循環模式下,當傳輸計數器的值減為0后,會重新裝載;而內存(緩存)地址則不管循環非循環模式,都會在每次傳輸完成后重置為基地址。所以,如果我們把DMA設置會正常模式,那么在下次傳輸前,只需對DMA的傳輸數量計數器重新寫入就行。循環模式一般用于數據更新,比如ADC采用需要不停更新數據。”
在初始化完成之后要開啟DMA的中斷,在我的程序中開啟的是DMA傳輸完成中斷。
?
?
DMA傳輸有3個中斷標志位,常用的是傳輸完成的中斷。如下:
?
?
這樣在傳輸完設定的數據個數之后就會觸發傳輸完成的中斷,用戶可以再中斷服務函數中,進行相應的操作,有一點特別注意,就是要及時清除中斷標志位,為下次能夠正常觸發中斷做準備。
?
?
在我的中斷服務函數中有一個標志位SpiCommon,被置1后再中斷之外進行其他的處理,同時調用DMA_ClearITPendingBit(DMA1_IT_TC2)來及時清除中斷標志。
在進行DMA的數據傳輸時要先禁能DMA的通道,重置傳輸數據個數的值,數據的存儲位置等,再使能DMA的通道,等待DMA的傳輸完成。
我的操作時這樣的,先往DMATX[]里寫入相應的數據,然后如下
?
這樣可能有一點不好的地方,因為只改變了SpiTXSize的值,卻又重新執行了DMATXInit()?函數,可能此處能夠再改善一下。
總結
以上是生活随笔為你收集整理的基于STM32的SPI基本介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: paho mqtt client调试记录
- 下一篇: STM32F10x_硬件I2C读写EEP