一、简介
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分钟。