轉自: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.\r\n");
Error_Handler();
}
if (VIRT_UART_Init(&huart1) != VIRT_UART_OK) {
printf("VIRT_UART_Init UART1 failed.\r\n");
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 \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 */
-
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\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));} -
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\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));}
為了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\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 */
}
/**
* @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 */
}
/************************ (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\n\r
,正確的應該是收到*led_on\r
。-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)\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");
|
驅動比較簡單,核心是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\r\n", 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\r\n");Error_Handler();}elseprintf("send_rpmsg=%s\r\n", 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\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 */
}
/**
* @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 */
}
/************************ (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
|
