STM32输入捕获实战:从原理到代码实现,精准测量信号时间 1. 项目概述从“捕获”一个信号说起搞嵌入式开发尤其是玩STM32的定时器这玩意儿绝对是绕不开的核心外设。今天我们不聊那些基础的定时、PWM输出来啃一块稍微硬点的骨头——输入捕获。这名字听起来有点抽象其实干的事儿很具体精准地测量一个外部信号的“时间”。比如一个脉冲的高电平持续了多久两个上升沿之间隔了多少微秒一个PWM波的频率和占空比是多少这些问题的答案输入捕获都能给你抓出来。我最早接触输入捕获是为了做一个转速测量的小模块需要测电机编码器输出的脉冲频率。当时对着手册和例程照猫画虎代码是跑起来了但心里总是不踏实中断里为什么这么写计数值怎么换算成时间遇到信号毛刺怎么办这些问题不搞清楚项目出点怪问题就只能抓瞎。后来在几个实际项目里反复折腾、踩坑、优化才算把输入捕获从“会用”磨到了“理解”。这篇内容我就把自己这些年实操STM32输入捕获的经验、步骤和避坑心得掰开揉碎了讲给你听。无论你是刚接触STM32的新手还是想深入理解定时器机制的老鸟相信都能找到对你有用的干货。我们以最经典的STM32F1系列为例但其原理和思路通用于大多数STM32型号。核心工具是Keil MDK和一块STM32开发板当然一个能产生PWM或方波的信号源比如另一块板子、函数发生器或者就用同一个定时器输出PWM自测也是必需的。2. 输入捕获的核心原理与设计思路在撸起袖子写代码之前我们必须先弄明白STM32的定时器是怎么实现“捕获”这个功能的。这决定了我们后续的配置和代码逻辑。2.1 定时器的基础骨架时基单元你可以把STM32的每个通用定时器如TIM2, TIM3, TIM4想象成一个精密且自律的“计数器”。它的核心部件是一个16位或32位的向上/向下计数寄存器CNT。在时钟驱动下这个CNT的值会规律地递增或递减。时钟源决定它“数”得多快。可以来自内部系统时钟经过分频也可以来自外部。预分频器PSC对时钟源进行分频。比如系统时钟72MHzPSC设为71那么驱动CNT的时钟就是72M / (711) 1MHz此时CNT每1微秒变化一次。自动重装载寄存器ARR设定CNT计数的上限。当CNT计数到ARR值向上计数或计数到0向下计数时会产生一个“更新事件”并且CNT重置开始新一轮计数。这个“更新事件”的周期就是定时器产生中断或DMA请求的基准。时基单元PSC和ARR的配置直接决定了我们“时间标尺”的精度和量程。例如时钟1MHzARR设为999那么CNT从0数到999再溢出正好是1000微秒1毫秒。这个“1毫秒”就是我们测量外部信号时间的基础单位。2.2 “捕获”的瞬间发生了什么输入捕获功能建立在时基单元之上。定时器的每个通道比如CH1, CH2都有一对“捕获/比较寄存器”CCR和相关的输入电路。当外部信号通过GPIO引脚连接到定时器通道并配置为输入捕获模式后魔法就开始了边沿检测你可以选择在信号的上升沿、下降沿或双边沿触发捕获。瞬间冻结当指定的边沿到来时硬件会立即将当前CNT计数器的值“抓拍”并复制到对应的捕获/比较寄存器CCR中。这个动作是硬件自动完成的速度极快精度极高。产生中断同时如果使能了捕获中断硬件会置位一个标志位并可能触发中断。在中断服务程序里我们就能安全地读取这个被“冻结”在CCR里的值。举个例子我们想测量一个高电平脉冲的宽度。可以这么做第一次配置为上升沿捕获上升沿到来时捕获值CCR1CNT当时的值t1。在中断里立刻将通道改为下降沿捕获。下降沿到来时再次捕获CCR1CNT值t2。那么高电平时间 (t2 - t1) * 计数周期。如果计数周期是1微秒(t2-t1)是500那么高电平就是500微秒。2.3 方案选型为何选择中断方式而非DMA或查询实现输入捕获常见有三种方式查询方式主循环不断检查捕获标志位。效率低实时性差容易丢失边沿不推荐用于精确测量。DMA方式捕获发生时硬件自动将CCR值通过DMA传输到指定内存数组。适合高速、连续、大批量的信号采集不占用CPU。但对初学者来说配置复杂且对于单次或低频测量有点“杀鸡用牛刀”。中断方式捕获发生时触发中断在中断服务函数里读取CCR值并进行处理。这是我们本次重点讲解的方式。为什么选中断方式平衡性它在实时性和CPU占用率之间取得了很好的平衡。对于频率不是特别高比如几百KHz以下的信号中断处理绰绰有余。灵活性在中断里我们可以进行复杂的逻辑判断比如切换捕获边沿测脉宽、处理计数器溢出测长周期、计算频率和占空比等。学习价值理解了中断方式就掌握了输入捕获最核心的硬件交互和数据处理流程之后再学DMA方式会容易得多。资源消耗不需要额外的DMA通道代码结构清晰易于调试。注意中断方式的最大挑战是中断响应时间和中断服务程序ISR的执行速度。如果信号频率太高中断过于频繁可能导致CPU无法处理其他任务甚至丢失捕获事件。因此评估信号最高频率和ISR代码长度是关键。3. 硬件连接与软件环境准备3.1 硬件连接引脚不是随便选的STM32的定时器输入通道与GPIO引脚是复用的有固定的映射关系不能随意连接。以STM32F103C8T6的TIM2_CH1为例查数据手册找到你的芯片型号对应的数据手册Datasheet或引脚定义图。对于TIM2_CH1它可能映射到PA0、PA5、PA15等引脚具体看型号和重映射。常用组合对于基础实验PA0TIM2_CH1和PA6TIM3_CH1是常用的输入捕获引脚。我们以PA0为例。信号源连接将待测信号如PWM输出连接到开发板的PA0引脚。如果使用另一块板子的PWM输出确保共地。上拉/下拉根据待测信号特性可能需要配置GPIO的内部上拉或下拉电阻。如果信号源是推挽输出且驱动能力强可以不配。如果信号源是高阻态或开漏输出建议启用上拉对于通常为高电平脉冲为低电平的信号或下拉对于通常为低电平脉冲为高电平的信号以避免引脚悬空引入噪声。连接示意图信号源输出引脚 ---- STM32 PA0 (TIM2_CH1) 信号源地线 GND ---- STM32 GND3.2 软件环境与工程建立IDE使用Keil uVision 5 (MDK-ARM)。确保已安装对应芯片的Device Family Pack。固件库使用标准外设库Standard Peripheral Library或HAL库均可。标准库更贴近硬件利于理解原理HAL库封装程度高开发速度快。本文以标准库为例进行讲解因为其配置过程更透明。如果你用HAL库CubeMX生成的代码逻辑是相通的。新建工程选择正确的芯片型号如STM32F103C8。在项目管理中添加必要的库文件CMSIS核心文件StdPeriph_Driver下的misc.c,stm32f10x_gpio.c,stm32f10x_rcc.c,stm32f10x_tim.c。配置魔术棒Options for TargetTarget标签页正确设置晶振频率如8MHz外部晶振。C/C标签页在Define中填入USE_STDPERIPH_DRIVER, STM32F10X_MD根据你的芯片型号选择。Debug标签页选择你的调试器如ST-LINK并勾选Run to main()。准备测试信号为了自检我们可以先用同一个定时器的另一个通道输出一个已知参数的PWM波然后用输入捕获去测量它。例如用TIM2的CH2PA1输出1KHz占空比50%的PWM用CH1PA0去捕获测量。4. 代码实现从零配置输入捕获我们以实现测量PWM波频率和占空比为例分步拆解。4.1 第一步GPIO和定时器时钟使能任何外设使用前必须先打开它的时钟门控。// 开启GPIOA和TIM2的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);注意STM32F1的APB1总线挂载了TIM2~TIM7等定时器APB2总线挂载了GPIOA~G和高级定时器TIM1、TIM8。时钟使能函数不同别搞混了。4.2 第二步配置GPIO为复用输入模式PA0要作为TIM2的输入通道而不是普通的输入或输出。GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; // PA0 GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; // 上拉输入根据信号调整。也可用GPIO_Mode_IN_FLOATING浮空输入 // 关键对于定时器输入模式是复用功能但标准库中输入捕获配置下GPIO通常配置为输入模式即可。 // 更严谨的配置可以是GPIO_Mode_AF_IN但标准库中常用输入模式。 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; // 速度可选 GPIO_Init(GPIOA, GPIO_InitStructure);4.3 第三步配置定时器时基单元这是决定测量精度的核心。假设我们使用72MHz的系统时钟AHBAPB1预分频器为2则TIM2的时钟为72MHz因为APB1分频后若不为1则定时器时钟会倍频。我们配置为1MHz的计数频率这样每个计数代表1微秒。TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_Period 65535; // ARR值16位定时器最大值。用于周期测量时需考虑溢出。 TIM_TimeBaseInitStructure.TIM_Prescaler 71; // PSC值72M / (711) 1MHz TIM_TimeBaseInitStructure.TIM_ClockDivision TIM_CKD_DIV1; // 时钟分频与输入滤波相关通常DIV1 TIM_TimeBaseInitStructure.TIM_CounterMode TIM_CounterMode_Up; // 向上计数模式 TIM_TimeBaseInitStructure.TIM_RepetitionCounter 0; // 重复计数器高级定时器用通用定时器设为0 TIM_TimeBaseInit(TIM2, TIM_TimeBaseInitStructure);参数计算解析TIM_Prescaler 71预分频器寄存器值。实际分频系数 PSC 1。所以72MHz / (711) 1MHz。此时计数器CNT每增加1时间过去1 / 1MHz 1微秒。TIM_Period 65535自动重装载值。CNT从0计数到65535后溢出产生更新事件。这个周期是65536 * 1us 65.536 ms。这意味着在不处理溢出的情况下我们最多能测量65.536ms长的连续高电平或低电平。对于测量频率周期通常小于65ms够用但对于测量单个脉冲宽度如果信号周期很长就需要处理计数器溢出。4.4 第四步配置输入捕获通道这里我们配置TIM2的通道1。TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICInitStructure.TIM_Channel TIM_Channel_1; // 通道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; // 输入滤波器0表示不滤波 TIM_ICInit(TIM2, TIM_ICInitStructure);关键参数详解TIM_ICPolarity捕获极性。Rising上升沿Falling下降沿Both双边沿需要高级定时器支持。TIM_ICSelection非常重要它决定了引脚信号如何映射到捕获通道。TIM_ICSelection_DirectTI输入信号直接连接到对应的TIx例如CH1对应TI1。这是我们最常用的模式。TIM_ICSelection_IndirectTI输入信号交叉连接到另一个TIx例如CH1可以配置为捕获TI2的信号。这在一些特殊测量如PWM输入模式中用到。TIM_ICSelection_TRC捕获内部触发信号。TIM_ICPrescaler捕获预分频。可以配置为每N个边沿捕获一次用于降低中断频率。测频率/脉宽时通常设为DIV1每次边沿都捕获。TIM_ICFilter数字滤波器。设置一个采样频率和采样次数只有当连续N次采样值一致时才认为边沿有效用于滤除高频毛刺。值越大滤波效果越强但会引入延迟。对于干净的信号可以设为0。4.5 第五步配置中断并开启定时器我们需要使能捕获比较中断CC1IE和更新中断UIE。前者用于在边沿到来时读取CCR后者用于处理计数器溢出。// 使能捕获/比较1中断和更新中断 TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_Update, ENABLE); // 配置NVIC嵌套向量中断控制器 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel TIM2_IRQn; // TIM2全局中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; // 抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; // 子优先级 NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); // 使能定时器 TIM_Cmd(TIM2, ENABLE);4.6 第六步编写中断服务函数这是逻辑的核心。我们要在中断里记录时间、切换边沿、处理溢出、计算参数。首先定义几个全局变量用于在中断和主程序间传递数据volatile uint32_t IC_Value1 0, IC_Value2 0; // 记录两次捕获值 volatile uint32_t Period 0, Duty 0; // 计算的周期和占空比单位us volatile uint8_t Capture_State 0; // 捕获状态机0-等待上升沿1-已捕获上升沿等待下降沿2-已捕获下降沿等待上升沿 volatile uint16_t Overflow_Count 0; // 溢出次数计数器然后编写TIM2的中断服务函数void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) ! RESET) // 处理溢出中断 { Overflow_Count; // 溢出次数加1 TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除更新中断标志 } if (TIM_GetITStatus(TIM2, TIM_IT_CC1) ! RESET) // 处理捕获中断 { switch(Capture_State) { case 0: // 初始状态等待第一个上升沿 IC_Value1 TIM_GetCapture1(TIM2); // 读取上升沿时刻的CCR1值 Overflow_Count 0; // 清空溢出计数器开始新的测量周期 // 切换为下降沿捕获准备测量高电平宽度 TIM_OC1PolarityConfig(TIM2, TIM_ICPolarity_Falling); Capture_State 1; break; case 1: // 已捕获上升沿等待下降沿测量高电平宽度 IC_Value2 TIM_GetCapture1(TIM2); Duty (IC_Value2 - IC_Value1) (Overflow_Count * 65536); // 计算高电平时间考虑溢出 // 切换为上升沿捕获准备测量周期 TIM_OC1PolarityConfig(TIM2, TIM_ICPolarity_Rising); Capture_State 2; break; case 2: // 已捕获下降沿等待下一个上升沿测量周期 IC_Value1 TIM_GetCapture1(TIM2); Period (IC_Value1 - IC_Value2) (Overflow_Count * 65536); // 计算整个周期时间考虑溢出 Overflow_Count 0; // 为下一次测量重置溢出计数器 // 状态可以回到0或保持2取决于你想连续测周期还是重新开始 // 这里我们保持状态2连续测量周期和占空比 // 下一个下降沿会触发状态1的计算 // 注意此时IC_Value2还是上一个下降沿的值用于计算下一个周期 // 更清晰的写法是引入更多变量这里为简化逻辑演示核心思想 // 实际项目中建议用两个变量分别存储当前和上一次的捕获值 Capture_State 1; // 准备测量下一个高电平 // 切换为下降沿捕获 TIM_OC1PolarityConfig(TIM2, TIM_ICPolarity_Falling); break; } TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); // 清除捕获中断标志 } }中断逻辑解析状态机驱动使用Capture_State变量来记录测量阶段这是一个非常清晰且可靠的编程模式。溢出处理Overflow_Count用于记录在两次捕获之间定时器计数器溢出了多少次。计算时间差时必须加上Overflow_Count * 65536因为ARR65535。这是测量长周期信号不丢精度的关键。边沿切换通过TIM_OC1PolarityConfig函数动态改变捕获边沿从而实现一次上升沿、一次下降沿交替捕获分别得到脉宽和周期信息。时间计算Period和Duty的单位是“定时器计数次数”。因为我们配置了1MHz的计数时钟所以这个值直接就是微秒数。频率Freq 1000000 / Period(Hz)占空比百分比DutyCycle (Duty * 100) / Period。重要心得在中断里先读取捕获值再改变捕获边沿。这个顺序不能错。因为中断是在边沿发生时触发的此时CCR寄存器里已经是当前边沿的捕获值。读取完成后再配置下一个要捕获的边沿。4.7 第七步主程序与测试主程序初始化后可以在一个循环里打印或处理计算出的Period和Duty。#include stm32f10x.h #include stdio.h // 如果使用串口打印 // ... 上述初始化函数调用 ... int main(void) { // 系统时钟、GPIO、定时器、中断初始化调用上面的函数 TIM2_InputCapture_Init(); // 初始化串口用于打印略 USART1_Init(); printf(STM32 Input Capture Test Start...\r\n); while(1) { // 由于Period和Duty是volatile变量在中断中被更新这里可以直接使用 if(Period ! 0) // 避免除零错误 { float freq 1000000.0 / Period; // 计算频率单位Hz float duty_cycle (Duty * 100.0) / Period; // 计算占空比单位% printf(Freq: %.2f Hz, Duty: %.1f%%\r\n, freq, duty_cycle); } Delay_ms(500); // 延时500ms打印一次避免刷屏 } }5. 高级话题与性能优化基础功能实现后我们可以探讨一些更深入的话题和优化技巧。5.1 处理高频信号与中断优化当信号频率很高时比如几十KHz以上频繁的中断会消耗大量CPU资源。优化方法精简ISR中断服务函数里只做最必要的操作——读取CCR、切换状态、更新关键变量。复杂的计算如浮点运算、printf放到主循环中。使用DMA对于需要连续记录大量捕获值的应用如波形分析应使用输入捕获的DMA模式。将CCR寄存器与一个内存数组建立DMA通道捕获发生时硬件自动传输攒够一定数量后再由DMA中断或定时器统一处理极大减轻CPU负担。提高定时器时钟在系统时钟允许的情况下减小预分频器PSC提高计数频率。这样在测量相同物理时间时计数器差值更大相对误差更小。但要注意ARR溢出会更快。利用定时器溢出对于频率测量可以不测两个上升沿的时间而是在固定时间窗口内比如1秒统计上升沿触发的捕获中断次数。这需要开启更新中断1秒定时并在更新中断里读取一个在捕获中断中自增的计数器。这种方法在测低频时精度高测高频时也能稳定工作。5.2 输入滤波与边沿检测抗干扰工业环境中信号常有毛刺。STM32的输入捕获提供了硬件滤波器。TIM_ICFilter参数这个4位值0x0~0xF并非直接定义频率而是定义采样频率和连续采样次数。采样频率f_{SAMPLING}由TIMx_CR1中的CK_INT频率和TIMx_CCMR1中的ICxF位域共同决定有公式详见参考手册。例如当ICxF[3:0] 0011(0x3) 时表示以f_{DTS}/32的频率采样且连续8次采样值一致才确认边沿。建议从0开始如果发现误触发逐渐增大滤波值直到信号稳定。但要注意过强的滤波会延迟边沿检测引入测量误差。5.3 PWM输入模式硬件自动测量周期和占空比这是一个非常强大的硬件特性它利用一个定时器的两个捕获通道自动测量PWM波的周期和占空比几乎不占用CPU。原理以TIM2为例将PWM信号同时输入到TI1CH1和TI2CH2。配置为“PWM输入模式”实际上是将通道1设置为双边沿捕获并触发内部从模式控制器复位定时器。上升沿到来时同时触发两个动作a) 通道1捕获值CCR1被更新b) 计数器CNT被清零。下降沿到来时通道2捕获值CCR2被更新。这样CCR1里存储的就是PWM的周期计数器从0开始到下一个上升沿的计数值CCR2里存储的就是高电平脉宽。只需要读取这两个寄存器就能直接得到结果而且硬件自动处理了计数器复位无需软件处理溢出。配置关键// 1. 将输入信号映射到TI1使用TIM_ICSelection_DirectTI或TIM_ICSelection_TRC取决于具体连接 // 2. 配置TIMx_SMCR寄存器选择触发源为TI1从模式为复位模式。 // 3. 配置通道1为上升沿捕获通道2为下降沿捕获。 // 在标准库中有专门的函数 TIM_PWMIConfig() 来简化此配置。使用PWM输入模式可以极大地简化代码提高测量效率和可靠性特别适合固定测量PWM信号的场合。6. 调试技巧与常见问题排查即使代码逻辑正确调试阶段也可能遇到各种问题。下面是一些实战中总结的排查清单。6.1 问题速查表现象可能原因排查步骤完全无中断1. 定时器时钟未使能。2. NVIC未配置或优先级错误。3. 中断标志未清除导致后续中断被屏蔽。4. GPIO模式配置错误应为复用/输入。1. 检查RCC_APBxPeriphClockCmd。2. 检查NVIC_Init配置确保中断通道正确且使能。3. 在中断函数开头/结尾确认清除了对应标志位TIM_ClearITPendingBit。4. 用万用表或逻辑分析仪检查引脚是否有信号输入。能进中断但捕获值不变或乱跳1. 捕获极性配置错误。2. 信号本身不稳定有毛刺。3. 没有处理计数器溢出导致计算错误。4. 在中断中读取CCR的时机不对。1. 确认TIM_ICPolarity与信号边沿匹配。2. 增加输入滤波TIM_ICFilter或检查信号源质量。3. 开启更新中断并在计算时间差时加入Overflow_Count * (ARR1)。4. 确保在捕获中断发生后立即读取CCR。测量结果精度差1. 定时器时钟源不准如内部RC振荡器。2. 预分频器PSC计算错误导致计数周期不准确。3. 中断响应延迟引入误差对于极高频率信号。1. 使用外部晶振作为时钟源。2. 重新计算系统时钟树和PSC值。3. 对于高频信号考虑使用PWM输入模式或DMA方式减少软件干预。只能测到上升沿测不到下降沿或反之1. 在中断中忘记切换捕获极性。2. 切换极性的函数用错应用TIM_OCxPolarityConfig而非TIM_OCxPolarity初始化结构体。3. 状态机逻辑混乱导致边沿配置错误。1. 在中断服务函数中在读取CCR后立即使用TIM_OC1PolarityConfig()切换极性。2. 单步调试观察每次中断后TIMx_CCER寄存器中CCxP位的变化。测量长周期信号65ms不准未处理定时器溢出。使能更新中断TIM_IT_Update并维护一个Overflow_Count变量。计算时间差时差值 CCR2 - CCR1 Overflow_Count * (ARR 1)。6.2 必备调试工具逻辑分析仪几十块钱的USB逻辑分析仪如Saleae克隆版是调试数字信号的利器。可以同时抓取输入信号和某个GPIO用来在中断里翻转标记中断发生时刻的波形直观地看到信号边沿、中断响应延迟、捕获是否触发。Keil Debugger在调试模式下可以查看定时器相关寄存器的实时值CNT, CCR1, CCR2, SR状态寄存器单步执行中断服务函数观察变量变化。串口打印将计算出的周期、占空比、溢出次数等关键信息通过串口打印到PC是最基本的调试手段。6.3 一个关键的实操心得关于“影子寄存器”在配置定时器参数如PSC, ARR, CCR时需要了解“影子寄存器”概念。我们通过库函数写入的是预装载寄存器。在发生更新事件UEV时预装载寄存器的值才会被转移到影子寄存器中真正生效。TIM_ARRPreloadConfig(TIM2, ENABLE)使能ARR预装载。这意味着你可以在任何时候写ARR但新值只在下次更新事件时才生效。这可以避免在定时器运行时修改ARR导致当前计数周期错乱。对于输入捕获的CCR寄存器它没有预装载功能写入的就是当前值。但作为捕获寄存器时它是只读的由硬件在边沿触发时写入。在初始化时通常我们会使能ARR预装载确保参数同步更新。这个细节不影响基础功能但在高级应用如动态调整定时周期时很重要。7. 项目扩展与进阶思路掌握了基本的输入捕获你可以尝试以下更有挑战性的项目它们能帮你更深入地理解定时器超声波测距利用输入捕获测量HC-SR04超声波模块返回的高电平脉冲宽度从而计算距离。这是输入捕获的经典应用。编码器测速使用定时器的编码器接口模式配合光电或霍尔编码器直接读取旋转方向和计数值结合输入捕获或另一个定时器进行定时采样可以计算出精确的转速。红外遥控解码NEC等红外协议使用特定脉宽组合表示0和1。用输入捕获测量每个脉冲的高低电平宽度即可解码出按键值。频率计/占空比仪将上述代码完善加上按键切换、OLED显示做成一个便携式简易仪器。多通道同步捕获使用一个定时器的多个通道同时捕获多个相关信号的时间点用于分析信号间的相位差等。输入捕获功能就像STM32定时器赋予你的一双“高速电眼”能让你精确感知外部世界的时间信息。从理解原理、配置寄存器、编写中断服务函数到处理溢出、抗干扰、优化性能每一步都需要耐心和实践。希望这篇超详细的实操指南能帮你打通任督二脉下次再遇到需要“抓时间”的任务时能够从容不迫游刃有余。