15-1. 임베디드 심화: 하드웨어 제어와 혁신적 디버깅 🔴
학습 목표:
- 타입 안전한 하드웨어 레지스터 접근(MMIO)과 인터럽트 처리 기법을 배웁니다.
- C의
volatile키워드와 수동 레지스터 정의가 가진 한계를 Rust의 **PAC(Peripheral Access Crate)**가 어떻게 해결하는지 알아봅니다.- RTIC를 활용한 데드락 없는 실시간 동시성 제어 모델을 익힙니다.
- **
probe-rs**와 **defmt**를 활용한 현대적인 임베디드 디버깅 환경을 구축합니다.- **
embedded-hal**을 통해 칩 제조사에 독립적인 드라이버를 설계하는 법을 배웁니다.
1. 하드웨어 레지스터 제어 (MMIO)
C에서는 #define과 volatile 포인터로 레지스터에 접근하지만, Rust는 칩 제조사의 SVD 파일을 바탕으로 자동 생성된 PAC를 사용합니다.
- C 방식 (위험): 오타, 잘못된 비트 마스크, 권한(R/W) 위반 등을 컴파일 타임에 잡을 수 없어 런타임에 장치가 먹통이 되기 쉽습니다.
- Rust 방식 (안전): 각 레지스터 필드가 고유한 타입으로 정의되어 있어, 잘못된 필드에 접근하거나 읽기 전용 레지스터에 쓰는 행위가 컴파일 단계에서 차단됩니다.
#![allow(unused)] fn main() { // PAC를 사용한 안전한 타이머 레지스터 조작 fn configure_timer(dp: pac::Peripherals) { // 타임아웃 인터럽트 활성화 — 명시적인 필드 메서드 사용 (Safe) dp.TIM2.dier.modify(|_, w| w.uie().enabled()); // 카운터 값 초기화 — 타입 체크가 이뤄지는 필드 접근 dp.TIM2.cnt.write(|w| unsafe { w.bits(0) }); } }
2. 인터럽트와 실시간 동시성 (RTIC)
임베디드 시스템에서 가장 버그가 빈번한 지점은 인터럽트 핸들러와 메인 루프 간의 데이터 공유입니다.
- C 방식: 전역 변수 +
volatile+ 수동__disable_irq()/__enable_irq(). (잠금 해제를 잊는 실수가 잦음) - Rust (RTIC) 방식: **RTIC(Real-Time Interrupt-driven Concurrency)**는 인터럽트 우선순위와 자원 점유를 분석하여, 컴파일 타임에 데드락(Deadlock)이 없음을 증명하고 최적화된 잠금 코드를 생성합니다.
#![allow(unused)] fn main() { #[rtic::app(device = stm32f4xx_hal::pac)] mod app { #[shared] struct Shared { temp: f32 } #[task(binds = SysTick, shared = [temp])] fn tick(mut cx: tick::Context) { // 우선순위에 따른 안전한 잠금(Lock) 제공 cx.shared.temp.lock(|val| { *val += 0.1; }); } } }
3. 혁신적인 디버깅: probe-rs와 defmt
전통적인 OpenOCD + GDB 조합의 복잡함을 벗어나, Rust 전용의 통합 툴체인을 사용합니다.
probe-rs: 일체형 디버그 툴
- 드라이버 설치 한 번으로 수천 개의 칩을 즉시 지원합니다.
cargo embed명령어 하나로 빌드 → 플래싱 → 실시간 로그 확인이 원스톱으로 이루어집니다.
defmt: 지연 포맷팅 (Deferred Formatting)
- 초고성능: 로그 메시지 문자열을 칩에 저장하지 않고, 호스트로 인덱스만 전송합니다.
- 최저 부하: 기존
printf보다 10~100배 빠르며, 칩의 플래시 메모리 점유율을 획기적으로 줄입니다.
| 비교 항목 | 전통적인 C (OpenOCD/GDB) | 현대적인 Rust (probe-rs/defmt) |
|---|---|---|
| 설정 복잡도 | .cfg, .gdbinit 등 다수 파일 관리 | Embed.toml 파일 하나로 해결 |
| 로깅 오버헤드 | printf (CPU 중단 발생 가능) | defmt (비차단형, 초고속 전송) |
| GUI 통합 | 벤더 전용 IDE 필요 | VS Code + probe-rs-debug 확장 |
4. 하드웨어 추상화 혁명: embedded-hal
C 드라이버는 특정 칩 제조사의 HAL에 종속되는 경우가 많아 포팅이 고통스럽습니다. Rust는 embedded-hal 트레이트를 통해 한 번 작성한 드라이버를 모든 MCU에서 재사용할 수 있는 아키텍처를 제공합니다.
📝 실습 연습: 재사용 가능한 센서 드라이버 설계
SPI 통신을 사용하는 센서용 no_std 드라이버를 작성해 보세요.
- 목표:
embedded_hal::spi::SpiDevice를 사용하여, 특정 칩에 의존하지 않고 SPI 인터페이스를 가진 모든 환경에서 동작하는 드라이버를 만듭니다.
📌 요약
- PAC는 레지스터 조작의 안정성을 컴파일 타임에 보장합니다.
- RTIC는 동시성 오류와 데드락을 방지하는 현대적인 임베디드 프레임워크입니다.
- **
defmt**와 **probe-rs**는 임베디드 개발 경험을 웹/앱 개발 수준으로 끌어올립니다. - **
embedded-hal**을 활용해 진정한 의미의 플랫폼 독립적 드라이버를 설계하세요.