왜 MCP23017이 GPIO 확장의 “새로운 강자”인가?
PCF8575가 **’가성비와 간편함’**으로 승부했다면, MCP23017은 **’정밀 제어와 압도적 확장성’**으로 중무장한 모듈입니다.
1. “저항 16개를 아껴주는 마법” (내부 풀업)
가장 큰 강점은 소프트웨어 설정만으로 내부 풀업 저항을 켤 수 있다는 점입니다. 16개의 버튼을 연결할 때 일일이 저항을 납땜할 필요가 없어, 회로 설계 시간을 획기적으로 줄여줍니다.
2. “I2C 핀 2개로 128개 제어” (주소 설정)
$A0, A1, A2$ 주소 핀을 조합하면 한 회로에 최대 8개의 MCP23017을 병렬로 물릴 수 있습니다. 이는 곧 단 2개의 핀으로 128개의 입출력을 다스릴 수 있다는 뜻입니다. 88건반 피아노를 만들고도 40개의 핀이 남는 압도적인 성능이죠.
3. “0.001초의 차이를 읽어내는 인터럽트”
단순히 핀이 많은 게 다가 아닙니다. 어떤 핀에 신호가 들어왔는지 MCU(ESP32 등)에 즉시 알려주는 인터럽트 기능이 강력하여, 전자 피아노의 건반 강약(Velocity)처럼 정밀한 시간 측정이 필요한 프로젝트에 최적화되어 있습니다.
“PCF8575가 단순히 핀을 늘려주는 ‘멀티탭’이라면, MCP23017은 각 포트의 전압과 신호를 정밀하게 관리하는 ‘스마트 배전반’과 같습니다.”
** GPIC 확장 모듈 MCP23017 을 테스트 하면서 먼저 막히는 부분이였던게 scl , sda , vdd, gnd 4개 만 연결해도 코드는 작동되어야 하지 않나 였습니다.
이론적으로는 SDA, SCL, VDD, GND 4개만 연결해도 통신은 되어야 할 것 같지만, MCP23017은 좀 까다로운 녀석이라 꼭 추가로 연결해줘야 하는 핀들이 있습니다.
이걸 안 하면 I2C 스캔조차 안 되거나, 주소가 지멋대로 변해서 코드가 작동하지 않습니다.
🛠️ 4개 외에 ‘반드시’ 추가 연결해야 할 핀
1. 주소 설정 핀 (A0, A1, A2) → 모두 GND로!
이 핀들을 공중에 띄워두면(Floating), 칩이 자기 주소를 결정하지 못하고 계속 흔들립니다.
- 해결법: A0, A1, A2 핀 3개를 모두 GND에 연결하세요.
- 이렇게 해야 주소가 확실하게 **
0x20**으로 고정되어 제가 짜드린 코드가 작동합니다.
2. 리셋 핀 (RESET) → VDD(3.3V)로!
리셋 핀은 ‘Low(GND)’일 때 칩을 초기화합니다. 이 핀을 그냥 두면 칩이 지멋대로 리셋 상태에 빠져서 응답을 안 할 수 있습니다.
- 해결법: **RESET 핀(18번 핀)을 VDD(3.3V)**에 연결해서 “나 지금 리셋 안 할 거야”라고 신호를 줘야 합니다.
📋 최종 체크리스트 (총 9개 연결)
브레드보드에서 아래 핀들이 다 꽂혀 있는지 확인해보세요!
- VDD: 3.3V
- VSS(GND): GND
- SCL: ESP32-C3의 1번 핀
- SDA: ESP32-C3의 2번 핀
- A0: GND (주소 결정)
- A1: GND (주소 결정)
- A2: GND (주소 결정)
- RESET: 3.3V (작동 대기)
- GND(공통): ESP32의 GND와 MCP23017의 GND가 서로 연결되어 있는지 확인!
PCF8575는 주소 핀에 기본 풀다운이 되어 있는 경우가 많아 대충 4개만 꽂아도 돌아갔을 수 있지만, MCP23017은 정석대로 주소와 리셋을 잡아줘야 합니다.
특히 A0~A2를 GND에 안 꽂으면 I2C 스캔할 때마다 주소가 0x20이었다가 0x27이었다가 춤을 출 거예요. ㅋ
지금 A0, A1, A2랑 RESET 핀 연결하셨나요? 요거 딱 연결하는 순간 i2c.scan() 결과에 0x20이 예쁘게 찍힐 겁니다! 😊🚀
MCP23017은 아두이노나 라즈베리 파이 같은 마이크로컨트롤러의 부족한 I/O 핀을 확장해주는 아주 고마운 칩입니다. 17HS4401 스텝모터나 여러 부품을 제어하다 보면 핀이 모자랄 때가 많은데, 이때 이 칩 하나로 I2C 통신을 통해 핀 16개를 추가할 수 있습니다.
1. 주요 특징 (왜 쓸까?)
- 16비트 확장: 핀 16개를 입력 또는 출력으로 사용할 수 있습니다. (8개씩 A포트, B포트로 나뉩니다.)
- I2C 통신: 단 2개의 선(SDA, SCL)만 있으면 됩니다.
- 주소 설정 가능: 주소 핀(A0, A1, A2)을 조절해 한 회로에 최대 8개까지 연결할 수 있습니다. (즉, 핀 16×8=128개 확장 가능!)
- 인터럽트 기능: 핀 상태가 변했을 때 MCU에 바로 알려주는 기능이 있어 효율적입니다.
2. 핀 구성 (Pinout)
칩을 보시면 총 28개의 핀이 있는데, 가장 중요한 것만 기억하세요.
- VDD / VSS: 전원 (+5V 또는 +3.3V) 및 그라운드(GND)
- GP A0 ~ A7 / GP B0 ~ B7: 우리가 실제로 제어할 16개의 확장 핀
- SCL / SDA: I2C 통신 핀 (아두이노의 경우 A5, A4 등에 연결)
- RESET: 칩 초기화 (보통 전원에 연결해둡니다)
- A0, A1, A2: 칩의 고유 주소를 정하는 핀 (모두 GND에 연결하면 기본 주소
0x20)
MCP23017 vs PCF8575 비교표
| 항목 | PCF8575 (간편함) | MCP23017 (고성능) |
| I/O 핀 수 | 16개 | 16개 (Port A/B로 구분) |
| 인터럽트 | 기본 지원 (INT 하나) | 강력함 (포트별/핀별 설정 가능) |
| 풀업 저항 | 내부 고정 (약함) | 내부 풀업 소프트웨어 설정 가능 |
| 통신 속도 | 최대 400kHz | 최대 1.7MHz (High Speed 모드) |
| 출력 방식 | Quasi-bidirectional (전류 약함) | Totem-pole (전류 강함) |
| 설정 레지스터 | 없음 (데이터만 보냄) | 20개 이상의 설정 레지스터 존재 |
| 동작 전압 | 2.5V ~ 5.5V | 1.8V ~ 5.5V |
1. 출력 능력과 제어 방식 (핵심 차이)
- PCF8575: 핀 하나하나를 ‘입력’인지 ‘출력’인지 따로 선언할 필요가 없습니다. 그냥 쓰면 됩니다. 하지만 핀에서 전류를 밀어내는 힘(Source)이 거의 없어 LED를 켤 때 반드시 Sink 방식을 써야 합니다.
- MCP23017:
IODIR레지스터를 통해 각 핀을 입력/출력으로 명확히 정해줘야 합니다. 대신 Push-Pull(Totem-pole) 구조라 전류 제어 능력이 훨씬 안정적이고 강력합니다.
2. 인터럽트(Interrupt)의 디테일
- PCF8575: 어떤 핀이든 상태가 변하면 신호를 보내지만, ‘어느 핀’이 변했는지는 직접 읽어서 확인해야 합니다.
- MCP23017: 특정 핀이 변했을 때만 인터럽트를 걸거나, 이전 값과 비교해서 변했을 때 등 아주 상세하게 조건을 걸 수 있습니다. 버튼 입력을 많이 받을 때 MCU의 부담을 확 줄여줍니다.
3. 내부 풀업 저항 유무
- PCF8575: 내부 풀업이 매우 약해서 버튼 입력 시 외부 저항이 필요한 경우가 많습니다.
- MCP23017: 소프트웨어 코드(
GPPU레지스터) 한 줄로 100kΩ 내부 풀업을 켤 수 있습니다. 부품 하나라도 아껴야 하는 ‘기술 방어’ 상황에서 최고죠.
** A0, A1, A2 사용법
MCP23017의 A0, A1, A2 핀은 이 칩의 **”주소(Address) 이름표”**를 결정하는 핀입니다. I2C 통신은 한 라인에 여러 개의 칩을 달 수 있기 때문에, 각 칩마다 고유한 주소가 있어야 하는데 이걸 하드웨어적으로 정해주는 역할을 합니다.
1. 핀 설정에 따른 I2C 주소 변화
MCP23017의 기본 주소는 이진수로 0100[A2][A1][A0]입니다. 핀을 **VCC(3.3V)**에 연결하면 1, GND에 연결하면 0이 됩니다.
| A2 연결 | A1 연결 | A0 연결 | 16진수 주소 (Hex) | 10진수 주소 (Dec) |
| GND | GND | GND | 0x20 | 32 (가장 기본) |
| GND | GND | 3.3V | 0x21 | 33 |
| GND | 3.3V | GND | 0x22 | 34 |
| … | … | … | … | … |
| 3.3V | 3.3V | 3.3V | 0x27 | 39 |
⚠️ 주의: 이 핀들을 아무 데도 연결하지 않고 공중에 띄워두면(Floating), 주변 노이즈에 따라 주소가 0x20이었다가 0x27이었다가 멋대로 변합니다. 그래서 반드시 GND나 VCC 중 한 곳에 고정해야 합니다.
2. 왜 이게 필요할까?
LED 32개를 제어하고 싶다면, MCP23017 칩 2개를 준비한 뒤 이렇게 하면 됩니다.
- 1번 칩: A0, A1, A2를 모두 GND에 연결 → 주소
0x20 - 2번 칩: A0만 3.3V에 연결, 나머지는 GND → 주소
0x21 - 코드:
I2C(0x20)에 명령 보내고, 이어서I2C(0x21)에 명령 보내면 끝!
3. 배선과 LED 테스트

노란 점퍼는 Reset 핀 HIGH 로 해놔야 노이즈로 인한 재부팅이 방지 됩니다.
python mcp23017.py : A_set_pin(), B_set_pin()
from machine import Pin, I2C
import time
# 1. 연결 확인용 I2C 스캔
print("Scan 1 (SDA2, SCL1):", I2C(0, sda=Pin(2), scl=Pin(1)).scan())
# 2. I2C 및 주소 설정
i2c = I2C(0, sda=Pin(2), scl=Pin(1), freq=400000)
ADDR = 0x20
# MCP23017 핵심 레지스터
IODIRA, IODIRB = 0x00, 0x01 # 입출력 방향 설정
GPIOA, GPIOB = 0x12, 0x13 # 실제 출력 값 제어
# 각 포트 상태 저장 (초기값: 0xFF = 모두 OFF)
state_a, state_b = 0xFF, 0xFF
speed = 0.1
def setup():
try:
# A, B 포트 모두 출력으로 설정
i2c.writeto_mem(ADDR, IODIRA, b'\x00')
i2c.writeto_mem(ADDR, IODIRB, b'\x00')
all_off()
print("A & B 포트 준비 완료!")
except Exception as e:
print(f"Error: {e}")
all_off()
def all_off():
i2c.writeto_mem(ADDR, GPIOA, b'\xFF')
i2c.writeto_mem(ADDR, GPIOB, b'\xFF')
def A_set_pin(no, state):
global state_a
if state == 0: state_a &= ~(1 << no)
else: state_a |= (1 << no)
i2c.writeto_mem(ADDR, GPIOA, bytes([state_a]))
def B_set_pin(no, state):
global state_b
if state == 0: state_b &= ~(1 << no)
else: state_b |= (1 << no)
i2c.writeto_mem(ADDR, GPIOB, bytes([state_b]))
# --- 포트 전용 핑퐁 로직 ---
def ping_pong_A():
for i in range(8):
A_set_pin(i, 0); time.sleep(speed); A_set_pin(i, 1)
for i in range(6, -1, -1):
A_set_pin(i, 0); time.sleep(speed); A_set_pin(i, 1)
def ping_pong_B():
for i in range(8):
B_set_pin(i, 0); time.sleep(speed); B_set_pin(i, 1)
for i in range(6, -1, -1):
B_set_pin(i, 0); time.sleep(speed); B_set_pin(i, 1)
setup()
while True:
ping_pong_A()
ping_pong_B()
◎ 제가 사용한 부품은 여기서 구매했습니다. 아래 링크를 통해 구매 시 소정의 수수료를 받습니다.
– TENSTAR 2개 MCP23017 시리얼 인터페이스 모듈 🔗 클릭 앤 구매: https://s.click.aliexpress.com/e/_c4PAltFd



micro2iot에 답글 남기기 응답 취소