TMS320F28377D 基于CPU2的CLA辅助计算综合实现指南

—— 结合逆变电源RMS计算与PID控制例程深度解析

本指南基于实际工程代码(RMSCalculate.clacla_cpu01.ccla_shared.hadc_epwm_cpu02.c),详细拆解如何在 CPU2 上配置CLA(控制律加速器),以实现高实时性的ADC采样处理、RMS(有效值)计算及闭环PID调节。该例程充分利用了CLA与CPU的并行性,将繁重的平方累加和浮点运算卸载给CLA,CPU2则专注于流程控制、PID运算和IPC通信。


第一部分:项目整体架构与数据流概览

1.1 硬件资源分配

  • CLA1:隶属于CPU1子系统,但通过内存配置(LSxMSEL)将LS0/LS1/LS4划归给CLA使用,CPU2可触发并控制CLA1。

  • CPU2:主控核心,运行主循环、状态机、PID算法,并通过IPC与CPU1交互。

  • ADC-A:16通道SOC,由EPWM1/EPWM2触发,采样初级电流(通道0~14)和次级电压(通道15)。

1.2 核心数据流(代码逻辑映射)


第二部分:内存配置与共享变量映射(关键步骤)

2.1 物理内存分配(adc_epwm_cpu02.c 与 cla_cpu01.c

在F28377D中,LS0~LS5是CLA与CPU共享的本地RAM。您的代码通过 CLA_configClaMemory() 明确划分:

c

// 文件:cla_cpu01.c / adc_epwm_cpu02.c
void CLA_configClaMemory(void) {
    EALLOW;
    // 1. 将LS4配置为CLA程序空间(存放CLA任务机器码)
    MemCfgRegs.LSxMSEL.bit.MSEL_LS4 = 1;
    MemCfgRegs.LSxCLAPGM.bit.CLAPGM_LS4 = 1;
    // 2. 将LS0/LS1配置为CLA数据空间(存放共享变量)
    MemCfgRegs.LSxMSEL.bit.MSEL_LS0 = 1;
    MemCfgRegs.LSxCLAPGM.bit.CLAPGM_LS0 = 0; // 0表示数据空间
    MemCfgRegs.LSxMSEL.bit.MSEL_LS1 = 1;
    MemCfgRegs.LSxCLAPGM.bit.CLAPGM_LS1 = 0;
    // 3. 初始化CLA消息RAM(用于额外通信)
    MemCfgRegs.MSGxINIT.bit.INIT_CLA1TOCPU = 1;
    while(MemCfgRegs.MSGxINITDONE.bit.INITDONE_CLA1TOCPU != 1);
    EDIS;
}

2.2 共享变量在数据空间的具体定位(adc_epwm_cpu02.c

#pragma DATA_SECTION 是连接CPU和CLA的关键纽带。以下变量均被放置在 CLADataLS0(即物理LS0 RAM),使得CPU2和CLA均可直接寻址:

变量名数据类型功能描述代码中的位置
ClaPCurrValvolatile Uint32初级电流前8通道的平方累加和adc_epwm_cpu02.c
ClaPCurrVal1volatile Uint32初级电流后8通道的平方累加和(拆分防竞争)adc_epwm_cpu02.c
ClaVoltValvolatile Uint32电压平方累加和adc_epwm_cpu02.c
ClaTask1Result1volatile Uint32合并后的总电流平方和(供CPU中断读取)adc_epwm_cpu02.c
ClaTask1Result2volatile Uint32电压平方和(供CPU中断读取)adc_epwm_cpu02.c
WeldProcessVariable1volatile struct包含CLA Task2/3计算的RMS、峰值等焊接参数adc_epwm_cpu02.c
tempAdc0/tempAdc1volatile uint16_t用于CLA汇编指令中暂存ADC原始值adc_epwm_cpu02.c
sharedVarsvolatile struct用于测量逆变频率等时间参数adc_epwm_cpu02.c

关键设计思想:将 ClaPCurrVal 拆分为 Val 和 Val1 是为了规避32位数据竞争。在CLA中,MADD32 指令对32位内存的读-改-写操作不是原子的。若CPU在CLA写中间插入读取(尽管代码尽量避免,但中断延迟可能导致重叠),可能读脏数据。拆分后,CPU只读合并后的 ClaTask1Result1,而合并操作在CLA内部完成,极大缩小了竞争窗口。

2.3 头文件声明(cla_shared.h

此文件作为CPU和CLA编译时的“契约”,使用 extern 声明所有共享变量。注意 volatile 关键字必不可少,防止编译器优化掉对共享内存的重复访问。

c

// 文件:cla_shared.h
extern volatile Uint32 ClaPCurrVal;
extern volatile Uint32 ClaPCurrVal1;
extern volatile Uint32 ClaTask1Result1;
extern volatile Uint32 ClaTask1Result2;
// ... 以及所有CLA任务和CPU ISR的原型

第三部分:CLA任务的具体实现深度解析(RMSCalculate.cla

您的 .cla 文件包含了Task1~Task8的实现,其中Task1~Task3承担了核心的数值预处理工作。

3.1 Task1:高速ADC结果读取与平方累加(硬实时)

触发源ADCINT2(ADC转换完成)。
执行频率:与PWM周期同步(4kHz或8kHz)。
代码剖析(以第一个通道为例):

c

// RMSCalculate.cla - Task1
__interrupt void Cla1Task1 ( void ) {
    // 1. 清零累加寄存器(防止上次残留)
    asm(" MMOVI32 MR0, #0");   // MR0用于暂存转换结果
    asm(" MMOVI32 MR1, #0");   // MR1用于累加

    // 2. 读取ADC结果寄存器(12位整型)
    tempAdc0 = AdcaResultRegs.ADCRESULT0;
    asm(" MNOP"); // 编译器屏障,确保读取完成

    // 3. 整型转浮点、平方、再转回整型累加
    asm(" MI16TOF32 MR1, @_tempAdc0"); // 12位整型 -> 浮点
    asm(" MMPYF32 MR2, MR1, MR1");     // 浮点平方
    asm(" MF32TOUI32 MR0, MR2");       // 浮点 -> 32位整型
    asm(" MMOV32 MR1,@_ClaPCurrVal");  // 加载当前累加值
    asm(" MADD32 MR2, MR1, MR0");      // 整型加法
    asm(" MMOV32 @_ClaPCurrVal , MR2");// 存回

    // ... 重复通道1~14,其中前8个累加到ClaPCurrVal,后8个累加到ClaPCurrVal1
    // 通道15(电压)累加到ClaVoltVal

    // 4. 合并结果,供CPU中断使用
    ClaTask1Result1 = ClaPCurrVal + ClaPCurrVal1;
    ClaTask1Result2 = ClaVoltVal;
}

为什么用汇编而不用C?
CLA的C编译器支持有限,使用内联汇编可以精准控制寄存器分配(MR0~MR3),并确保单周期完成关键运算,最大程度压缩任务执行时间(需小于125us的PWM周期)。

3.2 Task2与Task3:周期性统计与RMS计算(准实时)

Task2由CPU在特定时刻(如每100个中断)触发,或者通过软件强制触发。它负责持续累加瞬时值的平方(从Task1间接获取),并追踪峰值。

c

// RMSCalculate.cla - Task2
__interrupt void Cla1Task2 ( void ) {
    // 累加平方和(用于后续RMS)
    WeldProcessVariable1.ClaTask2CurrSum += 
        WeldProcessVariable1.ClaTask2CurrCurrentVal * 
        WeldProcessVariable1.ClaTask2CurrCurrentVal;
    // 追踪峰值
    if(WeldProcessVariable1.ClaTask2CurrCurrentVal > 
       WeldProcessVariable1.ClaTask2CurrPeekPv) {
        WeldProcessVariable1.ClaTask2CurrPeekPv = 
            WeldProcessVariable1.ClaTask2CurrCurrentVal;
    }
    // 计数
    WeldProcessVariable1.PirCNT++;
}

Task3在采样点数达到预设值(WeldProcessVariable1.ClaTask2FlagSet)后由CPU强制触发(Cla1ForceTask3()),执行开方运算得到最终RMS。

重要警告:您的Task3中使用了 __sqrt(),这是C28x CPU的库函数,CLA无法直接调用!在CLA中必须使用硬件指令 MEISQRTF32。正确写法应为:

c

float temp = (float)sum / count;
asm(" MEISQRTF32 MR0, MR1"); // MR1为输入,MR0输出
// 或使用CLAmath库的 __meisqrtf32()

否则链接器会报错或生成软件模拟开方,极耗时间。


第四部分:CPU2初始化和中断服务程序(cla_cpu01.c & adc_epwm_cpu02.c

4.1 CLA初始化流程(main函数中调用)

c

// adc_epwm_cpu02.c - main
void main(void) {
    // 1. 基础系统时钟、GPIO、PIE初始化...
    InitSysCtrl(); InitPieCtrl(); InitPieVectTable();

    // 2. 【关键】配置CLA内存和任务向量
    CLA_configClaMemory();   // 见第二部分
    CLA_initCpu2Cla1();      // 设置MVECT1~8,配置触发源

    // 3. 配置ADC(设置SOC0~15的触发源为EPWM1/2)
    ConfigureADC();
    CurrControlMode = SetupADCEpwm(4, 3, 5); // 初级电流通道4,电压通道3

    // 4. 配置EPWM以产生SOC触发
    InitEPwm1Example(); InitEPwm2Example();

    // 5. 使能PIE中断(CLA1_1~8对应PIE组11)
    PieCtrlRegs.PIEIER11.all = 0xFFFF;
    IER |= M_INT11;
    EINT; // 开启全局中断
}

4.2 触发源配置详解(CLA_initCpu2Cla1

c

// cla_cpu01.c
void CLA_initCpu2Cla1(void) {
    EALLOW;
    // 1. 设置任务入口地址
    Cla1Regs.MVECT1 = (uint16_t)(&Cla1Task1);
    // ...
    // 2. 使能任务及IACK指令
    Cla1Regs.MCTL.bit.IACKE = 1;
    Cla1Regs.MIER.all = 0x00FF; // 使能所有8个任务
    // 3. 【核心】将ADCINT2映射为Task1的触发源
    DmaClaSrcSelRegs.CLA1TASKSRCSEL1.bit.TASK1 = 2; // 2代表ADCINT2
    // 4. 注册CPU端的任务结束中断ISR
    PieVectTable.CLA1_1_INT = &cla1Isr1;
    // ...
    EDIS;
}

4.3 CPU中断回调函数(数据消费端)

cla1Isr1(对应Task1结束):
负责将CLA算出的12次采样平方和进行累加,并最终开方得到RMS(用于PID)。

c

// cla_cpu01.c
__interrupt void cla1Isr1 () {
    // 跳过前11个采样点(去除暂态),第12个计算RMS
    if(ClaTask1IntIndex > 0 && ClaTask1IntIndex < 12) {
        ClaSum1 += ClaTask1Result1; // 电流平方累加
    }
    ClaSum2 += ClaTask1Result2;     // 电压平方累加

    if(++ClaTask1IntIndex == 12) {
        // 计算真实RMS(注意180代表点数:12次中断 * 15个有效通道)
        ClaRms1 = (Uint32)(__sqrt((float)ClaSum1 / 180));
        ClaRms2 = (Uint32)(__sqrt((float)ClaSum2 / 12));
        // 清零,准备下一轮
        ClaSum1 = 0; ClaSum2 = 0;
        ClaTask1IntIndex = 0;
        ClaFinish = 1; // 通知主循环数据就绪
    }
    PieCtrlRegs.PIEACK.all = M_INT11;
}

cla1Isr3(对应Task3结束):
处理焊接质量的上下限判定,并最终将结果打包发送给CPU1。


第五部分:CPU主循环如何消费CLA数据

在 adc_epwm_cpu02.c 的 main() 主循环中,CPU不断检测 ClaFinish 标志,一旦置位便读取 ClaRms1 进行PID调节,并更新PWM比较值。

c

// adc_epwm_cpu02.c - main loop (示意)
do {
    // 等待CLA Task1完成一轮数据采集
    if(ClaFinish) {
        ClaFinish = 0;
        // 1. 读取最新RMS值
        float feedback = (float)Pri_rms(); // 调用函数处理ClaRms1
        // 2. PID计算
        PID_Out = PID_Calculate(&myPID, feedback);
        // 3. 更新PWM脉宽
        CMP_Value = (Uint16)PID_Out;
        // 4. 数据上传到CPU1(通过IPC)
        if(条件满足) WRITEtoCPU1();
    }
} while(1);

第六部分:关键实战经验总结(基于您的代码)

6.1 数据竞争解决范例

您将电流平方和拆为两个32位变量,并在CLA中合并后供CPU读取。这是一种“生产者-消费者”握手的有效变种,代价是额外消耗了一个内存单元。更标准的做法是使用 CLA1ToCPUMsgRAM 并配合标志位。

6.2 中断嵌套与优先级

在 adca1_isr(ADC中断)中,您动态切换了 EPWM1/EPWM2 的SOC触发源。需注意ADC中断优先级默认高于CLA任务结束中断(PIE组1 vs 组11),若需实时响应,可调整PIE优先级。

6.3 调试监测点

  • Cla1Regs.MIRUN:指示当前正在执行的CLA任务号,若Task1长时间占用(>125us),说明代码优化不足,需缩减汇编指令或降低采样点数。

  • ClaFinish 与 ClaFinish1:作为简单的信号量,确保CPU不在CLA写数据时读取。您已正确使用,但需注意 volatile 修饰。

6.4 移植注意事项

若将本工程移植到其他项目:

  1. 修改ADC通道:调整 SetupADCEpwm 的 channel 参数。

  2. 调整采样点数:修改 ClaTask1IntIndex 的判断阈值(当前为12),以及RMS计算的分母。

  3. 替换开方函数:若使用CLA Task3,务必替换 __sqrt 为 MEISQRTF32


第七部分:完整代码调用关系索引表

功能模块涉及文件关键函数/变量
CLA内存划分cla_cpu01.cCLA_configClaMemory()
CLA任务向量设置cla_cpu01.cCLA_initCpu2Cla1()
共享变量定义adc_epwm_cpu02.c#pragma DATA_SECTION(... "CLADataLS0")
CLA任务实体RMSCalculate.claCla1Task1~8
CPU端中断响应cla_cpu01.ccla1Isr1~8
ADC+PWM触发adc_epwm_cpu02.cSetupADCEpwm()InitEPwm1Example()
主循环消费数据adc_epwm_cpu02.cmain() 中的 if(ClaFinish) 块
数据上传CPU1adc_epwm_cpu02.cWRITEtoCPU1()

通过以上配置,您的系统实现了 “ADC采样 → CLA预处理 → CPU中断响应 → PID调节 → PWM输出” 的全链路硬件加速。CLA的介入使得CPU2的负载大幅降低,能够从容处理焊接状态机、IPC通信和复杂的人机交互逻辑。如需进一步优化,可以将PID计算也下放到CLA Task2/3中,实现真正的控制环路全硬件化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值