pico를 미니 NPU로 사용하는 프로젝트를 진행하며 어려웠던 내용과 해결한 방법에 대해 작성하겠다.
현재 pico에 tinyusb를 사용하여 vendor specific interface로 driver와 통신하고 있다. 개발을 진행하며 너무나 불편한 점이 많았다. pico에 올린 펌웨어 내용을 디버깅할 수 없었다. 익숙한 개발 환경이 아니라서 잘 못하는 걸 수도 있는데 일단 한번 올리고 나면 디버깅을 하기 힘들었다. LED 색상을 통해 해봤는데 컴퓨터를 부실뻔했다.
그래서 찾아보니 USB 장치의 경우 물리적 하드웨어 내에 여러 개의 인터페이스가 존재한다. 호스트(PC)에 연결했을 때, 장치 관리자에서 CDC 용 가상 COM 포트와 Vendor-Specific 장치가 각각 독립적으로 나타날 수 있다고 한다.

위 사진처럼 USB Composite Device 하위에 여러 장치가 나타나는 것을 말한다.
pico의 경우 printf를 호출하면 내부적으로 표준 출력(stdout)이 USB CDC Serial로 리다이렉션되어 PC의 터미널에서 내용을 볼 수 있다고 한다.
소스레벨에서는 tusb_config.h와 usb_descriptor.c를 수정해주면 된다. 자세한 내용은 깃헙을 참조하면 된다.
https://github.com/wangki-kyu/pico_usb_vendor
pico에 소스를 올리게 되면 다음과 같은 동작을 예상할 수 있다. vendor specific interface로 인식된 장치의 경우 직접 만든 driver가 올라갈 것이고 CDC 인터페이스는 usbser.sys 드라이버가 자동을 로드되어 가상 COM 포트를 생성할 것이다.

장치 관리자를 확인해보니 정확히 로드가 잘 된 것을 확인할 수 있다.

속성에서 하드웨어 ID를 확인해 보면 기존과 다르게 MI_02가 붙은 것을 확인할 수 있다. Multiple Interface의 약자로 해당 장치가 복합 장치일 때, 몇 번째 인터페이스인지를 나타내는 식별자라고 한다.
USB Device Viewer를 확인해 보면 정확히 볼 수 있다.

InterfaceNuber가 0x02이고 InterfaceClass가 0xFF로 vendor specific인 것을 확인할 수 있다. 동일하게 가상 COM 장치도 확인해보겠다.

역시 MI_00이 붙은 것을 확인할 수 있다. USB Device Viewer로 확인해보면 아래와 같다.

pico에서의 설정은 깃헙을 참조하면 된다.
printf를 호출했을 때 Serial로 받을 수 있으므로 테스트를 해봐야 한다.
import serial
import time
# 포트 설정 (COM27)
PORT = 'COM27'
BAUD = 115200
try:
# timeout을 설정해야 장치가 응답 없을 때 무한 대기에 빠지지 않습니다.
ser = serial.Serial(PORT, BAUD, timeout=1)
print(f"Connected to {PORT}")
while True:
if ser.in_waiting > 0: # 읽을 데이터가 있을 때만 처리
try:
# decode('utf-8', 'ignore')를 써야 이상한 바이트가 섞여도 안 죽습니다.
line = ser.readline().decode('utf-8', 'ignore').strip()
if line:
print(f"[LOG] {line}")
except Exception as e:
print(f"Read error: {e}")
time.sleep(0.01) # CPU 점유율 과다 방지
except serial.SerialException as e:
print(f"Could not open port {PORT}: {e}")
except KeyboardInterrupt:
print("\nStop logging.")
finally:
if 'ser' in locals() and ser.is_open:
ser.close()
PORT는 현재 가상 장치의 COM을 확인하고 입력해 주면 된다.

실행해 주면 정상적으로 COM27에 연결이 된 것을 확인할 수 있다.
test 영상입니다.
https://github.com/wangki-kyu/pico_usb_vendor
https://github.com/wangki-kyu/pico_driver
led 디버깅으로 답답한 부분을 해결할 수 있었다. 기존에 이미지 추론이 되지 않아서 너무 답답했는데 이유를 알 수가 없었다. 로깅으로 확인하니 이미지가 정상적으로 수신되고 있지 않았다.

cmd + image data를 붙여서 보냈는데 모든 수신에서 cmd를 체크하니 정상적인 로직을 태우지 못했던 것이다. 수정을 하여 정상으로 수신하도록 하였다.
cdc를 통한 디버깅을 할 수 있음으로 인해 개발 난이도가 확 낮아진 느낌이다. 나이스