[STM32#21] freeRTOS를 적용하는 방법과 메시지큐(osMessageQueueId_t)를 이용해서 데이터를 주고받는 방법 간단하게 알아보기!(녹칸다 내맘대로 STM32)
프로그래밍/STM32 2025. 12. 22. 23:08
https://youtube.com/live/vSkSt9_30jQ
[STM32#21] freeRTOS를 적용하는 방법과 메시지큐(osMessageQueueId_t)를 이용해서 데이터를 주고받는 방법 간단하게 알아보기!(녹칸다 내맘대로 STM32)
심심한녹칸다의 내맘대로 STM32시리즈이다!
STM32시리즈의 모든 자료는 구글 슬라이드에 작성하고 모두에게 공유되어있음!
https://docs.google.com/presentation/d/1myA5iYbjuKsLWLqtRLKAiRfwUwvqB1d1RGjiMIIgp3I/edit?slide=id.g3b24791482c_1_0#slide=id.g3b24791482c_1_0
STM32에서 freeRTOS를 사용하는 방법을 아주 간단히만 알아보겠습니다!
1.main 루프에서 HAL_getTick을 이용해서 2개의 task를 구현하는 방법에 대해서 보이시오!
-task1 : PB3을 1000밀리초 간격으로 깜빡인다!
-task2 : PB13을 1500밀리초 간격으로 깜빡인다!
/* USER CODE BEGIN 2 */
uint32_t t1 = 0;
uint32_t t2 = 0;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//-task1 : PB3을 1000밀리초 간격으로 깜빡인다!
if(HAL_GetTick() - t1 >= 1000){
t1 = HAL_GetTick();
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
}
//-task2 : PB13을 1500밀리초 간격으로 깜빡인다!
if(HAL_GetTick() - t2 >= 1500){
t2 = HAL_GetTick();
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
2.task1을 TIM1에 task2를 TIM2에 적용해서 LED를 깜빡이시오!

/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
//TIM2일수도있고 TIM3일수도있고~
if (htim->Instance == TIM2)
{
// TASK1
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
}
else if (htim->Instance == TIM3)
{
// TASK2
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13);
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_Base_Start_IT(&htim3);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//메인루트에서는 할일 없음!
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
3.cubeide에서 tim1을 클락소스로 지정하고 rtos로 task2개를 생성해서 작동시키는 예시를 보이시오!

void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
//task1
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
osDelay(1000);
}
/* USER CODE END 5 */
}
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
for(;;)
{
//task2
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_13);
osDelay(1500);
}
/* USER CODE END StartTask02 */
}
4.아래와 같이 task를 구성해서 예제를 구현하시오!
-task1 : 1000밀리초 간격으로 task1의 내부변수값을 usart2에 출력한다!
-task2 : 1000밀리초 간격으로 task2의 내부변수값을 usart2에 출력한다!

void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
int num = 1234;
char buff[20];
for(;;)
{
//task1
sprintf(buff,"TASK1=%d\n",num);
HAL_UART_Transmit(&huart2,buff,strlen(buff),100);
osDelay(1000);
}
/* USER CODE END 5 */
}
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
int num = 5678;
char buff[20];
for(;;)
{
//task2
sprintf(buff,"TASK2=%d\n",num);
HAL_UART_Transmit(&huart2,buff,strlen(buff),100);
osDelay(1000);
}
/* USER CODE END StartTask02 */
}
5.예제 4에서 아래와 같이 task를 구성하고 메시지큐를 활용해서 충돌이 나는것을 방지하시오!
-task1 : 1000밀리초 간격으로 내부에 있는 변수값을 메시지큐에 넣는다!(생산자)
-task2 : 1000밀리초 간격으로 내부에 있는 변수값을 메시지큐에 넣는다!(생산자)
-task3 : 메시지큐에 있는 내용을 usart2에 출력한다!(소비자)

/* USER CODE BEGIN 0 */
//주고받는 데이터 (사용자 정의)
typedef struct {
uint16_t id;
int value;
} Msg_t;
//메시지 큐(전역)
osMessageQueueId_t dataQueue;
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
//메시지큐(메인)
dataQueue = osMessageQueueNew(
8, // queue length
sizeof(Msg_t), // item size
NULL);
/* USER CODE END 2 */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
int num = 1234;
Msg_t msg;
msg.id = 1;
for(;;)
{
//task1(공급자)
msg.value = num;
osMessageQueuePut(dataQueue, &msg, 0, 0); //non-blocking
osDelay(1000);
}
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
int num = 5678;
Msg_t msg;
msg.id = 2;
for(;;)
{
//task2(공급자)
msg.value = num;
osMessageQueuePut(dataQueue, &msg, 0, 0); //non-blocking
osDelay(1000);
}
void StartTask03(void *argument)
{
/* USER CODE BEGIN StartTask03 */
/* Infinite loop */
Msg_t msg;
char buff[30];
for(;;)
{
//소비자
//dataQueue에 공급자가 넣은 데이터가 없으면 무한대기
osMessageQueueGet(dataQueue, &msg, NULL, osWaitForever); //blocking
sprintf(buff,"id=%d, value=%d\n",msg.id,msg.value);
HAL_UART_Transmit(&huart2,buff,strlen(buff),100);
}
/* USER CODE END StartTask03 */
}
6.예제5에서 공급자가 소비자보다 큐에 데이터를 넣는 속도가 더 빠르면 큐가 용량초과가 발생할 수 있다! 간단하게 예외처리하는 기법에 대해서 예제로 만드시오!

/* USER CODE BEGIN 0 */
//주고받는 데이터 (사용자 정의)
typedef struct {
uint16_t id;
int value;
} Msg_t;
//메시지 큐(전역)
osMessageQueueId_t dataQueue;
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
//메시지큐(메인)
dataQueue = osMessageQueueNew(
8, // queue length
sizeof(Msg_t), // item size
NULL);
/* USER CODE END 2 */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
int num = 1234;
Msg_t msg;
msg.id = 1;
for(;;)
{
//task1(공급자)
msg.value = num;
if(osMessageQueuePut(dataQueue, &msg, 0, 0) == osOK){
//큐에 빈공간이 있어서 성공적으로 넣었다!
}else{
//큐에 빈공간이 없어서 실패했다!
}
osDelay(1000);
}
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
int num = 5678;
Msg_t msg;
msg.id = 2;
for(;;)
{
//task2(공급자)
msg.value = num;
if(osMessageQueuePut(dataQueue, &msg, 0, 0) == osOK){
//큐에 빈공간이 있어서 성공적으로 넣었다!
}else{
//큐에 빈공간이 없어서 실패했다!
}
osDelay(1000);
}
void StartTask03(void *argument)
{
/* USER CODE BEGIN StartTask03 */
/* Infinite loop */
Msg_t msg;
char buff[30];
for(;;)
{
//소비자
//dataQueue에 공급자가 넣은 데이터가 없으면 무한대기
osMessageQueueGet(dataQueue, &msg, NULL, osWaitForever); //blocking
sprintf(buff,"id=%d, value=%d\n",msg.id,msg.value);
HAL_UART_Transmit(&huart2,buff,strlen(buff),100);
}
/* USER CODE END StartTask03 */
}
7.아래와 같이 task를 구현해서 usart2로 출력하시오!
-task1 : 1000밀리초 간격으로 num1의 값은 1씩 증가하고 num2의 값은 1씩 감소시켜서 큐에 넣는다!
-task2 : 1000밀리초 간격으로 num1의 값은 1씩 증가하고 num2의 값은 1씩 감소시켜서 큐에 넣는다!
-task3 : 메시지큐에 있는 내용을 usart2에 출력한다!(소비자)

/* USER CODE BEGIN 0 */
//주고받는 데이터 (사용자 정의)
typedef struct {
uint16_t id;
int value1;
int value2;
} Msg_t;
//메시지 큐(전역)
osMessageQueueId_t dataQueue;
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
//메시지큐(메인)
dataQueue = osMessageQueueNew(
8, // queue length
sizeof(Msg_t), // item size
NULL);
/* USER CODE END 2 */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
Msg_t msg = {1,0,0};
for(;;)
{
//task1(공급자)
msg.value1++;
msg.value2--;
if(osMessageQueuePut(dataQueue, &msg, 0, 0) == osOK){
//큐에 빈공간이 있어서 성공적으로 넣었다!
}else{
//큐에 빈공간이 없어서 실패했다!
}
osDelay(1000);
}
/* USER CODE END 5 */
}
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
Msg_t msg = {2,0,0};
for(;;)
{
//task2(공급자)
msg.value1++;
msg.value2--;
if(osMessageQueuePut(dataQueue, &msg, 0, 0) == osOK){
//큐에 빈공간이 있어서 성공적으로 넣었다!
}else{
//큐에 빈공간이 없어서 실패했다!
}
osDelay(1000);
}
/* USER CODE END StartTask02 */
}
void StartTask03(void *argument)
{
/* USER CODE BEGIN StartTask03 */
/* Infinite loop */
Msg_t msg;
char buff[30];
for(;;)
{
//소비자
//dataQueue에 공급자가 넣은 데이터가 없으면 무한대기
osMessageQueueGet(dataQueue, &msg, NULL, osWaitForever); //blocking
sprintf(buff,"id=%d, value1=%d, value2=%d\n",msg.id,msg.value1,msg.value2);
HAL_UART_Transmit(&huart2,buff,strlen(buff),100);
}
/* USER CODE END StartTask03 */
}
8.아래와 같이 task를 구현하시오!
-task1 : 100밀리초 간격으로 LED 8개가 링모양으로 순차적으로 켜진다!
-task2 : 1초 간격으로 1씩 증가하는 카운터값을 usart2로 전송하시오!

/* USER CODE BEGIN 0 */
GPIO_TypeDef *mygpio[] = {GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOB,GPIOC,GPIOB};
uint16_t mypin[] = {GPIO_PIN_3,GPIO_PIN_13,GPIO_PIN_5,GPIO_PIN_14,GPIO_PIN_4,GPIO_PIN_15,GPIO_PIN_4,GPIO_PIN_7};
/* USER CODE END 0 */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
int led_num = 0;
for(;;)
{
//task1
//100밀리초 간격으로 LED를 빙글빙글돌린다
for(int i = 0;i<8;i++){
//led_num와 i가 같은 녀석만 켜고 나머지는 끈다!
if(led_num == i){
HAL_GPIO_WritePin(mygpio[i], mypin[i], 1);
}else{
HAL_GPIO_WritePin(mygpio[i], mypin[i], 0);
}
}
led_num++;
if(led_num == 8) led_num = 0;
osDelay(100);
}
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
/* Infinite loop */
int num = 0;
char buff[30];
for(;;)
{
sprintf(buff,"num = %d\n",num);
HAL_UART_Transmit(&huart2,buff,strlen(buff),100);
num++;
osDelay(1000);
}
/* USER CODE END StartTask02 */
}


