在学习超子说物联网“GD32/STM32单片机,OTA网络远程升级,手把手编写BootLoader程序教程”视频时,本地传输程序需要使用分段传输,采用XModem协议,使用到16位CRC校验,而STM32自带的是一个32位CRC计算单元,所以我们需要手动实现一个CRC校验功能。结合超哥的代码和前辈的帖子,开此贴以作学习记录。如果本帖有侵权还请联系我删除!
简单介绍
CRC全名(Cyclic Redundancy Check,循环冗余校验)。CRC-16中的16指的是算法中使用了16位(2字节)的校验码,而非传入数据的长度等其它含义。
CRC对我们来说只是协议中的校验工具,我们关心实现而不在意原理,所以我们直接给出代码编写逻辑,参考自C语言版CRC-16系列校验算法,五行字让我会写代码,非常感谢!
个人愚见,CRC(循环冗余校验)名字里的“循环”体现在:我们所进行的所有操作都在处理INIT这个初值,最终输出的返回值也是它,记住这点将帮助我们理解编写逻辑。
1.根据所用CRC-16协议选择初始值CRC16_INIT和多项式 CRC16_POLY的初值;
2.将当前待校验数据与CRC16_INIT的高8位异或;
3.判断CRC16_INIT的最高位,若该位为 0 则左移一位,若为 1 则左移一位并与CRC16_POLY异或;
4.重复第3步直至8位全部移位计算结束;
5.重复将所有输入数据完成2、3、4步操作,所得16位数即16位CRC校验码。
第1步中,我们通过CRC(循环冗余校验)在线计算这个网站可以获知初始值 INIT(Hex)和多项式 POLY(Hex),如下图所示:
CRC-16/
代码实现
最终的代码实现如下所示:
u16 XModem_CRC16(u8 *data, u16 datalen)
{
u16 CRC16_INIT = 0x0000; //初值
u16 CRC16_POLY = 0x1021; //多项式值
while(datalen){
CRC16_INIT = ((*data) << 8) ^ CRC16_INIT; //将当前待校验数据与CRC16_INIT的高8位异或;
for(u8 i=0; i < 8; i++){ //一个for循环完成一个8位数据的校验计算
if((CRC16_INIT & 0x8000) == 0){ //判断CRC16_INIT的最高位
CRC16_INIT = CRC16_INIT << 1;
}else{
CRC16_INIT = (CRC16_INIT << 1) ^ CRC16_POLY;
}
}
data++; //指向下一个数据
datalen--; //所有数据是否校验完成的标志
}
return CRC16_INIT; //返回校验值
}
实验测试
我们传入五个数据,获得最终校验值。
u8 test1[5] = {0x1,0x2, 0x3,0x4,0x5};
u1_printf("test1 XModem_CRC16 = %x !\r\n",XModem_CRC16(test1, 5));
u8 test2[5] = {0x5,0x6, 0x7,0x6,0x5};
u1_printf("test2 XModem_CRC16 = %x !\r\n",XModem_CRC16(test2, 5));
串口打印结果如下:
我们来看一下CRC(循环冗余校验)在线计算上的结果:
两个结果均正确,实验成功!
ps:个人水平有限,如果文中有错误还请指正!