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方式。