配合我的上一篇SPI ??????通信 協議-CSDN博客一起理解更佳,本文后看
? ? ? ?SPI 是由摩托羅拉(Motorola)公司開發的全雙工同步串行總線,是 MCU 和外圍設備之間進行通信的同步串行端口。主要應用在EEPROM、Flash、RTC、ADC、網絡控制器、MCU、DSP以及數字信號解碼器之間。SPI 系統可直接與各個廠家生產的多種標準外圍器件直接接口,一般使用4 條線:SCK、MISO、MOSI 、SS
STM32內部集成了硬件SPI收發電路,可以由硬件自動執行時鐘生成、數據收發等功能,減輕CPU的負擔
- 可配置8位/16位數據幀、高位先行/低位先行
- 時鐘頻率: fPCLK / (2, 4, 8, 16, 32, 64, 128, 256) 支持多主機模型、主或從操作
- 可精簡為半雙工/單工通信
- 支持DMA 兼容I2S協議
STM32F103C8T6 硬件SPI資源:SPI1(APB2)、SPI2(APB1)
SPI基本圖?
NSS :從設備選擇。這是一個可選的引腳,用來選擇主 / 從設備。它的功能是用來作為“片選引腳”,讓主設備可以單獨地與特定從設備通訊,避免數據線上的沖突。
LSBFIRST是低位先行 。如果是MSBFIRST,那么就是高位先行,
此時MISO和MOSI需要換,如圖的另一種指法。
SPI基本結構?
注意:該圖是高位先行?
主模式傳輸?
主模式、全雙工模式下
連續傳輸(BIDIMODE=0并且RXONLY=0)時
TXE/RXNE/BSY 的變化示意圖
?
本質上來說:第一個字節數據發送,發送時下一個字節就已經到發送緩沖器等待,當第一個字節發送成功,立馬發送第二個,第三個字節進入發送緩沖器等待?。
非連續傳輸發送(BIDIMODE=0并且RXONLY=0)時
TXE/BSY變化示意圖?
非連續傳輸發送來說:第一個字節數據發送,發送成功后,下一個字節才到發送緩沖器,再發送第二個,等發送成功后,第三個字節再進入發送緩沖器。
代碼?
/*** 函 數:SPI初始化* 參 數:無* 返 回 值:無*/
void MySPI_Init(void)
{/*開啟時鐘*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //開啟GPIOA的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //開啟SPI1的時鐘/*GPIO初始化*/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); //將PA4引腳初始化為推挽輸出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); //將PA5和PA7引腳初始化為復用推挽輸出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); //將PA6引腳初始化為上拉輸入/*SPI初始化*/SPI_InitTypeDef SPI_InitStructure; //定義結構體變量SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //模式,選擇為SPI主模式SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//方向,選擇2線全雙工SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //數據寬度,選擇為8位SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //先行位,選擇高位先行SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;//波特率分頻,選擇128分頻SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //SPI極性,選擇低極性SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //SPI相位,選擇第一個時鐘邊沿采樣,極性和相位決定選擇SPI模式0SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS,選擇由軟件控制SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC多項式,暫時用不到,給默認值7SPI_Init(SPI1, &SPI_InitStructure); //將結構體變量交給SPI_Init,配置SPI1/*SPI使能*/SPI_Cmd(SPI1, ENABLE); //使能SPI1,開始運行/*設置默認電平*/MySPI_W_SS(1); //SS默認高電平
}
SPI起始?
void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue); //根據BitValue,設置SS引腳的電平
}/*** 函 數:SPI起始* 參 數:無* 返 回 值:無*/
void MySPI_Start(void)
{MySPI_W_SS(0); //拉低SS,開始時序
}
SPI終止?
/*** 函 數:SPI終止* 參 數:無* 返 回 值:無*/
void MySPI_Stop(void)
{MySPI_W_SS(1); //拉高SS,終止時序
}
交換一個字節?
/*** 函 數:SPI交換傳輸一個字節,使用SPI模式0* 參 數:ByteSend 要發送的一個字節* 返 回 值:接收的一個字節*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);//等待發送數據寄存器空SPI_I2S_SendData(SPI1, ByteSend); //寫入數據到發送數據寄存器,開始產生時序while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);//等待接收數據寄存器非空return SPI_I2S_ReceiveData(SPI1); //讀取接收到的數據并返回
}