STM32學習之SPI序列通訊

電子小蛇發表於2020-11-08

SPI 簡介
SPI 是英語 Serial Peripheral interface 的縮寫,顧名思義就是序列外圍裝置介面。是 Motorola首先在其 MC68HCXX 系列處理器上定義的。SPI 介面主要應用在 EEPROM,FLASH,實時時鐘,AD 轉換器,還有數字訊號處理器和字訊號解碼器之間。SPI,是一種高速的,全雙工,同步的通訊匯流排,並且在晶片的管腳上只佔用四根線,節約了晶片的管腳,同時為 PCB 的佈局上節省空間,提供方便,正是出於這種簡單易用的特性,現在越來越多的晶片整合了這種通訊協議,STM32 也有 SPI 介面。下面我們看看 SPI 的內部簡明圖:
在這裡插入圖片描述SPI 介面一般使用 4 條線通訊:
MISO 主裝置資料輸入,從裝置資料輸出。
MOSI 主裝置資料輸出,從裝置資料輸入。
SCLK 時鐘訊號,由主裝置產生。
CS 從裝置片選訊號,由主裝置控制

從圖中可以看出,主機和從機都有一個序列移位暫存器,主機通過向它SPI 序列暫存器寫入一個位元組來發起一次傳輸。暫存器通過 MOSI 訊號線將位元組傳送給從機,從機也將自己的移位暫存器中的內容通過 MISO 訊號線返回給主機。這樣,兩個移位暫存器中的內容就被交換。外設的寫操作和讀操作是同步完成的。如果只進行寫操作,主機只需忽略接收到的位元組;反之,若主機要讀取從機的一個位元組,就必須傳送一個空位元組來引發從機的傳輸。

STM32 的主模式配置步驟如下:

1)配置相關引腳的複用功能,使能 SPI2 時鐘
我們要用 SPI2,第一步就要使能 SPI2 的時鐘。其次要設定 SPI2 的相關引腳為複用輸出,這樣才會連線到 SPI2 上否則這些 IO 口還是預設的狀態,也就是標準輸入輸出口。這裡我們使用的是 PB13、14、15 這 3 個(SCK.、MISO、MOSI,CS 使用軟體管理方式),所以設定這三個為複用 IO。

GPIO_InitTypeDef GPIO_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

2)初始化 SPI2,設定 SPI2 工作模式

SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //雙線雙向全雙工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主 SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // SPI 傳送接收 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; //預分頻 256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //資料傳輸從 MSB 位開始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值計算的多項式
SPI_Init(SPI2, &SPI_InitStructure); //根據指定的引數初始化外設 SPIx 暫存器

3)使能 SPI2
初始化完成之後接下來是要使能 SPI2 通訊了,在使能 SPI2 之後,我們就可以開始 SPI 通訊了。使能 SPI2 的方法是:

SPI_Cmd(SPI2, ENABLE); //使能 SPI 外設

完整程式碼如下:

#include "spi.h"
//以下是 SPI 模組的初始化程式碼,配置成主機模式,訪問 SD Card/W25Q128/NRF24L01
//SPI 口初始化
//這裡針是對 SPI2 的初始化
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 工作模式:設定為主 SPI
	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; //預分頻 256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //資料傳輸從 MSB 位開始
	SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值計算的多項式
	SPI_Init(SPI2, &SPI_InitStructure); //②根據指定的引數初始化外設 SPIx 暫存器
	SPI_Cmd(SPI2, ENABLE); //③使能 SPI 外設
	SPI2_ReadWriteByte(0xff); //④啟動傳輸
} 
//SPI 速度設定函式
//SpeedSet://SPI_BaudRatePrescaler_256 256 分頻 (SPI 281.25K@sys 72M)
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
	assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
	SPI2->CR1&=0XFFC7;
	SPI2->CR1|=SPI_BaudRatePrescaler; //設定 SPI2 速度
	SPI_Cmd(SPI2,ENABLE); 
} 
//SPIx 讀寫一個位元組
//TxData:要寫入的位元組
//返回值:讀取到的位元組
u8 SPI2_ReadWriteByte(u8 TxData)
{
	u8 retry=0;
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //等待傳送區空
{
	retry++;
	if(retry>200)
		return 0;
} 
	SPI_I2S_SendData(SPI2, TxData); //通過外設 SPIx 傳送一個資料
	retry=0;
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //等待接收//完一個 byte
	{ 
		retry++;
		if(retry>200)return 0; 
	}
		return SPI_I2S_ReceiveData(SPI2); //返回通過 SPIx 最近接收的資料
}

相關文章