RK3568内置MCU开发介绍之二

描述

本文重点介绍RK3568内置的MCU与AP之间的通信功能,首先介绍RPMsg的框架,然后介绍MCU端RPMSG的构建方法,最后介绍AP端的RPMSG的配置方法,并进行MCU和AP之间通信的演示。

RK3568 MCU开发的入门介绍可以参考笔者的上一篇文章。

硬件准备

首先介绍一下硬件。主板为风火轮科技的YY3568开发板,主控RK3568。此开发板的相关介绍可以参考

https://wiki.youyeetoo.cn/zh/YY3568

RK3568的MCU核心需要使用串口调试,笔者这里使用的是UART4,这里也可以选择其他的,不要跟AP核心的调试串口UART2冲突就行。 UART4的位置如下

RK3568

RPMsg框架介绍

RPMsg(Remote Processor Messaging)是一种基于virtio的消息传递总线,专为异构处理器系统之间的通信设计。其定义了一个标准化的通信接口,使得不同架构的核心能够方便地进行通信。

RK3568上面有ARM核心和RISCV核心,需要使用RPMsg这种标准化的接口,这样可以不用考虑大小端转换等兼容问题。在RK3568上面,AP核心是RPMSG的master端,而MCU核心则是remote端。

使用RPMSG通信时,通常会从VirtIo-Ring缓冲区中申请一块buffer,然后将这个buffer作为共享内存,在两个核心之间传输数据。另外二者之间通信时,还需要中断通知对方有数据到达。以master向remote发数据为例,

1. Master Core 发送时,从 vring0中取得一块 buffer,再将消息按照 RPMsg 协议填充

2. 将处理好的内存 buffer 链接到vring1

3. 触发中断通知 Remote Core 有数据处理待处理

这个通信流程如下图所示

RK3568

反过来也是一样的

在RK3568,可以选择软中断,或者mailbox实现这个中断,通常的做法是选择mailbox,这样还可以在触发中断时额外附带少量信息。

MCU端RPMSG适配

RK SDK里面提供了两个可选的rpmsg组件,分别是由NXP主导开发的rpmsg-lite,以及openamp项目下面的rpmsg。前者比较简单,不需要太多额外的组件,因此选择前者。其工程路径为

external/hal/middleware/rpmsg-lite

如果想要进一步了解rpmsg-lite,其原始的git项目地址为

https://github.com/nxp-mcuxpresso/rpmsg-lite

原始的rpmsg-lite工程仅支持NXP的异构处理器,如imx6/imx7/imx8等,RK在此基础上增加了RK3562/RK3568/RK3588等处理器的支持。

不过RK3568仅支持AP运行这个rpmsg-lite,因此,这里我们要添加支持RK3568 MCU的相关源码。具体步骤如下

1、打开external/hal/middleware/rpmsg-lite/lib/include/rpmsg_compiler.h,增加riscv处理器的MEM_BARRIER实现

RK3568

RISCV处理器使用fence作为内存屏障指令,功能类似ARM的dsb指令。

2、屏蔽rpmsg-lite/lib/init/platform/RK3568/rpmsg_init.c中的功能,这个源文件中的API专门用于AP运行bare-metal时使用rpmsg-lite的,这里使用MCU,需要将其禁用

RK3568

3、接下来修改

rpmsg-lite/lib/rpmsg_lite/porting/platform/RK3568/rpmsg_platform.c

文件。这个文件就是实现上述rpmsg的传输流程,即通过mbox触发中断,然后用virtio来传输数据。其中virtio传输数据的功能已经由rpmsg-lite实现了,这里只需要调用。

platform_init platform_deinit函数是用于平台初始化的,在rpmsg功能init和deinit的时候会调用这两个函数,保持跟原来的一致即可

RK3568

platform_map_mem_region 

platform_cache_all_flush_invalidate 

platform_cache_disable 

platform_vatopa

platform_patova

几个函数在MCU上没有特殊实现,保持跟原来的一致

platform_interrupt_enable

platform_interrupt_disable

两个函数用于开启和关闭mbox中断,RISCV和ARM中断控制器不同,需要将原来的GIC的API换成INTMUX的API。此外,此处要选择一个mbox通道,笔者选用ch3,后续AP Linux端也要配置同样的通道。

RK3568

 

RK3568

platform_notify函数为virtqueue的回调函数,用于在接收数据的时候产生一个中断。这里实现是要通过mbox应答AP,这样AP才认为收到了数据

RK3568

platform_init_interrupt用于平台初始化中断,这里的实现则是初始化mbox,具体修改如下

RK3568

platform_global_isr_disable

platform_global_isr_enable

用于开启和关闭全局中断,这里使用默认的实现即可

然后要定义mbox的通道信息

RK3568

rpmsg_remote_cb函数属于rpmsg的回调,具体由上述的rpmsg_mbox_isr调用,这两个函数实现如下

RK3568

修改完上面的rpmsg之后,还需要实现一个测试demo,对前一篇文章介绍的external/hal/project/rk3568-mcu目录做修改

1.增加mcu对rpmsg的共享内存区域的访问。rpmsg使用的共享内存区域由AP端Linux指定,作为预留内存。修改GCC目录下面的 Makefile,改用自定义的ld文件。这里同步打开rpmsg-lite的mk文件编译

RK3568

这个ld文件可以从lib/CMSIS/Device/RK3568/Source/Templates/GCC复制一个过来,然后添加如下内容

RK3568

 

RK3568

2.修改testdemo,增加rpmsg测试功能

这个测试demo实现的功能是remote端收到数据后,就给master端发送一条固定内容的数据

先增加如下所示宏定义

RK3568

然后添加一个名为 rpmsg_linux_test的测试函数

RK3568

实现如下

RK3568

这里使用rpmsg_lite_remote_init初始化,这个初始化会调用上述rpmsg_platform.c中实现的API,就是初始化mbox和中断

然后使用rpmsg_lite_wait_for_link_up,这个API会循环等待AP Linux端的mbox初始化完成才返回

然后使用rpmsg_ns_bind指定rpmsg回调函数为rpmsg_ns_cb。下面看下rpmsg_ns_cb的实现,在进入此回调之后,同时会向master端,也就是AP Linux端发送一条数据

RK3568

然后使用rpmsg_lite_send发送一条数据给master端,发起第一次通信

到这里,mcu端的修改就完成了,按照上一篇文章介绍的方法编译,下载到板上。

AP端RPMSG适配

AP端的Linux上已经有完整的rpmsg框架了,这里只需要修改下配置即可。首先在defconfig文件中打开如下内容,这里将支持mbox触发rpmsg相关中断的源码编译进内核,将测试demo编译为模块

RK3568

然后在设备树打开rpmsg的配置,注意rpmsg-tx的通道需要为3,跟上面MCU配置的一样

RK3568

完成这些配置之后,编译出内核,然后烧录到板上,同时将kernel/drivers/rpmsg/ rockchip_rpmsg_test.ko通过adb/ssh等方式推到板上。

MCU与AP rpmsg通信演示

按上面的步骤烧录amp和kernel后,重启板子,可以看到MCU端串口有如下打印信息

RK3568

AP端的dmesg log有如下信息

RK3568

然后在AP端加载 rockchip_rpmsg_test.ko

RK3568

可以看到这个信息跟源码里面配置的是一致的

RK3568

说明MCU确实有收到AP的数据,并且发送的数据能被AP正确接收

总结

本文介绍RK3568内置的MCU与AP之间的通信功能,MCU端使用rpmsg-lite组件,AP端使用Linux自带的rpmsg框架,中断依靠mbox实现,此种方式可以在异构核之间可靠传输大量数据。

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分