• 使用外部时钟,通过TIM21_CH1,对STM32L0XX内部的RC时钟(HSI/MSI等)进行校准


    前言:

    在有些应用中,无法使用外部晶振来获取准确的时钟,不得已使用STM32内部的RC时钟。HSI以及MSI等。虽然这些时钟在出厂时,已经进行了校准。但是随着环境温度变化,RC时钟会有所改变,这在对时钟敏感的场合下,是不可接受的。幸好STM32提供了一个再校准的机会。利用外部精准时钟信号,对内部时钟进行校准。           

    ​​​​​​​​​​​​​​

    官方文件:AN4631: How to calibrate an STM32L0xx internal RC oscillator

    ​​​​​​​本文只展示对HSI的校准例程,MSI等方法相同。

    正文:

    一、使用TIM21_CH1通道

     以上为数据手册介绍的方法,本文使用4096Hz的标准信号,通过GPIO引脚输入TIM21_CH1中,内部TIM21会有输入4分频,因此TIM21需要测量的周期应为(1/1024Hz) ,使用HSI的16MHz主时钟进行测量,得到的理论精确数值应为16MHz*(1/1024Hz)=15625

    下面为TIM21配置函数

    1. /*函数名:TIM21_Configuration
    2. *函数描述: 定时器配置 TIM21用来对HSI进行修正
    3. *输入参数:无
    4. *输出结果:无
    5. *返回值:无
    6. ***************************************/
    7. void TIM21_Configuration(void)
    8. {
    9. LL_TIM_InitTypeDef LL_TIM21_InitStruct;
    10. LL_TIM_IC_InitTypeDef LL_TIM21_IC_InitStruct;
    11. LL_TIM_DeInit (TIM21);
    12. LL_TIM21_InitStruct .Autoreload =0xffff;
    13. LL_TIM21_InitStruct .ClockDivision= LL_TIM_CLOCKDIVISION_DIV1;
    14. LL_TIM21_InitStruct .CounterMode =LL_TIM_COUNTERMODE_UP;
    15. LL_TIM21_InitStruct .Prescaler =0;
    16. LL_TIM_Init(TIM21,&LL_TIM21_InitStruct);
    17. LL_TIM21_IC_InitStruct .ICActiveInput =LL_TIM_ACTIVEINPUT_DIRECTTI;//CH1连接IC1
    18. LL_TIM21_IC_InitStruct .ICFilter =LL_TIM_IC_FILTER_FDIV1;//无滤波器
    19. LL_TIM21_IC_InitStruct .ICPolarity =LL_TIM_IC_POLARITY_FALLING;//下降沿
    20. LL_TIM21_IC_InitStruct .ICPrescaler =LL_TIM_ICPSC_DIV4;//输入4分频
    21. LL_TIM_IC_Init(TIM21,LL_TIM_CHANNEL_CH1,&LL_TIM21_IC_InitStruct );
    22. LL_TIM_EnableIT_TRIG(TIM21);
    23. LL_TIM_EnableIT_CC1(TIM21);
    24. NVIC_EnableIRQ(TIM21_IRQn);
    25. }

    其中LL_TIM21_InitStruct .Autoreload =0xffff;表示从0数到最大值,在中间进行捕获一段,即输入周期。因为已知输入的周期核算成数值在15625左右,所以不用再对TIM21进行分频。65536(0xffff)足矣。

    二、配置TIM21_CH1对应的GPIO

    由于使用的是TIM21_CH1功能,是复用功能,所以需要在GPIO配置中设置

    LL_GPIO_SetAFPin_0_7 (GPIOA,LL_GPIO_PIN_2,LL_GPIO_AF_0 ); /*使用PA2的AF0功能:TIM21_CH1*/

    三、校准程序

    根据官方文件给的流程图,可以进行编写相应的程序。

    1. /*************************************
    2. *函数名:HSI_Measurement
    3. *函数描述: 通过TIM21采集外部时钟信号
    4. 参考AN4631中 《FIGURE 12. HSI16/MSI OSCILLATOR FREQUENCY MEASUREMENT FLOWCHART》
    5. *输入参数:无
    6. *输出结果:无
    7. *返回值:uint16_t
    8. ***************************************/
    9. uint16_t HSI_Measurement(void)
    10. {
    11. uint8_t NUMER_OF_LOOPS;
    12. uint32_t Measure_Val=0;
    13. for(NUMER_OF_LOOPS=0;NUMER_OF_LOOPS<10;NUMER_OF_LOOPS++)//采集10次取平均值
    14. {
    15. Capture_state =CAPTURE_START; //标志位设置为START
    16. LL_TIM_SetCounter(TIM21,0);
    17. LL_TIM_EnableCounter(TIM21);
    18. LL_TIM_EnableIT_CC1(TIM21);
    19. while(Capture_state!=CAPTURE_COMPLETED ); //等待捕获周期
    20. Measure_Val+= Capture_Period; //捕获到的周期累加
    21. }
    22. LL_TIM_DisableIT_CC1(TIM21);
    23. LL_TIM_DisableCounter(TIM21);
    24. return (Measure_Val/10); //取平均
    25. }
    26. /*************************************
    27. *函数名:CALC_HSI
    28. *函数描述: 根据外部时钟信号,对HSI进行微调
    29. 进入单片机频率为4096Hz
    30. 经过预分频1/4,使得IC1PS变为1024Hz
    31. 而TIM21采集到的数据应该为【15625】16MHz*(1/1024Hz)=15625
    32. *输入参数:无
    33. *输出结果:无
    34. *返回值:无
    35. ***************************************/
    36. void CALC_HSI(void)
    37. {
    38. uint16_t HSI_Measured_Val,HSI_Trimming_Val=0,HSI_Frequency_Error=0,HSI_Optimum_Error=20,HSI_Optimum_Trimming_Val=0xf;
    39. TIM21_CLK_ENABLE;//打开TIM21时钟
    40. //RCC
    41. //GPIO
    42. TIM21_Configuration();
    43. for(HSI_Trimming_Val=0;HSI_Trimming_Val<32;HSI_Trimming_Val++) //历遍32个trimming值,查找最佳值
    44. {
    45. LL_RCC_HSI_SetCalibTrimming(HSI_Trimming_Val ); //设置当前trimming值
    46. HSI_Measured_Val=HSI_Measurement(); //测量输入周期
    47. HSI[HSI_Trimming_Val]=HSI_Measured_Val;
    48. if(HSI_Measured_Val>15625) HSI_Frequency_Error = HSI_Measured_Val-15625; //计算误差值
    49. else HSI_Frequency_Error = 15625-HSI_Measured_Val;
    50. if(HSI_Frequency_Error <HSI_Optimum_Error ) //如果误差值比最佳值还好,那么取代最佳值
    51. {
    52. HSI_Optimum_Error =HSI_Frequency_Error ;
    53. HSI_Optimum_Trimming_Val=HSI_Trimming_Val ;
    54. }
    55. }
    56. LL_RCC_HSI_SetCalibTrimming(HSI_Optimum_Trimming_Val );
    57. RCC_Configuration(); /*重新对RCC基本设置*/
    58. }

    其中TIM21时钟打开的宏如下

    #define TIM21_CLK_ENABLE                 LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM21)

    另外

    HSI[HSI_Trimming_Val]=HSI_Measured_Val;

    这个数组是用来保存,在各个Trimming_Val下(HSI校准一共32个),测量到的周期值,后面会贴出来实测数据。这个值理想情况下应该就是前面计算得到的15625。也就是说,测到的数值越接近这个数,那么时钟此时越精准。我们的目的也就是找到这个最佳值。

    中断程序

    1. void TIM21_IRQHandler(void)
    2. {
    3. if (Capture_state==CAPTURE_ONGOING)/*在使用TIM21测量频率时,用来表示当前状态,第一次进入中断为CAPTURE_START=0,第二次为CAPTURE_ONGOING=1*/
    4. {
    5. IC1ReadValue2= LL_TIM_IC_GetCaptureCH1(TIM21); //保存捕获的值RV2
    6. LL_TIM_DisableIT_CC1(TIM21);
    7. Capture_Period=IC1ReadValue2 -IC1ReadValue1 ; //RV2-RV1差值为测量到的周期
    8. Capture_state=CAPTURE_COMPLETED ;
    9. }
    10. else
    11. {
    12. IC1ReadValue1= LL_TIM_IC_GetCaptureCH1(TIM21);//保存捕获的值RV1
    13. Capture_state=CAPTURE_ONGOING;
    14. }
    15. }

    四、经过上述程序的校准,我们如愿找到了最佳Trimming Value

    这个校准值默认为16.  

     而我们找到的最佳值是15,在此参数下,HSI测量值为15622,最接近15625

    结论:

    从上面的数据可知,不同的校准值下,测量出的周期数值相差很大。虽说是微调,但是正所谓“差之毫厘谬以千里”,时钟的精准度是非常重要的。在温度变化较大的情况下,对HSI微调是必不可少的。

    自此,完成了对HSI的校准

  • 相关阅读:
    Codeforces Round 910 (Div. 2)(D~F)
    docker命令学习
    【嵌入式】STM32控制脉冲个数
    FLinkCDC
    c++ 与 lua socket脚本高级调用
    Django 管网项目 三
    力扣第236题 二叉树的最近公共祖先 c++ 递归和回溯 附注释和简短代码
    acwing算法提高之图论--最小生成树的扩展应用
    MongoDB 集群配置
    消息队列延时聚合通知的重要性
  • 原文地址:https://blog.csdn.net/weixin_43128823/article/details/125519785