1、情景描述:
最近在做一个项目,X86的上位机通过串口控制MCU,使用串口中断接收上位机数据时,MCU在上电的情况下烧录程序,可以正常接收上位机的数据,在断电重启后,一直进入不了中断回调函数,上电的情况是X86上电,MCU也同时上电。
2、原因分析:
造成这个的原因是因为硬件上电的时候,因为X86跟MCU是同时上电的,上电后会把串口的电平拉高,这个高电平触发了MCU的串口中断,导致MCU的串口中断误以为接收到了一个数据,例如 HAL_UART_Receive_IT(&huart1, (uint8_t *)Rx_buff, 5) 这里,上电后MCU误以为接收了一个数据,还剩下4个数据没有接收,然后上位机每次发送5个数据过来后MCU中断数据接收个数错误,所以一直无法进入中断回调函数。
我们看到 HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart) ,里面的 RxXferCount 是告诉我们中断要接收的剩余数据量大小,根据上面举例子的话,上电时因为那个高电平的原因导致 RxXferCount 变成了4,如下图打印信息所示
接着我们重新看回 HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart) 函数里的调用回调函数部分,下图所示,发现 RxXferCount 要为0的时候才会调用中断回调函数,依旧以上面例子说明,当MCU误以为上电时的高电平为数据时,上位机再发送5个数据下来,RxXferCount 就永远无法变成0,所以导致一直进入不了中断回调函数。
3、解决方法:
3.1软件解决方法
软件解决的时候,我们要知道导致这个问题的根源是 RxXferCount 这个值被误判了,所以我们只需要在上电的时候,对这个值进行修正即可;
首先我们定义一个标志位,用来标志MCU的状态是刚上电的状态
char uart_error_flag=0;
接着我们编写函数对 RxXferCoun 值进行处理
/*** 函数名:void uart_error(void) 说 明:解决刚上电时,由于串口电平拉高,导致串口中断误以为接收到了一个字节, 导致后面接收数据个数一直错误,无法进入中断回调函数问题 传入值:无 传出值:无 **/ void uart_error(void) { if( (huart1.RxXferCount < Rxdsize) && (uart_error_flag==0) ) { /*RxXferCount 告诉我们剩余空间大小,如果剩余空间和总空间不一样,则说明中断收到数据了*/ printf("huart1.RxXferCount = %d ",huart1.RxXferCount); uart_error_flag = 1; huart1.RxXferCount = 5; //修改剩余空间,防止无法进入回调 } }
最后我们在 main 函数里的 while 循环前调用即可
int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART2_UART_Init(); //初始化打印信息串口 MX_USART1_UART_Init(); //初始化中断接收串口 HAL_UART_Receive_IT(&huart1, (uint8_t *)Rx_buff, Rxdsize); //打开串口中断接收 uart_error(); //处理上电时串口中断误判的问题 while (1) { /* 注意要在中断回调函数里重新打开串口中断接收,否则串口中断接收只能接收一次 */ } }
3.2 硬件解决方法
硬件解决方法比较粗暴,就是做一个电源延时电路,等X86重新上电后,再给MCU上电。
补充说明:
使用DMA接收时候不会出现这种情况,不过使用DMA接收,如果上位机发送数据过快,会出现数据粘包现象,例如上位机发送是数据一包是5个数据,如果上位机发送数据过快(20ms以内),MCU就好会把接收到的好几包数据当做一包数据来处理,例如把3包数据当做1包数据来处理,这样MCU就会误以为一包数据的15个了,出现误判的情况,不过它接收的数据是准确的,就是分包能力没有串口中断强。