【风电控制】TI TMS320F28379D 双CPU架构解析与任务分布设计
一、C2000 28379D 双核架构总览
1.1 芯片基本参数
| 参数 | CPU1 (CLA1 + CPU1) | CPU2 (CLA2 + CPU2) |
|---|---|---|
| CPU主频 | 200 MHz | 200 MHz |
| CPU核心 | C28x浮点核 | C28x浮点核 |
| FPU | 硬件单精度浮点 | 硬件单精度浮点 |
| TMU | 三角函数加速单元 | 三角函数加速单元 |
| CLA | 协处理器CLA1 | 协处理器CLA2 |
| Flash | 共享1MB(各512KB分区) | |
| RAM | 各有独立的本地RAM |
1.2 双核的硬件关系
┌─────────────────────────────────────────────────────────┐
│ TMS320F28379D │
│ │
│ ┌─────────────┐ IPC总线 ┌─────────────┐ │
│ │ CPU1 │◄══════════════►│ CPU2 │ │
│ │ C28x + FPU │ │ C28x + FPU │ │
│ │ + TMU │ │ + TMU │ │
│ │ 200MHz │ │ 200MHz │ │
│ ├─────────────┤ ├─────────────┤ │
│ │ CLA1 │ │ CLA2 │ │
│ │ 协处理器 │ │ 协处理器 │ │
│ │ (独立执行) │ │ (独立执行) │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ ┌────┴────┐ ┌────┴────┐ │
│ │ 本地RAM │ │ 本地RAM │ │
│ │ LS0~LS5 │ │ LS0~LS5 │ │
│ └────┬────┘ └────┬────┘ │
│ │ │ │
│ └──────────┬─────────────────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ │ 共享资源 │ │
│ │ │ │
│ │ · Flash │ │
│ │ · GS RAM │ │
│ │ · 外设 │ │
│ │ ADC │ │
│ │ PWM │ │
│ │ EQEP │ │
│ │ SPI/CAN │ │
│ │ GPIO │ │
│ └───────────┘ │
└─────────────────────────────────────────────────────────┘
1.3 关键差异:不是"两个相同的核"
虽然CPU1和CPU2的计算能力相同,但它们在外设访问权限、启动顺序、系统管理权限上有本质区别:
| 差异点 | CPU1 | CPU2 |
|---|---|---|
| 系统管理权限 | 主核,负责系统初始化 | 从核,由CPU1唤醒 |
| 启动顺序 | 先启动,执行Boot ROM | 被CPU1通过IPC唤醒 |
| 时钟配置 | CPU1负责配置系统时钟 | 不能配置时钟 |
| 外设时钟使能 | CPU1负责使能所有外设时钟 | 不能使能外设时钟 |
| GPIO配置 | CPU1负责配置GPIO复用 | 不能配置GPIO |
| Flash等待状态 | CPU1配置 | 不能配置 |
| ADC外设 | 可访问ADC-A, ADC-C | 可访问ADC-B, ADC-D |
| PWM外设 | 可访问ePWM1~8 | 可访问ePWM9~12 |
| EQEP(编码器) | 可访问EQEP1~2 | 可访问EQEP3 |
| DMA | DMA通道1~6 | DMA通道7~12 |
核心原则:CPU1是"管家",CPU2是"干活的"。 系统级配置必须由CPU1完成,CPU2只负责执行分配给它的控制任务。
二、外设资源分配------双核分工的硬件基础
2.1 外设分配表
28379D的外设在两个CPU之间的分配是硬件固化的,不能随意更改:
CPU1 专属外设:
├── ADC-A (ADCINA0~15) ← 机侧三相电流/电压采样
├── ADC-C (ADCINC0~15) ← 机侧备用/直流母线电压
├── ePWM1~8 ← 机侧PWM驱动(三相6路)
├── EQEP1~2 ← 编码器接口
├── GPIO (大部分) ← 通信、指示灯、保护信号
├── SPI-A ← 与上位机/FPGA通信
├── CAN-A ← CAN总线
└── I2C ← EEPROM
CPU2 专属外设:
├── ADC-B (ADCINB0~15) ← 网侧三相电流/电压采样
├── ADC-D (ADCIND0~15) ← 网侧备用/电网电压
├── ePWM9~12 ← 网侧PWM驱动(三相6路)
├── EQEP3 ← 备用编码器
├── SPI-B ← 备用通信
└── CAN-B ← 备用CAN
共享资源:
├── GS RAM (全局共享RAM) ← 双核交换数据的桥梁
├── IPC (核间通信) ← Flag + Message RAM
└── Flash (各512KB分区)
2.2 ADC分配的设计考虑
28379D有4个独立的ADC模块(ADC-A/B/C/D),每个ADC模块有自己的采样保持电路和转换器,可以并行采样。
关键设计:将机侧和网侧的模拟信号分配到不同的ADC模块:
机侧信号 (CPU1负责):
├── ADC-A: 定子三相电流 Is_a, Is_b, Is_c
├── ADC-A: 转子三相电流 Ir_a, Ir_b, Ir_c (或用ADC-C)
├── ADC-C: 直流母线电压 Vdc, 中点电压 Vnp
└── ADC-C: 定子三相电压 Vs_a, Vs_b (用于磁链观测)
网侧信号 (CPU2负责):
├── ADC-B: 电网侧三相电流 Ig_a, Ig_b, Ig_c
├── ADC-B: 电网三相电压 Vg_a, Vg_b, Vg_c
├── ADC-D: 直流母线电压 Vdc (与CPU1共享,用于协调)
└── ADC-D: 备用通道
为什么这样分配?
- 采样同步性 :机侧和网侧的电流需要在同一时刻 采样,否则功率计算有误差。将两侧信号分配到不同ADC模块,可以利用ADC的同步采样功能。
- 中断隔离:ADC-A的完成中断给CPU1,ADC-B的完成中断给CPU2,互不干扰。
- 减少IPC通信:如果把机侧信号都给CPU1、网侧信号都给CPU2,两个核可以独立完成各自的控制,不需要通过IPC传递采样数据。
2.3 PWM分配的设计考虑
机侧PWM (CPU1控制):
├── ePWM1A/B → A相上/下桥臂
├── ePWM2A/B → B相上/下桥臂
├── ePWM3A/B → C相上/下桥臂
├── ePWM4 → 机侧Crowbar/Chopper控制
└── ePWM5 → 备用
网侧PWM (CPU2控制):
├── ePWM9A/B → A相上/下桥臂
├── ePWM10A/B → B相上/下桥臂
├── ePWM11A/B → C相上/下桥臂
└── ePWM12 → 网侧Chopper控制
ePWM模块的同步:
机侧和网侧的PWM需要同步(避免直流母线纹波),通过ePWM模块的同步链实现:
ePWM1 (机侧主PWM, CPU1控制)
│
├── 同步信号 → ePWM2 → ePWM3 (机侧从PWM)
│
└── 同步信号 → ePWM9 (网侧主PWM, CPU2控制)
│
└── ePWM10 → ePWM11 (网侧从PWM)
三、任务分布策略
3.1 分布原则
| 原则 | 说明 |
|---|---|
| 物理就近 | 外设分配到哪个CPU,控制任务就分配到哪个CPU |
| 时间隔离 | 机侧和网侧的快任务不抢占同一个CPU的时间 |
| 独立性 | 尽量减少双核之间的数据交换 |
| 安全冗余 | 关键保护逻辑在两个核上都有副本 |
3.2 推荐的任务分布方案
┌──────────────────────────────────┐ ┌──────────────────────────────────┐
│ CPU1 (机侧) │ │ CPU2 (网侧) │
│ │ │ │
│ ┌────────────────────────────┐ │ │ ┌────────────────────────────┐ │
│ │ PWM中断 (250μs, ePWM1触发) │ │ │ │ PWM中断 (250μs, ePWM9触发) │ │
│ │ │ │ │ │ │ │
│ │ · ADC-A/C采样 (Is, Ir, Vdc)│ │ │ │ · ADC-B/D采样 (Ig, Vg, Vdc)│ │
│ │ · Clarke/Park变换 │ │ │ │ · Clarke/Park变换 │ │
│ │ · RSC电流环PI │ │ │ │ · GSC电流环PI │ │
│ │ · 前馈解耦 │ │ │ │ · 前馈解耦 + 电压前馈 │ │
│ │ · SVPWM计算 │ │ │ │ · SVPWM计算 │ │
│ │ · 更新ePWM1~3占空比 │ │ │ │ · 更新ePWM9~11占空比 │ │
│ │ · RSC过流保护 │ │ │ │ · GSC过流保护 │ │
│ │ · 采样数据→共享RAM(给CPU2) │ │ │ │ · 采样数据→共享RAM(给CPU1) │ │
│ └────────────────────────────┘ │ │ └────────────────────────────┘ │
│ │ │ │
│ ┌────────────────────────────┐ │ │ ┌────────────────────────────┐ │
│ │ 定时器中断 (1ms) │ │ │ │ 定时器中断 (1ms) │ │
│ │ │ │ │ │ │ │
│ │ · 转矩外环PI │ │ │ │ · 直流母线电压环PI │ │
│ │ · 弱磁控制 │ │ │ │ · PLL锁相环 │ │
│ │ · 磁链观测器 │ │ │ │ · 无功功率控制 │ │
│ │ · 编码器角度计算 │ │ │ │ · LVRT无功注入逻辑 │ │
│ └────────────────────────────┘ │ │ └────────────────────────────┘ │
│ │ │ │
│ ┌────────────────────────────┐ │ │ ┌────────────────────────────┐ │
│ │ 后台任务 (10ms, 主循环) │ │ │ │ 后台任务 (10ms, 主循环) │ │
│ │ │ │ │ │ │ │
│ │ · 同步/并网状态机 │ │ │ │ · 故障录波 │ │
│ │ · LVRT检测与切换 │ │ │ │ · MODBUS通信 │ │
│ │ · MPPT计算 │ │ │ │ · CAN通信 │ │
│ │ · 系统状态管理 │ │ │ │ · 参数读写 │ │
│ │ · 故障管理 │ │ │ │ · 功率协调(与CPU1交换数据) │ │
│ │ · 编码器自检 │ │ │ │ · DC-Chopper控制 │ │
│ └────────────────────────────┘ │ │ └────────────────────────────┘ │
│ │ │ │
│ ┌────────────────────────────┐ │ │ ┌────────────────────────────┐ │
│ │ CLA1 (协处理器) │ │ │ │ CLA2 (协处理器) │ │
│ │ │ │ │ │ │ │
│ │ · sin/cos查表加速 │ │ │ │ · sin/cos查表加速 │ │
│ │ · Park/Clarke变换 │ │ │ │ · Park/Clarke变换 │ │
│ │ · SVPWM计算 │ │ │ │ · SVPWM计算 │ │
│ └────────────────────────────┘ │ │ └────────────────────────────┘ │
└──────────────────────────────────┘ └──────────────────────────────────┘
3.3 为什么这样分配?
理由1:外设物理就近(最重要)
ADC-A采样的机侧电流,转换完成后立即触发CPU1的中断。CPU1就地执行Park变换、PI控制、SVPWM,然后更新ePWM1~3。整个链路不需要跨核通信,延迟最小。
如果把机侧控制放到CPU2,就需要:
- CPU1读取ADC-A
- CPU1通过IPC将数据传给CPU2
- CPU2执行控制算法
- CPU2通过IPC将PWM占空比传回CPU1
- CPU1更新ePWM
每一步IPC通信都会增加延迟,且增加了出错的风险。
理由2:CPU时间预算
让我们计算CPU1在一个PWM周期(250μs)内需要完成的计算量:
CPU1快任务 (250μs内完成):
├── ADC读取: 6个通道 × ~1μs = 6μs
├── Clarke变换: 3次乘法+加法 ≈ 0.5μs
├── Park变换: 4次乘法+2次加法 ≈ 1μs (sin/cos用查表)
├── PI控制器 (2个): 2 × (2次乘法+2次加法+限幅) ≈ 2μs
├── 前馈解耦: 4次乘法+2次加法 ≈ 1μs
├── 反Park变换: 4次乘法+2次加法 ≈ 1μs
├── SVPWM: 扇区判断+时间计算 ≈ 3μs
├── PWM更新: 3次寄存器写入 ≈ 0.5μs
├── 保护判断: 比较+逻辑 ≈ 1μs
├── 数据写入共享RAM ≈ 1μs
└── 中断响应/恢复开销 ≈ 5μs
────────────────────────────
总计: ≈ 22μs
CPU1利用率: 22/250 = 8.8%
如果把网侧控制也放到CPU1:
CPU1快任务 (250μs内, 双侧):
├── 机侧控制: 22μs
├── 网侧控制: 22μs
├── IPC通信开销: ~5μs
├── 上下文切换: ~5μs
────────────────────────────
总计: ≈ 54μs
CPU1利用率: 54/250 = 21.6%
看起来21.6%也不高?但这是理想情况。 在实际中:
- 中断嵌套和优先级管理会增加开销
- Flash等待状态会增加代码执行时间
- 如果机侧和网侧的PWM中断同时触发,高优先级会抢占低优先级,导致延迟抖动
双核分离后,两侧的快任务完全并行执行,互不影响。
理由3:故障隔离
如果机侧控制出现异常(如PI积分风暴、死循环),不会影响网侧控制。网侧可以继续维持直流母线电压和并网运行,给系统留出安全降额的时间。
四、核间通信(IPC)设计
4.1 IPC机制
28379D提供以下IPC通信方式:
| 方式 | 延迟 | 带宽 | 适用场景 |
|---|---|---|---|
| IPC Flag | ~100ns | 1 bit × 16通道 | 状态标志、触发信号 |
| IPC Message RAM | ~500ns | 4×32bit × 2通道 | 少量数据交换 |
| 共享RAM (GS) | ~100ns | 任意大小 | 大量数据交换 |
4.2 共享RAM的数据结构设计
c
// 共享RAM区域 (放在GS0 RAM中)
#pragma DATA_SECTION(shared_data, "SHAREDMEM")
volatile SharedData_t shared_data;
typedef struct {
// ---- CPU1 → CPU2 (机侧给网侧) ----
float Vdc_rsc; // 机侧测量的直流母线电压
float P_rsc; // 机侧有功功率
float Q_rsc; // 机侧无功功率
float id_rsc; // 机侧d轴电流 (实际值)
float iq_rsc; // 机侧q轴电流 (实际值)
float omega_r; // 转子电角速度
int system_state; // 系统状态 (同步/运行/LVRT/故障)
int fault_code; // 故障码
int is_lvrt; // LVRT标志
// ---- CPU2 → CPU1 (网侧给机侧) ----
float Vdc_gsc; // 网侧测量的直流母线电压
float P_gsc; // 网侧有功功率
float Q_gsc; // 网侧无功功率
float id_gsc; // 网侧d轴电流
float iq_gsc; // 网侧q轴电流
float theta_pll; // PLL角度
float omega_pll; // PLL角速度
float Vg_pu; // 电网电压标幺值
int grid_connected; // 并网状态
// ---- 保护协调 (双向) ----
int emergency_stop; // 紧急停机标志 (任一核可设置)
int chopper_enable; // DC-Chopper使能
float Vdc_max_recorded; // 记录到的最大Vdc
} SharedData_t;
4.3 IPC同步策略
原则:尽量减少同步等待,用"最新值"策略。
c
// CPU1: 写入共享数据 (快任务末尾)
void CPU1_WriteSharedData(void) {
// 使用"先写后标记"策略
shared_data.Vdc_rsc = Vdc_measured;
shared_data.P_rsc = P_stator;
shared_data.Q_rsc = Q_stator;
shared_data.omega_r = omega_elec;
shared_data.system_state = current_state;
shared_data.is_lvrt = is_lvrt_flag;
// 原子标记: 数据已更新
// 使用Flag通知CPU2
IPC_setFlag(IPC_CPU1_TO_CPU2, FLAG_DATA_READY);
}
// CPU2: 读取共享数据 (快任务开始)
void CPU2_ReadSharedData(void) {
// 直接读取, 不等待Flag (最新值策略)
// 如果上一拍的数据还没更新, 用上一拍的也没关系
Vdc_from_rsc = shared_data.Vdc_rsc;
P_rsc_actual = shared_data.P_rsc;
omega_elec = shared_data.omega_r;
system_state = shared_data.system_state;
is_lvrt = shared_data.is_lvrt;
// 清除Flag
IPC_clearFlag(IPC_CPU1_TO_CPU2, FLAG_DATA_READY);
}
关键设计 :不要用IPC Flag做阻塞等待(如 while(!flag)),这会导致CPU空转,浪费时间。用"最新值"策略------直接读共享RAM中的数据,如果数据是上一拍的,对控制性能的影响微乎其微。
4.4 共享RAM的Cache一致性问题
28379D的CPU有L1 Cache(指令Cache和数据Cache)。如果共享RAM的数据被Cache缓存,CPU2修改了共享RAM,CPU1的Cache中还是旧值。
解决方案:
c
// 方法1: 将共享RAM区域配置为Non-Cacheable
// 在CMD文件中:
// SHAREDMEM : > GS0, PAGE 1, type = NOCACHE
// 方法2: 在读写共享数据前后手动刷新Cache
void CPU2_WriteSharedData_Safe(void) {
// 写入
shared_data.P_gsc = P_grid;
// 写回Cache到RAM (确保CPU1能看到)
// 对于C2000, 通常不需要手动flush (Write-Through策略)
// 但建议使用volatile关键字确保编译器不优化掉读写操作
}
工程建议 :在CMD链接文件中将共享RAM区域标记为 NOCACHE,最简单可靠。
五、启动与初始化流程
5.1 双核启动顺序
┌─────────────────────────────────────────────────────────────────┐
│ 上电复位 │
│ │ │
│ ▼ │
│ CPU1 Boot ROM (自动执行) │
│ │ │
│ ▼ │
│ CPU1 main() │
│ │ │
│ ├── Step 1: 系统时钟初始化 (200MHz) │
│ ├── Step 2: 使能所有外设时钟 │
│ ├── Step 3: GPIO复用配置 │
│ ├── Step 4: 配置ADC-A, ADC-C (机侧) │
│ ├── Step 5: 配置ePWM1~8 (机侧) │
│ ├── Step 6: 配置EQEP1 (编码器) │
│ ├── Step 7: 配置共享RAM │
│ ├── Step 8: 配置IPC │
│ │ │
│ ├── Step 9: 启动CPU2! ─────────────────────────┐ │
│ │ (通过写IPC Boot寄存器) │ │
│ │ │ │
│ ├── Step 10: 配置CPU1的中断 │ │
│ ├── Step 11: 使能PWM中断 │ │
│ │ ▼ │
│ ├── Step 12: 进入主循环 CPU2 Boot ROM │
│ │ │ │
│ │ ▼ │
│ │ CPU2 main() │
│ │ │ │
│ │ ├── 配置ADC-B, ADC-D │
│ │ ├── 配置ePWM9~12 │
│ │ ├── 配置中断 │
│ │ ├── 使能PWM中断 │
│ │ └── 进入主循环 │
│ │ │
│ ▼ │
│ 双核并行运行 │
└─────────────────────────────────────────────────────────────────┘
5.2 CPU1启动CPU2的代码
c
// CPU1代码中, 启动CPU2
void CPU1_BootCPU2(void) {
// Step 1: 将CPU2的程序加载到Flash/RAM
// (通常由链接器自动完成, CPU2代码放在CPU2的Flash分区)
// Step 2: 配置CPU2的Boot地址
// 写入IPC Boot寄存器
IPC_setBootMode(IPC_CPU2, BOOT_FROM_FLASH); // 或 BOOT_FROM_RAM
// Step 3: 释放CPU2
IPC_releaseCPU2();
// Step 4: 等待CPU2就绪 (可选)
while (!IPC_getFlag(IPC_CPU2_TO_CPU1, FLAG_CPU2_READY)) {
// 等待, 但加超时保护
if (timeout_counter++ > MAX_TIMEOUT) {
FAULT_Set(FAULT_CPU2_BOOT_FAIL);
break;
}
}
}
5.3 双核同步的关键时序
问题:CPU1和CPU2的PWM中断必须同步,否则机侧和网侧的控制不同步,直流母线功率会出现不平衡。
解决方案:用ePWM的同步链实现硬件级同步:
c
// CPU1配置 (ePWM1作为主同步源)
void CPU1_PWM_SyncConfig(void) {
// ePWM1: 主PWM, 产生同步信号
EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE; // 不接收外部同步
EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO; // 在计数器=0时输出同步
// ePWM2, ePWM3: 从PWM, 接收ePWM1的同步
EPwm2Regs.TBCTL.bit.PHSEN = TB_ENABLE;
EPwm2Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;
EPwm3Regs.TBCTL.bit.PHSEN = TB_ENABLE;
EPwm3Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;
// 同步信号链: ePWM1 → ePWM2 → ePWM3 → ... → ePWM9 (CPU2)
}
// CPU2配置 (ePWM9接收来自ePWM1的同步)
void CPU2_PWM_SyncConfig(void) {
// ePWM9: 接收CPU1 ePWM1的同步信号
EPwm9Regs.TBCTL.bit.PHSEN = TB_ENABLE;
EPwm9Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;
// ePWM10, ePWM11: 接收ePWM9的同步
EPwm10Regs.TBCTL.bit.PHSEN = TB_ENABLE;
EPwm11Regs.TBCTL.bit.PHSEN = TB_ENABLE;
}
这样,所有PWM模块(ePWM1~11)都以ePWM1的计数器为基准同步运行,采样时刻和PWM更新时刻完全对齐。
六、CLA协处理器的使用
6.1 CLA是什么?
CLA(Control Law Accelerator)是C2000 DSP中的独立协处理器,专门用于执行控制算法。它有以下特点:
| 特性 | CLA | CPU (C28x) |
|---|---|---|
| 架构 | 独立哈佛架构 | 冯·诺依曼架构 |
| 浮点支持 | 硬件单精度浮点 | 硬件单精度浮点 |
| 指令集 | C28x子集(无中断、无I/O) | 完整C28x |
| 执行方式 | 独立并行执行 | 主CPU执行 |
| 与CPU的关系 | 被CPU触发,执行完通知CPU | 触发CLA,不等待 |
6.2 CLA的价值
CLA可以在CPU执行中断服务的同时,独立执行控制算法。 这相当于免费获得了额外的计算能力。
不使用CLA:
时间 ──────────────────────────────────►
CPU1: [ADC中断]──[控制算法计算]──[更新PWM]──[主循环]──[ADC中断]...
│←──────── 22μs ────────→│
使用CLA:
时间 ──────────────────────────────────►
CPU1: [ADC中断]──[触发CLA]──[主循环]──[CLA完成中断]──[更新PWM]...
│← 2μs →│ │← 3μs →│
CLA: [控制算法计算]──[完成]
│←── 20μs ──→│
CPU1的中断服务时间从22μs缩短到5μs,释放了大量CPU时间。
6.3 CLA的任务分配建议
CLA1 (配合CPU1, 机侧):
├── Park/Clarke变换
├── PI控制器计算
├── 前馈解耦计算
├── SVPWM占空比计算
└── sin/cos查表 (通过TMU)
CLA2 (配合CPU2, 网侧):
├── Park/Clarke变换
├── PI控制器计算
├── 前馈解耦计算
├── SVPWM占空比计算
└── PLL计算
注意:CLA不能访问外设寄存器(如ADC结果寄存器、PWM占空比寄存器)。CPU需要先把数据从外设读到RAM中,CLA从RAM读取计算,计算结果放回RAM,CPU再写入外设。
c
// CPU1的ADC中断服务 (触发CLA)
__interrupt void ADC_ISR(void) {
// Step 1: 从ADC读取原始数据到CLA的数据RAM
Cla1Regs.MVECT = TASK_CONTROL_ALGORITHM; // 指定CLA任务
// 将ADC结果拷贝到CLA可访问的RAM
Cla1Data.Ia_raw = AdcaResultRegs.ADCRESULT0;
Cla1Data.Ib_raw = AdcaResultRegs.ADCRESULT1;
Cla1Data.Vdc_raw = AdccResultRegs.ADCRESULT0;
Cla1Data.theta = encoder_angle;
// Step 2: 触发CLA执行
__cla1_force_task(TASK_CONTROL_ALGORITHM);
// Step 3: CPU1不等待CLA, 继续执行其他任务
// (如保护判断、通信处理)
// Step 4: CLA完成后, 在CLA完成中断中读取结果并更新PWM
}
// CLA完成中断
__interrupt void CLA1_Done_ISR(void) {
// 从CLA的数据RAM读取结果
float Ta = Cla1Data.Ta_result;
float Tb = Cla1Data.Tb_result;
float Tc = Cla1Data.Tc_result;
// 更新PWM占空比
EPwm1Regs.CMPA.bit.CMPA = (uint16_t)(Ta * PWM_PERIOD);
EPwm2Regs.CMPA.bit.CMPA = (uint16_t)(Tb * PWM_PERIOD);
EPwm3Regs.CMPA.bit.CMPA = (uint16_t)(Tc * PWM_PERIOD);
}
七、中断优先级设计
7.1 CPU1的中断优先级
优先级 中断源 用途 最大响应延迟
─────────────────────────────────────────────────────────────────
最高 ADC-A完成中断 机侧电流采样+控制 < 1μs
↓
高 ePWM1过流比较中断 硬件过流保护 < 200ns (硬件)
↓
中 定时器0中断 (1ms) 转矩环/弱磁/磁链观测 < 5μs
↓
低 ePWM1周期中断 同步/状态更新 < 10μs
↓
最低 IPC中断 核间通信 < 20μs
↓
最低 SCI/SPI中断 通信 不要求实时
7.2 关键设计:ADC中断必须最高优先级
c
// ADC中断配置 (PIE Group 1, 最高优先级)
void Config_ADC_ISR(void) {
// ADC-A完成 → PIE1.1 (ADCA1)
PieCtrlRegs.PIEIER1.bit.INTx1 = 1; // 使能ADCA1中断
IER |= M_INT1; // 使能Group 1
// 优先级: 通过PIE内的轮转顺序, INTx1自然具有最高优先级
}
为什么ADC中断必须最高?
ADC中断是整个控制链的"起点"。如果ADC中断被延迟,后续所有计算都会延迟,导致:
- 采样时刻偏移(不在PWM中心)
- PI计算延迟增大(等效增大 1.5Ts1.5T_s1.5Ts 中的 TsT_sTs)
- 电流控制性能恶化
八、工程实践中的常见问题
问题1:双核同时写同一个寄存器
现象:CPU1和CPU2同时尝试写ADC的某个配置寄存器,导致配置混乱。
对策:严格遵守"谁的外设谁配置"原则。在CPU1的初始化代码中,一次性配置完所有共享外设,然后CPU2不再修改。
问题2:共享RAM数据不一致
现象:CPU1正在写共享数据结构(多字节),CPU2读到了"一半新一半旧"的值。
对策 :使用双缓冲 或原子标志:
c
// 双缓冲方案
typedef struct {
SharedData_t buffer[2];
volatile int write_index; // 0 或 1
} DoubleBuffer_t;
// CPU1写入
void CPU1_Write(DoubleBuffer_t *db) {
int idx = db->write_index;
db->buffer[idx].P_rsc = P_measured;
db->buffer[idx].Vdc = Vdc_measured;
// ... 写完所有数据后, 切换索引
db->write_index = 1 - idx; // 原子切换
}
// CPU2读取
void CPU2_Read(DoubleBuffer_t *db) {
int idx = 1 - db->write_index; // 读另一个buffer
P_from_rsc = db->buffer[idx].P_rsc;
Vdc_from_rsc = db->buffer[idx].Vdc;
}
问题3:CPU2启动失败
现象:上电后CPU2没有正常运行,网侧变流器不工作。
对策:
- CPU1启动CPU2后加超时检测
- CPU2启动后通过IPC Flag报告"就绪"
- 如果CPU2启动失败,CPU1进入安全模式(只运行机侧,限制功率)
问题4:Flash访问竞争
现象:CPU1和CPU2同时从Flash取指令,Flash只有一个读端口,需要仲裁,导致其中一个CPU等待。
对策:
- 将热点代码(快任务)拷贝到RAM中执行
- 使用
#pragma CODE_SECTION将关键函数分配到RAM
c
#pragma CODE_SECTION(RSC_CurrentLoop, "ramfuncs")
void RSC_CurrentLoop(void) {
// 这个函数将在RAM中执行, 不受Flash等待状态影响
// ...
}
九、任务分布的替代方案对比
| 方案 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| A: 机侧/网侧分离(推荐) | CPU1=RSC, CPU2=GSC | 外设就近、独立性强、故障隔离 | 需要IPC交换数据 |
| B: 快任务/慢任务分离 | CPU1=快任务(两侧), CPU2=慢任务 | 不需要IPC交换实时数据 | CPU1负担重、两侧不独立 |
| C: 单核运行 | 只用CPU1, CPU2空闲 | 最简单 | 浪费50%计算能力 |
| D: 三环分离 | CPU1=电流环, CPU2=转矩环+电压环 | 与控制层级对应 | 电流环延迟增大 |
工程界主流选择是方案A(机侧/网侧分离),原因是外设分配决定了任务分配,强行将两侧的控制放在同一个核上只会增加不必要的复杂度和延迟。
十、总结
| 问题 | 答案 |
|---|---|
| CPU1和CPU2的计算能力有区别吗? | 没有,完全相同的C28x核,都是200MHz + FPU + TMU |
| 那区别在哪? | 外设访问权限和系统管理权限不同,CPU1是"管家" |
| 任务怎么分? | 按外设分:机侧外设→CPU1,网侧外设→CPU2 |
| 为什么这样分? | 物理就近(减少IPC延迟)、时间隔离(并行执行)、故障隔离 |
| 两个核怎么交换数据? | 共享RAM + IPC Flag,用"最新值"策略,不用阻塞等待 |
| CLA怎么用? | 承担数学密集型计算(三角函数、PI、SVPWM),与CPU并行执行 |
| 启动顺序? | CPU1先启动→配置系统→启动CPU2→双核并行运行 |
| PWM怎么同步? | ePWM硬件同步链,ePWM1为全局主时基 |
一句话:28379D的双核设计不是让你"把一个程序拆成两半",而是让你"把两个独立的控制系统放在一颗芯片上"。机侧变流器和网侧变流器本身就是两个独立的物理系统,天然适合分配到两个核上。