
FreeRTOS의 모든 Task는 다음 4가지 상태 중 하나에 있습니다.
| 상태 | 영문 | 설명 |
|---|---|---|
| 실행 | Running | CPU에서 현재 실행 중인 상태 |
| 준비 | Ready | 실행 가능하지만 대기 중인 상태 |
| 대기 | Blocked | 이벤트를 기다리는 상태 |
| 정지 | Suspended | 명시적으로 정지된 상태 |
중요: 멀티코어가 아닌 단일 코어 시스템에서는 오직 하나의 Task만 Running 상태가 될 수 있습니다.
tip: MCU에서 테스크는 CS에서 얘기하는 프로세스(Process)보단 쓰레드(Thread)에 가깝습니다 MCU는 단일 주소 공간에 대해 각각의 테스크가 stack 영역을 할당받아 실행 흐름을 유지합니다. 이 때 인터럽트가 발생하면 현재 실행 중인 테스크와는 별개의 스택을 사용해 긴급한 처리를 수행합니다.
1) Running (실행) 상태
// Running 상태: CPU에서 현재 이 코드를 실행 중
void vMyTask(void *pvParameters) {
while(1) {
// 이 순간 이 Task는 Running 상태
printf("Task 실행 중!\n");
// 다른 상태로 전이될 때까지 계속 실행
process_data();
}
}
특징:
2) Ready (준비) 상태
void vReadyTask(void *pvParameters) {
while(1) {
// 이 Task는 Ready 상태에서 대기 중
// 더 높은 우선순위 Task가 실행 중이라면
// 이 Task는 Running으로 전이되기를 기다림
printf("준비 완료, 실행 대기 중...\n");
vTaskDelay(pdMS_TO_TICKS(100));
}
}
특징:
3) Blocked (대기) 상태
void vBlockedTask(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
while(1) {
printf("작업 수행\n");
// vTaskDelay() 호출 시 Blocked 상태로 전이
// 지정된 시간 동안 대기
vTaskDelay(pdMS_TO_TICKS(1000)); // 1초 동안 Blocked
// 1초 후 자동으로 Ready 상태로 전이
}
}
특징:
Blocked 상태로 전이되는 경우:
vTaskDelay() / vTaskDelayUntil() 호출xQueueReceive() 타임아웃)xSemaphoreTake() 타임아웃)4) Suspended (정지) 상태
TaskHandle_t xTaskHandle;
void vSuspendableTask(void *pvParameters) {
while(1) {
printf("Task 실행 중\n");
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void vControlTask(void *pvParameters) {
vTaskDelay(pdMS_TO_TICKS(3000));
// 다른 Task를 Suspended 상태로 전이
printf("Task 정지\n");
vTaskSuspend(xTaskHandle);
vTaskDelay(pdMS_TO_TICKS(3000));
// Suspended 상태에서 Ready 상태로 복구
printf("Task 재개\n");
vTaskResume(xTaskHandle);
vTaskDelete(NULL);
}
int main(void) {
xTaskCreate(vSuspendableTask, "Suspendable", 128, NULL, 2, &xTaskHandle);
xTaskCreate(vControlTask, "Control", 128, NULL, 3, NULL);
vTaskStartScheduler();
while(1);
}
특징:
vTaskSuspend() 호출 시 전이vTaskResume() 호출 전까지 복구 불가 생성
↓
┌─────────────────┐
│ Ready │ ←─────────────┐
│ (준비 완료) │ │
└─────────────────┘ │
↓ ↑ │
스케줄러 선택 선점됨 │
↓ ↑ │
┌─────────────────┐ │
│ Running │ │
│ (실행 중) │ │
└─────────────────┘ │
↓ ↑ │
Delay/대기 이벤트 발생 │
↓ ↑ │
┌─────────────────┐ │
│ Blocked │ │
│ (대기 중) │ │
└─────────────────┘ │
│
┌─────────────────┐ │
│ Suspended │ ──────────────┘
│ (정지됨) │ vTaskResume()
└─────────────────┘
↑
vTaskSuspend()
Running → Ready
void vHighPriorityTask(void *pvParameters) {
while(1) {
// 이 Task가 Ready되면 낮은 우선순위 Task를
// Running → Ready로 전이시킴 (선점)
printf("높은 우선순위 Task 실행\n");
vTaskDelay(pdMS_TO_TICKS(100));
}
}
void vLowPriorityTask(void *pvParameters) {
while(1) {
// Running 상태에서 실행 중
printf("낮은 우선순위 Task 실행\n");
// 높은 우선순위 Task가 Ready되면
// 이 Task는 즉시 Ready로 전이됨
for(volatile int i = 0; i < 100000; i++);
}
}
Running → Blocked
void vTaskWithDelay(void *pvParameters) {
while(1) {
printf("Running: 작업 수행\n");
process_data();
// vTaskDelay() 호출 순간:
// Running → Blocked로 전이
vTaskDelay(pdMS_TO_TICKS(1000));
// 1초 후 자동으로 Blocked → Ready
// 우선순위에 따라 Ready → Running
}
}
Blocked → Ready
void vTimerTask(void *pvParameters) {
while(1) {
printf("타이머 시작 (Running)\n");
// Blocked 상태로 전이
vTaskDelay(pdMS_TO_TICKS(5000));
// 5초 후:
// 1) Blocked → Ready 자동 전이
// 2) 스케줄러가 선택하면 Ready → Running
printf("타이머 완료 (다시 Running)\n");
}
}
Any State → Suspended
void vAnyTask(void *pvParameters) {
while(1) {
printf("Task 실행 중\n");
// 어떤 상태에서든 Suspended로 전이 가능
// 다른 Task가 vTaskSuspend(thisHandle) 호출 시
vTaskDelay(pdMS_TO_TICKS(100));
}
}
C 버전:
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
TaskHandle_t xTask1Handle, xTask2Handle;
void vTask1(void *pvParameters) {
while(1) {
printf("[Task1] Running: 작업 수행 중...\n");
// 1초 동안 Blocked 상태
printf("[Task1] Running → Blocked (1초 대기)\n");
vTaskDelay(pdMS_TO_TICKS(1000));
// Blocked → Ready → Running
printf("[Task1] Blocked → Ready → Running\n");
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void vTask2(void *pvParameters) {
vTaskDelay(pdMS_TO_TICKS(3000));
while(1) {
printf("\n[Task2] Task1을 Suspended 상태로 전이\n");
vTaskSuspend(xTask1Handle);
vTaskDelay(pdMS_TO_TICKS(3000));
printf("[Task2] Task1을 Suspended → Ready로 복구\n");
vTaskResume(xTask1Handle);
vTaskDelay(pdMS_TO_TICKS(3000));
}
}
int main(void) {
xTaskCreate(vTask1, "Task1", 128, NULL, 2, &xTask1Handle);
xTaskCreate(vTask2, "Task2", 128, NULL, 3, &xTask2Handle);
vTaskStartScheduler();
while(1);
}
C++ 버전:
#include "FreeRTOS.h"
#include "task.h"
#include <iostream>
class Task {
protected:
TaskHandle_t taskHandle;
const char* taskName;
uint16_t stackSize;
UBaseType_t priority;
virtual void run() = 0;
static void taskEntry(void* pvParameters) {
Task* task = static_cast<Task*>(pvParameters);
task->run();
}
public:
Task(const char* name, uint16_t stack, UBaseType_t prio)
: taskName(name), stackSize(stack), priority(prio), taskHandle(nullptr) {}
void create() {
xTaskCreate(taskEntry, taskName, stackSize, this, priority, &taskHandle);
}
void suspend() { vTaskSuspend(taskHandle); }
void resume() { vTaskResume(taskHandle); }
TaskHandle_t getHandle() const { return taskHandle; }
virtual ~Task() {}
};
class StateTransitionTask : public Task {
public:
StateTransitionTask() : Task("StateTask", 128, 2) {}
void run() override {
while(1) {
std::cout << "[StateTask] Running: 작업 수행 중..." << std::endl;
std::cout << "[StateTask] Running → Blocked (1초 대기)" << std::endl;
vTaskDelay(pdMS_TO_TICKS(1000));
std::cout << "[StateTask] Blocked → Ready → Running" << std::endl;
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
};
class ControlTask : public Task {
private:
StateTransitionTask& target;
public:
ControlTask(StateTransitionTask& t) : Task("Control", 128, 3), target(t) {}
void run() override {
vTaskDelay(pdMS_TO_TICKS(3000));
while(1) {
std::cout << "\n[Control] Task를 Suspended 상태로 전이" << std::endl;
target.suspend();
vTaskDelay(pdMS_TO_TICKS(3000));
std::cout << "[Control] Task를 Suspended → Ready로 복구" << std::endl;
target.resume();
vTaskDelay(pdMS_TO_TICKS(3000));
}
}
};
int main(void) {
StateTransitionTask stateTask;
ControlTask controlTask(stateTask);
stateTask.create();
controlTask.create();
vTaskStartScheduler();
while(1);
}
함수 원형:
void vTaskDelay(const TickType_t xTicksToDelay);
동작 방식:
사용 예제:
void vDelayTask(void *pvParameters) {
while(1) {
printf("Task 시작: %lu ms\n", xTaskGetTickCount());
// 작업 수행 (예: 50ms 소요)
process_data(); // 50ms
// 1000ms 대기 (현재 시점부터)
vTaskDelay(pdMS_TO_TICKS(1000));
// 실제 주기 = 작업시간(50ms) + 대기시간(1000ms) = 1050ms
}
}
문제점 - 주기 누적 오차:
void vDriftingTask(void *pvParameters) {
while(1) {
printf("Tick: %lu\n", xTaskGetTickCount());
// 목표: 1000ms 주기
expensive_operation(); // 실행 시간이 가변적 (10~100ms)
vTaskDelay(pdMS_TO_TICKS(1000));
// 실제 주기:
// 1010ms, 1050ms, 1020ms, ... (불규칙)
}
}
타이밍 다이어그램:
목표 주기: 1000ms
실제:
0ms 작업(50ms) Delay(1000ms) 1050ms
|-------████--------------|-------...
↑ ↑
작업 시간 다음 시작
누적 오차: +50ms, +100ms, +150ms, ...
함수 원형:
BaseType_t vTaskDelayUntil(TickType_t *pxPreviousWakeTime,
const TickType_t xTimeIncrement);
동작 방식:
사용 예제:
void vPeriodicTask(void *pvParameters) {
TickType_t xLastWakeTime;
const TickType_t xPeriod = pdMS_TO_TICKS(1000); // 1000ms 주기
// 현재 Tick을 초기 기준점으로 설정
xLastWakeTime = xTaskGetTickCount();
while(1) {
printf("정확한 Tick: %lu\n", xTaskGetTickCount());
// 작업 수행 (실행 시간 가변적)
process_data(); // 10~100ms
// 다음 깨어날 시간 = xLastWakeTime + 1000ms
// 작업 시간이 자동으로 보상됨
vTaskDelayUntil(&xLastWakeTime, xPeriod);
// 정확히 1000ms 주기 유지
}
}
동작 원리:
// vTaskDelayUntil() 내부 동작 (의사 코드)
void vTaskDelayUntil(TickType_t *pxPreviousWakeTime,
const TickType_t xTimeIncrement) {
TickType_t xTimeToWake;
// 다음 깨어날 절대 시간 계산
xTimeToWake = *pxPreviousWakeTime + xTimeIncrement;
// 현재 시간과 비교하여 필요한 대기 시간 계산
TickType_t xCurrentTime = xTaskGetTickCount();
TickType_t xTicksToWait = xTimeToWake - xCurrentTime;
// 대기
vTaskDelay(xTicksToWait);
// 다음 주기를 위해 기준 시간 업데이트
*pxPreviousWakeTime = xTimeToWake;
}
타이밍 다이어그램:
목표 주기: 1000ms (정확히 유지)
0ms 작업(50ms) Delay(950ms) 1000ms 작업(80ms) Delay(920ms) 2000ms
|-------████-----------|-------...-------|--------████---------|-------...-------|
↑ ↑ ↑ ↑
작업 시간 다음 시작 작업 시간 다음 시작
(자동 보상) (자동 보상)
누적 오차: 0ms (항상 정확)
C 버전:
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#include <stdlib.h>
// 가변 작업 시간 시뮬레이션
void variable_work(void) {
TickType_t workTime = (rand() % 50) + 10; // 10~60ms
vTaskDelay(pdMS_TO_TICKS(workTime));
}
// vTaskDelay() 사용 (상대적 지연)
void vDelayTask(void *pvParameters) {
int cycle = 0;
TickType_t startTime = xTaskGetTickCount();
while(cycle < 10) {
TickType_t currentTime = xTaskGetTickCount();
printf("[Delay] Cycle %d: %lu ms (오차: %ld ms)\n",
cycle,
currentTime,
(long)(currentTime - startTime - cycle * 1000));
variable_work(); // 가변 작업 시간
vTaskDelay(pdMS_TO_TICKS(1000)); // 1초 대기
cycle++;
}
vTaskDelete(NULL);
}
// vTaskDelayUntil() 사용 (절대적 지연)
void vDelayUntilTask(void *pvParameters) {
TickType_t xLastWakeTime;
const TickType_t xPeriod = pdMS_TO_TICKS(1000);
int cycle = 0;
TickType_t startTime = xTaskGetTickCount();
xLastWakeTime = xTaskGetTickCount();
while(cycle < 10) {
TickType_t currentTime = xTaskGetTickCount();
printf("[DelayUntil] Cycle %d: %lu ms (오차: %ld ms)\n",
cycle,
currentTime,
(long)(currentTime - startTime - cycle * 1000));
variable_work(); // 가변 작업 시간
vTaskDelayUntil(&xLastWakeTime, xPeriod); // 정확한 주기
cycle++;
}
vTaskDelete(NULL);
}
int main(void) {
printf("=== vTaskDelay() 테스트 ===\n");
xTaskCreate(vDelayTask, "Delay", 256, NULL, 2, NULL);
vTaskDelay(pdMS_TO_TICKS(12000)); // 첫 번째 Task 완료 대기
printf("\n=== vTaskDelayUntil() 테스트 ===\n");
xTaskCreate(vDelayUntilTask, "DelayUntil", 256, NULL, 2, NULL);
vTaskStartScheduler();
while(1);
}
예상 출력:
=== vTaskDelay() 테스트 ===
[Delay] Cycle 0: 0 ms (오차: 0 ms)
[Delay] Cycle 1: 1045 ms (오차: 45 ms)
[Delay] Cycle 2: 2110 ms (오차: 110 ms)
[Delay] Cycle 3: 3152 ms (오차: 152 ms)
...
=== vTaskDelayUntil() 테스트 ===
[DelayUntil] Cycle 0: 12000 ms (오차: 0 ms)
[DelayUntil] Cycle 1: 13000 ms (오차: 0 ms)
[DelayUntil] Cycle 2: 14000 ms (오차: 0 ms)
[DelayUntil] Cycle 3: 15000 ms (오차: 0 ms)
...
C++ 버전:
#include "FreeRTOS.h"
#include "task.h"
#include <iostream>
#include <cstdlib>
class Task {
protected:
TaskHandle_t taskHandle;
const char* taskName;
uint16_t stackSize;
UBaseType_t priority;
virtual void run() = 0;
static void taskEntry(void* pvParameters) {
Task* task = static_cast<Task*>(pvParameters);
task->run();
vTaskDelete(nullptr);
}
public:
Task(const char* name, uint16_t stack, UBaseType_t prio)
: taskName(name), stackSize(stack), priority(prio), taskHandle(nullptr) {}
void create() {
xTaskCreate(taskEntry, taskName, stackSize, this, priority, &taskHandle);
}
virtual ~Task() {}
};
void variableWork() {
TickType_t workTime = (rand() % 50) + 10;
vTaskDelay(pdMS_TO_TICKS(workTime));
}
class DelayTestTask : public Task {
public:
DelayTestTask() : Task("DelayTest", 256, 2) {}
void run() override {
int cycle = 0;
TickType_t startTime = xTaskGetTickCount();
while(cycle < 10) {
TickType_t currentTime = xTaskGetTickCount();
std::cout << "[Delay] Cycle " << cycle << ": " << currentTime
<< " ms (오차: " << (currentTime - startTime - cycle * 1000)
<< " ms)" << std::endl;
variableWork();
vTaskDelay(pdMS_TO_TICKS(1000));
cycle++;
}
}
};
class DelayUntilTestTask : public Task {
public:
DelayUntilTestTask() : Task("DelayUntilTest", 256, 2) {}
void run() override {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xPeriod = pdMS_TO_TICKS(1000);
int cycle = 0;
TickType_t startTime = xTaskGetTickCount();
while(cycle < 10) {
TickType_t currentTime = xTaskGetTickCount();
std::cout << "[DelayUntil] Cycle " << cycle << ": " << currentTime
<< " ms (오차: " << (currentTime - startTime - cycle * 1000)
<< " ms)" << std::endl;
variableWork();
vTaskDelayUntil(&xLastWakeTime, xPeriod);
cycle++;
}
}
};
int main(void) {
std::cout << "=== vTaskDelay() 테스트 ===" << std::endl;
DelayTestTask delayTask;
delayTask.create();
vTaskDelay(pdMS_TO_TICKS(12000));
std::cout << "\n=== vTaskDelayUntil() 테스트 ===" << std::endl;
DelayUntilTestTask delayUntilTask;
delayUntilTask.create();
vTaskStartScheduler();
while(1);
}
| 상황 | 권장 함수 | 이유 |
|---|---|---|
| 주기적 센서 읽기 | vTaskDelayUntil() | 정확한 샘플링 주기 필요 |
| 주기적 제어 신호 | vTaskDelayUntil() | 제어 주기 정확도 중요 |
| 단순 대기 | vTaskDelay() | 정확한 주기 불필요 |
| 타임아웃 | vTaskDelay() | 상대적 시간만 필요 |
| 데이터 수집 | vTaskDelayUntil() | 시간 동기화 필요 |
| LED 깜빡임 | vTaskDelay() | 정확한 주기 불필요 |
목표: 정확한 100ms 주기로 센서 값 읽기
C 버전:
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
// 센서 값 시뮬레이션
float read_sensor(void) {
return (float)(xTaskGetTickCount() % 100) / 10.0f;
}
void vSensorTask(void *pvParameters) {
TickType_t xLastWakeTime;
const TickType_t xPeriod = pdMS_TO_TICKS(100); // 100ms 주기
int sampleCount = 0;
// 초기 시간 설정
xLastWakeTime = xTaskGetTickCount();
printf("센서 Task 시작: 100ms 주기\n");
while(1) {
// 센서 읽기
float sensorValue = read_sensor();
TickType_t currentTime = xTaskGetTickCount();
printf("[샘플 %d] 시간: %lu ms, 센서 값: %.1f\n",
sampleCount, currentTime, sensorValue);
sampleCount++;
// 정확히 100ms마다 깨어남
vTaskDelayUntil(&xLastWakeTime, xPeriod);
}
}
int main(void) {
xTaskCreate(vSensorTask, "Sensor", 256, NULL, 2, NULL);
vTaskStartScheduler();
while(1);
}
예상 출력:
센서 Task 시작: 100ms 주기
[샘플 0] 시간: 0 ms, 센서 값: 0.0
[샘플 1] 시간: 100 ms, 센서 값: 0.0
[샘플 2] 시간: 200 ms, 센서 값: 0.0
[샘플 3] 시간: 300 ms, 센서 값: 0.0
...
목표: 서로 다른 주기를 가진 여러 Task 동시 실행
C 버전:
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
// 빠른 주기 Task (10ms)
void vFastTask(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xPeriod = pdMS_TO_TICKS(10);
while(1) {
printf("[Fast-10ms] Tick: %lu\n", xTaskGetTickCount());
vTaskDelayUntil(&xLastWakeTime, xPeriod);
}
}
// 중간 주기 Task (50ms)
void vMediumTask(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xPeriod = pdMS_TO_TICKS(50);
while(1) {
printf(" [Medium-50ms] Tick: %lu\n", xTaskGetTickCount());
vTaskDelayUntil(&xLastWakeTime, xPeriod);
}
}
// 느린 주기 Task (100ms)
void vSlowTask(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xPeriod = pdMS_TO_TICKS(100);
while(1) {
printf(" [Slow-100ms] Tick: %lu\n", xTaskGetTickCount());
vTaskDelayUntil(&xLastWakeTime, xPeriod);
}
}
int main(void) {
xTaskCreate(vFastTask, "Fast", 256, NULL, 3, NULL);
xTaskCreate(vMediumTask, "Medium", 256, NULL, 2, NULL);
xTaskCreate(vSlowTask, "Slow", 256, NULL, 1, NULL);
vTaskStartScheduler();
while(1);
}
C++ 버전:
#include "FreeRTOS.h"
#include "task.h"
#include <iostream>
class Task {
protected:
TaskHandle_t taskHandle;
const char* taskName;
uint16_t stackSize;
UBaseType_t priority;
virtual void run() = 0;
static void taskEntry(void* pvParameters) {
Task* task = static_cast<Task*>(pvParameters);
task->run();
}
public:
Task(const char* name, uint16_t stack, UBaseType_t prio)
: taskName(name), stackSize(stack), priority(prio), taskHandle(nullptr) {}
void create() {
xTaskCreate(taskEntry, taskName, stackSize, this, priority, &taskHandle);
}
virtual ~Task() {}
};
class PeriodicTask : public Task {
protected:
TickType_t period;
const char* label;
public:
PeriodicTask(const char* name, const char* lbl, uint16_t periodMs, UBaseType_t prio)
: Task(name, 256, prio), period(pdMS_TO_TICKS(periodMs)), label(lbl) {}
void run() override {
TickType_t xLastWakeTime = xTaskGetTickCount();
while(1) {
std::cout << label << " Tick: " << xTaskGetTickCount() << std::endl;
vTaskDelayUntil(&xLastWakeTime, period);
}
}
};
int main(void) {
PeriodicTask fastTask("Fast", "[Fast-10ms]", 10, 3);
PeriodicTask mediumTask("Medium", " [Medium-50ms]", 50, 2);
PeriodicTask slowTask("Slow", " [Slow-100ms]", 100, 1);
fastTask.create();
mediumTask.create();
slowTask.create();
vTaskStartScheduler();
while(1);
}
목표: 정확한 주기로 데이터를 수집하고 기록
C 버전:
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
#define MAX_SAMPLES 100
typedef struct {
TickType_t timestamp;
float value;
} DataSample;
DataSample dataBuffer[MAX_SAMPLES];
int sampleIndex = 0;
// 데이터 수집 Task (정확한 50ms 주기)
void vDataCollectorTask(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xPeriod = pdMS_TO_TICKS(50);
while(1) {
if(sampleIndex < MAX_SAMPLES) {
// 데이터 수집
dataBuffer[sampleIndex].timestamp = xTaskGetTickCount();
dataBuffer[sampleIndex].value = read_sensor();
printf("[수집] 샘플 %d: 시간=%lu, 값=%.2f\n",
sampleIndex,
dataBuffer[sampleIndex].timestamp,
dataBuffer[sampleIndex].value);
sampleIndex++;
} else {
// 수집 완료
printf("\n데이터 수집 완료 (총 %d 샘플)\n", MAX_SAMPLES);
vTaskDelete(NULL);
}
vTaskDelayUntil(&xLastWakeTime, xPeriod);
}
}
// 데이터 분석 Task (1초마다)
void vDataAnalyzerTask(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xPeriod = pdMS_TO_TICKS(1000);
while(1) {
if(sampleIndex > 0) {
// 평균 계산
float sum = 0;
for(int i = 0; i < sampleIndex; i++) {
sum += dataBuffer[i].value;
}
float average = sum / sampleIndex;
printf("[분석] 현재까지 평균: %.2f (샘플 수: %d)\n",
average, sampleIndex);
}
vTaskDelayUntil(&xLastWakeTime, xPeriod);
}
}
int main(void) {
xTaskCreate(vDataCollectorTask, "Collector", 512, NULL, 3, NULL);
xTaskCreate(vDataAnalyzerTask, "Analyzer", 512, NULL, 2, NULL);
vTaskStartScheduler();
while(1);
}
C++ 버전:
#include "FreeRTOS.h"
#include "task.h"
#include <iostream>
#include <vector>
struct DataSample {
TickType_t timestamp;
float value;
};
class DataLogger {
private:
std::vector<DataSample> samples;
size_t maxSamples;
public:
DataLogger(size_t max) : maxSamples(max) {
samples.reserve(max);
}
bool addSample(TickType_t time, float value) {
if(samples.size() < maxSamples) {
samples.push_back({time, value});
return true;
}
return false;
}
size_t getSampleCount() const { return samples.size(); }
float getAverage() const {
if(samples.empty()) return 0.0f;
float sum = 0;
for(const auto& s : samples) {
sum += s.value;
}
return sum / samples.size();
}
const DataSample& getSample(size_t index) const {
return samples[index];
}
};
class Task {
protected:
TaskHandle_t taskHandle;
const char* taskName;
uint16_t stackSize;
UBaseType_t priority;
virtual void run() = 0;
static void taskEntry(void* pvParameters) {
Task* task = static_cast<Task*>(pvParameters);
task->run();
}
public:
Task(const char* name, uint16_t stack, UBaseType_t prio)
: taskName(name), stackSize(stack), priority(prio), taskHandle(nullptr) {}
void create() {
xTaskCreate(taskEntry, taskName, stackSize, this, priority, &taskHandle);
}
virtual ~Task() {}
};
class DataCollectorTask : public Task {
private:
DataLogger& logger;
public:
DataCollectorTask(DataLogger& log)
: Task("Collector", 512, 3), logger(log) {}
void run() override {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xPeriod = pdMS_TO_TICKS(50);
while(1) {
TickType_t currentTime = xTaskGetTickCount();
float value = read_sensor();
if(logger.addSample(currentTime, value)) {
std::cout << "[수집] 샘플 " << logger.getSampleCount() - 1
<< ": 시간=" << currentTime
<< ", 값=" << value << std::endl;
} else {
std::cout << "\n데이터 수집 완료" << std::endl;
vTaskDelete(taskHandle);
}
vTaskDelayUntil(&xLastWakeTime, xPeriod);
}
}
};
class DataAnalyzerTask : public Task {
private:
DataLogger& logger;
public:
DataAnalyzerTask(DataLogger& log)
: Task("Analyzer", 512, 2), logger(log) {}
void run() override {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xPeriod = pdMS_TO_TICKS(1000);
while(1) {
if(logger.getSampleCount() > 0) {
std::cout << "[분석] 현재까지 평균: " << logger.getAverage()
<< " (샘플 수: " << logger.getSampleCount() << ")"
<< std::endl;
}
vTaskDelayUntil(&xLastWakeTime, xPeriod);
}
}
};
int main(void) {
DataLogger logger(100);
DataCollectorTask collector(logger);
DataAnalyzerTask analyzer(logger);
collector.create();
analyzer.create();
vTaskStartScheduler();
while(1);
}
C 버전:
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
void vMonitoredTask(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xPeriod = pdMS_TO_TICKS(100);
while(1) {
TickType_t startTime = xTaskGetTickCount();
// 작업 수행
complex_calculation();
TickType_t endTime = xTaskGetTickCount();
TickType_t executionTime = endTime - startTime;
printf("[모니터] 실행 시간: %lu ms (주기: 100ms)\n", executionTime);
if(executionTime > xPeriod) {
printf("경고: 실행 시간이 주기를 초과했습니다!\n");
}
vTaskDelayUntil(&xLastWakeTime, xPeriod);
}
}
C 버전:
void vDeadlineMonitorTask(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xPeriod = pdMS_TO_TICKS(100);
TickType_t xMaxExecutionTime = pdMS_TO_TICKS(80); // 80ms 제한
int missedDeadlines = 0;
while(1) {
TickType_t startTime = xTaskGetTickCount();
// 작업
process_data();
TickType_t executionTime = xTaskGetTickCount() - startTime;
if(executionTime > xMaxExecutionTime) {
missedDeadlines++;
printf("⚠️ 데드라인 위반! 실행: %lu ms (제한: %lu ms)\n",
executionTime, xMaxExecutionTime);
printf(" 누적 위반 횟수: %d\n", missedDeadlines);
}
vTaskDelayUntil(&xLastWakeTime, xPeriod);
}
}
| 측면 | C 방식 | C++ 방식 |
|---|---|---|
| 시간 관리 | 지역 변수로 xLastWakeTime 관리 | 멤버 변수로 캡슐화 |
| 주기 설정 | 함수 내부에서 직접 설정 | 생성자 파라미터로 설정 |
| 재사용성 | 함수 복사 필요 | 상속으로 재사용 |
| 데이터 공유 | 전역 변수 또는 구조체 | 클래스 멤버로 안전하게 공유 |
| 코드 구조 | 절차적, 평면적 | 계층적, 모듈화 |
C 방식을 선택할 때:
C++ 방식을 선택할 때:
4가지 Task 상태
상태 전이
지연 함수
주기적 Task 구현
| 개념 | 설명 |
|---|---|
| Task 상태 | Running, Ready, Blocked, Suspended |
| 상태 전이 | 조건에 따른 자동/수동 상태 변경 |
| vTaskDelay() | 현재 시점부터 상대적 대기 |
| vTaskDelayUntil() | 절대 시간 기준 주기적 실행 |
| 주기 정확도 | 실행 시간 자동 보상 |
모든 상태 전이를 로깅하는 Task 구현
요구사항:
3개의 서로 다른 주기를 가진 센서 Task
요구사항:
시스템 부하에 따라 주기를 동적으로 조정
요구사항: