基于GD32的输入捕获(过零点捕获)

 背景:最近开发的一款设备,需要测量交流电的相位和频率。由此,有了这篇总结。

        芯片:GD32F303CBT6

一、过零点捕获定时器初始化

初始化步骤:

  1. 配置定时器的时钟源和计数模式。需要选择内部或外部时钟源,并设置计数模式为向上计数或向下计数。

  2. 设置定时器的预分频和重装载值,以确定定时器的计数频率和溢出时间。

  3. 配置捕获/比较通道。您需要选择一个或多个通道,设置它们为捕获模式,并配置相应的输入/输出参数。

  4. 配置输入捕获滤波器。这可以帮助消除输入信号的噪声,并提高捕获的准确性。

  5. 配置中断或DMA请求。需要在捕获事件发生时执行某些操作,配置定时器产生中断或DMA请求。

  6. 启动定时器。最后,需要启动定时器以开始捕获输入信号。

首先,关于第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

这样就能计算出相位差了。

你可能感兴趣的:(stm32,单片机,嵌入式硬件,c语言,物联网)