STM32输入捕获原理与应用:从脉宽测量到PWM输入模式详解

STM32输入捕获原理与应用:从脉宽测量到PWM输入模式详解
1. 从“计时”到“捕获”为什么我们需要输入捕获如果你已经跟着教程玩过STM32的通用定时器那你应该对它的“计时”和“输出”功能不陌生了。比如我们可以让定时器每隔1毫秒产生一个中断或者让它在某个引脚上输出一个精确的PWM波。这些功能的核心是定时器在“主动”地工作它按照我们设定的节奏有条不紊地计数、比较、触发动作。但现实世界中的信号往往不是那么听话地按我们的节奏来。比如你想测量一个外部按键被按下的持续时间或者一个旋转编码器输出的脉冲宽度甚至是一个超声波模块返回的高电平时间。这些信号的到来是“被动”的、随机的。定时器如果还像以前那样自顾自地数数就没办法精确捕捉这些外部事件发生的时刻和持续的长度。这时候输入捕获Input Capture功能就登场了。它就像是给定时器这个“时钟”装上了一双敏锐的“眼睛”和一只快速的“手”。当指定的外部引脚上发生一个特定事件比如上升沿或下降沿时这双“眼睛”会立刻发现然后这只“手”会以最快的速度把当前定时器的计数值CNT瞬间“抓拍”下来保存到一个专用的捕获/比较寄存器CCR里。这个过程是硬件自动完成的速度极快不受软件中断延迟的影响从而实现了对信号边沿时间的超高精度测量。所以TIM输入捕获要解决的核心问题就是如何高精度地测量外部信号的脉宽、周期或频率。这是从“主动控制”到“被动感知”的关键一步在电机测速、红外解码、频率计、脉冲计数等场景中不可或缺。理解了这一点我们再去配置那些寄存器就会明白每一个操作背后的意图而不是机械地抄代码了。2. 输入捕获的完整工作链路与核心寄存器剖析输入捕获不是一个孤立的开关而是一套精密的联动机制。为了彻底搞懂它我们必须把从引脚到寄存器再到中断的整个数据流和事件流梳理清楚。以最常见的测量高电平脉宽为例其硬件工作链路如下图所示我们会在后续章节用文字详细拆解每一步整个链路可以分解为几个关键环节每个环节都对应着寄存器的特定配置。2.1 信号输入通道与边沿检测器首先外部信号需要通过GPIO引脚进入定时器。STM32的定时器输入通道通常与特定的GPIO引脚复用需要先将引脚配置为复用功能模式并映射到对应的定时器通道上。信号进入定时器后首先到达的是边沿检测器。这个检测器的工作状态由TIMx_CCER寄存器中的CCxP和CCxNP位控制。对于基本输入捕获我们通常只使用CCxP位CCxP0捕获发生在上升沿。CCxP1捕获发生在下降沿。这个配置决定了“眼睛”在什么时刻会触发“抓拍”。2.2 捕获的瞬间预分频器、滤波器与CCR寄存器当边沿检测器触发后信号并不会直接去“抓拍”CNT值而是会先经过两个可选的“处理站”输入滤波器和预分频器它们由TIMx_CCMRx寄存器配置。输入滤波器ICxF这相当于一个数字防抖模块。如果输入信号有毛刺滤波器可以设置一个采样窗口只有当信号在连续N个时钟周期内保持稳定才认为是一个有效的边沿。这对于消除机械开关抖动或噪声干扰非常有用。输入预分频器ICxPSC这是一个“事件分频器”。比如设置为012分频则意味着每2个有效的边沿事件才会触发一次捕获。这常用于测量频率你不需要捕获每个边沿只需每隔几个周期捕获一次来计算频率可以降低CPU中断负担。信号经过或绕过这两个模块后真正的“抓拍”动作发生硬件将此刻定时器计数器TIMx_CNT的值一次性锁存到对应的捕获/比较寄存器TIMx_CCRx中。这是输入捕获最核心的一步CCRx里保存的就是边沿发生时刻的时间戳。2.3 通知CPU中断与DMA“抓拍”完成后硬件需要通知CPU“数据已就绪快来处理”。这里有两种方式中断通知这是最常用的方式。当捕获事件发生时硬件会置位TIMx_SR寄存器中的CCxIF标志位。如果我们在TIMx_DIER寄存器中使能了CCxIE中断就会触发定时器的捕获中断。在中断服务函数里我们可以读取CCRx的值并进行计算。DMA请求在超高频率或需要连续捕获的场景下频繁进入中断可能成为瓶颈。此时可以启用DMA。当CCxIF标志置位时它也可以作为一个DMA请求源自动将CCRx寄存器中的值搬运到指定的内存数组中完全不需要CPU干预。这对于高速数据流采集至关重要。2.4 定时器时基测量的尺子别忘了CCRx里捕获的只是一个数字这个数字的意义取决于TIMx_CNT这把“尺子”是怎么走的。定时器的时基单元由TIMx_PSC和TIMx_ARR等寄存器配置决定了这把尺子的刻度。时钟源CNT的时钟从哪里来内部时钟CK_INT外部时钟还是其他定时器预分频器PSC对时钟源进行分频得到计数时钟CK_CNT。若系统时钟72MHzPSC71则CK_CNT 72MHz / (711) 1MHz即CNT每1微秒加1。计数模式向上、向下还是中央对齐这决定了CNT的变化规律。自动重装载值ARRCNT计数的上限。达到ARR后根据计数模式决定是清零、从ARR向下计数还是产生更新事件。测量的本质就是计算两次捕获的CNT值之差再乘以CNT的时钟周期。因此时基配置的精度直接决定了测量的精度。3. 实战测量PWM波高电平脉宽的两种经典方案理论很丰满现在我们来点实际的。假设我们要测量一个PWM信号的高电平宽度这是一个非常经典的需求。这里我分享两种最常用、也最具代表性的方案并详细解释为什么这么设计以及它们各自的优劣。3.1 方案一双沿捕获 中断处理适合中低频思路清晰这是最直观的方法分别在上升沿和下降沿各捕获一次两次捕获值之差就是高电平期间CNT计数的个数。步骤拆解与配置详解初始化时基配置定时器以合适的频率计数。例如使用72MHz系统时钟PSC设为71则计数频率为1MHz1微秒/计数。ARR可以设到最大如0xFFFF因为我们只关心两次捕获的差值只要差值不超过ARR就不会有定时器溢出问题。// 时基单元初始化示例 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_Period 0xFFFF; // ARR自动重装载值 TIM_TimeBaseInitStructure.TIM_Prescaler 71; // PSC预分频器72M/(711)1M TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; // 向上计数 TIM_TimeBaseInit(TIMx, TIM_TimeBaseInitStructure);配置输入捕获通道关键在这里我们需要在运行中动态切换捕获边沿。初始状态设置为上升沿捕获。当第一个上升沿到来时捕获CNT值到CCR1并进入中断。// 初始配置为上升沿捕获 TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICInitStructure.TIM_Channel TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity TIM_ICPolarity_Rising; // 上升沿 TIM_ICInitStructure.TIM_ICSelection TIM_ICSelection_DirectTI; // 直接映射到TI1 TIM_ICInitStructure.TIM_ICPrescaler TIM_ICPSC_DIV1; // 不分频 TIM_ICInitStructure.TIM_ICFilter 0x0; // 不滤波 TIM_ICInit(TIMx, TIM_ICInitStructure);中断服务函数逻辑在第一次上升沿捕获中断中我们做三件事 a. 读取CCR1的值存为Rise_Value。 b.立即将捕获极性改为下降沿(TIM_SetIC1Polarity(TIMx, TIM_ICPolarity_Falling))。 c. 清除中断标志。当下一个下降沿到来时硬件会自动捕获CNT值到CCR1并再次进入中断。在第二次下降沿中断中 a. 读取CCR1的值存为Fall_Value。 b. 计算脉宽PulseWidth Fall_Value - Rise_Value注意处理CNT溢出见后文。 c.将捕获极性改回上升沿为下一次测量做准备。 d. 清除中断标志。注意这里有一个非常重要的细节。TIM_ICInit函数在标准库中会同时配置CCMR和CCER寄存器。而切换极性时我们使用TIM_SetIC1Polarity函数它只修改TIMx_CCER寄存器的CC1P位这是一种更安全、更高效的做法避免了重新初始化整个通道可能带来的副作用。方案一优缺点分析优点原理简单易于理解和调试。只需要一个捕获通道。缺点每个PWM周期需要进入两次中断CPU开销较大。如果PWM频率很高比如几十kHz中断频繁可能影响系统其他任务。另外在中断中切换极性有极小的软件延迟对于超窄脉冲可能引入误差。3.2 方案二PWM输入模式硬件全自动适合高频STM32的通用定时器提供了一个强大的“作弊”功能——PWM输入模式。它本质上是将两个输入捕获通道通常是TI1和TI2联动起来硬件自动完成对一个周期内上升沿和下降沿的捕获并且将两次捕获值存放到不同的CCR寄存器中通常只产生一个中断。配置精髓信号连接将被测PWM信号连接到定时器的TI1引脚。寄存器配置魔法将TIMx_CCMR1寄存器中的CC1S位设置为01表示TI1映射到IC1。关键步骤将CC2S位设置为10表示TI1也映射到IC2。这样同一个TI1输入信号同时进入了IC1和IC2两条捕获通路。配置IC1捕获上升沿IC2捕获下降沿。最后设置TIMx_SMCR寄存器中的SMS111选择“复位模式”并将TS位选择为TI1FP1。这个配置的意思是当TI1上出现上升沿IC1捕获时不仅会捕获CNT值到CCR1还会同时将CNT计数器清零复位。硬件自动流程第一个上升沿到来IC1捕获CNT值到CCR1此时CNT可能是任意值同时CNT被清零开始重新向上计数。接下来的下降沿到来IC2捕获CNT值到CCR2。这个值就是从上升沿到下降沿的计数即高电平脉宽。再下一个上升沿到来IC1再次捕获CNT值到CCR1。注意此时CNT已经从一个上升沿计数到了下一个上升沿所以CCR1中的值就是整个PWM信号的周期。同时这个上升沿再次将CNT清零循环往复。如此一来我们只需要使能CC1IE中断上升沿中断在中断里同时读取CCR1周期值和CCR2脉宽值就能一次性获得周期和占空比方案二优缺点分析优点硬件自动完成每个PWM周期只产生一次中断效率极高软件开销极小精度最高无软件切换延迟。缺点占用了一个定时器的两个捕获通道IC1和IC2。逻辑上稍微复杂一点需要理解SMCR寄存器的复位模式。个人心得在资源允许的情况下优先考虑PWM输入模式。它不仅是“高效”的体现更是“把专业的事交给硬件”这一嵌入式设计哲学的典范。除非定时器通道特别紧张或者需要测量非PWM的单个脉冲宽度否则双沿切换方案更多是用于理解原理。4. 精度提升与边界处理溢出、滤波与时钟校准即使方案选对了在实际测量中还是会遇到各种影响精度和稳定性的“坑”。这部分是教程里往往一笔带过但实际开发中必须严肃对待的。4.1 计数器溢出处理让测量范围突破16位我们的CNT计数器是16位的对于通用定时器最大值65535。当时基时钟为1MHz时最大只能测量65.535ms的脉宽。超过这个时间CNT就会从0重新开始溢出。如果我们简单地用Fall_Value - Rise_Value当下降沿发生在下一个计数周期时计算结果会是负数或一个很小的值完全错误。解决方案引入溢出计数器。使能更新中断在TIMx_DIER寄存器中使能UIE更新中断。每当CNT从ARR溢出回到0时就会触发此中断。在中断中维护一个全局变量例如uint16_t TIMx_Overflow_Cnt 0;。每次进入更新中断这个变量加1。修正捕获计算在捕获中断中计算脉宽时公式变为// 假设 Rise_Value 和 Fall_Value 是捕获时刻的CCRx值 // Overflow_Cnt_Rise 和 Overflow_Cnt_Fall 是捕获时记录的溢出计数器快照 uint32_t total_ticks (uint32_t)Fall_Value - (uint32_t)Rise_Value; total_ticks (uint32_t)(Overflow_Cnt_Fall - Overflow_Cnt_Rise) * 65536UL;这样测量的时间范围就从16位扩展到了(溢出次数 * 65536 CCR差值)只要你的溢出计数器变量足够大比如32位理论上可以测量任意长的时间。踩坑记录务必在捕获中断中立刻读取并保存当前的溢出计数器值因为更新中断可能随时发生。一个经典的错误是先读CCR再读溢出计数在这两条语句之间恰好发生了一次溢出导致计数对不上。更稳妥的做法是在进入捕获中断后暂时关闭更新中断读取并保存所有必要值后再开启但这会增加中断延迟。需要根据实际情况权衡。4.2 输入滤波器的合理使用在稳定性和响应速度间权衡输入滤波器不是摆设在信号有噪声时是救命稻草。但滤波深度ICxF位的值设置不当会带来新问题。滤波过深例如设置需要连续8个时钟采样稳定才确认边沿。如果信号频率很高边沿变化快可能永远无法满足滤波条件导致信号“消失”无法触发捕获。滤波过浅或无滤波信号上的毛刺会被误认为是边沿导致捕获值剧烈跳动测量结果不稳定。如何设置这里有个经验法则滤波器的窗口时间T_filter (ICxF) * T_ck_int。其中T_ck_int是定时器输入滤波器的时钟周期通常等于APB总线时钟或2倍频后的时钟需查数据手册。设置的原则是让T_filter略大于你信号上可能出现的最大毛刺宽度同时远小于你待测信号的有效脉宽或周期。例如信号有100ns的毛刺系统时钟72MHz则T_ck_int约13.9ns。设置ICxF0x88次采样则T_filter ≈ 111ns可以滤掉这个毛刺。如果待测脉宽为1ms则滤波窗口仅占0.011%影响可忽略。4.3 时钟精度的影响与校准一切测量的基石是时钟。如果你用的HSI内部高速RC振荡器其典型精度可能只有±1%这意味着你的1MHz时基实际可能在0.99MHz到1.01MHz之间波动测量结果自然也有±1%的误差。对于精度要求高的场合如精密测速必须使用高精度的外部晶振HSE。更进一步即使使用了HSE定时器的时钟路径从晶振到APB总线再到定时器也可能存在微小的分频误差。对于极端苛刻的场合可以考虑用更高精度的外部时钟源如GPS的1PPS信号来校准定时器或者使用定时器的外部时钟模式。5. 从输入捕获到高级应用编码器接口与触发注入掌握了基本输入捕获其实你已经摸到了STM32定时器一些高级功能的大门。它们本质上都是输入捕获机制的组合与升级。5.1 正交编码器接口本质上是双通道输入捕获的协同读取旋转编码器如伺服电机上的是工业控制中的常见任务。编码器输出两路相位差90度的方波A相和B相。STM32的定时器硬件直接提供了“编码器接口模式”可以自动根据A、B相的边沿和相对相位判断转向并计数。你可以这样理解它它相当于同时配置了两个输入捕获通道CH1和CH2都工作在双边沿捕获模式。硬件内部有一个逻辑单元根据两个通道捕获事件的先后顺序自动控制CNT计数器向上或向下计数。你只需要读取CNT的值就知道编码器转了多少个“滴答”根据计数方向就知道正反转。这比用外部中断手动判断A、B相边沿要高效、可靠得多。5.2 定时器级联与触发注入实现同步与精准时序单个定时器的测量范围有限。有时我们需要测量很长的脉冲或者需要多个定时器同步动作。这时就需要用到定时器的从模式和触发功能。主从定时器级联你可以配置一个定时器TIM2作为主模式输出一个更新事件作为触发信号TRGO。另一个定时器TIM3配置为从模式选择ITR0内部触发0具体连接关系查手册作为时钟源。这样TIM2的每次溢出更新都会驱动TIM3的CNT加1。TIM3的CNT变成了TIM2的溢出计数器。此时用TIM3做输入捕获其CNT的高16位TIM2的溢出次数和低16位TIM2的CNT值共同组成了一个32位甚至更宽的计数器可以测量超长时间。触发注入这是一个更高级的功能。假设你在用定时器输出复杂的PWM序列如电机驱动SVPWM同时需要在一个由外部事件如过流保护信号触发的特定时刻立刻捕获当前的时间点用于故障分析。你可以配置输入捕获通道为“触发注入”模式。当指定的外部触发信号到来时硬件会暂停当前的输出比较等操作强制进行一次捕获并将结果存到专用的寄存器中。这保证了即使在定时器忙于输出时也能以最高优先级记录下关键事件的发生时刻。从基础的脉宽测量到编码器读数再到复杂的定时器网络同步其内核思想一脉相承利用硬件捕获外部事件发生的时间戳。理解了输入捕获这个核心再去学习这些高级功能你会发现它们不再是孤立的、复杂的知识点而是同一棵树干上生长出的不同枝丫。