DSP28379D 您所在的位置:网站首页 dsp是什么工作 DSP28379D

DSP28379D

2023-09-17 18:53| 来源: 网络整理| 查看: 265

双核IPC内部通信 IPC对于双核的重要性IPC模块通信流程IPC收发举例IPC收发具体实现CPU1发送-->CPU2接收CPU2发送-->CPU1接收 实验验证结束语参考资料目录

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 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 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的所有例程



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有