前阵子调试HPM6E80的电流环,卡住我时间最长的不是PID参数,而是ADC采回来的电流波形。波形里的毛刺一度大到电流环不敢把速度加上去,后来把ADC抢占模式配合PWMV2计数器中心点触发采样彻底弄明白并落地之后,波形才真正干净下来。这篇学习笔记想把原理、配置过程、验证手段和踩过的坑完整梳理一遍,给后面要做FOC电流采样、数字电源电压采样或者其他依赖“精确时刻采样”的朋友做个参考。
1. 为什么采样点必须盯住PWM周期的“中心点”
1.1 开关噪声并不是均匀分布的
PWM驱动半桥电路时,MOSFET在导通和关断的瞬间会经历米勒平台、寄生电感放电、寄生电容充放电等一系列过程,Vds节点电压和相电流上都会出现高频振铃。很多刚接触电机控制的同事以为这个噪声是随机的,其实它的分布非常有规律:换流过程集中在PWM比较值匹配的时刻附近,振铃主要出现在开关动作之后的几百纳秒到几微秒;而两次开关动作的中间,功率回路处于相对稳定的导通或续流状态,电压和电流都比较平缓。如果ADC在开关动作附近触发采样,采回来的数值就是叠加了高频分量的错误值,后面的电流环一闭环,控制量里全是这个噪声的痕迹。
类比一下:你拍照想拍清楚运动物体,快门落在物体运动的瞬间,画面必然是糊的;只有等物体位置稳定的时候按下快门,画面才干净。ADC采样也一样,PWM开关瞬间就是“运动最剧烈”的时刻,而周期中间那段相对平稳的窗口就是最好的拍摄时机。问题是,大部分MCU的ADC触发源并不会主动帮你选这个窗口,得靠PWM定时器配合才能做到。
1.2 中心对齐模式下,计数器有几个关键时刻
要理解“中心点触发”,先得看清PWMV2在中心对齐模式下的计数器动作。中心对齐模式又叫up/down计数模式:计数器从0递增到周期值(这里记为PERIOD),再从PERIOD递减回0,如此反复。一个完整的PWM周期包含两次计数行程,所以输出波形的周期是计数器上升段和下降段相加。
在这个模式下有几个时刻非常关键:
| 时刻 | 计数器位置 | 事件含义 | 对采样的意义 |
|---|---|---|---|
| 周期归零 | counter=0 | 新周期开始 | 对应载波谷底,通常也是低电平的中点 |
| 周期峰值 | counter=PERIOD | 计数到顶 | 对应载波峰顶,通常也是高电平的中点 |
| 比较匹配 | counter=CMP | 输出翻转点 | 换流发生的起点,也就是开关噪声的源头 |
| 死区插入 | CMP附近 | 互补输出延迟 | 换流过程被拉长,噪声窗口变宽 |
为什么说counter=PERIOD是高电平的中点?中心对齐PWM的输出翻转规则大致是:上升计数到CMP时输出拉高,下降计数到CMP时输出拉低,那么高电平区间就对称地分布在PERIOD两侧。无论占空比调到多大,高电平的中心时刻永远等于计数器到达PERIOD的时刻;同理,低电平的中心时刻等于counter=0。这才是“计数器中心点”这个名字的来源,它和PWM占空比无关,是定时器结构决定的天然几何中心。具体还要看比较输出极性配置,如果SDK里的CMP行为是反向的,中心点仍然在这两个时刻,只是对应的高/低电平关系换了一下。
1.3 中心点采样的真正优势
把采样点放在中心点,好处有以下几条:
- 离所有换流时刻都最远。中心点与相邻两次比较匹配点之间的距离大约是半个PWM周期,开关振铃到这时已经充分衰减,信噪比最高。
- 与占空比解耦。不管占空比是10%还是90%,中心点永远在开关过程的中间位置,不需要为了占空比变化动态调整采样时刻。
- 电流纹波的平均效果。电感电流在PWM周期里呈三角波,中心点恰好落在纹波的平均值附近,采到的数值更接近理想平均电流,这对电流环的稳态精度很有帮助。
对比一下边沿对齐模式:计数器只做0到PERIOD的单向递增,周期事件发生在计数器回0的时刻,而这个时刻同时是上一个周期的结束和新周期的开始,紧挨着开关动作。也就是说,边沿对齐模式下你几乎找不到一个“远离所有开关噪声”的稳定窗口,所以电机控制和数字电源需要高质量电流采样时,基本都优先选择中心对齐PWM,再配合中心点触发。
2. 抢占模式机制拆解:ADC如何在精确时刻“插队”
2.1 没有抢占时,触发请求只能排队
很多人觉得“PWM给ADC一个触发信号,ADC不就立刻转换了吗?”实际没那么简单。HPM6E80的ADC在一个时刻只能执行一个转换序列,如果ADC正忙着为别的通道做转换,新来的硬件触发只能排队等待。等多久取决于当前转换还剩多少时间、队列里还有多少请求,这是一个不确定的延迟。
想象一下去银行柜台办事,你赶着拿一个非常重要的号,但前面的人正在办理三笔业务,你只能站在队伍里等着。电流采样就是那个“重要号”,控制算法从产生PWM到下一次采样点的间隔是固定的,你希望采样永远发生在PWM中心点附近那一小段窗口里,结果ADC却可能因为排队错过窗口。排队延迟如果只有几百纳秒还好,如果前面刚好在执行一个长扫描序列,延迟可能拉到几微秒甚至十几微秒。对20kHz的PWM来说,一个周期才50us,十几微秒的偏差足以让采样点偏到开关噪声区里去。
2.2 抢占的“打断再恢复”流程
抢占模式解决的就是这个问题。给关键触发源分配一个高优先级,当这个触发到达时,如果ADC正在执行普通转换,硬件会保存当前转换状态,暂停手上的活,先去完成抢占通道的转换,结果写入专门的结果FIFO,然后再恢复刚才被打断的转换。
注意这里不是“新请求插到队首”,而是真正意义上的打断并恢复。对普通转换来说,它只是晚了一小会儿完成;对关键的抢占通道来说,从触发到转换开始的时间是确定且很小的。这种机制特别适合那种“过了这个村就没这个店”的时刻敏感型采样,比如电流环的采样点就只存在于PWM中心点前后一小段窗口,错过了就只能等下一个周期,而且控制周期会因此抖动。
2.3 抢占模式下,普通转换会“迟到”一小会儿
这一点是很多人容易忽略的。开启抢占后,每次抢占触发都可能打断普通通道的转换,普通通道的采样节奏会变成“时断时续”。如果你的普通通道是一个温度传感器,晚个几微秒完全没感觉;但如果普通通道也在做时序敏感的测量,就需要评估打断带来的影响。
我在配置时是这样处理的:把所有“时刻不敏感的通道”(比如温度、母线电压慢速监测)归到一个普通转换队列里,把“时刻敏感通道”(比如两相电流)全部走抢占通道。这样两者互不干扰,普通队列晚一点完成无所谓,抢占通道则永远能准点采样。这种“慢的走队列,快的走抢占”的思路,比把所有通道都塞进抢占里要干净得多。
2.4 为什么不用软件中断去触发
还有一种做法是让PWM中心点中断里用软件触发ADC。但软件触发有几个硬伤:第一,从PWM事件发生到进入中断,需要CPU响应中断、压栈、跳转,通常几十条指令的延迟,即使主频很高,这个延迟也远大于硬件直达的几百纳秒;第二,中断响应可能被更高优先级中断抢占,采样时刻进一步抖动;第三,CPU在电流环里往往已经很忙,软件触发等于把时序敏感的任务丢给通用处理器做,浪费算力。
抢占模式的好处是,整个链路全部由硬件完成:PWMV2在中心点产生事件,经过触发路由矩阵到达ADC,ADC中断当前普通转换,执行采样,结果进FIFO,再由DMA搬走。CPU只需要在DMA搬完数据后做一次运算。这才是真正适合电流环的节奏。
3. 配置全流程:从PWMV2中心对齐到ADC触发绑定
3.1 先把PWMV2的中心对齐和周期算清楚
配置的第一步是让PWMV2工作在中心对齐模式,并算出PERIOD值。这里以本工程的实际时钟为例:PWM外设时钟设为150MHz,目标载波频率20kHz。中心对齐模式下,计数器从0走到PERIOD再走回0才是完整的一个PWM周期,所以周期值大约是:
PERIOD = PWM时钟 / (2 × 载波频率) = 150e6 / (2 × 20e3) = 3750
实际开发中,SDK的period寄存器取值可能存在“period本身加1还是减1”的差异,也就是计数器的终点到底是PERIOD还是PERIOD-1,这会影响最后一个时钟周期的归属。按我调这块芯片的经验,最终频率误差在千分之几以内都无所谓,关键是确认PWM输出在示波器上的实际频率,别只看寄存器里的数字。
占空比通过比较值CMP设置,比如50%占空比就把CMP设为PERIOD的一半左右。这里要注意,中心对齐模式下“占空比”和CMP的关系会受计数方向影响,通常SDK里会有补码或shadow寄存器机制,配置时最好先输出一个固定占空比验证,确认极性正确后再往下做。
3.2 把“中心点事件”选成周期峰值事件
PWMV2在中心对齐模式下,有两个天然的“中心点”:counter=PERIOD(高电平中点)和counter=0(低电平中点)。电流采样常用的是counter=PERIOD时刻,也就是周期峰值事件。有的SDK或者数据手册里可能叫法不同,比如“up-counter period match event”或者“peak event”,但核心含义都是“计数器走到最大值”。
这一步有一个极其容易搞混的点:周期峰值事件不等于周期溢出/周期结束事件。在边沿对齐模式下,计数器到达PERIOD后清零,这个时刻既是中心也是周期终点,撞在一起;但中心对齐模式下,计数器到达PERIOD后会转头向下走,周期并没有结束,要再走完下降段计数器回到0才是周期终点。如果你把触发源错配成了“周期结束事件”,那采样点等于落在了PWM周期的边界上,恰好是开关动作密集区,整个中心点采样的思路就白费了。这是我在验证阶段踩得最重的一个坑,后面会细说。
3.3 通过触发路由矩阵接到ADC
HPM6E80这类芯片里,外设之间的触发信号不是硬连线直通的,而是经过一个叫TRGMUX的触发路由矩阵。PWMV2产生的事件先进入矩阵的输入侧,再由软件选择把它接到哪个外设的触发通道。ADC的硬件触发通道一般有好几个,每个通道可以绑定一个TRGMUX输出。
配置时分两步走:第一步,在PWMV2那边使能中心点事件输出,相当于把信号源打开;第二步,在TRGMUX里把该事件映射到目标ADC的某个触发通道号。这里需要对照芯片手册里的TRGMUX输入/输出映射表,不同型号的通道编号可能完全不一样。我看到过有同事直接照抄另一个型号的配置,结果ADC被一个完全不相关的事件触发,半天排查不出来。
另外要注意触发极性。PWM事件输出通常是一个短脉冲或者电平信号,ADC一侧的触发通道一般配成上升沿触发。如果误配成了电平触发,会变成持续触发状态,FIFO会被不断灌入重复采样,这个现象很隐蔽,表现形式就是“数据量比预期多好几倍”。
3.4 抢占通道、优先级和FIFO
到了ADC这一侧,先在配置里打开抢占功能,然后把刚才TRGMUX里选好的触发通道绑定到抢占通道,指定采样的通道号。比如本工程里用ADC0的某个模拟通道采U相电流,那么抢占通道就绑定这个模拟通道。接着设置优先级,抢占模式的核心就是优先级,要让中心点事件在所有可能的ADC触发里排第一。
优先级数字方向最容易出问题。有的芯片是数字越大优先级越高,有的是越小越高,还有的SDK在头文件里定义成了“high但是底层取反”。这个没有统一规律,我每次换平台都会写个最简单的测试用例验证一下:把两个触发源错开几十微秒触发,看哪个先完成。验证一次比翻文档猜半天省时间得多。
FIFO深度也要设置。抢占转换的结果进入专门的FIFO,如果DMA还没把上一次结果搬走,FIFO满了,新的采样结果就会丢失。操作上建议把FIFO深度配置和DMA请求周期对齐:采样率越高的场景,DMA的优先级也要相应提高。
3.5 结果搬运:用DMA而不是中断读FIFO
结果搬运我强烈建议走DMA。抢占模式的意义是“时刻准、CPU闲”,如果每来一个采样都让CPU进中断读FIFO,那准确时刻倒是保证了,CPU却被高频中断拖累。本工程的配置是:DMA的源地址指向ADC抢占结果FIFO,目标地址是一块内存数组,每次触发搬运一个转换结果。DMA搬完可以产生一个块传输完成中断,控制算法在这个中断里统一做坐标变换和PI运算。
代码结构大致是这样的(以厂商SDK风格描述,具体接口名随版本会变):
// 1. PWM中心对齐初始化 pwm_config_center_aligned(PWM, PERIOD); pwm_set_compare_value(PWM, CMP_CH0, PERIOD/2); // 50%占空比 // 2. 使能中心点事件输出 pwm_enable_peak_event(PWM); // 3. TRGMUX路由:PWM峰值事件 -> ADC0触发通道 trgmux_set_route(TRGMUX_ADC0_TRIG0, TRGMUX_SRC_PWM_PEAK); // 4. ADC抢占配置:绑定触发通道、采样通道、优先级 adc_config_preempt_channel(ADC0, PREEMPT_CH_PRIO_HIGH, ADC_TRIG_CH0, ADC_CH_U_PHASE); // 5. DMA:FIFO结果搬到内存数组 dma_config_transfer(DMA, ADC0_PREEMPT_FIFO, ¤t_samples[0], 1, DMA_PER_TO_MEM);这里每个步骤都只是示意,真正在SDK里会有更多的结构体字段。但配置的逻辑顺序是固定的:先有时钟和定时器,再有事件路由,再有ADC抢占,最后接DMA。顺序反了,后面收到的事件没有来源,排查起来会绕很大圈子。
3.6 采样保持时间别偷懒
还有一个和触发时刻相关的细节:采样保持时间。ADC采样分成采样阶段和转换阶段,采样阶段里采样电容要对输入信号充电,时间太短会导致采到的电压还没建立稳定,尤其信号源阻抗大的时候。中心点触发只保证了“什么时候开始采样”,并不能保证“采得准”。工程上要么把信号经过运放缓冲后再进ADC,要么把采样保持时间配置得足够长。代价是总转换时间变长,要确保它在两次中心点事件的时间间隔以内。
4. 验证与踩坑:如何确认采样点真的在中心点
4.1 最直接的验证:翻转GPIO看时序
配置完成后,第一个要验证的不是电流波形数据好不好看,而是采样时刻到底落在哪。我常用的方法是:在DMA完成中断或者ADC抢占完成中断里翻转一个GPIO,然后用逻辑分析仪同时观察PWM输出和这个GPIO。如果GPIO翻转沿稳定出现在PWM周期的同一个相位里,说明采样链路时序基本正确。
这个方法的精度有限,因为中断响应本身会引入延迟,但用来排查“采样点是不是偏到半个周期外”这种级别的问题足够了。如果你要更精确的验证,可以看看手里的型号是否支持读取转换时间戳。把每次转换发生的时间戳读出来,连续跑几千次,观察时间戳相对于PWM中心点的分布,这是最扎实的验证方式。
4.2 占空比扫描法:采样值不该随占空比大幅跳变
另一个很实用的验证思路是利用电气特性反推采样时刻。给ADC输入一个稳定的直流电压,然后把PWM占空比从10%一直扫到90%。如果采样点真的在中心点附近,理论上采样结果应该在很小范围内波动,因为中心点对应的瞬时值基本稳定;如果采样结果随着占空比明显变化,说明采样点落到了换流区附近,采到的是处于陡峭变化中的电压电流,这时候就该回头检查事件源是不是配错了。
这个方法做起来很快,不用示波器,只需要串口把采样值打印出来就行。我第一次做这个测试时,数据随占空比变化的幅度非常大,才意识到触发源配成了周期结束事件,折腾了半天的问题根源其实在事件选型上。
4.3 最大坑:中心点事件和周期结束事件混淆
这个值得单独说。中心对齐模式下,counter=PERIOD和counter=0都是中心点,但SDK里的事件命名五花八门,有的叫period event,有的叫peak event,有的叫zero event。如果选错成“周期结束/计数器回零后再触发”的事件,采样的时间基准就整个错位了。
我在测试时遇到的现象是:占空比改变后,采样数据跟随占空比剧烈变化,并且波形上有一种“跟随开关边沿移动”的痕迹。后来查配置发现,我选的事件实际上在计数器回到0的时刻附近触发,对于占空比很大的PWM来说,这个时刻距离上一个高电平结束很近,正好处于开关噪声尚未平息的区间,采出来的数据自然不稳。改正成period峰值事件后,数据立刻稳定下来。所以遇到“采样结果和占空比强相关”的问题,优先怀疑事件源选型。
4.4 TRGMUX路径上的延迟和电平问题
PWM事件经过TRGMUX矩阵再到ADC,中间有组合逻辑延迟,通常是几个系统时钟。这个延迟本身不大,但在要求采样点精确到亚微秒的场合要考虑进去。如果发现采样时刻比理想中心点偏早或者偏晚固定的量,可以在软件上做一个固定补偿,或者调整PWM事件相对于采样窗口的相位。
触发极性也是常见翻车点。PWM事件输出如果是高电平有效的脉冲,ADC触发通道配成上升沿触发没问题;但如果配成了高电平触发,会出现一个中心点事件却连续触发多轮采样的现象,FIFO很容易溢出。判断方法是看DMA搬运的数据量是不是明显多于预期值。多出来的数据都集中在中心点附近,波形上表现为同一位置有几个几乎相等的点。
4.5 FIFO溢出和DMA带宽
把采样率算清楚。中心对齐PWM 20kHz,如果每个周期触发一次抢占采样,采样率就是20kSa/s;如果一个周期触发两次(峰值和零点各一次),那就是40kSa/s。以12位ADC典型的转换时间1us左右来算,40kSa/s意味着ADC有4%的时间被抢占通道占用,这对一个ADC来说压力不大。但如果普通通道队列也在跑长序列,叠加起来就可能不够,FIFO就会溢出。
溢出的表现不是数据直接消失,而是FIFO状态寄存器出现overflow标志,DMA搬到的数据里漏掉了某几次采样。漏点会导致电流波形出现周期性缺口,如果没注意FIFO错误标志,很容易怀疑到DMA配置或者中断上。建议在调试阶段每隔一段时间读取一次FIFO溢出标志,确认采样链路始终健康后再把它关掉。
4.6 多通道串行转换的相位差
如果你把一个ADC的抢占通道配置成多个模拟通道,比如同时采U相和V相电流,要注意它们不是同时采的,而是一个接一个串行转换,通道之间存在一个转换时间的间隔。假设单通道转换需要1us,那么U相采完后,V相要等1us才开始采,这对电流环来说就引入了固定的相位差。
解决思路是:能分到不同ADC就分到不同ADC,比如ADC0采U相、ADC1采V相,并且让同一个中心点事件同时触发多个ADC的抢占通道,这样两相电流的采样时刻真正对齐;如果不得不在同一个ADC里串行采样,要么接受这个固定相位差并在软件里补偿,要么把两个通道配置到不同的触发时刻,让它们各自对准自己需要的相位。
5. 进阶玩法:双触发点、多ADC同步和硬件平均
5.1 一个PWM周期内用两个触发点
中心点采样的思路稳定下来后,自然会想“既然高电平中心能采,低电平中心能不能也采?”完全可以。配置counter=PERIOD事件触发电流采样、counter=0事件触发母线电压采样,这样电流环和电压环的数据都有了可靠的时间基准。注意触发频率翻倍后,FIFO和DMA的吞吐量核算要跟上。很多数字电源就是靠这种双触发设计,在一个PWM周期里同时拿到电流和电压的准确瞬时值。
这里有个小经验:两个触发点的事件源虽然在同一个PWMV2里,但它们在TRGMUX里是两路不同的事件输出,可以分别路由到同一个ADC的两个不同触发通道,也可以路由到两个ADC。如果路由到同一个ADC,要确认两个触发事件之间的时间间隔大于每组采样的总转换时间,否则第二组触发会被第一组拖住,时间基准又被破坏了。
5.2 三相电流同时采:多ADC硬件同步
三相FOC里有个经典做法叫“一次同时采样三相电流”。如果只靠软件轮流采,三相电流之间会有固定的时间差,折算成角度误差后会影响坐标变换的精度,特别是在电机高速运行时误差会被放大。正确的做法是把同一个PWM中心点事件,通过TRGMUX同时路由到ADC0、ADC1、ADC2三个ADC的抢占通道,让三个采样在硬件层面几乎同时完成。这个配置和单个ADC的抢占配置没有本质区别,只是触发路由要重复做三次。
我当时验证多ADC同步时,是先在三个ADC的输入引脚分别接三个已知电压,然后用同一个中心点事件触发,取回三组数据看它们的时刻是否一致。这个方法比靠波形推测要快得多,毕竟三相电流同时采样错半个微秒,从示波器上很难看出问题,但从数据上能直接看出来。
5.3 中心点附近的硬件平均
如果觉得单次采样的噪声还是偏大,可以利用ADC的多次转换能力在中心点附近连续采几个点,最后在软件里做平均。这个做法对随机的热噪声和高频噪声压制效果明显,但要注意它会把中心点附近一小段时间内的信号变化混在一起,如果电流在这个窗口里有明显变化,平均结果反而会失真。我的经验是:中心点采样的信号本身就比较平稳,做硬件平均通常能换来信噪比提升;如果发现平均后波形变钝,说明窗口内的信号变化已经不能忽略,那时候该考虑的不是平均,而是检查采样点是否偏离了真正平稳的区间。
5.4 和PWM故障保护联动
最后可以提一个和采样系统相关的扩展:ADC抢占的结果还可以接到片内的比较器/阈值判断模块,由硬件直接比较,一旦电流超过门限就触发PWM故障保护关断,完全绕开CPU。这个用法和中心点触发采样是同一条数据链路的两种消费方式——采样数据既可以送去控制环计算,也可以送给硬件保护逻辑。对电机控制来说,这条链路做出来之后,过流保护的响应时间比纯软件方案低一个数量级。
配合中心点触发的思路,保护逻辑也可以做得更聪明:不是在任意时刻随机判断,而是只在每个PWM周期最稳定的中心点窗口里判断,这样硬件比较器看到的值本身就不带开关噪声,阈值可以设置得比传统方案更紧而不会误触发,把保护裕量利用到极限。
5.5 采样触发时刻的微调与补偿
如果你做完以上所有配置后发现采样时刻还是和理想中心点有固定偏差,不要急着改硬件。先分析偏差来源:TRGMUX的组合逻辑延迟、ADC触发同步电路的打拍延迟、PWM事件输出到TRGMUX输入之间的路径延迟,这几部分加起来是一个固定值。可以从两个方向处理:一是利用PWMV2自身的相位控制功能,把事件产生时刻略微提前或延后;二是在软件里对采样结果的时间标签做固定补偿。对电流环来说,固定相位补偿完全可行,关键是先把偏差测准,别拍到一半盲调。
按我自己的调试体会,中心点触发采样的配置本身并不复杂,真正花时间的是把“采样时刻”这个看不见摸不着的东西验证清楚。宁可先在GPIO上拉出波形确认时序,再回头调控制环,也不要一上来就盯着电流波形好看不好看。把时间基准钉死了,后面所有算法才有意义。这份笔记如果能让正在折腾HPM6E80或者类似芯片的朋友少走几步弯路,就不算白写。