查看: 1281|回复: 0

[评测分享] 【NXP OKdo E1双核Cortex M33开发板】+ 5.双核代码之通信小议

[复制链接]
  • TA的每日心情
    慵懒
    2024-3-10 16:58
  • 签到天数: 223 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2020-11-15 21:06:48 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 az158 于 2020-11-15 22:29 编辑

    今天我们继续看双核有关的代码,下面我们以官方提供的一个双核实例,分析它的通信功能。程序位置在SDK_2.8.2_LPC55S69\boards\lpcxpresso55s69\multicore_examples\rpmsg_lite_pingpong

    首先编译core1的代码。
    然后对core0的代码进行一些修改,因为这个板子上没有外部晶振,所以要将clock_config.c文件里面的BOARD_InitBootClocks函数修改如下。
    1. void BOARD_InitBootClocks(void)
    2. {
    3.   BOARD_BootClockFROHF96M();
    4. }
    复制代码

    将编译好的代码下载,程序运行后串口打印如下。
    QQ截图20201115211010.png
    ...............省略部分打印.................
    QQ截图20201115211031.png

    今天主要看它的双核通信部分的程序。有关启动的部分在【NXP OKdo E1双核Cortex M33开发板】+ 4.双核代码之启动流程小议已经讨论过了。
    因为双核代码需要两个程序一起看,来回横跳,这里我们一段段来分析。

    先看core0的程序
    1. /* Initialize MCMGR before calling its API */
    2. (void)MCMGR_Init();

    3. /* Register the application event before starting the secondary core */
    4. (void)MCMGR_RegisterEvent(kMCMGR_RemoteApplicationEvent, RPMsgRemoteReadyEventHandler,
    5.                                                   (void *)&RPMsgRemoteReadyEventData);

    6. /* Boot Secondary core application */
    7. (void)MCMGR_StartCore(kMCMGR_Core1, (void *)(char *)CORE1_BOOT_ADDRESS, (uint32_t)rpmsg_lite_base,
    8.                                           kMCMGR_Start_Synchronous);

    9. /* Print the initial banner */
    10. (void)PRINTF("\r\nRPMsg demo starts\r\n");

    11. /* Wait until the secondary core application signals the rpmsg remote has been initialized and is ready to
    12. * communicate. */
    13. while (APP_RPMSG_READY_EVENT_DATA != RPMsgRemoteReadyEventData)
    14. {
    15. };
    复制代码

    首先利用了MCMGR_RegisterEvent注册了一个kMCMGR_RemoteApplicationEvent事件,也就是当另一个核触发kMCMGR_RemoteApplicationEvent事件的时候,本核将产生一个RPMsgRemoteReadyEventHandler的中断,并将RPMsgRemoteReadyEventData的地址作为一个参数传递进去。中断函数如下。
    1. static void RPMsgRemoteReadyEventHandler(uint16_t eventData, void *context)
    2. {
    3.     uint16_t *data = (uint16_t *)context;
    4.     *data = eventData;
    5. }
    复制代码

    这个函数的功能是将另一个核传来的参数给RPMsgRemoteReadyEventData赋值。
    之后同步启动另一个核的代码,并将rpmsg_lite_base内存池地址传递给另一个核。

    然后等待RPMsgRemoteReadyEventData值变化为APP_RPMSG_READY_EVENT_DATA时继续运行。

    接下来我们先看一下core1的程序
    1. /* Initialize MCMGR before calling its API */
    2. (void)MCMGR_Init();

    3. /* Get the startup data */
    4. do
    5. {
    6.         status = MCMGR_GetStartupData(&startupData);
    7. } while (status != kStatus_MCMGR_Success);

    8. my_rpmsg = rpmsg_lite_remote_init((void *)(char *)startupData, RPMSG_LITE_LINK_ID, RL_NO_FLAGS, &rpmsg_ctxt);

    9. /* Signal the other core we are ready by triggering the event and passing the APP_RPMSG_READY_EVENT_DATA */
    10. (void)MCMGR_TriggerEvent(kMCMGR_RemoteApplicationEvent, APP_RPMSG_READY_EVENT_DATA);
    复制代码

    首先获取启动的时候core0的时候发送的数据也就是rpmsg_lite_base内存池地址,赋值给startupData

    之后利用这个地址去申请共享内存,当然这时候core0还没有对这个内存池进行初始化。

    最后触发kMCMGR_RemoteApplicationEvent事件通知core0,值为APP_RPMSG_READY_EVENT_DATA也就是core0在等的APP_RPMSG_READY_EVENT_DATA这个数值。

    接下来接着去看core0的程序
    1. my_rpmsg = rpmsg_lite_master_init(rpmsg_lite_base, SH_MEM_TOTAL_SIZE, RPMSG_LITE_LINK_ID, RL_NO_FLAGS, &rpmsg_ctxt);

    2. my_ept = rpmsg_lite_create_ept(my_rpmsg, LOCAL_EPT_ADDR, my_ept_read_cb, (void *)&has_received, &my_ept_context);

    3. has_received = 0;

    4. /* Wait until the secondary core application signals the rpmsg remote endpoint has been created. */
    5. while (APP_RPMSG_EP_READY_EVENT_DATA != RPMsgRemoteReadyEventData)
    6. {
    7. };
    复制代码

    等到需要的值,程序继续运行,首先初始化rpmsg_lite_base内存池,长度为SH_MEM_TOTAL_SIZE

    之后利用刚刚初始化的内存池,新建一个通信节点,可以用于接受对方的消息,类似信箱的机制。当收到对方的消息的时候,本核将产生一个my_ept_read_cb的中断,并将has_received的地址作为一个参数传递进去。中断函数如下。
    1. static int32_t my_ept_read_cb(void *payload, uint32_t payload_len, uint32_t src, void *priv)
    2. {
    3.     int32_t *has_received = priv;

    4.     if (payload_len <= sizeof(THE_MESSAGE))
    5.     {
    6.         (void)memcpy((void *)&msg, payload, payload_len);
    7.         *has_received = 1;
    8.     }
    9.     (void)PRINTF("Primary core received a msg\r\n");
    10.     (void)PRINTF("Message: Size=%x, DATA = %i\r\n", payload_len, msg.DATA);
    11.     return RL_RELEASE;
    12. }
    复制代码

    这个函数的功能是当来了消息之后,先判断消息的长度是否小于sizeof(THE_MESSAGE),本例程中,每一条消息长度都是这个。然后将数据拷贝到msg里面。然后将has_received赋值为1。最后进行log打印。
    然后等待RPMsgRemoteReadyEventData值变化为APP_RPMSG_EP_READY_EVENT_DATA时继续运行。

    我们接着看core1的程序
    1. while (0 == rpmsg_lite_is_link_up(my_rpmsg))
    2.     {
    3.     }

    4.     my_ept = rpmsg_lite_create_ept(my_rpmsg, LOCAL_EPT_ADDR, my_ept_read_cb, (void *)&has_received, &my_ept_context);

    5.     /* Signal the other core the endpoint has been created by triggering the event and passing the
    6.      * APP_RPMSG_READY_EP_EVENT_DATA */
    7.     (void)MCMGR_TriggerEvent(kMCMGR_RemoteApplicationEvent, APP_RPMSG_EP_READY_EVENT_DATA);
    复制代码

    先等待core0的内存池初始化完毕。

    然后新建一个通信节点,这里信箱的地址与主机信箱的地址是不同的。中断回调如下。
    1. static int32_t my_ept_read_cb(void *payload, uint32_t payload_len, uint32_t src, void *priv)
    2. {
    3.     int32_t *has_received = priv;

    4.     if (payload_len <= sizeof(THE_MESSAGE))
    5.     {
    6.         (void)memcpy((void *)&msg, payload, payload_len);
    7.         remote_addr   = src;
    8.         *has_received = 1;
    9.     }
    10.     return RL_RELEASE;
    11. }
    复制代码

    里面与core0的程序类似,只是多了一个获取对方信箱地址,以及减少了log的程序。

    最后触发kMCMGR_RemoteApplicationEvent事件通知core0,值为APP_RPMSG_EP_READY_EVENT_DATA也就是core0在等的APP_RPMSG_EP_READY_EVENT_DATA这个数值。

    接着看core0的程序
    1. /* Send the first message to the remoteproc */
    2. msg.DATA = 0U;
    3. (void)rpmsg_lite_send(my_rpmsg, my_ept, REMOTE_EPT_ADDR, (char *)&msg, sizeof(THE_MESSAGE), RL_DONT_BLOCK);

    4. while (msg.DATA <= 100U)
    5. {
    6.         if (1 == has_received)
    7.         {
    8.                 has_received = 0;
    9.                 msg.DATA++;
    10.                 (void)rpmsg_lite_send(my_rpmsg, my_ept, REMOTE_EPT_ADDR, (char *)&msg, sizeof(THE_MESSAGE), RL_DONT_BLOCK);
    11.         }
    12. }

    13. (void)rpmsg_lite_destroy_ept(my_rpmsg, my_ept);
    14. my_ept = ((void *)0);
    15. (void)rpmsg_lite_deinit(my_rpmsg);

    16. /* Print the ending banner */
    17. (void)PRINTF("\r\nRPMsg demo ends\r\n");
    18. for (;;)
    19. {
    20. }
    复制代码

    向对方的信箱发送msg数据。

    进入循环,就是当has_received变为1的时候,也就是对面信箱来消息的时候。将msg.DATA++,同时对面信箱来消息的回调函数也会将另一个核发来的数据赋值这个量。

    msg.DATA大于100时,程序结束。

    接着看core1的程序
    1. has_received = 0;

    2. while (msg.DATA <= 100U)
    3. {
    4.         if (1 == has_received)
    5.         {
    6.                 has_received = 0;
    7.                 msg.DATA++;
    8.                 (void)rpmsg_lite_send(my_rpmsg, my_ept, remote_addr, (char *)&msg, sizeof(THE_MESSAGE), RL_DONT_BLOCK);
    9.         }
    10. }

    11. (void)rpmsg_lite_destroy_ept(my_rpmsg, my_ept);
    12. my_ept = ((void *)0);
    13. (void)rpmsg_lite_deinit(my_rpmsg);
    14. msg.DATA = 0U;

    15. /* End of the example */
    16. for (;;)
    17. {
    18. }
    复制代码

    程序与core0相似。

    可以看到msg.DATA这个值会在core0的程序中打印并自增1,在core1的程序中自增1。所以现象就是打印1 3 5...这样奇数的数据。

    简单总结一下,LPC55S69这个芯片双核通信是利用类似邮箱的机制,当数据来的时候会产生中断。同时从核还可以产生事件传参通知主核自身状态。虽然粗略一看比较复杂,不过在使用的时候会非常方便。能否发挥双核的性能,就要看程序了。
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-4-27 00:59 , Processed in 0.117695 second(s), 17 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.