10. 插补周期定时器:硬件定时器配置、软件定时器使用、周期抖动控制

插补周期,说白了就是运动控制的心脏跳动。你想想看,一个插补算法算得再漂亮,如果定时器不准,那输出到电机上的轨迹就是歪的。我在做多轴联动项目时,就吃过这个亏——算法仿真完美,一上机就跑偏,查了三天才发现是定时器抖动在作怪。

今天咱们就把这个「心脏」拆开来看。我会从硬件定时器配置讲起,再聊软件定时器的灵活用法,最后重点说说周期抖动怎么控制。嗯,这部分内容,我个人觉得是RTOS运动控制里最容易被忽视,但又最关键的一环。

10.1 硬件定时器配置:选型与初始化

硬件定时器,是RTOS里最可靠的时钟源。它直接挂在CPU的时钟树上,不受任务调度影响。我习惯用芯片的通用定时器(比如STM32的TIM、NXP的PIT)来做插补周期基准。

配置时要注意几个关键参数:

  • 时钟源:一般选内部高速时钟,别用外部晶振分频,容易受干扰
  • 预分频系数:决定了定时器的计数精度。我一般设到能让计数器在1us内计数一次
  • 自动重装载值:这个值直接决定了插补周期。比如1ms周期,就设1000
  • 中断优先级:必须设为最高优先级,不能被其他任务打断

核心原则:硬件定时器的中断服务函数里,只做最轻量级的工作——比如设置一个信号量或者触发一个任务。千万别在里面做插补计算!

我曾经在一个项目中,把插补计算直接放在定时器中断里。结果呢?计算时间稍微超了一点,下一个周期就错过了,整个运动轨迹开始抖动。后来改成中断里只发信号,计算交给高优先级任务,问题就解决了。

10.2 软件定时器使用:灵活但需谨慎

软件定时器,是RTOS提供的一种「软时钟」。它基于系统滴答(SysTick)实现,可以动态创建、删除、修改周期。相比硬件定时器,它更灵活,但精度差一些。

在运动控制里,我一般这样用软件定时器:

  • 非实时性任务:比如状态监控、日志记录、参数更新
  • 辅助定时:比如插补开始前的延时、加减速阶段的过渡等待
  • 看门狗:监控硬件定时器是否正常工作

但要注意,软件定时器的精度受系统滴答影响。如果系统滴答是1ms,那软件定时器的周期误差就在±1ms。对于插补周期来说,这个误差太大了。所以,主插补周期必须用硬件定时器,软件定时器只能打辅助。

我的习惯:硬件定时器负责「节拍」,软件定时器负责「节奏」。节拍必须精准,节奏可以稍微灵活。

10.3 周期抖动控制:从根源解决问题

周期抖动,就是实际定时周期和理论周期之间的偏差。你想想看,如果理论是1ms,实际有时候0.9ms,有时候1.1ms,那插补出来的轨迹能平滑吗?

抖动的来源主要有三个:

  1. 中断延迟:高优先级中断打断了定时器中断
  2. 任务抢占:其他任务占用了CPU,导致定时器任务不能及时执行
  3. 计算超时:插补计算本身耗时太长,超过了周期时间

针对这些,我总结了一套「三板斧」:

抖动来源 控制方法 优先级
中断延迟 关闭其他中断,或降低其优先级
任务抢占 插补任务设为最高优先级,且不可被抢占
计算超时 优化算法,或拆分为多个周期执行

嗯,这里要特别说一下「计算超时」。我曾经在一个五轴联动项目里,插补周期设了1ms,但算法算一次要1.2ms。结果就是每5个周期就丢一个,电机嗡嗡响。后来我把算法拆成两步:第一步在周期内算完粗插补,第二步在下一个周期开始前算完精插补。这样虽然增加了代码复杂度,但抖动问题彻底解决了。

避坑指南:我曾经在FreeRTOS里直接用vTaskDelayUntil()做周期控制,以为很准。结果发现任务被挂起后,再恢复时周期就乱了。后来改用硬件定时器+二值信号量,才真正做到了微秒级精度。

10.4 实战:一个稳定的插补周期实现

下面是我在项目中常用的实现框架。核心思路是:硬件定时器产生周期中断,中断里释放信号量,插补任务等待信号量后执行计算。

// 硬件定时器中断服务函数
void TIM_IRQHandler(void) {
    if (TIM_GetITStatus(TIMx, TIM_IT_Update)) {
        TIM_ClearITPendingBit(TIMx, TIM_IT_Update);
        // 释放信号量,通知插补任务
        xSemaphoreGiveFromISR(interp_sem, NULL);
    }
}

// 插补任务
void InterpolationTask(void *pvParameters) {
    while (1) {
        // 等待信号量,超时设为最大值
        xSemaphoreTake(interp_sem, portMAX_DELAY);
        
        // 执行插补计算
        // 注意:这里必须保证计算时间 < 插补周期
        InterpolationStep();
        
        // 输出到驱动器
        DriveOutput();
    }
}

这个框架的好处是:定时器中断只做最轻量的事,插补计算在任务里执行,不会被其他中断打断。而且,如果计算超时了,信号量会累积,下一个周期会连续执行两次——虽然不完美,但至少不会丢步。

10.5 周期抖动的测量与优化

怎么知道你的定时器抖不抖?我建议用示波器量一个GPIO的翻转。在插补任务开始和结束时翻转GPIO,看波形就能直观看到抖动情况。

如果发现抖动,按这个顺序排查:

  • 先看中断优先级:定时器中断是不是最高?
  • 再看任务优先级:插补任务是不是最高?
  • 然后看计算时间:有没有超过周期?
  • 最后看系统负载:其他任务是不是太占CPU?

我记得有一次,抖动始终在±50us左右,怎么优化都降不下去。后来发现是DMA传输占用了总线,导致定时器读取计数值时被延迟。解决办法是把DMA优先级降低,或者用双缓冲技术。嗯,这种问题,不实际调一调,光看代码是看不出来的。

总结一下:插补周期定时器,硬件是骨架,软件是血肉,抖动控制是灵魂。三者缺一不可。

插补周期定时器知识体系 硬件定时器配置 软件定时器使用 周期抖动控制 时钟源 · 预分频 · 重装载值 中断优先级 · 轻量级ISR 非实时任务 · 辅助定时 精度受限 · 不可做主周期 中断延迟 · 任务抢占 计算超时 · 示波器测量 硬件是骨架 · 软件是血肉 · 抖动是灵魂

这张图把咱们今天讲的内容串起来了。硬件定时器是基础,软件定时器是补充,抖动控制是保障。三者配合好了,你的运动控制才算真正「稳」了。

最后一个小建议:如果你刚开始做运动控制,先别追求极致精度。把硬件定时器配好,插补任务跑起来,用示波器看看抖动。等你能把抖动控制在±10us以内,再考虑优化算法。一步一步来,别急。