串口中断(9)即时解析用户自定义通讯协议--接收数据固定情况

news2024/12/25 14:06:53

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

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

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

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

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

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

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

6fc58cb779cf4b1abfd70ea313d654a5.png

目录

一、系统设计要求

二、系统设计原理

三、硬件设计

3.1串口设计

3.2LED电路设计

3.3蜂鸣器设计

四、软件设计

4.1串口初始化

4.2接收中断

4.3定时器T0初始化

4.4定时器中断

4.5发送数据函数

4.6主程序

4.7uart.h

五、结果展示

5.1控制LED

5.2控制蜂鸣器

六、效验软件推荐

一、系统设计要求

虚拟终端com1发数据给单片机com1,接收后将数据重新发给com3。

com3发送到数据:帧头(1、2帧)+数据类型(3帧)+数据块(4、5帧)+效验位(6、7帧)

帧头:共2帧,55 AA   

数据类型:共1帧,01时控制LED灯,02时控制蜂鸣器

数据块:共2帧,第一帧为高8位,第二帧为低8位,两帧合并为16位2进制,用于控制LED亮/蜂鸣器发声时间。

效验位:共2帧,第一帧为和效验,第二帧为异或效验。

com1将数据重新的数据:发送到数据不是将接收到的数据全部发送给com3,而是第3-5帧数据发送给com3。

二、系统设计原理

本次实验采取"边接收边处理式"的思路。

由于串口通信是一帧一帧接收的,所以我们可以每接收一帧就判断帧头数据的正确性

至于com1要重新发送的数据,可以定义一个数组recv_buf用于存储com1要发送的数据

使用switch-case语句,分支0、1,分别判断帧头数据55和AA后,进入下一个分支。

分支2接收类型+代码快(共3帧),并将3帧存储到数组recv_buf,同时定义两个专门计算效验值的变量(和效验变量sum_check和异或效验变量xor_check),在分支2中计算出第3-5帧的和效验值、异或效验值。

然后进入分支3、4,用于判断计算出的和效验、异或效验值是否与接收到效验帧数据相同。如果和效验和异或效验位正确,则接收完成标志位recv_flag置1,代表接收数据完成且正确 。

主程序中,在recv_flag==1的基础上进行,根据类型数据帧,采用switch-case语句分别处理LED/蜂鸣器。

持续时间:高8位左移8位+低8位

LED发亮持续时间:led_data=recv_buf[1]<<8+recv_buf[2];

蜂鸣器发声时间:beep_data=recv_buf[1]<<8+recv_buf[2];

注:recv_buf[0]是数据类型,recv_buf[1]是高8位,recv_buf[2]是低8位

三、硬件设计

3.1串口设计

com1发送端TXD接com2发送端TXD,com1接收端RXD接com2接收端RXD。

虚拟终端RXD接TXD(因为单片机发送数据给com3,而虚拟终端可以理解为虚拟串口com3)。

d530f4a2d47647dbb61a93cd7d44fab1.png

3.2LED电路设计

LED灯采用共阳极接法,左端接电源(高电平1),右端通过电阻接P1^0,P1^0低电平时,LED导通,发亮;P1^0高电平时,LED不导通,不亮(熄灭)。

5086ca43e8d248269ac4b0f196b9d4ec.png

3.3蜂鸣器设计

R2右端接到P1^7,P1^7为低电平时,三极管导通,+5V电源(提供电压电流的作用)与蜂鸣器连接,蜂鸣器正极为高电平;P1^7为高电平,三极管不导通,蜂鸣器正极为低电平。

4b3aaaa7758a4a07bf8557137f7c498b.png

四、软件设计

4.1串口初始化

波特率9600b/s,晶振频率为11.0592Mhz,串口工作方式1(8位异步通信,波特率可变),定时器T1的工作方式2(8位自动重载)。

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接收中断

头判断55 AA==>将3帧(1帧类型+2帧数据块),计算和效验值、异或效验值==>判断和效验、异或效验是否正确==>正确则接收完成标志位recv_flag置1。

不直接用数组recv_buf来存储接收数据,因为recv_buf存储的数据用于发送给com3,而单片机只将一部分数据发送给com3,不能将接收到的数据全存储到recv_buf。需要定义recv_data(中间值),用于接收com3发来的数据。

void ES_timers() interrupt 4 //接收中断
{
	static unsigned char machine_step=0;//分支变量
	static unsigned char sum_check;//和效验
	static unsigned char xor_check;//异或效验
	if(RI)
	{ 
		RI=0; //RI清0
		recv_data=SBUF;//接收数据
		switch(machine_step)
		{
			case 0:
				 if(recv_data==0x55)//帧头:55
				 {
					 machine_step=1;
				 }
				 else
				 {
					 machine_step=0;
				 }
				break;
			case 1:
				 if(recv_data==0xAA)//帧头:AA
				 {
					 machine_step=2;
					 recv_cnt=0;
				 }
				 else
				 {
					 machine_step=0;
				 }
				break;
			case 2:
				 recv_buf[recv_cnt]=recv_data;//将单片机com1要发送到数据存储到数组recv_buf
				 recv_cnt++;
				 sum_check+=recv_data; //计算和效验
				 xor_check^=recv_data; //计算异或效验
				 if(recv_cnt>2) //存储完3个数据(类型01/02+代码快)后进入下一分支
				 {
					 machine_step=3;
				 }
				 else
				 {
					 machine_step=2;
				 }
			   break;
			case 3:
				 if(sum_check==recv_data) //和效验正确
				 {
					 machine_step=4;
				 }
				 else
				 {
					 machine_step=0;
				 }
			   break; 
			 case 4:
				 if(xor_check==recv_data) //异或效验正确
				 {
					 recv_flag=1;//接收正确
						
				 }
				 machine_step=0;
				 sum_check=0;//和效验变量清0
				 xor_check=0;//异或效验变量清0
				 recv_cnt=0;
			   break; 			
			 default:break;		 
		}
   }
}

4.3定时器T0初始化

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

4.4定时器中断

void T0_timer() interrupt 1 //利用1ms计数,判断是否接收完成
{
	    TR0=0;
		if(led_cnt<led_data)//LED发亮
		{
			LED=0;
			led_cnt++;
		}
		else //LED灯灭
		{
			LED=1;
		}
		if(beep_cnt!=0) //蜂鸣器发声
		{
			Beep=~Beep;
			beep_cnt--;
		}
	    TL0 = 0x66;				//设置定时初始值
	    TH0 = 0xFC;				//设置定时初始值
	    TR0=1;
}

4.5发送数据函数

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

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

4.6主程序

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

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

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

void main()
{
	UartInit();     //调用串口初始化函数
	Timer0_Init();
	EA=1;           //总中断允许
	while(1)
	{
		if(recv_flag==1)//接收完成
		{
			recv_flag=0;
			sendString(recv_buf);//发送缓冲区数据
			switch(recv_buf[0])//根据类型判断对LED处理还是蜂鸣器处理
			{
				case 0x01:
					led_data=recv_buf[1]<<8;//高8位recv_buf[1]左移8位
					led_data=led_data+recv_buf[2];//计算LED控制时间
					led_cnt=0;
					break;
				case 0x02:
					beep_data=recv_buf[1]<<8;//高8位recv_buf[1]左移8位
					beep_data=beep_data+recv_buf[2];//计算蜂鸣器控制时间
					beep_cnt=beep_data;
					break;
				default:clr_recvbuffer(recv_buf);//清除缓存
				break;	
			}
		}
	}
}

4.7uart.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控制LED

com3发送给com1的数据:55 AA 01 20 20 41 01

com1重新发回给com3的数据:01 20 20

结果:LED亮2020ms,2020ms后灭。

c7a62e12be0b478ca077b210ca92f679.png

e4922b820c124eedb1ef586a15da24ec.png

5.2控制蜂鸣器

com3发送给com1的数据:55 AA 02 20 20 42 02

com1重新发回给com3的数据:02 20 20

结果:蜂鸣器发声2020ms,2020ms后停止发声。

855db8ca351b49368636bf5dc079fb3a.png

cfaf9f643bfc44adb59cd93c79ee8c16.png

六、效验软件推荐

为方便读者们学习串口通信,为大家推荐效验计算工具。

链接:

异或校验/BCC校验计算-ME2在线工具 (metools.info)

亲爱的读者敬请期待,下一文更精彩!!!

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

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

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

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

相关文章

共享WiFi贴项目可以带来哪些优势?

当谈到共享WiFi贴的推广时&#xff0c;我们不禁要问&#xff1a;这项新兴的服务项目究竟给我们带来了哪些便利&#xff1f;在这篇文章中&#xff0c;我们将探讨共享WiFi贴推广所带来的益处以及其未来潜力。 共享WiFi贴的推广为人们提供了更加快捷的网络接入方式。无论是商场、餐…

远程运维用什么软件?可以保障更安全?

远程运维顾名思义就是通过远程的方式IT设备等运行、维护。远程运维适用场景包含因疫情居家办公&#xff0c;包含放假期间出现运维故障远程解决&#xff0c;包含项目太远需要远程操作等等。但远程运维过程存在一定风险&#xff0c;安全性无法保障&#xff0c;所以一定要选择靠谱…

项目构建工具maven的基本配置+idea 中配置 maven

&#x1f451; 博主简介&#xff1a;知名开发工程师 &#x1f463; 出没地点&#xff1a;北京 &#x1f48a; 2023年目标&#xff1a;成为一个大佬 ——————————————————————————————————————————— 版权声明&#xff1a;本文为原创文…

hp惠普星15青春版笔记本15s-dr2000/15s-du2000原厂Windows11家庭中文版OEM预装系统

适用型号&#xff1a; 15s-dr2002tx&#xff0c;15s-dr2003tx&#xff0c;15s-dr2004tx&#xff0c;15s-dr2005tx&#xff0c;15s-dr2006tx&#xff0c;15s-dr2016TU&#xff0c;15s-dr2017TU&#xff0c;15s-dr2020TU 15s-du2003tx&#xff0c;15s-du2004tx&#xff0c;15s-…

微信小程序(非个人)备案指南

一、小程序备案法律法规参考 1、《中华人民共和国反电信网络诈骗法》 第二十三条 设立移动互联网应用程序应当按照国家有关规定向电信主管部门办理许可或者备案手续。 2、《互联网信息服务管理办法》 第四条 国家对经营性互联网信息服务实行许可制度&#xff1b;对非经营性互…

多维时序 | MATLAB实现TCN-selfAttention自注意力机制结合时间卷积神经网络多变量时间序列预测

多维时序 | MATLAB实现TCN-selfAttention自注意力机制结合时间卷积神经网络多变量时间序列预测 目录 多维时序 | MATLAB实现TCN-selfAttention自注意力机制结合时间卷积神经网络多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现TCN-s…

python自动化测试(2)-自动化基本技术原理

1 概述 在之前的文章里面提到过&#xff1a;做自动化的首要本领就是要会 透过现象看本质 &#xff0c;落实到实际的IT工作中就是 透过界面看数据。 掌握上面的这样的本领可不是容易的事情&#xff0c;必须要有扎实的计算机理论基础&#xff0c;才能看到深层次的本质东西。 …

零代码编程:用ChatGPT批量删除文件名的字符

文件夹里面的文件标题如下&#xff0c;后面都带有一个网址&#xff0c;怎么批量删除掉呢&#xff1f; 这样让ChatGPT编写一段Python代码&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个处理批量删除文件名中字符的任务&#xff0c;具体步骤如下&#xff1a; 打开…

GO语言数据抓取代码示例

安装Go语言的net/http和io/ioutil包&#xff0c;这两个包是爬虫程序的基础。你可以使用下面的命令来安装&#xff1a; bash go get -u github.com/golang.org/x/net/http2 go get -u golang.org/x/net/html 然后&#xff0c;你可以使用下面的代码来获取爬虫信息&#xff1a; …

第一个ARM程序裸板点灯

硬件知识LED原理图 如何点亮一个LED灯&#xff1f; 看原理图&#xff0c;确定控制LED的引脚。看主芯片的芯片手册&#xff0c;确定如何设置控制这个引脚。写程序。 LED有插脚封装的、贴片封装的。 它们长得完全不一样&#xff0c;因此我们在原理图中把它们抽象出来。 点亮…

ESXi配置两个不同网段虚拟机互通

ESXi配置两个不同网段虚拟机互通 拓扑图&#xff1a; 步骤 在ESXi上新建一个虚拟交换机新建两个端口组&#xff0c;VLAN ID分别为30和31&#xff0c;添加到新建的虚拟交换机上创建两个虚拟机&#xff0c;网络适配器分别使用新建的端口组30和31对新建的虚拟机配置IP在物理交换…

论文阅读:LOGO-Former: Local-Global Spatio-Temporal Transformer for DFER(ICASSP2023)

文章目录 摘要动机与贡献具体方法整体架构输入嵌入生成LOGO-Former多头局部注意力多头全局注意力 紧凑损失正则化 实验思考总结 本篇论文 LOGO-Former: Local-Global Spatio-Temporal Transformer for Dynamic Facial Expression Recognition发表在ICASSP&#xff08;声学顶会…

369B1860G0028 44A730240-G01 IC697ACC722B

369B1860G0028 44A730240-G01 IC697ACC722B 在NOA&#xff0c;一个名为MO(监控和优化)的独立领域与现有系统分开准备&#xff0c;数据直接从机器人、无人机和新传感器收集&#xff0c;例如腐蚀、声音和振动传感器。此外&#xff0c;现有系统中的数据通过OPC UA导入&#xff0c…

实现前后端分离开发:构建现代化Web应用

文章目录 什么是前后端分离开发&#xff1f;为什么要采用前后端分离开发&#xff1f;前后端分离的最佳实践1. 定义API2. 使用RESTful风格3. 选择适当的前端框架4. 选择合适的后端技术5. 数据交互格式6. 前端路由7. 自动化构建和部署8. 跨域问题 示例&#xff1a;前后端分离开发…

使用JavaScript编写的爬虫程序

这是一个使用JavaScript编写的爬虫程序&#xff0c;它使用了Elasticsearch和Nginx来收集和存储数据。在这个程序中&#xff0c;我们首先设置了代理信息&#xff0c;然后使用JavaScript编写了一个爬虫程序来收集数据。以下是每行代码和步骤的详细解释&#xff1a; // 定义代理信…

Tkinter创建菜单窗口

一、说明 本文专门记录如何添加Tkinter的菜单&#xff0c;包括&#xff0c;菜单如何生成&#xff0c;菜单如何布局&#xff0c;菜单如何绑定回调函数&#xff0c;并安排代码示例&#xff0c;凡期望做菜单界面的读者&#xff0c;可以全面参考。 二、创建菜单实现步骤 2.1 总体…

2023-11笔记

1.switch空指针异常 Exception in thread "main" java.lang.NullPointerException:Cannot invoke "String.hashCode()" because "<local2>" is nullat Study5.Test03.main(Test03.java:6)我们由此可以知道&#xff0c;switch语句部分情况下…

正点原子嵌入式linux驱动开发——Linux Regmap驱动

在前面学习I2C和SPI驱动的时候&#xff0c;针对I2C和SPI设备寄存器的操作都是通过相关的API函数进行操作的。这样Linux内核中就会充斥着大量的重复、冗余代码&#xff0c;但是这些本质上都是对寄存器的操作&#xff0c;所以为了方便内核开发人员统一访问I2C/SPI设备的时候&…

每日一题 2586. 统计范围内的元音字符串数(简单)

简单题&#xff0c;走流程 class Solution:def vowelStrings(self, words: List[str], left: int, right: int) -> int:ans 0for i in range(left, right 1):if words[i][0] in aeiou and words[i][-1] in aeiou:ans 1return ans

1300*B. Big Vova(最大公约数贪心)

Problem - 1407B - Codeforces 解析&#xff1a; 显然最大的数肯定放到第一个&#xff0c;然后对于之后每一个数&#xff0c;贪心考虑让其最大公约数最大即可。 时间复杂度log(n^2logn) #include<bits/stdc.h> using namespace std; #define int long long const int N…