多核MCU外设协同开发:从CPU2访问权限到RTOS资源管理实战

📅 2026/8/13 12:30:10
多核MCU外设协同开发:从CPU2访问权限到RTOS资源管理实战
1. 项目概述多核MCU中的外设协同挑战在嵌入式开发领域多核微控制器MCU正变得越来越普遍它们通过集成多个处理器核心来提升系统性能和处理并行任务的能力。德州仪器TI的C2000系列特别是像TMS320F2837xD这样的双核DSP就是其中的典型代表。这类芯片通常包含两个高性能的C28x CPU核心CPU1和CPU2共享丰富的外设资源如ADC、ePWM、eCAP、eQEP、SCI、SPI等。然而一个看似简单却极易引发困惑的问题随之而来CPU2如何正确地使用这些共享外设这绝不是一个简单的“使能时钟、配置寄存器、开始工作”的流程。在多核架构下外设的访问权限、配置归属、中断路由、内存视图同步以及任务间的通信与同步构成了一个复杂的系统工程。很多开发者尤其是从单核MCU如STM32转过来的工程师很容易在这里“踩坑”。他们可能会发现明明在CPU1上运行良好的外设驱动在CPU2上却毫无反应或者试图通过CPU2去访问某个外设寄存器时系统直接进入了错误处理。网络上相关的热词如“rtos3.0外设”、“esp-idf调试 查看外设寄存器”、“keil5的peripherals没外设”都从侧面反映了开发者在多核或复杂环境下调试外设时的普遍痛点。本文将以TI C2000 F2837xD为例深入拆解CPU2使用外设的全过程。我们将超越简单的代码示例深入到系统架构、寄存器映射、核间通信IPC机制以及实时操作系统RTOS下的最佳实践中去。目标是让你不仅知道“怎么做”更透彻理解“为什么这么做”从而能够举一反三应对任何多核MCU的外设协同开发挑战。2. 核心架构与访问权限解析在开始写第一行代码之前我们必须先理解F2837xD的架构是如何组织外设资源的。这是所有后续操作的基础理解错误会导致整个项目方向走偏。2.1 内存映射与CPU视图F2837xD采用哈佛架构并有统一的内存映射。最关键的概念是CPU1和CPU2看到的外设寄存器空间地址是不同的。这并不是说物理上存在两套外设而是芯片通过不同的总线路径和地址映射为两个CPU核心提供了访问同一组物理寄存器的不同“窗口”。以ePWM1模块为例从CPU1的角度看ePWM1的控制寄存器可能映射到地址0x0000 4000。从CPU2的角度看要访问同一个物理的ePWM1模块它可能需要使用一个完全不同的地址例如0x0004 4000。这个差异是由芯片的“外设帧”Peripheral Frame映射机制决定的。TI的文档中会明确区分“CPU1总线”和“CPU2总线”对应的外设地址。如果你直接使用为CPU1编写的驱动头文件这些头文件中的寄存器地址宏定义是基于CPU1视图的在CPU2上运行那么你的读写操作实际上会访问到错误的内存位置自然无法控制目标外设。注意务必使用对应CPU核心的器件支持库DSP库或头文件。TI通常会在库中提供针对不同CPU的编译选项或不同的头文件路径。在编译CPU2的工程时必须确保链接的是CPU2视角的寄存器定义。2.2 外设时钟与低功耗模式管理外设时钟的使能通过PCLKCRx寄存器也是一个关键点。在F2837xD上外设时钟模块PCM通常由CPU1主导控制。这意味着即使CPU2拥有对某个外设寄存器的写入权限如果该外设的时钟没有被CPU1使能它仍然是“沉睡”的无法工作。因此一个标准的启动流程是由CPU1完成系统初始化包括配置PLL、分配时钟、使能需要使用的所有外设模块的时钟。然后CPU1再通过核间通信IPC通知CPU2告知其哪些外设已经准备就绪。如果CPU2试图独立使能一个外设时钟很可能会因为权限问题而失败或者引发不可预知的系统行为。这种设计有助于集中管理功耗避免因CPU2的误操作而意外唤醒本应休眠的外设。2.3 外设中断的归属与路由中断是多核系统中最复杂的部分之一。F2837xD的外设中断请求可以路由到CPU1或CPU2的相应中断线上例如CPU1.Int1 到 CPU2.Int1。这个路由配置通常是通过“中断选择器”Interrupt Selector或类似的寄存器完成的。配置原则外设中断的服务者决定路由哪个CPU核心负责处理该外设产生的中断就应该将中断路由到那个CPU。例如如果ePWM1的周期中断由CPU2的中断服务程序ISR处理那么就必须将ePWM1的中断输出配置为连接到CPU2的INTx。避免竞争与重复处理绝对禁止将同一个外设中断同时路由到两个CPU这会导致不可预测的行为可能引发数据损坏或系统崩溃。CPU间中断IPC中断这是协调双核行动的关键机制。当CPU1为CPU2配置好一个外设后它可以触发一个IPC中断给CPU2通知其开始操作。反之当CPU2完成某项任务也可以通过IPC中断通知CPU1。3. 核间通信IPC机制详解IPC是F2837xD双核协同工作的“神经系统”。没有IPC两个核心就如同两个独立的单核芯片无法有效共享信息和同步任务。TI的C2000Ware库提供了强大的IPC驱动程序抽象了底层的硬件细节。3.1 IPC的核心硬件资源F2837xD提供了专用的硬件模块来支持高效的IPCIPC中断一组专门用于在CPU之间触发中断的信号。它们有最高的优先级和最低的延迟。IPC消息寄存器一组共享的32位寄存器例如32个两个CPU都可以读写。它们用于传递命令、状态或小规模数据。IPC标志一组共享的标志位可以用于简单的信号量或事件通知机制。3.2 使用IPC驱动配置外设的典型流程假设我们要让CPU2独立控制ePWM1模块流程如下系统级规划设计阶段确定ePWM1完全由CPU2管理配置、启动、中断处理。定义CPU1和CPU2之间的IPC协议例如使用IPC消息寄存器MSG1传递“启动PWM”命令使用MSG2传递“PWM故障”状态。CPU1端初始化代码// CPU1主函数中 void main(void) { // 1. 初始化系统时钟、PLL InitSysCtrl(); // 2. 使能ePWM1的模块时钟PCLKCR CpuSysRegs.PCLKCR2.bit.EPWM1 1; // 3. 初始化IPC驱动 IPCBootCPU2(C1C2_BROM_BOOTMODE_BOOT_FROM_FLASH, 0x80000); // 启动CPU2 IPC_init(); // 4. 将ePWM1模块的“所有权”通过IPC机制移交给CPU2可选但推荐 // 这通常意味着设置一个软件状态表明CPU1不会再主动配置该外设。 // 5. 发送IPC命令通知CPU2ePWM1已就绪可进行配置 IPC_sendCommand(CPU2_L_IPC_INT, IPC_CMD_EPWM1_READY, 0); // ... CPU1继续自己的其他初始化 }CPU2端初始化与响应代码// CPU2主函数中 void main(void) { // 1. 初始化CPU2本地的系统控制但注意不要重新配置全局时钟 InitSysCtrl(); // 这个函数需要是CPU2安全的版本或进行适当修改 // 2. 初始化IPC驱动注册命令回调函数 IPC_init(); IPC_registerCommand(IPC_CMD_EPWM1_READY, cpu2_epwm1_ready_callback); // 3. 等待CPU1的指令 while(1) { // 通常进入低功耗或执行其他后台任务等待IPC中断 // IPC中断会触发回调函数 } } // IPC命令回调函数 void cpu2_epwm1_ready_callback(uint32_t cmd, uint32_t data) { if(cmd IPC_CMD_EPWM1_READY) { // 4. 现在可以安全地配置ePWM1了 // 注意必须使用为CPU2编译的寄存器地址头文件 EPWM1_Config(); // 这个函数内部使用CPU2视角的EPWM1寄存器地址 // 5. 启动ePWM1 EPWM_start(EPWM1_BASE); // 6. 可选配置ePWM1中断并路由到CPU2 EPWM_registerInterrupt(EPWM1_BASE, my_epwm1_isr); // 需要配置PIE向量表CPU2有自己的PIE和中断选择器 } }3.3 IPC数据共享与同步对于需要共享的数据例如ADC采样结果由CPU1读取但需要给CPU2的算法使用不能简单地通过全局变量访问因为每个CPU有自己的缓存会导致数据一致性问题。正确的方法是使用共享内存区域在链接器命令文件.cmd中定义一段两个CPU都能访问的RAM区域例如CPU1_CPU2_SHARED_RAM。通过IPC进行同步CPU1将数据写入共享区域后触发一个IPC标志或中断通知CPU2。CPU2收到通知后从共享区域读取数据。在读取前后可能需要使用内存屏障指令如__asm(“ nop”)或特定的DSP库函数来确保缓存一致性或者直接使用memcpy函数其实现通常考虑了缓存操作。对于频繁更新的数据可以考虑使用双缓冲ping-pong buffer和IPC信号量来避免读写冲突。4. 在RTOS环境下的外设管理当项目复杂度提升引入实时操作系统如TI-RTOS FreeRTOS后外设的管理又增加了任务调度和资源互斥的维度。4.1 外设作为共享资源如果一个外设例如一个SPI总线需要被两个CPU上的多个任务访问那么它就是一个全局共享资源。必须严格防止访问冲突。解决方案使用互斥锁Mutex但锁的范畴需要仔细设计。方案A核间互斥锁。使用IPC机制实现一个跨核的互斥锁。当一个CPU上的任务要使用SPI时先尝试获取这个跨核锁。如果锁被另一个CPU上的任务持有则等待。这种方案保证了绝对的安全性但IPC通信会带来额外的延迟。// 伪代码示例 bool acquire_spi_lock(uint32_t timeout_ms) { return IPC_tryAcquireMutex(SPI_MUTEX_ID, timeout_ms); } void release_spi_lock() { IPC_releaseMutex(SPI_MUTEX_ID); }方案B外设所有权核内互斥锁。这是更高效也更常见的模式。在系统设计时就将某个外设的“所有权”明确分配给一个CPU核心。例如SPI总线归CPU1所有。那么只有CPU1上的任务可以访问SPI。如果CPU2上的任务需要SPI服务它必须通过IPC向CPU1发送请求由CPU1上的一个专用“服务任务”来代理完成SPI操作并将结果返回。在CPU1内部多个任务访问SPI时使用普通的RTOS互斥锁即可。// CPU2上的任务 void task_on_cpu2(void *pvParameters) { spi_transfer_request_t req; spi_transfer_response_t resp; // ... 填充请求数据 ... IPC_sendRequest(CPU1_L_IPC_INT, IPC_CMD_SPI_TRANSFER, (uint32_t)req); IPC_awaitResponse(resp, portMAX_DELAY); // ... 处理响应 ... } // CPU1上的SPI服务任务 void spi_service_task_on_cpu1(void *pvParameters) { while(1) { IPC_waitForCommand(IPC_CMD_SPI_TRANSFER, portMAX_DELAY); spi_transfer_request_t *req IPC_getRequestData(); xSemaphoreTake(spi_mutex, portMAX_DELAY); // 核内锁 // 执行实际的SPI传输 SPI_transfer(req, resp); xSemaphoreGive(spi_mutex); IPC_sendResponse(CPU2_L_IPC_INT, (uint32_t)resp); } }方案B的优势在于它将复杂的跨核同步问题简化为了一个清晰的“客户端-服务器”模型并且将核间通信次数降到最低一次请求一次响应性能更好也更易于维护和调试。4.2 中断服务程序ISR与RTOS的交互在RTOS中外设中断服务程序的设计需要特别小心保持ISR短小精悍ISR中只做最紧急的处理如清除中断标志、从硬件读取数据或发送一个信号量/任务通知。使用RTOS的ISR安全API在ISR中释放信号量、发送消息或通知任务时必须使用带FromISR后缀的API如xSemaphoreGiveFromISR,xTaskNotifyFromISR。中断优先级与RTOS内核中断确保外设中断的优先级低于RTOS用于任务调度如PendSV或IPC的中断优先级以防止高优先级外设中断长时间阻塞RTOS内核操作。5. 实战CPU2独立控制ePWM产生PWM波让我们通过一个完整的、可复现的示例将上述理论串联起来。目标是让CPU2独立配置并启动ePWM1模块产生一个频率1kHz、占空比50%的PWM波。5.1 硬件与软件准备硬件TI F28379D LaunchPad基于F2837xD系列。软件Code Composer Studio (CCS) C2000Ware库包含F2837xD支持库和IPC驱动。工程结构创建一个双核工程Dual Core Example模板会生成CPU1和CPU2两个独立的项目共享部分头文件和源文件。5.2 链接器命令文件.cmd配置这是确保双核能正确访问共享内存和各自私有内存的关键。TI的模板通常已经配置好。你需要检查确认CPU1项目的.cmd文件将程序和数据分配到以0x80000CPU1 RAM和0x90000CPU1 FLASH为主的区域。CPU2项目的.cmd文件将程序和数据分配到以0xA0000CPU2 RAM和0xB0000CPU2 FLASH为主的区域。定义了一段共享内存区例如SHARED_RAM : origin 0x00C000, length 0x001000并在两个项目的.cmd文件中都将其包含在内存映射中。5.3 CPU1主程序实现cpu1_main.c:#include “F28x_Project.h” #include “ipc.h” #define IPC_CMD_EPWM_START 0x1001 interrupt void cpu1_ipc_isr(void); void main(void) { // 初始化系统控制时钟、看门狗等 InitSysCtrl(); // 初始化GPIO将ePWM1A输出引脚配置为功能引脚 InitEPwm1Gpio(); // **关键使能ePWM1模块时钟** CpuSysRegs.PCLKCR2.bit.EPWM1 1; // 初始化PIE向量表使能中断 InitPieCtrl(); IER 0x0000; IFR 0x0000; InitPieVectTable(); // 将IPC中断服务函数挂载到PIE EALLOW; PieVectTable.IPC1_INT cpu1_ipc_isr; EDIS; // 初始化IPC驱动 IPC_init(); // 启动CPU2从FLASH的0xB0000地址开始执行 IPCBootCPU2(C1C2_BROM_BOOTMODE_BOOT_FROM_FLASH, 0xB0000); // 使能CPU1的IPC1中断用于接收CPU2的消息 IPC_enableInterrupt(CPU1_L_IPC_INT); IER | M_INT1; // 使能PIE组1中断包含IPC1 EINT; // 全局开中断 // 发送“启动ePWM”命令给CPU2 IPC_sendCommand(CPU2_L_IPC_INT, IPC_CMD_EPWM_START, 0); // CPU1进入主循环可以执行其他任务 for(;;) { asm(“ NOP”); } } // CPU1的IPC中断服务程序用于接收CPU2的回复或状态 interrupt void cpu1_ipc_isr(void) { uint32_t cmd IPC_getCommand(); // 可以处理来自CPU2的命令 IPC_clearCommand(); PieCtrlRegs.PIEACK.all PIEACK_GROUP1; }5.4 CPU2主程序与外设配置cpu2_main.c:#include “F28x_Project.h” #include “ipc.h” // **关键包含CPU2专用的外设寄存器头文件或使用条件编译** // 通常库头文件内部会根据预编译宏如 CPU2来切换地址定义 #define CPU2 #include “F2837xD_epwm.h” #define PWM_FREQ_HZ 1000 // 1 kHz #define SYSTEM_CLOCK_MHZ 200 // 假设系统时钟200MHz #define EPWM_TIMER_TBPRD (SYSTEM_CLOCK_MHZ * 1000000 / (PWM_FREQ_HZ * 2)) // 对称PWM周期寄存器值 void EPWM1_Config(void); interrupt void epwm1_isr(void); void cpu2_ipc_isr(void); void main(void) { // 初始化CPU2的系统控制注意不要重新配置PLL和主时钟 // 通常有一个专门为CPU2准备的初始化函数它不会触碰全局时钟设置 InitSysCtrl(); // 需要确认这个函数在CPU2上下文下的行为是安全的 // 初始化PIE InitPieCtrl(); IER 0x0000; IFR 0x0000; InitPieVectTable(); // 挂载中断 EALLOW; PieVectTable.EPWM1_INT epwm1_isr; // ePWM1中断 PieVectTable.IPC2_INT cpu2_ipc_isr; // CPU2的IPC中断 EDIS; // 初始化IPC驱动 IPC_init(); IPC_registerCommand(IPC_CMD_EPWM_START, EPWM1_Config); // 使能CPU2的IPC2中断 IPC_enableInterrupt(CPU2_L_IPC_INT); IER | M_INT1; // 使能PIE组1 EINT; // CPU2主循环等待IPC命令 for(;;) { asm(“ NOP”); } } // IPC中断服务程序用于接收命令 interrupt void cpu2_ipc_isr(void) { uint32_t cmd IPC_getCommand(); // IPC驱动会自动调用注册的回调函数 IPC_clearCommand(); PieCtrlRegs.PIEACK.all PIEACK_GROUP1; } // ePWM1配置函数由IPC回调触发 void EPWM1_Config(void) { // 配置为递增-递减计数模式产生对称PWM EPwm1Regs.TBCTL.bit.CTRMODE TB_COUNT_UPDOWN; EPwm1Regs.TBPRD EPWM_TIMER_TBPRD; // 设置周期 EPwm1Regs.TBPHS.half.TBPHS 0; // 相位清零 EPwm1Regs.TBCTL.bit.PHSEN TB_DISABLE; // 禁止相位加载 EPwm1Regs.TBCTL.bit.HSPCLKDIV TB_DIV1; EPwm1Regs.TBCTL.bit.CLKDIV TB_DIV1; // 设置比较器A产生50%占空比 EPwm1Regs.CMPA.half.CMPA EPWM_TIMER_TBPRD / 2; // 配置动作限定器AQ EPwm1Regs.AQCTLA.bit.CAU AQ_SET; // 计数值等于CMPA时向上计数置高 EPwm1Regs.AQCTLA.bit.CAD AQ_CLEAR; // 计数值等于CMPA时向下计数置低 // 可选使能周期中断 EPwm1Regs.ETSEL.bit.INTSEL ET_CTR_ZERO; // 在计数器0时触发中断 EPwm1Regs.ETSEL.bit.INTEN 1; // 使能中断 EPwm1Regs.ETPS.bit.INTPRD ET_1ST; // 每个事件产生一次中断 // 将ePWM1中断路由到CPU2的INT3假设 // 这通常在PIE配置和中断选择寄存器中完成代码略复杂此处示意 EALLOW; // InputXBarRegs.INPUT7SELECT ... ; // 将ePWM1_INT连接到某个输入 // XIntruptRegs.XINT1CR.bit.... // 配置XINT1 // PieCtrlRegs.PIEIER3.bit.INTx 1; // 使能PIE组3的相应中断 EDIS; // 启动ePWM1计时器 EPwm1Regs.TBCTL.bit.CTRMODE TB_COUNT_UPDOWN; // 重新使能计数 // 通知CPU1配置完成可选 IPC_sendCommand(CPU1_L_IPC_INT, IPC_CMD_EPWM_CONFIG_DONE, 0); } // ePWM1中断服务程序在CPU2上执行 interrupt void epwm1_isr(void) { // 处理中断例如更新占空比、清除标志等 EPwm1Regs.ETCLR.bit.INT 1; // 清除中断标志 PieCtrlRegs.PIEACK.all PIEACK_GROUP3; // 应答PIE组3中断 }5.5 编译、加载与调试分别编译在CCS中分别编译CPU1和CPU2工程。加载程序首先通过JTAG连接板子。将CPU1的.out文件加载到CPU1。将CPU2的.out文件加载到CPU2。注意在加载CPU2程序时CCS可能会询问加载地址务必按照.cmd文件中的定义指定到CPU2的FLASH地址如0xB0000。调试技巧视图切换在CCS的调试视图中可以方便地在CPU1和CPU2的上下文中切换。确保你在查看寄存器或反汇编时处于正确的CPU上下文。共享变量观察要观察共享内存区的变量需要将其添加到表达式窗口并注意内存地址是否属于共享区域。IPC状态监控TI的IPC驱动可能提供状态查看函数或者你可以直接查看IPC相关的硬件寄存器如IPC标志寄存器来监控核间通信是否正常。6. 常见问题与深度排查指南在实际开发中你几乎一定会遇到CPU2无法控制外设的情况。下面是一个系统化的排查清单。6.1 问题现象与排查步骤问题现象可能原因排查步骤与解决方案CPU2写外设寄存器无任何效果1. 使用了错误的寄存器地址CPU1视图。2. 外设时钟未使能。3. 外设模块被复位或处于低功耗状态。1.检查头文件确认编译CPU2工程时预定义了CPU2宏确保寄存器地址是CPU2视图。最直接的方法是在调试器里查看你写入的寄存器地址并与数据手册中CPU2的地址映射表对比。2.检查时钟在CPU1初始化代码中确认PCLKCRx寄存器中对应外设的位已被置1。可以在CPU1运行后暂停系统查看该寄存器的值。3.检查低功耗模式确认PCLKCRx和LPWRCR等寄存器没有将该外设置于禁用或休眠状态。CPU2无法进入外设中断1. 中断未路由到CPU2。2. CPU2的PIE或IER未正确使能。3. 中断标志未清除或优先级问题。4. IPC中断抢占导致外设中断被延迟。1.检查中断路由查阅数据手册的“Interrupt”章节找到配置外设中断输出目的CPU的寄存器如XINT1CRINTERRUPT_MUX等确认其指向CPU2。2.检查CPU2中断配置单步调试CPU2代码确认PieCtrlRegs.PIEIERx和IER寄存器已正确使能目标中断。3.检查中断服务程序确认ISR中清除了外设和PIE的中断标志并正确进行了PIEACK应答。4.检查中断优先级如果使用了RTOS确保外设中断优先级低于用于任务切换的SysTick或PendSV中断。双核访问共享外设导致数据损坏或系统锁死1. 无同步机制发生竞态条件。2. 缓存一致性问题。1.引入互斥机制严格按照第4章所述采用“外设所有权”或“核间互斥锁”方案。对于简单外设也可以设计为只由单个CPU在单个时刻访问。2.处理缓存对于共享内存数据在CPU1写入后执行数据缓存写回并无效化CPU2缓存的操作。TI的memcpy函数通常已处理但直接访问共享结构体时需调用CACHE_INVALIDATE等库函数。CPU2程序无法加载或运行1. 链接地址错误。2. CPU1未正确启动CPU2。3. CPU2的向量表地址错误。1.检查.cmd文件确认CPU2的程序段.text和数据段正确链接到了CPU2的专属RAM/FLASH区域。2.检查CPU1启动代码确认IPCBootCPU2函数的参数正确特别是CPU2程序的入口地址。3.检查CPU2向量表确认CPU2的PIEVECTTABLE初始化正确其基地址位于CPU2可访问的内存中。6.2 高级调试技巧寄存器实时查看CCS的“Registers”视图可以查看所有外设寄存器。务必在正确的CPU上下文中查看。如果你在CPU2的调试会话中看到的ePWM1寄存器值全是0或默认值但实际PWM有输出那很可能你看的是CPU1的映射地址。尝试切换到CPU1的上下文再看或者直接使用内存浏览器Memory Browser输入CPU2视角的绝对地址查看。IPC通信可视化可以编写简单的调试函数将IPC消息、标志的状态通过某个串口打印出来或者在共享内存中设置一个“调试日志区”供两个CPU共同写入再通过一个核心统一输出。使用硬件断点和观察点在共享内存的关键变量上设置数据观察点Data Watchpoint当变量被任一CPU修改时触发断点是追踪竞态条件的利器。分阶段验证静默测试先让CPU2只配置外设但不启动如不使能PWM输出。通过读取配置寄存器确认写入成功。CPU1代理测试让CPU1先配置并运行外设确认硬件和基础驱动是好的。然后改为由CPU1配置但中断路由到CPU2处理。完整移交测试最后实现完整的“CPU1初始化时钟 - IPC通知 - CPU2配置并运行”流程。7. 总结与最佳实践提炼经过以上深入剖析我们可以将CPU2使用外设的核心要点提炼为以下最佳实践清单这适用于绝大多数多核MCU场景明确架构区分视图首要任务是吃透芯片手册的内存映射图彻底分清CPU1和CPU2的外设访问地址空间。编译时使用正确的宏定义来区分。集中管理分步启动系统关键资源时钟、电源、复位应由一个主核通常是CPU1统一管理。主核完成全局初始化后再通过IPC引导和通知从核CPU2启动。设计清晰的IPC协议在项目初期就定义好核间通信的命令字、数据格式和流程。避免使用“魔数”使用枚举或宏定义提高代码可读性。外设所有权单一化尽可能为每个共享外设指定一个“所有者”CPU。该CPU负责该外设的所有配置和运行时控制。其他CPU通过向所有者发送IPC请求来间接使用外设。这是减少复杂度和避免竞态的最有效方法。中断路由匹配服务者外设中断必须路由到负责处理该中断的CPU。仔细规划中断分配避免冲突和遗漏。共享数据必先同步任何在双核间共享的变量或缓冲区都必须通过IPC机制标志、消息、信号量进行同步并考虑缓存一致性问题。善用RTOS抽象如果使用RTOS充分利用其提供的任务、队列、信号量、互斥锁等机制来构建清晰的双核应用架构。将IPC机制封装成RTOS友好的服务。调试时保持上下文清醒在使用调试器时时刻意识到当前是在哪个CPU的上下文中查看内存、寄存器和变量。错误的上下文是导致迷惑的常见根源。多核MCU的开发从单核的“直接控制”思维转变为了“分布式协同”思维。将F2837xD的双核想象成一个小型网络中的两个节点IPC是它们之间的网络协议外设是网络中的共享设备。按照这个思路去设计软件架构很多问题就会迎刃而解。记住清晰的协议和明确的职责划分是成功驾驭多核系统的关键。