第4章 定时器与PWM生成:STM32定时器原理、PWM模式配置、占空比与频率计算、电机调速基础
各位同学,咱们今天聊点硬核的——定时器与PWM。说实话,在电梯门机系统里,PWM就是那个让电机听话的“指挥家”。你想想看,门开多大、开多快、关的时候要不要缓冲,全靠它来调度。我做了这么多年嵌入式,可以说搞不定定时器,你就别想玩转电机控制。
4.1 STM32定时器到底是个啥?
STM32的定时器,说白了就是一个会数数的硬件计数器。它内部有个寄存器,每来一个时钟脉冲就加1,加到溢出值就清零重新开始。这个溢出事件,就是我们产生PWM的“节拍器”。
STM32的定时器分三类:
- 基本定时器(TIM6/TIM7):只能计时,不能输出PWM。我一般只用它做系统心跳。
- 通用定时器(TIM2~TIM5):最常用的,有4个独立通道,可以输出4路PWM。电梯门机里,一个通道控制开门速度,一个控制关门速度,绰绰有余。
- 高级定时器(TIM1/TIM8):带死区插入、刹车功能,适合电机控制。不过门机系统用通用定时器就够了,别杀鸡用牛刀。
核心概念:定时器的核心寄存器就三个——ARR(自动重装载值)、PSC(预分频器)、CCR(捕获比较值)。ARR决定周期,PSC决定时钟分频,CCR决定占空比。记住这三个,你就掌握了PWM的命门。
4.2 PWM模式配置——让定时器“唱起歌”
配置PWM其实就三步走。我刚开始学的时候,总觉得寄存器多到头皮发麻,后来发现套路都一样。
第一步:计算时钟频率
假设系统时钟是72MHz,我们想要一个20kHz的PWM。为什么是20kHz?因为人耳听不到这个频率,电机也不会嗡嗡叫。我曾在项目里用过1kHz的PWM,结果电梯一开门,整个楼道都在响,被业主投诉了三次……
计算公式很简单:
PWM频率 = 系统时钟 / ((PSC+1) * (ARR+1))
比如我们设PSC=71,ARR=49:
频率 = 72,000,000 / ((71+1) * (49+1)) = 72,000,000 / 3600 = 20,000 Hz
嗯,刚好20kHz。这里要注意,PSC和ARR都是16位的,最大值65535,别超了。
第二步:配置GPIO复用功能
PWM信号要从芯片管脚输出,得把GPIO配置成复用推挽输出。我见过有人忘了这步,折腾了半天PWM就是不出来,最后发现管脚没配置对。
// 以TIM2的CH1为例,PA0管脚
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
第三步:初始化定时器并启动PWM
这一步就是填参数,没什么技术含量,但容易漏。我习惯把配置写成一个函数,每次调用就行。
void PWM_Init(void)
{
TIM_OC_InitTypeDef sConfigOC = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71; // PSC
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 49; // ARR
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim2);
sConfigOC.OCMode = TIM_OCMODE_PWM1; // PWM模式1
sConfigOC.Pulse = 25; // CCR,初始占空比50%
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 启动!
}
我的小习惯:初始化完成后,先用示波器看看波形。别急着接电机,万一频率不对,电机抖起来能把门机支架震松。我曾经吃过这个亏,后来每次调试都先看波形。
4.3 占空比与频率计算——数学其实很简单
占空比就是高电平时间占整个周期的比例。公式是:
占空比 = CCR / (ARR + 1) * 100%
比如ARR=49,CCR=25,占空比就是25/50=50%。
那怎么调频率呢?记住一个原则:先定频率,再调占空比。频率由ARR和PSC决定,占空比由CCR决定。两者互不干扰。
| 目标频率 | PSC | ARR | 实际频率 |
|---|---|---|---|
| 20 kHz | 71 | 49 | 20,000 Hz |
| 10 kHz | 71 | 99 | 10,000 Hz |
| 1 kHz | 719 | 99 | 1,000 Hz |
你看,只要调整ARR,频率就变了。但要注意,ARR变了之后,CCR也要跟着调,否则占空比会变。比如原来ARR=49、CCR=25是50%,ARR改成99后,CCR得改成50才能保持50%。
避坑指南:我曾经在项目里直接修改ARR的值,忘了同步修改CCR,结果电机突然全速运转,门“砰”的一声撞到限位开关上。从那以后,我每次改频率都会重新计算CCR,或者用百分比的方式动态设置:CCR = (uint32_t)((float)ARR * 0.5f)。
4.4 电机调速基础——PWM就是电机的“油门”
直流电机怎么调速?说白了就是改变电压。但STM32输出的是数字信号,只有0和3.3V。怎么办?用PWM!
PWM的平均电压 = 占空比 × 电源电压。比如占空比50%,电源12V,平均电压就是6V。电机转速和平均电压成正比,所以调占空比就是调转速。
电梯门机里,我们一般这样调速:
- 启动阶段:占空比从0慢慢升到80%,这叫软启动,防止电机冲击。
- 匀速阶段:占空比保持80%,门匀速运行。
- 减速阶段:占空比从80%降到20%,门慢慢停下来。
- 保持阶段:占空比降到10%左右,提供保持力矩,防止门被风吹开。
代码实现也很简单,就是不断修改CCR的值:
void Set_Motor_Speed(uint8_t percent)
{
uint16_t ccr_value;
ccr_value = (uint16_t)((uint32_t)htim2.Init.Period * percent / 100);
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, ccr_value);
}
调用Set_Motor_Speed(80)就是80%占空比,电机全速跑。调用Set_Motor_Speed(10)就是10%,电机慢慢转。
经验之谈:电机调速不是线性的。占空比从0到10%,电机可能根本不转,因为启动转矩不够。我一般会做个“死区补偿”,低于15%的占空比直接设成15%。另外,不同电机的参数不一样,建议拿到电机后先测一下最小启动占空比。
4.5 实战中的那些坑
最后跟大家分享几个我踩过的坑:
- PWM频率太低:电机发出“滋滋”声,那是人耳能听到的频率。建议门机系统用16kHz以上。
- 频率太高:MOS管开关损耗变大,发热严重。我试过100kHz,没跑几分钟,驱动板烫得能煎鸡蛋。
- 占空比突变:从0直接跳到100%,电机电流瞬间飙升,容易烧驱动。一定要用软启动,慢慢升。
- 忘了使能时钟:配置定时器前,记得调用
__HAL_RCC_TIM2_CLK_ENABLE()。我刚开始学的时候,这个坑踩了不下五次。
好了,这一章的内容就到这里。PWM这东西,说难不难,说简单也不简单。关键是多动手,多拿示波器看波形。下一章咱们聊聊电梯门机的位置检测,那个更有意思。