STM32定时器输入捕获实现方波频率测量
1. 为什么需要测量方波频率在嵌入式系统开发中频率测量是一项基础但至关重要的功能。无论是工业控制中的传感器信号监测还是消费电子中的用户输入检测准确测量方波频率都是常见需求。以STM32为例使用其内置的定时器输入捕获功能可以高效实现这一目标。我刚接触STM32时曾用示波器测量PWM信号后来发现完全可以用MCU自己实现这个功能。这不仅节省了硬件成本还能将频率数据直接用于系统控制算法实现闭环反馈。2. 输入捕获的基本原理2.1 定时器与输入捕获通道STM32的通用定时器如TIM2-TIM5通常都支持输入捕获功能。每个定时器有4个独立的输入捕获通道TIMx_CH1-CH4可以配置为捕获上升沿、下降沿或双边沿。当指定边沿到来时定时器的当前计数值会被自动锁存到对应的捕获/比较寄存器CCRx中。通过记录两次捕获事件的时间差就能计算出输入信号的周期。2.2 测量频率的核心算法假设我们捕获到连续两个上升沿的时刻分别为T1和T2定时器的时钟频率为Fclk分频系数为PSC则信号周期T和频率F的计算公式为T (T2 - T1) * (PSC 1) / Fclk F 1 / T这里有个关键点(PSC1)是因为分频器是从0开始计数的。我曾经在这个细节上栽过跟头导致计算结果总是偏差一个分频系数。3. 硬件连接与配置3.1 典型硬件连接方式以测量外部信号发生器产生的方波为例信号源正极 → STM32的任意GPIO需映射到定时器输入通道信号源负极 → STM32的GND注意如果信号电压超过3.3V必须使用分压电路或电平转换芯片否则会损坏MCU。3.2 CubeMX配置步骤启用对应定时器如TIM3将某个通道如CH1配置为Input Capture direct mode设置预分频器PSC和自动重装载值ARR对于72MHz主频测量1kHz信号PSC71ARR0xFFFF配置NVIC启用捕获中断这里有个实用技巧PSC的设置应该使定时器计数周期略大于待测信号周期。例如测量1kHz信号周期1ms72MHz/(711)1MHz这样每个计数代表1μs1ms对应计数值1000既保证了精度又不会溢出。4. 代码实现详解4.1 初始化代码// TIM3初始化示例 void MX_TIM3_Init(void) { TIM_IC_InitTypeDef sConfigIC {0}; htim3.Instance TIM3; htim3.Init.Prescaler 71; htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 0xFFFF; htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_IC_Init(htim3); sConfigIC.ICPolarity TIM_ICPOLARITY_RISING; sConfigIC.ICSelection TIM_ICSELECTION_DIRECTTI; sConfigIC.ICPrescaler TIM_ICPSC_DIV1; sConfigIC.ICFilter 0; HAL_TIM_IC_ConfigChannel(htim3, sConfigIC, TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(htim3, TIM_CHANNEL_1); }4.2 中断处理逻辑uint32_t IC_Val1 0, IC_Val2 0; uint32_t Difference 0; uint8_t Is_First_Captured 0; // 是否第一次捕获 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM3) { if (Is_First_Captured 0) { IC_Val1 HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); Is_First_Captured 1; } else { IC_Val2 HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); if (IC_Val2 IC_Val1) Difference IC_Val2 - IC_Val1; else Difference (0xFFFF - IC_Val1) IC_Val2; // 计算频率 (单位Hz) float frequency 1000000.0 / Difference; // 1MHz时钟 printf(Frequency: %.2f Hz\r\n, frequency); Is_First_Captured 0; } } }这段代码有个关键点处理计数器溢出的情况。当IC_Val2 IC_Val1时说明定时器发生了溢出此时实际时间差应该是(0xFFFF - IC_Val1) IC_Val2。5. 实测中的常见问题与解决方案5.1 测量结果不稳定可能原因信号抖动添加硬件滤波或软件中值滤波中断优先级冲突确保定时器中断有足够高的优先级接地不良检查电路接地解决方案在CubeMX中增加输入滤波sConfigIC.ICFilter 0x0F; // 最大滤波值5.2 高频信号测量不准对于高于定时器时钟频率的信号直接测量会失效。这时可以采用分频法硬件分频后再测量周期测量法测量多个周期取平均使用更高频率的定时器时钟我曾经测量一个2MHz信号发现直接测量误差很大。后来将定时器时钟配置为144MHz通过PLL倍频问题迎刃而解。5.3 占空比测量技巧如果需要同时测量频率和占空比配置两个通道分别捕获上升沿和下降沿计算上升沿到下降沿的时间得到高电平时间计算两个上升沿时间得到周期占空比 高电平时间 / 周期 * 100%6. 进阶应用自动量程频率计对于宽范围频率测量如1Hz-1MHz可以动态调整定时器配置void Adjust_Timer_Prescaler(float measured_freq) { uint16_t new_psc 0; if (measured_freq 100000) { // 100kHz new_psc 0; // 不分频 } else if (measured_freq 1000) { // 1kHz-100kHz new_psc 71; // 1MHz计数频率 } else { // 1kHz new_psc 7199; // 10kHz计数频率 } __HAL_TIM_SET_PRESCALER(htim3, new_psc); }这种自适应方法既能保证高频信号的测量精度又能避免低频信号测量时的计数器溢出。7. 性能优化技巧DMA传输捕获值对于高频测量可以使用DMA将捕获值直接传输到内存减少中断开销定时器级联将两个定时器级联扩展计数范围滑动窗口滤波在软件中实现滑动平均算法提高稳定性一个实测有效的优化当测量频率高于10kHz时可以禁用printf输出改为定时批量发送数据这样可以减少串口通信对测量的影响。在STM32F4系列上实测优化后的代码可以稳定测量高达10MHz的信号使用72MHz定时器时钟无分频误差小于0.1%。