串口中断(10)自定义通讯协议-协议带数据长度及接收应答处理

news2024/12/27 0:19:22

本文为博主 日月同辉,与我共生,csdn原创首发。希望看完后能对你有所帮助,不足之处请指正!一起交流学习,共同进步!

> 发布人:@日月同辉,与我共生_单片机-CSDN博客

> 欢迎你为独创博主日月同辉,与我共生点赞❤🧡💛+关注👍+收藏🌹+评论☺。

系列专栏: CSDN-单片机串口通信学习系列🎁

> 我的格言是:“尽最大努力,做最好的自己!💪

要转载,请提前告知!!!

版权声明:本文为CSDN博主「日月同辉,与我共生」的原创文章,CSDN独一份。

69d2c602c4ec47cab5112eeed53f9d69.png

目录

一、自定义通讯协议

二、系统设计要求

三、硬件设计

3.1UART通信模块

3.2数码管模块

3.3仿真图

四、软件设计

4.1串口初始化

4.2接收中断

4.3数码管模块

4.4定时器模块

4.5主程序

4.6发送数据模块

4.7清除缓存

4.8uart.h

五、系统测试

5.1正确应答-类型01,长度01

5.2正确应答-类型02,长度02

5.3正确应答-类型03,长度03

5.4错误应答-和效验错误

5.5错误应答-异或效验错误

 

一、自定义通讯协议

自定义通讯协议是指一种特定的数据交互方式,该方式不同于通用的通讯协议,例如HTTP、TCP等。自定义通讯协议可以根据具体的业务需求和通讯要求进行设计,满足数据传输的可靠性、效率、安全性等方面的需求。

⛄定义通信内容及传输方式:首先需要明确通信双方需要交换哪些数据。例如,数据头、数据体、校验位、结束符等。同时需要确定数据传输的方式,如串口通信、TCP/IP通信、WiFi通信等。

 

⛄定义字节序列:根据通信内容,将需要传输的数据按照一定的规则组成字节序列,以便在传输过程中进行处理。

 

⛄添加校验和:在传输数据时,为了保证数据的完整性和正确性,需要给数据添加校验和。常用的校验方式有CRC校验、累加和校验等。

 

⛄定义解析规则:通信双方需要约定如何解析接收到的数据。例如,如何区分数据头和数据体,如何解析数据体中的不同字段等。

 

⛄计好通信协议后,需要进行通信测试。测试时需要模拟发送和接收端,并验证数据传输和解析是否正确。如果出现问题,需要及时修改协议设计或者程序代码。

🌟在实际应用中,自定义通讯协议可以应用于各种领域,例如智能家居、工业控制、物联网等。自定义协议的设计能够在满足特殊需求的同时,提高通信效率、降低通信成本,为应用领域提供更加可靠、安全、高效的通信解决方案。

二、系统设计要求

2.1总设计要求

虚拟终端com3发送数据给单片机com1,发送的数据帧由帧头、类型、数据长度、代码块、和校验、异或校验组成。接收完数据后,单片机根据用户双方约定好的校验位是否正确,发送不同的应答(数据帧),如果数据正确,根据不同的数据类型,让4位数码管显示不同的数据。

2.2设计细节

数据帧:55 AA 类型(01/02/03) 数据长度(1/2/3) 代码块 和校验 异或校验

数据类型为01时,数据长度为1,显示1个字节;数据类型为02时,数据长度为2,显示2个字节;数据类型为03时,数据长度为3,显示2个字节(4位数码管只能显示2个字节,这里显示代码块第2、3个数据);

正确应答:0x55,0xAA,0x80,0x0,0x80,0x80

错误应答:分和效验错误应答和异或效验错误应答。

和效验错误应答:0x55,0xAA,0x81,0x0,0x80,0x80

异或效验错误应答:0x55,0xAA,0x82,0x0,0x80,0x80

三、硬件设计

3.1UART通信模块

d46307903ec043dbb46178025efca0e0.png

3.2数码管模块

线码:dp g f e d c b a

段码(控制数码管第几位显示):1 2 3 4

数码管模块:采用定时延时法。软件编码步骤:1.消隐 2.段码 3.线码 4.延时1ms(定时器定时1ms)。

74HC245芯片用于提高P0的电流驱动能力,让数码管能够显示数据。

a7f04e63db0542daa559ab6dcf9d9bf1.png

3.3仿真图

元件清单:四位数码管采用共阴极。在本文基础上,大家可以尝试使用6位数码管用于显示数据。

e0fb29ddaf3646fe91fbfada31cf5810.png

9cee1a76ec6e424e8a35b249aa92cc62.png

四、软件设计

本文4.1、4.2、4.6、4.7节均为uart.c(串口通信源文件)

代码文件整理:

f9ceb0fcb7244a0fb1ef225e355ef69f.png

4.1串口初始化

void UartInit(void)		//9600bps@11.0592MHz
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x50;		//8位数据,可变波特率
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x20;		//设置定时器模式
	TL1 = 0xFD;			//设置定时初始值
	TH1 = 0xFD;			//设置定时重载值
	ET1 = 0;			  //禁止定时器中断
	ES=1;           //串口中断打开
	TR1 = 1;			  //定时器1开始计时
}

4.2接收中断

数组recv_buf存储类型、长度、代码块数据,用于主程序处理数码管显示。

定义sum_check、 xor_check分别求和效验值、异或效验值。

分支0:判断帧头数据55是否正确==>分支1:判断帧头数据AA是否正确==>分支2:接收类型,从分支2开始计算和效验、异或效验==>分支3:接收长度,计算和效验、异或效验==>分支4:接收代码块,计算和效验、异或效验==>分支5:判断和效验是否正确,若错误发送和效验错误应答==>分支6:判断异或效验是否正确,若错误单片机发送异或效验错误应答,正确发送单片机发送正确应答。

void ES_timers() interrupt 4 //接收中断
{
	static unsigned char machine_step=0;
	static unsigned char sum_check;
	static unsigned char xor_check;
	unsigned char i;
	if(RI)
	{ 
		RI=0; 
		recv_data=SBUF;//recv_data为中间变量,用于接收发送的数据
		switch(machine_step) //55 AA 01/02/03 长度 代码块 和效验 异或效验
		{
			case 0:
				 if(recv_data==0x55) //帧头:55H AAH
				 {
					 machine_step=1;
				 }
				 else
				 {
					 machine_step=0;
				 }
				break;
			case 1:
				 if(recv_data==0xAA)
				 {
					 machine_step=2;
					 recv_cnt=0;
				 }
				 else
				 {
					 machine_step=0;
				 }
				break;
			case 2:
				 recv_buf[recv_cnt]=recv_data; //接收数据类型
				 sum_check=recv_data;
				 xor_check=recv_data;
				 machine_step=3;
				 recv_cnt++;
				break;
			case 3:
				 recv_length=recv_data; //数据字节长度
				 sum_check+=recv_data;
				 xor_check^=recv_data;
				 machine_step=4;
			    break;
			case 4:
				 recv_buf[recv_cnt]=recv_data; //接收数据
			     sum_check+=recv_data;         //计算和效验
				 xor_check^=recv_data;         //计算异或效验
			   if(recv_cnt==recv_length)     
				 {
					  machine_step=5;//接收数据完成后跳到下一个分支
				 }
				 else
				 {
					 machine_step=4;
				 }
				 recv_cnt++;
				break;
			case 5:
				 if(sum_check==recv_data) //和效验正确
				 {
					 machine_step=6;
				 }
				 else                     //和效验错误
				 {
					 machine_step=0;
					 for(i=0;i<6;i++)
						{
							sendByte(sum_check_error[i]);//和效验错误应答
						}  
				 }
				break; 
			case 6:
				if(xor_check==recv_data) //异或效验正确
				{
					recv_flag=1; //接收正确,标志位置1
					for(i=0;i<6;i++)
					{
							sendByte(recv_correct[i]);//发送正确应答
					}  
				}
				else
				{
					for(i=0;i<6;i++)
					{
						sendByte(xor_check_error[i]);//异或效验错误应答
					}  
				}
				machine_step=0;
				sum_check=0;
				xor_check=0;
				recv_cnt=0;
			   break; 			
			 default:break;		 
		}
   }
}

4.3数码管模块

display.c

#include "display.h"

unsigned char code leddata[]={
	              0x3F, //0
	              0x06, //1
                  0x5B, //2
	              0x4F, //3
	              0x66, //4																																																																		
	              0x6D, //5
	              0x7D, //6
	              0x07, //7
	              0x7F, //8
	              0x6F, //9
	              0x77, //A																																																																							
	              0x7C, //B
	              0x39, //C
	              0x5E, //D
	              0x79, //E
	              0x71, //F
	              0x76, //H
	              0x38, //L
	              0x37, //n
                  0x3E, //u	
	              0x73, //P																																																																	
	              0x5C, //o
	              0x40, //-
	              0x00  //熄灭
                             };//数码管段码表

unsigned char LEDBuf[]={8,8,8,8};//缓冲区
unsigned char code PLACE_COOE[]={0xfe,0xfd,0xfb,0xf7};//位码
/******************************************************************************************
函数名:display
功能:数码管显示函数
参数:无
返回值:无
******************************************************************************************/
void display()
{
	unsigned char i;
	IO_DIG=0x00;//消隐
	IO_DIG=leddata[LEDBuf[i]];//段码
	IO_PLACE=PLACE_COOE[i];//位码
  i++;
	if(N==i)
		i=0;
}

display.h

#ifndef __DISPLAY_H__
#define __DISPLAY_H__

#include <reg51.h>


#define IO_DIG   P0 //段码IO
#define IO_PLACE P2 //位码IO

#define N 4 //数码管个数

unsigned char code leddata[];//变量声明
extern unsigned char LEDBuf[];

void display();//数码管显示函数声明

#endif

4.4定时器模块

定时1ms,用于数码管能够显示出数字。

void Timer0_Init(void)		//1毫秒@11.0592MHz
{
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |= 0x01;			//设置定时器模式
	TL0 = 0x66;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	ET0=1;          //定时器0中断打开
	TR0 = 1;				//定时器0开始计时
}

void T0_timer() interrupt 1 //利用1ms计数,判断是否接收完成
{
	TR0=0;
	display();
	TL0 = 0x66;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	TR0=1;
}

4.5主程序

LEDBuf[ ]:数码管显示缓冲区,用于显示display.c表中对应的数字/字符。

recv_buf[0]:数据类型

长度为1,显示recv_buf[0]

长度为2,显示recv_buf[1]、recv_buf[2]

长度为3,显示recv_buf[2]、recv_buf[3]

要想显示数据,就要将数据(2位数)拆成每一位分别显示到对应的位置。十位数=数据右移4位,个位数=数据&0x0f。

#include <reg51.h>
#include "uart.h"
#include "display.h"


void Timer0_Init(); //定时器0函数声明

sbit LED=P1^0;//位定义
sbit Beep=P1^7;

void main()
{
	UartInit();     //调用串口初始化函数
	Timer0_Init();
	EA=1;           //总中断允许
	printf("Wait for Serial Communication Tset Start.\r\n");
	printf("Please Send a string of data:\r\n");
	while(1)
	{
		if(recv_flag==1) //接收完毕,开始解析
		{
			recv_flag=0;
			switch(recv_buf[0])//数据类型---命令
			{
				case 0x01://数码管显示1字节
					LEDBuf[0]=23;
				    LEDBuf[1]=23;
					LEDBuf[2]=recv_buf[1]>>4;
				    LEDBuf[3]=recv_buf[1]&0x0f;
					break;
				case 0x02://数码管显示2字节(第1、2个数据)
					LEDBuf[0]=recv_buf[1]>>4;
				    LEDBuf[1]=recv_buf[1]&0x0f;
					LEDBuf[2]=recv_buf[2]>>4;
				    LEDBuf[3]=recv_buf[2]&0x0f;
					break;
				case 0x03://数码管显示2字节(第2、3个数据)
					LEDBuf[0]=recv_buf[2]>>4;
				    LEDBuf[1]=recv_buf[2]&0x0f;
					LEDBuf[2]=recv_buf[3]>>4;
				    LEDBuf[3]=recv_buf[3]&0x0f;
					break;
				default:clr_recvbuffer(recv_buf); //清除缓存
				break;	
			}
		}
	}
}

4.6发送数据模块

发送数据dat==>等待发送完成(发送完成TI自动置1)==>TI清0,以便下次能够发送数据帧。

void sendByte(unsigned char dat) //发送一帧数据功能函数
{
	SBUF=dat;
	while(!TI);
	TI=0;
}

void sendString(unsigned char *dat)//发送字符串函数
{
	while(*dat != '\0')
	{
		sendByte(*dat++);
	}
}

4.7清除缓存

void clr_recvbuffer(unsigned char *buf)//清除缓冲
{
	unsigned char i;
	for(i=0;i<MAX_REX_NUM;i++)
	{
	  buf[i]=0;
	}
}

4.8uart.h

#ifndef __UART_H__
#define __UART_H__

#include <reg51.h>
#include <stdio.h>

#define MAX_REX_NUM 20
#define MAX_timer_cnt 5

extern unsigned char recv_buf[MAX_REX_NUM];
extern unsigned char recv_cnt;
extern unsigned char start_timer;
extern unsigned char recv_timer_cnt;
extern unsigned char recv_flag;
extern unsigned int led_data;
extern unsigned int beep_data;
extern unsigned int led_cnt;
extern unsigned int beep_cnt;

void UartInit(void);
void sendByte(unsigned char dat);
void sendString(unsigned char *dat);
char putchar(char c);
void clr_recvbuffer(unsigned char *buf);

#endif

五、系统测试

串口助手模式:十六进制

5.1正确应答-类型01,长度01

类型:01,长度:01,代码快:10

和效验正确,异或效验正确,正确应答55 AA 80 00 80 80

数码管显示  10

c7d16f56d8724f18a4682039e34c411a.png

5.2正确应答-类型02,长度02

类型:02,长度:02,代码快:10 11

和效验正确,异或效验正确,正确应答55 AA 80 00 80 80

数码管显示1011

461b247642da4dc78415425d318cc246.png

5.3正确应答-类型03,长度03

类型:03,长度:03,代码快:10 11 13

和效验正确,异或效验正确,正确应答55 AA 80 00 80 80

数码管显示1113

8b92579ced0145f6927f22de8ac75f2d.png

5.4错误应答-和效验错误

和效验错误,异或效验正确,和效验错误应答55 AA 81 00 80 80

ee0f1c75fcb0415f84200566054b1902.png

5.5错误应答-异或效验错误

和效验错误,异或效验错误,异或效验错误应答55 AA 82 00 80 80

6a3f01d5372648b880685ff3d3524832.png

想要快速计算出效验值吗?关注我并在评论区留言“666”,博主@日月同辉,与我共生将免费提供快速计算出效验值的网站。

目前,串口通信系列已经写了9篇,大家可以到我的主页​​​​​​​免费查询。

下一文将着重讲述CRC16效验,亲爱的读者敬请期待,下一文更精彩!!!

一日不读书,胸臆无佳想。我叫不白吃,喜欢我的,可以支持我,博主名叫@日月同辉,与我共生

@日月同辉,与我共生_单片机基础,单片机串口通信-CSDN博客@日月同辉,与我共生擅长单片机基础,单片机串口通信,等方面的知识,@日月同辉,与我共生关注stm32,c语言,51单片机,proteus,单片机领域.https://blog.csdn.net/LIN___IT?spm=1000.2115.3001.5343

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1185740.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年11月数据库流行度最新排名

点击查看最新数据库流行度最新排名&#xff08;每月更新&#xff09; 2023年11月数据库流行度最新排名 TOP DB顶级数据库索引是通过分析在谷歌上搜索数据库名称的频率来创建的 一个数据库被搜索的次数越多&#xff0c;这个数据库就被认为越受欢迎。这是一个领先指标。原始数…

微信小程序登录后端

一、 概念 code code是用户登录凭证,个人理解为用户的授权码&#xff08;需要用户本人授权给小程序&#xff0c;小程序才有权力获取到你这个用户的数据&#xff09;&#xff0c;code需要由小程序向微信服务器获取。 注意&#xff1a; 每个code只能使用一次&#xff0c;且有效…

射频功率放大器应用中GaN HEMT的表面电势模型

标题&#xff1a;A surface-potential based model for GaN HEMTs in RF power amplifier applications 来源&#xff1a;IEEE IEDM 2010 本文中的任何第一人称都为论文的直译 摘要&#xff1a;我们提出了第一个基于表面电位的射频GaN HEMTs紧凑模型&#xff0c;并将我们的工…

kubernetes集群编排——k8s认证授权

pod绑定sa [rootk8s2 ~]# kubectl create sa admin [rootk8s2 secret]# vim pod5.yaml apiVersion: v1 kind: Pod metadata:name: mypod spec:serviceAccountName: admincontainers:- name: nginximage: nginxkubectl apply -f pod5.yamlkubectl get pod -o yaml 认证 [rootk8s…

SpringBoot案例学习(黑马程序员day10,day11)

1 环境准备&#xff1a; 1.idea 创建spring项目&#xff0c;选择springweb,mybatis framework ,sql drive框架 2.添加pom.xml依赖&#xff1a; <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependen…

关于 pthread_create 传参的疑问

对于函数原型 int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) 里的参数 arg&#xff0c;之前一直有疑问&#xff0c;就是把 &thread 传给 arg时&#xff0c;新创建的线程里是否能取到这个值呢&#xff1…

小程序多文件上传 Tdesign

众所周知&#xff0c;小程序文件上传还是有点麻烦的&#xff0c;其实主要还是小程序对的接口有诸多的不便&#xff0c;比如说&#xff0c;文件不能批量提交&#xff0c;只能一个个的提交&#xff0c;小程序的上传需要专门的接口。 普通的小程序的页面也比普通的HTML复杂很多 现…

SQL SERVER Inregration Services-OLE DB、Oracle和ODBC操作

OLE DB链接器 OLE DB插件下载&#xff1a;https://learn.microsoft.com/zh-cn/sql/connect/oledb/download-oledb-driver-for-sql-server?viewsql-server-ver16 配置OLE DB Connection Manager 在点击“新建”时&#xff0c;会弹出警告信息“不支持指定的提供程序&#xff0…

SpringBoot系列之集成Redission入门与实践教程

Redisson是一款基于java开发的开源项目&#xff0c;提供了很多企业级实践&#xff0c;比如分布式锁、消息队列、异步执行等功能。本文基于Springboot2版本集成redisson-spring-boot-starter实现redisson的基本应用 软件环境&#xff1a; JDK 1.8 SpringBoot 2.2.1 Maven 3.2…

k8s 目录和文件挂载到宿主机

k8s生产中常用的volumes挂载方式有&#xff1a;hostPath、pv&#xff0c;pvc、nfs 1.hostPath挂载 hostPath是将主机节点文件系统上的文件或目录挂载到Pod 中&#xff0c;同时pod中的目录或者文件也会实时存在宿主机上&#xff0c;如果pod删除&#xff0c;hostpath中的文…

CANoe测试报告如何打印表格?

文章目录 效果使用函数TestInfoTableTestInfoHeadingBeginTestInfoCellTestInfoHeadingEndTestInfoRow代码效果 使用函数 以下函数使用方法查看帮助文档。 TestInfoTable TestInfoHeadingBegin TestInfoCell TestInfoHeadingEnd TestInfoRow 代码

css设置浏览器表单自动填充时的背景

浏览器自动填充表单内容&#xff0c;会自动设置背景色。对于一般的用户&#xff0c;也许不会觉得有什么&#xff0c;但对于要求比较严格的用户&#xff0c;就会“指手画脚”。这里&#xff0c;我们通过css属性来设置浏览器填充背景的过渡时间&#xff0c;使用户看不到过渡后的背…

Python之字符串、正则表达式练习

目录 1、输出随机字符串2、货币的转换&#xff08;字符串 crr107&#xff09;3、凯撒加密&#xff08;book 实验 19&#xff09;4、字符替换5、检测字母或数字6、纠正字母7、输出英文中所有长度为3个字母的单词 1、输出随机字符串 编写程序&#xff0c;输出由英文字母大小写或…

相亲交友小程序源码 同城相亲交友小程序源码

相亲交友小程序源码 同城相亲交友小程序源码 收费模式&#xff1a; 1、会员开通VIP收费&#xff1b;3、会员购买服务项目收费&#xff08;可以自定义服务项目&#xff09;&#xff1b; 二、全民推广系统&#xff1a; 1、邀请用户注册奖励&#xff08;邀请一个用户进入注册…

基础:JavaScript的怪癖之一:提升(Hoisting)

JavaScript&#xff0c;通常被称为“Web 语言”&#xff0c;是一种多功能且广泛使用的编程语言。它以其怪癖而闻名&#xff0c;其中之一就是 hoisting&#xff08;提升&#xff09;。无论你是经验丰富的开发人员还是刚刚开始你的编码之旅&#xff0c;理解提升对于编写干净和高效…

设计模式-状态模式 golang实现

一 什么是有限状态机 有限状态机&#xff0c;英⽂翻译是 Finite State Machine&#xff0c;缩写为 FSM&#xff0c;简称为状态机。 状态机不是指一台实际机器&#xff0c;而是指一个数学模型。说白了&#xff0c;一般就是指一张状态转换图。 已订单交易为例&#xff1a; 1.…

绿光集团荣获美业科技创新大奖,杨全军董事长荣获杰出人物

近日&#xff0c;在2023中国&#xff08;南昌&#xff09;国际美发美容节之“凤凰之夜&#xff0c;美业盛典”上&#xff0c;香港绿光国际科技集团股份有限公司董事长杨全军先生荣获了2023年度“凤凰”杰出人物奖。同时&#xff0c;绿光集团也因其研发的AI人工智能数字光磁床、…

第21章_InnoDB数据页结构

文章目录 概述UserRecords和FreeSpaceInfimum Supremum&#xff08;最小记录和最大记录&#xff09;File Header&#xff08;文件头部&#xff09;Page Directory&#xff08;页目录&#xff09;File Trailer 概述 它是InnoDB管理存储空间的基本单位&#xff0c;一个页的大小…

【3D图像分割】基于Pytorch的 VNet 3D 图像分割4(改写数据流篇)

在这篇文章&#xff1a;【3D 图像分割】基于 Pytorch 的 VNet 3D 图像分割2&#xff08;基础数据流篇&#xff09; 的最后&#xff0c;我们提到了&#xff1a; 在采用vent模型进行3d数据的分割训练任务中&#xff0c;输入大小是16*96*96&#xff0c;这个的裁剪是放到Dataset类…

C++中如何获取虚表和虚函数的地址

获取虚函数的地址 虚函数是C中用于实现多态的一种机制&#xff0c;该机制的原理在此不做赘述。本文主要讨论如何获取虚表以及虚函数的地址&#xff1f; class ClassA { private:int _a;double _b; public:ClassA(int a, double b) : _a(a), _b(b) { }virtual int funcA(int a…