STM32學習筆記(15)SPI介紹

SPI介紹

SPI : Serial Peripheral interface ,即串行外圍設備接口。 SPI是一種高速的,全雙工,同步的通信總線,並且在芯片的管腳上只佔用四根線,主要應用在 EEPROM(一種存儲器),FLASH,實時時鐘, AD 轉換器,還有數字信號處理器和數字信號解碼器之間。

SPI 主要特點: 可以同時發出和接收串行數據; 可以當作主機或從機工作; 提供頻率可
編程時鐘; 發送結束中斷標誌; 寫衝突保護; 總線競爭保護等。

SPI內部結構簡明圖

在這裏插入圖片描述
如圖,SPI 接口一般使用 4 條線通信:

  • MISO :主設備數據輸入,從設備數據輸出。
  • MOSI :主設備數據輸出,從設備數據輸入。
  • SCLK: 時鐘信號,由主設備產生。
  • CS :從設備片選信號,由主設備控制。

SPI接口框圖

在這裏插入圖片描述

字節的輸送

從圖中可以看出, 主機和從機都有一個串行移位寄存器,主機通過向它的 SPI 串行寄存器
寫入一個字節來發起一次傳輸。寄存器通過 MOSI 信號線將字節傳送給從機(從最高位開始,取代從機的最高位),從機也將自己的移位寄存器中的內容通過 MISO 信號線返回給主機(從最低位開始,取代主機的最低位)。這樣,兩個移位寄存器中的內容就被交換。

外設的寫操作和讀操作是同步完成的。如果只進行寫操作,主機只需忽略接收到的字節;反之,若主機要讀取從機的一個字節,就必鬚髮送一個空字節來引發從機的傳輸。

時鐘極性與相位

時鐘極性(CPOL)和相位(CPHA)可以進行配置。

時鐘極性(CPOL)對傳輸協議沒有重大的影響。(決定上升沿採集還是下降沿採集)
CPOL=0,串行同步時鐘的空閒狀態爲低電平。
CPOL=1,串行同步時鐘的空閒狀態爲高電平。

時鐘相位(CPHA)能夠配置用於選擇兩種不同的傳輸協議之一進行數據傳輸。
CPHA=0,在串行同步時鐘的第一個跳變沿(上升或下降)數據被採樣;
CPHA=1,在串行同步時鐘的第二個跳變沿(上升或下降)數據被採樣。

SPI 主模塊和與之通信的外設備時鐘相位和極性應該一致。
在這裏插入圖片描述

SPI特徵

  • 3線全雙工同步傳輸
  • 8或16位傳輸幀格式選擇
  • 主或從操作(支持多主模式)
  • 8個主模式波特率預分頻係數(最大爲fpcux/2)
  • 從模式頻率 (最大爲fpcu/2)
  • 主模式和從模式的快速通信
  • 主模式和從模式下均可以由軟件或硬件進行NSS管理:主從操作模式的動態改變
  • 可編程的時鐘極性和相位
  • 可編程的數據順序,MSB在前或LSB在前
  • 可觸發中斷的專用發送和接收標誌
  • SPI總線忙狀態標誌
  • 支持可靠通信的硬件CRC
    -在發送模式下,CRC值可以被作爲最後一個字節發送
    -在全雙工模式中對接收到的最後-一個字節自動進行CRC校驗
  • 可觸發中斷的主模式故障、 過載以及CRC錯誤標誌
  • 支持DMA功能的1字節發送和接收緩衝器:產生髮送和接受請求

STM32 SPI接口可配置爲支持SPI協議或者支持I2S音頻協議,默認是SPI模式。可以通過軟件切換到|2S方式。

從選擇(NSS)腳管理

Negative of Slave Select,也就是同步串行通訊的片選信號(低電平有效)

SS/CS(Slave Select/Chip Select):用於Master片選Slave,使被選中的Slave能夠被Master訪問;

NSS相當於主機的CS(自己理解的)

  • 軟件NSS模式: 可以通過設置SPI _CR1寄存器的SSM位來使能這種模式。在這種模式下NSS引腳可以用作它用,而內部NSS信號電平可以通過寫SPI_CR1的SSI位來驅動

  • 硬件NSS模式,分兩種情況
    -NSS輸出被使能:當STM32 工作爲主SPI,並且NSS輸出已經通過SPI _CR2寄存器的SSOE位使能,這時NSS引腳被拉低,所有NSS引腳與這個主SPI的NSS引腳相連並配置爲硬件NSS的SPI設備,將自動變成從SPI設備。
    此時,當一個SPI設備需要發送廣播數據,它必須拉低NSS信號,以通知所有其它的設備它是主設備;如果它不能拉低NSS,這意味着總線上有另外一個主設備在通信,這時將產生-一個
    硬件失敗錯誤(Hard Fault)。
    -NSS輸出被關閉:允許操作於多主環境。

部分狀態標誌

應用程序通過3個狀態標誌可以完全監控SPI總線的狀態。

發送緩衝器空閒標誌(TXE)

此標誌爲’1’時表明發送緩衝器爲空,可以寫下一個待發送的數據進入緩衝器中。當寫入SPI _DR
時,TXE標誌被清除。

接收緩衝器非空(RXNE)

此標誌爲’1’時表明在接收緩衝器中包含有效的接收數據。讀SPI數據寄存器可以清除此標誌。

忙(Busy)標誌

BSY標誌由硬件設置與清除(寫入此位無效果),此標誌表明SPI通信層的狀態。

配置過程

1.配置相關引腳的複用功能,使能SPlx時鐘

GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)

2.初始化SPIx,設置SPlx工作模式

SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)

3.SPI傳輸數據

SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)//發送數據
SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)//接收數據

4.查看SPI傳輸狀態

SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)//類似上面的狀態標誌

相關結構體

typedef struct
{
uint16_t SPI_Direction;//通信方式, 半雙工,全雙工,以及串行發和串行收方式
uint16_t SPI_Mode;//主從模式
uint16_t SPI_DataSize;
uint16_t SPI_CPOL;//時鐘極性
uint16_t SPI_CPHA;//時鐘相位
uint16_t SPI_NSS;//設置 NSS 信號由硬件(NSS 管腳)還是軟件控制
uint16_t SPI_BaudRatePrescaler;//波特率預分頻值
uint16_t SPI_FirstBit;//數據傳輸順序是先MSB 位還是先LSB 位
uint16_t SPI_CRCPolynomial;// CRC 校驗
}SPI_InitTypeDef;

相關配置

void SPI2_Init(void)
{
 	GPIO_InitTypeDef GPIO_InitStructure;
    SPI_InitTypeDef  SPI_InitStructure;//相關結構體初始化

	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );//PORTB時鐘使能 
	RCC_APB1PeriphClockCmd(	RCC_APB1Periph_SPI2,  ENABLE );//SPI2時鐘使能 	
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15複用推輓輸出 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB

 	GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);  //PB13/14/15上拉

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //設置SPI單向或者雙向
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//工作模式
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//數據大小:8位幀結構
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;		//串行同步時鐘的空閒狀態爲高電平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//採樣串行同步時鐘的第二個跳變沿(上升或下降)數據
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS的軟硬件控制
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;		//波特率預分頻
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//數據傳輸開始位
	SPI_InitStructure.SPI_CRCPolynomial = 7;	
	SPI_Init(SPI2, &SPI_InitStructure);  //初始化SPIx寄存器
 
	SPI_Cmd(SPI2, ENABLE); //使能SPI外設
	
	SPI2_ReadWriteByte(0xff);//啓動傳輸		 
}
//SPIx 讀寫一個字節(SPI想要讀必須寫)
//TxData:要寫入的字節
//返回值:讀取到的字節
u8 SPI2_ReadWriteByte(u8 TxData)
{		
	u8 retry=0;				 	
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //檢查指定的SPI標誌位設置與否:發送緩存空標誌位
		{
		retry++;
		if(retry>200)return 0;
		}			  
	SPI_I2S_SendData(SPI2, TxData); //通過外設SPIx發送一個數據
	retry=0;

	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //檢查指定的SPI標誌位設置與否:接受緩存非空標誌位
		{
		retry++;
		if(retry>200)return 0;
		}	  						    
	return SPI_I2S_ReceiveData(SPI2); //返回通過SPIx最近接收的數據					    
}

以W25Q128爲例的讀寫數據

//pBuffer:數據存儲區
//Read/writeAddr:開始讀取的地址(24bit)
//NumByteToRead/write:要讀取/寫入的字節數(讀取最大65535,寫入不超過該頁的剩餘字節數)

void W25Q128_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
{ 
 	u16 i;   										    
	W25Q128_CS=0;                            	//使能器件   
    SPI2_ReadWriteByte(W25X_ReadData);         	//發送讀取命令   
    SPI2_ReadWriteByte((u8)((ReadAddr)>>16));  	//發送24bit地址    
    SPI2_ReadWriteByte((u8)((ReadAddr)>>8));   
    SPI2_ReadWriteByte((u8)ReadAddr);   
    for(i=0;i<NumByteToRead;i++)
	{ 
        pBuffer[i]=SPI2_ReadWriteByte(0XFF);   	//循環讀數  
    }
	W25Q128_CS=1;  				    	      
}  



void W25Q128_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
 	u16 i;  
    W25Q128_Write_Enable();                  	//SET WEL 
	W25Q128_CS=0;                            	//使能器件   
    SPI2_ReadWriteByte(W25X_PageProgram);      	//發送寫頁命令   
    SPI2_ReadWriteByte((u8)((WriteAddr)>>16)); 	//發送24bit地址    
    SPI2_ReadWriteByte((u8)((WriteAddr)>>8));   
    SPI2_ReadWriteByte((u8)WriteAddr);   
    for(i=0;i<NumByteToWrite;i++)SPI2_ReadWriteByte(pBuffer[i]);//循環寫數  
	W25Q128_CS=1;                            	//取消片選 
	W25Q128_Wait_Busy();					   		//等待寫入結束
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章