4. 定时器与PWM生成:STM32定时器详解、PWM模式配置、占空比与频率控制

电机控制这件事,说白了就是跟「时间」打交道。什么时候该通电,什么时候该断电,电流该给多大——这些全得靠定时器来掐表。我刚开始做电机驱动那会儿,总觉得定时器就是个计数的玩意儿,后来才发现,它才是整个控制系统的「心脏」。

这一章,咱们就把STM32的定时器掰开揉碎了讲。你想想看,一个电机转得快不快、稳不稳,本质上就是PWM的占空比和频率在起作用。搞懂了定时器,你就掌握了电机控制的命门。

核心要点:STM32定时器不仅仅是计时工具,更是PWM生成的硬件基础。理解它的工作原理,是电机控制入门的必修课。

4.1 STM32定时器家族:你该用哪一个?

STM32的定时器分三类:基本定时器、通用定时器、高级定时器。我见过不少新手一上来就开高级定时器,结果被一堆寄存器搞晕了。其实选对型号,事半功倍。

定时器类型 主要特性 电机控制用途
基本定时器(TIM6/TIM7) 仅基础计时,无PWM输出 产生定时中断,做软件计时
通用定时器(TIM2~TIM5) 4通道PWM、输入捕获、输出比较 普通直流电机、步进电机控制
高级定时器(TIM1/TIM8) 互补PWM、死区插入、刹车功能 BLDC、PMSM矢量控制(必须用)

我个人习惯是:做简单的有刷电机,用通用定时器就够了;一旦涉及无刷电机或者FOC,直接上高级定时器。别问我为什么——我在项目里吃过亏,通用定时器没有互补输出,做H桥驱动时差点把MOS管烧了。

4.2 定时器核心原理:从计数器到PWM

定时器工作的本质,就是一个计数器在跑。它从0开始往上数,数到某个值就翻回来重新数。这个「某个值」就是自动重装载寄存器(ARR)。

PWM是怎么来的?很简单:

  • 计数器从0开始递增
  • 当计数值小于比较值(CCR)时,输出高电平
  • 当计数值大于等于比较值时,输出低电平
  • 计数器到达ARR后归零,重新开始

嗯,这里要注意:频率由ARR决定,占空比由CCR决定。公式是这样的:

PWM频率 = 定时器时钟频率 / (预分频器 + 1) / (ARR + 1)
占空比 = CCR / (ARR + 1) × 100%

举个例子:假如定时器时钟是72MHz,预分频器设为71(即72分频),ARR设为999,那么PWM频率就是72M / 72 / 1000 = 1kHz。如果CCR设为500,占空比就是50%。

我的小技巧:调频率时先固定ARR,再调预分频器。ARR一般设成999或1999,这样占空比调节精度高,而且计算方便。我曾经为了追求高频率把ARR设得很小,结果占空比只能跳变,电机抖得像筛糠。

4.3 PWM模式配置:边沿对齐 vs 中央对齐

STM32的PWM有两种模式:边沿对齐模式和中央对齐模式。说白了,就是计数器计数的方向不一样。

  • 边沿对齐模式:计数器从0递增到ARR,然后跳回0。PWM波形的一边是对齐的。
  • 中央对齐模式:计数器从0递增到ARR,再递减回0。PWM波形左右对称。

做电机控制时,我建议用边沿对齐模式。为什么?因为中央对齐模式虽然谐波小,但更新频率减半,而且配置起来容易踩坑。除非你做的是高精度伺服,否则边沿对齐完全够用。

配置代码其实不复杂,以通用定时器TIM3为例:

// 开启定时器时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

// 配置预分频器和ARR
TIM_TimeBaseInitTypeDef timBase;
timBase.TIM_Prescaler = 71;        // 72分频,得到1MHz计数频率
timBase.TIM_Period = 999;          // ARR=999,PWM频率1kHz
timBase.TIM_CounterMode = TIM_CounterMode_Up;  // 向上计数(边沿对齐)
TIM_TimeBaseInit(TIM3, &timBase);

// 配置PWM通道
TIM_OCInitTypeDef timOC;
timOC.TIM_OCMode = TIM_OCMode_PWM1;      // PWM模式1
timOC.TIM_OutputState = TIM_OutputState_Enable;
timOC.TIM_Pulse = 500;                   // CCR=500,占空比50%
timOC.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &timOC);

// 使能定时器
TIM_Cmd(TIM3, ENABLE);

注意:高级定时器(TIM1/TIM8)使能后,还需要调用TIM_CtrlPWMOutputs(TIM1, ENABLE)才能输出PWM。我当初第一次用高级定时器时,查了半天才发现少了这行代码。

4.4 占空比与频率的动态控制

电机控制中,占空比和频率经常需要动态调整。比如启动时要软启动,占空比从0慢慢升到目标值;调速时要改变频率。

动态修改占空比很简单:

// 修改CCR值,改变占空比
TIM_SetCompare1(TIM3, newCCRValue);

动态修改频率稍微麻烦点,因为要改ARR。但要注意:修改ARR时,如果计数器正在运行,可能会产生意外的波形。我建议先关闭定时器,改完再打开:

// 安全修改频率
TIM_Cmd(TIM3, DISABLE);
TIM_SetAutoreload(TIM3, newARRValue);
TIM_Cmd(TIM3, ENABLE);

还有一种更优雅的方式:使用预装载寄存器。配置时开启TIM_ARRPreload,这样ARR会在下一个更新事件时才生效,避免波形畸变。

实战经验:做BLDC六步换相时,我需要在每个换相点同时修改三个通道的占空比。如果逐个修改,中间会有短暂的错误波形。解决办法是:先关闭定时器更新中断,批量修改所有CCR,再重新使能。这样波形切换就是「无感」的。

4.5 知识体系总览

下面这张图,是我自己梳理的定时器与PWM的知识脉络。你照着这个思路学,不会乱。

定时器与PWM生成知识体系 定时器分类 基本定时器(TIM6/7) 通用定时器(TIM2~5) 高级定时器(TIM1/8) 核心原理 计数器 + ARR + CCR 频率 = f_clk / (PSC+1) / (ARR+1) 占空比 = CCR / (ARR+1) × 100% PWM配置 边沿对齐模式 中央对齐模式 互补输出 + 死区 电机控制应用场景 直流有刷电机调速 BLDC六步换相 PMSM矢量控制(FOC)

4.6 避坑指南:我踩过的那些坑

做电机控制这些年,我在定时器上栽过不少跟头。挑几个典型的说说:

  • 坑一:预分频器改了没生效——我调试时发现频率不对,查了半天才发现预分频器有缓冲寄存器,要等更新事件后才生效。解决办法是手动产生一个更新事件:TIM_GenerateEvent(TIMx, TIM_EventSource_Update);
  • 坑二:高级定时器不输出——前面提过,忘了调TIM_CtrlPWMOutputs。这个函数在标准库和HAL库里的名字还不一样,换库时容易忘。
  • 坑三:占空比突变导致电机抖动——动态修改CCR时,如果新值和旧值差距太大,电机电流会突变。我后来加了限幅函数,每次只允许占空比变化不超过5%。

我的调试习惯:先用示波器看PWM波形,确认频率和占空比都对,再接到电机驱动板上。别一上来就接电机,万一波形不对,轻则电机乱转,重则烧驱动芯片。我吃过这个亏,烧过三块L298N。

好了,定时器和PWM这部分就讲到这里。你把这些搞明白,电机控制的基础就算打牢了。下一章咱们聊聊怎么用这些知识,让电机真正转起来。


公众号:蓝海资料掘金营,微信deep3321