GD32定时器——单个定时器下多个通道PWM捕获

GD32定时器——单个定时器下多个通道PWM捕获

背景

目前在GD32上开发,由于IO资源不足,需要在一个定时器下进行多个PWM的捕获。
定时器可以配置PWM捕获,方法有二:

  • 配置定时器通道的PWM模式
    部分定时器配置为PWM模式后,可以直接捕获PWM,这样做方便快捷,缺点是需要定时器通道本身支持该功能,并不是所有的定时器通道都支持配置PWM捕获。
  • 定时器计数,通过计数值(高电平和低电平)换算
    原理也很简单,检测电平边缘触发,例如电平从低到高,则开始计数,记为count1,从高到低时,开始计数,记为count2,则可以换算出高低电平的时长,从而计算出占空比。例如高电平的占空比为:count1/(count1+count2)

本文主要描述第二种方案。

方案实现

受限于硬件资源,需要在一个定时器上开通四个通道,然后在四个通道上捕获电平变化,然后开始计数,从而计算出每个通道的占空比。
这里选定的是
芯片型号:GD32F330RBT6,主频为84Mhz。
定时器通过阅读Datasheet,选的是Timer2,然后通过读取四个通道的PWM输入,读入,获取每个通道对应的编码器的角度。
TIMER_CH0CV宏的定义可以看出,用于获取对应定时器的通道计数值。
CH0也可以是CH1/CH2/CH3这些,这对于单个定时器多个通道的场景,很方便。
这里,编码器配置为PWM波输出。
根据编码器的规格书,共计4119个PWM时钟,故最好便是计数为4119或者其倍数,频率为994.4Hz,而GD32芯片主频为84MHz,将prescale定为20,那么就是84MHz/(20+1),即4MHz的频率,每个时钟为1/4Mhz,即250ns。
编码器的则是:1s/994.4Mz/4119,为244ns,做个简单转换,如果定时器计数值为x,编码器角度值为y,则:

x * 250 = 244 * (24 + y / 360 * 4095)

所以可以计算y,即编码器角度可知。

代码

main.c

#include <stdio.h>
#include <string.h>
#include "gd32f3x0_it.h"
#include "gd32f3x0_timer.h"
#include "gd32f3x0.h"
#include "systick.h"
#include "pwm_capture.h"

int main(void)
{
	pwm_capture();
}

pwm_capture.c:

#include "gd32f3x0_gpio.h"
#include "gd32f3x0_timer.h"
#include "stdint.h"
#include "pwm_capture.h"

#define RCU_GPIO_PORT 		RCU_GPIOC
#define RCU_TIMER			RCU_TIMER2
#define GPIO_PORT			GPIOC
#define ENCODER_TIMER		TIMER2
#define TIMER_CHAN0_PIN		GPIO_PIN_6
#define TIMER_CHAN1_PIN		GPIO_PIN_7
#define TIMER_CHAN2_PIN		GPIO_PIN_8
#define TIMER_CHAN3_PIN		GPIO_PIN_9
#define TIMER_PRESCALE		(20) //21 - 1

#define TIMER_PERIOD_NS         (250)
#define ENCODER_PEROID_NS       (244)
#define ENCODER_FIXED_CLK       (24)
#define ENCODER_ANGLE_CLK       (4095)

//#define ENCODE_PWM 	(4119)

volatile uint32_t ch0_cnt = 0;
volatile uint32_t ch1_cnt = 0;
volatile uint32_t ch2_cnt = 0;
volatile uint32_t ch3_cnt = 0;

volatile uint32_t ch0_1 = 0;
volatile uint32_t ch0_2 = 0;
volatile uint32_t ch1_1 = 0;
volatile uint32_t ch1_2 = 0;
volatile uint32_t ch2_1 = 0;
volatile uint32_t ch2_2 = 0;
volatile uint32_t ch3_1 = 0;
volatile uint32_t ch3_2 = 0;

volatile float encode0 = 0.0;
volatile float encode1 = 0.0;
volatile float encode2 = 0.0;
volatile float encode3 = 0.0;

float to_angle(uint32_t clk_cnt)
{
		float ret = (float)(TIMER_PERIOD_NS * clk_cnt / ENCODER_PEROID_NS - 24) * 360 / ENCODER_ANGLE_CLK;
		float EPSINON = 0.00001;
		if (ret < EPSINON)
				ret = 0.0;
		EPSINON = 359.99999;
		if (ret > EPSINON)
				ret = 360.0;
		return ret;
}

#if 0
void TIMER0_Channel_IRQHandler(void)
#else
void TIMER2_IRQHandler(void)
#endif
{
    if (timer_interrupt_flag_get(ENCODER_TIMER, TIMER_INT_FLAG_CH0) != RESET)
    {
        if (SET == gpio_input_bit_get(GPIO_PORT, TIMER_CHAN0_PIN))
        {
            ch0_1 = TIMER_CH0CV(ENCODER_TIMER);
        }
        else
        {
            ch0_2 = TIMER_CH0CV(ENCODER_TIMER);
        }

        if (ch0_2 > ch0_1)
        {
            ch0_cnt = ch0_2 - ch0_1;
            ch0_2 = ch0_1 = 0;
            encode0 = to_angle(ch0_cnt);
        }
    }
    else if (timer_interrupt_flag_get(ENCODER_TIMER, TIMER_INT_FLAG_CH1) != RESET)
    {
        if (SET == gpio_input_bit_get(GPIO_PORT, TIMER_CHAN1_PIN))
        {
            ch1_1 = TIMER_CH1CV(ENCODER_TIMER);
        }
        else
        {
            ch1_2 = TIMER_CH1CV(ENCODER_TIMER);
        }

        if (ch1_2 > ch1_1)
        {
            ch1_cnt = ch1_2 - ch1_1;
            ch1_2 = ch1_1 = 0;
            encode1 = to_angle(ch1_cnt);
        }
    }
    else if (timer_interrupt_flag_get(ENCODER_TIMER, TIMER_INT_FLAG_CH2) != RESET)
    {
        if (SET == gpio_input_bit_get(GPIO_PORT, TIMER_CHAN2_PIN))
        {
            ch2_1 = TIMER_CH2CV(ENCODER_TIMER);
        }
        else
        {
            ch2_2 = TIMER_CH2CV(ENCODER_TIMER);
        }

        if (ch2_2 > ch2_1)
        {
            ch2_cnt = ch2_2 - ch2_1;
            ch2_2 = ch2_1 = 0;
            encode2 = to_angle(ch2_cnt);
        }
    }
    else if (timer_interrupt_flag_get(ENCODER_TIMER, TIMER_INT_FLAG_CH3) != RESET)
    {
        if (SET == gpio_input_bit_get(GPIO_PORT, TIMER_CHAN3_PIN))
        {
            ch3_1 = TIMER_CH3CV(ENCODER_TIMER);
        }
        else
        {
            ch3_2 = TIMER_CH3CV(ENCODER_TIMER);
        }

        if (ch3_2 > ch3_1)
        {
            ch3_cnt = ch3_2 - ch3_1;
            ch3_2 = ch3_1 = 0;
            encode3 = to_angle(ch3_cnt);
        }
    }
}

void timer_config(void)
{
	timer_parameter_struct timer_initpara;
    timer_ic_parameter_struct timer_icinitpara;

    //rcu	
    rcu_periph_clock_enable(RCU_GPIO_PORT);
    rcu_periph_clock_enable(RCU_TIMER);

    gpio_mode_set(GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, TIMER_CHAN0_PIN);
    gpio_mode_set(GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, TIMER_CHAN1_PIN);
    gpio_mode_set(GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, TIMER_CHAN2_PIN);
    gpio_mode_set(GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, TIMER_CHAN3_PIN);

    gpio_output_options_set(GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, TIMER_CHAN0_PIN);
    gpio_output_options_set(GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, TIMER_CHAN1_PIN);
    gpio_output_options_set(GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, TIMER_CHAN2_PIN);
    gpio_output_options_set(GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, TIMER_CHAN3_PIN);

    //timer init
    /* TIMER configuration */
    timer_deinit(ENCODER_TIMER);

    timer_initpara.prescaler         = TIMER_PRESCALE;
    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(ENCODER_TIMER, &timer_initpara);

    //PWM capture parameters		
    timer_icinitpara.icpolarity = TIMER_IC_POLARITY_BOTH_EDGE;
    timer_icinitpara.icfilter = 0x0;
    timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1;
    timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI;

    timer_input_capture_config(ENCODER_TIMER, TIMER_CH_0, &timer_icinitpara);
    timer_input_capture_config(ENCODER_TIMER, TIMER_CH_1, &timer_icinitpara);
    timer_input_capture_config(ENCODER_TIMER, TIMER_CH_2, &timer_icinitpara);
    timer_input_capture_config(ENCODER_TIMER, TIMER_CH_3, &timer_icinitpara);

    //interrupt priority/flag
#if 0
	vic_irq_enable(TIMER0_Channel_IRQn, 1, 1);
#else
    nvic_irq_enable(TIMER2_IRQn, 1, 1);
#endif

    timer_interrupt_flag_clear(ENCODER_TIMER, TIMER_INT_FLAG_CH0);
    timer_interrupt_flag_clear(ENCODER_TIMER, TIMER_INT_FLAG_CH1);
    timer_interrupt_flag_clear(ENCODER_TIMER, TIMER_INT_FLAG_CH2);
    timer_interrupt_flag_clear(ENCODER_TIMER, TIMER_INT_FLAG_CH3);

    timer_interrupt_flag_clear(ENCODER_TIMER, TIMER_INT_FLAG_UP);

    timer_interrupt_enable(ENCODER_TIMER, TIMER_INT_CH0 | TIMER_INT_UP);
    timer_interrupt_enable(ENCODER_TIMER, TIMER_INT_CH1 | TIMER_INT_UP);
    timer_interrupt_enable(ENCODER_TIMER, TIMER_INT_CH2 | TIMER_INT_UP);
    timer_interrupt_enable(ENCODER_TIMER, TIMER_INT_CH3 | TIMER_INT_UP);

    timer_auto_reload_shadow_enable(ENCODER_TIMER);
    timer_enable(ENCODER_TIMER);
}

void pwm_capture(void)
{
	timer_config();
	while (1)
	{
		;
	}
}

问题

  1. 定时器没有生效
  • 主程序没有while 1语句,需要不停的监听中断
  • 接线问题,定时器的PWM管脚需要检查正确
  • 6
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我明白了。关于GD32定时器PWM的问题,我可以给您一些基本的介绍和使用方法。 GD32定时器可以用来产生PWM信号,实现对外设的控制。具体步骤如下: 1. 配置GPIO口为复用输出模式,选择对应的定时器通道。 2. 配置定时器的工作模式、时钟源、计数器自动重载值等参数。 3. 配置定时器通道输出模式、占空比等参数。 4. 启动定时器。 下面是一个简单的示例代码,演示如何使用GD32定时器产生PWM信号: ```c #include "gd32f10x.h" void timer_pwm_init(void) { /* 使能GPIOB时钟 */ rcu_periph_clock_enable(RCU_GPIOB); /* 使能TIMER2时钟 */ rcu_periph_clock_enable(RCU_TIMER2); /* 配置GPIOB10为复用输出模式 */ gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10); /* 配置TIMER2工作模式为PWM模式1 */ timer_mode_config(TIMER2, TIMER_OC_MODE_PWM1, TIMER_OCMODE_PWM1); /* 配置TIMER2时钟源为内部时钟 */ timer_clock_source_config(TIMER2, TIMER_CLOCK_DIV1); /* 配置TIMER2计数器自动重载值 */ timer_auto_reload_value_config(TIMER2, 999); /* 配置TIMER2通道1的输出模式和占空比 */ timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_0, 500); timer_channel_output_mode_config(TIMER2, TIMER_CH_0, TIMER_OC_MODE_PWM1); timer_channel_output_shadow_config(TIMER2, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE); /* 启动TIMER2 */ timer_enable(TIMER2); } int main(void) { /* 初始化定时器PWM */ timer_pwm_init(); while (1) { /* 主循环 */ } } ``` 在上面的代码中,我们使用了TIMER2通道1产生了一个占空比为50%的PWM信号,输出到GPIOB10上。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值