第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 实战中的那些坑

最后跟大家分享几个我踩过的坑:

  1. PWM频率太低:电机发出“滋滋”声,那是人耳能听到的频率。建议门机系统用16kHz以上。
  2. 频率太高:MOS管开关损耗变大,发热严重。我试过100kHz,没跑几分钟,驱动板烫得能煎鸡蛋。
  3. 占空比突变:从0直接跳到100%,电机电流瞬间飙升,容易烧驱动。一定要用软启动,慢慢升。
  4. 忘了使能时钟:配置定时器前,记得调用__HAL_RCC_TIM2_CLK_ENABLE()。我刚开始学的时候,这个坑踩了不下五次。

好了,这一章的内容就到这里。PWM这东西,说难不难,说简单也不简单。关键是多动手,多拿示波器看波形。下一章咱们聊聊电梯门机的位置检测,那个更有意思。