MM32F013x

在上几个章节中我们介绍了MM32相关的IEC60730-1 B类认证启动自检与运行自检的相关的流程,本章节我们将会介绍一下ClassB认证的程序流程控制,栈溢出检测以及MCU通用检测的相关文件,以方便客户移植到自己的代码中,从而提高产品的可靠性。

1. 程序流程控制

程序流程控制是在认证中我们建议加入的一种检测手段,这是因为它能确保所有特定代码能正确执行并按照一定的流程执行。

为了确保所有特定代码能正确执行并通过,代码流程中的所有关键点(组件测试块)能够按照程序设计思路正确执行,应定义独特的标签进行标记,从而确保不跳过任何块,并且所有流程均按照程序设计思路能够正常执行。

其实现原理是在每次处理任何重要的测试块时,两个互补计数器的值(增加或减去独特的标签值)发生对称的四步变化,其中两个检查步骤安排在调用程序(主流程)层的调用块外。这确保了块从主流程层级被成功调用(在调用前及从调用程序返回后处理)。下两个步骤是在调用程序内执行,以确保块正确完成(在进入程序后及从程序返回前处理)。

按控制流程顺序调用执行组件测试的例程,显示四步检查服务。因为所有的点都通过计算一组互补计数对来检查,所以该方法可降低CPU的负载。由于有些调用/返回编号及进入/退出点始终相同,在每个块完全通过后,储存在计数对内的值必须始终互补。检查计数对完整性时,对几个执行流程检查点进行评估并放入代码流程。如果任何检查点的计数器不互补或其不包含预期值,应调用故障保护例程。

当程序计数器丢失或终止时,两个不同的看门狗可复位设备,它们在两个独立的时钟源运行。窗口看门狗由主振荡器驱动,可执行时隙监控,独立看门狗由内部低速RC振荡器驱动。程序控制流程可采用专用软件方法监控。

“MM32F013x——IEC60730-1

在MM32的ClassB认证的函数库中的程序控制流程检查函数如下:

ErrorStatus control_flow_check_point(uint32_t chck)
{
    ErrorStatus Result= SUCCESS;

    if ((CtrlFlowCnt != (chck)) || ((CtrlFlowCnt ^ CtrlFlowCntInv) != 0xFFFFFFFFuL))
    {
      Result= ERROR;
    }
    return(Result);
}

2. 栈边界检测

该测试主要用来检测MCU在运行的过程中是否出现栈溢出的问题。

根据IEC60730-1 B类认证标准,栈溢出检测不是强制性的。它们可在用于间接测试某些功能时添加,如果用户喜欢使用其它方法,也可将其禁用或跳过。

其实现原理为:通常采用的做法是在stack创建之初就填充满固定的字符(比如0x5a5a5a5a),如果发生了“上溢”,那么stack末端(最低地址处)的填充字符则有可能会被更改。

在MM32自检库中也是采用这一个实现方式,在栈底设定特定的数值,在每次进行检测的时候检测栈底的数值,当栈底的数值发生变化的时候就判断栈溢出了。具体的流程图如下所示:

“MM32F013x——IEC60730-1

在栈底地址填写数据:

    control_flow_call(STACK_OVERFLOW_TEST);

    aStackOverFlowPtrn[0] = 0xEEEEEEEEuL;
    aStackOverFlowPtrn[1] = 0xCCCCCCCCuL;
    aStackOverFlowPtrn[2] = 0xBBBBBBBBuL;
    aStackOverFlowPtrn[3] = 0xDDDDDDDDuL;

    control_flow_resume(STACK_OVERFLOW_TEST);

堆栈溢出判断函数如下:

/******************************************************************************/
ErrorStatus STL_CheckStack(void)
{
    ErrorStatus result = SUCCESS;

    CtrlFlowCnt += STACK_OVERFLOW_CALLEE;

    if ( aStackOverFlowPtrn[0] != 0xEEEEEEEEuL )
    {
        result = ERROR;
    }

    if ( aStackOverFlowPtrn[1] != 0xCCCCCCCCuL )
    {
        result = ERROR;
    }

    if ( aStackOverFlowPtrn[2] != 0xBBBBBBBBuL )
    {
        result = ERROR;
    }

    if ( aStackOverFlowPtrn[3] != 0xDDDDDDDDuL )
    {
        result = ERROR;
    }

    CtrlFlowCntInv -= STACK_OVERFLOW_CALLEE;

    return (result);
}

3. B类安全方案软体结构

3.1 、用户应用的集成

在上面的几个章节中我们可以了解到,ClassB类认证分为启动自检和运行自检,在系统复位完成以后然后进行ClassB相关的启动自检,启动自检完成以后用户可以进行相应的用户应用初始化操作,然后会进行到主循环进行运行自检以及客户任务的处理操作,具体的启动以及定期运行时自检结构框图如下:

“MM32F013x——IEC60730-1

用户需要做ClassB认证时候,仅仅需要将MM32提供的官方例程中的启动自检与运行自检加入到相应的代码段即可。

3.2 、自检驱动接口文件

用户在参考MM32-ClassB的例程的时候可以参考下面的表格来进行文件的移植操作。

“MM32F013x——IEC60730-1

4. 符合IEC标准

IEC60730标准涵盖了交流电器的机械、电气、电子、EMC和交流电器的异常操作,包括自动电子控制定义的三种软件分类:A类认证,B类认证,C类认证。

在前面的几个章节中介绍的关于MCU自检认证属于B类认证操作,在电子应用领域经常需要MCU做各种自检来保证产品安全可靠的运行。在家用电器方面,家用电器制造商必须采取相关的安全措施来保证其产品安全可靠的运行,以符合IEC60730标准。

MM32的ClassB例程有相应的故障处理函数,其基本原则是,如果出现任何组件故障,家用电器必须保证安全,严格遵守以上原则,微控制器的通用检测一般分为内核诊断、易失和非易失内存完整性检查,时钟系统测试等,目前,MM32已包含了上述所有的通用性检测功能。

来源:灵动MM32MCU
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 202

在前面的几个章节中我们介绍了关于MM32F0130系列的ClassB 认证的实现方式,本章节中我们将为大家介绍在MM32F0130上ClassB认证的各部分代码段测试所花费的时间。

ClassB认证的各部分代码段运行都会增加MCU的负担,为防止认证代码影响应用控制,我们需要对各个部分代码执行时间进行测试,分时段利用时间空隙进行自检。耗时与很多因素有关系,MCU的性能和开发环境都会有影响,在单次运行中执行启动测试,测试时间取决于MCU性能和测试区的大小。运行期间测试逐块执行,因此其时间也取决于被测试块大小和测试的重复频率。

用户需要在应用所需的性能和安全认证所需的性能之间找到平衡,主要挑战在于实现较短的整体诊断测试间隔,同时保持应用过程安全时间在可接受的长度范围内。在关键情况下,运行时测试可限制在收集关键代码或数据的区域。

用户应考虑到该间隔也用于在运行期间计算系统时钟和LSI之间的时钟交叉参考率,因而其长度不得低于时钟交叉测量所用的LSI周期数相应间隔。

当特定测试(RAM、Flash、时钟)启动时及每次完成测试程序(启动时和运行期间)时,通过切换I/O的翻转来测试各个部分的耗时。其中,在启动时和运行期间,这些I/O可用于测量测试长度、局部测试的频率及完成整个单测试周期所需的时间。完全检测被测试的专用区域后,一组局部测试又重新开始。

在ClassB自检的过程中,ClassB自检流程分为启动自检与运行自检两个部分,本次例程我们将采用翻转GPIO口的方式来测试ClassB启动自检以及运行自检所花费的时间。

本次测试我们翻转PA0来进行计算时间(即在自检之前设置为低电平,自检完成以后设置为高电平,然后使用逻辑分析仪抓取低电平的持续时间即为自检部分花费的时间)。

启动自检

启动自检部分包含CPU寄存器自检,看门狗自检,FLASH自检,RAM自检以及时钟自检,启动自检的流程如下图所示:

“MM32F013x——IEC60730-1

01、CPU寄存器自检

CPU寄存器自检调用的函数为STL_StartUpCPUTest(), 在此函数之前我们调用GPIO的配置函数,将PA0配置为低电平,在函数运行完成后将PA0配置为高电平,然后使用逻辑分析仪抓取两次电平翻转的脉宽即可,逻辑分析仪获取波形如下:

“MM32F013x——IEC60730-1

由上图可以看出CPU寄存器的启动自检的时间为12.51us。

02、看门狗自检

看门狗自检调用的函数为STL_WDGSelfTest() ,在此函数中进行PA0的翻转设置,其操作与CPU寄存器的操作一样,但是我们需要屏蔽掉CPU寄存器自检操作过程中PA0的翻转操作(由于本次测试的内容较多,为了防止计算错误,故一个一个的测试)。

“MM32F013x——IEC60730-1

由上述逻辑分析仪抓取的波形可知看门狗自检花费的时间为3.591ms。

03、FLASH自检

FALSH自检操作在FLASH自检的地方对GPIO口进行翻转操作,逻辑分析仪获取波形如下:

“MM32F013x——IEC60730-1

由逻辑分析仪抓取的波形可知64K的FLASH自检的时间为9.36ms,其中包含CRC校验等耗费的时间。

04、RAM自检

RAM自检操作在RAM自检的地方对GPIO口进行翻转操作,逻辑分析仪获取波形如下:

“MM32F013x——IEC60730-1

由上图可知16K的RAM启动自检花费的时间为1.1ms。

05、时钟自检

时钟自检操作在时钟自检的地方对GPIO口进行翻转操作,具体操作如下:

“MM32F013x——IEC60730-1

由上图可知在启动时时钟自检花费的时间为9.37ms。

运行自检

运行自检耗时的测试方法与启动自检的方法一致,也是通过翻转GPIO来实现的。

“MM32F013x——IEC60730-1

运行自检周期直接调用函数STL_DoRunTimeChecks()函数即可,在函数中包含了所有的运行自检的流程,下面我们来测试运行自检花费的时间。

测试运行自检周期如下所示:

“MM32F013x——IEC60730-1

由上图可知,官方例程设置自检周期的时间为10.008ms。

测试CPU寄存器运行自检所花费的时间如下所示:

“MM32F013x——IEC60730-1

由上图可知CPU寄存器运行自检的时间为10.18us。

运行期间的测试逐块执行,因此其时间也取决于被测试块大小和测试的重复频率,同时开发环境也影响执行效率,使用IAR和MDK开发的执行时间也有差异,本次实验系统时钟设置48MHz,基于MDK5.27版本的工程测试得出的数据。

对算法在运行时间和存储空间这两种资源的利用效率进行研究是一门学问,研究如何用更小的空间、更短的时间完成所需要的功能是一件很有意思的工作,有兴趣的可以深入研究一下。

来源:灵动微电子
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 363

上一章节中我们介绍了FLASH检测的实现方法,本章节中给大家介绍一下系统时钟自检的检测方法。

MCU内部有4个时钟源:

外部高速时钟源(HSE):外部晶振选择范围为2-24MHz

外部低速时钟源(LSE):可以使用一个32.768KHz的晶体/陶瓷谐振器构成的振荡器产生

内部高速时钟源(HSI):HSI振荡器的典型频率为 48MHz

内部低速时钟源(LSI):LSI振荡器的典型频率为40KHz

MCU有时钟安全系统(CSS)模块,除了MCU的硬件时钟安全机制之外,还可以结合软件对系统时钟进行检测保障安全。

在出厂时,两个内部时钟的精度会校准在±1%以内,但是两个时钟源在环境温度下精度特性表现不尽相同,具体的特性参数可以查看芯片数据手册,因此检测的极限限定值需要参考芯片的特性进行设定。

系统时钟自检的检测方法就是采用两个独立时钟源交叉检查来进行测量,被测量的频率控制定时器时钟,另一个来给定定时器时钟输入。例如,使用内部低速RC振荡器时基(LSI)可检测外部晶振的错误频率。

两个独立时钟源交叉检查来进行测量有两种实现方式:

1、运行IWDG或RTC(LSI)和WWDG或SysTick(HSE)进行计数比较,这种实现方式需要支持硬件的RTC或者IWDG中断方式。

2、通过输出LSI时钟利用另外一个高速时钟的定时器来捕获该频率,然后计算差值。

在这里需要MCU支持一个功能,LSI输出频率内部可以通过定时器来捕获,在MM32F013x系列中硬件支持该功能,无需外部MCO输出LSI连接另外一个TIMx_CHx去捕获计数,LSI内部硬件直接输出到TIM2\3通道捕获,无需额外的引脚互联。

“MM32F013x——IEC60730-1

同样,在ClassB检测中,系统时钟自检也分为启动自检与运行自检两部分。

01、时钟启动自检

第一步:内部低速时钟(LSI)源启动。

第二步:外部高速时钟源(HSE)启动,由HSE作为时钟源的PLL设为系统时钟。

第三步:专用计时器初始化,用于HSE频率的交叉参考测量,由预先定义的LSI周期数控制。该定时器计数器连续两次获取的值不同,从而可得出LSI和HSE频率之间的比率。获取的值及其差别在定时器信道的定期中断服务期间处理。将比率测量与预期范围相比较,如果与标称值的差超过+/-20%,则报错。范围由宏HSE_LimitHigh()和HSE_LimitLow()根据一些常量定义在mm32_STLparam.h文件中定义。

用户负责正确输入这些宏,并考虑这些限值是否能保持恒定,或这些限值是否根据应用而动态调整(例如,由于整个温度范围内参考时钟信号的准确性)。

第四步:测试完成后,CPU时钟切换回默认HSI源。

“MM32F013x——IEC60730-1

02、时钟运行自检

时钟运行时自检采用与启动自检相似的程序,时钟交叉参考率的真实性检查取决于定时器捕获事件最后两次结果的差别。在专用定时器定期中断服务过程中,对这些结果加以储存,并提供系统(HSE)和参考(LSI)频率间的交叉参考测量。

测试检查HSE率是否在预期范围内(标称值的+/- 20%)。如果发现差别较大或HSE信号丢失,或测量中断消失,则CPU时钟源会立即切换回HSI且HSE故障状态返回,否则,测试返回至正常状态。在对HSE范围比较前,会检查所有和报告时钟检测结果有关的变量的完整性。

如果使用HSE,应对另一个应用时钟源(如HSI)进行交叉检查。用户必须修改专用定时器的设置,以确保正确的时钟交叉测量并检查这种测量是否受实际产品支持。

具体来说使用LSI的定时1ms,在这1ms中断中查看TIM3计数值与上次进入时TIM3的计数值是否是在有效范围内(HSE_LimitLow<PeriodValue<HSE_LimitHigh)。

“MM32F013x——IEC60730-1

03、相关外设初始化

在前面有提到过,两个独立时钟源交叉检查来进行测量目前有两种实现方式,分别通过不同时钟源的定时器进行计算比较,另外一种通过输出LSI定时器捕获方式进行比较,MM32的ClassB认证库中有提供了两种实现方式:

1、通过选择LSI作为时钟源的IWDG产生中断与选择HSE作为时钟源的WWDG或TIM3产生中断的方式进行交叉校验。

2、直接通过LSI输出TIM3捕获的方式,该种方式操作简单。

今天我们主要讲解第一种实现方式,对TIM3,IWDG外设的初始化,以及相关的中断进行初始化配置,主要函数有:

HAL_StatusTypeDef Iwdg_Init(unsigned short int IWDG_Prescaler, unsigned short int Reload);
void TIM3_UPCount_test(u16 Prescaler, u16 Period);
ErrorStatus STL_InitClock_Xcross_Measurement(void);
void WWDG_Init(unsigned char ucTcnt, unsigned char ucWcnt);
HAL_StatusTypeDef Iwdg_Init(unsigned short int IWDG_Prescaler, unsigned short int Reload);
void WWDG_IWDG_IRQHandler(void);

04、时钟验证

时钟的验证可以分为5个步骤,如下所示:

1. 保留上一次TIM3计数值

2. 读取当前的TIM3计数值

3. 计算2次TIM3的差

4. 设置相关标记位,做验证流程控制使用

5. 重设LSI时间

void WWDG_IWDG_IRQHandler(void)
{
    uint16_t tmpCC1_last_cpy;

    IWDG->CTRL |= 0x00000002;

    while ((IWDG->RLR) != 0xFFF)
    {
    }
    /* store previous captured value */
    tmpCC1_last_cpy = tmpCC1_last;
    /* get currently captured value */
    tmpCC1_last = (uint16_t)(TIM3->CNT);
    /* The CC4IF flag is already cleared here be reading CCR4 register */

    /* overight results only in case the data is required */
    if (LSIPeriodFlag == 0u)
    {
        /* take correct measurement only */
        {
            /* Compute period length */
            if (tmpCC1_last > tmpCC1_last_cpy)
            {
                PeriodValue = ((uint32_t)(tmpCC1_last) - (uint32_t)(tmpCC1_last_cpy)) & 0xFFFFuL;
            }
            else
            {
                PeriodValue = ((uint32_t)(tmpCC1_last) + SYSTCLK_AT_RUN_HSI / 1000 - 1 - (uint32_t)(tmpCC1_last_cpy)) &
                              0xFFFFuL;
            }

            PeriodValueInv = ~PeriodValue;

            /* Set Flag tested at main loop */
            LSIPeriodFlag = 0xAAAAAAAAuL;
            LSIPeriodFlagInv = 0x55555555uL;
        }
        else
        {
            /* ignore computation in case of IC overflow */
        }
    }

    Iwdg_Init(IWDG_Prescaler_4, (LSI_Freq / 4000 - 2));
    EXTI_ClearITPendingBit(EXTI_Line24); //清除EXTI线24上的IWDG中断
}

计算HSI和HSE的数据的范围:

  /* HSE frequency above this limit considered as harmonics */
  #define HSE_LimitHigh(fcy) ((uint32_t)(((fcy)/LSI_Freq)*8u*5u)/4u) 
  /* (HSEValue + 20%) */

  /* HSE frequency below this limit considered as sub-harmonics*/
  #define HSE_LimitLow(fcy) ((uint32_t)(((fcy)/LSI_Freq)*8u*3u)/4u)  
  /* (HSEValue - 20%) */

  /* here you can define HSI frequency limits  */
  #define HSI_LimitHigh(fcy) ((uint32_t)(((fcy)*6))/(1000*2u*5)) 
  /* (HSIValue + 20%) */

  #define HSI_LimitLow(fcy) ((uint32_t)(((fcy)*4))/(1000*2u*5))  
  /* (HSIValue - 20%) */

判断采集的数据是否在计算的HSI的范围之内:

ClockStatus STL_MainClockTest(void)
{
#if 1
    ClockStatus result = TEST_ONGOING; /* In case of unexpected exit */

    CtrlFlowCnt += CLOCKPERIOD_TEST_CALLEE;

  #ifdef __IAR_SYSTEMS_ICC__ /* IAR Compiler */
    #pragma diag_suppress=Pm026
  #endif /* __IAR_SYSTEMS_ICC__ */

    /* checking result of HSE measurement done at TIM17 interrupt */
    if (((PeriodValue ^ PeriodValueInv) == 0xFFFFFFFFuL)         \
        &&  ((LSIPeriodFlag ^ LSIPeriodFlagInv) == 0xFFFFFFFFuL) \
        &&   (LSIPeriodFlag != 0u))
  #ifdef __IAR_SYSTEMS_ICC__ /* IAR Compiler */
    #pragma diag_default=Pm026
  #endif /* __IAR_SYSTEMS_ICC__ */
    /* 
    {
  #ifdef HSE_CLOCK_APPLIED
        if (PeriodValue < HSE_LimitLow(SYSTCLK_AT_RUN_HSE))
  #else
        if (PeriodValue < HSI_LimitLow(SYSTCLK_AT_RUN_HSI))
  #endif
        {
            result = EXT_SOURCE_FAIL; /* Sub-harmonics: HSE - 20% below expected */
        }
        else
        {
  #ifdef HSE_CLOCK_APPLIED
            if (PeriodValue > HSE_LimitHigh(SYSTCLK_AT_RUN_HSE))
  #else
            if (PeriodValue > HSI_LimitHigh(SYSTCLK_AT_RUN_HSI))
  #endif
            {
                /* Switch back to internal clock */

                result = EXT_SOURCE_FAIL; /* Harmonics: HSE + 20% above expected */
                //result = FREQ_OK; /* Harmonics: HSE + 20% above expected */
            }
            else
            {
                result = FREQ_OK; /* Crystal or Resonator started correctly */
                /* clear flag here to ensure refresh LSI measurement result will be taken at next check */
                LSIPeriodFlag = 0u;
            } /* No harmonics */
        } /* No sub-harmonics */
    }     /* Control Flow error */
    else
    {
        result = CLASS_B_VAR_FAIL;
    }

    CtrlFlowCntInv -= CLOCKPERIOD_TEST_CALLEE;
    return result;
#endif
}

以上流程是系统时钟自检的检测方法,具体的实现方式可以参考例程。

在此功能中,用户还可以通过高精度的HSI \ HSE校准LSI,具体实现方式如下:

启用定时器计数后,当第一个参考信号上升沿发生时,捕捉定时器计数值,储存于ReadValue1中。在第二个上升沿,又捕捉到定时器计数,储存于ReadValue2中。在两个连续上升沿之间的时间 (ReadValue2 - ReadValue1)表示了参考信号的整个周期。

因为定时器计数器的时钟由系统时钟提供(内部RC振荡器HSI或 HSE),因此与参考信号有关的内部 RC 振荡器生成的真正频率为:

Measuredfrequency = (ReadValue2 – ReadValue1) × referencefrequency

误差(单位Hz)为测量频率与典型值之差的绝对值。

因此,内部振荡器频率误差表示为:

Error(Hz) = abs(Measuredfrequency - typicalvalue) 

对每个微调值计算误差之后,算法会决定最优微调值(对应于最接近典型值的频率),然后根据差值就可以进行软件补偿处理。

来源:灵动微电子
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 459

上一章节中我们介绍了RAM检测的实现方法,本章节我们将给大家介绍FLASH检测的实现方法。

测试Flash存储器完整性时,需要用到CRC校验,在参考代码中我们使用了硬件的CRC单元,通过硬件CRC生成器完成的CRC计算大大降低CPU负载。

Flash存储器测试的范围由用户界定,在运行时,如果整个内存范围因测试时间太长而不能接受,用户可将其分成若干片段,Flash存储器测试范围由用户界定,我们需要对被测试区域进行动态修改,以便在那些区域单独执行测试。

检查区域的边界必须与测试中用到的多重测试块的大小一致。根据mm32_STLparam.h文件中定义的参数FLASH_BLOCK_WORDS,块大小默认设为16字( 64字节),检查中未使用的内存区域可通过预定义值来识别。

我们会将CRC校验值存储在某一个地址,然后将CRC计算结果与相应的参考值进行比较,参考模式可由编译器( IAR™)自动提供或由在外部计算后由最终用户添加( MDK-ARM®和AC6)。

在ClassB检测中,Flash存储器检测也分为启动自检与运行自检两部分。

启动时对FLASH的检测方法:

  • 编译时计算整个FLASH的CRC校验值,并存储在FLASH末尾位置
  • 启动时,用同样的算法重新计算整个FLASH的CRC校验值(不包括前面存储在FLASH中的CRC校验值),并与存储在FLASH中的CRC校验值做比较

函数为:

void STL_StartUp(void);

运行时对FLASH的检测方法:

  • 对FLASH分块逐次计算出最终的CRC校验值
  • 将最终结果与正确的CRC校验值做比较

函数为:

ClassBTestStatus STL_crc32Run(void);

01、FLASH验证方法

FLASH的验证分为2个步骤,如下所示:

1. 读取PC端计算FLASH对应的CRC值。

2. MCU运行计算出来的CRC值与读取到的PC端计算的CRC值进行比较。

“MM32F013x——IEC60730-1

1.1、设置IAR生成CRC

1)IAR可以很方便的支持在编译时计算CRC32,并存储在FLASH指定位置。

“MM32F013x——IEC60730-1

Fill unused code memory:定义了FLASH中需要计算CRC的范围和空闲字节填充值;

Checksum size:选择checksum的大小(字节数);

Alignment:指定checksum的对齐方式,不填的话默认2字节对齐;

Algorithm:选择checksum的算法;

Complement:是否需要进行补码计算,选择“As is”就是不进行补码计算;

Bit order:位输出的顺序,MSB first,每个字节的高位在前;LSB first,每个字节的低位在前;

Reverse byte order within word:对于输入数据,在一个字内反转各个字节的顺序;

Initial value:checksum计算的初始化值;

Checksum unit size:选择进行迭代的单元大小,按8-bit,16-bit还是32-bit进行迭代。

2)指定checksum在FLASH中的存储位置——修改linker文件:

place at end of ROM_region { ro section .checksum };

1.2、设置Keil生成CRC脚本

在Keil中需要单独计算CRC的值,并手动添加代码将其放在FLASH末尾。

当MDK在工程编译完成以后会在option for target ->User页面下面,勾选After build/Rebuild中的Run#1选项,则会在工程编译完成以后执行After build/Rebuild中的Run#1选项后面User Command路径选择中选择的脚本(crc_gen_keil.bat脚本)。

“MM32F013x——IEC60730-1

SREC_PATH:设定工具路径;

TARGET_NAME:生成Hex文件名;

MAP_FILE:原始生成的map文件;

INPUT_FILE:原始生产的HEX文件;

OUTPUT_FILE:计算CRC后的CRC文件;

crc_gen_keil.bat脚本内容:

REM Path configuration
SET SREC_PATH=C:\SREC
SET TARGET_NAME=CLASSBTEST
SET TARGET_PATH=OBJ
SET BYTE_SWAP=1
SET COMPARE_HEX=1
SET CRC_ADDR_FROM_MAP=1
REM Not used when CRC_ADDR_FROM_MAP=1
SET CRC_ADDR=0x08007ce0

执行SET批处理命令,将相关的文件赋值给相对应的变量。

REM Derived configuration
SET MAP_FILE=%TARGET_PATH%\%TARGET_NAME%.map
SET INPUT_HEX=%TARGET_PATH%\%TARGET_NAME%.hex
SET OUTPUT_HEX=%TARGET_PATH%\%TARGET_NAME%_CRC.hex
SET TMP_FILE=crc_tmp_file.txt

通过srec_cat.exe工具查找map文件中的__check_sum地址,并把计算的新的CRC的值填入到这个地址。

“MM32F013x——IEC60730-1

1.3、在启动文件中设置CRC校验

AREA    CHECKSUM, DATA, READONLY, ALIGN=6
    EXPORT  __Check_Sum
    ALIGN
   __Check_Sum     DCD     0x8A7A061D;                
   END

1.4、配置硬件CRC

MM32的CRC初始化配置较简单,使能CRC时钟,复位 CRC 计算单元即可。

RCC_AHBPeriphClockCmd(RCC_AHBENR_CRCEN,ENABLE);
CRC_ResetDR();

计算CRC的数值:

 for(index = 0; index < (uint32_t)ROM_SIZEinWORDS; index++)
  {
    CRC->DR = __REV(*((uint32_t *)ROM_START + index));
  }
  crc_result = CRC->DR;

02、MCU运行中计算FLASH中的CRC

把要计算的FLASH大小分成多块累计计算CRC。

uint32_t index;

for(index = 0; index < (uint32_t)FLASH_BLOCK_WORDS; index++)
  {
    CRC->DR = __REV(*(pRunCrc32Chk + index));
   }
  pRunCrc32Chk += FLASH_BLOCK_WORDS; 
  pRunCrc32ChkInv = ((uint32_t *)~((uint32_t)pRunCrc32Chk));

  result = TEST_RUNNING;

2.1 MCU运行中计算的CRC的数值与PC端计算的CRC数值进行比较

如果两者计算的CRC的数值相同则FLASH验证通过,否则验证失败。

启动时对比结果:

#if defined(__CC_ARM) || defined(__GNUC__) 
    if(crc_result != *(uint32_t *)(&REF_CRC32))
  #endif 
    {
      #ifdef STL_VERBOSE_POR
        printf("FLASH 32-bit CRC Error at Start-up\n\r");
      #endif  /* STL_VERBOSE_POR */
        FailSafePOR();    //for ROM test  realse must  open;
        //control_flow_resume(CRC_TEST_CALLER);  
    }
    else
    { /* Test OK */
      #ifdef STL_VERBOSE_POR
        printf(" Start-up FLASH 32-bit CRC OK\n\r");
      #endif  /* STL_VERBOSE_POR */

      control_flow_resume(CRC_TEST_CALLER);  
    }

运行时对比结果:

if(CRC->DR == *(uint32_t *)(&REF_CRC32))
{
   result = TEST_OK;
}
else
{                   
   //result = TEST_OK ;     //  for ROM test  realse must  open;*******
   result = TEST_FAILURE;
}

如果该块的FLASH检测通过,则进行下一次的检测,循环这一个过程。

至此,FLASH的校验已经全部完成,具体的配置程序可以参考官网例程。

来源:灵动微电子
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 558

上一章节中我们介绍了CPU寄存器检测的实现方法,本章节我们将给大家介绍RAM检测的实现方法。

SRAM检测不仅检测数据区域的错误,还检测其内部地址和数据路径的错误。

检测时由于需要执行软件算法(March-C),会影响CPU的利用率,所以SRAM测试采用拓扑模式,按位阵列推进测试,阵列中的逻辑邻位(属于单字)物理上彼此分离,而带后续逻辑地址的字对在物理上却共享邻位,此时可采用按字测试的方法。

SRAM自检采用March-C算法,March-C是一种用于嵌入式芯片RAM测试的算法,是安全认证的一部分,可用于测试那些未被硬件奇偶校验覆盖的部分RAM。为了不影响MCU的运行,将RAM分成很多小块,每次测试其中一块, 先将要测试的块清零,然后按位逐位置1,每置一位,测试该位是不是1,是就继续,不是就报错;全部置完后,再逐位清0,每清一个位,测试该位清0是不是0,如果是就正确,不是就报错。如果是对工作区的RAM,数据需要保留,在RAM中开一个安全保留区,先对安全保留区March-C,然后把要测试的区的数据copy进安全区,再对要测试的工作区进行March-C,测试-- copy进安全区-- 测试-- copy进安全区...... 完成整个空间的测试。

在ClassB检测中,RAM自检也分为启动自检与运行自检两部分。

01、ClassB RAM启动自检

RAM在启动自检过程中会检测全部的RAM。算法中会用值(0x00000000)和值(0xFFFFFFFF)逐字填充,填充后读取出来比较看值是否相等。

具体的运行流程图如下:

“MM32F013x——IEC60730-1

主要有6步:

  • 对所有测试单元写0 (按地址增加的顺序)

  • 逐个检测每个单元是否为0,如果全为0,然后写为0xFF* (按地址增加的顺序)

  • 逐个检测每个单元是否为0xFF*,如果全为F,然后写为0 (按地址增加的顺序)

  • 逐个检测每个单元是否为0,如果全为0,然后写为0xFF* (按地址递减的顺序)

  • 逐个检测每个单元是否为0xFF*,如果全为F,然后写为0 (按地址递减的顺序)

  • 逐个检测每个单元是否为0(按地址递减的顺序)

Class_FullRamMarchC
  PUSH  {R4-R7}
  MOVS  R4, #0x1       ; Test success status by default
  MOVS  R3,R2          ; setup inverted background pattern
  RSBS  R3, R3, #0
  SUBS  R3,R3, #1

; *** Step 1 *** 
; Write background pattern with addresses increasing
  MOVS  R5,R0
__FULL1_LOOP
  CMP   R5,R1
  BHI   __FULL_RET

  LDR   R7,[R5, #+0]

  STR   R2,[R5, #+0]
  LDR   R6,[R5, #+0]
  CMP   R2,R6
  BNE   __FULL_ERR

  STR   R3,[R5, #+0]
  LDR   R6,[R5, #+0]
  CMP   R3,R6  
  BNE   __FULL_ERR

  STR   R7,[R5, #+0]  

  ADDS  R5,R5,#+4
  B     __FULL1_LOOP

; *** Step 2 *** 
; Write background pattern with addresses decreasing  

  MOVS  R5,R1
  SUBS  R5,R5,#+4
__FULL2_LOOP
  CMP   R5,R0
  BLO   __FULL_RET

  LDR   R7,[R5, #+0]

  STR   R2,[R5, #+0]
  LDR   R6,[R5, #+0]
  CMP   R2,R6
  BNE   __FULL_ERR

  STR   R3,[R5, #+0]
  LDR   R6,[R5, #+0]
  CMP   R3,R6  
  BNE   __FULL_ERR

  STR   R7,[R5, #+0]  

  SUBS  R5,R5,#+4
  B     __FULL2_LOOP  

__FULL_ERR
  MOVS  R4,#0       ; error result

__FULL_RET

  MOVS  R0,R4
  POP   {R4-R7}
  BX    LR          ; return to the caller

02、ClassB RAM运行自检程序

RAM在运行自检的过程中只检测部分RAM,不会检测全部的RAM。目前主要自检0x20000030-0x2000007B范围内的RAM以及0x20000000-0x2000001F缓冲区范围的RAM,具体的操作流程如下:

“MM32F013x——IEC60730-1

在mm32_RamMcMxKeil.c进行如下操作:

; ***************** test of the RAM slice *********************
  MOVS  R5, #0       ; NO - save content of the RAM slice into the backup buffer
__SAVE_LOOP
  LDR   R6,[R4, R5]  ; load data offset
  LDR   R7,[R0, R6]  ; load data from RAM
  ADDS  R5,R5,#4     ; original data are stored starting from second item of the buffer
  STR   R7,[R1, R5]  ; (first and last items are used for testing purpose exclusively)
  CMP   R5, #20
  BLE   __SAVE_LOOP

; *** Step 1 *** 
; Write background pattern with addresses increasing
  MOVS  R5, #0
__STEP1_LOOP
  LDR   R6,[R4, R5]  ; load data offset
  STR   R2,[R0, R6]  ; store background pattern
  ADDS  R5,R5,#4
  CMP   R5, #20
  BLE   __STEP1_LOOP

; *** Step 2 ***
; Verify background and write inverted background with addresses increasing
  MOVS  R5, #0
__STEP2_LOOP
  LDR   R6,[R4, R5]  ; load data offset
  LDR   R7,[R0, R6]  ; verify background pattern
  CMP   R7, R2
  BNE   __STEP_ERR
  STR   R3,[R0, R6]  ; store inverted background pattern
  ADDS  R5,R5,#4
  CMP   R5, #20
  BLE   __STEP2_LOOP

在MDK工程的mm32_STLClassBvar.h文件中设置一些变量进行定址,并在工程配置中设定区域。

 /* RAM location for temporary storage of original values at run time RAM transparent test */
  EXTERN uint32_t aRunTimeRamBuf[RT_RAM_BLOCKSIZE + 2] __attribute__((section("RUN_TIME_RAM_BUF")));

  /* RAM pointer for run-time tests */
  EXTERN uint32_t *pRunTimeRamChk        __attribute__((section("RUN_TIME_RAM_PNT")));
  EXTERN uint32_t *pRunTimeRamChkInv     __attribute__((section("RUN_TIME_RAM_PNT")));
  EXTERN uint32_t aGAP_FOR_RAM_TEST_OVERLAY[2] __attribute__((section("RUN_TIME_RAM_PNT")));

  /*Note:the zero_init forces the linker to place variables in the bsssection */
  /*This allows the UNINIT directive(in scatter file)to work. On the contrary */
  /* all Class B variables pairs should be initialized properly by user before using them */

  /* Counter for verifying correct program execution at start */
  EXTERN uint32_t CtrlFlowCnt             __attribute__((section("CLASS_B_RAM"), zero_init));
  EXTERN uint32_t CtrlFlowCntInv          __attribute__((section("CLASS_B_RAM_REV"), zero_init));

  /* Counter for verifying correct program execution in interrupt */
  EXTERN uint32_t ISRCtrlFlowCnt          __attribute__((section("CLASS_B_RAM"), zero_init));
  EXTERN uint32_t ISRCtrlFlowCntInv       __attribute__((section("CLASS_B_RAM_REV"), zero_init));

  /* LSI period measurement at TIM5 IRQHandler */
  EXTERN uint32_t PeriodValue           __attribute__((section("CLASS_B_RAM"), zero_init));
  EXTERN uint32_t PeriodValueInv        __attribute__((section("CLASS_B_RAM_REV"), zero_init));

  /* Sofware time base used in main program (incremented in SysTick timer ISR */
  EXTERN uint32_t TickCounter             __attribute__((section("CLASS_B_RAM"), zero_init));
  EXTERN uint32_t TickCounterInv          __attribute__((section("CLASS_B_RAM_REV"), zero_init));

  /* Indicates to the main routine a 100ms tick */
  EXTERN __IO uint32_t TimeBaseFlag       __attribute__((section("CLASS_B_RAM"), zero_init));
  EXTERN __IO uint32_t TimeBaseFlagInv    __attribute__((section("CLASS_B_RAM_REV"), zero_init));

  /* Indicates to the main routine a 100ms tick */
  EXTERN __IO uint32_t LSIPeriodFlag      __attribute__((section("CLASS_B_RAM"), zero_init));
  EXTERN __IO uint32_t LSIPeriodFlagInv   __attribute__((section("CLASS_B_RAM_REV"), zero_init));

  /* Stores the Control flow counter from one main loop to the other */
  EXTERN uint32_t LastCtrlFlowCnt         __attribute__((section("CLASS_B_RAM"), zero_init));
  EXTERN uint32_t LastCtrlFlowCntInv      __attribute__((section("CLASS_B_RAM_REV"), zero_init));

  /* Pointer to FLASH for crc32 run-time tests */
  EXTERN uint32_t *pRunCrc32Chk           __attribute__((section("CLASS_B_RAM"), zero_init));
  EXTERN uint32_t *pRunCrc32ChkInv        __attribute__((section("CLASS_B_RAM_REV"), zero_init));

/* Reference 32-bit CRC for run-time tests */
  EXTERN uint32_t RefCrc32                __attribute__((section("CLASS_B_RAM"), zero_init));
  EXTERN uint32_t RefCrc32Inv             __attribute__((section("CLASS_B_RAM_REV"), zero_init));
  EXTERN uint32_t LSI_Freq                __attribute__((section("CLASS_B_RAM"), zero_init));
  /* Magic pattern for Stack overflow in this array */
  EXTERN __IO uint32_t aStackOverFlowPtrn[4]   __attribute__((section("STACK_BOTTOM"), zero_init));

在MDK的Options for Target 选项卡中进行如下配置:

“MM32F013x——IEC60730-1

在ClassBtest.sct文件中可以看到如下结果:

“MM32F013x——IEC60730-1

栈检查的操作原理:设定特定的值在栈底,当检测到栈底的数值发生变化了,就认为栈溢出了。

 control_flow_call(STACK_OVERFLOW_TEST);
    aStackOverFlowPtrn[0] = 0xEEEEEEEEuL;
    aStackOverFlowPtrn[1] = 0xCCCCCCCCuL;
    aStackOverFlowPtrn[2] = 0xBBBBBBBBuL;
    aStackOverFlowPtrn[3] = 0xDDDDDDDDuL;
    control_flow_resume(STACK_OVERFLOW_TEST);

/**
  * @brief  This function verifies that Stack didn't overflow
  * @param  : None
  * @retval : ErrorStatus = (ERROR, SUCCESS)
  */
ErrorStatus STL_CheckStack(void)
{
  ErrorStatus result = SUCCESS;

  CtrlFlowCnt += STACK_OVERFLOW_CALLEE;

  if ( aStackOverFlowPtrn[0] != 0xEEEEEEEEuL )
  {
    result = ERROR;
  }
  if ( aStackOverFlowPtrn[1] != 0xCCCCCCCCuL )
  {
    result = ERROR;
  }
  if ( aStackOverFlowPtrn[2] != 0xBBBBBBBBuL )
  {
    result = ERROR;
  }
  if ( aStackOverFlowPtrn[3] != 0xDDDDDDDDuL )
  {
    result = ERROR;
  }
  CtrlFlowCntInv -= STACK_OVERFLOW_CALLEE;
  return (result);
}

以上代码是RAM自检的汇编代码,整个算法执行会花费一定时间,如果整个内存范围的测试时间太长,就会对用户应用程序造成较大的局限性,所以将其分成若干片段,这些片段与执行程序的本地区域相一致。对被测试区域进行动态修改,然后进行单独执行测试。检测出问题则会跳转后面的故障函数,可以添加对应的操作进行故障处理。

来源:灵动微电子
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 633

上一章节中我们介绍了主要介绍了ClassB软件的作用、框架和流程等,本章节将给大家介绍CPU寄存器检测实现方法。

01、CPU寄存器自检

R0 ~ R12我们称为通用寄存器,其中寄存器R0 ~ R7我们称为低寄存器,寄存器R8 ~ R12我们称为高寄存器,可用于数据操作。

在对R0 ~ R12寄存器进行操作时需要注意:绝大多数 16 位 Thumb 指令只能访问 R0‐R7,而 32 位 Thumb‐2 指令可以访问所有寄存器。

R13作为堆栈指针 SP,SP有两个:

主堆栈指针(MSP):复位后缺省使用的堆栈指针,用于操作系统内核以及异常处理例程(包括中断服务例程)。

进程堆栈指针(PSP):由用户的应用程序代码使用。

R14连接寄存器:当呼叫一个子程序时,由 R14 存储返回地址。

CPU自检在启动时和运行时都会进行,在启动时,所有寄存器(R0~R12,PSP,MSP)和标记的功能测试都会进行一次自检;运行时,周期性自检,不检测R13,R14,仅检测寄存器R0~R12。

02、CPU寄存器测试原理

在mm32_cpu_startKeil.s与mm32_cpu_runKeil.s文件中会对CPU寄存器进行测试,其中mm32_cpu_startKeil.s文件中的测试为启动过程中对CPU寄存器进行的测试,mm32_cpu_runKeil.s文件中的测试为运行自检流程中的CPU寄存器测试,其原理为对寄存器每一位写0和写1操作,对比是否有错误。

具体实现:分别写入0xAAAAAAAA和0x55555555再进行比较是否为写入的值。

03、CPU寄存器测试实现

以启动时CPU自检程序为例进行分析,将conAA数据定义为0xAAAAAAAA,将con55定义为0x55555555,并使用EXPORT指令进行声明。

conAA       DCD     0xAAAAAAAA
con55       DCD     0x55555555 
con80       DCD     0x80000000
conA8       DCD     0xAAAAAAA8
con54       DCD     0x55555554

EXPORT conAA
EXPORT con55

声明函数ClassB_StartUpCPUTest函数,并进行CPU寄存器中的R1寄存器的检测,先将conAA的数据赋值给R0寄存器,然后赋值给R1寄存器,然后比较R0与R1寄存器的数据,如果两者的数据相同则将con55分别赋值给R1与R0寄存器进行比较,否则会跳转执行CPUTestFail函数,如果两者的数据相同则将0x01赋值给R1寄存器,否则将会跳转到CPUTestFail函数中。

    ; Register R1
    LDR R0, =conAA
    LDR R1,[R0]
    LDR R0,[R0]
    CMP R0,R1
    BNE CPUTestFail
    LDR R0, =con55
    LDR R1,[R0]
    LDR R0,[R0]
    CMP R0,R1
    BNE CPUTestFail
    MOVS R1, #0x1             ; For ramp test

在汇编程序中大家可以看到,如果检测失败,会跳转到CPUTestFail函数执行,如果检测成功则进行下一步的检测。

依次针对R2 ,R3,R4,R5,R6,R7,R8,R9,R10,R11,R12寄存器进行与R1一样的操作,在上述的寄存器比较完成以后相应的寄存器会被赋予新的数据。

Class_StartUpCPUTest PROC
      EXPORT Class_StartUpCPUTest

    PUSH {R4-R6}              ; Safe critical registers

    MOVS R0, #0x00            
    UXTB R0,R0
    ADDS R0,#0                            ; Set Z(ero) Flag
    BNE CPUTestFail        ; Fails if Z clear
    BMI CPUTestFail        ; Fails if N is set
    SUBS R0,#1             ; Set N(egative) Flag
    BPL CPUTestFail        ; Fails if N clear
    ADDS R0,#2              ; Set C(arry) Flag and do not set Z
    BCC CPUTestFail         ; Fails if C clear
    BEQ CPUTestFail         ; Fails if Z is set
    BMI CPUTestFail         ; Fails if N is set

    LDR R0,=con80                 ; Prepares Overflow test
    LDR R0,[R0]
    ADDS R0, R0, R0          ; Set V(overflow) Flag
    BVC CPUTestFail          ; Fails if V clear

    ; This is for control flow test (ENTRY point)
    LDR R0,=CtrlFlowCnt
    LDR R1,[R0]
    ADDS R1,R1,#0x3             ; CtrlFlowCnt += OxO3
    STR R1,[R0]

    ; Register R1
    LDR R0, =conAA
    LDR R1,[R0]
    LDR R0,[R0]
    CMP R0,R1
    BNE CPUTestFail
    LDR R0, =con55
    LDR R1,[R0]
    LDR R0,[R0]
    CMP R0,R1
    BNE CPUTestFail
    MOVS R1, #0x1             ; For ramp test

    ; Register R2
    LDR R0, =conAA
    LDR R2,[R0]
    LDR R0,[R0]
    CMP R0,R2
    BNE CPUTestFail
    LDR R0, =con55
    LDR R2,[R0]
    LDR R0,[R0]
    CMP R0,R2
    BNE CPUTestFail
    MOVS R2, #0x2             ; For ramp test

    ; Register R3
    LDR R0, =conAA
    LDR R3,[R0]
    LDR R0,[R0]
    CMP R0,R3
    BNE CPUTestFail
    LDR R0, =con55
    LDR R3,[R0]
    LDR R0,[R0]
    CMP R0,R3
    BNE CPUTestFail
    MOVS R3, #0x3             ; For ramp test

    ; Register R4
    LDR R0, =conAA
    LDR R4,[R0]
    LDR R0,[R0]
    CMP R0,R4
    BNE CPUTestFail
    LDR R0, =con55
    LDR R4,[R0]
    LDR R0,[R0]
    CMP R0,R4
    BNE CPUTestFail
    MOVS R4, #0x4             ; For ramp test

    ; Register R5
    LDR R0, =conAA
    LDR R5,[R0]
    LDR R0,[R0]
    CMP R0,R5
    BNE CPUTestFail
    LDR R0, =con55
    LDR R5,[R0]
    LDR R0,[R0]
    CMP R0,R5
    BNE CPUTestFail
    MOVS R5, #0x5             ; For ramp test

    ; Register R6
    LDR R0, =conAA
    LDR R6,[R0]
    LDR R0,[R0]
    CMP R0,R6
    BNE CPUTestFail
    LDR R0, =con55
    LDR R6,[R0]
    LDR R0,[R0]
    CMP R0,R6
    BNE CPUTestFail
    MOVS R6, #0x6             ; For ramp test

    ; Register R7
    LDR R0, =conAA
    LDR R7,[R0]
    LDR R0,[R0]
    CMP R0,R7
    BNE CPUTestFail
    LDR R0, =con55
    LDR R7,[R0]
    LDR R0,[R0]
    CMP R0,R7
    BNE CPUTestFail
    MOVS R7, #0x7             ; For ramp test

    ; Register R8
    LDR R0, =conAA
    LDR R0,[R0]
    MOV R8,R0
    CMP R0,R8
    BNE CPUTestFail
    LDR R0, =con55
    LDR R0,[R0]
    MOV R8,R0
    CMP R0,R8
    BNE CPUTestFail
    MOVS R0, #0x08            ; For ramp test
    MOV    R8,R0

    BAL CPUTstCont

CPUTestFail
    BLAL FailSafePOR

CPUTstCont
    ; Register R9
    LDR R0, =conAA
    LDR R0,[R0]
    MOV R9,R0
    CMP R0,R9
    BNE CPUTestFail
    LDR R0, =con55
    LDR R0,[R0]
    MOV R9,R0
    CMP R0,R9
    BNE CPUTestFail
    MOVS R0, #0x09            ; For ramp test
    MOV    R9,R0

    ; Register R10
    LDR R0, =conAA
    LDR R0,[R0]
    MOV R10,R0
    CMP R0,R10
    BNE CPUTestFail
    LDR R0, =con55
    LDR R0,[R0]
    MOV R10,R0
    CMP R0,R10
    BNE CPUTestFail
    MOVS R0, #0x0A            ; For ramp test
    MOV    R10,R0

    ; Register R11
    LDR R0, =conAA
    LDR R0,[R0]
    MOV R11,R0
    CMP R0,R11
    BNE CPUTestFail
    LDR R0, =con55
    LDR R0,[R0]
    MOV R11,R0
    CMP R0,R11
    BNE CPUTestFail
    MOVS R0, #0x0B            ; For ramp test
    MOV    R11,R0

    ; Register R12
    LDR R0, =conAA
    LDR R0,[R0]
    MOV R12,R0
    CMP R0,R12
    BNE CPUTestFail
    LDR R0, =con55
    LDR R0,[R0]
    MOV R12,R0
    CMP R0,R12
    BNE CPUTestFail
    MOVS R0, #0x0C            ; For ramp test
    MOV    R12,R0
    LDR R0, =CPUTstCont

R1~R12寄存器比较完成以后会在Ramp中做进一步的判断,判断R1寄存器的数值是否为0x01,R2寄存器的数值是否为0x02,依次判断到R12寄存器,如果以上的判断都是正确的则说明R1~R12寄存器验证通过。

 ; Ramp pattern verification    (R0 is not tested)
    CMP R1, #0x01
    BNE CPUTestFail
    CMP R2, #0x02
    BNE CPUTestFail
    CMP R3, #0x03
    BNE CPUTestFail
    CMP R4, #0x04
    BNE CPUTestFail
    CMP R5, #0x05
    BNE CPUTestFail
    CMP R6, #0x06
    BNE CPUTestFail
    CMP R7, #0x07
    BNE CPUTestFail
    MOVS R0, #0x08
    CMP R0,R8
    BNE CPUTestFail
    MOVS R0, #0x09
    CMP R0,R9
    BNE CPUTestFail
    MOVS R0, #0x0A
    CMP R0,R10
    BNE CPUTestFail
    MOVS R0, #0x0B
    CMP R0,R11
    BNE CPUTestFail
    MOVS R0, #0x0C
    CMP R0,R12
    BNE CPUTestFail

对R13的自检功能与通用寄存器的操作不同,需要先保存堆栈到R0,操作如下:

   ; Process Stack pointer (banked Register R13)
    MRS R0,PSP          ; Save process stack value

    LDR R1, =conA8      ; Test is different (PSP is word aligned, 2 LSB cleared)
    LDR R1,[R1]
    MSR PSP,R1          ; load process stack value
    MRS R2,PSP          ; Get back process stack value
    CMP R2,R1           ; Verify value
    BNE CPUTestFail

    LDR R1, =con54      ; Test is different (PSP is word aligned, 2 LSB cleared)
    LDR R1,[R1]
    MSR PSP,R1          ; load process stack value
    MRS R2,PSP          ; Get back process stack value
    CMP R2,R1           ; Verify value
    BNE CPUTestFail

    MSR PSP, R0         ; Restore process stack value

    ; Stack pointer (Register R13)
    MRS R0,MSP          ; Save stack pointer value

    LDR R1, =conA8      ; Test is different (SP is word aligned, 2 LSB cleared)
    LDR R1,[R1]
    MSR MSP,R1            ; load SP value
    MRS R2,MSP            ; Get back SP value
    CMP R2,R1            ; Verify value
    BNE CPUTestFail

    LDR R1, =con54
    LDR R1,[R1]            ; load SP value
    MSR MSP,R1            ; Get back SP value
    MRS R2,MSP            ; Verify value
    CMP R2,R1
    BNE CPUTestFail

    MSR MSP,R0            ; Restore stack pointer value

    ; Control flow test (EXIT point)
    LDR R0,=CtrlFlowCntInv
    LDR R1,[R0]
    SUBS R1,R1,#0x3        ; CtrlFlowCntInv -= OxO3
    STR R1,[R0]

    POP {R4-R6}            ; Restore critical registers

    ; If next instruction is not executed, R0 will hold a value different from 0
    MOVS R0, #0x1       ; CPUTEST_SUCCESS
    BX LR               ; return to the caller

    ALIGN

    ENDP

  END

以上代码是在启动时CPU寄存器自检的汇编代码,在运行时CPU寄存器自检功能与启动自检的代码原理相同,只是对寄存器操作个数有差异,运行时不用对R13\14进行操作。

在汇编程序中我们会使用CMP指令进行判断,后面会跟着BNE指令,其作用就是判断如果CMP判断的两个寄存器不相等,则直接执行BNE指令后面的CPUTestFail函数,直接提醒错误报警,或可以添加对应的处理机制。

来源:灵动微电子
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 399

家用电器制造商在设计时就被要求考虑到设备的安全性,产品的硬件和软件要求遵循相关的安全标准,所以很多新技术解决方案被应用在产品中。

IEC60730 ClassB 认证简介

在全球范围内,IEC(国际电工委员会)、UL(美国保险商实验室)及CSA(加拿大标准协会)颁布了全球公认标准,主要针对电气设备有了规范和要求。该标准被TUV及VDE(大部分在欧洲)、UL及CSA(目标市场在美国和加拿大)等机构认可,并被要求应用在认证程序中。

IEC(国际电工委员会)是由所有国家电工委员会组成的世界性标准化组织,IEC针对家用电器的正常安全运行制定了安全标准IEC60730。

IEC60730安全标准里面定义了多种测试和诊断方法,确保面向家用电器的嵌入式控制硬件和软件的安全运行。IEC60730安全标准主要针对安全和性能、环境、电能效率及其可再生能力,其标准为硬件及软件解决方案提供建议,根据应用目的,这些标准可分为若干安全等级。

家用电器制造商必须采取措施,以确保其产品的安全和可靠地运行,以符合IEC60730标准。IEC60730标准涵盖了交流电器的机械、电气、电子、EMC和交流电器的异常操作,包括为自动电子控制定义的三种软件分类:

A类功能,如房间恒温器、湿度控制、照明控制、计时器和开关,它们的区别在于不依赖于设备的安全。
B类功能,如热切断装置,旨在防止洗衣机、洗碗机、烘干机、冰箱、冰柜和炊具等电器的不安全操作。
C类功能,旨在防止特殊危险,如爆炸。这些包括自动燃烧器控制和封闭的热水器的热切断。
MM32开发出多种安全特性和软件开发包(包括IEC60730安全库),可帮助大型电器市场的自动化控制制造商满足IEC60730 B类规范。

ClassB 验证

#01、ClassB 软件库

1.1 评估的目的

对软件启动时自检和运行时自检,对风险控制措施进行评估;
确保在使用电器时安全,对人体不会造成伤害。

1.2 评估的内容

对硬件结构和软件结构的综合检查,防止系统性问题;
对软件开发过程的评估,针对程序潜在性风险进行检查。

1.3 评估的对象

家用电器如果同时具备以下两个条件,就应当进行软件评估:

1、使用可编程电子电路,即嵌入式微控制器MCU;
2、可编程电子电路具有安全保护功能。

比如具有过热控制的电磁炉,带自动门锁控制的洗衣机等。

如果MCU仅实现产品功能,安全保护由硬件进行,这类家电不需要进行软件评估。

#02、MM32提供的示例代码测试项

(1) CPU寄存器测试
(2) 时钟验证测试
(3) 不变存储器FLASH验证测试
(4) 可变存储器RAM验证测试
(5) 看门狗验证测试
(6) 控制流程检查验证测试

MM32 ClassB所有示例代码的源码向所有有需要的用户开放,用户可以将它们添加到现有的项目中。

#03、验证方法

3.1 MCU相关故障检测

“MM32F013x——IEC60730-1

3.2 应用相关故障检测

“MM32F013x——IEC60730-1

其他的对应控制模块也需要从应用层进行自检,具体的实现方式需要根据具体的应用需求进行评估,今天不做过多的假设。

#04、系统测试流程

MCU在上电的时候会进行复位启动,在启动完成以后会进行运行自检,ClassB系统流程如下:

“MM32F013x——IEC60730-1

4.1 启动自检

CPU寄存器进行自检操作,如果检测没有故障则进行看门狗的自检,如果有故障则进行故障的处理。看门狗自检流程与CPU寄存器自检的流程一致,后面依次会进行FLASH,RAM以及时钟自检,启动自检完成以后会进入程序的主循环函数中。

启动自检流程如下:

“MM32F013x——IEC60730-1

4.2 运行自检

在主循环中会定期的进行自检操作,在进行自检操作的时候会依次进行如下操作:CPU寄存器的自检、栈自检、时钟自检、FLASH自检、并设置定时器定时进行RAM自检,如果在自检的过程中出现故障则会进行故障的处理。

运行自检流程如下:

“MM32F013x——IEC60730-1

以上就是ClassB的整体的测试流程。

#05、代码整合

如果用户已经开发好相关的应用控制程序,只需要在原有的应用程序中添加ClassB的程序,操作方法如下:

1、在原有的工程框架中添加ClassB的相关.c和.s文件。

“MM32F013x——IEC60730-1

2、在执行用户程序之前,先执行相关的变量初始化及监测、校准时钟。

STL_InitRunTimeChecks();

3、设置WWDG和IWDG功能,防止其在程序正常运行时复位。

WWDG_Init(0xFF,0x7F);
Iwdg_Init(IWDG_Prescaler_4, (LSI_Freq/4000-2));

4、设置启动和运行时的RAM和FLASH检测范围,CRC校验的范围,checksum在Flash中存储的位置。
STL_FlashCrc32Init();

5、ClassB变量的存储地址范围,堆栈边界检测区的位置,对检测到的故障进行处理。

6、根据具体的应用,增加用户相关的故障检测内内容,根据具体应用定义程序运行时自检的频率,相关的变量设置MM32_STLparam.h文件中。

7、主循环中调用STL_DoRunTimeChecks()

STL_DoRunTimeChecks();

今天主要介绍了ClassB软件的作用、框架和流程等,后续文章将逐一介绍MM32F013x在各个自检过程中的实现方式。

来源:灵动微电子
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 752

01、开发环境描述

Cortex-M0型号:MM32F0133C7P
下载器与调试接口:MM32 DAP-Link + SWD
操作系统:Ubuntu20.0.4
集成开发环境平台:eclipse IDE for C/C++ developers
交叉编译链:arm-none-eabi-gcc
调试服务器:JLink GDB Server

02、安装eclipse IDE for C/C++ developers

2.1、准备工作

需要下载两个软件包:

JDK:
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

JDK是Eclipse的必要运行环境,本次实验使用的是jdk-8u231-linux-x64.tar.gz。

Eclipse:

https://www.eclipse.org/downloads/packages/

Eclipses根据开发语言选择合适的安装包,我们主要用C/C++开发,使用的是
Eclipse IDE for C/C++ Developers linux 64-bit。

注:安装包版本可自行选择,但要注意一点,新版本的eclipse可能会与低版本的JDK不兼容。

2.2、JAVA环境

在/opt文件夹下新建一个jvm目录,将解压后得到的jdk1.8.0_231(取决于JDK版本)移动到新建的jvm目录下,添加java路径后,在终端中键入命令。

sudo mkdir /opt/jvm
sudo gedit .profile

在文件最后添加如下内容。(JDK版本号可能有所不同,文件夹的名称由实际的文件夹名称为准)

# java path
export JAVA_HOME=/opt/jvm/jdk1.8.0_231
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

最后键入命令使得路径生效。

source .profile

此时,在终端中键入java版本查看命令。

java -version

如果出现如下信息,,则说明JAVA环境配置成功。

“”

2.3、安装Eclipse

解压eclipse的软件包会得到一个eclipse的文件夹,将其移动到/opt目录下,建立jre软连接。

sudo mkdir /opt/eclipse/jre
sudo ln -s /opt/jvm/jdk1.8.0_231/bin /opt/eclipse/jre/

添加eclipse的桌面图标。

sudo gedit /usr/share/applications/eclipse.desktop

在打开的文件中键入。(注意Exe和Icon路径是否正确)

[Desktop Entry]
Encoding=UTF-8
Name=Eclipse
Comment=Eclipse
Exec=/opt/eclipse/eclipse 
Icon=/opt/eclipse/icon.xpm
Terminal=false
StartupNotify=true
Type=Application
Categories=Application;Development;

接着ctrl+s保存文件,然后赋予可执行权限。

sudo chmod u+x /usr/share/applications/eclipse.desktop

最后将eclipse.desktop复制到桌面或者固定在dock上,后面就可以通过图标启动eclipse。

2.4、安装交叉编译链arm-none-eabi-gcc

我们从ARM官方选择合适的版本下载(此处选择了Linux64):

https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads

“”

下载的为tar.ba2格式压缩包,使用命令tar -jxf <要解压的文件>解压到我们要安装的目录:

“”

为了以后使用方便,将文件夹重命名:

“”

它下面的bin目录就是我们要使用的编译工具链:

“”

接下来我们要将bin目录添加到环境变量,这样可以直接在命令行输入要使用的工具名,然后系统就可以找到该工具,在此我们仅为当前用户添加环境变量,使用gedit ~/.bashrc编辑当前用户配置文件,在文件最后添加:

export PATH=$PATH:/home/neomissing/gcc-arm-none-eabi/bin

“”

然后使用命令source ~/.bashrc更新系统路径,使添加的环境变量立即生效:

“”

然后输入命令arm-none,然后按三下Tab(一定不要输入全部),检查系统是否可以自动补全。如果系统可以提示,说明环境变量配置成功,可以放心使用arm-none-eabi工具链。

2.5、安装GNU ARM Eclipse插件包

The recommended way to install these plug-ins is to use the Eclipse standard install/update mechanism: In the Eclipse menu: Help → Install New Software…
in the Install window, click the Add… button (on future updates, just select the URL in theWork with: combo)
fill in Name: with GNU ARM Eclipse Plug-ins
fill in Location:
with http://gnuarmeclipse.sourceforge.net/updates

“”

click the Add button
normally the main window should list a group named CDT GNU Cross Development Tools; expand it select all the plug-ins (the one marked End of life is needed only for compatibility with previous version, normally can be safely skipped)
click the Next button and follow the usual installation procedure

“”

Once you define the update site URL, further updates are greatly simplified (Help → Check For Updates)。

2.6、安装OpenOCD

安装openocd 打开Ubuntu终端(alt+ctrl+t)输入命令:

sudo apt install openocd

完成之后 输入openocd查看版本信息。

“”

目前默认安装的openocd芯片支持不全,找到openocd安装路径删除,复制替换MindMotion提供的openocd即可。

检查MM32 DAP-Link连接正常。

打开Ubuntu终端(alt+ctrl+t)输入lsusb,查看连接如下:

“”

03、创建MM32工程并配置、编译、调试

3.1、工程创建

打开eclipse,创建一个c工程,点击file,新建New Project,选择C Project。

“”

点击next,输入工程名字test,如下配置:

“”

连续点击next。

“”

选择选择工具链路径,这一路径就是我们第二步中arm-none-eabi-gdb的路径,需要匹配,点击finish。

“”

添加自己的工程文件,然后指定头文件路径以及配置路径,在工程浏览器中选中工程,右键单击选择Properties,再选择C/C++ Build-->Settings,跳出如下界面:

“”

配置汇编器Cross ARM GNU Assembler,主要是添加预处理宏:

“”

配置编译器Cross ARM C Compiler,添加预处理宏。

“”

添加头文件搜索目录。

“”

配置连接器 Cross ARM C Linker,主要是选择连接脚本文件。

“”

3.2、编译工程

选择Project-> Build Project选项来编译整个工程。

“”

3.3、连接目标板

在eclipse中配置openocd,连接目标板。点击External Tools Configurations,双击Program选项。

“”

然后打开终端输入如下命令 which openocd,找可执行文件openocd的路径,然后把路径复制到Location一栏,在Arguments一栏中输入如下配置信息:

-f /usr/share/openocd/scripts/interface/cmsis-dap.cfg
-f /usr/share/openocd/scripts/target/mm32f013x.cfg

其中的mm32f013x.cfg需要根据你的目标板上的MCU不同而会改变。

此配置文件路径是安装openocd通过命令sudo apt install openocd安装的,是系统默认路径。如果用户在此路径中找不到相关文件,那么请自行查找与修改路径。

“”

“”

然后点击Apply,Run后会在窗口出现信息,表示连接成功。

3.4、Debug调试

配置debug环境:点击Debug Configurations选项,双击GDB OpenOCD Debugging在窗口点击Debugger一栏。

“”

分别修改如下三个窗口的内容,第一个为可执行openocd命令所在路径,第二个为openocd连接DAP-Link与目标板的命令,第三个为工具链arm-none-eabi-gdb等所在的路径。

“”

点击Apply,Debug就可以开始调试。

来源:灵动微电子
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 135

在前面几个专题中介绍了Ozone、RTT-Viewer、J-Scope在MM32F013x上的使用方法,虽然以上的几种方式简单方便,但是也仅仅局限于J-Link工具,而使用Event Recorder则没有以上的限制,Event Recorder是MDK在5.22版本加入的新功能,使用标准SWD的下载接口,基本上适用于各种Link,只要MDK v5.22以上的版本即可,支持时间测量、功耗测量、printf打印等。到了5.25版本以后,这个功能更加的完善,并且增加了时间测量等功能。

Event Recorder简介

Event Recorder的特色主要有以下几个方面:

1. 提升应用程序动态执行期间的检测能力。

2. 支持事件类型滤除机制,比如运行错误,API调用,内部操作和操作信息的区分。

3. 可以在任务中,RTOS内核中和中断服务程序中任意调用。

4. 对于带ITM功能的Cortex-M3/M4/M7/M33内核芯片,执行记录期间,全程无需开关中断操作。

对于不带ITM功能的Cortex-M0/M0+/M23,是需要开关中断的。

5. 支持printf重定向。

6. 各种Link通吃,支持SWD接口或者JTAG接口方式的J-Link、U-Link和CMSIS-DAP。

7. 对于带DWT时钟周期计数器功能的Cortex-M3/M4/M7/M33内核芯片,创建时间戳时,可以有效降低系统负担,无需专用定时器来实现。

8. Event Recorder执行时间具有时间确定性,即执行的时间是确定的,而且执行速度超快,因此,实际产品中的代码依然可以带有这部分,无需创建debug和release两种版本。

9. RTX5及其所有中间件都支持Event Recorder调试。

创建Event Recorder工程

01、添加Event Recorder组件

使用MDK5.25以上版本创建MM32F013x工程模板(或者是能够被MDK5.25版本正确打开的工程),具体的工程搭建这里不做过多的介绍,具体请参考MM32官方的工程搭建文档。

“”

“”

打开RTE(Manage Run-Time-Environment窗口),在Compiler组件下勾选Event Recorder,并在Compiler组件下面的I/O组件下中的STDOUT中的Variant修改为EVR,此处的修改是为了将printf函数冲定向到Event Recorder上。

在需要使用Event Recorder的代码处,直接调用Event Recorder的API函数,如果需要使用printf函数的地方直接使用printf函数输出即可,在MDK中需要勾选MicroLib,并删掉文件中的重定向函数。

“”

添加Event Recorder的功能以后,打开EventRecorderConf.h,具体的配置如下:

“”

Number of Recorder:表示Event Recorder缓冲可以记录的消息条数。

Time Stamp Source:表示时间戳来源,有如下四种可以选择,我们这里使用DWT时钟周期计数器。
到此,添加工程文件已经完成。

“”

02、Event Recorder注意事项

使用CMSIS软件包一定要使用当前最新的,否则可能会出现错误。

下载完成以后需要将工程下面Device文件夹下面的\CMSIS\KEIL_CORE文件中的文件替换为MDK安装路径下面ARM\PACK\ARM\CMSIS\5.3.0\CMSIS\Include中的文件。

03、Event Recorder代码实现

在主函数中加入Event Recorder初始化函数:

EventRecorderInitialize(EventRecordAll, 1U);
EventRecorderStart();

在主函数的wilie循环中加入以下函数:

EventStartA(0);
BSP_LED1_TOGGLE();
EventStopA(0);

EventStartA(1);
BSP_LED2_TOGGLE();
EventStopA(1);

EventStartA(2);
BSP_LED3_TOGGLE();
EventStopA(2);

t0++;
EventStartAv(3, t0, t0);
BSP_LED4_TOGGLE();
EventStopAv(3, t0, t0);
t1 += 1;
t2 += 2;
EventRecord2(1 + EventLevelAPI, t1, t2);

04、API接口函数

时间测量功能简单易用,仅需一个起始函数,一个停止函数即可。当前支持4组,每组支持16路测量,也就是可以同时测量64路。

时间测量的API函数支持多任务和中断里面随意调用,测量起始函数:EventStartG(slot) 或者EventStartGv (slot, val1, val2),函数中的字母G是表示分组A,B,C,D,即实际调用函数为EventStartA,EventStartB,EventStartC和EventStartD,函数的第一个形参slot的范围是0-15,也就是每个分组可以测试16路,函数后面的两个形象val1和val2是32位变量,用户可以用这两个形参来传递变量数值给Event Statistics调试组件里面,方便图形化展示。简单的说,这两个变量仅仅起到一个传递变量数值的作用。

测量停止函数:EventStopG(slot) 或者EventStopGv (slot, val1, val2),函数中的字母G是表示分组A,B,C,D,即实际调用函数为EventStopA,EventStopB,EventStopC和EventStopD,函数的第一个形参slot的范围是0-15,也就是每个分组可以测试16路,函数后面的两个形象val1和val2是32位变量,用户可以用这两个形参来传递变量数值给Event Statistics调试组件里面,方便图形化展示。简单的说,这两个变量仅仅起到一个传递变量数值的作用。

到此程序的配置已经完成。

J-Link配置说明

在Option->Debug选项卡中选择J-Link作为MCU的调试工具,并选择SWD模式。

“”

“”

在Trace选项卡中使能Core Clock并选择48MHz,其他的配置默认即可。

“”

进入debug模式,在View->Serial Windows选项卡中选择Debug Viewer选项,打开Debug Viewer窗口,可以查看虚拟串口打印。

“”

勾选View选项卡的Periodic Window Update选项来进行周期更新。

“”

打开View选项卡勾选Analvsis Windows选项的Event Recorder与Event Statistics选项来调出相关的组件。

“”

“”

全速运行,效果如下:

“”

另外Event Recorder支持筛选功能,使用这个功能需要先暂停全速运行,然后点击下面这个选项:

“”

弹出的界面里面可以设置选项显示(勾上表示显示),我们这里取消Event Statistics的显示,设置完毕后记得点击OK按钮。

“”

虚拟串口数据打印:

“”

全速运行结果如下:

“”

CMSIS-DAP配置说明

在Option->Debug选项卡中选择CMSIS-DAP作为MCU的调试工具。

“”

设置Core Clock时钟为48MHz。

“”

在Option->Utilities选项卡中配置CMSIS-DAP,具体操作如下:

“”

至此CMSIS-DAP的配置也已经完成,使用相关功能跟J-Link操作方法一样。

前面分享了这么多SEGGER相关工具使用的主题帖,主要是为了给MM32用户提供更多的调试、下载或者定位问题等的方法,借助这些工具能有效的节省时间、提高效率,希望能够帮助到大家!

来源:灵动微电子
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 272

在实际项目中,很多时候,我们需要将传感器、ADC的数值和计算的结果变量以波形的形式显示。通常的解决办法是用串口上位机、USB接口上位机或者MDK的逻辑分析仪等方式输出查看,使用这三种方式都比较繁琐,需要占用额外的系统资源。今天为大家讲解的J-Scope波形软件简单易用,不占用系统额外资源,无需用户写目标板代码,仅需将JLINK插上即可。

针对以上的问题,SEGGER推出了J-Scope波形显示软件。

J-Scope上位机

J-Scope是SEGGER公司推出的,可以在目标MCU运行时,实时分析数据并图形化显示的软件。它不需要SWO或目标上的任何额外引脚等功能,使用标准的调试接口即可。J-Scope波形显示软件主要有RTT和HSS两种工作方式。

01、HSS模式

HSS模式不需要在用户代码上面添加任何代码即可实现。只需要使用J-Scope加载MDK或者IAR的可执行文件即可(MDK生成的可执行文件为.axf文件,IAR生成的文件为.out文件),而且随时随地都可以连接目标板,不影响目标板的正常功能,不需要额外资源,同时可以设置上升沿,下降沿或者双沿触发。

02、RTT模式

RTT快速上传模式,需要用户在代码上加入RTT的相关组件(相关组件的添加请参考上一章节),并使用J-Link与目标板连接,此模式采用标准的下载接口,用到的下载接口有VCC、GND、SWDIO、SWCLK、RST。

03、J-Scope在HSS模式下支持的产品型号

“”

04、JScope在RTT模式下支持的产品型号

“”

05、不同版本的J-Link速度对比

“”

HSS模式获取数据

HSS模式比较简单,仅需要将编译器生成的可执行文件加入到J-Scope上位机软件即可。

相对于RTT模式来说此模式随时随地都可以连接目标板,并且不影响目标板的正常功能,并且不需要额外的资源。但是相对于RTT模式也存在着速度慢的缺点,采样的速度基本上在1KHZ左右,仅适合采样变量变化速度低于1KHZ的情况。

01、HSS模式使用方法

将目标板与J-Link连接,下载程序后并重新上电。

打开J-Scope的上位机软件,如下图所示:

“”

创建工程,选择“Create new project”选项,弹出工程配置对话框。

“”

J-Link使用的为USB接口,所以在进行配置的时候选择USB接口即可,芯片的具体型号可以根据用户自己的需求来选择,通信接口可以选择JTAG接口或者SWD接口,调试的模式选择HSS模式,并在elf file对话框中加载可执行文件。

“”

勾选需要显示的变量名。

“”

然后直接按F5开始运行,针对不同用户的使用情况,J-Scope可能会弹出不同的提示对话框,直接点击OK确认即可。

“”

J-Scope上位机软件打印出波形。

“”

RTT模式获取数据

RTT模式获取数据的原理就是利用SWD接口完成数据从MCU到J-Scope上位机软件的数据传输。此模式相对于HSS模式有更高的数据传输速率,最高可以达到2MB/s,并且随时可以连接目标板且不影响目标板的正常功能,并且可以添加时间戳。

01、RTT模式使用方法

1.1 软件配置

在目标工程中加入RTT的代码文件,具体的操作请参考上一讲RTT的移植教程,在主函数中加入RTT的初始化配置。

SEGGER_RTT_ConfigUpBuffer(1,"JScope_u2",buf,2048,SEGGER_RTT_MODE_NO_BLOCK_SKIP);

然后创建一个定时器,利用定时器定时向J-Scope上位机发送数据。

/******************************************************************************
 * @brief
 * @param
 * @retval
 * @attention
******************************************************************************/
void TIM2_Configure(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef        NVIC_InitStructure;

    TIM_DeInit(TIM2);

    /* Enable TIM2 Clock */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    /* Config TIM2 Every 1ms Generate Interrupt */
    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
    TIM_TimeBaseStructure.TIM_Prescaler           = (RCC_GetSysClockFreq() / 1000 - 1);
    TIM_TimeBaseStructure.TIM_CounterMode       = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_Period                = (1000 - 1);
    TIM_TimeBaseStructure.TIM_ClockDivision         = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_RepetitionCounter     = 0;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    /* Clear TIM2 Update Flag */
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);

    /* Enable TIM2 Update Interrupt */
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    /* Enable TIM2 */
    TIM_Cmd(TIM2, ENABLE);

    /* Enable TIM2 Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}


/******************************************************************************
 * @brief
 * @param
 * @retval
 * @attention
******************************************************************************/
void TIM2_IRQHandler(void)
{

    /* Clear TIM2 Update Interrupt Flag */
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    SEGGER_RTT_Write(1, &g_SineWave128[(i++) % 128], 2);
}

在主函数中初始化TIM2的配置:

TIM2_Configure();

“”

1.2 J-Scope上位机配置

RTT模式的配置与HSS模式的配置类似,但是在J-Scope Configuration选项卡中的Sampling Source界面选择RTT模式,在RTT模式下不需要加载可执行文件。

“”

配置完成以后运行可以看到J-Scope上位机打印的波形。

“”

来源:灵动微电子
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 216

页面

订阅 RSS - MM32F013x