前言
最近調試工作上用到和很多SPI,不同傳感器的、不同控制平台之間的。遇到了不少奇奇怪怪的問題,記錄一下。主要是stm32上這個平台上的使用,當然對於SPI這個常用的通信協議這里不多介紹了,可以找一下相關的資料熟悉相關概念和使用,全雙工下的使用無論是中斷還是DMA都比較簡單沒有啥好說的,這里就講一下做從機和使用半雙工的一些問題。
從機
由於使用 stm32 做從機的時候使用的是軟件去操作CS引腳,出現的問題是從機接收端會出現錯誤的數據,對比發現是數據錯位了,出現這種情況可能是 SPI 主機復位、主機重新重新上電、意外的時鍾電平干擾都可能會讓 SPI 從機接受數據的時候錯位,整體數據右移了。所以一開始的解決思路就是看有沒有相關的操作可以在每次清空掉RX - Shift Register里面的錯誤數據,但是查了很多資料都沒有相關的描述。
嘗試了如下的方式:
- 關閉使能關閉后重新打開
- 關閉SPI時鍾后重新初始化(deinit)
結論是關閉SPI使能位SPE后在打開使能,問題依舊仍是接受到了錯誤數據;而使用后面的操作可以清除掉原來移位寄存器中的數據,為了速度直接使用寄存器賦值的操作直接先關閉 spi 時鍾后再重新打開,然后對 CR1 賦值完成初始化,實測第二種方式解決了我的問題
半雙工
關於半雙工的使用是因為使用 TLE5012B 這個磁編碼器的時候它使用的是3線的 SPI ,發送和接受數據都在同一條數據線上分時復用也就是半雙工的方式,但是看了網上很多資料發現他們的操作都是使用下面這種方式,在MOSI和MISO之間串聯一個電阻這樣就可以使用全雙工的SPI去通信了。不過我印象中stm32 的SPI 是支持半雙工通信的,所以嘗試了一下使用半雙工的方式來通信。
-
更改后使用如下的連接方式
-
半雙工和只讀模式
這里配置成雙向數據線
-
發送和接受的切換
有前面可以知道傳輸的方向是由 BIDIOE 位控制的所以只需要在發送完成的時候切換成接受狀態就可以了,那什么時候可以切換狀態呢,首先先等待TXE = 1 ,這表明數據移入到移位寄存器中開始發送,接着等待 BSY = 0,這表明物理線上的傳輸已經結束了,這時候我們就可以切換狀態到接受模式了,在hal庫和LL庫中都有相應的操作,當然也可以直接操作 BIDIOE 這個位進入到接受模式。
-
時鍾信號的控制
可以看出在雙向模式接受的是時候只要使能了SPE,那么就意味着傳輸開始了,SPI主機會自動發送時鍾這時候就可以接受到數據
- 參考代碼
uint8_t send_and_read_byte(uint8_t cmd)
{
uint8_t result;
GPIO_ResetBits(GPIOA, GPIO_Pin_4); // CS low
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); //wait buffer empty
SPI_SendData8(SPI1, cmd);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); //wait finish sending
// Read receiving FIFO until it is empty
while (SPI_GetReceptionFIFOStatus(SPI1) != SPI_ReceptionFIFOStatus_Empty)
SPI_ReceiveData8(SPI1);
SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Rx);
while (!(SPI1->SR & SPI_I2S_FLAG_RXNE)) ; // wait data received
GPIO_SetBits(GPIOA, GPIO_Pin_4); // CS high
SPI1->CR1 |= SPI_Direction_Tx; // Set Tx mode to stop Rx clock
result = SPI_ReceiveData8(SPI1);
return result;
}
參考:HOWTO: Use STM32 SPI half duplex mode – Digital Me (ba0sh1.com)