Files
2026-08-22 19:14:55 +09:00

282 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 펌웨어 구현하면서 한 일 정리 (개인용 메모)
readme.md는 그대로 두고, 코드 작업하면서 실제로 뭘 했고 뭘 확인했는지를 여기 따로 적는다. 어려운 말 최소화.
---
## 1. 뭘 만들었나 (간단 설명)
보드에 LED 9개가 물려 있고, 각 LED는 "이번 판에는 몇 번 깜빡일지"가 정해져 있다. 예를 들어 1번 LED는 3번, 2번 LED는 5번, 이런 식으로 9개가 각자 다른 횟수만큼 깜빡인다.
동작은 이렇게 반복된다:
1. 8초짜리 한 판이 시작
2. 처음 6초 동안 9개가 각자 정해진 횟수만큼 깜빡임 (다 끝난 애는 먼저 꺼지고 기다림)
3. 나머지 2초는 전부 꺼짐 — 이 "다 같이 꺼진 구간"이 다음 판이 시작하는 기준점 역할을 함
4. 다음 판에서는 깜빡이는 횟수가 전부 1씩 밀림 (1번이 3번이었으면 다음 판엔 4번)
5. 이걸 계속 반복
그리고 판이 끝날 때마다 컴퓨터(시리얼 포트)로 "이번 판 정답은 1번=3번, 2번=5번, ..." 하고 로그를 찍어준다. 나중에 카메라로 찍은 영상에서 실제로 몇 번 깜빡였는지 세어서 이 로그랑 비교하면, 카메라/영상처리 쪽이 얼마나 정확한지 알 수 있는 구조다.
숫자를 랜덤이 아니라 "1씩 밀리는" 규칙으로 정한 이유: 무작위로 하면 어쩌다 맞았는지 진짜 맞은 건지 구분이 안 된다. 규칙적으로 밀리면 "특정 LED만 계속 틀리네" 같은 패턴이 바로 보인다.
---
## 2. 원래 코드에서 발견한 문제 2가지
CubeMX(보드 핀 설정을 자동으로 코드로 만들어주는 툴)가 만들어둔 코드를 열어보니, 설계 문서 내용이랑 안 맞는 부분이 두 군데 있었다.
### 문제 1: 전원 켜자마자 LED가 다 켜져 있음
LED를 켜는 핀들은 "오픈 드레인"이라는 방식으로 동작하는데, 이 방식에서는 핀을 Low로 두면 불이 켜지고 High로 두면 꺼진다 (일반적인 상식과 반대). 그런데 자동 생성된 코드는 시작할 때 이 핀들을 전부 Low로 초기화하고 있었다. 즉 리셋 버튼을 누르는 순간 9개 LED가 전부 켜진 채로 시작하는 상태였다.
→ 시작값을 High로 바꿔서, 전원 켜졌을 때 전부 꺼진 상태에서 시작하도록 고쳤다.
### 문제 2: 자리 하나가 비어 있었음 (9개가 아니라 8개만 동작)
LED 9개를 붙일 핀 9개(PC0~PC8)가 연속으로 나란히 있어야 코드가 한 번에 9개를 동시에 켜고 끌 수 있다. 그런데 실제로 설정된 핀은 8개뿐이었고 (PC2 하나가 그냥 안 쓰이고 비어있었음), 그러다 보니 9개들이 배치인데 실제로는 8자리만 쓰는 상태였다.
→ 비어있던 PC2를 9번째 자리로 채워서 9개가 전부 동작하도록 고쳤다. 기존에 이미 배정돼 있던 1~8번 자리는 그대로 두고, 마지막 9번째만 새로 채운 것이라 기존 배선을 바꿀 필요는 없다.
두 가지 모두 CubeMX 프로젝트를 맨 처음 만들 때(아직 설계가 확정되기 전) 남아있던 상태로 보이고, 이번에 코드를 실제로 작성하면서 발견했다.
---
## 3. 컴파일이 되는지 확인
먼저 컴퓨터에 있는 컴파일러로 빌드를 시도했는데 실패했다. 원인은 그 컴파일러(Homebrew로 설치된 것)에 표준 라이브러리 파일 일부가 빠져 있었기 때문. ST에서 따로 제공하는 컴파일러(STM32CubeCLT에 포함된 것)로 바꿔서 다시 빌드하니 문제없이 성공했다.
```
사용 메모리: RAM 2224B / 20KB (10.9%), FLASH 17232B / 192KB (8.8%)
```
여유가 충분해서 메모리 문제로 막힐 일은 없다.
---
## 4. 동작 검증 과정
**실제 보드(NUCLEO 보드 + LED + 카메라)는 아직 연결 안 해봤다.** 그래서 "진짜 불이 깜빡이는지, 카메라로 잘 보이는지"는 이번에 확인한 범위 밖이다 — 이건 readme.md 10장에 있는 순서대로 나중에 직접 해봐야 하는 부분.
대신, MCU에 올리기 전에 **로직 자체가 맞게 짜였는지**는 컴퓨터에서 미리 확인할 수 있다. 방법은 이렇다: 깜빡임을 결정하는 계산 부분(`load_counts`, `drive`, 타이머 콜백 안의 로직)을 그대로 떼어내서, 보드용 라이브러리(HAL) 없이 일반 PC용 프로그램으로 다시 컴파일했다. 그리고 "실제로 몇 번 깜빡였는지"를 프로그램이 스스로 세어서, 원래 의도한 횟수랑 맞는지 비교하게 만들었다.
파일 위치: [`doc/verify/sim_check.c`](verify/sim_check.c) — 순수 C코드라 아무 컴퓨터에서나 아래처럼 바로 돌려볼 수 있다.
```
gcc -Wall -Wextra -o sim_check doc/verify/sim_check.c
./sim_check
```
### 확인한 것 4가지
**① 한 판이 정확히 8초(40틱)인지**
→ 40틱 정확히 맞음.
**② 216판(약 8시간 분량)을 계속 돌려서, "이번 판 목표 횟수"와 "실제로 깜빡인 횟수"가 매번 정확히 같은지**
→ 216판 전부 정확히 일치. 단 한 번도 어긋나지 않음.
```
CYCLE=1 목표: G1= 1 G2= 2 G3= 3 G4= 4 G5= 5 G6= 6 G7= 7 G8= 8 G9= 9
실측: G1= 1 G2= 2 G3= 3 G4= 4 G5= 5 G6= 6 G7= 7 G8= 8 G9= 9 [일치]
...
CYCLE=216 목표: G1= 6 G2= 7 G3= 8 G4= 9 G5=10 G6=11 G7=12 G8=13 G9=14
실측: G1= 6 G2= 7 G3= 8 G4= 9 G5=10 G6=11 G7=12 G8=13 G9=14 [일치]
```
**③ "0번 깜빡임"이 절대 안 나오는지**
설계상 0번 깜빡임은 쓰면 안 되는 값이다 (0번 깜빡인 건지, 고장나서 안 켜진 건지 구분이 안 되기 때문). 200판 넘게 돌려봐도 0이 나온 적이 없는지 확인함.
→ 한 번도 안 나옴. 정상.
**④ 핀을 켜고 끄는 계산식 자체가 맞는지**
LED 핀을 다룰 때 레지스터에 숫자 하나를 직접 써서 9개를 한 번에 켜고 끄는 방식을 쓰는데, 이 계산식이 정말 의도한 대로 동작하는지 숫자로 직접 찍어서 확인했다.
```
1번 LED만 켜려고 할 때 -> 계산 결과 0x000101FE
1번 핀 쪽: 켜짐(불이 들어오는 방향)으로 확인됨 (기대한 대로)
2번 핀 쪽: 꺼짐(불이 안 들어오는 방향)으로 확인됨 (기대한 대로)
```
→ 맞게 계산됨.
### 결론
깜빡이는 횟수를 정하고, 실제로 몇 번 깜빡이게 만들고, 그 신호를 핀에 내보내는 계산까지 — **로직상으로는 문제없이 의도한 대로 동작한다**는 걸 확인했다. 다만 이건 "코드 안의 계산이 맞다"는 확인이지 "실제 LED가 카메라에 잘 잡히는지"까지 확인한 건 아니다. 그건 실물을 연결해봐야 안다.
---
## 5. 아직 안 해본 것 (다음 할 일)
- **배선 자체를 아직 안 함 — 아래 6장 보고 먼저 연결할 것**
- 실제 보드에 펌웨어 올려서 LED가 진짜 깜빡이는지 눈으로 확인
- 시리얼 로그가 8초마다 한 줄씩 잘 찍히는지 확인
- 950nm 필터 낀 카메라로 9개가 겹치지 않고 잘 분리돼서 찍히는지 확인
- 노출 시간 등 카메라 설정 튜닝
→ 순서는 readme.md의 "10. 브링업 절차"에 이미 정리돼 있어서 그대로 따라가면 됨.
---
## 6. 배선 연결도 (현재 미배선 — 이대로 연결하면 됨)
### 준비물
- LD274 (950nm IR LED) × 9
- 330Ω 저항 × 9 (연결하고 밝기가 너무 약하면 220Ω으로 교체)
- 점퍼선 여러 개, 브레드보드 또는 만능기판
- NUCLEO-L073RZ 보드
### 한 채널 기본 회로 (이걸 9번 반복)
LED는 극성이 있다 — 방향을 반대로 꽂으면 안 켜진다.
- **긴 다리 = +(애노드)**
- **짧은 다리 = -(캐소드)**, 다리를 이미 잘랐다면 LED 몸통 테두리에 살짝 평평하게 깎인 부분이 있는 쪽이 캐소드
```
NUCLEO 보드의 3V3 핀
│ (9개 LED가 전부 여기 한 곳에 같이 연결됨 — 공통)
LED 애노드(+, 긴 다리)
LED 캐소드(-, 짧은 다리)
330Ω 저항
MCU 핀 (아래 표에서 해당 LED에 배정된 핀)
```
전류가 3V3 → LED → 저항 → MCU 핀 순서로 흐르고 MCU 안에서 빠지는 구조라 별도로 GND를 연결할 필요는 없다 (보드가 USB로 컴퓨터에 물려 있으면 그걸로 충분).
### 어떤 LED를 어떤 핀에 연결할지 (9개 전부)
| LED 번호 | 연결할 MCU 핀 | 저항 |
|---|---|---|
| G1 | PC0 | 330Ω |
| G2 | PC1 | 330Ω |
| G3 | PC3 | 330Ω |
| G4 | PC4 | 330Ω |
| G5 | PC5 | 330Ω |
| G6 | PC6 | 330Ω |
| G7 | PC7 | 330Ω |
| G8 | PC8 | 330Ω |
| G9 | PC2 | 330Ω |
핀 이름(PC0, PC1 ...)은 NUCLEO 보드의 커넥터(모포 헤더, CN10 쪽)에 실크스크린으로 그대로 인쇄되어 있어서 이름으로 찾으면 된다. 3V3 핀도 마찬가지로 보드에 "3V3"라고 인쇄돼 있는 핀을 쓰면 된다.
> G9가 PC2인 게 좀 이상해 보일 수 있는데, G1~G8을 먼저 PC0~PC1, PC3~PC8에 배정해놓은 상태에서 마지막으로 남는 자리가 PC2였기 때문이다 (자세한 사연은 readme.md 6.7절). 표 그대로 연결하면 문제없다.
### 카메라 쪽에서 봤을 때 실제 배치 (3×3 격자, 간격 10mm)
패널에 LED를 심을 때 이 배치를 따른다 — 카메라가 찍는 화면 기준으로 자리를 잡은 것이다.
```
좌 중 우
┌──────────┬──────────┬──────────┐
상단 │ G1 │ G2 │ G3 │
│ (PC0) │ (PC1) │ (PC3) │
├──────────┼──────────┼──────────┤
중단 │ G4 │ G5 │ G6 │
│ (PC4) │ (PC5) │ (PC6) │
├──────────┼──────────┼──────────┤
하단 │ G7 │ G8 │ G9 │
│ (PC7) │ (PC8) │ (PC2) │
└──────────┴──────────┴──────────┘
LED 중심 간 간격 10mm, LED 몸통 지름 5.1mm
```
### 연결 순서 (체크리스트)
1. [ ] LED 9개 + 저항 9개 극성/자리 맞춰서 위 표대로 하나씩 연결
2. [ ] LED 애노드(+) 9개를 전부 한 군데로 모아서 3V3 핀 하나에 연결
3. [ ] 저항 반대쪽 끝을 표에 있는 MCU 핀에 각각 연결
4. [ ] 전부 연결했으면 다시 한번 표랑 대조 — 특히 G9가 PC2로 갔는지 확인
5. [ ] readme.md 10장 "브링업 절차"대로, 처음엔 LED 1개(G1/PC0)만 켜보고 정상이면 나머지 연결
**주의할 것:**
- LED 극성 반대로 끼우면 안 켜짐 (고장 아니니 방향만 바꿔서 다시 확인)
- 저항 없이 LED를 직접 핀에 연결하면 과전류로 LED나 핀이 망가질 수 있음 — 저항 절대 빼먹지 말 것
- 9개 중 하나라도 다른 핀에 잘못 꽂으면, 코드 로그(G1=.. G2=..)랑 실제 불빛 위치가 안 맞게 되니 표 대조를 꼼꼼히
### 정답 로그(시리얼)는 배선 필요 없음
LED와 달리 UART 로그(USART2, PA2/PA3)는 **추가로 연결할 게 없다.** NUCLEO 보드 안에서 PA2/PA3가 보드에 내장된 ST-LINK로 이미 연결돼 있어서, 지금 펌웨어 업로드용으로 꽂는 USB 케이블 하나가 곧 가상 COM 포트(VCP) 역할도 한다.
- PC에서 터미널 프로그램(PuTTY, screen, CoolTerm, VS Code 시리얼 모니터 등) 실행
- 보드가 잡힌 COM 포트를 **115200 8N1**로 열기
- 8초마다 `CYCLE=.. G1=.. G2=.. ... G9=..` 한 줄씩 찍히면 정상
### CoolTerm에 로그가 안 뜬 문제 — 해결됨 (원인은 보드 배선, 펌웨어는 정상이었음)
**→ SB13, SB14 브리지하고 나서 CoolTerm에 로그 정상적으로 뜨는 거 확인함 (2026-08-22).**
2026-08-22, CoolTerm으로 열었는데 아무것도 안 뜬다는 얘기가 나와서 ST-LINK로 보드에 직접 붙어 확인했다. 순서대로:
1. 최신 펌웨어를 다시 플래시하고 리셋 → CoolTerm 대신 터미널에서 시리얼 포트를 직접 열어서 20초 넘게 지켜봐도 **0바이트**. CoolTerm 설정 문제가 아니라 보드에서 아무것도 안 나오고 있다는 뜻.
2. ST-LINK로 MCU에 붙어서 **보드가 정지 없이 실제로 돌아가는 상태 그대로(Hot Plug 모드)** 레지스터를 직접 읽어봄:
- GPIOA/C/H 클럭 켜짐, TIM6·USART2 클럭 켜짐 — 전부 코드에서 의도한 그대로
- PA2/PA3가 USART2용 핀 모드(AF4)로 정확히 설정돼 있음
- PC0~PC8(G1~G9, PC2 포함) 전부 출력모드로 정확히 설정돼 있음 — 6.7절에서 고친 내용이 실제로 반영돼서 동작 중
- USART2 레지스터 확인 결과 **송신 기능이 켜져서 실제로 활성 상태**(TEACK=1), 보레이트 값도 계산했던 값(139)과 정확히 일치
- RAM에 있는 카운트 값도 우리가 만든 공식과 한 자리도 안 틀리고 일치
- 반대로 USART2 수신 쪽에는 "깨진 데이터를 받았다"는 에러 플래그가 떠 있었음 — 그 핀이 뭔가에 연결 안 된 채 붕 떠서 잡음을 줍고 있다는 신호
**MCU 내부에서는 데이터를 제대로 내보내고 있는데, 그게 컴퓨터까지 한 바이트도 안 온다.** 펌웨어/설정 쪽은 이걸로 결백이 증명된 셈이고, 남은 용의선상은 **PA2/PA3와 ST-LINK를 이어주는 보드 위의 연결(솔더 브리지)이 끊겨 있는 경우**뿐이다.
**정확한 솔더 브리지 번호 (ST 공식 UM1724 매뉴얼, "5.7 USART communication" / "Table 9. Solder bridges" 확인함):**
NUCLEO-64 보드(L073RZ 포함)는 PA2/PA3를 어디로 보낼지 솔더 브리지 4개로 정한다.
| 브리지 | 정상(VCP 사용) 상태 | 역할 |
|---|---|---|
| **SB13, SB14** (ST-LINK-USART) | **ON (이어져 있어야 함)** | PA2/PA3 ↔ ST-LINK MCU의 USART를 연결 — 이게 켜져 있어야 시리얼 로그가 PC로 나옴 |
| **SB62, SB63** (USART) | **OFF (끊겨 있어야 함)** | PA2/PA3를 대신 Arduino 커넥터(CN9의 D1/D0)나 Morpho 커넥터(CN10) 쪽으로 돌림. 이게 켜져 있으면 ST-LINK 쪽 연결이 끊긴다 |
**SB13·SB14가 열려있거나(OFF), SB62·SB63가 닫혀있으면(ON)** 지금 겪는 증상(펌웨어는 정상인데 로그가 안 뜸)과 정확히 일치한다.
**확인 방법:**
1. 보드에서 SB13, SB14, SB62, SB63 실크스크린 표시를 찾는다 (ST-LINK 쪽 회로와 CN9/CN10 커넥터 사이 어딘가에 작은 두 패드로 표시돼 있음)
2. 멀티미터 연속성(continuity) 모드로 SB13, SB14 각각의 두 패드를 찍어본다 → **삑 소리(연결됨)가 나야 정상.** 안 나면 납땜으로 이어줘야 함
3. SB62, SB63도 같은 방식으로 찍어본다 → **삑 소리가 안 나야(끊겨 있어야) 정상.** 소리가 나면 커터로 끊어야 함
4. 넷 다 정상 상태로 맞춘 뒤 다시 CoolTerm으로 확인
**급하면:** 별도 USB-TTL 시리얼 어댑터를 PA2(TX)/PA3(RX)/GND에 직접 물려서 ST-LINK VCP를 아예 안 거치고 확인. 아니면 다른 NUCLEO-L073RZ 보드로 교체 — 펌웨어는 이미 검증됐으니 보드만 바꾸면 바로 될 가능성이 큼.
출처: [UM1724 User manual - STM32 Nucleo-64 boards (MB1136)](https://www.st.com/resource/en/user_manual/um1724-stm32-nucleo64-boards-mb1136-stmicroelectronics.pdf), §5.7 USART communication / Table 9 Solder bridges (DocID025833 Rev 8)
이걸로 카메라 쪽 판독 결과랑 대조하면 된다. 안 찍히면 보드레이트(115200)부터 다시 확인.
---
## 7. LED가 잘 안 보임 — 저항/노출 얼마까지 밀어붙여도 되나
2026-08-22, 노출 20ms + 330Ω→220Ω로 바꿨는데도 LED가 협각(±10°) 때문에 잘 안 보인다는 얘기가 나와서, STM32L073 공식 데이터시트(DS10685)로 실제 한계를 확인했다.
### 저항 — 220Ω에서 더 낮추지 말 것
데이터시트 Table 24(절대최대정격) 기준:
| 항목 | 절대최대값 |
|---|---|
| 핀 1개가 sink하는 전류 | 16 mA |
| **9개 핀 합계로 sink하는 전류** | **90 mA** |
Table 61(출력전압특성)을 보면 15mA 지점에서 V_OL이 **최대 1.3V**까지 올라간다(8mA 기준으로 잡았던 0.4V가 아님). 즉 저항을 낮춰도 핀 자체의 전압강하가 커져서 기대만큼 전류가 안 오를 수 있다.
**더 중요한 건 합계 90mA 쪽이다.** 사이클 시작 직후엔 설계상 9개가 동시에 켜지는 순간이 항상 있는데, 지금 220Ω(약 8mA×9≈72mA)만 해도 이미 90mA 한도에 꽤 붙어 있다. 여기서 더 낮추면 그 동시 점등 순간에 절대정격을 넘길 수 있다.
**220Ω보다 낮추지 말 것.** 180Ω 정도가 실질적 하한선.
### 노출 — 60ms 근처까지는 늘려도 됨
지금 20ms는 30fps 프레임 주기(33ms) 안에 들어가 안전. 5.4절에서 이미 "카메라가 15fps로 떨어져도 상태당 최소 3프레임 확보"를 설계 마진으로 잡아뒀으므로, **15fps로 떨어지는 걸 감수하면 노출을 60ms 근처까지 늘려도 이 마진 안에 있음.** 그 이상(10fps대)은 5.4절 표에서 이미 "위험"으로 표시해둔 구간이라 피할 것.
전류보다 노출을 늘리는 쪽이 안전하고 효과도 큼 — 3.4절에서 이미 권장했던 방향과 같음.
### 그래도 안 보이면: 협각 자체가 원인일 가능성
±10° 협각은 전류·노출로 완전히 못 고친다. 카메라가 LED 패널 정면 중앙에 잘 정렬돼 있는지(각 LED가 카메라를 정확히 향하고 있는지) 먼저 확인. 정렬이 어긋나 있으면 아무리 밝게/오래 노출해도 카메라가 빔 가장자리(광량 30~50% 이하 구간)만 보게 되므로 근본 해결이 안 됨.
출처: [Datasheet - STM32L073x8 STM32L073xB STM32L073xZ (DS10685)](https://www.st.com/resource/en/datasheet/stm32l073v8.pdf), §6.2 Table 24 Current characteristics / §6.3.13 Table 61 Output voltage characteristics