AWorksLP 对外设进行了高度抽象化,为同一类外设提供了相同的接口,应用程序可以轻松跨平台。本文以MR6750 平台为例,介绍AWorksLP 双核通信的基本用法。
简介
通信信箱MBX 有2 套寄存器访问接口,接口A 和接口B。A 和B 接口都具有一套TX FIFO 寄存器、RX FIFO
寄存器、控制寄存器和状态寄存器。用户从A 接口的发送端TX 发送的数据,可以在B 接口的接收端RX 接收 到。同理,A 接口的接收端RX 可以接收到B 接口发送端TX 发送的数据。
双核烧录的用法请参考《AWorksLP样例详解(MR6750)——双核烧录》
双核调试的用法请参考《AWorksLP样例详解(MR6750)——双核调试》
双核通信
1. MBX信箱
{SDK}demosmulti-coreopenamp路径下为openamp的例程。双核通信需要使用信箱在gui上勾选对应的信箱接口,hart0和hart1需勾选同一个信箱的两个不同接口。例如hart0勾选了mbx0a、则hart1需勾选mbx0b。
图1 mbx设备
2. OpenAMP
OpenMP是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受,用于共享内存并行系统的多处理器程序设计的一套指导性编译处理方案。
3. 例程
#if CONFIG_AW_OPENAMP_MASTER
aw_local int rx_callback (struct rpmsg_endpoint *ept, void *data,
size_t len, uint32_t src, void *priv)
{
aw_kprintf("[Master receive]: %sn", data);
return 0;
}
#else
aw_local int rx_callback (struct rpmsg_endpoint *ept, void *data,
size_t len, uint32_t src, void *priv)
{
char sendbuf[512];
aw_kprintf("[Slave receive]: %sn", data);
aw_snprintf(sendbuf, sizeof(sendbuf), "%s ACK", data);
if (rpmsg_send(&__resmgr_ept, sendbuf, strlen(sendbuf) + 1) < 0) {
aw_kprintf("[Slave send]: error!n");
}
return 0;
}
#endif
aw_local int __mail_box_notify(void *priv, uint32_t id)
{
uint32_t tmp;
#if CONFIG_AW_OPENAMP_MASTER
/* master to remote */
if (id == VRING1_ID) {
/* send msg */
tmp = EPT_SEND_MSG_FLAG;
} else { /* remote to master */
/* send ack */
tmp = EPT_SEND_ACK_FLAG;
}
#else
if (id == VRING1_ID) {
/* send ack */
tmp = EPT_SEND_ACK_FLAG;
} else {
/* send msg */
tmp = EPT_SEND_MSG_FLAG;
}
#endif
aw_write(__g_mbx_fd, &tmp, 4);
return 0;
}
/* 处理其它设备发送过来的MBX */
aw_local void __openamp_task(void *p_arg)
{
struct rpmsg_virtio_device *p_dev = (struct rpmsg_virtio_device *)p_arg;
aw_kprintf("Entry OpenAMP task!n");
while(1) {
uint32_t tmp;
aw_read(__g_mbx_fd, &tmp, 4);
/*
* 默认Master VRING0是接收, VRING1是发送, 从机反之
*/
#if CONFIG_AW_OPENAMP_MASTER
if (tmp == EPT_SEND_MSG_FLAG) {
/* 接收到来自从机的消息 */
rproc_virtio_notified(p_dev->vdev, VRING0_ID);
} else {
/* 接收到来自从机的ACK */
rproc_virtio_notified(p_dev->vdev, VRING1_ID);
}
#else
if (tmp == EPT_SEND_MSG_FLAG) {
rproc_virtio_notified(p_dev->vdev, VRING1_ID);
} else {
rproc_virtio_notified(p_dev->vdev, VRING0_ID);
}
#endif
}
}
void rpmsg_demo()
{
int ret = 0;
#if CONFIG_AW_OPENAMP_MASTER
int i = 0;
int RPMsgRole = 0;
#else
int RPMsgRole = 1;
#endif
__g_mbx_fd = aw_open(CONFIG_MBX_CHOOSE, AW_O_RDWR, 0);
ret = aw_openamp_init(&rpmsg_dev, RPMsgRole, NULL, __mail_box_notify);
#if CONFIG_AW_OPENAMP_MASTER
/* 启动固件 */
ret = aw_openamp_remoteproc_init(&__aworks_rproc_ops);
if (ret) {
aw_kprintf("Start processor fail!n");
}
#endif
if (ret) {
aw_kprintf("OpenAMP init error!n");
while(1);
}
aw_openamp_create_ept(&rpmsg_dev,
&__resmgr_ept,
"rpmsg-client-sample",
0xFFFFFFFF,
rx_callback, NULL);
aw_openamp_ep_poll_task_start(&rpmsg_dev);
aw_openamp_wait_ept_ready(&__resmgr_ept);
while(1) {
#if CONFIG_AW_OPENAMP_MASTER
char sendbuf[512];
aw_snprintf(sendbuf, sizeof(sendbuf), "AWorks %d", i);
aw_kprintf("[Master send]: %sn", sendbuf);
if (aw_openamp_send(&__resmgr_ept, sendbuf, strlen(sendbuf) + 1) < 0) {
aw_kprintf("[Master send]: error!n");
}
i++;
#else
aw_kprintf("Salve is alive!n");
#endif
aw_mdelay(100);
}
}
由于篇幅原因本文仅截取部分关键代码。
-
- 在rpmsg_demo中使用aw_open打开信箱、使用aw_openamp_init并注册__mail_box_notify函数;在__mail_box_notify函数中发送相应的标记、使用aw_openamp_remoteproc_init函数注册__aworks_rproc_ops,参数中是各运行阶段的函数接口;使用aw_openamp_create_ept函数注册rx_callback接收回调函数,当作为主核时打印从核发送的数据,当作为从核时将收到的数据发送回去;使用aw_openamp_ep_poll_task_start函数创建一个任务,任务的函数入口为__openamp_task,在__openamp_task函数中根据读到的标记做相应的处理。
使用aw_openamp_wait_ept_ready函数等待从机准备好。
在while循环中主核使用aw_openamp_send函数循环的发送数据、从核在rx_callback回调函数中将主核发送的数据发送回去、串口打印如下图。
图2 串口打印