ARM Cortex-M 마이크로컨트롤러 환경에서 멀티태스킹을 구현할 때, CMSIS-RTOS 인터페이스 표준은 코드의 이식성과 개발 효율을 결정짓는 핵심 요소입니다.
기존 v1에서 한 단계 진화한 CMSIS-RTOS v2는 더욱 현대적인 API를 제공하며, RTOS 커널(FreeRTOS, RTX 등)에 상관없이 동일한 코드로 태스크를 관리할 수 있게 해줍니다. 이번 포스팅에서는 CMSIS-RTOS v2를 기준으로 태스크 생성, 상태 관리, 통신 방법을 단계별로 알아보겠습니다.

1. CMSIS-RTOS v2 태스크 생성 3단계
태스크를 생성하고 실행하기 위해서는 아래의 3단계 표준 절차를 따릅니다.
- 태스크 함수 정의: 실제로 수행할 로직을 void 함수로 작성합니다.
- 속성(Attributes) 설정: 태스크 이름, 스택 크기, 우선순위를 정의합니다.
- osThreadNew() 호출: 커널에 태스크를 등록하고 실행합니다.
태스크 정의 및 생성 예제
#include "cmsis_os2.h" // CMSIS-RTOS v2 헤더
// 1. 태스크 함수 정의
void Blink_Task(void *argument) {
while (1) {
// 하드웨어 제어 로직 (예: LED 토글)
osDelay(1000); // 1000 Tick(일반적으로 1초) 동안 Blocked 상태로 전환
}
}
int main(void) {
osKernelInitialize(); // RTOS 커널 초기화
// 2 & 3. 태스크 속성 정의 및 생성
const osThreadAttr_t task_attr = {
.name = "LED_Task",
.stack_size = 512,
.priority = osPriorityNormal
};
osThreadNew(Blink_Task, NULL, &task_attr);
osKernelStart(); // 스케줄러 시작
while (1);
}
2. 태스크의 상태와 생명 주기 (Task States)
RTOS 환경에서 태스크는 단순히 "실행 중"인 것만이 아닙니다. 효율적인 리소스 관리를 위해 다음과 같은 상태를 오갑니다.
- Running (실행): 현재 CPU 점유권을 가지고 코드를 실행 중인 상태입니다.
- Ready (준비): 실행될 준비가 되었으나, 우선순위가 더 높은 태스크에 밀려 대기 중인 상태입니다.
- Blocked (차단): osDelay() 호출이나 메시지 수신 대기 등 특정 이벤트가 발생할 때까지 실행을 멈춘 상태입니다.
- Inactive (비활성): 태스크가 아직 생성되지 않았거나 삭제된 상태입니다.
핵심 팁: osDelay()를 사용하면 해당 태스크는 Blocked 상태로 전환되어 다른 태스크에 CPU를 양보합니다. 반면 HAL_Delay() 같은 단순 루프 지연을 사용하면 CPU를 계속 점유하여 전체 시스템 효율이 떨어지므로 주의해야 합니다.
3. osThreadNew() 함수의 주요 인자
태스크 생성 시 사용하는 osThreadNew() 함수는 다음과 같은 파라미터를 가집니다.
osThreadId_t osThreadNew(osThreadFunc_t func, void *argument, const osThreadAttr_t *attr);
- func: 태스크가 실행할 함수 주소
- argument: 함수에 전달할 사용자 인자 (없으면 NULL)
- attr: 스택 크기나 우선순위 등을 설정한 구조체 주소 (기본값 사용 시 NULL)
4. 태스크 간 통신 (Message Queue)
멀티태스킹 환경에서는 태스크끼리 데이터를 안전하게 주고받는 것이 중요합니다. 메시지 큐(Message Queue)는 데이터를 FIFO(선입선출) 방식으로 전달하는 가장 안전한 방법입니다.
메시지 큐 송수신 예제
osMessageQueueId_t msgQueue;
void SenderTask(void *argument) {
uint32_t count = 0;
while (1) {
count++;
// 큐에 데이터 넣기 (ID, 데이터주소, 우선순위, 대기시간)
osMessageQueuePut(msgQueue, &count, 0, osWaitForever);
osDelay(500);
}
}
void ReceiverTask(void *argument) {
uint32_t received;
while (1) {
// 큐에서 데이터 받기
osStatus_t status = osMessageQueueGet(msgQueue, &received, NULL, osWaitForever);
if (status == osOK) {
// 수신 데이터 처리 로직
}
}
}
결론
CMSIS-RTOS v2 표준을 사용하면 복잡한 멀티태스킹 환경에서도 안정적이고 이식성 높은 임베디드 소프트웨어를 개발할 수 있습니다. 특히 osThreadNew를 통한 세밀한 속성 관리와 메시지 큐를 통한 데이터 보호는 실무 프로젝트의 필수 요소입니다.
이 가이드를 통해 여러분의 프로젝트에 최적화된 RTOS 구조를 설계해 보시기 바랍니다!
'Firmware & RTOS > FreeRTOS & Real-time Scheduling' 카테고리의 다른 글
| FreeRTOS 세마포어(Semaphore) vs 뮤텍스(Mutex) 차이점과 올바른 사용법 (0) | 2025.01.14 |
|---|---|
| FreeRTOS 큐(Queue) 완벽 가이드: 태스크 간 데이터 통신 및 예제 (CMSIS-RTOS v2) (0) | 2025.01.13 |
| FreeRTOS 핵심 개념 완벽 정리: Task, Queue, Semaphore, Mutex 활용법 (0) | 2025.01.11 |
| FreeRTOS 시작하기: CMSIS-RTOS v1 설정 및 태스크 구현 완벽 가이드 (0) | 2025.01.10 |
| FreeRTOS vs CMSIS-RTOS v1 완벽 비교: 임베디드 개발자를 위한 선택 가이드 (0) | 2025.01.09 |