第一次接触STM32F030的PWM功能时,我被数据手册里那些寄存器描述绕得头晕。直到把开发板连上示波器,看着波形跳出来才恍然大悟——原来PWM本质上就是定时器控制的"电子开关"。以TIM15为例,这个定时器在STM32F030里属于通用定时器中的"轻量级选手",特别适合电机控制这类基础应用。
TIM15的硬件结构可以拆解为三个关键部分:时基单元、输出比较通道和刹车电路。时基单元就像心脏起搏器,由预分频器(TIM_Prescaler)和自动重装载寄存器(TIM_Period)决定心跳频率。举个例子,当主频48MHz时,设置Prescaler=0和Period=47999,实际PWM频率就是48M/(0+1)(47999+1)=1kHz。这个计算公式我当初在项目调试本上写了不下二十遍。
输出比较通道则是肌肉,负责执行具体动作。TIM15有两个通道(CH1和CH2),每个通道都有独立的捕获/比较寄存器(CCRx)。重点在于TIM_OCMode_PWM1和PWM2模式的选择,这决定了脉冲的有效电平。就像控制电机的正反转,PWM1模式下,计数器值小于CCRx时输出有效高电平;PWM2模式则相反。实际项目中我更喜欢用PWM1模式配合高极性,这样占空比调节更符合直觉。
拿到一块新的STM32F030开发板时,我通常会先用STM32CubeMX快速验证PWM功能。但手动配置寄存器才是真正理解底层的方式。以PB15引脚输出PWM为例,第一步要像开门锁一样启用相关时钟:
c复制RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM15, ENABLE);
GPIO配置有个细节容易踩坑——复用功能选择。STM32F030的PB15对应TIM15_CH1N(注意带N表示互补输出),需要配置为GPIO_AF_1复用功能。曾经因为错选AF_0导致波形输出失败,调试了半天才发现问题。完整的GPIO初始化应该这样写:
c复制GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_1);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
定时器基础配置中,TIM_ClockDivision这个参数很多人不理解。其实它只与输入滤波相关,在PWM输出场景下保持TIM_CKD_DIV1即可。真正影响频率的是TIM_Period和TIM_Prescaler这对黄金组合。有个实用技巧:先确定所需频率,再根据主频计算周期值。比如要生成20kHz PWM,假设主频48MHz,可以设Prescaler=0,Period=(48M/20k)-1=2399。
电机控制最考验PWM配置功底。去年做直流有刷电机项目时,发现简单的占空比调节会出现启动抖动问题。后来通过调整PWM模式和极性组合才解决。TIM15的PWM模式选择就像选择驾驶模式:
配合TIM_OCPolarity参数,能组合出四种输出特性。实测发现,对于大多数电机驱动芯片,TIM_OCMode_PWM1+TIM_OCPolarity_High是最佳组合。此时占空比计算公式为:(CCRx / (TIM_Period+1))×100%。例如设置CCRx=2400,Period=47999时,占空比就是5%。
电机调速的完整实现还需要加入ADC采样和PID算法。这里分享一个简易调速代码框架:
c复制void Motor_Speed_Set(uint16_t speed) // speed:0-100
{
TIM15->CCR1 = (TIM15->ARR + 1) * speed / 100;
}
while(1) {
current_speed = ADC_GetValue();
if(current_speed < target_speed) {
Motor_Speed_Set(++duty);
} else {
Motor_Speed_Set(--duty);
}
Delay_ms(10);
}
调通第一个PWM波形只是开始,真正的挑战在细节优化。有一次客户反映电机在特定速度下有啸叫声,后来发现是PWM频率落在人耳敏感范围。通过调整TIM_Prescaler将频率从1kHz提升到18kHz后问题解决。这里有个频率选择经验值:
| 应用场景 | 推荐频率范围 | 设置要点 |
|---|---|---|
| 普通直流电机 | 5kHz-20kHz | 避开人耳敏感频段 |
| 步进电机细分 | 20kHz-50kHz | 考虑驱动器响应速度 |
| LED调光 | 100Hz-1kHz | 避免肉眼可见闪烁 |
另一个常见问题是占空比突变导致电机抖动。解决方法是在修改CCRx值时启用预装载功能:
c复制TIM_OC2PreloadConfig(TIM15, TIM_OCPreload_Enable);
这样新的占空比只在下一个周期生效,避免当前周期波形被截断。如果遇到PWM输出异常,建议按以下步骤排查:
最后分享一个寄存器级调试技巧:当库函数配置不奏效时,可以直接操作寄存器。比如强制重载预分频值:
c复制TIM15->EGR |= TIM_EGR_UG; // 产生更新事件