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字节。其通讯流程为
- Cortex-M3 先在MTOC Message RAM中写入一帧数据;
- Cortex-M3 置位MTOCIPCSET(CM3 映射存储器区)的Bit9,如图6 所示,此时MTOCIPCSTS
(C28x 映射存储器区)的Bit9 也将置位; - C28x 轮询MTOCIPCSTS 的Bit9,查询到Bit9 已置位;(如果之前的操作是Bit0 到 Bit3 其中之
一, 则将触发C28x 产生一个IPC 中断) - 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;
}