2、GD32定时器与PWM生成:高级定时器配置,互补PWM与死区插入

做FOC控制,说白了就是跟三个东西打交道:电流采样、PWM生成、还有算法运算。而PWM生成这块,是连接你代码和物理世界的桥梁。今天我们就来聊聊GD32的高级定时器,看看怎么用它生成FOC需要的互补PWM,以及那个让人又爱又恨的死区时间。

2.1 为什么FOC需要高级定时器?

普通的定时器,比如GD32的通用定时器(TIMER2、TIMER3这些),也能输出PWM。但FOC控制要求三相桥臂的上下管不能同时导通,否则就是短路。这就需要一对互补的PWM信号,而且上下沿之间要插入一段“死区时间”。

高级定时器(TIMER0、TIMER7)内部集成了硬件死区插入逻辑。你只需要配置好参数,硬件自动帮你处理。我刚开始做FOC时,曾经试图用软件延时来模拟死区,结果电机一启动就炸管。嗯,从那以后我再也不敢用软件做死区了。

核心结论: FOC的PWM必须使用高级定时器的互补输出模式,死区由硬件生成,绝对不能用软件延时。

2.2 GD32高级定时器结构概览

GD32的高级定时器,本质上是一个16位的自动重装载计数器。但它比通用定时器多了几个关键模块:

  • 刹车功能(Break Function):紧急情况下强制关闭所有PWM输出
  • 互补输出(Complementary Outputs):自动生成反向信号
  • 死区发生器(Dead-Time Generator):可编程的死区插入
  • 多通道同步:三相互补PWM必须严格同步

下面这张图是我自己整理的GD32高级定时器PWM生成路径,你看一眼就明白了:

GD32高级定时器PWM生成路径 CK_TIMER时钟 PSC预分频器 16位自动重装载计数器 (ARR = 周期-1) 捕获/比较通道(CCR) 输出控制/极性/使能 死区发生器(DTG) CHx CHxN CHx输出上管PWM,CHxN输出下管PWM,两者之间自动插入死区

2.3 互补PWM与死区插入配置

配置高级定时器输出互补PWM,其实就三步。我习惯按这个顺序来,不容易漏掉关键寄存器。

2.3.1 第一步:配置时基单元

时基单元决定了PWM的频率。FOC控制通常用10kHz到20kHz的PWM频率。我一般用16kHz,因为人耳对这个频率不太敏感,电机噪音小一些。

/* 配置TIMER0时基 */
timer_parameter_struct timer_init_struct;

timer_struct_para_init(&timer_init_struct);
timer_init_struct.prescaler         = 107;      // 108MHz / (107+1) = 1MHz
timer_init_struct.period            = 62;       // 1MHz / (62+1) = 16kHz
timer_init_struct.clockdivision     = TIMER_CKDIV_DIV1;
timer_init_struct.repetitioncounter = 0;        // 不重复计数
timer_init(TIMER0, &timer_init_struct);
小技巧: 我习惯先算好时钟树。GD32F303的主频是108MHz,APB2定时器时钟也是108MHz。预分频器设107,得到1MHz的计数时钟。ARR设62,周期就是63个计数,16kHz。这个组合在FOC里很常用。

2.3.2 第二步:配置互补输出通道

这一步是关键。你需要指定哪个通道输出上管,哪个输出下管。GD32的高级定时器,CHx是上管,CHxN是下管。

/* 配置通道0为PWM模式1 */
timer_oc_parameter_struct oc_init_struct;

timer_oc_struct_para_init(&oc_init_struct);
oc_init_struct.outputstate  = TIMER_CCX_ENABLE;    // 使能CH0输出
oc_init_struct.outputnstate = TIMER_CCXN_ENABLE;   // 使能CH0N输出
oc_init_struct.ocpolarity   = TIMER_OC_POLARITY_HIGH;  // CH0高电平有效
oc_init_struct.ocnpolarity  = TIMER_OCN_POLARITY_HIGH; // CH0N高电平有效
oc_init_struct.ocidlestate = TIMER_OC_IDLE_STATE_LOW;  // 空闲时CH0低电平
oc_init_struct.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW; // 空闲时CH0N低电平
timer_channel_output_config(TIMER0, TIMER_CH_0, &oc_init_struct);

/* 配置PWM模式1:递增计数时,CNT < CCR时输出有效电平 */
timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, 31);  // 50%占空比
timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_PWM1);
timer_channel_output_shadow_config(TIMER0, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);

2.3.3 第三步:配置死区时间

死区时间取决于你用的功率管。IGBT一般需要2-3微秒,MOSFET可以短一些,0.5-1微秒就够了。我做过一个项目,客户用的国产MOSFET,死区设了1.2微秒,结果管子还是发热。后来换成1.5微秒才稳定。

/* 配置死区时间 */
/* DTG[7:0] = 死区时间值 */
/* 死区时间 = DTG * Tck_timer */
/* Tck_timer = 1 / 1MHz = 1us */
/* 设死区1.5us,DTG = 1.5 / 1 = 1.5,取整为2 */
timer_break_parameter_struct break_init_struct;

timer_break_struct_para_init(&break_init_struct);
break_init_struct.deadtime       = 2;      // 死区时间 = 2 * 1us = 2us
break_init_struct.breakstate     = TIMER_BREAK_ENABLE;   // 使能刹车功能
break_init_struct.breakpolarity  = TIMER_BREAK_POLARITY_LOW;
break_init_struct.outputautomatic = TIMER_OUTAUTO_ENABLE; // 自动恢复输出
timer_break_config(TIMER0, &break_init_struct);
注意: 死区时间不是越大越好。死区太大,会导致PWM占空比失真,电流波形畸变。死区太小,上下管可能直通。我曾经在一个项目中,死区设了0.3微秒,结果上电瞬间MOSFET直接冒烟。后来查了数据手册,那个型号的MOSFET最小死区要求是0.8微秒。

2.4 三相互补PWM的同步启动

FOC需要三相PWM严格同步。GD32的高级定时器支持多通道同时更新。你只需要把所有通道的CCR值准备好,然后通过一个软件事件同时加载。

/* 三相PWM同步更新 */
/* 先更新所有通道的占空比 */
timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, u_duty);
timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_1, v_duty);
timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_2, w_duty);

/* 产生更新事件,所有通道同时生效 */
timer_event_software_generate(TIMER0, TIMER_EVENT_SRC_UPG);

为什么要同步?你想想看,如果三相PWM不是同时更新的,那么在一个PWM周期内,三相的占空比可能来自不同的控制周期。这会导致电流波动,严重时电机抖动。我调试时遇到过这个问题,用示波器一看,三相PWM的上升沿差了几个微秒。加上同步更新后,问题就解决了。

2.5 刹车功能与故障保护

FOC系统必须考虑故障情况。比如过流、过压、电机堵转。GD32高级定时器的刹车功能,可以在硬件层面瞬间关闭所有PWM输出。

刹车源 触发条件 推荐配置
刹车输入引脚 外部故障信号(如过流比较器输出) 低电平触发,使能自动恢复
软件刹车 MCU检测到异常(如ADC过流) 调用 timer_break_enable()
CPU崩溃刹车 看门狗复位或硬件错误 刹车引脚接上拉电阻,默认关闭
/* 使能刹车功能 */
timer_break_enable(TIMER0);

/* 当刹车触发时,所有PWM输出进入空闲状态 */
/* 空闲状态由 ocidlestate 和 ocnidlestate 决定 */
经验之谈: 我建议把过流保护信号直接接到定时器的刹车引脚上,而不是通过ADC采样后再用软件处理。硬件刹车响应时间在纳秒级,软件处理至少需要几微秒。对于MOSFET来说,几微秒的短路就可能烧毁。

2.6 常见问题与避坑指南

做PWM配置时,有几个坑我踩过,分享给你:

  • 极性搞反:CHx和CHxN的极性要一致。如果上管高有效,下管低有效,那么死区时间内上下管都是关断的。但如果极性设反了,死区时间内上下管可能同时导通。
  • 空闲状态配置:刹车触发后,PWM进入空闲状态。如果空闲状态电平设错了,电机可能会反转或者抖动。我一般把空闲状态设成低电平,这样MOSFET是关断的。
  • 重复计数器:GD32的高级定时器有重复计数器,用于产生更精确的PWM周期中断。FOC中一般不用,设成0就行。如果设成非零值,PWM更新频率会变慢。
  • 影子寄存器:CCR和ARR都有影子寄存器。默认是使能的,也就是说你写入的值会在下一个更新事件才生效。如果你需要立即生效,可以关闭影子寄存器,但要注意可能产生不完整的PWM脉冲。
调试技巧: 配置完PWM后,先用示波器看波形。重点看三点:频率对不对、占空比对不对、死区时间对不对。我习惯先输出50%占空比,然后慢慢调。不要一上来就跑FOC算法,先确认PWM硬件没问题。