很多工程师都是通过循环DMA配合UART的IDLE中断实现1帧数据接收的。
但是这种方式也有缺点,比如一个设备在发送同一帧数据时,在发送数据帧的中间去处理中断,造成一个数据帧分成2个部分发出的。这两个部分中间出现了一个空闲帧。那么我们使用上面的方法去接收数据就有问题了。
那么能不能不要一收到空闲帧就断帧,而是有一定的延时呢?有一定的延时就能很好的解决上面的问题。在以前需要使用一个定时器计时。但现在在STM32U5中提供了一个"接收超时"的功能。
通过这个功能,我们可以设置一定的延时时间,如果在收到最后一个字节的停止位后,这个延时时间之后没有收到新的数据,就产生一个中断。
在STM32U5中,通过RTOEN位可以使能这个功能,通过RTOIE可以使能对应的中断。通过RTO可以设置一定的延时时间,注意这个时间是按照波特率的位时间计算的,比如波特率是9600,那么如果想延时100ms,则对应的是发送了960位,那么就要在RTO中写入960,以设置100ms的延时。
在下面的例子中,使用RTOF中断配合DMA实现数据的自动收取和断帧。
一、使能
首先在串口初始化的下面添加使能"接收超时"的功能。
HAL_UART_EnableReceiverTimeout(&huart1); //使能接收超时
HAL_UART_ReceiverTimeout_Config(&huart1,960); //设置超时时间,这个需要根据波特率算,如果波特率是9600,那么超时按照100ms算的话,就是960,也就是接收960个位的时长为100ms
__HAL_UART_CLEAR_FLAG(&huart1,UART_CLEAR_RTOF); //先清楚RTOF位,这个位指示是否接收超时
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RTO); //使能接收超时的中断
二、中断函数
然后在串口的中断函数中添加处理函数,因RTOF需要软件清除,因此需要在中断中手动清除标志位。因为DMA配置的是循环接收,所以是用的环形数组。进入RTOF中断后,就认为1帧数据结束,从数组中取出数据放到另一个缓存区,并置标志位。有main函数将数据原样发出。
注意自己定义的RTOF中断函数要放在中断函数的前部,不要放在 *HAL_UART_IRQHandler(&huart1);*的后面,因为这个函数里也有RTOF的处理功能,会影响使用的。
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
static uint16_t last_index;
uint16_t index;
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RTOF) != 0){
__HAL_UART_CLEAR_FLAG(&huart1,UART_CLEAR_RTOF); //清除标志位
//以下内容为环形数组的数据读取,写的有点粗糙,仅做参考
index = 100-( __HAL_DMA_GET_COUNTER(huart1.hdmarx));
if(index != last_index){
if(index > last_index){
for(int i = last_index;i<index;i++){
send_buf[i-last_index] = recv_buf[i];
}
send_buf[100] = index - last_index;
}else{
uint16_t s_index = 0;
for(int i=last_index;i<100;i++){
send_buf[s_index++] = recv_buf[i];
}
for(int i =0;i<index;i++){
send_buf[s_index++] = recv_buf[i];
}
send_buf[100] = s_index;
}
last_index = index;
}
}
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
三、测试IDLE和RTOF中断
最后测试了一下使用IDLE中断和RTOF中断,这两个的时间差。和预期的一样。如果使用115200的波特率,超时值设为1152,则IDLE中断应该比RTOF中断早100ms。通过systick的计数值估算时间,如下图所示。
其中time1是进入IDLE中断的时间,time2是进入RTOF中断的时间。两者相差正好是100,也就是100ms 。
四、总结
IDLE和RTOF两种方式获取1帧数据,各有各的有点。使用IDLE的话有现成的HAL库,但是确实会有判断出错的情况。而使用RTOF的话会更灵活,但是需要自己编写一部分代码。