简介
I.MX RT1176 是一个双核的MCU,那么两个核之间是如何启动的呢?通过什么方式通信的呢?这里多废话几句,nxp demo中有提供相关框架(如下图)但本章不讲多核框架,只是一个初步认识,尽量用最底层思维去实现基本功能。
1.双核启动方法
rt1176 默认是m7核启动m4核的,如果需要修改,需要烧写eFuse(这里我们就默认官方的启动吧,手里只有一台开发板,烧eFuse还是需要勇气的。。。手动狗头)。
首先要双核启动,我们必须先让m7核跑起来,通过m7核操作m4才能完整实现双核启动,具体流程如下:
- m7核完全启动(能跑到main,文章最后会给工程链接)
- m7拷贝m4代码(比如bin文件在Flash某个区域或者SD卡里面等等)到指定Ram(m4 TCM)
为啥不是跳转m4实际ram 0x1ffe_0000 而是 0x2020_0000,官方手册是有说明大致意思就是在m7内核中通过 0x2020_0000能重映射到m4 ram中的0x1ffe_0000
#define CM4_IMAGE_START 0x30FC0000U //Flash镜像文件起始地址,这个位置其实可以任意定义,只要不跟M7重叠
#define CM4_RAM_START 0x20200000U
uint32_t imageSize = xBSP_MuliteCore_GetImageSize();
/*将Flash 镜像文件拷贝到指定RAM*/
SCB_CleanInvalidateDCache_by_Addr((void*)CM4_RAM_START, imageSize);
memcpy((void*)CM4_RAM_START, (void*)CM4_IMAGE_START, imageSize);
SCB_CleanInvalidateDCache_by_Addr((void *)CM4_RAM_START, imageSize);
笔者为了证实这个重映射问题,特意仿真测试了m7和m4的内存,事实证明是这样的
- 设置m4中断向量表映射位置
void xBSP_SetCM4_Vector(uint32_t vectorAddr)
{
IOMUXC_LPSR_GPR->GPR0 = IOMUXC_LPSR_GPR_GPR0_CM4_INIT_VTOR_LOW(vectorAddr >> 3);
IOMUXC_LPSR_GPR->GPR1 = IOMUXC_LPSR_GPR_GPR1_CM4_INIT_VTOR_HIGH(vectorAddr >> 16);
}
/*设置中断向量映射位置*/
xBSP_SetCM4_Vector(CM4_RAM_START);
- 启动m4内核
void launch_cm4_core(void)
{
/* If CM4 is already running (released by debugger), then reset the CM4.
If CM4 is not running, release it. */
if ((SRC->SCR & SRC_SCR_BT_RELEASE_M4_MASK) == 0)
{
SRC_ReleaseCoreReset(SRC, kSRC_CM4Core);
}
else
{
SRC_AssertSliceSoftwareReset(SRC, kSRC_M4CoreSlice);
}
}
/*启动M4内核*/
launch_cm4_core();
执行上述步骤,m4核也就正常启动了
2.双核通信MU
说到多核通信,若是多块MCU:类似于整车系统ECU单元的话,通常使用CAN或LIN总线实现通信;当然也有使用串口,USB等等实现通信。但是这里说的通信是单MCU,多个内核,像RT1176,内核提供了MU接口,如下图所示:
看到框图,我们可以将MU理解成m7和m4之前连接了串口接口。可以通过轮询或中断的方式查询MU状态。可以通过TR寄存器发送数据;RR寄存器接收数据;SR为状态寄存器用于查询TR和RR状态;CR控制寄存器用于配置MU。
/** MU - Register Layout Typedef */
typedef struct {
__IO uint32_t TR[4]; /**< Processor A Transmit Register 0..Processor A Transmit Register 3, array offset: 0x0, array step: 0x4 */
__I uint32_t RR[4]; /**< Processor A Receive Register 0..Processor A Receive Register 3, array offset: 0x10, array step: 0x4 */
__IO uint32_t SR; /**< Processor A Status Register, offset: 0x20 */
__IO uint32_t CR; /**< Processor A Control Register, offset: 0x24 */
} MU_Type;
接下来我们配置m7和m4核。
m7核:
#define BOOT_FLAG (0x01U) /* Flag indicates Core Boot Up*/
void xBSP_MUInit(void)
{
muMutex = xSemaphoreCreateMutex();
/* MUA init */
MU_Init(MUA);
/* Wait Core 1 is Boot Up */
while (BOOT_FLAG != MU_GetFlags(MUA))
{
}
// /*是否使用中断方式判断*/
// MU_EnableInterrupts(MUA, kMU_Rx0FullInterruptEnable);
// NVIC_EnableIRQ(MUA_IRQn);
}
//MU接收任务,用来观测m4发送的参数
void xTASK_ReceiveMsg(void* pvParameters)
{
for(;;)
{
MuData =xBSP_MU_ReceiveMsg();
muDataBuff[wrp++] = MuData;
wrp = wrp % 1024;
vTaskDelay(100);
}
}
m4核:
#define BOOT_FLAG (0x01U) /* Flag indicates Core Boot Up*/
void xBSP_MUInit(void)
{
/* MUB init */
MU_Init(MUB);
/* Send flag to Core 0 to indicate Core 1 has startup */
MU_SetFlags(MUB, BOOT_FLAG);
}
void xTASK_SendMsg(void)
{
static uint32_t data;
xBSP_MU_SendMsg(data);
data++;
}
测试(仿真M7核):
利用MU标志,可观测出M4启动完成
在m7接收任务中观测MU,可接收到m4发送的参数
另外双核间有共享内存,在m7和m4都能正常观测,使用前需要配置MPU。
注:多核通信并不是这么简单,这里只是做个简单演示,涉及到的问题很多。所以这时候是需要多核框架来实现的,如nxp提供的:erpc、rpmsg-lite(基于linux多核裁剪的)、stream buffer(rtos提供)。
这里提供M7核源码(内含m4镜像文件,点击下载可将m7和m4可执行文件烧录到指定路径):
RT1176_M7_RTOS