Nuoeriris · 2021年06月10日

MM32F013x——IEC60730-1 B类认证软件设计指南(五)

在上一章节中我们介绍了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通道捕获,无需额外的引脚互联。

1.png

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

1. 时钟启动自检

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

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

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

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

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

2.jpg

2. 时钟运行自检

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

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

在对HSE范围比较前,会检查所有和报告时钟检测结果有关的变量的完整性。

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

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

3.jpg

3. 相关外设初始化

在前面有提到过,两个独立时钟源交叉检查来进行测量目前有两种实现方式,分别通过不同时钟源的定时器进行计算比较,另外一种通过输出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);

4. 时钟验证

时钟的验证可以分为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)

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

推荐阅读
关注数
6152
内容数
276
灵动MM32 MCU相关技术知识,欢迎关注~
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息