1. STM32输入捕获测方波频率从零开始的实战指南第一次接触STM32的输入捕获功能时我盯着示波器上跳动的方波信号发愁——如何让单片机准确测量这个频率经过三个项目的实战踩坑终于总结出这套零基础也能快速上手的方案。不同于官方手册的复杂描述这里我会用最直白的语言带你从电路连接到代码实现完整走通整个测量流程。输入捕获本质上是个秒表功能当检测到方波边沿时记录下定时器的计数值两次捕获的差值就对应着方波的周期。以STM32F103的TIM2为例当系统时钟72MHz预分频设为71时定时器实际计数频率为1MHz每个计数1微秒若两次捕获值相差1000则周期为1ms频率就是1kHz。这种测量方式在1Hz-500kHz范围内误差可控制在0.1%以内完全满足大多数工控场景需求。2. 硬件准备与电路设计2.1 最小系统搭建需要准备以下硬件STM32F103C8T6核心板蓝色药丸板性价比最高示波器用于验证信号质量信号发生器或555定时器自制方波源杜邦线若干关键提示输入信号电压必须介于0-3.3V之间若信号源为5V电平需使用1N4148二极管10kΩ电阻构成简易电平转换电路2.2 信号接入方案推荐两种典型连接方式直接测量模式信号源 → PA0(TIM2_CH1)适用于干净的数字信号施密特整形模式信号源 → LM393比较器 → PA0适合存在噪声的模拟信号实测中发现当频率超过100kHz时必须使用短接的屏蔽线连接信号否则寄生电容会导致边沿畸变。我曾用普通杜邦线测500kHz方波结果读数波动达±3%改用屏蔽线后稳定在±0.2%。3. 软件配置全流程3.1 开发环境搭建使用Keil MDK-ARM V5进行开发关键配置步骤安装STM32F1xx_DFP芯片支持包新建工程时选择STM32F103C8器件在Manage Run-Time Environment中勾选CMSIS → COREDevice → Startup3.2 定时器初始化代码void TIM2_Capture_Init(u16 arr, u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 时基单元配置 TIM_TimeBaseStructure.TIM_Period arr; TIM_TimeBaseStructure.TIM_Prescaler psc; 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 0x04; // 4个时钟周期滤波 TIM_ICInit(TIM2, TIM_ICInitStructure); // 中断配置 NVIC_InitStructure.NVIC_IRQChannel TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); TIM_ITConfig(TIM2, TIM_IT_CC1|TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); }3.3 中断服务程序实现u32 capture_buf[2] {0}; u8 capture_cnt 0; float freq 0; void TIM2_IRQHandler(void) { if(TIM_GetITStatus(TIM2, TIM_IT_CC1) ! RESET) { capture_buf[capture_cnt] TIM_GetCapture1(TIM2); if(capture_cnt 2) { u32 period (capture_buf[1] capture_buf[0]) ? (capture_buf[1] - capture_buf[0]) : (0xFFFF - capture_buf[0] capture_buf[1]); freq 1000000.0 / period; // 单位Hz capture_cnt 0; } TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); } if(TIM_GetITStatus(TIM2, TIM_IT_Update) ! RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } }4. 精度优化与误差分析4.1 关键参数设置原则参数推荐值计算依据预分频(psc)7172MHz/(711)1MHz计数频率重载值(arr)0xFFFF最大测量周期65.535ms滤波器0x04(4个周期)滤除250ns的毛刺4.2 实测误差来源及对策定时器时钟偏差现象测量100kHz信号时出现±20Hz波动对策启用TIM2的时钟同步功能RCC_ClockSecuritySystemCmd(ENABLE);边沿抖动误差现象低频测量时读数跳变优化改用双边沿触发模式TIM_ICInitStructure.TIM_ICPolarity TIM_ICPolarity_BothEdge;中断延迟误差当频率200kHz时中断处理时间不可忽略解决方案改用DMA传输捕获值TIM_DMACmd(TIM2, TIM_DMA_CC1, ENABLE);5. 进阶应用PWM占空比测量在原有代码基础上增加下降沿捕获即可同时获取占空比TIM_ICInitStructure.TIM_ICPolarity TIM_ICPolarity_Rising; TIM_ICInit(TIM2, TIM_ICInitStructure); TIM_ICInitStructure.TIM_Channel TIM_Channel_2; TIM_ICInitStructure.TIM_ICPolarity TIM_ICPolarity_Falling; TIM_ICInitStructure.TIM_ICSelection TIM_ICSelection_IndirectTI; TIM_ICInit(TIM2, TIM_ICInitStructure);占空比计算公式duty_cycle (fall_edge - rise_edge) * 100.0 / period;6. 常见问题速查表现象可能原因解决方案读数始终为0信号未接入正确引脚检查PA0与信号源连接高频测量不稳定未启用输入滤波设置TIM_ICInitStructure.ICFilter0x08测量值偏大定时器时钟配置错误确认APB1分频系数为1偶尔出现异常值未处理计数器溢出在中断中检测TIM_IT_Update标志通过串口输出测量结果时建议采用均值滤波算法#define SAMPLE_SIZE 10 float freq_buf[SAMPLE_SIZE] {0}; void USART1_Report_Freq(void) { static u8 index 0; float sum 0; freq_buf[index] freq; if(index SAMPLE_SIZE) index 0; for(u8 i0; iSAMPLE_SIZE; i) { sum freq_buf[i]; } printf(当前频率: %.2fHz\r\n, sum/SAMPLE_SIZE); }