728x90
반응형
임베디드 시스템에서 여러 개의 태스크(Task)가 동시에 돌아갈 때, 가장 중요한 문제 중 하나는 "어떻게 데이터를 안전하게 주고받을 것인가?"입니다. 전역 변수를 사용할 경우 데이터가 깨지는 레이스 컨디션(Race Condition)이 발생할 수 있습니다.
이를 해결하기 위해 FreeRTOS는 큐(Queue)라는 강력한 데이터 구조를 제공합니다. 이번 포스팅에서는 CMSIS-RTOS v2 표준 API를 사용하여 큐의 개념을 이해하고, 센서 데이터를 전송하는 실전 예제를 구현해 보겠습니다.

1. 큐(Queue)란 무엇인가?
큐는 데이터 항목을 순서대로 저장하고 관리하는 자료 구조로, FIFO(First In First Out, 선입선출) 방식으로 동작합니다. 즉, 먼저 들어간 데이터가 가장 먼저 나옵니다.
언제 큐를 사용하는가?
- 태스크 간 데이터 전송: 데이터를 생성하는 태스크(Producer)와 처리하는 태스크(Consumer) 간의 통신.
- 인터럽트와 태스크 간 통신: ISR(인터럽트 서비스 루틴)에서 발생한 급박한 데이터를 태스크로 전달하여 후처리할 때.
- 버퍼링: 데이터 생성 속도와 처리 속도가 다를 때 데이터를 임시 저장하는 완충 지대 역할.
2. 핵심 API 사용법 (CMSIS-RTOS v2)
2.1 osMessageQueuePut(): 데이터 삽입
큐에 데이터를 삽입합니다. 큐가 가득 찼을 경우, 설정한 timeout 동안 대기할 수 있습니다.
C
osStatus_t osMessageQueuePut(osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, uint32_t timeout);
- mq_id: 생성된 큐의 ID
- msg_ptr: 보낼 데이터의 주소
- timeout: 대기 시간 (osWaitForever 설정 시 데이터 공간이 생길 때까지 무한 대기)
2.2 osMessageQueueGet(): 데이터 추출
큐에서 데이터를 읽어옵니다. 큐가 비어 있다면 데이터가 들어올 때까지 대기합니다.
C
osStatus_t osMessageQueueGet(osMessageQueueId_t mq_id, void *msg_ptr, uint8_t *msg_prio, uint32_t timeout);
- msg_ptr: 데이터를 복사해올 버퍼 주소
- timeout: 데이터가 없을 경우 대기할 시간
3. [실습] 센서 데이터 교환 시스템 구현
시나리오: SensorTask가 주기적으로 값을 읽어 큐에 넣으면, ProcessTask가 이를 받아 출력합니다.
Step 1: 큐 선언 및 생성
C
#include "cmsis_os2.h"
#define QUEUE_SIZE 5
osMessageQueueId_t messageQueue;
// main 함수 등 초기화 영역에서 생성
messageQueue = osMessageQueueNew(QUEUE_SIZE, sizeof(uint32_t), NULL);
Step 2: 데이터를 보내는 태스크 (Producer)
C
void SensorTask(void *argument) {
uint32_t sensorData = 0;
while (1) {
sensorData = ReadSensor(); // 가상 센서 읽기 함수
// 큐에 데이터 넣기 (대기 시간 0: 즉시 반환)
if (osMessageQueuePut(messageQueue, &sensorData, 0, 0) == osOK) {
// 송신 성공 로그
}
osDelay(1000); // 1초 주기
}
}
Step 3: 데이터를 처리하는 태스크 (Consumer)
C
void ProcessTask(void *argument) {
uint32_t receivedData;
while (1) {
// 데이터가 올 때까지 무한 대기
if (osMessageQueueGet(messageQueue, &receivedData, NULL, osWaitForever) == osOK) {
// 수신 데이터 처리 로직 (예: 출력 또는 연산)
printf("Received: %lu\n", receivedData);
}
}
}
4. 큐 활용 시 주의사항 (개발자 팁)
- 복사 방식 전송: FreeRTOS 큐는 데이터의 '값'을 복사해서 저장합니다. 큰 구조체를 보낼 때는 포인터를 보내는 방식을 고려해야 메모리 오버헤드를 줄일 수 있습니다.
- Timeout 설정: osWaitForever를 사용할 때는 데드락(Deadlock) 가능성을 항상 염두에 두어야 합니다.
- 우선순위 역전: 큐를 기다리는 태스크들의 우선순위에 따라 스케줄링이 어떻게 변하는지 관찰하는 것이 중요합니다.
결론
FreeRTOS의 큐는 실시간 시스템에서 데이터의 무결성을 지키며 태스크 간 협력을 가능하게 하는 가장 기본적인 도구입니다. 이번 포스팅에서 다룬 CMSIS-RTOS v2 API를 활용해 보다 안정적인 임베디드 소프트웨어를 설계해 보세요!
도움이 되셨다면 공감과 댓글 부탁드립니다! 여러분은 큐를 어떤 용도로 주로 사용하시나요?
반응형
'Firmware & RTOS > FreeRTOS & Real-time Scheduling' 카테고리의 다른 글
| FreeRTOS 소프트웨어 타이머 완벽 가이드: osTimerNew 사용법 및 예제 (CMSIS-RTOS v2) (0) | 2025.01.15 |
|---|---|
| FreeRTOS 세마포어(Semaphore) vs 뮤텍스(Mutex) 차이점과 올바른 사용법 (0) | 2025.01.14 |
| CMSIS-RTOS v2 태스크 관리 완벽 가이드: 생성부터 상태 전환까지 (0) | 2025.01.12 |
| FreeRTOS 핵심 개념 완벽 정리: Task, Queue, Semaphore, Mutex 활용법 (0) | 2025.01.11 |
| FreeRTOS 시작하기: CMSIS-RTOS v1 설정 및 태스크 구현 완벽 가이드 (0) | 2025.01.10 |