08. GPIO 입력 — 버튼/스위치 읽기
7편이 핀으로 신호를 내보냈다면 이번엔 핀으로 신호를 읽어들입니다. 8051 특유의 함정은 포트가 준쌍방향(quasi-bidirectional)이라, 입력으로 읽기 전에 그 핀에 먼저 1을 써 두어야 외부 신호를 제대로 읽는다는 점입니다. P3.2 버튼을 읽어 P1.0 LED를 제어하고, "누르는 순간마다 토글"하는 에지 검출 로직을 익힙니다. 채터링은 21편에서 다룹니다.
이 강의에서 배우는 것
- 1준쌍방향 포트에서 입력 전 1을 써야 하는 이유를 설명한다
- 2sbit 으로 입력 핀을 읽어 조건 분기한다
- 3풀업/액티브 로우 버튼(평소 1, 누르면 0)을 이해한다
- 4버튼 상태를 LED로 미러링한다
- 5에지 검출(1→0)로 누르는 순간만 감지한다
소개
버튼·스위치·센서의 디지털 신호를 받아 프로그램이 반응하게 만드는 일입니다. 8051 준쌍방향 포트의 함정을 이해하는 것이 이번 편의 핵심입니다.
핵심 개념
1) 준쌍방향 포트 — 읽기 전에 1을 써라
8051 포트 핀은 출력 래치와 입력 버퍼를 공유합니다. 래치에 0을 써 두면 핀이 강하게 Low로 고정되어 외부 High가 읽히지 않습니다. 입력으로 쓸 핀에는 먼저 1을 써 핀을 "풀어 둬야" 합니다.
sbit BTN = P3 ^ 2;
BTN = 1; /* 입력 준비: 핀을 1(약한 풀업)로 */
if (BTN == 0) { } /* 이제 외부에서 0 을 주면 읽힌다 */2) 풀업+액티브 로우 버튼
| 상태 | 핀 값 |
|---|---|
| 안 누름 | 1 (풀업) |
| 누름 | 0 (GND) |
그래서 "눌림"의 판단은 `BTN == 0` 입니다.
3) 미러링 vs 에지 검출
if (prev_btn == 1 && btn == 0) { /* 하강 에지 = 막 눌린 순간 */
led = !led; /* 토글 (한 번만) */
}
prev_btn = btn;미러링은 눌린 동안 계속 반응(레벨), 에지 검출은 누르는 순간만 한 번 반응. "한 번 누를 때 한 번만"이 필요하면 에지 검출이 필수입니다. 접점 떨림(채터링)은 21편 디바운싱에서.
핵심 예제
#include <reg52.h>
sbit BTN = P3 ^ 2; /* 입력 */
sbit LED = P1 ^ 0; /* 출력(액티브 로우) */
void main(void) {
BTN = 1; /* 입력 준비 */
while (1) {
if (BTN == 0) LED = 0; /* 눌림 → LED ON */
else LED = 1; /* 뗌 → LED OFF */
}
}시뮬레이터: Port 3 창에서 P3.2 비트를 클릭해 0/1을 바꿔 "버튼"을 누르면 Port 1의 P1.0이 따라 바뀝니다.
자주 하는 실수
Q. 버튼을 눌러도 입력이 항상 같은 값으로 읽혀요.
A. 준쌍방향 포트 함정입니다. 그 핀에 출력 0을 써 둔 적이 있으면 입력이 막힙니다. 읽기 전에 BTN = 1; 로 핀을 풀어 두세요.
Q. 한 번 눌렀는데 LED가 여러 번 토글돼요.
A. 루프가 너무 빨라 "누르고 있는 동안" 여러 번 도는 것을 토글로 착각한 경우 → 에지 검출로 바꾸세요. 채터링이면 디바운싱(21편).
Q. if (P3 & 0x04 == 0) 이 이상해요.
A. 우선순위 함정. ==가 &보다 먼저라 0x04==0 이 먼저 계산됩니다. 괄호로 if ((P3 & 0x04) == 0), 또는 sbit 으로 if (BTN == 0) 이 명확합니다.
정리
- 준쌍방향 포트는 입력으로 읽기 전 핀에 1을 써 둔다
- 풀업+액티브 로우 버튼은 평소 1, 누르면 0 — 판단은 BTN == 0
- 미러링은 현재 상태를 따르고, 에지 검출은 누르는 순간만 잡는다
- 에지 검출은 직전 상태와 현재 상태를 비교한다(prev==1 && cur==0)
- 채터링은 디바운싱으로 해결한다(21편)
과제
- 버튼을 누를 때마다 LED 밝기 단계(흐르는 위치)를 1칸씩 옮기는 에지 검출 코드 작성
- 버튼 상태 시퀀스 {1,1,0,0,1,0}에 대해 토글 결과를 손으로 예측한 뒤 pc_test 로 검산
- 두 버튼(P3.2/P3.3)을 읽어 각각 다른 LED를 제어