DSP28m35的IPC通讯编程经验

28m35介绍

  28m35属于Concerto 系列,这一系列特点是具有异构双核。它同时具有F28x和ARM Cortex M3内核,相关的例程可以在controlsuit中找到。以下TI官网对这一系列的介绍。
  Concerto 系列是一款多内核片上系统微控制器单元 (MCU),此控制器单元具有独立的通信和实时控制子系统。 F28M35x 系列器件是 Concerto 产品中的第一系列产品。
  此通信子系统基于行业标准 32 位 ARM Cortex-M3 CPU,并且特有多种通信外设,其中包括以太网 1588,具有 PHY 的 USB OTG,CAN,UART,SSI,I2C 和一个外部接口。
  此实时控制子系统基于 TI 行业领先的私有 32 位 C28x 浮点 CPU,并且特有最灵活和高精度的控制外设,其中包括具有故障保护功能的 ePWM,和编码器以及捕捉 - 所有这些外设均由 TI 的 TMS320C2000 Piccolo 和 Delfino 系列产品来执行。 此外,C28-CPU 已经添加了执行高效 Viterbi,复杂算术运算,16 位快速傅里叶变换 (FFT) 和 CRC 算法的 VCU 指令加速器。
  共享一个高速模拟子系统和补充 RAM 内存,还有片上电压稳压和冗余计时电路。 安全考虑还包括纠错码 (ECC),奇偶校验和代码安全内存,以及辅助系统级工业安全认证的文档。

28m35双核IPC通讯编程

  首先建议一定要从controlsuit中的例程开始学习,TI给出的demo可以帮助了解异构双核的运行。
  使用高版本CCS进行编程,因为里面带有新的编译器。在起初我使用CCS5.4编程时,烧入flash时程序无法正常工作,而当我换为CCS8.1时,在没有修改代码的情况下问题就消失了。根据网上介绍,旧版本的编译器对于烧写flash支持有问题,所以我后来的编程都是在CCS8.1下完成的。

Message RAM与Sx RAM

  IPC通讯,是将双核的数据交互的方式。Message ram,又可以称为IPC ram,主要作为双核IPC通讯的缓存。IPC Message RAM大小为2K字节。其通讯流程为

  1. Cortex-M3 先在MTOC Message RAM中写入一帧数据;
  2. Cortex-M3 置位MTOCIPCSET(CM3 映射存储器区)的Bit9,如图6 所示,此时MTOCIPCSTS
    (C28x 映射存储器区)的Bit9 也将置位;
  3. C28x 轮询MTOCIPCSTS 的Bit9,查询到Bit9 已置位;(如果之前的操作是Bit0 到 Bit3 其中之
    一, 则将触发C28x 产生一个IPC 中断)
  4. C28x 读MTOC Message RAM中的数据,此时,Cortex-M3 成功将一帧数据发送至C28x。
    在这里插入图片描述
      此外,28m35还有64K 字节大小的Shared RAM 区,s0~s7等也可以作为共享区缓存,适合大量数据的传输。Cortex-M3 可以设置让任何一块Shared RAM区由C28x 或M3 主控,比如,映射S0 至C28x 侧以后,C28x CPU 和DMA 可以读写S0,而M3 和uDMA 将只能读S0,不能写入和预取。假如Cortex-M3 需要一次性发送6K 字节的数据到C28x 侧,它可以先将Shared RAM区S0 映射到本地存储器空间,接着通过IPC 发送一个标志位给C28x 来通知其可以将数据取走。
    在这里插入图片描述
    在这里插入图片描述

IPC通讯基本思路

  一般来讲,m3内核适合负责通讯,IO口配置等任务,类似于IO密集型的任务,做一些对CPU性能要求相对低的任务,而dsp的c28内核,适合进行算法的计算,如fft,小型神经网络,复杂的控制算法等,类似于计算密集型任务,做一些对CPU性能相对高的任务。而使得这两个内核配合好的关键,在于两个内核的交互通讯,因此,ipc通讯十分重要。
  对于缓存区,一方写入数据,一旦完成后,另一方的IPC中断告知读取完成,使得另一方在主循环中处理数据。本文对s0和message ram都使用了,主要的数组传递在message ram里面,并且使用CPU读写,如果改为DMA读写,可以提升效率。这里的操作主要基于TI提供的库函数来执行。

m3侧程序编写

  IPC中部分地址宏定义

#define M3_CTOM_PASSMSG   0x2007F7E8            // CTOM MSG RAM offsets for
                                                // passing addresses
#define M3_MTOC_PASSMSG 0x2007FFE8  // MTOC MSG RAM offsets for passing
                                    // addresses
#define M3_S0SARAM_START  0x20008000            // Start of S0 SARAM in M3
                                                // memory map
#define M3_S1SARAM_START  0x2000A000            // Start of S1 SARAM in M3
                                                // memory map
#define usMBuffer_SIZE 100 //IPC通讯

  28m35中,C28x内核启动的时候,需要M3来发命令告诉C28启动的。因此,编程是先从M3内核的程序开始写起。M3内核的主要作用是通讯,包含与上位机的通讯、C28x内核的数据交互。这里主要讲与c28x内核的通讯,为此,M3侧的IPC初始化

    // 以下变量的作用是为了与c28x内核通讯用
    unsigned short counter;
    unsigned short *pusMBufferPt;
    unsigned short *pusCBufferPt;
    unsigned long *pulMsgRam;

    // 禁止保护
    HWREG(SYSCTL_MWRALLOW) =  0xA5A5A5A5;

    // 设置PLL, M3 行于75MHz,C28 运行于150MHz
    SysCtlClockConfigSet(SYSCTL_USE_PLL | (SYSCTL_SPLLIMULT_M & 0xF) |
                         SYSCTL_SYSDIV_1 | SYSCTL_M3SSDIV_2 |
                         SYSCTL_XCLKDIV_4);

    // 初始化消息ram,包含 M3toC28 message RAM and Sx SARAM,一直等初始化完以后,程序才继续运行,这边是根据controlsuit学习
    //S0的控制权给M3
    RAMMReqSharedMemAccess(S0_ACCESS, SX_M3MASTER);  
    // 对 Sx RAM and MtoC MSG RAM初始化
    HWREG(RAM_CONFIG_BASE + RAM_O_MSXRTESTINIT1) |= 0x1;
    while((HWREG(RAM_CONFIG_BASE + RAM_O_MSXRINITDONE1)&0x1) != 0x1)
    {
    }
    HWREG(RAM_CONFIG_BASE + RAM_O_MTOCCRTESTINIT1) |= 0x1;
    while((HWREG(RAM_CONFIG_BASE + RAM_O_MTOCRINITDONE)&0x1) != 0x1)
    {
    }
    //  进行向保护寄存器写
    HWREG(SYSCTL_MWRALLOW) = 0;

    // 配置M3侧的中断,用于IPC通讯
    IntRegister(INT_CTOMPIC2, CtoMIPC2IntHandler);

//烧写FLASH时用到的函数
#ifdef _FLASH
// Copy time critical code and Flash setup code to RAM
// This includes the following functions:  InitFlash();
// The  RamfuncsLoadStart, RamfuncsLoadSize, and RamfuncsRunStart
// symbols are created by the linker. Refer to the device .cmd file.
    memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);

// Call Flash Initialization to setup flash waitstates
// This function must reside in RAM
    FlashInit();
#endif

    // 初始化 IPC 控制器
    IPCMInitialize (&g_sIpcController2, IPC_INT2, IPC_INT2);

    // 使能处理器的中断
    IntMasterEnable();
    // 使能IPC中断
    IntEnable(INT_CTOMPIC2);

    pulMsgRam = (void *)M3_MTOC_PASSMSG;
    pulMsgRam[2]= (unsigned long)&gusMBuffer[0];


    //本地变量的初始化,为了M3与28x通讯
    pulMsgRam = (void *)M3_CTOM_PASSMSG;

    pusMBufferPt = (void *)M3_S0SARAM_START;
    pusCBufferPt = (void *)(M3_S0SARAM_START + usMBuffer_SIZE);

    ErrorCount = 0;
	//建立两个缓存数组,对其进行初始化
    for (counter = 0; counter < usMBuffer_SIZE; counter++)
    {
        usMBuffer[counter] = 0;
    }
    for (counter = 0; counter < usMBuffer_SIZE; counter++)
    {
        gusMBuffer[counter] = 0;
    }

    //IPC中断开启
    // Spin here until C28 has written variable addresses to pulMsgRam
    while ((HWREG(MTOCIPC_BASE + IPC_O_CTOMIPCSTS) & IPC_CTOMIPCSTS_IPC17) !=
           IPC_CTOMIPCSTS_IPC17)
    {
    }
    HWREG(MTOCIPC_BASE + IPC_O_CTOMIPCACK) = IPC_CTOMIPCACK_IPC17;


    //IPC正常通讯后,才允许C28X获得外设控制

    // Enable clock supply for the peripherals
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);

  剩余的代码是负责分配给c28x外设

    // 给c28x GPIO控制权
    //EPWM 
    GPIOPinConfigureCoreSelect(GPIO_PORTA_BASE, GPIO_PIN_0, GPIO_PIN_C_CORE_SELECT);
    GPIOPinConfigureCoreSelect(GPIO_PORTA_BASE, GPIO_PIN_1, GPIO_PIN_C_CORE_SELECT);
    GPIOPinConfigureCoreSelect(GPIO_PORTA_BASE, GPIO_PIN_2, GPIO_PIN_C_CORE_SELECT);
    GPIOPinConfigureCoreSelect(GPIO_PORTA_BASE, GPIO_PIN_3, GPIO_PIN_C_CORE_SELECT);
    GPIOPinConfigureCoreSelect(GPIO_PORTA_BASE, GPIO_PIN_4, GPIO_PIN_C_CORE_SELECT);
    GPIOPinConfigureCoreSelect(GPIO_PORTA_BASE, GPIO_PIN_5, GPIO_PIN_C_CORE_SELECT);

    //Led 指示
    GPIOPinConfigureCoreSelect(GPIO_PORTB_BASE, GPIO_PIN_6, GPIO_PIN_C_CORE_SELECT);


然后是配置m3侧的uart,即串口,这一部分程序可以根据例程学习

    // 禁止看门狗
    SysCtlPeripheralDisable(SYSCTL_PERIPH_WDOG1);
    SysCtlPeripheralDisable(SYSCTL_PERIPH_WDOG0);

    // Enable processor interrupts.
    IntMasterEnable();


    //  D2 and D3 为UART引脚
    GPIOPinConfigure(GPIO_PD2_U1RX);
    GPIOPinConfigure(GPIO_PD3_U1TX);
    GPIOPinTypeUART(GPIO_PORTD_BASE, GPIO_PIN_2 | GPIO_PIN_3);

    // 配置UART 波特率 9600, 8-N-1 operation.
    UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(SYSTEM_CLOCK_SPEED), 9600,
                        (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                         UART_CONFIG_PAR_NONE));

    // 使能UART 中断
    IntRegister(INT_UART1, UARTIntHandler);
    IntEnable(INT_UART1);
    UARTIntEnable(UART1_BASE, UART_INT_RX | UART_INT_RT);

  在主循环中,包含了对c28x数据的读取和发送

    while(1)
    {
        if(IPC_get_flag)//这一标志位在IPC中断里面置1
        {
            IPCdata_tran();
            IPC_get_flag=0;
        }
        //与上位机的数据通讯与处理,具体代码就不展示了
        //收到上位机数据后,就置IPC_send_flag为1
         Checkdata();
         SciSend();

//根据标志位判断是否向28x传递数据,这一部分操作代码来源于例程
         if(IPC_send_flag==1)
         {
         //块数据传输
             // Data Block Writes
             // Request Memory Access to S0 SARAM for M3
             RAMMReqSharedMemAccess (S0_ACCESS, SX_M3MASTER);
             while ((HWREG(RAM_CONFIG_BASE + RAM_O_MSXMSEL) & S0_ACCESS) != 0)
             {
             }
             // Write a block of data from M3 to S0 shared RAM which is then written to a
             // C28 address.
             for (counter = 0; counter < usMBuffer_SIZE; counter++)
             {
                 pusMBufferPt[counter] = usMBuffer[counter];
             }

             IPCMtoCBlockWrite(&g_sIpcController2, pulMsgRam[2],
                               (unsigned long)pusMBufferPt, usMBuffer_SIZE, IPC_LENGTH_16_BITS,
                               ENABLE_BLOCKING);

             // Give Memory Access to S0 SARAM to C28
             RAMMReqSharedMemAccess (S0_ACCESS, SX_C28MASTER);

             while ((HWREG(RAM_CONFIG_BASE + RAM_O_MSXMSEL) & S0_ACCESS) != S0_ACCESS)
             {
             }

             // Read data back from C28.验证发送是否完成
             IPCMtoCBlockRead(&g_sIpcController2, pulMsgRam[2],
                              (unsigned long)pusCBufferPt, usMBuffer_SIZE, ENABLE_BLOCKING,
                              IPC_FLAG17);

             // Wait until read data is ready (by checking IPC Response Flag is cleared).
             // Then check for correct data.
             while (HWREG(MTOCIPC_BASE + IPC_O_MTOCIPCFLG) & IPC_MTOCIPCFLG_IPC17)
             {
             }

             for (counter = 0; counter <usMBuffer_SIZE; counter++)
             {
                 if (usMBuffer[counter] != pusCBufferPt[counter])
                 {
                     ErrorFlag = 1;
                 }
                 else
                 {
                     ErrorFlag = 0;
                 }
             }

             if (ErrorFlag == 1)
             {
                 ErrorCount++;
             }

             IPC_send_flag=0;
         }

    }

  这里IPCdata_tran的实现为,44和我发送的数据量有关,这边用位域的操作将两个16位变量合并位一个32位变量。确实就是前面博客讲过的浮点数位域传输法。c28x的数据中,32位变量分拆为了两个16位变量,这里将其重恢复为原来数据。

void IPCdata_tran(void)
{
    int i=0;

    for(i=0;i<44;i++)
    {
        IPC_get.bit.MEM1=gusMBuffer[2*i];
        IPC_get.bit.MEM2=gusMBuffer[2*i+1];
        Paramet[i]=IPC_get.all;
    }

}

  IPC中断负责数据接收,其实更准确应该是提醒m3可以接收数据了。

void
CtoMIPC2IntHandler (void)
{
    // 这一中断说明c28x数据已传入共享,m3可以读取数据了
    tIpcMessage sMessage;

    // Continue processing messages as long as CtoM GetBuffer2 is full
    while (IpcGet (&g_sIpcController2, &sMessage,
                   DISABLE_BLOCKING)!= STATUS_FAIL)
    {
        switch (sMessage.ulcommand)
        {
        case IPC_SET_BITS_PROTECTED:
            IPCCtoMSetBits_Protected(&sMessage);       // Processes
                                                       // IPCCtoMReqMemAccess()
                                                       // function
            break;
        case IPC_CLEAR_BITS_PROTECTED:
            IPCCtoMClearBits_Protected(&sMessage);     // Processes
                                                       // IPCCtoMReqMemAccess()
                                                       // function
            break;
        case IPC_BLOCK_WRITE:
            IPCCtoMBlockWrite(&sMessage);
            break;
        case IPC_BLOCK_READ:
            IPCCtoMBlockRead(&sMessage);
            break;
        default:
            ErrorFlag = 1;
            break;
        }
    }
    IPC_get_flag=1;
    // Acknowledge IPC INT2 Flag
    HWREG(MTOCIPC_BASE + IPC_O_CTOMIPCACK) |= IPC_CTOMIPCACK_IPC2;
}

  如果没有交互通讯,那么M3初始化代码也应该有这一段,如果C28X跑在FLASH,则是前者,否则后者。在我的程序中没有这一段话。

#ifdef _FLASH
// Send boot command to allow the C28 application to begin execution
IPCMtoCBootControlSystem(CBROM_MTOC_BOOTMODE_BOOT_FROM_FLASH);
#else
// Send boot command to allow the C28 application to begin execution
IPCMtoCBootControlSystem(CBROM_MTOC_BOOTMODE_BOOT_FROM_RAM);
#endif

c28x侧程序编写

  IPC的部分宏定义

//*****************************************************************************
// IPC
//*****************************************************************************
#define C28_CTOM_PASSMSG  0x0003FBF4            // Used by C28 to pass address
                                                // of local variables to perform
                                                // actions on
#define C28_MTOC_PASSMSG  0x0003FFF4            // Used by M3 to pass address of
                                                // local variables to perform
                                                // actions on

#define C28_S0SARAM_START 0xC000                // Start of S0 SARAM in C28
                                                // memory map
#define C28_S1SARAM_START 0xD000                // Start of S1 SARAM in C28
                                                // memory map
#define usMBuffer_SIZE 100

  基本的外设配置如下,基本都是对应m3的配置而来,可以参考controlsuit编写

// 定义变量
    Uint32 *pulMsgRam;
    Uint16 counter;

    Uint16 *pusCBufferPt = (void *)C28_S0SARAM_START;
    Uint16 *pusMBufferPt = (void *)(C28_S0SARAM_START + usMBuffer_SIZE);

    InitSysCtrl();

#ifdef _FLASH
// Copy time critical code and Flash setup code to RAM
// This includes the following functions:  InitFlash();
// The  RamfuncsLoadStart, RamfuncsLoadSize, and RamfuncsRunStart
// symbols are created by the linker. Refer to the device .cmd file.
    memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);

// Call Flash Initialization to setup flash waitstates
// This function must reside in RAM
    InitFlash();
#endif


    DINT;
    InitPieCtrl();
    IER = 0x0000;
    IFR = 0x0000;
    InitPieVectTable();
	//这一部分对应了前面m3分配的外设控制器
    InitBoardGpio();
    ConfigureEPwm();
    ConfigureADC();//ADC为c28x控制
    Initparameter();

    EALLOW;
    PieVectTable.MTOCIPC_INT2 = &MtoCIPC2IntHandler;
    EDIS;

    // 初始化IPC
    IPCCInitialize (&g_sIpcController2, IPC_INT2, IPC_INT2);

    //使能CPU中断
    IER |= M_INT11;
    //使能PIE中断
    PieCtrlRegs.PIEIER11.bit.INTx2 = 1; // MTOCIPC INT2


    //开全局中断
    EINT;//使能全局中断(开中断)(CPU级的)
    ERTM;//使能实时中断(CPU级的)

    //初始化变量和缓冲数组
    ErrorFlag = 0;
    wErrorFlag = 0;
    for (counter = 0; counter < usMBuffer_SIZE; counter++)
    {
        usMBuffer[counter] = 0;
    }
    for (counter = 0; counter < usMBuffer_SIZE; counter++)
    {
        usCBuffer[counter] = 0;
    }
    // Point array to address in CTOM MSGRAM for passing variable locations
    pulMsgRam = (void *)C28_CTOM_PASSMSG;

    // Write addresses of variables where words should be written to pulMsgRam
    // array.
    pulMsgRam[2] = (unsigned long)&usMBuffer[0];


    pulMsgRam = (void *)C28_MTOC_PASSMSG;

    // Flag to M3 that the variables are ready in MSG RAM with CTOM IPC Flag 17
    CtoMIpcRegs.CTOMIPCSET.bit.IPC17 = 1;

  主循环的程序处理

    while(1)
    {
		//接收到m3数据标志置1,将数据读取转化
        if(IPC_get_flag)
        {
            IPCdata_tran();
            IPC_get_flag=0;
        }
		//这边负责拆分c28x数据,写入缓存数组,为发送给m3准备
        n_coop++;
        if(n_coop==30000)
        {
            int i=0;
            for(i=0;i<44;i++)
            {
                IPC_send.all=Paramet[i];
                usCBuffer[2*i]=IPC_send.bit.MEM1;
                usCBuffer[2*i+1]=IPC_send.bit.MEM2;
            }
            ipc_to_pso_flag=1;
            n_coop=0;
        }

		//参照例程的数据发送函数
        if(ipc_to_pso_flag)
        {
        // Data Block Writes
            // Request Memory Access to S0 SARAM for C28 (Invokes
            // IPCCtoMSetBits_Protected() function)
            IPCCtoMReqMemAccess (&g_sIpcController2, S0_ACCESS, IPC_SX_C28MASTER,
                                 ENABLE_BLOCKING);
            while ((RAMRegs.CSxMSEL.all & S0_ACCESS) != S0_ACCESS)
            {
            }

            // Write a block of data from C28 to S0 shared RAM which is then written to
            // an M3 address.
            for (counter = 0; counter < usMBuffer_SIZE; counter++)
            {
                pusCBufferPt[counter] = usCBuffer[counter];
            }

            IPCCtoMBlockWrite(&g_sIpcController2, pulMsgRam[2], (Uint32)pusCBufferPt,
                              usMBuffer_SIZE, IPC_LENGTH_16_BITS,
                              ENABLE_BLOCKING);

            // Return Memory Access to S0 SARAM to M3 (Invokes
            // IPCCtoMSetBits_Protected() function)
            IPCCtoMReqMemAccess (&g_sIpcController2, S0_ACCESS, IPC_SX_M3MASTER,
                                 ENABLE_BLOCKING);
            while ((RAMRegs.CSxMSEL.all & S0_ACCESS) != 0)
            {
            }


            // Read data back from M3.验证发送是否完成
            IPCCtoMBlockRead(&g_sIpcController2, pulMsgRam[2], (Uint32)pusMBufferPt,
                             usMBuffer_SIZE, ENABLE_BLOCKING,
                             IPC_FLAG17);

            // Wait until read data is ready (by checking IPC Response Flag is cleared).
            // Then check for correct data.
            while (CtoMIpcRegs.CTOMIPCFLG.bit.IPC17)
            {
            }

            for (counter = 0; counter <usMBuffer_SIZE; counter++)
            {
                if (usCBuffer[counter] != pusMBufferPt[counter])
                {
                    wErrorFlag = 1;
                }
                else
                {
                    wErrorFlag = 0;
                }
            }

            if (wErrorFlag == 1)
            {
                wErrorCount++;
            }
            ipc_to_pso_flag=0;
        }




        // Flag an Error if an Invalid Command has been received.
        //
        if (ErrorFlag == 1)
        {
            Error();
        }

    }

  IPC中断中接收完成标志位置1

//*****************************************************************************
// MtoC INT2 Interrupt Handler - Handles Data Block Reads/Writes
//*****************************************************************************
__interrupt void
MtoCIPC2IntHandler (void)
{
    tIpcMessage sMessage;

    // Continue processing messages as long as MtoC GetBuffer2 is full
    while (IpcGet (&g_sIpcController2, &sMessage,
                   DISABLE_BLOCKING)!= STATUS_FAIL)
    {
        switch (sMessage.ulcommand)
        {
        case IPC_BLOCK_WRITE:
            IPCMtoCBlockWrite(&sMessage);
            break;
        case IPC_BLOCK_READ:
            IPCMtoCBlockRead(&sMessage);
            break;
        default:
            ErrorFlag = 1;
            break;
        }
    }
    IPC_get_flag=1;
    // Acknowledge IPC INT2 Flag and PIE to receive more interrupts from group
    // 11
    CtoMIpcRegs.MTOCIPCACK.bit.IPC2 = 1;
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP11;
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值