资讯动态

ODrive固件时基系统设计:从定时器配置到8kHz FOC控制环实现

发布时间:2026/10/3 7:08:10 来源:尧图企业网站定制
1. 从滴答声到控制节拍ODrive 固件时基系统的整体设计很多人第一次翻 ODrive 的源码注意力都会被 FOC 算法、电流环 PI 参数、编码器校准这些显学吸引走结果看到Axis::run_control_loop里那一堆状态机就晕了。但真正决定这套固件能不能跑稳的其实是更底层的东西——时基。你所有的采样、计算、PWM 更新全都挂在一个 8 kHz 的节拍上。这个节拍不准后面算法写得再漂亮都是空中楼阁。ODrive 固件里这个节拍由 STM32 的高级定时器产生具体来说是 TIM1 或 TIM8 这类带互补输出和死区插入的定时器。为什么不用滴答定时器SysTick因为 SysTick 是 Cortex-M 内核自带的 24 位递减计数器它适合做系统节拍、任务调度但它的输出没法直接驱动三相逆变桥。而 FOC 控制要求 PWM 载波和 ADC 采样、控制环计算严格同步这个同步信号必须由同一个硬件定时器产生才能保证相位关系确定。所以 ODrive 选择用 TIM1 的更新事件UEV作为整个控制环的心跳。这个设计思路可以概括成一句话用 PWM 定时器的更新事件同时触发 ADC 采样和控制环中断让采样时刻永远落在 PWM 波形的固定相位点上。这样做的好处是电流采样点可以精确对齐到 PWM 中心对称位置避开开关噪声最大的边沿区域。如果你用过 STM32 的 CubeMX 配置过定时器应该知道有个TRGO输出选项ODrive 就是把它设成 Update Event然后 ADC 的外部触发源选这个 TRGO。这一条链路打通了整个控制环的时序就锁死了。8 kHz 这个数字也不是随便定的。它对应的是 8 kHz 的 PWM 频率也就是每个 PWM 周期 125 微秒。对于中小功率的电机驱动这个频率刚好在开关损耗可接受和电流纹波不太大之间取得平衡。频率再低电流纹波会变大电机噪音明显频率再高MOSFET 开关损耗上升驱动器发热严重。ODrive 面向的是机器人关节、云台这类应用8 kHz 是经过验证的甜点值。当然源码里也留了修改空间但改之前你得想清楚 ADC 采样窗口、死区时间、中断负载这些连锁反应。理解了这个整体设计我们再往下拆。接下来我会从定时器的寄存器配置讲起然后说 ADC 怎么跟它同步再到中断服务函数里控制环是怎么被调起来的最后聊聊实际调试中容易踩的坑。这套逻辑不只适用于 ODrive任何做 FOC 的 STM32 项目都能借鉴。2. 定时器配置的底层细节不只是设个频率那么简单2.1 时钟树与预分频计算STM32F4 系列ODrive 用的是 F405 或 F446的定时器时钟来源是 APB2 总线。这里有个容易搞混的点APB2 的预分频系数如果不为 1定时器时钟会自动倍频。比如 APB2 分频设为 2那定时器时钟就是 APB2 时钟的 2 倍。ODrive 固件里系统时钟一般跑 168 MHzAPB2 分频通常设为 2所以 TIM1 的时钟是 168 MHz。要得到 8 kHz 的 PWM 频率我们需要设置预分频器PSC和自动重装载寄存器ARR。公式是PWM 频率 定时器时钟 / ((PSC 1) * (ARR 1))ODrive 源码里通常把 PSC 设为 0也就是不分频那么 ARR 168000000 / 8000 - 1 20999。这个值在 16 位寄存器范围内最大 65535没问题。但如果你想把 PWM 频率提到 24 kHz 甚至更高ARR 就会变小这时候要留意 ADC 采样窗口是否还够用。我见过有人把频率拉到 40 kHz结果电流采样值全是噪声就是因为采样窗口被压缩到几个微秒ADC 的采样保持电容还没充够电就转换了。还有一个细节ODrive 用的是中心对齐模式Center-Aligned Mode不是边沿对齐。中心对齐模式下计数器从 0 数到 ARR 再数回 0一个完整周期产生两次比较匹配。这样做的好处是 PWM 波形对称谐波分布更友好而且 ADC 可以在计数器过零点或峰值点时触发采样点天然避开开关瞬间。代价是同样的 ARR 值下PWM 频率会减半所以计算时要按ARR 时钟 / (2 * 频率) - 1来算。源码里timer_config那一段对 CR1 寄存器的 CMS 位操作就是在设中心对齐模式。2.2 死区时间与互补输出三相逆变桥的上下管不能同时导通否则就是直通短路瞬间炸管。所以 TIM1 的互补输出通道要插入死区时间。ODrive 源码里通过 BDTR 寄存器的 DTG 位来配置具体值取决于你的栅极驱动芯片和 MOSFET 的开关速度。一般来说死区时间设在 100 纳秒到 500 纳秒之间。设太小直通风险高设太大输出电压失真低速时尤其明显。计算死区时间的方法DTG 的值和定时器时钟周期挂钩。比如 168 MHz 时钟一个周期约 5.95 纳秒。如果 DTG 设为 0x20 左右对应大概 200 纳秒死区。源码里通常有个宏定义或者配置结构体来存这个值改的时候要同步检查栅极驱动器的传播延迟。我个人的经验是先用示波器看上下管的栅极波形确认没有交叠再逐步减小死区到刚好不交叠加一点余量这样能把失真降到最低。2.3 更新事件与中断使能定时器配置的最后一步是使能更新中断。在中心对齐模式下更新事件发生在计数器溢出和下溢时也就是一个 PWM 周期触发两次。但 ODrive 的控制环是 8 kHzPWM 也是 8 kHz所以它只需要每个完整周期触发一次控制中断。源码里通过设置 CR1 的 URS 位Update Request Source来选择更新事件源再配合 DIER 寄存器的 UIE 位使能中断。这样中断频率就是 8 kHz和 PWM 频率一致。这里有个坑如果你不小心让更新中断在每个半周期都触发控制环就变成 16 kHz 了但 ADC 采样可能只配置了一次导致一半的中断里读到的是旧数据。表现出来就是电机运行有轻微抖动或者电流波形上有毛刺。排查的时候可以用示波器量中断服务函数里翻转的 GPIO看频率对不对。3. ADC 与定时器的同步机制采样点为什么这么关键3.1 外部触发源的选择ADC 要跟 PWM 同步靠的是外部触发。STM32 的 ADC 可以配置成由定时器的 TRGO 信号触发转换。ODrive 里 TIM1 的 TRGO 设置为 Update Event这样每次计数器溢出中心对齐模式下是过零点时TRGO 发出一个脉冲ADC 立刻开始采样。这个时刻正好是 PWM 波形的中点三相下管都处于导通状态或者按特定矢量分布电流采样电阻上的电压最能反映真实的相电流。为什么不在 PWM 边沿采样因为边沿处开关管正在切换dv/dt 和 di/dt 都很大采样电阻上的信号全是振铃和尖峰。你在那个时刻采到的值基本等于噪声。中点采样就稳得多这也是所有 FOC 方案的通用做法。3.2 采样窗口与转换时间ADC 采样需要时间。STM32F4 的 ADC 时钟一般配置在 21 MHz 到 36 MHz 之间采样周期可以设 3、15、28、56、84、112、144、480 个周期。ODrive 里通常选较短的采样周期因为要在一个 PWM 周期内完成多通道扫描。假设 ADC 时钟 36 MHz采样周期 15 个周期转换时间 12 个周期那一次转换约 0.75 微秒。三相电流加母线电压四个通道轮流采总共约 3 微秒。8 kHz 周期是 125 微秒时间绰绰有余。但如果你把 PWM 频率提到 40 kHz周期只有 25 微秒采样窗口就得重新算。而且高频率下开关噪声更容易耦合进来可能需要加 RC 滤波但滤波又会引入相位延迟影响电流环带宽。这是个权衡没有免费午餐。3.3 注入通道与规则通道的取舍STM32 的 ADC 有规则通道和注入通道两组。注入通道可以打断规则通道的转换优先级更高。ODrive 里电流采样通常用注入通道因为要保证采样时刻精确。规则通道可以用来采温度、母线电压这些慢变量。源码里对 ADC 的配置比较细包括设置 JSQR 寄存器来选择注入通道和触发源还有采样时间寄存器 SMPR 的配置。我调试时遇到过一个情况注入通道的触发源设成了定时器的某个比较事件而不是更新事件结果采样点偏移了电流波形在特定占空比下畸变。后来对着参考手册把 TRGO 和 ADC 外部触发映射表重新查了一遍才找到问题。所以这部分一定要对着芯片手册确认不能凭感觉。4. 8 kHz 控制环的中断服务函数剖析4.1 中断入口与优先级TIM1 的更新中断服务函数是控制环的入口。在 ODrive 源码里这个函数通常叫TIM1_UP_TIM10_IRQHandler或者类似的向量名。进去之后第一件事是清中断标志然后调用控制环的主函数。中断优先级要设得比较高但不能最高因为还有故障保护中断比如刹车、过流需要更快响应。ODrive 里一般把 PWM 中断设成中等优先级故障输入设成最高。优先级配置用 NVIC 的NVIC_SetPriority函数。这里要注意STM32 的优先级数值越小优先级越高。如果你把控制环中断设成 0那故障中断就没法抢占它了过流时可能来不及关断 PWM。我一般把故障中断设成 0控制环设成 1 或 2这样保护永远优先。4.2 控制环的执行流程中断进去之后控制环大致做这几件事读取 ADC 采样值、做 Clarke 和 Park 变换、跑电流 PI 环、跑速度环如果使能、做反 Park 变换、更新 PWM 比较寄存器。整个过程要在 125 微秒内完成否则下一个中断来了还没处理完就会丢拍。丢拍的后果是电流环相位滞后严重时电机啸叫甚至失步。ODrive 的代码结构比较清晰Axis::run_control_loop里按状态机走。每个状态Idle、Calibration、Closed Loop Control做的事情不同但都在这个 8 kHz 节拍下执行。源码里用了不少内联函数和查表来优化执行时间比如正弦余弦用查表加插值避免在中断里调sinf这种耗时函数。4.3 执行时间测量与优化怎么知道控制环有没有超时最简单的办法是在中断入口翻转一个 GPIO出口再翻转一次用示波器量高电平时间。ODrive 的板子上有测试点或者你可以自己飞线。我实测过在 168 MHz 主频下完整的电流环加 PWM 更新大概占 20 到 30 微秒余量还算充足。但如果你开了速度环、位置环、还有滤波器时间会往上走。超过 80 微秒就要警惕了。优化手段包括把不紧急的计算挪到后台循环、用定点数代替浮点、减少中断里的函数调用层次。ODrive 用的是硬件浮点单元FPU所以浮点运算不算太慢但三角函数和除法还是尽量少用。源码里有些地方用arm_sqrt_f32这种 CMSIS-DSP 库函数比标准库快不少。5. 实操调试与常见问题排查5.1 用示波器验证时基拿到一块 ODrive 板子或者自己画的 STM32 FOC 板第一步应该是验证时基。把示波器探头接到 PWM 输出上看频率是不是 8 kHz占空比是否随控制量变化。然后找一个 GPIO 在中断里翻转确认中断频率也是 8 kHz。如果 PWM 是 8 kHz 但中断是 16 kHz说明更新事件配置有问题如果两个都不对检查时钟树和 PSC/ARR 计算。我习惯用双通道示波器一个通道看 PWM一个通道看中断翻转 GPIO触发设在 PWM 上升沿。这样能直观看到中断相对于 PWM 的相位关系。理想情况下中断应该固定在 PWM 周期的某个位置不随占空比变化而漂移。5.2 电流采样异常的排查电流采样出问题表现五花八门电机抖动、发热、噪音大、效率低。排查顺序一般是先看采样时刻对不对再看采样值范围是否合理最后看变换和 PI 参数。采样时刻用示波器同时看 PWM 和采样保持信号如果有测试点或者看 ADC 转换完成中断的翻转 GPIO。常见问题包括采样电阻虚焊、运放供电不足、ADC 参考电压不稳、注入通道配置错误。我遇到过一次采样值在电机静止时正常一转起来就偏最后发现是采样电阻的走线太细大电流下压降导致共模电压超出运放输入范围。换了粗走线就好了。这种问题数据手册不会告诉你只能靠实测。5.3 中断丢拍的检测丢拍是 FOC 里比较隐蔽的故障。电机可能还能转但电流波形上有周期性畸变或者高速时突然失步。检测方法是在中断里对一个计数器累加后台循环里定时读取并清零如果计数值小于预期说明丢了中断。预期值 8 kHz * 读取周期。比如每秒读一次预期 8000如果读到 7990就丢了 10 拍。丢拍的原因通常是中断执行时间过长或者有更高优先级的中断频繁抢占。检查一下有没有在中断里做浮点除法、有没有开不必要的其他中断、有没有在中断里等标志位。ODrive 源码里对中断执行时间比较克制但如果你自己加了调试代码或者额外的滤波就可能超时。5.4 常见问题速查表现象可能原因排查方法解决方向PWM 频率不对PSC/ARR 计算错误示波器量频率重算寄存器值中断频率是 PWM 两倍更新事件源配置错误量中断翻转 GPIO检查 CR1 的 URS 位电流采样噪声大采样点不在中点对比 PWM 和采样信号调整 TRGO 触发源电机低速抖动死区时间过大看栅极波形交叠减小死区高速失步中断丢拍计数器检测优化中断执行时间采样值随电流偏移采样电阻走线问题大电流下测共模电压加粗走线或改布局5.5 几个容易忽略的细节第一个是中断向量表的重映射。如果你用 bootloader 或者把固件放到非默认地址记得检查SCB-VTOR有没有设对。向量表错了中断可能跳到默认处理函数里表现就是电机完全不转但程序没死。第二个是ADC 校准。STM32 的 ADC 上电后建议跑一次校准否则零点有偏差。ODrive 源码里有校准流程但如果你自己写初始化别忘了这一步。校准不做的后果是电流采样有固定偏置FOC 会把这个偏置当成真实电流去调节导致电机发热。第三个是定时器刹车功能。TIM1 的 BKIN 引脚可以配置成故障输入一旦拉低立刻关断 PWM 输出。这个功能在过流保护里很有用但配置错了可能导致 PWM 完全不输出。调试时如果发现 PWM 没波形先查 BKIN 引脚状态和 BDTR 寄存器的 MOE 位。6. 从 ODrive 到通用 FOC 项目的移植思路ODrive 的时基和控制环设计不是它独有的任何 STM32 FOC 项目都可以照搬这套架构。移植的时候核心是把三个东西对齐PWM 定时器的更新事件、ADC 的外部触发、控制环中断。这三个用同一个信号源串起来时序就稳了。具体步骤先配定时器中心对齐8 kHzTRGO 选更新事件再配 ADC注入通道外部触发选 TIM1_TRGO采样时间按 ADC 时钟算最后写中断服务函数清标志、读 ADC、跑算法、更新 PWM。每一步都用示波器验证不要等全部写完再调。我见过有人一口气写完然后发现电机不转回头查每一步花的时间比一步步验证多得多。如果你用的是 STM32CubeMX它可以帮你生成定时器和 ADC 的初始化代码但 TRGO 和注入通道的触发映射有时候需要手动改。生成的代码里HAL_TIM_ConfigClockSource和HAL_ADCEx_InjectedConfigChannel这两个函数要重点看。HAL 库把很多寄存器操作封装了但底层逻辑还是那些理解了对调试有帮助。最后说一个我自己的体会时基这东西平时不出问题的时候你感觉不到它存在一旦出问题就是全局性的。所以每次改时钟配置、改 PWM 频率、改中断优先级都要重新验证一遍时基。别嫌麻烦这比电机炸了再查便宜多了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑