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

08【基础学习】串口通信(三):收发数据包+数据校验

收发数据包+数据校验

  • 1、和校验+异或校验
    • 1.1、HEX固定长度数据包校验
    • 1.2、HEX不固定长度数据包校验
  • 2、CRC校验

1、和校验+异或校验

和校验:将接收到的数据全部相加后,取结果的最后一个字节的数据
异或校验:将接收到的数据全部相异或后,取结果的最后一个字节的数据

在线计算和校验/异或校验工具:链接: link

例如:
对 0x33 0x56 0x14 0x32 这4个数据求和校验数据为:0xCF
对 0x33 0x56 0x14 0x32 这4个数据求异或校验数据为:0x43

1.1、HEX固定长度数据包校验

创建工程名为29_UART_Send_Receive_CheckFixedPage

①UART.c文件的代码如下

#include "UART.h"

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

/**
 * 发送一个字节的数据
 */
void Send_Char(unsigned char ch)
{
	SBUF = ch;	//给SBUF写入一个字节数据
	while(!TI);	//等待数据发送完成(未完成TI = 0,完成TI = 1)
	TI = 0;		//手动清0
}

/**
 * 发送多个字节的数据
 */
void Send_Array(unsigned char* Array,unsigned char Len)
{
	unsigned char i;
	for(i = 0; i<Len; i++)
	{
		Send_Char(*(Array+i));
	}
}

/**
 * 发送字符串
 */
void Send_String(unsigned char* str)
{
	while(*str != '\0')
	{
		Send_Char(*str++);
	}
}

/**
 * 对printf函数进行重定向
 */
char putchar(char ch)
{
	Send_Char(ch);
    return ch;
}


/***********中断服务函数*************/
/**
 * 中断服务函数:接收固定长度为4的HEX数据包,且进行和校验与异或校验
 * HEX数据包格式:0xFF x x x x 和校验字符 异或校验字符 0xFE
 */
unsigned char Buffer[BUFF_Len];		//固定数据包缓存区
unsigned char Index = 0;			//缓存区索引
unsigned char Receive_Flag = 0;		//接收完成标志
void UART_Routine(void) interrupt 4
{	
	static unsigned char Status = 0;	//状态机变量
	static unsigned char Sum_Check = 0;	//和校验变量
	static unsigned char X0r_Check = 0;	//异或校验变量
	unsigned char ReceiveData;			//暂存数据变量
	
	/* 若是发送完成中断:TI = 1*/
	if(TI)
	{
		//一般情况下不使用你中断进行发送数据
		TI = 0;
	}
	
	/* 若接收数据完成中断 RI = 1*/
	if(RI && !Receive_Flag)				//Receive_Flag = 0表示上一次接收的数据处理完成
	{
		RI = 0;
		ReceiveData = SBUF;
		switch(Status)
		{
			case 0:	//判断帧头0xFF
				if(ReceiveData == 0xFF)
				{
					Status = 1;//改变状态变量
					Index = 0;
				}
				else
				{
					Status = 0;
				}
				break;
			case 1:		//接收数据	
				Sum_Check += ReceiveData;	//每接收到一个字节的数据就计算出和校验字符
				X0r_Check ^= ReceiveData;	//每接收到一个字节的数据就计算出异或校验字符
				Buffer[Index++] = ReceiveData;
				if(Index >= BUFF_Len)		//接收到4个数据帧
				{
					Status = 2;				//改变状态变量
				}
				break;
			case 2:		//进行和校验比对
				if(Sum_Check == ReceiveData)
				{
					Status = 3;
				}
				else	//进行和校验比对,校验比对失败
				{
					Send_Char(0XE0);//返回发送0xE0
					Status = 0;
					memset(Buffer,0,4);//清空数据包缓存区
				}
				Sum_Check = 0;//和校验变量清零
			break;
			case 3:		//进行异或校验比对
				if(X0r_Check == ReceiveData)//异或校验比对成功
				{
					Status = 4;		
				}
				else
				{
					Send_Char(0XE1);//返回发送0xE1
					Status = 0;
					memset(Buffer,0,4);//清空数据包缓存区
				}
				X0r_Check = 0;//和校验变量清零
			break;
			case 4:	//判断帧尾部
				if(ReceiveData == 0xFE)
				{
					Receive_Flag = 1;	//接收完成	
				}
				else
				{
					memset(Buffer,0,4);//清空数据包缓存区
				}
				Status = 0;				//将状态变量置0,便于下次数据包的接收
			break;
			default:break;
		}
	}
}

②UART.h文件的代码如下

#ifndef __UART_H
#define __UART_H
#include <REGX52.H>	//包含51头文件,里面全是寄存器地址
#include <string.h>
#include <stdio.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()重定向

extern unsigned char Buffer[BUFF_Len];		//固定数据包缓存区
extern unsigned char Receive_Flag;
extern unsigned char Index;


#endif

③main.c文件的代码如下

#include "UART.h"

void main(void)
{
	UART_Init(9600);			//9600波特率
	
	printf("Code Runing\r\n");
	
	while(1)
	{
		if(Receive_Flag)		//Receive_Flag = 1,数据接收完成
		{
			Receive_Flag = 0;
			Send_Array(Buffer,Index);//将数据发送出去
		}
	}
}

在这里插入图片描述

1.2、HEX不固定长度数据包校验

创建工程名为30_UART_Send_Receive_CheckChangePage
①UART.c文件的代码如下

#include "UART.h"

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

/**
 * 发送一个字节的数据
 */
void Send_Char(unsigned char ch)
{
	SBUF = ch;	//给SBUF写入一个字节数据
	while(!TI);	//等待数据发送完成(未完成TI = 0,完成TI = 1)
	TI = 0;		//手动清0
}

/**
 * 发送多个字节的数据
 */
void Send_Array(unsigned char* Array,unsigned char Len)
{
	unsigned char i;
	for(i = 0; i<Len; i++)
	{
		Send_Char(*(Array+i));
	}
}

/**
 * 发送字符串
 */
void Send_String(unsigned char* str)
{
	while(*str != '\0')
	{
		Send_Char(*str++);
	}
}

/**
 * 对printf函数进行重定向
 */
char putchar(char ch)
{
	Send_Char(ch);
    return ch;
}


/***********中断服务函数*************/
/**
 * 中断服务函数:接收不固定长度的HEX数据包,且进行和校验与异或校验
 * HEX数据包格式:0xFF x x x x 和校验字符 异或校验字符 0xFE
 */
unsigned char Buffer[BUFF_Len];		//固定数据包缓存区
unsigned char Index = 0;			//缓存区索引
unsigned char Receive_Flag = 0;		//接收完成标志
void UART_Routine(void) interrupt 4
{	
	static unsigned char Status = 0;	//状态机变量
	static unsigned char Sum_Check = 0;	//和校验变量
	static unsigned char X0r_Check = 0;	//异或校验变量
	unsigned char ReceiveData;			//暂存数据变量
	
	/* 若是发送完成中断:TI = 1*/
	if(TI)
	{
		//一般情况下不使用你中断进行发送数据
		TI = 0;
	}
	
	/* 若接收数据完成中断 RI = 1*/
	if(RI && !Receive_Flag)
	{
		RI = 0;
		ReceiveData = SBUF;
		
		/* 使用状态机对数据包进行处理 */
		switch(Status)	//Receive_Flag = 0表示上一次接收的数据处理完成
		{
			case 0:
				if(ReceiveData == 0xFF)				//是帧头0xFF
				{
					Index = 0;
					Status = 1;						//改变状态变量
				}
				else
				{
					Status = 0;						//不是帧头
				}
				break;
			case 1:
				if(ReceiveData == 0xFE)				//判断是否为帧尾
				{
					Sum_Check -= Buffer[Index-1];
					Sum_Check -= Buffer[Index-2];
					
					X0r_Check ^= Buffer[Index-1];
					X0r_Check ^= Buffer[Index-2];
					
					/* 和校验与异或校验比对 */
					if(Sum_Check == Buffer[Index-2])		//进行和校验比对
					{
						if(X0r_Check == Buffer[Index-1])	//进行异或校验比对
						{
							Index -= 2;
							Receive_Flag = 1;				//将标志位置1
						}
						else
						{
							Send_Char(0xE1);				//返回发送0xE1
							memset(Buffer,0,BUFF_Len);		//清空数据包缓存区
						}
					}
					else
					{
						Send_Char(0xE0);				//返回发送0xE0
						memset(Buffer,0,BUFF_Len);		//清空数据包缓存区
					}
					Sum_Check = 0;						//和校验变量置0
					X0r_Check = 0;						//异或校验变量置0
					Status = 0;							//将状态变量置0,便于下次数据包的接收
				}
				else//不是帧尾,接收数据
				{
					Sum_Check += ReceiveData;		//每接收到一个字节的数据就计算出和校验字符
					X0r_Check ^= ReceiveData;		//每接收到一个字节的数据就计算出异或校验字符
					Buffer[Index++] = ReceiveData;	//对数据进行处理
				}
				break;
			default:break;
		}
	}
}

②UART.h文件的代码如下

#ifndef __UART_H
#define __UART_H
#include <REGX52.H>	//包含51头文件,里面全是寄存器地址
#include <string.h>
#include <stdio.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()重定向

extern unsigned char Buffer[BUFF_Len];		//固定数据包缓存区
extern unsigned char Receive_Flag;
extern unsigned char Index;


#endif

③main.c文件的代码如下

#include "UART.h"

void main(void)
{
	UART_Init(9600);			//9600波特率
	
	printf("Code Runing\r\n");
	
	while(1)
	{
		if(Receive_Flag)		//Receive_Flag = 1,数据接收完成
		{
			Receive_Flag = 0;
			Send_Array(Buffer,Index);//将数据发送出去
		}
	}
}

在这里插入图片描述

2、CRC校验

在线CRC校验工具:链接: link

相关文章:

  • 某公司网络OSPF单区域配置
  • 作业帮前端面试题及参考答案 (100道面试题-上)
  • 交易所开发全流程解析:KYC与U盾在安全合规中的战略价值
  • 基于Ubuntu系统搭建51单片机开发环境的详细教程
  • Auto-Encoder --李宏毅机器学习笔记
  • 视觉算法+雾炮联动:开创智能降尘新时代
  • 基于unsloth微调大模型并上传到huggingface
  • zephyr RTOS 中 bt_le_adv_start函数的功能应用
  • 基础知识:离线安装docker、docker compose
  • Docker 代理配置全攻略:从入门到企业级实践
  • iOS设备文件管理实践
  • 【数据分享】1981-2024年全国各城市逐日、逐月、逐年平均气温(Shp格式)
  • 高性能内存kv数据库Redis
  • 个人博客系统
  • 【转载翻译】Open3D和PCL的一些比较
  • 如何协调跨部门资源争夺
  • 【KWDB 创作者计划】_产品技术解读_1
  • 谈谈 typescript 中 namespace 的理解
  • AQchat
  • Vite配置postcss-px-to-viewport
  • 人民日报头版:各地扎实开展学习教育,一体推进学查改
  • 人民日报和音:书写周边命运共同体建设新篇章
  • 山东临沂市市长张宝亮履新市委书记
  • 一镇一链、一园一策,上海闵行发布重点产业区镇协同产业地图
  • “月光女神”莎拉·布莱曼来上海,再现好莱坞浮华幻梦
  • 腾讯启动史上最大就业计划:三年新增2.8万个校招实习岗位,今年技术岗位占六成