转自: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-ARM
和IAR 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多核通信框架如下图。
消息服务基于共享内存,使用RPMsg
和Virtio
框架,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多核消息通信应用接口框图如下,在RPMsg
和Virtio
框架创建一个面向用户态的/dev/ttyRPMSG
接口,M4在OpenAMP
上创建虚拟串口,两者最终效果像是串口透传。
3.1.2 M4准备
创建一个STM32工程,在STM32CubeMX里,依次配置GPIO用于LED、配置UART5用于M4打印、以及配置IPCC和OPENAMP用于通信。
注意配置IPCC时,需要在NVIC Settings
选项卡里,将IPCC RX1 occupied interrupt
和IPCC TX1 free interrupt
的使能勾选上,不然后面的OPENAMP的Activated
始终为灰色,无法激活。
生成初始化代码后,在USER CODE BEGIN 0
和USER 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 */
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
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.注册回调函数以按通道接收消息
123456789if(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收到数据后,将调用该回调函数。在此函数里,需要将接收的数据复制到用户内存,并修改接收标志位,通知用户完成数据接收。123456789101112131415161718192021/* 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
标志位发生变化时,即可获取接收的数据。1234567891011121314151617181920while (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。12345678910111213141516171819202122if (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唤醒。1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465if (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>© 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 ------------------------------------------------------------------*/
/* 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 */
/* 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];
/* 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 */
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
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 */
}
/**
* @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 */
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
3.1.4 测试效果
- 1.测试A7命令M4控制LED灯
首先将前面编译生成的rpmsg_user_CM4.elf
放在/lib/firmware目录下
。
然后启动固件,此时看到打印信息里提示创建了两个通道ttyRPMSG0
和ttyRPMSG1
,同时M4也打印了测试开始信息。123cd /sys/class/remoteproc/remoteproc0echo rpmsg_user_CM4.elf > firmwareecho 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
。12stty -onlcr -echo -F /dev/ttyRPMSG1cat /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.
*/
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通道
1OPENAMP_create_endpoint(&resmgr_ept, RPMSG_SERVICE_NAME, RPMSG_ADDR_ANY, rx_callback, NULL);
注意这里的RPMSG_SERVICE_NAME
和驱动里rpmsg_device_id
结构体的.name
的名字一样。
之后,一旦rpmsg
通道数据,将回调rx_callback()
函数。
-
2.编写接收回调函数
此函数里,需要将接收的数据复制到用户内存,并修改接收标志位。1234567static 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
标志位发生变化时,即可获取接收的数据。12345678910111213while (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发送数据。123456789101112if (++count < 100)sprintf((char *)msg, "hello world! M4->A7 %02ld", count);elsestrcpy((char *)msg, "goodbye!");if (OPENAMP_send(&resmgr_ept, msg, strlen((char *)msg) + 1) < 0){printf("Failed to send message ");Error_Handler();}elseprintf("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>© 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 ------------------------------------------------------------------*/
/* 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 */
/* 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 */
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
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 */
}
/**
* @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 */
}
/************************ (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
|