从RK3562实践出发:AMP架构下的RPMsg核间通信实战解析

张开发
2026/4/22 7:24:36 15 分钟阅读

分享文章

从RK3562实践出发:AMP架构下的RPMsg核间通信实战解析
1. AMP架构与RK3562的核间通信基础在嵌入式系统开发中多核异构架构已经成为提升性能的主流方案。RK3562作为一款典型的异构多核处理器采用了四核Cortex-A53搭配Cortex-M0的设计。这种架构下AMP非对称多处理模式能够充分发挥不同核心的特长——A53运行Linux处理复杂应用M0运行RT-Thread保证实时性。我第一次接触这种架构是在开发智能扫地机项目时需要Linux处理SLAM算法同时M0实时控制电机。当时最头疼的就是两个系统间的数据交换问题。传统方案用UART或SPI通信实测延迟高达20ms根本满足不了实时控制需求。直到发现芯片内置的RPMsg核间通信机制才真正解决了这个痛点。RPMsg协议栈建立在共享内存和中断机制基础上通过VirtIO虚拟化技术实现。简单来说就像在两个核心之间建立了快递系统共享内存相当于快递仓库vring缓冲区中断机制就像快递员的电话通知协议头则是包裹上的快递单在RK3562上这个快递系统的吞吐量实测能达到50MB/s往返延迟仅3μs。下面这张表对比了常见通信方式的性能通信方式最大带宽典型延迟适用场景UART1.5Mbps1ms调试日志SPI50Mbps100μs传感器数据RPMsg400Mbps3μs核间大数据量交换2. RK3562开发环境搭建实战2.1 硬件准备与软件工具链我推荐使用官方EVB开发板入门它的引脚引出完善配套资料齐全。第一次使用时踩过的坑是必须使用5V/3A电源否则M0核心会运行不稳定。软件方面需要准备瑞芯微提供的AMP SDK版本建议v1.0以上交叉编译工具链gcc-arm-none-eabi和aarch64-linux-gnuDocker环境官方提供的编译镜像这里有个小技巧在~/.bashrc中添加这些环境变量能省去每次切换目录的麻烦export RK3562_SDK/path/to/amp-sdk-v1.0 export PATH$RK3562_SDK/toolchain/bin:$PATH2.2 系统镜像编译步骤编译过程就像做汉堡需要分层准备MCU层用scons编译RT-Threadcd $RK3562_SDK/rtos/bsp/rockchip/rk3562-mcu scons --menuconfig # 勾选RPMSG_LITE和LINUX_RPMSG scons -j8U-Boot层开启AMP支持# 在.config中添加 CONFIG_AMPy CONFIG_ROCKCHIP_AMPyKernel层配置关键选项make menuconfig # 开启这些选项 # Device Drivers - RPMSG - Rockchip RPMsg Mailbox # Device Drivers - RPMSG - VirtIO RPMsg Bus编译时常见的一个报错是undefined reference to rl_memcpy这是因为忘记链接rpmsg_lite库。解决方法是在rtconfig.py中添加LIBS [rpmsg_lite]3. 设备树配置的魔鬼细节3.1 内存分区规划RK3562的DDR内存就像一块大蛋糕需要合理分配给各个核心。我在工业控制器项目中使用这样的分配方案reserved-memory { mcu_reserved: mcu7b00000 { reg 0x0 0x7b00000 0x0 0x100000; // M0的1MB内存 }; rpmsg_reserved: rpmsg7c00000 { reg 0x0 0x07c00000 0x0 0x400000; // 共享内存4MB }; };关键点no-map属性告诉Linux不要映射这段内存地址必须4KB对齐实际项目中建议预留额外10%空间3.2 外设资源分配当Linux和RT-Thread需要共用外设时比如I2C1设备树要这样配置i2c1 { status disabled; // Linux端禁用 }; rockchip_amp { clocks cru CLK_I2C1; // 时钟交给AMP管理 pinctrl-0 i2c1m0_xfer; // 引脚复用 };然后在RT-Thread的board.c中初始化void rt_hw_i2c_init(void) { HAL_PINCTRL_SetIOMUX(GPIO_BANK0, GPIO_PIN_B3 | GPIO_PIN_B4, PIN_CONFIG_MUX_FUNC1); }4. RPMsg通信全流程解析4.1 通道建立过程核间通信的建立就像两个人打电话M0端先拨号初始化共享内存rl_instance rpmsg_lite_remote_init(SHARED_MEM_BASE, RL_PLATFORM_SET_LINK_ID(0, 4));Linux端接听创建virtio设备echo start /sys/class/rpmsg/rpmsg_ctrl0/open双方交换名片NS Announcerpmsg_ns_announce(rl_instance, ept, rpmsg-chrdev);4.2 数据收发实战发送数据时要注意Linux应用层最简单的方式int fd open(/dev/rpmsg0, O_RDWR); write(fd, hello, 5); // 发送 read(fd, buf, sizeof(buf)); // 接收RT-Thread端需要回调处理static int32_t callback(void *payload, uint32_t len, uint32_t src, void *priv) { struct rt_rpmsg_device *rdev priv; rt_device_write(rdev-parent, 0, payload, len); return 0; }性能优化技巧批量发送时设置RL_BLOCKING标志大数据传输分片到496字节/包启用CMA内存分配减少拷贝5. 调试与问题排查5.1 常见故障现象通道无法建立检查dmesg | grep rpmsg是否有错误用memtool查看共享内存区域是否被正确写入数据丢包# 监控统计信息 cat /sys/kernel/debug/rpmsg_statsM0核心无响应用J-Link读取M0的PC指针检查时钟树配置是否正确5.2 调试工具推荐Linux端rpmsg_char_simple # 官方测试工具 memtool 0x7c00000 0x100 # 查看共享内存RT-Thread端list_thread() # 查看线程状态 rpmsg_lite_debug() # 打印通信状态记得在量产前用stress-ng做压力测试stress-ng --rpmsg 4 -t 1h6. 进阶应用场景6.1 智能扫地机案例在我们的扫地机方案中这样划分功能Linux端运行ROS导航算法M0端实时控制电机和传感器通信内容激光雷达点云数据通过RPMsg批量传输电机控制指令实时性要求高// 运动控制消息结构体 struct motor_cmd { uint16_t left_speed; uint16_t right_speed; uint8_t brake_flag; } __attribute__((packed));6.2 工业控制器设计在PLC项目中我们实现了Linux处理Modbus TCP协议M0实时处理GPIO中断通过RPMsg传递IO状态# Python端读取DI状态 with open(/dev/rpmsg0, rb) as f: f.write(bGET_DI_STATE) state f.read(8)特别要注意的是工业环境需要添加CRC校验uint32_t calc_crc(void *data, size_t len) { // 使用硬件CRC单元加速 HAL_CRC_Reset(hcrc); return HAL_CRC_Calculate(hcrc, data, len); }在项目开发过程中最深刻的体会是一定要先规划好内存布局。曾经因为DDR分区冲突导致系统随机崩溃花了整整两周才定位到问题。建议在方案设计阶段就用Excel画出内存映射图标注每个区域的作用和大小。

更多文章