当前位置: 首页 > article >正文

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输入捕获测量的结果如下:


http://www.kler.cn/a/154216.html

相关文章:

  • PyTorch实战-手写数字识别-单层感知机
  • vs2022搭建opencv开发环境
  • 笔记|M芯片MAC (arm64) docker上使用 export / import / commit 构建amd64镜像
  • 使用Python和BeautifulSoup进行网页抓取:通过Python编程语言,结合BeautifulSoup库,可以轻松地从网站上抓取所需的信息。
  • 人工智能训练师 综合测试题库一
  • MySQL的游标和While循环的详细对比
  • SSM项目实战-POJO设计
  • 系统地自学 Python
  • 学习TypeScrip1(基本类型)
  • 论文阅读——Img2LLM(cvpr2023)
  • flink源码分析之功能组件(四)-slot管理组件II
  • Linux 匿名页反向映射
  • SpringBoot+redis实现接口防刷
  • Web前端 ---- 【Vue】(组件)父子组件之间的通信一文带你了解
  • 【C语言:数据在内存中的存储】
  • xilinx系列FPGA基于VIVADO的pin delay列表生成说明
  • 2017年全国硕士研究生入学统一考试管理类专业学位联考英语(二)试题
  • 基于helm的方式在k8s集群中部署gitlab - 备份恢复(二)
  • 【Java Spring】SpringBoot 配置文件
  • 速通MySql
  • 熬夜会秃头——beta冲刺Day4
  • MySQL备份与恢复(重点)
  • Golang笔记|Atomic
  • 【一周安全资讯1202】信安标委发布《网络安全标准实践指南—网络安全产品互联互通 告警信息格式》;网络安全纳入注册会计师考试科目
  • Android 手机屏幕适配方式和原理
  • (一)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)