XM10 Task Topology — RTOS Task API 사용자 가이드¶
XM10 SDK 의 사용자 보조 task 작성 가이드. 우선순위 + 데이터 흐름 + 공유변수 패턴 4가지.
Audience: SDK 사용자 (연구자 / 학습자 / 일반 로보틱스 개발자)
Date: 2026-05-15
Related:
- 사용자 API: XM_FW/XM_API/xm_api_freertos.h
- 예제: Examples/38_Periodic_Background_Task/ · Examples/39_Task_Lifecycle/
1. 시스템 Task¶
XM10 SDK 는 부팅 시 필요한 시스템 task 를 자동으로 만듭니다. 사용자가 직접 변경하면
안 됩니다 (시스템 안정성 손상). Control_Setup() 과 Control_Loop() 는 그중
제어 루프 task (Control_Loop) 에서 1 ms (1 kHz) 주기로 호출됩니다 (우선순위 53, 스택 32 KB).
사용자 보조 task 는 XM_Task_Create*() 와 아래 XM_PRIO_* 힌트로만 만드세요.
2. 사용자 Task 우선순위 영역¶
사용자가 XM_Task_CreateOneShot() / XM_Task_CreatePeriodic() 로 보조 task 를
만들 때 prio_hint 로 선택할 수 있는 영역입니다.
┌───────────────────────────────────────────────────────────────────────────────┐
│ 시스템 task — 사용자가 만들거나 바꿀 수 없음 │
│ 55 Realtime7 시스템 시작 / CAN-FD 수신 ← 로봇·센서 데이터 수신 즉시 처리 │
│ 54 Realtime6 UART 수신 ← 센서 패킷 파싱 │
│ 53 Realtime5 Control_Loop ← 1 kHz 제어 루프 │
│ 51 Realtime3 모듈 설정 메시지 처리 │
│ 25 Normal1 모듈 연결 관리 │
├───────────────────────────────────────────────────────────────────────────────┤
│ 사용자 선택 가능 영역 (XM_PRIO_*) │
│ 48 Realtime ← XM_PRIO_NEAR_REALTIME (주의: 모듈 연결 처리와 경합 가능) │
│ 40 High ← XM_PRIO_ABOVE_CONTROL │
│ 32 AboveNormal ← XM_PRIO_BELOW_CONTROL │
│ 24 Normal ← XM_PRIO_BACKGROUND ⭐ (권장 기본값) │
│ 8 Low ← XM_PRIO_IDLE │
└───────────────────────────────────────────────────────────────────────────────┘
| 우선순위 hint | 숫자 | 기본 stack | 용도 |
|---|---|---|---|
XM_PRIO_IDLE |
8 | 1 KB | 통계 / 로깅 등 매우 가벼운 작업 |
XM_PRIO_BACKGROUND |
24 | 8 KB | 권장 기본 — FFT / 학습 / 큰 행렬 등 |
XM_PRIO_BELOW_CONTROL |
32 | 4 KB | 100 ms 단위 PD 게인 갱신 등 |
XM_PRIO_ABOVE_CONTROL |
40 | 2 KB | 안전 감시 task |
XM_PRIO_NEAR_REALTIME |
48 | 2 KB | (주의) 100 Hz 이상 빠른 제어 보조 |
권장: 처음에는
XM_PRIO_BACKGROUND로 만드세요. stack 8 KB 가 일반 알고리즘에 충분하고 Control_Loop (1 kHz) jitter 에도 영향 최소.
3. 데이터 흐름 — Control_Loop ↔ 사용자 보조 Task¶
┌─────────────────────────────────────────────────────────────┐
│ 제어 루프 task (Control_Loop, 1 kHz) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 입력 수집 ─▶ XM.status.* │ │
│ │ Control_Loop() ◀── 사용자 알고리즘 │ │
│ │ XM.command.* ─▶ 출력 전송 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↕ (단일 워드 volatile / 멀티 워드 XM_Mutex) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 사용자 보조 task (Normal=24 등) — XM_Task_* │ │
│ │ 예: AdcSummary (100 Hz), HeavyCalc (OneShot) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
공유변수 패턴 4가지¶
| 패턴 | 변수 형태 | 보호 방법 | 예제 |
|---|---|---|---|
| A Single-word flag | volatile bool ready; |
volatile 만 | Ex.38 s_adc_avg |
| B Multi-word data | float buf[10]; |
XM_Mutex_* |
Ex.38 s_adc_buf |
| C ISR → Task | ISR write / Task read | volatile + memory barrier | (시스템 영역) |
| D Snapshot | Mutex 안 memcpy → 외부 read | Mutex+Snapshot | XM.status 스냅샷 |
4. 사용자 task 한도¶
| 상수 | 값 | 의미 |
|---|---|---|
XM_TASK_MAX_INSTANCES |
4 | 동시 사용자 task 한도 |
XM_TASK_HEAP_BUDGET_BYTES |
32768 | 사용자 task 가 쓸 수 있는 heap 한도 (32 KB) |
XM_TASK_STACK_MAX_WORDS |
2048 | 단일 task 최대 stack (8 KB) |
XM_TASK_STACK_MIN_WORDS |
128 | 단일 task 최소 stack (512 B) |
위반 시 동작:
- 4개 초과 / budget 초과 → XM_Task_Create* 가 NULL 반환 (단순 거부)
- 잘못된 핸들 사용 → API 가 false/NULL 반환 (Use-After-Delete 방어)
- self-Delete 시도 → no-op (거부)
⚠️ 워치독 (v2.6.0~): 시스템 워치독(IWDG, 약 8 초)은
Control_Loop가 도는 1 kHz 주기에서 갱신됩니다. 따라서Control_Setup이나Control_Loop안에서 8 초 넘게 머물면 보드가 리셋됩니다. 오래 걸리는 작업은 나눠서 수행하세요.여기서 만드는 사용자 task 는
XM_PRIO_*가 모두Control_Loop보다 낮으므로 (최고값XM_PRIO_NEAR_REALTIME= 48 < 제어 루프 task 53) 워치독 갱신을 막을 수 없습니다. 무거운 계산은 오히려 이쪽으로 옮기는 것이 안전합니다 (Ex.36 의 NN 학습이XM_PRIO_BACKGROUND로 도는 이유).
5. 권장 사용 패턴¶
5.1 Periodic Reader + Control_Loop Writer (Ex.38)¶
static XmMutexHandle_t s_mutex;
static volatile float s_result; /* 단일 워드 — volatile */
static float s_buffer[10]; /* 멀티 워드 — Mutex */
static void _Reader(void) { /* 100 Hz */
if (XM_Mutex_Lock(s_mutex, 0)) {
/* read s_buffer + compute */
XM_Mutex_Unlock(s_mutex);
s_result = computed_value; /* lock 밖 OK */
}
}
void Control_Setup(void) {
s_mutex = XM_Mutex_Create();
XM_Task_CreatePeriodic("Reader", _Reader, 10, XM_PRIO_BACKGROUND);
}
void Control_Loop(void) { /* 1 kHz */
if (XM_Mutex_Lock(s_mutex, 0)) { /* 항상 timeout=0 */
/* write s_buffer */
XM_Mutex_Unlock(s_mutex);
}
}
5.2 OneShot Trigger + Lifecycle 관리 (Ex.39)¶
static XmTaskHandle_t s_heavy;
void Control_Loop(void) {
if (trigger && s_heavy == NULL) {
s_heavy = XM_Task_CreateOneShot("Heavy", _Calc, NULL, XM_PRIO_BACKGROUND);
}
if (s_heavy && XM_Task_IsComplete(s_heavy)) {
XM_Task_Delete(s_heavy);
s_heavy = NULL; /* ← 필수: dangling 방지 */
}
}
6. 흔한 실수 (Pitfalls)¶
| 실수 | 결과 | 대응 |
|---|---|---|
Control_Loop 안에서 Mutex_Lock(m, > 0) |
1 ms 주기 깨짐 | 항상 timeout = 0, 실패 시 skip |
| Lock 후 Unlock 누락 | mutex 영구 점유 | early return 전에 Unlock |
| Task Delete 후 handle 재사용 | API false 반환 | Delete 직후 handle = NULL |
| Task Delete 누락 | 4개 도달 후 새 task 생성 NULL | IsComplete → Delete 사이클 |
XM_PRIO_NEAR_REALTIME 무한 루프 |
모듈 연결 처리와 경합 / 시스템 지연 | XM_PRIO_BACKGROUND 권장 |
| 단일 워드 vs 멀티 워드 race | 데이터 깨짐 | 32-bit 변수는 volatile, 그 이상은 Mutex |
| Float NaN/Inf 출력 | CAN-FD 송신 시 모터 폭주 | 계산 전 분모 != 0 / clamp |
7. 추천 학습 순서¶
- Ex.00 ~ Ex.35 — 단일 task 알고리즘 학습
- Ex.38 Periodic_Background_Task — Mutex+Snapshot 패턴 (이 문서 §3 참조)
- Ex.39 Task_Lifecycle — OneShot 생성/삭제 사이클
- 본 문서 §4~6 — 사용자 task 한도 + Pitfalls
8. 직접 점검할 것¶
Task API 는 §4 의 제약(개수·스택·우선순위)을 지키면 동작합니다. 실행 시간 초과나 교착(deadlock)은 자동으로 감시하지 않으니 직접 점검하세요.
9. 변경 이력¶
| 날짜 | 버전 | 변경 |
|---|---|---|
| 2026-05-15 | 1.0 | 초안 |