title: TIM输出比较 tags: [STM32, TIM, PWM, 电机]
输出比较(Output Compare)的基本工作流程:
TIM_CLK → PSC分频 → CNT计数器 → 比较器(CNT vs CCR) → OC输出(高/低/翻转/PWM)
↑
CCR(捕获比较寄存器)
核心逻辑:计数器 CNT 不断递增,与捕获比较寄存器 CCR 进行比较,根据比较结果控制 OC 引脚输出电平。
PWM 模式是最常用的输出比较模式:
ARR=999, CCR=300, 向上计数模式
CNT: 0 → 300 → 900 → 999 → 0 → 300 → ...
████████████████░░░░░░░░░░░░░░░░░
↑ ↑
有效电平 无效电平
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 无效,否则有效 |
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 | 备注 |
|---|---|---|---|
| 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 |
#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;
}
}
}
#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);
}
}
#include "stm32f10x.h"
// AIN1/AIN2 控制正反转,PWM 控制速度
// 初始化 PA0(CH1) 和 PA1(CH2) 为 PWM 输出
// 正转:TIM_SetCompare2(TIM2, 0) → 关闭反转PWM
// TIM_SetCompare1(TIM2, speed) → 设置正转速度
CCR 修改立即生效:默认无预加载,TIM_SetCompare1() 写入后下一周期立即生效;如需同步更新,请使能 TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable)
PWM 频率避开人耳范围:20Hz–20kHz 是听觉范围,电机/PWM 尽量避免此区间(用 >20kHz 或 <20Hz),否则会听到啸叫
互补通道:TIM1/TIM8 才有互补输出(TIM_OutputNState),普通定时器 TIM2–TIM5 没有,配置后无效
GPIO 模式必须为 AF_PP:复用推挽输出,定时器才能控制引脚;如果设为普通推挽(GPIO_Mode_Out_PP),引脚不受定时器控制
ARR 预装载:修改 ARR 后建议使能 TIM_ARRPreloadConfig(TIMx, ENABLE),否则可能立即改变周期造成异常波形
TIM_OCPolarity 决定有效电平是高还是低:TIM_OCPolarity_High 时 CCR 越大占空比越大;TIM_OCPolarity_Low 时 CCR 越大占空比越小
PWM 初始电平:定时器使能前,引脚为 GPIO 默认状态;可先配置 GPIO 初始电平避免上电抖动