PCF8575는 I2C 통신 단 2선(SDA, SCL)만으로 GPIO 핀을 16개나 늘려주는 모듈입니다. ESP32-C3처럼 핀 수가 적은 보드를 쓸 때 필요한 아이템이죠.
라이브러리 없이도 i2c.writeto()와 i2c.readfrom()만으로 제어가 가능합니다. PCF8575는 16비트(2바이트) 데이터를 주고받습니다.
메카넘 휠 RC카 제작 시 4개의 모터를 독립적으로 구동하려면 정방향, 역방향, 속도 조절(PWM)을 위해 모터당 3개씩 총 12개의 GPIO 핀이 필수적인데, 제가 주력으로 사용하는 ESP32-C3 미니 보드는 실제 가용 핀이 11개 내외인 데다 일부 핀은 부팅 시 설정(Strapping)이나 플래시 메모리 점유 등 하드웨어적 제약이 따라 12개의 핀을 온전히 확보하기가 매우 어렵습니다.
이러한 핀 부족 문제를 해결하기 위해 이전에 데이터 전송 시 3개의 핀이 필요하고 제어 로직이 상대적으로 복잡한 비트 시프트 방식(74HC595 등)을 사용해보기도 했지만, 단 2개의 I2C 통신 핀(SDA, SCL)만으로 최대 16개의 입출력 포트를 즉시 확장해주는 PCF8575 모듈을 활용하면 배선이 획기적으로 간결해질 뿐만 아니라, 모터 제어는 물론 향후 로봇 팔 구동이나 화려한 LED 점등 시스템 같은 추가 장치까지 제약 없이 유연하게 확장할 수 있겠습니다.
테스트는 공통저항 방식과 Sink 방식으로 구동하는 방법을 적용해서 테스트했습니다.
1. 공통저항 방식의 장점
모든 LED의 공통 단자(Common)에 저항 하나만 연결하는 방식은 개별 LED마다 저항을 달아야 하는 번거로운 배선 작업을 편하게 해 테스트할 때 좋습니다.
2. SINK(Active Low) 방식의 장점
부하의 마이너스(-) 극을 MCU 핀에 연결하여 전류를 빨아들이는 SINK 방식은 MCU가 직접 전류를 밀어내는 SOURCE 방식보다 전력 공급의 부담이 적어 회로를 더욱 안정적으로 보호할 수 있으며, 특히 PCF8575와 같은 확장 모듈이나 특정 MCU에서 훨씬 강력한 전류 구동 능력을 발휘함과 동시에 부팅 시 발생할 수 있는 원치 않는 LED 깜빡임(Glitch) 현상을 억제하는 데 유리합니다.
모듈에는 VDD, GND, SLC, SDA 만 ESP32-C3랑 연결하면 끝입니다.
추가로 PCF8575 모듈 양쪽에 VCC와 GND가 있는 이유는 I2C 장치들을 직렬로 계속 연결하기 편하게 하기 위해서입니다. 이를 데이지 체인(Daisy Chain) 방식이라고 하는데, 덕분에 빵판이 훨씬 깔끔해집니다. 또한 INT 핀을 활용하면 CPU 부하를 획기적으로 줄일 수 있는 저전력 설계를 할 수 있습니다.
from machine import Pin, I2C
import time
# 1. 설정 변수
speed = 0.08 # 점멸 속도 (초 단위)
PCF8575_ADDR = 0x20
# 2. I2C 설정 (SDA=2, SCL=1)
i2c = I2C(0, sda=Pin(2), scl=Pin(1), freq=100000)
# 3. ESP32 직접 연결 핀 설정 (11개)
esp_pin_numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 21]
esp_pins = [Pin(i, Pin.OUT, value=1) for i in esp_pin_numbers] # 처음엔 모두 끄기(1)
# PCF8575 상태 저장 변수
pcf_state = 0xFFFF
def ExtPin(pin_num, value):
"""PCF8575의 16개 핀 제어 함수"""
global pcf_state
if value == 0: pcf_state &= ~(1 << pin_num)
else: pcf_state |= (1 << pin_num)
low_byte = pcf_state & 0xFF
high_byte = (pcf_state >> 8) & 0xFF
i2c.writeto(PCF8575_ADDR, bytes([low_byte, high_byte]))
#print(f"PCF8575 State: {pcf_state:016b}")
print(f"i2c.writeto({hex(PCF8575_ADDR)}, bytes([0b{low_byte:08b}, 0b{high_byte:08b}]))")
# [초기화] 모든 LED 끄기
def init():
i2c.writeto(PCF8575_ADDR, b'\xff\xff')
for p in esp_pins: p.value(1)
init()
def test():
print(f"27개 LED 통합 핑퐁 테스트 시작! (속도: {speed}s)")
while True:
try:
# --- [순방향] 0번(PCF)에서 26번(ESP)까지 ---
# 1. PCF8575 핀 16개 (0~15)
for i in range(16):
ExtPin(i, 0)
time.sleep(speed)
ExtPin(i, 1)
# 2. ESP32 직접 핀 11개 (16~26)
for p in esp_pins:
p.value(0)
time.sleep(speed)
p.value(1)
# --- [역방향] 25번(ESP)에서 1번(PCF)까지 ---
# 1. ESP32 직접 핀 역순 (마지막 핀 제외)
for i in range(len(esp_pins)-2, -1, -1):
esp_pins[i].value(0)
time.sleep(speed)
esp_pins[i].value(1)
# 2. PCF8575 핀 역순 (첫 번째 핀 제외)
for i in range(15, 0, -1):
ExtPin(i, 0)
time.sleep(speed)
ExtPin(i, 1)
except Exception as e:
print("중단됨:", e)
break
finally:
init()
if __name__ == "__main__" :
test()
# i2c.writeto(PCF8575_ADDR, bytes([0b11110111, 0b11111111]))




답글 남기기