## 一、输入捕获原理概述 输入捕获模式通过定时器通道检测外部信号的边沿(上升沿或下降沿),并在边沿到来时将当前计数器值(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。 ## 五、完整代码示例(寄存器版) ```c // 使用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输入模式通过硬件自动捕获周期和脉宽,极大简化了软件逻辑。开发者只需正确配置时基、捕获通道和从模式,即可在主循环中直接读取结果,非常适合实时性要求高的嵌入式应用。掌握这一技术,也为后续实现编码器测速、红外解码等高级功能打下坚实基础。