Initial commit: IR test firmware for STM32L073XX

This commit is contained in:
Dongubak
2026-08-22 14:49:29 +09:00
commit f75318d889
113 changed files with 112190 additions and 0 deletions
+386
View File
@@ -0,0 +1,386 @@
# IR 광신호 Mock 프로토타입 설계 정리
**대상 과제:** IR 광신호 기반 무선 토양수분 센서 (농촌진흥청 국립원예특작과학원)
**본 문서 범위:** 센서 미개발 상태에서 온도·습도 mock 신호를 IR LED로 송출하고 카메라로 인식하는 벤치 프로토타입
**작성 기준일:** 2026-08-22
---
## 1. 프로토타입 목표
각 식물 팟에 온·습도 센서가 내장되고, 그 값을 IR LED 광신호로 변환해 외부 카메라가 판독하는 시스템을 검증한다. 실제 센서가 아직 없으므로 **MCU가 결정적 패턴의 mock 값을 생성**하여 송출하고, 카메라 측 디코더가 이를 정확히 복원하는지 확인하는 것이 이번 단계의 목적이다.
배터리 수명, 방수, 양산성은 **본 단계에서 고려하지 않는다.**
---
## 2. 확정된 설계 결정
| 항목 | 결정 | 근거 |
|---|---|---|
| MCU 보드 | NUCLEO-L073RZ (STM32L073RZ) | 기 확보 |
| LED | Siemens LD274 (950nm) × 9 | 기 확보, 추후 변경 예정 |
| 카메라 필터 | 950nm, C 마운트 | LD274 피크 파장과 정합 |
| 셔터 방식 | 글로벌 셔터 | 확인 완료 |
| 인코딩 | 공간 병렬 (3×3 동시) | 벤치 환경이라 분리 해상 가능 |
| 구동 방식 | GPIO 오픈 드레인 직결 | 근거리라 저전류로 충분 |
| 테스트 거리 | 1 ~ 1.5 m | 블롭 분리 여유 확보 |
### 2.1 기본 카메라 필터 제거 건
카메라에 기본 장착돼 있던 필터(IR-cut 추정)를 제거하고, 별도 950nm 필터를 C 마운트로 장착했다. 제거한 기본 필터는 이후 설계와 무관하다.
---
## 3. LED 특성 (LD274) 및 그 함의
### 3.1 데이터시트 주요 값
| 항목 | 값 |
|---|---|
| 피크 파장 λ_peak | 950 nm |
| 스펙트럼 폭 (50%) | 55 nm |
| **반각 φ** | **±10°** |
| 방사강도 I_e (I_F = 100 mA) | 최소 50 mW/sr |
| 순방향 전압 V_F (I_F = 100 mA) | 1.30 V (max 1.5 V) |
| 최대 연속 순방향 전류 | 100 mA |
| 패키지 | 5 mm (T-1 3/4), 지름 5.1 mm |
| 스위칭 시간 t_r, t_f | 1 µs |
### 3.2 파장 정합 — 양호
950nm 필터와 LD274의 950nm 피크가 일치한다. 스펙트럼 폭 55nm를 감안해도 에너지가 대략 920~990nm에 몰려 있어 필터 통과에 문제가 없다.
### 3.3 반각 ±10° — 최대 리스크 요인
LD274는 TV 리모컨용으로 설계된 협각 부품이다. 방사 특성상 ±10°에서 50%로 떨어지고 약 15° 이후 급락한다.
거리별 50% 빔 지름:
| 거리 | 빔 지름 |
|---|---|
| 1 m | 0.35 m |
| 3 m | 1.06 m |
| 10 m | 3.5 m |
**문제의 본질은 거리가 아니라 조준이다.** 팟이 흩어져 있고 카메라가 한쪽에 있으면 팟마다 LED가 향해야 할 방향이 전부 다르다. LED를 모두 수직으로 세우면 카메라 바로 아래 팟만 인식된다.
벤치 테스트(카메라를 LED 정면에 배치)에서는 문제가 되지 않으나, **양산 부품 선정 시 반각을 1순위 기준으로 삼아야 한다.** 파장과 전류는 나중에 맞출 수 있지만 반각은 고칠 수 없다. 권장: ±20~25° 급 광각 부품.
### 3.4 펄스 구동은 채택하지 않음
서지 3A, 펄스 내량상 D=0.05에서 1A까지 가능하나, 방사강도 그래프상 1A에서 광량은 100mA 대비 약 7배에 그친다(전류는 10배). 효율이 떨어진다.
카메라는 노출 시간 동안 에너지를 적분하므로, 같은 평균 전류라면 DC 구동이 유리하다. 펄스가 유리한 경우는 카메라 노출 창에 동기하여 구동할 때뿐인데, 프리러닝 카메라에서는 동기를 잡을 수 없다.
**대안: 카메라 노출 시간을 늘린다.** 33ms 프레임에서 노출을 1ms → 20ms로 늘리면 LED 에너지를 20배 적분한다. 950nm 필터가 주변광을 이미 억제하고 있다.
---
## 4. 하드웨어 구성
### 4.1 구동 전류 및 저항 산출
벤치 1m 환경에서는 오히려 **과다 광량이 문제**가 된다. 포화된 블롭이 번지면(blooming) 인접 LED와 뭉쳐 공간 병렬 인코딩이 깨진다.
```
목표 전류: 6 mA
V_F ≈ 1.1 V (순방향 특성 그래프의 저전류 영역 판독값)
V_OL ≈ 0.4 V (STM32L0 오픈 드레인 Low 출력)
R = (3.3 - 1.1 - 0.4) / 0.006 ≈ 300 Ω → 330 Ω (E24)
```
- 9개 합계: 약 54 mA
- 밝기 부족 시 220 Ω으로 하향 (합계 약 72 mA)
거리별 조도 추정 (8 mA 기준, I_e ≈ 4 mW/sr):
| 거리 | 조도 | 판단 |
|---|---|---|
| 1 m | 4 mW/m² | 20ms 노출로 충분히 포화 |
| 3 m | 0.44 mW/m² | 가능, 노출 조정 필요 |
| 10 m | 0.04 mW/m² | 외부 드라이버 필요 |
**결론: 벤치 범위에서는 MOSFET·ULN2803A 등 외부 드라이버가 불필요하다.**
### 4.2 GPIO 전류 한계
STM32L073 GPIO는 정규 스펙에서 핀당 ±8 mA, 비정규 V_OL/V_OH를 감수하면 ±15 mA까지 sink/source 가능하다. 6 mA 구동은 정규 스펙 내에 안전하게 들어간다.
전체 I/O 합산 전류(ΣI_VSS) 한계는 데이터시트 절대최대정격 표에서 확인할 것.
### 4.3 전원 경로
LED 전류를 Nucleo의 3.3V 핀에서 뽑는 것은 무방하나(54mA 수준), 향후 전류를 크게 올릴 경우 주의가 필요하다.
- ST-LINK USB 급전 시 열거 전에는 100 mA 제한
- 온보드 LDO를 MCU와 공유
전류를 20mA/개 이상으로 올릴 경우 **5V 핀 또는 별도 전원에서 공급하고 GND만 공유**한다. 이때는 반드시 MOSFET 또는 ULN2803A를 경유해야 한다 — **PC0~PC5는 ADC 겸용 핀이라 5V 톨러런트가 아니다.**
### 4.4 LED 물리 배치
**3×3 배열, 피치 10 mm**
LD274 패키지 지름이 5.1 mm이므로 몸통 간 5 mm 간격이 확보된다.
1 m 거리, FHD, 6 mm 렌즈 기준 약 2 px/mm:
| 항목 | 픽셀 |
|---|---|
| LED 중심 간 거리 (10 mm) | 약 21 px |
| 렌즈 돔 블롭 (5 mm) | 약 10 px |
| 블롭 사이 어두운 골 | 약 11 px |
3 m로 물러나면 분리 7 px, 블롭 3.4 px로 제안서의 "3×3 픽셀 이상 투영" 조건을 겨우 만족한다. **벤치는 1~1.5 m 권장.**
### 4.5 회로 요약
```
VDD 3.3V ──┬── LED(애노드) ... LED(캐소드) ── 330Ω ── PC0 (오픈 드레인)
├── LED ── 330Ω ── PC1
├── ...
└── LED ── 330Ω ── PC8
오픈 드레인: 핀 Low = 전류 싱크 = 점등 / 핀 High = Hi-Z = 소등
```
---
## 5. 프로토콜 설계
### 5.1 비트 배치
| 위치 | 핀 | 역할 |
|---|---|---|
| 중앙 | PC0 | **토글 클록** |
| 상단 좌 | PC1 | 온도 b3 (MSB) |
| 상단 중 | PC2 | 온도 b2 |
| 상단 우 | PC3 | 온도 b1 |
| 중단 좌 | PC4 | 온도 b0 (LSB) |
| 중단 우 | PC5 | 습도 b3 (MSB) |
| 하단 좌 | PC6 | 습도 b2 |
| 하단 중 | PC7 | 습도 b1 |
| 하단 우 | PC8 | 습도 b0 (LSB) |
온도 4비트(16단계) + 습도 4비트(16단계) + 클록 1비트 = 9비트.
### 5.2 PC0의 역할: 토글 클록
글로벌 셔터가 확정되면서 롤링 셔터 대비용 동기 LED는 불필요해졌다. PC0을 **심볼마다 반전하는 토글 클록**으로 재배정한다.
검토했던 대안과 비교:
| 후보 | 얻는 것 | 잃는 것 |
|---|---|---|
| **토글 클록 (채택)** | 심볼 경계 검출, 누락 심볼 감지, 링크 생존 확인 | 고정 밝기 기준 없음 |
| 상시 점등 기준 | 자동 임계값 산출, ROI 앵커 | 경계·누락 미검출 |
| 패리티 | 단일 비트 오류 검출 | 위 둘 다 불가 |
채택 이유: 다수결 디코딩과 궁합이 좋다. 클록 전환 시점이 곧 심볼 경계이므로, 디코더는 전환 사이의 프레임만 모아 투표하면 되고 경계 프레임을 따로 판정할 필요가 없다.
밝기 임계값은 9개 중 켜진 LED들의 밝기 분포에서 얻을 수 있어 별도 기준 LED가 없어도 무방하다.
### 5.3 심볼 타이밍
**심볼 주기 200 ms (30fps 기준 6프레임)**
MCU와 카메라는 서로 독립적으로 동작한다. 노출 창 한가운데서 심볼이 바뀌면 전환된 LED들이 중간 밝기로 찍힌다. 글로벌 셔터라 9개가 균일하게 이상해질 뿐, 값 자체는 무효다.
```
심볼 N (200ms) │ 심볼 N+1 (200ms)
[F][F][F][F][F] [X] │ [F][F][F][F][F]
경계 걸침 프레임 1개만 폐기
```
최악의 경우에도 **6프레임 중 5프레임이 유효**하므로 다수결로 뽑으면 오류율이 사실상 0이 된다. 요구 갱신 주기가 시간당 1회임을 감안하면 초당 5심볼은 과도한 속도이며, 남는 속도를 전부 신뢰성으로 환산하는 설계다.
---
## 6. CubeMX 설정
### 6.1 클록
| 항목 | 값 |
|---|---|
| RCC | HSI16, PLL 미사용 |
| SYSCLK | 16 MHz |
프로토타입이므로 LPTIM·RTC·저전력 모드는 설정하지 않는다.
### 6.2 GPIO (PC0 ~ PC8)
| 항목 | 설정값 | 이유 |
|---|---|---|
| GPIO mode | Output Open Drain | 요구 사양 |
| Pull-up/Pull-down | No pull-up and no pull-down | 풀업은 외부 LED+저항이 담당. 내부 풀업 시 소등 상태에서도 누설 |
| Maximum output speed | Low | 저속 신호에 고속 슬루 불필요, EMI·소비전류 감소 |
| **GPIO output level** | **High** | OD에서 High = Hi-Z = 소등. Low로 두면 부팅 즉시 9개 전부 점등 |
**User Label:**
```
PC0 → CLK
PC1 → T3 PC2 → T2 PC3 → T1 PC4 → T0
PC5 → H3 PC6 → H2 PC7 → H1 PC8 → H0
```
`GPIO output level`을 High로 두는 것이 가장 실수하기 쉬운 지점이다. 푸시풀 습관대로 Low를 넣으면 리셋 직후부터 LED가 켜진 채로 있게 된다.
### 6.3 PC0~PC8 선택 이유
- **단일 포트 연속 배치** → `BSRR` 한 번의 쓰기로 9개를 완전히 동시에 전환 가능
- 회피 대상 핀과 충돌 없음: PA13/PA14(SWD), PA2/PA3(ST-LINK VCP), PC13(B1 버튼), PC14/PC15(LSE), PA5(LD2)
- NUCLEO-64 모포 헤더 실제 핀맵은 보드 문서에서 확인할 것
### 6.4 타이머
| 항목 | 값 |
|---|---|
| 타이머 | TIM6 |
| Prescaler | 15999 |
| Counter Period (ARR) | 199 |
| 결과 주기 | 200 ms |
| NVIC | TIM6 global interrupt 활성화 |
TIM6를 선택한 이유는 기본 타이머(Basic Timer)라 GPIO 대체 기능을 전혀 점유하지 않기 때문이다. **PC6~PC8은 TIM3 채널과 겹치므로 TIM3는 사용하지 않는다.**
### 6.5 UART
| 항목 | 값 |
|---|---|
| USART2 | Asynchronous, 115200 8N1 |
| 용도 | 송출값 로그 (ST-LINK VCP 경유) |
**이 로그가 핵심이다.** MCU가 "지금 온도 N, 습도 M을 송출했다"를 시리얼로 찍어주면 카메라 디코더 결과와 대조하여 에러율을 정량화할 수 있다. 이것이 없으면 디코더 오류인지 광학계 오류인지 구분할 수 없다.
---
## 7. 펌웨어
```c
#define LED_MASK 0x01FFu
#define BIT_CLK 0x0001u /* PC0 */
static uint8_t clk_state = 0;
static uint8_t mock_t = 0, mock_h = 0;
static void emit(uint8_t t4, uint8_t h4, uint8_t clk)
{
uint16_t bits = 0;
if (clk) bits |= BIT_CLK;
bits |= (uint16_t)(t4 & 0x0F) << 1; /* PC1..PC4 */
bits |= (uint16_t)(h4 & 0x0F) << 5; /* PC5..PC8 */
/* 오픈 드레인: 0 = 점등이므로 반전. 9핀 원자적 동시 전환 */
GPIOC->BSRR = ((uint32_t)(bits & LED_MASK) << 16)
| ((uint32_t)(~bits & LED_MASK));
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance != TIM6) return;
clk_state ^= 1;
emit(mock_t, mock_h, clk_state);
printf("T=%2u H=%2u CLK=%u\r\n", mock_t, mock_h, clk_state);
/* 결정적 스윕: 온도는 매 심볼, 습도는 16심볼마다 */
mock_t = (mock_t + 1) & 0x0F;
if (mock_t == 0) mock_h = (mock_h + 1) & 0x0F;
}
```
### 7.1 BSRR 직접 사용
`HAL_GPIO_WritePin()`을 9번 호출하면 핀마다 수십 ns씩 어긋나고 함수 호출 오버헤드가 붙는다. BSRR 직접 쓰기는 원자적 1사이클 연산이다.
### 7.2 Mock 값은 난수가 아닌 결정적 스윕
온도가 매 심볼 1씩 증가하므로, 디코더가 `0, 1, 2, 4, 5`를 출력하면 **3을 놓쳤다는 사실이 즉시 드러난다.** 난수 mock으로는 알 수 없는 정보다.
- 전체 주기: 온도 16 × 습도 16 = 256 심볼 = **51.2초**
- 1분 녹화로 전 조합 커버리지 확보
---
## 8. 카메라 설정 — 반드시 수동 고정할 항목
여기서 실패하는 경우가 많다.
| 항목 | 조치 | 이유 |
|---|---|---|
| 자동 노출 (AE) | **끄기** | LED 점멸마다 노출이 요동쳐 비트 패턴에 따라 밝기가 달라지고 임계값이 무너짐 |
| 자동 게인 (AGC) | **끄기** | 동일 |
| 자동 화이트밸런스 | **끄기** | 950nm 단색이라 무의미하고 방해만 됨 |
| 프레임레이트 자동 조정 | **끄기** | 어두우면 자동으로 15fps로 떨어지는 카메라가 많음 |
| 노출 시간 | 20 ms 부근에서 시작 후 튜닝 | 블롭 중심만 포화되고 번지지 않는 지점을 탐색 |
노출 튜닝이 이번 벤치 테스트에서 가장 시간이 많이 소요될 지점이다.
---
## 9. 브링업 절차
1. **LED 1개(PC0)만 330Ω으로 연결** → 스마트폰 카메라로 점등 확인 (필터 없이)
2. **950nm 필터 장착** → 실제 카메라로 블롭 확인, 노출 튜닝
3. **9개 전부 연결** → 모든 비트를 수동으로 켜서 9개 블롭이 분리되는지 확인
4. **TIM6 활성화, 스윕 시작** → USART 로그와 디코더 출력 대조
3번에서 블롭이 뭉치면 피치를 넓히거나 카메라 거리를 조정한다. **이 단계가 통과되지 않으면 디코더를 아무리 수정해도 소용없다.**
---
## 10. 제안서 수정 필요 항목
프로토타입 완료 후 문서 갱신 시 놓치기 쉬운 지점들.
| 위치 | 현재 기재 | 수정 방향 |
|---|---|---|
| 2. 다. 4단계 | 850nm 파장의 IR LED | 950nm |
| 3. 가. 광출력 발광부 | 고효율 850nm IR LED | 950nm |
| 4. 가. 신호 발광 소자 | 850nm 고출력 IR 광 LED | 950nm |
| 4. 나. 수집 카메라 사양 | 850nm 밴드패스 필터 일체형 | 950nm 필터, C 마운트 별도 장착 |
| 6. BOM 2번 | 850nm 파장 5mm 외형 | 950nm |
| 2. 다. 1단계 | Stop2 초저전력 슬립 모드 | **Stop2는 STM32L4 계열 용어.** L0는 Stop 모드 |
**Stop 모드 관련 보충:** STM32L0의 Stop 모드는 RTC 유지 시 수 µA대로, 제안서에 기재된 5µA보다 오히려 유리하다. 수치는 문제없으나 모드 명칭이 발주처 기술 검토에서 지적될 수 있다.
---
## 11. 본 프로토타입 이후 결정할 사항
### 11.1 LED 부품 교체 기준 (우선순위 순)
1. **반각** — ±20~25° 이상. 이것이 실제 설치 가능성을 좌우함
2. 파장 — 필터와 정합 (현 필터 유지 시 940~950nm)
3. 방사강도 — 목표 거리에서의 조도로 역산
4. 패키지 — 3×3 배치 시 피치 확보 가능한 크기
### 11.2 미결 항목
- **온도 4비트(16단계)로 충분한가** — 제안서의 "수분 14단계"와는 정합하나 온도는 부족할 수 있음. 온도 5비트 + 습도 4비트로 가려면 심볼 2개로 분할하거나 LED 수를 늘려야 함
- **실제 목표 인식 거리** — 벤치 1m 검증 후 온실 실환경 거리 확정 필요. 3m 초과 시 외부 드라이버 및 광각 LED 필수
- **팟 배치와 카메라 위치 기하** — 조준 문제의 실제 발생 여부는 이 기하에 달려 있음
- **디코더 구현** — ROI 자동 검출, 클록 기반 심볼 분할, 다수결 로직 (Python)
---
## 부록: 핵심 수치 요약
```
LED : LD274, 950nm, 반각 ±10°, V_F ≈ 1.1V @ 6mA
저항 : 330Ω (대안 220Ω)
구동 전류 : 6 mA/개, 합계 54 mA
배치 : 3×3, 피치 10mm
거리 : 1 ~ 1.5 m
심볼 주기 : 200 ms (6 프레임 @ 30fps)
유효 프레임 : 심볼당 최소 5
전체 스윕 주기 : 51.2 초 (256 심볼)
MCU : STM32L073RZ, HSI16, TIM6 (PSC 15999 / ARR 199)
GPIO : PC0~PC8, Open Drain, No pull, Low speed, Output level High
로그 : USART2 115200 8N1
```