最近做了关于IEC60730 classB的软件认证,关于这个认证是做什么的以及什么样的流程网上有很多大佬都已经介绍过了,这里也就不再误人子弟了,只简单说一下我在其中遇到的几个问题以及解决的方案吧。
classB认证一般都包含了寄存器自检、PC自检、Clock检、RAM检、Flash检、中断检、输入输出外设检等检测项,而这些检测项又包括了运行前自检以及运行中的周期检。
首先需要准备classB库文件,这个可以找供应商直接索要对应MCU的classB库文件或者网上找一下对应的库文件。有了库文件后一定要先完整的运行一遍,避免因为不同型号MCU的RAM空间不足等问题进入hard fault。能完整运行后,将classB库加到自己的程序中就可以了。
因为我自己用的是GD32,所以以GD32为例,GD官方提供的库文件是比较全面的,所以在使用上也没什么大的问题,下面介绍下遇到的一些问题以及解决的办法。
1、CPU寄存器运行前自检。
在官方提供的库文件中,寄存器的上电自检是在中断向量表里设置的,也就是如下的改法:
__Vectors DCD __initial_sp ; Top of Stack
DCD test_prerun ; Reset Handler --> test_prerun
DCD NMI_Handler ; NMI Handler
2、FLASH自检。
flash自检需要对代码进行CRC校验,然后将计算的CRC值存放到flash的末尾几位,在上电时以及运行时再次计算程序的CRC值,将再次计算的CRC值与最开始写入的CRC值做对比,一致即可。
原理比较简单,问题主要在于:(1)如何在编译后计算程序的CRC值 (2)如何将计算得到的CRC值写入到Flash的末尾几个字节。如果使用的是IAR,这两个问题就比较好解决,通过配置IAR的CRC计算参数,自动对整个FLASH空间进行CRC计算,并将计算结果放到FLASH的末尾。但是如何使用keil的话就复杂一些,因为keil没有提供直接生成CRC值的功能,所以需要借助外部的工具计算CRC值,然后添加到可执行文件的末尾。
(1)计算CRC值:
需要借助Srecord工具(SRecord 1.65)。添加crc_gen_keil.bat及crc_load.ini文件到KEIL工程同级目录下,在.bat脚本中调用srec_cat.exe计算程序的CRC值,然后在编译后调用以下.bat脚本计算CRC值。crc_load.ini:这个文件调试时有用,用来配置调试时导入带CRC校验码的HEX,避免对FLASH检测失败导致程序无法正常运行。
(2)将CRC值写入Flash末尾几个字节:
这里可以借助分散加载文件(.sct)来实现。在编辑.sct文件前,需要先在工程设置中做如下修改。
然后在.sct中做如下修改:
LR_IROM2 0x0807FFFC 0x0000004 {
ER_IROM2 0x0807FFFC 0x0000004
{
*.o (CHECKSUM, +Last)
}
}
并在中断向量表的向量后做如下修改即可,其中CRC值无所谓:
__Vectors_End
AREA CHECKSUM, DATA, READONLY, ALIGN=2
EXPORT __Check_Sum
ALIGN
;MARCHC MARCHX
__Check_Sum DCD 0xA8756A8F ;0xA8756A8F 0x7F4DE1BD This value is different according to actual condition
__Vectors_Size EQU __Vectors_End - __Vectors
AREA |.text|, CODE, READONLY
3、PC自检。
这个PC自检也是当时问题比较多的,因为要求对程序涉及到的每一个flash位都能做到翻转,以证明可以控制程序的正常运行。当然,由于arm指令的原因,程序编译完成后不会出现奇数地址,所以可以不用考虑PC指针最低位的翻转。若在程序运行过程中跳转了奇数地址,程序会跳转hard fault。至于其他位的翻转,可以利用attribute的方式指定函数生成后的地址。参考代码如下:
__attribute__((section(".ARM.__at_address")))
例如:
void func (void) __attribute__((section(".ARM.__at_0x00000100")));
void func (void) {
int i;
for (i = 0; i < 100; i++){
}
}
同样的,也可指定变量及数组的地址,如下:
int value __attribute__((section(".ARM.__at_0x20000000"))) = 0x33;
const char ziku[] __attribute__((section(".ARM.__at_0x00001000"))) = {0x1, 0x2, 0x3};
对于变量,在其后边加修饰;而对于函数,在声明处加修饰,注意,是在声明处,不是在函数定义处