08-TIM输入捕获.md 9.4 KB


title: TIM输入捕获 tags: [STM32, TIM, 输入捕获, 频率测量]

created: 2026-07-06

TIM — 输入捕获

原理

输入捕获(Input Capture)的工作流程:

外部信号 → GPIO(TIx) → 滤波器 → 边沿检测 → 捕获CNT → 存入CCR
                 ↑           ↑           ↑
            ICx配置     ICFilter    ICPolarity

三种捕获模式:

直接模式: TI1 → IC1    (捕获通道1)
间接模式: TI1 → IC2    (交换捕获通道)
PWMI模式: TI1 → IC1 + TI2 → IC2  (同时捕获频率和占空比)

频率测量

上升沿    上升沿    上升沿
  ↓         ↓         ↓
  |----T----|----T----|
      捕获1    捕获2

频率 = TIM_CLK / (PSC + 1) / (Capture2 - Capture1)

PWMI 模式测占空比

          ______              ______
         |      |            |      |
_________|      |____________|      |
↑ 捕获CH1 ↑ 捕获CH2            ↑ 捕获CH1
(上升沿)   (下降沿)            (下次上升沿)

频率 = 1 / (CCR1_2 - CCR1_1)
占空比 = (CCR2 - CCR1) / (CCR1_2 - CCR1_1)

溢出处理

16位计数器(ARR=65535)在1MHz时钟下约65ms溢出一次:

CNT: ───0────65535────0────65535────
                 ↑溢出中断
记录溢出次数 overflow_cnt,则:
总时间 = overflow_cnt × 65536 + CCR

API 表格

TIM_ICInitTypeDef

typedef struct {
    uint16_t TIM_Channel;     // TIM_Channel_1/2/3/4
    uint16_t TIM_ICPolarity;  // TIM_ICPolarity_Rising / Falling / Both
    uint16_t TIM_ICSelection; // TIM_ICSelection_DirectTI / IndirectTI / TRC
    uint16_t TIM_ICPrescaler; // 0/1/2/4/8 分频 (ICxPSC)
    uint16_t TIM_ICFilter;    // 0x00~0x0F 数字滤波
} TIM_ICInitTypeDef;

核心函数

函数 描述
TIM_ICInit(TIMx, &TIM_ICInitStructure) 初始化输入捕获通道
TIM_PWMIConfig(TIMx, &TIM_ICInitStructure) 配置 PWMI 模式(同时配 CH1+CH2)
TIM_GetCapture1(TIMx) 读取 CCR1
TIM_GetCapture2(TIMx) 读取 CCR2
TIM_GetCapture3(TIMx) 读取 CCR3
TIM_GetCapture4(TIMx) 读取 CCR4
TIM_ITConfig(TIMx, TIM_IT_Update/CC1/CC2, ENABLE) 使能中断
TIM_GetITStatus(TIMx, TIM_IT_Update) 获取中断状态
TIM_ClearITPendingBit(TIMx, TIM_IT_Update) 清除中断标志
TIM_SelectInputTrigger(TIMx, TIM_TS_TI1FP1) 选择触发输入(从模式)
TIM_SelectSlaveMode(TIMx, TIM_SlaveMode_Reset) 选择从模式(复位模式自动清零CNT)

TIM_ICPolarity 枚举

说明
TIM_ICPolarity_Rising 上升沿捕获
TIM_ICPolarity_Falling 下降沿捕获
TIM_ICPolarity_Both 双边沿捕获(需 TIM5 或高级定时器)

TIM_ICSelection 枚举

说明
TIM_ICSelection_DirectTI 直接模式,TIx → ICx
TIM_ICSelection_IndirectTI 间接模式,TIx → ICy
TIM_ICSelection_TRC 内部触发输入

滤波器配置 (TIM_ICFilter)

采样频率 采样次数 说明
0x00 无滤波
0x01–0x0F fDTS/N 递增 N 次一致才输出有效电平

代码示例

1. 频率测量 (PA6, TIM3_CH1, 1MHz分辨率)

#include "stm32f10x.h"

volatile uint16_t overflows = 0;

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

void TIM3_InputCapture_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;    // 上拉输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Prescaler     = 71;      // 1MHz
    TIM_TimeBaseStructure.TIM_Period        = 65535;   // 最大周期
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    TIM_ICInitTypeDef TIM_ICInitStructure;
    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    = 0x0F;        // 最大滤波
    TIM_ICInit(TIM3, &TIM_ICInitStructure);

    // 使能更新中断(用于溢出处理)
    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

    NVIC_InitTypeDef NVIC_InitStructure;
    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);

    TIM_Cmd(TIM3, ENABLE);
}

uint32_t IC_GetFreq(void)
{
    // 等待两个上升沿
    TIM_SetCounter(TIM3, 0);
    overflows = 0;
    while (TIM_GetFlagStatus(TIM3, TIM_FLAG_CC1) == RESET); // 等待第一次捕获
    TIM_ClearFlag(TIM3, TIM_FLAG_CC1);
    while (TIM_GetFlagStatus(TIM3, TIM_FLAG_CC1) == RESET); // 等待第二次捕获
    TIM_ClearFlag(TIM3, TIM_FLAG_CC1);

    uint32_t capture = TIM_GetCapture1(TIM3);
    uint32_t total   = overflows * 65536UL + capture;
    // 频率 = 1MHz / total(单位Hz)
    return 1000000UL / total;
}

void TIM3_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {
        overflows++;
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
    }
}

int main(void)
{
    TIM3_InputCapture_Init();
    while (1) {
        uint32_t freq = IC_GetFreq();
        Delay_ms(500);
        // 此处可用串口打印 freq
    }
}

2. PWMI 模式测频率和占空比 (PA6, TIM3_CH1)

#include "stm32f10x.h"

volatile uint16_t overflows = 0;

void TIM3_PWMI_Init(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Prescaler     = 71;
    TIM_TimeBaseStructure.TIM_Period        = 65535;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    // PWMI 模式:CH1 上升沿测频率,CH2 下降沿测占空比
    TIM_ICInitTypeDef TIM_ICInitStructure;
    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    = 0x0F;
    TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);  // 自动配置 CH1+CH2

    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

    NVIC_InitTypeDef NVIC_InitStructure;
    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);

    TIM_Cmd(TIM3, ENABLE);
}

// 返回频率(Hz)和占空比(0.01%)
void IC_GetFreqAndDuty(uint32_t *freq, uint16_t *duty)
{
    TIM_SetCounter(TIM3, 0);
    overflows = 0;

    while (TIM_GetFlagStatus(TIM3, TIM_FLAG_CC1) == RESET);
    TIM_ClearFlag(TIM3, TIM_FLAG_CC1);

    while (TIM_GetFlagStatus(TIM3, TIM_FLAG_CC1) == RESET);
    TIM_ClearFlag(TIM3, TIM_FLAG_CC1);

    uint16_t ccr1_2 = TIM_GetCapture1(TIM3);  // 第二次上升沿
    uint16_t ccr2   = TIM_GetCapture2(TIM3);  // 下降沿
    uint32_t period = overflows * 65536UL + ccr1_2;

    if (period == 0) {
        *freq = 0;
        *duty = 0;
        return;
    }

    *freq = 1000000UL / period;
    *duty = (uint16_t)((uint32_t)ccr2 * 10000 / period);
}

void TIM3_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {
        overflows++;
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
    }
}

常见坑点

  1. 溢出处理:16位计数器在1MHz下每65.5ms溢出一次。测低频时必须用更新中断记录溢出次数,否则结果完全错误

  2. 滤波影响高频TIM_ICFilter 过大会滤掉窄脉冲。测高频(>100kHz)时设 TIM_ICFilter = 0 或小值

  3. 从模式复位:使用 TIM_SlaveMode_Reset 可让捕获到边沿时自动清零 CNT,无需手动 TIM_SetCounter(0),但初始化较复杂

  4. PSC 影响分辨率:PSC 越小分辨率越高,但计数器溢出更快。测低频需要大 PSC + 大 ARR,测高频需要小 PSC + 小 ARR

  5. PWMI 模式注意TIM_PWMIConfig() 会同时配置 CH1 和 CH2,不要再去手动调用 TIM_ICInit() 配置 CH2,否则覆盖

  6. 输入引脚:确保 GPIO 模式正确(浮空或上拉输入),不要误用为输出模式

  7. 捕获中断 vs 更新中断:需要分别使能,不要混淆 TIM_IT_CC1TIM_IT_Update