Bare metal C Ch02 레지스터 직접정의 - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki

Chapter 02 — Memory-Mapped Register Access

1. 목표

CMSIS나 HAL 없이 Reference Manual의 주소 정보만 사용해서 GPIO 레지스터를 직접 정의하고 LED를 제어한다.

핵심은 다음 한 문장이다.

Peripheral register는 CPU 입장에서 특정 memory address에 배치된 32-bit 값이다.

이번 실습에서는 NUCLEO-F439ZI의 LD1(녹색 LED, PB0) 을 대상으로 한다.


2. 원서와 실제 실습 보드 차이

항목 | 원서 NUCLEO-F411 | 실습 NUCLEO-F439ZI -- | -- | -- MCU | STM32F411RE | STM32F439ZI LED | LD2 / PA5 | LD1 / PB0 GPIO Port | GPIOA | GPIOB GPIO Clock Enable | RCC_AHB1ENR bit0 | RCC_AHB1ENR bit1 Reference Manual | STM32F411 계열 | RM0090

따라서 원서 코드를 그대로 복사하지 않고 PB0 기준으로 주소와 bit를 다시 계산했다.


3. Register 주소 계산

STM32F4의 peripheral 영역은 0x4000_0000에서 시작한다.

PERIPH_BASE
= 0x4000_0000

AHB1PERIPH_BASE = PERIPH_BASE + 0x0002_0000 = 0x4002_0000

GPIOB와 RCC의 base address:

GPIOB_BASE
= AHB1PERIPH_BASE + 0x0400
= 0x4002_0400

RCC_BASE = AHB1PERIPH_BASE + 0x3800 = 0x4002_3800

필요한 register:

RCC_AHB1ENR
= RCC_BASE + 0x30
= 0x4002_3830

GPIOB_MODER = GPIOB_BASE + 0x00 = 0x4002_0400

GPIOB_ODR = GPIOB_BASE + 0x14 = 0x4002_0414

핵심 공식

Register Address = Peripheral Base Address + Register Offset

4. 구현 순서

RCC_AHB1ENR
│
└─ GPIOB clock enable
│
▼
GPIOB_MODER
│
└─ PB0 = General Purpose Output
│
▼
GPIOB_ODR
│
└─ PB0 Toggle
│
▼
LD1 LED

Step 1 — GPIOB Clock Enable

RCC_AHB1ENR의 bit1을 1로 설정한다.

RCC_AHB1ENR |= GPIOBEN;

STM32는 사용하지 않는 peripheral의 clock을 차단하는 clock gating을 사용하므로, GPIOB를 설정하기 전에 clock을 먼저 활성화해야 한다.

Step 2 — PB0 Output Mode

GPIOx_MODER는 pin 하나당 2bit를 사용한다.

00 : Input
01 : General-purpose output
10 : Alternate function
11 : Analog

PB0은 MODER의 bit [1:0]을 사용한다.

먼저 2bit field를 00으로 clear한 뒤 01을 설정한다.

GPIOB_MODER &= ~(0x3UL << (0 * 2));
GPIOB_MODER |=  (0x1UL << (0 * 2));

Step 3 — PB0 Toggle

GPIOB_ODR의 bit0은 PB0 output 값과 대응된다.

GPIOB_ODR ^= LD1_PIN;

XOR 연산으로 bit0을 반복해서 뒤집어 LED를 점멸한다.


5. 전체 코드

실제 Ch02_register/Src/main.c에서 사용한 코드다.

#define PERIPH_BASE       (0x40000000UL)
#define AHB1PERIPH_BASE    (PERIPH_BASE + 0x00020000UL)

#define RCC_BASE (AHB1PERIPH_BASE + 0x3800UL) #define GPIOB_BASE (AHB1PERIPH_BASE + 0x0400UL)

#define RCC_AHB1ENR (*(volatile unsigned int )(RCC_BASE + 0x30UL)) #define GPIOB_MODER ((volatile unsigned int )(GPIOB_BASE + 0x00UL)) #define GPIOB_ODR ((volatile unsigned int *)(GPIOB_BASE + 0x14UL))

#define GPIOBEN (1UL << 1) #define LD1_PIN (1UL << 0)

static void delay(volatile unsigned int count) { while (count--) { __asm("nop"); } }

int main(void) { RCC_AHB1ENR |= GPIOBEN;

GPIOB_MODER &amp;= ~(0x3UL &lt;&lt; (0 * 2));
GPIOB_MODER |=  (0x1UL &lt;&lt; (0 * 2));

while (1)
{
    GPIOB_ODR ^= LD1_PIN;
    delay(800000);
}

}


6. 코드 문법 핵심

volatile

volatile unsigned int

Hardware register나 delay counter처럼 컴파일러가 임의로 접근을 생략하면 안 되는 대상에 사용한다.

Register 직접 접근

(*(volatile unsigned int *)ADDRESS)

해석 순서:

ADDRESS
↓
해당 숫자를 pointer로 변환
↓
volatile unsigned int *
↓
  • 연산자로 역참조 ↓ 해당 주소의 32-bit register에 직접 접근

Bit Set

reg |= (1UL << n);

특정 bit를 1로 만든다.

Bit Clear

reg &= ~(1UL << n);

특정 bit를 0으로 만든다.

Bit Toggle

reg ^= (1UL << n);

특정 bit를 0 ↔ 1로 반전한다.


7. Hardware 관점에서 코드 읽기

예를 들어:

GPIOB_ODR ^= LD1_PIN;

은 단순 C 변수 연산이 아니다.

Cortex-M4
│
│ Memory-Mapped Write
▼
AHB1 Bus
│
▼
GPIOB Peripheral
│
▼
ODR bit0 Toggle
│
▼
PB0 HIGH / LOW
│
▼
LD1 ON / OFF

즉 C 코드 → bus transaction → peripheral register → 실제 pin 출력으로 이어진다.


8. 프로젝트에서 확인한 파일

Ch02_register/
├── Src/
│   ├── main.c
│   ├── syscalls.c
│   └── sysmem.c
├── Startup/
│   └── startup_stm32f439zitx.s
├── STM32F439ZITX_FLASH.ld
└── STM32F439ZITX_RAM.ld

Chapter 02에서는 핵심 학습 대상이 main.c이며, startup/linker 관련 파일은 이후 Chapter 04에서 별도로 분석한다.


9. 검증 상태

  • 코드 작성: ✅

  • STM32CubeIDE Build: ✅

  • ELF 생성: ✅

  • Register 직접 접근 구조 확인: ✅

Hardware 실기 검증 여부는 실제 보드 테스트 기록에 맞춰 별도로 갱신한다.


10. 핵심 정리

  • Memory-Mapped I/O에서는 peripheral register가 memory address에 배치된다.

  • STM32 peripheral 사용 전에는 RCC를 통해 해당 peripheral clock을 활성화한다.

  • Register 주소는 Base Address + Offset으로 계산한다.

  • MODER는 GPIO pin의 mode를 설정한다.

  • ODR은 output pin의 실제 HIGH/LOW 상태를 제어한다.

  • volatile은 hardware register 접근에서 필수적으로 이해해야 하는 C qualifier다.

⚠️ **GitHub.com Fallback** ⚠️