TMS320F28388D使用sysconfig配置IPC

第1章 配置IPC底层代码

使用IPC的动机:

我计划我的项目中要使用RS485,CANFD通信和EtherCAT通信,由于通信种类较多,而对于电机控制来说大部分数据都是重复的,并且有些数据可以很久才改变一次,所以我计划使用CPU2做FOC算法控制电机,使用CPU1与上位机进行通信,使用IPC做电机参数之间的通信。

实现目的:

CPU1与CPU2之间可以相互发送数据,数据支持浮点数。通信速度尽可能快和稳定。

下面的代码只有几百行,但是网上资料较少,问了TI工程师也没有答复,整了两三天才整出来,制作不易,希望点赞收藏加个关注,后期会不定时更新单片机的学习笔记。

1.1 CPU1的sysconfig配置

我的CPU1主要是用来做通信的,其实做通信最好是使用CM核,因为他是cortex-M内核,对信息的接收比较好,但是由于网上对这个核的资料很少,并且不能使用sysconfig进行配置,开发速度比较慢,综合起来就使用了CPU1做通信,CPU2做其他外设的控制代码,去跑算法。我的想法是使用CPU1能够主动发送和读取CPU2的数据,CPU2是跑算法和其他外设控制的,所以CPU1的IPC不需要被CPU2的中断,CPU2的数据应能够被CPU1中断。但是看很多解释以及芯片手册,貌似必须要有IPC中断才能接收数据,这点现在依然有疑问。知道的伙伴还望解答。

CPU1的sysconfig配置为:

第一行配置为选择CPU2的boot Mode即启动模式,Sysconfig对其的解释为在FLASH启动,配置完成后仍然需要在CPU1主函数中调用

Device_bootCPU2(BOOT_MODE_CPU2);函数来使能CPU2.

第二行配置为选择IPC的FLAG,CPU的IPC一共有32个标志,这里使用了IPC_FLAG0用作通信触发CPU2的中断,使用IPC_FLAG31来作为标志来同步两个CPU时钟。

第三行配置为选择能使CPU1的IPC_FLAG0,必须要勾选下面的对号,用来触发CPU2的接收中断。

第四行配置为使能IPC_FLAG31来作为标志来同步两个CPU时钟。可以选测0~31任一。

第五行配置为CPU1接收CPU2的IPC触发中断,IPC的中断有IPC0-3四个,这里选择了IPC1,也可使用IPC0,IPC1,IPC2,IPC3。

第六行配置为使能PIE里对应的中断。

1.2 CPU2的sysconfig配置

CPU2的sysconfig配置为

第一行配置为使用了IPC_FLAG1,IPC_FLAG31。

第二行配置为使能IPC_FLAG1,用于配置触发CPU1接收IPC中断的FLAG。

第三行配置为使能IPC_FLAG31,用于同步CPU核的时钟,可用其他FLAG,但要与CPU1的对应。

第四行配置为打开CPU2的IPC0中断,用于触发接收CPU1通过IPC_FLAG0发送的数据的中断

第五行为开启IPC0中断函数

第2章 IPC的应用层代码

2.1 CPU1的主函数IPC代码

第一步:根据1.1的介绍,需要先执行开启CPU2的代码,即:

复制代码
Device_bootCPU2(BOOT_MODE_CPU2);

第二步:清除IPC标志位,通过IPC_FLAG31同步两个CPU时钟,代码如下

复制代码
    IPC_clearFlagLtoR(IPC_CPU1_L_CPU2_R, IPC_FLAG_ALL);//清除IPC的所有标志位
    IPC_sync(IPC_CPU1_L_CPU2_R, IPC_FLAG31);//通过IPC_FLAG31同步两个CPU时钟

第一行解释为:清除CPU1与CPU2通信的IPC的所有FLAG。

第二行解释为:同步CPU时钟代码。

第三步:设置要发送的信息,代码为下:

复制代码
#pragma DATA_SECTION(readData,"MSGRAM_CPU1_TO_CPU2")
float readData[10];
int i,j;
for(i=0; i<10; i++)
{
   j++;
   readData[i] = i + j*0.5f;
}

第一行解释为:是 TI 编译器的一条 编译指令(pragma 指令) ,用于将变量 readData 放入指定的内存段 "MSGRAM_CPU1_TO_CPU2" 中,而不是默认的数据段(如 .data.bss

地址在:

复制代码
MEMORY
{

   CPU1TOCPU2RAM   : origin = 0x03A000, length = 0x000800
   CPU2TOCPU1RAM   : origin = 0x03B000, length = 0x000800

}

SECTIONS
{

   MSGRAM_CPU1_TO_CPU2 : > CPU1TOCPU2RAM, type=NOINIT
   MSGRAM_CPU2_TO_CPU1 : > CPU2TOCPU1RAM, type=NOINIT

}

第二行解释位:定义数据类型,定义为了一个数组。

第三,四,五行解释为:对数据进行初始化。

第四步:发送数据

复制代码
while(1)
{
  DEVICE_DELAY_US(5000000);
  IPC_sendCommand(IPC_CPU1_L_CPU2_R, IPC_FLAG0, IPC_ADDR_CORRECTION_ENABLE,
                  IPC_CMD_READ_MEM, (uint32_t)readData, 10);
  for(i=0; i<10; i++)
  {
     j++;
     readData[i] = i + j*0.5f;
  }
}

延迟是为了防止数据发送较快,看不出效果,IPC_sendCommand函数中为要发送的数据,数据使用了地址修正功能,

为对数据发送没有影响,可以理解为对数据的功能进行标定,方便对数据进行存储,处理。

CPU1主函数总代码为:

复制代码
#include "driverlib.h"
#include "device.h"
#include "board.h"
#include "IPC.h"
#include "stdio.h"
#include "Interrupt.h"
#include "string.h"
#include "Modbus.h"
#pragma DATA_SECTION(readData,"MSGRAM_CPU1_TO_CPU2")
float readData[10];
int i,j;
void main(void)
{
    Device_init();//初始化所有时钟以及外设
    Interrupt_initModule();//初始化PIE,清除所有PIE寄存器,失能CPU中断
    Interrupt_initVectorTable();//初始化PIE向量表
    Board_init();//初始化来自SysConfig的配置
    Device_bootCPU2(BOOT_MODE_CPU2);//启动CPU2核心部分
    TX_EN;//RS485发送信息使能
    IPC_clearFlagLtoR(IPC_CPU1_L_CPU2_R, IPC_FLAG_ALL);//清除IPC的所有标志位
    IPC_sync(IPC_CPU1_L_CPU2_R, IPC_FLAG31);//通过IPC_FLAG31同步两个CPU时钟
    EINT;//使能全部中断
    ERTM;//使能允许调试
    setvbuf(stdout, NULL, _IONBF, 0);//防止发送堵塞
    while(1)
    {
        DEVICE_DELAY_US(5000000);
        IPC_sendCommand(IPC_CPU1_L_CPU2_R, IPC_FLAG0, IPC_ADDR_CORRECTION_ENABLE,
                        IPC_CMD_READ_MEM, (uint32_t)readData, 10);

        for(i=0; i<10; i++)
        {
            j++;
            readData[i] = i + j*0.5f;
        }
    }
}

我是接着我上个文章的代码写的,使用这个主要是为了好测试,若有不懂的代码可以观看我的另一篇博客:TMS320F28388使用sysconfig配置SCI通信(RS485+FIFO+Modbus)

2.2 CPU1的IPC中断代码

复制代码
__interrupt void IPC_1_ISR(void)
{
    int i;
    uint32_t command, addr, data;
    IPC_readCommand(IPC_CPU1_L_CPU2_R, IPC_FLAG1, IPC_ADDR_CORRECTION_ENABLE,
                    &command, &addr, &data);

    if(command == IPC_CMD_RESP)
    {
        float* recvData = (float*)addr;
        for(i=0; i<data; i++)
        {
            printf("Float[%d] = %f\r\n", i, recvData[i]);
        }
    }

    IPC_ackFlagRtoL(IPC_CPU1_L_CPU2_R, IPC_FLAG1);
    Interrupt_clearACKGroup(IPC_1_INTERRUPT_ACK_GROUP);

}

2.3 CPU2的主函数IPC代码

第一步:同CPU1的一样,清除IPC标志位,通过IPC_FLAG31同步两个CPU时钟,代码如下

复制代码
IPC_clearFlagLtoR(IPC_CPU2_L_CPU1_R, IPC_FLAG_ALL);//清除IPC的所有标志位
IPC_sync(IPC_CPU2_L_CPU1_R, IPC_FLAG31);//通过IPC_FLAG31同步两个CPU时钟

第一行解释为:清除CPU1与CPU2通信的IPC的所有FLAG。

第二行解释为:同步CPU时钟代码。

第二步:设置要发送的信息,代码为下:

复制代码
#pragma DATA_SECTION(Data,"MSGRAM_CPU2_TO_CPU1")
float Data[10];
int i,j;
for(i = 0; i < 10; i++)
{
    Data[i] = i + j*0.1f; // 与CPU1的内容区分
}

第一行解释为:同CPU1一样,是 TI 编译器的一条 编译指令(pragma 指令) ,用于将变量 Data 放入指定的内存段 "MSGRAM_CPU2_TO_CPU1" 中,而不是默认的数据段(如 .data.bss

地址在:

复制代码
MEMORY
{

   CPU1TOCPU2RAM   : origin = 0x03A000, length = 0x000800
   CPU2TOCPU1RAM   : origin = 0x03B000, length = 0x000800

}

SECTIONS
{

   MSGRAM_CPU1_TO_CPU2 : > CPU1TOCPU2RAM, type=NOINIT
   MSGRAM_CPU2_TO_CPU1 : > CPU2TOCPU1RAM, type=NOINIT

}

第二行解释位:定义发送数据类型,定义为了一个数组。

第三,四,五行解释为:对数据进行初始化。

第四步:发送数据

复制代码
while(1)
{
   DEVICE_DELAY_US(5000000);
   IPC_sendCommand(IPC_CPU2_L_CPU1_R, IPC_FLAG1, IPC_ADDR_CORRECTION_ENABLE,
                 IPC_CMD_RESP, (uint32_t)Data, 10);
   for(i = 0; i < 10; i++)
   {
     Data[i] = i + j*0.1f; // 与CPU1的内容区分
   }
}

延迟是为了防止数据发送较快,看不出效果,IPC_sendCommand函数中为要发送的数据,数据使用了地址修正功能,

为对数据发送没有影响,可以理解为对数据的功能进行标定,方便对数据进行存储,处理。

CPU2主函数总代码为:

复制代码
#include "driverlib.h"
#include "device.h"
#include "board.h"
#include "IPC.h"
#include "Interrupt.h"
#include "stdio.h"
int i,j;
#pragma DATA_SECTION(Data, "MSGRAM_CPU2_TO_CPU1")
float Data[10];
void main(void)
{
    Device_init();//初始化设备时钟和外设
    Interrupt_initModule();//初始化PIE,清除PIE寄存器,失能CPU中断
    Interrupt_initVectorTable();//初始化PIE向量表
    Board_init();//初始化SysConfig配置的外设
    IPC_clearFlagLtoR(IPC_CPU2_L_CPU1_R, IPC_FLAG_ALL);//清除IPC的所有标志位
    IPC_sync(IPC_CPU2_L_CPU1_R, IPC_FLAG31);//通过IPC_FLAG31同步两个CPU时钟
    EINT;//使能所有中断
    ERTM;//使能允许调试
    while(1)
    {
        DEVICE_DELAY_US(5000000);
        IPC_sendCommand(IPC_CPU2_L_CPU1_R, IPC_FLAG1, IPC_ADDR_CORRECTION_ENABLE,
                        IPC_CMD_RESP, (uint32_t)Data, 10);
        j++;
        for(i = 0; i < 10; i++)
        {
            Data[i] = i + j*0.1f; // 与CPU1的内容区分
        }
    }
}

2.4 CPU2的IPC中断代码为

复制代码
__interrupt void IPC_0_ISR()
{
    int i;
    uint32_t command, addr, data;
    IPC_readCommand(IPC_CPU2_L_CPU1_R, IPC_FLAG0, IPC_ADDR_CORRECTION_ENABLE,
                    &command, &addr, &data);
    if(command == IPC_CMD_READ_MEM)
    {
        float* recvData = (float*)addr;
        for(i=0; i<data; i++)
        {
            printf("Float[%d] = %f\r\n", i, recvData[i]);
        }
    }
    IPC_ackFlagRtoL(IPC_CPU2_L_CPU1_R, IPC_FLAG0);
    Interrupt_clearACKGroup(IPC_0_INTERRUPT_ACK_GROUP);
}

第三章 测试结果

CPU1接收到的数据为:

该数据是由CPU2发送的,发送的数据代码为:

复制代码
for(i = 0; i < 10; i++)
{
    Data[i] = i + j*0.1f; // 与CPU1的内容区分
}

CPU1接收到的数据符合我们想要发送的数据。通信正常,关于如何使用printf函数,我的上一篇博客有详细的介绍。链接为:TMS320F28388使用sysconfig配置SCI通信(RS485+FIFO+Modbus)

CPU2接收到的数据为:

该数据是由CPU1发送的,发送的数据代码为:

复制代码
for(i=0; i<10; i++)
{
   j++;
   readData[i] = i + j*0.5f;
}

CPU2接收到的数据也符合我们想要发送的数据。通信正常,关于如何使用printf函数打印到控制台,我的上一篇博客也有详细的介绍。链接为:TMS320F28388使用sysconfig配置SCI通信(RS485+FIFO+Modbus)

制作不易,使用请点赞收藏。

相关推荐
森焱森41 分钟前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白44 分钟前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D1 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术4 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt5 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘5 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang5 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c
几个几个n7 小时前
STM32-第二节-GPIO输入(按键,传感器)
单片机·嵌入式硬件
慕尘11 小时前
Clion配置51单片机开发环境
单片机
良许Linux13 小时前
32岁入行STM32迟吗?
stm32·单片机·嵌入式硬件