TMS320F280049C ePWM配置实战:死区、故障保护与占空比控制详解

发布时间:2026/9/8 2:00:10
TMS320F280049C ePWM配置实战:死区、故障保护与占空比控制详解 简介这份工程面向TMS320F280049C DSP的PWM应用基于C28x内核演示了脉宽调制初始化、相位偏移配置以及CCS工程构建方法可服务于电机驱动、电源管理等工业控制场景也适合嵌入式开发人员与相关专业学生用作入门实战参考。压缩包共33个文件大小仅318KB主要包含C源码、汇编启动文件、链接命令脚本、CCS工程配置和TI驱动库同时带有编译生成的obj、out、map等产物便于对比理解完整构建流程。工程内置相移PWM示例、外设驱动库和说明文档通过CCS打开即可查看工程结构切换RAM/Flash链接脚本进行编译调试能直观观察PWM波形与寄存器配置有助于掌握F280049C的PWM模块、死区控制、中断处理以及代码烧写流程。目前已有1028人学习了该资源适合作为DSP PWM入门的小型参考工程与练手项目。 如果你手里也有一个名为TMS320F280049C_PWM.zip的工程包或者看到这个名字就知道它大概是什么——那你八成正在被 TI 的 C2000 系列折腾。这个压缩包内容基本可以猜个八九不离十一套在 CCS 下基于 C2000Ware driverlib 的 ePWM 配置工程核心就是让 TMS320F280049C 输出两路可控、可配死区、还能被硬件故障信号强制拉掉的 PWM 波形。网上关于 STM32、树莓派、NE555 怎么做 PWM 的教程一抓一大把但 C2000 这种面向电机控制和数字电源的专用芯片反而少有人把配置逻辑讲透。真正让工程师栽跟头的从来不是“PWM 能不能输出”而是死区方向、故障保护动作、占空比边界这些细节。这篇文章就把这套 ePWM 配置从里到外拆开适合刚好要上手 F28004x、或者打算从 28335/STM32 迁移过来的开发者。1. 解压之后先分清哪些文件是“真干货”1.1 这个zip包里大概率装了什么按照 C2000 系列的常见工程结构这个包解压后大致是这几类东西CCS 工程描述文件、C2000Ware 的 driverlib 库文件、device/board 初始化代码、SysConfig 图形化配置产物以及你自己真正要看的main.c和 PWM 初始化函数。我的建议是别一上来就翻寄存器定义先按这个顺序读代码打开main.c看整体流程找到initEPWM之类的函数跟着它把时基、比较、动作限定、死区、故障保护这几块配置逐一过一遍。device.c和board.c里的内容大多是芯片上电必须的“基础设施”比如时钟、看门狗、GPIO 引脚复用不是每个都要看懂但至少要知道它们干了什么。很多新手拿到工程包后直接下载到板子看到有波形出来就以为完事了其实连死区方向和故障保护都没验证。这个包真正值钱的地方就是把 ePWM 模块的完整配置流程走了一遍值得逐行读懂。1.2 ePWM模块资源全景TMS320F280049C 有 8 个 ePWM 模块每个模块输出 EPWMxA 和 EPWMxB 两路信号。每个模块内部又拆成好几块时基子模块 TB、比较子模块 CC、动作限定子模块 AQ、死区子模块 DB、斩波子模块 PC、故障保护子模块 TZ、事件触发子模块 ET、数字比较子模块 DCE。如果用 STM32 的视角理解一个 ePWM 模块约等于一个高级定时器有周期寄存器、比较寄存器、互补输出、刹车保护但 C2000 把这些功能拆得更细可配置性也更高。比如动作限定子模块允许你规定“计数到比较值时输出高还是低”“计数到周期值时输出高还是低”两路输出可以独立配置灵活性远超普通 MCU 的 PWM 外设。1.3 为什么死区和故障保护是重点如果是单纯点亮一个 LED 呼吸灯任何单片机都行。但 C2000 系列的定位是电机驱动、数字电源、逆变器这类“一个失误就炸机”的场景所以 ePWM 模块里最值得研究的恰恰是死区子模块和故障保护子模块。死区解决的是半桥电路上下管直通问题故障保护解决的是过流、过压发生时能不能在微秒级把功率管关断的问题。这两个功能如果只在软件里做CPU 中断延迟就可能让管子烧掉。F28004x 的 ePWM 把这些做成了硬件联动配置正确后不依赖 CPU 也能保护这才是它和普通 PWM 外设拉开差距的地方。2. 时基频率计算让PWM频率不是“调出来”的而是“算出来”的2.1 从晶体到ePWM的时钟链路TMS320F280049C 上电后默认使用内部振荡器通过 PLL 配置后系统时钟 SYSCLK 最高到 100MHz。ePWM 模块的时钟 EPWMCLK 默认就等于 SYSCLK所以算频率时先记住一个基准时基输入时钟 TBCLK 通常是 100MHz。从 28335 迁移过来的朋友特别容易在这踩坑28335 主频 150MHzF280049C 是 100MHz老工程里直接搬过来的 TBPRD 数值会导致 PWM 频率整体偏移电机转速、电源频率全不对。C2000Ware 的Device_init()会做一部分时钟初始化但如果你在 SysConfig 或主函数里手动改过 PLL 配置一定要回头确认 EPWMCLK 到底是多少否则后面所有计算都是空中楼阁。2.2 TBPRD、TBCTR、CMPA三件套和频率公式ePWM 的时基逻辑不复杂寄存器TBCTR在时钟驱动下不断计数计数范围由周期寄存器TBPRD决定比较寄存器CMPA和CMPB则用来在计数过程中产生比较事件。PWM 波形的高低电平切换就是动作限定子模块对比较事件的响应。向上计数模式下的频率公式是f_PWM f_TBCLK / (TBPRD 1)为什么是 TBPRD1因为计数器从 0 计到 TBPRD 一共经历了 TBPRD1 个时钟周期。如果在递增-递减计数模式下计数器从 0 到 TBPRD 再回到 0周期翻倍公式变成f_PWM f_TBCLK / (2 * TBPRD)占空比同理。向上计数、输出高有效时占空比就是 CMPA 对应的“高电平时间”除以整个周期时间Duty CMPA / (TBPRD 1)举个例子要做 20kHz 电机 PWMTBCLK100MHz那 TBPRD 100M / 20k - 1 4999。想要 50% 占空比CMPA 取 2500非常干净。2.3 舵机50Hz这种低频怎么配分频是个好东西有朋友做舵机控制时算完一手冷汗舵机标准周期是 20ms也就是 50Hz如果 TBCLK 直接 100MHz那 TBPRD 要 1999999而 TBPRD 是 16 位寄存器最大只能到 65535根本放不下。这时候就要用分频。ePWM 时基子模块的 CLKDIV 可以按 2 的幂次分频。把 TBCLK 分到 3.125MHzCLKDIV3220ms 对应 62500 个时钟周期TBPRD 取 62499正好落在 16 位范围内。舵机 0°180° 对应 1ms2ms 高电平换算成 CMPA 就是 312562501.5ms 中位对应 4688。这个数值不要求绝对精确因为舵机脉宽本身就有容差误差零点几微秒完全不影响角度。| 应用场景 | CLKDIV | TBCLK | TBPRD | CMPA示例 | | 20kHz 电机 | 1 | 100MHz | 4999 | 2500 50% | | 50Hz 舵机 | 32 | 3.125MHz | 62499 | 4688 ≈ 1.5ms |分频的意义不只是让数值落在寄存器范围内还能让一些频率对应到整数比较值减少占空比量化误差。算频率时先把分频、TBPRD、CMPA 三者关系写在纸上比在示波器上反复试错高效得多。3. 死区配置DBRED、DBFED和主源选择逐个掰清楚3.1 死区到底防的是什么半桥电路里上管关断后下管才能导通如果两个管子同时导通电源直接对地短路电流瞬间飙升功率管大概率当场烧毁。死区就是在上下管的开关切换之间插入一段“双方都关断”的空白时间。常见做法是把 ePWM 模块的 A、B 两路配置成互补输出一路高电平另一路低电平两路切换时先让导通的那一路关断等待一段时间再让另一路导通。这个等待时间就是死区时间。28335 老用户熟知的 DBRED 和 DBFED就是设置死区延时的两个寄存器DBRED 控制上升沿延时DBFED 控制下降沿延时。3.2 死区模块的信号流和主源选择死区配置里最容易出问题的是“主源选择”。ePWM 的 A、B 两路信号进入死区模块后先要经过一个输入选择决定上升沿和下降沿的延时信号是从 EPWMxA 取还是从 EPWMxB 取。用 driverlib 配置时核心是这三步// 设置死区输入源上升沿和下降沿都从 EPWMxA 取 EPWM_setDeadBandInputMode(EPWM1_BASE, EPWM_DB_INPUT_EPWMxA); // 设置输出极性A 路高有效B 路低有效互补 EPWM_setDeadBandOutputPolarity(EPWM1_BASE, EPWM_DB_POLARITY_ACTIVE_HIGH, EPWM_DB_POLARITY_ACTIVE_LOW); // 启用上升沿和下降沿延时 EPWM_setDeadBandDelayMode(EPWM1_BASE, EPWM_DB_RED, true); EPWM_setDeadBandDelayMode(EPWM1_BASE, EPWM_DB_FED, true);主源选择的要求其实就一条你必须确定你设置的这个 DBRED 和 DBFED 到底是作用在哪一路信号产生的边沿上。常见错误是把输入模式保持默认结果 A、B 两路里只有一侧有死区另一侧几乎没有用示波器看波形像“半残”的互补输出上电后轻则波形难看重则直接炸桥臂。3.3 死区时间怎么算死区时间的计算非常直观。F28004x 死区模块的延时计数器是 10 位范围 01023默认死区时钟 DBCLK 等于 TBCLK。在 100MHz 下每个时钟周期 10ns所以死区时间 延时寄存器值 × 10ns想要 1us 死区DBRED 和 DBFED 都设 100。如果你需要更长的死区可以给死区模块单独分频但要注意分频后死区时间的步进会变大不再是 10ns 的整数倍。实际调试时我习惯把死区时间设得稍大一点比如 1.5us示波器看波形确认无误后再慢慢缩小到设计目标。死区太大会让输出波形畸变负载电感续流时会出现一段平台波形看起来像梯形而不是方波死区太小又失去保护意义。不同功率管的开启、关断延迟不一样这个参数最后还是要根据实际驱动芯片的时序来定。4. Trip Zone故障保护真正能让功率管不冒烟的硬件机制4.1 为什么必须用硬件保护软件里写“如果过流就关断 PWM”看起来逻辑正确但 CPU 响应中断需要时间主循环里还有各种拖后腿的代码万一 CPU 卡死在某个状态功率管就没人管了。F28004x 的 Trip Zone 机制就是专门解决这个问题的它能让 PWM 输出在硬件层面直接响应故障信号不依赖 CPU。TZ1、TZ2、TZ3 是三个外部故障输入引脚多个 ePWM 模块可以同时受同一个故障信号控制。实际接法是让板上过流比较器、过压保护电路的输出信号接到这些引脚故障发生时电平一跳变PWM 输出立刻被强制到安全状态。4.2 故障触发配置和输出动作触发方式上Trip Zone 分两大类cycle-by-cycleCBC和 one-shotOST。CBC 适合逐周期限流每个 PWM 周期自动清除故障标志并恢复输出常用于峰值电流模式控制OST 适合硬过流、短路这类严重故障触发后锁存住必须软件手动清除才能恢复防止故障反复触发反复冲击。配置故障时要明确两点哪些信号源触发故障发生后 A/B 两路输出变成什么状态。// 故障发生后 A、B 两路都强制输出低电平 EPWM_setTripZoneAction(EPWM1_BASE, EPWM_TZ_ACTION_LOW, EPWM_TZ_ACTION_LOW); // 使能 TZ1 作为 one-shot 故障输入 EPWM_enableTripZoneSignals(EPWM1_BASE, EPWM_TZ_SIGNAL_OSHT1);| 故障输出状态 | 适用场景 | | 高阻 | 让外部电路自己决定电平适合图腾柱驱动 | | 强制低 | 最常用直接关断功率管 | | 强制高 | 特殊拓扑需要一般不推荐 | | 不变 | 仅记录故障不干预输出 |4.3 故障恢复流程和软件模拟OST 故障触发后输出一直锁定在故障状态中断里判断是不是过流记录故障信息排除故障后写 TZCLR 寄存器清除标志再重新使能输出。还有一招特别实用软件强制故障。调试时不用真的去拉故障引脚直接调用EPWM_forceTripZoneEvent就能模拟 Trip Zone 触发用来验证你的保护逻辑是否生效。我第一次做电机驱动板时上电前都会先用这个函数强制触发一次故障确认 PWM 输出确实被拉掉再让系统正常跑。这个小步骤能避免很多“上电就炸管”的惨剧。5. 高频应用场景的配置思路呼吸灯、舵机、电机、RC滤波5.1 呼吸灯核心是让CMPA平滑变化呼吸灯本质是占空比周期性缓慢变化的 PWM。TMS320F280049C 做这个有点“大材小用”但配置思路值得讲因为几乎所有 PWM 应用最后都要落到“动态更新 CMPA”这个操作上。更新时最忌讳的是直接在主循环里高频改比较值导致波形瞬时跳变。正确做法是在周期中断里按固定步进修改 CMPA让它遵循“渐亮→渐暗”的曲线// 假设 PWM 频率 500Hz每 2ms 中断一次更新 CMPA volatile uint16_t duty 0; volatile int16_t step 8; if (duty 4999 - step) step -8; if (duty 8) step 8; duty step; EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_A, duty);注意 ePWM 的比较寄存器有 shadow 加载机制配置好加载时机后CMPA 的更新会在指定时间点比如计数器归零时统一生效而不是写寄存器瞬间立即改变这是避免波形毛刺的关键。5.2 舵机控制50Hz周期加1~2ms脉宽舵机控制不是调占空比数字而是调“高电平持续时间”。按第 2 章表格里的参数TBCLK3.125MHz、TBPRD62499 时1ms 对应 CMPA31252ms 对应 CMPA6250线性映射到 0180°// angle: 0~180 uint32_t cmp 3125 (uint32_t)(3125UL * angle / 180UL); EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_A, cmp);实际项目里舵机转动要加加减速处理否则直接跳变角度会让舵机内部齿轮承受很大冲击。可以在中断里做梯形速度规划每 10ms 把 CMPA 调整一小步。5.3 电机调速占空比边界行为的坑电机调速用的是高频 PWM比如 20kHz配置互补输出加死区通过改变 CMPA 调节占空比。这里有一个必须知道的边界行为当 CMPA 等于 TBPRD 时输出并不是 100% 恒高而是每个周期末尾会有一个时钟周期的窄低脉冲。原因在于向上计数模式下PWM 的“周期匹配”动作会把输出拉低等到下一个周期开始计数到 CMPA 时再拉高中间缺了一个时钟周期。这个现象在 STM32 上同样存在很多帖子问“STM32 定时器输出 PWM 时 100% 占空比异常”根因就在这里。CMPA0 时也有类似的窄脉冲只不过这次是窄高脉冲。| CMPA取值 | 实际输出 | | 0 | 接近 0%但可能有窄高脉冲 | | TBPRD/2 | 精确 50% | | TBPRD | 接近 100%但周期末有窄低缺口 |如果你需要严格意义上的 100% 占空比比如电池电压全加在电机上不要把 CMPA 设成 TBPRD 去“赌”直接把 PWM 输出强制为恒高更干净。5.4 PWM转模拟和RC电路PWM 经过 RC 低通滤波后可以变成模拟电平等效电压约等于占空比乘以供电电压。比如 10kΩ 电阻配 1uF 电容截止频率约 16HzPWM 频率远高于它时输出就是平滑的直流。这个思路常用于简易 DAC、调光、恒温控制——热词里“开关量 PWM 恒温控制”本质也是这回事用 PWM 占空比控制加热器的平均功率温度传感器闭环调节占空比。至于 PWM 三倍压电路方向是利用 PWM 方波驱动电容-二极管电荷泵实现低成本升压。这个属于电源拓扑设计的范畴了和 ePWM 本身的配置关系不大但能说明一件事PWM 不只是给电机和 LED 用的它在模拟电路里也有不可替代的位置。6. 把配置移植到自己板子上的关键检查点6.1 GPIO复用波形出不去的最常见原因代码算得再好波形出不来十有八九是 GPIO 复用没配。F28004x 的引脚功能不像老单片机那样固定同一个引脚可能有排功能必须显式选择它作为 EPWMxA 使用。// 把 GPIO0 复用为 EPWM1A GPIO_setPinConfig(GPIO_0_EPWM1A); GPIO_setPadConfig(0, GPIO_PIN_TYPE_STD);换引脚时一定对照芯片 datasheet 的 MUX 表格查一下某 EPWM 模块的 A 路可能只在特定几个引脚上出现不是随便哪个 GPIO 都能用。遇到“PWM 模块明明初始化成功但没有输出”的问题先查复用配置再查示波器探头有没有接对位置。6.2 初始化顺序和shadow更新初始化顺序虽然看起来琐碎但很影响排查效率。我习惯的顺序是先Device_init()初始化时钟和系统基础外设再配 GPIO 复用然后配 ePWM 时基、比较、死区、故障保护最后开中断。有一点提醒如果你在 SysConfig 里改了时钟配置回到手写代码里就不要再重复初始化 PLL否则两边配置打架PWM 频率会变得莫名其妙。6.3 验证波形和调试方法调 PWM 一定要配合示波器。看互补死区波形时把 A、B 两路同时显示开启余晖模式确认两路信号之间有明显的“双方都为低”的空白段。再用光标量一下空白段宽度和 DBRED/DBFED 计算的理论死区时间对比。没有示波器时可以用 ePWM 的事件触发 ADC 采样把关键变量送回内存在 CCS 的 Expressions 窗口里看 CMPA 和 TBPRD 的实时值再用波形图把这些变量画出来。这和 STM32 里用 Keil 仿真看 PWM 相关变量是同样的思路虽然看不到真实波形但至少能确认逻辑层占空比更新是否正确。最后说个我自己的习惯做电机驱动类项目第一次上电我不会直接让程序跑起来而是先把 TZ1 接到一个自锁按钮上上电后先按一下按钮确认 PWM 输出确实能被故障信号拉掉再继续调试。这个习惯救过我好几块板子。死区方向、故障保护动作、占空比边界这些配置最好在实验室就全部验证到位别等上了负载再出问题——那样往往代价就不是一块板子的事了。本文还有配套的精品资源点击获取