본문으로 바로가기
반응형

안녕하세요, 허블입니다.

 

오늘은 메시지 큐에 대해서 알아볼까 합니다.

 

메시지 큐는 Task 간에 데이터를 전송할 때 필요한 개념입니다.

테스크와 테스크가 통신하기 위한 인터페이스로 보면 될듯합니다.

 

Message Queue(keil 참조)

 

구조는 이런 식으로 되어있습니다. 

Task에서 Message Queue(메시지큐)를 Put을 하면 다른 쪽 Task에서 Get을 하여 받습니다.

 

이렇게 구성을 하면 좋은 점은 Task 간의 독립성을 유지할 수 있습니다.

전역 변수로 여기저기서 사용하게 되는 것보다 간결하게 만들 수 있고 독립성을 유지할 수 있기 때문에 모듈로 붙이기 편하다는 장점이 있습니다.

 

바로 코드를 통해서 만드는 방법도 있지만 CubeMX를 통해서 만드는 방법을 알아보도록 하겠습니다.

 

 

 

 

Cubemx 설정

CubeMx RTOS 설정

좌측 카테고리에서 FREERTOS를 ENABLE 해줍니다.

 

 

 

 

ST Cubemx Task 설정

Task를 생성하여 2개를 만들어줍니다.

 

 

 

 

ST Cubemx  Qeueue 설정

하단에 Add 버튼을 통해서 Queue도 1개 생성해줍니다.

 

 

 

 

ST Cubemx Task, Queue 설정

이렇게 Task 2개와 Queue 1개가 설정이 되었습니다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  /* Create the queue(s) */
  /* definition and creation of Queue_ */
  osMessageQDef(Queue_, 16, uint16_t);
  Queue_Handle = osMessageCreate(osMessageQ(Queue_), NULL);
 
  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */
 
  /* Create the thread(s) */
  /* definition and creation of _Task1 */
  osThreadDef(_Task1, Task1, osPriorityNormal, 0128);
  _Task1Handle = osThreadCreate(osThread(_Task1), NULL);
 
  /* definition and creation of _Task2 */
  osThreadDef(_Task2, Task2, osPriorityIdle, 0128);
  _Task2Handle = osThreadCreate(osThread(_Task2), NULL);
 
  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */
 
  /* Start scheduler */
  osKernelStart();
cs

Main문 안에는 큐와 테스크가 생성이 되었습니다.

여기서 구조체와 Memory Pool Management를 추가하도록 하겠습니다.

 

 

 

에제 코드
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
osThreadId _Task1Handle;
osThreadId _Task2Handle;
osMessageQId Queue_Handle;
osPoolId Pool_ID ;
 
void SystemClock_Config(void);
void Task1(void const * argument);
void Task2(void const * argument);
 
typedef struct {
    uint8_t var;
    uint8_t param[3];
}MessageQ;
 
MessageQ *pMsgQ;
 
 
 
// Main문/////////////////////////////////////////////////////////////////////////////////
  osPoolDef (Pool_ID_, sizeof(MessageQ), MessageQ); 
  Pool_ID = osPoolCreate(osPool(Pool_ID_));
  
  /* Create the queue(s) */
  /* definition and creation of Queue_ */
  osMessageQDef(Queue_, sizeof(MessageQ), MessageQ);
  Queue_Handle = osMessageCreate(osMessageQ(Queue_), NULL);
 
  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */
 
  /* Create the thread(s) */
  /* definition and creation of _Task1 */
  osThreadDef(_Task1, Task1, osPriorityNormal, 0128);
  _Task1Handle = osThreadCreate(osThread(_Task1), NULL);
 
  /* definition and creation of _Task2 */
  osThreadDef(_Task2, Task2, osPriorityIdle, 0128);
  _Task2Handle = osThreadCreate(osThread(_Task2), NULL);
 
  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */
 
  /* Start scheduler */
  osKernelStart();
///////////////////////////////////////////////////////////////////////////////////////////
 
/**
  * @brief  Function implementing the _Task1 thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_Task1 */
void Task1(void const * argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  for(;;)
  {
    pMsgQ = (MessageQ *)osPoolAlloc (Pool_ID);
    
    pMsgQ->var = 'A';
    pMsgQ->param[0= 'B';
    pMsgQ->param[1= 'C';
    pMsgQ->param[2= 'D';
    osMessagePut(Queue_Handle, (uint32_t)pMsgQ, osWaitForever);    
    osDelay(1000);
  }
  /* USER CODE END 5 */
}
 
/* USER CODE BEGIN Header_Task2 */
/**
* @brief Function implementing the _Task2 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_Task2 */
void Task2(void const * argument)
{
  /* USER CODE BEGIN Task2 */
  /* Infinite loop */
  for(;;)
  {
    osEvent event = osMessageGet(Queue_Handle, osWaitForever);
    
    if (event.status == osEventMessage) {
        pMsgQ = event.value.p;
        
        printf("%c %c %c %c\r\n", pMsgQ->var, pMsgQ->param[0], pMsgQ->param[1], pMsgQ->param[2]);
        osPoolFree(Pool_ID, pMsgQ);        
    }    
    osDelay(1);
  }
  /* USER CODE END Task2 */
}
cs

전체 코드는 이렇습니다.

 

 

 

osPoolId Pool_ID ;

osPoolDef (Pool_ID_, sizeof(MessageQ), MessageQ);

Pool_ID = osPoolCreate(osPool(Pool_ID_));

이 부분은 메모리 Allocate를 하기 위해 추가하는 코드입니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
  * @brief  Function implementing the _Task1 thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_Task1 */
void Task1(void const * argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  for(;;)
  {
    pMsgQ = (MessageQ *)osPoolAlloc (Pool_ID);
    
    pMsgQ->var = 'A';
    pMsgQ->param[0= 'B';
    pMsgQ->param[1= 'C';
    pMsgQ->param[2= 'D';
    osMessagePut(Queue_Handle, (uint32_t)pMsgQ, osWaitForever);    
    osDelay(1000);
  }
  /* USER CODE END 5 */
}
cs

Task1을 살펴보겠습니다.

 

pMsgQ = (MessageQ *)osPoolAlloc (Pool_ID);

먼저 메모리를 Allocate를 합니다.

 

pMsgQ->var = 'A';

pMsgQ->param[0= 'B';

pMsgQ->param[1= 'C';

pMsgQ->param[2= 'D';

Allocate후 구조체에 값을 담습니다.

 

osMessagePut(Queue_Handle, (uint32_t)pMsgQ, osWaitForever);

MessagePut을 통해서 인자를 전달합니다. 메시지 Put에 첫 번째 인자는 큐 핸들, 두 번째는 주소, 세 번째는 시간입니다.

 

정리를 하자면 메모리 Allocate를 하고 구조체에 값을 담고 메시지큐를 1초마다 Put을 합니다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* @brief Function implementing the _Task2 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_Task2 */
void Task2(void const * argument)
{
  /* USER CODE BEGIN Task2 */
  /* Infinite loop */
  for(;;)
  {
    osEvent event = osMessageGet(Queue_Handle, osWaitForever);
    
    if (event.status == osEventMessage) {
        pMsgQ = event.value.p;
        
        printf("%c %c %c %c\r\n", pMsgQ->var, pMsgQ->param[0], pMsgQ->param[1], pMsgQ->param[2]);
        osPoolFree(Pool_ID, pMsgQ);        
    }    
    osDelay(1);
  }
  /* USER CODE END Task2 */
}
cs

마지막으로 Task2를 살펴보겠습니다.

osEvent event = osMessageGet(Queue_Handle, osWaitForever);

메시지 Get을 합니다.

 

 if (event.status == osEventMessage) {

메시지 이벤트가 발생하면 해당 조건에 들어갑니다.

 

pMsgQ = event.value.p;

printf("%c %c %c %c\r\n", pMsgQ->var, pMsgQ->param[0], pMsgQ->param[1], pMsgQ->param[2]);

osPoolFree(Pool_ID, pMsgQ);  

메시지를 받고 printf를 통해 출력 후 메모리 해제를 해줍니다.

 

 

 

 

 

코드 검증

세미호스팅

먼저 완성된 코드를 실행하고 View탭에서 Terminal I/O를 실행합니다.

 

 

 

 

IAR Terminal I/O

터미널 IO에서 1초마다 구조체에 입력했던 문자들이 출력되고 있습니다.

 

 

 

요약

Task1:

1. 메모리 Allocation

2. 구조체 데이터 Set

3. 데이터 Put

 

Task2:

1. 데이터 Get

2. 데이터 출력

3. 메모리 Free

 

 

프로그램이 실행이 되기 위해서 메모리가 필요합니다.

Message queue를 이용할 때 pool을 같이 사용하지 않아도 대부분의 경우엔 문제가 없습니다. 다만 pool을 사용하게 되면 메모리를 고정으로 가져가게 되므로 더 안정적으로 사용할 수 있습니다.

 

이상으로 마치겠습니다.

 

2021.08.12 - [프로그래밍/C#] - C# winform Chart 사용 방법, 간단한 예제

 

C# winform Chart 사용 방법, 간단한 예제

안녕하세요, 허블입니다. 오늘은 C# winform에서 제공해주는 Chart 클래스에 대해서 알아보려고 합니다. Chart를 이용하면 별도의 라이브러리 설치 없이 그래프를 렌더링 할 수 있습니다. Chart 클래스

hubbleconstant.tistory.com

2020.05.10 - [MCU/STM32F429] - STM32 Semihosting(STM32F429I Discovery)

 

STM32 Semihosting(STM32F429I Discovery)

오늘 포스팅할 내용은 세미호스팅(Semihosting)에 대한 내용입니다. 세미호스팅은 ARM 타겟 보드에서 실행되는 코드가 디버거를 통해서 호스트 컴퓨터에 Input / output 기능을 통신하고 사용할 수 있도

hubbleconstant.tistory.com

반응형

'MCU' 카테고리의 다른 글

STM32 부트로더는 어떻게 만들어지는가?  (1) 2021.12.08