串口收到的两组数据之间,往往会有一定的时间间隔。可以判断这个间隔,来实现无需结束符,无需指定长度,串口可接收不定长数据的功能。如果串口在一定的时间内没有收到新的数据,可以认为一组数据已经接收完毕了。思路是用定时器来设置一个“闹钟”,连续的一段时间没有收到新的数据,闹钟响起,就把已经收到的数据打包,做相应处理。
定时器溢出时间配置
首先修改定时器的溢出时间。本文规定使用5ms的间隔。在某些通信协议中,会规定间隔时间。例如Modbus规定两组数据之间要间隔3.5字符。
实际上,间隔的时间常常与通信的波特率是相关的。在9600波特率下,一个字节的数据共 起始+8数据+结束=10位,一位是104us,所以一个字节的数据是1.04ms,3.5个字节,我们就认为是4ms。有时可能有校验位,稍微保险一点,5ms吧。假如使用115200的波特率,5ms已经算是非常“奢侈”了。
本文使用定时器3来计时,配置的PSC为8399,ARR为49,可得5ms的溢出时间,配置过程可以参考通用定时器章节。
串口接收中断服务函数
我们在串口接收中断服务中,把收到的所有数据都放到数组中去,判断收到的是否是第一个字符,如果是则开启定时器。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance==USART1)
{
__HAL_TIM_SET_COUNTER(&htim3,0);
if(0 == UART1_Rx_cnt)//如果是第一个字符,则开启定时器
{
__HAL_TIM_CLEAR_FLAG(&htim3,TIM_FLAG_UPDATE);
HAL_TIM_Base_Start_IT(&htim3);
}
UART1_Rx_Buf[UART1_Rx_cnt] = UART1_temp[0];
UART1_Rx_cnt++;
HAL_UART_Receive_IT(&huart1,(uint8_t *)UART1_temp,REC_LENGTH);
}
}
其中__HAL_TIM_SET_COUNTER是HAL提供的一个宏定义,类似于函数,功能是通过宏来直接修改寄存器的值。
由于HAL库的串口接收中断在每次执行后都会关闭,所有在串口的中断里要重新手动开启串口接收中断。
另外,由于定时器中断在开启定时器的时候就会执行,所以需要开启定时器之前就把中断标记位清除。
定时器中断服务
一旦定时器发生溢出中断,说明已经到了5ms的时间间隔,可以把数据截断,根据业务需求来做相应处理,我的做法是设着一个标志位,然后在主函数的死循环内不断检测标志位,如果标志位被置1,则把收到的数据发送出去。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==(&htim3))
{
LED1 = !LED1;
UART1_Rx_flg = 1;
HAL_TIM_Base_Stop_IT(&htim3);//关闭定时器
}
}
//main() while(1)
if(UART1_Rx_flg)
{
HAL_UART_Transmit(&huart1,UART1_Rx_Buf,UART1_Rx_cnt,0xffff); //发送接收到的数据
for(int i = 0;i<UART1_Rx_cnt;i++)
UART1_Rx_Buf[i] = 0;
UART1_Rx_cnt = 0;
UART1_Rx_flg = 0;
}
功能是串口接收什么就回复什么,但无需结束符,也不用指定长度。当然最长不能超过UART1_Rx_Buf数据的大小。
附上源码