ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于DSP2833X的移相全桥PWM控制设计与实现

2026/9/17 10:18:14 拓冰建站 浏览量
基于DSP2833X的移相全桥PWM控制设计与实现 简介移相全桥控制是电力电子领域广泛应用的技术通过改变PWM信号的占空比来调整全桥开关管的导通时序从而提升电源转换效率并抑制电磁干扰。这套基于TI DSP2833X微控制器的完整工程代码面向电力电子与电机驱动方向的开发者解决全桥变换器中移相控制策略的实现问题。工程包含主程序HVPSFB-Main.c、PWM移相配置源文件、DPL-ISR中断服务程序及DSP2802x外设头文件覆盖初始化设置、PWM生成、死区时间配置、故障检测与闭环调节等关键模块。资源共45个文件以C语言源码、汇编程序、CCS工程配置、编译脚本及输出文件为主压缩包仅266KB结构清晰紧凑。目前已有1864人学习/下载适合想快速上手DSP2833X移相全桥控制的工程师或研究者通过研读代码可掌握边沿对齐/中心对齐PWM配置、上下桥臂锁存器管理、过流保护等实用技巧并便于在CCS工程中直接阅读与调试。1. 移相全桥控制为什么值得用 DSP2833X 写移相全桥代码里真正值钱的只有一行EPwm2Regs.TBPHS 的赋值。其余 PWM 初始化、死区配置、中断服务程序都是在为这行赋值搭台子。移相全桥Phase-Shifted Full BridgePSFB在通信电源、充电桩 DC-DC 和车载 OBC 里几乎是标配拓扑原边四个管子实现 ZVS副边整流后等效开关频率翻倍效率曲线在 40%60% 负载附近尤其好看。但代码层面真正有信息量的不是「全桥」——那只是两路互补 PWM而是「移相」两个 50% 占空比的桥臂之间那几十度相位差被用来调节输出能量这才是 DSP 比 UCC28950 这类模拟控制器值得用的地方。DSP2833X 有浮点核算占空比不需要查表ePWM 模块原生支持主从同步、相位寄存器和互补死区四路 PWM 让两个模块级联就够。你在 Gitee 或 GitHub 搜「移相全桥代码」时常能看到带 jungleysm 这类个人标识的 DSP2833X 仓库里面大多是裸机寄存器工程没有 RTOS核心就是几十行寄存器配置加一个周期中断。下面按参数换算、寄存器初始化、闭环更新、验证排错四个层次展开新手能从最小系统跟到波形老手可以重点看相位限幅和中断时序。2. PWM 移相在 DSP2833X 上的时序实现与参数换算2.1 移相角与输出占空比的关系先算清楚移相全桥的四个开关中Q1/Q2 组成超前桥臂A 点Q3/Q4 组成滞后桥臂B 点。两个桥臂都是接近 50% 的占空比滞后桥臂相对超前桥臂滞后一个相位角 δ。原边电压 V_AB 在 Q1、Q4 同时开通时为正在 Q2、Q3 同时开通时为负两个桥臂完全同相时正负抵消原边没有电压输出滞后 180° 时变成普通硬开关全桥副边得到近乎满幅的方波电压。数值关系可以写成非常干净的形式D_eff 1 - δ / 180°δ 用度表示。D_eff 是折算到副边的有效占空比理想输出电压约为 Vin × (Ns/Np) × D_eff。比如 400V 输入、变压器匝比 4:1、目标 48V 输出时需要 D_eff 0.48对应 δ ≈ 93.6°。这个公式决定了闭环程序里电压误差到相位角的变换方向误差为正说明需要更大的有效占空比也就是减小 δ误差为负则要增大 δ。方向弄反的直接结果是环路变成正反馈输出电压一下顶到保护值。实际工程里不能把 0.48 直接当成最终占空比因为原边电流在漏感 Lr变压器漏感加外置谐振电感上换流会吃掉一部分相位。CCM 模式下副边占空比丢失的近似公式是 D_loss ≈ 4·Lr·Ip / (Vin·Tsw)。举一组常见参数Lr 12μHIp 6AVin 400VTsw 10μs算出来大约 7.2%换成角度就是 13° 左右。这也是纯查表开环方案容易出问题的地方换流时间随负载电流变化开环表很难覆盖每条负载线。提示上面公式里的 Ip 是换流前的原边峰值电流不是副边折算电流估算时用满载参数算一次相位补偿再留出 10° 左右的软件裕量。2.2 ePWM 主从模块怎么搭出四路 PWMDSP2833X 的 ePWM 模块结构决定了实现移相全桥最顺手的方案ePWM1 作主模块驱动超前桥臂ePWM2 作从模块驱动滞后桥臂两个模块共享 EPWMCLK用同步信号把从模块的计数器对齐到主模块的周期起点。两个模块之间唯一的差异就是 TBPHS 寄存器它决定从模块计数器在同步事件到来时的初始相位。寄存器层面的行为是主模块在 TBCTR 计到 0 时通过 EPWM1SYNCO 输出同步脉冲从模块配置 PHSEN 1 后收到同步脉冲就把 TBPHS 里的值装载进 TBCTR。换句话说任意时刻改写 TBPHS下一个开关周期开始就会生效。这个机制天然支持在线调相不需要额外的软件定时器去对齐换相时刻。四路 PWM 映射为EPWM1A → Q1EPWM1B → Q2EPWM2A → Q3EPWM2B → Q4。EPWM1A/B 靠 DB 模块生成互补关系并插入死区EPWM2 的 A/B 两路同样处理。这里要提醒一个容易漏的配置如果只盯着 ePWM1A 和 ePWM2A 之间的相位差忘记给 EPWM2B 配置同样的死区参数滞后桥臂上下管会在换相时直通。DB 模块的寄存器是每个 ePWM 模块独立的主模块配好了不等于从模块也配好。2.3 死区与最小移相角的工程约束死区时间按开关管关断特性估算常见的 CoolMOS 和 IGBT 在硬开关条件下需要 150300ns。EPWMCLK 为 150MHz 时一个计数周期约 6.67ns30 个计数就是 200ns。推荐把 ePWM1、ePWM2 的 DBRED 和 DBFED 设成相同值先按 200ns 起调上电后观察原边电压尖峰再微调不要一上来就追求最大 ZVS 范围而把死区拉满。死区还约束着最小移相角。δ/360° × Tsw 如果小于死区时间副边正负脉冲会窄到被死区完全覆盖输出电压出现不可控跳变。以 100kHz、200ns 死区为例对应的最小角度约 7.2°。软件里要给相位角加下限幅不能只依赖硬件过流保护这是我见过移相角没有下限制导致轻载失控的常见原因。下面是一个顺手可用的换算表按 100kHz、EPWMCLK 150MHz、TBPRD 1500 计算TBPHS 计数值 1500 × δ / 180°。表里没扣除占空比丢失实际调试时输出电压会比表格低 3%8%看到偏差先算 Lr 的贡献别急着怀疑 PID。移相角 δ有效占空比 D_eff折算输出电压400V / 4:1TBPHS 计数值60°0.66766.7V50072°0.60060.0V60090°0.50050.0V750108°0.40040.0V900120°0.33333.3V10003. DSP2833X 移相全桥 PWM 的初始化代码与寄存器配置3.1 GPIO 复用与 ePWM1/2 时钟使能DSP28335 的 ePWM1A/B 默认映射在 GPIO0/GPIO1ePWM2A/B 在 GPIO2/GPIO3。GPIO 配置只需要把 GPAMUX1 对应位置成 1不需要单独设置方向寄存器复用功能会接管引脚控制。时钟方面有一个容易忽视的点EPWMCLK 默认跟随 SYSCLKOUT但如果所有 ePWM 模块先跑起来再逐个初始化两个模块的计数器会处在未知相位关系上。标准做法是先通过 TBCLKSYNC 0 冻结全部时基等两个模块都配置完成后再统一放开。void InitPsfbGpio(void) { EALLOW; // GPIO0-3 复用为 EPWM1A / EPWM1B / EPWM2A / EPWM2B GpioCtrlRegs.GPAMUX1.bit.GPIO0 1; GpioCtrlRegs.GPAMUX1.bit.GPIO1 1; GpioCtrlRegs.GPAMUX1.bit.GPIO2 1; GpioCtrlRegs.GPAMUX1.bit.GPIO3 1; // 先停掉所有 TBCLK主从计数器保持静止状态 SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC 0; EDIS; }这段代码里 GPIO0-3 的四行是 28335 的固定映射换用 2837x 或其它系列时引脚编号会变需要重新查数据手册的引脚复用表。TBCLKSYNC 是系统控制寄存器必须放在 EALLOW/EDIS 保护区内漏掉 EDIS 会导致后续寄存器写入失败而且这种问题在仿真器里不容易直接定位到原因。3.2 主模块 ePWM1 的寄存器配置主模块负责超前桥臂自身不加载外部相位但要负责给从模块发同步脉冲。配置分三块时基TB、动作限定AQ、死区DB。时基决定开关频率和计数模式动作限定决定 EPWM1A 什么时候拉高拉低死区模块从 A 信号生成互补的 B 信号。void InitPsfbEpmw1(void) { EALLOW; // 时基up-count150MHz 不分频TBPRD1500 对应 100kHz EPwm1Regs.TBCTL.bit.PRDLD 0; // TBPRD 立即装载 EPwm1Regs.TBCTL.bit.CTRMODE 0; // 0 up-count EPwm1Regs.TBCTL.bit.CLKDIV 0; // TBCLK EPWMCLK / 1 EPwm1Regs.TBCTL.bit.HSPCLKDIV 0; // 高速时钟分频 1 EPwm1Regs.TBCTL.bit.PHSEN 0; // 主模块不从同步输入装相位 EPwm1Regs.TBCTL.bit.SYNCOSEL 1; // CTR0 时输出同步脉冲 EPwm1Regs.TBPRD 1500; EPwm1Regs.CMPA.bit.CMPA 750; // 50% 占空比 // 动作CTR0 输出低向上计数碰到 CMPA 输出高 EPwm1Regs.AQCTLA.bit.ZRO 1; // 清除输出拉低 EPwm1Regs.AQCTLA.bit.CAU 2; // 置位输出拉高 // 死区上升沿和下降沿都延时 30 计数 200ns EPwm1Regs.DBCTL.bit.OUT_MODE 3; // A/B 都由 DB 模块输出 EPwm1Regs.DBCTL.bit.POLSEL 2; // A 不反相B 反相构成互补 EPwm1Regs.DBRED.bit.DBRED 30; EPwm1Regs.DBFED.bit.DBFED 30; // 周期中断每次 CTR0 触发一次中断 EPwm1Regs.ETSEL.bit.INTEN 1; EPwm1Regs.ETSEL.bit.INTSEL 1; // 1 CTR0 事件 EPwm1Regs.ETPS.bit.INTPRD 1; // 每个周期事件都响应 EDIS; }逐项说明。SYNCOSEL 1 让主模块在 TBCTR 归零时输出同步脉冲这是从模块对齐相位的前提。AQCTLA 里 ZRO 1 表示计数到 0 时清除输出CAU 2 表示向上计数碰到 CMPA 时置位输出所以 EPWM1A 高电平区间是 0 到 750占空比正好 50%。注意 AQ 模块里没有配置 AQCTLBEPWM1B 由 DB 模块根据 A 信号反相生成再去操作 AQCTLB 属于重复配置会让输出动作不确定。死区的 OUT_MODE 3 配合 POLSEL 2 是互补 PWM 的标准写法EPWM1A 保持原极性EPWM1B 反相DBRED 和 DBFED 分别延时 200ns另一路从信号不会和原信号发生重叠导通。3.3 从模块 ePWM2 的相位加载与同步配置从模块的寄存器配置和主模块大部分一致多出的关键位是 PHSEN 1以及初始相位寄存器 TBPHS。同步事件到来时TBPHS 的值被装载进 TBCTRePWM2 的周期起点因此落后 ePWM1 一个可编程角度。启动时把 TBPHS 设为 0意味着两个桥臂完全同相原边电压为零这是相对安全的软启动起点。void InitPsfbEpmw2(void) { EALLOW; EPwm2Regs.TBCTL.bit.PRDLD 0; EPwm2Regs.TBCTL.bit.CTRMODE 0; EPwm2Regs.TBCTL.bit.CLKDIV 0; EPwm2Regs.TBCTL.bit.HSPCLKDIV 0; EPwm2Regs.TBCTL.bit.PHSEN 1; // 从模式同步事件装载 TBPHS EPwm2Regs.TBCTL.bit.SYNCOSEL 0; // 同步输入来自 EPWM2SYNCI EPwm2Regs.TBPRD 1500; EPwm2Regs.CMPA.bit.CMPA 750; EPwm2Regs.AQCTLA.bit.ZRO 1; EPwm2Regs.AQCTLA.bit.CAU 2; EPwm2Regs.DBCTL.bit.OUT_MODE 3; EPwm2Regs.DBCTL.bit.POLSEL 2; EPwm2Regs.DBRED.bit.DBRED 30; EPwm2Regs.DBFED.bit.DBFED 30; EPwm2Regs.TBPHS.bit.TBPHS 0; // 初始相位 0原边无输出 EDIS; // 两个模块配置完成统一启动时基 EALLOW; SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC 1; EDIS; }TBPHS 的更新时机值得说清楚。周期中断里直接写这个寄存器不会立刻改变输出而是等下一个同步事件到来才生效因此不会出现半周期乱脉冲。但如果在中断里同时改动 CMPA 和 TBPHS两者生效点不同步CMPA 改动要等到下一个周期边界TBPHS 改动要等到同步事件。好在 100kHz 下 TBPRD 为 1500 个计数完成两个 16 位寄存器写操作的时间裕量非常充足。下表把关键寄存器整理出来调试时对照看寄存器/位域作用本文配置值TBCTL.CTRMODE计数模式up-count 便于周期中断固定相位0TBCTL.PHSEN从模块使能相位装载ePWM2 1TBCTL.SYNCOSEL同步输出源选择ePWM1 1ePWM2 0TBPRD周期计数1500CMPA占空比比较值750AQCTLA.CAU / ZRO动作限定决定高电平区间2 / 1DBRED / DBFED死区上升/下降延时30TBPHS移相计数值015004. 移相全桥闭环的 PID 更新与软启动实现4.1 控制变量的选择用有效占空比算移相角写移相全桥闭环时有一种常见做法是让 PI 直接输出移相角这样不是不能用但增益的物理意义不直观。我习惯让 PI 输出一个「指令有效占空比」D_cmd限幅在 0.050.90 之间再用 D_eff 1 - δ/180° 反算相位角。理由是 D_cmd 可以直接和副边电压、匝比去比对目标 48V、Vin 400V、匝比 4:1 时 D_cmd ≈ 0.48调 PI 时看这个量比看角度更容易判断是超调还是静差。反算角度时要考虑占空比丢失。补偿值可以按满载工况估算例如第 2.1 节算出的 13°写成常量 D_LOSS_DEG 放进程序。负载变化时补偿不会完全精确剩余误差交给 PI 积分器修正。这个策略实现简单不用实时估算 Lr 上的换流时间工程上最稳。4.2 周期中断里的增量式 PI 与限幅控制循环放在 ePWM1 周期中断里。中断频率等于开关频率 100kHz在 150MHz 主频下开销偏高实际项目里我推荐把 ETPS.INTPRD 设成 2 或 4控制频率降到 50kHz 或 25kHz。对 48V 输出的电源25kHz 控制已经足够还能给 ADC 转换和浮点运算留足裕量。下面是一段可以直接放进中断服务函数的增量式 PI__interrupt void epwm1_isr(void) { // 读 ADC 结果并按实际分压比换算成电压值单位 V float32 v_fb (float32)AdcRegs.ADCRESULT0 * V_SCALE; // 增量式 PIu 是有效占空比指令 float32 err v_ref - v_fb; u kp * (err - err_prev) ki * err; err_prev err; // 幅值限制D_MAX 留出死区和换流裕量 if (u D_MAX) u D_MAX; if (u D_MIN) u D_MIN; // 有效占空比换算到移相角再换算到 TBPHS 计数值 float32 delta 180.0f * (1.0f - u) - D_LOSS_DEG; if (delta MIN_DEG) delta MIN_DEG; EPwm2Regs.TBPHS.bit.TBPHS (uint16_t)(TBPRD * delta / 180.0f); // 清中断标志 EPwm1Regs.ETCLR.bit.INT 1; PieCtrlRegs.PIEACK.all PIEACK_GROUP3; }增量式 PI 的公式里kp 控制误差变化的响应速度ki 控制误差积分速度。增益的起步值按电压单位估算48V 电源上 kp 取 0.0030.008即 1V 误差产生 0.3%0.8% 的占空比修正量ki 取 kp 的 1/10 到 1/5。下面给一组按控制频率区分的参考起点值实际整定时按阶跃响应方向微调。注意这些数值依赖 ADC 采样比例和电压基准换一套硬件需要重新标定。控制频率kp 起点ki 起点D_MAXD_MIN50kHz0.0050.0010.900.0525kHz0.0080.0020.900.0510kHz0.0120.0040.880.05增量式 PI 自带一部分抗饱和能力因为没有单独的积分累加器但输出持续顶在限幅值时误差累积仍会造成恢复迟滞。更严格的做法是饱和期间冻结 u 的更新也就是检测到 u 达到 D_MAX 且误差仍为正时不执行 u 这一段。4.3 软启动和中断响应时间的实际处理移相全桥启动时最怕直接给满载阶跃变压器磁化电流和输出电容充电电流叠加会让原边电流瞬间冲高。我在项目里通常把 v_ref 做成软件斜坡每次中断只增加额定目标值的一小段。以 25kHz 控制频率为例每帧加 0.08V48V 目标约 2.4 秒爬升完毕比反复整定 PI 省事得多。斜坡结束后按额定电压闭环运行切换点放在电压误差小于阈值的那一帧。ADC 采样时刻对环路品质的影响比很多人想象的大。副边电感电流在开关周期起点和终点处于谷底中点附近处于峰值如果在任意相位启动 ADC 采样采样值叠加周期纹波环路容易在高频抖动。推荐把 ADC 触发对齐到 CMPA 事件也就是副边电压脉冲的中点附近采样或者对齐到 CTR0 后再做两次平均。中断函数执行时间是另一个关键点。代码放在 Flash 里运行时有取指等待中断响应时间会随流水线状态波动。TI 的 C28x 编译器支持把中断函数放到 RAM 执行写法是__attribute__((ramfunc)) __interrupt void epwm1_isr(void)也可以等价的用#pragma CODE_SECTION(epwm1_isr, .TI.ramfunc)把函数放进可初始化 RAM 段。对 25kHz 或更高的控制频率这一步能让 ISR 抖动小一个数量级代价只是几十字节 RAM。需要注意链接脚本里.TI.ramfunc段必须被分配到 RAM并且有对应的初始化拷贝。5. 验证移相全桥代码的示波器技巧与常见踩坑5.1 先观察滞后桥臂的 ZVS再谈效率示波器探头接在滞后桥臂下管 Q4 的 Vds 和 Vgs 上重点看开通瞬间如果 Vds 降到零之后栅极才拉高ZVS 成立如果栅极拉高时 Vds 上还有几十伏电压说明死区太短或谐振电感能量不足。轻载下滞后桥臂会先丢失 ZVS这是移相全桥的固有特性不是 PID 能解决的但至少要先确认满载时波形是正确的再继续调环路。原边电压 V_AB 的波形也要一起看。它不是干净方波在每个半周期开始处会有一块被吞掉的平台平台宽度等于原边电流换流时间随负载电流增大而加宽。这就是占空比丢失的直观表现。如果平台宽度明显超过按 D_loss ≈ 4·Lr·Ip / (Vin·Tsw) 估算的数值说明 Lr 偏大或变压器漏感超出预期需要回到磁件参数上调整。5.2 CCS 仿真器驱动加载失败的排查路径换新电脑跑老工程时常会遇到仿真器驱动加载失败日志里出现 unable to load C:\ti\ccsv6\ccs_base\emulation\drivers\tixds560icepick_d.dvr 这类信息。这条报错通常和移相全桥代码无关而是 CCS 版本与 XDS560 驱动不匹配或者安装目录权限不够。先确认 CCS 是完整安装而不是只拷贝了工程再看仿真器驱动服务有没有被安全软件拦截。如果只是为了验证代码逻辑换 XDS100v2 这类低成本仿真器往往更快不必在 XDS560 驱动上耗太多时间。5.3 用 LTspice 把 PWM 策略先验证一遍LTspice 不能直接编译 DSP 程序但不妨碍做联合验证把 DSP 中断里计算出的移相角和 TBPHS 值导出成时间序列在 LTspice 里转成 PWL 电压源作为理想开关管的驱动信号。死区时间、移相角变化速率都做成参数可以快速模拟轻载到满载的相位跨度以及死区边界处原边电压的畸变情况。TI 的 DSP 模型在 LTspice 里确实不常见但移相全桥只需要理想开关、二极管和耦合电感用 LTspice 自带器件就能搭出可用的功率级模型。这部分验证在真实硬件上要多轮试错在仿真里十几分钟就能跑完。调完这一轮再回到硬件上看原边电流波形和副边二极管反向恢复特性剩下的就是死区时间与磁件参数的微调。本文还有配套的精品资源点击获取