지노랩 /JinoLab
FreeRTOS 태스크 우선순위 본문
두 개의 “Hello-World” 태스크로 배우는 우선순위·스택·생성 API
1. 우선순위가 필요한 이유
상황 설명
| 단일-코어 MCU | 한 순간에 CPU 명령을 실행할 수 있는 태스크는 1개뿐입니다. |
| READY 태스크 ≥ 2 | 어느 태스크에게 CPU를 양보할지 스케줄러가 판단해야 함 |
| 우선순위 | 숫자가 높을수록 시급함을 뜻하며, 스케줄러는 높은 값부터 실행 |
우선순위는 실시간성 확보와 자원 효율성 간의 균형점입니다.
2. configMAX_PRIORITIES 설정
/* FreeRTOSConfig.h */
#define configMAX_PRIORITIES 5 /* 0~4 총 5단계 */
- 0 : 최하위(백그라운드 로깅 등)
- 4 : 최상위(센서 캡처, 모터 제어 등)
- 값을 지나치게 키우면 ➜ 문맥 전환/RAM 사용량 증가 → 성능 저하
3. 태스크 생성 API xTaskCreate()
BaseType_t xTaskCreate(
TaskFunction_t pxTaskCode, /* 태스크 함수(핸들러) */
const char *pcName, /* 디버그용 이름(선택) */
uint16_t usStackDepth, /* 스택 크기 – “Word” 단위 */
void *pvParameters, /* 첫 매개변수(선택) */
UBaseType_t uxPriority, /* 우선순위(0~configMAX_PRIORITIES)*/
TaskHandle_t *pxCreatedTask); /* 핸들 저장 포인터(선택) */
반환값 의미
| pdPASS | 태스크·스택 메모리 동적 할당 성공 |
| errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY | 힙 공간 부족 |
4. 스택 크기(usStackDepth) 계산법
- Word 단위다.
- Cortex-M (32-비트) ⇒ 1 Word = 4 Byte
- 예) usStackDepth = 256 → 256 ×4 = 1 KiB
🚩 스택 오버플로 감시 → configCHECK_FOR_STACK_OVERFLOW 2 활성화 권장
5. 예제 코드 — 두 태스크(동일 우선순위) 만들기
목표 : 1 초 간격으로 교대로 “Hello World from Task-1/2” 출력
/*-----------------------------------------------------------
* 1. include
*----------------------------------------------------------*/
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
/*-----------------------------------------------------------
* 2. 태스크 핸들
*----------------------------------------------------------*/
static TaskHandle_t hTask1, hTask2;
/*-----------------------------------------------------------
* 3. 태스크 함수 선언
*----------------------------------------------------------*/
static void vTaskFn1(void *arg);
static void vTaskFn2(void *arg);
/*-----------------------------------------------------------
* 4. main()
*----------------------------------------------------------*/
int main(void)
{
HAL_Init(); /* HAL 초기화 */
SystemClock_Config(); /* CubeMX 생성 함수 */
/* ----- Task-1 생성 ----- */
configASSERT(
xTaskCreate( vTaskFn1, /* 태스크 함수 */
"Task-1", /* 태스크 이름 */
256, /* 스택(Word) = 1 KiB */
NULL, /* 첫 매개변수 없음 */
2, /* 우선순위 = 2 */
&hTask1 ) == pdPASS);
/* ----- Task-2 생성 ----- */
configASSERT(
xTaskCreate( vTaskFn2,
"Task-2",
256,
NULL,
2, /* 같은 우선순위 */
&hTask2 ) == pdPASS);
vTaskStartScheduler(); /* 스케줄러 시작 – 반환 안 됨 */
while (1); /* 비실행 – 예외 상황용 루프 */
}
/*-----------------------------------------------------------
* 5. 태스크 핸들러 구현
*----------------------------------------------------------*/
static void vTaskFn1(void *arg)
{
(void)arg;
for (;;)
{
printf("Hello World from Task-1\r\n");
vTaskDelay(pdMS_TO_TICKS(1000)); /* 1 초 block → 타임슬라이스 */
}
}
static void vTaskFn2(void *arg)
{
(void)arg;
for (;;)
{
printf("Hello World from Task-2\r\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
필수 설정 체크
항목 값
| configMAX_PRIORITIES | ≥ 3 (예: 5) |
| configUSE_PREEMPTION | 1 (프리엠프형 스케줄링) |
| configUSE_TIME_SLICING | 1 (같은 우선순위끼리 타임슬라이스) |
| USE_NEWLIB_REENTRANT | 1 (printf 다중-태스크 안전) |
6. 실행 결과 (UART 콘솔 예시)
Hello World from Task-1
Hello World from Task-2
Hello World from Task-1
Hello World from Task-2
...
- Tick 인터럽트마다 동일 우선순위 태스크가 교대로 CPU 차지
- configUSE_TIME_SLICING을 0으로 바꾸면 자진 양보(vTaskDelay) 시에만 교대됨
7. 더 실험해보기
- 우선순위 변경
- vTaskPrioritySet(hTask2, 3); // 런타임 중 Task-2 승격
- 스택 모니터링
- UBaseType_t watermark = uxTaskGetStackHighWaterMark(NULL); printf("남은 워터마크 = %lu word\r\n", watermark);
- 우선순위 역전 → 뮤텍스(Priority Inheritance) 사용 예제는 다음 섹션에서.
정리
- 태스크마다 우선순위를 부여해 실시간 요구를 만족시킨다.
- 너무 많은 단계는 문맥 전환·RAM 사용량을 늘려 오히려 성능 저하시킬 수 있다.
- 간단한 예제로 구조를 익힌 뒤, 실제 프로젝트에서 점진적으로 적용해보자!
'임베디드 시스템 > RTOS' 카테고리의 다른 글
| FreeRTOS 태스크 시작과 SWO 출력 설정 (ITM_SendChar 적용) (1) | 2025.06.14 |
|---|---|
| FreeRTOS의 스케줄링 심화 설명: 선점형 vs 협력형 (0) | 2025.06.13 |
| FreeRTOS Task Priority 완전 정복 (0) | 2025.06.12 |
| FreeRTOS 태스크 생성 API(xTaskCreate) 상세 분석 (0) | 2025.06.12 |
| FreeRTOS에서의 태스크(Task)란 무엇인가? (0) | 2025.06.11 |