当前位置:网站首页>通讯协议设计与实现
通讯协议设计与实现
2022-07-07 10:31:00 【咕咚.萌西】
通讯协议分类
硬件协议
在物理层进行数据传输时,发送端和接收端之间需要遵循相应的硬件协议,例如我们通过IO采集一个电压信号,当输入电压高于2V时候,硬件会认为采集到一个高电平,低于2V则认为是一个低电平。常见的光纤,串口,CAN总线我们将其划分为硬件协议,他们是数据传输的载体。
软件协议
根据总线的物理特性,在此之上可以设计出各种各样的软件协议,此处涉及到的协议讲解部分为软件协议,后面简称协议。例如通过IO可以设计出SPI通讯协议和IIC协议,通过CAN总线可以设计出CANOpen协议,通过串口可以设计出Modbus协议。
帧格式
在硬件基础上进行数据传输时需要设计一个帧格式,例如串口传输的数据通常是以字节为单位,那么协议设计时传输的最小的单位也必须是字节,通讯帧通常分为两类,一类是定长帧,一类是不定长帧。
定长帧
所有帧长度都相同,接收者先接收完指定长度字符,然后根据报文类型做进一步处理,这种协议可拓展性较低,适合简单数据传输。
不定长帧
第一种:通讯双方根据功能划分,划分为不同的报文类型,每种类型的长度不尽相同,但是接收者和发送者对每种类型的数据长度都是提前约定好的,传输数据中不包含长度信息。
第二种:帧长度不固定,每帧可以分为三个部分,帧头,数据,校验,其中帧头和校验的长度是固定的,数据的长度是可变的,帧头中包含长度信息,当接收完一个完整的帧头后进行解析,通过读取其中的长度信息获取到剩余数据的长度,然后继续接收,直到接收到校验,一帧数据接收完成,然后进行数据的校验,校验成功进行下一步处理,这种情况下接收端在发送前并不清楚需要接收的数据长度,相比第一种协议可拓展性更强,可以用于传输文件等超长信息。
通讯帧设计
起始标识 | 帧头 | 数据 | 校验 | 结束标识 |
---|---|---|---|---|
1 byte | 16 bytes | n bytes | 1 byte | 1 byte |
通讯报文主要分成两个部分:
- 用户层包含帧头和数据,这部分定义了报文的类型和传输的内容
- 传输层为了保证数据的准确性和完整性,加上了起始标识,校验和结束标识
起始标志和结束标志
为了避免在数据中出现起始标识和结束标识,因此对数据中出现该字符时需要进行转义。
起始标识 | 结束标识 | 0xdb |
---|---|---|
0x55 | 0x56 | 0xdb |
0xdb 0xdc | 0xdb 0xdd | 0xdb 0xde |
帧头
类型 | 指令 | 帧号 | 总长度 | 数据长度 |
---|---|---|---|---|
2 byte | 2 byte | 2 bytes | 4 bytes | 4 bytes |
- 类型和指令用于告知接收者如何处理数据
- 帧号和总长度用于传输大数据,例如文件
- 数据长度则代表数据区的字节数
数据
Data |
---|
n bytes |
数据长度不固定,是可变的,接收端会根据帧头中包含的长度信息接收数据,如果没有数据则直接接收帧尾
校验
CRC |
---|
1 bytes |
帧尾部分包含1字节CRC,用于对整个数据进行校验,如果采用TCP这种可靠通讯,可以去掉CRC,CRC是为了防止传输过程中出错
帧数据结构
typedef struct
{
uint16_t type;
uint16_t cmd;
uint16_t index;
uint32_t total;
uint32_t len;
} msg_header_t;
typedef struct
{
msg_header_t header;
uint8_t payload[];
} msg_t;
- 按照C语言的默认边界对齐方式,帧头大小为16字节。
- 数据长度不固定此处使用一个指针来表示。
- msg_t大小为20字节。
通讯帧传输
发送报文
uint32_t msg_data_convert(uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t out_len)
{
int i = 0;
int j = 0;
uint8_t *p = in;
for (; (i < in_len) && (j < (out_len - 1)); i++, p++)
{
if (MSG_BEGIN_FLAG == *p)
{
out[j++] = 0xdb;
out[j++] = 0xdc;
}
else if (MSG_END_FLAG == *p)
{
out[j++] = 0xdb;
out[j++] = 0xdd;
}
else
{
out[j++] = *p;
if (0xdb == *p)
{
out[j++] = 0xde;
}
}
}
DEBUG_ASSERT(j < (out_len - 1));
return j;
}
bool msg_create(msg_parser_t *obj, msg_type_def type, msg_cmd_def cmd, uint32_t index, uint32_t total, uint8_t *payload, uint32_t len)
{
uint8_t crc = 0;
bool ret = false;
msg_t msg = {
0};
uint32_t offset = 0;
if ((payload && (len > 0)) || (!payload && (len == 0)))
{
ret = true;
}
msg.header.type = (uint16_t)type;
msg.header.cmd = (uint16_t)cmd;
msg.header.index = index;
msg.header.total = total;
msg.header.len = len;
msg.payload = payload;
crc = msg_parser_calculate_crc(0, (uint8_t *)&msg, sizeof(msg_header_t));
crc = msg_parser_calculate_crc(crc, msg.payload, msg.header.len);
obj->msg_send_buffer[offset++] = MSG_BEGIN_FLAG;
/* 对数据中出现帧头和帧尾的情况进行转义 */
offset += msg_data_convert((uint8_t *)&msg, sizeof(msg_header_t), &obj->msg_send_buffer[offset], sizeof(obj->msg_send_buffer) - offset);
offset += msg_data_convert(msg.payload, msg.header.len, &obj->msg_send_buffer[offset], sizeof(obj->msg_send_buffer) - offset);
offset += msg_data_convert(&crc, sizeof(crc), &obj->msg_send_buffer[offset], sizeof(obj->msg_send_buffer) - offset);
obj->msg_send_buffer[offset++] = MSG_END_FLAG;
/* 通过串口发送报文 */
com_write(obj->com_drv, obj->msg_send_buffer, offset);
return ret;
}
接收报文
msg_t *msg_parser_recv(msg_parser_t *obj)
{
uint8_t temp = 0;
uint32_t crc = 0;
uint8_t recv_crc = 0;
uint32_t recv_cnt = 0;
msg_t *ret = &obj->recv_msg;
/* 接收帧头 */
do
{
com_read_byte(obj->com_drv, &temp, osWaitForever);
} while (MSG_BEGIN_FLAG != temp);
/* 接收剩余数据 */
do
{
com_read_byte(obj->com_drv, &temp, osWaitForever);
if (temp == 0xdb)
{
com_read_byte(obj->com_drv, &temp, osWaitForever);
if (0xdc == temp)
obj->msg_recv_buffer[recv_cnt++] = MSG_BEGIN_FLAG;
else if (0xdd == temp)
obj->msg_recv_buffer[recv_cnt++] = MSG_END_FLAG;
else if (0xde == temp)
obj->msg_recv_buffer[recv_cnt++] = 0xdb;
}
else
{
obj->msg_recv_buffer[recv_cnt++] = temp;
}
} while ((MSG_END_FLAG != temp) && (recv_cnt < sizeof(obj->msg_recv_buffer)));
if (recv_cnt < sizeof(obj->msg_recv_buffer))
{
/* 获取帧头 */
memcpy(&ret->header, obj->msg_recv_buffer, sizeof(msg_header_t));
if (recv_cnt == (sizeof(msg_header_t) + 2 + ret->header.len))
{
/* 获取数据 */
ret->payload = obj->msg_recv_buffer + sizeof(msg_header_t);
/* 获取校验 */
recv_crc = obj->msg_recv_buffer[recv_cnt - 2];
/* 计算校验 */
crc = msg_parser_calculate_crc(0, (uint8_t *)ret, sizeof(msg_header_t));
crc = msg_parser_calculate_crc(crc, ret->payload, ret->header.len);
if (recv_crc != crc)
{
ret = NULL;
}
}
else
{
ret = NULL;
}
}
else
{
ret = NULL;
}
return ret;
}
边栏推荐
- 爱可可AI前沿推介(7.7)
- 数据库系统原理与应用教程(008)—— 数据库相关概念练习题
- Using stack to convert binary to decimal
- H3C HCl MPLS layer 2 dedicated line experiment
- Epp+dis learning road (2) -- blink! twinkle!
- ES底层原理之倒排索引
- 【玩转 RT-Thread】 RT-Thread Studio —— 按键控制电机正反转、蜂鸣器
- 30. Few-shot Named Entity Recognition with Self-describing Networks 阅读笔记
- NGUI-UILabel
- SQL Lab (36~40) includes stack injection, MySQL_ real_ escape_ The difference between string and addslashes (continuous update after)
猜你喜欢
SQL lab 26~31 summary (subsequent continuous update) (including parameter pollution explanation)
Solutions to cross domain problems
H3C HCl MPLS layer 2 dedicated line experiment
千人规模互联网公司研发效能成功之路
Idea 2021 Chinese garbled code
The IDM server response shows that you do not have permission to download the solution tutorial
关于 Web Content-Security-Policy Directive 通过 meta 元素指定的一些测试用例
In the small skin panel, use CMD to enter the MySQL command, including the MySQL error unknown variable 'secure_ file_ Priv 'solution (super detailed)
IPv6 experiment
(待会删)yyds,付费搞来的学术资源,请低调使用!
随机推荐
What are the technical differences in source code anti disclosure
[pytorch practice] image description -- let neural network read pictures and tell stories
EPP+DIS学习之路(2)——Blink!闪烁!
An error occurred when vscade tried to create a file in the target directory: access denied [resolved]
Sonar:cognitive complexity
Up meta - Web3.0 world innovative meta universe financial agreement
Upgrade from a tool to a solution, and the new site with praise points to new value
The left-hand side of an assignment expression may not be an optional property access. ts(2779)
Vxlan 静态集中网关
College entrance examination composition, high-frequency mention of science and Technology
Typescript interface inheritance
SQL Lab (36~40) includes stack injection, MySQL_ real_ escape_ The difference between string and addslashes (continuous update after)
(待会删)yyds,付费搞来的学术资源,请低调使用!
zero-shot, one-shot和few-shot
ps链接图层的使用方法和快捷键,ps图层链接怎么做的
SQL lab 21~25 summary (subsequent continuous update) (including secondary injection explanation)
金融数据获取(三)当爬虫遇上要鼠标滚轮滚动才会刷新数据的网页(保姆级教程)
Configure an encrypted web server
MPLS experiment
数据库系统原理与应用教程(009)—— 概念模型与数据模型