<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>CRC on 我的博客</title><link>https://diankao.github.io/tags/crc/</link><description>Recent content in CRC on 我的博客</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Tue, 18 Aug 2026 11:21:06 +0800</lastBuildDate><atom:link href="https://diankao.github.io/tags/crc/index.xml" rel="self" type="application/rss+xml"/><item><title>你说的 CRC 是哪个 CRC：一次上下位机联调翻车实录</title><link>https://diankao.github.io/posts/which-crc-do-you-mean/</link><pubDate>Tue, 18 Aug 2026 11:21:06 +0800</pubDate><guid>https://diankao.github.io/posts/which-crc-do-you-mean/</guid><description>&lt;p&gt;前段时间组里上下位机联调。协议文档看起来挺完整：帧头、命令字、数据、帧尾带 CRC16 校验。两边各自实现、各自自测，全都通过；一联调，各种 bug 排着队来。查了好几天，最后发现元凶平平无奇：&lt;strong&gt;两边都实现了 CRC16，但从头到尾没人问过一句——是哪个 CRC16&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;先说结论：CRC 不是&amp;quot;一个算法&amp;quot;，是一个算法族。&amp;ldquo;加个 CRC 校验&amp;quot;这句话的规格含量，跟&amp;quot;加点辣&amp;quot;差不多——都是辣，川湘黔滇各不一样。&lt;/p&gt;
&lt;h2 id="翻车姿势"&gt;翻车姿势&lt;/h2&gt;
&lt;p&gt;CRC 变体没对齐时的症状，通常是这三种：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;全帧校验失败&lt;/strong&gt;。发什么错什么，怀疑链从串口时序、大小端、接地一路排查到换线换板子，通信工程师的经典受难路线。其实链路好得很，两边算的压根不是同一个数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;为联调把校验旁路&lt;/strong&gt;。有人图快，把&amp;quot;校验失败&amp;quot;改成&amp;quot;打条日志继续跑&amp;rdquo;，业务先跑通了，恢复和补测永远排在下个迭代。校验从此形同虚设，比没有还危险——它给人安全感。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;偶发&amp;quot;漏检&amp;quot;玄学&lt;/strong&gt;。压力测试几百万帧，偶尔一两帧校验通过但数据是错的。于是开始研究 CRC 漏检率、查电磁干扰、上磁环。其实是两套算法在 65536 个值里随缘撞上了——不是漏检，是压根没检。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="crc-是一个算法族"&gt;CRC 是一个算法族&lt;/h2&gt;
&lt;p&gt;CRC 除了&amp;quot;用哪个多项式做除法&amp;quot;这个自由度，除法前后还有一串参数。业界通用的描述模型叫 Rocksoft 模型，出自 Ross Williams 1993 年那篇 A Painless Guide to CRC Error Detection Algorithms，一共五个参数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;width&lt;/strong&gt;：位宽&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;poly&lt;/strong&gt;：多项式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;init&lt;/strong&gt;：寄存器初值&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;refin / refout&lt;/strong&gt;：输入、输出是否按位反转&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;xorout&lt;/strong&gt;：结果异或值&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;ldquo;CRC16&amp;rdquo; 只锁定了第一个参数，剩下四个全开放。同一个多项式能配出多少种不同结果？看下表（check 是官方测试向量：9 字节 ASCII 字符串 &lt;code&gt;&amp;quot;123456789&amp;quot;&lt;/code&gt; 的校验值）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名字&lt;/th&gt;
&lt;th&gt;poly&lt;/th&gt;
&lt;th&gt;init&lt;/th&gt;
&lt;th&gt;refin/refout&lt;/th&gt;
&lt;th&gt;xorout&lt;/th&gt;
&lt;th&gt;check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CRC-16/XMODEM&lt;/td&gt;
&lt;td&gt;0x1021&lt;/td&gt;
&lt;td&gt;0x0000&lt;/td&gt;
&lt;td&gt;否/否&lt;/td&gt;
&lt;td&gt;0x0000&lt;/td&gt;
&lt;td&gt;0x31C3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRC-16/CCITT-FALSE&lt;/td&gt;
&lt;td&gt;0x1021&lt;/td&gt;
&lt;td&gt;0xFFFF&lt;/td&gt;
&lt;td&gt;否/否&lt;/td&gt;
&lt;td&gt;0x0000&lt;/td&gt;
&lt;td&gt;0x29B1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRC-16/KERMIT&lt;/td&gt;
&lt;td&gt;0x1021&lt;/td&gt;
&lt;td&gt;0x0000&lt;/td&gt;
&lt;td&gt;是/是&lt;/td&gt;
&lt;td&gt;0x0000&lt;/td&gt;
&lt;td&gt;0x2189&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRC-16/ARC&lt;/td&gt;
&lt;td&gt;0x8005&lt;/td&gt;
&lt;td&gt;0x0000&lt;/td&gt;
&lt;td&gt;是/是&lt;/td&gt;
&lt;td&gt;0x0000&lt;/td&gt;
&lt;td&gt;0xBB3D&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRC-16/MODBUS&lt;/td&gt;
&lt;td&gt;0x8005&lt;/td&gt;
&lt;td&gt;0xFFFF&lt;/td&gt;
&lt;td&gt;是/是&lt;/td&gt;
&lt;td&gt;0x0000&lt;/td&gt;
&lt;td&gt;0x4B37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRC-32/ISO-HDLC&lt;/td&gt;
&lt;td&gt;0x04C11DB7&lt;/td&gt;
&lt;td&gt;0xFFFFFFFF&lt;/td&gt;
&lt;td&gt;是/是&lt;/td&gt;
&lt;td&gt;0xFFFFFFFF&lt;/td&gt;
&lt;td&gt;0xCBF43926&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;前三个共享多项式 0x1021，check 是三个毫不相干的数；中间两个只差一个 init（0x0000 与 0xFFFF），结果就分道扬镳。网上抄的 crc16.c，十份里能抄出五种参数组合，而且多半不写注释。&lt;/p&gt;</description></item></channel></rss>