ARTICLE DETAIL

资讯详情

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

PWM更新模式为何延迟?影子寄存器与电机控制优化指南

PWM更新模式为何延迟?影子寄存器与电机控制优化指南 做电机驱动和电源控制的朋友应该都撞见过这样一个场景程序里明明已经把目标占空比写进了定时器的比较寄存器示波器上 PWM 输出的变化却总要迟那么一拍才出现偏偏在最需要“立刻刹车”的瞬间输出还在按照旧值稳稳地执行。我第一次被这个问题坑到是在调一台直流无刷电机的高速换相时电流波形上的毛刺怎么都消不掉参数试了一圈都不对最后才意识到不是电流环的问题而是传统 PWM 更新模式在背后拖后腿。这里说的“传统 PWM 更新模式的固有局限”简单概括就是绝大多数单片机内置的 PWM 外设出于波形完整性和硬件安全考虑并不会让你写入寄存器的值立刻生效而是把新值“扣”到某个特定的更新事件通常是计数周期边界才真正加载。这种机制在低速、低动态的场合没有任何问题但在电机控制、数字电源、精密调光这类对响应时间敏感的应用里它带来的延迟、阶跃跳变和采样同步问题会直接变成系统性能的上限。这篇文章会把这类局限从底层原理到实际表现逐个拆开再给出一套不换主控也能用的优化思路。适合正在调试 PWM 驱动、电机闭环或者数字电源的朋友参考。1. 更新事件与影子寄存器传统 PWM 模式为什么非要“等一下”1.1 影子寄存器机制用“延迟”换“波形完整性”其实任何一个合格的 PWM 外设在比较寄存器前面都会再加一层影子寄存器Shadow Register也就是常说的预装载寄存器。你软件里写入 TIMx_CCRx写的其实是影子寄存器计数器在运行过程中真正拿来和计数值做比较的是另一套活动寄存器。把影子寄存器的值搬运到活动寄存器的那一下就是更新事件Update Event。为什么要多这一层中转设想一下如果不这么做会发生什么PWM 波形正处在高电平中间你突然把比较值从 100 改到 80计数器在下一个瞬间就匹配了当前周期会突然提前翻转输出端出现一个极窄的高电平脉冲。对 LED 灯来说也许只是闪一下对电机而言就是一次额外的电流冲击对数字电源来说甚至可能直接触发过流保护。所以硬件工程师宁可在更新时刻牺牲一拍延迟也不愿意让输出波形在周期中间被“打断”。这个设计本质上是在拿“响应延迟”换取“波形完整性”本身是合理的选择。问题在于很多工程师在写应用层代码时根本没有意识到这一层存在——库函数封装得太好你调一个pwm_set_duty()里面做了什么都看不见于是“写寄存器立即生效”这个错误假设就埋下了隐患。等你在控制环里发现相位滞后、系统不稳定时往往已经排查了很久别的方向。1.2 更新事件的位置选择周期边界、立即更新、事件触发从更新的触发位置来分常见的有三类更新方式加载时机典型场景代价传统周期更新计数器溢出/下溢产生更新事件时加载常规电机控制、调光、加热至少一个计数周期的延迟立即更新软件写入后直接修改活动寄存器初始化、停机、一次性配置周期中段可能产生畸形脉冲事件触发更新外部输入、另一定时器或比较事件触发加载多外设同步、固定相位控制需要额外硬件资源和严格时序设计绝大多数 PWM 外设默认走的是第一种。STM32 里 ARPE 位控制自动重载寄存器是否预装载OCxPE 位控制比较寄存器是否预装载AURIX TC3xx 的 CCU6 模块里T13 周期值、CC60/CC61/CC62 比较通道的装载逻辑也类似。不同芯片对“更新事件在哪个时刻产生”的定义还有差异这就是很多移植代码跑起来行为不一致的根本原因。1.3 中心对齐模式下更新点的额外复杂性边沿对齐模式向上计数里更新事件比较简单基本就是计数器溢出那一下。但中心对齐模式向上-向下计数下计数器从 0 递增到 ARR 再递减回 0一个完整周期存在两个特殊时刻计数值为 0 的谷值下溢和计数值为 ARR 的峰值上溢。不同外设在中心对齐模式下产生更新事件的策略不完全一样有的在上溢和下溢都触发有的允许软件选择其一。这意味着你写进去的新占空比可能在本周期的上半段生效也可能要等下半段才生效。很多 STM32 工程师在中心对齐模式下踩的坑就在这里——发现 PWM 输出和预期值差了一个相位怎么查都查不出问题其实是更新的时间点和自己以为的不一样。后面第 3 章我会专门展开这个和 ADC 采样联动的问题。2. 一拍延迟与阶跃跳变高动态场景下两个绕不过去的代价2.1 量化“一拍延迟”从写寄存器到边沿变化到底有多久先算一笔账。以 20kHz 的 PWM 为例周期是 50µs。控制指令随机落在某个时刻更新要到下一个周期边界才发生所以延迟在 0~50µs 之间分布平均 25µs最坏情况接近 50µs。如果电流环或者速度环的控制周期恰好也是 50µs那整条链路就是采样当前电流 → 运算出目标电压 → 写入比较寄存器 → 下一周期才真正生效。这等于在闭环里硬生生多了一个载波周期的纯滞后。自动控制里纯滞后是最伤稳定性的一类因素相位裕度会直接掉一截系统容易振荡或者动态响应变差。低频场景更夸张。舵机接收机的 PWM 信号常用 50Hz周期 20ms你在任意时刻写入目标角度输出实际要到下一个周期才开始变化平均 10ms、最坏 20ms 的延迟人手都能直接试出来。这也是为什么舵机控制里“写入占空比”和“舵机开始动”之间总有一段肉眼可见的等待。不同类型 PWM 对应的延迟量级可以参考这张表PWM 频率周期平均更新延迟最坏更新延迟20kHz常见电机控制50µs25µs50µs10kHz部分工业驱动100µs50µs100µs1kHz部分运动控制1ms0.5ms1ms50Hz舵机/伺服20ms10ms20ms2.2 阶跃跳变整周期替换带来的电流和机械冲击除了延迟还有一个更隐蔽的问题传统更新模式对目标值的处理是“整周期替换”占空比从 10% 直接切到 90%中间没有任何过渡。这在软件逻辑上很简单但在物理世界是一次标准的阶跃输入。电机上表现为电流尖峰和转矩突变加热器上表现为功率骤变LED 高速调光时会看到亮度跳变。有一次我在调一个小型加热器目标是快速升温到设定值再保温。温度环输出变化其实不大但 PWM 满占空比和低占空比之间切换时加热丝的电流冲击把同一个电源轨上的传感器信号干扰得乱七八糟。后来在软件里加了一个斜坡限幅每次更新事件只允许占空比变化几个百分点冲击才压下去。斜坡限幅看着简单但要注意它本身也依赖更新事件——如果 PWM 外设不支持周期内多次更新斜坡就只能一步一个周期地走系统响应速度又变慢了。所以这一步是用动态性能换冲击抑制项目里要权衡好。2.3 被延迟放大的“电机飞车”风险“PWM 电机飞车”这类事故严格讲通常由驱动失效、控制逻辑错误、传感器断线等根本性故障引起。但传统更新模式有一个逃不开的缺点故障状态下软件试图把占空比紧急降到 0这个“紧急”指令同样要被扣到下一个更新事件才生效。20kHz 下最坏 50µs 的延迟在很多人看来不算长但在低速重载、大惯量的系统里这个控制窗口的延长可能让电机在危险转速上多转不少角度。更麻烦的情况是如果主循环响应慢或者中断被长时间屏蔽指令写进影子寄存器之后迟迟等不到加载危险状态持续的时间就不是几微秒而是几十毫秒了。所以设计安全逻辑的时候不能假设“写入比较寄存器”等于“输出立刻变化”。PWM 输出真正发生变化是在下一个更新事件这个认知必须刻在脑子里。真正常规的安全设计会单独拉一路 GPIO 作为功率级使能或者用独立硬件看门狗、故障比较器去强制关断而不是指望依靠 PWM 更新本身来救命。3. 中心对齐模式下更新点与 ADC 采样时刻的三角博弈3.1 为什么电机控制偏爱中心对齐模式边沿对齐模式实现简单每个周期开始时开关动作比较集中电流纹波大谐波噪声也集中在固定频点。中心对齐模式下开关切换分散在周期的不同位置等效开关频率翻倍电流纹波更小噪声频谱更扁平方便 EMI 滤波设计。这就解释了为什么做电机控制、数字电源的工程师都偏好在中心对齐模式下工作。但中心对齐模式也把前面说的“更新事件位置”问题放大了——因为一个周期里有两个候选时刻上溢峰值和下溢谷值。3.2 更新点在峰值和谷值的行为差异一个容易被忽略的坑以很多 MCU 的典型实现为例如果你在中心对齐模式下把影子寄存器写入了新占空比更新事件发生在谷值那么新值会在之后的整个上半周期生效如果更新事件发生在峰值新值则会从下半周期开始生效如果上下溢都会产生更新事件那一整个周期里可能发生两次加载输出波形会有一次不预期的二次调整。我见过最典型的翻车案例某工程师从网上拷了一段霍尔传感器驱动的 PWM 配置跑起来电机转是能转就是噪音大、电流波形怪。查了两天最后发现芯片手册里写得很清楚该型号在这种中心对齐模式下更新事件只在上溢产生而他所有的占空比写入逻辑是按“下溢更新”来设计的等于他的控制输出永远晚了半个周期。他以为是换相逻辑写错了其实是更新点没对齐。这里给一个实操提示移植或者参考别人的 PWM 代码时拿到新芯片的第一件事不是跑 demo而是翻参考手册确认三件事——更新事件在哪个计数时刻产生比较寄存器是否默认带预装载中心对齐模式下上溢和下溢分别对更新、比较中断和 ADC 触发各有什么影响。3.3 ADC 采样和更新时刻的相互挤压中心对齐模式下最佳 ADC 采样点一般选在计数值为 ARR 的峰值附近因为所有开关动作在比较匹配点附近完成峰值和谷值时刻刚好是开关动作的空窗期开关噪声最小采到的电流最干净。问题来了如果更新事件也在峰值附近那么“新值刚生效采样立刻开始”采到的是包含新占空比初始响应的电流还不是稳态值如果更新事件在谷值而采样在峰值那这一周期采到的电流是上一周期设置的占空比产生的控制环路就固定存在一个周期的相位滞后。这个矛盾没有绝对正确的解全看控制策略怎么设计。常见的做法有两种一种是在 PWM 周期开始时刻采样并计算在周期起始点更新让新占空比在一个干净的时间基准上生效另一种是采样放在峰值更新放在谷值两个动作错开半个周期用相位固定的延迟换取采样窗口的干净。哪种更好取决于你控制的是电流还是位置、环路带宽是多少但有一点是共通的采样点和更新点的相位关系必须固定不能在运行中漂移。3.4 STM32 高级定时器实操把采样时刻精确卡在中心点STM32 的高级定时器TIM1/TIM8在中心对齐模式下的典型配置思路是这样的中心对齐模式选择 CMS01 或 10开启自动重载预装载ARPE1比较寄存器预装载OCxPE1用重复计数器 RCR 控制更新事件的发生频率不让每次计数满都触发更新配置 TRGO 为更新事件触发 ADC 注入组采样ADC 采样完成后通过中断或 DMA 启动控制算法。下面是一段简化示意具体位域编码请以对应型号参考手册为准// 以STM32高级定时器TIM1为例中心对齐、重复计数、TRGO触发ADC TIM1-CR1 | TIM_CR1_CMS_0 | TIM_CR1_ARPE; // 中心对齐模式1自动重载预装载 TIM1-RCR 1; // 每2次计数翻转才产生一次更新事件 TIM1-CCMR1 | TIM_CCMR1_OC1PE; // 通道1比较寄存器预装载 TIM1-CR2 | TIM_CR2_MMS_1; // TRGO选择更新事件按手册确认编码 TIM1-EGR | TIM_EGR_UG; // 先手动产生一次更新初始化影子寄存器配置完定时器再把 ADC 的触发源设为定时器 TRGO注入组转换序列里放电流采样对应的通道。这样采样时刻就严格卡在 PWM 周期的指定相位上不会随软件执行时间漂移。这里要特别注意如果 ADC 由更新事件触发而更新又发生在计数器峰值附近你采到的电流其实是“新占空比刚刚生效瞬间”的电流。它和理论上“占空比稳定之后的平均电流”之间有一个差值控制算法里如果没考虑这一拍环路可能会表现出奇怪的振荡。4. 死区、互补输出与更新粒度的硬约束4.1 动态死区为何难以实现全桥和半桥驱动里死区时间是防止上下管直通的必要设置。传统更新模式下死区时间一般作为定时器外设的全局静态寄存器运行中修改需要等待更新事件加载。问题在于死区本身并不是一个“逐周期动态调节”的常规参数硬件设计也不鼓励你在运行中频繁改它。有动态死区需求的项目比如轻载时希望减小死区来降低输出波形失真重载时希望加大死区来提高可靠性传统 PWM 更新模式直接帮你把这扇门关上了。替代做法只能是在换相逻辑内部用不同的比较值去做等效补偿或者干脆用支持动态死区的专用电机驱动芯片、FPGA 方案。这个决策要在项目初期做等到 PCB 打样回来再想改就晚了。4.2 互补输出 CH1/CH1N 与更新粒度的分辨率墙STM32 的高级定时器通道 CH1 和 CH1N 是一对互补输出死区时间由定时器自动插入。使用 CH1N 时要注意几个开关主输出 MOE 要打开刹车输入 BKIN 不能处于锁定状态还要把 CCxNE 位置位。很多人第一次用 CH1N 发现没有波形八成是 MOE 没开或者刹车被触发了。但真正卡住系统性能的是更新粒度。假设定时器时钟 100MHzPWM 频率做到 1MHzARR 就只有 100边沿对齐模式下占空比分辨率是 1/100也就是 1%中心对齐模式等效超过 1MHz 也才 0.5%。如果死区需要 50ns那就是 5 个计数周期死区时间的调节步进已经不小了。这还不是最难受的。传统更新模式下任何比较值的跳变都会在输出端出现至少一个计数步的抖动频率越高这个抖动占周期比例越大。MHz 级 PWM 配合高分辨率现场总线比如 EtherCAT 的直流母线同步更新粒度不够会让整个系统的控制精度打折。4.3 TC3xx CCU6 死区设置里的更新点选择英飞凌 TC3xx 的 CCU6 模块里T13 负责定时周期CC60/CC61/CC62 是三个比较通道死区由 DTM 模块产生插入在 CCx 输出和最终输出之间。配置死区时有一个和前面类似的坑DTM 相关配置寄存器的更新与 T13 周期同步不能想什么时候写就什么时候写。我在实际项目中的做法是初始化阶段把所有死区参数一次性写入并且等 T13 走到更新点之后再确认一次寄存器值运行中如果要调整死区先暂停 PWM 输出、更新配置、再恢复。这个流程不是最优雅的但安全性最高。如果非要动态调节也只能在小范围、经过充分测试的条件下做而且修改点要严格放在更新事件发生的同一时刻否则可能出现一个周期里上下管死区时间长短不一极端情况下有直通风险。5. 软件模拟 PWM 时更新延迟问题被进一步放大5.1 8051 模拟 16 位 PWM用中断换分辨率也在用分辨率换确定性很多老工程师用 8051 这类没有硬件 PWM 的 MCU 做过模拟 PWM典型方案是用定时器中断 GPIO 翻转配合一个 16 位累加器每次中断累加一次溢出就翻转引脚占空比由比较值控制。想要 16 位分辨率累加频率就不可能高因为中断里能执行的指令有限频率做得越高周期抖动越明显。这种纯软件 PWM 的“更新模式”完全在软件层你更新一个全局变量下一次中断才生效而中断本身存在不确定性——其他中断抢占、临界区保护、指令执行时间抖动都会让每次中断到达的时间不完全一致。于是整个 PWM 周期不均匀在示波器上能看到边沿在抖动。16 位分辨率在这种实现下只是一个“理论精度”实际的边沿抖动早就把精度吃掉了。如果非要用这种方式驱动 WS2811 这类对时序敏感的芯片建议直接用硬件 SPI 或者专门的单线协议外设别拿模拟 PWM 去硬扛时序抖动会让你怀疑人生。5.2 RT-Thread 软件定时器 PWM调度延迟让更新时刻变成“随机事件”RT-Thread 环境下用软件定时器或者线程去做 GPIO 翻转本质上也是一种软件 PWM。RTOS 调度有最小时间单位——tick常见配置是 1ms 或者 10ms。要做 1kHz 的 PWM 输出一个周期 1ms用软件定时器做翻转已经逼近极限每个 tick 的误差都会直接叠加到脉宽上。实测数据是我在调一个小型舵机时发现的用 RT-Thread 的软件定时器生成 50Hz、双极性脉宽控制信号舵机在静止位置附近持续微抖用逻辑分析仪抓数据脉宽在 1.47ms 到 1.53ms 之间随机漂移。对舵机来说这已经是一个不可接受的控制误差了。所以要说清楚RTOS 下软件 PWM 只适合呼吸灯、蜂鸣器、低速加热、LED 氛围灯这类对时序不敏感的场景。一旦涉及舵机闭环、电机电流环、或者任何对相位有要求的同步场景必须回到硬件 PWM 外设把更新动作交给定时器而不是交给调度器。5.3 PWM 故障保护不能把性命押在更新路径上前面反复强调“写寄存器到更新事件之间有延迟”在正常工作时只是控制性能问题在异常时就会变成安全问题。如果 CPU 因为某种原因卡死或者看门狗复位传统 PWM 外设会保持最后一个有效占空比输出。对电机驱动来说这可能意味着保持一个危险的高占空比飞车事故往往就是这么来的。所以一个合格的设计里一定会有独立于更新路径的故障保护通道STM32 的刹车输入 BKIN、故障比较器直接关断触发信号、TC3xx CCU6 的 TRP 机制、或者外部硬件看门狗直接拉低功率级的 EN/INH 引脚。这些保护不依赖 PWM 更新事件走的是另一条独立的硬件链路。这一点务必记住更新模式的延迟不能当成安全冗余安全必须另有通道。6. 不换方案的优化手段把更新动作精准“卡”到正确时刻6.1 控制预装载位和软件更新事件如果某个应用场景确实需要让新值立刻生效可以关闭对应通道的比较寄存器预装载位直接写活动寄存器。但这么做之前要评估周期中途翻转带来的波形畸变风险一般只建议在停机、急停这类非正常流程中使用不要放在正常调制路径里。另一种方法是软件主动产生更新事件STM32 里就是置 UG 位。但 UG 会同时复位计数器重新装载预装载寄存器在波形上会留下明显的相位跳变不适合精细控制。它最合适的用法是初始化阶段逐个装载所有影子寄存器让 PWM 从一个已知的干净状态开始运行。6.2 DMA 批量搬运占空比表绕过单拍更新延迟SPWM 逆变器、数控波形发生这类场景常用一个很有效的招内存里放一张预先算好的占空比序列表配置 PWM 更新事件触发 DMADMA 自动把表里的下一个值搬运到比较寄存器。CPU 完全不参与逐周期更新自然也就不存在“CPU 写入 → 等待更新事件”的延迟问题。配置要点有三个内存数组开启循环模式外设地址指向定时器比较寄存器DMA 触发源选为定时器更新事件。这样每个 PWM 周期结束DMA 自动把新值搬进去更新事件一到就生效整个过程是流水线式的。CPU 只需要在运行过程中偶尔刷新整张表粒度可以放宽到几百个周期一次。这个方案的代价是内存占用和预计算量但对逆变器、变频空调压缩机这类应用来说非常划算。它本质上是用“批量更新”代替“逐次更新”有效绕开了传统更新模式单拍延迟的约束。6.3 重复计数器把更新和采样彻底错峰RCR 重复计数器可以设定每隔 N 次计数满才产生一次更新事件。利用这个功能可以让更新和采样在两个不同子周期完成比如计数峰值时做 ADC 采样计数谷值时执行更新两者在不同半周期发生打破了“采样点被更新点挤压”的困局。在实际配置中RCR 改的是更新事件频率对比较寄存器的预装载行为也有连带影响必须查参考手册确认。我一般配合 DMA 一起用RCR 把更新事件频率压低DMA 在每次更新时搬运新占空比ADC 在另一个相位触发采样CPU 只在中断里跑控制算法。这样整个信号链路由硬件时钟统一驱动软件部分几乎不会引入不确定延迟。6.4 选型判断什么时候必须换高分辨率或可配更新相位的方案如果项目已经踩到传统更新模式的天花板与其在软件里打补丁不如回头评估硬件选型。下面几个判断条件如果满足两条以上建议尽早换平台控制周期和 PWM 周期接近一拍延迟对环路稳定性影响显著需要周期内多次更新或者动态死区调节对占空比分辨率要求高于传统定时器计数位数的能力尤其是 MHz 级 PWM多个 PWM 通道需要严格保持同相位输出软件同步成本偏高项目要求高带宽电流环采样点必须精确落在某几个固定相位上。TI 的高分辨率 PWMHRPWM用 MEP 微边沿定位可以把占空比精度做到 150ps 量级英飞凌的 CCU6 和新一代电机控制 MCU 也提供了更灵活的更新点配置。这些方案比传统定时器 PWM 贵但省下来的调试时间和性能收益往往远超差价。最后分享一个我个人的经验习惯拿到任何新平台第一件事不是跑 demo而是用逻辑分析仪去测“从软件写寄存器到 PWM 输出边沿变化”的延迟。如果延迟严格等于一个 PWM 周期且相位固定说明是最传统的更新模式如果延迟在随机波动说明更新路径里混入了软件调度如果延迟小于一个周期说明硬件支持立即更新或者多更新点。这个测量结果基本决定了整套控制策略怎么写。做 PWM 相关的项目先把更新模式研究透比盲目调 PID 参数有效得多。
返回列表