打印
[LKS32 硬件]

【LKS32AT085评测】03.ClassB看门狗、堆栈溢出、FLASH运行自检

[复制链接]
1905|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
GrandLine|  楼主 | 2022-9-25 09:39 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 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运行自检的实现:
  • 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;
}
  • FLASH CRC运行自检实现部分:
/*******************************************************************************
* [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;
    }
}
  • FLASH CRC运行自检调用部分:
在主程序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__);
    }
}


运行结果

使用特权

评论回复
沙发
GrandLine|  楼主 | 2022-9-25 10:45 | 只看该作者
附件
软件工程源代码: ClassB_FlashCRC_WDT_STACK.zip (189.81 KB)

使用特权

评论回复
板凳
cooldog123pp| | 2022-9-25 16:43 | 只看该作者
楼主分享的很详细,感谢辛苦付出,如果以后用到这款单片机很有参考价值。

使用特权

评论回复
地板
上下而求索| | 2022-9-25 19:12 | 只看该作者
cooldog123pp 发表于 2022-9-25 16:43
楼主分享的很详细,感谢辛苦付出,如果以后用到这款单片机很有参考价值。 ...

车规级应用,能用到的话,可以联系我哦

使用特权

评论回复
5
GrandLine|  楼主 | 2022-9-25 19:44 | 只看该作者
cooldog123pp 发表于 2022-9-25 16:43
楼主分享的很详细,感谢辛苦付出,如果以后用到这款单片机很有参考价值。 ...

使用特权

评论回复
6
Henryko| | 2022-10-4 19:41 | 只看该作者
很详细

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

6

主题

35

帖子

4

粉丝