循环冗余校验(Cyclic Redundancy Check,CRC)是一种常用的检测数据完整性的算法。相比MD5、SHA1等Hash算法,它的计算复杂度更低,更方便用于低功耗设计。MSPM0中包含CRC硬件模块,用于高效可靠地计算CRC值,并且支持CRC32(以前MSP430只支持CRC16)。
场景
通常CRC的用法是:
1、发送端发送一段信息,然后附带上这段信息的CRC(1)。
2、接收端收到信息后,用相同的CRC算法计算得到CRC(2)。
3、比较CRC(1)和CRC(2)是否相等,从而判定信息传输是否准确。
问题
根据使用场景,我们希望CRC存在一个标准算法,可以同时在发送端和接收端实现。但实际情况是,CRC存在很多不同的版本,每个版本还可能有不同的初始化参数。开发中常见的问题在于如何选择一个标准版本和对应参数?
CRC8和更高位数的算法和参数:
常见的CRC有:
MSPM0L1306中的CRC
一共实现了2种CRC算法,分别是CRC16-CCITT、CRC32-ISO3309。这里以新增的CRC32为例来进行实测和分析。
实测和分析
SysConfig配置:
SysConfig中能调整的参数并不多,但我修改了Input Byte Order,所以依然是个自定义的Profile。
测试代码不复杂,只是每一个细节都值得注意。完整代码如下:
对应的输出如下:
比对了多个结果,和网上流传的CRC32/MPEG-2版本结果一致:
来源:https://crccalc.com/?crc=EEWorld!&method=crc32&datatype=ascii&outtype=0
在PC上有很多CRC32的软件实现,但我测试用python的zlib库(python常用的运算CRC32的库)计算结果是不匹配的。
因此这里重新实现一个PC版本的CRC32,它的结果和LaunchPad的结果可以相匹配。
运行结果如下:
可以看到只有重新实现的crc32mpeg2()的结果和LaunchPad的输出相匹配。
这段python代码主要操作是位运算,也可以方便地用其它语言实现。
小结
CRC32是一组常见的CRC实现算法,为了使得PC和LaunchPad上的结果相匹配,需要精心选择一组参数。本文给出了LaunchPad上一组参数对应的软件实现,方便大家在应用开发时利用MSPM0片上的CRC模块进行数据校验。
完整代码
PC上的python程序
import zlib
data = b"EEWorld!"
for d in data:
print(hex(d), end=' ')
print()
crc1 = zlib.crc32(data)
print('crc1', hex(crc1))
def crc32mpeg2(msg):
crc = 0xffffffff
for b in msg:
crc ^= b << 24
for _ in range(8):
crc = (crc << 1) ^ 0x04c11db7 if crc & 0x80000000 else crc << 1
return crc & 0xffffffff
crc2 = crc32mpeg2(data)
print('crc2', hex(crc2))
LaunchPad主程序
#include "ti_msp_dl_config.h"
#include <stdio.h>
uint32_t data[] = {0x4545576f,0x726c6421}; // 字符串"EEWorld!"
int data_length = 2;
const uint32_t gCrcSeed = CRC_SEED; //0xFFFFFFFF
int main(void) {
volatile uint32_t crcChecksum;
volatile bool crcChecksumMatch __attribute__((unused));
SYSCFG_DL_init();
crcChecksum = DL_CRC_calculateBlock32(CRC, gCrcSeed, &data[0], data_length);
printf("crc32: 0x%x\n", crcChecksum);
while (1) {
__WFI();
}
}
SysConfig参考代码
/**
* These arguments were used when this file was generated. They will be automatically applied on subsequent loads
* via the GUI or CLI. Run CLI with '--help' for additional information on how to override these arguments.
* @cliArgs --device "MSPM0L130X" --package "VQFN-32(RHB)" --part "Default" --product "mspm0_sdk@1.20.00.06"
* [url=home.php?mod=space&uid=304333]@versions[/url] {"tool":"1.18.0+3266"}
*/
/**
* Import the modules used in this configuration.
*/
const Board = scripting.addModule("/ti/driverlib/Board");
const CRC = scripting.addModule("/ti/driverlib/CRC");
const GPIO = scripting.addModule("/ti/driverlib/GPIO", {}, false);
const GPIO1 = GPIO.addInstance();
const SYSCTL = scripting.addModule("/ti/driverlib/SYSCTL");
/**
* Write custom configuration values to the imported modules.
*/
CRC.seed = 0xFFFFFFFF;
CRC.endianness = "BIG_ENDIAN";
GPIO1.$name = "GPIO_LEDS";
GPIO1.associatedPins.create(2);
GPIO1.associatedPins[0].$name = "USER_LED_1";
GPIO1.associatedPins[0].initialValue = "SET";
GPIO1.associatedPins[0].assignedPin = "0";
GPIO1.associatedPins[0].assignedPort = "PORTA";
GPIO1.associatedPins[1].$name = "USER_TEST";
GPIO1.associatedPins[1].initialValue = "SET";
GPIO1.associatedPins[1].assignedPort = "PORTA";
GPIO1.associatedPins[1].assignedPin = "1";
SYSCTL.clockTreeEn = true;
/**
* Pinmux solution for unlocked pins/peripherals. This ensures that minor changes to the automatic solver in a future
* version of the tool will not impact the pinmux you originally saw. These lines can be completely deleted in order to
* re-solve from scratch.
*/
Board.peripheral.$suggestSolution = "DEBUGSS";
Board.peripheral.swclkPin.$suggestSolution = "PA20";
Board.peripheral.swdioPin.$suggestSolution = "PA19";
GPIO1.associatedPins[0].pin.$suggestSolution = "PA0";
GPIO1.associatedPins[1].pin.$suggestSolution = "PA1";