背景:最近开发的一款设备,需要测量交流电的相位和频率。由此,有了这篇总结。
芯片:GD32F303CBT6
初始化步骤:
配置定时器的时钟源和计数模式。需要选择内部或外部时钟源,并设置计数模式为向上计数或向下计数。
设置定时器的预分频和重装载值,以确定定时器的计数频率和溢出时间。
配置捕获/比较通道。您需要选择一个或多个通道,设置它们为捕获模式,并配置相应的输入/输出参数。
配置输入捕获滤波器。这可以帮助消除输入信号的噪声,并提高捕获的准确性。
配置中断或DMA请求。需要在捕获事件发生时执行某些操作,配置定时器产生中断或DMA请求。
启动定时器。最后,需要启动定时器以开始捕获输入信号。
首先,关于第1点,我这边选择向上计数
第2点,由于定时器的时钟频率为120MHz,所以预分频为120 ,120MHz/120 =1 MHz,所以,定时器频率为1MHz,重装载值为65536
初始化配置分为两部分,一部分为定时器的配置,一部分为捕获通道的配置
void Time_Zero_Init_Config(void)
{
timer_ic_parameter_struct timer_icinitpara;
timer_parameter_struct timer_initpara;
//时钟
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_AF);
rcu_periph_clock_enable(RCU_TIMER4);
//GPIO
gpio_init(GPIOA,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,GPIO_PIN_0);
/* TIMER4 configuration */
timer_initpara.prescaler = 120-1;//定时器的时钟频率是120MHz,预分频120-1,120/120=1MHZ
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 CH0 input capture configuration */
timer_icinitpara.icpolarity = TIMER_IC_POLARITY_RISING;//捕获极性,上升沿捕获
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_0,&timer_icinitpara);
/* auto-reload preload enable */
timer_auto_reload_shadow_enable(TIMER4);//自动重载使能
/* clear channel 0 interrupt bit */
timer_interrupt_flag_clear(TIMER4,TIMER_INT_FLAG_CH0);//CH0 通道中断清除
/* channel 0 interrupt enable */
timer_interrupt_enable(TIMER4,TIMER_INT_CH0);//CH0 通道中断使能
/* TIMER4 counter enable */
timer_enable(TIMER4);
NVIC_SetPriority(TIMER4_IRQn,0);
NVIC_EnableIRQ(TIMER4_IRQn);
}
在定时器中断处理函数中,可以通过检测输入捕获的脉冲边沿来测量频率。首先,你需要定义两个静态变量,用于保存当前和上一次捕获到的计数器值。然后,每次在中断处理函数中,你都会读取当前的计数器值,并与上一次的值进行比较,得到两个脉冲间的计数差,即脉冲宽度。
简单说就是:每次捕获到上升沿,就在中断中记录下定时器的计数值,然后通过
这次得到的计数值 - 上次得到的计数值 = 计数差
然后:因为定时器的频率是1MHz,所以,频率 = 1000000 / 计数差
在这个地方,我遇到了一个问题,定时器溢出,因此,每次遇到当前捕获值<上次捕获值,就是发生了溢出,就需要再当前值加上65535
void TIMER4_IRQHandler(void)
{
if(SET == timer_interrupt_flag_get(TIMER4,TIMER_INT_FLAG_CH0))
{
Timer_Zero_Info.time_up_num_last_B = Timer_Zero_Info.time_up_num_now_B;
Timer_Zero_Info.time_up_num_now_B = timer_channel_capture_value_register_read(TIMER4,TIMER_CH_0)+1; // 读取捕获计数时间
if(Timer_Zero_Info.time_up_num_now_B > Timer_Zero_Info.time_up_num_last_B)
{
Timer_Zero_Info.time_differ_B = Timer_Zero_Info.time_up_num_now_B - Timer_Zero_Info.time_up_num_last_B;
Timer_Zero_Info.time_up_over_flag_B = 0;
Timer_Zero_Info.time_up_num_over_B = Timer_Zero_Info.time_up_num_now_B;
}
else
{
Timer_Zero_Info.time_up_over_flag_B = 1;
Timer_Zero_Info.time_up_num_over_B = 65535 + Timer_Zero_Info.time_up_num_now_B;
Timer_Zero_Info.time_differ_B = 65535 + Timer_Zero_Info.time_up_num_now_B - Timer_Zero_Info.time_up_num_last_B;
}
Timer_Zero_Info.frequency_B = 10000000 / Timer_Zero_Info.time_differ_B;
timer_interrupt_flag_clear(TIMER4,TIMER_INT_FLAG_CH0);
}
到此,频率就计算出来了。
那么如何计算两个信号的相位差呢?(此处只说实现方法,就不放代码了。)
首先,需要配置两个定时器,用来分别捕获两个波形。然后,在中断中记录下定时器计数值,然后就能计算出两路计数值的差值。
因为我所测量的信号频率为60Hz,那么周期就是1/60秒,在上诉的代码中,已经将定时器的频率设定为1MHz,因此周期就是1000000/60。
最后利用这个差值,计算出相位差: 相位差 = (两路计数值差值 / (1000000/60))* 360
这样就能计算出相位差了。