本帖最后由 GrandLine 于 2022-9-25 09:43 编辑
上一小节我们了解了IEC60730 ClassB以及在KEIL MDK集成开发环境下如何配置生成HEX文件的CRC校验方法,并对烧录到FLASH中的程序进行CRC校验检测,本小节继续来围绕IEC60730 ClassB其它功能实现进行分享。
FLASH CRC运行自检
在上节中提到,在启动自检时,ClassB会对整个应用代码一次性做完整的CRC计算,对计算的结果进行判断比较;而在运行周期性自检时,为了减少ClassB自检程序所占用的时间对应用程序的影响,对于CRC部分会分扇区进行CRC运算,一般一次只计算FLASH中的一个SECTOR或者更小的PAGE,在计算完所有的SECTOR或者PAGE后,再将此时的计算结果进行判断比较。如下所示为FLASH CRC运行自检的实现:
- #define CLASSB_FLASH_START (uint32_t)(0x00000000)
- #define CLASSB_FLASH_END (uint32_t)((uint32_t *)&__Check_Sum)
- #define CLASSB_FLASH_SECTOR_SIZE (uint32_t)(0x200)
- /*******************************************************************************
- * [url=home.php?mod=space&uid=247401]@brief[/url]
- * @param
- * @retval
- * [url=home.php?mod=space&uid=93590]@Attention[/url]
- *******************************************************************************/
- void CLASSB_FALSH_InitCRC(void)
- {
- CRC_CR = 0x00000000;
- CRC_CR |= BIT0;
- CRC_POL = 0x04C11DB7;
- CRC_INIT = 0xFFFFFFFF;
- }
- /*******************************************************************************
- * [url=home.php?mod=space&uid=247401]@brief[/url]
- * @param
- * @retval
- * [url=home.php?mod=space&uid=93590]@Attention[/url]
- *******************************************************************************/
- void CLASSB_FLASH_Running(void)
- {
- static uint32_t Address = CLASSB_FLASH_START;
- uint32_t StartAddress = 0, EndAddress = 0;
- if(Address < CLASSB_FLASH_END)
- {
- if((Address + CLASSB_FLASH_SECTOR_SIZE) < CLASSB_FLASH_END)
- {
- StartAddress = Address;
- EndAddress = Address + CLASSB_FLASH_SECTOR_SIZE;
- }
- else
- {
- StartAddress = Address;
- EndAddress = CLASSB_FLASH_END;
- }
- for(uint32_t i = StartAddress; i < EndAddress; i += 4)
- {
- REG32(&CRC_DR) = *((uint32_t *)(i));
- }
- Address += CLASSB_FLASH_SECTOR_SIZE;
- }
- else
- {
- if(((REG32(&CRC_DR)) ^ 0x00000000) == __Check_Sum)
- {
- printf("\r\nFlash CRC Correct!");
- }
- else
- {
- printf("\r\nFlash CRC Error!!!");
- }
- CLASSB_FALSH_InitCRC();
- Address = CLASSB_FLASH_START;
- }
- }
在主程序main函数中完成启动自检后,通过调用CLASSB_FALSH_InitCRC函数重新初始化配置CRC配置参数,然后在while(1)中周期性CLASSB_FLASH_Running函数进行FLASH局部CRC的运算,直到将整个FLASH程序数据的CRC计算完成,进行结果比对。
看门狗自检
在SYS_RST_SRC寄存器中标注了系统不同复位源的标识,其中BIT3为看门狗复位标识,我通过这个标识来判断当前系统是正常的上电复位,还是看门狗复位;以此来实现看门狗的启动自检和运行,具体实现如下:
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- void WDT_Feed(uint32_t TimeOut)
- {
- SYS_WR_PROTECT = 0xCAFE;
- switch(TimeOut)
- {
- case SYS_WD_TimeOut2s : SYS_WDT_CLR = 0x798F; break;
- case SYS_WD_TimeOut4s : SYS_WDT_CLR = 0x798D; break;
- case SYS_WD_TimeOut8s : SYS_WDT_CLR = 0x798B; break;
- case SYS_WD_TimeOut64s : SYS_WDT_CLR = 0x7989; break;
- default : break;
- }
- SYS_WR_PROTECT = 0;
- }
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- void WDT_Init(void)
- {
- uint32_t REG_TEMP = SYS_RST_CFG;
- SYS_WR_PROTECT = 0x7A83;
- REG_TEMP |= BIT0;
- SYS_RST_CFG = REG_TEMP;
- }
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- void WDT_MultiTimerCallback(MultiTimer *timer, void *userData)
- {
- WDT_Feed(SYS_WD_TimeOut2s);
- MultiTimerStart(&WDT_MultiTimer, 250, WDT_MultiTimerCallback, "WDT");
- }
- if(SYS_RST_SRC & BIT3)
- {
- printf("\r\n-----------------");
- printf("\r\nWDT Reset Test OK\r\n");
- printf("-----------------\r\n");
- MultiTimerStart(&WDT_MultiTimer, 250, WDT_MultiTimerCallback, "WDT");
- }
- else
- {
- WDT_Init();
- WDT_Feed(SYS_WD_TimeOut2s); while(1);
- }
程序设计中使用了MultiTimer组件,每间隔250ms调用一次喂狗操作。这些需要注意2点:一是喂狗操作不要放在定时器中来实现周期性执行,定时器中断是抢占式的,当后台应用程序卡住了,定时器还是会正常运行的,所以会导致错误的判断;另外一个就是LKS32AT085的看门狗使用的LSI时钟,这个时钟默认就是打开的,且关闭不了,这个时钟在全温范围内具有一定偏差,手册上标注常温在23~42kHz,全温在16~48kHz,所以在喂狗的期间上需要考虑到这一点。
堆栈溢出自检
堆栈溢出检测实现比较简单,就是在栈顶定义一个数组,在程序初始化完成时给这个数组中的每个成员赋值,然后在运行的时候,周期性的来读取这个数组中成员变量的值进行判断,是否是与之前所赋的值相一致,具体的实现如下:
- 在startup_lks32mc08x.s文件中定义堆栈的大小:
- Stack_Size EQU 0x00000300
- AREA STACK, NOINIT, READWRITE, ALIGN=3
- Stack_Mem SPACE Stack_Size
- __initial_sp
- ; <h> Heap Configuration
- ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
- ; </h>
- Heap_Size EQU 0x00000200
- AREA HEAP, NOINIT, READWRITE, ALIGN=3
- __heap_base
- Heap_Mem SPACE Heap_Size
- __heap_limit
- 在ClassB_LKS32MC08x.sct文件中手动规划RAM区域功能,并定义STACK_BOTTOM:
- ; *************************************************************
- ; *** Scatter-Loading Description File generated by uVision ***
- ; *************************************************************
- LR_IROM1 0x00000000 0x00010000 { ; load region size_region
- ER_IROM1 0x00000000 0x00010000 { ; load address = execution address
- *.o (RESET, +First)
- *(InRoot$Sections)
- .ANY (+RO)
- .ANY (+XO)
- *.o (CHECKSUM, +Last)
- }
- RW_IRAM1 0x20000000 0x00001B00 { ; RW data
- .ANY (+RW +ZI)
- }
- STACK_NO_HEAP 0x20001B00 UNINIT 0x500
- {
- main.o (STACK_BOTTOM)
- startup_lks32mc08x.o (STACK, +Last)
- }
- }
- /* Private macro -------------------------------------------------------------*/
- volatile uint32_t aStackOverFlowPtrn[4] __attribute__((section("STACK_BOTTOM"), zero_init));
- 给堆栈溢出检测数组的成员变量赋值,并打印地址、数值对应信息:
- aStackOverFlowPtrn[0] = 0xEEEEEEEEuL;
- aStackOverFlowPtrn[1] = 0xCCCCCCCCuL;
- aStackOverFlowPtrn[2] = 0xBBBBBBBBuL;
- aStackOverFlowPtrn[3] = 0xDDDDDDDDuL;
- printf("\r\n");
- printf("\r\n0x%08x : 0x%08x", &aStackOverFlowPtrn[0], aStackOverFlowPtrn[0]);
- printf("\r\n0x%08x : 0x%08x", &aStackOverFlowPtrn[1], aStackOverFlowPtrn[1]);
- printf("\r\n0x%08x : 0x%08x", &aStackOverFlowPtrn[2], aStackOverFlowPtrn[2]);
- printf("\r\n0x%08x : 0x%08x", &aStackOverFlowPtrn[3], aStackOverFlowPtrn[3]);
- printf("\r\n");
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- void CLASSB_CheckStackOverflow(void)
- {
- if(aStackOverFlowPtrn[0] != 0xEEEEEEEEuL)
- {
- printf("\r\n%s[%d] : ERROR!", __FUNCTION__, __LINE__);
- }
- if(aStackOverFlowPtrn[1] != 0xCCCCCCCCuL)
- {
- printf("\r\n%s[%d] : ERROR!", __FUNCTION__, __LINE__);
- }
- if(aStackOverFlowPtrn[2] != 0xBBBBBBBBuL)
- {
- printf("\r\n%s[%d] : ERROR!", __FUNCTION__, __LINE__);
- }
- if(aStackOverFlowPtrn[3] != 0xDDDDDDDDuL)
- {
- printf("\r\n%s[%d] : ERROR!", __FUNCTION__, __LINE__);
- }
- }
运行结果
|