嵌入式 > 嵌入式开发 > 详情

STM32F10x 学习笔记4(CRC计算单元 续)

发布时间:2020-09-01 发布时间:
|
上篇博客给出了 STM32F10X 系列单片机CRC 单元的用法。还指出了这个CRC 单元计算的结果与常见的CRC32 算法得到的结果不相同。但是为什么不相同,是什么原因造成的却没有写出来。这里再补一篇,把这些都说清楚。

下面先给个crc32的计算函数,这个函数计算的结果与STM32F单片机上硬件单元的计算结果相同。

  1. uint32_tcrc32(uint32_t*addr,intnum,uint32_tcrc)
  2. {
  3. inti;
  4. for(;num>0;num--)
  5. {
  6. crc=crc^(*addr++);
  7. for(i=0;i<32;i++)
  8. {
  9. if(crc&0x80000000)
  10. crc=(crc<<1)^POLY;
  11. else
  12. crc<<=1;
  13. }
  14. crc&=0xFFFFFFFF;
  15. }
  16. return(crc);
  17. }

在我写的文章《写给嵌入式程序员的循环冗余校验(CRC)算法入门引导》(/zixunimg/eepwimg/blog.csdn.net/liyuanbhu/article/details/7882789)中给了个利用查表法计算crc的程序。那个程序稍微修改一点就能计算CRC32。下面给出改动后的程序。

  1. //crc32.h
  2. #ifndefCRC32_H_INCLUDED
  3. #defineCRC32_H_INCLUDED
  4. #ifdef__cplusplus
  5. #if__cplusplus
  6. extern"C"{
  7. #endif
  8. #endif/*__cplusplus*/
  9. #include
  10. /*
  11. *TheCRCparameters.CurrentlyconfiguredforCRC32.
  12. *CRC32=X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X1+X0
  13. */
  14. #definePOLYNOMIAL0x04C11DB7
  15. #defineINITIAL_REMAINDER0xFFFFFFFF
  16. #defineFINAL_XOR_VALUE0x00000000
  17. /*
  18. *ThewidthoftheCRCcalculationandresult.
  19. *Modifythetypedefforan8or32-bitCRCstandard.
  20. */
  21. typedefuint32_twidth_t;
  22. #defineWIDTH(8*sizeof(width_t))
  23. #defineTOPBIT(1<
  24. /**
  25. *InitializetheCRClookuptable.
  26. *ThistableisusedbycrcCompute()tomakeCRCcomputationfaster.
  27. */
  28. voidcrcInit(void);
  29. /**
  30. *ComputetheCRCchecksumofabinarymessageblock.
  31. *@paramessage,用来计算的数据
  32. *@paranBytes,数据的长度
  33. *@noteThisfunctionexpectsthatcrcInit()hasbeencalled
  34. *firsttoinitializetheCRClookuptable.
  35. */
  36. width_tcrcCompute(unsignedchar*message,unsignedintnBytes,width_tremainder);
  37. #ifdef__cplusplus
  38. #if__cplusplus
  39. }
  40. #endif
  41. #endif/*__cplusplus*/
  42. #endif//CRC32_H_INCLUDED

对应的C程序如下:

  1. #include"crc32.h"
  2. /*
  3. *Anarraycontainingthepre-computedintermediateresultforeach
  4. *possiblebyteofinput.Thisisusedtospeedupthecomputation.
  5. */
  6. staticwidth_tcrcTable[256];
  7. /**
  8. *InitializetheCRClookuptable.
  9. *ThistableisusedbycrcCompute()tomakeCRCcomputationfaster.
  10. */
  11. voidcrcInit(void)
  12. {
  13. width_tremainder;
  14. width_tdividend;
  15. intbit;
  16. /*Performbinarylongdivision,abitatatime.*/
  17. for(dividend=0;dividend<256;dividend++)
  18. {
  19. /*Initializetheremainder.*/
  20. remainder=dividend<
  21. /*ShiftandXORwiththepolynomial.*/
  22. for(bit=0;bit<8;bit++)
  23. {
  24. /*Trytodividethecurrentdatabit.*/
  25. if(remainder&TOPBIT)
  26. {
  27. remainder=(remainder<<1)^POLYNOMIAL;
  28. }
  29. else
  30. {
  31. remainder=remainder<<1;
  32. }
  33. }
  34. /*Savetheresultinthetable.*/
  35. crcTable[dividend]=remainder;
  36. }
  37. }/*crcInit()*/
  38. /**
  39. *ComputetheCRCchecksumofabinarymessageblock.
  40. *@paramessage,用来计算的数据
  41. *@paranBytes,数据的长度
  42. *@noteThisfunctionexpectsthatcrcInit()hasbeencalled
  43. *firsttoinitializetheCRClookuptable.
  44. */
  45. width_tcrcCompute(unsignedchar*message,unsignedintnBytes,width_tremainder)
  46. {
  47. unsignedintoffset;
  48. unsignedcharbyte;
  49. //width_tremainder=INITIAL_REMAINDER;
  50. /*Dividethemessagebythepolynomial,abyteatatime.*/
  51. for(offset=0;offset
  52. {
  53. byte=(remainder>>(WIDTH-8))^message[offset];
  54. remainder=crcTable[byte]^(remainder<<8);
  55. }
  56. /*ThefinalremainderistheCRCresult.*/
  57. return(remainder^FINAL_XOR_VALUE);
  58. }/*crcCompute()*/

不过用这个程序直接计算得到的CRC值与STM32给出的并不相同。之所以会这样是因为字节序的原因。可以举个例子来说明这个问题。比如我们有一片内存区域要计算CRC值。这片内存区域的起始地址是0x1000,共有8个字节。用crcCompute()函数计算时是按照地址顺序依次传入各个字节。也就是先计算0x1000处的字节,再计算0x0001处的字节,以此类推最后计算0x1007地址处的字节。而STM32的硬件CRC单元是以32位的字为单位计算的。我们知道CRC实际上是个多项式的除法运算,而除法运算是从高位算起的。也就是相当于它是按照0x1003、0x1002、0x1001、0x1000这个顺序计算第一个字,然后按照0x1007、0x1006、0x1005、x1004的顺序计算第二个字。因此。我们要是预先将字节序调换一下得到结果就没有问题了。这就有了下面的改造。其中remainder传入0xffffffff。因为STM32中的CRC余数初始值为0xffffffff。

  1. uint32_tstm32crc32(uint32_t*message,unsignedintnWords,uint32_tremainder)
  2. {
  3. unsignedintoffset;
  4. unsignedcharbyte;
  5. unsignedchar*p=(unsignedchar*)message;
  6. //width_tremainder=INITIAL_REMAINDER;
  7. /*Dividethemessagebythepolynomial,abyteatatime.*/
  8. for(offset=0;offset
  9. {
  10. byte=(remainder>>(WIDTH-8))^p[3];
  11. remainder=crcTable[byte]^(remainder<<8);
  12. byte=(remainder>>(WIDTH-8))^p[2];
  13. remainder=crcTable[byte]^(remainder<<8);
  14. byte=(remainder>>(WIDTH-8))^p[1];
  15. remainder=crcTable[byte]^(remainder<<8);
  16. byte=(remainder>>(WIDTH-8))^p[0];
  17. remainder=crcTable[byte]^(remainder<<8);
  18. p+=4;
  19. }
  20. /*ThefinalremainderistheCRCresult.*/
  21. return(remainder);
  22. }/*crcCompute()*/

大家可以验证这个函数的计算结果与STM32上的结果完全一样。

写到这里本该就结束了,不过我要多说一句,之所以要这么麻烦的调换字节序,都是小端(littleendian)惹的祸。要是都采用大端格式就没这些麻烦的转换了。



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
定时器CTC模式的测试