文章目錄
- 前言
- 一、SPI是什么?SPI的硬件電路?SPI發送的時序?
- 二、庫函數
- 二、庫函數示例代碼
- 總結
前言
SPI和IIC通信算是我在大學和面試中用的最多,問的最多的通信協議
IIC問到了,一般SPI也一定會問到。
SPI相對于IIC多了一個片選信號(CS)
SPI相對于IIC,最主要特點,傳輸速率增快,不需要等待下位機回復,有了片選信號,可以明確控制信號傳輸的單對單。所以,代碼相對于IIC也簡單一些。(很多時候,芯片不會有那么多的GPIO口給SPI用作片選信號)
IIC可以用兩根線連接多設備,SPI需要再增加片選線,假如連接3個設備,IIC只需要2根通信線,SPI則需要(3+n)條線,n為設備數量,也就是6根線。
一、SPI是什么?SPI的硬件電路?SPI發送的時序?
SPI是什么?
SPI(Serial Peripheral Interface,串行外設接口)是由Motorola公司開發的一種通用數據總線,與IIC 差不多,也是為了實現主控芯片和各種外掛芯片之間的數據交流。SPI和IIC都是常用的接口協議,只是根據其不同的特點,應用場景有所不同。
SPI特點:SPI(Serial Peripheral Interface)是由Motorola公司開發的一種通用數據總線
四根通信線:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)
同步,全雙工
支持總線掛載多設備(一主多從)
SPI沒有應答機制
硬件特點(參考上圖):所有SPI設備的SCK、MOSI、MISO分別連在一起
主機另外引出多條SS控制線,分別接到各從機的SS引腳
輸出引腳配置為推挽輸出,輸入引腳配置為浮空或上拉輸入
STM32的SPI電路:
STM32內部集成了硬件SPI收發電路,可以由硬件自動執行時鐘生成、數據收發等功能,減輕CPU的負擔
可配置8位/16位數據幀、高位先行/低位先行
時鐘頻率: fPCLK / (2, 4, 8, 16, 32, 64, 128, 256)
支持多主機模型、主或從操作
可精簡為半雙工/單工通信
支持DMA
兼容I2S協議
STM32F103C8T6 硬件SPI資源:SPI1、SPI2
***寄存器配合介紹: ***
**移位寄存器:**右側的數據一位一位地從MOSI輸出,MOSI的數據一位一位地移到左側數據位。
LSBFIRST控制位:用于控制移位寄存器是低位先行(1)還是高位先行(0)。
MISO和MOSI的交叉:用于切換主從模式。不交叉時為主機模式,交叉時為從機模式。
接收緩沖區、發送緩沖區:實際上分別就是接收數據寄存器RDR、發送數據緩沖區TDR。TDR和RDR占用同一個地址,統一叫作DR。移位寄存器空時,TXE標志位置1,TDR移入數據,下一個數據移入到TDR;移位寄存器接收完畢(同時也標志著移出完成),RXNE標志位置1,數據轉運到RDR,此時需要盡快讀出RDR,以防止被下一個數據覆蓋。
細節:SPI為全雙工同步通信,所以為一個移位寄存器、兩個緩沖區;IIC為單工通信,所以只需要一個移位寄存器、一個緩沖區;USRT為全雙工異步通信,所以需要兩個移位寄存器、兩個緩沖區,且這兩套分別獨立。
***控制邏輯介紹: ***
波特率發生器:本質是一個分頻器,用于產生SCK時鐘。輸入時鐘就是外設時鐘f =72MHz/36MHz。每產生一個時鐘,就移入/移出一個比特。SPI_CR1中的[BR2,BR1,BR0]用于產生分頻系數。
SPI_CR1:SPI控制寄存器1,下面簡單介紹一下。詳細可以參考中文數據手冊“23.5 SPI和I2S寄存器描述”一節。
SPE(SPI Enable):SPI使能,就是SPI_Cmd函數配置的位。
BR(Baud Rate):配置波特率,也就是SCK時鐘頻率。
MSTR(Master):配置主機模式(1)、從機(0)模式。
CPOL、CPHA:用于選擇SPI的四種模式。
簡化后:
波特率發生器:用于產生SCK時鐘。
數據控制器:根據配置,控制SPI外設電路的運行。
字節交換過程:交換完畢,移位寄存器空,則TXE位置1、RXNE位置1,TDR會自動轉運數據到移位寄存器,RDR數據等待用戶讀取。
開關控制【代碼】:SPI外設使能。
GPIO【代碼】:用于各引腳的初始化。
從機使能引腳SS【代碼】:并不存在于SPI硬件外設中,實際使用隨便指定一個GPIO口(例如PA4)即可。在一主多從模式下,GPIO模擬SS是最佳選擇。
在實際書寫代碼的過程中,使用一個結構體便可以直接配置 波特率發生器 和 字節交換的默認模式,這是SPI外設內部便會自動工作,用戶額外需要關心的只是何時讀寫DR。下面介紹讀寫時序的流程,分別是性能更高、使用復雜的“主模式全雙工連續傳輸”,以及性能較低、常用且簡單易學的“非連續傳輸”:
不常用:
本模式可以實現數據的連續傳輸。
連續寫入數據:只要TXE置1,就立馬進中斷寫入數據(會同時清除TXE位);當寫入到最后一個數據時,等待BSY位清除,發送流程完畢。
連續讀出數據:只要RXNE位置1,就立馬進中斷讀出數據(會同時清除RXNE位)。若不及時讀出,現有數據就會被新的數據覆蓋。
評價:連續數據流傳輸對于軟件的配合要求較高,需要在每個標志位產生后及時讀寫數據,整個發送和接收的流程是交錯的,但是傳輸效率是最高的。對傳輸效率有嚴格要求才會用到此模式,否則一般采用下面更為簡單的“非連續傳輸”。
常用:
本模式對于程序設計非常友好。(示例代碼基于此)
字節交換流程:最開始等待TXE位置1,發送一個數據(會自動清除TXE);等待RXNE置1,讀取數據。再進行下一次的字節交換。
評價:非連續傳輸會損失數據傳輸效率,數據傳輸速率越快,損失越明顯。
SPI發送的時序
SPI通信的基礎是交換字節。也就是說,每次SPI通信的過程中,通過各自的MOSI、MISO線,主機和從機的寄存器會形成一個循環移位操作,每個比特的通信都是轉圈的循環移位,8個時鐘周期完整的交換一個字節。那么根據需求有選擇的忽略交換過來的數據,就可以實現(以主機舉例,從機同理)主機只發送、主機只接收、主從機交換數據這三類操作。
工作原理:
波特率發生器上升沿:所有寄存器左移一位。
波特率發生器下降沿:將采樣輸入的數據放到寄存器的最低位。
重復8個時鐘周期,便可以實現主機和從機的數據交換。
注:實際上,何時移位、何時采樣、時鐘極性都是可以設置的,下面將介紹。
功能介紹:顯然存在資源浪費現象。
同時進行發送和接收:正常的交換字節。
只想發送、不想接收:不看接收過來的數據。
只想接收、不想發送:隨便發一個數據,比如0x00/0xFF。
時序舉例:SPI交換單個字節的時序:
起始條件和終止條件:起始條件是SS從高電平切換到低電平,終止條件是SS從低電平切換到高電平。
交換一個字節:兩個配置位分別為CPOL(Clock Polarity, 時鐘極性)規定空閑狀態的時鐘高低電平、CPHA(Clock Phase, 時鐘相位)規定數據移入(數據采樣)、移出的時機。
總共有四種模式可以選擇,常用模式0,會配置一個就足夠,其他的只是改變時鐘電平。
-
【模式0】[CPOL,CPHA] = [0,0],SCK低電平為空閑狀態;SCK第一個邊沿(上升沿)移入數據,第二個邊沿(下降沿)移出數據。
-
【模式1】[CPOL,CPHA] = [0,1],SCK低電平為空閑狀態;SCK第一個邊沿移出數據,第二個邊沿移入數據。
-
【模式2】[CPOL,CPHA] = [1,0],SCK高電平為空閑狀態;SCK第一個邊沿移入數據,第二個邊沿移出數據。
-
【模式3】[CPOL,CPHA] = [1,1],SCK高電平為空閑狀態;SCK第一個邊沿移出數據,第二個邊沿移入數據。
由于從機SPI協議由硬件控制,所以從機發送過來的數據,其數據變化邊沿都是緊貼著時鐘下降沿完成的。并且,如果最后接收完一個字節后時鐘仍為低電平,那么從機會繼續將下一個地址的數據發送過來,就實現了“連續地址讀”。
二、庫函數
SPI/I2S常用設置:
SPI_I2S_DeInit :將SPI或I2S外設恢復到默認的初始狀態。
SPI_Init 【必需】:初始化SPI(串行外設接口)外設,并配置其相關參數,包括數據傳輸模式、時鐘極性和相位、數據位長度等。
I2S_Init :初始化I2S(串行音頻接口)外設,并配置其相關參數,包括數據格式、數據位長度、時鐘極性和相位等。
SPI_StructInit :將SPI外設的配置結構體初始化為默認值。
I2S_StructInit :將I2S外設的配置結構體初始化為默認值。
SPI_Cmd 【必需】:使能或禁用SPI外設。
I2S_Cmd :使能或禁用I2S(串行音頻接口)外設。
SPI_I2S_SendData 【常用】:用于向SPI或I2S外設發送數據。但只是將數據送到發送緩沖區。
SPI_I2S_ReceiveData 【常用】:從SPI或I2S外設接收數據。但只是將數據從接收緩沖區讀出。
關于中斷及標志位【必需】:
SPI_I2S_GetFlagStatus :常用于非中斷函數。獲取SPI或I2S外設的特定標志位的狀態。常見的標志位包括傳輸完成標志(TXE或BTF)、接收緩沖區非空標志(RXNE)、傳輸錯誤標志(OVR、CRCERR等)以及其他特定功能的標志。
SPI_I2S_ClearFlag :常用于非中斷函數。用于軟件清除SPI或I2S外設的特定標志位。
SPI_I2S_ITConfig :配置SPI或I2S外設的中斷使能狀態。注意在使用中斷功能之前,還需要配置中斷優先級、編寫中斷服務程序以及使能全局中斷。
SPI_I2S_GetITStatus :常用于中斷函數。用于獲取SPI或I2S外設的特定中斷標志位的狀態。
SPI_I2S_ClearITPendingBit :常用于中斷函數。用于清除SPI或I2S外設的特定中斷標志位。
單獨參數的配置:
SPI_DataSizeConfig :配置SPI外設的數據位長度。
SPI_NSSInternalSoftwareConfig :配置SPI外設的NSS(多主機模式)的內部軟件控制模式。
SPI_SSOutputCmd :使能或禁用SPI外設的SS(片選信號)輸出功能。
SPI_BiDirectionalLineConfig :用于配置SPI外設的雙向數據線模式。
DMA配置(連續數據傳輸):
SPI_I2S_DMACmd :使能或禁用SPI或I2S外設的DMA傳輸。
CRC配置:
SPI_TransmitCRC :向SPI外設發送CRC(循環冗余校驗)值。
SPI_CalculateCRC :用于計算SPI外設接收到的數據的CRC(循環冗余校驗)值。
SPI_GetCRC :用于從SPI外設獲取計算得到的CRC(循環冗余校驗)值。
SPI_GetCRCPolynomial :用于從SPI外設獲取當前配置的CRC(循環冗余校驗)多項式值
二、庫函數示例代碼
代碼如下(示例):
#include "stm32f10x.h" // Device header//SPI-SS引腳寫操作
void SPI_User_W_SS(uint8_t BitValue){GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);}//SPI初始化
void SPI_User_Init(void){//1.開啟外設時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//GPIO時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //SPI1時鐘//2.初始化端口//初始化SS-推挽輸出GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化CLK、MOSI-外設復用推挽輸出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化MISO-上拉輸入GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//3.配置SPISPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //APB2的2分頻-36MHzSPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //第一個邊沿采樣,第二個邊沿輸出SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //時鐘空閑時低電平SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //數據位寬8bitSPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI雙線全雙工SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位先行SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主機模式SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //軟件自定義片選信號SPI_InitStructure.SPI_CRCPolynomial = 0x0007; //CRC用不到,所以默認值7SPI_Init(SPI1, &SPI_InitStructure);//4.SPI使能SPI_Cmd(SPI1, ENABLE);SPI_User_W_SS(1);//默認不選中從機
}//SPI起始信號
void SPI_User_Start(void){SPI_User_W_SS(0);}//SPI終止信號
void SPI_User_Stop(void){SPI_User_W_SS(1);}//SPI交換一個字節(模式0)uint8_t SPI_User_SwapByte(uint8_t SendByte){while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)!=SET); //等待TXE置1SPI_I2S_SendData(SPI1,SendByte); //發送數據到TDRwhile(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)!=SET);//等待RNXE置1return SPI_I2S_ReceiveData(SPI1); //從RDR接收數據
}
流程:開時鐘 — 配置串口 — 配置SPI — 開啟SPI使能 — 交換數據 — 關使能
總結
IIS是一種用于將數字音頻設備連接在一起的電氣串行總線接口標準。 在工作中音頻采集,AD和主芯片之間的通信都是使用IIS進行的,它用于在電子設備中的集成電路之間傳送PCM音頻數據。在IIS總線上,只能同時存在一個主設備和發送設備。主設備可以是發送設備,也可以是接收設備,或是協調發送設備和接受設備的其它控制設備。在IIS系統中,提供時鐘的設備為主設備。(知道有這么個東西就行,工作和大學沒用到,我也不會)
關于是否清除標志位。手冊上寫明了TXE和RXNE“由硬件置位并由軟件清除”,但是這并不代表需要一條專門的語句來清除標志位,比如SPI中就是讀寫數據的過程中就自動清除了,所以具體還需要查看數據手冊的描述。