- USB 기기를 처음 만들 때 필요한 흐름을 물리 배선부터 PCB, USB 2.0 속도, 프로토콜 계층, STM32 실습까지 한 번에 따라갈 수 있게 정리함
- USB 2.0의 기본 연결은 +5V, GND, D+, D- 네 선이며,
D+와 D-는 차동 쌍으로 함께 1비트를 전달함
- PCB에서는 차동 쌍의 길이 맞춤, 가까운 배치, 임피던스가 중요하지만, 12 Mbit/s full speed 프로토타입은 비교적 관대하게 동작할 수 있음
- 실습은 NUCLEO-F103RB에서
PA12를 USB_DP, PA11을 USB_DM으로 설정하고, CDC 가상 COM 포트로 인식되는 USB serial 기기를 만든 뒤 1 입력으로 LED를 켬
- STM32CubeIDE 방식은 UI 기반 코드 생성과 보일러플레이트 결합이 부담이며, Linux 기반 USB 기기 구현은 더 표준화된 API와 분리된 구조를 제공할 수 있음
USB를 처음 구현할 때의 범위
- 목표는 컴퓨터에 꽂으면 인식되는 USB 기기를 가장 단순한 형태로 끝까지 만들어 보는 것임
- 여기서 기기는 컴퓨터의 기능을 확장하는 주변 장치를 뜻하고, 컴퓨터는 host로 부름
- 범위는 USB 기기와 host 사이의 물리 연결부터 host에서 USB 기기와 상호작용하는 간단한 애플리케이션까지 포함함
- 권위 있는 명세 해설보다는 가장 단순한 E2E USB 기기 제작 프로젝트와 참고 자료 색인에 가까움
USB의 기본 개념
- USB는 여러 전자기기 사이에서 데이터 교환과 전원 공급을 가능하게 하는 산업 표준임
- USB는 직렬 버스라서 비트가 병렬이 아니라 하나씩 버스 위로 전달됨
- 현대 버스는 주로 직렬 방식이며, USB는 host와 device 사이에서 비트를 직렬로 교환하는 방식으로 이해할 수 있음
- USB는 두 장치를 연결하고 비트를 주고받는 물리 규격뿐 아니라, 데이터 전송과 전원 전달을 위한 통신 프로토콜도 포함함
- 설명의 초점은 USB 2.0에 맞춰짐
USB 배선과 차동 쌍
- 일반적인 USB 2.0 연결은 최소 네 선으로 설명됨
+5 V: host가 device에 전원을 공급하는 선
D-, D+: 두 선이 함께 동작해 1비트를 전달하는 차동 쌍
GND: 접지
- 일부 연결에는
ID 같은 추가 핀이 있을 수 있지만, 실습은 위 네 선만 다룸
-
USB-C에 대한 주의점
- USB-C도 내부적으로 차동 쌍을 사용하지만, 방향을 바꿔 꽂아도 동작해야 하는 점 등에서 일반 USB 2.0 설명과 차이가 있음
- USB-C 커넥터를 쓴다고 해서 속도나 USB 버전이 정해지는 것은 아님
- USB-C 기기라도 USB 2.0일 수 있고, 더 현대적인 USB 3.0 기기일 수도 있음
- 이후 실습에서는 USB-C를 더 다루지 않음
-
차동 쌍이 노이즈를 줄이는 방식
- 단일 선은 전압을
GND와 비교해 비트 값을 표현하지만, 긴 유선 연결에서는 노이즈 영향이 커질 수 있음
- 차동 쌍은 두 선을 사용하며, 한 선은
V, 다른 선은 -V를 전달함
- 수신 측은 두 선의 전압 차이를 봄
- 두 선에 같은 노이즈
Vn이 더해진다고 단순화하면, (V + Vn) - (-V + Vn) = 2V가 되어 노이즈가 상쇄됨
- 이 모델은 매우 단순화된 설명이며, 더 깊은 차동 쌍 이해를 위해 Zach Peterson의 Altium YouTube 영상을 참고할 수 있음
- Video 15
PCB에서 USB를 다룰 때
- 직접 하드웨어를 만들지 않고 개발 보드를 쓴다면 PCB 섹션은 건너뛸 수 있지만, USB 커넥터를 PCB에 올릴 때의 기본 원칙은 유용함
- USB 커넥터 라이브러리 부품에는 앞서 언급한 핀들이 있고, 차동 쌍은 마이크로컨트롤러나 SoC의 관련 인접 핀으로 라우팅해야 함
- 차동 쌍 라우팅의 기본 원칙은 세 가지임
D+에서 칩의 plus 핀까지 가는 트레이스와 다른 쪽 트레이스의 길이를 같게 맞춤
- 두 트레이스를 서로 매우 가깝게 배치함
- 신호에 맞는 임피던스를 고려함
- 두 선이 거의 동일한 환경을 지나가도록 만들기 때문에, 앞선 단순 모델에서 두 선의 노이즈가 같다고 가정할 수 있음
- 임피던스 계산은 제조사의 계산기에 목표 임피던스, 그라운드 플레인과의 거리, 차동 쌍 사이 거리 등을 입력해 필요한 트레이스 폭을 구하는 방식으로 처리할 수 있음
- Zach Peterson의 차동 쌍 및 USB 라우팅 영상이 참고 자료로 포함됨
USB 2.0의 속도
- USB 2.0이라고 해서 속도가 하나로 고정되지는 않음
- USB 2.0은 예를 들어 두 속도 수준을 가질 수 있음
- full speed: 12 Mbit/s
- high speed: 480 Mbit/s
- device와 host는 연결 시 어떤 속도를 사용할지 결정해야 함
- PCB 위에서 high speed를 안정적으로 달성하려면 임피던스를 맞추는 것이 중요함
- 기본 프로토타입에서는 12 Mbit/s full speed가 충분할 수 있고, USB 커넥터에서 칩까지의 트레이스가 짧다면 트레이스 폭 등에 비교적 관대할 수 있음
프로토콜과 소프트웨어 계층
- USB는 하드웨어뿐 아니라 host와 device 양쪽의 소프트웨어 계층도 함께 고려해야 함
- Linux 관점에서 USB를 설명하는 약 45분 영상이 추천 자료로 포함됨
- 이 영상은 USB 프레임, endpoint, configuration, 하나의 device가 여러 USB 기능을 수행할 수 있는 방식 등을 다룸
- USB를 기기들의 네트워크처럼 생각하는 관점이 중요함
- host 운영체제가 모든 USB 기기별 드라이버를 개별 구현하는 것은 비현실적이므로, 운영체제는 여러 USB device class를 인식함
- mass storage device
- serial device
- 그 외 여러 클래스
- 실습 기기는 host 관점에서 serial port 기기로 동작함
STM32로 USB serial port 기기 만들기
- 실습 기기는 host에서 요청을 받으면 LED를 켜는 단순한 USB 기기임
- host는 이 기기를 serial port device로 인식함
- 구현 방식은 크게 두 가지로 나뉨
- USB 지원 마이크로컨트롤러 사용
- Linux를 실행할 수 있는 SoC를 사용해 커널이 많은 작업을 처리하게 함
- 실습은 단순성을 위해 마이크로컨트롤러 방식을 사용함
-
NUCLEO-F103RB 보드
- 개발 보드는 NUCLEO-F103RB를 사용함
- 미국에서는 Digikey 같은 곳에서 10달러 조금 넘는 가격에 구매할 수 있음
- 이 보드는 두 부분이 붙어 있는 구조임
- 작은 쪽은 programmer 영역, 큰 쪽은 실제로 프로그래밍할 main MCU 영역임
- 보드의 기본 USB 연결은 main MCU가 아니라 programmer 쪽 MCU와 연결됨
- 이 programmer는 ST-LINK 프로토콜을 사용해 컴퓨터와 USB 메시지를 주고받고, 이후 main MCU를 프로그래밍함
- 직접 STM32 기반 PCB를 만든다면 보드에 칩 하나만 두고, USB로 프로그래밍한 뒤 같은 USB 포트를 기기 로직에 사용할 수도 있음
- STM32 기반 PCB와 USB 프로그래밍 참고 자료로 Phil’s Lab 영상이 포함됨
- Video 19
- Video 20
실제 USB 포트 배선
- Nucleo 보드의 기본 USB 연결은 main MCU에 연결된 것이 아니므로, main MCU의 USB 포트를 직접 host와 연결해야 함
- 프로그램을 올린 뒤에는 ST-LINK programmer를 컴퓨터에서 분리하고, main MCU를 실제 USB 포트로 전원 공급함
- 소프트웨어 작성에는 STM32CubeIDE, 보드에 올리는 작업에는 STM32CubeProgrammer를 사용함
-
필요한 핀과 설정
- USB 2.0 기기를 만들기 위해 필요한 핀은 네 가지임
- host에서 들어오는 5V 전원
GND
D+
D-
- NUCLEO-F103RB에서 main MCU 쪽 USB 포트로 전원을 받으려면
JP5 점퍼를 바꿔 보드가 external 5 V 공급을 받도록 설정해야 함
- STM32CubeIDE에서 다음처럼 핀을 설정함
PA12 → USB_DP
PA11 → USB_DM
- 이 보드의 칩은 USB 연결을 위해 외부 1.5 kΩ 풀업 저항이 필요함
- 저항은
PA12를 3.3V로 pull-up함
- breadboard로 이 회로를 구성함
- MacBook Pro와 개별 핀을 연결하기 위해 Amazon의 USB breakout cable을 사용했고, terminal block을 제거해 USB 핀을 노출시킨 뒤 jumper wire로 Nucleo 보드와 breadboard에 연결함
- 차동 쌍 길이 맞춤과 임피던스 제어를 엄격히 지키지 않아도, 이 실습에서는 12 Mbit/s 연결이 가능했음
USB CDC 소프트웨어 작성
- CubeIDE에서 USB 핀을 설정하면 clock 설정 관련 알림이 나오며, CubeIDE가 자동으로 처리하는 옵션을 선택할 수 있음
Pinout & Configuration의 Middleware and Software Packs 아래에서 USB_DEVICE를 설정함
- 실습에서 중요한 모드는 다음과 같음
Communication Device Class (Virtual Port Com)
- 이 설정으로 Nucleo 보드는 host 관점에서 CDC serial port 기기로 동작함
- host는 이 클래스 정보를 바탕으로 custom device와 통신할 적절한 드라이버를 설정할 수 있음
- CubeIDE는 C 코드를 생성하며,
main.c에는 다음 초기화 호출이 포함됨
MX_USB_DEVICE_Init();
- LED를 켜기 위해
CDC_Receive_FS 루틴에 다음 코드를 추가함
/* USER CODE BEGIN 6 */
if (Buf[0] == '1') {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, 1);
}
- 이 HAL 호출은 port A의 pin 5에 연결된 onboard LED를 켬
- 단계별 참고 영상은 다음과 같음
플래싱과 실행
- ELF 파일을 빌드한 뒤 CubeProgrammer로 보드에 코드를 올림
- 이후 programmer를 분리하고, 앞서 설명한 방식대로 external 5V 공급을 사용해 보드를 배선함
- 보드가 켜지면 운영체제의 device manager에서 COM port나 Serial port와 비슷한 항목으로 나타나야 함
- CubeIDE의
USB_DEVICE middleware 메뉴에서 Device Descriptor 값을 바꾸면 운영체제 device manager에 표시될 custom device name을 설정할 수 있음
- Mac OS에서는 새 기기가
/dev 파일 시스템 아래에 나타남
- 예시 경로:
/dev/tty.usbmodem497A0F6739561
- Linux에서는
/dev/ttyUSB0 같은 이름으로 보일 수 있음
- Minicom으로 serial device에 연결하는 예시는 다음과 같음
minicom --device /dev/tty.usbmodem497A0F6739561
- 연결 후 키보드에서
1을 입력하면 Nucleo 보드의 초록 LED가 켜짐
STM32 방식과 Linux 방식의 소프트웨어 차이
- 실습 결과는 mainstream 운영체제에서 인식되는 USB serial port device를 만드는 것임
- STM32CubeIDE 기반 방식에는 소프트웨어 엔지니어링 관점의 불편함이 있음
- UI 메뉴를 클릭해 많은 보일러플레이트를 생성해야 함
InitUsbDevice(UsbClass.CDC)처럼 코드에서 유연하게 파라미터화된 라이브러리를 쓰는 방식이 아님
- 생성 코드가 사용자 코드와 강하게 결합되어 코드 리뷰가 어려워질 수 있음
- 새 버전이 나왔을 때 보일러플레이트를 어떻게 갱신할지 명확하지 않음
- 현재 설정은 STM32 생태계에 강하게 묶임
- Linux가 USB device처럼 동작하는 방식은 더 깔끔한 접근으로 평가됨
- Linux API는 더 견고하고 표준화되어 있음
- pseudo-file 및 system call과의 상호작용을 기반으로 할 수 있음
- user space와 kernel space가 분리됨
- Linux를 HAL 계층처럼 볼 수 있음
- 다만 가볍고 저렴하며 생산하기 쉬운 USB 기기가 필요한 경우 Linux SoC는 무거울 수 있고, 많은 사용 사례에서는 과할 수 있음
- bare metal USB 기기 개발에는 더 portable하고 덜 opinionated한 프레임워크가 있으면 좋겠다는 결론으로 이어짐