MCU與CPLD之間有3根線,那麼能夠選擇UART通訊或者SPI通訊方式。異步
因爲CPLD沒法通知MCU數據傳輸的開始與結束,MCU須要自行判別,那麼MCU能夠經過中斷方式來檢測數據傳輸的開始,經過超時來檢測數據傳輸的結束。函數
UART與SPI的區別在於前者是異步通訊後者是同步通訊方式,不管是SPI仍是UART方式都須要MCU經過IO模擬方式軟件實現。使用UART傳輸若是收發雙方產生的波特率存在誤差則會致使數據傳輸出錯,而同步傳輸方式有時鐘信號的約束,相比異步傳輸方式數據準確率會更高。若是使用軟件模擬UART,須要使用定時器做爲波特率發生器。若是波特率比較高,那麼定時器中斷頻率就須要更高,這樣會影響整個MCU系統的實時性。綜合考慮後選擇SPI方式。ui
CPLD對MCU只發送數據,那麼MCU只須要做爲SPI的從機便可,三個IO分配爲SPI的CS、CLK、DAT引腳。code
因爲CS是低電平有效,那麼將CS引腳配置爲中斷輸入方式,當CS中斷觸發後開始數據接收處理。由於CPLD也不知道數據傳輸何時結束,因此沒法經過將CS置高電平來告訴數據傳輸的結束,那麼CS置高電平只能代表一個字節傳輸結束。MCU能夠經過超時方式來判斷一包數據的結束,相似於串口的空閒中斷方式。blog
SPI數據接收在外部中斷中操做。將CLK引腳配置爲外部中斷的上升沿觸發
,CS有效的狀況下CLK中斷觸發後進行數據接收。同步
SPI空閒中斷採用100us週期定時器判斷。爲了MCU系統的實時性,只有CS中斷觸發後纔會開啓定時器,超時判斷完成後關閉定時器。it
CPLD向MCU發送一字節的時序圖以下(速率:200KBit/s):event
GPIO的配置:無數據CLK爲低電平,CS低有效。CS上升沿、降低沿都會觸發中斷,判斷1字節傳輸的起始與結束;CLK上升沿觸發中斷,數據在CLK上升沿採樣class
/* *********************************************************************************************** * 函 數: BSP_CPLD_GPIO_Init * 描 述: 配置CPLD的SPI通訊引腳 * 輸 入: 無 * 輸 出: 無 *********************************************************************************************** */ void BSP_CPLD_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ CPLD_PIN_CLK_ENABLE(); /*Configure GPIO pin : PtPin */ GPIO_InitStruct.Pin = CPLD_SPI_CSN_PIN; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(CPLD_SPI_CSN_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = CPLD_SPI_SCK_PIN; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; GPIO_InitStruct.Pull = GPIO_PULLDOWN; HAL_GPIO_Init(CPLD_SPI_SCK_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = CPLD_SPI_DAT_PIN; GPIO_InitStruct.Pull = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(CPLD_SPI_DAT_PORT, &GPIO_InitStruct); }
CPLD_SPI_CS外部中斷函數:用於使能數據接收、空閒檢測軟件
/* *********************************************************************************************** * 函 數: CPLD_CS_EXTI_IRQHandler * 描 述: CPLD_SPI_CS中斷函數 * 輸 入: 無 * 輸 出: 無 *********************************************************************************************** */ void CPLD_CS_EXTI_IRQHandler(void) { if (__HAL_GPIO_EXTI_GET_IT(CPLD_SPI_CSN_PIN)) { if (READ_BIT(CPLD_SPI_CSN_PORT->IDR, CPLD_SPI_CSN_PIN)) { /* CS高失能SPI數據接收 */ CLEAR_BIT(g_tCpldSpi.ucState, CPLD_FLAG_CSN); } else { /* CS低使能SPI數據接收 */ s_tCpldSpi.ucByte = 0; s_tCpldSpi.ucBitCount = 0; SET_BIT(g_tCpldSpi.ucState, CPLD_FLAG_CSN); /* 開啓空閒檢測 */ if (0 == s_tCpldSpi.ucIdleCheck) { s_tCpldSpi.ucIdleCheck = 1; HAL_TIM_Base_Start_IT(&Tim7Handle); } } __HAL_GPIO_EXTI_CLEAR_IT(CPLD_SPI_CSN_PIN); } }
CPLD_SPI_SCK外部中斷函數:用於SPI數據的接收
/* *********************************************************************************************** * 函 數: CPLD_SCK_EXTI_IRQHandler * 描 述: CPLD_SPI_SCK中斷函數 * 輸 入: 無 * 輸 出: 無 *********************************************************************************************** */ void CPLD_SCK_EXTI_IRQHandler(void) { if (__HAL_GPIO_EXTI_GET_IT(CPLD_SPI_SCK_PIN)) { /* CSN有效則進行數據接收 */ if (READ_BIT(g_tCpldSpi.ucState, CPLD_FLAG_CSN)) { if (READ_BIT(CPLD_SPI_DAT_PORT->IDR, CPLD_SPI_DAT_PIN)) { s_tCpldSpi.ucByte |= (0x80 >> s_tCpldSpi.ucBitCount); } else { s_tCpldSpi.ucByte &= ~(0x80 >> s_tCpldSpi.ucBitCount); } /* 收滿一字節後存向接收FIFO */ if (++s_tCpldSpi.ucBitCount > 7) { g_tCpldSpi.ucaRxBuf[g_tCpldSpi.usRxWrite] = s_tCpldSpi.ucByte; if (++g_tCpldSpi.usRxWrite >= 1024) { g_tCpldSpi.usRxWrite = 0; } if (g_tCpldSpi.usRxCount < CPLD_SPI_RX_BUF_LEN) { g_tCpldSpi.usRxCount++; } /* SPI收到新數據,設置一個標記,供應用程序查詢 */ SET_BIT(g_tCpldSpi.ucState, CPLD_FLAG_RXNE); } } __HAL_GPIO_EXTI_CLEAR_IT(CPLD_SPI_SCK_PIN); } }
定時器中斷函數:判斷CPLD_SPI空閒中斷的發生
/* *********************************************************************************************** * 函 數: TIM7_IRQHandler * 描 述: 定時器7中斷函數,100us中斷週期 * 輸 入: 無 * 輸 出: 無 *********************************************************************************************** */ void TIM7_IRQHandler(void) { static uint16_t t100us_cnt = 0; if (__HAL_TIM_GET_FLAG(&Tim7Handle, TIM_FLAG_UPDATE) && __HAL_TIM_GET_IT_SOURCE(&Tim7Handle, TIM_IT_UPDATE)) { /* CPLD-SPI空閒檢測,1ms */ if (READ_BIT(g_tCpldSpi.ucState, CPLD_FLAG_CSN)) { t100us_cnt = 0; } else { t100us_cnt++; } if (t100us_cnt > 10) { /* SPI收到一幀數據,設置一個標記,供應用程序查詢 */ SET_BIT(g_tCpldSpi.ucState, CPLD_FLAG_IDLE); #if ENABLE_RTOS tx_event_flags_set(&tx_event_flags, TX_EVENT_CPLD_SPI_IDLE, TX_OR); #endif s_tCpldSpi.ucIdleCheck = 0; HAL_TIM_Base_Stop_IT(&Tim7Handle); } __HAL_TIM_CLEAR_IT(&Tim7Handle, TIM_IT_UPDATE); } }
經屢次發送固件數據驗證,MCU均能正常接收數據,而且沒有出現數據錯誤的狀況,可用於該項目。
該方法也能夠用於實現模擬UART功能,僅提供思路,未通過驗證(以115200,8-N-1
爲例)。
使用該UART方式優點在於比SPI方式使用更少的引腳,只須要1個IO便可完成通訊。缺點在於若是要求通訊速率高或須要屢次採樣,那麼產生波特率的定時器中斷頻率高,若是被其餘更高優先級中斷打斷可能形成波特率不許,數據錯誤。還有就是UART方式在數據通訊速率上沒有SPI有優點。不到萬不得已不建議使用軟件UART方式。