本帖最后由 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__);
}
}
运行结果
|