diff --git a/manual/nrf_connect_vscode_manual.md b/manual/nrf_connect_vscode_manual.md new file mode 100644 index 0000000..ff6d3d3 --- /dev/null +++ b/manual/nrf_connect_vscode_manual.md @@ -0,0 +1,659 @@ +--- +marp: true +theme: default +paginate: true +backgroundColor: #ffffff +style: | + section { + font-family: 'Noto Sans KR', 'Apple SD Gothic Neo', sans-serif; + font-size: 22px; + } + h1 { + color: #00a9ce; + font-size: 42px; + border-bottom: 3px solid #00a9ce; + padding-bottom: 10px; + } + h2 { + color: #003087; + font-size: 32px; + } + h3 { + color: #555; + font-size: 26px; + } + code { + background: #f4f4f4; + padding: 2px 6px; + border-radius: 4px; + font-size: 18px; + } + pre { + background: #1e1e1e; + color: #d4d4d4; + border-radius: 8px; + padding: 16px; + font-size: 16px; + } + .columns { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + } + ul li { + margin-bottom: 6px; + } + table { + font-size: 18px; + width: 100%; + } + th { + background: #00a9ce; + color: white; + } +--- + + + + + +# nRF Connect for VS Code +## 완전 입문 가이드 + +**설치 → 프로젝트 생성 → 파일 구조 이해 → 빌드 & 플래싱** + +--- + +# 목차 + +1. **nRF Connect SDK 개요** +2. **개발 환경 설치** +3. **첫 프로젝트 만들기** +4. **핵심 파일 구조** + - `CMakeLists.txt` + - `prj.conf` (Kconfig) + - `.overlay` (Device Tree) +5. **빌드 & 플래싱** +6. **디버깅 (RTT / J-Link)** +7. **실제 프로젝트 예시 (VesiScan)** + +--- + +# 1. nRF Connect SDK 개요 + +## nRF Connect SDK란? + +Nordic Semiconductor의 **nRF 시리즈 MCU**를 위한 공식 개발 환경 + +- **기반**: Zephyr RTOS + 자체 미들웨어 레이어 +- **지원 칩**: nRF52 / nRF53 / nRF91 / nRF54 시리즈 +- **VS Code 확장**: nRF Connect for VS Code Extension Pack + +``` +nRF Connect SDK +├── Zephyr RTOS ← 코어 OS (스케줄러, 드라이버 등) +├── nrfxlib ← Nordic 전용 라이브러리 (BLE, LTE 등) +├── mcuboot ← 부트로더 +└── 내 프로젝트 코드 +``` + +> **핵심 개념**: 내 코드는 SDK 위에 "앱" 형태로 올라감 +> SDK 자체는 수정하지 않음 → `west` 도구로 관리 + +--- + +# 2. 개발 환경 설치 (1/3) + +## 필요한 소프트웨어 + +| 소프트웨어 | 역할 | 다운로드 | +|---|---|---| +| VS Code | 코드 편집기 | code.visualstudio.com | +| nRF Connect Extension Pack | VS Code 확장 묶음 | VS Code 마켓플레이스 | +| nRF Connect SDK | Zephyr + Nordic 라이브러리 | 확장에서 자동 설치 | +| J-Link | 플래싱 / 디버깅 드라이버 | segger.com | + +--- + +# 2. 개발 환경 설치 (2/3) + +## VS Code 확장 설치 + +1. VS Code 실행 → Extensions (`Cmd+Shift+X` / `Ctrl+Shift+X`) +2. **"nRF Connect for VS Code Extension Pack"** 검색 후 설치 +3. 설치되는 확장 목록: + +``` +nRF Connect for VS Code Extension Pack +├── nRF Connect ← 메인 확장 (빌드, 플래싱, 디버그) +├── nRF DeviceTree ← .overlay 파일 자동완성 / 검사 +├── nRF Kconfig ← prj.conf 자동완성 / 검사 +└── nRF Terminal ← 시리얼/RTT 터미널 +``` + +--- + +# 2. 개발 환경 설치 (3/3) + +## SDK 설치 (Toolchain Manager) + +1. VS Code 좌측 사이드바 → **nRF Connect 아이콘** 클릭 +2. **"Install Toolchain"** → 원하는 SDK 버전 선택 (예: `v2.6.0`) +3. 자동으로 다음을 다운로드: + - ARM GCC 툴체인 + - west (Python 기반 빌드/패키지 도구) + - nRF Connect SDK 소스 + +> **설치 경로 예시 (macOS)** +> `~/ncs/v2.6.0/` → SDK 소스 +> `~/ncs/toolchains/` → GCC, west 등 + +--- + +# 3. 첫 프로젝트 만들기 (1/2) + +## 방법 A: Sample에서 시작 (추천) + +1. 사이드바 → **nRF Connect** 패널 +2. **"Create a new application"** 클릭 +3. 옵션 선택: + - **Copy a sample**: SDK 내 샘플을 복사해서 시작 + - **Blank application**: 빈 프로젝트 생성 + +``` +추천 시작 샘플: +├── zephyr/samples/basic/blinky ← GPIO LED 기초 +├── zephyr/samples/bluetooth/peripheral_uart ← BLE NUS +└── nrf/samples/bluetooth/nordic_uart_service ← Nordic BLE +``` + +--- + +# 3. 첫 프로젝트 만들기 (2/2) + +## 방법 B: Build Configuration 추가 + +1. **"Add Build Configuration"** 클릭 +2. **Board** 선택 (예: `nrf52840dk_nrf52840`) +3. 설정 확인 후 **Build** 클릭 + +## 프로젝트 최소 구성 + +``` +my_project/ +├── CMakeLists.txt ← 빌드 대상 소스 파일 목록 +├── prj.conf ← Kconfig: 기능 ON/OFF 스위치 +├── src/ +│ └── main.c ← 진입점 +└── boards/ ← (선택) 보드별 오버레이 + └── nrf52840dk_nrf52840.overlay +``` + +--- + +# 4. CMakeLists.txt + +## 역할 + +> **빌드 시스템에게 "무엇을 컴파일할지" 알려주는 파일** + +## 기본 구조 + +```cmake +# 최소 CMake 버전 요구 +cmake_minimum_required(VERSION 3.20.0) + +# Zephyr SDK 찾기 (필수 - 항상 이 줄이 있어야 함) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +# 프로젝트 이름 +project(vesiscan) + +# 헤더 파일 검색 경로 추가 +target_include_directories(app PRIVATE + src + src/ble + src/drivers/battery +) + +# 컴파일할 소스 파일 목록 +target_sources(app PRIVATE + src/main.c + src/parser.c + src/ble/ble_service.c +) +``` + +--- + +# 4. CMakeLists.txt - 핵심 포인트 + +## 자주 쓰는 명령어 + +| 명령어 | 역할 | +|---|---| +| `find_package(Zephyr ...)` | **필수** - Zephyr 빌드 시스템 로드 | +| `project(이름)` | 프로젝트 이름 설정 | +| `target_sources(app PRIVATE ...)` | 컴파일할 .c 파일 추가 | +| `target_include_directories(app PRIVATE ...)` | 헤더 검색 경로 추가 | +| `target_compile_definitions(app PRIVATE ...)` | 전처리기 매크로 정의 | + +## 주의사항 + +- `app`은 Zephyr에서 정의한 고정 타겟 이름 → 변경 불가 +- 새 `.c` 파일을 만들면 **반드시 이 파일에 추가** 해야 컴파일됨 +- 헤더(`.h`)는 추가 불필요, 소스(`.c`)만 등록 + +--- + +# 4. prj.conf (Kconfig) + +## 역할 + +> **Zephyr 커널 & 드라이버 기능을 ON/OFF 하는 설정 파일** +> "어떤 기능을 활성화할지" 결정하는 스위치 모음 + +## 구조 예시 (VesiScan 기반) + +```ini +# GPIO 활성화 +CONFIG_GPIO=y + +# 로깅 (RTT로 출력) +CONFIG_LOG=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_USE_SEGGER_RTT=y + +# BLE 스택 +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_DEVICE_NAME="VB026030000" + +# BLE NUS (Nordic UART Service) +CONFIG_BT_NUS=y + +# ADC (배터리 측정) +CONFIG_ADC=y + +# I2C (IMU 센서) +CONFIG_I2C=y +``` + +--- + +# 4. prj.conf - 값의 종류 + +## Kconfig 값 타입 + +| 타입 | 예시 | 설명 | +|---|---|---| +| `bool` | `CONFIG_GPIO=y` / `=n` | 켜기 / 끄기 | +| `string` | `CONFIG_BT_DEVICE_NAME="MyDevice"` | 문자열 | +| `int` | `CONFIG_HEAP_MEM_POOL_SIZE=2048` | 정수 | +| `hex` | `CONFIG_FLASH_BASE_ADDRESS=0x0` | 16진수 | + +## 팁 + +- `=y` → 기능 활성화, `=n` → 비활성화 +- nRF Kconfig 확장 설치 시 **자동완성** 지원 +- 잘못된 `CONFIG_` 이름은 **빌드 시 경고** 발생 +- 의존 관계 있음: `CONFIG_BT_NUS=y` 하려면 `CONFIG_BT=y` 필요 + +--- + +# 4. .overlay (Device Tree) — 개요 + +## Device Tree란? + +> **하드웨어 구성을 코드 없이 선언하는 데이터 포맷** +> "어떤 핀에 무엇이 연결되어 있는지" 알려주는 하드웨어 지도 + +``` +.overlay 파일 위치: +boards/nrf52840dk_nrf52840.overlay + ↑ 이 이름이 빌드 보드와 일치해야 자동으로 적용됨 +``` + +## 기본 문법 + +```dts +/* 노드 수정 */ +&노드이름 { + 속성이름 = <값>; + status = "okay"; /* 활성화 */ + status = "disabled"; /* 비활성화 */ +}; + +/* 새 노드 추가 */ +/ { + 내이름: 노드이름 { + compatible = "gpio-leds"; + gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; + }; +}; +``` + +--- + +# 4. .overlay — GPIO 핀 설정 + +## LED & 버튼 정의 (VesiScan 예시) + +```dts +/ { + led { + compatible = "gpio-leds"; + + /* LED_BLE: GPIO0 12번 핀, Active Low */ + LED_BLE: LED_BLE { + gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; + label = "Green LED"; + }; + + /* FUNCTION_LED: GPIO0 29번 핀, Active Low */ + FUNCTION_LED: FUNCTION_LED { + gpios = <&gpio0 29 GPIO_ACTIVE_LOW>; + label = "Orange LED"; + }; + }; + + pin { + compatible = "gpio-keys"; + + /* 전원 홀드 핀: GPIO0 8번, Active High */ + PWR_HOLD: PWR_HOLD { + gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>; + }; + }; +}; +``` + +--- + +# 4. .overlay — I2C 핀 재설정 + +## I2C 핀을 기본값에서 변경 (VesiScan IMU 예시) + +```dts +/* I2C0 핀 재설정: SCL=P1.14, SDA=P1.15 */ +&i2c0_default { + group1 { + psels = , + ; + }; +}; + +&i2c0 { + status = "okay"; + clock-frequency = ; +}; +``` + +## 충돌 방지 — 기본 주변장치 비활성화 + +```dts +/* P0.08이 UART0 RX와 충돌 → UART0 비활성화 */ +&uart0 { + status = "disabled"; +}; +``` + +--- + +# 4. .overlay — ADC 채널 설정 + +## 배터리 전압 측정 ADC (VesiScan 예시) + +```dts +&adc { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + /* AIN2 = P0.04, 배터리 전압 측정 */ + channel@2 { + reg = <2>; + zephyr,gain = "ADC_GAIN_1_6"; /* 1/6 게인 */ + zephyr,reference = "ADC_REF_INTERNAL"; /* 0.6V 기준 */ + zephyr,input-positive = ; + zephyr,resolution = <12>; /* 12비트 */ + zephyr,oversampling = <2>; /* 4x 오버샘플링 */ + }; +}; + +/ { + /* 코드에서 ADC 채널 참조용 */ + zephyr,user { + io-channels = <&adc 2>; + }; +}; +``` + +--- + +# 4. 파일 구조 요약 + +## 세 파일의 관계 + +``` +┌─────────────────────────────────────────────────┐ +│ 내 프로젝트 │ +│ │ +│ CMakeLists.txt → "어떤 파일을 컴파일하나?" │ +│ │ +│ prj.conf → "어떤 기능을 켜나?" │ +│ (Kconfig) BLE, I2C, ADC, LOG 등 │ +│ │ +│ .overlay → "하드웨어가 어떻게 연결됐나?" │ +│ (Device Tree) 핀 번호, 주소, 설정 등 │ +└─────────────────────────────────────────────────┘ + ↓ 빌드 시 Zephyr가 모두 합쳐서 컴파일 +``` + +| 파일 | 변경 시 영향 | 자동완성 확장 | +|---|---|---| +| `CMakeLists.txt` | 새 파일 추가/제거 | 없음 | +| `prj.conf` | 기능 활성화/비활성화 | nRF Kconfig | +| `.overlay` | 핀/주변장치 설정 | nRF DeviceTree | + +--- + +# 5. 빌드 & 플래싱 + +## VS Code에서 빌드 + +1. 사이드바 → **nRF Connect** 패널 +2. **APPLICATIONS** 섹션에서 프로젝트 확인 +3. **Build** 버튼 (망치 아이콘) 클릭 + +``` +빌드 결과물 위치: +build/ +├── zephyr/ +│ ├── zephyr.hex ← 플래싱용 HEX 파일 +│ ├── zephyr.elf ← 디버깅용 ELF 파일 +│ └── zephyr.bin ← 바이너리 +└── CMakeFiles/ ← 빌드 캐시 +``` + +## 플래싱 + +1. J-Link 또는 DK 보드를 USB로 연결 +2. **Flash** 버튼 클릭 +3. `west flash` 명령도 가능: +```bash +west flash --runner jlink +``` + +--- + +# 5. 빌드 에러 해결 팁 + +## 자주 발생하는 에러 + +| 에러 메시지 | 원인 | 해결 | +|---|---|---| +| `undefined reference to ...` | .c 파일이 CMakeLists에 없음 | `target_sources`에 추가 | +| `Kconfig warning: ...` | 잘못된 CONFIG 이름 | 철자 확인, 의존성 확인 | +| `DTS error: ...` | .overlay 문법 오류 | nRF DeviceTree 확장으로 확인 | +| `No boards found` | 보드 이름 불일치 | overlay 파일명 = 보드 이름 | +| `west not found` | Toolchain 설정 안 됨 | nRF Connect 패널에서 SDK 재설치 | + +--- + +# 6. 디버깅 — RTT + +## Segger RTT (Real-Time Transfer) + +> UART 없이 J-Link를 통해 **실시간 로그** 출력 + +### prj.conf 설정 + +```ini +CONFIG_LOG=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_USE_SEGGER_RTT=y +CONFIG_RTT_CONSOLE=y +CONFIG_UART_CONSOLE=n ← UART 비활성화 +``` + +### 코드에서 사용 + +```c +#include +LOG_MODULE_REGISTER(my_module, LOG_LEVEL_DBG); + +void my_func(void) { + LOG_INF("동작 시작"); + LOG_DBG("디버그 값: %d", value); + LOG_ERR("에러 발생!"); +} +``` + +### 로그 확인 + +VS Code 사이드바 → **nRF Terminal** → **Start Terminal with New Configuration** → RTT 선택 + +--- + +# 6. 디버깅 — GDB + +## VS Code 중단점 디버깅 + +1. `.elf` 파일이 `build/zephyr/zephyr.elf`에 있어야 함 +2. VS Code 상단 **Debug** 버튼 (▶ 아이콘 옆 벌레) +3. 또는 **nRF Connect** 패널 → **Debug** 버튼 + +### 가능한 작업 + +- 중단점 설정/해제 +- 변수 값 실시간 확인 +- 스택 추적 (Call Stack) +- 레지스터 / 메모리 뷰 + +> **팁**: `CMakeLists.txt`에 `-O0` 최적화 옵션 없어도 +> Zephyr 디버그 빌드 (`-DCONF_FILE=prj.conf` + Debug 모드)로 자동 설정됨 + +--- + +# 7. 실제 프로젝트 예시 — VesiScan + +## 프로젝트 구조 + +``` +VesiScan-Basic_zephyr/ +├── CMakeLists.txt ← 빌드 설정 +├── prj.conf ← GPIO, BLE, ADC, I2C, RTT +├── boards/ +│ └── nrf52840dk_nrf52840.overlay ← 핀 설정 +└── src/ + ├── main.c ← 메인 루프 + ├── parser.c / .h ← 데이터 파싱 + ├── power_control.c / .h ← 전원 관리 + ├── ble/ + │ └── ble_service.c / .h ← BLE NUS 서비스 + └── drivers/ + ├── battery/battery_adc.c ← 배터리 ADC + ├── led/led_control.c ← LED 제어 + └── imu/imu_i2c.c ← ICM42670P IMU +``` + +--- + +# 7. VesiScan — 활성화된 기능 정리 + +## prj.conf 기준 활성화 기능 + +``` +VesiScan 기능 스택 +│ +├── 하드웨어 +│ ├── GPIO → LED, 전원버튼, 전원홀드 +│ ├── I2C → ICM42670P IMU (P1.14/P1.15) +│ └── ADC → 배터리 전압 (AIN2 = P0.04) +│ +├── 통신 +│ └── BLE → NUS (Nordic UART Service) +│ ├── Device Name: "VB026030000" +│ ├── MTU: 247 bytes +│ └── TX Power: +8 dBm +│ +└── 디버그 + └── RTT → Segger J-Link RTT 로그 +``` + +--- + +# 7. VesiScan — .overlay 핀 맵 요약 + +## 핀 배치 한눈에 보기 + +| 핀 | 방향 | 기능 | 코드 참조 | +|---|---|---|---| +| P0.08 | 출력 | 전원 홀드 래치 | `PWR_HOLD` | +| P1.08 | 입력 | 전원 버튼 | `BUTTON_CHECK` | +| P0.12 | 출력 | BLE LED (녹색) | `LED_BLE` | +| P0.29 | 출력 | 기능 LED (주황) | `FUNCTION_LED` | +| P0.04 | 아날로그 | 배터리 ADC (AIN2) | `io-channels` | +| P1.14 | I2C | IMU SCL | `i2c0` | +| P1.15 | I2C | IMU SDA | `i2c0` | + +> **overlay의 노드 라벨** (`LED_BLE`, `PWR_HOLD` 등)을 코드에서 +> `DT_ALIAS()` / `DT_NODELABEL()`로 직접 참조 가능 + +--- + + + + +# 요약 & 체크리스트 + +## 새 프로젝트 시작 시 확인 사항 + +- [ ] nRF Connect Extension Pack 설치 +- [ ] SDK + Toolchain 버전 설치 (Toolchain Manager) +- [ ] Board 선택하여 Build Configuration 추가 +- [ ] `CMakeLists.txt`에 모든 `.c` 파일 등록 +- [ ] `prj.conf`에 필요한 기능 `CONFIG_XXX=y`로 활성화 +- [ ] `.overlay` 파일명 = 보드 이름과 일치 확인 +- [ ] 핀 충돌 확인 (기본 주변장치 비활성화 필요 시) +- [ ] Build → Flash → RTT 로그로 동작 확인 + +--- + + + + + +# Thank you + +**nRF Connect for VS Code 매뉴얼** + +작성 기준 프로젝트: **VesiScan-Basic_zephyr** +SDK 버전: nRF Connect SDK v2.x / Zephyr RTOS + +--- + +*이 문서는 Marp를 이용해 PPT로 내보낼 수 있습니다.* +*VS Code에서 Marp for VS Code 확장 설치 후* +*`Export Slide Deck` → PPTX / PDF / HTML 선택* diff --git a/plan/measurement_pipeline.md b/plan/measurement_pipeline.md new file mode 100644 index 0000000..67ff268 --- /dev/null +++ b/plan/measurement_pipeline.md @@ -0,0 +1,197 @@ +# VesiScan BASIC — 초음파 측정 파이프라인 (Zephyr 포팅 계획) + +## 1. 하드웨어 구성 + +| 역할 | 칩 | 핀 | +|---|---|---| +| 피에조 전원 | DC/DC 컨버터 (+/-20V) | PWR_EN: P1.9 | +| TX Pulse Enable | MOSFET 드라이버 | PE: P0.25 | +| TX 양극 출력 | MOSFET | P_OUT: P1.7 | +| TX 음극 출력 | MOSFET | N_OUT: P1.6 | +| TX 방전 | Dump 회로 | DMP: P1.0 | +| 채널 선택 | 8ch 아날로그 MUX | EN_MUXA: P0.21, EN_MUXB: P0.23, SEL0: P1.10, SEL1: P0.28 | +| Echo ADC | ADC121S051 (TI, 12-bit) | SCLK: P0.14, MISO: P0.15, CS: P0.19 | + +### MUX 채널 매핑 (6채널 사용) + +| CH | EN_MUXA | EN_MUXB | SEL0 | SEL1 | +|---|---|---|---|---| +| 0 | 1 | 0 | 0 | 0 | +| 1 | 1 | 0 | 1 | 0 | +| 2 | 1 | 0 | 0 | 1 | +| 3 | 1 | 0 | 1 | 1 | +| 4 | 0 | 1 | 1 | 1 | +| 5 | 0 | 1 | 0 | 1 | + +--- + +## 2. 전체 측정 흐름 + +``` +[측정 커맨드 수신] + │ + ▼ +piezo_power_on() + └─ PWR_EN = HIGH + └─ 3ms 안정화 대기 + +echo_adc_init() + └─ SPIM3 초기화 (16MHz, SPI Mode 3, CPOL=1 CPHA=1) + └─ dummy read 1회 (ADC wake-up) + + │ + ▼ +┌─── for ch = 0 ~ 5 ───────────────────────────────────────────┐ +│ │ +│ piezo_select_channel(ch) │ +│ └─ MUX 핀 설정 │ +│ └─ 1300us settling 대기 │ +│ │ +│ [averaging 횟수만큼 반복] (기본 3회) │ +│ piezo_burst_sw(cycles) ← SW NOP 버스트 │ +│ ├─ __disable_irq() │ +│ ├─ PE ON │ +│ ├─ for i in cycles: │ +│ │ P_OUT=HIGH, N_OUT=LOW (NOP × 14) ┐ 2.1MHz │ +│ │ P_OUT=LOW, N_OUT=HIGH (NOP × 9) ┘ │ +│ ├─ DMP 펄스 (500ns) │ +│ ├─ PE OFF │ +│ └─ __enable_irq() │ +│ │ +│ delay_us 대기 (기본 20us) ← 버스트 → ADC 시작 딜레이 │ +│ │ +│ echo_adc_capture(buf, num_samples) ← SPIM3 연속 읽기 │ +│ └─ 100샘플 × 2byte = 200byte │ +│ └─ 소요시간: ~100us (16MHz SPI) │ +│ │ +│ 샘플 합산 (averaging용) │ +│ │ +│ averaging 나눗셈 → channel_buf[ch] 저장 │ +│ │ +└───────────────────────────────────────────────────────────────┘ + │ + ▼ +BLE 전송 (채널별) + reb: [샘플수(2B)] [raw data (200B)] + CRC16 ← 채널당 1패킷 + ... + raa: [상태(2B)] + CRC16 ← 완료 패킷 + + │ + ▼ +piezo_power_off() + └─ PE, P_OUT, N_OUT, DMP = LOW + └─ MUX EN 핀 = LOW + └─ PWR_EN = LOW +``` + +--- + +## 3. 타이밍 정리 + +| 구간 | 시간 | 비고 | +|---|---|---| +| 전원 안정화 | 3ms | DC/DC +/-20V 안정화 | +| MUX settling | 1.3ms | 채널 전환 후 아날로그 경로 안정화 | +| SW 버스트 (5 cycles @ 2.1MHz) | ~2.4µs | __disable_irq() 구간 | +| burst → ADC 딜레이 | 20µs (기본) | 에코 신호 도달 대기 | +| ADC 캡처 (100샘플 @ 16MHz SPI) | ~100µs | 1샘플 = 16 SCLK = 1µs | +| 채널당 총 측정 시간 (averaging 3회) | ~4.4ms + 3ms | MUX + (burst+딜레이+ADC) × 3 | +| 6채널 전체 측정 | ~30ms | 채널 × 6 | + +--- + +## 4. ADC121S051 SPI 프레임 + +``` +CS ────┐ ┌──── + └────────────────────┘ + +SCLK ────┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ──── (idle HIGH, 16 clocks) + └─┘ └─┘ └─ ... └─┘ └─┘ + +DATA ----...---- + │← 3 zeros →│← 12 data bits →│ + +변환 공식: value = (raw16 >> 1) & 0x0FFF +``` + +- SPI Mode 3: CPOL=1(idle HIGH), CPHA=1(rising edge sample) +- 16MHz clock → 1샘플 = 16 clocks = 1µs +- 12-bit 출력 (0 ~ 4095), VREF = 3.3V +- 1LSB = 3300mV / 4096 ≈ 0.8mV + +--- + +## 5. BLE 응답 패킷 포맷 + +### reb: (echo raw data) +``` +[r][e][b][:] [num_samples H][num_samples L] [s0_H][s0_L] [s1_H][s1_L] ... [CRC_L][CRC_H] + 4 bytes 2 bytes num_samples × 2 bytes 2 bytes +``` +- 100샘플 기준: 4 + 2 + 200 + 2 = **208 bytes** +- BLE MTU 247byte 이내이므로 단일 패킷으로 전송 가능 + +### raa: (완료) +``` +[r][a][a][:] [status H][status L] [CRC_L][CRC_H] + 4 bytes 2 bytes 2 bytes = 8 bytes +``` +- status 0x0000 = 성공 + +--- + +## 6. Zephyr 구현 계획 + +### 신규 파일 + +| 파일 | 내용 | +|---|---| +| `src/drivers/piezo/piezo.c` | SW 버스트, MUX 제어, 전원 제어 | +| `src/drivers/piezo/piezo.h` | 공개 API 선언 | +| `src/drivers/echo_adc/echo_adc.c` | ADC121S051 SPIM3 드라이버 | +| `src/drivers/echo_adc/echo_adc.h` | 공개 API 선언 | + +### 수정 파일 + +| 파일 | 변경 내용 | +|---|---| +| `boards/nrf52840dk_nrf52840.overlay` | 피에조/MUX GPIO, SPI 핀 비활성화 충돌 처리 | +| `prj.conf` | `CONFIG_NRFX_GPIOTE=y`, `CONFIG_NRFX_SPIM3=y` 추가 | +| `CMakeLists.txt` | 신규 소스/인클루드 추가 | +| `src/parser.c` | `maa?` 커맨드 핸들러 추가 | + +### 기존 SDK → Zephyr 변환표 + +| nRF5 SDK | Zephyr | +|---|---| +| `#include "nrf_gpio.h"` | `#include ` | +| `#include "nrf_gpiote.h"` | `#include ` | +| `#include "nrfx_spim.h"` | `#include ` | +| `nrf_delay_ms(x)` | `k_msleep(x)` | +| `nrf_delay_us(x)` | `k_busy_wait(x)` | +| `NRF_P0->OUTSET/OUTCLR` | 동일 (레지스터 직접 접근) | +| `__disable_irq()` / `__NOP()` | 동일 (ARM CMSIS) | +| `NRFX_SPIM_INSTANCE(3)` | 동일 | + +### SW 버스트 NOP 타이밍 (변경 없음) + +CPU 64MHz → 1 NOP = 15.625ns. nRF5 SDK, Zephyr 모두 동일하므로 NOP 개수 수정 불필요. + +| 주파수 | 반주기 | 1st half NOP | 2nd half NOP | +|---|---|---|---| +| 1.7 MHz | 294ns | 18 | 10 | +| 1.8 MHz | 278ns | 17 | 11 | +| 1.9 MHz | 263ns | 15 | 9 | +| 2.0 MHz | 250ns | 15 | 10 | +| 2.1 MHz | 238ns | 14 | 9 | +| 2.2 MHz | 227ns | 13 | 8 | + +--- + +## 7. 구현 순서 (권장) + +1. `piezo.c` — GPIO 초기화, MUX, 전원, SW 버스트 +2. `echo_adc.c` — SPIM3 초기화, 단일 샘플 읽기, 연속 캡처 +3. `parser.c` — `maa?` 커맨드 핸들러 (6채널 측정 → BLE 전송) +4. 빌드 검증 및 오실로스코프로 타이밍 확인 diff --git a/plan/system_pipeline.md b/plan/system_pipeline.md new file mode 100644 index 0000000..b995eeb --- /dev/null +++ b/plan/system_pipeline.md @@ -0,0 +1,425 @@ +# VesiScan-Basic — 시스템 전체 파이프라인 + +## 목차 + +1. [전체 동작 개요](#1-전체-동작-개요) +2. [부팅 및 BLE 연결 흐름](#2-부팅-및-ble-연결-흐름) +3. [커맨드/응답 테이블](#3-커맨드응답-테이블) +4. [msp? 흐름 (1초 주기 IMU)](#4-msp-흐름-1초-주기-imu) +5. [mbb? 흐름 (10초 주기 전체 측정)](#5-mbb-흐름-10초-주기-전체-측정) +6. [구현 현황](#6-구현-현황) +7. [핀 배치 전체 정리](#7-핀-배치-전체-정리) +8. [IMU 테스트 가이드](#8-imu-테스트-가이드-msp) +9. [다음 구현 순서](#9-다음-구현-순서) + +--- + +## 1. 전체 동작 개요 + +``` +[앱] ──BLE NUS── [디바이스] + │ │ + │── msp? (1초마다) ──▶ │ IMU 측정 → rsp: (accel+gyro) + │ │ + │── mbb? (10초마다) ──▶ │ 6ch 피에조 측정 + 배터리/IMU/온도 + │ │ → rbb: (센서 번들) + │◀── rbb: ────────────│ + │◀── reb: × 6 ────────│ (채널별 echo 데이터) + │◀── raa: ────────────│ (완료) +``` + +--- + +## 2. 부팅 및 BLE 연결 흐름 + +``` +[부팅] + │ + ▼ +전원 버튼 2초 유지 + │ + ▼ +P0.08 래치 ON (전원 자가유지) + │ + ▼ +HW 초기화 + ├─ GPIO, LED + ├─ Battery ADC (SAADC AIN2) + ├─ IMU (I2C0, ICM42670P @ 0x68) + ├─ Temperature ADC (SAADC AIN3, TMP235-Q1) + ├─ [TODO] Piezo 드라이버 (GPIO + SW burst) + └─ [TODO] Echo ADC (SPIM3, ADC121S051) + │ + ▼ +BLE 스택 초기화 (NUS) + │ + ▼ +Advertising 시작 → LED: 파란불 깜빡임 + │ + ▼ +앱 연결됨 → LED: 연결 표시 + │ + ▼ +앱에서 주기적 커맨드 수신 + ├─ msp? (1초마다) + └─ mbb? (10초마다) +``` + +--- + +## 3. 커맨드/응답 테이블 + +| 커맨드 | 설명 | 응답 | 주기 | 구현 상태 | +|---|---|---|---|---| +| `msp?` | IMU 1회 측정 | `rsp:` (accel+gyro, 12B) | 1초 | ✅ 완료 | +| `mbb?` | 전체 측정 (센서+피에조 6ch) | `rbb:` + `reb:` ×6 + `raa:` | 10초 | ❌ 미구현 | +| `maa?` | 피에조 6ch 단독 측정 | `reb:` ×6 + `raa:` | 수동 | ❌ 미구현 | +| `msn?` | 배터리 전압 단독 측정 | `rsn:` (mV) | 수동 | ✅ 완료 | +| `mls?` | LED 상태 설정 | `rls:` (state echo) | 수동 | ✅ 완료 | + +--- + +## 4. msp? 흐름 (1초 주기 IMU) + +``` +앱 → msp? + │ + ▼ + imu_read(accel, gyro) + ├─ GYRO_CONFIG0 = 0x09 (±2000dps, 100Hz) + ├─ ACCEL_CONFIG0 = 0x29 (±4g, 100Hz) + ├─ PWR_MGMT0 = 0x0F (low-noise ON) + ├─ 80ms 대기 (자이로 스타트업) + ├─ 0x0B부터 12바이트 읽기 + └─ PWR_MGMT0 = 0x00 (슬립) + │ + ▼ + rsp: [accel X(2)] [accel Y(2)] [accel Z(2)] + [gyro X(2)] [gyro Y(2)] [gyro Z(2)] + [CRC16(2)] = 18 bytes +``` + +**구현 상태: ✅ 완료** +- `src/drivers/imu/imu_i2c.c` — ICM42670P I2C 드라이버 +- `src/parser.c` — `msp?` 핸들러, `rsp:` 응답 + +--- + +## 5. mbb? 흐름 (10초 주기 전체 측정) + +``` +앱 → mbb? + │ + ▼ + [Phase 1] 피에조 6채널 캡처 (타이밍 크리티컬) + ┌──────────────────────────────────────────────┐ + │ piezo_power_on() ← PWR_EN=HIGH, 3ms 대기 │ + │ echo_adc_init() ← SPIM3 초기화 │ + │ │ + │ for ch = 0 ~ 5: │ + │ piezo_select_channel(ch) ← MUX, 1.3ms │ + │ for avg = 0 ~ 2: ← 3회 평균 │ + │ piezo_burst_sw(5) ← 2.1MHz SW burst│ + │ delay 20us │ + │ echo_adc_capture(buf, 100) ← SPIM3 │ + │ channel_data[ch] = 평균값 │ + └──────────────────────────────────────────────┘ + │ + ▼ + [Phase 2] 센서 측정 (피에조 캡처 완료 후) + ┌──────────────────────────────────────────────┐ + │ battery_read_mv() → info_batt (mV) │ + │ imu_read(accel, gyro) → info_imu[6] │ + │ temperature_read() → info_temp (°Cx100) │ + └──────────────────────────────────────────────┘ + │ + ▼ + [Phase 3] BLE 전송 + ┌──────────────────────────────────────────────┐ + │ rbb: [batt(2)][IMU(12)][temp(2)][CRC(2)] │ + │ = 20 bytes │ + │ │ + │ for ch = 0 ~ 5: │ + │ reb: [num_samples(2)][raw(200)][CRC(2)] │ + │ = 204 bytes per channel │ + │ │ + │ raa: [status(2)][CRC(2)] = 8 bytes │ + └──────────────────────────────────────────────┘ + │ + ▼ + piezo_power_off() +``` + +**rbb: 패킷 포맷** +``` +[r][b][b][:] [batt_L][batt_H] + [imu0_L][imu0_H] ... [imu5_L][imu5_H] ← accel XYZ + gyro XYZ + [temp_L][temp_H] + [CRC_L][CRC_H] += 4 + 2 + 12 + 2 + 2 = 22 bytes +``` + +**reb: 패킷 포맷 (채널당)** +``` +[r][e][b][:] [num_samples_H][num_samples_L] + [s0_H][s0_L] [s1_H][s1_L] ... [s99_H][s99_L] + [CRC_L][CRC_H] += 4 + 2 + 200 + 2 = 208 bytes +``` + +--- + +## 6. 구현 현황 + +### ✅ 완료 + +| 모듈 | 파일 | 기능 | +|---|---|---| +| BLE NUS | `src/ble/ble_service.c` | advertising, 연결, RX/TX | +| 전원 제어 | `src/power_control.c`, `src/main.c` | 버튼 상태머신, 래치, 슬립 | +| LED | `src/drivers/led/led_control.c` | 상태별 LED 패턴 | +| 배터리 ADC | `src/drivers/battery/battery_adc.c` | SAADC AIN2, 주기 모니터링 | +| IMU | `src/drivers/imu/imu_i2c.c` | ICM42670P I2C (SCL=P1.14, SDA=P1.15) | +| 온도 센서 | `src/drivers/temperature/tmp235.c` | TMP235-Q1, SAADC AIN3 (P0.05) | +| 파서 | `src/parser.c` | `msn?`, `mls?`, `msp?` | + +#### SAADC 채널 공유 (배터리 ↔ 온도) + +배터리(AIN2)와 온도(AIN3)는 nRF52840 SAADC 하나를 공유한다. + +**nRF5 SDK에서는** 채널 전환 시 `nrfx_saadc_uninit()`을 명시적으로 호출하지 않으면 다음 `nrfx_saadc_channel_init()` 호출에서 `NRFX_ERROR_BUSY`가 발생하는 문제가 있었다. + +**Zephyr에서는** `adc_channel_setup_dt()` + `adc_read_dt()` 내부적으로 init/uninit 처리가 되므로 명시적 uninit 불필요. `battery_read_mv()`와 `temp_read_cdeg()` 모두 읽기 전에 `adc_channel_setup_dt()`를 호출하여 해당 채널로 SAADC를 재설정하기 때문에 임의 순서로 교대 호출해도 정상 동작한다. + +> 단, 두 함수를 **서로 다른 스레드에서 동시에** 호출하면 안 됨. mbb? 핸들러에서 직렬로 호출하는 한 문제없음. + +### 🔲 테스트 필요 + +| 모듈 | 테스트 방법 | 확인 항목 | +|---|---|---| +| 배터리 ADC (`msn?`) | RTT 로그 확인 + 앱에서 `msn?` 전송 | 아래 테스트 가이드 참고 | +| 온도 센서 | RTT 로그 확인 | 아래 테스트 가이드 참고 | +| IMU (`msp?`) | RTT 로그 확인 + 앱에서 `msp?` 전송 | 아래 테스트 가이드 참고 | + +#### 배터리 ADC 테스트 가이드 (msn?) + +**1. 부팅 시 확인 (RTT 로그)** + +`[1] HW Init` 단계에서 아래 줄이 나와야 함: +``` +[BATT] ADC init OK (DT-based, ch=2, res=12, os=2) +``` + +실패 패턴: + +| 로그 | 원인 | 조치 | +|---|---|---| +| `[BATT] ADC device not ready` | SAADC 드라이버 초기화 실패 | `CONFIG_ADC=y` 확인, overlay channel@2 확인 | +| `[BATT] Channel setup failed (err -x)` | 채널 설정 실패 | overlay AIN2 설정 확인 | + +**2. msn? 전송 시 확인 (RTT 로그)** + +앱에서 `msn?` 전송 시 아래 형식으로 출력됨: +``` +[CMD] msn -> 3800 mV +``` +- 정상 범위: 3500 ~ 4200 mV (완충 4200mV, 저전압 경고 3500mV) +- 3500mV 미만이 10회 연속이면 자동 전원 OFF + +**3. BLE 응답 패킷 포맷** +``` +[r][s][n][:] [mV_H][mV_L] [CRC_L][CRC_H] = 8 bytes +``` + +--- + +#### 온도 센서 테스트 가이드 (TMP235) + +**1. 부팅 시 확인 (RTT 로그)** + +`[1] HW Init` 단계에서 아래 줄이 나와야 함: +``` +[TEMP] OK — TMP235 ch=3, res=12, os=2 +``` + +실패 패턴: + +| 로그 | 원인 | 조치 | +|---|---|---| +| `[TEMP] FAIL — ADC device not ready` | SAADC 드라이버 초기화 실패 | overlay channel@3 확인 | +| `[TEMP] FAIL — channel setup (err=-x)` | 채널 설정 실패 | overlay AIN3 설정 확인, P0.05 핀 확인 | + +**2. 측정 시 확인 (RTT 로그)** + +`temp_read_cdeg()` 호출 시 아래 형식으로 출력됨: +``` +[TEMP] raw=1234 -> 25.50 C +``` +- 실온(25°C) 기준 raw 값 약 1404 (V = 750mV, raw = 750 × 4095 / 3600 ≈ 853) + > 실제 분압 회로가 있으면 값이 다를 수 있음, 첫 측정값 보고 판단 +- `INT16_MIN(-32768)` 반환 시 ADC 읽기 실패 + +**3. mbb? 패킷 내 위치** +``` +rbb: ... [temp_L][temp_H] ... ← °C × 100 단위 (25.50°C = 2550) +``` + +--- + +#### IMU 테스트 가이드 (msp?) + +**1. 부팅 시 확인 (RTT 로그)** + +`[1] HW Init` 단계에서 아래 줄이 나와야 함: +``` +[IMU] OK — ICM42670P detected (WHOAMI=0x67, addr=0x68) +``` + +실패 패턴: + +| 로그 | 원인 | 조치 | +|---|---|---| +| `[IMU] FAIL — I2C bus not ready` | Zephyr I2C 드라이버 초기화 실패 | `CONFIG_I2C=y` 확인, overlay 확인 | +| `[IMU] FAIL — WHOAMI read error (check SCL=P1.14, SDA=P1.15)` | I2C 통신 자체 실패 | 핀 납땜/연결 확인, 풀업 저항 확인 | +| `[IMU] FAIL — WHOAMI mismatch (got=0x00, expected=0x67)` | 버스는 살아있으나 응답 이상 | I2C 주소(0x68) 확인, AD0 핀 상태 확인 | + +**2. msp? 전송 시 확인 (RTT 로그)** + +앱에서 `msp?` 전송 시 아래 형식으로 출력됨: +``` +[IMU] msp: A=( 12345, -1234, 3210) G=( 100, -50, 200) +``` +- `A=` : 가속도 XYZ (int16, ±4g 풀스케일 → 1g ≈ 8192) +- `G=` : 자이로 XYZ (int16, ±2000dps 풀스케일 → 1dps ≈ 16.4) +- 디바이스 수평 정치 시 Z축 가속도 약 `+8192` 근처, 자이로 `0` 근처 (±수십 이내) + +실패 패턴: + +| 로그 | 원인 | +|---|---| +| `[IMU] FAIL — gyro config write (ret=-5)` | I2C TX 에러 | +| `[IMU] FAIL — data read (ret=-5)` | I2C RX 에러 | +| BLE로 `rsp: 0xFFFF` 수신 | 위 에러 발생 시 앱으로 전송되는 에러 응답 | + +**3. BLE 응답 패킷 포맷** +``` +[r][s][p][:] [AX_H][AX_L] [AY_H][AY_L] [AZ_H][AZ_L] + [GX_H][GX_L] [GY_H][GY_L] [GZ_H][GZ_L] + [CRC_L][CRC_H] = 18 bytes +``` + +### ❌ 미구현 + +| 모듈 | 목표 파일 | 필요 기능 | +|---|---|---| +| 피에조 드라이버 | `src/drivers/piezo/piezo.c` | SW burst (2.1MHz), MUX, 전원 제어 | +| Echo ADC | `src/drivers/echo_adc/echo_adc.c` | ADC121S051, SPIM3 @ 16MHz | +| `maa?` 커맨드 | `src/parser.c` 추가 | 피에조 6ch 단독 측정 → `reb:` ×6 + `raa:` | +| `mbb?` 커맨드 | `src/parser.c` 추가 | 전체 측정 오케스트레이션 → `rbb:` + `reb:` ×6 + `raa:` | + +--- + +## 7. 핀 배치 전체 정리 + +| 신호 | 핀 | 방향 | 모듈 | 상태 | +|---|---|---|---|---| +| POWER_HOLD | P0.08 | OUT | 전원 래치 | ✅ | +| POWER_BTN | P1.08 | IN | 전원 버튼 | ✅ | +| LED_BLE | P0.12 | OUT | 파란 LED | ✅ | +| LED_FUNC | P0.29 | OUT | 주황 LED | ✅ | +| BATT_ADC | P0.04 (AIN2) | AIN | 배터리 ADC | ✅ | +| IMU_SCL | P1.14 | I2C | ICM42670P | ✅ | +| IMU_SDA | P1.15 | I2C | ICM42670P | ✅ | +| TEMP_ADC | P0.05 (AIN3) | AIN | TMP235-Q1 | 🔲 | +| PIEZO_PWR_EN | P1.09 | OUT | DC/DC +/-20V | ❌ | +| PIEZO_PE | P0.25 | OUT | Pulse Enable | ❌ | +| PIEZO_P_OUT | P1.07 | OUT | 양극 출력 | ❌ | +| PIEZO_N_OUT | P1.06 | OUT | 음극 출력 | ❌ | +| PIEZO_DMP | P1.00 | OUT | Dump | ❌ | +| MUX_EN_A | P0.21 | OUT | MUXA 활성화 | ❌ | +| MUX_EN_B | P0.23 | OUT | MUXB 활성화 | ❌ | +| MUX_SEL0 | P1.10 | OUT | MUX 채널 선택 | ❌ | +| MUX_SEL1 | P0.28 | OUT | MUX 채널 선택 | ❌ | +| ECHO_SCLK | P0.14 | SPI | ADC121S051 CLK | ❌ | +| ECHO_MISO | P0.15 | SPI | ADC121S051 DATA | ❌ | +| ECHO_CS | P0.19 | OUT | ADC121S051 CS | ❌ | + +--- + +## 8. IMU 테스트 가이드 (msp?) + +### 부팅 시 확인 (RTT 로그) + +부팅 직후 `[1] HW Init` 단계에서 아래 줄이 나와야 함: + +``` +[IMU] OK — ICM42670P detected (WHOAMI=0x67, addr=0x68) +``` + +#### 실패 패턴별 원인 + +| 로그 | 원인 | 조치 | +|---|---|---| +| `[IMU] FAIL — I2C bus not ready` | Zephyr I2C 드라이버 초기화 실패 | `CONFIG_I2C=y` 확인, overlay 확인 | +| `[IMU] FAIL — WHOAMI read error (check SCL=P1.14, SDA=P1.15)` | I2C 통신 자체 실패 | 핀 납땜/연결 확인, 풀업 저항 확인 | +| `[IMU] FAIL — WHOAMI mismatch (got=0x00, expected=0x67)` | 버스는 살아있으나 응답 이상 | I2C 주소(0x68) 확인, AD0 핀 상태 확인 | + +--- + +### msp? 커맨드 전송 시 확인 (RTT 로그) + +앱에서 `msp?` 전송 시 아래 형식으로 출력됨: + +``` +[IMU] msp: A=( 12345, -1234, 3210) G=( 100, -50, 200) +``` + +- `A=` : 가속도 XYZ (int16, ±4g 풀스케일 → 1g ≈ 8192) +- `G=` : 자이로 XYZ (int16, ±2000dps 풀스케일 → 1dps ≈ 16.4) +- 디바이스가 수평으로 놓여 있으면 Z축 가속도가 약 `+8192` 근처여야 함 +- 자이로는 정지 상태에서 `0` 근처 (±수십 이내) + +#### 실패 패턴 + +| 로그 | 원인 | +|---|---| +| `[IMU] FAIL — gyro config write (ret=-5)` | I2C TX 에러 (부팅 후 재연결 문제) | +| `[IMU] FAIL — data read (ret=-5)` | I2C RX 에러 | +| BLE로 `rsp: 0xFFFF` 수신 | 위 에러 발생 시 앱으로 전송되는 에러 응답 | + +--- + +### BLE 응답 패킷 확인 + +정상 수신 시 앱에서 받는 `rsp:` 패킷 포맷: + +``` +[r][s][p][:] [AX_H][AX_L] [AY_H][AY_L] [AZ_H][AZ_L] + [GX_H][GX_L] [GY_H][GY_L] [GZ_H][GZ_L] + [CRC_L][CRC_H] += 18 bytes +``` + +--- + +## 9. 다음 구현 순서 + +``` +Step 1 ✅ 온도 센서 (tmp235.c) — 완료, 테스트 필요 + +Step 2 피에조 드라이버 (piezo.c) + └─ GPIO 초기화, MUX, SW burst (NOP 기반, nrf HAL 직접 사용) + └─ CONFIG_NRFX_GPIOTE=y 추가 + +Step 3 Echo ADC (echo_adc.c) + └─ SPIM3 초기화, 연속 샘플 캡처 + └─ CONFIG_NRFX_SPIM3=y 추가 + +Step 4 maa? 커맨드 (parser.c 추가) + └─ 피에조 6ch 측정 + BLE 전송 (reb: ×6 + raa:) + └─ 센서 번들 없음 — 피에조/ADC 단독 검증용 + +Step 5 mbb? 커맨드 (parser.c 추가) + └─ 배터리 + IMU + 온도 + 피에조 6ch 전체 오케스트레이션 + └─ rbb: + reb: ×6 + raa: +```