最近做了一个项目,涉及到了串口,本来以为像串口这种经常使用的通讯方式,开发起来应该是很简单的,不说易如反掌,至少也不应该在一个问题上卡壳太久。说到底还是自己经验不足,还得多多学习才是!
该项目是使用CubeMX生成的初始化代码,在配置串口的时候我格外小心,该配置的都配置了,但是生成代码后烧到单片机中,却发现串口接收数据出现问题,只能接收到一次数据,后面无论如何都接收不到了。但是我已经在串口初始化的时候增加了一个函数HAL_UART_Receive_IT(&huart1,RevBuff,5);开启串口接收中断,按理说开启了中断,就应该可以正常接受了,但实际上只接收到一次。后面经过上网查找资料,看到有相同经历的人说除了在串口初始化函数后面增加HAL_UART_Receive_IT()函数,还应该在串口接收回调函数后面增加这个函数,也就是说每一次接收到数据之后都应该重新开启中断。经过测试,果然这个做法可以解决我的这个问题。
敢情这个函数只能起作用一次,每一次接收处理完数据,这个中断又关闭了?看了很多网上的资料,很少有说为什么需要重复开启,但是都会重复开启。那么我们的猜想应该就是成立的了。这里附上代码:
1 void MX_USART1_UART_Init(void) 2 { 3 4 huart1.Instance = USART1; 5 huart1.Init.BaudRate = 115200; 6 huart1.Init.WordLength = UART_WORDLENGTH_8B; 7 huart1.Init.StopBits = UART_STOPBITS_1; 8 huart1.Init.Parity = UART_PARITY_NONE; 9 huart1.Init.Mode = UART_MODE_TX_RX; 10 huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; 11 huart1.Init.OverSampling = UART_OVERSAMPLING_16; 12 if (HAL_UART_Init(&huart1) != HAL_OK) 13 { 14 _Error_Handler(__FILE__, __LINE__); 15 } 16 while(HAL_UART_Receive_IT(&huart1,RevBuff,5) != HAL_OK); 17 }
1 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 2 { 3 ... 4 ... 5 while(HAL_UART_Receive_IT(&huart1,RevBuff,5) != HAL_OK); //如果要正确接收数据,串口初始化函数和回调函数之后都要加上这个函数 6 }
然后我还遇到一个问题,就是每次当我上位机发送某一条指令的时候,总会引起单片机出现问题,具体表现为上位机发了该指令后单片机没有做出相对应的动作,甚至后面再发其他指令都没有任何对应的动作了。而经过测试,其他指令正常情况下都是可以被正确解读并执行的,那么问题应该就是出在这条特殊的指令了,这个问题现在说得轻巧,但是之前在排查的时候并没有考虑到指令的问题,绕了不少弯路,最后才找到问题所在的。问题出在这条特殊的指令不像其他指令一样是5字节的,它只有3字节,所以就导致单片机一直进入不了串口接收的回调函数。后来我查阅了资料,并且看了HAL库函数,知道了原因,这里跟大家说明一下。首先看一张图,是串口接收中断的流程图:
由上图可知,RxXferCount必须等于0的时候,才能调用HAL_UART_RxCpltCallback(huart);函数。RxXferCount是接收到一个字节递减一次的,而它原始的值使我们调用HAL_UART_Receive_IT(&huart1,RevBuff,5)的时候确定的,就是我定了5。所以上位机只发了3个字节,它就只递减了3次,还没达到0,就没办法调用接收回调函数,也就不会执行里面的内容。解决的办法就是让上位机再填充2个没有含义的数据,或者把HAL_UART_Receive_IT(&huart1,RevBuff,5)里的5改为1,一次接收一个,通过修改回调函数里的逻辑去判断。但是我更倾向于第一种方法,所有的指令统一格式,看起来比较整洁有条理,且下位机一次性接收多个数据,效率更高。
接下来,我们看看HAL_UART_Receive_IT()函数的源代码:
1 HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) 2 { 3 /* Check that a Rx process is not already ongoing */ 4 if(huart->RxState == HAL_UART_STATE_READY) 5 { 6 if((pData == NULL) || (Size == 0U)) 7 { 8 return HAL_ERROR; 9 } 10 11 /* Process Locked */ 12 __HAL_LOCK(huart); 13 14 huart->pRxBuffPtr = pData; 15 huart->RxXferSize = Size; 16 huart->RxXferCount = Size; 17 18 huart->ErrorCode = HAL_UART_ERROR_NONE; 19 huart->RxState = HAL_UART_STATE_BUSY_RX; 20 21 /* Process Unlocked */ 22 __HAL_UNLOCK(huart); 23 24 /* Enable the UART Parity Error Interrupt */ 25 __HAL_UART_ENABLE_IT(huart, UART_IT_PE); 26 27 /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */ 28 __HAL_UART_ENABLE_IT(huart, UART_IT_ERR); 29 30 /* Enable the UART Data Register not empty Interrupt */ 31 __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE); 32 33 return HAL_OK; 34 } 35 else 36 { 37 return HAL_BUSY; 38 } 39 }
从第15行和16行我们可以看到,我们把Size赋值给了 huart->RxXferSize 和 huart->RxXferCount这两个参数,所以当我们把Size设置为5的时候,huart->RxXferCount必须递减5次才会变成0,从而进入回调函数执行其中的内容。