上一篇我们用函数实现了 CRC-16 MODBUS 校验。程序能跑了,但有一个让人不太满意的地方: 每校验一个字节,都要循环 8 次。

今天我们来解决这个问题。用 查表法 ,运算速度直接快上 8 到 10 倍。

一、为什么一张表能涵盖所有运算结果

你可能会担心: CRC 的运算结果千变万化,一张小小的表,真的都能覆盖吗?会不会碰到 " 表里没有 " 的情况?

答案是: 不会。表中涵盖了所有可能的结果。

为什么?

查表法是一个字节一个字节处理数据的。每次处理,都是把 " 当前 CRC 寄存器 " 和 " 新来的一个字节 " 揉在一起,然后用揉出来的结果去查表。

关键在于: 这个 " 揉在一起 " 的结果,永远是一个字节,也就是 8 个比特。

8 个比特一共能表示多少种不同的值?只有 256 ( 0 到 255 )。

所以不管你的数据有多长、 CRC 寄存器之前是什么状态,每次用来查表的那个 " 索引 " ,一定落在 0 到 255 之间。

表里刚好有 256 个元素,每一个索引对应的结果都提前算好了。你来一个索引,我就返回一个结果 —— 不可能出现第 257 种情况。

这就好比你有一张试卷,上面有 256 道选择题,编号从 0 到 255 。你提前把所有题目的标准答案写好,钉在墙上。考试的时候,别人随便报一个编号,你去墙上找对应的答案,一秒交卷。

二、 怎么获得这张表?

用这个函数直接生成 CRC-16MODBUS 查找表:

打开网易新闻 查看精彩图片

运行一次 Generate_CRC16_Table 函数,表就生成了。

三、节约 RAM 的小技巧

表 crc16_table 占用了 512 字节的 RAM ,如果在上位机程序中,这点内存不算事,可要是单片机程序, 512 字节的 RAM 做一个表怪心疼的。

解决的办法很简单,那就是把表放在 Flash 里:

打开网易新闻 查看精彩图片

加一个 const ,编译器就会把它放进 Flash 而不是 RAM 。这样既不用运行时动态生成表,也不占用宝贵的 RAM 空间。

四、 如何查表计算完整数据的 CRC

有了表,计算就变成 “ 字节级操作 ” ,一个字节只需一次查表和两次异或。

我们来试试效果。还是用上一篇一样的例子:

打开网易新闻 查看精彩图片

unsigned short crc16_result;

crc16_result = crc16_modbus(U1TxBuffer,6);

结果: crc16_result = 0xC755 。

和上一篇逐位算法算出来的完全一致,但速度快了 8 到 10 倍。

五、简单总结

查表法的本质,就是把 " 一个字节被 CRC 多项式反复折腾 8 次 " 这件事,提前算好 256 种标准答案。实际计算时,你只需要根据当前状态查一下表,两步异或就完事了。

至于那张表 —— 它不是在算什么新东西,它只是把你上一篇已经写过的逐位循环,提前跑了 256 遍而已。