Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

15-1. 임베디드 심화: 하드웨어 제어와 혁신적 디버깅 🔴

학습 목표:

  • 타입 안전한 하드웨어 레지스터 접근(MMIO)과 인터럽트 처리 기법을 배웁니다.
  • C의 volatile 키워드와 수동 레지스터 정의가 가진 한계를 Rust의 **PAC(Peripheral Access Crate)**가 어떻게 해결하는지 알아봅니다.
  • RTIC를 활용한 데드락 없는 실시간 동시성 제어 모델을 익힙니다.
  • **probe-rs**와 **defmt**를 활용한 현대적인 임베디드 디버깅 환경을 구축합니다.
  • **embedded-hal**을 통해 칩 제조사에 독립적인 드라이버를 설계하는 법을 배웁니다.

1. 하드웨어 레지스터 제어 (MMIO)

C에서는 #definevolatile 포인터로 레지스터에 접근하지만, 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-rsdefmt

전통적인 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**을 활용해 진정한 의미의 플랫폼 독립적 드라이버를 설계하세요.