在玩转 瑞萨RA4M2 单片机,肯定离不开串口。串口使用一个称为串行通信协议的协议来管理数据传输,该协议在数据传输期间控制数据流,包括数据位数、波特率、校验位和停止位等。由于串口简单易用,在各种产品交互中都有广泛应用。
但在使用串口通讯的时候,我们并不知道对方会发送多少个数据,也不知道数据什么时候发送完,简单来讲就是:如何确保收到一帧完整的数据?
串口发送的数据有长有短,如果没有接收完整,肯定会影响后续业务的处理。
为了接收不定长数据,常见的处理方法有:
固定格式
比如双方约定,一帧的数据以 AA BB 开头,以 BB AA 结尾,这样在从机接收数据的时候,一旦收到 AA BB 字符,就知道对方要发来一个数据包了,然后就把后面发来的数据保存起来,直到接收到 BB AA 为止。
这种方法简单高效,但缺点就是需要每个字符都进行判断,浪费 CPU 资源,增加功耗。
数据接收完成判断
如何判断一帧的数据接收完成了?
我使用超时的方法进行判断,这种方法虽然会耗费 CPU 资源,但因为比较简单,所以使用也很广泛。
超时判断的思路如下:
将接收到的字符保存在接收缓冲区里,并定义一个变量 U1_Rxlen 计算总共收到了多少个字符;
假如一帧的数据接收完成了,那么 U1_Rxlen 变量的值应该维持不变。
第一个步骤比较好实现,还是在串口 2 接收中断里,做一些小小的改动:
uint16_t U1_Rxlen = 0;
uint16_t U1_RxlencntPre = 0;
void uart9_callback (uart_callback_args_t * p_args)
{
if(p_args->event == UART_EVENT_TX_COMPLETE)
{
uart_send_complete_flag = true;
}
if(p_args->event == UART_EVENT_RX_CHAR)
{
if(U1_Rxlen >= sizeof(U1_RxBuff)) U1_Rxlen = 0;
U1_RxBuff[U1_Rxlen++] = (uint8_t)p_args->data;
}
}

关键是第二步,我们如何判断 U1_Rxlen 什么时候维持不变(也就是一帧的数据接收完成了)?也很简单,我们就定时去查看一下这个变量的值,看看是否跟上一次一样,如果一样的话就说明数据接收完成了。
因此我们需要再借助一个新的变量 U1_RxlencntPre ,记录上一次接收到的数据的长度(上面的代码已经定义好了)。
void user_uart_clear(void)
{
memset(U1_RxBuff, 0, sizeof(U1_RxBuff));
U1_Rxlen = 0;
}
uint8_t user_uart_wait_receive(void)
{
if(U1_Rxlen == 0)
return REV_WAIT;
if(U1_Rxlen == U1_RxlencntPre)
{
U1_Rxlen = 0;
return REV_OK;
}
U1_RxlencntPre = U1_Rxlen;
return REV_WAIT;
}

然后我们在 main 函数里的 while 死循环定期(例如10ms)调用 user_uart_wait_receive 函数,如果返回值为 REV_OK 则代表帧数据接收完成,我们就可以将数据打印出来。
while(1)
{
if(user_uart_wait_receive() == REV_OK)
{
printf("recv: %s\r\n", U1_RxBuff);
user_uart_clear();
}
HAL_Delay(10);
}

编译代码,烧录到板子


打开串口助手

发送数据


可以看到可以接收不定长度数据
完美!!!!!!
详情看视频