LLC硬件框架与底层软件架构深度解析

LLC硬件框架与底层软件架构深度解析

在现代大功率数字开关电源设计中,LLC 谐振变换器因其高效率、高功率密度以及优异的软开关特性而被广泛应用。本章将以 TMS320F28034 微控制器为核心,详细拆解全桥 LLC 变换器的底层硬件信号连接拓扑与中断驱动式的软件工程架构。


硬件框架与端口配置说明

数字电源的核心在于通过 DSP(数字信号处理器)对模拟电路进行高频采样与精准的脉冲控制。基于 TMS320F28034 控制的全桥 LLC 整体硬件框架及其信号连接关系如下图所示。

LLC硬件框架以及信号连接图

如上图所示,图中清晰标出了 TMS320F28034 控制器与全桥 LLC 主功率电路、以及辅助电路(如与 PFC 级的信息交互逻辑)的信号物理连接定义。为了方便软件底层配置,我们将控制板上 TMS320F28034 控制器的端口复用功能进行了详细梳理,具体分配情况如下表所示:

信号名称内容定义控制板信号引脚硬件说明与功能解析
ADC_VOUT输出电压信号ADCINA0ADCA0通道:用于电压闭环控制的高频反馈采样。
ADC_IOUT输出电流信号ADCINB0ADCB0通道:用于电流闭环及限流保护的反馈采样。
ADC_VADJ调压/调流电位器信号ADCINA1ADCA1通道:读取外部模拟旋钮或电位器,设定输出基准。
DC_MOS_T散热器温度信号1ADCINB1ADCB1通道:监控一次侧 MOSFET 散热器温度,用于过温降额或保护。
DC_DIO_T散热器温度信号2ADCINA3ADCA3通道:监控二次侧整流二极管散热器温度。
OCPLLC电流信号(过流保护)ADCINA2ADCA2通道:连接内部比较器,实现微秒级硬件快速过流封波保护。
PWM1AMOS驱动PWM信号PWM1AePWM模块1A通道:驱动全桥 LLC 桥臂功率管(如 Q1/Q4)。
PWM1BMOS驱动PWM信号PWM1BePWM模块1B通道:驱动全桥 LLC 桥臂功率管(如 Q2/Q3),与 1A 呈互补带死区输出。
LED_G显示灯-绿灯控制信号GPIO32GPIO32口:用于指示系统正常运行状态。
LED_Y显示灯-黄灯控制信号GPIO22GPIO22口:用于指示系统待机或警告状态。
LED_R显示灯-红灯控制信号GPIO33GPIO33口:用于指示系统触发严重故障状态。
I2C_SDAI2C数据信号-OLED控制I2C1_SDAI2C通信数据口:驱动外部液晶屏幕显示实时电压电流。
I2C_SCLI2C时钟信号-OLED控制I2C1_SCLI2C通信时钟口:提供屏幕通信的同步时钟。
CANRXACAN通信接收端CANRXACAN通信接收口:接入工业总线,接收上位机或其他模块指令。
CANTXACAN通信发送端CANTXACAN通信发送口:向上位机广播 LLC 的实时运行状态与遥测数据。
D_SCI_RXDSCI通信接收信号SCIARXDSCIA通信接收口:主要用于与前级 PFC 控制板进行数据与状态握手。
D_SCI_TXDSCI通信发送信号SCIATXDSCIA通信发送口:向 PFC 控制板发送 LLC 侧的负载与状态数据。
PFC_OKPFC状态IO口信号GPIO24GPIO24口:硬线握手信号。仅当 PFC 侧母线电压建立并稳定后,才允许 LLC 启动抽电。

整体软件架构与中断嵌套机制说明

为了极大简化系统软件架构的时间轴管理,同时增加软件系统的可维护性与可移植性,本系统摒弃了低效的前后台轮询模式,转而采用了以中断事件触发为核心思想的高实时性软件设计方案。

整体 LLC 的软件系统架构与中断优先级分布如下图 1.2 所示:

整体软件架构

系统运行总览与多任务机制:

  1. 系统初始化与主循环: LLC 控制器在初次上电或硬件复位后,首先集中执行 DSP 所有底层硬件外设的初始化,并配置打开各组模块功能所需的中断使能。随后,程序跳转并陷入一个无尽的 for 死循环中。这个主循环专门用于处理那些时间资源占比大但对实时性要求不高的后台程序,例如 OLED 液晶显示屏的点阵刷新程序与 CAN 报文的主体打包程序。
  2. 四大核心中断(按优先级由高到低):
    • 50kHz 中断 (核心环路):承担微秒级的闭环控制,优先级绝对最高。
    • 200Hz 中断 (状态机与慢保护):承担毫秒级的全局状态调度。
    • SCI 接收中断:负责 PFC 级间通信的字节接收。
    • CAN 接收中断:负责总线数据的底层拦截。
  3. 中断嵌套机制(Interrupt Nesting): 整个软件系统在运行过程中均开启了中断嵌套功能。这意味着,优先级最高的中断(如 50kHz 控制环路)可以在任意时刻强势打断正在执行的低优先级中断(如 200Hz 状态机或通信中断),确保电源的高频发波控制不会因为串口通信等杂务而产生致命的延迟失控。
  4. 通信中断的减负设计: 值得特别注意的是,为了合理珍惜宝贵的高频中断 CPU 资源,在 SCI 接收中断和 CAN 接收中断内,程序仅仅只对硬件接收到的数据字节进行快速转存(Buffer 缓存)处理,绝对不在此处进行复杂的数据解析与操作。真正的数据校验与逻辑处理全部下放回主函数的 for 循环或慢速中断中执行。

工程文件拆解与核心函数全景图

优秀的软件架构不仅体现在运行逻辑上,也体现在工程文件的模块化解耦设计上。本设计遵循了规范简易原则,将不同频率、不同功能的操作严格封装在对应的源文件中。下表为整个 LLC 控制工程的文件划分定义与功能速览:

核心源文件与头文件清单

文件名核心使命与功能说明
main.c主函数文件:负责硬件初始化配置,并持有主函数无限循环(调度 OLED 刷新与 CAN 通信等非时敏任务)。
ISR1.c / ISR1.h50kHz 核心中断文件:这是电源的灵魂。包含高速采样数据获取、电压/电流环路运算、Burst (打嗝) 模式控制、参考指令计算、PWM 寄存器底层刷新以及致命错误的快速保护函数。头文件包含相关高频宏定义。
ISR2.c / ISR2.h200Hz 慢速中断文件:统筹大局。包含系统的慢速保护判别、LLC 主运行状态机(FSM)、输出参考电压的爬坡与计算、SCI 发送调度以及状态指示灯的点亮逻辑。
SCIcom.c / SCIcom.hSCI 通信模块:处理与前级 PFC 控制器的交互,包含硬件接收中断、通信状态机处理、数据打包与发送。
CANcom.c / CANcom.hCAN 总线通信模块:包含 CAN 底层初始化、邮箱接收中断、CAN 主业务流程序及报文发送。
Function.c / .h多功能工具函数合集:包含细化的状态机各分支处理函数、电流底层偏置校准函数、以及各种保护逻辑(OVP过压、OCP过流、OTP过温等)的具体实现。

重点模块函数功能详细定义

为了后续代码分析的连贯性,以下将对各个文件中的关键函数 API 及其控制论功能进行深度解析说明:

【系统总控与配置:main.c】

  • main():工程总入口,串联所有初始化并启动系统大循环。
  • SysCtrlInit():配置 DSP 内部 PLL、看门狗及高速系统时钟。
  • Timer0Init():配置硬件 CPU 定时器 0,以 20us 为周期触发 50kHz 的核心闭环控制中断。
  • Timer1Init():配置硬件 CPU 定时器 1,以 5ms 为周期触发 200Hz 的全局状态机中断。
  • GPIOInit() / ePWMInit() / ADCInit():底层片上外设引脚与功能模块的初始化映射。
  • ePWMDebugInit():特殊的调试配置,利用 ePWM 的 4A 通道经过 RC 滤波模拟 DAC 输出,便于在示波器上实时观察系统内部环路变量。
  • TZInit():Trip-Zone(错误联锁)事件初始化。极其关键的底层硬件保护配置,利用比较器捕捉过流毛刺,能在不经过 CPU 运算的情况下直接物理封锁 PWM 脉冲。

【极速控制环路:ISR1.c (50kHz)】

  • ISR_20US():20us 中断服务程序的总入口。
  • ADCSample():从 ADC 结果寄存器中提取电压、电流及温度的原始数字量并做一次滤波整理。
  • VILoopCtl() / IloopCtl():执行数字 PI/PID 差分方程,完成恒压(CV)或恒流(CC)模式的闭环频率计算。
  • BurstCtl():轻载打嗝模式控制。当系统空载时,为防止 LLC 频率跑飞引发失控,间歇性地关闭和开启发波以维持极低的输出能量。
  • RegReflash():将环路计算得出的开关周期变量,换算并写入真实的 ePWM 周期寄存器中。
  • FastProtection():快速软件保护。针对无需等待滤波确认的恶劣过流/过压,执行即刻关机。

【全局状态调度:ISR2.c (200Hz)】

  • ISR_200Hz():5ms 定时中断总入口。
  • StateM():有限状态机枢纽,调度电源的软启动、空闲、运行与故障跳转。
  • VrefGet():慢速参考基准生成。例如在恒流/恒压切换,或者接收到上位机调压指令时,对目标参考值进行渐变限制,避免阶跃扰动。
  • SlowP():慢速保护判定。例如温度保护等需要长时间滤波防抖的故障捕捉。

【核心业务与保护实现:Function.c】

  • StateMInit() / StateMWait() / StateMRise() / StateMRun() / StateMErr():分别对应状态机中的五大核心节点:上电初始化、待机等 PFC 就绪、闭环软启动爬坡、稳态输出、以及故障死锁封波。
  • ValInit():清空所有历史积分与运行标志。
  • HwOpp() / SwOCP() / ShortOff():谐振电流过大、软件过流及深度短路的三级阶梯式保护实现。
  • SwUVP() / SwOVP() / SwOTP():电压跌落、输出过充与散热器过温的判定逻辑。
  • CANLost():通讯超时看门狗。若长时间未收到上位机心跳包,系统将自动降额或安全停机。

【内外层通信:SCIcom.c & CANcom.c】

  • ISR_SCI() / sMailboxISR():纯粹的字节或数据帧拦截转存中断,剥离复杂运算。
  • MesStore() / CANMessage():在后台大循环或慢速中断中被调用,对收集到的原始缓冲数据进行解包、校验(Checksum/CRC)、以及指令意图翻译。
  • SetCmdUpdate():将校验通过的上位机设定值(如目标电压、限流点)安全地更新进入 LLC 主控内存。
  • FrameBuild() / SciCom() / CANTx():将电源自身的温度、电压、电流状态打包为标准帧格式,向网络主动广播或应答。
LLC初始化-中断,时钟,定时器配置

LLC初始化-中断,时钟,定时器配置

DSP 的所有高级数字控制功能均基于控制器内部的各类硬件外设模块运行。因此,在系统上电或硬件复位的情况下,首要任务就是先对 DSP 的全局时钟、中断向量表以及底层外设进行严格的初始化配置。

需要特别指出的是,TMS320F28034 芯片在上电后,看门狗(Watchdog)默认是关闭的状态。这就为我们从容地进行各项复杂的初始化赢得了时间,而无需在初始化早期频繁地去“喂狗”。

本章将详细拆解 LLC 程序的初始化主流程、60MHz 核心时钟的倍频与锁存过程,以及产生 50kHz 和 200Hz 核心调度时基的定时器配置。


初始化配置流程与系统主循环

LLC 程序整体初始化函数配置逻辑如下图所示:

LLC程序初始化程序流程图

在完成了繁杂的底层硬件寄存器配置后,主函数 main() 会进入一个死循环(for(;;))。
值得注意的是,主程序中的这个 for 循环,主要用以处理那些耗时资源比较多的后台程序。例如,每 300 毫秒去刷新一次 OLED 液晶显示屏、处理 CAN 总线接收到的海量数据、以及定时发送模块的状态信息等非时间敏感型任务。

主函数与核心中断代码展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include "DSP28x_Project.h"
#include "ISR1.h"
#include "ISR2.h"
#include "SCIcom.h"
#include "CANcom.h"

//******************函数声明*********************//
//系统时钟初始化函数声明
void SysCtrlInit(void);
//定时器0初始化函数声明
void Timer0Init(void);
//定时器1初始化函数声明
void Timer1Init(void);
//PWM1初始化函数声明
void EPWMInit(void);
//硬件过流封波
void TZInit(void);
//ADC采样初始化配置函数声明
void ADCInit(void);
//IO口配置初始化函数声明
void GPIOInit(void);
//SCI通信初始化
void SCIInit(void);
//ePWM初始化函数(调试用)
void ePWMDebugInit(void);

extern Uint16 RamfuncsLoadStart;
extern Uint16 RamfuncsLoadSize;
extern Uint16 RamfuncsRunStart;

void main(void)
{
DINT;//关闭所有中断,确保初始化过程不被打断

SysCtrlInit(); //初始化时钟
InitPieCtrl();//复位中断向量控制寄存器
InitPieVectTable();//初始化中断向量表,用系统默认 ISR 填充

EALLOW; // 开启寄存器写保护解除
// 重新映射中断向量,将我们自己的中断服务函数挂载上去
PieVectTable.TINT0 = &ISR_20US;//50kHz定时中断函数向量定义
PieVectTable.TINT1 = &ISR_200Hz;//200Hz定时中断函数向量定义
PieVectTable.SCIRXINTA=&ISR_SCI;//SCI接收中断
PieVectTable.ECAN0INTA=&sMailboxISR; // 函数sMailboxISR设定为CanInt0中断指针
EDIS; // 恢复写保护

//将Flash中的对时间敏感的代码(如控制环路)复制至 RAM 内存中高速运行
memcpy((Uint16 *)&RamfuncsRunStart,(Uint16 *)&RamfuncsLoadStart,(unsigned long)&RamfuncsLoadSize);

//调用初始化Flash
InitFlash();

// 【外设模块逐一初始化】
Timer0Init(); //定时器0初始化
Timer1Init(); //定时器1初始化
EPWMInit(); //PWM1初始化
TZInit(); //硬件过流封波
ePWMDebugInit(); //ePWM4初始化函数(调试用)
ADCInit(); //ADC采样初始化配置
GPIOInit(); //IO口配置初始化
SCIInit(); //SCI初始化
CanInit(); //CAN功能初始化

//延时5mS,等待所有外设电路供电与状态稳定
DELAY_US(5000);

EALLOW;//保护,防止破坏性写入Edit allow
// 在 PIE 级使能所需的中断线路
PieCtrlRegs.PIEIER1.bit.INTx7 = 1;//timer 0 中断 (50kHz环路)
PieCtrlRegs.PIEIER9.bit.INTx1 = 1; // SCI中断使能
PieCtrlRegs.PIEIER9.bit.INTx5=1; // PIE Group 9,使能canA Int0
EDIS;//Edit disable

PieCtrlRegs.PIEACK.all = 0xFFFF; // 预先清空所有中断应答

// 在 CPU 级使能 INT1(包含Timer0), INT9(包含SCI/CAN), INT13(Timer1)
IER = M_INT1|M_INT9|M_INT13;
EINT;//全局开中断
ERTM;//使能仿真器调试事件

//延时
DELAY_US(5000);

//正式启动CPU定时器,时基开始流转
CpuTimer0Regs.TCR.bit.TSS = 0;
CpuTimer1Regs.TCR.bit.TSS = 0;

EALLOW;
//开启PWM计时器TB模块时钟同步功能,保证所有 ePWM 模块动作完全对齐
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1;
EDIS;

// 系统陷入后台空闲任务大循环
for(;;)
{
CANMessage();//CAN后台数据解包处理与长耗时任务
}
}

高速环路与慢速状态机中断的入口函数定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/** ===================================================================
** Funtion Name : __interrupt void ISR_20US(void)
** Description : 中断优先级为Level1,执行参数采样、环路计算及PWM寄存器更新、Burst控制、快速保护功能,配置ADC采样等。
** Parameters :无
** Returns :无
** ===================================================================*/
__interrupt void ISR_20US(void)
{
PWMDAC(SADC.IoutOffset); // 刷新仿真探针输出

ADCSample(); //ADC采样函数,采样输出电压电流等相关温度信息
VLoopCtlPI();//PI电压环环路计算
BurstCtl();//空载或轻载情况,调频不能满足输出电压需求,Burst(打嗝)控制
RegReflash(); //PWM寄存器更新,频率实装更新
FastProtection(); //模块快速保护函数,输出短路等实时截断

CpuTimer0Regs.TCR.bit.TIF = 1; //清 CPU 定时器中断标志位
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // 清 PIE 级应答
}

struct _FLAG DF={0};
struct _Ctr_value ID2DCtrValue={0};
/** ===================================================================
** Funtion Name : __interrupt void ISR_200Hz(void)
** Description : 中断优先级为Level2,执行电源状态机和慢速保护,获取电压参考值等
** Parameters :
** Returns :
** ===================================================================*/
__interrupt void ISR_200Hz(void)
{
//实现中断嵌套,保证环路控制中断优先级最高
IER = M_INT1; // 主动开中断INT1,允许 50kHz 的 Timer0 随时打断本函数
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
__asm(" NOP");
EINT; // Enable Global interrupt INTM

SlowP(); //慢速保护函数滤波判定
CanOutEn();//CAN输出开关机指令解析
StateM();//电源核心运行状态机函数
CANVrefGet();//CAN获取并平滑参考电压
SciCom();//SCI内部芯片间通信
LEDShow(); //状态显示灯UI函数

CpuTimer1Regs.TCR.bit.TIF = 1;//清 CPU 定时器中断标志位
return;
}

核心系统时钟初始化 (SysCtrlInit)

配置 TMS320F28034 的时钟功能,是为了给芯片内部对应的高速与低速模块提供精准、同步对应的时钟信号。这也是 DSP 能够以 60MHz 狂飙运行的基础。

控制器的晶振与时钟功能配置过程极为严谨,步骤如下:

  1. 时钟预矫正: 调用厂家固化的矫正时钟信号函数,在这之前必须先使能 ADC 时钟信号,矫正完成后再安全关闭 ADC 时钟。
  2. 源时钟选择: 禁用外部时钟输出功能;使用片内的内部晶振 1(INTOSC1)作为系统时钟 1 源;系统时钟 2 使用外部时钟(实际在本板上不起作用)。
  3. 看门狗与定时器配置: 看门狗使用内部时钟 1;CPU 定时器 2 同样使用内部时钟 1 且配置为 CPU 定时器 2 不分频。
  4. 振荡器启停管理: 彻底使能内部晶振 1(时钟 1);在仿真器暂停(Debug)时,配置为时钟 1 信号暂停;主动关闭内部晶振 2 及禁止外部晶振和外部时钟输入功能以降低功耗并提高抗干扰能力;暂停时,看门狗时钟信号暂停(实际项目中并未开启看门狗复位功能)。
  5. 安全保护: 时钟丢失时,配置为立即无延时复位(默认安全策略)。
  6. PLL(锁相环)倍频设置: 这是提速的关键步骤。
    • 先确认系统时钟正常,如果检测到系统时钟丢失将停止 PLL 设置,直接陷入急停保护。
    • 确保 PLL 分频为 0,如果非 0 则将其写 0。
    • 关闭丢失时钟检测,防止在 PLL 切换频率的瞬间被硬件误判为时钟丢失而报错。
    • 配置系统时钟为 60MHz:已知内部基础时钟 $CLKIN = 10\text{MHz}$,将其进行 6 倍频设置:$CLKIN \times 6 = 60\text{MHz}$。
    • CPU 原地一直等待,直到 PLL 时钟锁定(Lock)标志位置位。
    • 重新开启丢失时钟检测。
    • 设置分频器为 1(DIVSEL=3 对应不分频),最终输出核心频率:$\frac{CLKIN \times 6}{1} = 60\text{MHz}$,即 $SYSCLKOUT = 60\text{MHz}$。
  7. 外设时钟精细化裁剪: 开启所需外设时钟(开启 ADC、ePWM1、ePWM4、定时器0、定时器1、比较器1、CAN 通信及 SCI 通信时钟);对于其余不需要的时钟信号全部予以关闭,以减少内部开关噪声并降低芯片发热。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
void SysCtrlInit(void)
{
EALLOW;
//使能ADC时钟,这是调用内部校准函数的前提
SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 1;
//时钟矫正函数,在这之前要使能ADC时钟
(*Device_cal)();
//关闭ADC时钟,节约功耗
SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 0;

// 禁用外部时钟输出功能,防止辐射干扰
SysCtrlRegs.XCLK.bit.XCLKOUTDIV = 3;

//时钟控制寄存器配置
//系统时钟1使用内部时钟1
SysCtrlRegs.CLKCTL.bit.OSCCLKSRCSEL = 0;
//系统时钟2使用外部时钟(实际不起作用)
SysCtrlRegs.CLKCTL.bit.OSCCLKSRC2SEL = 0;
//看门狗使用内部时钟1
SysCtrlRegs.CLKCTL.bit.WDCLKSRCSEL = 0;
//CUP定时器2使用内部时钟1
SysCtrlRegs.CLKCTL.bit.TMR2CLKSRCSEL = 2;
// CPU定时器2不分频
SysCtrlRegs.CLKCTL.bit.TMR2CLKPRESCALE = 0;
//使能内容部晶振1(时钟1)
SysCtrlRegs.CLKCTL.bit.INTOSC1OFF = 0;
//暂停时,时钟1信号暂停(默认)
SysCtrlRegs.CLKCTL.bit.INTOSC1HALTI = 0;
//关闭内部晶振2(时钟2)
SysCtrlRegs.CLKCTL.bit.INTOSC2OFF = 1;
//暂停时,时钟2信号暂停(默认)
SysCtrlRegs.CLKCTL.bit.INTOSC2HALTI = 0;
//暂停时,看门狗时钟信号暂停(默认)
SysCtrlRegs.CLKCTL.bit.WDHALTI = 0;
//禁止外部时钟输入功能
SysCtrlRegs.CLKCTL.bit.XCLKINOFF = 1;
//禁止外部晶振功能
SysCtrlRegs.CLKCTL.bit.XTALOSCOFF = 1;
//时钟丢失时,立即无延时复位(默认)
SysCtrlRegs.CLKCTL.bit.NMIRESETSEL = 0;

// ============= PLL 锁相环设置 =============
// 先将PLL分频寄存器清0,关闭系统时钟检测,修改PLL倍频寄存器,等待PLL时钟锁存;

//确认系统时钟正常,系统时钟丢失将停止PLL设置;
if(SysCtrlRegs.PLLSTS.bit.MCLKSTS)
{
// 清除丢时钟标志位
SysCtrlRegs.PLLSTS.bit.MCLKCLR = 1;
// 系统停止工作 (仿真急停指令)
ESTOP0;
}
// 确保PLL分频为0,如果非0,将其写0;
if((SysCtrlRegs.PLLSTS.bit.DIVSEL == 2) || (SysCtrlRegs.PLLSTS.bit.DIVSEL == 3))
SysCtrlRegs.PLLSTS.bit.DIVSEL = 0; // PLL分频寄存器写0

// 关闭丢失时钟检测 PLL切换时防止检测到系统时钟报错
SysCtrlRegs.PLLSTS.bit.MCLKOFF = 1;
// CLKIN*6=60Mhz CLKIN = 10Mhz;
SysCtrlRegs.PLLCR.bit.DIV = 6;
// 一直等待,直到PLL时钟锁存稳定;
while(!SysCtrlRegs.PLLSTS.bit.PLLLOCKS);
// 丢失时钟检测重新开启;
SysCtrlRegs.PLLSTS.bit.MCLKOFF = 0;
// CLKIN*6/1 = 60Mhz;SYSCLKOUT=60Mhz (3 代表 /1)
SysCtrlRegs.PLLSTS.bit.DIVSEL = 3;

// ============= 外设门控时钟使能管理 =============
SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 1; // ADC CLK开启
SysCtrlRegs.PCLKCR1.bit.EPWM7ENCLK = 0; // EPWM7 CLK关闭
SysCtrlRegs.PCLKCR1.bit.EPWM6ENCLK = 0; // EPWM6 CLK关闭
SysCtrlRegs.PCLKCR1.bit.EPWM5ENCLK = 0; // EPWM5 CLK开启 (实为赋0关闭)
SysCtrlRegs.PCLKCR1.bit.EPWM4ENCLK = 1; // EPWM4 CLK开启
SysCtrlRegs.PCLKCR1.bit.EPWM3ENCLK = 0; // EPWM3 CLK开启 (实为赋0关闭)
SysCtrlRegs.PCLKCR1.bit.EPWM2ENCLK = 0; // EPWM2 CLK开启 (实为赋0关闭)
SysCtrlRegs.PCLKCR1.bit.EPWM1ENCLK = 1; // EPWM1 CLK开启
SysCtrlRegs.PCLKCR3.bit.CPUTIMER1ENCLK = 1;// CPU Timer1 CLK开启
SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK = 1;// CPU Timer0 CLK开启
SysCtrlRegs.PCLKCR3.bit.COMP1ENCLK = 1; // COMP1 CLK开启
SysCtrlRegs.PCLKCR0.bit.ECANAENCLK = 1; // ECANA CLK开启
SysCtrlRegs.PCLKCR0.bit.SCIAENCLK = 1; // SCIA CLK开启 (注释写关闭但代码写1开启)
SysCtrlRegs.PCLKCR0.bit.SPIAENCLK = 0; // SPIA CLK关闭
SysCtrlRegs.PCLKCR0.bit.SPIBENCLK = 0; // SPIB CLK关闭
SysCtrlRegs.PCLKCR0.bit.I2CAENCLK = 1; // I2C CLK开启
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; // TBC CLK关闭 (在此处保持0,等待所有PWM配置好后由主函数统一置1)
SysCtrlRegs.PCLKCR0.bit.LINAENCLK = 0; // LINA CLK关闭
SysCtrlRegs.PCLKCR0.bit.HRPWMENCLK = 0; // HRPWM CLK关闭
SysCtrlRegs.PCLKCR1.bit.EQEP1ENCLK = 0; // EQEP CLK关闭
SysCtrlRegs.PCLKCR1.bit.ECAP1ENCLK = 0; // ECAP2 CLK关闭
SysCtrlRegs.PCLKCR3.bit.CPUTIMER2ENCLK = 0;// CPU Timer2 CLK关闭
SysCtrlRegs.PCLKCR3.bit.COMP3ENCLK = 0; // COMP3 CLK关闭
SysCtrlRegs.PCLKCR3.bit.COMP2ENCLK = 0; // COMP2 CLK关闭
EDIS;
}

定时器(时基)模块初始化配置

DSP 内部集成了 3 个 32 位的 CPU 级硬件定时器(Timer0, Timer1, Timer2)。本 LLC 框架利用 Timer0 生成高速采样环路,利用 Timer1 生成低速状态机。

定时器 0 初始化 (Timer0Init)

定时器 0 被配置为执行超高优先级的系统控制中断。代码逻辑设定其产生 50kHz(周期为 20us)的中断。
其底层寄存器装载推导与配置过程如下:

  • 先关闭定时器 0 并清除其积压的中断标志位。
  • 将其基础预分频配置为不分频(分频系数 1,即 0x1),时钟分频等于 TPR+1;此时 TDDR 寄存器置为 0。
  • 以 $60\text{MHz}$ 为时基,欲产生 $50\text{kHz}$ 的溢出中断,计算周期重载值寄存器(PRD)的数学公式如下:将 1200 转换为十六进制即为 0x4B0
  • PRD 数据载入定时模块(TIM),将 TDDR 数据载入预分频计数器(PSC),最后将 Timer0 的硬件中断使能正式开启。

定时器 1 初始化 (Timer1Init)

定时器 1 用以产生 200Hz(周期为 5mS)的慢速状态机中断。
其配置过程如下:

  • 同样先关闭定时器 1 并清除其中断标志位。
  • 为了获得更长的定时时间,将其底层配置为分频 30。时钟分频等于 TPR+1,因此将 TDDR 寄存器配置为 29(十六进制 0x1D)。
  • 以 $60\text{MHz}$ 经过 30 分频后,欲产生 $200\text{Hz}$ 的中断,计算重载值 PRD 如下:将 10000 转换为十六进制即为 0x2710
  • 将数据重载入底层寄存器,并开启 Timer1 的中断使能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*****************************************************************************
* 函 数:void Timer0Init(void)
* 功 能:20us(50Khz)时钟中断;对信号采集、环路值计算与更新PWM,并计算模块
* 快速保护功能
* Parameters : none
* Returns : none
*****************************************************************************/
void Timer0Init(void)
{
// 关闭Timer0,配置期间停止计数
CpuTimer0Regs.TCR.bit.TSS = 1;
// 清除Timer0中断标志
CpuTimer0Regs.TCR.bit.TIF = 1;
// 分频1(0x1),时钟分频为TPR+1; TDDR寄存器为0
CpuTimer0Regs.TPRH.bit.TDDRH = 0x00;
CpuTimer0Regs.TPR.bit.TDDR = 0x00;
// 时钟60Mhz分频1, PRD = 60Mhz/1/50Khz = 1200(0x4B0)
CpuTimer0Regs.PRD.all = 0x000004B0;
// 将PRD数据载入TIM(当前计数值清初值), TDDR数据载入PSC
CpuTimer0Regs.TCR.bit.TRB = 1;
// Timer 级别中断使能
CpuTimer0Regs.TCR.bit.TIE = 1;
}

/*****************************************************************************
* 函 数:void Timer1Init(void)
* 功 能:5ms(200hz)时钟中断
* Parameters : none
* Returns : none
*****************************************************************************/
void Timer1Init(void)
{
// 关闭Timer1,配置期间停止计数
CpuTimer1Regs.TCR.bit.TSS = 1;
// 清除Timer1中断标志
CpuTimer1Regs.TCR.bit.TIF = 1;
// 分频30,时钟分频为TPR+1; TDDR寄存器为29(0x1D)
CpuTimer1Regs.TPRH.bit.TDDRH = 0x00;
CpuTimer1Regs.TPR.bit.TDDR = 0x1D;
// 时钟60Mhz分频30, PRD = 60Mhz/30/200hz = 10000(0x2710)
CpuTimer1Regs.PRD.all = 0x00002710;
// 将PRD数据载入TIM, TDDR数据载入PSC
CpuTimer1Regs.TCR.bit.TRB = 1;
// Timer 级别中断使能
CpuTimer1Regs.TCR.bit.TIE = 1;
}
LLC初始化-PWM初始化与发波机制

LLC初始化-PWM初始化与发波机制

在 LLC 谐振变换器的数字控制系统中,PWM(脉宽调制)的配置是整个电源驱动的核心。与传统的定频调宽控制不同,LLC 控制为变频控制方式(PFM)。在闭环调节的过程中,PWM 频率时刻变化,即定时器的周期值时刻变化,而占空比必须时刻保持 50% 以保证变压器的磁通平衡。

本章将详细解析如何配置 ePWM 模块来满足 LLC 的发波需求,并重点介绍如何通过中心点对齐技术实现无噪声的 ADC 采样触发。


PWM发波方式与 ADC 无噪触发机制

系统配置了 ePWM1 的 1A 和 1B 互补通道,用以控制输出全桥 LLC 的 PWM 波形产生。

为了实现 50% 占空比并灵活调节频率,配置 ePWM1 发波计数使用上升下降载波模式(Up-Down Count Mode)。在该模式下,计数器从 0 开始向上计数至周期寄存器(PRD)的值,然后再向下倒数回 0。通过调整对应的周期量大小即可实现不同输出 PWM 频率,同时保证占空比完美锁定在 50%。

ADC 采样触发的艺术:
在大功率高频开关电源中,MOS 管开通和关断的瞬间会产生极大的开关噪声。为了获取最纯净的反馈信号,系统配置在每个 ePWM 开关周期的中心点(即计数器达到顶点 PRD 时)触发 ADC 采样输出电压电流等参数。此时 MOS 管处于稳定的导通或关断状态,从而完美避免 PWM 开通关断的瞬间干扰影响采样精准性。

发波方式与 ADC 触发逻辑的波形对应关系如下图所示:

PWM发波方式与ADC触发


ePWM 初始化具体配置逻辑解析

根据 LLC 的控制要求,EPWMInit() 函数完成了从引脚复用到死区注入的全流程配置。其具体配置方式与设计意图如下:

  1. 引脚安全初始化
    • GPIO0-EPWM1A-预先设置为普通IO。
    • GPIO1-EPWM1B-预先设置为普通IO。
    • GPIO0-EPWM1A-预先设置为输出。
    • GPIO1-EPWM1B-预先设置为输出。
    • GPIO0-EPWM1A-拉低。
    • GPIO1-EPWM1B-拉低。
    • 解释:在 PWM 模块完全配置好之前,先将引脚作为普通 GPIO 并强行拉低,防止在上电瞬间输出不可控的随机脉冲导致桥臂直通短路。
  2. 时基与频率设定
    • 配置周期值,初始设定为 150kHz,计数上升下降方式计算公式为:EPwm1Regs.TBPRD=60M/150K/2=200
    • 不相移,计数器与时钟信号同步。
    • 计数采用上升下降模式。
    • 禁用相移功能,配置为 Master 方式。
    • 周期寄存器使用镜像寄存器(Shadow),不立即使用,防止变频时波形错乱。
    • 同步模式,同步信号来自系统时钟,且在计数器为 0 时同步。
    • 相移后,Count UP 模式。
    • 使用系统时钟作为 TB 信号时钟,不分频。
  3. 占空比与动作限定(Action Qualifier)
    • CMPA 寄存器使用镜像模式。
    • CMPB 寄存器使用镜像模式。
    • 当 CTR = 0 时,将 CMPA 镜像中的数据加载到 CMPA 执行寄存器中。
    • 当 CTR = 0 时,将 CMPB 镜像中的数据加载到 CMPA (CMPB) 执行寄存器中。
    • 向上计数时,当计数器=CMPA 时,Epwm1A 置高电平。
    • 向下计数时,当计数器=CMPA 时,Epwm1A 置低电平。
    • 配置 CMPA 初始值为 100,由于 TBPRD 为 200,刚好实现占空比为 50%。
  4. 死区时间(Dead-Band)配置
    • 使能死区功能。
    • 上升沿延迟器输出端口不翻转,下升沿延迟器输出端口翻转,形成标准的互补驱动。
    • 初始化下降沿延时 150 个时钟周期。
    • 初始化上升沿延时 150 个时钟周期。
  5. ADC 采样触发配置
    • 使能 SOCA 触发功能。
    • 当计数器等于周期值(CTR=PRD,即周期中心点)时触发 ADC 采样。
    • 配置为单次触发模式。

对应的 C 语言实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/*****************************************************************************
* 函 数:void EPWMInit(void)
* 功 能:EPWM1A/B初始化,互补配置;占空比50%;
* Parameters : none
* Returns : none
*****************************************************************************/
void EPWMInit(void)
{
EALLOW; // 解除寄存器写保护

// 【1. 引脚安全初始化】
// GPIO0 <-> EPWM1A-预先设置为普通IO口并拉低
GpioCtrlRegs.GPAMUX1.bit.GPIO0=0;
// GPIO1 <-> EPWM1B
GpioCtrlRegs.GPAMUX1.bit.GPIO1=0;
// GPIO0 <-> EPWM1A
GpioCtrlRegs.GPADIR.bit.GPIO0=1;
// GPIO1 <-> EPWM1B
GpioCtrlRegs.GPADIR.bit.GPIO1=1;
// GPIO0 <-> EPWM1A
GpioDataRegs.GPACLEAR.bit.GPIO0=1;
// GPIO1 <-> EPWM1B
GpioDataRegs.GPACLEAR.bit.GPIO1=1;

// 【2. 时基与频率设定】
// 150kHz初始频率设定,EPwm1Regs.TBPRD=60M/150K/2=200
EPwm1Regs.TBPRD =200;
// 不相移,计数器与时钟信号同步
EPwm1Regs.TBPHS.half.TBPHS = 0;
// 采用向上向下(上升下降)计数模式;
EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN;
// 禁用相移功能 Master方式
EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;
// 使用镜像寄存器,不直接操作TBPRD执行寄存器
EPwm1Regs.TBCTL.bit.PRDLD = TB_SHADOW;
// CTR = ZERO 时,发出同步时钟
EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO;
// TBCLK时钟分频; TBCLK = SYSCLKOUT/(CLKDIV * HSPCLKDIV), CLKDIV = 1;
EPwm1Regs.TBCTL.bit.CLKDIV=TB_DIV1;
// HSPCLKDIV = 1; TBCLK = SYSCLKOUT(60Mhz);
EPwm1Regs.TBCTL.bit.HSPCLKDIV=TB_DIV1;

// 【3. 比较器与动作限定】
// CMPA寄存器使用镜像模式;
EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
// CMPB寄存器使用镜像模式;
EPwm1Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
// 当CTR = 0 时,将CMPA镜像中的数据加载到CMPA执行寄存器中;
EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_PRD;
// 当CTR = 0 时,将CMPB镜像中的数据加载到CMPA(或CMPB)执行寄存器中;
EPwm1Regs.CMPCTL.bit.LOADBMODE = CC_CTR_PRD;

// 向上计数,当CTR = CMPA时, 拉高
EPwm1Regs.AQCTLA.bit.CAU = AQ_SET;
// 向下计数,当CTR = CMPA时(原注释写为PERIOD,实则为匹配CMPA下行),拉低
EPwm1Regs.AQCTLA.bit.CAD= AQ_CLEAR;

// 50%占空比 (100 是 200 的一半)
EPwm1Regs.CMPA.half.CMPA = 100;

// 【4. 死区配置】
// enable Dead-band module DBA_ALL
EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
// 上升沿延迟器输出端口不翻转,下升沿延迟器输出端口翻转;
EPwm1Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;
// 死区下降沿延时 150 * 1/60MHz
EPwm1Regs.DBFED = 150;
// 死区上升沿延时 150 * 1/60MHz
EPwm1Regs.DBRED = 150;

// 【5. ADC采样中心点触发配置】
// 在每个PWM的中点位置采样,避开mos开通和关断的干扰对采样的影响
// 使能SOCA触发功能
EPwm1Regs.ETSEL.bit.SOCAEN = 1;
// Cnt=PERIOD (PRD) 时发送触发信号
EPwm1Regs.ETSEL.bit.SOCASEL = 2;
// 立即触发 Generate pulse on 1st event
EPwm1Regs.ETPS.bit.SOCAPRD = 1;

EDIS; // 恢复系统写保护
}

PWM 安全使能与硬核截断机制

在控制逻辑中,电源的启动与保护停机不仅需要停止软件的 PID 运算,更需要在物理引脚层面进行发波的开启与硬核截断。本代码通过操作 GPIO 的复用开关(MUX)来实现这一点,这种方式比单纯修改占空比更为安全可靠。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/*
** ===================================================================
** Funtion Name :void PWMEn(void)
** Description :打开PWM发波输出
** Parameters : none
** Returns : none
** ===================================================================
*/
void PWMEn(void)
{
EALLOW;
// 将 GPIO0 和 GPIO1 的功能复用选择位设置为 1,
// 引脚控制权正式交由 ePWM 硬件模块,允许 PWM 波形向外输出。
GpioCtrlRegs.GPAMUX1.bit.GPIO0=1;
GpioCtrlRegs.GPAMUX1.bit.GPIO1=1;
EDIS;
}

/*
** ===================================================================
** Funtion Name :void PWMDis(void)
** Description :关闭PWM发波输出 (硬件级封锁)
** Parameters : none
** Returns : none
** ===================================================================
*/
void PWMDis(void)
{
EALLOW;
// 第一步:将复用开关拨向 0,褫夺 ePWM 模块对引脚的控制权,将其降级为普通 GPIO
GpioCtrlRegs.GPAMUX1.bit.GPIO0=0;
GpioCtrlRegs.GPAMUX1.bit.GPIO1=0;

// 第二步:将这俩引脚强行配置为推挽输出模式
GpioCtrlRegs.GPADIR.bit.GPIO0=1;
GpioCtrlRegs.GPADIR.bit.GPIO1=1;

// 第三步:向清零寄存器写入 1,强制输出极低电平,
// 从物理上拉低栅极驱动信号,确保全桥上下管彻底关断,实现硬件级别的死锁保护。
GpioDataRegs.GPACLEAR.bit.GPIO0=1;
GpioDataRegs.GPACLEAR.bit.GPIO1=1;
EDIS;
}
LLC初始化-ADC初始化、TZ硬件保护与通信配置

LLC初始化-ADC初始化、TZ硬件保护与通信配置

在数字 LLC 谐振变换器中,软件的运算速度再快也存在执行周期的延迟。为了应对极其恶劣的短路或过流工况,必须依赖片上比较器构成的纯硬件联锁保护(Trip-Zone, TZ)来实现纳秒级的封波响应。
同时,精准的高频 ADC 采样、与前级 PFC 通信的 SCI 串口,以及用于调试监测的 PWM-DAC 外设,都是支撑系统稳健运行的重要基石。本章将对这些模块的底层初始化过程与核心计算原理进行深度解析。


TZ(Trip-Zone)硬件过流保护初始化

LLC 控制具有卸载电流硬件过流快速关闭 PWM 的功能。这种保护机制完全绕过 CPU 软件运算,通过底层的事件触发 TZ 模块直接关闭 PWM 脉冲输出,从而最大限度保护功率器件。

TZ 与比较器硬件协同机制

LLC 的原边谐振电流经过互感器或采样电阻变换并经过 RC 滤波后,直接送入 DSP 内置的模拟比较器(COMP1)的正相端,与内部 DAC 产生的基准电压进行实时比较。
当电流转换量(尖峰瞬态)大于 DAC 设定的基准电压门限时,比较器输出状态瞬间翻转,该电平变化直接产生 TZ 事件信号,立刻切断 ePWM 发波。

其底层硬件信号的配合逻辑如下图所示:

TZ与比较器配合

核心参数:DACVAL 保护点计算的物理推导

在代码中,有一行极其关键的初始化赋值:Comp1Regs.DACVAL.bit.DACVAL = 500;。这个 500 是如何映射到物理原边谐振电流的保护点呢?

DSP 内部的 DAC 为 10 位精度,其最大数字量为 $1024$(对应 $2^{10}$),满量程基准电压为 $3.3\text{V}$。
外部电流采样调理电路中,等效的电流-电压转换放大比可以推导为一个乘除系数(即除以 100 再乘以 300,代表外围互感变比与采样电阻运放增益的综合)。
因此,当 DACVAL = 500 时,对应的原边电流初始保护点计算公式如下:

即系统的初始硬件限流点被设定为 4.834A

若需要将保护点提升至硬件允许的最大极限值,可以将设定值改为 672,代入相同的换算公式:

这里的 6.497A 就是物理硬件层面允许通过的绝对保护电流最大值,而程序中初始化的 500(即 4.834A)是一个保守、安全的初始设定保护值,后续可根据工况动态调整。

TZInit 代码逐行精析

根据系统设计,比较器 1 与 PWM TZ 的初始化配置流程被严格落实于代码中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/*****************************************************************************
* 函 数: void TZInit(void)
* 功 能: 硬件过流封波初始化
* 用以判定过流,配置比较器及TZ事件
*****************************************************************************/
void TZInit(void)
{
EALLOW;

// 【1. 引脚复用配置】
// 配置GPIO42为比较器输出端口 (COMP_OUT GPIO42 <-> OPP_COMP1_OUT)
GpioCtrlRegs.GPBMUX1.bit.GPIO42 = 3;
// 配置AIO2端口为比较器正端输入 (AI02 <-> COPM1+)
GpioCtrlRegs.AIOMUX1.bit.AIO2 = 2;
// 配置GPIO15为PWM的TZ事件输入端口 (GPIO15 <-> TZ1)
GpioCtrlRegs.GPAMUX1.bit.GPIO15 = 1;

// 【2. 比较器抗干扰滤波与逻辑配置】
// 同步比较器时钟信号至系统时钟;接受数据连续5个SYSCLKOUT时钟(约83ns)一致,COMPOUT才正式输出,滤除高频杂波干扰
Comp1Regs.COMPCTL.bit.QUALSEL = 5;

// 配合TZ信号,输出信号反向配置;即 COMP+ > COMP- 时,输出低电平(产生下降沿触发TZ);反之,输出高电平
Comp1Regs.COMPCTL.bit.CMPINV = 1;

// COMP- 链接内部DAC模块,作为比较基准
Comp1Regs.COMPCTL.bit.COMPSOURCE = 0;

// 内部DAC由 DAC Value 寄存器直接控制
Comp1Regs.DACCTL.bit.DACSOURCE = 0;

// 【3. 设定硬件过流保护点】
// 保护点设定:500对应 4.834A 原边保护阈值 (若设为672则对应最大保护点 6.497A)
Comp1Regs.DACVAL.bit.DACVAL = 500;

// 比较器和DAC模块联合使能上电
Comp1Regs.COMPCTL.bit.COMPDACEN = 1;

// 【4. TZ 封波事件联动配置】
// 使能 TZ1 功能,将其配置为单次触发模式(One-shot)作用于 EPWM1
EPwm1Regs.TZSEL.bit.OSHT1=TZ_ENABLE;

// 当 TZ1 事件发生时,强制 Epwm1A 信号拉低 (Force EPWM1A to a low state)
EPwm1Regs.TZCTL.bit.TZA=TZ_FORCE_LO;
// 当 TZ1 事件发生时,强制 Epwm1B 信号拉低,确保全桥桥臂物理关断
EPwm1Regs.TZCTL.bit.TZB=TZ_FORCE_LO;

// TZ 中断标志位全部清0,清除历史报警记录
EPwm1Regs.TZCLR.all = 0xffff;

// 中断均不使能 (纯硬件动作,不打扰 CPU)
EPwm1Regs.TZEINT.all = 0;

// 数值比较器(Digital Compare)高级功能暂不使用
EPwm1Regs.TZDCSEL.all = 0;

EDIS;
}

调试用 PWM-DAC 初始化 (ePWMDebugInit)

在复杂的数字环路调试中,通过 CCS 观察变量往往不够直观。工程中巧妙地将未使用的 ePWM4A 配置为 200kHz 的高频 PWM 输出,并经外部两级 RC 滤波后,将其转化为平滑的模拟电压,用以模拟 DAC 输出,非常方便在示波器上实时观察系统内部隐变量的变化轨迹。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*****************************************************************************
* 函 数:void ePWMDebugInit(void)
* 功 能:ePWM用以配置用来模拟DAC的效果,用以调试用
* 配置情况:ePWM4A配置
*****************************************************************************/
void ePWMDebugInit(void)
{
EALLOW;
// 引脚复用:GPIO0 <-> EPWM4A (原注释标注GPIO0,实际代码写的是GPIO6复用为EPWM4A)
GpioCtrlRegs.GPAMUX1.bit.GPIO6 = 1;

// 配置周期值,200kHz 计数上升方式 EPwm4Regs.TBPRD = 60M/200k = 300
EPwm4Regs.TBPRD = 300;
// 不相移,计数器与时钟信号同步
EPwm4Regs.TBPHS.half.TBPHS = 0;
// 计数采用单一向上的计数模式 (Up-count)
EPwm4Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;
// 禁用相移功能 Master方式
EPwm4Regs.TBCTL.bit.PHSEN = TB_DISABLE;
// 周期寄存器使用镜像寄存器,不立即生效
EPwm4Regs.TBCTL.bit.PRDLD = TB_SHADOW;
// 同步模式,同步信号来自系统时钟,且在计数器为0时发出同步
EPwm4Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO;
// 使用系统时钟作为TB信号时钟,不分频
EPwm4Regs.TBCTL.bit.CLKDIV=TB_DIV1;
EPwm4Regs.TBCTL.bit.HSPCLKDIV=TB_DIV1;

// CMPA使用镜像寄存器
EPwm4Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
// CMPA在计数器为0 (PRD边界) 时装载更新
EPwm4Regs.CMPCTL.bit.LOADAMODE = CC_CTR_PRD;

// 动作限定:向上计数时,计数器=0时,置高电平
EPwm4Regs.AQCTLA.bit.ZRO = AQ_SET;
// 向上计数时,计数器=CMPA时,置低电平。通过改变CMPA即可改变平均输出电压
EPwm4Regs.AQCTLA.bit.CAU = AQ_CLEAR;

// B通道不使用,动作清除
EPwm4Regs.AQCTLB.bit.ZRO = AQ_NO_ACTION;
EPwm4Regs.AQCTLB.bit.CBU = AQ_NO_ACTION;

// 配置CMPA寄存器初始值为0,即占空比为0 (模拟输出 0V)
EPwm4Regs.CMPA.half.CMPA = 0;
EDIS;
}

ADC 初始化与同步采样配置 (ADCInit)

为了精确控制 LLC 的恒压与恒流,必须配合 EPWM1 模块中心对齐触发 ADC 采样,并在采样完成后触发高频控制中断进行数据处理。

ADC 配置的核心难点在于多通道数据的同步性。为此,程序采用了双通道同时采样模式(Simultaneous Sampling),一次触发获取两路相互绑定的关联数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/*****************************************************************************
* 函 数: ADCInit(void)
* 功 能:输出电压采样(ADCINTA0);
* 输出电流采样(ADCINTB0);
* 温度采样(ADCCINTA3,ADCINTB1);
* 滑动变阻器采样(ADCINTA1);
*****************************************************************************/
void ADCInit(void)
{
EALLOW;
// ADC 采集数据允许被覆盖 (非重叠模式)
AdcRegs.ADCCTL2.bit.ADCNONOVERLAP = 0;
// ADC时钟为系统时钟60MHz,不分频
AdcRegs.ADCCTL2.bit.CLKDIV2EN=0;
// ADC ONESHOT不启动,常规连续转换模式
AdcRegs.SOCPRICTL.bit.ONESHOT = 0;

// 核心并发配置:SOC0~1, SOC2~3, SOC4~5 同时采样模式
// 当触发信号到来时,A/B两路通道的采样保持电路同时关断,获取同一瞬间的物理状态
AdcRegs.ADCSAMPLEMODE.bit.SIMULEN0=1;
AdcRegs.ADCSAMPLEMODE.bit.SIMULEN2=1;
AdcRegs.ADCSAMPLEMODE.bit.SIMULEN4=1;

// 配置 SOC0~5 的优先级最高,防止被其他低级触发抢占
AdcRegs.SOCPRICTL.bit.SOCPRIORITY = 0x06;

// 【1. 输出电压与电流对采样】
// SOC0对应ADCINTA0(输出电压); SOC1对应ADCINTB0(输出电流)
AdcRegs.ADCSOC0CTL.bit.CHSEL = 0;
// ePWM1 SOCA触发采样 (TRIGSEL = 5 对应 ePWM1 SOCA)
AdcRegs.ADCSOC0CTL.bit.TRIGSEL = 5;
// 所有采样窗口周期配置为 (0x15+1) = 22 个时钟周期,确保采样电容充分充电
AdcRegs.ADCSOC0CTL.bit.ACQPS = 0x15;

// 【2. 电位器及散热器温度1采样】
// SOC2对应ADCINTA1(电位器电压); SOC3对应ADCINTB1(散热器温度)
AdcRegs.ADCSOC2CTL.bit.CHSEL = 1;
AdcRegs.ADCSOC2CTL.bit.TRIGSEL = 5;
AdcRegs.ADCSOC2CTL.bit.ACQPS = 0x15;

// 【3. 散热器温度2采样】
// SOC4对应ADCINTA3(散热器温度)
AdcRegs.ADCSOC4CTL.bit.CHSEL = 3;
AdcRegs.ADCSOC4CTL.bit.TRIGSEL = 5;
AdcRegs.ADCSOC4CTL.bit.ACQPS = 0x15;

// 【硬件核心上电】
// ADC 模拟电路内部核模块上电
AdcRegs.ADCCTL1.bit.ADCPWDN = 1;
// ADC 参考电路内部核模块上电
AdcRegs.ADCCTL1.bit.ADCREFPWD = 1;
// ADC Bandgap带隙基准电路内部核模块上电
AdcRegs.ADCCTL1.bit.ADCBGPWD = 1;

// 正式启动 ADC
AdcRegs.ADCCTL1.bit.ADCENABLE = 1;

// 延迟 1ms 等待内部基准电压彻底稳定,这是极为关键的防跑飞措施
DELAY_US(1000l);
EDIS;
}

这些采样完成的数据最终在定时触发的高频控制中断 ISR_20US 中被集中调用和处理,如下所示,采样函数直接供给后续的 PI 闭环运算:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
__interrupt void ISR_20US(void)
{
PWMDAC(SADC.IoutOffset); // 刷新调试 DAC

// ADC采样函数,读取刚才配置好的各个 SOC 通道结果寄存器信息
ADCSample();

VLoopCtlPI(); // PI电压环环路计算
BurstCtl(); // 空载或轻载情况的 Burst 打嗝控制
RegReflash(); // PWM 频率与周期更新
FastProtection();// 模块快速保护函数检测

CpuTimer0Regs.TCR.bit.TIF = 1; // 清除中断标志位
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}

SCI(串口通信)初始化配置 (SCIInit)

作为一个协同工作的系统,LLC 控制级往往需要与前级 PFC 通信交互(如接收 PFC 的母线电压建立信号,并上报 LLC 的工作状态)。系统选用了 SCI(串行通信接口)来进行内部级联。

波特率的计算公式为:

已知系统时钟为 60MHz,低速外设预分频(LOSPCP)默认为 4(即 15MHz LSPCLK),配置 BRR(即 SCIHBAUDSCILBAUD 组成的 16位 寄存器)为 0x0186 (十进制 390),则波特率为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/*****************************************************************************
* 函 数: void SCIInit(void)
* 功 能: SCI通信初始化,与前级或后级进行数据协同通讯
*****************************************************************************/
void SCIInit(void)
{
EALLOW;
// 端口复用配置:将 GPIO12 和 GPIO7 映射为 SCI 收发引脚
GpioCtrlRegs.GPAMUX1.bit.GPIO7 = 2; // GPIO7 <-> SCI_RX
GpioCtrlRegs.GPAMUX1.bit.GPIO12 = 2; // GPIO12<-> SCI_TX

// 数据格式配置:1位停止位
SciaRegs.SCICCR.bit.STOPBITS = 0x0;
// 8字节数据长度
SciaRegs.SCICCR.bit.SCICHAR = 0x7;
// 无奇偶校验位 (No parity)
SciaRegs.SCICCR.bit.PARITYENA = 0x0;
// 禁止内部硬件 Loop (自收自发测试) 模式
SciaRegs.SCICCR.bit.LOOPBKENA = 0x0;
// 选用 Idle-line 协议模式
SciaRegs.SCICCR.bit.ADDRIDLE_MODE = 0x1;

// 使能 RX 接收端
SciaRegs.SCICTL1.bit.RXENA = 0x1;
// 使能 TX 发送端
SciaRegs.SCICTL1.bit.TXENA = 0x1;

// 禁止接收错误中断功能,简化中断逻辑
SciaRegs.SCICTL1.bit.RXERRINTENA = 0x0;
// 禁止睡眠模式
SciaRegs.SCICTL1.bit.SLEEP = 0x0;

// 允许接收中断使能,依靠中断来异步捕获外部发来的报文
SciaRegs.SCICTL2.bit.RXBKINTENA = 0x1;
// 禁止发送中断使能,发送采用轮询或主循环调用
SciaRegs.SCICTL2.bit.TXINTENA = 0x0;

// 波特率配置:设定为 4800bps
// BRR = 0x0186
SciaRegs.SCIHBAUD =0x0001;
SciaRegs.SCILBAUD =0x0086;

// 重启并使能 SCI 模块 (Relinquish SCI from Reset)
SciaRegs.SCICTL1.bit.SWRESET = 0x1;

EDIS;
}
ADC数据采集和数字量转换

ADC数据采集和数字量转换

在数字控制系统中,DSP 必须通过模数转换器(ADC)将外部的模拟电压、电流及温度等物理量转化为内部可运算的数字标幺值。为了保证实时控制的高速性与稳定性,整个 LLC 系统的核心计算全部分布在最高优先级的 20μs 定时中断中。

本章将详细解析 20μs 高频中断的调度逻辑、采样数据的滤波处理、底层 Q 格式的数据重定标机制,以及关键的输出电压、输出电流外围硬件转换电路和数字推导过程。


20μs 中断核心控制流 (ISR_20US)

程序运行含有定时 20μs 中断和 5ms 中断。其中 20μs 中断优先级最高,主要对模块相关参数采样、电压电流环路计算及 PWM 寄存器更新、Burst 控制、快速保护功能等核心操作进行调度;而 5ms 中断优先级为较低,主要执行状态机运行和一些辅助保护功能。

20μs 整体中断程序流程图如下图所示:

20uS程序流程图

在 20μs 中断中,软件依次对输出电压、输出电流、滑动变阻器电压,散热器温度数据进行采样和求平均。根据所采样的电流电压值进行环路计算得到对应 PWM 周期量(控制频率),并更新 PWM 周期寄存器。在空载或轻载的情况下使用 Burst 控制(打嗝控制)。同时比较器检测原边电流,当原边谐振电流较大时,比较器翻转,通过 TZ 信号快速关闭 PWM,保护机器。

对应的代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/** ===================================================================
** Funtion Name : __interrupt void ISR_20US(void)
** Description : 中断优先级为Level1,执行参数采样、环路计算及PWM寄存器更新、Burst控制、快速保护功能,配置ADC采样等。
** Parameters :无
** Returns :无
** ===================================================================*/
__interrupt void ISR_20US(void)
{
// 刷新调试用的 DAC 输出(将内部变量以PWM形式输出供示波器观察)
PWMDAC(SADC.IoutOffset);

// 1. 获取采集数据:从 ADC 结果寄存器读取最新采样值并进行 Q 格式转换与滑动滤波
ADCSample();

// 2. 环路计算:执行 PI 电压环/电流环路计算,输出调节频率(周期量)
VLoopCtlPI();

// 3. 极轻载模式干预:空载或轻载情况,调频不能满足输出电压需求时进入 Burst (打嗝)控制
BurstCtl();

// 4. 驱动实装:将计算得到的最新周期值更新至 PWM 寄存器,完成频率更新
RegReflash();

// 5. 故障监测:模块快速保护函数,如输出短路等致命错误检测
FastProtection();

// 清除定时器底层中断标志位,准备迎接下一次触发
CpuTimer0Regs.TCR.bit.TIF = 1;
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}

ADC 采样数据结构体与转换函数

采样数据结构定义

为了对全系统的模拟量进行统一的高效管理,代码中定义了一个集中存储 ADC 采样值及滤波结果的结构体 _ADI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct _ADI
{
long Iout; //输出电流 (瞬时值)
long IoutOffset; //输出电流偏置 (硬件偏置基准量)
long IAvg; //输出电流平均值 (滤波后)
long Vout; //输出电电压 (瞬时值)
long VAvg; //输出电电压平均值 (滤波后)
long Vadj; //滑动变阻器电压值 (外置参考电压设定)
long VaAvg; //滑动变阻器电压平均值
long SinkT1; //散热器温度1
long SinkT1Avg; //散热器温度1平均值
long SinkT2; //散热器温度2
long SinkT2Avg; //散热器温度2平均值
long MosDeg; //MOS实际温度值
long DioDeg; //二极管温度值
};

采样与标幺化处理 (ADCSample)

该函数主要在 50kHz 发生中断后执行,从 ADC 采样结果寄存器中转存数据至对应定义的结构体变量中(ADC自动在 PWM 周期值处触发采样并将结果保存于 ADC 结果寄存器中)。

在数据处理过程中,有两个极为关键的工程优化:

  • Q 格式精度的统一提升:由于 DSP 内部的 ADC 是 12 位的硬件模块(满量程为 4096),而控制环路为了保证极高的调节精度,统一采用 Q15 格式计算。因此,为了统一计算标量,程序将所有 Q12 的采样值均左移 3 位(<<3)转换成 Q15 位数据。即原先 3.3V 对应数字量 4096,变换成了 3.3V 对应 32767。
  • 零偏消除与滑动滤波:SADC.Iout 为输出电流采样值,同时减去 SADC.IoutOffset 为输出电流偏置值,对应硬件电路上的 0.877V,同时对处理后的电流采样值进行小于零限制(抗负漂移)。最后,对输出电压采样值和输出电流采样值进行滑动求平均值以熨平电磁噪声。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/*
** ===================================================================
** Funtion Name : void ADCSample(void)
** Description : 采样输出电压、输出电流、滑动变阻器电压值(输出电压参考值设置)并对其求平均
** Parameters :
** Returns :
** ===================================================================
*/
void ADCSample(void)
{
// 滑动平均求和中间变量,跨周期保持历史数据
static long VSum = 0, ISum = 0;

// SADC.Vout定义为输出电压采样值
// ADC本身为Q12位,统一将采样更换成Q15 (左移3位)
SADC.Vout = AdcResult.ADCRESULT0<<3;
// 运放有零偏,强行去除底噪盲区
if(SADC.Vout<200) SADC.Vout=0;

// SADC.Iout为输出电流采样值
// 输出电流Q15格式,并动态减去硬件上的固有运放偏置
SADC.Iout = (AdcResult.ADCRESULT1<<3) - SADC.IoutOffset;
// 采样限制,限制DSP采样本身存在零偏(或轻微负偏)的情况
if(SADC.Iout<0) SADC.Iout=0;

// SADC.Vadj为滑动电位器分压电压采样值
// 采样滑动变阻器值(参考电压)Q15
SADC.Vadj = AdcResult.ADCRESULT2<<3;

// SADC.SinkT1与SADC.SinkT2分别为散热器MOS的温度和二极管温度采样值
// 散热器温度Q15-MOS温度
SADC.SinkT1 = AdcResult.ADCRESULT3<<3;
// 散热器温度Q15-二极管温度
SADC.SinkT2 = AdcResult.ADCRESULT4<<3;

// 输出电压采样滑动求平均 (高效的指数平滑滤波算法)
// 公式: 新Sum = 老Sum + 新Sample - 老Average (即 老Sum / 256)
VSum = SADC.Vout + VSum - (VSum>>8);
SADC.VAvg = VSum>>8;

// 输出电流采样滑动求平均
ISum = SADC.Iout + ISum - (ISum>>8);
SADC.IAvg = ISum>>8;
}

LLC 工作参数与常规数字量定义

在代码计算之前,我们需要确定系统的基础频率和电压电流基准。相关的全局数学参数定义如下:

常规数字量(Q格式缩放倍数)定义:

LLC 工作整体参数定义:

  • 定义 LLC 的输出最高电压与最低电压为:
  • 定义 LLC 的输出最大电流为:
  • 定义 LLC 的工作开关频率的谐振频率,最高频率,最低频率为:
  • 定义控制器的核心主频为:

由于 PWM 采用上升下降(Up-Down)计数方式,开关频率与定时器周期寄存器 Period 的映射关系公式为:

根据此公式,可求得各极限频率对应的开关周期数字量:

  • 谐振频率对应周期:
  • 最低频率对应最大周期:
  • 最高频率对应最小周期:
  • 举例,当频率为 $100kHz$ 时,对应的开关周期数字量为:

输出电压转换与标幺化计算

由于高压无法直接送入 DSP 的 ADC 端口,必须经过硬件分压电阻网络进行衰减。输出电压采样调理电路如下:

输出电压转换

定义输出电压取样电路的硬件分压电阻分别为:

定义 DSP 能够采集到的最高基准电压为:

根据串联分压原理可知,当 ADC 端口电压达到满量程 $3.3V$ 时,反推 DSP 能够采集到系统输出的最高极值电压为:

在代码中,我们以 Q15 格式(放大 32768 倍)来表示模拟小数。则任意时刻真实的物理输出电压 $V_o$ 对应的 DSP 数字量函数关系为:

举例说明,当系统真实输出电压为 $40V$ 时,经过分压和转换后,DSP 内部读取并转为 Q15 格式的数字量为:


输出电流转换与标幺化计算

对于负载大电流,系统使用锰铜微欧电阻进行检流,并送入高精密运算放大器构建零偏置差分放大网络,最终送入 ADC。由于单端 ADC 无法采集负电流,且存在硬件底噪,硬件设计时特意引入了一个正向偏置。

电流采样调理电路如下:

输出电流转换

设计使用运放的 1 通道构建零偏置电压,定义偏置电路的电阻参数为:

根据分压公式,计算硬件强制抬升的基准偏置电压为:

(注:根据元器件离散公差,部分批次硬件的实际量测偏置值在0.767V至0.877V附近,软件中最终对应的 IoutOffset 就是在扣除此固定的直流垫底偏置。)

定义主干路串联的输出电流取样检流电阻为:

定义输出电流差分放大电路的反馈与输入电阻为:

则核心差分电流变换的运放闭环放大增益为:

根据差分变换电路增益、采样电阻,以及 DSP 满量程标定电压 $3.3V$,则可推算出电路不溢出的输出电流最大硬件物理采样值为:

定义 Q15 数据为 DSP 最大量程电压数字量(对应 $3.3V$ 满偏),则剥离掉偏置后,任意负载电流 $I_o$ 转换到 DSP 内部的纯电流数字量函数关系为:

举例说明,当系统实际带载输出电流为 $5A$ 时,其对应的 DSP 内部数字量计算值为:

LLC输出限流环路与电压环路解析

LLC输出限流环路与电压环路解析

在 LLC 谐振变换器的数字控制中,为了保证电源在各种极端负载条件下的安全运行,闭环控制策略不仅需要精确调节输出电压,还需要对输出电流进行严格的限制。通常而言,电源均具有合理的输出电流范围,电源应当控制电源模块输出电流在合理的范围内,确保不过流或者因电流过大损坏模块。

本章将详细解析 LLC 闭环控制中“电压内环 + 限流外环”的双环架构,并对相关的 PI 控制代码进行深度的逐行剖析。


LLC 双环闭环控制逻辑架构

在分析限流环之前,我们首先定义整个 LLC 输出控制逻辑功能的宏观架构。整体 LLC 闭环控制由电压环(内环)与限流环(外环)组成,其中电压环内环为快速环,限流环外环为慢速环。

在单电压环运行情况下,参考电压与输出电压相减得到电压误差量,经电压环路计算后得到 LLC 控制量(周期值)。然而,若电源检测到输出电流过大时,限流环需动作用以限制输出电流。

由于对于特定情况下的输出电流与输出电压均成正比相关的情况,即可以通过控制使得输出电压降低的方式去限制输出电流,因此,若输出电流过大,可通过降低电压环参考电压值去减小模块输出电流。

其整体控制逻辑结构如下图所示:

图LLC闭环逻辑

正如上图所示,系统的初始参考电压值与限流环的输出相减,得到电压环的总参考电压:

  1. 未限流状态(正常带载):当模块输出电流小于参考电流时,配置限流环的输出限制为0,即此时限流环不起作用,系统表现为纯粹的恒压源。
  2. 限流状态(过载保护):当模块输出电流大于限流值时,限流环输出正值,即强制将电压环的参考电压降低,系统自动切换为恒流下垂特性。

为了要想系统具有稳定,限流环调节速度应当小于电压环调节速度,避免参考电压变化过快引起整体输出不稳定,程序中使用位置式 PI 控制方式设置限流环。


限流外环控制程序精析 (ILimitCtlPI)

限流环(外环)通过位置式 PI 算法计算出需要从参考电压中扣除的惩罚量(即限流压降)。其程序流程图如下:

限流环程序流程图

下面是限流环 C 语言代码的深度解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/*
** ===================================================================
** Funtion Name : void ILimitCtlPI(void)
** Description : 限流环控制
** 功能逻辑:
** - 当输出电流小于限流值时,限流环输出为零 (不干预电压环)
** - 当输出电流大于限流值时,限流环输出为正 (产生压降惩罚)
** - 其输出叠加在电压环的电压参考值上面,通过降低电压环参考值,降低输出电压从而限制输出电流
** Parameters :无
** Returns :无
** ===================================================================
*/
// 限流环 PI 环路比例系数 P值 (Q15 格式定点数)
#define DSP_ILIMIT_PI_KP 500
// 限流环 PI 环路积分系数 I值 (Q15 格式定点数)
#define DSP_ILIMIT_PI_KI 100

void ILimitCtlPI(void)
{
// 电流误差 Q15 (静态变量,保持历史状态)
static long IErr=0;
// 限流环路的最终输出 Q15 (即需要从目标电压中减去的值)
static long In=0;
// 环路积分量 Q30 (高精度累加器,防止截断误差)
static long I_Integral=0;

// 【1. 计算电流误差量】
// 采用平滑滤波后的平均值 SADC.Iout。
// 注意方向:当实际输出电流大于设定的参考保护电流时,误差 IErr 为正,表示需要介入限流。
IErr = SADC.Iout - ID2DCtrValue.Ioref;

// 【2. 积分环节累加 (位置式PI)】
// 积分量 = 上一次积分量 + KI * 误差量。
// 由于 IErr(Q15) * KI(Q15) = Q30,正好存入高精度的 I_Integral 中。
I_Integral = I_Integral + IErr * DSP_ILIMIT_PI_KI ;

// 【3. 计算限流环总输出】
// 限流环路输出 = (积分量 + KP * 误差量) >> 15
// 右移 15 位是为了将 Q30 的计算结果重新降维回 Q15 格式,便于后续与电压参考值做加减法。
In = (I_Integral + IErr * DSP_ILIMIT_PI_KP ) >> 15;

// 【4. 抗积分饱和与限幅保护 (Anti-Windup)】
// 积分量最小限制:限制为 0 意味着当电流未超标时,积分器会被强行清空,确保退出限流时响应迅速。
if( I_Integral < 0)
I_Integral = 0;

// 积分量最大值限制:防止持续深度过载导致积分器爆表。
// 722855646 对应 48V 数字量展开至 Q30 的安全极值。
if(I_Integral > 722855646)
I_Integral = 722855646 ;

// 输出最小限制:同样钳位在 0,保证未过载时绝对不干扰电压环。
if( In < 0)
In = 0;

// 输出最大限制:22060 对应物理上约 48V 的 Q15 数字量。
// 意味着限流环最多把参考电压“拉跨” 48V,实现深度的恒流下垂。
if(In > 22060)
In = 22060;

// 将最终计算出的压降惩罚值赋给全局结构体,供电压环使用
ID2DCtrValue.Ilimit = In;
}

电压内环控制程序精析 (VLoopCtlPI)

电压内环是整个 LLC 调频发波的直接驱动者。它接收原始的参考电压,并主动扣除限流环传来的 Ilimit 惩罚量。
需要特别注意的是 LLC 的调频特性:频率越低,增益越高,输出能量越大。因此,当输出电压偏低需要增加能量时,程序实际上是在减小频率,也就是增加 PWM 的周期寄存器(Period)值

下面是电压环 C 语言代码的深度解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/*
** ===================================================================
** Funtion Name : void VLoopCtlPI(void)
** Description : 输出恒压PI环路计算 (LLC 调频核心)
** Parameters :无
** Returns :无
** ===================================================================
*/
// 电压环 PI 环路 P值 (Q15 格式定点数)
#define DSP_V_PI_KP 2678
// 电压环 PI 环路 I值 (Q15 格式定点数)
#define DSP_V_PI_KI 88

// 积分量最大值限制 (Q30格式):LLC 运行的最低频率 35kHz,对应最大 PWM 周期量 857
#define MAX_INTE 28086857
// 积分量最小值限制 (Q30格式):LLC 运行的最高频率 150kHz,对应最小 PWM 周期量 200
#define MIN_INTE 6553600

// 环路输出最小量限制 (Q15格式):最高频率 150kHz -> 最小周期量 200 -> 限制在 199 留有 1 个单位余量
#define MIN_LOOPOUT 199
// 环路输出最大量限制 (Q15格式):最低频率 35kHz -> 最大周期量 857
#define MAX_LOOPOUT 857

void VLoopCtlPI(void)
{
// 电压误差 Q15
static long VErr=0;
// 电压环路的输出 Q15 (即最终的 PWM 周期值 Period)
static long Vn=0;
// 环路积分量 Q30
static long Integral=0;

// 【1. 计算融合了限流逻辑的电压误差量】
// 误差 = (原始设定目标电压) - (实际输出电压) - (限流外环要求降压的惩罚量)
// 物理逻辑:当参考电压大于输出电压时,VErr 为正,说明能量不够,需要:频率往小调 -> 周期量往大调 -> 输出量增加。
VErr = ID2DCtrValue.Voref - SADC.Vout - ID2DCtrValue.Ilimit;

// 【2. 积分环节累加 (位置式PI)】
// 积分量 = 历史积分量 + KI * 当前误差量
Integral = Integral + VErr * DSP_V_PI_KI ;

// 【3. 计算电压环输出 (PWM Period 定时器周期值)】
// 电压环路输出 = (KP * 误差量 + 积分量) >> 15 (从 Q30 还原为 Q15)
Vn = (VErr * DSP_V_PI_KP + Integral) >> 15;

// 【4. 抗积分饱和与动态边界限幅】
// 积分量最小限制:对应系统允许的最高频率极限 (150kHz 对应的小周期)
if(Integral < MIN_INTE)
Integral = MIN_INTE;

// 积分量最大值限制:对应系统允许的最低频率极限 (35kHz 对应的大周期)
// 注意这里利用了 ID2DCtrValue.MaxPD 左移 15 位动态限制,方便在启动过程(软启)中动态放宽最低频率限制
if(Integral > (ID2DCtrValue.MaxPD << 15))
Integral = ID2DCtrValue.MaxPD << 15;

// 【5. 待机复位安全逻辑】
// 当处于待机状态 (RsingInitFlag == 0) 时,环路运算量必须清零初始化,
// 强制将积分器和输出量复位至最高频率 (最低能量) 状态,确保下次开机不产生浪涌冲击。
if(RsingInitFlag==0)
{
Integral = MIN_INTE;
Vn = MIN_LOOPOUT;
}

// 【6. 最终输出周期量 (Period) 绝对硬限幅】
// 最终输出量限制,最小值限制 (不能超过最高频率 150kHz)
if(Vn <= MIN_LOOPOUT)
Vn = MIN_LOOPOUT;

// 最终输出量限制,最大值限制 (不能低于最低频率 35kHz)
if(Vn > MAX_LOOPOUT)
Vn = MAX_LOOPOUT;

// 【7. 寄存器派发】
// 环路输出赋值给全局结构体 ID2DCtrValue.PD,随后会在 20us 中断的结尾被写入真实的 PWM 寄存器中生效。
ID2DCtrValue.PD = Vn;
}
LLC未补偿前波特图获取与自制环路扫描仪设计

LLC未补偿前波特图获取与自制环路扫描仪设计

在设计 LLC 谐振变换器的闭环控制系统时,获取精确的被控对象传递函数(即未补偿前的系统模型)是设计补偿器 $G_c(s)$ 的大前提。

传统电源拓扑(如 BUCK、BOOST、反激、移相全桥等定频调宽的拓扑电路)由于是占空比控制,其小信号理论推导比较成熟,计算得到的模型也较为准确。但对于 LLC 拓扑而言,其控制量为频率,系统为变频系统(PFM),其小信号数学特性极难通过纯理论推导获得,且理论模型往往存在极大的误差和不准确度。

由于理论推导的传递函数不太准,所以在工程实践中,我们通常直接通过扫频实测的方法获取电源实际的伯德图(Bode Plot),然后再据此制作补偿器。
然而,市面上专业的环路扫频设备动辄十几万甚至几十万。因为买不起十几万的扫描仪,我们自己设计并搭建了这套低成本的“环路扫描仪”,利用普通的信号发生器和示波器,完美模拟了专业设备的扫频特性。


闭环控制主电路说明

首先,我们需要梳理清楚在闭环系统中,哪些环节属于“被控对象”,哪些环节属于“补偿器”。数字控制 LLC 闭环控制整体系统架构如下图 1 所示:

数字控制LLC闭环控制系统

在上图的控制闭环中:

  1. 输入与输出:LLC 主电路的输入为前级提供的 $380\text{V}$ 直流母线,经 LLC 谐振主电路后,输出我们需要精准控制的直流电压 $V_o$。
  2. 信号采样调理:输出电压 $V_o$ 经分压转换网络 $H(s)$ 后,得到适合 DSP 采样的低压信号,该信号再经由 $R_1$ 和 $C_1$ 构成的 RC 低通滤波电路,滤除高频开关干扰后,送入 DSP 的 ADC 采样端口。
  3. 数字补偿器:$G_c(s)$ 为 DSP 内部我们需要专门设计的电压环补偿传递函数。经过 $G_c(s)$ 的 PI 计算后,得到 LLC 电路所需控制频率的开关周期量 $P_{ctl}$。
  4. 驱动发波:数字量 $P_{ctl}$ 经 DSP 内的 ePWM 模块后,生成 LLC 对应的特定频率 PWM 波形,驱动 LLC 主功率电路的 MOS 管运转。

核心思路:上图中的 $G_c(s)$ 为 DSP 内部需要设计的电压环补偿传递函数。设计和计算 $G_c(s)$ 必须对 LLC 主电路、分压转换电路、DSP 内部 ADC 采集模块和 ePWM 发波模块的整体综合特性进行分析。换句话说,我们需要获取除 $G_c(s)$ 以外的所有电路的开环传递函数特性,然后根据这根“未补偿曲线”,逆向设计出合理的 $G_c(s)$ 进行对消与补偿。


自制扫频结构与小信号注入原理

根据扫频获得传递函数特性的工作原理,我们可以使用一台普通的示波器和信号发生器,来模拟昂贵的扫频设备以获得 LLC 电路的传递函数特性。应用于该场合的整体扫频特性电路结构如下图 2 所示:

LLC扫频结构图

注入点的物理断开与信号合成

如上图所示,为了测试系统的响应,我们需要将闭环“打断”。操作方法是:将输出电压的分压转换电路 $H(s)$ 和 ADC 采样模块前端的 RC 滤波电路断开,并于 RC 滤波器的输入端手动注入人工控制信号。

此时,注入的控制信号必须由两部分叠加组成:

  • 直流分量(DC 偏置):用以使得 LLC 主功率输出稳定在我们所需要测试的工作点(例如 $40\text{V}$ / $10\text{A}$ 稳态)。
  • 交流分量(AC 扰动):通常为不同频率的纯正弦波输入,作为扫频的动态输入激励量。

直流偏置与数字基准换算

在本应用中,假设信号发生器注入信号中的直流分量设置为 $2\text{V}$,并期望此时 LLC 的输出电压恰好为 $40\text{V}$。
则注入的 $2\text{V}$ 电压进入 DSP 的 ADC 后,对应的内部数字量(以 Q15 格式,满量程 $3.3\text{V}$ 对应 $32768$)为:

根据先期设计的 LLC 宏观特性,当输出为 $40\text{V}$ 稳态时,其对应的 LLC 工作谐振频率刚好为 $50\text{kHz}$。此时 DSP 的定时器(主频 $60\text{MHz}$,上下计数模式)对应的控制量周期值 $P_{ctl}$ 为:

为了让注入的电压数字量与周期的数字量对应起来,我们在 DSP 内部人为串入一个比例系数 $K$。对应的 $K$ 值为(使用 Q15 代替小数运算):

即在 DSP 内部,开环映射的控制量(开关周期)表达式固定为:

通过这样的线性映射,我们只需调节注入控制信号中直流分量的大小,就等同于调整 $P_{ctl}$ 控制量,进而调整 LLC 所需要的输出电压。如上述描述:当调整直流分量为 $2\text{V}$ 时,$P_{ctl}$ 自动计算为 600,开关频率锁定为 $50\text{kHz}$,输出电压即稳定在 $40\text{V}$。


数据处理与波特图绘制

扫频法的本质是通过向待测试系统注入不同频率的小信号(交流分量),同时用示波器抓取待测试系统输出信号的动态响应(交流分量)。
通过计算不同频率下,注入小信号和系统输出小信号的幅值变比相位时差,即可得到待测试系统的幅频特性和相频特性。

如上图所示,注入信号由信号发生器产生,并经过运放构成的信号跟随电路增强驱动能力。我们使用示波器的 CH1 和 CH2 通道,分别测量注入信号的交流分量和系统输出的交流分量(示波器必须设置为交流耦合采样方式,以忽略庞大的直流量)。

增益与相位计算实战验证

以某一特定频率点为例进行说明:将信号发生器输出的直流分量(偏置)设置为 $2\text{V}$,交流分量设置为纯正弦波,频率为 $2\text{kHz}$,峰峰值为 $400\text{mV}$,如下图 3 中 CH1(黄色波形)所示。

此时,LLC 电路在直流分量的作用下稳定工作于 $40\text{V}$ / $10\text{A}$ 的输出状态,但由于存在交流扰动的注入,输出电压也必然包含同频率的交流扰动分量,如下图 CH2(蓝色波形)所示。

系统注入信号和输出

从示波器中读取 CH1 和 CH2 的幅值和时间信息,我们可以开始进行波特图的数据核算:

  • 增益曲线计算(Gain)
    增益就是根据该频率点下,输出交流幅值对输入交流幅值的变比,再取以 10 为底的对数并乘以 20。根据测得数据,输出峰峰值为 $830\text{mV}$,输入峰峰值为 $400\text{mV}$,则系统的开环增益为:

  • 相位曲线计算(Phase)
    相位曲线代表输出正弦波对输入正弦波的时间滞后角度。根据测得数据,CH1 和 CH2 波形之间的时间延时差为 $\Delta t = 210\mu\text{s}$。
    因为测试频率为 $2\text{kHz}$,其完整周期为 $T = \frac{1}{2000}\text{s} = 500\mu\text{s}$,则对应的滞后相位角度为:

闭环模型还原注意事项

通过改变信号发生器的频率,依次调节需要注入小信号的不同频率点(如从 $10\text{Hz}$ 扫到 $10\text{kHz}$),重复上述计算,可得到 LLC 在不同频率注入信号下的增益和相位散点数据。将其连线,即可绘制出 LLC 被控对象的开环增益曲线与相位曲线(波特图),然后再根据这段曲线设计和计算 PI 或 补偿器 $G_c(s)$。

工程排坑注意
本次手动注入测试得到的开环增益曲线与相位曲线,其物理路径仅仅是从 RC 滤波器前端到最终的输出电压
这意味着,该测量模型并未包含输出端的分压转换网络 $H(s)$ 的衰减系数;同时,在 DSP 内部计算开关周期值时,我们人为加入了一个放大系数 $K$ 值。
因此,在最终推导真实的系统开环特性曲线用于补偿设计时,必须从测量结果中人工加上分压转换 $H(s)$ 的衰减,并除以系数 $K$ 值,方可还原出纯正的主功率级真实波特图。

闭环模型还原注意事项与多工况恶劣点补偿

在掌握了上述小信号注入与单点频率的计算方法后,我们只需通过改变信号发生器的频率,依次调节需要注入小信号的不同频率点(通常建议进行对数扫频,例如从 10Hz 扫到 10kHz),并重复上述的增益与相位计算,即可得到 LLC 在不同频率注入信号下的增益和相位散点数据。

将这些散点数据在半对数坐标系中连线,即可初步绘制出 LLC 被控对象的开环增益曲线与相位曲线(即初步的波特图)。随后,我们将根据这段曲线的穿越频率和相位裕度,来设计和计算 PI 或是更高级的补偿器 $G_c(s)$。

环路测量数据记录与实测展示

为了清晰地记录和处理庞大的扫频数据,工程上通常会建立如下的数据分析表。表格中详细记录了不同频率下的注入信号峰峰值(CH1)、输出扰动峰峰值(CH2)以及两者的时间延时,并利用公式自动计算出相位滞后与 dB 增益。

以下为我们实测的两组极限工况扫频数据完整记录:

工况一:额定输入380V,满载输出 40V/10A

运行工况小信号频率f(Hz)小信号峰峰值/mVCH1(mV)CH2(mV)延时/us对应示波器图相位计算增益计算增益(db)
额定输入380V
输出40V/10A
5040040056401tek000000.01814.122.98
10040040055201tek000010.03613.822.80
2004004005760110tek000027.9214.423.17
4004004005800284tek0001540.89614.523.23
6004004003760364tek0001478.6249.419.46
8004004003440380tek00013109.448.618.69
10004004003120356tek00003128.167.817.84
2000400400830210tek00004151.22.0756.34
3000400400368114tek00005123.120.92-0.72
4000400400372136tek00006195.840.93-0.63
5000400400404125tek000072251.010.09
600040040016492tek00008198.720.41-7.74
700040040015498tek00009246.960.385-8.29
800040040011284tek00010241.920.28-11.06
900040040014692.8tek00011300.6720.365-8.75
100004004007274.8tek00012269.280.18-14.89

工况二:额定输入380V,轻载/降压输出 25V/10A

运行工况小信号频率f(Hz)小信号峰峰值/mVCH1(mV)CH2(mV)延时/us对应示波器图相位计算增益计算增益(db)
额定输入380V
输出25V/10A
5040040074402120tek0001638.1618.625.39
10040040044401600tek0001757.611.120.91
2004004002340900tek0001864.85.8515.34
4004004001200500tek000197239.54
600400400800324tek0002069.98426.02
800400400640272tek0002178.3361.64.08
1000400400488200tek00022721.221.73
2000400400280107tek0002377.040.7-3.10
300040040018062tek0002466.960.45-6.94
400040040016664tek0002592.160.415-7.64
500040040011459tek00026106.20.285-10.90

工程排坑核心:真实主功率模型的还原

【工程排坑注意】:这是无数新手在第一次做数字电源环路设计时最容易栽跟头的地方!

本次通过手动注入测试得到的开环增益曲线与相位曲线,其物理信号流转路径仅仅是从 RC 滤波器前端到最终的输出电压
这意味着,该测量模型在物理与软件逻辑上存在两个致命的“偏差”:

  1. 缺失了输出电压采样网络的衰减:由于注入点在 RC 滤波器前,测量模型并未包含输出端实际存在的分压转换网络 $H(s)$ 的衰减系数。
  2. 混入了人为的软件放大倍数:在 DSP 内部计算开关周期值时,为了让注入电压的数字量与底层 PWM 周期寄存器匹配,我们在测试代码中人为加入了一个放大系数 $K$ 值。

因此,在最终推导真实的系统开环特性曲线用于补偿设计时,绝对不能直接使用示波器实测算出的波特图。必须从测量结果中通过数学手段进行物理还原补偿:人工加上分压转换 $H(s)$ 的衰减,并除以系数 $K$ 值,方可还原出纯正的、剥离了所有测试干扰的主功率级真实波特图。

其等效的真实传递函数还原公式可表示为:

多工况恶劣点选取与最严苛补偿原则

LLC 谐振变换器是一个典型的强非线性系统。仔细观察上面提供的扫频实测表格,可以敏锐地发现一个关键现象:不同电压输出的时候(例如 40V/10A 与 25V/10A),系统在同一频率下的增益和相位衰减是完全不一样的!

随着工作频率的偏移和等效负载阻抗的剧烈变化,LLC 的主导极点和品质因数(Q值)会发生大幅漂移。如果仅仅针对某一个稳态标称工作点(如 40V 满载)设计补偿器,一旦电源切换到其他低压或轻载工况,极易因相位裕度不足而发生低频震荡或高频啸叫。

因此,在数字电源闭环设计的工程实践中,我们的标准做法是:
遍历扫描多个极限工况(如满载最高压、满载最低压、极轻载等),获取多组不同的波特图,然后对比取出相位裕度最差、穿越频率最容易失控的“最恶劣情况(Worst Case)”来进行统一的补偿设计。 只要能把最恶劣的工况稳妥镇压,电源在其他任何常规工况下的稳定性自然固若金汤。

软件实现位置指引

为了方便读者亲自进行扫频复现与实际测试操作,本章节所配套的底层开环注入与信号解析软件代码,已完整实现在工程目录的以下路径中:

02Bcode -> Code-LoopTest -> Demo-LLC-LoopTest

您可以直接编译烧录该专用测试代码,切断闭环,配合外围的信号发生器,即可快速获取属于您当前硬件参数的专属波特图数据集。

PI补偿器设计与代码分析

PI补偿器设计与代码分析

只有在 LLC 这种变频控制(PFM)的电路中,由于工作频率的偏移和等效负载阻抗的剧烈变化,其系统品质因数($Q$值)和主导极点会发生大幅漂移,才导致了不同工况下的伯德图(Bode Plot)变化如此之大。

因此,要想彻底驾驭 LLC 的稳定性,我们不能凭空捏造参数,必须严格基于扫频实测的恶劣工况数据,通过严密的数学计算,逆向推导并设计数字 PI 补偿器,最终将其转化为 DSP 的底层 C 语言代码。

本章将详细揭秘如何从“剥除测试干扰的原始 Bode 图”出发,一步步计算出理论 PI 参数,并完成定点化代码实装。


试数据导入与提取

根据《LLC环路补偿计算-基于PI控制参数》设计书,我们首先将 LLC 的扫频测试数据导入到数学运算软件(如 Mathcad)中。
其中第 0 列代表频率,第 6 列代表相位滞后,第 8 列代表测量总增益。

1. 额定输入 380V,满载输出 40V/10A 工况(Data1):

序号0 (频率 Hz)1 (CH1 mV)2 (CH1 mV)3 (CH2 mV)4 (延时 us)5 (示波器图)6 (相位 °)7 (放大倍数)8 (增益 dB)
050400400$5.64 \times 10^3$1“tek00000”0.01814.122.98
1100400400$5.52 \times 10^3$1“tek00001”0.03613.822.8
2200400400$5.76 \times 10^3$110“tek00002”7.9214.423.17
3400400400$5.8 \times 10^3$284“tek00015”40.89614.523.23
4600400400$3.76 \times 10^3$364“tek00014”78.6249.419.46
5800400400$3.44 \times 10^3$380“tek00013”109.448.618.69
6$1 \times 10^3$400400$3.12 \times 10^3$356“tek00003”128.167.817.84
7$2 \times 10^3$400400830210“tek00004”151.22.0756.34
8$3 \times 10^3$400400368114“tek00005”123.120.92-0.72
9$4 \times 10^3$400400372136“tek00006”195.840.93-0.63
10$5 \times 10^3$400400404125“tek00007”2251.010.09
11$6 \times 10^3$40040016492“tek00008”198.720.41-7.74
12$7 \times 10^3$40040015498“tek00009”246.960.385-8.29
13$8 \times 10^3$40040011284“tek00010”241.920.28-11.06
14$9 \times 10^3$40040014692.8“tek00011”300.6720.365-8.75
15$1.1 \times 10^4$4004007274.8“tek00012”269.280.18-14.89

2. 额定输入 380V,轻载降压输出 25V/10A 工况(Data2):

序号0 (频率 Hz)1 (CH1 mV)2 (CH1 mV)3 (CH2 mV)4 (延时 us)5 (示波器图)6 (相位 °)7 (放大倍数)8 (增益 dB)
050400400$7.44 \times 10^3$$2.12 \times 10^3$“tek00016”38.1618.625.39
1100400400$4.44 \times 10^3$$1.6 \times 10^3$“tek00017”57.611.120.91
2200400400$2.34 \times 10^3$900“tek00018”64.85.8515.34
3400400400$1.2 \times 10^3$500“tek00019”7239.54
4600400400800324“tek00020”69.98426.02
5800400400640272“tek00021”78.3361.64.08
6$1 \times 10^3$400400488200“tek00022”721.221.73
7$2 \times 10^3$400400280107“tek00023”77.040.7-3.1
8$3 \times 10^3$40040018062“tek00024”66.960.45-6.94
9$4 \times 10^3$40040016664“tek00025”92.160.415-7.64
10$5 \times 10^3$40040011459“tek00026”106.20.285-10.9
11$6 \times 10^3$4004007550“tek00027”1080.188-14.54
12$7 \times 10^3$40040010038.4“tek00028”96.7680.25-12.04
13$8 \times 10^3$40040010038.8“tek00029”111.7440.25-12.04
14$9 \times 10^3$40040010544.8“tek00030”145.1520.263-11.62
15$1.1 \times 10^4$4004006140.8“tek00031”146.880.153-16.33

开环传递函数 Bode 图的物理还原构建

环路扫频所得的第 8 列增益数据,仅仅是“扰动量到输出电压”的测试增益。为了构建纯正的系统主功率开环传递函数 Bode 图,我们必须通过数学方法消除测试外围电路的干扰。

1. 补偿硬件分压衰减:
忽略输出电压分压电路的滤波延迟,真实输出电压转换电路的硬件分压增益为:

2. 剥离软件放大系数:
在扫频注入程序中,为了使电压数字量匹配周期寄存器,人为将采样数据乘以 $\frac{990}{2^{15}}$ 这个常量参数。因此构建开环传递函数 Bode 图时,要将这个人额外加入的参数剔除:

3. 真实增益与相位重构:
提取频率量:

构建扰动量加入口到输出电压转换的整体真实增益:

构建扰动量加入口到输出电压转换的整体相位(取负值代表滞后):

计算数据点个数:

根据上述提取的真实数据点,绘制出剔除干扰后的原始未补偿 BODE 图的增益曲线与相位曲线如下:

原始BODE图的增益曲线与相位曲线


PI 环路补偿数学计算

从上述未补偿前的 Bode 图可以明显看出,红色曲线(40V/10A 满载)的相位裕度极小,高频衰减极快,系统最容易在这个恶劣工况下失控震荡。因此,我们以此红线作为补偿曲线设计的基准。

1. 穿越频率与增益提取:
定义设计的 PI 环路补偿后的系统闭环控制带宽(即期望的穿越频率)为:

从图中可知,未补偿前红线在穿越频率点 $800\text{Hz}$ 处的原始增益为:

计算得到实际的绝对放大倍数为:

2. 传递函数构建与零点配置:
传统 PI 的传递函数可表示为(其中 $K_{PI}$ 为总增益,$\omega_z$ 为比例积分零点):

定义 PI 补偿的零点(此为预设值,当补偿后的相位裕度或者增益裕度不满足要求时,可以适当调整零点位置来拉升相位):

构建 PI 传递函数对应频率的频域复数表达式:

3. 求解补偿器核心增益 $K_{PI}$:
根据闭环穿越频率的概念,穿越频率点处的总增益必须为 0dB(即整体放大倍数为 1)。换言之,原始传递函数在该点的幅值倒数,必须等于 PI 补偿器的幅值,因此可以构建对应的方程:

通过预设初始的探测值 $K_s$,使用迭代求解法(root函数)求解 $K_{PI}$ 值:

求解该方程得到:

4. 补偿后 Bode 图验证:
以此,得到彻底定型的补偿器复频域传递函数为:

求解对数频率下的增益和相位:

绘制出叠加了 $G_c(s)$ 补偿器后的增益曲线与相位曲线如下图:

补偿后的增益曲线与相位曲线

由上图可知,补偿后的红线工况(满载高压)穿越频率精准定格在 800Hz,相位裕度高达 50度,增益裕度在 -20dB 以下,极其稳定。
同时,补偿后的蓝线工况(降压轻载)穿越频率退至 200Hz,相位裕度达 60度,完全满足全工况的严苛稳定要求。一般大于45就好。


数字 PI 参数定点化换算

由于 DSP 单片机无法直接运行连续域的 $s$ 函数,我们必须将理论物理量离散化为 C 语言代码所需的数字系数。

将 PI 补偿器传递函数表示为标准位置式传递函数形式:

因此可以得到对应的 PI 连续态比例系数:

积分的时间常数:

系统采样频率(即 20us 控制中断频率)和采样周期为:

通过离散化(欧拉变换),得到数字化的积分系数:

Q15 格式定点化放大:
为匹配定点型 DSP 的高效移位乘法,将 PI 控制的比例和积分系数人为放大 Q15($2^{15}$)倍表示:


C 语言核心代码深度解析 (VLoopCtlPI)

在最终落地的工程代码中,根据实际带载动态微调,最终采用的宏定义参数为 DSP_V_PI_KP = 2678DSP_V_PI_KI = 88。这与我们上述硬核推导的理论标幺值 ($2327$ 和 $87.74$) 高度吻合,完美实现了理论对代码的闭环指导!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/*
** ===================================================================
** Funtion Name : void VLoopCtlPI(void)
** Description : 输出恒压 PI 环路计算 (LLC 调频核心算法)
** Parameters : 无
** Returns : 无
** ===================================================================
*/
// 依据波特图推导并微调后的电压环 PI 参数 (Q15定点格式)
#define DSP_V_PI_KP 2678 // 电压环 PI 环路 P值
#define DSP_V_PI_KI 88 // 电压环 PI 环路 I值

// 环路积分量绝对限幅 (对应 Q30 格式下的周期量极值):
// LLC 运行的最低频率为 35kHz,对应的底层定时器最大 PWM 周期量为 857
#define MAX_INTE 28086857 // 857 * 32768 = 28082176 (加上一点裕量)
// LLC 运行的最高频率为 150kHz,对应的底层定时器最小 PWM 周期量为 200
#define MIN_INTE 6553600 // 200 * 32768 = 6553600

// 最终控制量输出限制 (硬件级物理发波边界):
// 最高频率限制 -> 最小周期量 -> 限制为 199 (给 150kHz 的 200 留 -1 余量)
#define MIN_LOOPOUT 199
// 最低频率限制 -> 最大周期量 -> 限制为 857 (对应 35kHz 下限)
#define MAX_LOOPOUT 857

void VLoopCtlPI(void)
{
// 电压误差 Q15 格式 (静态变量保持历史状态)
static long VErr=0;
// 电压环路的最终输出 Q15 格式 (即下发的 PWM 周期值 Period)
static long Vn=0;
// 环路积分量 Q30 格式 (高精度池,防止定点截断导致低频震荡)
static long Integral=0;

// 【1. 误差计算与极性判定】
// 计算电压误差量。注意减去了 ID2DCtrValue.Ilimit (限流外环剥离出的惩罚项)。
// 控制逻辑极性:当参考电压 > 输出电压 时,VErr 为正 -> 需要更多能量 -> 必须使频率往小调 -> 即周期量(Period)往大调 -> 输出能量增加。
VErr = ID2DCtrValue.Voref - SADC.Vout - ID2DCtrValue.Ilimit;

// 【2. 积分与累加环节 (Integral)】
// 积分量 = 历史积分量 + KI * 误差量
Integral = Integral + VErr * DSP_V_PI_KI ;

// 【3. 总输出生成 (Proportional + Integral)】
// 电压环路输出 = (KP * 误差量 + 积分量) >> 15 (从 Q30 降维回 Q15 格式)
Vn = (VErr * DSP_V_PI_KP + Integral) >> 15;

// 【4. 积分器动态限幅保护 (Anti-Windup)】
// 积分量最小限制:限制在最高频(150kHz),防止反向积分跑飞导致死区
if(Integral < MIN_INTE)
Integral = MIN_INTE;

// 积分量最大值限制:最低频发波限制。
// 绝妙之处:这里利用了 ID2DCtrValue.MaxPD 左移15位进行启动软限幅。起机时 MaxPD 慢慢放大,强迫系统从最高频缓慢向谐振频率扫频启动。
if(Integral > (ID2DCtrValue.MaxPD << 15))
Integral = ID2DCtrValue.MaxPD << 15;

// 【5. 待机复位安全锁】
// 待机或关机状态下 (RsingInitFlag == 0),必须将积分器和输出量强行清零至最高频(最小能量)状态,杜绝开机瞬间因积分残余引发炸管浪涌。
if(RsingInitFlag == 0)
{
Integral = MIN_INTE;
Vn = MIN_LOOPOUT;
}

// 【6. 物理执行量硬钳位】
// 最终输出量限制,最小值限制 (绝不超越 150kHz 硬件物理上限)
if(Vn <= MIN_LOOPOUT)
Vn = MIN_LOOPOUT;
// 最终输出量限制,最大值限制 (绝不跌破 35kHz 硬件谐振下限)
if(Vn > MAX_LOOPOUT)
Vn = MAX_LOOPOUT;

// 【7. 寄存器派发】
// 将计算完备的周期数值赋给全局结构体,供外围 20us 中断结束前写入真实的 ePWM 定时器寄存器中生效
ID2DCtrValue.PD = Vn;
}
多零极点补偿器设计与代码分析

多零极点补偿器设计与代码分析

正如工程实践中常总结的那样:“零点增加相位超前,极点增加相位滞后。”

在 LLC 谐振变换器面临极端工况(如重载、输入电压剧变)时,其原始开环传递函数的相位常常会以极快的速度发生断崖式下跌。传统的 PI 控制器(仅包含一个原点极点和一个控制零点)理论上最多只能提供 $90^{\circ}$ 的相位超前,往往无法将穿越频率处的相位拉升到安全裕度之上,导致系统陷入低频震荡。

为了打破 PI 控制的物理极限,我们需要引入一种更强大的武器:多零极点补偿器(即经典的 Type-III / 2型或3型网络变形:2零2极补偿器)。本章将详细为您拆解,如何通过引入双零点来提供高达 $180^{\circ}$ 的相位超前,并通过严密的数学计算与 Z 变换,将其转化为极其高效的 C 语言差分方程代码。


测试数据导入与原始数据表

将前面利用自制环路扫描仪获取的 LLC 测试数据导入数学计算软件中。其中第 0 列代表频率,第 6 列代表相位,第 8 列代表增益。

1. 将 LLC 测试数据导入 Data1 中(输出 40V/10A 工况):

序号012345678
050400400$5.64 \cdot 10^3$1“tek00000”0.01814.122.98
1100400400$5.52 \cdot 10^3$1“tek00001”0.03613.822.8
2200400400$5.76 \cdot 10^3$110“tek00002”7.9214.423.17
3400400400$5.8 \cdot 10^3$284“tek00015”40.89614.523.23
4600400400$3.76 \cdot 10^3$364“tek00014”78.6249.419.46
5800400400$3.44 \cdot 10^3$380“tek00013”109.448.618.69
6$1 \cdot 10^3$400400$3.12 \cdot 10^3$356“tek00003”128.167.817.84
7$2 \cdot 10^3$400400830210“tek00004”151.22.0756.34
8$3 \cdot 10^3$400400368114“tek00005”123.120.92-0.72
9$4 \cdot 10^3$400400372136“tek00006”195.840.93-0.63
10$5 \cdot 10^3$400400404125“tek00007”2251.010.09
11$6 \cdot 10^3$40040016492“tek00008”198.720.41-7.74
12$7 \cdot 10^3$40040015498“tek00009”246.960.385-8.29
13$8 \cdot 10^3$40040011284“tek00010”241.920.28-11.06
14$9 \cdot 10^3$40040014692.8“tek00011”300.6720.365-8.75
15$1 \cdot 10^4$4004007274.8“tek00012”269.280.18-14.89

2. 将 LLC 测试数据导入 Data2 中(输出 25V/10A 工况):

序号012345678
050400400$7.44 \cdot 10^3$$2.12 \cdot 10^3$“tek00016”38.1618.625.39
1100400400$4.44 \cdot 10^3$$1.6 \cdot 10^3$“tek00017”57.611.120.91
2200400400$2.34 \cdot 10^3$900“tek00018”64.85.8515.34
3400400400$1.2 \cdot 10^3$500“tek00019”7239.54
4600400400800324“tek00020”69.98426.02
5800400400640272“tek00021”78.3361.64.08
6$1 \cdot 10^3$400400488200“tek00022”721.221.73
7$2 \cdot 10^3$400400280107“tek00023”77.040.7-3.1
8$3 \cdot 10^3$40040018062“tek00024”66.960.45-6.94
9$4 \cdot 10^3$40040016664“tek00025”92.160.415-7.64
10$5 \cdot 10^3$40040011459“tek00026”106.20.285-10.9
11$6 \cdot 10^3$4004007550“tek00027”1080.188-14.54
12$7 \cdot 10^3$40040010038.4“tek00028”96.7680.25-12.04
13$8 \cdot 10^3$40040010038.8“tek00029”111.7440.25-12.04
14$9 \cdot 10^3$40040010544.8“tek00030”145.1520.263-11.62
15$1 \cdot 10^4$4004006140.8“tek00031”146.880.153-16.33

开环传递函数 Bode 图构建

环路扫频的第 8 列增益数据为扰动量到输出电压的表象增益。为了得到纯净的主功率模型,需要剥离测试外围电路的干扰。

忽略输出电压分压电路的滤波延迟,则输出电压转换电路的硬件物理增益为:

在扫频程序中,人为将采样数据乘以 $\frac{990}{2^{15}}$ 这个参量,因此构建开环传递函数 Bode 图要将这个人额外加入的参数去除:

提取频率量:

构建扰动量加入口到输出电压转换的整体真实增益:

构建扰动量加入口到输出电压转换的真实相位:

计算数据点个数:

根据上述提取的数据点,绘制原始未补偿 BODE 图的增益曲线与相位曲线如下图(红线为 40V/10A,蓝线为 25V/10A):

多零极点原始BODE图的增益曲线与相位曲线


多零极点环路补偿计算

定义设计的多零极点环路补偿后的系统闭环控制带宽(穿越频率)为:

从上述未补偿前的 Bode 图可知,红色曲线(40V/10A)的相位较小,衰减快,以此为补偿曲线。从图中可知,未补偿前红线在穿越频率点处的增益为:

计算得到实际的放大倍数为:

传统两零两极的传递函数可表示为(其中 K 为增益,$\omega_{z1}$ 和 $\omega_{z2}$ 为零点,$\omega_{p1}$ 为极点):

定义零点和极点如下(预设,当补偿后的相位裕度或者增益裕度不满足要求可以适当调整零点位置):

构建 PI 传递函数对应频率的函数:

根据闭环穿越频率的概念,穿越频率点处的增益为 0,即原始传递函数乘以补偿器的值等于 1,因此可以构建对应的方程:

通过预设 K 值,使用迭代求法求解 K 值:

求解该方程得到:

以此,得到补偿器的真实传递函数为:

求解对应频率的增益和相位:

绘制补偿后的增益曲线与相位曲线如下图:

多零极点补偿后的增益曲线与相位曲线

如上图可知,补偿后的红线工况穿越频率为 $1000\text{Hz}$,相位裕度为 $60$ 度,增益裕度在 $-20\text{dB}$ 以下,系统极其稳定。


数字控制参数换算与 Matlab Z 变换

由于 DSP 只能执行离散控制,使用 Matlab 工具对补偿器传递函数进行 Z 变换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fz1=800;
fz2=800;
fpl=10e3;
wz1=2*pi*fz1;
wz2=2*pi*fz2;
wpl=2*pi*fpl;
Ks=206.266;
num=[Ks/wz1/wz2 Ks*(1/wz1+1/wz2) Ks];
den=[1/wpl 1 0];
s=tf('s');
Gc=tf(num,den)
w=logspace(1,7);
p=bodeoptions;
p.PhaseVisible='on';
p.FreqUnits='Hz';
margin(Gc);
grid on;
ts=2e-5;
dsys=c2d(Gc,ts,'tustin')

得到传递函数如下:

对应的 Z 变换方程如下:

传统零极点的 Z 域传递函数表示为:

则对应的参数:

数字控制差分方程为:

将所有参数统一用 Q12 表示:


2零2极点控制 C 语言代码深度解析 (VLoopCtl2Z2P)

复频域的复杂微积分,最终完美化作了这段以微秒级速度狂飙的 C 语言差分方程代码。

注意:在 C 语言宏定义中,为了提高 DSP 硬件乘加器的计算效率,直接将反馈系数取反(即 $-a_1$ 和 $-a_2$),这样代码里就只剩下纯粹的加法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/*
** ===================================================================
** Funtion Name : void VLoopCtl2Z2P(void)
** Description : 输出恒压 2零2极 补偿环路计算
** Parameters : 无
** Returns : 无
** ===================================================================
*/
#define MAX_INTE 28086857 // LLC运行的最低频率35kHz,即最大PWM周期量 857*Q15
#define MIN_INTE 6553600 // LLC运行的最高频率150kHz,即最小PWM周期量 200*Q15
#define MIN_LOOPOUT 199 // 环路输出最小量限制,最高频率,最小周期量,150kHz -1余量
#define MAX_LOOPOUT 857 // 环路输出最大量限,最低频率,最大周期量,35kHz

// 差分方程系数 Q12 格式定点化
#define DSPb0 1415 // Q12
#define DSPb1 -2574 // Q12
#define DSPb2 1164 // Q12
#define DSPa1 5030 // 5034 //Q12 (注意:这是推导中 -a1 取正后的值)
#define DSPa2 -934 // -935 //Q12 (注意:这是推导中 -a2 取正后的值)

void VLoopCtl2Z2P(void)
{
// 电压误差 Q15 (定义静态变量保留历史状态)
static long VErr0=0, VErr1=0, VErr2=0;
// 环路输出中间量 (定义静态变量保留历史状态)
static long Vn0=0, Vn1=0, Vn2=0;

// 计算电压误差量,当参考电压大于输出电压,频率往小调,周期量往大调,输出量增加
VErr0 = ID2DCtrValue.Voref - SADC.Vout;

// 计算环路输出 (核心差分方程)
// 运算结束后右移12位,将 Q12 格式降维回实际的周期控制量
Vn0 = (Vn1*DSPa1 + Vn2*DSPa2 + VErr0*DSPb0 + VErr1*DSPb1 + VErr2*DSPb2) >> 12;

// 最终输出量限制,最小值限制
//if(Vn0 <= MIN_LOOPOUT)
//Vn0 = MIN_LOOPOUT;

// 最终输出量限制,最大值限制
if(Vn0 > MAX_LOOPOUT)
Vn0 = MAX_LOOPOUT;

// 最低频率限制 (软起动过程控制)
if (Vn0 > ID2DCtrValue.MaxPD)
Vn0 = ID2DCtrValue.MaxPD;

// 启动时,环路变量初始化
// 必须清空历史数据,防止开机瞬间用旧数据算出天文数字引发炸机
if(RsingInitFlag == 0)
{
VErr0=0;
VErr1=0;
VErr2=0;
Vn0=MIN_LOOPOUT;
Vn1=MIN_LOOPOUT;
Vn2=MIN_LOOPOUT;
}

// 输出赋值 (历史变量滑移,更新节拍)
Vn2=Vn1;
Vn1=Vn0;

// 误差赋值 (历史变量滑移,更新节拍)
VErr2=VErr1;
VErr1=VErr0;

// 环路输出赋值,供底层驱动刷新发波
ID2DCtrValue.PD = Vn0;
}
空载轻载打嗝(Burst)控制与代码分析

空载轻载打嗝(Burst)控制与代码分析

在 LLC 谐振变换器的实际工程应用中,全负载范围内的稳定调压是一个巨大的挑战。特别是当系统处于极轻载或完全空载状态时,由于寄生电容和励磁电感的物理特性,传统的调频控制往往会面临失效的窘境。为了彻底解决这一问题,系统引入了打嗝(Burst)控制模式

本章将详细解析什么是打嗝控制、为什么要使用它,并深度剖析其底层的 C 语言实现逻辑。


什么是打嗝控制?为什么要使用它?

物理瓶颈与失效背景:
LLC 变换器通过改变开关频率来调节输出电压(频率越高,输出电压越低)。然而,当模块输出等效负载太轻或彻底空载时,LLC 的电压增益曲线会变得极其平缓。此时,即使系统控制环路将 LLC 工作频率推到了硬件允许的最高频率极限,变压器原边仍然会向副边泵入过多的能量,导致输出依然不能达到想要降低的参考设定电流和电压,使得输出电压不受控制地持续飘高。

Burst 控制的核心原理:
为了防止空载时输出电压失控过压,控制环路必须进入 Burst(打嗝)控制模式。
所谓打嗝控制,其本质是一种能量的“间歇性投递”策略:即当 PI 环路计算出的 LLC 控制频率大于设定的最高频率(对应周期量小于最小值)时,系统直接采取硬核手段彻底关闭 PWM 发波;此时能量停止传输,输出电容向负载缓慢放电,电压开始回落;当 PI 环路需求频率小于最高频率时,系统再次重新打开 PWM 恢复发波

系统通过这种一开一关的低频间歇工作方式,极大地降低了单位时间内的平均能量传输,从而使得极轻载下的输出电压和电流也能精准达到目标值。


Burst 控制模式程序流程图

在 20us 的超高频定时中断中,每次执行完 PI 环路计算后,程序都会紧接着进行 Burst 状态的判定与干预。其判定与执行逻辑的程序流程图如下图所示:

Burst控制模式

流程图逻辑简析:

  1. 首先检测是否处于 Burst 允许使能的状态。
  2. 判断当前 PI 环路输出的控制量(周期)是否已经小于设定的安全最小值(即频率超出了最高限制)。
  3. 如果超出极限,则将 PWM 关闭标志位置 1,并物理切断 PWM 输出。
  4. 如果控制量回到安全范围内,且检测到此前 PWM 处于关闭状态,则清除标志位,并重新开启 PWM 脉冲输出,恢复正常的调频工作。

核心代码深度解析

这段控制逻辑在代码层面主要分为两部分:一是状态检测与标志位调度的 BurstCtl 主函数;二是负责底层硬件发波切断与恢复的 PWMDisPWMEn 函数。

Burst 状态机主调度函数 (BurstCtl)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*
** ===================================================================
** Funtion Name : void BurstCtl(void)
** Description : 当调频和调宽的情况下还不能满足低压或轻载情况,则进入Burst模式,关闭PWM,
** Parameters : 无
** Returns : 无
** ===================================================================
*/
void BurstCtl(void)
{
// 定义 PWM 硬件关闭的静态历史标志位,用于记录当前系统是否正处于 "打嗝的停波期"
static unsigned char Flag=0;

// 首先判断系统整体配置中是否允许 (放开) 了 Burst 使能功能
if(getRegBits(DF.CtrFlag, F_BURST))
{
// ID2DCtrValue.PD 是 PI 环路计算出来的目标 PWM 周期值。周期越小,频率越高。
// MIN_BURST 是系统硬件允许的最小周期边界(最高频率极限)。
// 如果恒压环需求的周期数量足够小(小于 MIN_BURST),意味着常规调频已经无能为力,必须关闭 PWM。
if (ID2DCtrValue.PD < MIN_BURST)
{
// 设定 PWM 关闭标志位为 1,记录当前进入强制停波状态
Flag=1;
// 调用底层切断函数,从物理引脚层面关闭 PWM 输出
PWMDis();
}
else
{
// 如果 PI 环路计算出的周期值回到了正常区间(> MIN_BURST),
// 说明输出电压已经跌落,需要重新注入能量。
// 此时检测 PWM 标志位是否为关闭状态 (Flag == 1)
if(Flag)
{
// 清除停波标志位,宣告打嗝的停波期结束
Flag=0;
// 调用底层恢复函数,继续正常发波
PWMEn();
}
}
}
}

硬件级极速封波与恢复机制 (PWMDis & PWMEn)

在电源控制中,要求“关得死,开得快”。为了彻底防止在停波期间由空间电磁干扰(EMI)引起的引脚误动作导致全桥直通炸机,代码没有采取“将占空比设为 0”这种软做法,而是直接篡改了芯片底层 GPIO 的复用属性(MUX),实施了硬核物理隔离。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
** ===================================================================
** Funtion Name : void PWMDis(void)
** Description : 关闭PWM (硬件级物理钳位)
** Parameters : none
** Returns : none
** ===================================================================
*/
void PWMDis(void)
{
EALLOW; // 解除特殊寄存器写保护

// 【第一步:剥夺外设控制权】
// GPAMUX1 是引脚功能复用选择寄存器。
// 将 GPIO0 和 GPIO1 的 MUX 设置为 0,意味着这两个引脚不再受 ePWM 硬件模块的控制,
// 瞬间退化为最普通的通用输入/输出引脚 (GPIO)。发波被立刻物理截断。
GpioCtrlRegs.GPAMUX1.bit.GPIO0=0;
GpioCtrlRegs.GPAMUX1.bit.GPIO1=0;

// 【第二步:配置为推挽输出模式】
// 将这两个通用 GPIO 的方向 (DIR) 强制配置为输出模式。
GpioCtrlRegs.GPADIR.bit.GPIO0=1;
GpioCtrlRegs.GPADIR.bit.GPIO1=1;

// 【第三步:死锁低电平】
// 向 GPACLEAR 寄存器写入 1,强制这两个引脚输出绝对的低电平 (0V)。
// 这将死死拉低栅极驱动芯片的输入端,确保全桥上下管彻底熄灭,杜绝任何杂波干扰。
GpioDataRegs.GPACLEAR.bit.GPIO0=1;
GpioDataRegs.GPACLEAR.bit.GPIO1=1;

EDIS; // 恢复写保护
}

当输出电压回落,需要再次注入能量时,系统只需重新将引脚控制权交还给内部的 ePWM 硬件模块即可,脉冲会瞬间无缝衔接输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
** ===================================================================
** Funtion Name : void PWMEn(void)
** Description : 打开PWM (恢复外设控制权)
** Parameters : none
** Returns : none
** ===================================================================
*/
void PWMEn(void)
{
EALLOW; // 解除写保护

// 将 GPIO0 和 GPIO1 的功能复用开关重新拨向 1。
// 此时,引脚的电平状态立刻交由 ePWM1A 和 ePWM1B 模块接管。
// 由于 ePWM 模块内部的定时器和状态机一直在后台保持同步运行,
// 一旦复用开关打开,精准的互补带死区 PWM 波形就会立刻喷涌而出,恢复能量传输。
GpioCtrlRegs.GPAMUX1.bit.GPIO0=1;
GpioCtrlRegs.GPAMUX1.bit.GPIO1=1;

EDIS; // 恢复写保护
}
短路等快速保护与寄存器更新

短路等快速保护与寄存器更新

在数字开关电源的设计中,控制算法的闭环计算仅仅是系统稳定运行的前提。当系统面临突发的极端恶劣工况(如输出端意外短路、原边谐振腔电流激增、前级 PFC 意外掉电等)时,DSP 必须能够在微秒级别做出响应,强行接管或切断发波,以防止功率器件发生不可逆的物理损坏。

本章将详细梳理 20us 高频中断中最核心的两个收尾动作:PWM 寄存器的安全更新刷新,以及三大快速保护机制(输出短路、原边硬件过流、PFC 状态机联锁)的设计原理与代码实现。


PWM 寄存器安全更新 (RegReflash)

在执行完复杂的电压外环、电流内环以及 Burst 打嗝逻辑后,程序最终会得出系统需要的运行周期量(PD)。然而,在将这个数值真实地写入底层硬件 PWM 寄存器之前,必须进行最后一道安全防线的拦截——极限边界钳位

核心逻辑说明:

  1. 频率安全限制:电压环计算得到 LLC 的控制周期量(频率值)后,需对控制量进行最大和最小值限制,避免运行出现频率过小(导致变压器饱和)或者过大(导致开关损耗失控炸管)。
  2. 软启动平滑介入:在电源刚刚启动时,为了限制极大的启动电流过冲,程序引入了 MaxPD 变量。通过在启动阶段逐渐减小 MaxPD 的限幅值,强迫电源以最高频率、极小能量的状态平滑启动。
  3. 50%占空比锚定:根据配置的 PWM 发波方式,更新 PWM 的周期寄存器 TBPRD 时,为了保证占空比依然恒定为 50%,必须同步将对应的计数寄存器 CMPA 值更新为周期值的一半(Period >> 1)。
  4. 死区限制:对最小死区进行硬性限制,避免上下管死区过小发生桥臂直通。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*
** ===================================================================
** Funtion Name : void RegReflash(void)
** Description : PWM寄存器更新函数,更新频率周期值,更新LLC运行频率
** Parameters :无
** Returns :无
** ===================================================================
*/
void RegReflash(void)
{
// 定义频率对应的周期量,赋初值为系统允许的最小周期(最高频率)
static long Period = MIN_BURST;

// 软启动与最大周期(最低频率)限幅:
// 当环路输出量大于设定最大值时限制之。MaxPD值在启动时会由小逐渐增大,
// 用以强制高频和小占空比启动,有效限制启动时刻的谐振腔电流过冲。
if(ID2DCtrValue.PD > ID2DCtrValue.MaxPD)
ID2DCtrValue.PD = ID2DCtrValue.MaxPD;

// 最小周期(最高频率)限幅:
if(ID2DCtrValue.PD < MIN_LOOPOUT)
ID2DCtrValue.PD = MIN_LOOPOUT;

// 硬件死区时间(Dead-Time)安全限幅:
if(ID2DCtrValue.DT < MIN_DT)
ID2DCtrValue.DT = MIN_DT;

Period = ID2DCtrValue.PD ;

EALLOW; // 解除系统寄存器写保护

// 更新周期值 TBPRD,实装控制频率
EPwm1Regs.TBPRD = Period;
// 更新比较值 CMPA,Period 右移 1 位即除以 2,永远保持完美的 50% 占空比
EPwm1Regs.CMPA.half.CMPA = Period >> 1;
// 更新上升沿与下降沿死区寄存器值
EPwm1Regs.DBFED = ID2DCtrValue.DT;
EPwm1Regs.DBRED = ID2DCtrValue.DT;

EDIS; // 恢复写保护
}

快速保护函数总调度 (FastProtection)

模块具有快速保护功能和慢速保护功能,定义输出短路、LLC谐振电流过流保护、PFC_OK信号这三项为快速保护功能(其余如过温保护等为慢速保护,在 5ms 中断中执行)。这三项快速保护直接挂载在 20us 的超高频中断(ISR_20US)末尾,确保故障发生后在几十微秒内即可瞬间切断输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/** ===================================================================
** Funtion Name : __interrupt void ISR_20US(void)
** Description : 中断优先级为Level1,执行参数采样、环路计算及PWM寄存器更新、Burst控制、快速保护功能,配置ADC采样等。
** Parameters :无
** Returns :无
** ===================================================================*/
__interrupt void ISR_20US(void)
{
PWMDAC(SADC.IoutOffset);

ADCSample(); //ADC采样函数,采样输出电压电流等相关温度信息
ILimitCtlPI(); //输出限流环
VLoopCtlPI();//PI电压环环路计算
BurstCtl();//空载或轻载情况,调频不能满足输出电压需求,Burst控制,
RegReflash(); //PWM寄存器更新,频率更新

FastProtection(); //模块快速保护函数,包含输出短路等三大核心保护

CpuTimer0Regs.TCR.bit.TIF = 1; //清中断标志位
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}

/*
** ===================================================================
** Funtion Name :void FastProtection(void)
** Description : 保护函数,输出短路保护和原边硬件过流封波
** Parameters :无
** Returns :无
** ===================================================================
*/
void FastProtection(void)
{
// 只要系统不处于初始化(Init)状态,就必须开启严密的实时监控
if(DF.SMFlag != Init)
{
// 1. 输出短路保护 (软件计算快速判定)
ShortOff();
// 2. LLC原边硬件过流封波 (硬件比较器 TZ 判定)
HwOpp();
// 3. PFC_OK信号联锁检测 (前级母线健康度判定)
PFCokCheck();
}
}

输出短路保护与打嗝重启机制 (ShortOff)

当模块输出端负载意外短路时,由物理短路特性可知,此时输出侧的电压将被瞬间拉得很低,且电流会变得极其巨大。因此,我们可以通过同时检测输出电压跌落和输出电流飙升这两个条件,来综合判断输出是否发生短路工况。

短路阈值参数计算:
为了精准判定短路且防止误触发,我们定义输出短路保护的电压阈值为最低标称电压的 10%,最大电流阈值为额定最大电流的 150%。计算公式如下:

根据之前章节论述的数字量转换关系,将其映射为 DSP 内部的 Q15 数字量判定标准:

这正是代码中 #define IO_SHORT_DSP 22342#define VO_SHORT_DSP 1103 这两个宏定义的理论来源。

当模块检测到输出电流过大且输出电压过低时可认为发生短路故障,需进行短路保护处理:禁止模块 Burst 功能(防止 Burst 逻辑在低压时误打开 PWM)、强行关闭 PWM置位短路保护标志位,并跳转至故障状态机

为了提高电源的可用性,模块具备输出短路故障撤出后自动重启功能。发生短路保护关机后,程序会进入长达 4 秒的冷却等待时间。若计时器累加至 4 秒后,系统判断重启次数若大于 10 次,说明是永久性死短路,禁止再次重启并死锁保护;若重启次数小于 10 次,则清空标志位重新软起动。

其程序流程图及代码实现如下:

短路保护程序流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*
** ===================================================================
** Funtion Name :void ShortOff(void)
** Description :短路保护,可以打嗝重启10次
** Parameters : none
** Returns : none
** ===================================================================
*/
#define IO_SHORT_DSP 22342 // 短路电流判据 15A 对应的 Q15 转换值
#define VO_SHORT_DSP 1103 // 短路电压判据 2.4V 对应的 Q15 转换值

void ShortOff(void)
{
static long RSCnt = 0;
static unsigned char RSNum =0 ;

// 当输出电流大于设定电流(15A),且电压跌落小于设定电压(2.4V)时,可判定为发生真实短路
if((SADC.Iout > IO_SHORT_DSP) && (SADC.Vout < VO_SHORT_DSP))
{
// 禁止 Burst 功能 (防止打嗝逻辑误判需要注入能量而开启发波)
clrRegBits(DF.CtrFlag, F_BURST);
// 物理层关闭 PWM 驱动输出
PWMDis();
// 置位短路故障标志位
setRegBits(DF.ErrFlag, F_SW_SHORT);
// 状态机强行跳转至故障保护状态 (Err)
DF.SMFlag = Err;
}

// 【输出短路保护恢复与打嗝重启逻辑】
// 当发生输出短路保护或 LLC 硬件过流时,关机后等待 4S 后清除故障信息,进入等待状态尝试重启
if((getRegBits(DF.ErrFlag,F_SW_SHORT)) || (getRegBits(DF.ErrFlag,F_LLC_OCP)))
{
// 等待故障清除计时器累加 (每次 20us 中断加 1)
RSCnt++;

// 200000 * 20us = 4秒。冷却 4 秒钟,防止器件频繁受冲击过热
if(RSCnt > 200000)
{
// 计时器清零,准备下一轮判断
RSCnt=0;

// 短路重启只尝试重启 10 次,10 次后判定为长久性死短路,彻底锁死不重启
if(RSNum > 10)
{
// 确保不清除故障,不重启
RSNum = 11;
// 再次确认禁止 Burst 功能
clrRegBits(DF.CtrFlag, F_BURST);
// 再次确认关闭 PWM
PWMDis();
}
else
{
// 短路尝试重启次数计数器累加
RSNum++;
// 尝试清除短路过流保护故障标志位
clrRegBits(DF.ErrFlag,F_SW_SHORT);
// 短路时极大可能同时触发 LLC 原边硬件 OCP,一并清除 OCP 标志后准备重启
clrRegBits(DF.ErrFlag,F_LLC_OCP);
}
}
}
}

谐振原边电流硬件过流封波 (HwOpp)

当电源侧输出短路,或者突加大负载时,会造成模块副边电流迅速增加,同时原边谐振腔电流会瞬间产生剧烈的激增。为了防止瞬间极大的原边电流对功率半导体(MOSFET)造成电流击穿或瞬态结温过热,必须不依赖软件计算,采用最高级的硬件级封波来抑制电流。

硬件比较器机制:
谐振电流保护采用 DSP 控制器自身内设的模拟比较器设计。外部硬件电路将原边互感器采样的电流信号送进内设比较器的正输入端,控制器内部 DAC 产生比较器的负端比较参考阈值电压。
当原边电流峰值过大,达到 DAC 设定的参考电压时,内设比较器输出发生翻转。该翻转信号无需经过 CPU 软件运算,直接在硬件底层快速触发 ePWM 模块的 TZ(Trip Zone, one shot)端,瞬间强制剥夺 PWM 输出,硬件标志位 OST 置位。

软件在此处的职责仅仅是:检测到硬件已经动手封波后,同步更新软件状态机并维持锁定,随后交由上述的 ShortOff 里的打嗝逻辑来处理重启。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
** ===================================================================
** Funtion Name :void HwOpp(void)
** Description :原边电流过流保护,单周期封波,Peak电流保护 (硬件级)
** Parameters : none
** Returns : none
** ===================================================================
*/
void HwOpp(void)
{
// 检测到 PWM 模块内部的 TZ 硬件触发标志 (OST) 置 1,说明硬件比较器已经翻转,发生了原边电流过大封波
if((EPwm1Regs.TZFLG.bit.OST == 1) && (OppFlag == 0))
{
// 关闭软件 Burst 逻辑使能
clrRegBits(DF.CtrFlag, F_BURST);
// 软件层再次确认关闭 PWM,加固物理断开
PWMDis();
// 锁死本次故障的单次记录标志
OppFlag=1;
// 状态机强行进入故障状态
DF.SMFlag = Err;
// 置位 LLC 原边过流 (OCP) 软件标志位,通知短路打嗝逻辑准备 4 秒冷却
setRegBits(DF.ErrFlag, F_LLC_OCP);
}
}

PFC 前级健康度联锁保护 (PFCokCheck)

LLC 谐振变换器的正常运行必须基于前级 PFC(功率因数校正)电路提供稳定的直流母线电压。当模块的 PFC 尚未进入稳压状态,或 PFC 突发故障(如掉电、过压等)时,LLC 必须绝对禁止运行,否则 LLC 增益特性将彻底错乱,导致磁芯偏磁或输出失控。

PFC 芯片通过一个专用的硬件引脚(PFC OK 端口)输出高低电平,来告知 LLC 的 DSP 自身运行是否健康正常。
当 LLC 检测到 PFC OK 信号为高电平时,方可置位 F_PFC_OK 标志位,允许启动。反之,如果在运行中突然丢失此信号,LLC 必须紧急切断输出,退回到 Wait(等待)状态机。

PFC OK信号程序流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
** ===================================================================
** Funtion Name :void PFCokCheck(void)
** Description :只有 PFC 状态为 OK 的时候,后级 DCDC (LLC) 才允许启动和运行
** Parameters : none
** Returns : none
** ===================================================================
*/
// 宏定义读取 GPIO24 的电平状态,此引脚直接连接前级 PFC 控制器的 OK 信号输出端
#define PFCok GpioDataRegs.GPADAT.bit.GPIO24

void PFCokCheck(void)
{
if(PFCok) // 检测到高电平,说明 PFC 母线电压已经稳压准备好
setRegBits(DF.CtrFlag, F_PFC_OK); // 置位系统允许运行标志位
else
clrRegBits(DF.CtrFlag, F_PFC_OK); // 清除标志位,前级异常

// 运行态动态拦截:
// 当 DCDC (LLC) 正在启动 (Rise) 或者 稳定运行 (Run) 过程中,
// 若 PFC 突然进入不 OK 状态(例如突发掉电或 PFC 炸机),则必须立刻切断 DCDC
if((!getRegBits(DF.CtrFlag, F_PFC_OK)) && ((DF.SMFlag == Rise) || (DF.SMFlag == Run)))
{
// 紧急关闭 Burst 功能
clrRegBits(DF.CtrFlag, F_BURST);
// 瞬间物理关闭 PWM 输出脉冲
PWMDis();
// 状态机退出运行,跳转到空闲等待状态 (Wait),静静等待 PFC 重新恢复正常
DF.SMFlag = Wait;
}
}
过流过温等慢速保护与 200Hz 状态机调度

过流过温等慢速保护与 200Hz 状态机调度

在数字电源的控制架构中,任务的优先级分配是系统稳定运行的核心。前文所述的 20us 中断(50kHz)负责最核心的闭环计算与快速硬件保护,而对于一些需要一定时间滤波、不要求微秒级响应的保护逻辑(如软件过流、过温、通信丢失等),则被统一安排在优先级较低的 200Hz(5ms)中断中执行。

本章将详细解析 200Hz 中断的嵌套调度机制,并逐一剖析输出过流、过压、欠压、过温及通信丢失等慢速保护的底层 C 语言实现逻辑与恢复机制。


200Hz 中断总调度与嵌套逻辑 (ISR_200Hz)

5ms 中断(即 200Hz)优先级较低,运行包括慢速保护、LLC 状态机、参考电压计算函数、SCI 通信函数、LLC 状态显示等功能。其程序流程图如下图所示:

200Hz中断函数程序流程图

【深度代码解析:中断嵌套机制】
在单片机中,默认情况下进入一个中断后会屏蔽其他同级或低级中断。由于 5ms 中断执行的代码量非常庞大(包含复杂的通信协议与状态机),如果在此期间全局屏蔽中断,将会导致极其关键的 20us 高频控制中断被严重阻塞,进而引发电源炸机。
因此,代码在进入 200Hz 中断的第一时间,巧妙地使用了中断嵌套技术:手动清除应答标志位并重新开启全局中断(EINT),使得 20us 的核心环路中断可以随时打断当前的 5ms 慢速中断,保障了控制环路的绝对实时性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
struct  _FLAG    DF={0};
struct _Ctr_value ID2DCtrValue={0};

/** ===================================================================
** Funtion Name : __interrupt void ISR_200Hz(void)
** Description : 中断优先级为Level2,执行电源状态机和慢速保护,获取电压参考值等
** Parameters : 无
** Returns : 无
** ===================================================================*/
__interrupt void ISR_200Hz(void)
{
// 【核心机制:实现中断嵌套】
// 保证 20us 的高频环路控制中断优先级最高,能够打断本慢速中断
IER = M_INT1; // 重新开启第一组中断 (INT1,20us定时器所在组)
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // 清除应答,允许外设继续发送中断请求
__asm(" NOP"); // 延时等待硬件状态机同步
EINT; // 开启全局中断 (Enable Global interrupt INTM)

// 1. 慢速保护总线
SlowP();
// 2. CAN输出开关机逻辑
CanOutEn();
// 3. 电源主状态机调度函数
StateM();
// 4. CAN通信获取参考电压
CANVrefGet();
// 5. CAN通信获取参考电流
CANIrefGet();
// 6. SCI 级联通信
SciCom();
// 7. LED 状态指示灯刷新
LEDShow();

// 清除定时器 1 (5ms定时器) 的底层中断标志位,准备下一次触发
CpuTimer1Regs.TCR.bit.TIF = 1;
return;
}

慢速保护总线函数 (SlowP)

慢速保护功能函数包含电源所有的慢速保护功能,包含输出过流保护、输出过压保护、输出欠压保护、CAN 通信掉电保护、散热器过温保护等。慢速保护不同于快速保护,对时间响应性要求不高,因此可以加入更多的软件防抖滤波时间,避免误触发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/** ===================================================================
** Funtion Name :void SlowP(void)
** Description : 保护函数,执行输出过压保护,输出过流保护,器件过温保护等
** Parameters : 无
** Returns : 无
** ===================================================================*/
void SlowP(void)
{
// 只要电源脱离了初始上电状态 (Init),就全面开启慢速保护监控
if(DF.SMFlag != Init)
{
SwOCP(); // 输出软件过流保护函数
SwOVP(); // 输出软件过压保护函数
SwUVP(); // 输出欠压保护函数
//CANLost(); // CAN丢失保护 (按需开启)
SwOTP(); // 过温保护函数
}
}

软件输出过流保护 (SwOCP)

当电源模块正在运行过程中,检测到输出电流大于过流阈值,且连续保持若干时间电流过大,则判断为输出过流,系统将执行保护:禁止模块 Burst 功能(因 Burst 功能会主动打开 PWM),关闭 PWM,置位短路保护标志位,跳转至故障状态机。

过流保护恢复与重启机制
若检测到过流标志位为 1,则说明发生过流保护。计时器累加至 4 秒后,判断重启次数是否大于 10 次:

  • 若重启次数大于 10 次,说明输出持续过流、负载过大,禁止模块再次重启,进入死锁保护。
  • 若重启次数小于 10 次,则人为清空过流标志位。程序在过流标志位清除后,若无其他故障发生,则状态机自动从故障状态跳转至等待状态,并重新软起动。

其程序流程图及代码解析如下:

过流保护程序路程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*
** ===================================================================
** Funtion Name :void SwOCP(void)
** Description :软件过流保护,关机后等待若干时间后清除故障,模块打嗝重启
** ===================================================================
*/
// 设定过流保护点。17873 对应设定的硬件转换 Q15 数字量
#define IO_OCP_DSP 17873

void SwOCP(void)
{
// 过流保护判据防抖保持计数器
static unsigned int OCPCnt=0;
// 故障清除(冷却等待)保持计数器
static unsigned int RSCnt=0;
// 保护重启次数累加器
static unsigned int RSNum=0;

// 当输出平均电流大于设定值,且电源处于正常运行状态(Run)时
if((SADC.IAvg > IO_OCP_DSP) && (DF.SMFlag == Run))
{
// 异常条件保持计时 (每次进入为 5ms)
OCPCnt++;
// 条件连续保持 20 次 (20 * 5ms = 100 毫秒),则确认为真实过流,滤除瞬态尖峰
if(OCPCnt > 20)
{
OCPCnt = 0;
// 执行保护三板斧:禁止打嗝、封波、报错
clrRegBits(DF.CtrFlag, F_BURST);
PWMDis();
setRegBits(DF.ErrFlag, F_SW_OCP);
// 状态机跳转至故障状态
DF.SMFlag = Err;
}
}
else
OCPCnt = 0; // 条件不满足时,防抖计数器瞬间清零

// 【过流后的冷却与打嗝重启机制】
if(getRegBits(DF.ErrFlag,F_SW_OCP))
{
// 等待故障清除计数器累加
RSCnt++;
// 冷却 800 次 (800 * 5ms = 4 秒)
if(RSCnt > 800)
{
RSCnt=0;
RSNum++; // 重启次数累加

// 重启 10 次判定
if(RSNum > 10 )
{
RSNum = 11; // 锁定数值防溢出
clrRegBits(DF.CtrFlag, F_BURST);
PWMDis(); // 永久封锁
}
else
{
// 10次以内,清除过流标志位,放行系统进入 Wait 状态重新软起动
clrRegBits(DF.ErrFlag, F_SW_OCP);
}
}
}
}

软件输出过压保护 (SwOVP)

程序设置输出过压保护,旨在抑制输出电压持续过高损坏器件。
电源模块时刻检测输出电压,当输出电压大于过压保护阈值,且连续保持若干时间电压过高,则判断为输出过压,执行保护措施。

不可恢复特性:
值得注意的是,电源通常过压保护被设计为不可恢复性保护,这是为了防止持久输出的高压对电源后级负载造成毁灭性的电压击穿损坏。因此代码中没有像 OCP 那样的打嗝重启逻辑。

过压保护程序流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
** ===================================================================
** Funtion Name :void SwOVP(void)
** Description :软件过压保护,发生后不重启
** ===================================================================
*/
// 设定过压保护阈值。23163 对应设定的 Q15 数字量极值
#define VO_OVP_DSP 23163

void SwOVP(void)
{
// 过压保护判据防抖保持计数器
static unsigned int OVPCnt=0;

// 当输出电压瞬时值大于阈值
if (SADC.Vout > VO_OVP_DSP)
{
OVPCnt++;
// 连续保持 2 次 (2 * 5ms = 10 毫秒) 即触发,过压容忍度极低
if(OVPCnt > 2)
{
OVPCnt=0;
// 保护动作
clrRegBits(DF.CtrFlag, F_BURST);
PWMDis();
setRegBits(DF.ErrFlag, F_SW_OVP);
DF.SMFlag = Err;
}
}
else
OVPCnt = 0;
}

软件输出欠压保护 (SwUVP)

若电源的主功率器件短路、驱动芯片损坏等,均会导致输出无任何状态。程序通过判定输出电压的大小和电源运行状态信息,来综合判定电源是否触发了欠压保护。

电源模块时刻检测到输出电压,当输出电压小于欠压保护阈值,同时必须满足电源的状态机处于正常运行状态,且连续保持若干时间后,才判断为输出欠压,并执行保护停机。

欠压保护程序流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
** ===================================================================
** Funtion Name :void SwUVP(void)
** Description :软件欠压保护
** ===================================================================
*/
// 设定欠压保护阈值。9927 对应下限电压的 Q15 数字量
#define VO_UVP_DSP 9927

void SwUVP(void)
{
// 欠压防抖保持计数器
static unsigned int UVPCnt=0;

// 核心条件:不仅电压要低,且状态机必须已经进入 Run (运行状态)。
// 防止在 Rise(软启动) 阶段因为电压尚未建立就被误判为欠压。
if ((SADC.VAvg < VO_UVP_DSP) && (DF.SMFlag == Run))
{
UVPCnt++;
// 条件连续保持 100 次 (100 * 5ms = 500 毫秒)。
// 欠压的容忍时间最长,以容忍负载突加时的瞬态电压大跌落。
if(UVPCnt > 100)
{
UVPCnt=0;
clrRegBits(DF.CtrFlag, F_BURST);
PWMDis();
setRegBits(DF.ErrFlag, F_SW_UVP);
DF.SMFlag = Err;
}
}
else
UVPCnt = 0;
}

CAN通信丢失保护 (CANLost)

CAN 通信主要应用于上位机对模块的实时控制。正常运行时,上位机以一定时间间隔向模块发送控制信息,模块也以一定的时间间隔回复上位机的指令。当上位机与模块之间的 CAN 通信发生故障(如断开通信连接)时,为了安全起见,模块需自动停止输出功能。

模块设有 CAN 通信掉线保护功能。CanDat.LostCnt 为通信掉线计时器,在其他中断中以 5ms 为基准计时,当模块收到上位机的信息时,该计时器被清零。
若模块连续若干时间内并未收到来自上位机的 CAN 信息,将执行保护;若再次收到来自上位机的 CAN 通信指令,则标志位清理,模块重新启动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
** ===================================================================
** Funtion Name : void CANLost(void)
** Description : CAN通信丢失(掉线)超时保护
** ===================================================================
*/
void CANLost(void)
{
// 1200 次 * 5ms = 6000 毫秒 (6秒)
// 连续 6S 内没收到上位机的任何心跳或控制信息,判定脱机失联
if(CanDat.LostCnt > 1200)
{
CanDat.LostCnt =0;
// 执行安全保护:禁止发波,切断输出
clrRegBits(DF.CtrFlag, F_BURST);
PWMDis();
// 置位通信丢失故障标志位
setRegBits(DF.ErrFlag, F_CAN_LOST);
DF.SMFlag = Err;
}
}

散热器过温保护 (SwOTP)

电源长期运行或者高温状态运行下,半导体器件产生的热量会逐渐积累,必须设立过温保护功能,保护半导体器件温度不过高而损坏。

程序根据 NTC 温度传感器的 ADC 采样值,通过查表法查找与采样值对应的真实温度值。
当检测到散热器的温度大于过温保护阈值时,且连续 1S 保持过高温度,则认为器件过温,执行保护停机并跳转至故障状态机。

过温恢复(迟滞判定):
若检测到过温标志位为 1,程序会继续实时检测散热器温度。当在模块关闭输出后,散热器温度自然回落,恢复至设定的较低值(恢复阈值)以下时,则人为清空过温标志位。程序在过温标志位清除后,状态机自动从故障状态跳转至等待状态重新软起动。

过温保护程序流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/*
** ===================================================================
** Funtion Name :void SwOTP(void)
** Description :散热器过温保护与迟滞恢复
** ===================================================================
*/
// 【温度标定解密】
// 由于 C 语言无符号整型不便处理负数,底层查表代码将 0 数字量映射为 -40℃。
// 因此,宏定义中的 90 实际代表物理温度 50℃ (90 - 40 = 50)。
// 宏定义中的 80 实际代表物理温度 40℃ (80 - 40 = 40)。
#define MAX_HIGHEST_TEMP 90 // 触发保护阈值 (50摄氏度)
#define MAX_HIGHEST_TEMP_RE 80 // 恢复重启阈值 (40摄氏度)

void SwOTP(void)
{
static unsigned int OTPCnt=0; // 过温防抖保护计时

// 查表获取 Mos 实际的散热器温度值,以度数为单位,且向上相加 40 (平移坐标系消灭负数)
SADC.MosDeg = Temper[((unsigned int)SADC.SinkT1 >> 6)] + 40;
// 查表获取 Diode (二极管) 实际的散热器温度值
SADC.DioDeg = Temper[((unsigned int)SADC.SinkT2 >> 6)] + 40;

// 如果任意一个散热器的温度超过保护阈值 (50℃)
if( (SADC.MosDeg > MAX_HIGHEST_TEMP) || (SADC.DioDeg > MAX_HIGHEST_TEMP) )
{
OTPCnt++;
// 持续 200 次 (200 * 5ms = 1 秒) 确认为真过温,防止传感器瞬态干扰
if(OTPCnt > 200)
{
OTPCnt=0;
// 保护动作
clrRegBits(DF.CtrFlag, F_BURST);
PWMDis();
setRegBits(DF.ErrFlag, F_SW_OTP);
DF.SMFlag = Err;
}
}
else // 不满足过温条件,计时器复位清零
OTPCnt=0;

// 【过温保护后的迟滞恢复重启逻辑】
// 当输出过温保护后,强制要求散热器温度下降到更低的 40 度以下,才允许重新启动。
// 这形成了一个 10℃ 的滞回区间,彻底防止电源在临界温度点上疯狂启停跳动。
if(getRegBits(DF.ErrFlag,F_SW_OTP))
{
// 必须保证所有功率器件 (MOS 和 Diode) 的温度均恢复至可重启温度范围 (40℃) 以内
if( (SADC.MosDeg < MAX_HIGHEST_TEMP_RE) && (SADC.DioDeg < MAX_HIGHEST_TEMP_RE) )
{
// 清除过温故障标志位,放行系统重启
clrRegBits(DF.ErrFlag, F_SW_OTP);
}
}
}
LLC状态机函数与运行控制逻辑解析

LLC状态机函数与运行控制逻辑解析

在数字电源的高可靠性设计中,除了高频的闭环 PID 计算外,电源的启停、故障保护、自恢复以及各功能模块的时序调度,都需要一个严密且稳健的“状态机(State Machine)”来统筹管理。

程序以状态机的方式表明模块的每一个运行状态。程序状态机运行周期为 5mS,主要由以下五个核心状态组成:初始化状态(Init)、等待状态(Wait/Idle)、启动状态(Rise)、正常运行状态(Run)、故障状态(Err)。各个状态之间相互独立,且严格按照设定条件进行跳转,其跳转条件逻辑如下图所示。

LLC运行状态机

状态机跳转宏观条件总览如下

  • 初始化状态至空闲状态:上电程序参数初始化,电流偏置采样后跳转等待状态。
  • 空闲状态至软启动状态:等待 PFC 与 LLC 均无故障,且 PFC 正常运行,跳转软起动状态。
  • 软启动状态至正常运行状态:开启 PWM,频率逐渐从高频平滑启动并过渡到闭环。
  • 软启动状态至故障状态:启动过程中发生故障或保护,强制中断启动。
  • 正常运行状态至故障状态:正常运行过程中发生故障或保护,立即切断输出。
  • 故障状态至空闲状态:故障消除后自动跳转,模块自动恢复启动准备阶段。

状态机调度总入口 (ISR_200Hz & StateM)

状态机的大脑位于 200Hz(5ms)的定时器中断中。为了防止庞大的状态机逻辑阻塞 20us 的核心发波环路,这里极其关键地使用了中断嵌套机制。
StateM 函数则是状态机的核心集线器,使用 switch-case 结构对当前所处状态进行分支散转。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/** ===================================================================
** Funtion Name : __interrupt void ISR_200Hz(void)
** Description : 中断优先级为Level2,执行电源状态机和慢速保护,获取电压参考值等
** Parameters :
** Returns :
** ===================================================================*/
__interrupt void ISR_200Hz(void)
{
// 实现中断嵌套,保证 20us 高频环路控制中断优先级最高,随时可打断本程序
IER = M_INT1; // 开中断INT1 (环路所在组)
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // 清除组应答
__asm(" NOP"); // 等待流水线同步
EINT; // Enable Global interrupt INTM (开启全局中断)

SlowP(); // 执行慢速保护函数
CanOutEn(); // CAN输出开关机指令处理

// 【核心状态机调度入口】
StateM();

CANVrefGet(); // CAN获取参考电压
CANIrefGet(); // CAN获取参考电流
SciCom(); // SCI通信后台处理
LEDShow(); // 状态显示灯函数驱动

CpuTimer1Regs.TCR.bit.TIF = 1; // 清 5ms 中断标志位
return;
}

/** ===================================================================
** Funtion Name :void StateM()(void)
** Description : 状态机函数-初始化状态/等待启动状态/启动状态/运行状态/故障状态
** Parameters :
** Returns :
** ===================================================================*/
void StateM(void)
{
// 根据全局标志位 DF.SMFlag 的值,决定系统去执行哪一个具体的业务逻辑模块
switch(DF.SMFlag)
{
case Init :StateMInit(); break; // 初始化状态
case Wait :StateMWait(); break; // 等待(空闲)状态
case Rise :StateMRise(); break; // 软启动状态
case Run :StateMRun(); break; // 运行状态
case Err :StateMErr(); break; // 故障状态
}
}

初始化状态 (StateMInit & ValInit)

模块初上电/复位后,程序首先强制进入初始化状态(Init),完成程序运行相关基础参数的初始化配置。
这部分配置极其重要,包含控制变量初始化、SCI 通信数据初始化、以及极为核心的电流采样零偏计算等。为了消除硬件运放的直流偏置,必须在没发波时提前采出硬件的偏置基准量。
当输出电流偏置采样完成后,程序完成初始化,状态机才会进入空闲等待状态(Idle/Wait)。

输出电流采样偏置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/** ===================================================================
** Funtion Name :void ValInit(void)
** Description : 相关参数初始化函数 (由 StateMInit 调用)
** Parameters :无
** Returns :无
** ===================================================================*/
void ValInit(void)
{
// 最优先:确保物理层彻底关闭 PWM,防炸管
PWMDis();
// 禁止 Burst 功能,防止软启动过程误触发打嗝
clrRegBits(DF.CtrFlag, F_BURST);
// 清除一切历史故障标志位,白纸上电
DF.ErrFlag=0;
// 控制标志位全部复位
DF.CtrFlag=0;

// 初始化电压参考量
ID2DCtrValue.Voref=0;
ID2DCtrValue.CanVoref =11030; // 预设最低24V电压的数字量作为安全启动基准

// 初始化电流参考量
ID2DCtrValue.Ioref = 0;
ID2DCtrValue.CanIoref =14895; // 预设10A电流的安全限流初值
ID2DCtrValue.Ilimit=0; // 限流惩罚清零
}

/*
** ===================================================================
** Funtion Name : SCIValueInit
** Description : 相关变量初始化 (清理串口通信残存的历史脏数据)
** ===================================================================
*/
void SCIValueInit(void)
{
unsigned char cnt=0;

clrRegBits(SciReg.Flag,NEED_SENT); // 需要回送标志位清零
clrRegBits(SciReg.Flag,TX_FINISH); // 发送结束标志位清零

// 发送寄存器全部清零,防止发出乱码
for(cnt=0;cnt<4;cnt++)
{
TxData[cnt].high=0;
TxData[cnt].low=0;
}

// 级联 PFC 的数据初始化
PFCData.Vac=0;
PFCData.Iac=0;
PFCData.Err=0x0F; // 默认给个错误状态,逼迫其后续更新
PFCData.State=0;
}

空闲 / 等待状态 (StateMWait & CanOutEn)

进入空闲状态下,程序实时判断 LLC 的故障信息,PFC 的故障信息,PFC 的运行状态,PFC_OK 信号以及上位机开机命令。
程序进入状态机后会故意计时等待 5 秒时间,以确保上述硬件建立稳态且故障复位至正常状态。若 LLC 的故障信息与 PFC 的故障信息均为 0(无故障),且 PFC 处于正常运行状态,PFC_OK 信号有效,且上位机下发开机命令,程序状态机跳转至启动状态(Rise)。

空闲状态程序流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/** ===================================================================
** Funtion Name :void StateMWait(void)
** Description : 等待状态机,等待 5S 后无故障则开始进入软启
** ===================================================================*/
void StateMWait(void)
{
// 定义局部静态计数器,保持时间节拍
static unsigned int CntS = 0;

// 每次 5ms,累加到 1000 次即为 5 秒
// 等待 5 秒,让母线电容充满,芯片供电彻底稳定
if(CntS > 1000)
{
CntS = 1000; // 钳位防止溢出

// 严苛的五重连锁启动条件判定:
// 1. DF.ErrFlag == F_NOERR:本地 DCDC (LLC) 无任何过流/过压/过温故障
// 2. PFCData.Err == 0:前级 PFC 级联无故障
// 3. PFCData.State == Run:前级 PFC 已经进入运行态
// 4. getRegBits(DF.CtrFlag, F_PFC_OK):硬件级 PFC OK 信号真实拉高
// 5. getRegBits(DF.CtrFlag, F_OUT_EN):上位机 CAN 协议下发了明确的开机使能指令
if((DF.ErrFlag==F_NOERR) && (PFCData.Err==0) && (PFCData.State==Run) &&
(getRegBits(DF.CtrFlag, F_PFC_OK)) && (getRegBits(DF.CtrFlag, F_OUT_EN)))
{
CntS = 0; // 条件满足,清空计数器为下次开机做准备
// 状态标志位跳转至软启动状态
DF.SMFlag = Rise;
// 并将软启动下的【子状态机】拨到第一步:初始化状态
STState = SSInit;
}
}
else
CntS++;
}

/*
** ===================================================================
** Funtion Name :void CanOutEn(void)
** Description :CAN控制输出开关机 (停机指令拦截)
** ===================================================================
*/
void CanOutEn(void)
{
// 当状态机在 RUNNING(运行) 或 Rise(启动) 状态下,
// 只要检测到上位机撤销了输出使能 (F_OUT_EN == 0),必须立即安全停机
if((!getRegBits(DF.CtrFlag, F_OUT_EN)) && ((DF.SMFlag == Rise) || (DF.SMFlag == Run)))
{
clrRegBits(DF.CtrFlag, F_BURST); // 关Burst,防乱动作
PWMDis(); // 硬件物理关闭PWM
DF.SMFlag = Wait; // 退回到等待状态,等待再次开机指令
}
}

启动状态机 (StateMRise)

当程序进入启动状态机则准备模块启动输出。程序的启动状态机是一个嵌套的子状态机,由三个子步骤精细组成,分别为:启动初始化状态(SSInit),等待发波状态(SSWait),正在软启状态(SSRun)。

  1. SSInit:进入启动初始化状态,分别初始化启动相关状态和参量,包含启动标志位清 0,禁止 Burst 功能,关闭 PWM,设置 LLC 硬件过流保护点,将最大控制周期量限制值限制至最小值(即最高频率),设置最大死区,设置输出参考电压初值等,随后进入等待发波状态。
  2. SSWait:等待发波状态计时等待 100mS 后设置控制周期量为最小值(频率最大值),配置 LLC 从最高频率开始启动,避免过大的启动 PWM 引起谐振电流过大。随后更新 PWM 寄存器,并开启 Burst 控制功能,跳转至正在软起状态。
  3. SSRun:进入软起状态,置位启动标志位,最大控制周期量限制值逐渐增加(代表最低允许频率逐渐降低),死区时间逐渐减小。当最大控制周期量限制值增加至最大值时(最低频率完全放开)和死区减小至最小值时,软起动状态彻底结束,程序跳转至正常运行状态。

启动程序流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/*
** ===================================================================
** Funtion Name : void StateMRise(void)
** Description :软启阶段 (三步走战略)
** ===================================================================
*/
// 软启关键物理参数定义:
#define I_PRI_OCP_DSP 672 // 原边硬件比较器过流保护点 6.5A
#define MAX_SSCNT 20 // 等待 100 毫秒 (20 * 5ms)
#define MAX_PD 857 // 运行后彻底放开的最低频限制(周期值最大对应35kHz)

void StateMRise(void)
{
static unsigned int Cnt = 0; // 子状态机时序发生器

switch(STState) // 判断软启子状态
{
// 【第一阶段:启动初始化状态】
case SSInit:
{
RsingInitFlag=0; // 清软启运行标志
clrRegBits(DF.CtrFlag, F_BURST); // 封锁 Burst
PWMDis(); // 再次确认封锁 PWM

EALLOW;
// 设置原边硬件比较器过流保护阈值 (送给内部 DAC)
Comp1Regs.DACVAL.bit.DACVAL =I_PRI_OCP_DSP;
EDIS;

// 极高频启动:将允许运行的最大周期值限定在极小值,强迫变频器工作在感性区深处
ID2DCtrValue.MaxPD = MIN_BURST+1;
// 大死区启动:防止上下管直通
ID2DCtrValue.DT = MAX_DT;
// 电压缓升:只给定目标值一半的初值作为引导
ID2DCtrValue.Voref = 5515;

STState = SSWait; // 步进到下一阶段
break;
}

// 【第二阶段:等待软启动状态】
case SSWait:
{
Cnt++;
if(Cnt > MAX_SSCNT) // 等待 100ms 硬件偏置稳定
{
Cnt = 0;
ID2DCtrValue.PD = MIN_BURST+1; // 初始化环路输出为最高频率
RegReflash(); // 强制刷入 PWM 底层寄存器

setRegBits(DF.CtrFlag, F_BURST); // 允许轻载打嗝接管
PWMEn(); // 正式打开硬件发波,能量开始注入谐振腔!

STState = SSRun; // 步进到实质性软启
}
break;
}

// 【第三阶段:实质软启动演进状态】
case SSRun:
{
RsingInitFlag = 1; // 宣告正在软启

// 核心:像拉手刹一样,缓慢放宽频率的下限(逐渐允许周期变大,频率降低,能量加大)
ID2DCtrValue.MaxPD = ID2DCtrValue.MaxPD + 5;
// 同时逐步收紧死区时间,提升占空比至额定 50%
ID2DCtrValue.DT = ID2DCtrValue.DT - 1;

// 放宽过程的边界钳位
if(ID2DCtrValue.MaxPD > MAX_PD)
ID2DCtrValue.MaxPD = MAX_PD;
if(ID2DCtrValue.DT < MIN_DT)
ID2DCtrValue.DT = MIN_DT;

// 当运行的下限保护频率放宽到系统的物理极限,且死区收缩完毕,说明软启圆满成功
if( (ID2DCtrValue.MaxPD == MAX_PD) && (ID2DCtrValue.DT == MIN_DT) )
{
DF.SMFlag = Run; // 完美交棒给正常运行状态
STState = SSInit; // 重置子状态机为下一次开机做准备
}
break;
}
default:
break;
}
}

正常运行状态与故障状态 (StateMRun & StateMErr)

正常运行状态

当电源越过惊险的软启阶段后,便进入平稳的正常运行状态。
值得注意的是,该状态机内程序不处理任何实质性内容。因为所有的恒压/限流环路运行、打嗝模式控制和各种突发物理保护,均已分布在最高优先级的 20us 中断和 200Hz 中断内的 SlowP 中后台默默处理。这个空函数是留给未来业务扩展架构预留的锚点。

1
2
3
4
5
6
7
8
9
10
/*
** ===================================================================
** Funtion Name :void StateMRun(void)
** Description :正常运行(空),主函数核心控制进程都在高频中断中自驱动运行
** ===================================================================
*/
void StateMRun(void)
{
// Steady state maintained by high-frequency ISR
}

故障状态机

当模块发生故障或需要保护时(如过压、过流或过温保护等),模块由外层保护函数强行跳转进入保护状态。
在此状态下,系统不仅会保持关闭 PWM 和 Burst 功能,还会停留在当前死循环中静静等待。只有当故障彻底清除(DF.ErrFlag==F_NOERR)后,才能跳转至等待状态(Wait),进行 5 秒安全冷却后重新启动。其程序流程图如下图所示。

故障状态机程序流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*
** ===================================================================
** Funtion Name :void StateMErr(void)
** Description :故障锁定状态,清故障后重回 Wait
** ===================================================================
*/
unsigned char OppFlag=0;

void StateMErr(void)
{
// 在故障态中,死死封住发波权限
clrRegBits(DF.CtrFlag, F_BURST); // 禁止Burst
PWMDis(); // 物理层切断 PWM

// 如果底层的慢速/快速保护函数判断安全,将 ErrFlag 清零了,则触发重入网逻辑
if(DF.ErrFlag == F_NOERR)
{
OppFlag = 0; // 解锁单次封波状态

EALLOW;
// 清除 ePWM 硬件比较器模块内残存的 TZ (OST) 锁死标志位,
// 否则硬件仍会强制拉低引脚电平。
EPwm1Regs.TZCLR.bit.OST = 1;
EDIS;

// 故障解除,跳转至等待状态,进入 5秒 的观察冷却期
DF.SMFlag = Wait;
}
}
参考电压、电流更新计算与状态指示灯

参考电压、电流更新计算与状态指示灯

在 LLC 闭环控制系统中,PID 环路的目标是使得实际输出跟随给定的参考值。为了满足不同应用场景的需求,模块的输出电压控制由参考电压决定,设计的模块参考电压由输出侧滑动电源器(硬件电位器)调节产生或者由上位机设定产生。

本章将详细解析这两种参考值的获取与数字标幺化转换逻辑,并介绍用于人机交互的状态指示灯设计机制。


上位机 CAN 通信命令解析与标幺转换 (SetCmdUpdate)

在数字化程度较高的系统中,通常由上位机通过 CAN 总线下发目标电压和电流。接收到的原始十进制物理数据必须经过严格的合法性校验,并转换为 DSP 内部的 Q 格式数字量。

代码深度解析:
SetCmdUpdate 函数中,程序首先校验输出使能指令的合法性。随后对接收到的参考电压(要求在 24V-48V 之间)和参考电流(要求在 0.1A-10A 之间)进行安全范围钳位。
在进行数字量转换时,代码巧妙利用了移位操作代替除法以提高效率。例如,将上位机发送的数据转换成控制变量时,电压转换使用了乘 4706 再右移 10 位(即除以 1024)的操作,电流转换使用了乘 3813 再右移 8 位(即除以 256)的操作,这正是基于系统硬件采样分压比的逆运算标幺化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/*
** ===================================================================
** Funtion Name : unsigned char SetCmdUpdate(void)
** Description : 对上位机命令数据进行解析处理,校验合法性并转换成控制程序所需的内部变量数据
** ===================================================================
*/
unsigned char SetCmdUpdate(void)
{
unsigned char errType = ERR_NULL;

// 【1. 输出使能指令解析】
// 充电回路主接触器状态:校验数据是否合法 (高7位必须为0)
if((CanDat.OutEn & 0xFE) == 0)
{
if(CanDat.OutEn)
setRegBits(DF.CtrFlag, F_OUT_EN); // 允许发波
else
clrRegBits(DF.CtrFlag, F_OUT_EN); // 禁止发波
}
else // 数据不合法
{
errType = FAIL; // 返回错误状态,抛弃该帧指令
return errType;
}

// 【2. 转存并换算参考电压】
// 判断参考电压合理性,限制在 24V (2400) 到 48V (4800) 之间
if((CanDat.Voref > 4800) || (CanDat.Voref < 2400))
{
errType = FAIL;
return errType; // 数据越界,拒收
}
else
{
// 将上位机发送的物理数据转换成内部 Q15 控制变量
// 4706/1024 约为 4.595,这是包含硬件分压比和基准电压的综合转换系数
ID2DCtrValue.CanVoref = ((unsigned long)CanDat.Voref) * 4706 >> 10;
}

// 【3. 转存并换算参考电流】
// 判断参考电流合理性,限制在 0.1A (10) 到 10A (1000) 之间
if((CanDat.Ioref > 1000) || (CanDat.Ioref < 10))
{
errType = FAIL;
return errType;
}
else
{
// 将上位机发送的数据转换成控制变量
// 3813/256 约为 14.89,同样是底层差分电流采样的综合转换系数
ID2DCtrValue.CanIoref = ((unsigned long)CanDat.Ioref) * 3813 >> 8;
}

return errType; // 返回无错误
}

基于 CAN 的目标值缓变更新 (CANVrefGet / CANIrefGet)

上位机下发的电压电流参考值可能会发生阶跃性突变(例如直接从 24V 设定为 48V)。如果将这个阶跃信号直接喂给 PI 闭环控制器,会引发极其剧烈的积分饱和与过冲,甚至导致谐振腔失控炸机。

因此,代码中引入了步进逼近算法(Slew Rate Control):通过固定的步长(VREF_KIREF_K)将目标值缓慢增加或缓慢减小,使得阶跃指令被平滑为斜坡指令。这极大地保护了功率器件的稳定过渡。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/** ===================================================================
** Funtion Name :void CANVrefGet(void)
** Description : 从上位机获取参考电压,并执行平滑斜坡处理
** ===================================================================*/
// 输出最大参考电压 48.5V (含0.5V余量) 的内部数字量
#define MAX_VREF 22290
// 最低电压参考值 23.5V (含0.5V余量) 的内部数字量
#define MIN_VREF 10800
// 每次执行循环递增或递减的步长
#define VREF_K 100

void CANVrefGet(void)
{
// 如果上位机目标值远大于当前实际参考值,则以 VREF_K 为步长缓慢递增
if( ID2DCtrValue.CanVoref > (ID2DCtrValue.Voref + VREF_K))
ID2DCtrValue.Voref = ID2DCtrValue.Voref + VREF_K;

// 如果上位机目标值远小于当前实际参考值,则缓慢递减
else if( ID2DCtrValue.CanVoref < (ID2DCtrValue.Voref - VREF_K ))
ID2DCtrValue.Voref = ID2DCtrValue.Voref - VREF_K;

// 如果误差已在一步之内,则直接等于目标值,消除稳态残差
else
ID2DCtrValue.Voref = ID2DCtrValue.CanVoref ;

// 电压参考值绝对极值安全限幅
if( ID2DCtrValue.Voref > MAX_VREF )
ID2DCtrValue.Voref = MAX_VREF;
if( ID2DCtrValue.Voref < MIN_VREF )
ID2DCtrValue.Voref = MIN_VREF;
}

/** ===================================================================
** Funtion Name :void CANIrefGet(void)
** Description : 从上位机获取参考电流,并执行平滑斜坡处理
** ===================================================================*/
// 输出最大参考电流10A的内部数字量
#define MAX_IREF 14895
// 最低电流参考值 0A
#define MIN_IREF 0
// 递增或递减步长
#define IREF_K 100

void CANIrefGet(void)
{
// 逻辑同上,缓慢逼近目标电流
if( ID2DCtrValue.CanIoref > ( ID2DCtrValue.Ioref + IREF_K))
ID2DCtrValue.Ioref = ID2DCtrValue.Ioref + IREF_K;
else if( ID2DCtrValue.CanIoref < ( ID2DCtrValue.Ioref - IREF_K ))
ID2DCtrValue.Ioref = ID2DCtrValue.Ioref - IREF_K;
else
ID2DCtrValue.Ioref = ID2DCtrValue.CanIoref ;

// 电流参考值绝对极值安全限幅
if( ID2DCtrValue.Ioref > MAX_IREF )
ID2DCtrValue.Ioref = MAX_IREF;
if( ID2DCtrValue.Ioref < MIN_IREF )
ID2DCtrValue.Ioref = MIN_IREF;
}

基于硬件电位器的参考电压更新 (VrefGet)

在脱机或手动模式下,电源的输出电压由面板上的滑动电位器进行物理旋钮调节。

以滑动电位器调节参考电压为例,读取滑动电位器分压端的电压值,并对其进行 32 次滑动滤波(参考电压需要较稳定值)。
计算数学模型如下:
$V_{ref} = V_{ref_min} + V_{adj} \times K$。

上述 K 值的大小可以调整参考电压调节的灵敏度。为了抑制调节过程中输出电压过快的跳动造成振荡,可设置将参考电压缓慢增加或缓慢减小。并限制输出最大参考电压值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/** ===================================================================
** Funtion Name :void VrefGet(void)
** Description : 从滑动变阻器值获取输出电压参考值,引入低通滤波与步进平滑
** ===================================================================*/
#define MAX_VREF 22290 // 输出最大参考电压设定 (48.5V)
#define MIN_VREF 10800 // 最低电压参考值设定 (23.5V)
#define VREF_K 100 // 逼近步长限制

void VrefGet(void)
{
// 电压参考值中间计算变量
long VTemp = 0;
// 用于 32 次滑动平均滤波的静态累加器
static long VaSum=0;

// 滑动电位器 ADC 采样值求平均,滤除白噪声和旋钮接触不良的毛刺
VaSum = SADC.Vadj + VaSum - (VaSum >> 5);
SADC.VaAvg = VaSum >> 5;

// 参考电压 = 最低偏置 + 滑动变阻器采样值 * K
// 这里的 (2900 >> 12) 也就是乘以约 0.708 作为灵敏度系数 K
VTemp = MIN_VREF + (SADC.VaAvg * 2900 >> 12);

// 为了抑制物理旋钮猛拧导致输出跳变振荡,依然加入缓慢逼近逻辑
if( VTemp > ( ID2DCtrValue.Voref + VREF_K))
ID2DCtrValue.Voref = ID2DCtrValue.Voref + VREF_K;
else if( VTemp < ( ID2DCtrValue.Voref - VREF_K ))
ID2DCtrValue.Voref = ID2DCtrValue.Voref - VREF_K;
else
ID2DCtrValue.Voref = VTemp ;

// 绝对过压极值钳位,防止旋钮短路引发灾难
if( ID2DCtrValue.Voref > MAX_VREF )
ID2DCtrValue.Voref = MAX_VREF;
}

状态指示灯显示逻辑 (LEDShow)

优秀的人机交互界面能够帮助工程师和用户快速定位电源工况。模块设置输出红黄绿三色灯用以指示模块的运行状态信息。

状态显示描述如下:

绿灯黄灯红灯状态描述
机器掉电
初始化状态/等待状态
软启动过程
正常运行状态
故障或保护状态

根据上述表格,LEDShow 函数在 200Hz 的中断总调度中被周期性调用,它根据全局状态机变量 DF.SMFlag 的具体情况,向对应的 GPIO 寄存器写入高低电平,实时刷新面板三色灯的点亮逻辑。

OLED显示屏与I2C通信源代码分析

OLED显示屏与I2C通信源代码分析

在现代数字电源的工程应用与现场调试中,配备一个直观的人机交互界面(HMI)是极其必要的。本系统采用了一块标准的 I2C 接口 OLED 显示屏,用于实时监控并刷新电源的运行工况(状态机)、输出电压、输出电流,并在发生异常时精准报出具体的保护故障类型。

本章将详细剖析 DSP 底层 I2C 外设的硬件初始化配置过程,以及在 5ms 中断中调用的 OLED 屏幕数据格式化与刷新算法。


I2C 硬件通信接口初始化 (I2CA_Init)

OLED 屏幕使用 I2C(内部集成电路总线)协议与 DSP 进行异步串行通信。为了实现高速的数据发送并防止通信阻塞高频控制环路,程序不仅配置了精准的通信波特率,还启用了 DSP 内部的 TX-FIFO(发送硬件缓冲队列)。这样,CPU 只需要把一串显示数据丢进 FIFO 寄存器即可继续执行其他任务,极大地减轻了软件等待的时间开销。

代码中 I2CA_Init 函数完成了引脚复用、从机地址绑定、时钟分频以及 FIFO 的激活。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/*
** ===================================================================
** Funtion Name : void I2CA_Init(void)
** Description :I2C端口初始化
** Parameters : none
** Returns : none
** ===================================================================
*/
// 定义 OLED 显示屏在 I2C 总线上的从机地址 (7位地址格式)
#define OLED_ADD 0x78

void I2CA_Init(void)
{
EALLOW; // 解除系统寄存器写保护

// 【1. GPIO 端口复用与电气属性配置】
// 禁用 GPIO28 (SDAA) 和 GPIO29 (SCLA) 的内部上拉电阻,通常 I2C 总线会在外部电路上外接 4.7k 强上拉电阻
GpioCtrlRegs.GPAPUD.bit.GPIO28 = 0; // Enable pull-up for GPIO28 (SDAA)
GpioCtrlRegs.GPAPUD.bit.GPIO29 = 0; // Enable pull-up for GPIO29 (SCLA)

// 设置为异步输入模式,消除同步采样的毛刺干扰
GpioCtrlRegs.GPAQSEL2.bit.GPIO28 = 3; // Asynch input GPIO28 (SDAA)
GpioCtrlRegs.GPAQSEL2.bit.GPIO29 = 3; // Asynch input GPIO29 (SCLA)

// 配置复用开关,将这两个引脚的控制权交由 I2C 硬件外设模块
GpioCtrlRegs.GPAMUX2.bit.GPIO28 = 2; // Configure GPIO28 for SDAA operation
GpioCtrlRegs.GPAMUX2.bit.GPIO29 = 2; // Configure GPIO29 for SCLA operation

// 【2. I2C 核心协议配置】
// 设置从机地址 (Slave address - control code 7-bit address)
I2caRegs.I2CSAR = OLED_ADD;

// 在配置期间先禁用 I2C 模块 (将 IRS 位清零)
I2caRegs.I2CMDR.bit.IRS = 0;

// 【3. 时钟波特率配置】
// 预分频器配置:要求模块时钟在 7-12 MHz 之间。这里预分频设为 5
I2caRegs.I2CPSC.all = 5;

// 配置 I2C 通信的高低电平持续时间,决定最终的 I2C 通信速率
// NOTE: must be non zero Tmst = Tmod *[( ICCL + d ) + ( ICCH + d )] d = 5;
I2caRegs.I2CCLKL = 60; // 低电平时间分频
I2caRegs.I2CCLKH = 30; // 高电平时间分频

// 禁用 I2C 的常规中断,我们采用 FIFO 机制处理数据
I2caRegs.I2CIER.all = 0;

// 【4. 模块使能与仿真设置】
// 释放复位信号,启动 I2C (Take I2C out of reset)
I2caRegs.I2CMDR.all = 0x0020;
// 仿真断点发生时 I2C 模块的动作:0表示在断点处立刻挂起停止发波
I2caRegs.I2CMDR.bit.FREE = 0;

// 【5. FIFO 缓冲队列使能】
// 启用发送 FIFO 模式 (TXFIFO) - 允许多个数据连续写入寄存器,由硬件自动依次移位发送
I2caRegs.I2CFFTX.all = 0x6000;
// 启用接收 FIFO 模式 (RXFIFO)
I2caRegs.I2CFFRX.all = 0x2040;

EDIS; // 恢复系统寄存器写保护
}

OLED 数据格式化与屏幕刷新逻辑 (OLEDShow)

OLED 显示函数 OLEDShow 通常挂载在 5ms 的慢速定时器中断中,利用一个全局计数器 OledCnt 来控制刷新的帧率。

这个函数主要承担两大任务:

  1. 数字量解码与标幺逆转换:将 DSP 内部的 Q15 格式电压、电流变量,逆向转换为人类可读的实际十进制物理数值,并通过数学方法提取出各位数字(如十位、个位、十分位、百分位)。
  2. 状态机文字映射:监控系统的全局状态机 DF.SMFlag,一旦状态发生变化,立刻在屏幕上刷新对应的英文字符串提示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// 计时器,在 5 毫秒的中断中计时,控制屏幕的刷新频率
int OledCnt=0;

/** ===================================================================
** Funtion Name :void OLEDShow(void)
** Description : OLED显示函数
** 功能划分:
** 1. 显示模块当前状态:Init(初始化), Rising(软启), Running(运行), Err(故障)
** 2. 显示输出电压:保留两位小数
** 3. 显示输出电流:保留两位小数
** ===================================================================*/
void OLEDShow(void)
{
// 定义数组暂存各位数字,Vtemp用于电压,Itemp用于电流 (格式:十、个、0.1、0.01)
unsigned char Vtemp[4]={0,0,0,0};
unsigned char Itemp[4]={0,0,0,0};

// 暂存转换后的真实物理量放大值
unsigned long VoutT=0, IoutT=0;

// 静态变量,用于记录上一次的状态机标志,以此判断是否需要重绘状态栏
static unsigned char SMFlagTemp=10;

// 【1. 数据标幺逆转换】
// 将 Q15 格式的采样平均值转换成实际值,并人为扩大 100 倍以保留两位小数信息。
// 这里的 7130 和 2201 是根据外围电阻分压和运放放大倍数反推的换算系数。
// 物理意义:真实电压 V_real * 100 = (SADC.VAvg * 7130) >> 15
VoutT = SADC.VAvg * 7130 >> 15;
IoutT = SADC.IAvg * 2201 >> 15;

// 安全逻辑:当系统处于关机或死机状态时,强制显示电压电流为 0
if(DF.SMFlag == 0)
{
VoutT=0;
IoutT=0;
}

// 【2. 数据拆位解析算法】
// 为了不在 DSP 中使用极其耗时的 sprintf 或多次求余(%)和浮点除法,
// 这里采用了一种高效的“整数除法与减法”结合的拆位算法。

// 输出电压拆位:
// 获取千位 (实际对应十位数值)
Vtemp[0] = (unsigned char)(VoutT / 1000);
// 减去千位后,获取百位 (实际对应个位数值)
Vtemp[1] = (unsigned char)((VoutT - (unsigned char)Vtemp[0]*1000) / 100);
// 减去千位和百位后,获取十位 (实际对应小数点后第一位)
Vtemp[2] = (unsigned char)((VoutT - (unsigned int)Vtemp[0]*1000 - (unsigned int)Vtemp[1]*100) / 10);
// 减去前三位后,剩下个位 (实际对应小数点后第二位)
Vtemp[3] = (unsigned char)(VoutT - (unsigned int)Vtemp[0]*1000 - (unsigned int)Vtemp[1]*100 - (unsigned int)Vtemp[2]*10);

// 输出电流拆位 (逻辑同电压):
Itemp[0] = (unsigned char)(IoutT / 1000);
Itemp[1] = (unsigned char)((IoutT - (unsigned char)Itemp[0]*1000) / 100);
Itemp[2] = (unsigned char)((IoutT - (unsigned int)Itemp[0]*1000 - (unsigned int)Itemp[1]*100) / 10);
Itemp[3] = (unsigned char)(IoutT - (unsigned int)Itemp[0]*1000 - (unsigned int)Itemp[1]*100 - (unsigned int)Itemp[2]*10);

// 【3. 状态机动态侦测与文本映射】
// 只有当电源工作状态发生变换时,才下发数据更新状态栏屏幕,节约 I2C 带宽
if(SMFlagTemp != DF.SMFlag)
{
SMFlagTemp = DF.SMFlag; // 更新历史状态

// 显示电源工作状态
switch(DF.SMFlag)
{
case Init : OLED_ShowStr(55,2,"Init ",2); break; // 初始化
case Wait : OLED_ShowStr(55,2,"Waiting ",2); break; // 等待状态 (原代码少个空格,已补齐以覆盖旧字符)
case Rise : OLED_ShowStr(55,2,"Rising ",2); break; // 软启动状态
case Run : OLED_ShowStr(55,2,"Running ",2); break; // 正常运行状态
case Err : // 故障状态,需进一步细分具体故障标志位
{
// 根据底层的快速/慢速保护 ErrFlag 进行逐一排查
if(getRegBits(DF.ErrFlag,F_SW_OVP))
OLED_ShowStr(55,2,"Err:OVP ",2); // 软件过压
if(getRegBits(DF.ErrFlag,F_SW_OCP))
OLED_ShowStr(55,2,"Err:OCP ",2); // 软件过流
if(getRegBits(DF.ErrFlag,F_LLC_OCP))
OLED_ShowStr(55,2,"Err:LOCP",2); // LLC原边硬件过流
if(getRegBits(DF.ErrFlag,F_SW_SHORT))
OLED_ShowStr(55,2,"Err:Short",2); // 输出短路
if(getRegBits(DF.ErrFlag,F_SW_UVP))
OLED_ShowStr(55,2,"Err:UVP ",2); // 软件欠压
if(getRegBits(DF.ErrFlag,F_CAN_LOST))
OLED_ShowStr(55,2,"Err:CANL",2); // CAN通信丢失
if(getRegBits(DF.ErrFlag,F_SW_OTP))
OLED_ShowStr(55,2,"Err:OTP ",2); // 散热器过温
break;
}
}
}

// 【4. 推送电压电流数字量到显存】
// 通过调用底层 OLED 驱动函数 OLEDShowData(X坐标, Y坐标, 数字) 将数据打在屏幕固定位置

// 显示电压每一位 (50为起始X轴偏置,4为Y轴行数)
OLEDShowData(50,4,Vtemp[0]); // 十位
OLEDShowData(60,4,Vtemp[1]); // 个位
OLEDShowData(75,4,Vtemp[2]); // 十分位 (注: 60和75之间预留了15个像素宽度用于显示小数点 ".")
OLEDShowData(85,4,Vtemp[3]); // 百分位

// 显示电流每一位 (Y轴变更为第6行)
OLEDShowData(50,6,Itemp[0]);
OLEDShowData(60,6,Itemp[1]);
OLEDShowData(75,6,Itemp[2]);
OLEDShowData(85,6,Itemp[3]);
}

至此,系统通过 I2C 硬件 FIFO 与高效的无浮点数拆位算法,完美构建了 LLC 电源的本地可视化监控平台。

```