当前位置: 首页 > news >正文

第49讲:通信协议Spec——帧头、帧尾、CRC、应答机制

CSDN专栏:

  • 嵌入式程序开发实战
  • 嵌入式双范式AI编程
  • 嵌入式开发必掌握
  • 嵌入式求职面试技术资料

第49讲:通信协议Spec——帧头、帧尾、CRC、应答机制

一、通信协议Spec的重要性

通信协议Spec定义了设备间通信的完整规则,是通信可靠性的基础保证。

1.1 为什么需要通信协议Spec

原因一:通信不可靠

通信不可靠因素:

  • 数据丢失:传输过程中数据丢失
  • 数据错误:干扰导致数据错误
  • 数据乱序:数据包顺序错乱
  • 数据重复:数据包重复接收

必须定义完整协议,才能保证通信可靠

原因二:协议不兼容

协议不兼容问题:

  • 帧格式不同:无法解析
  • 字节序不同:数据错误
  • 校验方式不同:校验失败
  • 应答机制不同:通信失败

通信双方必须使用相同协议

原因三:协议错误难以定位

协议错误特点:

  • 隐蔽性强:编译通过,运行错误
  • 影响范围大:可能影响整个通信系统
  • 定位困难:需要逐字节对比分析
  • 修复复杂:可能涉及多个模块

协议Spec明确约束,减少错误

1.2 通信协议Spec的组成

通信协议Spec

帧格式定义

帧头

帧体

帧尾

帧长度

校验机制

校验算法

校验范围

校验位置

应答机制

应答类型

应答格式

超时重传

重传次数

流控机制

缓冲区管理

流控策略

背压机制

二、帧格式定义Spec

2.1 帧格式定义Spec模板

## 帧格式定义Spec ### 2.1 帧头定义 - DES-001:帧头 - 起始标志:[如0x55AA] - 长度:[字节数] - 位置:[字节位置] - 作用:[帧起始识别] ### 2.2 帧体定义 - DES-002:帧体 - 结构:[字段列表] - 长度:[固定长度/可变长度] - 位置:[字节位置] ### 2.3 帧尾定义 - DES-003:帧尾 - 结束标志:[如0xAA55] - 长度:[字节数] - 位置:[字节位置] - 作用:[帧结束识别] ### 2.4 完整帧格式 - DES-004:帧格式图

[帧头][帧体][帧尾]

2.2 帧格式定义Spec示例

示例一:Modbus RTU帧格式

## 帧格式定义Spec:Modbus RTU ### 2.1 帧结构 - DES-001:Modbus RTU帧结构 - 帧头:无(使用帧间间隔识别) - 帧体:从站地址 + 功能码 + 数据 - 帧尾:CRC校验 - 帧间间隔:3.5个字符时间 ### 2.2 请求帧格式 - DES-002:请求帧

字节0: 从站地址(1字节)
字节1: 功能码(1字节)
字节2~N+1: 数据域(N字节)
字节N+2: CRC低字节
字节N+3: CRC高字节
总长度: N+4字节

### 2.3 响应帧格式 - DES-003:正常响应帧

字节0: 从站地址(1字节)
字节1: 功能码(1字节)
字节2~N+1: 数据域(N字节)
字节N+2: CRC低字节
字节N+3: CRC高字节
总长度: N+4字节

- DES-004:异常响应帧

字节0: 从站地址(1字节)
字节1: 功能码+0x80(1字节)
字节2: 异常码(1字节)
字节3: CRC低字节
字节4: CRC高字节
总长度: 5字节

### 2.4 帧间间隔 - DES-005:帧间间隔 - 最小间隔:3.5个字符时间 - 字符间隔:≤1.5个字符时间 - 计算方法: - 波特率9600:1个字符时间≈1.04ms,帧间间隔≈3.6ms - 波特率19200:1个字符时间≈0.52ms,帧间间隔≈1.8ms - 波特率115200:1个字符时间≈0.087ms,帧间间隔≈0.3ms

示例二:自定义协议帧格式

## 帧格式定义Spec:自定义通信协议 ### 2.1 帧结构 - DES-001:自定义协议帧结构 - 帧头:0x55AA(2字节) - 帧体:帧类型+长度+数据+校验 - 帧尾:0xAA55(2字节) ### 2.2 完整帧格式 - DES-002:帧格式

字节0-1: 帧头(0x55AA)
字节2: 帧类型
字节3: 数据长度(N)
字节4~N+3: 数据域(N字节)
字节N+4: 校验和
字节N+5~N+6:帧尾(0xAA55)
总长度: N+7字节

### 2.3 帧类型定义 - DES-003:帧类型 - 0x01:心跳帧 - 0x02:数据帧 - 0x03:命令帧 - 0x04:应答帧 - 0x05:错误帧 ### 2.4 各类型帧示例 #### 2.4.1 心跳帧 - DES-004:心跳帧

字节0:0x55(帧头高字节)
字节1:0xAA(帧头低字节)
字节2:0x01(帧类型:心跳)
字节3:0x00(数据长度:0)
字节4:0x01(校验和)
字节5:0xAA(帧尾高字节)
字节6:0x55(帧尾低字节)
总长度:7字节

#### 2.4.2 数据帧 - DES-005:数据帧(数据:0x12, 0x34, 0x56)

字节0:0x55(帧头)
字节1:0xAA
字节2:0x02(帧类型:数据)
字节3:0x03(数据长度:3)
字节4:0x12(数据1)
字节5:0x34(数据2)
字节6:0x56(数据3)
字节7:0x5F(校验和:0x02+0x03+0x12+0x34+0x56=0xA1,取反=0x5E…)
字节8:0xAA(帧尾)
字节9:0x55
总长度:10字节

### 2.5 校验和计算 - DES-006:校验和计算 - 算法:累加和取反 - 范围:帧类型到数据域最后一个字节 - 公式:Checksum = ~(Byte[2] + Byte[3] + ... + Byte[N+3])

三、校验机制Spec

3.1 校验机制Spec模板

## 校验机制Spec ### 3.1 校验算法 - DES-001:校验算法 - 算法名称:[如CRC-16] - 多项式:[多项式值] - 初始值:[初始值] - 输入反转:[是/否] - 输出反转:[是/否] - 异或值:[异或值] ### 3.2 校验范围 - DES-002:校验范围 - 起始位置:[起始字节] - 结束位置:[结束字节] - 长度:[字节数] ### 3.3 校验位置 - DES-003:校验位置 - 位置:[字节位置] - 长度:[字节数] - 字节序:[大端/小端]

3.2 校验机制Spec示例

示例一:CRC-16校验(Modbus)

## 校验机制Spec:CRC-16(Modbus) ### 3.1 校验算法 - DES-001:CRC-16算法 - 算法名称:CRC-16-Modbus - 多项式:0x8005(标准),0xA001(反转) - 初始值:0xFFFF - 输入反转:是 - 输出反转:是 - 异或值:0x0000 ### 3.2 校验范围 - DES-002:校验范围 - 起始位置:从站地址(字节0) - 结束位置:数据域最后一个字节 - 长度:帧长度-2字节(减去CRC) ### 3.3 校验位置 - DES-003:校验位置 - 位置:帧最后两个字节 - 长度:2字节 - 字节序:小端序(低字节在前) ### 3.4 实现代码 ```c uint16_t CRC16_Modbus(uint8_t *data, uint16_t len) { uint16_t crc = 0xFFFF; for (uint16_t i = 0; i < len; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x0001) { crc = (crc >> 1) ^ 0xA001; } else { crc >>= 1; } } } return crc; } // 计算并添加CRC void AddCRC16(uint8_t *frame, uint16_t len) { uint16_t crc = CRC16_Modbus(frame, len); frame[len] = crc & 0xFF; // CRC低字节 frame[len + 1] = (crc >> 8); // CRC高字节 } // 校验CRC int8_t CheckCRC16(uint8_t *frame, uint16_t len) { uint16_t crc_calc = CRC16_Modbus(frame, len - 2); uint16_t crc_recv = frame[len - 2] | (frame[len - 1] << 8); return (crc_calc == crc_recv) ? 0 : -1; }
**示例二:累加和校验** ```markdown ## 校验机制Spec:累加和校验 ### 3.1 校验算法 - DES-001:累加和算法 - 算法名称:Checksum-8 - 计算方法:累加所有字节,取低字节 - 初始值:0x00 ### 3.2 校验范围 - DES-002:校验范围 - 起始位置:帧类型(字节2) - 结束位置:数据域最后一个字节 - 长度:数据长度+2字节 ### 3.3 校验位置 - DES-003:校验位置 - 位置:数据域后第一个字节 - 长度:1字节 ### 3.4 实现代码 ```c uint8_t Checksum8(uint8_t *data, uint16_t len) { uint8_t sum = 0; for (uint16_t i = 0; i < len; i++) { sum += data[i]; } return sum; } // 计算并添加校验和 void AddChecksum(uint8_t *frame, uint16_t data_len) { uint8_t checksum = Checksum8(&frame[2], data_len + 2); frame[4 + data_len] = checksum; } // 校验校验和 int8_t CheckChecksum(uint8_t *frame, uint16_t frame_len) { uint8_t checksum_calc = Checksum8(&frame[2], frame_len - 7); uint8_t checksum_recv = frame[frame_len - 3]; return (checksum_calc == checksum_recv) ? 0 : -1; }
## 四、应答机制Spec ### 4.1 应答机制Spec模板 ```markdown ## 应答机制Spec ### 4.1 应答类型 - DES-001:应答类型 - 类型:[无应答/立即应答/延迟应答] - 应答条件:[应答触发条件] ### 4.2 应答格式 - DES-002:应答格式 - 格式定义:[应答帧格式] - 应答内容:[应答数据] ### 4.3 超时重传 - DES-003:超时重传 - 超时时间:[时间] - 重传次数:[次数] - 重传间隔:[间隔] ### 4.4 应答确认 - DES-004:应答确认 - 确认方式:[ACK/NACK/无确认] - 确认超时:[超时时间]

4.2 应答机制Spec示例

示例一:Modbus应答机制

## 应答机制Spec:Modbus RTU ### 4.1 应答类型 - DES-001:Modbus应答类型 - 类型:立即应答 - 应答条件:收到有效请求帧后立即应答 ### 4.2 应答格式 - DES-002:正常应答格式

字节0: 从站地址
字节1: 功能码
字节2~N+1: 数据域
字节N+2: CRC低字节
字节N+3: CRC高字节

- DES-003:异常应答格式

字节0: 从站地址
字节1: 功能码+0x80
字节2: 异常码
字节3: CRC低字节
字节4: CRC高字节

### 4.3 异常码定义 - DES-004:异常码 - 0x01:非法功能码 - 0x02:非法数据地址 - 0x03:非法数据值 - 0x04:从站设备故障 ### 4.4 超时重传 - DES-005:超时重传 - 超时时间:100ms(典型值) - 重传次数:3次 - 重传间隔:100ms - 说明:主站未收到应答时重传 ### 4.5 实现代码 ```c // 主站发送请求并等待应答 int8_t Modbus_SendRequest(uint8_t slave_addr, uint8_t func_code, uint8_t *data, uint16_t len, uint8_t *response, uint16_t *resp_len) { uint8_t retry_count = 0; int8_t ret; retry: // 构建请求帧 uint8_t request[256]; request[0] = slave_addr; request[1] = func_code; memcpy(&request[2], data, len); AddCRC16(request, len + 2); // 发送请求 UART_Send(request, len + 4); // 等待应答 ret = UART_Receive(response, resp_len, 100); // 100ms超时 if (ret != 0) { // 超时,重传 if (retry_count < 3) { retry_count++; Delay_ms(100); goto retry; } return -2; // 通信失败 } // 校验CRC if (CheckCRC16(response, *resp_len) != 0) { if (retry_count < 3) { retry_count++; Delay_ms(100); goto retry; } return -3; // 校验失败 } // 检查异常应答 if (response[1] & 0x80) { return -4; // 异常应答 } return 0; // 成功 }
**示例二:自定义协议应答机制** ```markdown ## 应答机制Spec:自定义协议 ### 4.1 应答类型 - DES-001:应答类型 - 类型:立即应答 - 应答条件:收到命令帧后立即应答 ### 4.2 应答格式 - DES-002:应答帧格式

字节0-1: 帧头(0x55AA)
字节2: 帧类型(0x04:应答)
字节3: 数据长度(N)
字节4: 应答结果(0x00=成功,其他=失败)
字节5~N+4: 应答数据(N-1字节)
字节N+5: 校验和
字节N+6~N+7:帧尾(0xAA55)

### 4.3 超时重传 - DES-003:超时重传 - 超时时间:500ms - 重传次数:3次 - 重传间隔:100ms ### 4.4 应答确认 - DES-004:应答确认 - 确认方式:检查应答帧中的应答结果字段 - 成功:应答结果=0x00 - 失败:应答结果≠0x00 ### 4.5 实现代码 ```c // 发送命令并等待应答 int8_t SendCommandAndWait(uint8_t cmd_type, uint8_t *data, uint16_t len, uint8_t *result) { uint8_t retry_count = 0; int8_t ret; retry: // 构建命令帧 uint8_t frame[256]; frame[0] = 0x55; frame[1] = 0xAA; frame[2] = cmd_type; frame[3] = len; memcpy(&frame[4], data, len); AddChecksum(frame, len); frame[5 + len] = 0xAA; frame[6 + len] = 0x55; // 发送命令 UART_Send(frame, 7 + len); // 等待应答 uint8_t response[256]; uint16_t resp_len; ret = UART_Receive(response, &resp_len, 500); // 500ms超时 if (ret != 0) { if (retry_count < 3) { retry_count++; Delay_ms(100); goto retry; } return -2; // 超时 } // 校验帧 if (CheckFrame(response, resp_len) != 0) { if (retry_count < 3) { retry_count++; Delay_ms(100); goto retry; } return -3; // 校验失败 } // 检查应答结果 if (response[4] != 0x00) { *result = response[4]; return -4; // 命令失败 } return 0; // 成功 }
## 五、流控机制Spec ### 5.1 流控机制Spec模板 ```markdown ## 流控机制Spec ### 5.1 缓冲区管理 - DES-001:缓冲区配置 - 发送缓冲区大小:[大小] - 接收缓冲区大小:[大小] - 缓冲区类型:[环形缓冲区/线性缓冲区] ### 5.2 流控策略 - DES-002:流控策略 - 策略类型:[硬件流控/软件流控/无流控] - 实现方式:[RTS/CTS/XON/XOFF] ### 5.3 背压机制 - DES-003:背压机制 - 触发条件:[缓冲区使用率>阈值] - 响应动作:[暂停接收/通知发送方] - 恢复条件:[缓冲区使用率<阈值]

5.2 流控机制Spec示例

示例一:硬件流控(RTS/CTS)

## 流控机制Spec:硬件流控(RTS/CTS) ### 5.1 硬件流控引脚 - DES-001:流控引脚 - RTS(Request To Send):输出,请求发送 - CTS(Clear To Send):输入,允许发送 ### 5.2 流控逻辑 - DES-002:发送流控 - 发送前:检查CTS引脚 - CTS=高电平:允许发送 - CTS=低电平:暂停发送,等待CTS变高 - DES-003:接收流控 - 缓冲区快满时:RTS输出低电平,通知对方暂停发送 - 缓冲区有空间时:RTS输出高电平,允许对方发送 ### 5.3 实现代码 ```c // 发送数据(带流控) int8_t UART_SendWithFlowControl(uint8_t *data, uint16_t len) { for (uint16_t i = 0; i < len; i++) { // 等待CTS有效 while (GPIO_Read(CTS_PORT, CTS_PIN) == 0) { // CTS无效,等待 Delay_us(100); } // 发送字节 UART_SendByte(data[i]); } return 0; } // 接收中断(带流控) void UART_Rx_IRQHandler(void) { uint8_t data = UART_ReceiveByte(); // 写入缓冲区 if (Buffer_Write(&rx_buffer, data) != 0) { // 缓冲区快满,RTS输出低电平 GPIO_Write(RTS_PORT, RTS_PIN, 0); } // 检查缓冲区空间 if (Buffer_FreeSpace(&rx_buffer) > BUFFER_THRESHOLD) { // 缓冲区有空间,RTS输出高电平 GPIO_Write(RTS_PORT, RTS_PIN, 1); } }
**示例二:软件流控(XON/XOFF)** ```markdown ## 流控机制Spec:软件流控(XON/XOFF) ### 5.1 流控字符 - DES-001:流控字符 - XON(0x11):恢复发送 - XOFF(0x13):暂停发送 ### 5.2 流控逻辑 - DES-002:发送流控 - 收到XOFF:暂停发送,等待XON - 收到XON:恢复发送 - DES-003:接收流控 - 缓冲区使用率>80%:发送XOFF,通知对方暂停 - 缓冲区使用率<50%:发送XON,允许对方发送 ### 5.3 实现代码 ```c #define XON 0x11 #define XOFF 0x13 volatile uint8_t tx_paused = 0; // 发送数据(带流控) int8_t UART_SendWithFlowControl(uint8_t *data, uint16_t len) { for (uint16_t i = 0; i < len; i++) { // 等待发送恢复 while (tx_paused) { Delay_us(100); } // 发送字节 UART_SendByte(data[i]); } return 0; } // 接收中断(带流控) void UART_Rx_IRQHandler(void) { uint8_t data = UART_ReceiveByte(); // 检查流控字符 if (data == XOFF) { tx_paused = 1; // 暂停发送 return; } else if (data == XON) { tx_paused = 0; // 恢复发送 return; } // 写入缓冲区 Buffer_Write(&rx_buffer, data); // 检查缓冲区使用率 float usage = (float)Buffer_Used(&rx_buffer) / rx_buffer.size; if (usage > 0.8f && !xoff_sent) { // 缓冲区快满,发送XOFF UART_SendByte(XOFF); xoff_sent = 1; } else if (usage < 0.5f && xoff_sent) { // 缓冲区有空间,发送XON UART_SendByte(XON); xoff_sent = 0; } }
## 六、通信协议Spec完整示例 ### 6.1 完整通信协议Spec:Modbus RTU ```markdown ## 通信协议Spec:Modbus RTU ### 1. 帧格式定义 #### 1.1 请求帧 - DES-001:请求帧格式

字节0: 从站地址(1字节,范围0x01~0xF7)
字节1: 功能码(1字节)
字节2~N+1: 数据域(N字节)
字节N+2: CRC低字节
字节N+3: CRC高字节

#### 1.2 响应帧 - DES-002:正常响应帧

字节0: 从站地址
字节1: 功能码
字节2~N+1: 数据域
字节N+2: CRC低字节
字节N+3: CRC高字节

- DES-003:异常响应帧

字节0: 从站地址
字节1: 功能码+0x80
字节2: 异常码
字节3: CRC低字节
字节4: CRC高字节

### 2. 校验机制 #### 2.1 CRC-16校验 - DES-004:CRC-16(Modbus) - 多项式:0xA001(反转) - 初始值:0xFFFF - 校验范围:从站地址到数据域最后一个字节 - 校验位置:帧最后两个字节 - 字节序:小端序 ### 3. 应答机制 #### 3.1 超时重传 - DES-005:超时重传 - 超时时间:100ms - 重传次数:3次 - 重传间隔:100ms #### 3.2 异常码 - DES-006:异常码定义 - 0x01:非法功能码 - 0x02:非法数据地址 - 0x03:非法数据值 - 0x04:从站设备故障 ### 4. 流控机制 #### 4.1 帧间间隔 - DES-007:帧间间隔 - 最小间隔:3.5个字符时间 - 字符间隔:≤1.5个字符时间 ### 5. 功能码定义 #### 5.1 常用功能码 - DES-008:功能码 - 0x03:读保持寄存器 - 0x06:写单个寄存器 - 0x10:写多个寄存器

七、本讲核心要点

7.1 记住这三句话

  1. 通信协议Spec定义完整规则:帧格式、校验机制、应答机制、流控机制,缺一不可

  2. 校验是通信可靠保证:必须使用校验,校验失败必须丢弃数据

  3. 应答机制保证通信成功:超时重传、异常应答、应答确认

7.2 实践建议

对于新手

  • 学习常用通信协议(Modbus、自定义协议)
  • 学习校验算法实现
  • 学习应答机制设计

对于有经验工程师

  • 建立通信协议Spec模板库
  • 使用Spec约束协议实现
  • 使用Spec指导协议测试

对于团队负责人

  • 建立通信协议Spec编写规范
  • 培训团队协议设计方法
  • 建立协议审查流程

7.3 下讲预告

第50讲将详细讲解:低功耗固件Spec:休眠条件、唤醒源、功耗约束

如何定义低功耗固件Spec、降低系统功耗?下一讲将详细讲解。

http://www.jsqmd.com/news/1295933/

相关文章:

  • EDA软件-PCB智能体自动布线
  • 2026年想在南京或苏州就业,选本地IT培训机构还是全国品牌? - 甄选测评馆
  • [ 经验 ] XTM2600 100V三相栅极驱动器深度分析
  • generator-core高级功能:自定义菜单与用户交互实现
  • 用这3招,小白也能造出高通过率的“黄金商标名称”
  • shadcn-docs-nuxt性能优化指南:提升文档加载速度的7个实用技巧
  • ncmdump终极解密指南:3分钟快速解锁网易云音乐NCM格式
  • AI Agent技术解析:从原理到实战应用
  • Polymarket Copy Trading Bot未来路线图:即将推出的新功能预览
  • Codex 做视频完全指南:FFmpeg 剪辑 + Remotion 动效 + ComfyUI 生成,三条路线选哪条
  • LaTeX公式转图片:5分钟快速上手的终极转换指南
  • 带着宠物移居葡萄牙:brasil-portugal项目动物入境文件清单
  • 技术优雅:“三无刷”疯狂电路车模
  • 2026年AI降重工具实测:8款主流平台对比与学术写作指南
  • 2026年7月经验丰富的北京宠物店推荐榜单一览+FAQ答疑 - 互联网科技品牌测评
  • BERT4Rec数据处理实战:从原始数据到TFRecord的高效转换
  • 如何用modern-screenshot实现高质量DOM截图:5个实用技巧
  • 3分钟掌握大麦抢票神器:Python自动化抢票终极解决方案
  • 一文彻底搞懂RAID:RAID 0/1/5/6/10原理、选型、Foreign故障排查与Linux实战
  • 揭秘TLS安全测试的终极武器:tlsfuzzer深度实战指南
  • Midscene.js:如何用视觉AI解决跨平台自动化测试的三大技术挑战
  • 动态系统故障诊断与容错控制的Matlab实现
  • CSS specificity分析:用analyze-css解决样式优先级冲突
  • WorkshopDL:解决Steam创意工坊跨平台下载难题的最佳方案
  • Open-Lyrics:AI智能音频转歌词工具完整指南
  • 工作_R_备忘
  • Android应用崩溃防护:Countly SDK异常处理与日志分析指南
  • AI系统提示词泄露项目完全指南:揭秘主流AI模型的内部操作手册
  • 解密Awesome Claude Skills:如何用1000+技能让AI助手真正为您工作
  • 3.6亿文献库助力学术检索与知识获取 打造海量专业文献查询支撑平台