STM32串口自定义数据接收协议

写在前面

最近使用STM32做串口数据收发,遇到了一些问题。折腾了一番,在此记录一下。

0 需求

  1. 云平台通过“发布消息”,下行指令。
  2. 4G模块接收平台下行指令并转发到单片机,单片机通过串口(UART3)做数据接收与分析。
    总的来说,比较简单。单片机和4G通过串口通信,当4G与平台连接之后,在保证数据在平台与4G模块之间能正常流转的情况下,可视为单片机使用串口直接与平台进行通信。而原子亦给出了串口通信的相关例程,其中包含串口收发实验,可做参考。

1 问题产生

为了实现需求,先进行两个小实验
3. 模块+上位机实验 : 验证4G模块与平台之间数据收发正常。
4. 电路板串口数据接收实验 :排除电路板硬件异常问题。

1.1 模块+上位机实验

平台发布消息,模块TX引脚输出。通过CH340与串口调试助手相连,接收并显示数据。验证模块能否正常收发数据。
在这里插入图片描述
---------------------补一张串口接收数据图--------------

可以正常收发数据,排除模块问题。
ASCII编码对照表_911查询

1.2 电路板串口数据接收实验

实验硬件:UART1(COM3) , UART3(COM1)实验。
实验现象:UART1 接收到的消息通过UART1打印到串口;UART3 接收到的消息亦通过UART1打印到串口。(UART1做串口调试使用)
实验结论: 排除电路板硬件异常原因,UART1 & UART3 可以正常收发数据。
示例代码:

while(1)
{
		
   if(USART_RX_STA&0x8000)
	{		
		LED0 = 0;			 //点亮LED显示接收到消息	
		len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
		printf("\r\nUART1发送的消息为:\r\n");
		HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000);	//发送接收到的数据
		while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);		//等待发送结束
		printf("\r\n\r\n");//插入换行
		USART_RX_STA=0;
	}
	else if(USART3_RX_STA&0x8000)
	{
		LED0 = 1;			 //熄灭LED显示接收到消息	
		len=USART3_RX_STA&0x3fff;//得到此次接收到的数据长度
		printf("\r\nUART3发送的消息为:\r\n");
		HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART3_RX_BUF,len,1000);	//发送接收到的数据
		while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);		//等待发送结束
		printf("\r\n\r\n");//插入换行
		USART3_RX_STA=0;
	} 
}	

在这里插入图片描述

1.3 问题来了!

当以上两个小测试完成时,可以确定整个系统软件与硬件无误。也就是说,当电路板上插上4G模块时,即可实现单片机与平台通信的功能!
然而,当平台发布数据时,单片机未能接收到数据。通过软件调试等操作发现,UART3并未进入到接收到数据中断。用示波器测量模块TX引脚,平台发布消息时确实有信号输出!迷惑行为,,,,,,

2 开始分析

2.1 串口数据格式

如下图,可以看出

  1. 无数据时,电平始终为‘1’
  2. 起始位为1位‘0’,停止位为1位‘1’
    在这里插入图片描述
    串口助手配置为:
    bound :115200 ;停止位 :1 ;数据位 :8 ; 检验位 : None
    在这里插入图片描述

2.2 测一波波形

以上两个小测试确实可以验证软件与硬件无误。那么为什么单片机可以接收上位机传来的数据,而不能接收4G模块转发来的数据呢?二者数据有何区别?
验证方法 : 上位机和平台同时发送信息,测其输出信号。测试数据 11(0011_0001 0011_0001)

  • 注 :串口助手及平台发布的数据为字符(ASCII),助手可选择hex发送及显示。
    1. 平台发布数据 11(0011_0001 0011_0001) ,4G转发数据波形如图
    在这里插入图片描述
    **波形分析:**非常漂亮的波形,可以读出数据为 0011_0001 0011_0001(起始位为1位‘0’,停止位为1位‘1’)
    2. 串口助手通过CH340发送数据 11(0011_0001 0011_0001)
    在这里插入图片描述
    CH340 的 TX 引脚输出波形如下:X10_1000_1100_10_1000_1100_10_1011_0000_10_0101_0000_1X
    在这里插入图片描述
    参考串口数据格式,可知:串口发送的数据为:0011_0001 , 0011_0001 , 0000_1101 , 0000_1010

**波形分析:**前两个数据值为32,对应的字符为 ‘1’,‘1’,与发送数据一致。多了后两个值13 10,查询ASCII码为 0D 0A。对应换行键和归位键,嗯?
在这里插入图片描述
查看串口助手,果然!默认勾选了“发送新行”
在这里插入图片描述
再次测试,勾选了“发送新行”数据可以被接收;不勾选了“发送新行”,数据不被接收!可以看出“发送新行”即为单片机识别数据的“校验”格式。那么程序中一定有与“校验”相关的代码,那就找到他!

  • 测波形时遇到一个现象,在这记录一下。

当直接接CH340输出时,图形在上。可以看到高电平只有1,5V左右,电压驱动并不强;当CH340与单片机相连时,发送波形测得的波形如下。当时怀疑过是高电平的问题,在单片机上接了个上拉电阻,波形是好看了,但依旧没有解决问题。因为串口低电平有效,并不要求严格的高电平。
在这里插入图片描述

3 代码分析

关于串口接收的代码如下,一眼就可看到关于0x0a,0x0d的判断,确认是结尾校验无误了。若要修改为 ‘**’ 校验,改为0x2a,0x2a即可。修改后测试成功,没图。

if(huart->Instance==USART3)//如果是串口1
{
	if((USART3_RX_STA&0x8000)==0)//接收未完成
	{
		if(USART3_RX_STA&0x4000)//接收到了0x0d
		{
			if(aRx3Buffer[0]!=0x0a)USART3_RX_STA=0;//接收错误,重新开始
			else USART3_RX_STA|=0x8000;	//接收完成了 
		}
		else //还没收到0X0D
		{	
			if(aRx3Buffer[0]==0x0d)USART3_RX_STA|=0x4000;
			else
			{
				USART3_RX_BUF[USART3_RX_STA&0X3FFF]=aRx3Buffer[0] ;
				USART3_RX_STA++;
				if(USART3_RX_STA>(USART_REC_LEN-1))USART3_RX_STA=0;//接收数据错误,重新开始接收	  
			}		 
		}
	}
}
  • 注 :关于0x0a,0x0d 网上也有不少资料参考。原子的串口助手也有提示正确格式。由于很少使用16进制发送,一致没有注意到!
    在这里插入图片描述

4 新的问题:串口数据累加

新的问题收测试时出现寄存器数据累加情况,具体表现为:

  1. 当串口UART3接收到的数据未加结束校验“**”,单片机未能判断数据接收完毕。
  2. 当下次数据来临(带校验),单片机判断数据发送完毕。通过串口1将数据输出。
    在这里插入图片描述
    再使用例程时,取消"发送新行",会出现同样的问题,由此可以判断是底层代码问题。
    在这里插入图片描述
    猜测:串口在接收未加校验的数据时,已经将数据存入串口接收缓冲buff中。当之后数据(带校验)来临时,继续将数据存入buff,并判断数据接收完毕。此时,多次发送的数据集中在同一buff中,数据为及时清空,由此导致数据累加的情况。
    再来分析这段代码:
    其中,USART3_RX_STA 是串口接收状态标记。定义为: u16 USART_RX_STA=0; 功能如下
    | bit15 | bit14 | bit13~0 |
    | ---------------- | -------------- | -------------------- |
    | 接收完成标志0x0a | 接收到0X0d标志 | 接收到的有效数据个数 |
if(huart->Instance==USART3)	//如果是串口3
{		
	if((USART3_RX_STA&0x8000)==0)		//接收未完成  USART3_RX_STA最高位判断
	{
		if(USART3_RX_STA&0x4000)		//接收到了第一个0x2a  USART3_RX_STA次高位判断
		{
			if(aRx3Buffer[0]!=0x0a)
			{
				USART3_RX_STA=0;		//接收错误,重新开始
			}
			else USART3_RX_STA|=0x8000;	//接收完成了 
		}
		else 							//还没接收到第一个0x2a
		{	
			if(aRx3Buffer[0]==0x0d)
			{
				USART3_RX_STA|=0x4000;
			}
			else
			{
				USART3_RX_BUF[USART3_RX_STA&0X3FFF]=aRx3Buffer[0] ;  //0X3FFF  USART3_RX_STA 低14位是数据
				USART3_RX_STA++;
				if(USART3_RX_STA>(USART_REC_LEN-1))
				{
					USART3_RX_STA=0;//接收数据错误,重新开始接收
				}	  
			}		 
		}
	}
}

大致画了下流程图,时间关系。不再文字分析了,几个判断嵌套。串口通信实验讲解里关于USART_RX_STA的问题与思考这篇博客文字分析比较详细,推荐一波。
在这里插入图片描述
可以看出,与猜测一致。串口接收缓冲buff并没有进行数据清除。当数据(带校验)未临时,之前的数据会一直寄存在buff中,直到最终发送完毕。(当然数据累加有一定上限,USART_REC_LEN)

若要消除数据累加的情况,就必须在接收完一次不带校验的数据后,及时清除缓冲buff。实际使用中,两次接收数据之间有一定间隔,若能在间隔之中清除buff,即可规避。故加入以下代码,测试一下。

	/* 间隔一定时间清空串口缓冲BUF USART3_RX_BUF */
		if (time_10_ms)		 //定时器 10ms
		{ 
			time_10_ms = 0 ;
			memset(USART3_RX_BUF, 0, sizeof USART3_RX_BUF);  
		}

在这里插入图片描述
测试结果:不加校验的数据串口不识别,定时清空处理,无数据累加情况。测试ok!

总结

没啥写的,强迫症凑一凑。

版权声明:本文为CSDN博主「祥玉汪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_43063744/article/details/120224762

写在前面

最近使用STM32做串口数据收发,遇到了一些问题。折腾了一番,在此记录一下。

0 需求

  1. 云平台通过“发布消息”,下行指令。
  2. 4G模块接收平台下行指令并转发到单片机,单片机通过串口(UART3)做数据接收与分析。
    总的来说,比较简单。单片机和4G通过串口通信,当4G与平台连接之后,在保证数据在平台与4G模块之间能正常流转的情况下,可视为单片机使用串口直接与平台进行通信。而原子亦给出了串口通信的相关例程,其中包含串口收发实验,可做参考。

1 问题产生

为了实现需求,先进行两个小实验
3. 模块+上位机实验 : 验证4G模块与平台之间数据收发正常。
4. 电路板串口数据接收实验 :排除电路板硬件异常问题。

1.1 模块+上位机实验

平台发布消息,模块TX引脚输出。通过CH340与串口调试助手相连,接收并显示数据。验证模块能否正常收发数据。
在这里插入图片描述
---------------------补一张串口接收数据图--------------

可以正常收发数据,排除模块问题。
ASCII编码对照表_911查询

1.2 电路板串口数据接收实验

实验硬件:UART1(COM3) , UART3(COM1)实验。
实验现象:UART1 接收到的消息通过UART1打印到串口;UART3 接收到的消息亦通过UART1打印到串口。(UART1做串口调试使用)
实验结论: 排除电路板硬件异常原因,UART1 & UART3 可以正常收发数据。
示例代码:

while(1)
{
		
   if(USART_RX_STA&0x8000)
	{		
		LED0 = 0;			 //点亮LED显示接收到消息	
		len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
		printf("\r\nUART1发送的消息为:\r\n");
		HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000);	//发送接收到的数据
		while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);		//等待发送结束
		printf("\r\n\r\n");//插入换行
		USART_RX_STA=0;
	}
	else if(USART3_RX_STA&0x8000)
	{
		LED0 = 1;			 //熄灭LED显示接收到消息	
		len=USART3_RX_STA&0x3fff;//得到此次接收到的数据长度
		printf("\r\nUART3发送的消息为:\r\n");
		HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART3_RX_BUF,len,1000);	//发送接收到的数据
		while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);		//等待发送结束
		printf("\r\n\r\n");//插入换行
		USART3_RX_STA=0;
	} 
}	

在这里插入图片描述

1.3 问题来了!

当以上两个小测试完成时,可以确定整个系统软件与硬件无误。也就是说,当电路板上插上4G模块时,即可实现单片机与平台通信的功能!
然而,当平台发布数据时,单片机未能接收到数据。通过软件调试等操作发现,UART3并未进入到接收到数据中断。用示波器测量模块TX引脚,平台发布消息时确实有信号输出!迷惑行为,,,,,,

2 开始分析

2.1 串口数据格式

如下图,可以看出

  1. 无数据时,电平始终为‘1’
  2. 起始位为1位‘0’,停止位为1位‘1’
    在这里插入图片描述
    串口助手配置为:
    bound :115200 ;停止位 :1 ;数据位 :8 ; 检验位 : None
    在这里插入图片描述

2.2 测一波波形

以上两个小测试确实可以验证软件与硬件无误。那么为什么单片机可以接收上位机传来的数据,而不能接收4G模块转发来的数据呢?二者数据有何区别?
验证方法 : 上位机和平台同时发送信息,测其输出信号。测试数据 11(0011_0001 0011_0001)

  • 注 :串口助手及平台发布的数据为字符(ASCII),助手可选择hex发送及显示。
    1. 平台发布数据 11(0011_0001 0011_0001) ,4G转发数据波形如图
    在这里插入图片描述
    **波形分析:**非常漂亮的波形,可以读出数据为 0011_0001 0011_0001(起始位为1位‘0’,停止位为1位‘1’)
    2. 串口助手通过CH340发送数据 11(0011_0001 0011_0001)
    在这里插入图片描述
    CH340 的 TX 引脚输出波形如下:X10_1000_1100_10_1000_1100_10_1011_0000_10_0101_0000_1X
    在这里插入图片描述
    参考串口数据格式,可知:串口发送的数据为:0011_0001 , 0011_0001 , 0000_1101 , 0000_1010

**波形分析:**前两个数据值为32,对应的字符为 ‘1’,‘1’,与发送数据一致。多了后两个值13 10,查询ASCII码为 0D 0A。对应换行键和归位键,嗯?
在这里插入图片描述
查看串口助手,果然!默认勾选了“发送新行”
在这里插入图片描述
再次测试,勾选了“发送新行”数据可以被接收;不勾选了“发送新行”,数据不被接收!可以看出“发送新行”即为单片机识别数据的“校验”格式。那么程序中一定有与“校验”相关的代码,那就找到他!

  • 测波形时遇到一个现象,在这记录一下。

当直接接CH340输出时,图形在上。可以看到高电平只有1,5V左右,电压驱动并不强;当CH340与单片机相连时,发送波形测得的波形如下。当时怀疑过是高电平的问题,在单片机上接了个上拉电阻,波形是好看了,但依旧没有解决问题。因为串口低电平有效,并不要求严格的高电平。
在这里插入图片描述

3 代码分析

关于串口接收的代码如下,一眼就可看到关于0x0a,0x0d的判断,确认是结尾校验无误了。若要修改为 ‘**’ 校验,改为0x2a,0x2a即可。修改后测试成功,没图。

if(huart->Instance==USART3)//如果是串口1
{
	if((USART3_RX_STA&0x8000)==0)//接收未完成
	{
		if(USART3_RX_STA&0x4000)//接收到了0x0d
		{
			if(aRx3Buffer[0]!=0x0a)USART3_RX_STA=0;//接收错误,重新开始
			else USART3_RX_STA|=0x8000;	//接收完成了 
		}
		else //还没收到0X0D
		{	
			if(aRx3Buffer[0]==0x0d)USART3_RX_STA|=0x4000;
			else
			{
				USART3_RX_BUF[USART3_RX_STA&0X3FFF]=aRx3Buffer[0] ;
				USART3_RX_STA++;
				if(USART3_RX_STA>(USART_REC_LEN-1))USART3_RX_STA=0;//接收数据错误,重新开始接收	  
			}		 
		}
	}
}
  • 注 :关于0x0a,0x0d 网上也有不少资料参考。原子的串口助手也有提示正确格式。由于很少使用16进制发送,一致没有注意到!
    在这里插入图片描述

4 新的问题:串口数据累加

新的问题收测试时出现寄存器数据累加情况,具体表现为:

  1. 当串口UART3接收到的数据未加结束校验“**”,单片机未能判断数据接收完毕。
  2. 当下次数据来临(带校验),单片机判断数据发送完毕。通过串口1将数据输出。
    在这里插入图片描述
    再使用例程时,取消"发送新行",会出现同样的问题,由此可以判断是底层代码问题。
    在这里插入图片描述
    猜测:串口在接收未加校验的数据时,已经将数据存入串口接收缓冲buff中。当之后数据(带校验)来临时,继续将数据存入buff,并判断数据接收完毕。此时,多次发送的数据集中在同一buff中,数据为及时清空,由此导致数据累加的情况。
    再来分析这段代码:
    其中,USART3_RX_STA 是串口接收状态标记。定义为: u16 USART_RX_STA=0; 功能如下
    | bit15 | bit14 | bit13~0 |
    | ---------------- | -------------- | -------------------- |
    | 接收完成标志0x0a | 接收到0X0d标志 | 接收到的有效数据个数 |
if(huart->Instance==USART3)	//如果是串口3
{		
	if((USART3_RX_STA&0x8000)==0)		//接收未完成  USART3_RX_STA最高位判断
	{
		if(USART3_RX_STA&0x4000)		//接收到了第一个0x2a  USART3_RX_STA次高位判断
		{
			if(aRx3Buffer[0]!=0x0a)
			{
				USART3_RX_STA=0;		//接收错误,重新开始
			}
			else USART3_RX_STA|=0x8000;	//接收完成了 
		}
		else 							//还没接收到第一个0x2a
		{	
			if(aRx3Buffer[0]==0x0d)
			{
				USART3_RX_STA|=0x4000;
			}
			else
			{
				USART3_RX_BUF[USART3_RX_STA&0X3FFF]=aRx3Buffer[0] ;  //0X3FFF  USART3_RX_STA 低14位是数据
				USART3_RX_STA++;
				if(USART3_RX_STA>(USART_REC_LEN-1))
				{
					USART3_RX_STA=0;//接收数据错误,重新开始接收
				}	  
			}		 
		}
	}
}

大致画了下流程图,时间关系。不再文字分析了,几个判断嵌套。串口通信实验讲解里关于USART_RX_STA的问题与思考这篇博客文字分析比较详细,推荐一波。
在这里插入图片描述
可以看出,与猜测一致。串口接收缓冲buff并没有进行数据清除。当数据(带校验)未临时,之前的数据会一直寄存在buff中,直到最终发送完毕。(当然数据累加有一定上限,USART_REC_LEN)

若要消除数据累加的情况,就必须在接收完一次不带校验的数据后,及时清除缓冲buff。实际使用中,两次接收数据之间有一定间隔,若能在间隔之中清除buff,即可规避。故加入以下代码,测试一下。

	/* 间隔一定时间清空串口缓冲BUF USART3_RX_BUF */
		if (time_10_ms)		 //定时器 10ms
		{ 
			time_10_ms = 0 ;
			memset(USART3_RX_BUF, 0, sizeof USART3_RX_BUF);  
		}

在这里插入图片描述
测试结果:不加校验的数据串口不识别,定时清空处理,无数据累加情况。测试ok!

总结

没啥写的,强迫症凑一凑。

版权声明:本文为CSDN博主「祥玉汪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_43063744/article/details/120224762

生成海报
点赞 0

祥玉汪

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

STM32串口自定义数据接收协议

写在前面 最近使用STM32做串口数据收发,遇到了一些问题。折腾了一番,在此记录一下。 0 需求 云平台通过“发布消息”,下行指令。4G模块接收平台下行指令并转发到单片机,单片机通过

STM32 QSPI双闪存操作

STM32 QSPI双闪存操作 使用CubeMX或者CubeIDE生成框架 使用野火的开发板,MCU型号为stm32h750/743,他们家核心板上使用了QSPI挂了2片FLASH,型号为W25Q2