当前位置:首页 > 嵌入式 > 嵌入式分享
[导读]GD32系列MCU的定时器体系按功能复杂度分为三类:高级定时器(TIMER0/TIMER7)、通用定时器(TIMER1~TIMER4)和基础定时器(TIMER5/TIMER6)。三者共享相同的底层时钟树结构,但外围功能逐级增强。高级定时器在通用定时器基础上集成了互补PWM输出、死区插入、刹车机制和重复计数器,专为三相电机FOC控制设计。

一、程序原理说明

GD32系列MCU的定时器体系按功能复杂度分为三类:高级定时器(TIMER0/TIMER7)、通用定时器(TIMER1~TIMER4)和基础定时器(TIMER5/TIMER6)。三者共享相同的底层时钟树结构,但外围功能逐级增强。高级定时器在通用定时器基础上集成了互补PWM输出、死区插入、刹车机制和重复计数器,专为三相电机FOC控制设计。

定时器级联原理:GD32的定时器之间通过触发输出(TRGO)和触发输入(TRGI)信号链路实现硬件级同步。主定时器(Master)在特定事件(如更新事件、比较匹配事件)发生时,通过TRGO信号触发从定时器(Slave),从定时器可配置为"复位模式"——收到触发信号后计数器归零并重新开始计数。这种机制确保多个定时器的计数起点严格对齐,消除软件中断同步带来的抖动。在GD32F470等较新型号中,定时器之间还可以通过内部触发互联(ITI0~ITI3)实现更灵活的同步拓扑,部分型号还集成了事件互联单元(EVIC),可将定时器的通道事件路由到任意目标定时器,实现"一个时基同步全部TIMER"的硬件级同步方案。

PWM生成原理:PWM的核心是计数器CNT与捕获/比较寄存器CHxCV的比较机制。在PWM模式0下,当CNT < CHxCV时输出有效电平,CNT ≥ CHxCV时输出无效电平。PWM频率由自动重装载寄存器(CAR)决定:f_PWM = F_timer / ((PSC+1) × (CAR+1)),占空比由CHxCV/CAR的比值决定。 在中央对齐模式下,计数器先递增后递减,PWM频率为边沿对齐模式的一半,但谐波分布更优,适合电机控制。

多路电机控制的核心挑战:以三相无刷电机为例,需要3对互补PWM信号(6路输出),每对信号的占空比独立可调,且必须插入死区时间防止上下桥臂直通。高级定时器的3个通道(CH0/CH1/CH2)恰好对应三相(U/V/W),每通道输出一对互补PWM(CHx + CHxN),死区时间由timer_deadtime_config()统一配置。

二、程序框架

整个多路电机PWM驱动采用四层架构:

时钟与GPIO层:使能定时器时钟,配置PWM输出引脚为复用推挽模式,配置互补输出引脚和刹车引脚。

时基层:配置高级定时器的预分频器(PSC)、自动重装载值(CAR)、中央对齐模式和重复计数器,确定PWM频率和更新节奏。

输出比较层:为每个通道配置PWM模式、极性、死区时间和初始占空比,使能互补输出和主输出。

应用控制层:封装占空比动态修改接口,结合输入捕获实现编码器反馈和过零检测,形成闭环控制。

三、具体程序实现

以下基于GD32F303(120MHz主频),以TIMER0驱动三相电机PWM为例,演示完整配置流程。

1. 时钟与GPIO初始化

#include "gd32f30x.h"

void motor_pwm_gpio_config(void)

{

/* 使能GPIO和定时器时钟 */

rcu_periph_clock_enable(RCU_GPIOA);

rcu_periph_clock_enable(RCU_GPIOB);

rcu_periph_clock_enable(RCU_AF);

rcu_periph_clock_enable(RCU_TIMER0);

/* TIMER0_CH0/CH1/CH2 → PA8/PA9/PA10(复用推挽输出) */

gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,

GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10);

/* TIMER0_CH0N/CH1N/CH2N → PB13/PB14/PB15(互补输出) */

gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,

GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15);

/* 刹车引脚 PB12(浮空输入) */

gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_12);

}

2. 高级定时器时基配置——20kHz中央对齐PWM

void motor_pwm_timer_config(void)

{

timer_parameter_struct timer_initpara;

timer_struct_para_init(&timer_initpara);

timer_deinit(TIMER0);

/* 定时器时钟 = 120MHz(APB2分频系数≠1时自动×2)

* PSC = 0 → 不分频,计数时钟 = 120MHz

* CAR = 5999 → PWM频率 = 120MHz / (2 × 6000) = 10kHz(中央对齐)

* 若需20kHz,则CAR = 2999 */

timer_initpara.prescaler = 0;

timer_initpara.alignedmode = TIMER_COUNTER_CENTER; /* 中央对齐 */

timer_initpara.counterdirection = TIMER_COUNTER_UP;

timer_initpara.period = 2999; /* 20kHz */

timer_initpara.clockdivision = TIMER_CKDIV_DIV1;

timer_initpara.repetitioncounter = 0; /* 每周期更新一次 */

timer_init(TIMER0, &timer_initpara);

}

3. 三路互补PWM通道配置与死区插入

void motor_pwm_channel_config(void)

{

timer_oc_parameter_struct oc_initpara;

/* 三相通道统一配置 */

for (uint16_t ch = TIMER_CH_0; ch <= TIMER_CH_2; ch++) {

timer_channel_output_struct_para_init(&oc_initpara);

oc_initpara.outputstate = TIMER_CCX_ENABLE; /* 使能主输出 */

oc_initpara.outputnstate = TIMER_CCXN_ENABLE; /* 使能互补输出 */

oc_initpara.ocpolarity = TIMER_OC_POLARITY_HIGH;

oc_initpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH;

oc_initpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW;

oc_initpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;

timer_channel_output_config(TIMER0, ch, &oc_initpara);

/* 初始占空比50% */

timer_channel_output_pulse_value_config(TIMER0, ch, 1500);

timer_channel_output_mode_config(TIMER0, ch, TIMER_OC_MODE_PWM0);

timer_channel_output_shadow_config(TIMER0, ch, TIMER_OC_SHADOW_ENABLE);

}

/* 死区时间 = DTG / 120MHz,DTG由寄存器配置

* 典型值1~2μs,防止上下桥臂直通 */

timer_deadtime_config(TIMER0, 120); /* 120/120MHz = 1μs */

/* 刹车功能:PB12低电平触发,立即关断所有PWM输出 */

timer_break_config(TIMER0, TIMER_BREAK_ENABLE,

TIMER_BREAK_POLARITY_LOW,

TIMER_BREAK_LOCK_OFF);

/* 使能主输出(高级定时器必须调用此函数) */

timer_primary_output_config(TIMER0, ENABLE);

timer_enable(TIMER0);

}

4. 定时器级联——主从同步配置

/* 将TIMER0配置为主定时器,更新事件触发TIMER2从定时器 */

void timer_cascade_config(void)

{

/* TIMER0主模式:更新事件产生TRGO */

timer_master_output_trigger_source_select(TIMER0, TIMER_TRI_OUT_SRC_UPDATE);

/* TIMER2从模式:接收TRGI,复位模式——收到触发后计数器归零 */

timer_slave_mode_select(TIMER2, TIMER_SLAVE_MODE_RESTART);

timer_input_trigger_source_select(TIMER2, TIMER_SMCFG_TRGSEL_ITI0);

/* ITI0 = TIMER0的内部触发输出 */

}

5. 输入捕获——编码器反馈接口

/* TIMER2_CH0输入捕获:测量编码器脉冲间隔 */

timer_ic_parameter_struct ic_initpara;

void encoder_capture_config(void)

{

rcu_periph_clock_enable(RCU_TIMER2);

/* PA0 → TIMER2_CH0(浮空输入) */

gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_0);

timer_parameter_struct timer_initpara;

timer_struct_para_init(&timer_initpara);

timer_deinit(TIMER2);

timer_initpara.prescaler = 119; /* 120MHz/120 = 1MHz计数时钟 */

timer_initpara.period = 0xFFFF;

timer_initpara.counterdirection = TIMER_COUNTER_UP;

timer_initpara.alignedmode = TIMER_COUNTER_EDGE;

timer_initpara.clockdivision = TIMER_CKDIV_DIV1;

timer_init(TIMER2, &timer_initpara);

timer_channel_input_struct_para_init(&ic_initpara);

ic_initpara.icpolarity = TIMER_IC_POLARITY_RISING;

ic_initpara.icselection = TIMER_IC_SELECTION_DIRECTTI;

ic_initpara.icprescaler = TIMER_IC_PSC_DIV1;

ic_initpara.icfilter = 0x0F; /* 8个连续采样确认,抗干扰 */

timer_input_capture_config(TIMER2, TIMER_CH_0, &ic_initpara);

timer_interrupt_enable(TIMER2, TIMER_INT_CH0);

nvic_irq_enable(TIMER2_IRQn, 1, 0);

timer_enable(TIMER2);

}

/* 捕获中断:计算转速 */

volatile uint32_t capture_val1 = 0, capture_val2 = 0;

void TIMER2_IRQHandler(void)

{

if (timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_CH0) != RESET) {

capture_val2 = timer_channel_capture_value_register_read(TIMER2, TIMER_CH_0);

uint32_t interval = (capture_val2 >= capture_val1) ?

(capture_val2 - capture_val1) :

(0xFFFF - capture_val1 + capture_val2 + 1);

/* 转速 = 60 / (interval × 每转脉冲数) */

capture_val1 = capture_val2;

timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_CH0);

}

}

6. 运行时占空比动态修改

/* 设置三相占空比(0~10000对应0%~100%) */

void motor_set_duty(uint16_t duty_u, uint16_t duty_v, uint16_t duty_w)

{

timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, duty_u);

timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_1, duty_v);

timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_2, duty_w);

}

四、关键工程要点

影子寄存器机制:GD32的CHxCV和CAR寄存器均有影子寄存器。使能影子寄存器后(TIMER_OC_SHADOW_ENABLE),运行时修改占空比不会立即生效,而是在下一个更新事件(计数器溢出)时统一加载,避免PWM波形出现毛刺。对于电机控制,这一特性至关重要——它确保SVPWM算法计算的三相占空比在同一时刻同步更新。

死区时间的精确计算:死区时间由timer_deadtime_config()的DTG参数决定,计算公式与CKDIV分频因子相关。在CKDIV=1、定时器时钟120MHz时,DTG=120对应1μs死区。实际设计中需根据MOSFET/IGBT的开关特性(通常0.5~2μs)选择合适值,过小会导致直通,过大会降低有效占空比范围。

中央对齐模式的优势:相比边沿对齐,中央对齐模式下PWM谐波集中在开关频率的偶数倍附近,对电机绕组的电流纹波更小;同时计数器在峰值和谷值处各产生一次更新事件,为ADC采样和FOC计算提供了天然的对称采样点。

定时器级联的时序精度:硬件级联同步的精度可达1个定时器时钟周期(本例中约8.3ns),远优于软件中断同步(受中断延迟影响,抖动通常在数百纳秒级)。在多电机协同控制、交错并联电源等对相位关系有严格要求的场景中,定时器级联是不可替代的方案。

GD32的定时器体系通过"高级定时器PWM输出 + 定时器级联同步 + 输入捕获反馈"的三位一体架构,为多路电机控制提供了从信号生成到闭环反馈的完整硬件支撑。掌握时基配置、输出比较和级联同步三大核心环节,是构建高性能电机驱动系统的工程基石。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
换一批
延伸阅读

在GD32平台上引入FreeRTOS后,很多开发者以为任务调度从此高枕无忧。但实际调试时,高优先级任务莫名阻塞、低优先级任务"霸占"CPU、甚至整个系统卡死的现象并不少见。这些问题的根源往往隐藏在信号...

关键字: GD32 FreeRTOS

当STM32的供应波动和成本压力持续发酵,GD32作为国产替代方案正被越来越多的团队提上日程。硬件层面的Pin-to-Pin兼容性让板级替换变得简单——GD32F103与STM32F103在引脚上完全兼容。但“焊上去能跑...

关键字: GD32 STM32

嵌入式开发,一行代码的效率差异可能在百万次循环后被放大为秒级延迟。GD32系列MCU主频最高可达120MHz(F4系列),但再快的CPU也经不起低效代码的挥霍。位操作、内联函数与编译器优化等级,是三把从指令集底层到编译策...

关键字: GD32 代码优化

GD32F303开发板摆在桌上,芯片已焊接、供电已就绪,但屏幕上只有一片空白——没有Device Pack、没有启动文件、连编译都报错。对于刚从STM32生态转入GD32的开发者,环境搭建是第一道坎,也是最容易被忽略的&...

关键字: Keil MDK GD32

在GD32系列MCU运行FreeRTOS、RT-Thread等实时操作系统时,多任务并发访问共享资源(如UART、SPI总线、全局缓冲区)是常态。信号量(Semaphore)与互斥量(Mutex)是两种最常用的同步机制,...

关键字: 信号量 互斥量 GD32

在嵌入式实时系统中,任务之间如何高效通信、如何精确管理时间,是决定系统架构质量的核心问题。裸机编程中,超级循环配合中断的模型在复杂度上升时迅速失控——全局变量满天飞、中断嵌套混乱、时间管理依赖硬件定时器资源。μC/OS-...

关键字: μCOS-III GD32

程序跑了三天突然HardFault,DMA传着传着数据全乱了,I2C读到一坨0xFF——这些场景几乎是每个GD32开发者都经历过的"至暗时刻"。本文从程序原理、框架设计到具体实现,系统梳理GD32 C...

关键字: GD32 C语言开发

在电池供电的物联网设备中,MCU的待机功耗常常是决定续航时间的首要因素。一颗纽扣电池标称容量为200mAh,如果MCU始终运行在满速状态(功耗约20mA),理论续航仅10小时;而通过合理的睡眠模式配置,将待机功耗压低至μ...

关键字: C语言 GD32

异常处理的本质是"兜底"。‌ C语言没有try-catch,但GD32的硬故障异常(HardFault、BusFault、UsageFault、MemManage)天然提供了最后一道防线。当程序跑飞、...

关键字: C语言 GD32

USART(通用同步/异步收发器)是MCU与外部设备通信最基础也最常用的外设之一。在GD32系列MCU中,USART支持全双工异步通信,通过TX/RX两根信号线即可实现与PC上位机、蓝牙模块、其他MCU的数据交互。

关键字: C语言 GD32
关闭