一、输入捕获测频原理简述

STM32定时器输入捕获模式通过检测信号边沿,将当前计数器值锁存到捕获寄存器,结合两次捕获值和时间基准计算频率。常用方法有:

  • 直接测频法:固定闸门时间,统计脉冲个数。
  • 测周法:测量相邻同向边沿间的时间间隔,取倒数得频率。

本文以测周法为例,分析预分频(PSC)与捕获边沿(上升沿/下降沿)配置带来的误差。

二、误差来源分析

2.1 预分频引入的量化误差

定时器计数频率 fCK_CNT = fCK_PSC / (PSC+1)。捕获值 CCR 的分辨率为一个计数周期 Tcnt = 1/fCK_CNT。测周法频率计算:

freq = fCK_CNT / (CCR2 - CCR1);
  • 量化误差:Δf/f ≈ 1/(CCR2-CCR1)。预分频越大,fCK_CNT 越低,相同信号周期内计数值越小,量化误差越大。
  • 预分频相位不确定性:PSC 计数器与信号边沿异步,每次捕获时 PSC 相位随机,导致捕获值存在 ±1 个计数周期的抖动。

2.2 捕获边沿配置的误差

  • 边沿抖动:信号上升/下降沿非理想,存在噪声或振铃,可能触发多次捕获。
  • 边沿不对称:若信号占空比非 50%,上升沿与下降沿间隔不等,用不同边沿测周会引入系统误差。
  • 输入滤波器延迟:配置了输入滤波(ICF)会引入固定延迟,影响高频测量。

2.3 其他误差

  • 时钟源精度(HSI 偏差可达 ±1%)。
  • 中断响应延迟(若用中断读取 CCR)。
  • 计数器溢出处理不当。

三、配置步骤与代码示例

3.1 硬件设计

  • 信号接入 TIMx_CH1(如 PA0)。
  • 使用外部晶振(HSE)作为时钟源,提高基准精度。

3.2 定时器配置(以 TIM2 为例)

void TIM2_Capture_Init(void)
{
    // 1. 时钟使能
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

    // 2. GPIO 配置为复用功能
    GPIOA->MODER &= ~(3U << (0 * 2));
    GPIOA->MODER |=  (2U << (0 * 2));  // AF mode
    GPIOA->AFR[0] |= (1U << (0 * 4));  // AF1 = TIM2_CH1

    // 3. 时基配置:预分频尽量小,提高分辨率
    TIM2->PSC = 0;          // 不分频,fCK_CNT = 84MHz(假设APB1=84MHz)
    TIM2->ARR = 0xFFFF;     // 最大自动重装

    // 4. 输入捕获配置:上升沿,无滤波,无预分频
    TIM2->CCMR1 &= ~(TIM_CCMR1_CC1S | TIM_CCMR1_IC1PSC | TIM_CCMR1_IC1F);
    TIM2->CCMR1 |= (1U << 0);  // CC1S=01,IC1映射到TI1
    TIM2->CCER &= ~(TIM_CCER_CC1P | TIM_CCER_CC1NP); // 上升沿捕获
    TIM2->CCER |= TIM_CCER_CC1E; // 使能捕获

    // 5. 使能捕获中断
    TIM2->DIER |= TIM_DIER_CC1IE;
    NVIC_EnableIRQ(TIM2_IRQn);

    // 6. 启动定时器
    TIM2->CR1 |= TIM_CR1_CEN;
}

3.3 中断服务与频率计算

volatile uint32_t cap1 = 0, cap2 = 0;
volatile uint8_t  flag = 0;
volatile float    freq = 0.0f;

void TIM2_IRQHandler(void)
{
    if (TIM2->SR & TIM_SR_CC1IF) {
        TIM2->SR &= ~TIM_SR_CC1IF;
        if (flag == 0) {
            cap1 = TIM2->CCR1;
            flag = 1;
        } else {
            cap2 = TIM2->CCR1;
            uint32_t diff = (cap2 > cap1) ? (cap2 - cap1) : (0x10000 + cap2 - cap1);
            if (diff > 0) {
                freq = 84000000.0f / diff;  // fCK_CNT = 84MHz
            }
            flag = 0;
        }
    }
}

四、校准方法

4.1 硬件校准

  • 使用高精度时钟源:外部晶振(如 8MHz ±10ppm)替代 HSI。
  • 信号调理:加施密特触发器或低通滤波,消除边沿抖动。
  • 匹配阻抗:减少反射引起的振铃。

4.2 软件校准

  • 多次测量取平均:连续测量 N 次,剔除异常值后平均。
  • 动态调整预分频:根据信号频率范围自动选择 PSC,使计数值在 1000~60000 之间,平衡分辨率与溢出。
  • 边沿一致性校验:交替捕获上升沿和下降沿,比较周期差异,判断占空比影响。
  • 补偿固定延迟:通过已知频率信号标定,计算并扣除滤波/中断延迟。

4.3 校准代码示例

#define N 10
float measure_freq_avg(void)
{
    float sum = 0;
    for (int i = 0; i < N; i++) {
        flag = 0;
        while (flag == 0);  // 等待一次完整捕获
        sum += freq;
    }
    return sum / N;
}

五、注意事项

  • 预分频不宜过大:在满足量程前提下,PSC 越小,分辨率越高。
  • 捕获边沿选择:优先使用信号质量较好的边沿;若占空比未知,可配置双边沿捕获并计算平均周期。
  • 防止计数器溢出:测周法需处理 ARR 溢出,建议使能溢出中断并扩展计数。
  • 输入滤波配置:高频信号慎用 ICF,避免引入额外延迟;低频信号可适当开启以抑制噪声。
  • 中断优先级:捕获中断应设为较高优先级,减少响应延迟。

通过合理配置预分频与捕获边沿,并结合软硬件校准,可将测频误差控制在 0.1% 以内,满足大多数嵌入式应用需求。