Blinky 2 - Peripheral Access Crate 미리 보기

Nitroblue 1·2025년 8월 31일

지난 포스팅에서 MCU 레벨을 마무리했다.

이번 단계는 PAC (Peripheral Access Crate)이다.

들어가기에 앞서, PAC가 어떤 것인지 공부해보자.

RCC : Reset and Clock Control
→ STM32 시리즈에서 칩 내부 모든 주변장치(Peripheral)에 전원(클럭) 공급. 리셋 제어를 담당하는 중앙 제어 블록.

비유

  • MCU = 큰 빌딩
  • 주변장치(GPIO, USART, I2C…) = 각 방
  • RCC = 각 방에 전기 공급하는 분전함

스위치 내리면 방이 꺼짐 (전력 절약)
스위치 올려야만 방 안에 불을 켜거나 기기를 돌릴 수 있음


What's PAC?

  • 칩의 레지스터 맵을 코드로 자동 생성한 크레이트!
    • STM32, nRF, RP2040 같은 칩마다 데이터시트(= SVD 파일)가 있다. 이걸 바탕으로 svd2rust라는 툴이 안전한 Rust API로 변환해주는데, 이게 PAC인 것이다.
    • 즉, 데이터시트에 있는 GPIOA_MODER (0x4002_000) 같은 레지스터를 pac::GPIOA::ptr().MODER 같은 Rust 구조체/메서드로 접근 가능하게 해준다.
      -> 레지스터에 접근하는 명령어!!?
  1. GPIO : General Purpose Input/Output
    MCU(마이크로컨트롤러) 핀을 프로그램으로 입력/출력으로 자유롭게 쓸 수 있게 해주는 주변장치.
    예: 버튼 입력 받기, LED 켜기, 센서 데이터 읽기, 모터 제어 등.

  1. GPIOA란?
    STM32 시리즈 같은 칩에는 여러 개의 GPIO 포트가 있음:
    GPIOA, GPIOB, GPIOC, … 이런 식으로 알파벳으로 구분.

각 "포트"는 최대 16개의 핀을 가짐 (핀 번호 0~15).
예: PA5는 "GPIO A 포트의 핀 5"를 의미.

즉:
GPIOA = A번 포트
PA5 = A포트의 5번 핀 (Nucleo-F446RE 보드의 LD2 LED가 여기 연결되어 있음)


  1. 왜 A, B, C로 나눌까?
    MCU에는 수십~수백 개의 핀이 있는데, 그걸 그룹으로 묶어서 관리하기 위해.
  • STM32F446RE는 GPIOA ~ GPIOI 까지 제공.
    A~I 각각 최대 16핀 → 이론적으로 144개 핀까지.
    예:
    GPIOA 포트 = PA0 ~ PA15
    GPIOB 포트 = PB0 ~ PB15

  1. 실제 코드에서
    dp.RCC.ahb1enr.modify(|, w| w.gpioaen().set_bit()); // GPIOA 클럭 켜기
    dp.GPIOA.moder.modify(|
    , w| w.moder5().output()); // A포트 5번핀을 출력 모드로
    dp.GPIOA.bsrr.write(|w| w.bs5().set_bit()); // A포트 5번핀을 1로 (LED 켜기)

여기서:

  • RCC.ahb1enr.gpioaen() → GPIOA 포트 자체에 전원(클럭) 공급
  • GPIOA.moder5() → GPIOA 포트의 핀5 모드를 설정
  • GPIOA.bs5() → GPIOA 포트의 핀5 값을 제어 (Set/Reset)

✅ 정리
GPIOA = STM32 MCU의 A번 포트 (PA0~PA15)
PA5 = GPIOA 포트의 5번 핀, F446RE 보드 LED가 연결된 곳
따라서 “GPIOA”라고 부르면, 그 그룹(레지스터 세트 전체)을 의미하는 거고, “PAx”라고 부르면 그 포트 안의 특정 핀을 가리키는 거야.

Why PAC need?

우리가 지금 한 것처럼 write_volatile(0x40020000, 1) → 매직 넘버라서 읽기도 어렵고 버그 찾기도 힘듦.

PAC은:

  • 모든 레지스터 이름/필드 이름이 타입으로 표현됨
  • 잘못된 비트마스크 방지 (컴파일러가 도와줌)
  • 자동완성/문서화로 쓰기 훨씬 편함
예시
// Cargo.toml
stm32f4 = { version = "0.14.0", features = ["stm32f446"] }

use stm32f4::stm32f446;

fn main() {
    let dp = stm32f446::Peripherals::take().unwrap();

    // GPIOA 클럭 켜기
    dp.RCC.ahb1enr.modify(|_, w| w.gpioaen().set_bit());

    // PA5를 Output으로
    dp.GPIOA.moder.modify(|_, w| w.moder5().output());

    // LED On
    dp.GPIOA.bsrr.write(|w| w.bs5().set_bit());
}

✅ 정리:
PAC은 칩의 데이터시트를 안전하게 Rust 코드로 옮긴 자동 생성 라이브러리.
→ 덕분에 우리가 “매직 넘버” 없이, IDE 자동완성 도움받아 레지스터를 조작할 수 있음.
→ 보통 직접 PAC만 쓰기보다, 그 위에 얹은 HAL을 같이 씀.

더 나아가서

  • PAC vs HAL

PAC: 하드웨어 레벨 그대로. “레지스터 안전 래퍼”
모든 제어 가능 (풀 컨트롤), 하지만 저수준.

HAL (Hardware Abstraction Layer): PAC을 기반으로 만든 상위 추상화. gpioa.pa5.into_push_pull_output(); 처럼 훨씬 간단.
내부적으로는 PAC 호출.

즉:
PAC = 나사/볼트
HAL = 완성품 드라이버


0개의 댓글