一、输入捕获原理概述

输入捕获模式通过定时器通道检测外部信号的边沿(上升沿或下降沿),并在边沿到来时将当前计数器值(CNT)锁存到捕获寄存器(CCR)中。通过连续两次捕获的计数值差值,结合定时器时钟频率,即可计算出信号的周期或高电平时间。

对于频率测量,通常捕获相邻两个上升沿的CNT值,其差值即为一个完整周期内的计数个数。若定时器分频后计数频率为f_clk,则信号频率为f_clk / (capture2 - capture1)

对于脉宽(高电平时间)测量,则需捕获上升沿和下降沿的CNT值,两者之差对应高电平持续时间。

二、硬件资源与测量方案

以STM32F103系列为例,常用定时器TIM2、TIM3、TIM4、TIM5均支持输入捕获。每个定时器有4个独立通道,且每个通道可映射到不同的引脚。

测量方案有两种常用方式:

  • 单通道捕获:手动切换捕获边沿,先捕获上升沿,再捕获下降沿,计算脉宽。适合单通道实现,但需在中断中修改边沿极性。
  • PWM输入模式:使用同一定时器的两个通道(CH1和CH2)分别捕获上升沿和下降沿,硬件自动完成,无需切换极性,配合主从模式可同时获得周期和脉宽。

本文重点讲解PWM输入模式,因其效率最高,尤其适合测量PWM信号的频率和占空比。

三、PWM输入模式原理

STM32的PWM输入模式是输入捕获的一种特殊用法,它将TIMx_CH1映射到IC1和IC2两个捕获单元:

  • IC1捕获上升沿,记录周期计数值;
  • IC2捕获下降沿,记录脉宽计数值。

通过配置触发选择(TS=TI1FP1)和从模式(复位模式),使计数器在上升沿清零,从而CCR1直接存储周期值,CCR2直接存储脉宽值。这样无需中断处理即可实时读取两个寄存器,频率和占空比可通过简单计算获得。

四、寄存器配置步骤

以TIM3为例,使用CH1(PA6)作为PWM输入引脚,具体配置步骤如下:

  1. 开启时钟:使能GPIOA和TIM3时钟。
  2. 配置GPIO:PA6设置为浮空输入(或上拉输入,根据外部信号电平)。
  3. 设置时基:配置PSC预分频器,使计数频率适合待测信号。例如,72MHz时钟,PSC=71,则计数频率为1MHz,分辨率1μs。
  4. 配置捕获通道:将CH1设置为输入捕获,映射到TI1(即直接连接),并选择上升沿触发IC1,同时通过CC1S选择IC1映射到TI1。
  5. 设置捕获边沿:IC1上升沿,IC2下降沿(通过CCER配置CC1P=0,CC2P=1)。
  6. 配置触发和从模式:设置TS=101(TI1FP1),SMS=100(复位模式)。
  7. 使能捕获和定时器:使能CC1E、CC2E,最后使能TIM3。

五、完整代码示例(寄存器版)

// 使用TIM3_CH1 (PA6) 测量PWM频率和占空比
// 系统时钟72MHz,计数频率1MHz

void TIM3_PWM_Input_Init(void)
{
    RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;   // 使能TIM3时钟
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;   // 使能GPIOA时钟

    // PA6 浮空输入(外部信号驱动)
    GPIOA->CRL &= ~(0xF << (6 * 4));
    GPIOA->CRL |= (0x4 << (6 * 4));       // CNF=01, MODE=00 => 浮空输入

    // 时基配置:PSC=71, ARR=0xFFFF(最大计数)
    TIM3->PSC = 71;                       // 72MHz / 72 = 1MHz,每计数1次=1us
    TIM3->ARR = 0xFFFF;

    // 通道1映射到TI1,上升沿捕获(IC1)
    TIM3->CCMR1 |= (0x1 << 0);            // CC1S=01, IC1映射到TI1
    TIM3->CCER &= ~(0x1 << 1);            // CC1P=0, 上升沿捕获

    // 通道2映射到TI1,下降沿捕获(IC2)
    TIM3->CCMR1 |= (0x1 << 8);            // CC2S=01, IC2映射到TI1
    TIM3->CCER |= (0x1 << 5);             // CC2P=1, 下降沿捕获

    // 触发选择:TI1FP1作为触发输入
    TIM3->SMCR |= (0x5 << 4);             // TS=101

    // 从模式:复位模式,上升沿清零计数器
    TIM3->SMCR |= (0x4 << 0);             // SMS=100

    // 使能捕获通道
    TIM3->CCER |= (0x1 << 0);             // CC1E=1
    TIM3->CCER |= (0x1 << 4);             // CC2E=1

    // 使能TIM3
    TIM3->CR1 |= TIM_CR1_CEN;
}

typedef struct {
    uint32_t frequency;   // 单位Hz
    uint32_t duty;        // 占空比(百分比整数)
} PWM_Result;

void PWM_Measure(PWM_Result *result)
{
    uint32_t period = TIM3->CCR1;  // 周期计数值
    uint32_t pulse  = TIM3->CCR2;  // 脉宽计数值

    if (period == 0) {
        result->frequency = 0;
        result->duty = 0;
        return;
    }

    // 频率 = 1MHz / period (单位Hz)
    result->frequency = 1000000UL / period;

    // 占空比 = pulse / period * 100%
    result->duty = (uint32_t)((uint64_t)pulse * 100 / period);
}

int main(void)
{
    PWM_Result result;
    TIM3_PWM_Input_Init();

    while (1) {
        PWM_Measure(&result);
        // 在此使用result.frequency和result.duty,如串口输出
        // 注意调用间隔要大于一个输入周期,否则可能读到中间值
        delay_ms(100);
    }
}

六、注意事项

  • 预分频与精度:计数器频率越高,测量精度越高,但可测最大周期受ARR限制。若ARR=65535,计数频率1MHz时最大周期约65.5ms,即最小频率约15Hz。测量更高周期需降低计数频率或使用更高位定时器(如TIM2/TIM5为32位)。
  • 信号质量:输入信号应具有良好的边沿陡峭度,必要时使用外部施密特触发器整形,避免抖动引起误捕获。
  • 溢出处理:当信号周期大于定时器溢出周期时,需要在中断中处理计数器溢出(更新中断),累加溢出次数。若使用PWM输入模式,在复位模式下计数器不会自由溢出至ARR,因此适用于周期小于ARR的信号。
  • 连续读取一致性:在读取CCR1和CCR2时,两个寄存器并非原子更新,若在电机控制等实时性要求高的场景,建议先读取后验证或使用暂停计数器的方法,避免读到混合周期的新旧值。
  • 通道映射:PWM输入模式下,IC1和IC2的滤波(ICF)可配置以滤除高频毛刺,但会增加信号延迟,需权衡。

七、总结

利用STM32定时器的输入捕获功能,可以高效、精准地测量外部信号的频率和脉宽。PWM输入模式通过硬件自动捕获周期和脉宽,极大简化了软件逻辑。开发者只需正确配置时基、捕获通道和从模式,即可在主循环中直接读取结果,非常适合实时性要求高的嵌入式应用。掌握这一技术,也为后续实现编码器测速、红外解码等高级功能打下坚实基础。