4. SECS 消息帧格式:Message Header、Data Block、Block Count 与长度字段解析
SECS-I 数据链路层以固定长度的 Message Header(消息头) 作为每个数据块的起始标识,其后可跟随可变长度的 Data Block(数据块)。理解帧格式是分析 CRC 校验错误与链路层异常的基础——任何字段的错位、长度不匹配或块计数异常,都会直接导致 CRC 计算范围错误或接收端解析失败。
4.1 消息帧整体结构
一个完整的 SECS-I 消息帧由以下三部分组成:
| 组成部分 | 长度(字节) | 说明 |
|---|---|---|
| Message Header | 10 | 固定长度,包含设备ID、控制位、块计数、系统字节等 |
| Data Block | 0~N | 可变长度,承载 SECS-II 消息内容(仅多块消息的后续块可省略) |
| CRC 校验码 | 2 | 覆盖 Header + Data Block 的 CRC-16 校验值 |
关键点: CRC 校验范围包括整个 Message Header 和 Data Block,但不包括 CRC 字段本身。接收端在计算 CRC 时必须使用与发送端相同的多项式(0x1021)和初始值(0xFFFF)。
4.2 Message Header 字段详解
10 字节的 Message Header 是 SECS-I 帧的核心控制结构,每个字段都有明确的语义和约束:
| 字节偏移 | 字段名称 | 长度(bit) | 说明 |
|---|---|---|---|
| 0 | Device ID(高字节) | 8 | 设备标识符高 8 位 |
| 1 | Device ID(低字节) | 8 | 设备标识符低 8 位,共 16 位设备地址 |
| 2 | Control Byte | 8 | 控制位:bit7=R-bit, bit6=W-bit, bit5~0=保留 |
| 3 | Block Count(高字节) | 8 | 消息总块数高 8 位 |
| 4 | Block Count(低字节) | 8 | 消息总块数低 8 位,共 16 位块计数 |
| 5 | System Byte(高字节) | 8 | 系统字节高 8 位 |
| 6 | System Byte(中高字节) | 8 | 系统字节中高 8 位 |
| 7 | System Byte(中低字节) | 8 | 系统字节中低 8 位 |
| 8 | System Byte(低字节) | 8 | 系统字节低 8 位,共 32 位系统 ID |
| 9 | Length Byte | 8 | Data Block 长度(字节数),0~254 有效 |
常见陷阱: Length Byte 字段值为 0 时表示该帧不包含 Data Block(仅用于多块消息的中间块或控制帧)。但 CRC 校验仍然覆盖整个 Header(10 字节),此时 Data Block 长度为 0,CRC 仅对 Header 计算。
4.3 Control Byte 与 R/W 位
Control Byte 的第 7 位(R-bit)和第 6 位(W-bit)决定了消息的传输方向与应答要求:
- R-bit(Reply Expected):置 1 表示发送方期望接收方回复确认消息;置 0 表示无需回复。
- W-bit(Wait):置 1 表示发送方在收到回复前将保持等待状态;置 0 表示不等待。
R-bit 与 W-bit 的组合定义了四种消息类型:
| R-bit | W-bit | 消息类型 | 典型场景 |
|---|---|---|---|
| 0 | 0 | 无回复、不等待 | 单向通知、心跳 |
| 0 | 1 | 无回复、但等待 | 较少使用 |
| 1 | 0 | 期望回复、不等待 | 查询请求 |
| 1 | 1 | 期望回复、等待 | 标准事务消息 |
4.4 Block Count 与多块消息
Block Count 字段(2 字节,16 位无符号整数)表示整个消息被分割成的总块数。对于单块消息,Block Count = 1;对于多块消息,Block Count > 1。
多块消息的传输规则:
- 第一块:必须包含完整的 Message Header 和 Data Block(Data Block 包含 SECS-II 消息头)。
- 中间块:仅包含 Message Header,Data Block 长度为 0(Length Byte = 0)。
- 最后一块:包含 Message Header 和剩余的 Data Block 数据。
调试技巧: 当接收端检测到 Block Count 与已接收块数不一致时,应丢弃整个消息并触发链路层重传。CRC 校验错误常伴随块计数异常——例如中间块误传了非零 Data Block,导致 CRC 计算范围错误。
4.5 Length Byte 与 Data Block 长度约束
Length Byte 位于 Message Header 的第 9 字节,其值表示紧随 Header 之后的 Data Block 的字节数。约束规则如下:
- 取值范围:0 ~ 254(0x00 ~ 0xFE)。
- 值 255(0xFF):保留值,不可用于正常消息。
- 值 0:表示无 Data Block,常见于多块消息的中间块或控制帧。
- 值 1 ~ 254:表示 Data Block 的实际长度。
CRC 校验与 Length Byte 的关联: 接收端首先读取 Length Byte 确定 Data Block 长度,然后从 Header 起始位置开始,读取 (10 + Length) 字节作为 CRC 校验的输入数据。如果 Length Byte 因传输错误被篡改,接收端将使用错误的长度计算 CRC,导致校验失败。这是数据链路层最常见的 CRC 错误根源之一。
4.6 帧格式与 CRC 校验的关联分析
理解帧格式后,可以归纳出以下与 CRC 校验错误直接相关的异常场景:
| 异常场景 | 帧格式表现 | CRC 校验结果 |
|---|---|---|
| Length Byte 被篡改 | Data Block 长度与真实数据不匹配 | CRC 计算范围错误,校验失败 |
| Block Count 不一致 | 多块消息的块数声明与实际不符 | 接收端重组时 CRC 跨块校验失败 |
| Control Byte 位翻转 | R/W 位错误导致消息类型误判 | CRC 校验通过但协议层异常 |
| System Byte 重复 | 系统 ID 冲突导致消息混淆 | CRC 校验通过但事务层异常 |
| Header 长度偏移 | 接收端未按 10 字节解析 Header | CRC 计算起点错误,校验失败 |
工程建议: 在实现 SECS-I 驱动时,应优先验证 Length Byte 的合法性(0~254),再计算 CRC。如果 Length Byte 超出范围,应直接丢弃帧并记录链路层错误,避免无效的 CRC 计算消耗 CPU 资源。
4.7 代码示例:解析 Message Header 并验证长度
以下 C 语言代码演示如何从原始字节流中解析 SECS-I Message Header,并验证 Length Byte 的合法性:
#include <stdint.h>
#include <stdbool.h>
#define SECS_HEADER_LEN 10
#define MAX_DATA_LEN 254
typedef struct {
uint16_t device_id;
uint8_t control_byte;
uint16_t block_count;
uint32_t system_bytes;
uint8_t length_byte;
} SecsMessageHeader;
bool parse_secs_header(const uint8_t *buffer, SecsMessageHeader *header) {
if (buffer == NULL || header == NULL) {
return false;
}
// 解析 Device ID(大端序)
header->device_id = (buffer[0] << 8) | buffer[1];
// 解析 Control Byte
header->control_byte = buffer[2];
// 解析 Block Count(大端序)
header->block_count = (buffer[3] << 8) | buffer[4];
// 解析 System Bytes(大端序,4字节)
header->system_bytes = ((uint32_t)buffer[5] << 24) |
((uint32_t)buffer[6] << 16) |
((uint32_t)buffer[7] << 8) |
(uint32_t)buffer[8];
// 解析 Length Byte
header->length_byte = buffer[9];
// 验证 Length Byte 合法性
if (header->length_byte > MAX_DATA_LEN) {
return false; // 非法长度,触发 CRC 校验前拒绝
}
return true;
}
代码要点: 在调用 CRC 校验函数之前,先通过
parse_secs_header() 验证 Length Byte 的合法性。如果返回 false,应直接丢弃该帧并递增链路层错误计数器,避免无效的 CRC 计算。