使用Timer对内部低速时钟校准方法

一、简介

        GD32 MCU 内部的低速时钟(IRC32K IRC40K)可以担当一个低功耗时钟源的角色,其优 点是不需要外部器件从而可以降低硬件成本。其频率大约为 40kHz 32kHz,可以为独立看门狗(FWDGT)和实时时钟(RTC)等外设提供时钟。其缺点是精确度相对较低,适用的场景不多,但是可以利用 TIMER 对其捕获从而进行校准。本文以 GD32F4 系列 MCU 为例,介绍使用 TIMER 校准内部低速时钟的方法。

二、校准原理

       即使是同一个系列的 MCU,每一颗 MCU 的内部低速时钟都可能不同且有一定的偏差,因此当它给 RTC FWDGT 提供时钟时,软件设置 RTC FWDGT 的预分频时以 32K 的固定数值进行计算,会造成较大的误差。

       GD32F4 系列 MCU 可以通过软件配置重映射,将 TIMER4_CH3 通道与 IRC32K 在内部进 行连接,详细信息请参考TIMER4_IRMP寄存器的CI3_RMP位以及 TIMER10_IRMP寄存器ITI1_RMP位。

       进而可以使用 TIMER4_CH3 通道捕获 IRC32K 边沿得到准确的时钟频率,如下图所示。从而修改如 RTC FWDGT 等外设的时钟分频值,获得更精确的时基。

                               T = t1 - t0 = CNT / fck                      (2-1)

                               fIRC32K = 1 / T = fck/ CNT              (2-2)

       其中,t0 TIMER4 第一次捕获到 IRC32K 下降沿的时间,t1 TIMER4 第二次捕获到 IRC32K下降沿的时间,T IRC32K 的周期,fCK TIMER4 计数器的时钟。fCK 是已知的,CNT 通过两次捕获分别读取 TIMER 计数器可以得到,则 fIRC32K 通过式(2-2)计算得出。

三、实现方法

      1.通过配置 TIMER4_CH3 捕获 IRC32K 的下降沿

void timer_configuration(void)
{
    /* TIMER4 configuration: input capture mode -------------------
     TIMER4 CH3 input remap LSI
    the TIMER4 CH3CV is used to compute the frequency value
    ------------------------------------------------------------ */
    timer_ic_parameter_struct timer_icinitpara;
    timer_parameter_struct timer_initpara;

    rcu_periph_clock_enable(RCU_TIMER4);
    rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
    timer_deinit(TIMER4);

    /* TIMER4 configuration */
    timer_initpara.prescaler         = 0;       
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 65535;
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER4,&timer_initpara);

    /* TIMER4  configuration */
    /* TIMER4 CH3 input capture configuration */
    timer_icinitpara.icpolarity  = TIMER_IC_POLARITY_FALLING;    
    timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI;
    timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1;
    timer_icinitpara.icfilter    = 0x0;
    timer_input_capture_config(TIMER4,TIMER_CH_3,&timer_icinitpara);


    /* auto-reload preload enable */
    timer_auto_reload_shadow_enable(TIMER4);
    /* clear channel 3 interrupt bit */
    timer_interrupt_flag_clear(TIMER4,TIMER_INT_CH3);
    /* channel 3 interrupt enable */
    timer_interrupt_enable(TIMER4,TIMER_INT_CH3);
		
	  timer_channel_remap_config(TIMER4,TIMER4_CI3_RMP_IRC32K);
    /* TIMER4 counter enable */
    timer_enable(TIMER4);
}

      2.TIMER4_CH3 捕获到 IRC32K 的下降沿将触发捕获中断,计算得到实际频率fre。

void TIMER4_IRQHandler(void)
{
    if(SET == timer_interrupt_flag_get(TIMER4,TIMER_INT_CH3)){
        /* clear channel 0 interrupt bit */
        timer_interrupt_flag_clear(TIMER4,TIMER_INT_CH3);

        if(0 == ccnumber){
            /* read channel 0 capture value */
            readvalue1 = timer_channel_capture_value_register_read(TIMER4,TIMER_CH_3)+1;
            ccnumber = 1;
        }else if(1 == ccnumber){
            /* read channel 0 capture value */
            readvalue2 = timer_channel_capture_value_register_read(TIMER4,TIMER_CH_3)+1;

            if(readvalue2 > readvalue1){
                count = (readvalue2 - readvalue1); 
            }else{
                count = ((65535 - readvalue1) + readvalue2); 
            }

            fre = 200000000 / count;

            ccnumber = 0;
        }
    }
}

   3.通过实际测得的频率配置RTC的预分频值

   

ErrStatus rtc_prescaler_config(uint16_t freq)
{
    ErrStatus error_status = ERROR;
    /* disable the write protection */
    RTC_WPK = RTC_UNLOCK_KEY1;
    RTC_WPK = RTC_UNLOCK_KEY2;

    /* enter init mode */
    error_status = rtc_init_mode_enter();

    if(ERROR != error_status) {
			  presl_s = freq/100-1;
			  RTC_PSC = (uint32_t)(PSC_FACTOR_A(0x63) | PSC_FACTOR_S(presl_s));                             		
        /* exit init mode */
        rtc_init_mode_exit();
    }

    /* enable the write protection */
    RTC_WPK = RTC_LOCK_KEY;

    return error_status;
}

     4.测试main.c程序如下

   

int main(void)
{
    systick_config();
    gd_eval_com_init(EVAL_COM0);
    nvic_configuration();
  	rtc_configuration();
    timer_configuration();

        delay_1ms(3000);
        printf("the frequence is %d\n",fre);
	
        while(ERROR ==rtc_prescaler_config(fre));

			  while (1){
	      delay_1ms(3000);
			   printf("the frequence is %d\n",fre);
		  	rtc_show_time();
				}		
}

   四、测试结果

      用电脑时钟做参考,校准之前两个小时误差将近十分钟,校准之后两个半小时误差不到1分钟。

   

  

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值