当前位置: 首页 > news >正文

07:串口通信(二):收发数据包

1、数据包

  我们使用上位机个单片机发送数据包时,规定包头和包尾,将我们需要发送的数据放在中间,数据的长度我们也可以自己规定。一般情况下HEX数据包我们使用固定长度数据包。而文本数据包使用是可变长度数据包。在这里插入图片描述在这里插入图片描述

2、HEX数据包

2.1、HEX固定数据包收发

在这里插入图片描述①UART.c文件的代码如下

/**
 * 串口初始化,且开启中断
 * 参数:波特率
 */
void UART_It_Init(unsigned int Baud)
{
	/* 串口寄存器的配置 */
	 PCON &= 0x3F;	//PCON = 00xx xxxx
	 SCON &= 0x0F;
	 SCON |= 0x50;	//SCON = 0101 xxxx
	
	/* 配置T1的寄存器 */
	TMOD &= 0x0F;
	TMOD |= 0x20;	//TOMD = 0010 xxxx
	TH1 = 256 - (28800/Baud);//配置波特率
	TL1 = 256 - (28800/Baud);//配置波特率
	ET1 = 0;		//关闭T1溢出中断
	TR1 = 1;		//使能T1
	
	/* 使能串口中断 */
	ES = 1;			//使能串口中断
	EA = 1;			//使能中断总开关
}

/***********中断服务函数*************/
/**
 * 中断服务函数
 */
unsigned char Buffer[BUFF_Len];	//固定数据包缓存区
unsigned char Index = 0;		//缓存区索引
unsigned char Flag2 = 0;
void UART_Routine(void) interrupt 4
{	
	static unsigned char Status = 0;//状态机变量
	unsigned char ReceiveData;
	/* 若接收数据完成中断 RI = 1*/
	if(RI)
	{
		RI = 0;
		ReceiveData = SBUF;
		
		/* 使用状态机对数据包进行处理 */
		switch(Status)
		{
			case 0:
				if(ReceiveData == 0xFF)//是帧头0xFF
				{
					Status = 1;//改变状态变量
					Index = 0;
				}
				else
				{
					Status = 0;//不是帧头
				}
				break;
			case 1:
				Buffer[Index++] = ReceiveData;//接收数据
				if(Index >= BUFF_Len)
				{
					Status = 2;//改变状态变量
				}
				break;
			case 2:
				if(ReceiveData == 0xFE)//判断是否位帧尾0xFE
				{
					Flag2 = 1;//将标志位置1
				}
				else
				{
					memset(Buffer,0,4);//清空数据包缓存区
				}
				Status = 0;
				break;	
		}
	}
}

②UART.h文件的代码如下

#ifndef __UART_H
#define __UART_H

#include <reg51.h>		//包含51头文件,里面全是寄存器地址
#include <stdio.h>
#include <string.h>

#define BUFF_Len 4

void UART_Init(unsigned int Baud);//串口初始化
void Send_Char(unsigned char ch);//发送一个字节的数据
void Send_Array(unsigned char* Array,unsigned char Len);//发送多个字节的数据
void Send_String(unsigned char* str);//发送字符串
char putchar(char ch);//printf()重定向

void UART_It_Init(unsigned int Baud);//串口初始化(使能中断)

extern unsigned char Buffer[BUFF_Len];
extern unsigned char Flag2;

#endif

③main.c文件的代码如下

#include "UART.h"

void main(void)
{
	UART_It_Init(9600);		//9600波特率

	while(1)
	{
		if(Flag2)			//Flag = 1,数据处理完成
		{
			Flag2 = 0;
			Send_Array(Buffer,BUFF_Len);//将数据发送出去
		}
	}
}

在这里插入图片描述

2.2、HEX可变数据包收发

①UART.c文件的代码如下

/**
 * 串口初始化,且开启中断
 * 参数:波特率
 */
void UART_It_Init(unsigned int Baud)
{
	/* 串口寄存器的配置 */
	 PCON &= 0x3F;	//PCON = 00xx xxxx
	 SCON &= 0x0F;
	 SCON |= 0x50;	//SCON = 0101 xxxx
	
	/* 配置T1的寄存器 */
	TMOD &= 0x0F;
	TMOD |= 0x20;	//TOMD = 0010 xxxx
	TH1 = 256 - (28800/Baud);//配置波特率
	TL1 = 256 - (28800/Baud);//配置波特率
	ET1 = 0;		//关闭T1溢出中断
	TR1 = 1;		//使能T1
	
	/* 使能串口中断 */
	ES = 1;			//使能串口中断
	EA = 1;			//使能中断总开关
}

/***********中断服务函数*************/
/**
 * 中断服务函数
 */
unsigned char Buffer[BUFF_Len];	//固定数据包缓存区
unsigned char Index = 0;		//缓存区索引
unsigned char Flag2 = 0;
void UART_Routine(void) interrupt 4
{	
	static unsigned char Status = 0;//状态机变量
	unsigned char ReceiveData;
	/* 若接收数据完成中断 RI = 1*/
	if(RI)
	{
		RI = 0;
		ReceiveData = SBUF;
		
		/* 使用状态机对数据包进行处理 */
		switch(Status)
		{
			case 0:
				if(ReceiveData == 0xFF)//是帧头0xFF
				{
					Status = 1;	//改变状态变量
					Index = 0;
				}
				else
				{
					Status = 0;	//不是帧头
				}
				break;
			case 1:
				if(ReceiveData == 0xFE)//判断是否为帧尾
				{
					Status = 0;	//改变状态变量
					Flag2 = 1;	//将标志位置1
				}
				else//不是帧尾
				{
					Buffer[Index++] = ReceiveData;//对数据进行处理
				}
				break;
		}
	}
}

②UART.h文件的代码如下

#ifndef __UART_H
#define __UART_H

#include <reg51.h>		//包含51头文件,里面全是寄存器地址
#include <stdio.h>
#include <string.h>

#define BUFF_Len 10

void UART_Init(unsigned int Baud);//串口初始化
void Send_Char(unsigned char ch);//发送一个字节的数据
void Send_Array(unsigned char* Array,unsigned char Len);//发送多个字节的数据
void Send_String(unsigned char* str);//发送字符串
char putchar(char ch);//printf()重定向

void UART_It_Init(unsigned int Baud);//串口初始化(使能中断)

extern unsigned char Buffer[BUFF_Len];
extern unsigned char Flag2;
extern unsigned char Index;

#endif

③main.c文件的代码如下

#include "UART.h"


void main(void)
{
	UART_It_Init(9600);		//9600波特率

	while(1)
	{
		if(Flag2)			//Flag = 1,数据处理完成
		{
			Flag2 = 0;
			Send_Array(Buffer,Index);//将数据发送出去
		}
	}
}

在这里插入图片描述

3、文本数据包

3.1、文本定长数据包收发

①UART.c文件的代码如下

/**
 * 串口初始化,且开启中断
 * 参数:波特率
 */
void UART_It_Init(unsigned int Baud)
{
	/* 串口寄存器的配置 */
	 PCON &= 0x3F;	//PCON = 00xx xxxx
	 SCON &= 0x0F;
	 SCON |= 0x50;	//SCON = 0101 xxxx
	
	/* 配置T1的寄存器 */
	TMOD &= 0x0F;
	TMOD |= 0x20;	//TOMD = 0010 xxxx
	TH1 = 256 - (28800/Baud);//配置波特率
	TL1 = 256 - (28800/Baud);//配置波特率
	ET1 = 0;		//关闭T1溢出中断
	TR1 = 1;		//使能T1
	
	/* 使能串口中断 */
	ES = 1;			//使能串口中断
	EA = 1;			//使能中断总开关
}

/***********中断服务函数*************/
/**
 * 中断服务函数
 */
unsigned char Buffer[BUFF_Len];	//固定数据包缓存区
unsigned char Index = 0;		//缓存区索引
unsigned char Flag2 = 0;
void UART_Routine(void) interrupt 4
{	
	static unsigned char Status = 0;//状态机变量
	unsigned char ReceiveData;
	/* 若接收数据完成中断 RI = 1*/
	if(RI)
	{
		RI = 0;
		ReceiveData = SBUF;
		
		/* 使用状态机对数据包进行处理 */
		switch(Status)
		{
			case 0:
				if(ReceiveData == '@')//是帧头'@'
				{
					Status = 1;	//改变状态变量
					Index = 0;
				}
				else
				{
					Status = 0;	//不是帧头
				}
				break;
			case 1:
				Buffer[Index++] = ReceiveData;//对数据进行处理
				if(Index >= 4)
				{
					Status = 2;
				}
				break;
			case 2:
				//判断是否为'\r'
				if(ReceiveData == '\r')
				{
					Status = 3;
				}
				else
				{
					Status = 0;
					memset(Buffer,0,4);//将缓冲区的数据清空
				}
				break;
			case 3:
				//判断是否为'\n'
				if(ReceiveData == '\n')
				{
					Flag2 = 1;
				}
				else
				{
					memset(Buffer,0,4);//将缓冲区的数据清空
				}
				Status = 0;
			break;
		}
	}
}

②UART.h文件的代码如下

#ifndef __UART_H
#define __UART_H

#include <reg51.h>		//包含51头文件,里面全是寄存器地址
#include <stdio.h>
#include <string.h>

#define BUFF_Len 4

void UART_Init(unsigned int Baud);//串口初始化
void Send_Char(unsigned char ch);//发送一个字节的数据
void Send_Array(unsigned char* Array,unsigned char Len);//发送多个字节的数据
void Send_String(unsigned char* str);//发送字符串
char putchar(char ch);//printf()重定向

void UART_It_Init(unsigned int Baud);//串口初始化(使能中断)

extern unsigned char Buffer[BUFF_Len];
extern unsigned char Flag2;
extern unsigned char Index;

#endif

③main.c文件的代码如下

#include "UART.h"

void main(void)
{
	UART_It_Init(9600);		//9600波特率

	while(1)
	{
		if(Flag2)			//Flag = 1,数据处理完成
		{
			Flag2 = 0;
			Send_Array(Buffer,Index);//将数据发送出去
		}
	}
}

在这里插入图片描述

3.2、文本变长数据包收发

在这里插入图片描述
①UART.c文件的代码如下

/**
 * 串口初始化,且开启中断
 * 参数:波特率
 */
void UART_It_Init(unsigned int Baud)
{
	/* 串口寄存器的配置 */
	 PCON &= 0x3F;	//PCON = 00xx xxxx
	 SCON &= 0x0F;
	 SCON |= 0x50;	//SCON = 0101 xxxx
	
	/* 配置T1的寄存器 */
	TMOD &= 0x0F;
	TMOD |= 0x20;	//TOMD = 0010 xxxx
	TH1 = 256 - (28800/Baud);//配置波特率
	TL1 = 256 - (28800/Baud);//配置波特率
	ET1 = 0;		//关闭T1溢出中断
	TR1 = 1;		//使能T1
	
	/* 使能串口中断 */
	ES = 1;			//使能串口中断
	EA = 1;			//使能中断总开关
}

/***********中断服务函数*************/
/**
 * 中断服务函数
 */
unsigned char Buffer[BUFF_Len];	//固定数据包缓存区
unsigned char Index = 0;		//缓存区索引
unsigned char Flag2 = 0;
void UART_Routine(void) interrupt 4
{	
	static unsigned char Status = 0;//状态机变量
	unsigned char ReceiveData;
	/* 若接收数据完成中断 RI = 1*/
	if(RI)
	{
		RI = 0;
		ReceiveData = SBUF;
		
		/* 使用状态机对数据包进行处理 */
		switch(Status)
		{
			case 0:
				if(ReceiveData == '@')//是帧头'@'
				{
					Status = 1;	//改变状态变量
					Index = 0;
				}
				else
				{
					Status = 0;	//不是帧头
				}
				break;
            case 1:   
                if(ReceiveData == '\r')//接收的数据为\r,那么判断第二个数据是否为\n
                {
                    Status = 2;//状态置2
                }
                else
                {
                    Buffer[Index++] = ReceiveData;//对数据进行处理 
                }
				break;   
            case 2:
                if(ReceiveData == '\n')//若再次接收的数据是\n
                {
                    Flag2 = 1;//则代表接收结束
                    Buffer[Index++] = '\0';//给接收到的数据添加结束符
                    Status = 0;		//准备第二轮的接收
                }
                else//若数据不是\n
                {
                    Buffer[Index++] = '\r';//将上一次的\r存储在缓冲区中
                    Buffer[Index++] = ReceiveData;//将这一次接收到的数据存储在缓冲区中
                    Status = 1;//状态置1
                }
                break;
		}
	}
}

②UART.h文件的代码如下

#ifndef __UART_H
#define __UART_H

#include <reg51.h>		//包含51头文件,里面全是寄存器地址
#include <stdio.h>
#include <string.h>

#define BUFF_Len 4

void UART_Init(unsigned int Baud);//串口初始化
void Send_Char(unsigned char ch);//发送一个字节的数据
void Send_Array(unsigned char* Array,unsigned char Len);//发送多个字节的数据
void Send_String(unsigned char* str);//发送字符串
char putchar(char ch);//printf()重定向

void UART_It_Init(unsigned int Baud);//串口初始化(使能中断)

extern unsigned char Buffer[BUFF_Len];
extern unsigned char Flag2;
extern unsigned char Index;

#endif

③main.c文件的代码如下

#include "UART.h"

void main(void)
{
	UART_It_Init(9600);		//9600波特率

	while(1)
	{
		if(Flag2)			//Flag = 1,数据处理完成
		{
			Flag2 = 0;
			Send_String(Buffer);//将数据发送出去
		}
	}
}

在这里插入图片描述

4、定时器中断超时数据包收发

在这里插入图片描述
通过定时器中断计数延时来判断数据包是否完整。通过判断接收到数据包后面的超时时间,来辨别数据包是否接受完整。例如:规定数据包的间隔为10ms。上位机发送4个字节的数据包。单片机接受到4个字节后,通过定时器中断计数,若在10ms后没有在接受到数据,那么这个数据包就是4个字节的数据。时间的间隔 > 1/波特率 * 数据帧位个数
①UART.c文件的代码如下

/**
 * 串口初始化,且开启中断
 * 参数:波特率
 */
void UART_It_Init(unsigned int Baud)
{
	/* 串口寄存器的配置 */
	 PCON &= 0x3F;	//PCON = 00xx xxxx
	 SCON &= 0x0F;
	 SCON |= 0x50;	//SCON = 0101 xxxx
	
	/* 配置T1的寄存器 */
	TMOD &= 0x0F;
	TMOD |= 0x20;	//TOMD = 0010 xxxx
	TH1 = 256 - (28800/Baud);//配置波特率
	TL1 = 256 - (28800/Baud);//配置波特率
	ET1 = 0;		//关闭T1溢出中断
	TR1 = 1;		//使能T1
	
	/* 使能串口中断 */
	ES = 1;			//使能串口中断
	EA = 1;			//使能中断总开关
}

/***********中断服务函数*************/
/**
 * 中断服务函数
 */
unsigned char Buffer[BUFF_Len];	//固定数据包缓存区
unsigned char Index = 0;		//缓存区索引
unsigned char Flag2 = 0;
unsigned char start_time = 0;
unsigned char time_cnt = 0;
void UART_Routine(void) interrupt 4
{	
	unsigned char ReceiveData;
	
	/* 若接收数据完成中断 RI = 1*/
	if(RI)
	{
		start_time = 1;//打开软件定时器开始计数
		RI = 0;
		Buffer[Index++] = SBUF;//对数据进行处理
		if(Index >= BUFF_Len)//数据缓存区满了
		{
			Index = BUFF_Len;
		}
		time_cnt = 0;//接收一位数据,让它置0,不让定时器中断让它变大
	}
}

②UART.h文件的代码如下

#ifndef __UART_H
#define __UART_H

#include <reg51.h>		//包含51头文件,里面全是寄存器地址
#include <stdio.h>
#include <string.h>

#define BUFF_Len 4

void UART_Init(unsigned int Baud);//串口初始化
void Send_Char(unsigned char ch);//发送一个字节的数据
void Send_Array(unsigned char* Array,unsigned char Len);//发送多个字节的数据
void Send_String(unsigned char* str);//发送字符串
char putchar(char ch);//printf()重定向

void UART_It_Init(unsigned int Baud);//串口初始化(使能中断)

extern unsigned char Buffer[BUFF_Len];
extern unsigned char Flag2;
extern unsigned char Index;

#endif

③time.c文件的代码如下

#include "Time.h"

/**
 * 定时器T0的初始化,且开启中断
 */
void Time0It_Init(void)
{
	/* 设置T0的计数器为16,且时钟来源为晶振 TMOD = xxxx 0001 */
	TMOD &= 0xF0;			//将低4位置0
	TMOD |= 0x01;			//最低位置1
	
	/* 设置定时计数器的初始值,定时1ms */
	TH0 = ((65536 - 1000) >> 8);
	TL0 = (65536 - 1000) & 0x00FF;

	/* 启动T0 */
	TF0 = 0;				//清除标志位
	TR0 = 1;
	
	/* 开启中断 */
	ET0 = 1;				//使能定时器溢出中断
	EA = 1;					//开启总开关
}

/********中断服务函数*******/
/**
 * T0的中断服务函数
 */
extern unsigned char Flag2;
extern unsigned char start_time; 
extern unsigned char time_cnt;
void Time0_Rountine(void) interrupt 1
{	
	//这里不用清除标志位,硬件会自动清除
	TH0 = ((65536 - 1000) >> 8);
	TL0 = (65536 - 1000) & 0x00FF;
	
	if(start_time == 1)//表示串口开始接收数据
	{
		time_cnt++;
		if(time_cnt >= 10)//表示串口没有在接收数据了
		{
			start_time = 0;//关闭软件定时器
			time_cnt = 0;
			Flag2 = 1;
		}
	}
}

④main.c文件的代码如下

#include "UART.h"
#include "Time.h"

void main(void)
{
	UART_It_Init(9600);		//9600波特率
	Time0It_Init();
	while(1)
	{
		if(Flag2)			//Flag = 1,数据处理完成
		{
			Flag2 = 0;
			Send_Array(Buffer,Index);//将数据发送出去
			Index = 0;
			memset(Buffer,0,Index);//清除缓存区
		}
	}
}

在这里插入图片描述

相关文章:

  • Docker Desktop之Nginx
  • HTTP的状态码
  • 海思3559a_使用2.0.4.0版SDK编译固件下载后i2c_write和i2c_read不支持怎么办
  • OpenCV机器学习(4)k-近邻算法(k-Nearest Neighbors, KNN)cv::ml::KNearest类
  • 蓝桥杯(B组)-每日一题(求最大公约数最小公倍数)
  • MongoDB 7 分片副本集升级方案详解(下)
  • 用Echarts的柱状图实现圆柱体效果
  • linux c 读写锁pthread_rwlock
  • 3D打印技术:如何让古老文物重获新生?
  • MYSQL直接在SQL提取json字符串中的内容-----将13位时间戳转换成标准的日期格式【记录SQL常用函数】
  • 【如何掌握CSP-J 信奥赛中的模拟算法】
  • Python深度学习代做目标检测NLP计算机视觉强化学习
  • 【Linux系统】—— 简易进度条的实现
  • C++中运算符的底层是不是用函数实现的?
  • MATLAB使用技巧之局部放大图的制作(二)
  • 算法服务器跟算力卡有什么区别?
  • Eclipse JSP/Servlet 深入解析
  • 【C语言 】C语言 桌游开发数字竞拍(源码)【独一无二】
  • 微信小程序开发实用技巧篇
  • 支持列表拖拽嵌套,AI流式输出的多模态文档编辑器flowmix/docx: 全面升级
  • 6月底将返回中国,旅日大熊猫获颁“感谢状”
  • 多名幼师殴打女童被行拘后续,盘锦市教育局工作人员:该局将专项整治全市幼儿园
  • 集齐中国泳坛“老中青”!200自潘展乐力压汪顺、孙杨夺冠
  • 俄乌直接谈判勉强收场,特朗普再次“电话外交”能否有用?|907编辑部
  • 黄仁勋:新一代计算平台GB300三季度上市,AI计算能力每十年提升100万倍
  • 视觉周刊|走进变革中的博物馆