STM32F103定时器与PWM实战:从呼吸灯到电机控制进阶指南

在嵌入式开发领域,STM32F103系列微控制器因其出色的性价比和丰富的外设资源,成为众多工程师的首选。许多初学者通过经典的"呼吸灯"实验入门PWM技术,但往往止步于此,未能深入挖掘定时器的强大潜力。本文将带您突破基础应用,探索如何利用STM32F103的通用定时器实现舵机和步进电机的精确控制——这两种电机在机器人、3D打印机和自动化设备中无处不在。

1. STM32F103定时器系统深度解析

STM32F103系列微控制器配备了三种类型的定时器:高级定时器(TIM1/TIM8)、通用定时器(TIM2-TIM5)和基本定时器(TIM6/TIM7)。对于大多数电机控制应用,通用定时器已经能够完美胜任,它们提供了四路独立的PWM输出通道,且配置灵活。

定时器的核心工作原理围绕几个关键寄存器展开:

  • 自动重装载寄存器(ARR) :决定计数周期上限
  • 预分频寄存器(PSC) :对时钟源进行分频
  • 捕获/比较寄存器(CCR) :PWM脉宽控制的关键

PWM频率的计算公式为:

PWM频率 = 定时器时钟频率 / [(ARR+1) * (PSC+1)]

而占空比则由CCR值决定:

占空比 = CCR / (ARR+1)

下表对比了不同定时器类型的主要特性:

特性 高级定时器 通用定时器 基本定时器
PWM通道数 7 4 0
互补输出 支持 不支持 不支持
编码器接口 支持 支持 不支持
中断类型 丰富 常规 基本
典型应用 电机FOC控制 通用PWM生成 简单计时

提示:在资源允许的情况下,建议保留高级定时器(TIM1)用于更复杂的控制任务,通用定时器足以应对大多数舵机和步进电机应用。

2. 舵机控制:从理论到实践

舵机是一种位置伺服机构,其控制核心是50Hz的PWM信号(周期20ms),通过脉冲宽度在0.5ms-2.5ms范围内的变化来指定角度位置。这种控制方式看似简单,但实现精准控制需要深入理解其工作机制。

2.1 定时器参数计算与配置

以72MHz系统时钟为例,配置TIM3产生50Hz PWM:

  1. 选择预分频值(PSC)为71,将时钟分频至1MHz
  2. 设置自动重装载值(ARR)为19999,实现20ms周期:
    1MHz / (71+1) = 1MHz
    1MHz / (19999+1) = 50Hz
    
  3. 脉宽对应CCR值范围:
    • 0°位置:1000 (1ms)
    • 90°位置:1500 (1.5ms)
    • 180°位置:2000 (2ms)

实际初始化代码如下:

void Servo_PWM_Init(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    // GPIO配置
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // TIM3 CH3
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    // 时基单元配置
    TIM_TimeBaseStructure.TIM_Period = 19999;
    TIM_TimeBaseStructure.TIM_Prescaler = 71;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    
    // PWM模式配置
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_Pulse = 1500; // 初始位置90°
    TIM_OC3Init(TIM3, &TIM_OCInitStructure);
    
    TIM_Cmd(TIM3, ENABLE);
}

2.2 提高舵机控制精度的技巧

实际应用中,舵机可能出现抖动或定位不准的问题,以下是几种有效的解决方案:

  • 软件滤波 :对控制信号进行平滑处理

    #define FILTER_DEPTH 5
    uint16_t filterBuffer[FILTER_DEPTH];
    
    uint16_t SmoothingFilter(uint16_t newValue)
    {
        static uint8_t index = 0;
        static uint32_t sum = 0;
        
        sum -= filterBuffer[index];
        filterBuffer[index] = newValue;
        sum += newValue;
        index = (index + 1) % FILTER_DEPTH;
        
        return sum / FILTER_DEPTH;
    }
    
  • 电源去耦 :在舵机电源引脚附近添加100μF电解电容和0.1μF陶瓷电容

  • 机械减震 :使用橡胶垫片隔离舵机与安装结构

  • 死区补偿 :针对特定舵机特性添加微调偏移

注意:避免频繁发送微小角度变化指令,这会导致舵机电机持续工作而发热。建议设置变化阈值,只有当角度变化超过2-3°时才更新PWM信号。

3. 步进电机控制:精准脉冲生成艺术

与舵机不同,步进电机通过接收脉冲信号进行控制,每个脉冲对应一个固定的角度位移(步距角)。控制步进电机需要关注两个关键参数:脉冲频率(决定转速)和脉冲数量(决定位移)。

3.1 步进电机驱动器接口配置

常见步进电机驱动器需要三个控制信号:

  1. PUL :脉冲信号,每个上升沿触发一步
  2. DIR :方向信号,高/低电平控制正反转
  3. ENA :使能信号(可选)

使用TIM4生成脉冲信号,配合普通GPIO控制方向:

void Stepper_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    
    // 使能时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    // 配置方向控制引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2; // DIR和ENA
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    // 配置PUL引脚( TIM4 CH1 )
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    // 定时器基础配置
    TIM_TimeBaseStructure.TIM_Period = 1000; // 初始频率
    TIM_TimeBaseStructure.TIM_Prescaler = 71; // 1MHz计数频率
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
    
    // PWM模式配置 - 50%占空比方波
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_Pulse = 500; // 50%占空比
    TIM_OC1Init(TIM4, &TIM_OCInitStructure);
    
    TIM_Cmd(TIM4, ENABLE);
}

3.2 加减速控制算法实现

直接以高频率启动步进电机可能导致失步,合理的加减速曲线至关重要。常用的梯形加减速算法实现如下:

typedef struct {
    uint32_t currentFreq;
    uint32_t targetFreq;
    uint32_t stepInterval;
    uint32_t acceleration;
    uint16_t stepsRemaining;
} StepperProfile;

void Stepper_RunTrapezoid(StepperProfile *profile, uint32_t steps)
{
    uint32_t stepsAccel = (profile->targetFreq - profile->currentFreq) / profile->acceleration;
    stepsAccel = stepsAccel > steps/2 ? steps/2 : stepsAccel;
    
    // 加速阶段
    for(uint32_t i = 0; i < stepsAccel; i++) {
        profile->currentFreq += profile->acceleration;
        profile->stepInterval = 1000000UL / profile->currentFreq; // 转换为μs
        TIM4->ARR = profile->currentFreq - 1;
        Delay_us(profile->stepInterval);
    }
    
    // 匀速阶段
    uint32_t stepsConst = steps - 2 * stepsAccel;
    for(uint32_t i = 0; i < stepsConst; i++) {
        Delay_us(profile->stepInterval);
    }
    
    // 减速阶段
    for(uint32_t i = 0; i < stepsAccel; i++) {
        profile->currentFreq -= profile->acceleration;
        profile->stepInterval = 1000000UL / profile->currentFreq;
        TIM4->ARR = profile->currentFreq - 1;
        Delay_us(profile->stepInterval);
    }
}

提示:实际应用中,建议使用定时器中断来更新频率参数,避免在Delay函数中阻塞系统。可以使用S曲线算法实现更平滑的加减速过程。

4. 硬件布局与抗干扰设计

电机控制系统对电路布局极为敏感,不当的设计可能导致控制信号异常甚至MCU复位。以下是经过实际验证的有效措施:

  • 电源分离

    • 使用独立LDO为MCU供电
    • 电机电源与逻辑电源通过磁珠隔离
    • 在电源入口处布置大容量电解电容(100-470μF)
  • 信号保护

    • PWM信号线串联22-100Ω电阻
    • 敏感信号线使用双绞线或屏蔽线
    • 在信号线上添加小容量电容(10-100pF)到地
  • PCB布局要点

    • 电机驱动电路与MCU保持至少5mm间距
    • 大电流路径尽量短而宽
    • 晶振和关键信号线远离电机驱动部分

典型布局参考:

+-------------------+     +-------------------+     +-------------------+
|      STM32F103    |-----|   信号隔离电路    |-----|   电机驱动器      |
|                   |     | (光耦/电平转换)   |     |                   |
+-------------------+     +-------------------+     +-------------------+
        |                                                  |
        |                                                  |
+-------------------+                             +-------------------+
|   5V稳压电源      |                             |   电机电源        |
| (为MCU和逻辑供电) |                             | (12-24V)          |
+-------------------+                             +-------------------+

在调试过程中,如果遇到异常情况,可以按照以下步骤排查:

  1. 检查所有电源电压是否稳定
  2. 用示波器观察PWM信号波形
  3. 暂时降低电机工作电流测试
  4. 检查地线连接是否可靠
  5. 尝试逐步移除外围电路,定位干扰源

实际项目中,我曾遇到过一个棘手的问题:步进电机在高速运行时导致STM32间歇性复位。最终发现是电机电源回路的瞬态干扰通过地线耦合到了MCU的复位电路。解决方案是在复位引脚添加0.1μF电容并在电源入口增加TVS二极管,同时优化了地平面布局。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐