本文研究CRC校验的Simulink模型及其代码生成。
1 CRC校验
在汽车软件开发中,CRC校验常用于CAN通信中。通常将某个CAN报文中的数据通过生成CRC8校验码,将此校验码和数据打包到一个CAN报文中一起发送。这样的话,接收报文的节点就会判定校验码是否正确,以判断传输的过程是否正确。
本文以CRC校验中的CRC8为例,研究该算法的Matlab/Simulink建模过程。另外,网上有个便捷的小工具可以用于在线计算CRC:http://www.ip33.com/crc.html。
2 C代码形式
关于CRC的理解和代码,在网上有很多。其C代码如下:
#define FACTOR (0x107 & 0xFF) //多项式因子(取低8bit)
uint8_t calcCRC8(uint8_t *pbuf, uint8_t len)
{
uint8_t i;
uint8_t crc8 = 0x00;
while(len--)
{
crc8 ^= (*pbuf++); //前一字节计算CRC后的结果异或上后一字节,再次计算CRC
for (i=0; i<8; i++)
{
if (crc8 & 0x80) //高位为1时需要异或;否则不需要
{
crc8 = (crc8 << 1) ^ FACTOR; //多项式最高位为1,与数据高位1异或为0,所以数据左移一位后与多项式低8位异或即可
}
else
{
crc8 = (crc8 << 1);
}
}
}
return crc8;
}
从代码中可以看出,函数的入口参数是一个uint8的指针,以及长度参数。函数中会循环每一个字节,对其进行异或预算,最终返回crc8的值。
接下来,本文会通过Matlab/Simulink搭建CRC8的算法,并与C代码进行比较。
3 Matlab/Simulink建模及代码生成
3.1 Matlab Function建模
m语言和C语言很相似,所以可以通过m语言将算法写一遍,再嵌入到Matlab Function中,就可以再模型中实现CRC8。
1)新建立一个Matlab Function,配置输入参数为 arr 和 len,分别代表数组和长度。
2)在Matlab Function中模仿C语言形式,编写m语言的CRC算法;
function crc8 = calcCRC8(arr,len)
FACTOR = bitand(263,255);
crc8 = uint8(0);
for i = 1:len
crc8 = bitxor(crc8,arr(i));
for j = 1:8
if(bitand(crc8,128))
crc8 = bitxor(bitshift(crc8,1),FACTOR);
else
crc8 = bitshift(crc8,1);
end
end
end
end
由于Matlab不支持指针,所以第一个参数arr为数组(但是可以不定义数组长度,而是继承外面输入的数组长度),第二个参数为len。
在M语言中,异或运算符为bitxor(a,b),这点和C语言不同。
3)保存好Matlab Function之后,用常数简单测一下;
输入常数为0x2C,输出CRC8校验码为0xC4,说明算法没问题。
4)接下来,把输入输出替换成port,尝试生成代码;
这里,Inport1和Inport3分别设置为长度为1的单变量和长度为2的数组,是为了测试同样的CRC校验算法的Matlab Function在不同长度输入的情况下,会如何表现;
5)将两个Matlab Function都配置成原子子系统,Ctrl + B生成代码如下;
这里由于传参的长度不同,分别生成了两个函数。第一个函数的第一个参数是uint8 变量,第二个函数的第一个参数是uint8类型数组。
这里博主认为是Matlab代码生成工具比较格式化,必须在生成代码之前就分析好函数的参数类型等信息,然后生成固定的传参形式,而无法生成指针这样灵活的参数。这样就导致了在C语言中一个函数就搞定的CRC8算法,生成了两遍,占用了芯片Flash资源。
3.2 For Iteration子系统建模
Simulink中也可以通过For Iteration子系统实现类似于C语言中的for循环,方法如下:
1)首先新建一个for循环子系统,arr端口设为长度为2的数组;
2)在该层for循环子系统中,对应着对每个字节的循环处理,也即上文中while的循环;
其中,子系统中嵌套了另一个子系统,对应于C函数中的第二层循环 for (i=0; i<8; i++) ;
需要注意的是,该层中的selector模块用于挑选出数组中某个索引的元素,而且必须明确数组的长度。这就导致了不同长度的数组无法通用该模型。
3)第二层循环中如下所示;
这里建模有点复杂,远没有C代码那么简洁。而且也没必要再生成代码了,因为生成的代码更加复杂。而且不同长度的数组必定要生成不同的函数,甚至在模型层面都无法通用。
4 总结与思考
本文研究CRC校验的Simulink模型及其代码生成,博主认为通过这个例子可以看出,CRC校验算法不适合用Simulink建模生成代码,原因如下:
1)Simulink对指针的支持不够好
通过常规的建模方式,Simulink难以生成灵活的指针操作。或许Simulink可以通过一些配置,例如修改S Function或者TLC语言生成指针,但是那样做实在太麻烦了,没必要花精力钻研工程软件的底层实现。
2)处理循环和数组比较麻烦
在3.2的例子中,Simulink模型的for子系统远不如C语言中的for循环那样易读,对于数组的处理也不够直观。
CRC校验在软件开发中更加属于底层软件,而不是应用层软件。所以直接通过C语言实现或许是更好的选择。博主依然尝试用Simulink的两种方式实现,是希望通过实践更加直观地对比出不同方式地优劣。
其实很多软件需求,都不适合用Simulink生成代码的方式完成(例如自动驾驶感知规划),需要根据需求和工具的特点灵活选取软件工具。