07-TIM输出比较.md 8.0 KB


title: TIM输出比较 tags: [STM32, TIM, PWM, 电机]

created: 2026-07-06

TIM — 输出比较

原理

输出比较(Output Compare)的基本工作流程:

TIM_CLK → PSC分频 → CNT计数器 → 比较器(CNT vs CCR) → OC输出(高/低/翻转/PWM)
                                    ↑
                                CCR(捕获比较寄存器)

核心逻辑:计数器 CNT 不断递增,与捕获比较寄存器 CCR 进行比较,根据比较结果控制 OC 引脚输出电平。

PWM 模式

PWM 模式是最常用的输出比较模式:

ARR=999, CCR=300, 向上计数模式

CNT: 0 → 300 → 900 → 999 → 0 → 300 → ...
     ████████████████░░░░░░░░░░░░░░░░░
     ↑              ↑
     有效电平      无效电平
  • PWM模式1:向上计数时,CNT < CCR 输出有效电平,CNT ≥ CCR 输出无效电平
  • PWM模式2:向上计数时,CNT < CCR 输出无效电平,CNT ≥ CCR 输出有效电平
  • 中央对齐模式:计数器先向上再向下,输出对称 PWM

频率与占空比

PWM频率 = TIM_CLK / (PSC + 1) / (ARR + 1)
占空比  = CCR / (ARR + 1)
分辨率  = 1 / (ARR + 1)

例:TIM_CLK=72MHz, PSC=71, ARR=999 → 1kHz, 占空比精度 0.1%

输出比较模式

模式 描述
TIM_OCMode_Timing 冻结,不影响 OC 引脚
TIM_OCMode_Active 匹配时强制输出高电平
TIM_OCMode_Inactive 匹配时强制输出低电平
TIM_OCMode_Toggle 匹配时翻转输出
TIM_OCMode_PWM1 CNT < CCR 有效,否则无效
TIM_OCMode_PWM2 CNT < CCR 无效,否则有效

API 表格

TIM_OCInitTypeDef

typedef struct {
    uint16_t TIM_OCMode;      // TIM_OCMode_PWM1/PWM2/Toggle/Active/Inactive/Timing
    uint16_t TIM_OutputState; // TIM_OutputState_Enable / Disable
    uint16_t TIM_OutputNState;// TIM_OutputNState_Enable / Disable (互补输出)
    uint16_t TIM_Pulse;       // 初始 CCR 值
    uint16_t TIM_OCPolarity;  // TIM_OCPolarity_High / Low
    uint16_t TIM_OCNPolarity; // 互补输出极性
    uint16_t TIM_OCIdleState; // 空闲时状态
    uint16_t TIM_OCNIdleState;
} TIM_OCInitTypeDef;

核心函数

函数 描述
TIM_OC1Init(TIMx, &TIM_OCInitStructure) 初始化通道1输出比较
TIM_OC2Init(TIMx, &) 通道2
TIM_OC3Init(TIMx, &) 通道3
TIM_OC4Init(TIMx, &) 通道4
TIM_SetCompare1(TIMx, uint16_t CCR) 设置通道1比较值
TIM_SetCompare2(TIMx, uint16_t CCR) 设置通道2比较值
TIM_SetCompare3(TIMx, uint16_t CCR) 设置通道3比较值
TIM_SetCompare4(TIMx, uint16_t CCR) 设置通道4比较值
TIM_OC1PreloadConfig(TIMx, TIM_OCPreload_Enable) 使能 CCR 预装载
TIM_ARRPreloadConfig(TIMx, ENABLE) 使能 ARR 预装载

GPIO 复用映射

定时器 通道 GPIO 备注
TIM2 CH1 PA0 C8T6可用
TIM2 CH2 PA1
TIM2 CH3 PA2
TIM2 CH4 PA3
TIM3 CH1 PA6
TIM3 CH2 PA7
TIM3 CH3 PB0
TIM3 CH4 PB1

代码示例

1. PWM 呼吸灯 (PA0, TIM2_CH1, 1kHz)

#include "stm32f10x.h"

void Delay_ms(uint32_t ms)
{
    SysTick->LOAD = 72000 - 1;
    SysTick->VAL  = 0;
    SysTick->CTRL = 0x05; // HCLK/8=9MHz → 1ms
    for (uint32_t i = 0; i < ms; i++) {
        while (!(SysTick->CTRL & (1 << 16)));
    }
    SysTick->CTRL = 0;
}

void TIM2_PWM_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;  // 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Prescaler     = 71;      // 72MHz/(71+1) = 1MHz
    TIM_TimeBaseStructure.TIM_Period        = 999;     // 1MHz/(999+1) = 1kHz
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCInitStructure.TIM_OCMode     = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse       = 0;           // 初始占空比0
    TIM_OCInitStructure.TIM_OCPolarity  = TIM_OCPolarity_High;
    TIM_OC1Init(TIM2, &TIM_OCInitStructure);

    TIM_Cmd(TIM2, ENABLE);
}

int main(void)
{
    TIM2_PWM_Init();
    uint8_t dir = 1;
    uint16_t ccr = 0;

    while (1) {
        TIM_SetCompare1(TIM2, ccr);
        Delay_ms(5);

        if (dir) {
            ccr += 10;
            if (ccr >= 999) dir = 0;
        } else {
            ccr -= 10;
            if (ccr <= 0) dir = 1;
        }
    }
}

2. SG90 舵机驱动 (PA0, TIM2_CH1, 50Hz)

#include "stm32f10x.h"

void Delay_ms(uint32_t ms) { /* 同呼吸灯 */ }

void Servo_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Prescaler     = 71;      // 1MHz
    TIM_TimeBaseStructure.TIM_Period        = 19999;   // 1MHz/20000 = 50Hz
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_OCInitStructure.TIM_OCMode     = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse       = 1500;        // 中位 1.5ms
    TIM_OCInitStructure.TIM_OCPolarity  = TIM_OCPolarity_High;
    TIM_OC1Init(TIM2, &TIM_OCInitStructure);

    TIM_Cmd(TIM2, ENABLE);
}

// angle: 0~180°
void Servo_SetAngle(uint16_t angle)
{
    // 0° → 500 (0.5ms), 90° → 1500 (1.5ms), 180° → 2500 (2.5ms)
    uint16_t pulse = 500 + (uint16_t)((uint32_t)angle * 2000 / 180);
    TIM_SetCompare1(TIM2, pulse);
}

int main(void)
{
    Servo_Init();
    while (1) {
        Servo_SetAngle(0);
        Delay_ms(1000);
        Servo_SetAngle(90);
        Delay_ms(1000);
        Servo_SetAngle(180);
        Delay_ms(1000);
    }
}

3. TB6612 直流电机驱动 (两路PWM: PA0=正转, PA1=反转)

#include "stm32f10x.h"
// AIN1/AIN2 控制正反转,PWM 控制速度
// 初始化 PA0(CH1) 和 PA1(CH2) 为 PWM 输出
// 正转:TIM_SetCompare2(TIM2, 0) → 关闭反转PWM
//       TIM_SetCompare1(TIM2, speed) → 设置正转速度

常见坑点

  1. CCR 修改立即生效:默认无预加载,TIM_SetCompare1() 写入后下一周期立即生效;如需同步更新,请使能 TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable)

  2. PWM 频率避开人耳范围:20Hz–20kHz 是听觉范围,电机/PWM 尽量避免此区间(用 >20kHz 或 <20Hz),否则会听到啸叫

  3. 互补通道:TIM1/TIM8 才有互补输出(TIM_OutputNState),普通定时器 TIM2–TIM5 没有,配置后无效

  4. GPIO 模式必须为 AF_PP:复用推挽输出,定时器才能控制引脚;如果设为普通推挽(GPIO_Mode_Out_PP),引脚不受定时器控制

  5. ARR 预装载:修改 ARR 后建议使能 TIM_ARRPreloadConfig(TIMx, ENABLE),否则可能立即改变周期造成异常波形

  6. TIM_OCPolarity 决定有效电平是高还是低:TIM_OCPolarity_High 时 CCR 越大占空比越大;TIM_OCPolarity_Low 时 CCR 越大占空比越小

  7. PWM 初始电平:定时器使能前,引脚为 GPIO 默认状态;可先配置 GPIO 初始电平避免上电抖动