
去年调一块F28379D的永磁同步电机驱动板电流环写好了PWM也出来了结果一上电电流波形跟个锯齿似的怎么补偿都压不住。查了一整天最后发现根子不在PI参数而是SDFM的滤波器输出节拍和ePWM载波完全没对齐——采样点总是落在IGBT开关边沿上一采一个毛刺。今天就把这套ePWM与SDFM同步配置从寄存器到Driverlib完整梳理一遍。这篇文章不是给你搬TI例程而是把底层逻辑讲透为什么SDFM必须跟着ePWM走、同步信号在芯片内部到底是怎么连的、寄存器的每个位段在干什么、用Driverlib写出来又是什么样。适合正在用F2837x/F28004x做电机控制或数字电源的开发者尤其是想把电流环带宽往上推、或者自己画板子改采样方案的人。看完你至少能少走我当初两天的弯路。1. 采样对齐的必要性为什么SDFM必须跟着ePWM走很多刚接触C2000的人会把SDFM当成一个外挂ADC来看觉得只要把调制器位流接进去读出来的数字就是电流值。这个理解不算错但会漏掉最关键的一层SDFM不是按你的意愿随时采样的它的输出节拍由内部SINC滤波器决定而滤波器什么时候吐一个数据取决于你给它的同步信号。1.1 Σ-Δ调制位流是如何变成数字电流的先把SDFM的角色说清楚。外部Σ-Δ调制器比如TI的AMC1306、AMC1200这类隔离式调制器把模拟信号转成一位位流输出的是连续0/1序列。这个位流里每一位单独看没有任何意义必须通过数字滤波器抽取、累加才能还原出模拟量对应的数字值。C2000的SDFM模块内置的就是这种抽取滤波器常见的是SINC1、SINC2、SINC3三阶可选。SINC3的阻带衰减最好但延迟也最大SINC1延迟最小但对带外噪声的抑制最差。滤波器的核心参数是OSR过采样率比如OSR32意味着每32个位流时钟周期滤波器输出一个数据。这段关系是后面所有计算的基础先记着**滤波器每次输出一个数据需要消耗OSR个位流时钟周期。**如果你对OSR和SINC阶数没概念就把它想象成一个移动平均值窗口窗口越大数值越平滑但反应越迟钝。1.2 PWM开关噪声与采样窗口的冲突电机控制里最麻烦的不是白噪声而是开关噪声。IGBT或MOSFET在开关瞬间母线电压会在极短时间内发生大摆率变化通过寄生电容耦合到采样回路产生幅度很大、持续几百纳秒到几微秒的尖峰。如果用普通的逐次逼近型ADC你还可以通过等毛刺过去了再启动转换来规避。但SDFM不同SINC滤波器是个连续累加过程如果开关噪声发生在滤波器抽取窗口内这个噪声会被积分进最终输出值里。更糟的是如果采样时刻恰好落在PWM边沿上那每次采到的噪声相位不一样输出就会出现周期性波动。所以说白了采样点必须避开开关边沿也不要在死区时间内采样。避开之后选哪儿通常选在PWM周期内电流已经稳定的区域比如中心对齐PWM的载波顶点或者载波零点附近具体位置由DATAPOS这些参数来微调。1.3 多桥臂一致性对同步的需求还有一个容易忽略的点三相电机驱动需要同时采样至少两相电流第三相可以算出来如果三相电流的采样时刻互相错开算出来的电流矢量就有相位误差直接导致坐标变换后的d/q轴电流抖动。所以理想情况是所有SDFM滤波器的输出节拍必须对齐到同一个时间基准上。这个时间基准最自然的选择就是PWM时基——毕竟PWM载波周期就是电流采样周期而整个系统的控制定时都由它派生。这就是为什么我们不是简单地把SDFM置为自由运行而是要让它和ePWM绑在一起。2. 同步的底层逻辑从ePWM比较事件到SDFM输出节拍知道了为什么要同步接下来看怎么同步。这部分的重点是理解芯片内部的一条信号链顺着这条链走后面配置寄存器就不会觉得是在碰运气。2.1 一条完整的同步链路在F28379D这类芯片上链路是这样的ePWM时基计数器TBCTR在UP-DOWN模式下循环计数。当TBCTR等于比较寄存器CMPC时事件触发子系统的SOCB信号被拉高。SOCB信号一方面可以触发ADC转换另一方面在芯片内部也被引到了SDFM模块。SDFM收到这个同步脉冲后SINC滤波器完成当前抽取周期并在对应时刻更新输出数据寄存器。也就是说ePWM的CMPC比较事件决定了SDFM滤波器什么时候输出数据。这其实就是标题里同步的核心含义不是简单的电平同步而是让SDFM的抽取周期和PWM载波周期建立起固定的相位关系。还有一个外部路径SDFM的DRData Ready引脚可以接受外部输入同步信号。如果调制器有自己的独立时钟或者你需要跨板同步可以用这条路径。但板内做电机控制时我建议直接用内部SOCB理由后面会说。2.2 DATAPOS微调采样点在PWM周期内的位置直接让SDFM在CMPC事件发生时输出数据通常不够。因为CMPC对应的时刻还是有可能离开关边沿太近或者离死区时间太近。为此SDFM里有一个DATAPOS参数允许你把滤波器输出时刻往后推若干个模块时钟周期。打个比方CMPC给出了一个基准零点半DATAPOS就是在它后面加一段偏移把最终采样点挪到安全位置。这个偏移的单位是SDFM的模块时钟周期不是PWM时基周期配置的时候要看清楚。实际工程里我的做法是先估算死区时间和尖峰持续时间比如死区2us、毛刺1us那把DATAPOS设成能覆盖3~5us的数值再实测波形微调。如果PWM周期100us你完全有富裕空间把采样点放在周期中段。2.3 同步源的选择哪种方案更稳妥内部SOCB同步和外部DR引脚同步各有各的适用场景我根据自己的项目经验对比如下同步方式优点缺点适用场景内部ePWM SOCB无需额外连线相位关系天然对齐延迟小抗干扰好只能和本芯片的PWM同步跨板同步受限单板电机驱动、数字电源绝大多数情况外部DR引脚可由外部调制器或另一块板子提供同步灵活需要保证外部信号时序连线多容易受干扰多板级联、大功率变频器、特殊同步需求我在实际项目中99%用内部SOCB只有一次做多逆变器并联时为了对齐两台控制器的载波和采样节拍才专门研究了外部同步方案。对大多数开发者来说内部SOCB就是最省心、最可靠的选择。3. 寄存器视角关键位段逐项拆解配置过程现在进入寄存器的层面。这个部分不是让你背寄存器而是告诉你哪些位段和同步直接相关、它们之间是怎么配合的。我会以F2837x系列为例F28004x等后续型号的寄存器名可能略有差异但逻辑一致。3.1 ePWM侧的关键寄存器TBCTL、CMPC、ETSELePWM侧要解决两件事一是把时基配成你想要的工作模式二是在指定计数点触发SOCB。先看TBCTL。这里最重要的是时基时钟分频和计数模式。UP-DOWN模式下PWM频率等于TBCLK除以2×TBPRD这个系数很容易被忽略。比如TBCLK100MHz要10kHz PWMTBPRD应该是4999而不是9999否则出来的频率只有5kHz。再看CMPC。CMPA/CMPB通常用来控制占空比CMPC/CMPD则经常用来干事件触发这类副业。把CMPC设成一个固定值当TBCTR计数到这个值时事件触发模块就能产生SOCB。我惯用的做法是把CMPC设在载波周期的25%~50%范围内具体值配合DATAPOS的偏移一起计算确保最终采样点落在安全区间。ETSEL寄存器负责选择触发源。要把SOCB的触发源设为计数器等于CMPC并且设置触发事件分频ETPS——一般设成1也就是每个PWM周期都触发一次。如果设成2或更大那就变成每两个或更多PWM周期才同步一次采样率会下降。3.2 SDFM侧的关键寄存器SDFMCTL、SDFMMCTL、SDCTL、SDDATPARMSDFM侧寄存器看着多其实和同步强相关的就那几个。SDFMCTL是滤波器使能控制每个滤波器需要单独使能。这一步漏了后面配置全白做还没中断。SDFMMCTL是模块级控制寄存器这里的SSF位段专门管同步源选择。以F2837x为例你可以把每个滤波器的同步源配成ePWM1、ePWM2或ePWM3的SOCB也可以配成外部DR引脚。具体编码查对应型号的TRM但概念上就是给每个滤波器选一个同步源。SDCTL寄存器里和同步关系最大的是数据模式和使能控制位。你需要在SDCTL里打开高速滤波器使能并确认数据输出模式是PWM同步模式。所谓PWM同步模式就是滤波器输出节拍跟着同步源走而不是自由运行。如果忘了配这步SDFM虽然也在出数据但和PWM没有固定相位关系等于没同步。SDDATPARM寄存器就是前面说的DATAPOS参数所在的位置。它决定了从同步脉冲到滤波器输出数据之间隔多少模块时钟周期。这个值直接影响采样点在PWM周期里的实际位置是整条配置链里最需要在示波器上反复验证的参数。3.3 时钟与相位参数的计算方法这里给出一个可复用的计算思路我实际就是这么算的。先确定系统时钟假设EPWMCLK100MHz、SDFM的模块时钟也是100MHz。目标PWM频率10kHzUP-DOWN模式则TBPRD100MHz/(2×10kHz)-14999。再确定SINC滤波器参数。假设选用SINC3、OSR32。SINC3的群延迟经验值约1.5×OSR×位流时钟周期。如果位流时钟也是100MHz量级那就是约1.5×32/100MHz480ns。这个延迟虽然不大但如果你把电流环带宽推到2kHz以上就需要在控制环里做补偿或者在配置采样点时留出余量。DATAPOS的实际取值需要把死区时间和开关尖峰时间考虑进去。假设死区2us、尖峰1.5us那你希望采样点至少在PWM边沿后3.5us以上。DATAPOS的单位如果是一个100MHz时钟周期10ns那么从同步基准点往后推350个时钟周期左右比较安全。不过实际测试时我发现取值还要考虑PCB布局带来的额外噪声所以先按计算值设再上板实测微调。下面给一个我在项目里做参数估算时常用的表PWM频率TBPRD(UP-DOWN)OSRSINC3群延迟建议DATAPOS经验起点5kHz999964约1.92us400~60010kHz499932约0.48us300~50016kHz312424约0.36us200~40020kHz249916约0.24us150~300注意DATAPOS经验起点只是参考具体值一定要结合死区和尖峰实测调整。4. Driverlib实战F28379D上的可复用初始化代码寄存器讲完现在用Driverlib把这套配置写出来。我以TMS320F28379D C2000Ware 3.x为例外部用一个AMC1306隔离式Σ-Δ调制器做相电流采样。代码可以直接抄到工程里改参数用。4.1 环境准备与引脚分配我的板子配置如下你可以参照修改PWM输出ePWM1A/1B用于控制一个半桥输出频率10kHz中心对齐模式。调制器位流输入SDFM1的SD1_A引脚接收AMC1306的位流输出。调制器时钟由ePWM2的PWM输出提供20MHz方波接到AMC1306的CLKIN。同步信号ePWM1的SOCB内部连接到SDFM1的滤波器1同步源。先把引脚复用配好这是很多人省略的一步。SDFM的SD引脚、PWM引脚、以及调制器时钟引脚都必须使能对应的GPIO复用功能否则后面怎么配SDFM都不会有数据。4.2 ePWM初始化代码初始化ePWM1时我习惯先配时基再配比较最后配触发。关键代码如下#include driverlib.h #include device.h #define PWM_FREQ_HZ 10000u #define TBCLK_HZ 100000000u // UP-DOWN模式频率 TBCLK / (2 * TBPRD) #define PWM_TBPRD ((TBCLK_HZ / (2u * PWM_FREQ_HZ)) - 1u) // 4999 #define PWM_CMPC_VALUE 1250u void initEPWM1_ForSDFMSync(void) { // 1. 时基配置 EPWM_setClockPrescaler(EPWM1_BASE, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1); EPWM_setTimeBasePeriod(EPWM1_BASE, PWM_TBPRD); EPWM_setTimeBaseCounter(EPWM1_BASE, 0); EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP_DOWN); // 2. 占空比比较值这里给50%实际按控制需求修改 EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_A, 2500); // 3. 事件触发TBCTR CMPC 时产生SOCB EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_C, PWM_CMPC_VALUE); EPWM_setADCTriggerSource(EPWM1_BASE, EPWM_SOC_B, EPWM_SOC_TRIGGER_SOURCE_COUNTER_COMPARE_C); EPWM_setADCTriggerEventPrescale(EPWM1_BASE, EPWM_SOC_B, EPWM_SOC_TRIGGER_EVENT_PRESCALE_1); EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_B); }这段代码的核心是第三步CMPC1250意味着在UP-DOWN计数的上升段当计数器到达1250时产生SOCB。这个脉冲就是SDFM的同步基准。选择1250而不是5000是为了给后面DATAPOS留出调整空间方便把采样点放在周期中段。如果你不输出PWM波只是想生成同步信号可以不用配action qualifier但建议保留时基和SOCB配置。我调试初期就是这么干的先把同步链路跑通再接功率级。4.3 SDFM初始化代码SDFM的初始化分为四步模块级配置、滤波器参数配置、同步源选择、使能。下面是完整代码void initSDFM1_ForCurrentSense(void) { // 1. 模块级初始化 SDFM_initModule(SDFM1_BASE); // 2. 滤波器1SINC3 OSR32 SDFM_setFilterMode(SDFM1_BASE, SDFM_FILTER_1, SDFM_FILTER_MODE_SINC3); SDFM_setFilterOversampling(SDFM1_BASE, SDFM_FILTER_1, SDFM_FILTER_OSR_32); // 3. 数据位置在同步基准后增加偏移避开死区和尖峰 SDFM_setFilterDataPosition(SDFM1_BASE, SDFM_FILTER_1, 350); // 4. 同步源选择ePWM1的SOCB // 注意不同C2000Ware版本的函数名有差异以头文件为准 SDFM_setSyncSource(SDFM1_BASE, SDFM_FILTER_1, SDFM_SYNC_SOURCE_EPWM1_SOCB); // 5. 使能滤波器1 SDFM_enableFilter(SDFM1_BASE, SDFM_FILTER_1); }这里我特别强调一下第3步的350这个值是怎么来的。10kHz PWM周期100usCMPC1250对应的时刻是载波从0往上走的第12.5us处。DATAPOS350如果模块时钟10ns一位就是再往后3.5us。这样做是为了确保采样点落在CMPC之后的3.5us处而这个位置离最近的开关动作边沿有足够距离。不同SDK版本里SDFM_setSyncSource这个函数名可能不太一样。我自己换过C2000Ware版本遇到过函数被重命名或者参数结构变化的情况。遇到编译报错最直接的办法就是打开C2000Ware安装目录下的sdfm.h头文件查一下当前版本里同步源配置的API长什么样。别硬背函数名这属于正常迭代。4.4 中断与数据读取方式SDFM的数据寄存器有两种读取路径高速滤波器和低速滤波器。电流环要的是每个PWM周期更新一次的瞬时值所以用高速滤波器输出SDDATAHP1。数据就绪的判断可以用中断也可以用轮询。中断适合控制周期规律性强的情况轮询则更简单直接。我调试阶段建议先用轮询减少中断优先级带来的干扰因素// 在控制任务里调用读取最新的SDFM高速滤波值 uint16_t readSDFM1Filter1Raw(void) { // 等待数据就绪标志SDDATAHP寄存器读取后会自动清除标志 while((HWREG(SDFM1_BASE SDFM_O_SDFMIFLG) SDFM_SDFMIFLG_DDATAIF1) 0) { } return (uint16_t)HWREG(SDFM1_BASE SDFM_O_SDDATAHP1); }读取出来的16位原始值需要换算成实际电流。以AMC1306为例它输出的是±250mV对应满量程的位流。换算公式大致是I (int16_t)rawValue / 32767.0 * I_measure_range其中I_measure_range由采样电阻阻值和调制器输入范围共同决定。比如你用0.5mΩ采样电阻流过50A满量程产生25mV压降那么I_measure_range就是按这个关系算出来的实际电流满量程。5. 实测中的验证与踩坑记录配置写完不算完上板实测才是检验同步配置是否正确的唯一标准。这一节我把自己踩过的坑和验证方法整理出来这些经验在TI的官方例程里基本找不到。5.1 用电流波形抽丝判断同步是否成功一个非常有效的调试方法固定PWM占空比让电机不转或堵转然后连续读取SDFM的输出值通过CCS的Graph工具把数据数组显示出来。如果同步配置正确采到的电流值应该是一条平滑的直线或者只有非常小的随机抖动。如果采样点落在了开关边沿上你会看到波形上有规律性的尖刺而且尖刺频率和PWM频率一致——这就是我说的锯齿形电流的来源。有一次我为了验证同步点是不是真的避开了噪声故意把DATAPOS设成0结果波形立刻出现大量尖刺然后把DATAPOS调到经验值尖刺几乎消失。这个对比实验非常直观也适合用来教团队里的新人理解DATAPOS的作用。5.2 OSR太大导致响应迟钝、太小导致纹波大有个项目为了追求分辨率把OSR设成了128结果电流环带宽怎么提都提不上去电机高速运行时机身发烫。后来算了一下OSR128配合SINC3群延迟已经到好几微秒对电流环来说相当于在反馈通道里加了一个大滞后相位裕度直接被吃掉。反过来另一个项目把OSR压到8纹波又大得无法接受。这两个项目最终都落在OSR24~32这个区间。我的习惯是先按OSR32起步如果纹波能接受就尽量不往下调如果带宽不够再试着降到24同时用更高阶的SINC3来弥补纹波性能。OSR的调整不是孤立的它会直接影响群延迟、输出数据和噪声性能改完之后必须重新检查采样点位置和电流环参数不能只改一个数就完事。5.3 DATAPOS与死区时间的躲猫猫这可能是最容易被忽略的坑。很多人把死区时间当作PWM输出延迟觉得反正就2us影响不大。但在SDFM采样里死区时间内电流并不流过采样电阻或分流器而是通过续流二极管走。这个时候采到的相电流根本就不是真实的相电流而是某种寄生路径上的未知电流。换句话说采样点不仅要在开关边沿之后还必须在死区时间结束之后。否则你采到的值既不准又没有重复性电流环稍微跑起来就会抖动。我在一块板子上就踩过这个坑DATAPOS设好后静态波形很漂亮但负载一上去电流却有明显波动。最后用示波器对比了PWM驱动波形和SDFM的DR引脚输出发现采样点刚好落在死区时间结束前一点点。把DATAPOS往后推了1us问题立刻消失。5.4 不同芯片系列寄存器名与Driverlib API的差异F2837xD和F28004x都是C2000家族但SDFM的寄存器布局和Driverlib API并不是完全统一的。比如F28004x的SDFM在同步源配置上可选信号源列表就和F2837x不完全一样某些寄存器位段的名字也变了。我的建议是拿到一颗新芯片先不要急着复制之前的代码。打开对应型号的TRM找到SDFM章节重点看三个地方SDFMMCTL的SSF位段、SDCTL的PWM同步模式位、SDDATPARM的DATAPOS位宽。这三个地方是同步配置最容易出差异的地方。另外C2000Ware版本升级经常会导致Driverlib函数名或者结构体定义变化这种时候不要硬扛直接查当前版本的头文件和迁移指南。我见过太多人因为API变了就卡在那里实际上把函数签名校对一遍五分钟就解决了。6. 写在最后的两个小建议如果你刚开始做这件事我建议先用最简单的配置跑通内部SOCB同步源、SINC3、OSR32、DATAPOS先给一个保守值比如PWM周期的20%处。跑通之后再用示波器、Graph工具去验证采样点位置一步步优化。不要一开始就上极端参数否则出了问题很难定位是同步的问题还是硬件的问题。另外调试同步问题时优先确认ePWM的CMPC和SDFM的数据就绪标志是否和PWM周期严格对齐。如果SDFM的数据就绪标志出现周期性漂移或者间歇性缺失多半是同步源没选对或者滤波器配置处于非同步模式。先把这条链路彻底整明白了后续的电流环、速度环才谈得上稳定。