1. 项目概述与核心挑战

在电机驱动、不间断电源(UPS)或者多轴伺服控制这类电力电子应用里,我们常常会遇到一个经典问题:一个DSP控制器需要驱动不止一套三相逆变器。以经典的TI TMS320F240 DSP控制器为例,它内置的事件管理器(Event Manager)硬件资源是固定的——一个全比较单元(Full Compare Unit)自带硬件死区发生器,可以完美驱动一套三相桥臂。但当你手头的项目需要控制两套独立的电机,或者一个更复杂的多电平拓扑时,硬件资源就捉襟见肘了。第二套逆变器的六路PWM信号从哪里来?更关键的是,如何为这第二套逆变器生成同样至关重要的死区时间(Dead Time)?直接让互补的上下管PWM信号同时导通哪怕几微秒,都会导致桥臂直通(Shoot-Through),瞬间的大电流足以让昂贵的IGBT或MOSFET“烟花灿烂”。这个资源分配与安全保护的矛盾,就是项目起步时最现实的拦路虎。

我最初接触这个问题是在一个双电机同步驱动的项目上,硬件平台已经选定F240,成本控制很严,不可能换用更高端的、带更多硬件死区单元的DSP。TI那份经典的SPRA497应用报告给了我启发,它没有增加任何外部硬件,纯粹通过软件算法和芯片现有的事件管理器特性,巧妙地“榨干”了F240的PWM输出潜力,为第二套逆变器实现了可靠的软件死区。这不仅仅是写几行代码那么简单,它需要对PWM生成机制、定时器同步、影子寄存器更新时机以及中断服务程序(ISR)的时序有极其深刻的理解。一个微小的计算错误或时序偏差,轻则导致电机转矩脉动,重则直接炸管。接下来,我就结合自己多次调试和实际应用的经验,把这个方案的里里外外、坑坑洼洼都拆解清楚。

2. 硬件架构与信号分配策略

2.1 TMS320F240事件管理器PWM资源盘点

要解决问题,首先得摸清家底。TMS320F240的事件管理器是它的王牌外设,PWM生成能力是其核心。

  1. 全比较单元(Full Compare Unit) :这是主力部队。基于通用定时器1(GP Timer 1),它包含3个全比较寄存器(CMPR1, CMPR2, CMPR3),能产生 6路带硬件死区的互补PWM输出 (PWM1/2, PWM3/4, PWM5/6)。硬件死区单元(Dead-Band Unit)是集成在里面的,通过配置 DBTCON 寄存器,可以设置死区时间,硬件自动在互补信号间插入延迟,完全无需CPU干预。这6路信号天生就是为驱动一个标准的三相全桥逆变器(三个桥臂,每个桥臂上下管)准备的。

  2. 简单比较单元(Simple Compare Unit)与独立比较输出 :这是我们要挖掘的潜力股。它包含3个简单比较寄存器(SCMPR1, SCMPR2, SCMPR3),能产生3路独立的PWM输出(PWM7, PWM8, PWM9)。此外,三个通用定时器(Timer 1, 2, 3)各自还有一个独立的比较输出功能,可以产生 TxPWM 信号(即T1PWM, T2PWM, T3PWM)。注意, TxPWM 和全比较/简单比较单元是独立的通道。

所以,总的PWM输出引脚有:PWM1~PWM6(全比较),PWM7~PWM9(简单比较),以及T1PWM, T2PWM, T3PWM。加起来一共是12路独立的PWM输出引脚。硬件死区只自动服务于PWM1~6这6路。

2.2 双逆变器PWM信号连接方案

既然硬件死区只照顾第一套逆变器(Inverter #1),那么我们的目标就是用剩下的6路信号,拼凑出驱动第二套逆变器(Inverter #2)的3对互补PWM,并通过软件实现死区。连接方式非常巧妙:

  • Inverter #1 :直接使用PWM1~6。配置全比较单元,使能硬件死区。例如,PWM1驱动A相上管,PWM2驱动A相下管,硬件自动插入死区。
  • Inverter #2 :使用剩下的6路信号组合成3对“软件互补对”。
    • 上管驱动 :使用 简单比较输出 PWM7, PWM8, PWM9。将它们配置为 高电平有效 (Active High)。这意味着当定时器计数值小于比较值时,输出高电平,打开上管。
    • 下管驱动 :使用 独立定时器比较输出 T1PWM, T2PWM, T3PWM。将它们配置为 低电平有效 (Active Low)。这意味着当定时器计数值小于比较值时,输出低电平。但注意,对于下管,我们需要的是“当上管关闭时,经过一个死区延迟后,下管才打开”的逻辑。低电平有效配置正好可以与高电平有效的上管信号通过软件计算形成互补+死区的关系。

具体对应关系如下表所示:

逆变器 桥臂相位 上管驱动信号 (高有效) 下管驱动信号 (低有效)
Inverter #2 A相 PWM7 T1PWM
Inverter #2 B相 PWM8 T2PWM
Inverter #2 C相 PWM9 T3PWM

关键理解 :这里并没有使用“一个比较寄存器产生一对互补输出”的硬件逻辑。而是将 一个相位的上下管驱动,交给了两个完全独立的比较寄存器来控制 (SCMPRx 和 TxCMPR)。这就给了软件极大的灵活性:我们可以通过分别计算并设置这两个比较寄存器的值,来精确控制上管开启、上管关闭、下管开启、下管关闭这四个时刻,从而在中间插入我们想要的死区时间。

2.3 系统设计限制与前提

采用这个方案,意味着你必须接受以下几个系统级的设计约束,在项目规划初期就要考虑清楚:

  1. PWM周期必须同步 :因为第二套逆变器的死区是靠软件计算比较值实现的,而这个计算依赖于定时器的计数周期。在这个方案中,Timer 1, 2, 3都被配置为连续增/减计数模式,并且Timer 2和3的周期寄存器( TxPER )同步于Timer 1。这意味着 两套逆变器的PWM载波频率必须完全相同 。你不能让一个逆变器跑10kHz,另一个跑20kHz。

  2. 正交编码器接口(QEP)可能无法使用 :F240的QEP功能通常需要占用Timer 2和Timer 3。但在这个方案中,Timer 2和Timer 3已经被用于产生PWM(T2PWM, T3PWM)和作为简单比较单元(与Timer 1同步)的时基。因此, 如果你需要来自电机的速度/位置反馈,可能无法使用QEP模块 。替代方案是使用捕获单元(CAP)来测量脉冲频率,或者使用外部编码器接口芯片。这在SPRA497中也提到了。

  3. CPU中断开销 :软件死区需要CPU在特定的定时器时刻(下溢和周期匹配)进行中断,计算并更新比较寄存器。这意味着即使PWM生成是硬件自动的,CPU仍然每个PWM周期需要响应两次中断(对于对称PWM)。如果PWM频率很高(比如20kHz),中断开销(约几十个指令周期)就需要仔细评估,确保不会影响其他关键控制循环(如电流环)的执行。

3. 软件死区生成原理与核心算法

3.1 死区的本质与硬件实现的局限

死区的根本目的是在 同一桥臂的上下管开关状态切换时,插入一个两者都关闭的“安全区” 。假设上管(High-Side)由信号 H 控制,下管(Low-Side)由信号 L 控制。理想情况下, H L 应该是互补的。但为了防止直通,实际驱动波形应该是:

  • H 从开通(高电平)变为关断(低电平)后,延迟一个死区时间 T_dead L 才从关断(高电平,因为低有效)变为开通(低电平)。
  • L 从开通变为关断后,延迟 T_dead H 才从关断变为开通。

硬件死区单元就是在硬件逻辑里,在接收到原始的互补PWM信号后,自动对其中一个信号(通常是低侧信号)进行上升沿延迟,对另一个信号进行下降沿延迟,从而产生这个安全间隔。

软件死区的核心思想就是: 既然硬件不帮我们做,我们就自己来精确计算这两个延迟时刻对应的比较寄存器值

3.2 基于连续增/减计数模式的软件死区算法

F240的PWM生成通常采用连续增/减计数模式(Up-Down Mode),这样产生的PWM波形是关于周期中心对称的,谐波特性更好。这也是本方案的基础。

假设:

  • PWM_PERIOD :定时器周期值(对应半个电气周期,因为是从0增到PRD,再减到0)。
  • x :期望的“理想”比较值。即在没有死区的情况下,我们希望功率管在计数值等于 x 时动作。
  • d :期望的死区时间对应的计数值( d = T_dead * T_clk T_clk 为定时器时钟周期)。
  • 我们以**A相上管(PWM7,高有效) A相下管(T1PWM,低有效)**为例。

我们需要生成两套不同的比较值,分别在上管和下管需要动作的时刻(考虑死区后)加载到 SCMPR1 (控制PWM7)和 T1CMPR (控制T1PWM)中。

关键点在于加载时机 :F240的比较寄存器有影子寄存器,可以在 CNT=0 (下溢)或 CNT=PRD (周期匹配)时自动重载,避免在PWM周期中间更新导致毛刺。

  1. CNT=PRD 时刻(Timer下溢中断)

    • 此时定时器即将开始 递增计数
    • 对于 上管(PWM7,高有效) :我们希望它在计数值达到某个值时 关闭 (从高变低)。在增计数阶段,高有效PWM的输出在 CNT < CMP 时为高。为了插入死区,我们让上管 提前关闭 。即,将上管的关闭点从 x 提前到 x - d 。所以,在 CNT=PRD 时,为上管( SCMPR1 )加载的值应为 x - d
    • 对于 下管(T1PWM,低有效) :我们希望它在计数值达到某个值时 开启 (从高变低,因为低有效意味着输出低电平时管子开通)。在增计数阶段,低有效PWM的输出在 CNT < CMP 时为低(开通)。为了插入死区,我们让下管 延迟开启 。即,将下管的开启点从 x 延迟到 x + d 。所以,在 CNT=PRD 时,为下管( T1CMPR )加载的值应为 x + d
    • 简单记忆(增计数阶段) :上管提前关( -d ),下管延迟开( +d )。
  2. CNT=0 时刻(Timer周期中断)

    • 此时定时器即将开始 递减计数
    • 对于 上管(PWM7,高有效) :在减计数阶段,我们希望它在计数值达到某个值时 开启 (从低变高)。为了插入死区,我们让上管 延迟开启 。即,将上管的开启点从 x 延迟到 x + d 。所以,在 CNT=0 时,为上管( SCMPR1 )加载的值应为 x + d
    • 对于 下管(T1PWM,低有效) :在减计数阶段,我们希望它在计数值达到某个值时 关闭 (从低变高,即关断)。为了插入死区,我们让下管 提前关闭 。即,将下管的关闭点从 x 提前到 x - d 。所以,在 CNT=0 时,为下管( T1CMPR )加载的值应为 x - d
    • 简单记忆(减计数阶段) :上管延迟开( +d ),下管提前关( -d )。

总结成表格,对于一对互补的软件死区信号(上管:高有效,下管:低有效):

定时器阶段 中断发生时刻 上管比较值 (SCMPRx) 下管比较值 (TxCMPR) 动作描述
即将增计数 CNT=PRD (下溢中断) x - d x + d 上管提前关,下管延迟开
即将减计数 CNT=0 (周期中断) x + d x - d 上管延迟开,下管提前关

这里的 x 是由你的控制算法(如SVPWM、SPWM)计算出来的原始比较值, d 是死区时间对应的计数值。 x 必须严格满足约束条件: d <= x <= (PWM_PERIOD - d) 。否则,计算出的比较值可能为负或超过周期值,导致控制逻辑错误,失去死区保护甚至产生异常脉冲。

4. 软件实现与代码深度解析

理解了原理,我们来看TI示例代码的具体实现。这份汇编代码虽然古老,但清晰地展示了如何利用中断和影子寄存器实现上述算法。

4.1 系统初始化 ( init_ev 函数)

这是整个方案的基础,配置错了,后面全白搭。

_init_ev:
; ... (系统时钟配置略过)
; 配置I/O复用,将所需引脚设置为PWM功能
    splk #3F00h,OPCRA ; Port A all IO, Port B0-5 PWM
    splk #00F1h,OPCRB ; Port C as non IO function (PWM7-9, T1/2/3PWM)

; 配置全比较单元(Inverter #1)和硬件死区
    splk #55h,GPTCON ; 使能TxCMP输出,全部低有效(配合硬件死区单元)
    splk #05E8h,DBTCON; 设置硬件死区时间(例如500ns)
    splk #666h,ACTR   ; 设置全比较单元输出动作模式
    splk #02Ah,SACTR  ; 设置简单比较单元输出为高有效(用于Inverter #2上管)
    splk #32Fh,COMCON ; 使能PWM操作,设置重载条件
    splk #832Fh,COMCON; 最终使能比较操作

; 配置三个定时器为连续增/减模式,且Timer2/3同步于Timer1
    splk #PWM_PERIOD,T1PER
    splk #02806h,T1CON ; Timer1配置,内部时钟,使能比较操作
    splk #02887h,T2CON ; Timer2配置,使用Timer1的使能位和周期
    splk #0A887h,T3CON ; Timer3配置,使用Timer1的使能位和周期

; 设置中断
    splk #081h,IMRA    ; 使能Timer1周期中断(INT2)
    splk #0004h,IMRB   ; 使能Timer2下溢中断(INT3)

; 启动定时器
    lacc T1CON
    or #40h            ; 设置T1CON的bit6 (TENABLE) 启动Timer1
    sacl T1CON
    ret

关键配置解析

  • GPTCON ACTR :配置了全比较单元(PWM1-6)的输出极性。通常硬件死区单元会主动管理互补对的极性,这里设置为低有效是常见配置。
  • SACTR :将PWM7/8/9(简单比较输出)设置为 高有效 ,这是用于驱动第二逆变器上管的关键。
  • T2CON T3CON #02887h #0A887h 中的关键位是 TCLKS1/TCLKS0=10 ,这表示“使用T1的使能位”;以及 TCLD1/TCLD0=10 ,这表示“使用T1的周期值”。这确保了三个定时器完全同步,是软件死区计算能正确进行的前提。
  • 中断分配: Timer1 周期中断 CNT=PRD )连接到 INT2 Timer2 下溢中断 CNT=0 )连接到 INT3 。为什么用两个不同的定时器来产生这两个中断?是为了避免使用二级中断向量表,减少中断响应延迟。因为 INT2 INT3 是C24x内核的不同中断源。

4.2 中断服务程序(ISR)——软件死区的核心引擎

整个软件死区的魔法就在两个中断服务程序里: _c_int2 (Timer1周期中断)和 _c_int3 (Timer2下溢中断)。

_c_int2 (Timer1 Period Interrupt, CNT=PRD) 这个中断发生在定时器计数达到峰值(PRD)并即将开始递减的时刻。根据之前的算法,此时我们需要为 即将开始的减计数周期 设置比较值。

_c_int2:
; ... (上下文保存)
; 读取控制算法计算出的期望比较值 _inv2a, _inv2b, _inv2c
    ldp #DP_EV
    lar AR3,#_inv2a
    mar *,AR3
    lacc *+          ; 加载 _inv2a -> ACC
    sacl T1CMP       ; _inv2a -> T1CMPR (下管,低有效)
    add #DEADBAND    ; ACC = _inv2a + d
    sacl SCMPR1      ; _inv2a + DEADBAND -> SCMPR1 (上管,高有效)
    ; 重复上述过程处理B相(_inv2b -> T2CMPR, _inv2b+d -> SCMPR2)和C相
    ; ... (恢复上下文并返回)

这段代码在做什么? 它正在为 减计数阶段 设置比较值。对于A相:

  • 下管(T1PWM,低有效):加载 _inv2a T1CMPR 。根据算法,减计数阶段下管比较值应为 x - d 。但这里加载的是 x ?等等,这里有个 关键细节 :注意,在 _c_int2 中断里,我们是为 下一个PWM周期 (即将开始的减计数阶段)加载值。而控制算法计算出的 _inv2a ,其物理意义是 期望的导通时间点 。对于低有效信号,在减计数阶段,管子会在 CNT > CMP 时开通吗?不,我们需要重新审视。

实际上,TI的代码逻辑是统一的: _inv2a 这个变量,代表的是“下管应该开通”的那个时刻对应的比较值(对于低有效信号) 。或者说,是“理想互补信号”的跳变点。那么,根据我们之前的算法表格,在 CNT=PRD 中断(准备减计数)时,对于下管(低有效),我们应该让它 提前关断 ,所以比较值应该是 x - d 。但代码里写的是 sacl T1CMP ,即直接存入 _inv2a x )。这是矛盾的?

答案藏在极性配置和动作逻辑里 。我们设定下管信号(T1PWM)为 低有效 。在减计数阶段:

  • 我们希望下管在计数值从PRD下降到 x 关断 (从低变高)。
  • 为了插入死区,我们需要让它 提前关断 ,即在 x + d 时刻就关断。
  • 对于低有效信号,输出从低电平(开通)跳变到高电平(关断)发生在 CNT == CMP 的时刻吗?在连续增/减模式下,输出动作可以配置为在“匹配时”触发。但更常见的理解是:对于低有效,当 CNT 从小于 CMP 变化到大于 CMP 时,输出从低变高(关断)。在减计数阶段, CNT 从PRD开始下降, CNT > CMP 的条件会先满足,然后当 CNT 下降到等于 CMP 时,条件不再满足。实际上,跳变发生在 CNT 等于 CMP 的瞬间。

TI的示例代码可能采用了另一种等价的表述方式。我们不必纠结于 x x±d 的符号,关键是理解其对称性。从代码中我们可以明确看到:

  • _c_int2 中, SCMPR1 (上管)被设置为 _inv2a + DEADBAND
  • _c_int3 中, SCMPR1 (上管)被设置为 _inv2a
  • _c_int2 中, T1CMP (下管)被设置为 _inv2a
  • _c_int3 中, T1CMP (下管)被设置为 _inv2a - DEADBAND

这种对称的加/减操作,正是为了在上下管动作点之间插入一个固定的时间差( DEADBAND ),而这个时间差就是死区。只要保证 (上管关断时刻) - (下管开通时刻) >= DEADBAND 以及 (下管关断时刻) - (上管开通时刻) >= DEADBAND ,死区的目的就达到了。TI的代码逻辑是自洽的。

_c_int3 (Timer2 Underflow Interrupt, CNT=0) 这个中断发生在定时器计数归零并即将开始递增的时刻。此时需要为 即将开始的增计数周期 设置比较值。

_c_int3:
; ... (上下文保存)
; 每5个PWM周期调用一次控制算法 _control() 来更新 _inv1a,_inv1b,_inv1c, _inv2a,_inv2b,_inv2c
; ...
; 为增计数周期设置比较值
    ldp #DP_EV
    lar AR3,#_inv2a
    mar *,AR3
    lacc *+          ; 加载 _inv2a -> ACC
    sacl SCMPR1      ; _inv2a -> SCMPR1 (上管,高有效)
    sub #DEADBAND    ; ACC = _inv2a - d
    sacl T1CMP       ; _inv2a - DEADBAND -> T1CMPR (下管,低有效)
    ; 重复上述过程处理B相和C相
    ; ... (恢复上下文并返回)

这个逻辑与 _c_int2 正好形成互补。对于A相上管(SCMPR1),在增计数阶段,它被设置为 _inv2a (没有加死区),而在 _c_int2 中它被设置为 _inv2a + d 。这一加一减,就在上管自身的开通和关断时刻之间,以及相对于下管的动作时刻,插入了死区。

控制算法调用 :代码中, _c_int3 中断服务程序里有一个计数器 _int_cnt ,每发生一次 _c_int3 (即每个PWM周期)就加1。当计数达到10(因为对称PWM每个电气周期有两次中断,所以10次中断对应5个完整的PWM周期)时,调用 _control() 函数。这个函数是用户需要实现的核心控制算法(如PID调节、SVPWM生成),它负责计算出新的6个比较值( _inv1a/b/c 用于逆变器1, _inv2a/b/c 用于逆变器2)。 这里有一个至关重要的安全约束 _control() 函数必须确保计算出的 _inv2x 满足 DEADBAND <= _inv2x <= (PWM_PERIOD - DEADBAND) 。否则,在中断服务程序中进行加减 DEADBAND 操作时,可能会产生负数或超界的比较值,导致PWM输出混乱,失去死区保护。

4.3 第一套逆变器(硬件死区)的更新

你可能注意到了,在 _c_int2 中断的一开始,还有更新第一套逆变器比较值的代码:

    lacc *+  ; (_inv1a)
    sacl CMPR1 ; 更新全比较单元CMPR1
    lacc *+
    sacl CMPR2 ; 更新CMPR2
    lacc *+
    sacl CMPR3 ; 更新CMPR3

全比较单元(CMPR1/2/3)被配置为仅在 CNT=0 时重载影子寄存器(由 COMCON 寄存器设置)。而 _c_int2 CNT=PRD 中断。为什么在这里更新?因为影子寄存器机制允许你在任何时间写比较寄存器,但写入的值不会立即生效,而是先存到影子寄存器里,等到指定的重载时刻(这里是 CNT=0 )才被真正加载到工作寄存器中。所以,在 _c_int2 中断中更新 CMPRx ,是为 下一个PWM周期 (从即将开始的减计数,到下一个 CNT=0 时刻)做准备。这给了CPU充足的时间(半个PWM周期)去计算和更新这些值,避免了在中断中匆忙计算。

5. 实操要点、调试技巧与避坑指南

纸上得来终觉浅,绝知此事要躬行。把这个方案真正跑起来,并且稳定驱动电机,需要注意以下这些实战细节。

5.1 死区时间计算与参数配置

死区时间 T_dead 不是随便设的,它必须大于功率器件(IGBT/MOSFET)的关断延迟时间与开通延迟时间之差,并留有一定裕量。通常需要查阅器件数据手册中的 t_f(off) (关断下降时间)和 t_d(on) (开通延迟时间)等参数。

假设我们使用一款IGBT,其最大关断延迟为1.2μs,驱动器传播延迟为0.3μs,我们留0.5μs裕量。那么所需死区时间可估算为: T_dead = t_f(off) + t_driver + t_margin ≈ 1.2 + 0.3 + 0.5 = 2.0 μs

在F240中,定时器的时钟基准 T_clk 由系统时钟分频得到。假设系统时钟为20MHz,定时器预分频为1,则 T_clk = 50ns 。那么死区对应的计数值 d 为: d = T_dead / T_clk = 2.0μs / 50ns = 40

在代码中, DEADBAND 就被定义为这个值。同时,PWM周期 PWM_PERIOD 也需要根据开关频率计算。例如,开关频率 f_pwm = 10kHz ,对应周期 T_pwm = 100μs 。在连续增/减模式下,定时器从0计数到PRD再回到0是一个完整的三角波,对应 半个PWM载波周期 。因此,定时器峰值 PRD 与PWM周期关系为: T_pwm = 2 * PRD * T_clk 。 所以, PRD = T_pwm / (2 * T_clk) = 100μs / (2 * 50ns) = 1000 。 在代码中, PWM_PERIOD 就应设置为1000。

务必检查约束条件 :对于第二逆变器的任何一相比较值 x ,必须保证 d <= x <= (PRD - d) ,即 40 <= x <= 960 。你的SVPWM或SPWM算法必须在生成 x 时加入这个限幅。

5.2 中断服务程序优化与实时性保障

  • 中断嵌套与优先级 INT2 (Timer1周期中断)和 INT3 (Timer2下溢中断)每个PWM周期各发生一次。示例代码中,在 _c_int2 里用 clrc INTM 之前就 ret 了,意味着 INT2 是不可中断的,以保证其执行不被干扰。而 _c_int3 中较早地 clrc INTM ,允许自身被 INT2 打断,这是为了确保即使 _c_int3 中调用的 _control() 函数执行时间较长,也不会错过 INT2 的及时响应,从而保证PWM生成的连续性。你需要根据 _control() 函数的实际执行时间来评估这种设计。如果 _control() 很耗时,可能需要在 _c_int3 中先关闭全局中断,执行关键的比较值更新操作后再打开。
  • 中断延迟 :中断响应、上下文保存/恢复都需要时间。你需要计算从中断发生到ISR中真正更新比较寄存器这条指令执行完毕所需的最大指令周期数。这个时间必须小于死区时间 T_dead 。否则,你计算好的死区可能会因为中断延迟而部分甚至完全失效。F240的中断响应时间大约是几个时钟周期,加上ISR入口的上下文保存(示例代码中较多),总共可能几十个周期。对于50ns的时钟,几十个周期也就一两微秒,这与我们2μs的死区时间处于同一量级, 必须仔细核算 !必要时需要精简ISR,或者使用更高级的芯片。
  • 影子寄存器更新时机 :一定要确认 COMCON T1CON 等寄存器中关于比较寄存器重载时机的配置位( CLD1/CLD0 等)是否与你的中断设计匹配。示例中全比较单元只在 CNT=0 重载,而简单比较和独立比较在 CNT=0 CNT=PRD 都重载,这与两个中断服务程序的分工是吻合的。

5.3 调试方法与常见问题排查

  1. 无输出或输出异常

    • 检查时钟和定时器使能 :确认系统时钟PLL配置正确, T1CON TENABLE 位已置1。
    • 检查I/O复用配置 OPCRA OPCRB 寄存器配置是否正确,确保PWM引脚功能被启用,而不是普通的GPIO。
    • 用示波器观察 :先不接电机,用示波器同时测量同一桥臂的上下管驱动信号(如PWM7和T1PWM)。确保有PWM波形输出,并且两者之间有明显的不重叠区域(死区)。
  2. 死区时间不正确或不对称

    • 检查 DEADBAND 常量 :确认其值计算正确,并且与 PWM_PERIOD 匹配。
    • 检查控制算法输出 :在调试阶段,可以固定 _inv2a/b/c 为一个中间值(如 PRD/2 ),然后测量死区时间是否等于 DEADBAND * T_clk
    • 检查中断服务程序中的计算 :确认在 _c_int2 _c_int3 中,对 SCMPRx TxCMPR 的加减法操作是正确的,没有弄反。一个简单的验证方法是:让 _inv2a 为一个很小的值(略大于 DEADBAND )和一个很大的值(略小于 PWM_PERIOD - DEADBAND ),观察死区是否依然保持。
  3. 电机运行异常,有噪声或抖动

    • 死区时间不足 :这是最危险的情况。用示波器的高分辨率模式,放大上下管驱动信号的上升/下降沿交叉点,确认在任何占空比下,两个信号都绝对没有重叠。如果发现重叠,立即增大 DEADBAND 值。
    • 死区补偿 :死区时间会引入电压误差,导致低速时转矩脉动。需要在控制算法( _control() 函数)中加入死区补偿。常见方法是在计算出的电压指令上,根据电流方向叠加一个补偿电压。
    • 比较值限幅 :再次强调,确保 _control() 函数输出的 _inv2x 被严格限制在 [DEADBAND, PWM_PERIOD-DEADBAND] 区间内。可以在 _control() 函数内部或更新变量前加入限幅函数。
  4. 系统运行不稳定,偶尔炸管

    • 中断冲突或超时 :使用逻辑分析仪或调试器检查 INT2 INT3 的中断响应是否及时。确保没有其他更高优先级的中断长时间关闭全局中断,导致PWM中断被延误。
    • 变量共享与一致性 _inv2a/b/c 等变量在主循环(或 _control() 函数)和两个ISR中都会被访问。虽然示例中ISR只在 _control() 更新后读取它们,但为了安全,最好将这些变量声明为 volatile ,防止编译器优化导致意外。如果 _control() 计算时间超过5个PWM周期,需要考虑更复杂的同步机制。

5.4 方案扩展与替代思路

  • 更多逆变器 :F240最多只有12路PWM输出。如果需要驱动超过两个三相逆变器,此方案无法直接扩展。需要考虑使用多片DSP,或者选用PWM资源更丰富的控制器,如TI的C2000系列后续型号(如F2833x, F280x等),它们通常有更多带硬件死区的PWM通道。
  • 更高频率与更低延迟 :当PWM频率很高(如50kHz以上)或死区时间要求很小时(如几百纳秒),软件死区的中断开销和计算延迟可能变得不可接受。此时必须使用硬件死区单元。可以考虑使用外部CPLD或FPGA来扩展硬件死区功能,或者升级DSP平台。
  • 不对称死区 :有时上下管的开关特性不同,需要不对称的死区时间(开通延迟和关断延迟不同)。硬件死区单元通常是对称的。而软件死区方案可以灵活地实现不对称死区,只需在 _c_int2 _c_int3 中为上下管分别加减不同的值即可,提供了更大的灵活性。

这个基于TMS320F240的软件死区实现方案,是早期DSP资源受限情况下的一种经典而巧妙的解决方案。它完美诠释了如何通过深入理解硬件特性和精准的软件时序控制,来突破硬件限制,实现复杂功能。虽然如今芯片性能今非昔比,但其中蕴含的中断设计、影子寄存器运用、时序计算等思想,在嵌入式系统开发中依然具有很高的参考价值。在实际项目中,我建议在资源允许的情况下优先选用硬件资源更丰富的芯片,但如果确实遇到类似限制,这个方案无疑是一把可靠的开山斧。

更多推荐