文章目录
- 一、它演示什么
- 二、怎么用
- 三、一屏总览
- 三种校验方式对比
- 四、关键接口
- 五、容易踩的坑
- 六、跑起来该看到什么
- 七、什么时候用这个示例
- 八、下一步可以怎么玩
一、它演示什么
examples\modbus只讲 Modbus 这一种既有协议,「自己定一种帧、自己切帧与校验」这条路本示例来补。帧格式简单到一眼能懂:
[0xAA 0x55][长度 L][命令 cmd][负载 N 字节][校验 1 字节] L = 校验前的字节数 = 4 + N,整帧 = L + 1帧结构示意(字节偏移从 0 开始):
偏移 0 1 2 3 4 ... 4+N-1 4+N +------+------+------+------+------+------+------+------+ | 0xAA | 0x55 | L | cmd | 负载[0] ... 负载[N-1] | 校验 | +------+------+------+------+------+------+------+------+ \____/ \____/ \____/ \____/ \____________________/ \____/ 帧头 帧头 长度 命令 负载区(N 字节) 校验 1 字节 1 字节 1 字节 1 字节 1 字节 长度 L = 4 + N(校验前的字节数:帧头 2 + 命令 1 + 负载 N) 整帧 = L + 1(再算上校验字节)校验三选一(累加和低 8 位 / XOR / CRC16-Modbus),字节序两种可切(小端 / 大端)。数据有三种合成来源:粘贴十六进制、「模拟一帧」、「模拟连发」(每 300ms 造一批,故意夹带噪声与断帧)。
下面是本示例的帧解析流程:
二、怎么用
- 在十六进制输入框
edt_hex里粘贴一帧(预填AA 55 06 01 0A 0B 1B),或用「模拟一帧」按当前校验 / 命令 / 负载拼一帧填回输入框(只填不发)。 - 点「喂入输入的十六进制」:解析成字节喂进切帧器(空格 / 换行 / 逗号都当分隔符)。
- 合法帧进帧表
tbl_frames(含解码结果);校验错的帧也进表,结论列写清算出多少、帧里多少。 - 点「模拟连发」用定时器每 300ms 造一批(噪声 + 一帧合法帧 + 一段断帧),演示切帧健壮性。
- 点「清空 / 重置」丢掉半帧、清空统计 / 帧表 / 日志;中间分隔条调左右宽度。
- 命令行:
framedec.exe(自动找同名.cgui)、framedec.exe xxx.cgui、--selftest。
整体操作流程如下:
三、一屏总览
| 控件 id | 类型 | 干什么 |
|---|---|---|
spn_cmd | edit(spin) | 命令字节(01 温度 / 02 量程 / 03 电压组) |
edt_pay | edit | 负载 HEX(如0A 0B) |
cmb_sum | combo | 校验类型(累加和 / XOR / CRC16-Modbus) |
cmb_end | combo | 字节序(小端 / 大端) |
btn_mock/btn_burst | button | 模拟一帧 / 模拟连发 |
btn_feed/btn_reset | button | 喂入输入的十六进制 / 清空重置 |
edt_hex | edit | 十六进制输入 |
tbl_frames | table | 切出的帧表(含解码结论) |
term_log | terminal | 日志 |
sb_main | statusbar | 收字节 / 成帧 / 校验错 / 残帧 / 丢字节 |
三种校验方式对比
| 校验方式 | 计算复杂度 | 检错能力 | 适用场景 | 本示例推荐 |
|---|---|---|---|---|
| 累加和(低 8 位) | 最低:逐字节相加取低 8 位,O(N) 一次遍历 | 最弱:只能检出奇数个位翻转;字节顺序调换、偶数个位翻转会漏检 | 对可靠性要求不高、追求极简与速度的场合(如玩具协议、调试期快速验证) | 不推荐用于噪声场景:模拟连发夹带噪声时漏检概率最高 |
| XOR | 低:逐字节异或,O(N) 一次遍历 | 较弱:能检出奇数个位翻转,对字节顺序不敏感;但偶数个位翻转同样漏检 | 资源受限的嵌入式设备、需要极低开销的场合 | 比累加和略好,但噪声下仍不够稳,适合轻量场景 |
| CRC16-Modbus | 较高:查表法 O(N),需维护 16 位状态与多项式 | 强:能检出所有 1~2 位错误、奇数个位错误、长度 ≤16 的突发错误,以及绝大多数更长突发错误 | 工业总线、串口通信、对数据完整性要求高的正式协议 | 推荐:模拟连发噪声场景下漏检率最低,落地最稳 |
结论:本示例的「模拟连发」会故意夹带噪声与断帧,正是为了压测切帧器对脏数据的容忍度。在这种场景下,累加和与 XOR 的漏检率明显偏高,容易把被噪声污染的帧误判为「校验 OK」;而 CRC16-Modbus 的检错能力最强,能最大程度避免「坏帧被当成好帧」的误判。因此推荐默认选 CRC16-Modbus,把累加和 / XOR 留给对开销敏感、对可靠性要求不高的轻量场景。
四、关键接口
cg_crc16算 CRC16-Modbus;cg_hex_decode/cg_hex_encode互转;cg_bytes_get_u16取 16 位、cg_to_signed转有符号;cg_set_timer/cg_kill_timer管连发;cg_table_add_column/cg_table_add_row/cg_table_row_count/cg_table_clear建表与限行;cg_term_write写日志。
五、容易踩的坑
六、跑起来该看到什么
- 喂入
AA 55 06 01 0A 0B 1B(累加和、小端):帧表出现 1 行,结论列写「校验 OK,算出 0x1B / 帧里 0x1B」。 - 把负载改成
0A 0B但把校验字节改成00:帧表仍出现 1 行,但结论列写「校验错,算出 0x1B / 帧里 0x00」——不静默丢弃,一眼看出哪对不上。 - 点「模拟连发」:日志按 300ms 节奏刷「收 N 字节 / 成帧 M / 校验错 K / 残帧 R / 丢字节 D」,帧表持续增长,断帧被正确丢弃或拼接。
- 点「清空 / 重置」:帧表、统计、日志全部归零,半帧状态被丢弃。
七、什么时候用这个示例
- 你手头有一套私有协议,帧格式不是 Modbus / CAN / 常见标准,想先验证「切帧 + 校验」逻辑再写正式代码。
- 你要给串口 / 网络调试工具加一个「自定义帧」输入口,需要先确认切帧器对噪声、断帧、半帧的容忍度。
- 你想对比累加和 / XOR / CRC16-Modbus三种校验在真实噪声下的表现,选一种落地。
八、下一步可以怎么玩
把「模拟连发」的噪声比例、断帧长度做成可调参数,压一压切帧器的极限。
给帧表加一列「原始字节」,方便对照解码结果与原始帧。
把
drain_stream的切帧逻辑抽成独立函数,加--selftest覆盖断帧 / 半帧 / 校验错三种用例。参考
examples\modbus的既有协议实现,把本示例的「自定义帧」接到同一套框架里对比。框架的通用切帧器与本帧布局不匹配(它的「长度字段」模式按「总长 = 长度值 + 附加量」解释,表达不了本帧「长度字节在命令之前、整帧 = 长度 + 1」),所以这里手写切帧,原因写在
drain_stream的注释里。校验错的帧也进表、结论列写清「算出多少 / 帧里多少」,不静默跳过 —— 这是通信调试最想要的那一眼。
连发定时器只在真窗口下能开(自检合成点击时若启动定时器,CI 会空转)。
帧表最多留 200 行,超了清空重来。