TMS320F280049C ePWM死区控制实战:寄存器配置与代码实现
1. 为什么死区控制值得单独拎出来讲但凡用过C2000系列DSP做电机驱动、数字电源或者逆变器的朋友大概率都踩过这样一个坑代码逻辑看着没问题PWM波形在示波器上也方方正正结果一上电半桥上下管直接炸了。炸管的原因有很多但十有八九绕不开一个关键词——死区。TMS320F280049C这颗芯片在实时控制圈子里口碑很好100MHz主频、CLA协处理器、片上12位ADC、以及那套打磨了十几年的ePWM模块做数字电源和电机控制基本是标配。但它的ePWM死区配置寄存器DBCTL、DBRED、DBFED、DB Rising Edge Delay等散落在TRM手册的几百页里第一次上手的人很容易配错。配错的后果不是波形难看那么简单而是功率管直通短路轻则烧管重则连驱动芯片一起带走。这篇内容就是围绕“用TMS320F280049C实现精准PWM死区控制”这个主题把我在实际项目里反复验证过的配置思路、寄存器操作、代码实现和踩坑经验完整梳理一遍。适合正在用F280049C做电机FOC、LLC谐振变换器、光伏逆变或者Buck/Boost同步整流的工程师也适合刚接触C2000 ePWM模块、想搞明白死区到底怎么算、怎么配、怎么验证的朋友。代码部分基于TI的driverlib库可以直接在CCS里编译运行我会把关键寄存器的每一位都拆开讲清楚。2. 死区到底在防什么先搞懂原理再动手2.1 半桥直通的物理本质先把这个事情说透。一个典型的半桥拓扑上管Q1和下管Q2串联在母线电压Vbus和地之间中间节点就是输出。理想情况下Q1导通时Q2必须关断Q2导通时Q1必须关断。但现实是MOSFET和IGBT都不是理想开关它们有开通延迟、关断延迟而且关断过程还分电流拖尾阶段。问题出在开关切换的瞬间。假设Q1正在导通电流从Vbus流向负载。现在要让Q1关断、Q2导通。Q1的关断需要时间在这段时间里Q1的沟道还在导通。如果Q2这时候已经开通了那Vbus就直接通过Q1和Q2短路到地了。这个短路电流只受限于回路寄生电阻和管子导通电阻瞬间就能达到几百安培管子几微秒内就热失效了。死区时间就是在这个切换窗口里插入的一段“两个管子都关断”的空白期。Q1先关断等一段时间让Q1彻底关断再让Q2开通。反过来Q2关断到Q1开通之间也要插一段。这两段时间分别叫上升沿死区和下降沿死区或者叫死区上升沿延迟和死区下降沿延迟。2.2 死区时间不够和过长的双重代价死区时间配短了直通风险直线上升。配长了也不行因为死区期间负载电流只能通过体二极管续流体二极管的正向压降比MOSFET导通压降大得多损耗增加输出波形畸变也严重。在电机FOC控制里死区过长会导致电流过零点附近出现明显的波形失真低速时转矩脉动变大。所以死区时间的设定是一个权衡刚好覆盖功率管的最坏关断时间再加一点裕量。这个“最坏关断时间”跟管子型号、驱动电路、栅极电阻、温度都有关。工程上一般取管子数据手册里关断延迟时间t_off的最大值再乘1.2到1.5的裕量系数。举个例子某款MOSFET的t_off(max) 120ns驱动芯片传输延迟最大50ns那死区时间至少取(12050)×1.3 ≈ 220ns。实际项目中我一般会留到250ns到500ns之间具体看开关频率和效率要求。2.3 F280049C的ePWM死区模块能做什么F280049C的每个ePWM模块都自带独立的死区子模块支持三种模式模式0死区禁用上下管信号完全由AQ模块决定不插入任何延迟。模式1上升沿延迟有效只对PWMxA的上升沿插入延迟PWMxB不变。模式2下降沿延迟有效只对PWMxA的下降沿插入延迟。模式3上升沿和下降沿都延迟这是半桥互补输出最常用的模式。死区延迟的时钟源可以选择ePWM时钟的预分频版本分频系数从1到8配合16位计数器能覆盖从几十纳秒到几十微秒的延迟范围。这个灵活性是F280049C比早期C2000芯片强的地方早期芯片的死区分辨率固定调起来很别扭。3. 寄存器级拆解DBCTL和DBRED/DBFED怎么配3.1 死区控制寄存器DBCTL的位域解析DBCTL是死区模块的总控寄存器关键位域如下位域名称功能说明bit1:0OUT_MODE死区模式选择0禁用1上升沿延迟2下降沿延迟3双边延迟bit3:2POLSEL极性选择控制PWMxA和PWMxB是否反相bit7:4IN_MODE输入模式选择死区模块的输入源是AQ输出还是其他bit15:8保留保留位写0bit31:16保留保留位写0实际配置时半桥互补输出最常用的组合是OUT_MODE 3双边延迟POLSEL根据驱动芯片需求设置。如果驱动芯片是高电平有效、上下管独立输入那POLSEL一般设为0PWMxA走上升沿延迟PWMxB走下降沿延迟。3.2 上升沿和下降沿延迟寄存器DBRED寄存器存放上升沿延迟的计数值DBFED存放下降沿延迟的计数值。这两个都是16位寄存器计数值乘以死区时钟周期就是实际延迟时间。计算公式死区时间 DBxED × T_deadtime_clock T_deadtime_clock (DBCTL[DBFED]分频系数) / SYSCLKF280049C的SYSCLK默认100MHz如果死区时钟分频系数设为1那每个计数单位就是10ns。要得到250ns死区DBRED和DBFED都写25。但这里有个细节DBRED和DBFED可以写不同的值。有些场景下上升沿和下降沿需要的死区时间不一样比如驱动芯片上下管传输延迟不对称就可以分别设置。我做过一个项目上管驱动用的是光耦下管用的是磁耦两者延迟差了80ns就是靠DBRED和DBFED分别补偿的。3.3 死区时钟分频的选择逻辑DBCTL里还有几位控制死区时钟的分频系数。分频系数越大死区分辨率越低但能覆盖的延迟范围越大。选择逻辑很简单需要的死区时间 65536 × 10ns 655μs分频系数选1就够了。如果死区时间很短比如50ns分频系数选1写5就行分辨率10ns误差可控。如果死区时间很长比如10μs分频系数选1写1000也行但分辨率还是10ns没必要分频。实际上分频系数主要影响的是分辨率。分频系数越大每个计数单位代表的时间越长调节粒度越粗。对于大多数开关频率在100kHz到500kHz之间的应用死区时间在100ns到1μs之间分频系数选1完全够用。4. 完整代码实现从寄存器操作到driverlib封装4.1 工程环境与依赖说明代码基于TI的C2000Ware和driverlib库开发环境是Code Composer Studio。工程需要包含以下文件driverlib/epwm.h和driverlib/epwm.cdriverlib/gpio.h和driverlib/gpio.cdevice.h和driverlib/device.c链接命令文件F280049C.cmd如果你用的是C2000Ware里的示例工程这些都已经配好了。我下面的代码是在epwm_ex3_synchronization示例基础上改的去掉了同步功能专注死区配置。4.2 初始化函数完整代码#include driverlib.h #include device.h #define EPWM1_BASE EPWM1_BASE #define DEADTIME_NS 250U #define SYSCLK_FREQ 100000000UL void InitEPWM1WithDeadband(void) { // // 配置GPIO0和GPIO1为ePWM1A和ePWM1B输出 // GPIO_setPadConfig(0, GPIO_PIN_TYPE_STD); GPIO_setPinConfig(GPIO_0_EPWM1A); GPIO_setDirectionMode(0, GPIO_DIR_MODE_OUT); GPIO_setPadConfig(1, GPIO_PIN_TYPE_STD); GPIO_setPinConfig(GPIO_1_EPWM1B); GPIO_setDirectionMode(1, GPIO_DIR_MODE_OUT); // // 配置ePWM1时基 // 开关频率100kHz周期 100MHz / 100kHz 1000 // EPWM_setTimeBasePeriod(EPWM1_BASE, 1000U - 1U); EPWM_setPhaseShift(EPWM1_BASE, 0U); EPWM_setTimeBaseCounter(EPWM1_BASE, 0U); EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP_DOWN); EPWM_setClockPrescaler(EPWM1_BASE, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1); // // 配置计数比较器A和B // 占空比50%CMPA 500CMPB 500 // EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_A, 500U); EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_B, 500U); // // 配置动作限定器AQ // 上下计数模式下CMPA匹配时翻转PWM1A // EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO); EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_PERIOD); EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO); EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_B, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_PERIOD); // // 配置死区模块 // 模式3双边延迟 // 死区时钟分频 1每个计数单位10ns // 250ns / 10ns 25 // EPWM_setDeadBandDelayMode(EPWM1_BASE, EPWM_DB_RED, true); EPWM_setDeadBandDelayMode(EPWM1_BASE, EPWM_DB_FED, true); EPWM_setDeadBandDelayPolarity(EPWM1_BASE, EPWM_DB_RED, EPWM_DB_POLARITY_ACTIVE_HIGH); EPWM_setDeadBandDelayPolarity(EPWM1_BASE, EPWM_DB_FED, EPWM_DB_POLARITY_ACTIVE_HIGH); // // 设置死区计数值 // 250ns / 10ns 25 // EPWM_setRisingEdgeDelayCount(EPWM1_BASE, 25U); EPWM_setFallingEdgeDelayCount(EPWM1_BASE, 25U); // // 配置死区时钟分频 // EPWM_setDeadBandCounterClock(EPWM1_BASE, EPWM_DB_COUNTER_CLOCK_FULL_CYCLE); // // 使能ePWM输出 // EPWM_setEmulationMode(EPWM1_BASE, EPWM_EMULATION_FREE_RUN); }4.3 代码逐段解析与参数计算上面这段代码里几个关键参数的计算过程需要说清楚。时基周期计算F280049C的SYSCLK默认100MHzePWM时钟预分频设为1所以ePWM时钟就是100MHz。要得到100kHz的开关频率周期寄存器值 100MHz / 100kHz 1000。因为用的是上下计数模式实际开关频率 ePWM时钟 / (2 × TBPRD)所以TBPRD 1000 - 1 999。上下计数模式下一个完整周期计数器从0数到999再数回0所以实际周期是2×1000个时钟对应100kHz。死区计数值计算死区时钟分频设为1死区时钟频率 100MHz。每个计数单位 1/100MHz 10ns。要得到250ns死区计数值 250ns / 10ns 25。这个25就是写入DBRED和DBFED的值。占空比设置CMPA 500在上下计数模式下PWM1A在计数器等于CMPA时翻转。因为周期是1000CMPA500对应50%占空比。实际项目中占空比会根据控制环路动态调整但死区配置不变。4.4 验证波形的方法代码烧进去之后怎么确认死区真的生效了最直接的方法是用示波器看PWM1A和PWM1B的波形。把两个通道的探头分别接到GPIO0和GPIO1触发方式设为PWM1A上升沿时基打到100ns/div左右。正常的波形应该是PWM1A上升沿之后PWM1B延迟250ns才上升PWM1B下降沿之后PWM1A延迟250ns才上升。两个信号在任何时刻都不会同时为高。如果看到两个信号有重叠区域说明死区没生效检查DBCTL的OUT_MODE是不是写成了3或者DBRED/DBFED是不是写成了0。还有一种情况是死区时间明显不对比如设了250ns但实测只有100ns。这时候要检查死区时钟分频是不是被别的代码改了或者SYSCLK不是100MHz。F280049C可以通过PLL配置成其他频率如果SYSCLK变了死区时间也会跟着变。5. 实操中容易踩的坑和排查技巧5.1 死区配置了但波形没变化这是最常见的问题。代码里明明调用了EPWM_setDeadBandDelayMode但示波器上看不到死区。排查顺序如下第一确认ePWM模块的时钟使能了。F280049C的外设时钟默认是关闭的需要在Device_init()之后调用SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_EPWM1)。如果时钟没使能所有寄存器写入都是无效的。第二确认GPIO复用配置正确。GPIO0和GPIO1要配置成ePWM1A和ePWM1B功能如果还停留在默认的GPIO功能波形根本出不来。第三检查AQ模块的配置。死区模块是在AQ之后生效的如果AQ没有正确配置输出翻转死区模块输入就是恒定的自然看不到延迟效果。第四确认DBCTL的OUT_MODE位。driverlib的EPWM_setDeadBandDelayMode函数会同时设置RED和FED但如果你只调用了其中一个OUT_MODE可能只配了一半。5.2 死区时间实测偏差大设了250ns实测300ns或者200ns这种偏差通常来自几个方面SYSCLK实际频率不是100MHz。F280049C的PLL配置如果用了外部晶振实际频率可能跟标称值有偏差。用示波器测一下ePWM输出频率反推SYSCLK。死区时钟分频被覆盖。有些库函数会重置DBCTL寄存器如果你在死区配置之后又调用了其他ePWM配置函数可能会把分频设置冲掉。示波器探头带宽不足。测ns级延迟需要至少500MHz带宽的探头低带宽探头会把边沿变缓看起来延迟变大了。驱动芯片传输延迟。如果你测的是驱动芯片输出而不是DSP引脚那测到的延迟包含了驱动芯片的传输时间。要测DSP原始输出探头直接接GPIO。5.3 上下管驱动不对称怎么补偿实际项目中上下管的驱动电路往往不对称。上管可能用自举供电下管用独立电源上管驱动芯片的传输延迟可能比下管大几十纳秒。这种情况下DBRED和DBFED可以设不同的值来补偿。补偿方法是先测出上下管驱动电路的传输延迟差Δt然后把DBRED和DBFED的差值设为Δt对应的计数值。比如上管延迟比下管大80ns那就把DBRED设成25833DBFED设成25这样上管开通前多等80ns保证上下管都不会直通。5.4 死区与ADC采样触发的配合F280049C的ePWM模块可以触发ADC采样这个功能在电机FOC和数字电源里非常常用。死区配置会影响ADC触发点的选择。一般来说ADC采样应该在PWM周期中间点触发这时候电流纹波最小采样最准确。在上下计数模式下计数器等于0或者等于TBPRD时触发ADC。但要注意死区期间输出电平是不确定的如果ADC触发点落在死区窗口里采样到的电流可能不准确。我的做法是把ADC触发点设在计数器等于CMPA或者CMPB的时候这时候PWM输出已经稳定死区也结束了。具体配置用EPWM_setADCTriggerSource和EPWM_setADCTriggerEventPrescale函数。6. 进阶话题死区对系统性能的实际影响6.1 死区导致的输出电压损失死区期间负载电流通过体二极管续流输出电压会偏离理想值。在Buck变换器里死区会导致实际占空比小于设定值输出电压偏低。这个偏差在低压大电流应用里尤其明显。补偿方法有两种一是直接在占空比计算里加一个死区补偿项根据电流方向判断补偿量二是用更高精度的死区配置把死区时间压到刚好够用。我一般倾向于第二种因为第一种需要知道电流方向在过零点附近容易误判。6.2 死区与开关损耗的权衡死区时间越长体二极管续流时间越长反向恢复损耗越大。在硬开关拓扑里这个损耗可能占总损耗的10%到20%。把死区时间从500ns压到200ns效率可能提升0.5到1个百分点。但压死区时间不能盲目。要先确认功率管的最坏关断时间再留够裕量。我见过有人把死区压到50ns结果批量生产时因为管子批次差异部分板子直通炸管。死区时间的设定要覆盖所有工况和所有器件批次不能只按典型值来。6.3 不同拓扑对死区的要求差异同步Buck死区要求相对宽松因为体二极管续流时间短影响不大。典型值200ns到500ns。电机驱动逆变器死区要求严格因为死区会导致电流波形畸变影响FOC控制精度。典型值100ns到300ns。LLC谐振变换器死区时间跟谐振周期相关需要精确匹配。典型值100ns到200ns而且对死区稳定性要求很高。光伏逆变器死区要求跟电机驱动类似但还要考虑并网电流的THD指标。典型值200ns到400ns。7. 调试工具与实测记录7.1 用CCS的寄存器查看器验证配置Code Composer Studio里有一个Register View功能可以实时查看ePWM模块的所有寄存器值。调试死区配置时我习惯把DBCTL、DBRED、DBFED这三个寄存器加到Watch窗口里。配置完成后DBCTL的值应该是0x0000000F左右OUT_MODE3POLSEL根据实际设置DBRED和DBFED应该是250x19。如果DBCTL读出来是0说明死区模块没使能如果DBRED是0说明计数值没写进去。7.2 示波器实测波形分析我用一台500MHz带宽的示波器探头用10:1无源探头接地弹簧尽量短。触发设在PWM1A上升沿时基100ns/div。实测波形显示PWM1A上升沿到PWM1B上升沿之间的延迟是252nsPWM1B下降沿到PWM1A上升沿之间的延迟是248ns。跟设定的250ns偏差在2%以内这个精度对于大多数应用足够了。如果实测偏差超过10%就要检查SYSCLK频率和死区时钟分频。F280049C的内部振荡器精度有限如果对死区精度要求极高建议用外部晶振。7.3 常见问题速查表现象可能原因排查方法无PWM输出GPIO复用未配置检查GPIO_setPinConfig死区无效DBCTL OUT_MODE0读DBCTL寄存器死区时间偏大SYSCLK频率偏低测ePWM输出频率反推死区时间偏小死区时钟分频被改检查DBCTL分频位上下管同时导通DBRED/DBFED设反交换RED和FED值波形畸变死区过长减小死区时间ADC采样不准触发点落在死区窗口调整ADC触发源8. 代码扩展多路ePWM同步死区配置实际项目里往往不止一路ePWM三相电机驱动需要三路互补PWM每路都要配死区。F280049C支持ePWM模块之间的同步可以让多路ePWM的时基同步死区配置也保持一致。同步配置的关键是设置EPWM_setSyncInPulseSource和EPWM_setSyncOutPulseMode。把EPWM1设为主模块EPWM2和EPWM3设为从模块从模块的时基同步信号来自主模块。这样三路PWM的计数器同步翻转死区延迟也同步生效。死区配置本身不需要同步每个ePWM模块有独立的DBCTL和DBRED/DBFED寄存器。但为了保持一致建议把三路的死区计数值设成相同的值。如果某一路的驱动电路延迟不同可以单独调整。代码上把初始化函数封装成带参数的形式传入ePWM基地址和死区计数值就可以复用到多路void InitEPWMWithDeadband(uint32_t epwmBase, uint16_t deadbandCount) { EPWM_setDeadBandDelayMode(epwmBase, EPWM_DB_RED, true); EPWM_setDeadBandDelayMode(epwmBase, EPWM_DB_FED, true); EPWM_setRisingEdgeDelayCount(epwmBase, deadbandCount); EPWM_setFallingEdgeDelayCount(epwmBase, deadbandCount); // ... 其他配置 }调用时传入EPWM1_BASE、EPWM2_BASE、EPWM3_BASE和相同的deadbandCount即可。9. 个人实操体会与几个小技巧死区配置这件事说难不难说简单也不简单。我做了这么多年电机和电源项目死区炸管的次数一只手数得过来但每次炸管都印象深刻。分享几个我踩过坑之后总结的经验。第一个技巧死区时间先用大后调小。新板子第一次上电死区时间先设大一点比如1μs确认波形正常、没有直通之后再逐步减小到目标值。这样即使配置有误也不至于一上电就炸管。第二个技巧用电流探头看直通电流。如果怀疑有直通可以在母线回路上套一个电流探头看开关瞬间有没有尖峰电流。正常的开关过程电流是平滑的如果有直通会看到明显的尖峰。第三个技巧死区时间要覆盖全温度范围。功率管的关断时间随温度升高而增加常温下测的死区时间够用高温下可能就不够了。做可靠性验证时要把板子放到高温箱里在最高工作温度下再测一遍死区。第四个技巧不同批次的管子要重新验证。MOSFET的关断延迟时间有批次差异换供应商或者换批次之后最好重新测一下死区裕量。我吃过这个亏同一型号的管子换了批次关断时间多了30ns死区裕量不够批量生产时炸了几块板子。最后一个技巧死区配置写进初始化代码不要动态改。有些朋友想在运行中动态调整死区时间比如根据负载电流改变死区。这个做法风险很大因为死区寄存器写入不是原子操作写入过程中可能出现短暂的死区为零的状态直接导致直通。如果非要动态调整一定要在PWM输出关闭的情况下改改完再使能输出。死区控制是功率电子里最基础也最要命的一环配好了系统稳定运行配错了就是炸管冒烟。希望这篇内容能帮到正在用F280049C做项目的朋友少走点弯路少炸几个管子。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →