天天看點

【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)

【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)

SPI 是英語Serial Peripheral interface的縮寫,顧名思義就是串行外圍裝置接口。是Motorola(摩托羅拉)首先在其MC68HCXX系列處理器上定義的。

SPI,是一種高速的,全雙工,同步的通信總線,并且在晶片的管腳上隻占用四根線,節約了晶片的管腳,同時為PCB的布局上節省空間,提供友善,主要應用在 EEPROM,FLASH,實時時鐘,AD轉換器,還有數字信号處理器和數字信号解碼器之間。

SPI主從模式

SPI分為主、從兩種模式,一個SPI通訊系統需要包含一個(且隻能是一個)主裝置,一個或多個從裝置。提供時鐘的為主裝置(Master),接收時鐘的裝置為從裝置(Slave),SPI接口的讀寫操作,都是由主裝置發起。當存在多個從裝置時,通過各自的片選信号進行管理。

SPI是全雙工且SPI沒有定義速度限制,一般的實作通常能達到甚至超過10 Mbps

SPI信号線

SPI接口一般使用四條信号線通信:

SDI(資料輸入),SDO(資料輸出),SCK(時鐘),CS(片選)

MISO: 主裝置輸入/從裝置輸出引腳。該引腳在從模式下發送資料,在主模式下接收資料。

MOSI: 主裝置輸出/從裝置輸入引腳。該引腳在主模式下發送資料,在從模式下接收資料。

SCLK:串行時鐘信号,由主裝置産生。

CS/SS:從裝置片選信号,由主裝置控制。它的功能是用來作為“片選引腳”,也就是選擇指定的從裝置,讓主裝置可以單獨地與特定從裝置通訊,避免資料線上的沖突。

硬體上為4根線。

四線SPI可以同時發送和接收資料

另外,還有一種三線SPI,即SCLK、CS、DIO,通過DIO一條線實作MISO和MOSI的功能,三線SPI同時發送或接收

SPI協定可以一對多傳輸 拉低哪個CS就同哪個晶片通信

【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)
【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)

SPI工作模式

根據時鐘極性(CPOL)及相位(CPHA)不同,SPI有四種工作模式。

時鐘極性(CPOL)定義了時鐘空閑狀态電平:

CPOL=0為時鐘空閑時為低電平

CPOL=1為時鐘空閑時為高電平

時鐘相位(CPHA)定義資料的采集時間。

CPHA=0:在時鐘的第一個跳變沿(上升沿或下降沿)進行資料采樣。

CPHA=1:在時鐘的第二個跳變沿(上升沿或下降沿)進行資料采樣。

【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)

SPI通信的時序

【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)

傳輸一個位元組

【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)

如圖為傳輸一個24位的資料 在此期間片選SYNC一直為拉低的

SPI配置

【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)

這是一般情況的配置

SPI配置中設定資料長度為8bit,MSB先輸出分頻為64分頻,則波特率為125KBits/s。其他為預設設定。

Motorla格式,CPOL設定為Low,CPHA設定為第二個邊沿。不開啟CRC檢驗,NSS為軟體控制。

(CPOL=0,CPHA=1)

CRC根據裝置需求來

NSS片選這裡選擇的是軟體片選(GPIO設定為輸出,由GPIO控制拉高拉低) 之是以推薦這個配置 後面會詳細說明

CPOL和CPHA根據晶片來定

工作模式選擇全雙工

有主機模式全雙工/半雙工

從機模式全雙工/半雙工

隻接收主機模式/隻接收從機模式

隻發送主機模式

SPI函數

在stm32f1xx_hal_spi.h頭檔案中可以看到spi的操作函數。分别對應輪詢,中斷和DMA三種控制方式。

【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)

輪詢: 最基本的發送接收函數,就是正常的發送資料和接收資料(阻塞)

中斷: 在SPI發送或者接收完成的時候,會進入SPI回調函數,使用者可以編寫回調函數,實作設定功能(非阻塞)

DMA: DMA傳輸SPI資料(非阻塞)

利用SPI接口發送和接收資料主要調用以下兩個函數:

HAL_StatusTypeDef  HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);//發送資料
HAL_StatusTypeDef  HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);//接收資料
           

SPI發送資料函數:

參數:

*hspi: 選擇SPI1/2,比如&hspi1,&hspi2

*pData : 需要發送的資料,可以為數組

Size: 發送資料的位元組數,1 就是發送一個位元組資料

Timeout: 逾時時間,就是執行發送函數最長的時間,超過該時間自動退出發送函數

SPI接收資料函數:

參數:

*hspi: 選擇SPI1/2,比如&hspi1,&hspi2

*pData : 接收發送過來的資料的數組

Size: 接收資料的位元組數,1 就是接收一個位元組資料

Timeout: 逾時時間,就是執行接收函數最長的時間,超過該時間自動退出接收函數

SPI接收回調函數:

當SPI上接收出現了 CommSize個位元組的資料後,中斷函數會調用SPI回調函數:

使用者可以重新定義回調函數,編寫預定功能即可,在接收完成之後便會進入回調函數

另外,最常用又最友善的是:

此函數可以同時發送和接收

比如發送2個位元組而後又接收3個位元組,則Size=5(實際上發送5個位元組,在發送2個位元組後,開始接收3個位元組)

若要發送2個位元組的同時接收2個位元組,則Size=2

若要發送2個位元組,但在發送1個位元組後接收一個位元組,則Size=2

在這裡 發送和接收同時進行,根據需求 Size填入的值為時序的總長度

SPI連續傳輸

在HAL庫中,SPI的傳輸是不連續的

若是選擇硬體NSS,則每次發送一個位元組後,NSS都會拉高

是以我們選擇軟體NSS,這樣就可以在完成傳輸後手動拉高

另外,若CPHA設定為1edge,則預設開啟NSSP,在每次傳輸1個位元組後,都會有一段空閑,設定為2或關閉NSSP則沒有

如圖:

【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)

若是用阻塞的方式進行傳輸,則每傳輸完兩個位元組後會有一個空閑,如圖:

【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)

為了使每兩個位元組傳輸中不間隔(連續傳輸)

則使用HAL_SPI_TransmitReceive_IT或HAL_SPI_TransmitReceive_DMA

同時在cubemx中開啟中斷或DMA(普通模式,開啟TX和RX)

(其實說白了 DMA也算中斷的一種 DMA不經過CPU傳輸 發送完成以後也會進入DMA中斷回調函數)

由于這兩個函數為非阻塞 固在使用時要加上阻塞判斷

HAL_SPI_TransmitReceive_IT(hspi,pData,buf,x+y);
while(hspi->State!=HAL_SPI_STATE_READY);
Set_SPI_CS(hspi,GPIO_PIN_SET);
           

若不加 軟體片選會變成這樣:

【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)

SPI函數包裝如下:

/*!
 * @brief       	對SPI裝置進行發送和讀取
 *
 * @param 	[in]	hspi: SPI_HandleTypeDef 變量位址
 *					[in]	pData: 需要發送的資料變量位址
 *					[in]	x: 發送資料個數
 *					[in]	y: 讀取資料個數,最大為4,若大于4,則傳回0
 *					[in]	us: 拉高CS後的延時時長
 *					[in]	sync_flag: 同步标志
 *								當sync_flag為true時,發送資料和讀取資料同時進行,片選始終拉低,接收的資料為發送x個資料以後接收的y個資料
 *								當sync_flag為false時,發送資料和讀取資料分别進行,片選分兩次拉低,接收的資料為第二次片選拉低時的資料
 *
 * @return				dat: SPI讀取資料傳回
 */
uint32_t SPI_Send_x_Read_y(SPI_HandleTypeDef *hspi, uint8_t *pData, uint8_t x,uint8_t y,uint8_t us,bool sync_flag)
{	
	Set_SPI_CS(hspi,GPIO_PIN_SET);
	
	uint8_t buf[x+y];
	memset(buf,0,sizeof(buf));
	uint32_t dat=0;
	
	if(y>4 || x+y==0)
	{
		return 0;
	}
	
	if(sync_flag)
	{
		Set_SPI_CS(hspi,GPIO_PIN_RESET);
		if(pData!=NULL)
		{
			HAL_SPI_TransmitReceive_IT(hspi,pData,buf,x+y);
			while(hspi->State!=HAL_SPI_STATE_READY);
			Set_SPI_CS(hspi,GPIO_PIN_SET);
			delay_us(us);
		}
		else
		{
			Set_SPI_CS(hspi,GPIO_PIN_SET);
			delay_us(us);
			return 0;
		}		
	}
	else
	{		
		if(pData!=NULL && x!=0)
		{
			Set_SPI_CS(hspi,GPIO_PIN_RESET);
			HAL_SPI_Transmit_IT(hspi,pData,x);
			while(hspi->State!=HAL_SPI_STATE_READY);
			Set_SPI_CS(hspi,GPIO_PIN_SET);
			delay_us(us);
		}
		Set_SPI_CS(hspi,GPIO_PIN_RESET);
		HAL_SPI_Receive_IT(hspi,buf,y);
		while(hspi->State!=HAL_SPI_STATE_READY);
		Set_SPI_CS(hspi,GPIO_PIN_SET);
		delay_us(us);
		x=0;
	}
	
	for(uint8_t i=0;i<y;i++)
	{
		dat|=buf[x+i]<<(8*(y-1-i));
	}
	
	Set_SPI_CS(hspi,GPIO_PIN_SET);
	
	return dat;
}
           

連續傳輸後的時序如圖:

【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)【STM32筆記】HAL庫中的SPI傳輸(可利用中斷或DMA進行連續傳輸)

軟體片選中的拉高延遲50us,是為了滿足有的裝置對片選拉高時長的要求 50us可以滿足大多數裝置了

另外,傳輸完成的拉高也可以放在IT和DMA的回調中去,但是回調也是非阻塞的,若是兩次資料間隔時間長,則可以這樣使用,這樣就可以壓縮CS的時間。但如果兩次資料間隔很短,就要按剛剛說的軟體片選拉高後給延時,如果用回調的話,延時部分會被壓縮,原本延時50us,可能隻能延時40us,是以盡量不用這個。

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{  
	Set_SPI_CS(hspi,GPIO_PIN_SET);
	if (hspi == (&hspi2))
	{

	}	
}
           

繼續閱讀