GD32 定时器输入捕获模式测量PWM占空比和频率

简介

利用GD32 定时器的PWM输入捕获模式来实现PWM波形的占空比和频率的测量。相应的简介可以参考GD32用户手册中关于定时器输入捕获的章节,PWM输入捕获模式是输入捕获模式的一个特例。(记录自己学习过程,如有错误请留言指出)

原理

如何利用定时器测量一个PWM的频率和占空比

只需要测量出下面两个时间 T1 和 T2 即可算出。

frequency =  1/ T1            duty cycle =  T2/T1

再来简单看看定时器输入捕获的原理

通 道 输 入 信 号 CIx 有 两 种 选 择 , 一 种 是 TIMERx_CHx 信 号 , 另 一 种 是
TIMERx_CH0,TIMERx_CH1 和 TIMERx_CH2 异或之后的信号。通道输入信号 CIx 先被
TIMER_CK 信号同步,然后经过数字滤波器采样,产生一个被滤波后的信号。通过边沿检测
器,可以选择检测上升沿或者下降沿。通过配置 CHxP 选择使用上升沿或者下降沿。配置
CHxMS.,可以选择其他通道的输入信号,内部触发信号。配置IC 预分频器,使得若干个输入
事件后才产生一个有效的捕获事件。捕获事件发生, TIMERx_CHxCV 存储计数器的值。
 

如何使用定时器捕获PWM

用户手册里面的描述如下,我们来进行一步一步拆解:

(1)首先一个 PWM波连接到 CI0,在代码中配置TIM2的CH0输入进来,也就是选择了CI0输入信号为TIMER2_CH0 。

(2) 然后配置 TIMERx_CHCTL0 寄存器中 CH0MS 为2’b01,选择通道0 的捕获信号为CI0 并设置上升沿捕获。

(3) 配置 TIMERx_CHCTL0 寄存器中 CH1MS 为2’b10,选择通道1捕获信号为 CI0 并设置下降沿捕获。

具体怎么配置上升沿和下降沿捕获呢,可以看到下面的寄存器配置

内部捕获通道0设置为上升沿捕获,内部捕获通道1设置为下降沿捕获,在代码实现中其实只需要设置内部捕获通道0为上升沿,代码默认设置了内部捕获通道1为下降沿捕获,具体如下:

void timer_input_pwm_capture_config(uint32_t timer_periph, uint16_t channel, timer_ic_parameter_struct* icpwm)
{
    //进行了省略,只保留了配置极性

    /* Set channel input polarity */
    if(TIMER_IC_POLARITY_RISING == icpwm->icpolarity){    //极性取了反
        icpolarity = TIMER_IC_POLARITY_FALLING;
    }else{
        icpolarity = TIMER_IC_POLARITY_RISING;
    }

    if(TIMER_CH_0 == channel){
        /* reset the CH0P and CH0NP bits */
        TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH0P|TIMER_CHCTL2_CH0NP));
        /* set the CH0P and CH0NP bits */
        TIMER_CHCTL2(timer_periph) |= (uint32_t)(icpwm->icpolarity);

        /* reset the CH1P and CH1NP bits */
        TIMER_CHCTL2(timer_periph) &= (~(uint32_t)(TIMER_CHCTL2_CH1P|TIMER_CHCTL2_CH1NP));   //利用icpolarity 配置
        /* set the CH1P and CH1NP bits */
        TIMER_CHCTL2(timer_periph) |= (uint32_t)((uint32_t)icpolarity << 4U);
    }
}

(2) (3) 两步的配置CHxMS主要是,为了把经过边沿选择器生成的上升沿触发信号和下降沿触发信号分别映射到IS0和IS1上 (个人理解这个IS0和IS1是用于分别输入内部的输入捕获CH0通道和CH1通道,用于实现两个输入通道对1个信号的上升沿捕获和下降沿捕获,也就是一个信号输入到两个输入捕获器,只不过一个上升沿触发,一个下降沿触发)

定时器主从管理——复位模式

把TIM2设置为主从管理复位模式,触发源选择CI0FE0,当外部信号为上升沿时触发,根据复位模式的定义,上升沿触发时,会将定时器的计数值清空。定时器主从复位模式,真的是完美契合用于测量PWM频率和占空比,这里借用一下ST手册里面的图来理解一下具体怎么测量频率和占空比的。

第一次配置好定时器后,输入的上升沿会触发定时器,让定时器的计数归0,后面每一次下降沿触发时,CCR2记录定时器的CNT值,也就是高电平时间,上升沿触发时CCR1记录定时器的CNT值,也就是一个周期的时间,然后再将定时器复位从0开始计数。GD32中 CCR1为 CH0CV,CCR2为CH1CV。

(吐槽一下GD32的框图画的是真难理解,很多都是参考ST的框图来进行理解)

具体代码实现

void timer2_pwm_inputcapter_init(unsigned short arr, unsigned short psc)
{

	rcu_periph_clock_enable(RCU_GPIOA);
	rcu_periph_clock_enable(RCU_AF);

	/*configure PA6 (TIMER2 CH0) as alternate function*/
	gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_6);

	/* TIMER2 configuration: PWM input mode ------------------------
		the external signal is connected to TIMER2 CH0 pin
		the rising edge is used as active edge
		the TIMER2 CH0CV is used to compute the frequency value
		the TIMER2 CH1CV is used to compute the duty cycle value
	 ------------------------------------------------------------ */
	timer_ic_parameter_struct timer_icinitpara;
	timer_parameter_struct timer_initpara;

	rcu_periph_clock_enable(RCU_TIMER2);

	timer_deinit(TIMER2);

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

	/* TIMER2 configuration */
	/* TIMER2 CH0 PWM input capture configuration */
	timer_icinitpara.icpolarity = TIMER_IC_POLARITY_RISING; // 配置了 CH0P   [CH0NP==0, CH0P==0]:把 CIxFE0 的上升沿作为捕获或者从模式下触发的有效信
	// 号,并且 CIxFE0 不会被翻转。
	timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI;
	timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1;
	timer_icinitpara.icfilter = 0x0;
	timer_input_pwm_capture_config(TIMER2, TIMER_CH_0, &timer_icinitpara);

	/* slave mode selection: TIMER2 */
	timer_input_trigger_source_select(TIMER2, TIMER_SMCFG_TRGSEL_CI0FE0); // 触发中断源选择 TIMER_SMCFG_TRGSEL_CI0FE0
	timer_slave_mode_select(TIMER2, TIMER_SLAVE_MODE_RESTART);			  // 配置定时器为复位模式

	/* select the master slave mode */ // 复位模式. 选中的触发输入的上升沿重新初始化计数器,并且产生更新事件
	timer_master_slave_mode_config(TIMER2, TIMER_MASTER_SLAVE_MODE_ENABLE);

	/* auto-reload preload enable */
	timer_auto_reload_shadow_enable(TIMER2);
	/* clear channel 0 interrupt bit */
	timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_CH0);
	/* channel 0 interrupt enable */
	timer_interrupt_enable(TIMER2, TIMER_INT_CH0);

	/* channel 0 interrupt enable */
	timer_interrupt_enable(TIMER2, TIMER_INT_CH0); // 允许更新中断和CH0捕获中断
	nvic_irq_enable(TIMER2_IRQn, 5, 0);

	/* 创建消息队列用于接收数据 */
	xQueueTime2capter = xQueueCreate(10, sizeof(timer2_ic_pwm_struct));
	if (NULL == xQueueTime2capter)
	{
		while (1)
			;
	}

	/* TIMER2 counter enable */
	timer_enable(TIMER2);
}

__IO uint16_t dutycycle = 0;
__IO uint16_t frequency = 0;
void TIMER2_IRQHandler(void)
{
	timer2_ic_pwm_struct ic_data;
	BaseType_t xHigherPriorityTaskWoken;
	xHigherPriorityTaskWoken = pdFALSE;

	if (SET == timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_CH0))
	{
		/* clear channel 0 interrupt bit */
		timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_CH0);
		/* read channel 0 capture value */
		ic1value = timer_channel_capture_value_register_read(TIMER2, TIMER_CH_0) + 1;

		if (0 != ic1value)
		{
			/* read channel 1 capture value */
			ic2value = timer_channel_capture_value_register_read(TIMER2, TIMER_CH_1) + 1;

			/* calculate the duty cycle value */
			dutycycle = (ic2value * 100) / ic1value;
			/* calculate the frequency value */
			frequency = 1000000 / ic1value;
			ic_data.dutycycle = dutycycle;
			ic_data.frequency = frequency;
			ic_data.cnt = timer_counter_read(TIMER2);
			xQueueSendFromISR(xQueueTime2capter, &ic_data, &xHigherPriorityTaskWoken);
			/* Now the buffer is empty we can switch context if necessary. */
			if (xHigherPriorityTaskWoken)
			{
				/* Actual macro used here is port specific. */
				portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
			}
		}
		else
		{
			dutycycle = 0;
			frequency = 0;
		}
	}
}

输入一个5Khz,50%占空比的方波,使用pwm输入捕获测量的结果如下:

  • 26
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
GD32定时器输入捕获可以用来测量外部信号的频率占空比等参数,具体实现步骤如下: 1. 初始化定时器。选择合适的定时器,设置时钟源、预分频器、计数器自动重载值等参数,使定时器开始计数。 2. 配置输入捕获模式。选择输入捕获模式并设置输入捕获触发方式,可以选择上升沿、下降沿、或者两者都触发。 3. 开启输入捕获中断。当定时器捕获到外部信号时,会触发输入捕获中断,可以在中断服务函数中进行处理。 4. 计算捕获到的信号参数。通过捕获到的信号时间差,可以计算出信号的周期、频率占空比等参数。 下面是一个基于GD32F1X0系列芯片的定时器输入捕获的示例代码: ```c #include "gd32f1x0.h" void timer_config(void) { /* 使能定时器时钟 */ rcu_periph_clock_enable(RCU_TIMER0); /* 初始化定时器 */ timer_parameter_struct timer_initpara; timer_struct_para_init(&timer_initpara); timer_initpara.prescaler = 71; // 预分频器,72MHz/(71+1) = 1MHz timer_initpara.alignment = TIMER_COUNTER_EDGE; timer_initpara.count_mode = TIMER_COUNT_MODE_UP; timer_initpara.repetition_counter= 0; timer_initpara.period = 0xFFFF; // 自动重载值 timer_init(TIMER0, &timer_initpara); /* 配置输入捕获模式 */ timer_ic_parameter_struct timer_icinitpara; timer_ic_struct_para_init(&timer_icinitpara); timer_icinitpara.icpolarity = TIMER_IC_POLARITY_BOTHEDGE; // 上升沿和下降沿触发 timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI; // 直接映射到捕获通道 timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1; // 不分频 timer_icinitpara.icfilter = 0x0; // 不滤波 timer_input_capture_config(TIMER0, TIMER_CH_0, &timer_icinitpara); /* 开启输入捕获中断 */ nvic_irq_enable(TIMER0_IRQn, 0, 0); timer_interrupt_enable(TIMER0, TIMER_INT_CH0); } void TIMER0_IRQHandler(void) { if (timer_interrupt_flag_get(TIMER0, TIMER_INT_FLAG_CH0) == SET) { uint16_t capture_val = timer_channel_capture_value_register_get(TIMER0, TIMER_CH_0); /* 处理捕获到的信号 */ // ... timer_interrupt_flag_clear(TIMER0, TIMER_INT_FLAG_CH0); } } int main(void) { timer_config(); while (1); return 0; } ``` 在这个示例代码中,我们使用了TIMER0定时器的通道0进行输入捕获,捕获到外部信号后会触发TIMER0的中断服务函数。在中断服务函数中,我们可以通过timer_channel_capture_value_register_get函数获取输入捕获的数值,然后进行计算处理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值