☰
C2000 ePWM与SDFM同步配置:从寄存器到Driverlib完整解析
2026/10/5 1:02:02 网站建设 项目流程

去年调一台伺服驱动器,主控用的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,调制器在时钟驱动下输出串行数据 DOUT,DOUT 再回到 MCU 的 SDFM 数据引脚。

AMC1306 的典型时钟频率是 5MHz 到 20MHz。时钟频率越高,位流速率越快,SINC 滤波器用相同的抽取率时,输出数据率也越高,但开关噪声耦合也更敏感。频率选多少,取决于系统对采样延迟的要求和 PCB 布线质量。

SDFM 的数据控制寄存器里,可以配置数据采样的延迟(DLYCTL 字段)。这个字段存在的意义是补偿外部因素带来的时钟到数据的相位差——比如 SDCLK 经过 PCB 走线到 AMC1306,AMC1306 内部处理后再输出 DOUT,DOUT 回到 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) = 5000

SDCLK 目标 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 模式下,TBPRD=4 时计数器序列是 0、1、2、3、4,共 5 个 TBCLK 周期,高低电平各占一半,一个完整方波周期 10 个 TBCLK,正好 10MHz。如果沿用 up-down 模式,同样 TBPRD=4,周期变成 9 个或 18 个 TBCLK,计算基准完全不同。

2.3 相位对齐的关键:SYNCI/SYNCO 级联与 TBPHS 加载

时钟频率算对之后,下一步是相位对齐。C2000 的 ePWM 模块有一个专门的同步机制:主模块在指定事件(计数器等于 ZERO 或等于 PERIOD)时发出 SYNC 信号,通过内部的同步链传到下一个 ePWM 模块的 SYNCI 输入;从模块收到同步信号后,会把 TBPHS 寄存器的值加载到 TBCTR 计数器中,从而实现相位对齐。

配置上要做这几件事:

  1. 主 PWM(比如 EPWM1)设置同步输出源为 PERIOD 事件,这样每个 PWM 周期开始时会发出一次同步脉冲;
  2. 从模块(比如 EPWM2,用于生成 SDCLK)的同步输入源选择前一模块的同步信号;
  3. 从模块使能相位加载(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 字段集中在几个寄存器里。

TBCTL(Time-Base Control Register)是最核心的控制寄存器:

  • PHSEN 位:控制是否在收到同步信号时加载 TBPHS。同步链里从模块必须置 1;
  • SYNCOSEL 字段:选择同步输出源。可选值包括禁用同步输出、CTR=ZERO 事件、CTR=PERIOD 事件、以及比较事件。主模块一般选 CTR=PERIOD 或 CTR=ZERO;
  • PRDLD 位:控制 TBPRD 的加载方式,是立即生效还是等 CTR=ZERO 时生效。运行中修改周期时用得上,静态配置无所谓;
  • 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_DIV:SDCLK 分频系数。如果外部时钟 10MHz 进入,内部还可以继续分频;
  • DLYCTL:数据采样延迟,补偿时钟到数据的传输延迟;
  • DATA_MODE:数据模式,可选 1bit 或 4bit 模式。AMC1306 这类单比特调制器用 1bit;
  • CS0/CS1 等片选字段,多路复用场景下用到。

SDFLTCTL 是滤波器控制寄存器,每个滤波器单元(SDFLT1 到 SDFLT4)都有独立的一份:

  • FILTER_MODE:选择 SINC0、SINC1、SINC2 还是 SINC3 滤波器。常规电流采样用 SINC3,响应和噪声抑制最均衡;
  • OSR:抽取率,即每多少个 SDCLK 周期输出一个数据点。可配置 1 到 32 之间的值;
  • SHIFT:输出移位,避免滤波器输出饱和。SINC3 和 OSR 较大时,原始输出位数会超出 16bit 寄存器范围,需要右移若干位。

SDDATA 是数据寄存器,滤波器解调完成后输出值放在这里,CPU 或 CLA 直接读取。

还有中断相关寄存器。SDFM 每个滤波器单元可以产生 EOC(End of Conversion)中断,EOC 频率等于 SDCLK 频率除以 OSR。中断标志位需要软件清除,配置时留意对应寄存器。

3.3 EALLOW 保护与初始化顺序

C2000 上有一部分寄存器需要先执行 EALLOW 指令才能写,典型的是系统控制寄存器、GPIO 控制寄存器、PIE 配置寄存器。SDFM 和 ePWM 的大部分控制寄存器不在保护名单里,可以直接写,但有一个例外需要留意:ePWM 时基时钟的 TBCLKSYNC 位位于系统控制寄存器中,受 EALLOW 保护。

推荐的初始化顺序是:

  1. 配置系统时钟和 PLL,确认 TBCLK 频率;
  2. EALLOW,关闭 TBCLKSYNC,让所有 ePWM 时基停止;
  3. 配置 ePWM1 和 ePWM2 的 TBCTL、TBPRD、TBPHS、AQCTLA;
  4. 配置 SDFM 的 SDFMCTL、SDCTL、SDFLTCTL,使能滤波器;
  5. 配置 PIE 中断;
  6. 再次 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 10kHz,SDCLK 10MHz,SDFM 使用 SINC3 滤波器,抽取率 16(为了演示数据率更直观)。

#include "driverlib.h" #include "board.h" void initEPWM(void) { // ---- 关闭时基同步启动,先配置后统一起振 ---- EALLOW; CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 0; EDIS; // ---- EPWM1:主功率 PWM,up-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); // ---- EPWM2:SDCLK 生成器,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 输出 SDCLK:ZERO 低、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); // 滤波器1:SINC3,抽取率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 初始化完成后,如果读到的数据恒为零,先不要怀疑芯片坏了。按这个顺序排查:

  1. 示波器量 SDCLK 引脚,确认有时钟且频率符合预期。如果没波形,查 ePWM2 是否真的在输出,查 GPIO 复用配置;
  2. 量 AMC1306 的 DOUT 引脚,看有没有位流输出。位流的特点是密集的窄脉冲串,不是稳定电平;
  3. 量 SDFM 的 SD_D 引脚,确认信号到达 MCU;
  4. 在调试器里看 SDFMCTL,确认模块已经退出复位且通道使能;
  5. 看 SDFLTCTL 的 FILTER_EN 位,确认滤波器真的使能了;
  6. 检查 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 加 OSR=16 时,大概丢弃 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 周期的一半,控制环路的相位裕量会受到明显影响。

举个例子,SDCLK=10MHz,OSR=32,SDFM 输出数据率约 312.5kSPS,SINC3 群延迟约 4.8us。对于 10kHz 的 PWM 周期(100us)来说,4.8us 大约占周期的 5%,控制环还能接受。但如果 SDCLK 降到 2.5MHz,OSR 还是 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 是常用区间,具体选多少要看系统对噪声和延时的权衡。

我在实际项目里一般先用 OSR=32 起步,跑通之后用加持噪声对比不同 OSR 下的电流波形,再决定往 16 还是 64 调。这个方法比纸上谈兵可靠得多,因为 PCB 布局和功率级的寄生参数最终都会反映在噪声里,只有实测数据能告诉你答案。

最后再分享一个小技巧。C2000Ware 自带的 SDFM 例程里通常有完整的初始化函数和滤波器配置示例,不要自己从零开始写,而是在例程基础上替换掉引脚配置和同步相关的部分。省下来的时间去画时钟路径图,把 SDCLK 从哪个 ePWM 来、经过哪些引脚、SDFM 数据延迟配多少、中断从哪里触发,一张图画清楚,比你对着寄存器手册啃半天更有效。我后来在 F28004x 上做第二版时,就是把这张图直接贴到设计文档里,硬件工程师照着布线,一次打板回来采样就通了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询