임베디드 프로그래밍 특성
📌 개요
- 제어 알고리즘은 PC가 아니라 액추에이터 옆의 작은 마이크로컨트롤러(MCU) 위에서 돈다
- MCU 프로그래밍 = 자원 제약·실시간성·하드웨어 밀착 → 일반 애플리케이션 개발과 성격이 크게 다름 · 이 특성을 이해해야 펌웨어 구조(로봇 액추에이터 펌웨어 구조)를 설계 가능
- 시험 출제 포인트: 하드/소프트 실시간의 구분, 인터럽트와 폴링의 비교, 크로스 개발환경, 워치독 타이머, volatile 등 ISR 작성 규칙
📖 핵심 개념
임베디드 시스템과 MCU
-
임베디드 시스템(Embedded System) = 특정 기능을 수행하도록 장치 안에 내장된 컴퓨터 시스템
-
액추에이터 제어기 = 전형적인 임베디드 시스템, 그 두뇌가 MCU(Microcontroller Unit)
-
MCU는 칩 하나에 다음이 집적:
-
CPU 코어: 연산 장치 (예: ARM Cortex-M 계열, 수십~수백 MHz)
-
플래시 메모리(Flash): 프로그램 코드 저장 — 비휘발성 (전원 꺼져도 유지)
-
SRAM: 실행 중 데이터(변수·스택) 저장 — 휘발성
-
주변장치(Peripheral): 타이머/PWM 발생기, ADC(아날로그→디지털: 전류 센서 읽기), GPIO, 통신 컨트롤러(UART·SPI·I2C·CAN) — 모터 제어에 필요한 하드웨어가 내장되어 있다
범용 OS 위에서 도는 PC 프로그램과 달리, 펌웨어는 OS 없이(베어메탈) 또는 소형 RTOS 위에서 하드웨어를 직접 다룬다 (OS 관점은 로봇 소프트웨어 운영체제).
자원 제약 — 작고, 느리고, 아껴야 한다
MCU의 자원은 PC와 자릿수가 다르다 (메모리 KB~MB, 클록 수십~수백 MHz).
- 메모리 절약: 대형 라이브러리·재귀·무분별한 버퍼 사용 곤란. 동적 메모리 할당(malloc)은 단편화·실패 위험 때문에 기피하고 정적 할당을 선호
- 연산 절약: 부동소수점 연산은 FPU(부동소수점 유닛)가 없는 MCU에서는 소프트웨어로 흉내 내므로 매우 느리다 → 정수 연산으로 실수를 표현하는 고정소수점(Fixed-point) 기법을 쓰기도 한다 (스케일링 팩터 관리 필요)
- 전력 제약: 배터리 구동 장치는 슬립 모드 등 저전력 설계 고려
실시간성 — "빠르다"가 아니라 "제때를 보장한다"
-
제어 루프는 정해진 주기마다 어김없이 실행되어야 함
-
실시간(Real-time) = 평균적으로 빠른 것이 아니라 마감시간(Deadline) 안에 응답함을 보장(결정론적, Deterministic) 하는 성질
-
하드 실시간(Hard Real-time): 데드라인을 한 번이라도 놓치면 시스템 실패·안전사고로 이어짐 — 모터 전류 제어, 비상정지가 해당
-
소프트 실시간(Soft Real-time): 가끔 놓쳐도 품질이 떨어질 뿐 치명적이지 않음 — 상태 모니터링 전송, 로그
-
주기의 흔들림 = 지터(Jitter) → 제어 품질을 직접 깎으므로 최소화 필요
-
실행 시간은 평균이 아닌 최악 실행 시간(WCET, Worst-Case Execution Time) 기준으로 데드라인 충족을 따짐
인터럽트와 폴링 — 사건을 알아채는 두 방식
| 구분 | 폴링(Polling) | 인터럽트(Interrupt) |
|---|---|---|
| 방식 | 루프를 돌며 계속 물어봄 ("일 생겼니?") | 사건 발생 시 하드웨어가 CPU를 호출 |
| 응답성 | 루프 한 바퀴 시간만큼 지연 가능 | 즉각적 (수 µs) |
| CPU 효율 | 사건이 없어도 CPU를 소모 | 사건 없으면 CPU가 다른 일 가능 |
| 구조 | 단순, 타이밍 예측 쉬움 | ISR 작성·우선순위 관리 필요 |
- 정밀한 주기가 생명인 제어 루프는 타이머 인터럽트로 구동
- 인터럽트 발생 시 실행되는 함수 = ISR(Interrupt Service Routine, 인터럽트 서비스 루틴), 작성 규칙이 있다
- 짧게 끝낸다: ISR이 길면 다른 인터럽트·메인 루프가 밀린다. 긴 처리는 플래그만 세우고 메인 루프에 넘긴다
- ISR과 메인 코드가 공유하는 변수는
volatile로 선언한다 — 컴파일러가 "안 바뀌는 값"으로 착각해 최적화(레지스터 캐싱)하는 것을 막는다 - 공유 데이터 접근 구간은 인터럽트 잠금 등으로 원자성(Atomicity) 을 보장한다 (경쟁 상태 방지)
- ISR 안에서 블로킹 호출·동적 할당·긴 출력(printf)을 하지 않는다
크로스 개발환경 — 만드는 곳과 도는 곳이 다르다
MCU에서는 컴파일러를 돌릴 수 없으므로, 호스트(Host) PC에서 개발·컴파일하고 타깃(Target) 보드에 내려보내 실행한다.
- 크로스 컴파일러(Cross Compiler): 호스트(x86)에서 타깃(ARM 등) 명령어로 번역하는 컴파일러. 컴파일러+링커+라이브러리 묶음이 툴체인(Toolchain)
- 플래싱(Flashing/Writing): 빌드된 바이너리를 타깃 플래시 메모리에 굽는 것
- 디버깅: 타깃 칩의 JTAG/SWD 디버그 포트에 디버거를 연결해 중단점·변수 관찰. 화면·키보드가 없는 보드에서는 UART 로그 출력도 병용
신뢰성 장치 — 멈추면 스스로 살아나야 한다
제어기는 사람이 지켜보지 않는 곳에서 연속 동작하므로 자가 복구 장치가 필수다.
- 워치독 타이머(Watchdog Timer, WDT): 소프트웨어가 주기적으로 카운터를 리셋("킥") 해야 하는 타이머. 무한 루프·폭주로 킥이 끊기면 타이머가 만료되어 MCU를 자동 리셋한다
- 브라운아웃 리셋(BOR): 전원 전압이 기준 이하로 내려가면 오동작 전에 리셋
- 기동 시 자기 진단(메모리·센서 체크), 예외(폴트) 핸들러에서 안전 정지
📊 다이어그램 · 수식
크로스 개발환경의 구성
flowchart LR
subgraph HOST["호스트 PC (x86)"]
SRC["소스 코드
(C/C++)"] --> TC["크로스 컴파일러
(툴체인)"]
TC --> BIN["타깃용 바이너리"]
end
subgraph TARGET["타깃 보드 (MCU, ARM)"]
FLASH["플래시 메모리
(펌웨어 상주)"] --> RUN["실행
+ 주변장치 제어"]
end
BIN -- "플래싱" --> FLASH
RUN -- "JTAG/SWD 디버깅
UART 로그" --> HOST폴링 vs 인터럽트 응답 구조
flowchart TD
subgraph P["폴링 방식"]
L["메인 루프"] --> Q{"사건 발생?"}
Q -- "아니오" --> L
Q -- "예" --> H1["처리"] --> L
end
subgraph I["인터럽트 방식"]
M["메인 루프
(다른 일 수행)"] -.->|"사건 발생 시
하드웨어가 중단"| ISR["ISR 실행
(짧게: 플래그 설정)"]
ISR -.->|"복귀"| M
end🎯 핵심 요약 · 암기 포인트
✏️ 예상문제
1. 실시간 시스템에서 "하드 실시간(hard real-time)"의 의미로 옳은 것은?
① 연산 속도가 매우 빠른 시스템 ② 데드라인을 놓치면 시스템 실패로 이어지므로 응답 시간이 반드시 보장되어야 하는 시스템 ③ 하드웨어만으로 구성된 시스템 ④ 평균 응답 시간이 짧은 시스템
정답 및 해설
정답: ② 실시간성의 본질은 속도가 아니라 보장(결정론) 이다. 모터 전류 제어처럼 한 번의 데드라인 미스가 폭주·사고로 이어질 수 있으면 하드 실시간, 로그 전송처럼 품질 저하에 그치면 소프트 실시간이다.
2. 인터럽트 서비스 루틴(ISR) 작성 원칙으로 옳지 않은 것은?
① 처리 시간을 최대한 짧게 유지한다 ② 메인 코드와 공유하는 변수는 volatile로 선언한다 ③ 시간이 오래 걸리는 작업은 ISR 안에서 모두 완료하고 복귀한다 ④ 긴 처리는 플래그를 설정해 메인 루프에서 수행하게 한다
정답 및 해설
정답: ③ ISR이 길어지면 더 급한 인터럽트와 제어 주기가 밀려 실시간성이 깨진다. ISR에서는 최소한의 일(데이터 읽기, 플래그 설정)만 하고 나머지는 메인 루프(또는 하위 태스크)로 넘기는 것이 원칙이다.
3. ISR과 메인 루프가 공유하는 변수를 volatile로 선언하는 이유는?
① 변수를 플래시 메모리에 저장하기 위해 ② 컴파일러 최적화로 인해 변수의 변경이 무시되는 것을 막기 위해 ③ 변수의 접근 속도를 높이기 위해 ④ 변수를 자동으로 원자적(atomic)으로 만들기 위해
정답 및 해설
정답: ② 컴파일러는 메인 루프 코드만 보고 "이 변수는 안 바뀐다"고 판단해 레지스터에 캐싱할 수 있다. 그러면 ISR이 바꾼 값을 메인 루프가 영영 못 본다. volatile은 매번 메모리에서 다시 읽으라는 지시다. 단, volatile이 원자성까지 보장하지는 않는다(④는 틀림).
4. 크로스 개발환경(cross development environment)이 필요한 이유로 가장 적절한 것은?
① 타깃 MCU에는 컴파일러를 실행할 자원이 없어 호스트 PC에서 타깃용 코드를 생성해야 하므로 ② 호스트 PC에서 펌웨어를 직접 실행하기 위해 ③ 타깃 보드의 전력 소모를 줄이기 위해 ④ 소스 코드를 암호화하기 위해
정답 및 해설
정답: ① MCU는 메모리·연산 자원이 작아 개발 도구를 돌릴 수 없다. 그래서 호스트에서 크로스 컴파일러(툴체인)로 타깃 아키텍처용 바이너리를 만들고, 플래싱으로 내려보내며, JTAG/SWD로 원격 디버깅한다.
5. 워치독 타이머(watchdog timer)에 대한 설명으로 옳은 것은?
① 제어 주기를 만드는 일반 타이머다 ② 소프트웨어가 주기적으로 리셋하지 않으면 만료되어 MCU를 자동 리셋시키는 안전장치다 ③ 전원 전압 저하를 감지하는 회로다 ④ 인터럽트 우선순위를 결정하는 장치다
정답 및 해설
정답: ② 정상 동작 중에는 소프트웨어가 계속 "킥"으로 만료를 막는다. 무한 루프·폭주에 빠지면 킥이 끊겨 타이머가 만료되고 시스템이 리셋되어 복구된다. ③은 브라운아웃 리셋(BOR)의 설명이다.
🔗 관련 노트
- 로봇 액추에이터 펌웨어 구조 — 이 특성 위에 세우는 펌웨어의 구조
- 로봇 액추에이터 펌웨어 설계 — 설계 절차와 안전 설계
- 로봇 소프트웨어 운영체제 — RTOS와 스케줄링의 일반론
- 로봇센서 인터페이스 — ADC·통신 등 주변장치로 센서 읽기
- _MOC 모션소프트웨어