DSP28379D_双核IPC内部通信

IPC对于双核的重要性

DSP28379D作为一款双核的芯片,CPU1和CPU2共享外设单元。他们有各自的内存、中断、总线等。CPU1有的,CPU2也攀比着有,仅存在一点点区别(启动引导和看门狗)。总体上CPU1和CPU2大致相当,CPU1稍主。
既然是双核,那么为了两个核协同工作,不可避免需要交流,IPC起到内部通信的功能,大大方便了开发人员对于双核任务的分配,适时沟通。两个DSP核,都可以做电机驱动控制,不考虑性价比的情况下,一个核用来与上位机通信,类似于ARM+DSP。

IPC模块通信流程

思路:假设CPU1向CPU2发送数据。CPU1申请全局共享内存的写权限,将数据写入全局共享内存中,内存地址赋给IPC控制结构体,产生IPC1中断,复位IPC11标志。通知CPU2,CPU2同样使用IPC控制结构体,将共享内存地址赋给CPU2接收数组指针。CPU2查询到IPC11标志复位后,将接收数组的内容,赋给需要的变量,并置位IPC11标志。一次发送完成。需要注意的是:CPU2向CPU1发送数据时,需要通过IPC中断,申请全局共享内存的写权限,此步需要在CPU1内完成。除此之外,两个核的IPC控制结构体中的一系列地址索引,需要在IPC RAM中分配不同的区域,否则会造成读/写冲突,卡死在中断中。
理解数据手册、技术参考手册、官方例程。阐明IPC模块的通信流程:
① 初始化IPC。定义CPU01TOCPU02_PASSMSG、CPU02TOCPU01_PASSMSG、GS0SARAM_START地址,分别设置IPC控制结构体区域。定义接收和发送数据临时数组或者指针变量,并清除接收数组内的随机值。
② 开启IPC中断。对于C28x系列的内核,IPC共有32个中断信号,但是只有其中的IPC0~IPC3可用于配置IPC中断,处理双核之间的通信控制,剩余的标志信号可用于查询,像串口通信一样,只要CPU1与CPU2一一对应即可。
③ 使用IPC17标志位确认CPU2是否准备好。由于双核并不存在同步运行的关系,两个核独立运行,当CPU1已经初始化完毕,需要等待CPU2也初始化完毕。CPU2置位IPC17 Flag,CPU1查询到则开始运行收发函数。
④ 执行收发函数。发送函数需要申请Global Shared RAM写权限,并且写完后使用标志位的置位,通知接收方,已写完。接收方从Global Shared RAM读出数据,复位标志位,发送方可以再次发送。
⑤中断处理。中断中无论何种控制命令,都在处理地址传送,得益于IPC Message RAM,将实际数据通过地址链接在一起,提升传输容量。

IPC收发举例

CPUx需要一些来自CPUy的LS RAM的数据。数据位于CPUy地址0x9400,长度为0x80个16位字。协议实现如下:
①CPUx将0x1写入IPCSENDCOM,在软件中定义为“从地址复制数据”。它写到地址0x9400到IPCSENDADDR,数据长度0x80到IPCSENDDATA。
②CPUx写入IPCSET[3]和IPCSET[16]。这里,IPC标志3被配置为发送一个中断和IPCSET[16]在软件中定义,用于指示传入命令。CPUx开始轮询IPCFLG[3]降低。
③CPUy接收中断。在中断处理程序中,它检查IPCSTS,发现标志16已经设置,并且运行命令处理程序。
④CPUy从IPCRECVCOM读取命令0x1,从IPCRECVADDR读取地址0x9400,IPCRECVDATA的数据长度0x80。然后,CPUy将LS RAM数据复制到空内存中从偏移量0x210开始的可写共享内存中的空间。
⑤CPUy将共享内存地址0x210写入其IPCLOCALREPLY寄存器。然后它写到IPCACK[16]和IPCACK[3]清除标志,表示命令完成。CPUy工作就完成。
⑥CPUx看到IPCFLG[3]降低。它读取IPCREMOTEREPLY来获取复制数(0x210)。

IPC收发具体实现

收发之前需要先定义好一些变量、地址等,并且对IPC进行初始化,配对好中断、地址、清除接收区域等。
CPU1中:

#define CPU01TOCPU02_PASSMSG  0x0003FFCE   // CPU01 to CPU02 MSG RAM offsets for passing address
#define CPU02TOCPU01_PASSMSG  0x0003FBCE
#define GS0SARAM_START        0xC950       // Start of GS0 SARAM 0x00D000
volatile tIpcController g_sIpcController1;       // IPC0控制
volatile tIpcController g_sIpcController2;       // IPC1控制
float usCPU01Buffer[8];                          // 接收缓存
float usCPU01Buffer_B[8];                        // 接收缓存
float *pusCPU01BufferPt;
float *pusCPU01BufferPt_B;
Uint32 *pulMsgRam;
Uint32 *pulMsgRam2;
float test;
float test1;
void InitIPC(void)
{
    IPCInitialize(&g_sIpcController1, IPC_INT0, IPC_INT0);
    IPCInitialize(&g_sIpcController2, IPC_INT1, IPC_INT1);
    Uint16 counter;
    pulMsgRam = (void *)CPU01TOCPU02_PASSMSG;
    pulMsgRam2 = (void *)CPU02TOCPU01_PASSMSG;
    pusCPU01BufferPt = (void *)GS0SARAM_START;
    pusCPU01BufferPt_B = (void *)(GS0SARAM_START + 16); 
    pulMsgRam[0] = (Uint32)&usCPU01Buffer[0];
    pulMsgRam[2] = (Uint32)&usCPU01Buffer_B[0];
    for(counter = 0; counter <8; counter++)
    {
        usCPU01Buffer[counter] = 0.0;
        usCPU01Buffer_B[counter] = 0.0;
    }
    usCPU01Buffer[0]=3.14;
}

CPU2中:

#define CPU01TOCPU02_PASSMSG  0x0003FFCE   // CPU01 to CPU02 MSG RAM offsets for passing address
#define CPU02TOCPU01_PASSMSG  0x0003FBCE
#define GS0SARAM_START        0xC950       // Start of GS0 SARAM
volatile tIpcController g_sIpcController1;       // IPC0控制
volatile tIpcController g_sIpcController2;       // IPC1控制
float usCPU02Buffer[8];                          // 接收缓存
float usCPU02Buffer_B[8];                        // 接收缓存
float *pusCPU02BufferPt;
float *pusCPU02BufferPt_B;
Uint32 *pulMsgRam;
Uint32 *pulMsgRam2;
float test3;
float test2;
void InitIPC(void)
{
    IPCInitialize(&g_sIpcController1, IPC_INT0, IPC_INT0);
    IPCInitialize(&g_sIpcController2, IPC_INT1, IPC_INT1);
    Uint16 counter;
    pulMsgRam = (void *)CPU01TOCPU02_PASSMSG;
    pulMsgRam2 = (void *)CPU02TOCPU01_PASSMSG;
    pusCPU02BufferPt = (void *)(GS0SARAM_START + 8);
    pusCPU02BufferPt_B = (void *)(GS0SARAM_START + 24);
    pulMsgRam2[1] = (Uint32)&usCPU02Buffer[0];
    pulMsgRam2[3] = (Uint32)&usCPU02Buffer_B[0];
    for(counter = 0; counter <8; counter++)
    {
        usCPU02Buffer[counter] = 0.0;
        usCPU02Buffer_B[counter] = 0.0;
    }
}

值得注意的是,CMD文件中,检查一下以下内存的分配。这关系到IPC调用API函数IPCInitialize,进行初始化时对于控制结构体的配置。
CPU1中:

   CPU2TOCPU1RAM   : origin = 0x03F800, length = 0x000400
   CPU1TOCPU2RAM   : origin = 0x03FC00, length = 0x000400
   /* The following section definitions are required when using the IPC API Drivers */
    GROUP : > CPU1TOCPU2RAM, PAGE = 1
    {
        PUTBUFFER
        PUTWRITEIDX
        GETREADIDX
    }
        GROUP : > CPU2TOCPU1RAM, PAGE = 1
    {
        GETBUFFER :    TYPE = DSECT	/* 暂不使用 */
        GETWRITEIDX :  TYPE = DSECT
        PUTREADIDX :   TYPE = DSECT
    }

CPU2中:

   CPU2TOCPU1RAM   : origin = 0x03F800, length = 0x000400
   CPU1TOCPU2RAM   : origin = 0x03FC00, length = 0x000400
      /* The following section definitions are required when using the IPC API Drivers */
    GROUP : > CPU2TOCPU1RAM, PAGE = 1
    {
        PUTBUFFER
        PUTWRITEIDX
        GETREADIDX
    }
        GROUP : > CPU1TOCPU2RAM, PAGE = 1
    {
        GETBUFFER :    TYPE = DSECT	/* 暂不使用 */
        GETWRITEIDX :  TYPE = DSECT
        PUTREADIDX :   TYPE = DSECT
    }

IPC中断函数我也提前亮相,与收发密切相关,主要是地址的赋来赋去。
感兴趣可以深究,比较好玩的,提前准备好笔和纸梳理。两个核的中断函数有一定的区别,原因是申请全局共享内存写的权限。
CPU1中:

// CPU02toCPU01IPC0IntHandler - Handles Data Word Reads/Writes
__interrupt void IPC0_ISR(void)
{
    tIpcMessage sMessage;
    // Continue processing messages as long as CPU02toCPU01 GetBuffer1 is full
    while(IpcGet(&g_sIpcController1, &sMessage, DISABLE_BLOCKING)!= STATUS_FAIL)
    {
        switch (sMessage.ulcommand)
        {
            case IPC_SET_BITS_PROTECTED:
                IPCRtoLSetBits_Protected(&sMessage);       // Processes IPCReqMemAccess() function
                break;
            case IPC_CLEAR_BITS_PROTECTED:
                IPCRtoLClearBits_Protected(&sMessage);     // Processes IPCReqMemAccess() function
                break;
            case IPC_BLOCK_WRITE:
                IPCRtoLBlockWrite(&sMessage);
                break;
            case IPC_BLOCK_READ:
                IPCRtoLBlockRead(&sMessage);
                break;
            default:
//                  ErrorFlag = 1;
                break;
        }
    }
    // Acknowledge IPC INT0 Flag and PIE to receive more interrupts
    IpcRegs.IPCACK.bit.IPC0 = 1;
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}
// CPU02toCPU01IPC1IntHandler - Handles Data Block Reads/Writes
__interrupt void IPC1_ISR(void)
{
    tIpcMessage sMessage;
    // Continue processing messages as long as CPU02toCPU01 GetBuffer2 is full
    while(IpcGet(&g_sIpcController2, &sMessage, DISABLE_BLOCKING)!= STATUS_FAIL)
    {
        switch (sMessage.ulcommand)
        {
            case IPC_SET_BITS_PROTECTED:
                IPCRtoLSetBits_Protected(&sMessage);       // Processes IPCReqMemAccess() function
                break;
            case IPC_CLEAR_BITS_PROTECTED:
                IPCRtoLClearBits_Protected(&sMessage);     // Processes IPCReqMemAccess() function
                break;
            case IPC_BLOCK_WRITE:
                IPCRtoLBlockWrite(&sMessage);
                break;
            case IPC_BLOCK_READ:
                IPCRtoLBlockRead(&sMessage);
                break;
            default:
//                  ErrorFlag = 1;
                break;
        }
    }
    // Acknowledge IPC INT1 Flag and PIE to receive more interrupts
    IpcRegs.IPCACK.bit.IPC1 = 1;
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}

CPU2中:

// CPU02toCPU01IPC0IntHandler - Handles Data Word Reads/Writes
__interrupt void IPC0_ISR(void)
{
    tIpcMessage sMessage;
    // Continue processing messages as long as CPU01 to CPU02 GetBuffer1 is full
    while(IpcGet(&g_sIpcController1, &sMessage, DISABLE_BLOCKING) != STATUS_FAIL)
    {
        switch (sMessage.ulcommand)
        {
            case IPC_BLOCK_WRITE:
                IPCRtoLBlockWrite(&sMessage);
                break;
            case IPC_BLOCK_READ:
                IPCRtoLBlockRead(&sMessage);
                break;
            default:
//                    ErrorFlag = 1;
                break;
        }
    }
    // Acknowledge IPC INT0 Flag and PIE to receive more interrupts
    IpcRegs.IPCACK.bit.IPC0 = 1;
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}
// CPU02toCPU01IPC1IntHandler - Handles Data Block Reads/Writes
__interrupt void IPC1_ISR(void)
{
    // Should never reach here - Placeholder for Debug
    tIpcMessage sMessage;
    // Continue processing messages as long as CPU01 to CPU02 GetBuffer1 is full
    while(IpcGet(&g_sIpcController2, &sMessage, DISABLE_BLOCKING) != STATUS_FAIL)
    {
        switch (sMessage.ulcommand)
        {
            case IPC_BLOCK_WRITE:
                IPCRtoLBlockWrite(&sMessage);
                break;
            case IPC_BLOCK_READ:
                IPCRtoLBlockRead(&sMessage);
                break;
            default:
//                    ErrorFlag = 1;
                break;
        }
    }
    // Acknowledge IPC INT1 Flag and PIE to receive more interrupts
    IpcRegs.IPCACK.bit.IPC1 = 1;
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}

CPU1发送–>CPU2接收

CPU1获得全局共享内存写权限,进行赋值、写块,产生IPC1中断,CPU2响应IPC1中断,完成块写命令。使用IPC11标志位,通知CPU2我已发送完成,CPU2就可以读啦。
CPU1中发送函数:

/*   数据发送         */
void IPC11_TX(void)
{
    if(IPCRtoLFlagBusy(IPC_FLAG11) == 1)
    {
        if((MemCfgRegs.GSxMSEL.bit.MSEL_GS0) == 1)
        {
            EALLOW;
            MemCfgRegs.GSxMSEL.bit.MSEL_GS0 = 0;
            EDIS;
        }
        pusCPU01BufferPt[0] = test;
        pusCPU01BufferPt[1] = test;
        pusCPU01BufferPt[2] = test;
        IPCLtoRBlockWrite(&g_sIpcController2, pulMsgRam2[1],(uint32_t)pusCPU01BufferPt,16,IPC_LENGTH_16_BITS,ENABLE_BLOCKING);
        IPCRtoLFlagAcknowledge(IPC_FLAG11);
        IPCLtoRFlagClear(IPC_FLAG11);
    }
}

CPU2中接收函数:
(usCPU02Buffer已在CPU2的IPC1中断中处理好)

/*   数据接收         */
void IPC11_RX(void)
{
    /*********** Data Block Reads*************/
    if(IPCRtoLFlagBusy(IPC_FLAG11) == 0)
    {
        IPCLtoRFlagSet(IPC_FLAG11);
    }
    while(IpcRegs.IPCSTS.bit.IPC11)
    {
    }
    test3 = usCPU02Buffer[0];
    test3 = usCPU02Buffer[1];
    test3 = usCPU02Buffer[2];
}

CPU2发送–>CPU1接收

CPU2申请全局共享内存写权限,产生IPC1中断,CPU1响应IPC1中断,给CPU2写权限。CPU2进行赋值、写块,产生IPC1中断,CPU1响应IPC1中断,完成块写命令。使用IPC13标志位,通知CPU1我已发送完成,CPU2就可以读啦。
CPU2中发送函数:

/*   数据发送         */
void IPC13_TX(void)
{
    /*********** Data Block Writes*************/
    // Request Memory Access to GS0 SARAM for CPU02, Set bits to let CPU02 own GS0
    IPCReqMemAccess(&g_sIpcController2, GS0_ACCESS, IPC_GSX_CPU2_MASTER,ENABLE_BLOCKING);
    while(MemCfgRegs.GSxMSEL.bit.MSEL_GS0 != 1U)
    {
    }
    pusCPU02BufferPt[0] = test2;
    pusCPU02BufferPt[1] = test2;
    // Write a block of data from CPU02 to GS0 shared RAM which is then written to an CPU01 address.
    IPCLtoRBlockWrite(&g_sIpcController2, pulMsgRam[0],(uint32_t)pusCPU02BufferPt,16, IPC_LENGTH_16_BITS,ENABLE_BLOCKING);
    if(IPCRtoLFlagBusy(IPC_FLAG13) == 0)
    {
        IPCLtoRFlagSet(IPC_FLAG13);
    }
}

CPU1中接收函数:
(usCPU01Buffer已在CPU1的IPC1中断中处理好)

/*   数据接收         */
void IPC13_RX(void)
{
    if(IPCRtoLFlagBusy(IPC_FLAG13) == 1)
    {
       test1 = usCPU01Buffer[0];
       test1 = usCPU01Buffer[1];
       IPCRtoLFlagAcknowledge(IPC_FLAG13);
       IPCLtoRFlagClear(IPC_FLAG13);
    }
}

实验验证

使用一些test变量,作为观察对象,临时性地测试双核收发是否正确。实际中可以使用带有一定物理意义的全局变量,比如角度、启动、停止、警告等,给不同的核做不同的事。从下面两张图可以看出,IPC通信是成功的!来之不易。
在这里插入图片描述
在这里插入图片描述

结束语

当然,TI提供了其他更多IPC的API函数,可以实现更多的操作。这里仅仅介绍笔者认为比较简单的双核通信方式。因IPC模块在技术参考手册中篇幅较短,所以使用起来不易,需要结合官方给出的例程,多做区分。TI的讨论社区也是值得去的地方,多数人会遇到类似的问题。我在此抛砖引玉,希望大佬提出宝贵建议。

参考资料目录

《TMS320F2837xD Dual-Core Microcontrollers Datasheet》Memory章节
《TMS320F2837xD Dual-Core Microcontrollers Technical Reference Manual》IPC、System Control章节
《F2837xD_IPC_Driver_UG》
C2000Ware有关IPC的所有例程

  • 31
    点赞
  • 144
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值