(新手必看)自定义数据传输通信协议+STM32代码详解

前言


       本篇博客主要学习和了解一些单片机协议的格式,在对传输大数据或者要求准确性的时候,都需要通过协议来发送接收,下面通过了解协议的基本构成和代码来分析和实现协议的发送和接收。本篇博客大部分是自己收集和整理,如有侵权请联系我删除。

本次博客开发板使用的是正点原子精英版,芯片是STM32F103ZET6,需要资料可以@我拿取。

交流群:717237739

如果觉得有用点赞关注收藏三连,多谢支持

本博客内容原创,创作不易,转载请注明
————————————————

一 .什么是协议?

协议,是网络协议的简称,网络协议是通信计算机双方必须共同遵从的一组约定

如怎么建立连接,怎么样互相识别等,只有遵守这个约定,计算机之间才能相互通信交流。它的三要素是:语法,语义,时序。为了使数据在网络上从源到达目的,网络通信的参与方必须遵循相同的规则,这套规则为协议,最终体现为在网络上传输的数据包的格式。 

例如,串口的波特率,也是协议的一种表现格式。

参考知乎的资料了解七层协议:OSI 七层模型和TCP/IP模型及对应协议(详解)

OSI 七层模型和TCP/IP模型及对应协议(详解)

以上图片是为了让我们对协议有个充足的概念,具体的自己可以先行了解协议的含义和作用。

二 .协议的组成 

1.概念

这里说的数据协议是建立在物理层之上的通信数据包格式。所谓通信的物理层就是指我们通常所常用的RS232,RS485,红外,光纤,无线等通信方式

2.组成部分

比较可靠的通信协议包括:帧头,地址信息,数据类型,数据长度,数据块,校验位,帧尾

在日常自己定义的格式里,我们一般也是要遵循这些格式来定义一个数据包的发送和接收,以保证数据的完整性和准确性。

  • 帧头:一帧数据的开始,可以使用多个字节,具体内容自己定义。例如:0X55
  • 地址信息:主要用于多机通信中,通过地址信息的不同来识别不同的通信终端,确定和哪个设备进行通信(类似于IIC的芯片地址ID)。例如:0X01
  • 数据类型:可以标识后面紧接的是命令还是数据,例如0X01标识二进制,0x02标识十六进制等。
  • 数据长度:标识后面的要发送/接收的数据长度的个数。例如:0X02,表示后面跟两个数据。
  • 数据块:真正发送的数据内容,发送数据内容的长度和上面的数据长度对应。
  • 校验码:用来检验数据的完整性和准确性,一般常见的校验方式有(MODBUS_CRC16/CRC32,ADD8/16求和等等)
  • 帧尾:判断数据包的结束,可以为一个数据或者多个数据,自己定义,例如0XAA

根据以上的协议格式介绍,一般正常的数据包为:

0X55        0X01        0X01        0X02        0X13        0X88        CRC16H        CRC16L        0XAA

(帧头        地址        数据类型     长度        数据        数据        CRC检验H      CRC校验L      帧尾)

 

三. 类似的芯片手册协议格式讲解

这里参考一个芯片手册:SYN6658中文语音合成芯片 用户手册

 从这里分析可以得到:

帧头为:        0XFD        数据长度:2字节 0xXX 0xXX       命令字:参考手册一字节

命令参数:一字节     数据文本:最大4K字节

下面是手册的一些命令字和命令参数的参考:

完整的数据包格式:

 注意:在一些使用其他芯片的场景下,我们一般都需要遵守各个芯片的协议格式,然后根据手册和要求发送对应的数据包,这样协议对应才能驱动芯片。

四 . STM32代码实现自定义格式协议

我们在网上找到有对应的嵌入式协议测试题目,我们根据这个题目写出对应的协议发送和接收代码,代码内部做了注释,整体就不再讲解了,不懂的只能去补补C语言了,代码仅做参考。

测试题:

题目分析:

  • 1.数据包格式,建议直接做成结构体的模式,然后传入地址,一个个调用就很方便了
  • 2.已知的信息,直接宏定义调用,例如帧头帧尾
  • 3.提前写好串口数据包的发送和接收函数,类似数组遍历那种,然后了解清楚占用的字节大小
  • 4.题目要求最大65536,但是我们在STM32F103上实现,就最大256算了,不然超范围了
  • 串口发送和接收函数讲解和应用:基于STM32 + UART串口通信新手详解

 代码.C.H部分和main部分

#include "rs485.h"
#include <stdio.h>
#include <string.h>
u8 sendbuf[Send_Buf_Size];  //发送数据缓冲区
u8 recbuf[Rec_Buf_Size];		//接收数据缓冲区
RS485 rs485def={sendbuf,recbuf,0,0}; //rs485相关信息结构体//中断服务函数接收数据
void USART3_IRQHandler(void)
{u8 data;u32 head,tail;u8 len;if(USART3->SR & 1<<5){data= USART3->DR;
//		printf("%x\t",data);rs485def.recbuf[rs485def.reclen]=data ;//存放收到的数据rs485def.reclen++;if(rs485def.reclen >=8) //收到了包头和数据域长度{head = *(u32*)rs485def.recbuf;//获取包头
//			printf("head:0x%x\r\n",head);if(head == PACK_START)//收到包头{len = *(u32*)&rs485def.recbuf[4];//获取数据域长度
//				printf("reclen:%d,len:%d\r\n",rs485def.reclen,len);if(rs485def.reclen >= len+12) //一帧数据接收完成{tail = *(u32*)&rs485def.recbuf[len+4+4];//获取包尾 4包头所占4字节,4数据域长度所占4个字节
//					printf("tail:0x%x\r\n",tail);if(tail == PACK_TAIL )//包尾正确{rs485def.recflag = 1; //接收完成标志}else{rs485def.reclen = 0 ;}}}else{rs485def.reclen = 0 ;}}}
}//数据包接收函数
//函数功能:得到数据包的相关内容
//出口参数 :
//返回值 : 0 接收到数据 1 ,没收到数据
u8 RecPacket(Packet* pdata)
{u8* ptemp = rs485def.recbuf;u8 len;u8 buf[Rec_Buf_Size-20];pdata->pInform = (char *)buf;if(rs485def.recflag == 1){rs485def.recflag = 0 ;		//跳过包头ptemp += 4;//获取数据域长度len = *(u32*)ptemp;//获取验证码ptemp += 4;pdata->identify = *(u16*)ptemp;//获取源地址ptemp += 2;pdata->SrcAddr = *(u16*)ptemp;//获取目的地址ptemp += 2;pdata->DesAddr = *(u16*)ptemp;//获取命令码ptemp += 2;pdata->CmdNum = *(u16*)ptemp;ptemp += 2;  //指向信息内容//获取信息长度pdata->InformLlen = len - 8;	//获取信息内容memcpy(pdata->pInform,ptemp,pdata->InformLlen);//清除
//		memset(&rs485def,0,sizeof(RS485));rs485def.reclen = 0 ;return 0 ;}return 1;
}//数据包发送函数
//入口参数:发送的数据包
void SendPacket(Packet* pdata)
{u8* ptemp = rs485def.sendbuf;u8 sendlen;
//	u8 i;//把需要发送的数据赋给sendbuf//包头*(u32*)ptemp=PACK_START;
//	printf("0x%x\r\n",*ptemp);//数据域长度ptemp += 4;*(u32*)ptemp = pdata->InformLlen+8;//验证码ptemp += 4;*(u16*)ptemp = pdata->identify;//源地址ptemp += 2;*(u16*)ptemp = pdata->SrcAddr;//目的地址ptemp += 2;*(u16*)ptemp = pdata->DesAddr;//命令码ptemp += 2;*(u16*)ptemp = pdata->CmdNum;//信息内容ptemp += 2;//strcpy strncpy memcpy memset-->这几个函数的区别memcpy(ptemp,pdata->pInform,pdata->InformLlen);
//RS485_Send(ptemp,pdata->InformLlen);//包尾ptemp+=pdata->InformLlen;*(u32*)ptemp=0x88CC55AA;//发送数据ptemp += 4;sendlen = ptemp - rs485def.sendbuf;//发送数据长度	
//	printf("send:");
//	for(i=0;i<sendlen;i++)
//		printf("%x\t",rs485def.sendbuf[i]);RS485_Send(rs485def.sendbuf,sendlen); //发送数据
}
#ifndef _RS485_H_
#define _RS485_H_
#include "stm32f10x.h"
#include "io_bit.h"#define Send_Buf_Size 256
#define Rec_Buf_Size 	Send_Buf_Sizetypedef struct
{u8 *sendbuf;		 //发送数据缓冲区u8 *recbuf;			//接收数据缓冲区u8 reclen;			//接收数据总长度u8 recflag;			//接收完成事件标志位
}RS485;
extern RS485 rs485def;//数据包
#define PACK_START 0xAA55CC88	//帧头
#define PACK_TAIL  0x88CC55AA //帧尾
typedef struct
{u16 identify;  //验证码 --字节u16 SrcAddr;   //源地址u16 DesAddr;   //目的地址u16 CmdNum;    //命令码char* pInform; //信息内容u8 InformLlen; //信息长度
}Packet;u8 RecPacket(Packet* pdata);
void SendPacket(Packet* pdata);#endif

 main


int main()
{	Packet SendPack;Packet RecPack;char sendbuf[6]= "12345";LED_Init();KEY_Init();SCB->AIRCR= 0X05FA0500 ; //设置为分组2USART1_Init(115200);delay_init(72);OLED_Init();W25QXX_Init();DHT11_Init();RS485_Init();while(1){if(Key_Scanf(0))  //发送{	 
//			printf("key\r\n");//填充数据包   ---  学习 433模块 CC1101SendPack.identify = 0x1234;		//验证码SendPack.SrcAddr  = 0x5678;		//源地址SendPack.DesAddr  = 0x90ab;		//目的地址SendPack.CmdNum   = 0xcdef;	  //命令码SendPack.pInform  = sendbuf;	//信息内容SendPack.InformLlen=strlen(sendbuf);//信息长度SendPacket(&SendPack);}if(!RecPacket(&RecPack))//收到数据{//打印收到的数据printf("identify:0x%x\r\n",RecPack.identify);  //验证码printf("SrcAddr:0x%x\r\n",RecPack.SrcAddr);		 //源地址printf("DesAddr:0x%x\r\n",RecPack.DesAddr);		 //目的地址printf("CmdNum:0x%x\r\n",RecPack.CmdNum);			 //命令码printf("pInform:%s\r\n",RecPack.pInform);			 //信息内容printf("InformLlen:%d\r\n",RecPack.InformLlen);//信息长度}}	
}

总结:


      协议在一些公司的项目一般都会用到,最常见的就是485-modbus,不过基本的格式都差不多,这部分内容在项目了算是比较重要的,IAP升级的常用YModem协议也是异曲同工,类似的芯片DHT11和模块也会有协议格式要求。大家如果对我的博客有疑问或者错误,可以@我修改,大家相互交流。

交流群:717237739

如果觉得有用点赞关注收藏三连,多谢支持

本博客内容原创,创作不易,转载请注明

  点赞收藏关注博主,不定期分享单片机知识,互相学习交流。
————————————————
 

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

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

相关文章

机器学习-KL散度的直观理解+代码

KL散度 直观理解&#xff1a;KL散度是一种衡量两个分布之间匹配程度的方法。通常在概率和统计中&#xff0c;我们会用更简单的近似分布来代替观察到的数据或复杂的分布&#xff0c;KL散度帮我们衡量在选择近似值时损失了多少信息。 在信息论或概率论中&#xff0c;KL散度&#…

Java+Swing: 从数据库中查询数据并显示在表格中 整理11

分析&#xff1a;要想从数据库中查询数据并分页展示到表格中&#xff0c;我觉得应该按照这个思路&#xff1a;首先就是发起请求&#xff0c;此时需要向数据库中传递三个参数&#xff1a;当前页码&#xff08;pageNum&#xff09;、每一页的数量&#xff08;pageSize&#xff09…

Knowledge Graph知识图谱—8. Web Ontology Language (OWL)

8. Web Ontology Language (OWL) 在RDFs不可能实现&#xff1a; Property cardinalities, Functional properties, Class disjointness, we cannot produce contradictions, circumvent the Non Unique Naming Assumption, circumvent the Open World Assumption 8.1 OWL Tr…

网工排查网络故障,有这两款软件就够了

网络工程师的工作中&#xff0c;排查网络故障占很大一部分。领导让你查网络故障&#xff0c;批量Ping几十台电脑&#xff0c;结果你Ping了一个小时还没好。你总不能说批量Ping就是这么慢吧&#xff1f; 所以&#xff0c;在这里介绍两款网络工程师常用的排查网络故障工具。 Qu…

知名火锅连锁企业,IT 团队如何在数千家门店中先于用户发现故障

该知名火锅连锁企业是中国领先的餐饮企业&#xff0c;上千家门店遍布全球&#xff0c;由于门店餐饮行业的特殊性&#xff0c;需要靠前部署服务&#xff0c;所以在每家餐厅中&#xff0c;会部署相应的服务器&#xff0c;及相应 IT 设备&#xff0c;本地会运行POS、会员、下单等业…

[NCTF2019]Fake XML cookbook1

提示 xml注入 一般遇到像登录页之类的就因该想到sql注入、弱口令或者xml等 随便输入抓包 这里明显就是xml注入 这里我们来简单了解一下xml注入 这里是普通的xml注入 xml注入其实和sql注入类似&#xff0c;利用了xml的解析机制如果系统没有将‘<’‘>’进行转义&#xff0…

u盘格式化和快速格式化的区别是什么?为您揭晓答案

在日常使用中&#xff0c;我们经常遇到U盘无法正常读取或存储数据的情况。这时候&#xff0c;格式化U盘成为一种常见的解决方法。然而&#xff0c;在格式化U盘时&#xff0c;我们面临两种选择&#xff1a;普通格式化和快速格式化。这两种格式化方式有什么区别&#xff1f;我们又…

Git 硬重置之后恢复历史提交版本

****硬重置之前一定要备份分支呀&#xff0c;谨慎使用硬重置&#xff0c;特别是很多人一起使用的分支**** 如果你在reset的时候选择了Hard选项&#xff0c;也就是硬重置 重置完且push过&#xff0c;那么被你本地和远端后面的提交记录肯定就会被抹去。 解决办法&#xff1a; …

【MAC】iStatistica Pro — 硬件性能状态监控工具

1、iStatistica Pro简介 iStatistica Pro (含iStatistica Sensors mac温度监控模块) 是一款非常漂亮的菜单栏mac系统监控工具。 他的功能包含&#xff1a;Mac 系统摘要&#xff0c;Mac电池信息&#xff0c;Mac网络监控&#xff0c;Mac温度传感器监控&#xff0c;Mac磁盘管理&a…

C/C++ 两数之和为目标值时返回下标

题目&#xff1a;给定一个整数数组nums和一个整数目标值target&#xff0c;在该数组中找出和为目标值target的那两个整数&#xff0c;并返回它们的数组下标。 前提假设&#xff1a;每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。可以按任意…

物联网僵尸网络和 DDoS 攻击的 CERT 分析

在攻击发生当天早上&#xff0c;Dyn 证实其位于东海岸的 DNS 基础设施遭受了来自世界各地的 DDoS 攻击。这些攻击严重影响了 Dyn 的 DNS 客户的业务&#xff0c;更糟糕的是&#xff0c;客户的网站变得无法访问。这些攻击一直持续到美国东部时间下午13&#xff1a;45。Dyn在其官…

C语言学习----指针和数组

&#x1f308;这篇blog记录一下指针学习~ 主要是关于指针和数组之间的关系&#xff0c;还有指针的使用等~ &#x1f34e;指针变量是一个变量 其本身也有一个地址 也需要存放&#xff0c;就和int char等类型一样的&#xff0c;也需要有一个地址来存放它 &#x1f34c;而指针变量…

手机显示此应用专为旧版android打造,因此可能无法运行,点击应用后闪退的问题解决方案

如果您在尝试安装并运行一个Android应用&#xff08;APK文件&#xff09;时遇到错误消息“此应用专为旧版Android打造, 因此可能无法运行”&#xff0c;或者应用在启动时立即崩溃&#xff0c;以下是一些您可以尝试的解决步骤&#xff1a; 图片来源&#xff1a;手机显示此应用专…

抖音小店开设条件和区别:个人店 vs 企业店解析

抖音小店是抖音平台为商家提供的一种电商服务&#xff0c;可以帮助商家建立线上店铺&#xff0c;通过短视频和直播等形式进行商品展示和销售。在抖音小店中&#xff0c;商家可以选择开设个人店或企业店。下面不若与众将介绍抖音小店个人店和企业店的开设条件和区别。 1. 个人店…

资深测试总结,性能测试目的如何做?主要看什么指标?

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、性能测试是什么…

Jenkins 添加node节点

安装SSH插件 Jenkins- 插件管理- 可选插件- 搜索SSH Agent 配置启用SSH Server Jenkins- 系统管理 - 全局安全配置&#xff0c; 把 SSH Server 设置为启用(默认是禁用) 新增节点 第一种方式&#xff08;SSH密钥连接&#xff09;&#xff1a; 1.Jenkins主机生成SSH密钥 [rootk…

Python简单网抑云数据采集 JS逆向

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 环境使用: Python 3.10 Pycharm 模块使用: requests -> pip install requests execjs -> pip install execjs 爬虫实现基本思路流程: 一. 数据来源分析: 明确需求: 明确采集的网站以及数据内容 网址: https://mu…

【Spark精讲】Spark任务运行流程

Spark任务执行流程 部署模式是根据Drvier和Executor的运行位置的不同划分的。client模式提交任务与Driver进程在同一个节点上&#xff0c;而cluster模式提交任务与Driver进程不在同一个节点。 Client模式 Clinet模式是在spark-submit提交任务的节点上运行Driver进程。 执行流…

《码农的噩梦与修电脑的奇幻之旅》

故事从一个充满梦想的码农学习计算机编程开始。他对编写程序充满了热情&#xff0c;认为自己就像是一位能够编织魔法的巫师&#xff0c;能够创造出炫酷的虚拟世界。 然而&#xff0c;这个充满幻想的故事在码农入门的第一天就遭遇了突如其来的挫折。电脑故障了&#xff01;所有…

全网最新最全的基于Tensorflow和PyTorch深度学习环境安装教程: Tensorflow 2.10.1 加 CUDA 11.8 加 CUDNN8.8.1加PyTorch2.0.0

本文编写日期是&#xff1a;2023年4月. Python开发环境是Anaconda 3.10版本&#xff0c;具体Anaconda的安装这里就不赘述了&#xff0c;基础来的。建议先完整看完本文再试&#xff0c;特别是最后安装过程经验分享&#xff0c;可以抑制安装过程中一些奇怪的念头&#xff0c;减少…