
去年调一台伺服驱动器主控用的TMS320F2837xD三相电流采样走的是 AMC1306 加 SDFM 的方案。空载调试一切正常一带负载电流环的噪声就莫名其妙往上冒更诡异的是噪声不是均匀的白噪声而是有节奏的“台阶”。当时怀疑过布局布线、怀疑过隔离电源纹波折腾了好几天最后才发现根子在 ePWM 和 SDFM 的同步关系上——SDCLK 和 PWM 载波没有严格对齐SDFM 中断里读到的数据窗口在开关周期里“漂”。把同步链路理顺之后问题直接消失。今天就把 C2000 上 ePWM 与 SDFM 同步配置这件事从寄存器到 Driverlib 完整拆开讲一遍给做数字电源、伺服驱动和逆变器采样的朋友一个可以直接落地的参考。内容会涉及 SDFM 解调原理、时钟链路计算、关键寄存器字段、Driverlib API 对应的底层操作以及我实测踩过的几个坑。文章以 TMS320F2837xD 为例F28004x 等型号外设结构类似寄存器名和库函数名可能会有些差异但思路完全可以平移。1. 为什么 SDFM 必须和 ePWM“绑”在一起1.1 SDFM 解调的本质是“滑动平均”很多人第一次接触 SDFM 时都把它当成一个普通的 ADC 外设来用初始化几个滤波器配好中断然后读数据。这个思路没大问题但容易忽略一点——SDFM 输出的每一个数据并不是某一瞬间的瞬时值而是对一段时间内位流做加权平均的结果。SDFM 输入的是 Δ-Σ 调制器输出的串行位流比如 AMC1306 输出的就是 1bit 脉冲密度调制信号。SDFM 内部用 SINC 滤波器对这个位流做抽取和解调。SINC 滤波器的阶数不同等效的“平均窗口”形状也不同SINC1 相当于矩形窗滑动平均SINC3 则是一个更接近三角形的加权窗对带内噪声的抑制更好但群延迟也更大。这个平均窗口的位置是由 SDCLK 的相位决定的。SINC 滤波器每个输出点都是在固定的 SDCLK 周期序列上累计出来的。换句话说SDCLK 上升沿发生在什么时刻滤波器“看”到的时间片段就落在什么位置。这样一来问题就来了功率级的 PWM 开关动作会产生周期性的电压电流纹波如果 SDFM 的平均窗口与 PWM 开关点之间的相对位置不固定每次读到的数据里叠加的纹波残差就不一样。单独看一次可能感觉不到但把一串数据连续看下来噪底就上去了。1.2 不绑定的后果噪底升高和“台阶”我自己在调试时遇到的现象特别典型。把 SDFM 输出的电流数据用 DA 输出到示波器上观察能看到周期性的阶跃——不是毛刺而是持续几十个采样点之后突然跳一下然后又慢慢回来。这是因为 SDCLK 和 PWM 载波各自独立它们的相位关系在缓慢漂移SDFM 的平均窗口相对开关纹波的位置在周期性扫动。如果 SDCLK 直接来自外部晶振或者来自没有与主 PWM 同步的 ePWM 模块这种漂移一定存在。漂移周期取决于两个时钟源的频率差可能是个很长的周期导致问题在空载时不明显带载后纹波增大才暴露出来。还有一种是读取时机问题。SDFM 滤波器输出不是每时每刻都在更新它是按抽取率周期性地“吐”一个点。如果你在 PWM 中断里读取时恰好赶上滤波器输出正在切换那读到的值可能是上一拍和这一拍的中间状态表现出来就是偶发的采样台阶。这两个问题叠加在一起排查起来就特别迷惑。1.3 同步配置的三个层次要把 ePWM 和 SDFM 真正同步起来得从三个层面同时下手层次要解决的问题实现手段时钟层SDCLK 必须与 PWM 时基同源用 ePWM 模块生成 SDCLK 信号或使用同一 PLL 分频出来的时钟相位层SDCLK 的相位与 PWM 载波对齐通过 ePWM 同步链SYNCI/SYNCO和相位寄存器 TBPHS 对齐读取层读 SDFM 数据的时刻要固定在 ePWM 时基中断里读避免随意轮询必要时对滤波器做同步复位这三个层次缺一不可。很多时候大家配了时钟、开了中断但忘了相位对齐或者光对齐了相位但读取时机没固定问题照样存在。2. 从 SDCLK 生成到相位对齐信号链路完整拆解2.1 SDFM 输入侧的工作方式先理一下 SDFM 输入端口的角色。以 F2837xD 的 SDFM1 模块为例每个通道有对应的数据输入引脚和时钟输入引脚。外部 Δ-Σ 调制器比如 AMC1306需要 MCU 给它一个时钟信号这个时钟一般叫 CLKIN调制器在时钟驱动下输出串行数据 DOUTDOUT 再回到 MCU 的 SDFM 数据引脚。AMC1306 的典型时钟频率是 5MHz 到 20MHz。时钟频率越高位流速率越快SINC 滤波器用相同的抽取率时输出数据率也越高但开关噪声耦合也更敏感。频率选多少取决于系统对采样延迟的要求和 PCB 布线质量。SDFM 的数据控制寄存器里可以配置数据采样的延迟DLYCTL 字段。这个字段存在的意义是补偿外部因素带来的时钟到数据的相位差——比如 SDCLK 经过 PCB 走线到 AMC1306AMC1306 内部处理后再输出 DOUTDOUT 回到 MCU 时已经比 SDCLK 晚了一段时间。配置数据延迟是为了让 SDFM 在正确的时间点采样数据位流避免采到边沿附近的不稳定状态。2.2 用独立 ePWM 模块生成 SDCLK要让 SDCLK 和主 PWM 严格同步最直接的做法是用主 PWM 模块作为时间基准另用一个 ePWM 模块专门产生 SDCLK 方波信号。不要尝试从主 PWM 的通道里强行引出一个时钟主 PWM 要处理占空比、死区、比较动作逻辑复杂稍有不慎就影响功率级输出。假设系统 TBCLK 是 100MHz主 PWM 开关频率 10kHz使用 up-down 计数模式那么主 PWM 的 TBPRD 计算如下TBPRD TBCLK / (2 * fPWM) 100MHz / (2 * 10kHz) 5000SDCLK 目标 10MHz使用 up-count 模式。up-count 模式下计数器从 0 递增到 TBPRD然后回到 0。一个完整的输出周期需要 10 个 TBCLK 周期也就是 100ns。如果配置在 ZERO 事件输出低电平在 PERIOD 事件输出高电平那么高电平时间恰好是半个周期得到 50% 占空比的方波TBPRD TBCLK / (2 * fSDCLK) - 1 100MHz / (2 * 10MHz) - 1 4注意别算错了。我最初用 up-down 模式去生成 SDCLK结果频率整整差了一倍排查了半天才发现是计数模式的问题。up-count 模式下TBPRD4 时计数器序列是 0、1、2、3、4共 5 个 TBCLK 周期高低电平各占一半一个完整方波周期 10 个 TBCLK正好 10MHz。如果沿用 up-down 模式同样 TBPRD4周期变成 9 个或 18 个 TBCLK计算基准完全不同。2.3 相位对齐的关键SYNCI/SYNCO 级联与 TBPHS 加载时钟频率算对之后下一步是相位对齐。C2000 的 ePWM 模块有一个专门的同步机制主模块在指定事件计数器等于 ZERO 或等于 PERIOD时发出 SYNC 信号通过内部的同步链传到下一个 ePWM 模块的 SYNCI 输入从模块收到同步信号后会把 TBPHS 寄存器的值加载到 TBCTR 计数器中从而实现相位对齐。配置上要做这几件事主 PWM比如 EPWM1设置同步输出源为 PERIOD 事件这样每个 PWM 周期开始时会发出一次同步脉冲从模块比如 EPWM2用于生成 SDCLK的同步输入源选择前一模块的同步信号从模块使能相位加载PHSEN并设置 TBPHS 为期望的相位偏移通常设为 0让 SDCLK 的 ZERO 时刻和主 PWM 的 PERIOD 事件对齐。TBPHS 单位是 TBCLK 周期数。如果想让 SDCLK 的上升沿比主 PWM 的匹配点提前或滞后几个 ns就调整这个值。实际操作中可以先设为 0 跑起来再用示波器看 SDCLK 和 PWM 波形的相对位置按需微调。还有一点容易被忽略ePWM 模块上电后时基计数器需要先通过 TBCLKSYNC 机制统一启动。如果各模块的 TBCLKSYNC 没有同时使能即使同步链路配置正确计数器初始启动时刻也可能有偏差。TI 的 driverlib 例程里通常会在初始化最后统一使能 TBCLKSYNC这个顺序不要打乱。2.4 信号完整性上的几个注意点SDCLK 信号虽然看起来只是个方波但它是整个采样链路的时间基准波形质量直接影响 SDFM 的采样稳定性。几个实测经验SDCLK 走线尽量短和 DOUT 数据线保持等长避免数据和时钟之间的 skew 过大如果 SDCLK 频率超过 10MHz建议在靠近 AMC1306 的位置加一级缓冲驱动能力不足会导致上升沿变缓SDCLK 频率不要无脑拉高超过调制器规格上限AMC1306 是 20MHz 左右会直接导致数据错误数据延迟 DLYCTL 需要配合实际布线情况设置不要照抄别人的参数。3. 寄存器级配置拆解3.1 ePWM 侧的关键寄存器直接操作寄存器时最常用到的 ePWM 字段集中在几个寄存器里。TBCTLTime-Base Control Register是最核心的控制寄存器PHSEN 位控制是否在收到同步信号时加载 TBPHS。同步链里从模块必须置 1SYNCOSEL 字段选择同步输出源。可选值包括禁用同步输出、CTRZERO 事件、CTRPERIOD 事件、以及比较事件。主模块一般选 CTRPERIOD 或 CTRZEROPRDLD 位控制 TBPRD 的加载方式是立即生效还是等 CTRZERO 时生效。运行中修改周期时用得上静态配置无所谓CTRMODE 字段时基计数模式包括 up、down、up-down 三种CLKDIV 和 HSPCLKDIV 字段时基时钟分频决定 TBCLK 的实际频率。TBPRD 是周期寄存器。计算方式前面已经给了公式注意 up 和 up-down 模式下的公式不同。TBPHS 是相位寄存器。从模块收到同步信号后TBPHS 的值会加载到 TBCTR 中完成相位搬移。不少人在这个寄存器上踩坑TBPHS 写进去了但忘了置位 PHSEN导致怎么改都不生效。AQCTLA 是输出动作限定寄存器。它定义了计数器等于不同事件时ZERO、PERIOD、CMPA 向上匹配、CMPA 向下匹配等PWM 输出是高电平还是低电平。生成 SDCLK 方波时只需要用 ZERO 和 PERIOD 两个事件就能凑出 50% 占空比。3.2 SDFM 侧的关键寄存器SDFM 模块的寄存器在 TRM 里叫法比较统一主要有这几类。SDFMCTL 是模块级控制寄存器负责模块软复位、通道使能等基础操作。初始化 SDFM 时第一件事就是写这个寄存器把模块从复位状态释放出来。我见过有人配完了所有滤波器但忘了使能模块结果数据寄存器永远都是 0排查了半天才发现是模块整体没使能。SDCTL 是数据控制寄存器包含必须配置的几个字段SDCLK_DIVSDCLK 分频系数。如果外部时钟 10MHz 进入内部还可以继续分频DLYCTL数据采样延迟补偿时钟到数据的传输延迟DATA_MODE数据模式可选 1bit 或 4bit 模式。AMC1306 这类单比特调制器用 1bitCS0/CS1 等片选字段多路复用场景下用到。SDFLTCTL 是滤波器控制寄存器每个滤波器单元SDFLT1 到 SDFLT4都有独立的一份FILTER_MODE选择 SINC0、SINC1、SINC2 还是 SINC3 滤波器。常规电流采样用 SINC3响应和噪声抑制最均衡OSR抽取率即每多少个 SDCLK 周期输出一个数据点。可配置 1 到 32 之间的值SHIFT输出移位避免滤波器输出饱和。SINC3 和 OSR 较大时原始输出位数会超出 16bit 寄存器范围需要右移若干位。SDDATA 是数据寄存器滤波器解调完成后输出值放在这里CPU 或 CLA 直接读取。还有中断相关寄存器。SDFM 每个滤波器单元可以产生 EOCEnd of Conversion中断EOC 频率等于 SDCLK 频率除以 OSR。中断标志位需要软件清除配置时留意对应寄存器。3.3 EALLOW 保护与初始化顺序C2000 上有一部分寄存器需要先执行 EALLOW 指令才能写典型的是系统控制寄存器、GPIO 控制寄存器、PIE 配置寄存器。SDFM 和 ePWM 的大部分控制寄存器不在保护名单里可以直接写但有一个例外需要留意ePWM 时基时钟的 TBCLKSYNC 位位于系统控制寄存器中受 EALLOW 保护。推荐的初始化顺序是配置系统时钟和 PLL确认 TBCLK 频率EALLOW关闭 TBCLKSYNC让所有 ePWM 时基停止配置 ePWM1 和 ePWM2 的 TBCTL、TBPRD、TBPHS、AQCTLA配置 SDFM 的 SDFMCTL、SDCTL、SDFLTCTL使能滤波器配置 PIE 中断再次 EALLOW开启 TBCLKSYNC让所有时基同时起振。这个顺序保证所有外设初始化完成后时基才统一开始运行。如果顺序反过来比如先开启时基再配寄存器可能会出现时基已经跑了一段、相位基准错位的情况。4. Driverlib 对照每一行 API 背后动了哪些寄存器4.1 ePWM 侧 Driverlib 与寄存器映射手写寄存器最大的问题是可读性差而且容易漏配。Driverlib 的价值在于把一组相关寄存器操作封装成语义明确的函数同时几乎不损失性能——这些函数大多是内联的编译后就是几条写寄存器指令。Driverlib API对应寄存器/位作用EPWM_setTimeBasePeriod(EPWM1_BASE, 5000)TBPRD设置时基周期EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP_DOWN)TBCTL.CTRMODE设置计数模式EPWM_setSyncOutPulseMode(EPWM1_BASE, EPWM_SYNC_OUT_PULSE_ON_PERIOD)TBCTL.SYNCOSEL设置同步输出源EPWM_setSyncInPulseSource(EPWM2_BASE, EPWM_SYNC_IN_PULSE_SRC_SYNCHRONIZER)TBCTL.SYNCIN设置同步输入源EPWM_setPhaseShift(EPWM2_BASE, 0)TBPHS设置相位偏移EPWM_enablePhaseShiftLoad(EPWM2_BASE)TBCTL.PHSEN使能相位加载EPWM_setActionQualifierAction(EPWM2_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_ACTION_ON_PERIOD)AQCTLAPERIOD 事件置高EPWM_setActionQualifierAction(EPWM2_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_ACTION_ON_ZERO)AQCTLAZERO 事件置低EPWM_setInterruptSource(EPWM1_BASE, EPWM_INT_SOURCE_PERIOD)ETSEL中断源选择EPWM_setInterruptEventCount(EPWM1_BASE, 1)ETPS事件分频我最初从寄存器切换到 Driverlib 时总觉得中间隔了一层心里不踏实后来把映射关系逐个梳理了一遍发现 API 名字和寄存器字段是对得上的调试时看寄存器窗口就能验证配置是否正确。4.2 SDFM 侧 Driverlib 映射表SDFM 的 Driverlib 封装类似核心几个函数如下Driverlib API对应寄存器/位作用SDFM_initController(SDFM1_BASE)SDFMCTL模块初始化与复位释放SDFM_configDataCtrl(SDFM1_BASE, SDFM1_CH0, delay, mode, cs)SDCTL配置数据延迟、数据模式、片选SDFM_configFilterMode(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1, SDFM_FILTER_SINC3)SDFLTCTL.FILTER_MODE选择滤波器类型SDFM_setFilterShift(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1, SDFM_SHIFT_5)SDFLTCTL.SHIFT配置输出移位SDFM_setFilterOsr(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1, osr)SDFLTCTL.OSR配置抽取率SDFM_enableFilter(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1)SDFLTCTL.FILTER_EN使能滤波器SDFM_readFilterData(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1)SDDATA读取滤波结果SDFM_enableInterrupt(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1)SDFMITCTL使能 EOC 中断使用 Driverlib 时有一点要注意C2000Ware 版本更新很快同一函数的参数定义在不同版本里可能有调整。比如SDFM_setFilterOsr的第二个参数有的版本接收枚举值有的版本直接接收整数。碰到编译报错优先查当前 SDK 的头文件不要死记硬背。4.3 一个可以直接抄的同步配置代码下面这段是基于 Driverlib 的完整配置流程假设 TBCLK 100MHz主 PWM 10kHzSDCLK 10MHzSDFM 使用 SINC3 滤波器抽取率 16为了演示数据率更直观。#include driverlib.h #include board.h void initEPWM(void) { // ---- 关闭时基同步启动先配置后统一起振 ---- EALLOW; CpuSysRegs.PCLKCR0.bit.TBCLKSYNC 0; EDIS; // ---- EPWM1主功率 PWMup-down 模式10kHz ---- EPWM_setTimeBasePeriod(EPWM1_BASE, 5000); EPWM_setTimeBaseCounter(EPWM1_BASE, 0); EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP_DOWN); EPWM_setSyncOutPulseMode(EPWM1_BASE, EPWM_SYNC_OUT_PULSE_ON_PERIOD); EPWM_setClockPrescaler(EPWM1_BASE, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1); // EPWM1A 输出中心对齐 50% 占空比 EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_A, 2500); EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_ACTION_ON_UP); EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_ACTION_ON_DOWN); // EPWM1 周期中断用于控制环同步读取 EPWM_setInterruptSource(EPWM1_BASE, EPWM_INT_SOURCE_PERIOD); EPWM_setInterruptEventCount(EPWM1_BASE, 1); EPWM_enableInterrupt(EPWM1_BASE); // ---- EPWM2SDCLK 生成器up 模式10MHz ---- EPWM_setTimeBasePeriod(EPWM2_BASE, 4); EPWM_setTimeBaseCounter(EPWM2_BASE, 0); EPWM_setTimeBaseCounterMode(EPWM2_BASE, EPWM_COUNTER_MODE_UP); EPWM_setSyncInPulseSource(EPWM2_BASE, EPWM_SYNC_IN_PULSE_SRC_SYNCHRONIZER); EPWM_setPhaseShift(EPWM2_BASE, 0); EPWM_enablePhaseShiftLoad(EPWM2_BASE); // EPWM2A 输出 SDCLKZERO 低、PERIOD 高50% 占空比 EPWM_setActionQualifierAction(EPWM2_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_ACTION_ON_ZERO); EPWM_setActionQualifierAction(EPWM2_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_ACTION_ON_PERIOD); // ---- 统一启动时基 ---- EALLOW; CpuSysRegs.PCLKCR0.bit.TBCLKSYNC 1; EDIS; } void initSDFM(void) { // SDFM1 通道0 SDFM_initController(SDFM1_BASE); SDFM_configDataCtrl(SDFM1_BASE, SDFM1_CH0, SDFM_DATA_CTL_DELAY_1, // 数据延迟1个SDCLK SDFM_DATA_CTL_MODE_1BIT, // AMC1306是1bit调制器 SDFM_DATA_CTL_CS0); // 滤波器1SINC3抽取率16输出移位使结果落在16bit有效范围 SDFM_configFilterMode(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1, SDFM_FILTER_SINC3); SDFM_setFilterOsr(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1, 16); SDFM_setFilterShift(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1, SDFM_SHIFT_5); SDFM_enableFilter(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1); // 使能 EOC 中断可选 SDFM_enableInterrupt(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1); } __interrupt void epwm1ISR(void) { // 读取SDFM最新滤波结果 volatile uint16_t current 0; current SDFM_readFilterData(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1); // 放入控制环专用全局变量 gCurrentSample current; EPWM_clearInterruptStatus(EPWM1_BASE); // 清除PIE应答 PieCtrlRegs.PIEACK.all PIEACK_GROUP3; }这段代码对应的寄存器操作把第 3 节里讲到 TBCTL、TBPHS、AQCTLA、SDCTL、SDFLTCTL 全部覆盖到了。建议编译后先在寄存器窗口把关键字段逐个核对一遍确认和预期一致再跑硬件。4.4 Driverlib 与寄存器混用的建议我不建议在同一工程里混用 Driverlib 和直接寄存器访问。一方面代码风格混乱另一方面容易踩 EALLOW 保护的隐藏坑。比如你直接写CpuSysRegs.PCLKCR0.bit.TBCLKSYNC 1时忘了 EALLOW这条语句根本不会生效但 Driverlib 里对应的函数已经帮你处理了保护逻辑。如果必须混用至少在关键配置段加上明确的注释标明哪些寄存器需要 EALLOW哪些不需要。调试时一旦发现“配置不生效”第一反应应该是检查保护位而不是怀疑硬件坏了。5. 实测最容易翻车的四个坑与完整排查链路5.1 数据全零时钟极性还是使能时序SDFM 初始化完成后如果读到的数据恒为零先不要怀疑芯片坏了。按这个顺序排查示波器量 SDCLK 引脚确认有时钟且频率符合预期。如果没波形查 ePWM2 是否真的在输出查 GPIO 复用配置量 AMC1306 的 DOUT 引脚看有没有位流输出。位流的特点是密集的窄脉冲串不是稳定电平量 SDFM 的 SD_D 引脚确认信号到达 MCU在调试器里看 SDFMCTL确认模块已经退出复位且通道使能看 SDFLTCTL 的 FILTER_EN 位确认滤波器真的使能了检查 SDCTL 的 DLYCTL 设置。数据延迟设得不对可能每次都采到数据位流的无效区域输出会长期为零或随机小值。我之前遇到过最隐蔽的一例SDCLK 波形正常AMC1306 输出也正常SDFM 寄存器配置看起来都对最后发现是 GPIO 的输入限定配置把 SD_D 信号给滤掉了。GPIO 输入限定如果设置了较长的采样窗口高频位流被当成毛刺滤掉数据自然全零。解决方法是把 SDFM 相关 GPIO 的输入限定配置为同步模式或关闭限定。5.2 偶发台阶波读取时机和 EOC 对齐数据不是全零但波形上出现偶发台阶大概率是读取时机问题。SINC 滤波器输出更新是一个离散过程每个 EOC 周期更新一次。如果你在 PWM 中断里读取时恰好和滤波器输出更新撞在一起硬件读到的可能是跨周期的混合值。解决思路有两个方向。第一个是“足够晚地读”。统计一下 EOC 相对 PWM 同步点的相位把读取时刻安排在 EOC 之后、下一个 EOC 之前。只要 PWM 中断点离 EOC 更新点足够远读到的一定是稳定后的输出。第二个思路更稳妥在 PWM 中断里对滤波器做一次同步复位把滤波器输出更新的相位和 PWM 周期绑死。具体操作是在每个控制周期开始时先禁用滤波器再立即使能滤波器。这样滤波器重新开始累计经过固定的 OSR 个 SDCLK 周期后EOC 会按时基周期的固定位置产生后续读取就完全确定。// 在PWM周期中断里做滤波器同步复位 SDFM_disableFilter(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1); SDFM_enableFilter(SDFM1_BASE, SDFM1_CH0, SDFM_FILTER1);注意这样做之后重新使能滤波器的前几个输出样本是不稳定的需要在软件里做一个“丢弃前 N 个样本”的状态机。N 的取值跟滤波器阶数和抽取率相关SINC3 加 OSR16 时大概丢弃 3 到 5 个样本就够。5.3 EALLOW 和初始化顺序导致的“配置不生效”直接操作寄存器时最让人抓狂的问题就是“寄存器明明写了调试器看也是对的但外设就是不动”。这八成是 EALLOW 保护或者初始化顺序的问题。ePWM 的很多同步配置位比如 SYNCOSEL、PHSEN本身不受 EALLOW 保护但如果你先启动了时基再去配置这些位可能已经晚了。同步信号已经在时基启动的第一个周期就发出去了你后续的配置要等到下一个周期才生效甚至因为相位错误而完全错过同步窗口。SDFM 也有类似的顺序问题。滤波器使能之前必须先配置好 SDCTL 和 SDFLTCTL。如果先使能了滤波器再去改抽取率有些型号的 SINC 滤波器不会立即响应新配置必须重启滤波器才能加载新参数。养成好习惯先配置全部参数最后统一使能。5.4 抽取率与 PWM 频率不匹配的量化分析SDFM 的输出数据率等于 SDCLK 除以抽取率但这不是唯一的约束条件。更重要的是 SINC 滤波器的群延迟。SINC3 滤波器的群延迟大约等于 3 * OSR / 2 个 SDCLK 周期这段时间内滤波器输出的数据对应的其实是“过去”的电流值。如果群延迟超过 PWM 周期的一半控制环路的相位裕量会受到明显影响。举个例子SDCLK10MHzOSR32SDFM 输出数据率约 312.5kSPSSINC3 群延迟约 4.8us。对于 10kHz 的 PWM 周期100us来说4.8us 大约占周期的 5%控制环还能接受。但如果 SDCLK 降到 2.5MHzOSR 还是 32输出数据率只有 78.125kSPS群延迟达到 19.2us占周期的近 20%这时候电流环的带宽和稳定性就很难做了。SDCLKOSR输出数据率SINC3 群延迟10kHz PWM 周期占比10MHz16625kSPS2.4us2.4%10MHz32312.5kSPS4.8us4.8%5MHz32156.25kSPS9.6us9.6%2.5MHz6439.06kSPS38.4us38.4%最后一个配置已经不适合做电流环采样了。OSR 也不是越低越好OSR 太低时 SINC 滤波器的抗混叠能力变差开关噪声会直接折返进基带。经验上数字电源和电机控制的电流采样SINC3 加 OSR 16 到 64 是常用区间具体选多少要看系统对噪声和延时的权衡。我在实际项目里一般先用 OSR32 起步跑通之后用加持噪声对比不同 OSR 下的电流波形再决定往 16 还是 64 调。这个方法比纸上谈兵可靠得多因为 PCB 布局和功率级的寄生参数最终都会反映在噪声里只有实测数据能告诉你答案。最后再分享一个小技巧。C2000Ware 自带的 SDFM 例程里通常有完整的初始化函数和滤波器配置示例不要自己从零开始写而是在例程基础上替换掉引脚配置和同步相关的部分。省下来的时间去画时钟路径图把 SDCLK 从哪个 ePWM 来、经过哪些引脚、SDFM 数据延迟配多少、中断从哪里触发一张图画清楚比你对着寄存器手册啃半天更有效。我后来在 F28004x 上做第二版时就是把这张图直接贴到设计文档里硬件工程师照着布线一次打板回来采样就通了。