3、PWM输出原理:PWM信号参数、占空比与频率计算、STM32定时器PWM输出配置流程
PWM(Pulse Width Modulation,脉冲宽度调制)是嵌入式系统中最基础也最常用的信号形式之一。从电机调速、LED调光到舵机控制、音频输出,PWM几乎无处不在。理解PWM的本质——用数字引脚模拟模拟输出——是掌握STM32定时器高级应用的第一步。本章将深入剖析PWM信号的三个核心参数(频率、占空比、分辨率),推导占空比与频率的计算方法,并完整梳理STM32定时器输出PWM的配置流程,最后通过一个直流电机开环调速的实战案例,将理论落地为可运行的代码。
上图展示了PWM信号的三个核心参数与STM32定时器PWM输出配置流程之间的映射关系。理解这张图的关键在于:频率由时基单元决定,占空比由比较寄存器决定,分辨率由自动重载值决定。下面我们逐一展开。
3.1 PWM信号参数:频率、占空比与分辨率
PWM信号本质上是一个周期性的数字方波,其输出电平在高低之间切换。描述一个PWM信号,需要三个基本参数:
- 频率(f):单位Hz,表示每秒完成的周期数。频率的倒数是周期T(T=1/f)。频率决定了PWM信号的“快慢”,在电机控制中,频率过低会导致电机产生 audible noise(可听噪声)和转速波动;频率过高则会增加开关损耗。直流电机调速常用10kHz~50kHz,舵机控制通常为50Hz(周期20ms)。
- 占空比(D):高电平持续时间(Ton)与整个周期(T)的比值,即 D = Ton / T × 100%。占空比决定了输出信号的平均电压。例如,5V幅值的PWM信号,占空比60%时,平均电压为3V。对于电机,占空比直接决定其平均电枢电压,从而控制转速。
- 分辨率(Resolution):占空比可调节的最小步进。在STM32定时器中,分辨率由自动重载值ARR决定。若ARR=999,则占空比可以从0/1000到1000/1000,即0.1%步进。分辨率越高,控制越精细,但ARR过大会降低PWM频率(在时钟频率固定时)。
核心公式:
PWM频率 f = TIMxCLK / ((PSC+1) × (ARR+1))
占空比 D = CCR / (ARR+1) × 100%
分辨率 = 1 / (ARR+1) × 100%
其中TIMxCLK是定时器时钟源频率,PSC是预分频器值,ARR是自动重载值,CCR是捕获/比较寄存器值。这三个公式是PWM配置的基石,务必牢记。
3.2 占空比与频率的计算方法
在实际工程中,我们通常先确定所需的PWM频率和占空比范围,然后反推定时器配置参数。计算步骤如下:
- 确定定时器时钟源:STM32定时器时钟通常来自内部时钟(如APB1或APB2定时器时钟)。例如,STM32F103系列,APB1定时器时钟最高为72MHz,APB2为72MHz。具体值需查阅参考手册的时钟树。
- 选择预分频PSC:PSC将定时器时钟分频为计数时钟。计数时钟频率 = TIMxCLK / (PSC+1)。PSC的范围是0~65535。
- 计算自动重载ARR:根据目标频率f,ARR = TIMxCLK / ((PSC+1) × f) - 1。ARR决定了计数周期,也即PWM周期。
- 设置比较值CCR:根据目标占空比D,CCR = D × (ARR+1)。CCR的值在0~ARR+1之间。
举一个具体例子:假设STM32F103的TIM2时钟为72MHz,我们需要产生一个20kHz的PWM信号,占空比可在0%~100%之间调节,要求分辨率不低于0.1%。
| 参数 | 计算过程 | 结果 |
|---|---|---|
| 目标频率f | 20kHz | 20000 Hz |
| 定时器时钟 | TIM2CLK | 72 MHz |
| 预分频PSC | 选择PSC=0,则计数时钟=72MHz | 0 |
| 自动重载ARR | ARR = 72e6 / (1 × 20000) - 1 = 3599 | 3599 |
| 分辨率 | 1/(3599+1) ≈ 0.0278% | 优于0.1%要求 |
| 50%占空比CCR | CCR = 0.5 × 3600 = 1800 | 1800 |
如果希望降低频率到1kHz,同时保持分辨率,可以增大PSC。例如PSC=71,则计数时钟=1MHz,ARR=999,频率=1kHz,分辨率=0.1%。这说明PSC和ARR共同决定了频率与分辨率的平衡。
经验法则:在电机控制中,PWM频率选择应避开机械共振频率,同时高于人耳可听范围(>20kHz)以避免噪声。如果使用IR2104等栅极驱动器,还需考虑死区时间与PWM频率的匹配。
3.3 STM32定时器PWM输出配置流程
STM32的通用定时器(TIM2~TIM5)和高级定时器(TIM1、TIM8)都支持PWM输出。配置流程可以归纳为六个步骤,下面以标准外设库(SPL)风格代码为例,展示完整配置过程。
步骤1:使能定时器和GPIO时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
步骤2:配置GPIO为复用推挽输出
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // TIM2_CH1 映射到 PA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
步骤3:配置定时器时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 3599; // ARR
TIM_TimeBaseStructure.TIM_Prescaler = 0; // PSC
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
步骤4:配置输出比较模式
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 1800; // CCR = 1800,占空比50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
步骤5:使能输出比较预装载(可选)
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
步骤6:使能定时器
TIM_Cmd(TIM2, ENABLE);
如果是高级定时器(TIM1/TIM8),还需额外使能主输出:TIM_CtrlPWMOutputs(TIM1, ENABLE);。这是因为高级定时器带有刹车功能和死区插入,主输出默认关闭。
常见陷阱:忘记使能高级定时器的主输出(MOE位),导致PWM引脚无输出。另外,GPIO复用功能配置错误(如将PA0配置为普通推挽而非复用推挽)也会导致无波形。排查时先用示波器测量引脚,再检查寄存器配置。
3.4 实战案例:直流电机开环调速
下面通过一个完整的直流电机调速案例,将上述配置流程串联起来。场景:使用STM32F103C8T6最小系统板,通过TIM2_CH1(PA0)输出PWM,经过一个NPN三极管或MOSFET驱动电路,控制一个6V直流电机的转速。要求:PWM频率20kHz,占空比从0%到100%可调,通过按键增加/减少占空比。
硬件连接:
- PA0 → 驱动电路栅极/基极
- 电机正极 → VCC(6V),电机负极 → 驱动电路开关端
- 按键1 → PB0(增加占空比),按键2 → PB1(减少占空比)
软件设计思路:主循环中检测按键,每次按下改变CCR值(步进为10,即约0.28%),并限制在0~3599范围内。PWM初始化代码与3.3节相同,只需将ARR设为3599,PSC设为0。
关键代码片段:
uint16_t duty = 1800; // 初始50%
while(1) {
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == RESET) {
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == RESET); // 消抖
if(duty < 3599) duty += 10;
TIM_SetCompare1(TIM2, duty);
}
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == RESET) {
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == RESET);
if(duty > 10) duty -= 10;
TIM_SetCompare1(TIM2, duty);
}
}
测试结果与现象:上电后电机以50%占空比运转,按下PB0键,电机转速明显提升;按下PB1键,转速下降。用示波器测量PA0引脚,可以看到频率稳定在20kHz,脉宽随按键变化。用万用表测量电机两端平均电压,从0V到6V线性变化。
风险与代价:该电路为开环控制,电机转速受负载影响较大。如果负载增大,转速会下降,但PWM占空比不变,无法自动补偿。此外,驱动电路如果使用三极管,在大电流下会有较大压降和发热;建议使用MOSFET(如IRLZ44N)并加装续流二极管。
复盘经验:开环调速适合对精度要求不高的场景。若需精确转速控制,应增加编码器反馈,构成闭环PID调速。另外,PWM频率的选择需要权衡:20kHz虽然无声,但开关损耗较大;对于小功率电机,10kHz也足够,且驱动电路发热更小。
3.5 常见误区与总结
在PWM配置与使用中,初学者常犯以下错误:
- 误区一:混淆PSC和ARR的作用。PSC是预分频,影响计数时钟频率;ARR是自动重载值,决定计数周期。两者共同决定PWM频率,但ARR还影响分辨率。
- 误区二:忽略高级定时器的主输出使能。使用TIM1/TIM8时,必须调用
TIM_CtrlPWMOutputs(),否则引脚无输出。 - 误区三:GPIO复用配置错误。PWM输出引脚必须配置为复用推挽(AF_PP),而不是普通推挽输出。
- 误区四:占空比计算错误。CCR值范围是0~ARR+1,当CCR=ARR+1时,占空比为100%(持续高电平);当CCR=0时,占空比为0%(持续低电平)。
- 误区五:动态修改ARR时未考虑同步。如果运行中修改ARR,建议使用预装载功能(TIM_ARRPreloadConfig),否则可能产生不完整的PWM周期。
本章总结:PWM输出的本质是定时器计数与比较匹配的过程。频率由PSC和ARR决定,占空比由CCR决定,分辨率由ARR决定。配置流程遵循“时钟→GPIO→时基→输出比较→通道配置→使能”的顺序。掌握这些基础,后续的输入捕获、死区插入、互补输出等高级功能都将水到渠成。