资讯动态

用TMS320F28335实现SPWM:从ePWM到正弦查表全解析

发布时间:2026/9/12 11:06:55 来源:尧图企业网站定制
简介针对TI TMS320F28335浮点DSP的正弦脉宽调制SPWM生成工程完整源码包支持直接导入CCS开发环境。资源面向电机驱动、逆变器及电源控制方向的工程师和嵌入式学习者解决在DSP28335上从底层外设配置到SPWM波形输出的完整开发需求。压缩包共71个文件体积约90KB包含22个C源文件、6个汇编文件、4个makefile、3个cmd链接脚本及CCS工程配置文件、自定义头文件等DSP2833x标准外设库与用户代码分离目录结构清晰。目前已有316人学习下载。工程内不仅提供DSP2833x系列外设驱动源码如EPwm、Adc、Gpio等还封装了my_function.h、variable_define.c等自定义模块便于直接调用和二次修改。代码中针对EPWM定时器进行初始化可灵活配置载波频率与占空比帮助开发者深入理解SPWM实现原理同时附带readme.txt说明和编译链接脚本可作为DSP28335 SPWM实战项目的高质量参考模板。1. 用 F28335 做 SPWM先想清楚是「生成」还是「调制」手上一份SPWM.zip解压后是 TMS320F28335 的完整 CCS 工程spwm.c、my_function.c、variable_define.c加上 TI 标准库DSP2833x_*.c。如果你正在做变频器、异步电机交流调速系统这类 SPWM 生成任务很容易误以为 SPWM 是在中断里一个点一个点软件输出波形。实际上 F28335 上真正生成 SPWM 的硬件是 ePWM 模块的时基、比较、动作限定和死区单元CPU 要做的只是周期性更新 CMPA。这个压缩包的价值在于它不是孤立算法而是一套能直接跑起来的外设配置骨架。这篇文章从 ePWM 计数机制讲起把查表生成、死区设置、调制度和载波比逐一拆开最后给出验证波形时避开坑的具体做法。2. ePWM 时基与动作限定是 SPWM 成波的硬件地基2.1 时基 TBPRD 与载波频率的换算F28335 的 ePWM 时基时钟TBCLK默认来自 150MHz 系统时钟通过TBCTL里的CLKDIV和HSPCLKDIV两级分频得到。我一般把两个分频都设为 1TBCLK150MHz这样时基分辨率最高死区时间也能算得最准。选择增减计数模式后计数器从 0 增长到TBPRD再递减回 0形成三角载波载波频率写为f_PWM TBCLK / (2 * TBPRD)以TBPRD3750为例周期是2 * 3750 / 150MHz 50us对应 20kHz。20kHz 是 IGBT 逆变器和伺服驱动器最常用的开关频率工程里spwm.c的初始化参数大多围绕这个频率设计。目标载波频率TBPRDTBCLK150MHz增减计数典型应用40kHz1875小功率 MOSFET 驱动20kHz3750IGBT 逆变器、通用变频器10kHz7500中功率电机控制降低开关损耗5kHz15000大功率低频场景噪声明显EPwm1Regs.TBCTL.bit.CTRMODE TB_COUNT_UPDOWN; // 增减计数三角载波 EPwm1Regs.TBPRD 3750; // TBCLK150MHz 时载波 20kHz EPwm1Regs.TBCTL.bit.HSPCLKDIV TB_DIV1; EPwm1Regs.TBCTL.bit.CLKDIV TB_DIV1; EPwm1Regs.TBCTR 0; // 计数清零后开始这里有个容易犯的顺序错误先改CTRMODE再写TBPRD否则计数器会按旧边界先跑一个周期初始化阶段就可能输出一个异常宽脉冲。正确的做法是先设计数模式、写周期值最后清零TBCTR。2.2 CMPA 与占空比的映射关系在增减计数模式下CMPA决定 PWM 上下沿的位置。我习惯增计数到 CMPA 时输出高减计数到 CMPA 时输出低EPwm1Regs.CMPA.bit.CMPA 1875; // 50% 占空比 EPwm1Regs.AQCTLA.bit.CAD AQ_SET; // 增计数到 CMPA 置高 EPwm1Regs.AQCTLA.bit.CBD AQ_CLEAR; // 减计数到 CMPA 拉低这样高电平宽度等于2 * (TBPRD - CMPA)占空比为D (TBPRD - CMPA) / TBPRD。所以 CMPA 越小占空比越大。CAD和CBD是动作限定子模块的两个关键字段分别对应增计数和减计数到达比较值时输出引脚的动作。实际 SPWM 里 A 通道做原始调制信号B 通道通过死区模块生成互补信号。2.3 影子寄存器为什么改 CMPA 不会立刻生效CMPA是双缓冲的软件写入后先存在影子寄存器要到指定事件才加载到活动寄存器。常见加载点是归零或周期匹配。对增减计数模式我建议在归零事件加载保证一个完整 PWM 周期内 CMPA 恒定输出没有毛刺。EPwm1Regs.TBCTL.bit.PRDLD TB_SHADOW; EPwm1Regs.CMPCTL.bit.SHDWAMODE CC_SHADOW; EPwm1Regs.CMPCTL.bit.LOADAMODE CC_CTR_ZERO; // 归零时装载LOADAMODECC_CTR_ZERO是关键。如果改成CC_CTR_IMMEDIATE脉冲宽度会在一个周期中间跳变桥臂输出会突然多出一个窄脉冲。这个现象用示波器看就是一个周期突然变窄容易误判成 CPU 中断卡死。3. SPWM.zip 工程骨架与 CMPA 更新实现拆解3.1 工程文件按职责划分SPWM.zip里的文件很多但不是每个都要逐行读。我通常把它们分成四组分组文件作用系统启动DSP2833x_SysCtrl.c、DSP2833x_CodeStartBranch.asm、DSP2833x_ADC_cal.asm时钟、看门狗、外设时钟使能外设驱动DSP2833x_EPwm.c、DSP2833x_Adc.c、DSP2833x_GlobalVariableDefs.c寄存器封装、中断向量初始化用户算法spwm.c、my_function.c、variable_define.c查表、CMPA 更新、全局变量链接与调试F28335.cmd、28335_RAM_lnk.cmd、TMS320F28335.ccxml内存分配和调试器配置DSP2833x_EPwm.c里大部分内容只是 TI 的外设默认配置真正的 SPWM 策略在my_function.c和spwm.c里。variable_define.c则存放查表数组和调制度、载波比等全局变量方便在 CCS 的 Expression 窗口里实时观察。3.2 正弦查表生成与最小脉宽限制工程里没有独立的查表生成脚本常见做法是在my_function.c里写一个运行时初始化函数把 400 点正弦表直接算出来。400 点对应 20kHz 载波下的 50Hz 基波属于同步调制。#define SPWM_POINTS 400 #define TBPRD_VALUE 3750 float32 spwmDuty[SPWM_POINTS]; uint16 spwmCmp[SPWM_POINTS]; void BuildSpmTable(float32 M) { float32 duty, minDuty 0.03f; // 最小脉宽 3% for (uint16 i 0; i SPWM_POINTS; i) { duty 0.5f 0.5f * M * sinf(2.0f * PI * i / SPWM_POINTS); if (duty minDuty) duty minDuty; if (duty 1.0f - minDuty) duty 1.0f - minDuty; spwmDuty[i] duty; spwmCmp[i] (uint16)(TBPRD_VALUE * (1.0f - duty)); } }逻辑说明M是调制度正弦峰值占空比最大到0.50.5M。minDuty用来限制最小脉宽防止输出电压在过零附近出现极窄脉冲IGBT 来不及完全开通或关断。spwmCmp[i]的转换公式来自前面CMPA TBPRD * (1 - D)所以占空比越大CMPA 越小。参数说明SPWM_POINTS决定载波比400 点是 20kHz/50Hz 的结果。如果只做验证不需要同步调制这个值可以改成 256但中断频率和基波频率的关系需要重新算。sinf来自 TI 运行库记得包含math.h。3.3 在 ePWM 中断里更新 CMPA查表的更新逻辑放在 ePWM 周期中断里每次取一个点的 CMPA 值写入比较寄存器interrupt void epwm1_isr(void) { static uint16 idx 0; EPwm1Regs.CMPA.bit.CMPA spwmCmp[idx]; idx; if (idx SPWM_POINTS) idx 0; EPwm1Regs.ETCLR.bit.INT 1; // 清中断标志 PieCtrlRegs.PIEACK.all PIEACK_GROUP3; // 应答 ePWM1 所在 PIE 组 }中断触发条件配置在初始化代码里EPwm1Regs.ETSEL.bit.INTSEL ET_CTR_ZERO; // 归零事件触发 EPwm1Regs.ETSEL.bit.INTEN 1; EPwm1Regs.ETPS.bit.INTPRD ET_1ST; // 每个归零事件都触发 PieCtrlRegs.PIEIER3.all M_INT1; // 使能 PIE 第 3 组第 1 通道因为CMPA影子寄存器配置为归零加载所以中断也选归零触发。进入中断后先写 CMPA硬件在下一个归零点装载等于提前一个载波周期准备好数据输出不会出现半个周期用旧值的情况。如果中断触发条件选了周期匹配而LOADAMODE还是归零加载写入的数值要过两个计数方向才生效相位会比预期滞后。4. 调制度、载波比和死区在 DSP28335 上的整定方法4.1 调制度 M 的上限与输出电压SPWM 调制中单相桥输出基波电压峰值是M * Vdc / 2三相异步电机负载上线电压基波有效值约为M * Vdc * sqrt(3) / (2 * sqrt(2))。调制度 M 最大值是 1但实际往 1 推时正弦峰值附近的脉冲非常窄加上死区后等效输出电压会提前饱和。在线修改调制度的函数很容易写void SetModulationIndex(float32 M) { for (uint16 i 0; i SPWM_POINTS; i) { float32 duty 0.5f 0.5f * M * sinf(2.0f * PI * i / SPWM_POINTS); spwmCmp[i] (uint16)(TBPRD_VALUE * (1.0f - duty)); } }这个函数在运行中调用后下次进入epwm1_isr就会使用新表。需要注意不能在中断正在查表时从主循环改写数组否则前半周期用新表、后半周期用旧表输出电压会出现跳变。常见做法是加一个updatePending标志主循环重建表后置 1中断统一切换到新表。4.2 死区时间计算与寄存器配置F28335 的 ePWM 死区模块由DBCTL、DBRED、DBFED三个寄存器控制计数单位是 TBCLK。TBCLK150MHz 时一个计数周期约 6.67ns寄存器值 150 对应 1us 死区。EPwm1Regs.DBCTL.bit.IN_MODE DBA_ALL; // A/B 通道输入相同 PWM EPwm1Regs.DBCTL.bit.POLSEL DB_ACTV_HI; // 互补输出高有效 EPwm1Regs.DBCTL.bit.OUT_MODE DB_FULL_ENABLE; EPwm1Regs.DBRED 150; // 上升沿延迟 1us EPwm1Regs.DBFED 150; // 下降沿延迟 1usDBRED控制上升沿延时DBFED控制下降沿延时两者一起形成互补信号的死区。如果输出波形不对称优先检查这两个值是否写反。另外隔离驱动芯片自身的传输延时也要从死区里扣掉比如驱动芯片延时 200ns那寄存器值应减少 30最终实际死区才等于 1us。开关器件典型死区DBRED/DBFED150MHz TBCLK小功率 MOSFET200ns ~ 500ns30 ~ 7520A IGBT 模块1us ~ 3us150 ~ 450大功率 IGBT3us ~ 5us450 ~ 750死区过大不仅损失输出电压还会引入低次谐波电机电流里会有明显的低频脉动。用电流探头看相电流波形如果过零点出现阶梯状畸变基本可以断定是死区补偿没做。4.3 载波比 N 用整数还是小数步进同步调制要求N f_c / f_0为整数。SPWM.zip里最简单的做法就是固定 400 点表载波 20kHz 对应 50Hz。但如果要调 60Hz再把表长改成 333 就不方便了。我一般用相位累加器做异步调制表长固定 256用小数步进逼近目标频率float32 phaseAcc 0.0f; float32 phaseStep 256.0f * 50.0f / 20000.0f; // 载波 20k基波 50Hz interrupt void epwm1_isr_async(void) { uint16 idx (uint16)phaseAcc; EPwm1Regs.CMPA.bit.CMPA spwmCmp[idx 0xFF]; phaseAcc phaseStep; if (phaseAcc 256.0f) phaseAcc - 256.0f; }idx 0xFF利用表长 256 做取模省掉除法。相位累加器不会累积误差基波频率长期稳定代价是每个载波周期不总在整点采样输出频谱里会出现分布较散的边带谐波。对异步电机调速系统来说这种谐波比同步调制下的集中在开关频率附近要更友好音频噪声略微发散但不会产生固定拍频。5. 用示波器和回读验证 SPWM 波形的三个实用技巧5.1 数脉冲验证时基稳定直接看 20kHz 的 PWM 脉冲很容易疲劳。用一个计数器把载波分频到 1k 以下再通过 GPIO 翻转输出示波器就能稳定观察周期volatile uint16 divider 0; interrupt void epwm1_isr(void) { if (divider 20) { GpioDataRegs.GPBTOGGLE.bit.GPIO34 1; divider 0; } }GPIO34 的输出周期应该等于 20 个载波周期。如果翻转波形的周期随温度或负载漂移优先查TBPRD是否被误改以及系统时钟 PLL 是否在运行中跳变。5.2 用事件触发看查表连续性把 ePWM 的归零事件引到一个 GPIO以此作为示波器触发源同时观察 PWM 输出。正常 SPWM 波形在正弦峰值附近脉冲最宽过零附近最窄相邻载波周期的脉宽变化应连续。如果看到相邻两个周期占空比从接近 0 突然跳到接近 100%说明查表索引的取模运算写错或者中断里修改了索引变量。这个技巧比直接抓波形更能暴露代码逻辑错误。5.3 用 ADC 回读验证调制度线性相电压经过电阻分压后接到 ADC 输入在 ePWM 归零中断里同步采样一次。换算公式是U_pk (adc_code - mid) * Vref / 4096。把 M 分别设为 0.3、0.5、0.7读出峰值应近似线性增长。如果 M0.7 的输出比 0.5 没有明显变大多半是最小脉宽限制削掉了正弦峰值附近的脉冲需要检查minDuty是否设置过大。提示ADC 采样时刻必须放在 PWM 切换完成一个周期之后直接采桥臂中点的平均值会混入大量开关纹波验证结果不具备参考价值。工程里DSP2833x_Adc.c和DSP2833x_EPwm.c配合使用就是这个目的把 ADC 触发源设成 ePWM 的归零事件再由中断同步读取结果。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价