14–15. RCC·GPIO 레지스터로 LED · GPIO 입력·풀업/풀다운
13편의 토대(CMSIS·RCC·클럭)를 처음으로 실전에 씁니다. STM32의 "Hello, World"인 LED 점멸을 HAL 없이 레지스터만으로 구현합니다. Blue Pill의 온보드 LED는 PC13에 액티브 로우로 달려 있죠. 핵심은 STM32F1 특유의 GPIO 설정 — 핀마다 4비트(MODE 2 + CNF 2)를 CRL(핀0~7)·CRH(핀8~15)에 쓰는 방식입니다. 이 4비트 조합을 이해하면 F1의 모든 GPIO 설정이 같은 패턴으로 풀립니다. 14편이 핀으로 신호를 내보냈다면 이번엔 핀으로 신호를 읽습니다. 초보자가 꼭 걸리는 함정은 플로팅 입력 — 아무것도 연결 안 된 핀은 0도 1도 아닌 떠 있는 상태라 노이즈에 흔들립니다. 이를 막는 것이 풀업/풀다운 저항이고, STM32는 내부에 내장합니다. PA0를 입력+내부 풀업으로 두고 IDR로 버튼을 읽어 PC13 LED를 제어합니다. F1은 풀업/풀다운 선택을 ODR로 한다는 특이점이 핵심입니다.
이 강의에서 배우는 것
- 1RCC로 GPIO 클럭을 켜고 F1의 CRL/CRH로 핀 모드를 설정한다
- 2MODE/CNF 4비트 조합으로 푸시풀 출력 2MHz(0x2)를 만든다
- 3ODR로 토글하고 BSRR/BRR로 원자적 set/reset을 한다
- 4액티브 로우 LED(PC13)의 ON/OFF 논리를 이해한다
- 5지우고(clear) 쓰기(set) 패턴으로 다른 핀을 보존한다
- 6GPIO를 입력 모드로 설정하고 IDR로 핀 상태를 읽는다
- 7플로팅 입력의 위험과 풀업/풀다운의 필요성을 설명한다
- 8F1에서 내부 풀업/풀다운을 ODR 비트로 선택한다
- 9액티브 로우/하이 버튼 판정 논리를 구현한다
- 10버튼 입력으로 LED를 켜고 끈다
14. RCC·GPIO 레지스터로 LED
13편의 토대(CMSIS·RCC·클럭)를 처음으로 실전에 씁니다. STM32의 "Hello, World"인 LED 점멸을 HAL 없이 레지스터만으로 구현합니다. Blue Pill의 온보드 LED는 PC13에 액티브 로우로 달려 있죠. 핵심은 STM32F1 특유의 GPIO 설정 — 핀마다 4비트(MODE 2 + CNF 2)를 CRL(핀0~7)·CRH(핀8~15)에 쓰는 방식입니다. 이 4비트 조합을 이해하면 F1의 모든 GPIO 설정이 같은 패턴으로 풀립니다.
소개
F4/L4의 MODER와 달리 F1은 핀마다 4비트를 CRL/CRH에 씁니다. 핀 n의 4비트는 (n%8)*4 위치에 놓입니다(핀0~7 → CRL, 핀8~15 → CRH).
핵심 개념
1) F1 GPIO 4비트 = MODE + CNF
| 비트 | 이름 | 의미 |
|---|---|---|
| [1:0] | MODE | 00=입력, 10=출력2MHz, 11=출력50MHz |
| [3:2] | CNF(출력) | 00=푸시풀, 01=오픈드레인, 10=AF푸시풀 |
LED 구동은 빠를 필요 없으니 MODE=10(2MHz)+CNF=00(푸시풀) → 4비트 0b0010 = 0x2. PC13은 CRH의 (13-8)*4=20번 비트부터 4비트.
2) 지우고 쓰기 + 출력 레지스터
GPIOC->CRH &= ~(0xFu << 20); /* PC13 자리 4비트 클리어 */
GPIOC->CRH |= (0x2u << 20); /* 0010 설정 */
GPIOC->ODR ^= (1u << 13); /* 토글(읽고-쓰기) */
GPIOC->BSRR = (1u << 13); /* 원자적 set (PC13=1) */
GPIOC->BRR = (1u << 13); /* 원자적 reset (PC13=0) */BSRR/BRR은 읽기 없이 특정 핀만 원자적으로 1/0으로 만듭니다 — 인터럽트 환경에서 ODR 읽기-수정-쓰기보다 안전합니다. PC13 LED는 액티브 로우라 핀=0이 ON.
핵심 예제
#include "stm32f10x.h"
#define LED_PIN 13u
static void delay(volatile uint32_t c){ while(c--){} }
int main(void) {
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; /* GPIOC 클럭 */
GPIOC->CRH &= ~(0xFu << ((LED_PIN-8u)*4u));
GPIOC->CRH |= (0x2u << ((LED_PIN-8u)*4u)); /* 푸시풀 출력 2MHz */
while (1) {
GPIOC->ODR ^= (1u << LED_PIN); /* PC13 토글 */
delay(200000u);
}
}CRH 비트 조작 검산: 리셋값 0x44444444 → PC13만 0x2로 → 0x44244444. Logic Analyzer에 PORTC.13을 추가하면 주기적 사각파가 보입니다.
자주 하는 실수
Q. 분명히 설정했는데 핀이 안 움직여요.
A. GPIO 클럭(RCC->APB2ENR |= RCC_APB2ENR_IOPCEN)을 먼저 켰는지 확인하세요. 클럭이 없으면 CRH/ODR에 써도 반영되지 않습니다.
Q. 옆 핀 설정까지 같이 바뀌어요.
A. =로 통째 덮어쓰면 다른 핀이 날아갑니다. 항상 &= ~(0xF<<shift)로 그 자리만 지우고 |=로 새 값을 넣으세요.
Q. CRH인데 왜 PC13이 (13-8)*4인가요?
A. CRL은 핀0~7, CRH는 핀8~15를 담습니다. CRH 안에서 핀8이 0번째라 핀13은 (13-8)=5번째 → 비트 20부터입니다.
정리
- F1 GPIO는 핀당 4비트(MODE+CNF)를 CRL/CRH에 쓴다(푸시풀 출력 2MHz=0x2)
- 4비트만 바꿀 땐 지우고-쓰기 패턴으로 다른 핀을 보존한다
- 출력은 ODR(토글)·BSRR/BRR(원자적 set/reset)로 한다
- PC13 LED는 액티브 로우 — 핀=0이 ON
- 모든 동작의 전제는 RCC 클럭 인에이블이다
과제
- PA5(핀0~7 영역)를 푸시풀 출력으로 설정하는 CRL 비트 조작을 계산
- ODR 토글 대신 BSRR/BRR만 써서 LED를 점멸하도록 바꾸기
- PC13과 PA5 두 LED를 서로 반대 위상으로 점멸
15. GPIO 입력·풀업/풀다운
14편이 핀으로 신호를 내보냈다면 이번엔 핀으로 신호를 읽습니다. 초보자가 꼭 걸리는 함정은 플로팅 입력 — 아무것도 연결 안 된 핀은 0도 1도 아닌 떠 있는 상태라 노이즈에 흔들립니다. 이를 막는 것이 풀업/풀다운 저항이고, STM32는 내부에 내장합니다. PA0를 입력+내부 풀업으로 두고 IDR로 버튼을 읽어 PC13 LED를 제어합니다. F1은 풀업/풀다운 선택을 ODR로 한다는 특이점이 핵심입니다.
소개
버튼·스위치 같은 입력은 임베디드의 기본입니다. 플로팅 입력은 주변 노이즈에 따라 0과 1 사이를 떠돌아 버튼을 안 눌러도 눌린 것처럼 읽힐 수 있습니다. 풀업(평소 1)·풀다운(평소 0)으로 기본값을 정해 해결합니다.
핵심 개념
1) 입력 모드 4비트와 풀업 선택
| CNF | 입력 종류 | 4비트 |
|---|---|---|
| 00 | 아날로그 | 0x0 |
| 01 | 플로팅(리셋 기본) | 0x4 |
| 10 | 풀업/풀다운 | 0x8 |
GPIOA->CRL |= (0x8u << 0); /* PA0 = 풀업/풀다운 입력 */
GPIOA->ODR |= (1u << 0); /* ODR=1 → 풀업, ODR=0 → 풀다운 */F1의 특이점: CNF=10일 때 풀업이냐 풀다운이냐는 ODR 비트가 정합니다(1=풀업, 0=풀다운). ODR 설정을 빠뜨리면 풀업이 안 걸립니다.
2) 읽기(IDR)와 능동 논리
if ((GPIOA->IDR & (1u << 0)) == 0u) { /* PA0 = 0 (눌림) */ }
/* 풀업 + 버튼이 GND 로 연결 → 안 누름=1, 누름=0 (액티브 로우) */IDR(Input Data Register)은 읽기 전용입니다. 출력은 ODR/BSRR/BRR로 합니다.
핵심 예제
#include "stm32f10x.h"
#define BTN 0u /* PA0 */
#define LED 13u /* PC13 */
int main(void) {
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN;
GPIOA->CRL &= ~(0xFu << (BTN*4u));
GPIOA->CRL |= (0x8u << (BTN*4u)); /* 입력 풀업/풀다운 */
GPIOA->ODR |= (1u << BTN); /* 풀업 선택 */
GPIOC->CRH &= ~(0xFu << ((LED-8u)*4u));
GPIOC->CRH |= (0x2u << ((LED-8u)*4u));
while (1) {
if ((GPIOA->IDR & (1u << BTN)) == 0u) GPIOC->BRR = (1u << LED); /* 눌림 ON */
else GPIOC->BSRR = (1u << LED); /* OFF */
}
}판정 검산: IDR=0x0001→released(LED OFF), IDR=0x0000→PRESSED(LED ON). 시뮬레이터 Command 창에서 `PORTA &= ~0x0001`(눌림)/`PORTA |= 0x0001`(뗌)로 흉내 냅니다.
자주 하는 실수
Q. 버튼을 안 눌렀는데 값이 마구 흔들려요.
A. 풀업/풀다운을 안 켜서 플로팅 상태입니다. CNF=10(0x8)으로 두고 ODR로 풀업(1)/풀다운(0)을 선택하세요.
Q. CNF=10으로 했는데도 풀업이 안 걸려요.
A. F1은 ODR 비트로 풀업/풀다운을 고릅니다. 입력 모드에서 ODR=1이면 풀업, 0이면 풀다운입니다. ODR 설정을 빠뜨리지 마세요.
Q. IDR에 쓰면 핀이 바뀌나요?
A. IDR은 읽기 전용입니다. 출력은 ODR/BSRR/BRR로 합니다. IDR은 핀의 현재 입력 상태를 읽기만 합니다.
정리
- 입력은 MODE=00, CNF로 종류 결정(0x8=풀업/풀다운 입력)
- 플로팅을 피하려 풀업/풀다운을 켜고, F1은 그 선택을 ODR로 한다
- 핀 상태는 읽기 전용 IDR로 읽는다
- 풀업 + GND 버튼 = 눌림이 0(액티브 로우)
- 채터링 처리는 디바운싱(21편)에서
과제
- PA0를 풀다운(ODR=0)으로 바꾸고 버튼을 VCC에 달았을 때의 판정 논리 작성
- 버튼을 누르고 있는 동안만 LED ON 하는 미러링과, 누를 때마다 토글하는 에지 검출을 비교 구현
- IDR 마스킹 시 우선순위 함정((IDR & m) == 0)을 설명
댓글 0
아직 댓글이 없습니다. 첫 댓글을 남겨 주세요.
댓글은 회원만 쓸 수 있습니다.
광고·욕설·비방·개인정보·불법 내용이 담긴 댓글은 숨기거나 지웁니다. 댓글은 닉네임과 함께 공개됩니다. 개인정보처리방침