MYZR RZ/V2H EK320 核间通信实战


本文档记录了如何基于官方的rpmsg例程进行修改,达到个性化核间通信的目的。本文档完成了通过linux端控制舵机旋转特定角度。本文的前置博客为“MYZR RZ/V2H EK320 CM33 Freertos实战入门”,请确保自己已经完成此博客的学习。

1. 应用基础

瑞萨官方的rpmsg历程已经帮助我们完成了A55和M33在共享内存处的数据互访互取,所以我们直接调用顶层的封装函数或者直接在官方例程基础上进行改造,就可以简单地实现我们的个性化定制了 。我们在开发的过程中不需要去关心数据是怎么在共享内存中流动的,只需要做好两方的数据收发即可。

2. 修改官方linux端rpmsg例程

在“MYZR RZ/V2H EK320 多核异构与 Docker 适配镜像编译指南”这一篇博客,我们已经把multi-os层添加到了yocto工程中,在这里我们直接对rpmsg的源码进行修改重新编译即可。

打开wsl2,打开文件meta-rz-features/meta-rz-multi-os/meta-rzv2h/recipes-example/rpmsg-sample/files/main.c,在#include后添加我们的数据结构体:

typedef struct {
    uint32_t command_id;  // 指令类型:1 代表设置角度,2 代表其他...
    int32_t  angle;       // 具体的角度值 (例如 0 到 180)
} servo_control_msg_t;

由于这里我们使用了int32_t这种类型的变量,我们再引入一个头文件:

#include <stdint.h>

待会儿我们在CM33的Freertos端定义一个完全相同的结构体我们就可以实现数据的收发了。

修改下面的“static int app (struct rpmsg_device *rdev, struct remoteproc *priv, unsigned long svcno)”函数,在LPRINTF(“RPMSG service has created.”);这行代码下,在最后一个error前插入以下代码:

    int input_angle = 0;
    servo_control_msg_t send_msg;

    while (!force_stop) {
        // 1. 获取你要自定义的角度 (这里以命令行输入为例)
        printf("\n请输入舵机目标角度 (0-180,输入 -1 退出): ");
        if (scanf("%d", &input_angle) != 1) {
            // 清理输入缓冲
            while(getchar() != '\n'); 
            continue;
        }

        if (input_angle == -1) {
            break; // 退出循环
        }

        // 2. 打包数据
        send_msg.command_id = 1;         // 1 表示舵机角度控制
        send_msg.angle = input_angle;    // 填入自定义的角度

        LPRINTF("Sending command to RTOS -> Command: %u, Angle: %d", 
                send_msg.command_id, send_msg.angle);

        // 3. 调用函数发送数据包
        ret = rpmsg_send(&rp_ept, &send_msg, sizeof(servo_control_msg_t));
        if (ret < 0) {
            LPERROR("rpmsg_send failed: %d\n", ret);
            break;
        }
    }

完成修改后保存。进入命令行,进入目录rzv2h_ai_sdk_v500,激活编译工具:

source poky/oe-init-build-env

编译rpmsg层:

bitbake rpmsg-sample

编译成功后前往路径rzv2h_ai_sdk_v500/build/tmp/work/aarch64-poky-linux/rpmsg-sample/0.1-r0/image/usr/bin路径下提取生成的二进制文件,将它拷贝至U盘中。至此linux端的程序修改完毕。

3. 修改官方cm33端rpmsg例程

官方的rpmsg例程在cm33端也是作为Freertos的一个任务而存在的。所以之后我们在开发Freertos程序时可以将它当作一个“数据收发中心”,这极大地提高了我们编程地灵活性。

不同于信号量用来传递类似bool的状态,由于我们的rpmsg接收到数据后需要将数据发送至pwm_thread任务,所以我们这里增加一个队列queue用来传递我们接收到的数据。

依旧进入fsp配置界面,点击New Object,选择Queue,然后点击下方属性选项卡,进行设置。因为我们刚刚定义的结构体都是四个字节的数据,而我们待会儿也只会使用其中角度的四个字节数据,所以我们队列单独一个单元就设置为4字节,长度随意设置即可,如下图:

点击右上方绿色小箭头,生成代码。

回到资源管理器。rpmsg作为一个单独的任务,其实也存在入口函数和.c文件的,我们在src中寻找,实际上就是rpmsg_demo.c这个文件,现在对这个文件进行修改。

在文件的include中新增以下include,目的是把刚刚创建好的队列extern进来:

#include "hal_data.h"

修改函数static int rpmsg_endpoint_cb0 (struct rpmsg_endpoint * cb_rp_ept, void * data, size_t len, uint32_t src, void * priv):

static int rpmsg_endpoint_cb0 (struct rpmsg_endpoint * cb_rp_ept, void * data, size_t len, uint32_t src, void * priv)
{
    /* service 0 */
    (void)cb_rp_ept;
    (void) priv;
    (void) src;

    /* On reception of a shutdown we signal the application to terminate */
    if ((*(unsigned int *) data) == SHUTDOWN_MSG)
    {
        evt_svc_unbind[0] = 1;

        return RPMSG_SUCCESS;
    }

    if (len == sizeof(servo_control_msg_t)) {

            // 1. 将收到的二进制数据强转为结构体指针
            servo_control_msg_t *received_msg = (servo_control_msg_t *)data;

            // 2. 只提取 4 个字节的角度信息
            int32_t target_angle = received_msg->angle;

            BaseType_t xHigherPriorityTaskWoken = pdFALSE;

            // 3. 将这 4 个字节的纯角度数值压入 linux_angel 队列
            if (xQueueSendFromISR(linux_angle, &target_angle, &xHigherPriorityTaskWoken) == pdPASS) {
                portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
            } else {
                LPERROR("Queue linux_angel is FULL! Dropping angle.\n");
            }

        } else {
            LPERROR("RPMsg length mismatch! Expected 8, got %zu\n", len);
        }

    return RPMSG_SUCCESS;
}

当我的任务接收到数据以后,将其中的角度信息拿出来,再将角度信息压入队列中,接下来pwm_thread再从队列中取得数据进行操作,修改pwm_thread文件:

void pwm_thread_entry(void *pvParameters)
{
    FSP_PARAMETER_NOT_USED (pvParameters);

    uint32_t duty_0;
    uint32_t duty_180;
    int32_t received_angle = 0;

    /* 1. 初始化定时器 */
    R_GPT_Open(&g_timer0_ctrl, &g_timer0_cfg);
    timer_info_t info;
    R_GPT_InfoGet(&g_timer0_ctrl, &info);
    uint32_t period = info.period_counts;

    /* 2. 计算 0° 和 180° 的占空比边界 */
    // 假设你的舵机是标准的 0.5ms~2.5ms (对应 0~180°)
    duty_0   = (period * 25) / 1000;   // 0.5ms
    duty_180 = (period * 125) / 1000;  // 2.5ms

    /* 为了计算方便,算出每 1° 对应的占空比增量 */
    uint32_t duty_per_degree = (duty_180 - duty_0) / 180;

    /* 启动定时器 (舵机初始可能会转到某个默认位置,或者你可以先设为 0°) */
    R_GPT_Start(&g_timer0_ctrl);
    R_GPT_DutyCycleSet(&g_timer0_ctrl, duty_0, GPT_IO_PIN_GTIOCB);


    while (1)
    {
        /* 3. 阻塞等待队列中的消息
         * portMAX_DELAY 表示如果 Linux 没发数据,这个任务就一直睡觉,不占用 CPU。
         */
        if (xQueueReceive(linux_angle, &received_angle, portMAX_DELAY) == pdTRUE)
        {

            /* 4. 安全范围校验 (限制在 0 ~ 180 之间) */
            if (received_angle < 0) {
                received_angle = 0;
            } else if (received_angle > 180) {
                received_angle = 180;
            }

            /* 5. 计算目标角度对应的占空比 */
            uint32_t target_duty = duty_0 + (received_angle * duty_per_degree);

            /* 6. 设置 PWM 占空比,驱动舵机转动 */
            R_GPT_DutyCycleSet(&g_timer0_ctrl, target_duty, GPT_IO_PIN_GTIOCB);
        }
    }
}

当前代码实现的效果就是pwm_thread会持续等待队列中数据得到来然后被调度器踢醒进行工作。点击锤子进行编译,将编译得到的elf文件拷贝至U盘。

4. 启动程序查看效果

将U盘插上开发板开机,输入以下指令:

mount /dev/sda1 /mnt
cp /mnt/rpmsg_sample_client /usr/bin
cp /mnt/rzv2h_cm33_rpmsg_linux-rtos_demo.elf /lib/firmware
umount /mnt
echo rzv2h_cm33_rpmsg_linux-rtos_demo.elf > /sys/class/remoteproc/remoteproc0/firmware
echo start > /sys/class/remoteproc/remoteproc0/state

此时固件已经启动,接着在命令行敲入指令:

rpmsg_sample_client

可以看到命令行输出以下信息:

root@myzr-rzv2h-ek320:~# rpmsg_sample_client
[448] proc_id:0 rsc_id:0 mbx_id:1
metal: info:      metal_uio_dev_open: No IRQ for device 10480000.mbox-uio.
[448] Successfully probed IPI device
metal: info:      metal_uio_dev_open: No IRQ for device 42f00000.rsctbl.
[448] Successfully open uio device: 42f00000.rsctbl.
[448] Successfully added memory device 42f00000.rsctbl.
metal: info:      metal_uio_dev_open: No IRQ for device 43000000.vring-ctl0.
[448] Successfully open uio device: 43000000.vring-ctl0.
[448] Successfully added memory device 43000000.vring-ctl0.
metal: info:      metal_uio_dev_open: No IRQ for device 43200000.vring-shm0.
[448] Successfully open uio device: 43200000.vring-shm0.
[448] Successfully added memory device 43200000.vring-shm0.
metal: info:      metal_uio_dev_open: No IRQ for device 43100000.vring-ctl1.
[448] Successfully open uio device: 43100000.vring-ctl1.
[448] Successfully added memory device 43100000.vring-ctl1.
metal: info:      metal_uio_dev_open: No IRQ for device 43500000.vring-shm1.
[448] Successfully open uio device: 43500000.vring-shm1.
[448] Successfully added memory device 43500000.vring-shm1.
metal: info:      metal_uio_dev_open: No IRQ for device 42f01000.mhu-shm.
[448] Successfully open uio device: 42f01000.mhu-shm.
[448] Successfully added memory device 42f01000.mhu-shm.
[448] Initialize remoteproc successfully.
[448] proc_id:1 rsc_id:1 mbx_id:1
[448] Initialize remoteproc successfully.
[448] proc_id:0 rsc_id:0 mbx_id:2
[448] Initialize remoteproc successfully.
[448] proc_id:1 rsc_id:1 mbx_id:2
[448] Initialize remoteproc successfully.
[448] proc_id:0 rsc_id:0 mbx_id:3
[448] Initialize remoteproc successfully.
[448] proc_id:1 rsc_id:1 mbx_id:3
[448] Initialize remoteproc successfully.
******************************************
* Custom RPMSG Control Panel           *
******************************************
1. Send Target Angle to Servo
e. exit
please input >

输入1,点击回车,随后我们输入0-180°之间的角度可以发现舵机响应了我们的命令:

please input > 1
[454] thread start
[CM33] creating remoteproc virtio
[CM33] initializing rpmsg shared buffer pool
[CM33] initializing rpmsg vdev
[CM33]  1 - Send data to remote core, retrieve the echo and validate its integrity ..
[CM33] Remote proc init.
[CM33] RPMSG endpoint has created. rp_ept:0xffff8b288870
[CM33] register sig:2 succeeded.
[CM33] register sig:15 succeeded.
[453] cond signal 1 sync:0
[CM33] RPMSG service has created.

??擖懬婘栚?妏搙 (0-180丆?擖 -1 戅弌): 0
[CM33] Sending command to RTOS -> Command: 1, Angle: 0

??擖懬婘栚?妏搙 (0-180丆?擖 -1 戅弌):

下面是实际工作视频:

以上就是这期博客的全部内容。后面可能会继续更新在docker环境中进行核间通信,以及怎么将核间通信代码封装为一个ROS节点。

,

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注