SPI 全稱Serial Peripheral Interface
SPI的最高時鍾高達45MHZ
下圖表達了SPI的工程原理,通過兩根線(MISO和MOSI)進行數據傳輸,數據的讀寫同步進行,通過移位寄存器完成數據的交換。
SPI的4條通訊線:
MISO: Master Input Slave Output
MOSI: Master Output Slave Input
CS: Chip Select
SCLK: System Clock
前兩根線是數據傳輸線,最后一跟 是系統時鍾線 ,主要看第三根線CS,信號片選:
這是別人寫的一個不錯的解釋 https://blog.csdn.net/STL1634614466/article/details/69375138
總結如下:
CS在也叫NSS(Number Slave Select),分為軟件和硬件控制,如果用硬件NSS,只能通過PF6這個口控制,因此只能於一台Slave連接。因此一般用軟件NSS,通過普通IO輸出數字電平到Slave的片選端口,如果是低電平,則工作,如果是高電平則不連接。
SPI的相關配置如下:
1. 時鍾極性:CPOL = 0 -> 時鍾空閑為低電平; CPOL = 1 -> 時鍾空閑為高電平;
2. 時鍾相位:CPHA = 0 -> 時鍾第一個跳變沿采集數據; CPHA = 1 -> 時鍾第二個跳變沿采集數據;(兩種不同的傳輸協議)
提醒:主從機的時鍾極性和時鍾相位應保持一致。
3. MODE: 可以配置為Master 或者 Slave,主從模式的區別在於時鍾來源於主機
4. 傳輸方向:只讀,雙向單線,雙向雙線
5. SPI波特率分頻系數
6. 開始位:MSB 或者 LSB
7. NSS軟件控制還是硬件控制
8. 幀格式:SPI Motorola 或者 TI
9. 是否開啟CRC校驗
10. 如果開啟CRC,測需要設置CRC多項式
關於SPI的配置,差不多就這些了,需要配置相關的寄存器。當然,HAL庫早就將其封裝好了,直接調用就行了。
下面就是STM32中SPI的使用過程了:
STEP1:打開SPI時鍾
__HAL_RCC_SPI5_CLK_ENABLE();
STEP2:對SPI進行相關的配置
HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi);
上面的代碼中相關的配置很多,具體已經在之前羅列了,大概就10個相關的配置。
STEP3:使能SPI
__HAL_SPI_ENABLE(&SPI5_Handler);
STEP4:數據傳輸
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); HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData,uint8_t *pRxData, uint16_t Size, uint32_t Timeout);
以上部分大多參考正點原子的資料整理的。下面我們來通過CUBEMX配置自己的SPI,通過SPI連接到一個FLASH,並實現批量數據的傳輸。
首先打開在CUBEMX上的配置:
選擇全雙工,並且禁止硬件NSS。接下來就是在Configuration界面配置SPI的一些參數,下面我隨便配置了一下,注意主機和從機一些參數要保持一致,DMA和中斷我們就先不用了。
配置好了,接下來我們生成工程文件吧!
打開生成好的文件,找到Application/User目錄下spi.c,里面有個函數就是用於配置我們在圖形界面配置的參數。
void MX_SPI5_Init(void) { hspi5.Instance = SPI5; hspi5.Init.Mode = SPI_MODE_MASTER; hspi5.Init.Direction = SPI_DIRECTION_2LINES; hspi5.Init.DataSize = SPI_DATASIZE_8BIT; hspi5.Init.CLKPolarity = SPI_POLARITY_HIGH; hspi5.Init.CLKPhase = SPI_PHASE_2EDGE; hspi5.Init.NSS = SPI_NSS_SOFT; hspi5.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; hspi5.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi5.Init.TIMode = SPI_TIMODE_DISABLE; hspi5.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi5.Init.CRCPolynomial = 10; if (HAL_SPI_Init(&hspi5) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } }
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle) { GPIO_InitTypeDef GPIO_InitStruct; if(spiHandle->Instance==SPI5) { /* USER CODE BEGIN SPI5_MspInit 0 */ /* USER CODE END SPI5_MspInit 0 */ /* SPI5 clock enable */ __HAL_RCC_SPI5_CLK_ENABLE(); /**SPI5 GPIO Configuration PF7 ------> SPI5_SCK PF8 ------> SPI5_MISO PF9 ------> SPI5_MOSI */ GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF5_SPI5; HAL_GPIO_Init(GPIOF, &GPIO_InitStruct); /* USER CODE BEGIN SPI5_MspInit 1 */ /* USER CODE END SPI5_MspInit 1 */ } }
這個函數在main.c里已經幫你調用了,CUBEMX真棒!
接下來我們就用STM32這個主機與一塊FLASH從機進行通訊吧!
我們在這里調用一下正點原子整理的兩個文件spi.c和w25qxx.c以及他們的頭文件。通過一個定時器不斷掃描按鍵狀態,並且在while(1)里面寫入:
while (1) { switch (SPI_KEY) { case KEY0_PRES: W25QXX_Write((u8*)"showtimewalker", 0x00, 14); LED0 = !LED0; break; case KEY1_PRES: W25QXX_Write((u8*)"ShowTimeWalker", 0x00, 14); LED0 = !LED0; break; case KEY2_PRES: W25QXX_Read(DisPlayString, 0x00, 14); POINT_COLOR = BLUE; LCD_ShowString(10 + DisPlayCutoffX,60 + DisPlayCutoffY,300,16,16,(char*)DisPlayString); DisPlayCutoffY += 18; if (DisPlayCutoffY == 198){ DisPlayCutoffX += 120; DisPlayCutoffY = 0; } LED0 = !LED0; delay_ms(50); break; }
編譯通過,然后我們按下KEY0,寫入“showtimewalker”,按下KEY1,寫入“ShowTimeWalker”,按下KEY2,讀出數據,並且顯示到顯示屏上。看一下實際測試結果吧:
OK,調試成功,值得一提的是,FLASH中的數據是掉電不消失的哦,因此可以用於存儲一下特殊數據。
有疑問歡迎大家討論。謝謝閱讀。