STM32MP157——Remoteproc和RPMsg

CSDN仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
本博文对应地址:https://hceng.cn/2020/05/09/STM32MP157%E2%80%94%E2%80%94Remoteproc%E5%92%8CRPMsg/#more

简单介绍基于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()
{% codeblock lang:c %}
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);
{% endcodeblock %}

添加完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处理器。
{% codeblock lang:shell %}
[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
{% endcodeblock %}

除了在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绑定。
{% codeblock lang:c %}
/* 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 */
{% endcodeblock %}

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

  • 1.初始化两个RPMsg tty虚拟串口

{% codeblock lang:c %}
if (VIRT_UART_Init(&huart0) != VIRT_UART_OK) {
printf(“VIRT_UART_Init UART0 failed.\r\n”);
Error_Handler();
}

if (VIRT_UART_Init(&huart1) != VIRT_UART_OK) {
printf(“VIRT_UART_Init UART1 failed.\r\n”);
Error_Handler();
}
{% endcodeblock %}

  • 2.注册回调函数以按通道接收消息
    {% codeblock lang:c %}
    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();
    }
    {% endcodeblock %}

  • 3.编写虚拟串口回调函数
    当RPMsg收到数据后,将调用该回调函数。在此函数里,需要将接收的数据复制到用户内存,并修改接收标志位,通知用户完成数据接收。
    {% codeblock lang:c %}
    /* USER CODE BEGIN 4 */
    void VIRT_UART0_RxCpltCallback(VIRT_UART_HandleTypeDef *huart)
    {
    printf(“Msg received on VIRTUAL UART0 channel: %s \n\r”, (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 \n\r”, (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 */
{% endcodeblock %}

  • 4.主函数轮询RPMsg消息
    OPENAMP_check_for_message()查询MailBox状态。
    当收到数据时,VIRT_UARTx_RxCpltCallback()会保存好收到数据,然后修改VirtUartxRxMsg标志位。
    主函数里发现VirtUartxRxMsg标志位发生变化时,即可获取接收的数据。
    {% codeblock lang:c %}
    while (1)
    {
    OPENAMP_check_for_message();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    if (VirtUart0RxMsg)
    {
    VirtUart0RxMsg = RESET;

    /*VirUART0收到数据*/
    

    }

    if (VirtUart1RxMsg)
    {
    VirtUart1RxMsg = RESET;

    /*VirUART1收到数据*/
    

    }
    }
    {% endcodeblock %}

  • 5.VirUART0接收控制LED指令
    每次VirtUart0RxMsg发生变化,说明VirUART0收到了数据。
    然后比较收到的数据内容,执行对应的操作。
    这里,M4收到MSG_LED_ON(*led_on)则打开LED灯,并发送消息给A7;M4收到MSG_LED_OFF(*led_off)则关闭LED灯,并发送消息给A7。
    {% codeblock lang:c %}
    if (VirtUart0RxMsg)
    {
    VirtUart0RxMsg = RESET;

    if (!strncmp((char *)VirtUart0ChannelBuffRx, MSG_LED_ON, strlen(MSG_LED_ON)))
    {
        strcpy((char *)BuffTx, "m4:led on\n");
        printf("%s\r", 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\n");
        printf("%s\r", 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));
    

    }
    {% endcodeblock %}

  • 6.VirUART1接收休眠唤醒指令
    每次VirtUart1RxMsg发生变化,说明VirUART1收到了数据。
    然后比较收到的数据内容,执行对应的操作。
    这里,M4收到MSG_STOP(*stop)则进入CStop模式,中途A7再发任意数据给M4,由于IPCC也可设置为中断唤醒源,将M4唤醒;M4收到MSG_DELAY(*delay)则等待20S后发数据给A7,在这20S内,将A7先休眠,随后将被M4唤醒。
    {% codeblock lang:c %}
    if (VirtUart1RxMsg)
    {
    VirtUart1RxMsg = RESET;

    if (!strncmp((char *)VirtUart1ChannelBuffRx, MSG_STOP, strlen(MSG_STOP)))
    {
        strcpy((char *)BuffTx, "m4:stop\n");
        printf("%s\r", 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\r\n");
        /* (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\r\n");
    
         /* Test if system was on STOP mode */
         if( (PWR->MCUCR & PWR_MCUCR_STOPF) == PWR_MCUCR_STOPF)
         {
      	  printf("System was on STOP mode\r\n");
    
            /* Clear the MCU flags */
            SET_BIT(PWR->MCUCR, PWR_MCUCR_CSSF);
    
            /* Restore clocks */
            /*
            if (RCC_restoreClocks() == HAL_OK)
            {
          	  printf("CM4 restored clocks successfully\r\n");
            }
            */
          }
    
          /* 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\r\n");
        HAL_Delay(20 *1000);
    
        strcpy((char *)BuffTx, "m4:wakeup A7\n");
        printf("%s\r", BuffTx);
        VIRT_UART_Transmit(&huart1, BuffTx, strlen((const char *)BuffTx));
    }
    memset(VirtUart1ChannelBuffRx, 0 ,VirtUart1ChannelRxSize);
    memset(BuffTx, 0 ,strlen((const char *)BuffTx));
    

    }
    {% endcodeblock %}

为了A7能发消息将M4唤醒,还需要IPCC作为M4的中断唤醒源。
{% codeblock lang:c %}
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);
{% endcodeblock %}

3.1.3 M4完整代码

{% codeblock lang:c [main.c] %}
/* USER CODE BEGIN Header /
/
*


  • @file : main.c
  • @brief : Main program body

  • @attention
  • © Copyright (c) 2020 STMicroelectronics.

  • All rights reserved.
  • 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\r\n”);

if (VIRT_UART_Init(&huart0) != VIRT_UART_OK) {
printf(“VIRT_UART_Init UART0 failed.\r\n”);
Error_Handler();
}

if (VIRT_UART_Init(&huart1) != VIRT_UART_OK) {
printf(“VIRT_UART_Init UART1 failed.\r\n”);
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\n");
	  printf("%s\r", 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\n");
	  printf("%s\r", 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\n");
	  printf("%s\r", 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\r\n");
	  /* (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\r\n");

	   /* Test if system was on STOP mode */
	   if( (PWR->MCUCR & PWR_MCUCR_STOPF) == PWR_MCUCR_STOPF)
	   {
		  printf("System was on STOP mode\r\n");

	      /* Clear the MCU flags */
	      SET_BIT(PWR->MCUCR, PWR_MCUCR_CSSF);

	      /* Restore clocks */
	      /*
	      if (RCC_restoreClocks() == HAL_OK)
	      {
	    	  printf("CM4 restored clocks successfully\r\n");
	      }
	      */
	    }

	    /* 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\r\n");
	  HAL_Delay(20 *1000);

	  strcpy((char *)BuffTx, "m4:wakeup A7\n");
	  printf("%s\r", 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 \r\n”, (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 \r\n”, (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\r\n”, file, line) /
    /
    USER CODE END 6 /
    }
    #endif /
    USE_FULL_ASSERT */

/************************ © COPYRIGHT STMicroelectronics *END OF FILE/
{% endcodeblock %}

3.1.4 测试效果

  • 1.测试A7命令M4控制LED灯
    首先将前面编译生成的rpmsg_user_CM4.elf放在/lib/firmware目录下
    然后启动固件,此时看到打印信息里提示创建了两个通道ttyRPMSG0ttyRPMSG1,同时M4也打印了测试开始信息。
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\n\r,正确的应该是收到*led_on\r
-echo是禁止回显,以方便查看接收的字符。

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

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

echo "*led_on" > /dev/ttyRPMSG0
echo "*led_off" > /dev/ttyRPMSG0
  • 2.测试A7和M4休眠唤醒
    接着前面,启动M4后。
    首先设置虚拟串口/dev/ttyRPMSG1
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的休眠和唤醒。

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

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

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,这里直接使用该驱动,简单的修改了下打印内容,方便查看。
{% codeblock lang:c [rpmsg_client_sample.c] %}
/*

  • Remote processor messaging - sample client driver
  • Copyright © 2011 Texas Instruments, Inc.
  • Copyright © 2011 Google, Inc.
  • Ohad Ben-Cohen [email protected]
  • Brian Swetland [email protected]
  • 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
#include
#include

#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)\n", ++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 \n", data, ++idata->rx_count);


/* samples should not live forever */
if (idata->rx_count >= MSG_LIMIT) {
	//dev_info(&rpdev->dev, "goodbye!\n");
	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\n", ret);
else
	printk(KERN_DEBUG "send_rpmsg: %s \n", 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!\n",
				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\n", ret);
	return ret;
}
else
	printk(KERN_DEBUG "send_rpmsg: %s \n", MSG);
	
return 0;

}

static void rpmsg_sample_remove(struct rpmsg_device *rpdev)
{
dev_info(&rpdev->dev, “rpmsg sample client driver is removed\n”);
}

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”);
{% endcodeblock %}

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

3.2.2 M4准备

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

  • 1.建一个rpmsg通道
    {% codeblock lang:c %}
    OPENAMP_create_endpoint(&resmgr_ept, RPMSG_SERVICE_NAME, RPMSG_ADDR_ANY, rx_callback, NULL);
    {% endcodeblock %}

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

  • 2.编写接收回调函数
    此函数里,需要将接收的数据复制到用户内存,并修改接收标志位。
    {% codeblock lang:c %}
    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\r\n”, received_rpmsg);
    rx_status = SET;
    return 0;
    {% endcodeblock %}

  • 3.主函数轮询RPMsg消息
    OPENAMP_check_for_message()查询MailBox状态。
    当收到数据时,rx_callback()会保存好收到数据,然后修改rx_status标志位。
    主函数里发现rx_status标志位发生变化时,即可获取接收的数据。
    {% codeblock lang:c %}
    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 */
    }
    {% endcodeblock %}

  • 4.向M4发送数据
    使用OPENAMP_send()向A7发送数据。
    {% codeblock lang:c %}
    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\r\n");
      Error_Handler();
    }
    else
      printf("send_rpmsg=%s\r\n", msg);
    

{% endcodeblock %}

3.2.3 M4完整代码

{% codeblock lang:c [main.c] %}
/* USER CODE BEGIN Header /
/
*


  • @file : main.c
  • @brief : Main program body

  • @attention
  • © Copyright (c) 2020 STMicroelectronics.

  • All rights reserved.
  • 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\r\n”);
/
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\r\n");
	Error_Handler();
  }
  else
	printf("send_rpmsg=%s\r\n", 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\r\n”, 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\r\n”, file, line) /
    /
    USER CODE END 6 /
    }
    #endif /
    USE_FULL_ASSERT */

/************************ © COPYRIGHT STMicroelectronics *END OF FILE/
{% endcodeblock %}

3.2.4 测试效果

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

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

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

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

你可能感兴趣的:(Linux驱动,Linux应用,linux,嵌入式)