• STM32MP157——Remoteproc和RPMsg【转】


    转自:https://hceng.cn/2020/05/09/STM32MP157%E2%80%94%E2%80%94Remoteproc%E5%92%8CRPMsg/

    简单介绍基于STM32MP157的Remoteproc和RPMsg框架。

    STM32MP1系列产品,是STM32进军Linux的首款微处理器,采用MCU+MPU的组合,集成两颗主频为650MHz的Cortex-A7应用处理器内核和一颗主频为209MHz的Cortex-M4微控制器内核。

    非对称多处理Asymmetric Multiprocessing(AMP)虽然目前在嵌入式还不是主流,但未来肯定是趋势。将多媒体处理扔给专用的MCU,亦或将对控制延时敏感的传感器交给MCU实时控制,更多的组合给人更多的遐想。

    对于非对称多核架构,不同的核心是如何启动运行,又是如何进行通信?这些疑惑在上手STM32MP157后,逐渐明朗,因此记录下笔记。

    1.生成M4固件

    在进行启动M4之前,需要先建立工程,生成M4固件,这里以点灯为例,简单说下创建STM32MP157的M4工程。

    这里要M4点灯,涉及到资源的分配,资源分配如下图所示。


    深蓝色的IP为A7独占,浅蓝色的IP为M4独占,竖线分割的IP为同一时刻只能一个占有,斜线分割的IP为任意时刻两者可以同时占用。
    比如这里GPIO就为两者可以同时占用,在Linux中可以控制GPIO,同时M4也可控制GPIO。UART则为只能一个独占,分配给M4后,A7将不能控制。

    支持STM32开发的集成开发环境有很多,国内熟知的有Keil MDK-ARMIAR EWAR。这两个IDE都很好用,但它们都是商业软件,免费或评估版要么有器件型号限制,要么有程序容量限制。于是出现了免费的Eclipse+GNU GCC来搭建STM32开发环境,但搭建过程繁琐、版本差异大导致教程不统一,对新手很不友好。

    STM32CubeIDE是ST公司基于Eclipse/CDT框架和GUN GCC工具链制作的免费IDE,并集成了STM32CubeMX。一个软件就可以实现STM32系列芯片的外围设备配置、代码生成、代码编辑、代码编译、在线调试,并且支持数百个Eclipse现有插件。

    打开STM32CubeIDE,创建一个新的“STM32 Project”。

    在弹出的STM32CubeMX,选择“STM32MP157A”,具体型号以自己使用的开发板为准。注意这里的“芯片资料区”,提供了该型号的芯片手册,不用再去网上找了。

    再设置工程名字,打开STM32CubeMX的关联视图。

    我使用的板子,LED灯接在PD13引脚上,因此这里把PD13设置为输出引脚。

    需要注意,这里还要选中该引脚,右键弹出“Pin Reservation”,选择“Cortex-M4”,不然不会自动生成GPIO初始化代码。

    最后,如图设置下GPIO的属性。

    设置完后,在标签栏选择“Project”->“Generate Code”,即可自动生成相关初始化代码。默认的初始化代码如下图,需要注意的是“main.c”文件,在里面添加LED灯的控制逻辑。还有“stm32mp1xx_hal_gpio.c”,这个是hal库源码,从里面可知hal提供的GPIO相关操作函数,比如这里用到的HAL_GPIO_WritePin()

    1
    2
    3
    4
    HAL_GPIO_WritePin(LED_BLUE_GPIO_Port, LED_BLUE_Pin, GPIO_PIN_SET);
    HAL_Delay(1000);
    HAL_GPIO_WritePin(LED_BLUE_GPIO_Port, LED_BLUE_Pin, GPIO_PIN_RESET);
    HAL_Delay(1000);

    添加完LED的控制逻辑代码后,在标签栏选择“Project”->“Build Project”即可编译工程,得到GPIO_LED_CM4.elf。该文件就是M4的固件,包含Cortex-A7和Cortex-M4都可以访问的资源表(.resource_table)和LED的控制程序等。

    在Linux里,使用readelf -a GPIO_LED_CM4.elf命令,可以获取ELF文件的更多信息。

    2.Remoteproc框架

    Remoteproc(Remote Processor Framework),主要作用就是对远程处理器的生命周期进行管理,即启动、停止远程处理器。
    以STM32MP157为例,Cortex-A内核先启动,然后使用Linux RemoteProc框架进行加载Cortex-M4固件,启动M4内核。

    ST官方提供的内核已经默认配置了Remoteproc驱动,进入系统后,首先将要运行的M4固件放在/lib/firmware/目录下,然后将固件名字写到/sys/class/remoteproc/remoteproc0/firmware,再操作/sys/class/remoteproc/remoteproc0/state启动、停止M4处理器。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [root@stm32mp157:~]# ls /lib/firmware/
    DEMO_LED_CM4.elf
    [root@stm32mp157:~]# echo GPIO_LED_CM4.elf > /sys/class/remoteproc/remoteproc0/firmware
    [root@stm32mp157:~]# cat /sys/class/remoteproc/remoteproc0/state
    offline
    [root@stm32mp157:~]# echo start > /sys/class/remoteproc/remoteproc0/state
    [22683.222322] remoteproc remoteproc0: powering up m4
    [22683.229097] remoteproc remoteproc0: Booting fw image GPIO_LED_CM4.elf, size 1899976
    [22683.235549] remoteproc remoteproc0: header-less resource table
    [22683.241235] remoteproc remoteproc0: not resource table found for this firmware
    [22683.248749] remoteproc remoteproc0: header-less resource table
    [22683.254414] remoteproc remoteproc0: remote processor m4 is now up
    [root@stm32mp157:~]# echo stop > /sys/class/remoteproc/remoteproc0/state
    [22709.281733] remoteproc remoteproc0: warning: remote FW shutdown without ack
    [22709.287325] remoteproc remoteproc0: stopped remote processor m4

    除了在Linux的用户态控制M4内核的生命周期,还能在Linux内核态使用API控制(参考linux-origin_master/Documentation/remoteproc.txt),甚至U-boot中控制。

    3.RPMsg框架

    Remoteproc框架实现了对远程处理器生命周期的管理,RPMsg框架(Remote Processor Messaging Framework)则是实现对远程处理器信息传递。
    RPMsg是基于VirtIO的消息总线,它允许内核驱动程序与系统上可用的远程处理器进行通信,同时,驱动程序可以根据需要公开适当的用户空间接口(参考linux-origin_master/Documentation/rpmsg.txt)。

    STM32MP1多核通信框架如下图。

    消息服务基于共享内存,使用RPMsgVirtio框架,RemoteProc框架则控制远程处理器生命周期。
    信号通知(Mailbox)服务则基于内部IPCC(Inter-Processor communication controller),ST提供OpenAMP相关库。

    这里列举两个示例:
    第一个示例在Linux的用户态和M4通信,实现A7控制M4的灯,A7和M4的相互唤醒;
    第二个示例则是在Linux的内核态创建一个简单的RPMsg客户端,实现A7和M4的大量数据传输。

    3.1 用户态的通信

    3.1.1 A7准备

    ST官方提供的内核已经默认配置了RPMSG_TTY驱动,Linux这边就不需要做什么了。
    STM32MP1多核消息通信应用接口框图如下,在RPMsgVirtio框架创建一个面向用户态的/dev/ttyRPMSG接口,M4在OpenAMP上创建虚拟串口,两者最终效果像是串口透传。

    3.1.2 M4准备

    创建一个STM32工程,在STM32CubeMX里,依次配置GPIO用于LED、配置UART5用于M4打印、以及配置IPCC和OPENAMP用于通信。

    注意配置IPCC时,需要在NVIC Settings选项卡里,将IPCC RX1 occupied interruptIPCC TX1 free interrupt
    的使能勾选上,不然后面的OPENAMP的Activated始终为灰色,无法激活。

    生成初始化代码后,在USER CODE BEGIN 0USER CODE END 0之间添加printf的重定向函数,让UART5与printf绑定。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /* USER CODE BEGIN 0 */
    #ifdef __GNUC__
    /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
    set to 'Yes') calls __io_putchar() */
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #else
    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif /* __GNUC__ */
    PUTCHAR_PROTOTYPE
    {
    /* Place your implementation of fputc here */
    HAL_UART_Transmit(&huart5, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
    return ch;
    }
    /* USER CODE END 0 */

    这里计划创建两个RPMsg tty通道,一个用来LED控制命令,一个用来传输唤醒命令。

    • 1.初始化两个RPMsg tty虚拟串口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    if (VIRT_UART_Init(&huart0) != VIRT_UART_OK) {
    printf("VIRT_UART_Init UART0 failed. ");
    Error_Handler();
    }
     
    if (VIRT_UART_Init(&huart1) != VIRT_UART_OK) {
    printf("VIRT_UART_Init UART1 failed. ");
    Error_Handler();
    }
    • 2.注册回调函数以按通道接收消息
      1
      2
      3
      4
      5
      6
      7
      8
      9
      if(VIRT_UART_RegisterCallback(&huart0, VIRT_UART_RXCPLT_CB_ID, VIRT_UART0_RxCpltCallback) != VIRT_UART_OK)
      {
      Error_Handler();
      }
       
      if(VIRT_UART_RegisterCallback(&huart1, VIRT_UART_RXCPLT_CB_ID, VIRT_UART1_RxCpltCallback) != VIRT_UART_OK)
      {
      Error_Handler();
      }
    • 3.编写虚拟串口回调函数
      当RPMsg收到数据后,将调用该回调函数。在此函数里,需要将接收的数据复制到用户内存,并修改接收标志位,通知用户完成数据接收。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      /* USER CODE BEGIN 4 */
      void VIRT_UART0_RxCpltCallback(VIRT_UART_HandleTypeDef *huart)
      {
      printf("Msg received on VIRTUAL UART0 channel: %s ", (char *) huart->pRxBuffPtr);
       
      /* copy received msg in a variable to sent it back to master processor in main infinite loop*/
      VirtUart0ChannelRxSize = huart->RxXferSize < MAX_BUFFER_SIZE? huart->RxXferSize : MAX_BUFFER_SIZE-1;
      memcpy(VirtUart0ChannelBuffRx, huart->pRxBuffPtr, VirtUart0ChannelRxSize);
      VirtUart0RxMsg = SET;
      }
       
      void VIRT_UART1_RxCpltCallback(VIRT_UART_HandleTypeDef *huart)
      {
      printf("Msg received on VIRTUAL UART1 channel: %s ", (char *) huart->pRxBuffPtr);
       
      /* copy received msg in a variable to sent it back to master processor in main infinite loop*/
      VirtUart1ChannelRxSize = huart->RxXferSize < MAX_BUFFER_SIZE? huart->RxXferSize : MAX_BUFFER_SIZE-1;
      memcpy(VirtUart1ChannelBuffRx, huart->pRxBuffPtr, VirtUart1ChannelRxSize);
      VirtUart1RxMsg = SET;
      }
      /* USER CODE END 4 */
    • 4.主函数轮询RPMsg消息
      OPENAMP_check_for_message()查询MailBox状态。
      当收到数据时,VIRT_UARTx_RxCpltCallback()会保存好收到数据,然后修改VirtUartxRxMsg标志位。
      主函数里发现VirtUartxRxMsg标志位发生变化时,即可获取接收的数据。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      while (1)
      {
      OPENAMP_check_for_message();
      /* USER CODE END WHILE */
       
      /* USER CODE BEGIN 3 */
      if (VirtUart0RxMsg)
      {
      VirtUart0RxMsg = RESET;
       
      /*VirUART0收到数据*/
      }
       
      if (VirtUart1RxMsg)
      {
      VirtUart1RxMsg = RESET;
       
      /*VirUART1收到数据*/
      }
      }
    • 5.VirUART0接收控制LED指令
      每次VirtUart0RxMsg发生变化,说明VirUART0收到了数据。
      然后比较收到的数据内容,执行对应的操作。
      这里,M4收到MSG_LED_ON(*led_on)则打开LED灯,并发送消息给A7;M4收到MSG_LED_OFF(*led_off)则关闭LED灯,并发送消息给A7。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      if (VirtUart0RxMsg)
      {
      VirtUart0RxMsg = RESET;
       
      if (!strncmp((char *)VirtUart0ChannelBuffRx, MSG_LED_ON, strlen(MSG_LED_ON)))
      {
      strcpy((char *)BuffTx, "m4:led on ");
      printf("%s ", BuffTx);
      VIRT_UART_Transmit(&huart0, BuffTx, strlen((const char *)BuffTx));
      HAL_GPIO_WritePin(LED_BLUE_GPIO_Port, LED_BLUE_Pin, GPIO_PIN_RESET);
      }
       
      if (!strncmp((char *)VirtUart0ChannelBuffRx, MSG_LED_OFF, strlen(MSG_LED_OFF)))
      {
      strcpy((char *)BuffTx, "m4:led off ");
      printf("%s ", BuffTx);
      VIRT_UART_Transmit(&huart0, BuffTx, strlen((const char *)BuffTx));
      HAL_GPIO_WritePin(LED_BLUE_GPIO_Port, LED_BLUE_Pin, GPIO_PIN_SET);
      }
      memset(VirtUart0ChannelBuffRx, 0 ,VirtUart0ChannelRxSize);
      memset(BuffTx, 0 ,strlen((const char *)BuffTx));
      }
    • 6.VirUART1接收休眠唤醒指令
      每次VirtUart1RxMsg发生变化,说明VirUART1收到了数据。
      然后比较收到的数据内容,执行对应的操作。
      这里,M4收到MSG_STOP(*stop)则进入CStop模式,中途A7再发任意数据给M4,由于IPCC也可设置为中断唤醒源,将M4唤醒;M4收到MSG_DELAY(*delay)则等待20S后发数据给A7,在这20S内,将A7先休眠,随后将被M4唤醒。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      if (VirtUart1RxMsg)
      {
      VirtUart1RxMsg = RESET;
       
      if (!strncmp((char *)VirtUart1ChannelBuffRx, MSG_STOP, strlen(MSG_STOP)))
      {
      strcpy((char *)BuffTx, "m4:stop ");
      printf("%s ", BuffTx);
      VIRT_UART_Transmit(&huart1, BuffTx, strlen((const char *)BuffTx));
       
      //RCC_backupClocks();
      /* Clear the MCU flags before going into CSTOP */
      SET_BIT(PWR->MCUCR, PWR_MCUCR_CSSF);
       
      printf("Going to CStop mode ");
      /* (C)STOP protection mechanism
      * Only the IT with the highest priority (0 value) can interrupt.
      * RCC_WAKEUP_IRQn IT is intended to have the highest priority and to be the
      * only one IT having this value
      * RCC_WAKEUP_IRQn is generated only when RCC is completely resumed from
      * CSTOP */
      __set_BASEPRI(1 << (8 - __NVIC_PRIO_BITS));
       
      HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI);
       
      /* To allow Systick to increment after CSTOP (Eg.: to not block during
      * TIMEOUT routines), TICK_INT_PRIORITY < BASEPRI
      * For this example as TICK_INT_PRIORITY = 1, BASEPRI should be 2 */
      __set_BASEPRI(2 << (8 - __NVIC_PRIO_BITS));
       
      printf("Leaving CStop mode ");
       
      /* Test if system was on STOP mode */
      if( (PWR->MCUCR & PWR_MCUCR_STOPF) == PWR_MCUCR_STOPF)
      {
      printf("System was on STOP mode ");
       
      /* Clear the MCU flags */
      SET_BIT(PWR->MCUCR, PWR_MCUCR_CSSF);
       
      /* Restore clocks */
      /*
      if (RCC_restoreClocks() == HAL_OK)
      {
      printf("CM4 restored clocks successfully ");
      }
      */
      }
       
      /* All level of ITs can interrupt */
      __set_BASEPRI(0U);
      }
       
      if (!strncmp((char *)VirtUart1ChannelBuffRx, MSG_DELAY, strlen(MSG_DELAY)))
      {
      printf("Waiting 20 secs before sending the answer message ");
      HAL_Delay(20 *1000);
       
      strcpy((char *)BuffTx, "m4:wakeup A7 ");
      printf("%s ", BuffTx);
      VIRT_UART_Transmit(&huart1, BuffTx, strlen((const char *)BuffTx));
      }
      memset(VirtUart1ChannelBuffRx, 0 ,VirtUart1ChannelRxSize);
      memset(BuffTx, 0 ,strlen((const char *)BuffTx));
      }

    为了A7能发消息将M4唤醒,还需要IPCC作为M4的中断唤醒源。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    EXTI_ConfigTypeDef EXTI_ConfigStructure;
    EXTI_HandleTypeDef hexti62;
    /*
    * Set configuration of Exti line 62 (IPCC interrupt CPU2). It could be used to wakeup the
    * M4 from CStop mode when RPMsg received from Cortex-A7
    */
    EXTI_ConfigStructure.Line = EXTI_LINE_62;
    EXTI_ConfigStructure.Mode = EXTI_MODE_C2_INTERRUPT;
    //PERIPH_LOCK(EXTI);
    HAL_EXTI_SetConfigLine(&hexti62, &EXTI_ConfigStructure);
    //PERIPH_UNLOCK(EXTI);
     
    /*
    * Enable RCC_IT_WKUP to exit M4 from CStop mode.
    * Indeed, due to SOC issue, M4 firmware shall make sure
    * RCC_WAKEUP interrupt is the first one used to exit M4 from CStop mode.
    * Therefore, M4 masks all NVIC interrupts with priority higher than 0
    * before entering CStop mode and unmasks them when moving from WFI.
    * (in HAL_PWR_EnterSTOPMode function)
    * Note: All other NVIC interrupts shall be set to a different value
    * from 0 to make sure that this workaround works well.
    */
    __HAL_RCC_ENABLE_IT(RCC_IT_WKUP);

    3.1.3 M4完整代码

    [main.c]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    /* USER CODE BEGIN Header */
    /**
    ******************************************************************************
    * @file : main.c
    * @brief : Main program body
    ******************************************************************************
    * @attention
    *
    * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
    * All rights reserved.</center></h2>
    *
    * This software component is licensed by ST under BSD 3-Clause license,
    * the "License"; You may not use this file except in compliance with the
    * License. You may obtain a copy of the License at:
    * opensource.org/licenses/BSD-3-Clause
    *
    ******************************************************************************
    */
    /* USER CODE END Header */
     
    /* Includes ------------------------------------------------------------------*/
    #include "main.h"
    #include "openamp.h"
     
    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include "virt_uart.h"
    /* USER CODE END Includes */
     
    /* Private typedef -----------------------------------------------------------*/
    /* USER CODE BEGIN PTD */
     
    /* USER CODE END PTD */
     
    /* Private define ------------------------------------------------------------*/
    /* USER CODE BEGIN PD */
    #define MAX_BUFFER_SIZE RPMSG_BUFFER_SIZE
    /* USER CODE END PD */
     
    /* Private macro -------------------------------------------------------------*/
    /* USER CODE BEGIN PM */
     
    /* USER CODE END PM */
     
    /* Private variables ---------------------------------------------------------*/
    IPCC_HandleTypeDef hipcc;
     
    UART_HandleTypeDef huart5;
     
    /* USER CODE BEGIN PV */
    VIRT_UART_HandleTypeDef huart0;
    VIRT_UART_HandleTypeDef huart1;
     
    __IO FlagStatus VirtUart0RxMsg = RESET;
    uint8_t VirtUart0ChannelBuffRx[MAX_BUFFER_SIZE];
    uint16_t VirtUart0ChannelRxSize = 0;
     
    __IO FlagStatus VirtUart1RxMsg = RESET;
    uint8_t VirtUart1ChannelBuffRx[MAX_BUFFER_SIZE];
    uint16_t VirtUart1ChannelRxSize = 0;
     
    uint8_t BuffTx[MAX_BUFFER_SIZE];
     
    #define MSG_LED_ON "*led_on"
    #define MSG_LED_OFF "*led_off"
    #define MSG_STOP "*stop"
    #define MSG_DELAY "*delay"
    /* USER CODE END PV */
     
    /* Private function prototypes -----------------------------------------------*/
    void SystemClock_Config(void);
    static void MX_GPIO_Init(void);
    static void MX_IPCC_Init(void);
    static void MX_UART5_Init(void);
    int MX_OPENAMP_Init(int RPMsgRole, rpmsg_ns_bind_cb ns_bind_cb);
    /* USER CODE BEGIN PFP */
    void VIRT_UART0_RxCpltCallback(VIRT_UART_HandleTypeDef *huart);
    void VIRT_UART1_RxCpltCallback(VIRT_UART_HandleTypeDef *huart);
    /* USER CODE END PFP */
     
    /* Private user code ---------------------------------------------------------*/
    /* USER CODE BEGIN 0 */
    #ifdef __GNUC__
    /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
    set to 'Yes') calls __io_putchar() */
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #else
    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif /* __GNUC__ */
    PUTCHAR_PROTOTYPE
    {
    /* Place your implementation of fputc here */
    HAL_UART_Transmit(&huart5, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
    return ch;
    }
    /* USER CODE END 0 */
     
    /**
    * @brief The application entry point.
    * @retval int
    */
    int main(void)
    {
    /* USER CODE BEGIN 1 */
     
    /* USER CODE END 1 */
     
    /* MCU Configuration--------------------------------------------------------*/
     
    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();
     
    /* USER CODE BEGIN Init */
     
    /* USER CODE END Init */
     
    if(IS_ENGINEERING_BOOT_MODE())
    {
    /* Configure the system clock */
    SystemClock_Config();
    }
     
    /* IPCC initialisation */
    MX_IPCC_Init();
    /* OpenAmp initialisation ---------------------------------*/
    MX_OPENAMP_Init(RPMSG_REMOTE, NULL);
     
    /* USER CODE BEGIN SysInit */
     
    /* USER CODE END SysInit */
     
    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_UART5_Init();
    /* USER CODE BEGIN 2 */
    printf("RPMsg user mode test ");
     
    if (VIRT_UART_Init(&huart0) != VIRT_UART_OK) {
    printf("VIRT_UART_Init UART0 failed. ");
    Error_Handler();
    }
     
    if (VIRT_UART_Init(&huart1) != VIRT_UART_OK) {
    printf("VIRT_UART_Init UART1 failed. ");
    Error_Handler();
    }
     
    if(VIRT_UART_RegisterCallback(&huart0, VIRT_UART_RXCPLT_CB_ID, VIRT_UART0_RxCpltCallback) != VIRT_UART_OK)
    {
    Error_Handler();
    }
     
    if(VIRT_UART_RegisterCallback(&huart1, VIRT_UART_RXCPLT_CB_ID, VIRT_UART1_RxCpltCallback) != VIRT_UART_OK)
    {
    Error_Handler();
    }
     
    EXTI_ConfigTypeDef EXTI_ConfigStructure;
    EXTI_HandleTypeDef hexti62;
    /*
    * Set configuration of Exti line 62 (IPCC interrupt CPU2). It could be used to wakeup the
    * M4 from CStop mode when RPMsg received from Cortex-A7
    */
    EXTI_ConfigStructure.Line = EXTI_LINE_62;
    EXTI_ConfigStructure.Mode = EXTI_MODE_C2_INTERRUPT;
    //PERIPH_LOCK(EXTI);
    HAL_EXTI_SetConfigLine(&hexti62, &EXTI_ConfigStructure);
    //PERIPH_UNLOCK(EXTI);
     
    /*
    * Enable RCC_IT_WKUP to exit M4 from CStop mode.
    * Indeed, due to SOC issue, M4 firmware shall make sure
    * RCC_WAKEUP interrupt is the first one used to exit M4 from CStop mode.
    * Therefore, M4 masks all NVIC interrupts with priority higher than 0
    * before entering CStop mode and unmasks them when moving from WFI.
    * (in HAL_PWR_EnterSTOPMode function)
    * Note: All other NVIC interrupts shall be set to a different value
    * from 0 to make sure that this workaround works well.
    */
    __HAL_RCC_ENABLE_IT(RCC_IT_WKUP);
    /* USER CODE END 2 */
     
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
    OPENAMP_check_for_message();
    /* USER CODE END WHILE */
     
    /* USER CODE BEGIN 3 */
    if (VirtUart0RxMsg)
    {
    VirtUart0RxMsg = RESET;
     
    if (!strncmp((char *)VirtUart0ChannelBuffRx, MSG_LED_ON, strlen(MSG_LED_ON)))
    {
    strcpy((char *)BuffTx, "m4:led on ");
    printf("%s ", BuffTx);
    VIRT_UART_Transmit(&huart0, BuffTx, strlen((const char *)BuffTx));
    HAL_GPIO_WritePin(LED_BLUE_GPIO_Port, LED_BLUE_Pin, GPIO_PIN_RESET);
    }
     
    if (!strncmp((char *)VirtUart0ChannelBuffRx, MSG_LED_OFF, strlen(MSG_LED_OFF)))
    {
    strcpy((char *)BuffTx, "m4:led off ");
    printf("%s ", BuffTx);
    VIRT_UART_Transmit(&huart0, BuffTx, strlen((const char *)BuffTx));
    HAL_GPIO_WritePin(LED_BLUE_GPIO_Port, LED_BLUE_Pin, GPIO_PIN_SET);
    }
    memset(VirtUart0ChannelBuffRx, 0 ,VirtUart0ChannelRxSize);
    memset(BuffTx, 0 ,strlen((const char *)BuffTx));
    }
     
    if (VirtUart1RxMsg)
    {
    VirtUart1RxMsg = RESET;
     
    if (!strncmp((char *)VirtUart1ChannelBuffRx, MSG_STOP, strlen(MSG_STOP)))
    {
    strcpy((char *)BuffTx, "m4:stop ");
    printf("%s ", BuffTx);
    VIRT_UART_Transmit(&huart1, BuffTx, strlen((const char *)BuffTx));
     
    //RCC_backupClocks();
    /* Clear the MCU flags before going into CSTOP */
    SET_BIT(PWR->MCUCR, PWR_MCUCR_CSSF);
     
    printf("Going to CStop mode ");
    /* (C)STOP protection mechanism
    * Only the IT with the highest priority (0 value) can interrupt.
    * RCC_WAKEUP_IRQn IT is intended to have the highest priority and to be the
    * only one IT having this value
    * RCC_WAKEUP_IRQn is generated only when RCC is completely resumed from
    * CSTOP */
    __set_BASEPRI(1 << (8 - __NVIC_PRIO_BITS));
     
    HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI);
     
    /* To allow Systick to increment after CSTOP (Eg.: to not block during
    * TIMEOUT routines), TICK_INT_PRIORITY < BASEPRI
    * For this example as TICK_INT_PRIORITY = 1, BASEPRI should be 2 */
    __set_BASEPRI(2 << (8 - __NVIC_PRIO_BITS));
     
    printf("Leaving CStop mode ");
     
    /* Test if system was on STOP mode */
    if( (PWR->MCUCR & PWR_MCUCR_STOPF) == PWR_MCUCR_STOPF)
    {
    printf("System was on STOP mode ");
     
    /* Clear the MCU flags */
    SET_BIT(PWR->MCUCR, PWR_MCUCR_CSSF);
     
    /* Restore clocks */
    /*
    if (RCC_restoreClocks() == HAL_OK)
    {
    printf("CM4 restored clocks successfully ");
    }
    */
    }
     
    /* All level of ITs can interrupt */
    __set_BASEPRI(0U);
    }
     
    if (!strncmp((char *)VirtUart1ChannelBuffRx, MSG_DELAY, strlen(MSG_DELAY)))
    {
    printf("Waiting 20 secs before sending the answer message ");
    HAL_Delay(20 *1000);
     
    strcpy((char *)BuffTx, "m4:wakeup A7 ");
    printf("%s ", BuffTx);
    VIRT_UART_Transmit(&huart1, BuffTx, strlen((const char *)BuffTx));
    }
    memset(VirtUart1ChannelBuffRx, 0 ,VirtUart1ChannelRxSize);
    memset(BuffTx, 0 ,strlen((const char *)BuffTx));
    }
     
    }
    /* USER CODE END 3 */
    }
     
    /**
    * @brief System Clock Configuration
    * @retval None
    */
    void SystemClock_Config(void)
    {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
     
    /** Initializes the CPU, AHB and APB busses clocks
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = 16;
    RCC_OscInitStruct.HSIDivValue = RCC_HSI_DIV1;
    RCC_OscInitStruct.LSIState = RCC_LSI_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
    RCC_OscInitStruct.PLL2.PLLState = RCC_PLL_NONE;
    RCC_OscInitStruct.PLL3.PLLState = RCC_PLL_NONE;
    RCC_OscInitStruct.PLL4.PLLState = RCC_PLL_NONE;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
    Error_Handler();
    }
    /** RCC Clock Config
    */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_ACLK
    |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
    |RCC_CLOCKTYPE_PCLK3|RCC_CLOCKTYPE_PCLK4
    |RCC_CLOCKTYPE_PCLK5;
    RCC_ClkInitStruct.AXISSInit.AXI_Clock = RCC_AXISSOURCE_HSI;
    RCC_ClkInitStruct.AXISSInit.AXI_Div = RCC_AXI_DIV1;
    RCC_ClkInitStruct.MCUInit.MCU_Clock = RCC_MCUSSOURCE_HSI;
    RCC_ClkInitStruct.MCUInit.MCU_Div = RCC_MCU_DIV1;
    RCC_ClkInitStruct.APB4_Div = RCC_APB4_DIV1;
    RCC_ClkInitStruct.APB5_Div = RCC_APB5_DIV1;
    RCC_ClkInitStruct.APB1_Div = RCC_APB1_DIV1;
    RCC_ClkInitStruct.APB2_Div = RCC_APB2_DIV1;
    RCC_ClkInitStruct.APB3_Div = RCC_APB3_DIV1;
     
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct) != HAL_OK)
    {
    Error_Handler();
    }
    }
     
    /**
    * @brief IPCC Initialization Function
    * @param None
    * @retval None
    */
    static void MX_IPCC_Init(void)
    {
     
    /* USER CODE BEGIN IPCC_Init 0 */
     
    /* USER CODE END IPCC_Init 0 */
     
    /* USER CODE BEGIN IPCC_Init 1 */
     
    /* USER CODE END IPCC_Init 1 */
    hipcc.Instance = IPCC;
    if (HAL_IPCC_Init(&hipcc) != HAL_OK)
    {
    Error_Handler();
    }
    /* USER CODE BEGIN IPCC_Init 2 */
     
    /* USER CODE END IPCC_Init 2 */
     
    }
     
    /**
    * @brief UART5 Initialization Function
    * @param None
    * @retval None
    */
    static void MX_UART5_Init(void)
    {
     
    /* USER CODE BEGIN UART5_Init 0 */
     
    /* USER CODE END UART5_Init 0 */
     
    /* USER CODE BEGIN UART5_Init 1 */
     
    /* USER CODE END UART5_Init 1 */
    huart5.Instance = UART5;
    huart5.Init.BaudRate = 115200;
    huart5.Init.WordLength = UART_WORDLENGTH_8B;
    huart5.Init.StopBits = UART_STOPBITS_1;
    huart5.Init.Parity = UART_PARITY_NONE;
    huart5.Init.Mode = UART_MODE_TX_RX;
    huart5.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart5.Init.OverSampling = UART_OVERSAMPLING_16;
    huart5.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
    huart5.Init.ClockPrescaler = UART_PRESCALER_DIV1;
    huart5.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
    if (HAL_UART_Init(&huart5) != HAL_OK)
    {
    Error_Handler();
    }
    if (HAL_UARTEx_SetTxFifoThreshold(&huart5, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
    {
    Error_Handler();
    }
    if (HAL_UARTEx_SetRxFifoThreshold(&huart5, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
    {
    Error_Handler();
    }
    if (HAL_UARTEx_DisableFifoMode(&huart5) != HAL_OK)
    {
    Error_Handler();
    }
    /* USER CODE BEGIN UART5_Init 2 */
     
    /* USER CODE END UART5_Init 2 */
     
    }
     
    /**
    * @brief GPIO Initialization Function
    * @param None
    * @retval None
    */
    static void MX_GPIO_Init(void)
    {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
     
    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
     
    /*Configure GPIO pin : LED_BLUE_Pin */
    GPIO_InitStruct.Pin = LED_BLUE_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(LED_BLUE_GPIO_Port, &GPIO_InitStruct);
     
    }
     
    /* USER CODE BEGIN 4 */
    void VIRT_UART0_RxCpltCallback(VIRT_UART_HandleTypeDef *huart)
    {
    printf("Msg received on VIRTUAL UART0 channel: %s ", (char *) huart->pRxBuffPtr);
     
    /* copy received msg in a variable to sent it back to master processor in main infinite loop*/
    VirtUart0ChannelRxSize = huart->RxXferSize < MAX_BUFFER_SIZE? huart->RxXferSize : MAX_BUFFER_SIZE-1;
    memcpy(VirtUart0ChannelBuffRx, huart->pRxBuffPtr, VirtUart0ChannelRxSize);
    VirtUart0RxMsg = SET;
    }
     
    void VIRT_UART1_RxCpltCallback(VIRT_UART_HandleTypeDef *huart)
    {
    printf("Msg received on VIRTUAL UART1 channel: %s ", (char *) huart->pRxBuffPtr);
     
    /* copy received msg in a variable to sent it back to master processor in main infinite loop*/
    VirtUart1ChannelRxSize = huart->RxXferSize < MAX_BUFFER_SIZE? huart->RxXferSize : MAX_BUFFER_SIZE-1;
    memcpy(VirtUart1ChannelBuffRx, huart->pRxBuffPtr, VirtUart1ChannelRxSize);
    VirtUart1RxMsg = SET;
    }
    /* USER CODE END 4 */
     
    /**
    * @brief This function is executed in case of error occurrence.
    * @retval None
    */
    void Error_Handler(void)
    {
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
     
    /* USER CODE END Error_Handler_Debug */
    }
     
    #ifdef USE_FULL_ASSERT
    /**
    * @brief Reports the name of the source file and the source line number
    * where the assert_param error has occurred.
    * @param file: pointer to the source file name
    * @param line: assert_param error line source number
    * @retval None
    */
    void assert_failed(uint8_t *file, uint32_t line)
    {
    /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number,
    tex: printf("Wrong parameters value: file %s on line %d ", file, line) */
    /* USER CODE END 6 */
    }
    #endif /* USE_FULL_ASSERT */
     
    /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

    3.1.4 测试效果

    • 1.测试A7命令M4控制LED灯
      首先将前面编译生成的rpmsg_user_CM4.elf放在/lib/firmware目录下
      然后启动固件,此时看到打印信息里提示创建了两个通道ttyRPMSG0ttyRPMSG1,同时M4也打印了测试开始信息。
      1
      2
      3
      cd /sys/class/remoteproc/remoteproc0
      echo rpmsg_user_CM4.elf > firmware
      echo start > state

    然后还需要设置虚拟串口/dev/ttyRPMSG0
    -onlcr是不将NL字符映射为CR-NL字符,就是说发送给M4的数据,不会自动加上回车,不然这里发送led_on,M4收到的为led_on ,正确的应该是收到*led_on
    -echo是禁止回显,以方便查看接收的字符。

    1
    2
    stty -onlcr -echo -F /dev/ttyRPMSG0
    cat /dev/ttyRPMSG0 &

    最后,向/dev/ttyRPMSG0写入预定义的指令,M4收到指令便会控制LED灯,并发送结果给A7。

    1
    2
    echo "*led_on" > /dev/ttyRPMSG0
    echo "*led_off" > /dev/ttyRPMSG0
    • 2.测试A7和M4休眠唤醒
      接着前面,启动M4后。
      首先设置虚拟串口/dev/ttyRPMSG1
      1
      2
      stty -onlcr -echo -F /dev/ttyRPMSG1
      cat /dev/ttyRPMSG1 &

    然后向/dev/ttyRPMSG1写入*stop,M4随后卡在打印Going to CStop mode后,接着向/dev/ttyRPMSG1写入wakeup(任意字符即可),M4随后打印Leaving CStop mode。即实现了A7控制M4的休眠和唤醒。

    1
    2
    echo "*stop" > /dev/ttyRPMSG1
    echo "wakeup" >/dev/ttyRPMSG1

    再测试M4唤醒A7。
    先使能A7唤醒,然后向/dev/ttyRPMSG1写入*delay,M4收到指令后,20S后会向A7发数据,从而唤醒A7。
    此时控制A7进入休眠状态,20S后,A7被唤醒。

    1
    2
    3
    echo enabled > /sys/devices/platform/soc/4c001000.mailbox/power/wakeup
    echo "*delay" > /dev/ttyRPMSG1
    echo mem > /sys/power/state

    3.2 内核态的通信

    3.2.1 A7准备

    内核已经提供了示例驱动程序linux-origin_master/samples/rpmsg/rpmsg_client_sample.c,这里直接使用该驱动,简单的修改了下打印内容,方便查看。

    [rpmsg_client_sample.c]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    /*
    * Remote processor messaging - sample client driver
    *
    * Copyright (C) 2011 Texas Instruments, Inc.
    * Copyright (C) 2011 Google, Inc.
    *
    * Ohad Ben-Cohen <ohad@wizery.com>
    * Brian Swetland <swetland@google.com>
    *
    * This software is licensed under the terms of the GNU General Public
    * License version 2, as published by the Free Software Foundation, and
    * may be copied, distributed, and modified under those terms.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU General Public License for more details.
    */
     
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/rpmsg.h>
     
    #define MSG "hello world! A7->M4"
    #define MSG_LIMIT 100
     
    struct instance_data {
    int rx_count;
    };
     
    static int rpmsg_sample_cb(struct rpmsg_device *rpdev, void *data, int len,
    void *priv, u32 src)
    {
    int ret;
    struct instance_data *idata = dev_get_drvdata(&rpdev->dev);
     
    //dev_info(&rpdev->dev, "incoming msg %d (src: 0x%x) ", ++idata->rx_count, src);
    //print_hex_dump(KERN_DEBUG, __func__, DUMP_PREFIX_NONE, 16, 1, data, len, true);
     
    printk(KERN_DEBUG "received_rpmsg: %s %d ", data, ++idata->rx_count);
     
     
    /* samples should not live forever */
    if (idata->rx_count >= MSG_LIMIT) {
    //dev_info(&rpdev->dev, "goodbye! ");
    return 0;
    }
     
    /* send a new message now */
    ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG));
    if (ret)
    dev_err(&rpdev->dev, "rpmsg_send failed: %d ", ret);
    else
    printk(KERN_DEBUG "send_rpmsg: %s ", MSG);
     
    return 0;
    }
     
    static int rpmsg_sample_probe(struct rpmsg_device *rpdev)
    {
    int ret;
    struct instance_data *idata;
     
    dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x! ",
    rpdev->src, rpdev->dst);
     
    idata = devm_kzalloc(&rpdev->dev, sizeof(*idata), GFP_KERNEL);
    if (!idata)
    return -ENOMEM;
     
    dev_set_drvdata(&rpdev->dev, idata);
     
    /* send a message to our remote processor */
    ret = rpmsg_send(rpdev->ept, MSG, strlen(MSG));
    if (ret) {
    dev_err(&rpdev->dev, "rpmsg_send failed: %d ", ret);
    return ret;
    }
    else
    printk(KERN_DEBUG "send_rpmsg: %s ", MSG);
     
    return 0;
    }
     
    static void rpmsg_sample_remove(struct rpmsg_device *rpdev)
    {
    dev_info(&rpdev->dev, "rpmsg sample client driver is removed ");
    }
     
    static struct rpmsg_device_id rpmsg_driver_sample_id_table[] = {
    { .name = "rpmsg-client-sample" },
    { },
    };
    MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sample_id_table);
     
    static struct rpmsg_driver rpmsg_sample_client = {
    .drv.name = KBUILD_MODNAME,
    .id_table = rpmsg_driver_sample_id_table,
    .probe = rpmsg_sample_probe,
    .callback = rpmsg_sample_cb,
    .remove = rpmsg_sample_remove,
    };
    module_rpmsg_driver(rpmsg_sample_client);
     
    MODULE_DESCRIPTION("Remote processor messaging sample client driver");
    MODULE_LICENSE("GPL v2");

    驱动比较简单,核心是rpmsg_sample_cb()rpmsg收到数据后将回调该函数。
    data获得M4发来的数据,通过rpmsg_send()将数据发给M4。
    将该驱动编译成模块,并在A7中加载。

    3.2.2 M4准备

    参考前面的示例,依次在STM32CubeMX配置UART5、IPCC和OPENAMP,然后生成初始化代码。

    • 1.建一个rpmsg通道
      1
      OPENAMP_create_endpoint(&resmgr_ept, RPMSG_SERVICE_NAME, RPMSG_ADDR_ANY, rx_callback, NULL);

    注意这里的RPMSG_SERVICE_NAME和驱动里rpmsg_device_id结构体的.name的名字一样。
    之后,一旦rpmsg通道数据,将回调rx_callback()函数。

    • 2.编写接收回调函数
      此函数里,需要将接收的数据复制到用户内存,并修改接收标志位。

      1
      2
      3
      4
      5
      6
      7
      static int rx_callback(struct rpmsg_endpoint *rp_chnl, void *data, size_t len, uint32_t src, void *priv)
      {
      /* copy received msg, and raise a flag */
      memcpy(received_rpmsg, data, len > sizeof(received_rpmsg) ? sizeof(received_rpmsg) : len);
      printf("received_rpmsg=%s ", received_rpmsg);
      rx_status = SET;
      return 0;
    • 3.主函数轮询RPMsg消息
      OPENAMP_check_for_message()查询MailBox状态。
      当收到数据时,rx_callback()会保存好收到数据,然后修改rx_status标志位。
      主函数里发现rx_status标志位发生变化时,即可获取接收的数据。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      while (1)
      {
      OPENAMP_check_for_message();
      /* USER CODE END WHILE */
      if (rx_status == SET)
      {
      /* Message received: send back a message anwser */
      rx_status = RESET;
       
      /*rpmsg收到数据*/
      }
      /* USER CODE BEGIN 3 */
      }
    • 4.向M4发送数据
      使用OPENAMP_send()向A7发送数据。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      if (++count < 100)
      sprintf((char *)msg, "hello world! M4->A7 %02ld", count);
      else
      strcpy((char *)msg, "goodbye!");
       
      if (OPENAMP_send(&resmgr_ept, msg, strlen((char *)msg) + 1) < 0)
      {
      printf("Failed to send message ");
      Error_Handler();
      }
      else
      printf("send_rpmsg=%s ", msg);

    3.2.3 M4完整代码

    [main.c]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    /* USER CODE BEGIN Header */
    /**
    ******************************************************************************
    * @file : main.c
    * @brief : Main program body
    ******************************************************************************
    * @attention
    *
    * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
    * All rights reserved.</center></h2>
    *
    * This software component is licensed by ST under BSD 3-Clause license,
    * the "License"; You may not use this file except in compliance with the
    * License. You may obtain a copy of the License at:
    * opensource.org/licenses/BSD-3-Clause
    *
    ******************************************************************************
    */
    /* USER CODE END Header */
     
    /* Includes ------------------------------------------------------------------*/
    #include "main.h"
    #include "openamp.h"
     
    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
     
    /* USER CODE END Includes */
     
    /* Private typedef -----------------------------------------------------------*/
    /* USER CODE BEGIN PTD */
     
    /* USER CODE END PTD */
     
    /* Private define ------------------------------------------------------------*/
    /* USER CODE BEGIN PD */
    #define RPMSG_SERVICE_NAME "rpmsg-client-sample"
    /* USER CODE END PD */
     
    /* Private macro -------------------------------------------------------------*/
    /* USER CODE BEGIN PM */
     
    /* USER CODE END PM */
     
    /* Private variables ---------------------------------------------------------*/
    IPCC_HandleTypeDef hipcc;
     
    UART_HandleTypeDef huart5;
     
    /* USER CODE BEGIN PV */
    __IO FlagStatus rx_status = RESET;
    uint8_t received_rpmsg[128];
    /* USER CODE END PV */
     
    /* Private function prototypes -----------------------------------------------*/
    void SystemClock_Config(void);
    static void MX_GPIO_Init(void);
    static void MX_IPCC_Init(void);
    static void MX_UART5_Init(void);
    int MX_OPENAMP_Init(int RPMsgRole, rpmsg_ns_bind_cb ns_bind_cb);
    /* USER CODE BEGIN PFP */
    static int rx_callback(struct rpmsg_endpoint *rp_chnl, void *data, size_t len, uint32_t src, void *priv);
    /* USER CODE END PFP */
     
    /* Private user code ---------------------------------------------------------*/
    /* USER CODE BEGIN 0 */
    #ifdef __GNUC__
    /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
    set to 'Yes') calls __io_putchar() */
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #else
    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif /* __GNUC__ */
    PUTCHAR_PROTOTYPE
    {
    /* Place your implementation of fputc here */
    HAL_UART_Transmit(&huart5, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
    return ch;
    }
    /* USER CODE END 0 */
     
    /**
    * @brief The application entry point.
    * @retval int
    */
    int main(void)
    {
    /* USER CODE BEGIN 1 */
    struct rpmsg_endpoint resmgr_ept;
    uint32_t count = 0;
    uint8_t msg[32];
    /* USER CODE END 1 */
     
    /* MCU Configuration--------------------------------------------------------*/
     
    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();
     
    /* USER CODE BEGIN Init */
     
    /* USER CODE END Init */
     
    if(IS_ENGINEERING_BOOT_MODE())
    {
    /* Configure the system clock */
    SystemClock_Config();
    }
     
    /* IPCC initialisation */
    MX_IPCC_Init();
    /* OpenAmp initialisation ---------------------------------*/
    MX_OPENAMP_Init(RPMSG_REMOTE, NULL);
     
    /* USER CODE BEGIN SysInit */
     
    /* USER CODE END SysInit */
     
    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_UART5_Init();
    /* USER CODE BEGIN 2 */
    printf("RPMsg kernel mode test ");
    /* Create an rpmsg channel to communicate with the Master processor CPU1(CA7) */
    OPENAMP_create_endpoint(&resmgr_ept, RPMSG_SERVICE_NAME, RPMSG_ADDR_ANY,
    rx_callback, NULL);
    /* USER CODE END 2 */
     
    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1)
    {
    OPENAMP_check_for_message();
    /* USER CODE END WHILE */
    if (rx_status == SET)
    {
    /* Message received: send back a message anwser */
    rx_status = RESET;
     
    if (++count < 100)
    sprintf((char *)msg, "hello world! M4->A7 %02ld", count);
    else
    strcpy((char *)msg, "goodbye!");
     
    if (OPENAMP_send(&resmgr_ept, msg, strlen((char *)msg) + 1) < 0)
    {
    printf("Failed to send message ");
    Error_Handler();
    }
    else
    printf("send_rpmsg=%s ", msg);
    }
    /* USER CODE BEGIN 3 */
    }
    /* USER CODE END 3 */
    }
     
    /**
    * @brief System Clock Configuration
    * @retval None
    */
    void SystemClock_Config(void)
    {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
     
    /** Initializes the CPU, AHB and APB busses clocks
    */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = 16;
    RCC_OscInitStruct.HSIDivValue = RCC_HSI_DIV1;
    RCC_OscInitStruct.LSIState = RCC_LSI_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
    RCC_OscInitStruct.PLL2.PLLState = RCC_PLL_NONE;
    RCC_OscInitStruct.PLL3.PLLState = RCC_PLL_NONE;
    RCC_OscInitStruct.PLL4.PLLState = RCC_PLL_NONE;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
    Error_Handler();
    }
    /** RCC Clock Config
    */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_ACLK
    |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
    |RCC_CLOCKTYPE_PCLK3|RCC_CLOCKTYPE_PCLK4
    |RCC_CLOCKTYPE_PCLK5;
    RCC_ClkInitStruct.AXISSInit.AXI_Clock = RCC_AXISSOURCE_HSI;
    RCC_ClkInitStruct.AXISSInit.AXI_Div = RCC_AXI_DIV1;
    RCC_ClkInitStruct.MCUInit.MCU_Clock = RCC_MCUSSOURCE_HSI;
    RCC_ClkInitStruct.MCUInit.MCU_Div = RCC_MCU_DIV1;
    RCC_ClkInitStruct.APB4_Div = RCC_APB4_DIV1;
    RCC_ClkInitStruct.APB5_Div = RCC_APB5_DIV1;
    RCC_ClkInitStruct.APB1_Div = RCC_APB1_DIV1;
    RCC_ClkInitStruct.APB2_Div = RCC_APB2_DIV1;
    RCC_ClkInitStruct.APB3_Div = RCC_APB3_DIV1;
     
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct) != HAL_OK)
    {
    Error_Handler();
    }
    }
     
    /**
    * @brief IPCC Initialization Function
    * @param None
    * @retval None
    */
    static void MX_IPCC_Init(void)
    {
     
    /* USER CODE BEGIN IPCC_Init 0 */
     
    /* USER CODE END IPCC_Init 0 */
     
    /* USER CODE BEGIN IPCC_Init 1 */
     
    /* USER CODE END IPCC_Init 1 */
    hipcc.Instance = IPCC;
    if (HAL_IPCC_Init(&hipcc) != HAL_OK)
    {
    Error_Handler();
    }
    /* USER CODE BEGIN IPCC_Init 2 */
     
    /* USER CODE END IPCC_Init 2 */
     
    }
     
    /**
    * @brief UART5 Initialization Function
    * @param None
    * @retval None
    */
    static void MX_UART5_Init(void)
    {
     
    /* USER CODE BEGIN UART5_Init 0 */
     
    /* USER CODE END UART5_Init 0 */
     
    /* USER CODE BEGIN UART5_Init 1 */
     
    /* USER CODE END UART5_Init 1 */
    huart5.Instance = UART5;
    huart5.Init.BaudRate = 115200;
    huart5.Init.WordLength = UART_WORDLENGTH_8B;
    huart5.Init.StopBits = UART_STOPBITS_1;
    huart5.Init.Parity = UART_PARITY_NONE;
    huart5.Init.Mode = UART_MODE_TX_RX;
    huart5.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart5.Init.OverSampling = UART_OVERSAMPLING_16;
    huart5.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
    huart5.Init.ClockPrescaler = UART_PRESCALER_DIV1;
    huart5.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
    if (HAL_UART_Init(&huart5) != HAL_OK)
    {
    Error_Handler();
    }
    if (HAL_UARTEx_SetTxFifoThreshold(&huart5, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
    {
    Error_Handler();
    }
    if (HAL_UARTEx_SetRxFifoThreshold(&huart5, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
    {
    Error_Handler();
    }
    if (HAL_UARTEx_DisableFifoMode(&huart5) != HAL_OK)
    {
    Error_Handler();
    }
    /* USER CODE BEGIN UART5_Init 2 */
     
    /* USER CODE END UART5_Init 2 */
     
    }
     
    /**
    * @brief GPIO Initialization Function
    * @param None
    * @retval None
    */
    static void MX_GPIO_Init(void)
    {
     
    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOB_CLK_ENABLE();
     
    }
     
    /* USER CODE BEGIN 4 */
    static int rx_callback(struct rpmsg_endpoint *rp_chnl, void *data, size_t len, uint32_t src, void *priv)
    {
    /* copy received msg, and raise a flag */
    memcpy(received_rpmsg, data, len > sizeof(received_rpmsg) ? sizeof(received_rpmsg) : len);
    printf("received_rpmsg=%s ", received_rpmsg);
    rx_status = SET;
    return 0;
    }
    /* USER CODE END 4 */
     
    /**
    * @brief This function is executed in case of error occurrence.
    * @retval None
    */
    void Error_Handler(void)
    {
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
     
    /* USER CODE END Error_Handler_Debug */
    }
     
    #ifdef USE_FULL_ASSERT
    /**
    * @brief Reports the name of the source file and the source line number
    * where the assert_param error has occurred.
    * @param file: pointer to the source file name
    * @param line: assert_param error line source number
    * @retval None
    */
    void assert_failed(uint8_t *file, uint32_t line)
    {
    /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number,
    tex: printf("Wrong parameters value: file %s on line %d ", file, line) */
    /* USER CODE END 6 */
    }
    #endif /* USE_FULL_ASSERT */
     
    /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

    3.2.4 测试效果

    首先加载驱动rpmsg_client_sample.ko,并修改打印等级。

    1
    2
    insmod rpmsg_client_sample.ko
    echo 8 > /proc/sys/kernel/printk

    然后加载M4固件,可以看到A7和M4都同时发送和接收到相互之间的数据。

    1
    2
    3
    cd /sys/class/remoteproc/remoteproc0
    echo rpmsg_kernel_CM4.elf > firmware
    echo start > state

    知识共享许可协议
    本文采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可,欢迎转载,但转载请注明来自hceng blog,并保持转载后文章内容的完整。本人保留所有版权相关权利。
    【作者】张昺华
    【大饼教你学系列】https://edu.csdn.net/course/detail/10393
    【新浪微博】 张昺华--sky
    【twitter】 @sky2030_
    【微信公众号】 张昺华
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    spring-mvc----数据库数据到页面错误--tomcat启动不了
    springmvc注解@RequestMapping
    数据库到jsp页面报错(一)
    IDEA秒退或者一直让填写激活码问题
    spting Boot 创建一个springBoot项目
    SSM框架整合(实现从数据库到页面展示)
    asp.net dbproviderfactory(提供程序工厂模型)多数据库访问
    connectionStrings 中的 providerName 属性
    IDisposable
    利用提示引导语句运行
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/13556572.html
Copyright © 2020-2023  润新知