1. 双核IPC机制的设计哲学与核心价值
在嵌入式系统,尤其是工业控制、电机驱动和数字电源这类对实时性要求极高的领域,单核处理器的性能瓶颈日益凸显。为了应对更复杂的算法和更快的控制环路,多核架构成为了必然选择。德州仪器(TI)的TMS320F28P65x系列微控制器,作为C2000™实时控制MCU家族的新成员,其双核C28x架构正是这一趋势的产物。然而,将两个强大的CPU核心塞进一颗芯片只是第一步,如何让它们高效、有序、无冲突地协同工作,才是决定整个系统性能与可靠性的关键。这就是处理器间通信(IPC)机制存在的根本意义。
IPC不是简单的“发个消息”,它是一套完整的硬件辅助协同工作协议。你可以把它想象成在一个高度自动化的工厂里,有两个并行的生产车间(CPU1和CPU2)。它们共享原材料仓库(共享内存)、共用一套精密的生产设备(如Flash控制器)。如果没有一套明确的通信和调度规则,两个车间可能会同时去抢同一批原料,或者同时操作同一台设备,导致生产停滞甚至设备损坏。IPC机制,就是为这两个车间建立的一套“内部电话系统”、“任务看板”和“设备使用预约单”。
TMS320F28P65x的IPC硬件模块,其设计精髓在于将通信过程硬件化、原子化。它提供了一组映射到各自CPU内存空间的专用寄存器。对于CPU1来说,它看到的是CPU1TOCPU2_IPC_REGS_CPU1VIEW这一组寄存器;相应地,CPU2也有自己视角的寄存器组。这种设计使得每个CPU都可以像操作自己的外设一样去操作IPC,无需复杂的软件锁或总线仲裁,极大地降低了通信延迟和软件复杂度。其核心价值体现在三个方面:确定性、低开销和安全性。硬件事件标志的置位与清除是原子操作,保证了信号传递的确定性;专用的命令/数据寄存器避免了数据拷贝,实现了低开销的数据交换;而像FLASHCTLSEM这样的硬件信号量,则从硬件层面杜绝了对关键共享资源(如Flash编程器)的竞争访问,确保了系统安全。
2. IPC寄存器全景解析:从事件标志到数据通道
要驾驭双核通信,首先必须对IPC的“工具箱”——寄存器组——有一个全局的认识。CPU1TOCPU2_IPC_REGS_CPU1VIEW这一组寄存器,是CPU1用于与CPU2通信的专属接口。它们功能清晰,可以分为四大类:事件标志管理类、状态查询类、数据通信类和系统控制类。理解这个分类,是正确使用它们的前提。
事件标志管理类是IPC的“信号枪”。它包括:
CPU1TOCPU2IPCSET(偏移 4h):置位寄存器。CPU1写1到某个bit,即可在CPU2那边对应的CPU2TOCPU1IPCFLG标志位上产生一个事件。这就像CPU1按下了一个给CPU2的“呼叫按钮”。CPU1TOCPU2IPCCLR(偏移 6h):清除寄存器。CPU1写1到某个bit,可以清除自己这边的CPU1TOCPU2IPCFLG标志位。注意,它清除的是本地视图的标志,用于表示“我已处理完这个来自CPU2的事件”。清除对方标志需使用ACK寄存器。CPU1TOCPU2IPCACK(偏移 0h):应答寄存器。这是一个非常关键且容易混淆的寄存器。CPU1写1到某个bit,目的是清除CPU2那边的CPU2TOCPU1IPCFLG标志位。当CPU1收到了CPU2发来的事件(通过CPU2TOCPU1IPCSTS感知到),并处理完毕后,就通过写此寄存器来告知CPU2:“你发来的信号我已收到并处理,你可以继续了”。
状态查询类是IPC的“监控面板”。
CPU2TOCPU1IPCSTS(偏移 2h):状态寄存器。这是一个只读寄存器,反映了CPU2那边CPU2TOCPU1IPCFLG标志位的实时状态。CPU1通过轮询或中断(后文详述)检查这个寄存器,就能知道CPU2是否向自己发送了事件。CPU1TOCPU2IPCFLG(偏移 8h):本地标志寄存器。反映了由CPU1发出、尚未被CPU2应答的事件标志状态。通常CPU1通过IPCSET置位后,可以读此寄存器确认是否置位成功。
数据通信类是IPC的“货运通道”。它们构成了一个典型的“命令-地址-数据”通信模型,非常适合进行远程函数调用或数据块传输:
CPU1TOCPU2IPCSENDCOM(偏移 10h):命令寄存器。CPU1可以写入一个32位的命令码,比如0xA001代表“读取ADC结果”,0xB002代表“启动PWM模块”。CPU1TOCPU2IPCSENDADDR(偏移 12h):地址寄存器。配合命令使用,可以指定目标内存地址、外设寄存器地址等。CPU1TOCPU2IPCSENDDATA(偏移 14h):数据寄存器。用于发送伴随命令的32位数据。CPU2TOCPU1IPCRECVCOM/ADDR/DATA(偏移 18h/1Ah/1Ch):接收镜像寄存器。在CPU2的地址空间,有一组对应的IPCSEND寄存器。当CPU1写入自己的SEND寄存器时,CPU2对应的RECV寄存器会自动、同步地更新为相同值。CPU2无需主动去“读”CPU1的内存,直接读自己的RECV寄存器即可。这实现了数据的零拷贝传递。CPU2TOCPU1IPCREPLY(偏移 16h) &CPU1TOCPU2IPCREPLY(偏移 1Eh):应答寄存器。用于命令执行的返回值传递。例如,CPU1发送一个“读取某寄存器”的命令和地址,CPU2执行后,将读到的数据写入CPU2TOCPU1IPCREPLY,CPU1再从CPU1TOCPU2IPCREPLY(注意,这是CPU1视角下的回复寄存器,实际是CPU2写入的)中读取结果。
系统控制类用于底层的核间协调。
IPCCOUNTERL/H(偏移 Ch/Eh):64位时间戳计数器。由系统时钟驱动,为双核事件提供同步的时间基准,常用于性能分析和调试。CPU2TOCPU1IPCBOOTSTS(偏移 20h) &CPU1TOCPU2IPCBOOTMODE(偏移 22h):启动状态/模式寄存器。用于双核启动过程中的信息传递,例如从核(CPU2)告知主核(CPU1)自己的启动状态,或主核告知从核启动模式。FLASHCTLSEM(偏移 24h):Flash控制器信号量寄存器。这是共享资源保护的基石。它通过一个带密钥(KEY)的硬件信号量机制,确保同一时间只有一个CPU能对Flash进行编程或擦除操作,防止硬件冲突。
核心要点:务必区分“方向”。所有寄存器名中的
CPU1TOCPU2或CPU2TOCPU1,都是从当前CPU的视角出发的。CPU1TOCPU2IPCSET意思是“CPU1用来发给CPU2的置位寄存器”。而在CPU2那边,它看到的是一组名为CPU2TOCPU1_IPC_REGS_CPU2VIEW的寄存器,其中会有一个CPU2TOCPU1IPCSET,用于向CPU1发送事件。两组寄存器在物理上是耦合的,但地址映射不同,这构成了双向通信的基础。
3. 事件标志通信:从硬件操作到软件协议
事件标志是IPC中最基础、最常用的通信方式,其本质是一个由硬件维护的、可触发中断的全局状态位。我们以“CPU1通知CPU2处理数据”这一典型场景,拆解其完整流程。
3.1 单向事件通知流程
假设我们定义IPC事件标志位IPC3用于通知CPU2:“ADC转换已完成,数据已就绪”。
步骤1:CPU1发起事件(发送方)CPU1需要设置事件标志。它不能直接写IPCFLG寄存器,而是通过写IPCSET寄存器来间接设置。
// CPU1 代码 // 设置 IPC3 事件标志,通知CPU2 HWREG(IPC_BASE + IPC_O_CPU1TOCPU2IPCSET) = (1 << 3); // 向BIT3写入1这条语句执行了一个内存写操作。硬件检测到对IPCSET[3]的写1操作后,会自动且原子地将CPU1TOCPU2IPCFLG[3]和CPU2视角下的CPU2TOCPU1IPCFLG[3]同时置1。这是一个关键点:一次写入,两个核心的标志位同时更新,保证了状态的严格同步。
步骤2��CPU2检测事件(接收方)CPU2如何知道事件发生了?有两种方式:轮询和中断。
- 轮询方式:CPU2周期性地读取自己的状态寄存器
CPU2TOCPU1IPCSTS。
轮询简单,但占用CPU资源,实时性取决于轮询频率。// CPU2 代码 (轮询) if (HWREG(IPC_BASE_CPU2 + IPC_O_CPU2TOCPU1IPCSTS) & (1 << 3)) { // IPC3事件已发生,处理数据 process_adc_data(); } - 中断方式:这是更高效的方式。根据手册,IPC事件标志0-7可以触发中断。我们需要在CPU2上配置IPC中断。
- 使能PIE中断:在CPU2的PIE(外设中断扩展)模块中,找到IPC中断对应的向量(例如
INTx.y),并启用它。 - 使能CPU级中断:启用CPU2的INTM全局中断,并确保IER中对应中断级别已使能。
- 编写ISR:在中断服务例程中,检查
CPU2TOCPU1IPCSTS寄存器,确定是哪个IPC位触发的中断,并进行处理。
// CPU2 代码 (中断服务例程) __interrupt void cpu2_ipc_isr(void) { uint32_t ipc_status = HWREG(IPC_BASE_CPU2 + IPC_O_CPU2TOCPU1IPCSTS); if (ipc_status & (1 << 3)) { process_adc_data(); // 处理ADC数据 // 重要:清除IPC中断标志!通过写ACK寄存器清除对方标志。 HWREG(IPC_BASE_CPU2 + IPC_O_CPU2TOCPU1IPCACK) = (1 << 3); } // ... 检查其他IPC位 // 必须应答PIE中断 PieCtrlRegs.PIEACK.all = PIEACK_GROUPx; // x为对应的组 } - 使能PIE中断:在CPU2的PIE(外设中断扩展)模块中,找到IPC中断对应的向量(例如
步骤3:CPU2确认处理完成(接收方应答)处理完事件后,CPU2必须通知CPU1“事情办完了”,以便CPU1可以发送下一个通知。这是通过写自己的CPU2TOCPU1IPCACK寄存器实现的。如上述ISR代码所示,写IPCACK[3]为1,会清除CPU1那边的CPU1TOCPU2IPCFLG[3]标志位。
步骤4:CPU1确认事件完成(发送方收尾)CPU1可以通过轮询自己的CPU1TOCPU2IPCFLG寄存器,来等待CPU2的应答。当发现IPCFLG[3]被CPU2清除后,就知道CPU2已处理完毕。
// CPU1 代码 // 等待CPU2处理完成(轮询方式) while (HWREG(IPC_BASE + IPC_O_CPU1TOCPU2IPCFLG) & (1 << 3)) { // 等待,或可以执行其他任务 } // 标志位已清除,可以准备下一次ADC转换和通知3.2 双向握手与数据传递流程
单纯的事件通知往往不够,通常需要伴随数据传递。这时就需要结合数据通信类寄存器,实现一个“发送命令-等待应答”的握手协议。
场景:CPU1让CPU2计算一段数据的CRC32,数据存放在共享内存SharedBuffer中,长度为DataLength。
步骤1:CPU1发送命令与数据
// CPU1 代码 // 1. 将数据和参数准备好(假设已放入共享内存) // 2. 通过IPC寄存器发送命令和元数据 HWREG(IPC_BASE + IPC_O_CPU1TOCPU2IPCSENDCOM) = COMMAND_CALC_CRC32; // 命令字,如0xC001 HWREG(IPC_BASE + IPC_O_CPU1TOCPU2IPCSENDADDR) = (uint32_t)SharedBuffer; // 数据地址 HWREG(IPC_BASE + IPC_O_CPU1TOCPU2IPCSENDDATA) = DataLength; // 数据长度 // 3. 触发事件,通知CPU2去读取命令寄存器 HWREG(IPC_BASE + IPC_O_CPU1TOCPU2IPCSET) = (1 << IPC_CMD_EVENT_BIT); // 假设用IPC4传递命令事件步骤2:CPU2响应命令CPU2被IPC4中断唤醒,在ISR中:
__interrupt void cpu2_ipc_isr(void) { uint32_t status = HWREG(IPC_BASE_CPU2 + IPC_O_CPU2TOCPU1IPCSTS); if (status & (1 << IPC_CMD_EVENT_BIT)) { // 1. 读取命令和数据 uint32_t command = HWREG(IPC_BASE_CPU2 + IPC_O_CPU2TOCPU1IPCRECVCOM); uint32_t address = HWREG(IPC_BASE_CPU2 + IPC_O_CPU2TOCPU1IPCRECVADDR); uint32_t length = HWREG(IPC_BASE_CPU2 + IPC_O_CPU2TOCPU1IPCRECVDATA); // 2. 解析并执行命令 if (command == COMMAND_CALC_CRC32) { uint32_t crc_result = calculate_crc32((void*)address, length); // 3. 将结果写回回复寄存器 HWREG(IPC_BASE_CPU2 + IPC_O_CPU2TOCPU1IPCREPLY) = crc_result; // 4. 可选:触发一个“完成”事件给CPU1,使用另一个IPC位,如IPC5 HWREG(IPC_BASE_CPU2 + IPC_O_CPU2TOCPU1IPCSET) = (1 << IPC_RESP_EVENT_BIT); } // 5. 清除命令事件标志(向CPU1发送ACK) HWREG(IPC_BASE_CPU2 + IPC_O_CPU2TOCPU1IPCACK) = (1 << IPC_CMD_EVENT_BIT); } // ... 其他中断处理 }步骤3:CPU1获取结果CPU1在发送命令后,可以轮询或通过中断(如果配置了IPC5中断)等待CPU2的完成事件。
// CPU1 代码 (轮询等待结果) // 先等待命令被应答(IPC4标志清除) while (HWREG(IPC_BASE + IPC_O_CPU1TOCPU2IPCFLG) & (1 << IPC_CMD_EVENT_BIT)); // 然后等待结果事件(IPC5标志置位) while (!(HWREG(IPC_BASE + IPC_O_CPU2TOCPU1IPCSTS) & (1 << IPC_RESP_EVENT_BIT))); // 读取结果 uint32_t calculated_crc = HWREG(IPC_BASE + IPC_O_CPU1TOCPU2IPCREPLY); // 清除结果事件标志,完成整个握手 HWREG(IPC_BASE + IPC_O_CPU1TOCPU2IPCACK) = (1 << IPC_RESP_EVENT_BIT);实操心得:在设计双核通信协议时,为不同的任务分配不同的IPC事件位至关重要。例如,IPC0-7用于高优先级、可中断的事件(如紧急故障信号),IPC8-31用于低优先级、轮询状态的事件(如周期性数据就绪标志)。避免多个不相关的任务共用同一个事件位,否则状态管理会变得极其混乱。同时,发送-应答-清除这个三部曲必须严格遵守,否则会导致事件丢失或CPU死锁。
4. 高级功能与资源保护:信号量与时间戳
4.1 Flash控制器信号量(FLASHCTLSEM)的精密操作
在多核系统中,对Flash的编程和擦除操作是典型的“临界区”访问,必须严格串行化。FLASHCTLSEM寄存器提供了硬件级的互斥锁机制。它的操作比普通IPC寄存器更复杂,因为它引入了写保护密钥(KEY)。
寄存器结构解析:
- 位[31:16] - KEY:密钥域。任何对信号量位
SEM[1:0]的写操作,必须同时在KEY域写入0x5A5A,否则写操作被忽略。这防止了软件误操作意外修改信号量。 - 位[1:0] - SEM:信号量状态位。
00:只读状态。CPU1控制Flash泵,但CPU2可随时夺取控制权。可视为“空闲但偏向CPU1”状态。01:CPU1独占控制Flash控制器和此信号量寄存器。只有CPU1能将其改回00。10:CPU2独占控制Flash控制器和此信号量寄存器。只有CPU2能将其改回00。11:只读状态。同00,但为保留状态。
获取Flash控制权的标准流程(以CPU1为例):
// CPU1 尝试获取Flash控制权 uint32_t sem_key = 0x5A5A0001; // KEY=0x5A5A, SEM=01 (CPU1独占) HWREG(FLASH_CTRL_BASE + FLASH_O_FLASHCTLSEM) = sem_key; // 必须检查是否获取成功,因为CPU2可能同时也在争夺 uint32_t current_sem = HWREG(FLASH_CTRL_BASE + FLASH_O_FLASHCTLSEM) & 0x0003; if (current_sem != 0x0001) { // 获取失败,可能被CPU2抢占(状态为10),或发生错误 // 应等待或执行错误处理流程 return ERROR_SEM_BUSY; } // 获取成功,现在可以安全地进行Flash编程/擦除操作 flash_program_sector(...); // 操作完成后,释放控制权 sem_key = 0x5A5A0000; // KEY=0x5A5A, SEM=00 (释放) HWREG(FLASH_CTRL_BASE + FLASH_O_FLASHCTLSEM) = sem_key;严重警告:
FLASHCTLSEM的说明中有一条关键注释:“When CPU2SYSRSn asserted, user has to poll for FLASHCTLSEM value 0x0 and then program the FLASHCTLSEM depends on subsequent ownership requirement.”这意味着,当CPU2被复位(复位信号有效)时,软件必须轮询直到FLASHCTLSEM值变为0,然��才能根据后续需求重新编程此寄存器。如果不这样做,可能会因为硬件状态未同步而导致不可预知的行为。这是一个极易踩坑的细节,务必在双核启动序列中加入对此状态的检查。
4.2 64位时间戳计数器(IPCCOUNTERL/H)的应用
IPCCOUNTERL和IPCCOUNTERH组成了一个由PLLSYSCLK驱动的64位自由运行计数器。这个计数器为双核系统提供了高精度、同步的时间基准。
主要用途:
- 性能剖析:在代码关键段前后读取时间戳,计算执行周期数,精确评估双核任务执行时间。
- 事件时间对齐:当某个事件在双核中发生时,分别记录时间戳,可以事后分析两个核心感知到事件的先后顺序和延迟。
- 软定时:可以基于此计数器实现高精度的软件定时器,比依赖SysTick等有更宽的计数范围。
读取64位计数器的注意事项: 由于需要两次32位读操作,而计数器在不停运行,直接先后读取高、低32位可能会在两次读取之间发生进位,导致读出的高低位不匹配(例如,低32位从0xFFFFFFFF翻转到0x00000000,而高32位还没来得及读取)。必须实现回读保护机制。
uint64_t read_ipc_timestamp(void) { volatile uint32_t high1, low, high2; uint64_t timestamp; do { high1 = HWREG(IPC_BASE + IPC_O_IPCCOUNTERH); // 第一次读高32位 low = HWREG(IPC_BASE + IPC_O_IPCCOUNTERL); // 读低32位 high2 = HWREG(IPC_BASE + IPC_O_IPCCOUNTERH); // 第二次读高32位 } while (high1 != high2); // 如果两次高32位不同,说明发生了进位,需要重读 timestamp = ((uint64_t)high1 << 32) | low; return timestamp; }5. 实战中的陷阱与最佳实践
在真实的双核项目开发中,仅仅理解寄存器手册是远远不够的。下面是我在多个TMS320F28P65x项目中总结出的血泪教训和最佳实践。
5.1 中断与轮询的抉择
- 必须使用中断的场景:对事件响应延迟有严格要求的任务。例如,CPU1检测到过流故障,需要通过IPC立即通知CPU2关闭PWM。此时必须为对应的IPC事件位(如IPC0)使能中断,并将其中断优先级设置为最高级别之一。
- 可以使用轮询的场景:周期性、非紧急的状态同步。例如,CPU1每1ms计算一次控制律,并将新的占空比参数通过共享内存传递,然后置位IPC8通知CPU2。CPU2在主循环中轮询IPC8即可,无需中断开销。
- 混合模式:一种高效的架构是,将IPC0-7用于关键中断事件,IPC8-31用于状态标志轮询。在中断服务例程(ISR)中,只做最少的标志设置和内存指针交换,将耗时的数据处理移到后台循环中,避免长时间关中断影响系统实时性。
5.2 共享内存的管理艺术
IPC寄存器适合传递命令和小数据(32位)。大数据块(如数组、结构体)必须通过共享内存传递。管理共享内存是双核编程的核心挑战。
- 地址对齐与一致性:确保共享内存区域在链接器命令文件(.cmd)中为两个核心正确映射,并且起始地址是32位对齐的。使用
#pragma DATA_SECTION将变量定位到共享内存段。 - 数据一致性:C28x核心有数据缓存。当CPU1写数据到共享内存后,必须在置位IPC事件标志之前,执行数据缓存写回并无效化操作,以确保CPU2看到的是最新数据。同样,CPU2在读取共享数据后,如果会修改它,也需要考虑缓存一致性。
// CPU1 写入共享数据后 __asm(" CACHE_WB_ALL"); // 写回所有缓存行(具体指令取决于编译器) __asm(" CACHE_INV_ALL"); // 无效化所有缓存行(确保后续操作从内存读) // 然后再触发IPC事件 HWREG(IPC_BASE + IPC_O_CPU1TOCPU2IPCSET) = (1 << DATA_READY_BIT); - 使用软件“乒乓”缓冲区:对于高速数据流,可以设置两个共享内存缓冲区。当CPU1写满缓冲区A后,通过IPC通知CPU2处理A,同时CPU1开始写缓冲区B。CPU2处理完A后,通知CPU1。这避免了读写竞争,实现了无锁通信。
5.3 启动顺序与初始化的交响乐
双核系统的启动顺序至关重要,错误的初始化顺序是死锁和随机故障的主要根源。
- 主核(CPU1)先启动:通常CPU1从Flash启动,完成系统时钟、PLL、外设的初始化。
- 初始化共享资源和IPC:CPU1在释放CPU2之前,应初始化所有共享内存区域为已知状态(如清零),并配置好IPC中断向量表(虽然主要在CPU2侧使用,但相关配置寄存器可能需提前设置)。
- 配置从核(CPU2)启动:CPU1通过
CPU1TOCPU2IPCBOOTMODE寄存器向CPU2传递启动模式,然后释放CPU2复位(具体操作涉及系统控制寄存器,如CPUSYSRESET)。 - 从核初始化:CPU2从自己的入口点(可能是RAM或Flash特定区域)启动,读取
IPCBOOTMODE,初始化自己的局部外设和IPC中断。 - 握手同步:CPU2初始化完成后,向
CPU2TOCPU1IPCBOOTSTS写入就绪状态。CPU1轮询此寄存器,确认CPU2已就绪后,双核通信通道正式建立。 - 开始应用任务:此后,双方才能开始基于IPC和共享内存的正常应用逻辑通信。
5.4 调试技巧:让问题无处遁形
双核调试比单核复杂得多。以下技巧能帮你快速定位问题:
- 善用IPC计数器:在每次IPC事件发生时,读取
IPCCOUNTERL/H作为时间戳,连同事件ID一起存入一个循环日志缓冲区。当通信异常时,分析这个日志可以清晰看到事件顺序和延迟。 - 设计“心跳”机制:让双核通过一个专用的IPC事件位定期(如每10ms)互相“打招呼”。监控线程检查心跳是否按时到达。一旦心跳丢失,可以触发安全状态(如故障停车)并记录错误代码,这能有效诊断是哪个核心卡死或通信链路断裂。
- 静态代码分析:严格检查所有对IPC寄存器和共享内存的访问,确保没有竞态条件。使用
volatile关键字声明所有被双核访问的全局变量和硬件寄存器指针。 - 仿真器同步调试:如果条件允许,使用支持双核同步调试的仿真器(如TI的XDS系列)。可以同时暂停两个核心,查看各自寄存器和内存状态,这是定位死锁问题的最强武器。
6. 一个完整的案例:双核电机控制任务划分
让我们以一个典型的永磁同步电机(PMSM)磁场定向控制(FOC)为例,看如何利用TMS320F28P65x的IPC机制进行任务划分。
CPU1(主核):负责高速控制环路
- 任务:执行电流环、速度环计算(通常中断频率在10-100kHz)。
- 外设:直接控制ADC采样、PWM生成(ePWM)、编码器接口(eQEP)。
- 与CPU2的通信:
- 每次电流环计算完成后,将计算得到的
Iq_ref,Id_ref写入共享内存。 - 置位IPC事件标志
IPC1,通知CPU2“新的电流指令已就绪”。 - 等待CPU2通过IPC事件
IPC2回复“状态已更新”后,进行下一周期计算。 - 同时,CPU1轮询CPU2通过共享内存设置的系统状态字(如启动/停止命令、参数修改请求)。
- 每次电流环计算完成后,将计算得到的
CPU2(从核):负责低速管理与通信
- 任务:执行速度/位置规划、故障保护逻辑、通信协议栈(如EtherCAT, CANopen)、人机接口。
- 外设:管理UART, SPI, CAN, EMAC等通信接口。
- 与CPU1的通信:
- 响应IPC1中断,从共享内存读取电流指令,并可能结合速度规划算法进行修正(可选)。
- 将修正后的指令或直接原样写回共享内存的另一位置(避免读写冲突)。
- 置位IPC事件
IPC2通知CPU1读取。 - 处理来自通信接口���指令,更新系统状态字到共享内存。
- 当需要修改PWM频率、PID参数时,通过
IPCSENDCOM发送命令给CPU1,并等待IPCREPLY确认。
共享资源保护:
- Flash参数存储:当CPU2需要通过CAN更新电机参数并保存到Flash时,必须先通过
FLASHCTLSEM获取独占权,然后执行擦写操作。在此期间,CPU1的Flash访问(如果有)会被阻塞。 - 共享外设:某些外设可能被双核配置。约定由CPU1负责初始化(如PWM),CPU2只读其状态寄存器。或者通过IPC命令请求CPU1进行配置更改。
- Flash参数存储:当CPU2需要通过CAN更新电机参数并保存到Flash时,必须先通过
通过这样的架构,CPU1可以专注于对时间要求极其苛刻的实时控制任务,不受通信等非实时任务的干扰。CPU2则负责处理所有复杂但允许稍长延迟的管理和交互任务。两者通过精心设计的IPC和共享内存机制高效协同,充分发挥了双核MCU的性能潜力。