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

ops/2025/2/19 10:33:18/

1、数据包

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

2、HEX数据包

2.1、HEX固定数据包收发

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

/*** 串口初始化,且开启中断* 参数:波特率*/
void UART_It_Init(unsigned int Baud)
{/* 串口寄存器的配置 */PCON &= 0x3F;	//PCON = 00xx xxxxSCON &= 0x0F;SCON |= 0x50;	//SCON = 0101 xxxx/* 配置T1的寄存器 */TMOD &= 0x0F;TMOD |= 0x20;	//TOMD = 0010 xxxxTH1 = 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 4void 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 xxxxSCON &= 0x0F;SCON |= 0x50;	//SCON = 0101 xxxx/* 配置T1的寄存器 */TMOD &= 0x0F;TMOD |= 0x20;	//TOMD = 0010 xxxxTH1 = 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 10void 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 xxxxSCON &= 0x0F;SCON |= 0x50;	//SCON = 0101 xxxx/* 配置T1的寄存器 */TMOD &= 0x0F;TMOD |= 0x20;	//TOMD = 0010 xxxxTH1 = 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 4void 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 xxxxSCON &= 0x0F;SCON |= 0x50;	//SCON = 0101 xxxx/* 配置T1的寄存器 */TMOD &= 0x0F;TMOD |= 0x20;	//TOMD = 0010 xxxxTH1 = 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 4void 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 xxxxSCON &= 0x0F;SCON |= 0x50;	//SCON = 0101 xxxx/* 配置T1的寄存器 */TMOD &= 0x0F;TMOD |= 0x20;	//TOMD = 0010 xxxxTH1 = 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 4void 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位置0TMOD |= 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);//清除缓存区}}
}

在这里插入图片描述


http://www.ppmy.cn/ops/158724.html

相关文章

Python练习11-20

题目&#xff1a;古典问题&#xff1a;有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c;小兔子长到第三个月后每个月又生一对兔子&#xff0c;假如兔子都不死&#xff0c;问每个月的兔子总数为多少&#xff1f; 题目&#xff1a;判断101-200之间有多少…

【C#】条件运算符

1.逻辑与(&&) Console.WriteLine(true && true);//true Console.WriteLine(true && false);//false Console.WriteLine(false && false);//false2.逻辑或(||) Console.WriteLine(true || true);//true Console.WriteLine(true || false);//t…

尚硅谷爬虫note006

一、ajax的get请求 1. ajax的get请求—豆瓣电影第一页 # _*_ coding : utf-8 _*_ # Time : 2025/2/13 15:14 # Author : 20250206-里奥 # File : demo23_ajax的get请求 # Project : PythonProject10-14import urllib.requestfrom demo17_qingqiuduixaingdedingzhi import hea…

OpenVINO 2025.0重磅升级:开启⽣成式AI全场景⾰命!

2025年2⽉6⽇&#xff0c;英特尔OpenVINO™ 2025.0版本震撼发布&#xff0c;本次升级堪称近三年最⼤规模技术⾰新&#xff01;从⽣成 式AI性能跃升到全栈硬件⽀持&#xff0c;从开发者⼯具链优化到边缘计算突破&#xff0c;六⼤核⼼升级重新定义AI部署效率。 一&#xff0c;&a…

[思考记录.AI]关于Deepseek-r1的思维链

一、“思维链”让大模型更聪明&#xff08;不是唯一方式&#xff09; 以前在使用某些AI大模型时&#xff0c;为了获得相对更好的输出&#xff0c;一种方式是在提示词上下功夫——除了交代任务背景&#xff0c;甚至建议对复杂任务预设处理步骤、提供模板案例等。 夸张点说就是&a…

【设计模式】【行为型模式】命令模式(Command)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f3b5; 当你的天空突…

纪念日倒数日项目的实现-【纪念时刻-时光集】

纪念日/倒数日项目的实现## 一个练手的小项目&#xff0c;uniappnodemysql七牛云。 在如今快节奏的生活里&#xff0c;大家都忙忙碌碌&#xff0c;那些具有特殊意义的日子一不小心就容易被遗忘。今天&#xff0c;想给各位分享一个“纪念日”项目。 【纪念时刻-时光集】 一…

前端-干货链接(持续更新)

1. CSS-样式交互动画库 React Bits - Animated UI Components For React 2. CSS库 Documentation | anime.js 3. 巩固基础 闭包 - JavaScript | MDN 继承与原型链 - JavaScript | MDN 使用 Promise - JavaScript | MDN