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)
#define TIMER_PERIOD_NS (250)
#define ENCODER_PEROID_NS (244)
#define ENCODER_FIXED_CLK (24)
#define ENCODER_ANGLE_CLK (4095)
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_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_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);
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);
#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)
{
;
}
}
问题
- 定时器没有生效
- 主程序没有while 1语句,需要不停的监听中断
- 接线问题,定时器的PWM管脚需要检查正确
|