stm32 串口多字节接收

这篇具有很好参考价值的文章主要介绍了stm32 串口多字节接收。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

如果不想看的可以直接使用git把我的代码下载出来,里面工程挺全的,后期会慢慢的补注释之类的

码云地址:stm32学习笔记: stm32学习笔记源码

如果不会使用git快速下载可以选择直接下载压缩包或者去看看git的使用

git的使用(下载及上传_gitcode怎么下载文件_是小刘不是刘的博客-CSDN博客

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

因为现在很多设备都要的是多字节发送,和接收,所以我们需要学习如何去接收一些设备返回的多字节参数然后去对他解析。

1、通过串口收发HEX格式数据包

2、通过串口收发字符格式数据包并且点灯

3、既能接收HEX又能接收字符格式数据包

目录

一 、数据包理论部分

1、hex数据包

 2 文本数据包

 3 hex的数据接收

 4、接收文本数据包

 二、代码部分

1、数据包的发送

2 数据包的接收

1 中断方式接收HEX数据包

 2 文本数据包

3 既实现接收字符串又能实现接收HEX


一 、数据包理论部分

1、hex数据包

数据包一般会包含包头和包尾,但是在一些设备的协议中,只有包头,包的结束由一个时间段内没有收到数据为结束信号。

在传输中怎么防止数据和包头包尾重复呢,这里两个方法就是:一个是让其固定包长,一个是加长帧头,让数据和包头包尾的重复率降低。

stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件

 2 文本数据包

这里也加入包长包尾,但是文本解析率比较低,因为一个字符他就是八位。

stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件

 3 hex的数据接收

这里可以写一个状态机,用这个状态机来判断是否接收到了帧头桢尾,以及是否接收数据完成。

首先判断接收到的数据是否位0xFF,如果不是就让s一直为0继续等待数据

若收到FF后将其置为1,后面检测其是否收够四个数据收够为2(这里是固定包长的想法

之后等待包尾,若收到包尾,就将s为0,重新接收包头,否则循环接收包尾

stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件

 4、接收文本数据包

这里就是可变包长的想法了

首先还是当s=0时,等待包头,若包头一直不为@则一直等待,为@后将s置为1,接收数据,这时候就等待包尾如果收到了\r就将s置为2,到下一个接收包尾的环节(因为这里是有两个包尾的,然后程序一次又只能判断一个字节,如果只有一个包尾,那就接收到包尾之后直接将s=0,就不需要最后的一步等待包尾了。

stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件

 二、代码部分

1、数据包的发送

上面没写数据包的发送,因为发送没什么限制,就是把接收到的数据,或者你自己写入的数据直接发送就好,不需要那么多判断帧头尾的判断。

这里也就不接收传感器数据再转发了,这里我们先直接写个发送,后面写完接收再写接收之后返回。往TXBUF里面写入四个16进制,要带上0x前缀哦不然代码会被默认为10进制,10进制是没有abcdef这些字母的,会直接报错。

//usart.c
u8 serial_TxPack[4]={0};

void Send_Pack(void)
{
	 Send_Byte(0xFF);
	 Send_Array(serial_RxPack,4);
	 Send_Byte(0xFE);
}

//main.c
int main(void)
{
		
	Usart_Config();
	
	serial_RxPack[0]=0xf1;
	serial_RxPack[1]=0x02;
	serial_RxPack[2]=0x03;
	serial_RxPack[3]=0x04;
	
	Send_Pack();
  while(1)
	{	

	}	
}

stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件

这样我们直接发送就能从串口测试有没有发送成功了

2 数据包的接收

1、中断方式接收HEX数据包

逻辑就是首先判断接收到的数据是不是包头,然后接收数据之后,判断数据个数,之后判断包尾,和上面的流程图是一样的。

首先写一个串口中断,这个在前面的单字节接收里面已经写过了,stm32f103系列USART串口收发(单字节_八月风贼冷的博客-CSDN博客

如果还不会配置串口中断的可以去看一下,然后就是写一个状态机了,这注释写的还是挺详细的,按照上面写的那个状态转移表的逻辑来写的代码,则例用了两个静态变量

1、基本概念
静态存储方式:指在程序运行时,给变量分配固定的存储空间的方式
2、 静态存储区存放以下变量:
全局变量:在程序开始执行时给全局变量分配存储区,程序运行完毕之后释放。在程序运行过程中它们占据固定的存储单元而不动态进行分配和释放。
静态变量:有时希望变量的值在函数调用结束后不消失而保留原值,这时就应该指定变量为“静态变量”,用关键字static进行命名

有时候,我们希望函数中局部变量的值在函数调用结束之后不会消失,而仍然保留其原值。即它所占用的存储单元不释放,在下一次调用该函数时,其局部变量的值仍然存在,也就是上一次函数调用结束时的值。这时候,我们就应该将该局部变量用关键字 static 声明为“静态局部变量“。

因为我们这里需要每一次都进串口并且让其他文件不能调用,所以我们这里可以使用静态变量。

uint8_t RxData; //数据转存
u8 serial_RxPack[4]={0}; //接收数据的数组
u8 Serial_RXFlag=0; //接收完成标志位

void USART1_IRQHandler(void)
{
			static u8 RxState=0;
      static u8 pRxPacket=0;
		  if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET)
			{
				 //接收串口发来的数据
				 RxData=USART_ReceiveData(USART1);
			   
				 //判断数据是否为包头
				 if(RxState==0)
				 {
					  if(RxData==0xFF)
						{
							 RxState=1;
						}
						else
						{
							  
						}
				 }
				 else if(RxState==1)
				 {
					  //接收到了包头,可以开始接收数据之后进行组包
					  serial_RxPack[pRxPacket]=RxData;
					  pRxPacket++;
					  //接收到数据为4个将状态转移为2
					  if(pRxPacket==4)
						{
							 RxState=2;
							 pRxPacket=0;
						}
				 }
				 else if(RxState==2)
				 {
					  //判断是否接收到包尾
					  if(RxData==0xFE)
						{
							 RxState=0;
							 Serial_RXFlag=1;
						}
						else
						{
							 
						}
				 }
			}
}

 之后我们写一个判断是否接收完成的标志位,通过这个标志位我们可以调用这个标志位来判断是否完成,这个标志位的定义在上面那段代码。

u8 Serial_GetRxFlag(void)
{
	 if(Serial_RXFlag == 1)
	 {
		 Serial_RXFlag=0;
		 return 1;
	 }
	 return 0;
}

之后就能写主函数了,判断接收完成标志位是否为1,因为前面状态机写的如果接收到了结束帧就会将其写为1。然后为1我们就将接收到的数据发送出去。并且将标志位重新写为0。

int main(void)
{
		
	Usart_Config();
	
  while(1)
	{	
		   //判断是否接收完成
       if(Serial_RXFlag==1)
			 {    
				   //将接收完成标志位置0
				   Serial_GetRxFlag();
				   //将接收的数据发送给串口显示出来
				   Send_Array(serial_RxPack,4);
			 }
	}	
}

运行效果,前面几次发送了11 22 33 44 后面为了测试数据不会和包头冲突发送了ff 22 33 44

stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件

 2 文本数据包

文本数据包和hex的写法基本差不多但是也有些要注意的点

首先我们还是写一个串口中断的状态机

这里我们就还是以上面的状态转移图来写代码了,首先起始帧为@我们判断是否为@,然后给数组赋值接着判断包尾,这里要注意要给字符串一个’0‘因为c语言中字符串要有一个\0的结束位,不然后面我们使用这个字符串的时候就无法判断是否结束,中断函数如下。

uint8_t RxData;

void USART1_IRQHandler(void)
{
			static u8 RxState=0;
      static u8 pRxPacket=0;
		  if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET)
			{
				 //接收串口发来的数据
				 RxData=USART_ReceiveData(USART1);
			   
				 //判断数据是否为包头
				 if(RxState==0)
				 {
					  if(RxData=='@')
						{
							 RxState=1;
						}
						else{  
						}
				 }
				 else if(RxState==1)
				 {

					   //判断数据是不是包尾,不是的话就接收数据
					   if(RxData=='\r')
						 {
							   RxState=2;
						 }
						 else
						 {
							  serial_RxPack[pRxPacket]=RxData;
							  pRxPacket++;
						 }

				 }
				 else if(RxState==2)
				 {
						 //判断数据是不是'\n'
					   if(RxData=='\n')
						 {  
							  //状态转移为0并且将
							  RxState=0;
							  serial_RxPack[pRxPacket]='\0'; //给字符串一个结束符
							  Serial_RXFlag=1;
							  pRxPacket=0;
						 }
						 else{
						 }
				 }
			}
}

之后我们在主函数调用,这样就能打印刚刚接收到的字符串了

  while(1)
	{	
		   //判断是否接收完成
       if(Serial_RXFlag==1)
			 {    
				   //将接收完成标志位置0
				   Serial_GetRxFlag();
				   //将接收的数据发送给串口显示出来
				   Send_String(serial_RxPack);
			 }
	}	

打印结果如下,这里发了三次所以打印的多了几个

stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件

 成功发送,之后我们测试发送命令控制板子上的LED灯的开关

这里我们先打开自己板子灯的GPIO这些操作,这个我说在PWM那一节的制作呼吸灯写过了可以去参考一下stm32f103配置PWM及实践_stm32pwm配置详解_是小刘不是刘的博客-CSDN博客

然后就是灯,这个可以自己直接配置和,调用set和rest两个点灯,也可以直接去调用有些公司写好的库,这里我们就自己写一下把

这里我还是使用了PB0的蓝色灯,可以根据自己的板子选择。

首先初始化我们的GPIOB和引脚0,记得先自己去创建一个led的板级支持包哦别都写在串口的支持包里面去了 

stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件

//led.c
void LED_Config(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
 
	//配置GPIO
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; 
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
}

开启GPIO之后我做一个led的封装这样等会好调用,这样我们就能在主函数很好的实现开灯了哈

//led.h
#define ON  0
#define OFF 1

#define LEDG(a)	if (a)	\
					GPIO_SetBits(GPIOB,GPIO_Pin_1);\
					else		\
					GPIO_ResetBits(GPIOB,GPIO_Pin_1)

然后写完在主函数测试一下能不能点亮,别等下写串口点灯写了半天发现灯本来就点不亮= =。

之后我们开始写接收字符串点灯,这里我们使用一个字符串比较函数strcmp,这个是c语言函数,不了解的可以百度一下。

我们还是在串口接收字符的基础上写,这里就写一些判断就好,很简单的逻辑,使用这个函数记得调用string的库,这个之前写串口单字节的时候也用过这个库来计算平方。

int main(void)
{
		
	Usart_Config();
	LED_Config();
	printf("串口打印测试");
  while(1)
	{	
		   //判断是否接收完成
       if(Serial_RXFlag==1)
			 {    
				   //将接收完成标志位置0
				   Serial_GetRxFlag();
                  //实现字符串比较
				  if(strcmp(serial_RxPack,"LED_ON")==0)
					{  
						LEDG(ON);
						printf("LED_ON\r\n");
					}
					else if(strcmp(serial_RxPack,"LED_OFF")==0)
					{
						 LEDG(OFF);
						 printf("LED_OFF\r\n");
					}
					else
					{
						 printf("command erro\r\n");
					}
			 }
	}	
}

stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件

然后附上两张硬件效果图

开灯

stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件

关灯

 stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件

具体实现就这样了

3 既实现接收字符串又能实现接收HEX

思路还是写一个状态机,但是状态会多一点

首先在状态0时同时判断是@或者FF,若接收到得为@则进入状态1 这里接收字符,若字符为\r则进入状态3等待\n若为\n则返回状态0表示字符串接收完成

若接收到得为FF则跳到状态2接收HEX,但是现在是不定长16进制,所以数据为和包尾标志一点不可以重复,这时接收到FE则代表数据接收结束,回到状态0.

stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件

代码部分,两个数组分别存储字符串和HEX数据包

//main.c
int main(void)
{
	Usart_Config();
	LED_Config();
	printf("串口打印测试");
  while(1)
	{	
		   //判断是否接收完成
       if(Serial_RXFlag==1)
			 {    
				   //将接收完成标志位置0
				    Serial_GetRxFlag();
            Send_Array(modubus_RxPack,3);
				    Send_String(serial_RxPack);
			 }
	}	
}
//usart.c
char serial_RxPack[64]={0};
u8 modubus_RxPack[64]={0};
u8 Serial_RXFlag=0;
uint8_t RxData;
void USART1_IRQHandler(void)
{
			static u8 RxState=0;
      static u8 pRxPacket=0;
		  if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET)
			{
				 //接收串口发来的数据
				 RxData=USART_ReceiveData(USART1);
			   
				 //判断数据是否为包头
				 if(RxState==0)
				 {
					  if(RxData=='@')
						{
							 RxState=1; //状态1接收字符
						}
						else if(RxData==0xFF)
						{
							 RxState=2; //状态2接收到HEX
						}
				 }
				 else if(RxState==1)
				 {

					   //判断数据是不是包尾,不是的话就接收数据
					   if(RxData=='\r')
						 {
							   RxState=3;
						 }
						 else
						 {
							  serial_RxPack[pRxPacket]=RxData;
							  pRxPacket++;
						 }

				 }
				 else if(RxState==2)
				 {
					   if(RxData==0xFE)
						 {
							   Serial_RXFlag=1;
							   RxState=0; //重新接收包
							   pRxPacket=0;
						 }
						 else
						 { 
							  modubus_RxPack[pRxPacket]=RxData; 
							  pRxPacket++;
						 }
				 }
				 else if(RxState==3)
				 {
						 //判断数据是不是'\n'
					   if(RxData=='\n')
						 {  
							  //状态转移为0并且将
							  RxState=0;
							  serial_RxPack[pRxPacket]='\0'; //给字符串一个结束符
							  Serial_RXFlag=1;
							  pRxPacket=0;
						 }
						 else{
						 }
				 }
			}
}

 主函数还是只显示了3位数组,但是没关系的- -主要是为了回显,到时候处理数据的时候单片机直接接收到了之后处理就是了开不开回显都没关系,切换两个发送接收模式,测试是没有问题的。

stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件stm32串口接收多个字节,stm32学习笔记,stm32,单片机,嵌入式硬件文章来源地址https://www.toymoban.com/news/detail-741628.html

到了这里,关于stm32 串口多字节接收的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32单片机(九)USART串口----第四节:USART串口实战练习(串口发送+接收)

    ❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋

    2024年02月10日
    浏览(52)
  • 【STM32】HAL库的STOP低功耗模式UART串口唤醒,第一个接收字节出错的问题(已解决)

    【STM32】HAL库的STOP低功耗模式UART串口唤醒,第一个接收字节出错的问题(已解决) 最近做项目时 用到了STOP1停止模式的串口唤醒 唤醒配置如下: 【STM32】HAL库低功耗STOP停止模式的串口唤醒(解决进入以后立马唤醒、串口唤醒和回调无法一起使用、接收数据不全的问题) 我

    2024年01月15日
    浏览(49)
  • stm32串口自定义协议接收一串十六进制数据(将其中两个字节转化为十进制数据)+部分串口基础知识

    位(bit): 二进制数中的一个数位,可以是0或者1,是计算机中数据的最小单位。 字节(Byte): 计算机中数据的基本单位,每8位组成一个字节。各种信息在计算机中存储、处理至少需要一个字节。 例如,一个ASCII码用一个字节表示,一个汉字用两个字节表示。 字(Word):

    2023年04月08日
    浏览(59)
  • 【STM32笔记】HAL库低功耗STOP停止模式的串口唤醒(解决进入以后立马唤醒、串口唤醒和回调无法一起使用、接收数据不全的问题)

    【STM32】HAL库低功耗STOP停止模式的串口唤醒(解决进入以后立马唤醒、串口唤醒和回调无法一起使用、接收数据不全、首字节错误的问题) 【STM32笔记】低功耗模式配置及避坑汇总 前文: blog.csdn.net/weixin_53403301/article/details/128216064 【STM32笔记】HAL库低功耗模式配置(ADC唤醒无

    2024年02月14日
    浏览(37)
  • esp32单片机在arduino环境下,串口接收解码

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一、pandas是什么? 二、使用步骤 1.引入库 2.读入数据 总结 当主控芯片发送一串数据,但此数据为一串字符串,需要将此字符串翻译成整型数组,以形成指令,控制串口屏。 例如:esp32单

    2024年02月09日
    浏览(43)
  • 【STM32】学习笔记(串口通信)

    串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信 单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力。 电平标准

    2024年02月10日
    浏览(37)
  • 【STM32】STM32学习笔记-USART串口协议(25)

    按数据传送的方式,通讯可分为串行通讯与并行通讯,串行通讯是指设备之间通过少量数据信号线(一般是8根以下), 地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式。而并行通讯一般是指使用8、16、32及64根或更多的数据线进行传输的通讯方式, 它们的通

    2024年01月19日
    浏览(45)
  • 【STM32】STM32学习笔记-USART串口数据包(28)

    串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,因为它简单便捷,因此大部分电子设备都支持该通讯方式, 电子工程师在调试设备时也经常使用该通讯方式输出调试信息。 在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和

    2024年01月18日
    浏览(40)
  • STM32学习笔记(五)433M无线发射接收模块

    数据发射模块的工作频率为315M,采用声表谐振器SAW稳频,频率稳定度极高,当环境温度在-25~+85度之间变化时,频飘仅为3ppm 单片机供电VCC GND接单片机 接收到信号,接收模块对应针脚输出高电平 有D0 D1 D2 D3,对应遥控器的ABCD 需求:按下遥控器A按键,LED1亮1秒;按下遥控器

    2024年02月14日
    浏览(41)
  • stm32学习笔记-9 USART串口

    注:笔记主要参考B站 江科大自化协 教学视频“STM32入门教程-2023持续更新中”。 注:工程及代码文件放在了本人的Github仓库。 从本节开始,将逐一学习STM32的通信接口。首先介绍以下stm32都集成了什么通信外设。 为了控制或读取外挂模块,stm32需要与外挂模块进行通信,来扩

    2024年02月14日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包