ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ODrive电机控制:8kHz控制环与STM32定时器配置详解

ODrive电机控制:8kHz控制环与STM32定时器配置详解 1. 从时基单元说起为什么8 kHz不是一个随便选的数字搞电机控制的人都知道控制环频率决定了整个系统的响应带宽和电流纹波表现。ODrive选8 kHz作为电流环和速度环的基础频率不是拍脑袋定的。我在第一次读ODrive源码的时候看到TIM_1_8_CLOCK_HZ这个宏定义心里还在想为什么不是10 kHz或者20 kHz。后来把整个定时器配置链路捋清楚才明白这个数字背后是时钟树、PWM载波频率、ADC采样窗口三者博弈的结果。先看ODrive用的主控芯片STM32F405主频168 MHz。这个芯片有三个高级定时器TIM1、TIM8和若干个通用定时器。ODrive把TIM1和TIM8分别分配给M0和M1两个电机轴每个定时器同时承担三件事生成中心对齐PWM、触发ADC注入采样、产生控制环中断。一个定时器干三份活这在电机控制里是常规操作但细节处理的好坏直接决定电流环能不能跑稳。8 kHz对应的时间周期是125微秒。在这125微秒里电流环要完成读取两相电流采样值、执行Clarke变换、Park变换、两个PI调节器运算、反Park变换、SVPWM占空比计算、更新比较寄存器。听起来事情不少但在168 MHz的Cortex-M4上配合硬件FPU这些运算加起来大概占用15到20微秒。剩下的100多微秒是留给中断响应余量和其它任务的。如果你把频率提到20 kHz周期变成50微秒运算时间占比就超过40%中断嵌套的风险急剧上升而且MOSFET开关损耗也会明显增加。降到4 kHz的话电流纹波会变大电机噪音也会更明显。所以8 kHz是一个在算力、损耗、性能之间取平衡的结果。注意控制环频率和PWM频率是两回事。ODrive的PWM载波频率默认是24 kHz是控制环频率的三倍。这个三倍关系不是巧合后面讲中心对齐模式和ADC触发时机的时候会详细展开。2. 时钟树配置168 MHz是怎么喂给定时器的2.1 从HSE到PLL的倍频链路ODrive板子上焊的是8 MHz晶振这个晶振信号进入STM32F405的PH0和PH1引脚作为HSE时钟源。8 MHz直接给系统用太慢了必须经过PLL倍频。STM32F405的PLL配置有一套固定的公式我把它拆开说。PLL的输入分频器M默认设为8所以8 MHz先除以8得到1 MHz的参考频率。这个1 MHz进入VCO之前要先乘以分频器NODrive的N值设为336于是VCO输出336 MHz。VCO输出再经过分频器PP设为2最终得到168 MHz的系统时钟。整个链路写出来就是8 MHz ÷ 8 × 336 ÷ 2 168 MHz。这三个参数M、N、P在system_stm32f4xx.c里的RCC_PLLConfig函数中设置ODrive的固件里对应的是SystemClock_Config函数。这里有个容易踩的坑VCO输入频率推荐范围是1到2 MHz输出范围是100到432 MHz。如果你改晶振频率或者改M值一定要检查这两个范围否则PLL可能锁不定芯片跑飞。我见过有人把M改成4结果VCO输入变成2 MHz虽然还在范围内但已经到边缘了长时间运行偶尔会出现时钟抖动。2.2 APB总线分频与定时器时钟168 MHz的系统时钟出来之后要分给AHB总线和APB总线。AHB预分频器设为1所以HCLK就是168 MHz。APB1和APB2的预分频器都设为2所以PCLK1和PCLK2都是84 MHz。关键来了STM32的定时器时钟不是直接等于PCLK。如果APB预分频器不为1定时器时钟会自动倍频。具体规则是APB预分频器为1时定时器时钟等于PCLKAPB预分频器不为1时定时器时钟等于PCLK乘以2。ODrive的APB1和APB2预分频器都是2所以TIM1、TIM8以及其它挂在APB2上的定时器时钟频率都是84 MHz × 2 168 MHz。这意味着TIM1的计数器每走一个tick时间就是1/168 MHz ≈ 5.95纳秒。这个精度对于8 kHz控制环来说绰绰有余。我在调试的时候用示波器量过TIM1的更新中断引脚翻转频率精确到8 kHz抖动在纳秒级别说明时钟树配置是干净的。2.3 定时器预分频器与自动重载值的计算TIM1的时钟是168 MHz要得到8 kHz的更新频率需要设置预分频器PSC和自动重载寄存器ARR。ODrive的做法是让PSC等于0也就是不分频然后ARR设为(168000000 / 8000 / 2) - 1 10499。为什么除以2因为ODrive用的是中心对齐模式计数器从0数到ARR再数回0一个完整的上下计数周期才产生一次更新事件。所以实际计数频率是168 MHz但更新频率是168 MHz / (2 × (ARR1)) 8 kHz。这个计算过程在timer_config函数里体现为// 中心对齐模式ARR 10499 htim1.Init.Prescaler 0; htim1.Init.CounterMode TIM_COUNTERMODE_CENTERALIGNED1; htim1.Init.Period 10499; htim1.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter 0;RepetitionCounter设为0意味着每个更新事件都触发中断如果设为1就是每两个更新事件触发一次。ODrive这里没有用重复计数器来降频因为8 kHz已经是目标频率了。实操心得如果你想把控制环改成16 kHz不能只改ARR。ARR减半之后PWM频率也会跟着变ADC采样窗口、死区时间、电流环PI参数都要重新整定。我建议先用示波器确认PWM频率和中断频率都对了再动控制参数。3. 中心对齐PWM不只是为了对称3.1 边沿对齐与中心对齐的区别STM32的定时器支持边沿对齐和中心对齐两种PWM模式。边沿对齐模式下计数器从0数到ARR然后归零重新开始PWM波形是单边变化的。中心对齐模式下计数器从0数到ARR再倒着数回0PWM波形是对称的。ODrive选中心对齐核心原因是电流采样。在电机控制中电流采样必须在PWM周期的特定时刻进行避开MOSFET开关瞬间的噪声。中心对齐模式下计数器在ARR处和0处各产生一次比较匹配PWM波形中间高两边低开关动作集中在周期两端。如果在计数器过零点或者ARR点触发ADC采样此时开关管刚好完成切换电流已经稳定采样噪声最小。边沿对齐模式下开关动作集中在周期的一端采样窗口要么太靠近开关瞬间要么离得太远导致占空比更新延迟。中心对齐模式天然把采样点放在周期中间这时候上下桥臂的开关状态是确定的电流采样最干净。3.2 死区时间与互补输出ODrive驱动的是三相逆变桥每相有上下两个MOSFET。如果上下管同时导通电源直接短路管子瞬间炸掉。所以必须设置死区时间确保一个管子完全关断之后另一个才开通。STM32的高级定时器TIM1和TIM8硬件支持死区插入通过BDTR寄存器的DTG位配置。ODrive的死区时间设置大概在几百纳秒量级具体值取决于MOSFET的开关速度和栅极驱动电路。我实测过死区时间设得太小上下管直通风险高设得太大输出波形畸变严重电流谐波增加。一般取MOSFET关断时间的1.5到2倍比较稳妥。互补输出还需要配置CHxN通道也就是互补通道。TIM1的CH1和CH1N是一对CH2和CH2N是一对CH3和CH3N是一对。ODrive的配置里主通道和互补通道都使能死区时间插入在两者之间。这里有个细节CHxN的极性可以单独设置如果栅极驱动芯片是低电平导通就要把极性反过来。3.3 PWM频率为什么是24 kHz前面提到PWM频率是控制环频率的三倍也就是24 kHz。这个三倍关系是为了配合中心对齐模式下的ADC采样。在中心对齐模式下一个PWM周期内计数器会经过ARR点一次、0点一次总共两次比较匹配。如果每次匹配都触发ADC那么ADC采样频率就是PWM频率的两倍即48 kHz。但ODrive实际用的采样策略是每个控制环周期采样一次所以需要把ADC触发分频。24 kHz的PWM频率还有一个好处人耳听觉范围是20 Hz到20 kHz24 kHz刚好在听觉上限之上电机运行时的开关噪声人耳听不到体验更好。如果PWM频率降到16 kHz就能听到明显的啸叫声。当然频率越高开关损耗越大24 kHz是一个折中点。4. ADC注入采样与定时器触发联动4.1 注入通道与规则通道的区别STM32的ADC有两组通道规则通道和注入通道。规则通道就是常规的按顺序转换注入通道可以打断规则通道的转换优先级更高。ODrive用注入通道来采样电机电流因为电流采样对时序要求极其严格不能等规则通道转换完再轮到自己。注入通道最多支持4个通道ODrive用了其中两个来采样两相电流第三个通道采样母线电压。注入通道的转换由定时器事件触发具体来说是TIM1的TRGO信号。TRGO可以配置为更新事件、比较匹配事件、或者OC4REF等。ODrive把TRGO配置为OC4REF也就是通道4的比较输出。4.2 采样时刻的精确控制为什么用OC4REF而不是更新事件更新事件发生在计数器归零或者到达ARR的时候这时候开关管正在切换噪声最大。OC4REF可以设置在计数器计数过程中的任意一点ODrive把它设在PWM周期的中间偏后位置这时候开关管已经稳定电流采样最准确。具体配置是TIM1的CCR4寄存器设为一个特定值当计数器达到这个值时OC4REF信号翻转触发ADC注入转换。这个值需要根据PWM频率和死区时间来计算。假设ARR是10499中心对齐模式下计数器从0到10499再回0周期是20998个计数。如果要在周期75%的位置采样CCR4就设为10499 × 0.75 ≈ 7874。当然实际值还要考虑ADC采样保持时间和电流放大器的建立时间。4.3 ADC采样时间与电流放大器带宽ADC的采样时间决定了采样电容充电的快慢。STM32F405的ADC采样时间可以设置为3、15、28、56、84、112、144、480个ADC时钟周期。ODrive用的采样时间比较长因为电流放大器的输出阻抗和ADC的采样电容构成了RC电路采样时间太短的话电容充不满采样值偏低。我算过一笔账如果ADC时钟是21 MHz采样时间设为84个周期那么采样窗口就是4微秒。电流放大器的带宽如果是100 kHz建立时间大概是3.5微秒4微秒的采样窗口刚好够用。如果采样时间设成28个周期只有1.33微秒放大器还没建立好就采样了电流值会偏小导致转矩输出不足。注意ADC采样时间和电流放大器带宽要匹配。换放大器或者改采样电阻的时候一定要重新算这个时间。我见过有人换了低带宽的放大器但没改采样时间结果电机低速时抖动严重查了半天才发现是采样值不准。5. 控制环中断服务函数的执行流程5.1 中断优先级与嵌套TIM1的更新中断在ODrive里被配置为最高优先级之一因为电流环不能被打断。STM32F405的NVIC支持16级优先级ODrive把TIM1更新中断设为优先级1ADC注入完成中断设为优先级0其它通信中断如UART、CAN设为较低优先级。这里有个细节ADC注入转换完成之后也会产生中断这个中断的优先级比TIM1更新中断还高。因为ADC采样完成之后要尽快读取数据否则下一次采样会覆盖上一次的结果。TIM1更新中断里做的是控制运算ADC中断里做的是数据搬运和预处理。5.2 中断服务函数的主要步骤TIM1更新中断触发后进入TIM1_UP_TIM10_IRQHandler最终调用到control_loop函数。这个函数的主要步骤是读取ADC注入转换的结果得到两相电流和母线电压执行Clarke变换把三相电流变成αβ两相读取编码器位置计算电角度执行Park变换把αβ电流变成dq电流执行电流PI调节器得到dq电压指令执行反Park变换把dq电压变成αβ电压执行SVPWM计算得到三相占空比更新TIM1的CCR1、CCR2、CCR3寄存器整个过程要在125微秒内完成所以每一步都要尽量高效。ODrive用了大量的查表法和定点数运算来加速比如SVPWM的扇区判断用查表代替了复杂的三角函数计算。5.3 中断延迟与抖动测量中断延迟是指从中断触发到中断服务函数第一条指令执行的时间。STM32F405的中断延迟大概是12个时钟周期加上流水线刷新和寄存器压栈总共大概20到30个周期也就是120到180纳秒。这个延迟对于8 kHz控制环来说完全可以接受。抖动是指中断触发时刻的波动。如果定时器配置正确抖动应该只有几个时钟周期。我用GPIO翻转法测过在中断入口翻转一个引脚用示波器看上升沿的抖动实测在10纳秒以内。如果抖动超过100纳秒就要检查是否有更高优先级的中断在干扰或者Flash等待周期设置不对。6. 常见问题与排查技巧实录6.1 电机启动时电流冲击大这个问题我遇到过好几次表现是电机上电瞬间电流冲到额定值的两三倍有时候直接触发过流保护。排查下来通常是几个原因一是电流环PI参数太激进积分项在启动瞬间累积过大二是ADC采样时刻不对采到了开关噪声三是SVPWM的占空比更新时机和计数器不同步。解决办法先把电流环积分项限幅启动时用较小的比例增益等电流稳定后再切到正常参数。然后检查ADC触发点用示波器同时看PWM波形和ADC触发信号确保触发点在开关动作之后至少1微秒。最后确认CCR寄存器的更新是在更新事件中进行的而不是在中断里直接写。6.2 控制环频率不稳定有时候用示波器量中断频率发现不是精确的8 kHz而是在7.9到8.1 kHz之间跳。这种情况通常是时钟源的问题。如果用的是内部RC振荡器而不是外部晶振频率精度和稳定性都差很多。ODrive用的是外部8 MHz晶振精度在20 ppm以内不会出现这种问题。另一个可能是中断服务函数执行时间太长偶尔超过了125微秒导致下一次中断被延迟。这时候要优化代码把不必要的浮点运算改成定点或者把一些计算移到主循环里做。6.3 ADC采样值跳变电流采样值偶尔跳变导致电机噪音大或者转矩波动。排查思路先看ADC参考电压是否稳定如果参考电压有纹波采样值肯定跳。然后看采样时刻是否避开了开关噪声用示波器看电流放大器的输出如果采样点正好在振铃上那就要调整CCR4的值。最后检查PCB布局电流采样走线是否远离功率走线采样电阻的接地是否干净。6.4 定时器配置速查表参数值说明定时器时钟168 MHzAPB2预分频2定时器倍频2预分频器PSC0不分频自动重载ARR10499中心对齐8 kHz更新频率计数模式中心对齐1上下计数PWM频率24 kHz控制环频率的三倍死区时间约500 ns取决于MOSFETADC触发OC4REFCCR4 ≈ 7874中断优先级TIM1更新1ADC注入0数值越小优先级越高这张表是我调试的时候整理的每次改配置都对着看一遍能省不少时间。特别是ARR和CCR4这两个值改一个就要重新算另一个不然采样时刻就偏了。7. 从源码到实操我的调试记录第一次把ODrive固件跑起来的时候电机转是转了但噪音特别大低速时还有明显的抖动。我用示波器抓了PWM波形和电流波形发现电流波形上有周期性的尖刺频率正好是8 kHz跟控制环频率一致。这说明每次控制环更新的时候电流都会跳一下。查了半天最后定位到是CCR寄存器的更新时机问题。ODrive原本的代码是在中断服务函数里直接写CCR1、CCR2、CCR3但这时候计数器可能正在比较匹配的瞬间写入新值会导致输出脉冲异常。正确的做法是使能TIM1的预装载功能让CCR的更新在下一个更新事件时生效。这样占空比的变化和计数器周期同步不会产生毛刺。改完之后电流波形干净多了电机噪音也降下来了。这个坑让我明白一个道理定时器的寄存器不是随便写的什么时候写、怎么写都要看计数器的状态。预装载功能就是为这种场景设计的该用的时候一定要用。后来我又试过把控制环频率提到16 kHzARR改成5249CCR4改成3937。PWM频率变成48 kHz开关损耗明显增加MOSFET温度上升了十几度。但电流环的响应确实更快了阶跃响应的上升时间从原来的2毫秒缩短到1毫秒左右。所以频率的选择要看应用场景如果对动态响应要求不高8 kHz完全够用没必要追求更高频率。还有一个细节ODrive的固件里用了__HAL_TIM_SET_COMPARE宏来更新CCR这个宏最终写的是TIMx-CCR1 value。如果你直接写寄存器效果一样但用宏更规范也方便移植。我建议不管用什么芯片都尽量用厂商提供的库函数或者宏直接写寄存器虽然效率高但容易出错特别是涉及到预装载和影子寄存器的时候。最后分享一个测量中断抖动的小技巧在中断入口翻转一个空闲GPIO用示波器看这个GPIO的上升沿。如果上升沿很陡且间隔均匀说明中断触发很稳定。如果上升沿有抖动或者间隔不均匀就要查时钟源和中断优先级。这个方法简单有效不需要额外的调试工具一根示波器探头就能搞定。
返回列表