TIM定时器

STM32 定时器详解

定时中断、输出比较、输入捕获、触发源与从模式

面向嵌入式入门,以 STM32F10x 标准外设库(StdPeriph Library) 为例。
不绑定具体工程,可直接作为学习笔记或实验参考。


目录

  1. 定时器是什么
  2. 四种常见用法
  3. 输出比较(Output Compare)
  4. 输入捕获(Input Capture)
  5. 触发源与从模式
  6. 主模式与从模式联动
  7. 标准库代码示例
  8. 常用 API 对照表
  9. 配置思路 checklist

一、定时器是什么

1.1 本质

定时器(Timer,STM32 中常写作 TIM)是一个 硬件计数器

时钟脉冲 → [计数器 CNT] → 计满/匹配 → 产生中断或改变引脚
  • 每来一个时钟 tick,CNT 加 1(或减 1)
  • 可以设置 预分频(PSC)自动重装载(ARR / Period),控制计数速度
  • 计到设定值时,可以:
    • 触发 中断
    • 改变 GPIO 电平(PWM)
    • 记录 外部事件时间(输入捕获)

1.2 两个核心参数

参数寄存器含义
预分频 PSCTIM_PSC把定时器时钟降频
周期 ARRTIM_Period计数器最大值,计满后溢出

计数频率公式(STM32 常见):

定时器计数频率 = TIM_CLK / (PSC + 1)
溢出周期     = (ARR + 1) / 定时器计数频率

1.3 计算示例

假设 TIM 时钟为 72MHz

PSCARR计数频率溢出周期
72-11000-11MHz (1μs/tick)1ms
72-110000-11MHz10ms
720-11000-1100kHz10ms

1.4 TIM1~TIM8 功能与引脚对照

以下以 STM32F103 为基准(标准外设库最常用型号)。
TIM5、TIM8 仅存在于高密度 / 超大容量型号;TIM6、TIM7 无外部 GPIO 引脚。
引脚重映射需开启 AFIO 时钟并调用 GPIO_PinRemapConfig(),详见参考手册 Alternate function remapping 章节。

1.4.1 类型与能力总览

定时器类型总线通道支持功能特有功能
TIM1高级APB24 + 互补 3定时中断、OC/PWM、IC、编码器、主从、ETR重复计数器 RCR、互补输出 CHxN、死区、刹车 BKIN
TIM2通用APB14定时中断、OC/PWM、IC、编码器、主从、ETRETR 固定 PA0
TIM3通用APB14定时中断、OC/PWM、IC、编码器、主从、ETR
TIM4通用APB14定时中断、OC/PWM、IC、编码器、主从
TIM5通用APB14同 TIM232 位计数器(仅 XL 密度)
TIM6基本APB10定时中断、主模式 TRGO可触发 DAC
TIM7基本APB10定时中断、主模式 TRGO可触发 DAC
TIM8高级APB24 + 互补 3同 TIM1同 TIM1(仅高密度 / XL 密度)

各通道可复用的功能(TIM1-TIM5、TIM8 的 CH1-CH4 均适用):

功能说明对应引脚角色
输出比较 / PWMCNT 匹配 CCR 时驱动引脚CHx(复用推挽输出)
输入捕获外部边沿锁存 CNT 到 CCRCHx(浮空 / 上拉输入)
编码器接口两相正交脉冲计数CH1 + CH2
外部时钟模式 1外部脉冲驱动 CNTCH1 或 CH2
单脉冲输出硬件产生一个脉冲CHx

1.4.2 TIM1(高级定时器)

信号默认映射部分重映射 GPIO_PartialRemap_TIM1
CH1PA8PA8
CH2PA9PA9
CH3PA10PA10
CH4PA11PA11
CH1N(互补)PB13PA7
CH2N(互补)PB14PB0
CH3N(互补)PB15PB1
BKIN(刹车)PB12PA6
ETR(外部触发)PA12PA12

1.4.3 TIM2(通用定时器)

信号默认映射部分重映射 1 GPIO_PartialRemap1_TIM2部分重映射 2 GPIO_PartialRemap2_TIM2完全重映射 GPIO_FullRemap_TIM2
CH1PA0PA15PA0PA15
CH2PA1PB3PA1PB3
CH3PA2PA2PB10PB10
CH4PA3PA3PB11PB11
ETRPA0(固定)PA0(固定)PA0(固定)PA0(固定)

TIM2 的 ETR 引脚 始终为 PA0,与 CH1 默认引脚相同,重映射 CH1 不影响 ETR。


1.4.4 TIM3(通用定时器)

信号默认映射部分重映射 GPIO_PartialRemap_TIM3完全重映射 GPIO_FullRemap_TIM3
CH1PA6PB4PC6
CH2PA7PB5PC7
CH3PB0PB0PC8
CH4PB1PB1PC9
ETRPD2PD2PD2

ETR(PD2)仅在 64 引脚及以上 封装存在。


1.4.5 TIM4(通用定时器)

信号默认映射重映射 GPIO_Remap_TIM4
CH1PB6PD12
CH2PB7PD13
CH3PB8PD14
CH4PB9PD15

TIM4 无专用 ETR 引脚(外部时钟走 CH1/CH2 从模式)。


1.4.6 TIM5(通用定时器,XL 密度)

信号引脚说明
CH1PA0与 TIM2 默认 CH1 冲突,同一时刻只能用一个
CH2PA1与 TIM2 默认 CH2 冲突
CH3PA2与 TIM2 默认 CH3 冲突
CH4PA3与 TIM2 默认 CH4 冲突

TIM5 为 32 位 计数器,适合超长周期定时;无引脚重映射选项。


1.4.7 TIM6 / TIM7(基本定时器)

定时器外部引脚功能
TIM6仅定时中断;TRGO 可触发 DAC1
TIM7仅定时中断;TRGO 可触发 DAC2

基本定时器没有 CHx 引脚,不能用于 PWM / 输入捕获 / 编码器。


1.4.8 TIM8(高级定时器,高密度 / XL 密度)

信号默认映射
CH1PC6
CH2PC7
CH3PC8
CH4PC9
CH1N(互补)PA7
CH2N(互补)PB14
CH3N(互补)PB15
BKIN(刹车)PA6

TIM8 无引脚重映射;功能与 TIM1 对称,常用于第二路电机 PWM。


1.4.9 引脚冲突与选型提示

常见冲突涉及定时器建议
PA0~PA3TIM2 默认 vs TIM5二选一,或重映射 TIM2
PA6 / PA7TIM3 默认 vs TIM8 互补避免同时使用
PB0 / PB1TIM3 CH3/4 vs TIM1 部分重映射互补规划时错开
PC6~PC9TIM3 完全重映射 vs TIM8 默认二选一

快速选型参考(F103 最小系统板常见外设):

需求推荐定时器典型引脚
简单周期中断TIM2 / TIM3 / TIM4无需引脚
LED / 舵机 PWMTIM2 CH1~4PA0~PA3
编码器测速TIM3PA6 + PA7
电机互补 PWM + 刹车TIM1PA8 + PB13 + PB12
第二路电机 PWMTIM8PC6 + PA7
DAC 同步触发TIM6 / TIM7无引脚,TRGO → DAC

二、四种常见用法

定时器 TIM
├── 1. 定时中断(Time Base)     ← OS tick、周期任务、超时
├── 2. PWM 输出(输出比较 OC)   ← 电机调速、LED 亮度、舵机
├── 3. 输入捕获 IC             ← 测脉宽、测频率、测周期
└── 4. 编码器 / 外部时钟模式    ← 脉冲计数、测转速(进阶)

三者对比

定时中断输出比较 OC输入捕获 IC
方向内部计时MCU → 外部引脚外部引脚 → MCU
触发CNT 计到 ARRCNT == CCR外部边沿到来
主要用途系统 tick、周期任务PWM、方波、脉冲测脉宽、频率、周期
CPU 占用中断里需处理硬件自动,几乎不占硬件锁存,中断可选
典型 APITIM_IT_UpdateTIM_OCxInit PWMTIM_ICInit

三、输出比较(Output Compare)

3.1 是什么

输出比较 = 当计数器 CNT 和某个 比较寄存器 CCR 相等时,硬件自动对 引脚 做预定动作。

CNT:  0 ──1──2──...── CCR ──...── ARR ──0──1──...

                   到达 CCR 时触发动作

动作可以是

  • 置高 / 置低 / 翻转引脚
  • 产生中断(不直接改引脚)

3.2 与 PWM 的关系

PWM 就是输出比较最典型的应用。

ARR = 999CCR = 300

CNT  0 ───────── 300 ───────── 999 ──0──
引脚 ████████░░░░░░░░░░░░░░░░░░░░░░░░
     |← 30% 高 →|←── 70% 低 ──→|
  • CCR 越大 → 高电平时间越长 → 占空比越大
  • ARR 决定 PWM 频率
f_PWM = TIM_CLK / ((PSC + 1) × (ARR + 1))

3.3 输出比较模式(STM32)

模式行为
Frozen比较匹配时不改变引脚(只中断)
Active on match匹配时置高
Inactive on match匹配时置低
Toggle匹配时翻转 → 可生成方波
PWM Mode 1/2标准 PWM

3.4 典型应用

场景说明
LED 调光占空比控制亮度
舵机固定 50Hz,脉宽 1~2ms 控制角度
直流电机调速占空比 ≈ 平均电压
步进 STEP 脉冲定时翻转或单脉冲输出

四、输入捕获(Input Capture)

4.1 是什么

输入捕获 = 当 外部引脚 出现指定边沿(上升/下降)时,硬件 立刻把当前 CNT 值锁存到 CCR,并可触发中断。

外部信号:  ___/‾‾‾‾\___
CNT:       0  5  10  15  20 ...
                 ↑上升沿时,CCR = 10(锁存)

核心:用硬件记录「事件发生时的精确时刻」,不需要 CPU 轮询。

4.2 能测什么

(1)测脉宽

上升沿捕获 → CCR1 = t1
下降沿捕获 → CCR2 = t2
脉宽 = CCR2 - CCR1

应用:超声波测距、红外遥控解码。

(2)测频率 / 周期

连续两次 上升沿 捕获:

周期 T = CCR2 - CCR1
频率 f = 1 / T

应用:转速测量、信号频率检测。

(3)测占空比

一个通道捕上升沿,另一个捕下降沿,或同一通道交替切换边沿。

4.3 为什么不用轮询

方式问题
while(GPIO) 轮询CPU 占用高,边沿可能漏掉
外部中断 + 读 tick可行,但有中断响应抖动
输入捕获硬件在边沿瞬间锁存 CNT,精度最高

4.4 与触发源的区别

输入捕获 IC触发源 TRGI
目的记录边沿时的 CNT 值控制 CNT 怎么数 / 何时数
结果读到 CCR,用于测时间复位 / 门控 / 启动计数
类比拍照记下时刻遥控器控制播放 / 暂停 / 归零

五、触发源与从模式

5.1 整体模型

一个定时器 TIM 有两套角色:

┌─────────────────────────────────────┐
│              TIMx                   │
│                                     │
│  主模式 Master  ──TRGO──→  输出触发  │  「我触发别人」
│                                     │
│  从模式 Slave   ←──TRGI──  输入触发  │  「别人触发我」
│                                     │
│  [PSC] [CNT] [ARR] [CCR]            │
└─────────────────────────────────────┘
术语英文含义
触发源Trigger Source (TRGI)用哪个信号作为「触发输入」
从模式Slave Mode收到触发后,定时器 做什么动作
主模式Master Mode (TRGO)本定时器向外 发什么触发信号

5.2 触发源(TRGI)可选来源

触发源回答的是:「我根据什么信号来决定下一步动作?」

通过 TIM_SelectInputTrigger(TIMx, TIM_TS_xxx) 选择,写入 SMCR 寄存器的 TS 位。

硬件信号来源

触发源说明
ITR0 ~ ITR3来自 其他定时器 的 TRGO(内部联动)
TI1FP1 / TI2FP2来自本定时器 CH1 / CH2 引脚 的边沿,经输入滤波器
TI1F_EDCH1 双边沿 检测(不经过滤波器路径 FP1)
ETRF外部触发引脚 ETR,经滤波后
内部事件比较事件、更新事件等(视芯片而定)

标准库枚举对照

常量硬件信号含义
TIM_TS_ITR0 ~ TIM_TS_ITR3ITR0 ~ ITR3其他定时器 TRGO
TIM_TS_TI1F_EDTI1F_EDCH1 双边沿
TIM_TS_TI1FP1TI1 → 滤波 → FP1CH1 引脚滤波后信号
TIM_TS_TI2FP2TI2 → 滤波 → FP2CH2 引脚滤波后信号
TIM_TS_ETRFETR → 滤波ETR 引脚滤波后信号

文档和参考手册里常写 TI1F,标准库枚举写 TI1FP1——多出的 FP 表示 Filtered Path(滤波后路径),含义相同。

TIM_TS_TI1FP1 名字拆解

部分含义
TIM_TSTimer Trigger Source,触发源
TI1Timer Input 1,通道 1 输入(对应 CH1 引脚
FP1Filtered Path 1,经 输入捕获滤波器 处理后的信号

信号路径(以 CH1 为例)

外部引脚 (TIMx_CH1,如 PA6 = TIM3_CH1)

  TI1(原始输入)

  输入滤波器(TIM_ICInit 里的 TIM_ICFilter)

  TI1FP1  ← TIM_SelectInputTrigger(TIMx, TIM_TS_TI1FP1)

  TRGI → 从模式控制器(Reset / Gated / External Clock …)

使用 TIM_TS_TI1FP1 时,通常需先对 CH1 做输入捕获初始化(配置极性与滤波),否则滤波器未生效:

/* 先配 CH1 输入 + 滤波 */
TIM_ICInitStructure.TIM_Channel     = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity  = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICFilter    = 0x3;   /* 滤波强度 */
TIM_ICInit(TIM3, &TIM_ICInitStructure);

/* 再选触发源 + 从模式 */
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_External1);  /* 每脉冲 CNT+1 */

与输入捕获的区别

触发源 TRGI输入捕获 IC
作用决定 CNT 怎么数(复位 / 门控 / 外部时钟)边沿到来时把 CNT 锁存到 CCR
配置 APITIM_SelectInputTrigger + TIM_SelectSlaveModeTIM_ICInit + 捕获中断
典型用途编码器脉冲计数、外部同步清零测脉宽、测频率
类比遥控器:归零 / 暂停 / 步进秒表:按下时记下当前时间

两者可共用同一 CH1 引脚和滤波配置,但功能独立——前者管「怎么计数」,后者管「记录时刻」。

5.3 从模式(Slave Mode)

从模式 = 当 TRGI 触发源 来了之后,定时器 CNT 要执行什么动作

从模式行为直观理解
Reset(复位)触发边沿来时,CNT 清零每到同步点就「对齐」
Gated(门控)触发为高时 CNT 才计数,为低时停闸门打开才流水
Trigger(触发)触发边沿来时,CNT 开始计数按一下才开始跑
External Clock Mode 1触发边沿每来一次,CNT +1外部脉冲当时钟
Disabled不听触发,自己正常跑普通定时器

时序示意

Reset 模式

TRGI:  ↑     ↑     ↑     ↑
CNT:   0─1─2─0─1─2─3─0─1─2─...
           ↑复位   ↑复位

Gated 模式

TRGI:  ___/‾‾‾‾‾\_____/‾‾‾\___
CNT:      0 1 2 3       0 1 2
          |← 只有高电平期间计数 →|

External Clock Mode 1

外部脉冲:  ↑   ↑   ↑   ↑   ↑
CNT:       0   1   2   3   4

5.4 记忆口诀

  • 触发源 = 输入是什么
  • 从模式 = 输入来了做什么
  • 主模式 = 我自己何时输出触发给别人

六、主模式与从模式联动

6.1 主模式(Master / TRGO)

主模式决定:本定时器溢出 / 比较时,向外发什么 TRGO 信号

主模式 TRGO 源含义
Reset发复位信号(少用)
Enable计数器使能信号
Update更新事件(溢出时) ← 最常用
Compare Pulse比较匹配脉冲
OCxREF某个通道比较输出

6.2 经典组合:定时器级联

需求:实现 1 秒定时,但单次 16 位 CNT 装不下 1 秒。

TIM2(主)                    TIM3(从)
ARR=999, 1ms 溢出一次   →    触发源 = ITRx
                              从模式 = External Clock
                              每收到 1 次 TRGI,CNT+1
                              CNT 到 999 → 1 秒中断

6.3 PWM 同步 + ADC 采样

TIM1 输出 PWM
主模式 TRGO = 在 PWM 特定时刻

触发 ADC 采样电流

电流采样点与 PWM 周期 严格对齐,测量更准确。

6.4 典型工程场景

场景主 / 从用法
步进脉冲计数从模式 External Clock,每个 STEP 边沿 CNT+1
编码器测速编码器 A 相当外部时钟
多路 PWM 同相位多个 TIM 从模式 Reset,共用同一 TRGI
超声波测距输出比较发触发,输入捕获测回波时间
ADC + PWM 同步主模式 TRGO 触发 ADC

七、标准库代码示例

以下示例基于 STM32F10x StdPeriph Library,系统时钟假设 72MHz

7.1 定时中断 — 10ms 周期

#include "stm32f10x.h"

void TIM2_TimeBase_Init(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    /* 72MHz / 72 = 1MHz,每个 tick = 1us */
    TIM_TimeBaseStructure.TIM_Period        = 10000 - 1;   /* 10000us = 10ms */
    TIM_TimeBaseStructure.TIM_Prescaler     = 72 - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM2, ENABLE);

    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
    {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
        /* 每 10ms 执行一次 */
    }
}

7.2 输出比较 / PWM — PA0 呼吸灯(TIM2 CH1)

#include "stm32f10x.h"

void TIM2_PWM_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    /* PA0 = TIM2_CH1 */
    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);

    /* 72MHz / 72 / 1000 = 1kHz PWM */
    TIM_TimeBaseStructure.TIM_Period        = 1000 - 1;
    TIM_TimeBaseStructure.TIM_Prescaler     = 72 - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    TIM_OCInitStructure.TIM_OCMode      = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse       = 0;
    TIM_OCInitStructure.TIM_OCPolarity  = TIM_OCPolarity_High;
    TIM_OC1Init(TIM2, &TIM_OCInitStructure);

    TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM2, ENABLE);
    TIM_Cmd(TIM2, ENABLE);
}

void TIM2_PWM_SetDuty(uint16_t duty)   /* duty: 0 ~ 999 */
{
    TIM_SetCompare1(TIM2, duty);
}

7.3 输入捕获 — 测方波周期(TIM3 CH1 / PA6)

#include "stm32f10x.h"

volatile uint32_t g_capture1 = 0;
volatile uint32_t g_capture2 = 0;
volatile uint32_t g_period_ticks = 0;

void TIM3_IC_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_ICInitTypeDef TIM_ICInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    /* PA6 = TIM3_CH1 */
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* 1MHz 计数,1 tick = 1us */
    TIM_TimeBaseStructure.TIM_Period        = 0xFFFF;
    TIM_TimeBaseStructure.TIM_Prescaler     = 72 - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    TIM_ICInitStructure.TIM_Channel     = TIM_Channel_1;
    TIM_ICInitStructure.TIM_ICPolarity  = TIM_ICPolarity_Rising;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter    = 0x0;
    TIM_ICInit(TIM3, &TIM_ICInitStructure);

    TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
    TIM_Cmd(TIM3, ENABLE);

    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void TIM3_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
    {
        static uint8_t edge_count = 0;

        if (edge_count == 0)
        {
            g_capture1 = TIM_GetCapture1(TIM3);
            edge_count = 1;
            TIM_ICInitTypeDef ic = {
                TIM_Channel_1, TIM_ICPolarity_Falling, TIM_ICSelection_DirectTI,
                TIM_ICPSC_DIV1, 0x0
            };
            TIM_ICInit(TIM3, &ic);
        }
        else
        {
            g_capture2 = TIM_GetCapture1(TIM3);
            g_period_ticks = g_capture2 - g_capture1;
            edge_count = 0;
            TIM_ICInitTypeDef ic = {
                TIM_Channel_1, TIM_ICPolarity_Rising, TIM_ICSelection_DirectTI,
                TIM_ICPSC_DIV1, 0x0
            };
            TIM_ICInit(TIM3, &ic);
        }

        TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
    }
}

/* 频率计算(1 tick = 1us 时) */
float TIM3_GetFrequency(void)
{
    if (g_period_ticks == 0) return 0.0f;
    return 1000000.0f / g_period_ticks;
}

7.4 从模式:外部时钟 — 每个脉冲 CNT+1

适合 编码器脉冲计数统计 STEP 脉冲个数

#include "stm32f10x.h"

void TIM3_ExternalClock_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_ICInitTypeDef TIM_ICInitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_6;   /* TIM3_CH1 */
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    TIM_TimeBaseStructure.TIM_Period        = 0xFFFF;
    TIM_TimeBaseStructure.TIM_Prescaler     = 0;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    TIM_ICInitStructure.TIM_Channel     = TIM_Channel_1;
    TIM_ICInitStructure.TIM_ICPolarity  = TIM_ICPolarity_Rising;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter    = 0x3;
    TIM_ICInit(TIM3, &TIM_ICInitStructure);

    /* 触发源 = TI1,从模式 = 外部时钟模式1 */
    TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
    TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_External1);
    TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);

    TIM_SetCounter(TIM3, 0);
    TIM_Cmd(TIM3, ENABLE);
}

uint16_t TIM3_GetPulseCount(void)
{
    return TIM_GetCounter(TIM3);
}

7.5 从模式:Reset — 外部信号同步清零

void TIM2_SlaveReset_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_ICInitTypeDef TIM_ICInitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;   /* TIM2_CH1 作为触发输入 */
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    TIM_TimeBaseStructure.TIM_Period        = 1000 - 1;
    TIM_TimeBaseStructure.TIM_Prescaler     = 72 - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    TIM_ICInitStructure.TIM_Channel     = TIM_Channel_1;
    TIM_ICInitStructure.TIM_ICPolarity  = TIM_ICPolarity_Rising;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter    = 0x0;
    TIM_ICInit(TIM2, &TIM_ICInitStructure);

    TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1);
    TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
    TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);

    TIM_Cmd(TIM2, ENABLE);
}

效果:CH1 每来一个上升沿,TIM2 的 CNT 归零重新计数。


7.6 主从联动 — TIM3 计数 TIM2 的溢出次数

需求:TIM2 每 1ms 溢出,TIM3 计 1000 次 → 1 秒事件。

void TIM2_Master_1ms_Init(void)
{
    TIM_TimeBaseInitTypeDef tb;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    tb.TIM_Period        = 1000 - 1;
    tb.TIM_Prescaler     = 72 - 1;
    tb.TIM_ClockDivision = TIM_CKD_DIV1;
    tb.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &tb);

    TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
    TIM_SelectMasterSlaveMode(TIM2, TIM_MasterSlaveMode_Enable);

    TIM_Cmd(TIM2, ENABLE);
}

void TIM3_Slave_CountMaster_Init(void)
{
    TIM_TimeBaseInitTypeDef tb;
    NVIC_InitTypeDef nvic;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    tb.TIM_Period        = 1000 - 1;
    tb.TIM_Prescaler     = 0;
    tb.TIM_ClockDivision = TIM_CKD_DIV1;
    tb.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &tb);

    /* ITR 编号查参考手册 Timer linking 表 */
    TIM_SelectInputTrigger(TIM3, TIM_TS_ITR2);
    TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_External1);
    TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);

    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM3, ENABLE);

    nvic.NVIC_IRQChannel = TIM3_IRQn;
    nvic.NVIC_IRQChannelPreemptionPriority = 1;
    nvic.NVIC_IRQChannelSubPriority = 0;
    nvic.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvic);
}

注意TIM_TS_ITR2 仅为示意,TIM 之间 ITR 映射关系请查阅 STM32F10x 参考手册 中的 Timer linking 表。


7.7 单脉冲输出 — 硬件发一个 STEP 脉冲

void TIM4_OnePulse_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef tb;
    TIM_OCInitTypeDef oc;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6;   /* TIM4_CH1 */
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    tb.TIM_Period        = 100 - 1;
    tb.TIM_Prescaler     = 72 - 1;
    tb.TIM_ClockDivision = TIM_CKD_DIV1;
    tb.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM4, &tb);

    oc.TIM_OCMode      = TIM_OCMode_PWM1;
    oc.TIM_OutputState = TIM_OutputState_Enable;
    oc.TIM_Pulse       = 50;
    oc.TIM_OCPolarity  = TIM_OCPolarity_High;
    TIM_OC1Init(TIM4, &oc);

    TIM_SelectOnePulseMode(TIM4, TIM_OPMode_Single);
    TIM_Cmd(TIM4, ENABLE);
}

void TIM4_TriggerOnePulse(void)
{
    TIM_SetCounter(TIM4, 0);
    TIM_Cmd(TIM4, ENABLE);
}

八、常用 API 对照表

功能关键 API
定时基TIM_TimeBaseInit
PWM / 输出比较TIM_OC1Init, TIM_SetCompare1
输入捕获TIM_ICInit, TIM_GetCapture1
选触发源TIM_SelectInputTrigger
选从模式TIM_SelectSlaveMode
主模式输出TIM_SelectOutputTrigger
使能主从TIM_SelectMasterSlaveMode
读计数值TIM_GetCounter, TIM_SetCounter
开中断TIM_ITConfig
清中断TIM_ClearITPendingBit
单脉冲TIM_SelectOnePulseMode

关键寄存器概念

TIMx_PSC    预分频
TIMx_ARR    自动重装载(周期)
TIMx_CNT    当前计数值
TIMx_CCR1   比较/捕获寄存器 1
TIMx_CCER   通道使能、极性
TIMx_CCMR   模式:输出比较 or 输入捕获
TIMx_SMCR   从模式控制(SMS、TS)
TIMx_CR2    主模式控制(MMS = TRGO 源)

九、配置思路 checklist

配定时器功能前,按顺序回答这 4 个问题:

  1. 定时器用来干什么?
    → 定时中断 / PWM / 测时间 / 计脉冲

  2. PSC 和 ARR 怎么设?
    → 算出 tick 分辨率和溢出周期

  3. 要不要和外部信号联动?
    → 要:配触发源 + 从模式;不要:普通 Time Base

  4. 要不要触发别的外设?
    → 要:配主模式 TRGO(如触发 ADC)

学习实验建议

阶段实验
入门TIM 1ms/10ms 中断 + 串口打印
进阶TIM PWM 驱动 LED 呼吸灯
进阶TIM 输入捕获测外部方波频率
高级主从级联实现 1 秒长定时
高级外部时钟模式统计脉冲数

附录:概念速记

概念一句话
定时器硬件计数器,按时钟 tick 计数
输出比较CNT 数到 CCR 时 主动改变引脚 → PWM、脉冲
输入捕获外部边沿来时 记下 CNT → 测脉宽、频率
触发源定时器「听谁的信号」
从模式听到信号后「怎么数」
主模式自己「通知谁」

方向记忆

  • 输出比较 = MCU 告诉世界「到点了」(主动输出)
  • 输入捕获 = 世界告诉 MCU「刚才那一刻是几点」(被动记录)

On this page

目录
一、定时器是什么
1.1 本质
1.2 两个核心参数
1.3 计算示例
1.4 TIM1~TIM8 功能与引脚对照
1.4.1 类型与能力总览
1.4.2 TIM1(高级定时器)
1.4.3 TIM2(通用定时器)
1.4.4 TIM3(通用定时器)
1.4.5 TIM4(通用定时器)
1.4.6 TIM5(通用定时器,XL 密度)
1.4.7 TIM6 / TIM7(基本定时器)
1.4.8 TIM8(高级定时器,高密度 / XL 密度)
1.4.9 引脚冲突与选型提示
二、四种常见用法
三者对比
三、输出比较(Output Compare)
3.1 是什么
3.2 与 PWM 的关系
3.3 输出比较模式(STM32)
3.4 典型应用
四、输入捕获(Input Capture)
4.1 是什么
4.2 能测什么
(1)测脉宽
(2)测频率 / 周期
(3)测占空比
4.3 为什么不用轮询
4.4 与触发源的区别
五、触发源与从模式
5.1 整体模型
5.2 触发源(TRGI)可选来源
硬件信号来源
标准库枚举对照
TIM_TS_TI1FP1 名字拆解
信号路径(以 CH1 为例)
与输入捕获的区别
5.3 从模式(Slave Mode)
时序示意
5.4 记忆口诀
六、主模式与从模式联动
6.1 主模式(Master / TRGO)
6.2 经典组合:定时器级联
6.3 PWM 同步 + ADC 采样
6.4 典型工程场景
七、标准库代码示例
7.1 定时中断 — 10ms 周期
7.2 输出比较 / PWM — PA0 呼吸灯(TIM2 CH1)
7.3 输入捕获 — 测方波周期(TIM3 CH1 / PA6)
7.4 从模式:外部时钟 — 每个脉冲 CNT+1
7.5 从模式:Reset — 外部信号同步清零
7.6 主从联动 — TIM3 计数 TIM2 的溢出次数
7.7 单脉冲输出 — 硬件发一个 STEP 脉冲
八、常用 API 对照表
关键寄存器概念
九、配置思路 checklist
学习实验建议
附录:概念速记