4、硬件定时器捕获模式:使用定时器输入捕获功能、配置捕获通道、读取捕获值计算速度

定时器输入捕获,说白了就是让硬件帮我们精确记录外部事件发生的时间点。编码器输出的脉冲信号,正好需要这种机制来测量。我个人觉得,这是编码器驱动开发中最核心的一环——你捕获得准不准,直接决定了速度计算的精度。

4.1 输入捕获的工作原理

先说说基本原理。定时器内部有一个计数器,它会随着时钟源不断递增。当捕获通道检测到指定的边沿(上升沿或下降沿)时,硬件会立刻把当前计数器的值“拍个快照”,存到捕获寄存器里。

这个过程是纯硬件的,不需要CPU干预。你想想看,如果靠中断里读计数器,那延迟和抖动根本没法控制。硬件捕获的精度只取决于定时器时钟频率,比如72MHz的定时器,理论精度能达到13.8纳秒。

核心要点:输入捕获 = 硬件自动记录计数器快照。CPU只需要在捕获事件发生后去读寄存器就行。

我在项目中遇到过一个问题:编码器输出频率很高,CPU频繁进中断读捕获值,导致系统响应变慢。后来改用DMA批量搬运捕获数据,才把CPU解放出来。这个后面会讲到。

4.2 捕获通道的配置步骤

配置捕获通道,一般分这么几步。我习惯按这个顺序来,不容易漏掉关键寄存器。

  1. 使能定时器时钟——别笑,我真见过有人忘了这一步,折腾了半天。
  2. 配置GPIO为复用功能——捕获引脚必须接到定时器的输入通道上。
  3. 设置捕获通道的边沿触发——上升沿、下降沿,或者双边沿。
  4. 配置预分频器和滤波器——用于去抖和分频。
  5. 使能捕获通道和中断——或者DMA。
  6. 启动定时器计数器——开始干活。

下面是一个典型的STM32配置代码。我用的是HAL库,但寄存器级别的思路是一样的。

// 定时器3,通道1,上升沿捕获
TIM_IC_InitTypeDef sConfigIC = {0};

// 1. 使能定时器时钟
__HAL_RCC_TIM3_CLK_ENABLE();

// 2. GPIO配置(略,PA6复用为TIM3_CH1)

// 3. 设置捕获通道
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;   // 不分频
sConfigIC.ICFilter = 0;                   // 不滤波
HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1);

// 4. 使能捕获中断
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);

// 5. 启动定时器
HAL_TIM_Base_Start(&htim3);

小技巧:滤波器配置很有用。如果编码器信号有毛刺,可以设置ICFilter为0x0F(对应8个采样点),能有效去抖。但注意会增加延迟,高速场景要权衡。

4.3 读取捕获值并计算速度

捕获值读出来之后,怎么算速度?其实就一个公式:

速度 = (脉冲数 × 60) / (编码器线数 × 时间间隔)

但这里有个关键点:时间间隔怎么来?

假设我们捕获了两次上升沿,得到两个捕获值 capture1 和 capture2。那么时间间隔就是:

// 注意处理计数器溢出
if (capture2 > capture1) {
    diff = capture2 - capture1;
} else {
    diff = (TIM_PERIOD - capture1) + capture2 + 1;
}

// 时间间隔(秒)
time_interval = diff / timer_clock_freq;

// 速度(RPM)
rpm = (1.0 / time_interval) * 60.0 / encoder_lines;

我曾经踩过一个坑:计数器溢出没处理好,导致速度值偶尔跳变到天文数字。后来加了溢出中断,在中断里记录溢出次数,才彻底解决。

注意:如果编码器转速很低,两次捕获之间的时间很长,计数器可能溢出多次。这时候必须配合溢出中断,把溢出次数也计入总时间。

4.4 捕获模式 vs 编码器模式

很多初学者会混淆这两个概念。我简单对比一下:

特性 输入捕获模式 编码器模式
测量对象 脉冲宽度、周期、频率 位置、方向、速度
硬件资源 1个捕获通道 2个捕获通道(A/B相)
精度 高(取决于时钟频率) 高(4倍频)
适用场景 单脉冲信号测速 正交编码器
CPU负载 较低(中断或DMA) 极低(硬件自动计数)

说白了,如果你的编码器只有一根信号线(比如单端输出),那就用捕获模式。如果是A/B两相正交输出,编码器模式更省心。

4.5 性能分析与优化

捕获模式的性能瓶颈主要在两个方面:

  • 中断响应延迟——如果系统中断很多,捕获中断可能被延迟,导致捕获值读取不及时。
  • 计数器溢出处理——溢出中断如果处理不当,会引入误差。

我建议的做法是:

  1. 把捕获中断优先级设高,但别高过实时性更强的中断(比如电机控制PWM中断)。
  2. 用DMA把捕获值批量搬运到内存,CPU只在缓冲区满时处理一次。
  3. 如果定时器支持,用32位定时器或者级联两个16位定时器,减少溢出频率。

经验之谈:在高速电机控制中,我通常用DMA+双缓冲的方式。一个缓冲区在采集,另一个在计算,交替进行。这样CPU几乎零等待,速度计算延迟可以控制在微秒级。

4.6 知识结构图

下面这张图总结了输入捕获的核心流程和关键点。你可以把它当作配置时的检查清单。

输入捕获模式知识结构图 编码器脉冲信号 GPIO复用为TIMx_CHy 捕获通道配置 边沿选择 | 预分频 | 滤波器 捕获事件触发 读取CCRx寄存器 关键注意事项 • 处理计数器溢出 • 中断优先级设置 • DMA批量搬运 速度计算公式 RPM = (60 × 脉冲数) / (线数 × 时间) 时间 = 差值 / 时钟频率

嗯,这张图把整个流程串起来了。从编码器信号进来,到最终算出速度,每一步都有对应的硬件配置。你照着这个顺序配置,基本不会出错。

4.7 实际项目中的避坑指南

最后分享几个我踩过的坑,希望能帮你省点时间。

  • 信号毛刺问题——编码器线缆长的时候,信号容易有毛刺。我建议在硬件上加RC滤波,同时在软件里使能定时器的数字滤波器。
  • 低速测不准——转速很低时,两次捕获间隔很长,计数器可能溢出多次。我的做法是:用32位定时器,或者用溢出中断+软件扩展计数器位数。
  • 高速丢脉冲——如果编码器频率接近定时器时钟频率,捕获可能来不及。这时候可以降低预分频系数,或者换更高主频的MCU。
  • 中断嵌套导致数据错乱——捕获中断里如果又触发了其他高优先级中断,可能导致捕获值读取延迟。我习惯在中断里只做最轻量的操作——读寄存器、清标志、存数据,计算放到任务里做。

我的习惯:每次配置完捕获通道,先用示波器看编码器波形,再用逻辑分析仪抓捕获中断的响应时间。确认硬件和软件都没问题,再开始写上层应用。这一步能省掉后面80%的调试时间。

好了,输入捕获模式的核心内容就这些。配置不难,但细节很多。你只要把边沿选择、预分频、滤波器、溢出处理这几个点都照顾到,速度计算就不会出大问题。