第49讲:通信协议Spec——帧头、帧尾、CRC、应答机制 CSDN专栏嵌入式程序开发实战嵌入式双范式AI编程嵌入式开发必掌握嵌入式求职面试技术资料第49讲通信协议Spec——帧头、帧尾、CRC、应答机制一、通信协议Spec的重要性通信协议Spec定义了设备间通信的完整规则是通信可靠性的基础保证。1.1 为什么需要通信协议Spec原因一通信不可靠通信不可靠因素数据丢失传输过程中数据丢失数据错误干扰导致数据错误数据乱序数据包顺序错乱数据重复数据包重复接收必须定义完整协议才能保证通信可靠原因二协议不兼容协议不兼容问题帧格式不同无法解析字节序不同数据错误校验方式不同校验失败应答机制不同通信失败通信双方必须使用相同协议原因三协议错误难以定位协议错误特点隐蔽性强编译通过运行错误影响范围大可能影响整个通信系统定位困难需要逐字节对比分析修复复杂可能涉及多个模块协议Spec明确约束减少错误1.2 通信协议Spec的组成通信协议Spec帧格式定义帧头帧体帧尾帧长度校验机制校验算法校验范围校验位置应答机制应答类型应答格式超时重传重传次数流控机制缓冲区管理流控策略背压机制二、帧格式定义Spec2.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帧格式## 帧格式定义SpecModbus RTU ### 2.1 帧结构 - DES-001Modbus RTU帧结构 - 帧头无使用帧间间隔识别 - 帧体从站地址 功能码 数据 - 帧尾CRC校验 - 帧间间隔3.5个字符时间 ### 2.2 请求帧格式 - DES-002请求帧字节0 从站地址1字节字节1 功能码1字节字节2~N1 数据域N字节字节N2 CRC低字节字节N3 CRC高字节总长度 N4字节### 2.3 响应帧格式 - DES-003正常响应帧字节0 从站地址1字节字节1 功能码1字节字节2~N1 数据域N字节字节N2 CRC低字节字节N3 CRC高字节总长度 N4字节- DES-004异常响应帧字节0 从站地址1字节字节1 功能码0x801字节字节2 异常码1字节字节3 CRC低字节字节4 CRC高字节总长度 5字节### 2.4 帧间间隔 - DES-005帧间间隔 - 最小间隔3.5个字符时间 - 字符间隔≤1.5个字符时间 - 计算方法 - 波特率96001个字符时间≈1.04ms帧间间隔≈3.6ms - 波特率192001个字符时间≈0.52ms帧间间隔≈1.8ms - 波特率1152001个字符时间≈0.087ms帧间间隔≈0.3ms示例二自定义协议帧格式## 帧格式定义Spec自定义通信协议 ### 2.1 帧结构 - DES-001自定义协议帧结构 - 帧头0x55AA2字节 - 帧体帧类型长度数据校验 - 帧尾0xAA552字节 ### 2.2 完整帧格式 - DES-002帧格式字节0-1 帧头0x55AA字节2 帧类型字节3 数据长度N字节4~N3 数据域N字节字节N4 校验和字节N5~N6帧尾0xAA55总长度 N7字节### 2.3 帧类型定义 - DES-003帧类型 - 0x01心跳帧 - 0x02数据帧 - 0x03命令帧 - 0x04应答帧 - 0x05错误帧 ### 2.4 各类型帧示例 #### 2.4.1 心跳帧 - DES-004心跳帧字节00x55帧头高字节字节10xAA帧头低字节字节20x01帧类型心跳字节30x00数据长度0字节40x01校验和字节50xAA帧尾高字节字节60x55帧尾低字节总长度7字节#### 2.4.2 数据帧 - DES-005数据帧数据0x12, 0x34, 0x56字节00x55帧头字节10xAA字节20x02帧类型数据字节30x03数据长度3字节40x12数据1字节50x34数据2字节60x56数据3字节70x5F校验和0x020x030x120x340x560xA1取反0x5E…字节80xAA帧尾字节90x55总长度10字节### 2.5 校验和计算 - DES-006校验和计算 - 算法累加和取反 - 范围帧类型到数据域最后一个字节 - 公式Checksum ~(Byte[2] Byte[3] ... Byte[N3])三、校验机制Spec3.1 校验机制Spec模板## 校验机制Spec ### 3.1 校验算法 - DES-001校验算法 - 算法名称[如CRC-16] - 多项式[多项式值] - 初始值[初始值] - 输入反转[是/否] - 输出反转[是/否] - 异或值[异或值] ### 3.2 校验范围 - DES-002校验范围 - 起始位置[起始字节] - 结束位置[结束字节] - 长度[字节数] ### 3.3 校验位置 - DES-003校验位置 - 位置[字节位置] - 长度[字节数] - 字节序[大端/小端]3.2 校验机制Spec示例示例一CRC-16校验Modbus## 校验机制SpecCRC-16Modbus ### 3.1 校验算法 - DES-001CRC-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应答机制## 应答机制SpecModbus RTU ### 4.1 应答类型 - DES-001Modbus应答类型 - 类型立即应答 - 应答条件收到有效请求帧后立即应答 ### 4.2 应答格式 - DES-002正常应答格式字节0 从站地址字节1 功能码字节2~N1 数据域字节N2 CRC低字节字节N3 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~N4 应答数据N-1字节字节N5 校验和字节N6~N7帧尾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流控引脚 - RTSRequest To Send输出请求发送 - CTSClear 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流控字符 - XON0x11恢复发送 - XOFF0x13暂停发送 ### 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 完整通信协议SpecModbus RTU markdown ## 通信协议SpecModbus RTU ### 1. 帧格式定义 #### 1.1 请求帧 - DES-001请求帧格式字节0 从站地址1字节范围0x01~0xF7字节1 功能码1字节字节2~N1 数据域N字节字节N2 CRC低字节字节N3 CRC高字节#### 1.2 响应帧 - DES-002正常响应帧字节0 从站地址字节1 功能码字节2~N1 数据域字节N2 CRC低字节字节N3 CRC高字节- DES-003异常响应帧字节0 从站地址字节1 功能码0x80字节2 异常码字节3 CRC低字节字节4 CRC高字节### 2. 校验机制 #### 2.1 CRC-16校验 - DES-004CRC-16Modbus - 多项式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 记住这三句话通信协议Spec定义完整规则帧格式、校验机制、应答机制、流控机制缺一不可校验是通信可靠保证必须使用校验校验失败必须丢弃数据应答机制保证通信成功超时重传、异常应答、应答确认7.2 实践建议对于新手学习常用通信协议Modbus、自定义协议学习校验算法实现学习应答机制设计对于有经验工程师建立通信协议Spec模板库使用Spec约束协议实现使用Spec指导协议测试对于团队负责人建立通信协议Spec编写规范培训团队协议设计方法建立协议审查流程7.3 下讲预告第50讲将详细讲解低功耗固件Spec休眠条件、唤醒源、功耗约束如何定义低功耗固件Spec、降低系统功耗下一讲将详细讲解。