用串口读一块电表,报文发过去,设备回了一帧数据,末尾跟着两个字节,自己算的值怎么都对不上——很多第一次做单片机通信的人卡在这一步。这两个字节多半就是 crc16 校验码。

crc16 到底在算什么

CRC16 是循环冗余校验的一种:把整帧数据当成一长串二进制,除以一个双方约定的 16 位多项式,余数就是校验值。发送方算完附在帧尾,接收方按同样的规则再算一遍,两边一致才认为这一帧没传错。它能发现绝大多数位翻转和错位,但只能检错不能纠错,也防不了人为篡改。

同一段数据,为什么各家的结果不一样

CRC16 只约定了「16 位」这个宽度,多项式、初始值、结果异或、字节序都可以变,于是出现了一堆变体:

  • CRC-16/MODBUS:多项式 0x8005,初始值 0xFFFF,结果低字节在前,Modbus RTU 协议用它。
  • CRC-16/CCITT-FALSE:多项式 0x1021,初始值 0xFFFF。
  • CRC-16/XMODEM:多项式 0x1021,初始值 0x0000。

数据相同、参数不同,结果完全不同。调试时对不上号,先逐项核对这几个参数,十有八九是字节序反了。少数协议还会裁剪校验范围,比如帧头不参与计算,文档里一般有写明。

工程上怎么实现

两种路子:逐位计算法代码短,一位一位移位异或,慢但省资源;查表法预先把 256 个字节对应的表存进程序存储区,每个字节一次查表加异或,速度快,单片机和上位机里用得最多。网上也有在线 CRC 计算工具,贴上十六进制数据选好变体就能核对。

动手前先拿协议文档里给的示例报文算一遍,结果一致再往自己的程序里搬,能省掉大半调试时间。

实际判断:接新协议先翻文档,看写明的是哪种 CRC 变体,参数一个都不能差;文档没写就抓一帧真实数据反推字节序,比闷头改代码快得多。