클로드가 틀린 핀 알려줘서(핑계) 3시간 날린 썰.

현재 작성 중인 [왕초보 임베디드 공부 일기]중심의 이론 학습 말고, 임베디드 시스템을 이해하기 위해 실습 중심으로 또다른 학습을 진행 중이다. 이번 글은 실습 도중, AI의 잘못된 설명으로(나의 문해력 부족 탓도 있다.) 3시간 정도를 낭비한 좌충우돌 기록을 해결 과정 순서대로 작성해 보았다.

배경

STM32 Bare-Metal 프로그래밍을 공부하고 있다. HAL 라이브러리 없이 레지스터를 직접 제어하는 방식으로, 상반기 중에 데이터시트만 보고 GPIO, UART, I2C 드라이버를 직접 작성하는 것이 목표다. 이번 실습에서는 for 루프 기반 소프트웨어 딜레이와 4x4 키패드 스캔 알고리즘을 구현했다. 키패드는 4개 행(Row)과 4개 열(Column), 총 8개 핀으로 16개 버튼을 표현한다. 한 번에 한 행씩 LOW로 내리고 각 열 핀을 읽어 눌린 버튼을 감지하는 방식이다. ---

문제 1: 콘솔에 아무것도 출력되지 않음

코드 작성과 빌드는 문제없이 완료됐다. 그런데 디버그를 시작하고 키를 눌러도 ITM 콘솔에 아무것도 출력되지 않았다. 가장 먼저 ITM(콘솔에 자료형을 출력할 때 필요한 하드웨어 유닛) 설정을 의심했다. SWV Core Clock 값, Start Trace 버튼 활성화 여부, Port 0 체크 여부를 모두 확인했지만 설정에는 문제가 없었다. 중단점을 ITM_SendChar 내부에 걸어서 확인하니 printf에서 _write를 거쳐 ITM_SendChar까지는 정상적으로 도달하고 있었다. 즉 함수 호출 경로 자체는 맞았다. 원인은 syscalls.c_write 함수에 있었다. 이전 실습 프로젝트를 복사하는 과정에서 _write 함수가 ITM 버전이 아닌 기본 버전으로 교체되어 있었다.
// 기본 버전 — weak 속성의 빈 함수라 실제로 아무것도 출력하지 않음
__io_putchar(*ptr++);

// ITM 버전 — 이것으로 교체해야 함
ITM_SendChar(*ptr++);
교체 후 출력이 정상적으로 동작했다.
프로젝트를 복사할 때 syscalls.c_write 함수가 ITM 버전인지 반드시 확인해야 한다.
---

문제 2: 키패드 핀 순서가 예상과 달랐다

다음 문제는 하드웨어 연결 단계에서 발생했다. 멀티미터로 키패드 핀 작동을 확인하려 했는데 반응이 없었다. 이 키패드의 핀 배열이 일반적인 순서와 달랐다.
일반적인 배열: R1 R2 R3 R4 C1 C2 C3 C4 (행 먼저) 이 키패드: C4 C3 C2 C1 R1 R2 R3 R4 (열 먼저, 역순)
핀이 세로 방향 '수' 핀이라 멀티미터를 직접 대기도 쉽지 않았다. 브레드보드에서 키패드를 빼고 핀을 하나씩 직접 찍으며 구조를 확인했다. 연결 이후 키 배열 테이블(KEY_MAP)도 C4→C1 역순에 맞게 수정했다.
키패드는 제품마다 핀 순서가 다를 수 있다. 연결 전 데이터시트 또는 멀티미터로 핀 구조를 직접 확인해야 한다.
---

문제 3 (핵심): PB3이 SWO 핀이었다

배선을 완료하고 다시 테스트했지만 여전히 출력이 되지 않았다. 이번에는 boot completed라는 단순한 문자열조차 출력되지 않았다. GPIO_Init()printf 호출 뒤로 옮겼더니 "boot complet"까지만 출력되고 잘렸다. GPIO_Init() 내부의 무언가가 ITM 전송을 끊고 있다는 단서였다. GPIO 초기화 코드를 전부 주석 처리하니 printf가 정상 출력됐다. 이후 핀 설정 코드를 하나씩 주석 해제하며 범인을 좁혔다.
PB0 출력 설정 → 출력 정상 PB1 출력 설정 → 출력 정상 PB2 출력 설정 → 출력 정상 PB3 출력 설정 → 출력 즉시 끊김 PB3를 일반 GPIO 출력으로 설정하는 순간 ITM이 끊겼다.
원인은 PB3 = SWO(Serial Wire Output) 핀이었다. ITM은 SWO 핀을 통해 데이터를 PC로 전송하는데, PB3를 GPIO 출력 모드로 설정하면 SWO 기능이 즉시 비활성화된다. 보드 회로도에 명시되어 있는 내용이었는데, 내가 회로도를 읽는 기술이 아직 미숙해(회로도가 워낙 복잡하게 기술되어있기도 했다.) 클로드에게 대신 설명해달라고 맡겼더니 PB3가 SWO핀이라는 사실을 망각하고 핀 설정 가이드를 알려준 것이다. 이러한 상황을 겪은 직후 PB3와 비슷한 속성인 핀이 또 있는지 찾아보았다. STM32에서 디버그 전용으로 예약된 핀은 다음과 같다. | 핀 | 기능 | 충돌 대상 | |---|---|---| | PA13 | SWDIO | SWD 디버거 | | PA14 | SWCLK | SWD 디버거 | | PB3 | SWO | ITM (printf) | PB3 대신 PB8을 행 3번으로 변경하고 배선을 수정했다. 이후 출력이 정상적으로 동작했다.
GPIO 핀 배정 전에 보드 회로도에서 해당 핀의 기본 기능을 반드시 확인해야 한다. 특히 PB3(SWO)는 ITM과 직접 충돌하므로 일반 GPIO로 절대 사용하면 안 된다.
---

문제 4: printf 출력이 중간에 잘린다

PB3 문제를 해결했음에도 "boot completed""boot complet"까지만 출력되는 현상이 남아있었다. 원인은 ITM FIFO 타이밍이었다. ITM_SendChar가 문자를 FIFO에 쓴 직후 반환하는데, FIFO가 비워지기 전에 다음 문자가 전송되면 데이터가 유실된다. 기존 코드에는 전송 전 대기만 있었고, 전송 완료 후 대기가 없었다.
// 수정 전
while (!(ITM_STIMULUS_PORT0 & 1));  // 전송 전 대기
ITM_STIMULUS_PORT0 = ch;
// 바로 반환

// 수정 후
while (!(ITM_STIMULUS_PORT0 & 1));  // 전송 전 대기
ITM_STIMULUS_PORT0 = ch;
while (!(ITM_STIMULUS_PORT0 & 1));  // 전송 완료 대기 추가
전송 완료 대기를 추가한 후 문자 잘림 현상이 사라졌다. ---

최종 결과

위 네 가지 문제를 모두 해결한 후 정상 동작을 확인했다. boot completed key pressed: 1 key pressed: A key pressed: # ---

정리

이번 실습에서 시간이 가장 많이 소요된 부분은 PB3 SWO 충돌이었다. 핀 배정 단계에서 회로도를 확인했다면 발생하지 않았을 문제다. 디버깅 과정에서 유효했던 접근 방식은 문제를 작은 단위로 분리하는 것이었다. ITM 문제인지 GPIO 문제인지, GPIO 문제라면 어느 핀인지를 순서대로 좁혀가며 원인을 찾았다. 주석 처리로 범위를 좁혀가는 방식은 단순하지만 확실한 방법이었다. STM32처럼 디버그 전용 핀이 일반 GPIO 핀과 같은 포트에 섞여있는 환경에서는, 코드를 작성하기 전에 사용할 핀의 기본 기능을 회로도에서 반드시 확인하는 것을 습관으로 삼아야겠다. 이번 실습은 [왕초보 임베디드 공부 일기] 연재가 끝난 이후 만들어질 영상 컨텐츠로 사용될 예정이다. 이제는 기본적인 C언어 이론을 넘어, 실질적인 임베디드 환경을 실습을 통해 구석구석 살펴보는 과정을 영상 컨텐츠의 형태로 보다 생생하게 전달해보려고 준비 중이다.