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!

总结

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

  • 4
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32是一种常用的微控制器,它可以通过串口与其他设备进行通信。而自定义协议解析则是指在串口通信过程中,通过编写代码解析特定格式的数据帧。 首先,我们需要定义自己的协议格式。协议中包含了数据帧的起始符、帧长度、数据内容和校验位等信息。例如,我们可以使用起始符“$”表示数据帧的开始。接下来,根据协议定义的长度字段(可以是固定长度或可变长度),读取数据帧的长度。然后根据长度字段读取数据内容,并进行相应的处理,例如将数据存储到缓冲区中。在读取数据的过程中,还需要对数据的完整性进行校验,例如使用CRC校验算法。校验通过后,我们可以根据业务需求对数据进行进一步处理,例如将数据发送给其他模块进行处理,或者通过串口回传应答数据等。 在STM32上实现自定义协议解析的关键在于串口中断的使用。通过配置串口接收中断,我们可以在每次接收到一个字节的数据时触发中断服务函数。在中断服务函数中,我们需要根据协议解析的逻辑对接收到的数据进行处理,判断数据帧的起始和结束位置。根据不同的业务需求,我们还可以根据协议解析的结果触发其他的操作,例如更新LCD显示、控制外部设备等。 另外,为了提高解析效率和稳定性,我们还可以通过使用DMA(直接内存访问)模式进行串口接收。DMA可以在不需要CPU的干预下直接将接收到的数据存储到指定的缓冲区中,从而避免了中断服务函数的频繁调用,提高了系统的响应速度和并发处理能力。 总而言之,STM32串口自定义协议解析是指通过编写代码,按照自定义协议格式解析串口数据帧,并根据解析结果进行相应的处理。这样可以实现与其他设备的可靠通信,并且可以根据业务需求灵活地进行数据的处理。 ### 回答2: STM32是一款广泛应用于嵌入式系统中的微控制器芯片,它具有丰富的外设资源,其中包括串口(USART)模块。在进行串口通信时,我们可以使用自定义协议来实现数据的传输和解析。 串口自定义协议解析的过程主要分为两个步骤:发送端的数据封装和接收端的数据解析。 在发送端,我们需要将要传输的数据按照自定义协议格式进行封装。通常情况下,自定义协议包含数据头、数据内容和校验位等信息。数据头用于标识数据的起始位置,数据内容包含要传输的实际数据,而校验位则用于验证数据的完整性。在STM32中,我们可以使用串口发送函数来将封装好的数据发送出去。 在接收端,首先需要配置串口接收中断功能,以实现数据的异步接收。当接收数据后,中断服务程序会自动被触发。在中断服务程序中,我们可以通过读取串口接收寄存器的方式获取接收到的数据接收到的数据需要按照自定义协议进行解析,校验数据头,确认数据的起始位置。接着,我们可以提取出数据内容,并进行相应的处理。最后,我们还需要验证校验位,以确保接收到的数据的完整性。 在STM32中,除了基本的串口发送和接收函数外,还可以使用串口DMA功能来提高数据的传输效率,减少CPU的占用率。通过配置DMA通道和缓冲区,我们可以实现串口数据的自动发送和接收,而无需频繁地进行CPU的中断处理。 总而言之,STM32串口自定义协议解析需要在发送端进行数据封装,在接收端进行数据解析,通过中断或DMA机制实现数据的异步传输。这种方式可以使数据的传输更加稳定和高效。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值