[超详细][嵌入式软件入门案例]crc-4的代码理解与推导(坑还没填完)

[嵌入式软件入门案例]crc的代码实现理解与推导

什么是CRC

Wiki: 循环冗余校验(英语:Cyclic redundancy check,通称“CRC”),主要用来检测或校验数据传输或者保存后可能出现的错误。生成的数字在传输或者存储之前计算出来并且附加到数据后面,然后接收方进行检验确定数据是否发生变化。
个人本科时期对嵌入式掌握不好,通过这个推导和理解,感受到对位操作的理解大大加深,现在认为做嵌入式是在这儿入了门。个人认为非常适合做嵌入式软件编程的入门实战。

MS5611气压计的crc4校验案例及其解说

static u8 MS5611PromCrc(u16 Prom[]) // QB: it means each member has 2bytes(16bits). 
{
	s16 i;  // simple counter 
	u16 n_rem = 0; // crc reminder. QB: 0x0000 at first
	u16 Prom7 = Prom[7];  // original value of the crc
	u8 n_bit;
	
	
	Prom[7] = (0xFF00 & (Prom[7])); //CRC byte is replaced by 0 QB: P[n] is saved as each member with 2bytes

	for (i = 0; i < 16; i++) // operation is performed on bytes. QB: Prom[] is divided into 16 parts.
                           // QB: u16(2 bytes) to u8(1 byte & 16 parts) indeed. 
	{ // choose LSB or MSB QB: MSB: Most Significant Bit
    
		if (i%2==1) // QB: division operation. 
			n_rem ^= (u16) ((Prom[i>>1]) & 0x00FF);
		else 
			n_rem ^= (u16) (Prom[i>>1]>>8);
		for (n_bit = 8; n_bit > 0; n_bit--) // QB: CRC processing. Totally, it mod 0b10011.
		{
			if (n_rem & (0x8000)) // Sign: The highest bit is 1
			{
				n_rem = (n_rem << 1) ^ 0x3000; // 0b0011... So the relationship between 0x3000 & hand calculator?
			}
			else
			{
				n_rem = (n_rem << 1);
			}
		}
	}
	
  n_rem= (0x000F & (n_rem >> 12)); // qb: final 4-bit reminder is CRC code
	Prom[7] = Prom7; // restore the crc_read to its original place
	return (n_rem ^ 0x00); // QB: though it's saved as 1 byte, valid data is only 0.5 byte.

Prom[]是一个有8个元素,每个元素为2字节的消息数组,Prom[7]的低八位是主机发送的CRC校验码,所以需要先屏蔽置零。

首先考虑Prom[i>>1]操作。这一操作是i/2并作截尾近似,所以i=0,1时,表达式为Prom[0];…;i=14,15时,表达式为Prom[7]。这种方式完成了i的16次循环与Prom数组8个元素的映射联系。

接着考虑条件结构。i从0增加到15,当i是奇数时,取i关联的Prom元素的低8位,再与n_rem异或;当i是偶数时,取i关联的Prom元素的高8位并移动至低8位位置,再与n_rem异或。此处的n_rem不断更新,n_rem与之前处理过的数据具有计算来的相关性,体现出了之前数据的特征,并且该n_rem的特点是:高8位为特征值,第8位全是0,原因见下一段。而Prom不管取高8位还是低8位都会被移动到低8位去,而高8位都是0。由于0与二进制a异或的结果还是a,所以这里的异或,其实就是n_rem高8位与Prom低8位的非全0部分进行拼接。

完成了体现以前数据特征的n_rem与当前一个字节(无论是原Prom的高8还是低8)的拼接,接下来对n_rem进行运算。也就是说,将新的特征与旧的特征一同运算,如此计算出的新特征,就体现了从开始到当前字节的特征。如果n_rem的最高位不是1,那么左移1位。如果n_rem最高位是1,那么左移一位后的新n_rem,与0x3000进行异或,计算机的操作是:新n_rem的左数第3、4位翻转,其他不变。当然,我们不需要局限于计算机的处理手段中,而应该抽象为数学的理解:这8次循环完成的是以0b10011为除数的模2除法运算,关于模2除法和这种数学理解的内容,在下一章节介绍。
推导图1
较为特别的是,由于Prom[7]的低8位是主机传来的校验码,所以在最开始,这个字节就设成了0。所以i=15时,当前要处理字节全是0,所以n_rem与该字节进行拼接,获得的还是i=15时内循环完成后的n_rem,即没有变化,低字节位全是0。这与此前拼接后的n_rem不同:此前的n_rem的高字节和低字节都非0(除非安排的数据很巧,算出来就是0)。将该n_rem与0b10011进行模2除法,就得到了内外循环全部完成后的n_rem,它的低12位全是0,只有高4位有意义。

最后,对n_rem进行提取。将有意义的高4位右移12位,移动到低4位上,再通过与0x00异或,提取出一个字节的数字(尽管该字节内高4位一定是0),那么此时这个字节就是本机接收数据后计算出的crc校验码。当然,也不要忘了,把原本发送来的crc校验码进行还原。
推导图2
在本程序之外,调用本程序,能够计算出这段数据的crc校验码,再与主机发送来的crc校验码进行比较,如果不相同,那么从谨慎的角度出发,就应当认为传输过程可能发生了错误,要求主机重新发送消息。如果相同,就认为通讯正确,数据传输成功。(实际上,此处校验码只有4位,要想提升鲁棒性,扩展校验码有效位数是一个方法)

下面我们来考虑模2除法——这个提取出数据列特征的算法——讲解完成后,再来进行最终总结。

模2除法

百度百科:模2除法,从字面上可理解为二进制下的除法。模2除法与算术除法类似,但每一位除的结果不影响其它位,即不向上一位借位,所以实际上就是异或。在CRC运算中,总能保证除数的首位为1,则模2除法运算的商是由余数首位与除数首位的模2除法运算结果确定。因为除数首位总是1,按照模2除法运算法则,那么余数首位是1就商1,是0就商0。百科链接:https://baike.baidu.com/item/%E6%A8%A12%E9%99%A4%E6%B3%95/10416971
还可以参考:https://blog.csdn.net/weixin_39450145/article/details/83987836

而CRC的原理和计算,这个文章写的很好:https://zhuanlan.zhihu.com/p/348823629。不赘述(其实是懒)。

这里还可以再讲一讲,下次有时间再补上。

总结

在这里插入图片描述
推导完还挺有感受的,随便写的,懒得打字了,现在有空再总结下。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值