Compare commits

...

39 Commits

Author SHA1 Message Date
jhChun 3ec6afec36 MUX 전환 후 ADC S/H 커패시터 안정화를 위한 dummy read 2026-04-07 14:17:06 +09:00
jhChun 6cb927e831 DEBUG 빌드 해제
- 에러 처리 시 먹통 현상 = DEBUG 빌드
2026-04-03 16:43:29 +09:00
jhChun 47a31c1eb4 SAADC 타이밍 충돌로 인한 FW RESET 방지: 배터리 저전압 측정 주기 및 init 실패시 처리 변경
- 배터리 저전압 측정 주기 5초 -> 60초로 변경(충돌 확률 감소)
- SAADC init 실패 시 APP_ERROR_CHECK -> 측정 스킵 처리

***** 현재 에러 처리는 APP_ERROR_CHECK로 되어있는데, 에러 발생 시 APP_ERROR_CHECK에 걸리면 HardFault 무한 루프에 빠져서 기기가 먹통이 됨. 런타임 중 에러가 발생해도 안전하게 복구되도록 에러 처리 전환 필요함. *****
2026-04-02 11:27:28 +09:00
jhChun 5ee39f834e Slave Latency 4 -> 0 변경 2026-04-02 11:17:06 +09:00
jhChun 5128c44912 DR_ADC_ECHO_SAMPLES_MAX 100 -> 119 2026-04-01 16:22:42 +09:00
jhChun 8fdc153ed5 블루투스 스캐닝 LED 1초 간격 점멸 -> 0.5초 간격 점멸 2026-03-31 17:58:49 +09:00
jhChun d8ef9bcb41 dev 모드 BLE 연결 수정
- dev 모드인 경우 보안 x
2026-03-31 17:58:33 +09:00
jhChun 007e0b5683 Ver VBTFW0102 2026-03-31 14:27:14 +09:00
jhChun d63a9cfa3c BLE 보안 실패 시 자동 재페어링 및 버그 3건 수정
- 앱과 기기 사이 키가 불일치하는 경우 새로운 키 생성 요청(재페어링 허용)
- main.c: PM_EVT_CONN_SEC_CONFIG_REQ 중복 핸들러 제거
- main.c: DEV 모드에서 pm_peers_delete() 이중 호출 방지
- main.c: 활성 연결 중 advertising 재시작 방지(m_conn_handle 체크)
- ble_quick_security: 보안 실패 시 bond 삭제 + 자동 재페어링 시도
- ble_quick_security: allow_repairing = true (항상 재페어링 허용)
2026-03-31 14:17:35 +09:00
jhChun 217d741d31 LED 상태 설정 명령 추가
- BLE 명령으로 LED 상태 설정
2026-03-31 14:01:02 +09:00
jhChun f464838ed4 Connection Interval 30ms로 변경
- 기존: MIN=20, MAX=75
- 변경: MIN=30, MAX=30
- GAP Event Length=24(30ms)에 맞춰 변경함
- Piezo 6채널 ADC 데이터 7패킷 송신 시 3~4패킷/CI로, ~60ms 소요
2026-03-31 09:55:47 +09:00
jhChun a150d1da96 LED 직접 제어 모듈 추가(BSP 대체)
- 전원 ON/OFF, 블루투스 스캐닝 초록 LED 적용 완료
2026-03-30 18:00:38 +09:00
jhChun 11a0b3f11a FW 버전 업데이트 2026-03-30 17:10:17 +09:00
jhChun 5e27eb762d BLE Piezo 6채널 데이터 패킷 병합 (reb+red -> reb 단일 패킷)
- BLE_MTU_SIZE 240 -> 244 (ATT MTU 247 - 3 = 244, 기존 4B 낭비 해소)
- reb: 헤더 축소(14B → 6B) 후 데이터 병합 -> 119샘플까지 단일 패킷(현재 100샘플)
- reb: 헤더에서 peak_raw/peak_index/baseline_raw/버전마커 제거
  → PC에서 raw 데이터로 직접 계산, 단일 패킷이므로 버전마커 불필요
- 채널 간 딜레이 50ms -> 5ms (dr_binary_tx_safe 내부 재시도로 TX 보장)
- delta 전송(rdb/rdd)도 동일 방식 적용, 종료 패킷(ree:/rde:) 제거
- 채널 완료 판단: 종료 패킷(ree:) 제거, reb: 수신 시 채널 완료 (100샘플 기준 단일 패킷)
- 전체 완료는 기존과 동일하게 raa:로 판단
2026-03-30 16:37:41 +09:00
jhChun 689ad29aa6 BLE 명령 수신 시 Connection interval 재협상 요청 추가
- Windows BLE 스택이 장시간 연결 시 CI를 수초까지 늘려 응답이 지연되는 문제 대응
- 30초에 1회 20~75ms로 돌려달라고 요청
2026-03-30 15:27:22 +09:00
jhChun aaf22d942a BLE 연결 실패 발생 시 추적을 위한 RTT 로그 추가
- 0: IMU 값 송신(msp)- 1: 배터리 SAADC 시작- 2: 배터리 콜백 완료(battery_event_handler)- 3: 배터리 대기 루프 탈출- 4: IMU 읽기 완료- 5: Piezo RX/TX Active- 6: 온도 SAADC 시작- 7: 온도 콜백 완료(tmp235_voltage_handler)- 8: 온도 대기 루프 탈출- 9: rbb: 패킷 전송- 10: Piezo 캡처 시작(maa_async_start)- 11: 전체 종료(mbb)
2026-03-30 15:22:59 +09:00
jhChun 91e294da5a 2M PHY 능동 요청 추가
- BLE 5.0 2M PHY는 심볼 레이트를 1Msps -> 2Msps로 높여 동일 패킷을 절반 시간에 전송- 연결 즉시 Peripheral에서 2M PHY를 능동 요청- 2M PHY 미지원 Central인 경우 1M 유지
2026-03-30 14:49:34 +09:00
jhChun 856cf084b2 GAP Event Length 확장(6 -> 24) 및 RAM 주소 조정
- GAP Event Length: BLE 통신 시 Connection interval 마다 Central과 Peripheral이 데이터를 주고 받을 수 있는 시간 슬롯으로 단위는 1.25ms
- SoftDevice RAM 사용량 증가로 시작 주소 조정(0x20002AF8 → 0x20002C00)
2026-03-30 14:48:32 +09:00
jhChun 9e48c084b4 SAADC 안정성 개선: 버퍼/해제 순서 수정
- 배터리 SAADC 더블 버퍼 -> 싱글 버퍼(1회 측정 후 해제이므로 불필요)
- 콜백 내 buffer_convert 제거(해제 직전 다음 버퍼 등록 제거)
- uninit 순서 변경: channel_uninit -> uninit(채널 먼저 해제)
- 5초마다 배터리를 측정하는 battery_loop에 info4(전체 측정) 체크 추가(전체 측정 중 SAADC 동시 init 충돌 방지)
2026-03-30 12:10:53 +09:00
jhChun a9fa050bb0 SAADC 우선순위 변경
- NRFX_SAADC_CONFIG_IRQ_PRIORITY = 0 -> 6
- C:\jhChun\VesiScan-Basic_jhChun_new\components\libraries\util\app_util_platform.h
- 위 경로에 Cortex-M4(nRF52840) 우선순위 배분표가 정의되어 있음
- 0, 1, 4는 SoftDevice 전용, 앱 사용 가능한 우선순위는 2, 3, 5, 6(권장), 7
- 권장되는 우선순위인 6위로 변경
- SAADC 해상도 10 -> 12bit 변경 및 콜백 내 연산 int -> float 변경으로 ISR 처리 시간이 길어지고, SoftDevice와 충돌 가능성이 높아지는 것으로 추측
- 충돌이 발생하는 경우 콜백 지연 또는 누락
2026-03-30 11:41:59 +09:00
jhChun de9774a7ce 양산 모드인 경우 BLE 보안 실패 처리 개선
- PM 핸들러 중복 호출 제거
- conn_sec_failed 재페어링 로직 일원화
- NRF_ERROR_BUSY 크래시 수정: 보안 실패 시 보드 리셋되는 현상 개선
2026-03-27 16:47:46 +09:00
jhChun 3455bc2b38 DEFAULT_PASSKEY 매크로 통합 + advertising_start 조건 변경
- 본딩 삭제가 실행되지 않는 버그 개선
2026-03-27 16:17:09 +09:00
jhChun 153c4a027f 배터리, 온도 SAADC 해상도 12bit 전환 및 float 연산 적용
- SAADC 해상도 10bit -> 12bit
- int 연산 -> float 연산 -> BLE 전송 시 int
2026-03-27 16:12:31 +09:00
jhChun 7459fffc20 배터리 전압 TACQ 조정 및 콜백 대기 조건 변경
- 배터리, 온도 모두 오버샘플링 X4 및 TACQ=10us 적용
- 배터리 측정 시 SAADC 콜백 완료 최대 100ms 대기, 콜백 오면 즉시 탈출(온도 측정 시와 동일)
- 전체 측정 시 SAADC 점유 및 충돌 방지
2026-03-27 13:54:39 +09:00
jhChun 4c2948eb43 Firmware 버전 식별 코드 정의를 main.h로 이동
- parser.c: DR_DEVICE_VERSION 제거
2026-03-27 10:52:26 +09:00
jhChun 89cef54f86 cpd_eraseALL.bat 추가: 전체 erase 개발용 플래싱 스크립트 2026-03-27 10:50:56 +09:00
jhChun 923f29db82 Firmware 버전 식별 코드 정의를 main.h로 이동
- parser.c: DR_DEVICE_VERSION 제거
2026-03-27 10:05:46 +09:00
jhChun 92ebe8ebb4 기본 버전 정보 및 패스키 기본값을 fstorage.h 매크로로 통합 2026-03-27 09:43:50 +09:00
jhChun 5db067f483 FDS 영역 보존 erase 옵션 추가
- cpd.bat: 기존 erase 옵션 설정 x, DEFAULT=ERASE_ALL(칩 전체 삭제)
2026-03-27 08:53:27 +09:00
jhChun f666892987 배터리, 온도 전압 오버샘플링 및 TACQ 조정
- 배터리: 샘플링 4, TACQ 20us
- 온도: 샘플링 4, TACQ 10us(기본값)
2026-03-26 16:59:38 +09:00
jhChun 379bf9c294 배터리 전압 SAADC 오버샘플링 X4
- battery_saadc.c: 기존 1회 샘플링 -> 4회 샘플링 및 평균
- 배터리 전압 ADC 노이즈에 의한 측정값 흔들림 개선
2026-03-26 14:52:40 +09:00
jhChun 6fbd30244f 압력센서 관련 코드 정리
- battery_saadc.c/.h: 압력센서 미탑재로 관련 코드 제거
2026-03-26 12:23:40 +09:00
jhChun b820589883 GPIO 초기화 POWER_HOLD(P0.8) 중복 설정 제거
- main.c: power_hold_init()에서 전원버튼(P0.8) 설정 후 gpio_init()에서 중복 설정
- gpio_init()에서 중복 설정 부분 제거
2026-03-26 11:42:26 +09:00
jhChun af7afff117 EEPROM 레거시 코드 정리
- EEP_WP, eeprom_control, power_gpio_init 삭제
2026-03-26 11:39:12 +09:00
jhChun dd3bce4f94 부트 로그 수정 2026-03-26 11:24:13 +09:00
jhChun 9580bffa65 DEBUG_MINIMAL_BOOT 삭제
- main.c: DEBUG_MINIMAL_BOOT 삭제 및 분기문 정리
- main.c: minimal_gpio_init() + full_gpio_init() → gpio_init() 하나로 통합
- main.c: icm42670_init() 분기 제거, advertising_start()만 남김
- power_control.c: SW I2C/센서 전원 시퀀스 제거, p_order = 2로 즉시 완료
2026-03-26 11:12:26 +09:00
jhChun e461f59a5f 현재 IMU 센서는 폴링 방식 적용
- app_raw_main.c: 인터럽트 방식 적용을 위한 레거시 파일
2026-03-26 11:06:52 +09:00
jhChun ed3a8d7acd BLE TX 재시도 대기 시간 단축
- main.c: dr_binary_tx_safe 함수 nrf_delay_ms(5) × 최대 100회 -> nrf_delay_ms(2) × 최대 20회
- 전력 소모 방지
2026-03-25 16:47:48 +09:00
jhChun 7b8103a530 디버그 로그 통일(UART -> RTT)
- app_raw.c: printf() -> DBG_PRINTF(RTT)
- 전력 소모 방지: CPU 문자열 포맷팅 실행 제거
2026-03-25 16:07:28 +09:00
26 changed files with 1298 additions and 1586 deletions
-569
View File
@@ -1,569 +0,0 @@
# VesiScan-Basic 코드 리뷰 현황 보고서
작성일: 2026-03-17
대상: VesiScan BASIC 펌웨어 전체 (nRF52840 + S140 SoftDevice)
기준 문서: CODE_REVIEW.pdf (2026-03-15)
현재 빌드: DEBUG_MINIMAL_BOOT=1, BLE_DEV_MODE=1
## 목차
1. 삭제 대상: 미사용 하드웨어 드라이버
2. 삭제 대상: 미사용 코드
3. DEBUG_MINIMAL_BOOT 조건부 컴파일 정리
4. Keil 프로젝트 파일 정리
5. 버그 및 오류 수정
6. 저전력 최적화
7. 코드 품질 개선
8. 작업 우선순위 요약
---
## 1. 삭제 대상: 미사용 하드웨어 드라이버
### 1.1 회로 기반 분석
변경 없음. VB0HW0100 회로 기준 IC 목록은 CODE_REVIEW.pdf 1.1절과 동일.
### 1.2 회로에 없는 IC의 드라이버 (삭제 대상)
| 파일 | IC/기능 | 판정 | 현재 상태 |
|------|--------|------|----------|
| ad5272_i2c.c/h | AD5272 디지털 포텐시오미터 | 삭제 | **완료** |
| ada2200_spi.c/h | ADA2200 동기 복조기 | 삭제 | **완료** |
| mcp4725_i2c.c/h | MCP4725 DAC | 삭제 | **완료** |
| mcp4725_adc.c/h | MCP4725 ADC 피드백 | 삭제 | **완료** |
| ir_i2c.c/h | IR LED 센서 (0x50~0x57) | 삭제 | **완료** |
| LED_Parse.c/h | LED ROM 파싱 | 삭제 | **완료** |
### 1.3 기능적으로 미사용인 드라이버 (삭제 대상)
| 파일 | 기능 | 판정 | 현재 상태 |
|------|------|------|----------|
| measurements.c/h | 레거시 측정 루프 | 삭제 | **완료** |
| measurements_20.c/h | 20-샘플 측정 변형 | 삭제 | **완료** |
| meas_pd_imm.c/h | 즉시 PD 측정 | 삭제 | **완료** |
| meas_pd_buff.c/h | 버퍼드 PD 측정 | 삭제 | **완료** |
| meas_pd_voltage_simple.c/h | 단순 전압 측정 | 삭제 | **완료** |
| meas_pd_voltage_half.c/h | 반파 전압 측정 | 삭제 | **완료** |
| meas_pd_voltage_full.c/h | 전파 전압 측정 | 삭제 | **완료** |
| meas_pd_voltage_custom.c/h | 커스텀 전압 측정 | 삭제 | **완료** |
| full_agc.c/h | 자동 이득 제어 | 삭제 | **완료** |
| debuf_print.h | 오타 파일 | 삭제 | **완료** |
### 1.4 삭제 시 의존성 영향
cmd_parse.h에서 제거할 include 목록:
| include | 현재 상태 |
|---------|----------|
| #include "ir_i2c.h" | **완료** (제거됨) |
| #include "ad5272_i2c.h" | **완료** (제거됨) |
| #include "ada2200_spi.h" | **완료** (제거됨) |
| #include "mcp4725_i2c.h" | **완료** (제거됨) |
| #include "measurements.h" | **완료** (제거됨) |
| #include "meas_pd_voltage_simple.h" | **완료** (제거됨) |
| #include "meas_pd_voltage_half.h" | **완료** (제거됨) |
| #include "meas_pd_voltage_full.h" | **완료** (제거됨) |
| #include "full_agc.h" | **완료** (제거됨) |
| #include "meas_pd_imm.h" | **완료** (제거됨) |
### 1.5 cat_interface.c (EEPROM 드라이버) 삭제 분석
#### 1.5.1 결론: EEPROM 삭제 완료, TWI는 i2c_manager.c로 통합
현재 상태: **완료** - jhChun 26.03.16
cat_interface.c 삭제(EEPROM/AES 코드 전체 삭제). TWI 인스턴스(m_twi) 및 초기화/해제 함수는 i2c_manager.c로 통합.
#### 1.5.2 cat_interface.c가 제공하는 것
| 기능 | 판정 | 현재 상태 |
|------|------|----------|
| m_eeprom TWI 인스턴스 | i2c_manager.c로 이동, m_twi로 리네이밍 | **완료** |
| eeprom_initialize() / uninitialize() | i2c_manager.c로 이동, twi_initialize/uninitialize로 리네이밍 (static) | **완료** |
| eeprom_read/write_byte/word/page/bytes | 삭제 | **완료** |
| eeprom_read/write_uint16_array | 삭제 | **완료** |
| eeprom_read/write_uint32 | 삭제 | **완료** |
| eeprom_write_encrypted() / read_decrypted() | 삭제 | **완료** |
| encrypt_data() / decrypt_data() | 삭제 | **완료** |
| aes_key[16], aes_iv[16] | 삭제 | **완료** |
#### 1.5.3 권장 작업
| # | 작업 | 현재 상태 |
|---|------|----------|
| 1 | TWI 인스턴스/init/uninit → i2c_manager.c로 통합 | **완료** |
| 2 | m_eeprom → m_twi 리네이밍 | **완료** |
| 3 | EEPROM read/write 전체 삭제, AES 전체 삭제 | **완료** |
| 4 | cmd_parse.c EEPROM 호출 -> FDS 전환 | **완료** |
| 5 | main.c 버튼 롱프레스 리셋: EEPROM -> FDS | **완료** |
| 6 | power_control.c: cat_interface.h include 제거 | **완료** |
#### 1.5.4 cmd_parse.c EEPROM -> FDS 전환 목록
| EEPROM 호출 | 위치 | FDS 대체 방법 | 현재 상태 |
|------------|------|------------|----------|
| eeprom_init_values_read() | cmd_parse.c | 삭제 (FDS config_load 대체) | **완료** |
| eeprom_write_byte(0x0070, m_pd_adc_cnt) | cmd_parse.c | m_config.pd_adc_cnt = val; config_save(); | **완료** |
| eeprom_write_word(0x0080, m_pd_delay_us) | cmd_parse.c | m_config.pd_delay_us = val; config_save(); | **완료** |
| eeprom_write_byte(0x0060, bond_data_delete) | cmd_parse.c | m_config.bond_data_delete = val; config_save(); | **완료** |
| eeprom_write_byte(0x0065, m_reset_status) | cmd_parse.c | m_config.reset_status = val; config_save(); | **완료** |
| eeprom_write_encrypted(0x0020, passkey, 6) | cmd_parse.c | memcpy(m_config.static_passkey, val, 6); config_save(); | **완료** |
| eeprom_read_decrypted(0x0020, passkey, 6) | cmd_parse.c | memcpy(buf, m_config.static_passkey, 6); | **완료** |
| eeprom_write_uint32(0x0090, m_life_cycle) | cmd_parse.c | m_config.life_cycle = val; config_save(); (FDS 필드 추가 완료) | **완료** |
| eeprom_read_uint32(0x0090, &m_life_cycle) | cmd_parse.c | m_life_cycle = m_config.life_cycle; | **완료** |
| eeprom_write/read_uint16_array(0x0480, ...) | cmd_parse.c | AGC gain — HW 제거됨, 명령 비활성화 | **완료** |
---
## 2. 삭제 대상: 미사용 코드
### 2.1 AES 암호화 (cat_interface.c)
판정 변경: 전체 삭제
| 항목 | 위치 | 판정 | 현재 상태 |
|------|------|------|----------|
| aes_key[16] 하드코딩 | cat_interface.c | 삭제 | **완료** |
| aes_iv[16] (고정 IV = 0) | cat_interface.c | 삭제 | **완료** |
| encrypt_data() | cat_interface.c | 삭제 | **완료** |
| decrypt_data() | cat_interface.c | 삭제 | **완료** |
| eeprom_write_encrypted() | cat_interface.c | 삭제 | **완료** |
| eeprom_read_decrypted() | cat_interface.c | 삭제 | **완료** |
### 2.2 Serial Number / Passkey / HW Version 중복 선언 분석
| 데이터 | 전역 변수 (런타임) | FDS (내장 Flash) | EEPROM (외부, 제거됨) | 현재 상태 |
|--------|----------------|----------------|-------------------|----------|
| Serial Number | SERIAL_NO[12] | m_config.serial_no[12] | ~~addr 0x0030~~ | **EEPROM 경로 삭제 완료** |
| Passkey | m_static_passkey[6] | m_config.static_passkey[6] | ~~addr 0x0020~~ | **EEPROM 경로 삭제 완료** |
| HW Version | HW_NO[12] | m_config.hw_no[12] | ~~addr 0x0010~~ | **HW_NO 미삭제** |
권장 작업 현황:
| # | 작업 | 현재 상태 |
|---|------|----------|
| 1 | EEPROM 경로 삭제 -> eeprom_init_values_read() 삭제 | **완료** |
| 2 | HW_NO[12] 삭제 -> m_config.hw_no 직접 사용 | **미완료** |
| 3 | SERIAL_NO, m_static_passkey 유지 (BLE 스택 필요) | 해당없음 |
| 4 | 데이터 흐름 단순화: FDS -> 전역변수 -> BLE 스택 | **미완료** |
### 2.3 미사용 전역 변수
| 변수 | 위치 | 판정 | 현재 상태 |
|------|------|------|----------|
| roles_str[] | main.c:189 | 삭제 | **미완료** |
| m_encrypted_text[16] | main.c:194 | 삭제 | **미완료** |
| m_encrypted_text2[16] | main.c:195 | 삭제 | **미완료** |
| m_decrypted_text[16] | main.c:196 | 삭제 | **미완료** |
| DEVICE_NAME | cmd_parse.c | 삭제 | **완료** (cmd_parse.c 삭제됨) |
| HW_NO[12] | cmd_parse.c | 삭제 | **완료** (cmd_parse.c 삭제됨) |
| AES_KEY_SIZE | main.c:154 | 삭제 | **미완료** |
| AES_BLOCK_SIZE | main.c:155 | 삭제 | **미완료** |
| suppress_unused_warnings() | main.c:239 | 삭제 | **미완료** |
| c_addr[6] | main.c:187 | 삭제 | **미완료** |
| led_pd_dac_v[LED_NUM] | 참조 다수 | 삭제 | 확인 필요 |
### 2.3.1 cmd_parse.c → parser.c 통합 현황
현재 구조: main.c → `dr_cmd_parser()` [parser.c] (cmd_parse.c 삭제 완료)
#### parser.c 명령어 테이블 (g_cmd_table)
| 태그 | 핸들러 | enabled | 기능 | 비고 |
|------|--------|---------|------|------|
| `cmd?` | Cmd_cmd | true | 핀 테스트 (sudo) | parser.c 신규 |
| `mpa?` | Cmd_mpa | true | Piezo 활성화 | parser.c 신규 |
| `mpb?` | Cmd_mpb | true | Piezo 비활성화 | parser.c 신규 |
| `mpc?` | Cmd_mpc | true | Piezo Cycles 제어 | parser.c 신규 |
| `mdc?` | Cmd_mdc | true | Piezo burst + Echo (12-bit) | parser.c 신규 |
| `mec?` | Cmd_mec | true | Piezo burst + Echo (16-bit) | parser.c 신규 |
| `maa?` | Cmd_maa | true | 8채널 전체 캡처 | parser.c 신규 |
| `msp?` | Cmd_msp | true | IMU 6축 raw data | parser.c 신규 |
| `mwh?` | Cmd_mwh | true | HW번호 쓰기 (FDS) | shz? 대체 |
| `mws?` | Cmd_mws | true | 시리얼번호 쓰기 (FDS) | ssz? 대체 |
| `mrh?` | Cmd_mrh | true | HW번호 읽기 (FDS) | siz? 대체 |
| `mrs?` | Cmd_mrs | true | 시리얼번호 읽기 (FDS) | srz? 대체 |
| `mpz?` | Cmd_mpz | true | 패스키 쓰기 (FDS) | **신규 추가** (spz? 대체) |
| `mqz?` | Cmd_mqz | true | 패스키 읽기 | **신규 추가** (sqz? 대체) |
| `mxz?` | Cmd_mxz | true | life_cycle 쓰기 (FDS) | **신규 추가** (sxz? 대체) |
| `myz?` | Cmd_myz | true | life_cycle 읽기 | **신규 추가** (syz? 대체) |
| `mta?` | Cmd_mta | true | 디바이스 상태 | sta? 리네이밍 |
| `sta?` | Cmd_sta | true | 디바이스 활성화/슬립 | 레거시 호환 |
| `str?` | Cmd_str | false | 디바이스 상태 읽기 | 미완성 (TODO) |
| `mcj?` | Cmd_mcj | true | PD-ADC 풀 측정 | parser.c 신규 |
| `scj?` | Cmd_mcj | true | mcj 호환 | 레거시 호환 |
| `sej?` | Cmd_sej | true | PD-ADC 측정 | 레거시 호환 |
| `ssj?` | Cmd_ssj | false | PD-ADC 측정 | 비활성 |
| `msn?` | Cmd_msn | true | 배터리 측정 | ssn? 리네이밍 |
| `ssn?` | Cmd_msn | true | 배터리 측정 | 레거시 호환 |
| `spn?` | Cmd_spn | false | 압력 센서 측정 | 비활성 |
| `sso?` | Cmd_sso | false | 온도 센서 측정 | 비활성 |
| `ssp?` | Cmd_ssp | true | 모션 센서 raw data | 레거시 호환 |
| `ssq?` | Cmd_ssq | false | 전원 OFF | 비활성 |
| `ssr?` | Cmd_ssr | false | 본딩 삭제+리셋 | 비활성 |
| `sss?` | Cmd_sss | false | 디바이스 리셋 | 비활성 |
| `sst?` | Cmd_sst | false | Ready 응답 | 비활성 |
| `mfv?` | Cmd_mfv | true | 펌웨어 버전 읽기 | ssv? 대체 |
#### cmd_parse.c 레거시 명령어 → parser.c 이전 완료
| 태그 | 기능 | 비고 |
|------|------|------|
| `spz?` | 패스키 쓰기 | → parser.c `mpz?`로 대체 완료 |
| `sqz?` | 패스키 읽기 | → parser.c `mqz?`로 대체 완료 |
| `sxz?` | life_cycle 쓰기 | → parser.c `mxz?`로 대체 완료 |
| `syz?` | life_cycle 읽기 | → parser.c `myz?`로 대체 완료 |
| `sez?` | AGC gain (stub) | cmd_parse.c와 함께 삭제 |
| `sfz?` | AGC gain (stub) | cmd_parse.c와 함께 삭제 |
| `ssv?` | 펌웨어 버전 | → parser.c `mfv?`로 대체됨 |
| `ssz?` | 시리얼번호 쓰기 | → parser.c `mws?`로 대체됨 |
| `srz?` | 시리얼번호 읽기 | → parser.c `mrs?`로 대체됨 |
| `siz?` | HW번호 읽기 | → parser.c `mrh?`로 대체됨 |
| `shz?` | HW번호 쓰기 | → parser.c `mwh?`로 대체됨 |
#### 통합 작업 현황
| # | 작업 | 현재 상태 |
|---|------|----------|
| 1 | parser.c에 누락 명령어 추가 (mpz/mqz/mxz/myz) | **완료** |
| 2 | received_command_process() 래퍼를 main.c로 이동 | **완료** (cmd_parse.c 삭제됨) |
| 3 | 전역 변수 (SERIAL_NO, m_static_passkey 등) main.c로 이동 | **완료** (cmd_parse.c 삭제됨) |
| 4 | cmd_parse.h include 정리 (7개 파일) | **완료** (cmd_parse.h 삭제됨) |
| 5 | cmd_parse.c / cmd_parse.h 삭제 | **완료** |
| 6 | Keil 프로젝트 파일 업데이트 | **완료** |
### 2.4 주석 처리된 코드
| 위치 | 내용 | 판정 | 현재 상태 |
|------|------|------|----------|
| main_timer.c:37-88 | FEATURE_DETAIL_VALUE_FULL 주석 블록 | 삭제 | **미완료** |
| cmd_parse.c:282-317 | serial_to_passkey_hash, test_eeprom_page_rw | 삭제 | **완료** (cmd_parse.c 삭제됨) |
| cmd_parse.c:87-88 | pd_adc_half/full_a_start extern | 삭제 | **완료** (cmd_parse.c 삭제됨) |
| battery_saadc.c:22 | //#include "fstorage.h" | 삭제 | **미완료** |
---
## 3. DEBUG_MINIMAL_BOOT 조건부 컴파일 정리
현재 DEBUG_MINIMAL_BOOT=1로 MINIMAL 모드만 사용. Full boot 경로는 실행되지 않음.
### 3.1 제거 대상 #if 블록
| 파일 | 블록 | 판정 | 현재 상태 |
|------|------|------|----------|
| main.c:280-281 | #if !DEBUG_MINIMAL_BOOT full_gpio_init() | 삭제 (함수 전체) | **미완료** |
| main.c:354-355 | #if !DEBUG_MINIMAL_BOOT load_eeprom_config() | 삭제 (함수 전체, EEPROM 제거 완료) | **완료** (함수 삭제됨) |
| main.c:1437-1439 | Full boot 경로 (ada2200_init, mcp4725_init 등) | 삭제 | **미완료** |
| main.c GPIO init 분기 | GPIO init 분기 | MINIMAL 코드만 유지 | **미완료** |
| power_control.c | Full boot power sequence | 삭제 (minimal 경로만 유지) | **미완료** |
### 3.3 DEBUG_MINIMAL_BOOT 제거 시 함께 정리
| 항목 | 현재 상태 |
|------|----------|
| #define DEBUG_MINIMAL_BOOT 1 삭제 (main.c:96) | **미완료** (여전히 활성) |
| 모든 #if 조건 제거, MINIMAL 코드만 유지 | **미완료** |
| BLE_DEV_MODE define 유지 | 해당없음 |
---
## 4. Keil 프로젝트 파일 정리
### 4.1 삭제할 소스 참조 (uvprojx)
현재 상태: **정리 불필요**
ble_app_bladder_patch_s140.uvprojx 확인 결과, 아래 삭제 대상 파일들은 이미 프로젝트에 등록되어 있지 않음:
- ad5272_i2c.c, ada2200_spi.c, mcp4725_i2c.c, mcp4725_adc.c
- ir_i2c.c, LED_Parse.c, measurements.c, measurements_20.c
- full_agc.c, meas_pd_voltage_custom.c, cat_interface.c
uvprojx 수정 작업 없음.
---
## 5. 버그 및 오류 수정
### 5.1 심각도 높음 (HIGH)
#### B1: BLE TX 큐 풀 시 APP_ERROR_CHECK로 인한 시스템 먹통 현상
파일: main.c (구 binary_tx_handler)
현재 상태: **완료**
- `binary_tx_handler()` 함수 본체 삭제
- 모든 호출처를 `dr_binary_tx_safe()`로 교체 완료
- parser.c (17곳)
- battery_saadc.c (2곳)
- app_raw.c (2곳)
- tmp235_q1.c (1곳)
- cmd_parse.c (22곳) → 이후 cmd_parse.c 삭제됨
- main.h 선언 제거, dr_util.c/dr_adc121s051.c의 옛날 extern 선언 제거
- `dr_binary_tx_safe()`는 NRF_ERROR_RESOURCES 시 최대 100회 재시도 후 패킷 드롭, 연결 에러 시 graceful 리턴
- `data_tx_handler()`의 APP_ERROR_CHECK도 DBG_PRINTF로 교체 — 예상치 못한 에러 시에도 패킷 드롭 후 정상 동작 유지
#### B2: nrf_delay_ms(10) 블로킹 대기 (BLE TX)
파일: main.c:1221-1228
현재 상태: **완료**
조건문 로직이 정상 수정됨. NRF_ERROR_RESOURCES가 아닌 경우에만 delay 실행.
#### B3: extern + 초기화 동시 사용
파일: cmd_parse.c (삭제됨)
현재 상태: **완료**
`extern int8_t pd_adc_counts[4] = {8, 16, 24, 32};` 선언이 제거됨. 이후 cmd_parse.c 삭제.
#### B4: memset/memcpy 버퍼 크기 불일치 및 매직 넘버
파일: main.c config_apply_to_globals()
현재 상태: **완료**
- memset 오버런 수정: 하드코딩 `16``sizeof(SERIAL_NO)`, `PASSKEY_LENGTH` 사용
- memcpy 매직 넘버 수정: 하드코딩 `12``sizeof(m_config.hw_no)`, `sizeof(m_config.serial_no)`, `sizeof(SERIAL_NO)` 등으로 교체
- m_static_passkey memset/memcpy: `PASSKEY_LENGTH` define 활용
스택/힙 오버플로우로 인한 메모리 손상 가능성이 있는 심각한 버그.
#### B5: maa? 명령 err=2 (MAA_NUM_SAMPLES 초과)
파일: parser.c, dr_adc121s051.c
현재 상태: **완료** — jhChun 26.03.17
MAA_NUM_SAMPLES(140)이 DR_ADC_ECHO_SAMPLES_MAX(100)보다 커서 maa_async_start()에서 무조건 DR_ADC_ERR_INVALID_PARAM(err=2) 반환. MAA_NUM_SAMPLES를 100으로 변경하여 해결.
#### B5-2: mec/maa 명령 시 Piezo 전원 미관리
파일: parser.c, dr_adc121s051.c
현재 상태: **완료** — jhChun 26.03.17
- mec?, maa? 수신 시 `dr_piezo_is_power_on()` 확인 후 꺼져있으면 자동 `dr_piezo_system_init()` (Active)
- mec: 응답 송신 후 즉시 `dr_piezo_power_off()` (Sleep)
- maa: 비동기 완료 콜백에서 `dr_piezo_power_off()` (Sleep)
- `dr_piezo_is_power_on()` 함수 신규 추가 (dr_piezo.c/h)
#### B5-3: Cmd_mpa dr_piezo_power_on() 중복 호출
파일: parser.c
현재 상태: **완료** — jhChun 26.03.17
`dr_piezo_power_on()` + `dr_piezo_system_init()` 둘 다 호출하고 있었으나, `dr_piezo_system_init()` 내부에 이미 `dr_piezo_power_on()`이 포함되어 있어 중복 제거.
### 5.2 심각도 중간 (MEDIUM)
#### B6: battery_event_handler에서 floating-point 연산
파일: battery_saadc.c:167
현재 상태: **미완료**
여전히 부동소수점 연산 사용 중:
```c
batt_lvl_in_milli_volt_1 = (batt_lvl_in_milli_volt_0) *1.42;
```
수정 필요:
```c
batt_lvl_in_milli_volt_1 = (batt_lvl_in_milli_volt_0 * 142) / 100;
```
#### B7: main_timer 단발 모드 문제
현재 상태: **해당없음** (의도적 설계로 판단)
#### B8: processing_start_tick 미사용
파일: cmd_parse.c:57
현재 상태: **완료** (cmd_parse.c 삭제됨)
#### B9: config_load() goto 기반 흐름
파일: fstorage.c:157-261
현재 상태: **미완료**
`goto cfg_load_start` 패턴이 여전히 존재. 3개의 goto 문과 2개의 레이블로 복잡한 흐름 유지 중. FDS 초기화 실패 시 무한 루프 가능성 여전.
### 5.3 심각도 낮음 (LOW)
#### B11: debuf_print.h 파일명 오타
현재 상태: **완료** (파일 삭제)
#### B12: 함수 중복 선언
파일: main.h:44-54
현재 상태: **미완료**
static 함수 프로토타입 6개가 헤더에 여전히 선언됨:
- advertising_start (FEATURE_SECURE_CONNECTION 내)
- t_power_off_timeout_handler
- power_control_handler
- main_s
- PM_s
- main_routine_handler
#### B13: LED_ALLOFF, PD_ALLOFF 매크로 정의 위치
현재 상태: 확인 필요
---
## 6. 저전력 최적화
### 6.1 현재 전력 구조 분석
변경 없음. CODE_REVIEW.pdf 6.1절과 동일.
### 6.2 개선 항목
#### P1: UART 비활성화 (가장 큰 절약 -- 예상 ~1mA 절감)
파일: main.c:1427
현재 상태: **미완료**
`uart_init()`이 무조건 호출됨. `#if BLE_DEV_MODE` 조건으로 감싸져 있지 않음.
수정 필요:
```c
#if BLE_DEV_MODE
uart_init();
#endif
```
#### P2: SEGGER RTT 비활성화 (프로덕션)
파일: debug_print.h
현재 상태: **미완료**
`ENABLE_PRINTF``1`로 하드코딩되어 있음. `BLE_DEV_MODE`와 연동되지 않음.
수정 필요:
```c
#if BLE_DEV_MODE
#define ENABLE_PRINTF 1
#else
#define ENABLE_PRINTF 0
#endif
```
#### P3: 배터리 타이머 주기 최적화
파일: battery_saadc.c:48
현재 상태: **미완료**
`BATTERY_LOOP_INTERVAL = 5000ms` 고정. 동적 조절 로직 없음.
#### P4: IMU 타이머 조건부 시작
파일: main.c BLE 이벤트 핸들러
현재 상태: **미완료**
BLE_GAP_EVT_CONNECTED/DISCONNECTED 핸들러에 IMU 타이머 start/stop 로직 없음. BLE 미연결 시에도 IMU를 읽을 수 있는 상태.
#### P5: main_loop() 타이머 폴링 효율화
현재 상태: **미완료**
플래그 기반 폴링 패턴 그대로 유지 중.
#### P6: NRF_LOG 비활성화 고려
현재 상태: **미완료** (확인 필요)
### 6.3 예상 절감 효과
| 최적화 | 예상 절감 | 난이도 | 현재 상태 |
|--------|----------|--------|----------|
| P1: UART 비활성화 | ~1mA | 쉬움 | **미완료** |
| P2: RTT 비활성화 | ~0.1-0.5mA | 쉬움 | **미완료** |
| P4: IMU 타이머 조건부 시작 | ~41uA (미연결 시) | 쉬움 | **미완료** |
| P3: 배터리 타이머 동적 | ~5uA | 보통 | **미완료** |
| P5-A: 단발->직접호출 | 응답 개선 | 쉬움 | **미완료** |
| P5-B: app_scheduler | 코드 품질 + 확장성 | 보통 | **미완료** |
| P5-C: IMU FIFO+인터럽트 | 연속측정 시 ~50uA | 어려움 | **미완료** |
| P6: NRF_LOG 비활성화 | ~10uA | 쉬움 | **미완료** |
---
## 7. 코드 품질 개선
### 7.1 구조 개선
| # | 항목 | 현재 상태 |
|---|------|----------|
| Q1 | main.h에서 static 함수 선언 제거 | **미완료** (6개 잔존) |
| Q2 | cat_interface.c -> i2c_manager.c로 TWI 통합 | **완료** |
| Q3 | extern 변수 과다 사용 | **미완료** (30개 이상) |
| Q4 | main_timer.c main_loop() 플래그 기반 폴링 | **미완료** |
| Q5 | 매직 넘버 제거 | **미완료** |
### 7.2 코딩 스타일
변경 없음. CODE_REVIEW.pdf 7.2절과 동일.
---
## 8. 작업 우선순위 요약
### 즉시 수정 (안전/안정성)
| # | 항목 | 심각도 | 현재 상태 |
|---|------|--------|----------|
| 1 | B4: memset/memcpy 버퍼 크기 불일치 및 매직 넘버 | CRITICAL | **완료** |
| 2 | B1: BLE TX 큐 풀 시 시스템 먹통 (binary_tx_handler → dr_binary_tx_safe 교체) | HIGH | **완료** |
| 3 | B2: BLE TX 에러 핸들링 로직 반전 | HIGH | **완료** |
| 4 | B3: extern + 초기화 동시 사용 | HIGH | **완료** |
| 5 | B5: maa? err=2 (MAA_NUM_SAMPLES > DR_ADC_ECHO_SAMPLES_MAX) | HIGH | **완료** |
| 6 | B5-2: mec/maa Piezo 자동 전원 관리 | MEDIUM | **완료** |
| 7 | B5-3: Cmd_mpa dr_piezo_power_on 중복 호출 | LOW | **완료** |
### 1차 정리 (미사용 코드/EEPROM 삭제)
| # | 항목 | 현재 상태 |
|---|------|----------|
| 5 | 미사용 HW 드라이버 삭제 (1.2절) | **완료** |
| 6 | cat_interface.c EEPROM 기능 삭제 + TWI를 i2c_manager.c로 통합 (1.5절) | **완료** |
| 7 | cmd_parse.c EEPROM 호출 -> FDS 전환 (1.5.4절) | **완료** |
| 8 | cmd_parse.c → parser.c 통합 (2.3.1절) | **완료** |
| 9 | 미사용 전역변수 + HW_NO 삭제 (2.2-2.3절) | **미완료** |
| 10 | DEBUG_MINIMAL_BOOT 조건 제거 (3절) | **미완료** |
| 11 | Keil uvprojx 소스 참조 제거 (4절) | 확인 필요 |
| 12 | 주석 처리된 코드 삭제 (2.4절) | **미완료** |
### 2차 정리 (저전력 최적화)
| # | 항목 | 예상 절감 | 현재 상태 |
|---|------|----------|----------|
| 12 | P1: UART 비활성화 | ~1mA | **미완료** |
| 13 | P2: RTT 비활성화 | ~0.1-0.5mA | **미완료** |
| 14 | P4: IMU 타이머 BLE 연결 시에만 동작 | ~41uA | **미완료** |
| 15 | P5-A: main_loop 단발 이벤트 직접 호출로 전환 | 응답 개선 | **미완료** |
| 16 | P5-B: app_scheduler 도입 (선택) | 코드 품질 | **미완료** |
| 17 | P3: 배터리 타이머 동적 조절 | ~5uA | **미완료** |
### 3차 정리 (코드 품질)
| # | 항목 | 현재 상태 |
|---|------|----------|
| 18 | Q1: main.h static 함수 제거 | **미완료** |
| 19 | Q5: 매직 넘버 define화 | **미완료** |
| 20 | B9: config_load() goto 제거 | **미완료** |
Binary file not shown.
Binary file not shown.
Binary file not shown.
+34 -4
View File
@@ -95,9 +95,16 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
{
ret_code_t err_code;
// DEV 모드: 보안 실패 이벤트는 SDK 핸들러에 전달하지 않음 (disconnect 방지)
if (m_state.dev_mode && p_evt->evt_id == PM_EVT_CONN_SEC_FAILED) {
DBG_PRINTF("Security failed: error=%d\r\n",
p_evt->params.conn_sec_failed.error);
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
return;
}
// Call standard handlers (required)
pm_handler_on_pm_evt(p_evt);
pm_handler_disconnect_on_sec_failure(p_evt);
pm_handler_flash_clean(p_evt);
// Handle events
@@ -114,19 +121,42 @@ void ble_security_quick_pm_handler(pm_evt_t const *p_evt)
DBG_PRINTF("Security failed: error=%d\r\n",
p_evt->params.conn_sec_failed.error);
// Auto-retry if key missing
if (m_state.dev_mode) {
// DEV 모드: 보안 실패 무시 — 연결 유지
DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n");
break;
}
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) {
// Key missing: 재페어링 시도, 실패 시 disconnect로 폴백
err_code = pm_conn_secure(p_evt->conn_handle, true);
if (err_code != NRF_ERROR_INVALID_STATE) {
if (err_code != NRF_ERROR_INVALID_STATE &&
err_code != NRF_ERROR_BUSY &&
err_code != BLE_ERROR_INVALID_CONN_HANDLE) {
APP_ERROR_CHECK(err_code);
}
if (err_code != NRF_SUCCESS) {
// 재페어링 불가 → disconnect
pm_handler_disconnect_on_sec_failure(p_evt);
}
} else {
// 기타 보안 실패 → bond 삭제 후 재페어링 시도
pm_peer_id_t peer_id;
if (pm_peer_id_get(p_evt->conn_handle, &peer_id) == NRF_SUCCESS
&& peer_id != PM_PEER_ID_INVALID) {
pm_peer_delete(peer_id);
}
err_code = pm_conn_secure(p_evt->conn_handle, true);
if (err_code != NRF_SUCCESS) {
pm_handler_disconnect_on_sec_failure(p_evt);
}
}
break;
case PM_EVT_CONN_SEC_CONFIG_REQ:
{
pm_conn_sec_config_t config = {
.allow_repairing = m_state.dev_mode
.allow_repairing = true
};
pm_conn_sec_config_reply(p_evt->conn_handle, &config);
}
+127 -245
View File
@@ -517,17 +517,12 @@ extern void dr_sd_delay_ms(uint32_t ms);
extern void dr_piezo_burst_sw(uint8_t cycles);
/* BLE packet constants */
#define BLE_MTU_SIZE 240
#define BLE_FIRST_HEADER_LEN 14 /* "rec:" + header */
#define BLE_CONT_HEADER_LEN 6 /* "red:" + packet_index */
#define BLE_FIRST_DATA_LEN (BLE_MTU_SIZE - BLE_FIRST_HEADER_LEN)
#define BLE_CONT_DATA_LEN (BLE_MTU_SIZE - BLE_CONT_HEADER_LEN)
#define BLE_MTU_SIZE 244 /* ATT MTU 247 - 3 (ATT header) = 244 */
#define BLE_REB_HEADER_LEN 6 /* "reb:" tag(4) + num_samples(2) */
#define BLE_RED_HEADER_LEN 6 /* "red:" tag(4) + pkt_idx(2) */
#define BLE_REB_DATA_LEN (BLE_MTU_SIZE - BLE_REB_HEADER_LEN) /* 238 bytes = 119 samples */
#define BLE_RED_DATA_LEN (BLE_MTU_SIZE - BLE_RED_HEADER_LEN) /* 238 bytes = 119 samples */
#define BLE_PACKET_DELAY_MS 100 /* Inter-packet delay - allow BLE TX buffer to drain */
#define BLE_FIRST_PACKET_DELAY 5 /* Minimal delay */
/* Protocol version markers (OR'd with packet count in reb: header) */
#define MEC_VERSION_MARKER 0xD000 /* vD: raa only (no ree) - ree causes packet loss */
#define MAA_VERSION_MARKER 0xF000 /* vF: maa? hardcoded 8-channel debug */
/*==============================================================================
* PIEZO CHANNEL SELECTION
@@ -565,15 +560,14 @@ extern void dr_piezo_select_channel(uint8_t channel);
/**
* @brief Integrated burst + capture + BLE transmit (16-bit raw data)
*
* PROTOCOL v3: Small header packet + separate data packets with delays
* (Large first packets were being dropped by BLE stack)
* reb+red merged protocol: header와 데이터를 첫 패킷에 합침
*
* Response format:
* Packet 1 (reb:): header only (14 bytes)
* Packet 2~N (red:): pkt_idx(2) + raw_data(up to 234 bytes = 117 samples)
* Final (ree:): total_packets(2)
* Packet 1 (reb:): num_samples(2) + raw_data(up to 238 bytes = 119 samples)
* Packet 2~N (red:): pkt_idx(2) + raw_data(up to 238 bytes) — 119샘플 초과 시만
* Final (raa:): status(2) — skip_raa=0 일 때만
*
* With 140 samples: 280 bytes = reb:(14) + red:(6+234) + red:(6+46) + ree:(6)
* With 100 samples: 200 bytes = reb:(6+200) = 206 bytes (단일 패킷)
*/
dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_us,
uint16_t num_samples, uint8_t cycles,
@@ -589,13 +583,7 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u
if (averaging > 1000) averaging = 1000; /* Maximum 1000 */
if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0; /* 채널 범위 검증 */
dr_adc_echo_t echo;
echo.peak_raw = 0;
echo.peak_mv = 0;
echo.peak_index = 0;
echo.peak_time_us = 0;
echo.baseline_raw = 0;
echo.num_samples = 0;
/* echo 분석은 PC에서 raw 데이터로 직접 수행 */
/* Accumulator buffer for averaging (32-bit to prevent overflow) */
static uint32_t accum_buffer[DR_ADC_ECHO_SAMPLES_MAX];
@@ -717,82 +705,58 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u
ADC_LOG("mec: averaged %u measurements", averaging);
}
/*--- Step 6: Analyze averaged data ---*/
dr_adc_analyze_echo(m_echo_buffer, num_samples, &echo, 100);
/*--- Step 6: Transmit via BLE - reb: + red: merged protocol ---*/
/* Packet 1 (reb:): tag(4) + num_samples(2) + data(up to 238 bytes = 119 samples) */
/* Packet 2~N (red:): tag(4) + pkt_idx(2) + data(up to 238 bytes) — only if > 119 samples */
/*--- Step 6: Transmit via BLE - PROTOCOL v3 ---*/
/* Packet 1: reb:(4) + header(10) = 14 bytes ONLY (no data) */
/* Packet 2~N: red:(4) + pkt_idx(2) + data(up to 234 bytes) */
/* Final: ree:(4) + total_pkts(2) = 6 bytes */
uint16_t total_data_bytes = echo.num_samples * 2;
uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; /* 234 bytes */
/* Calculate data packets needed (header packet doesn't carry data) */
uint16_t data_packets = (total_data_bytes + cont_pkt_data_cap - 1) / cont_pkt_data_cap;
/* Version marker: select based on skip_raa (0=mec, 1=maa) */
uint16_t version_marker = skip_raa ? MAA_VERSION_MARKER : MEC_VERSION_MARKER;
uint16_t pkts_with_ver = data_packets | version_marker;
ADC_LOG("mec v6: samples=%u data=%u bytes, packets=%u", echo.num_samples, total_data_bytes, data_packets);
/* Packet 1: reb: header ONLY (14 bytes) - small packet won't be dropped */
ble_buffer[0] = 'r';
ble_buffer[1] = 'e';
ble_buffer[2] = 'b';
ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkts_with_ver & 0xFF);
ble_buffer[5] = (uint8_t)(pkts_with_ver >> 8);
ble_buffer[6] = (uint8_t)(echo.peak_raw & 0xFF);
ble_buffer[7] = (uint8_t)(echo.peak_raw >> 8);
ble_buffer[8] = (uint8_t)(echo.peak_index & 0xFF);
ble_buffer[9] = (uint8_t)(echo.peak_index >> 8);
ble_buffer[10] = (uint8_t)(echo.baseline_raw & 0xFF);
ble_buffer[11] = (uint8_t)(echo.baseline_raw >> 8);
ble_buffer[12] = (uint8_t)(echo.num_samples & 0xFF);
ble_buffer[13] = (uint8_t)(echo.num_samples >> 8);
dr_binary_tx_safe(ble_buffer, 7); /* Send header only: 14 bytes = 7 words */
dr_sd_delay_ms(BLE_PACKET_DELAY_MS); /* Wait for BLE stack */
/* Data packets (red:) - starting from pkt index 0 */
uint16_t total_data_bytes = num_samples * 2;
uint16_t src_idx = 0;
for (uint16_t pkt = 0; pkt < data_packets; pkt++) {
ble_buffer[0] = 'r';
ble_buffer[1] = 'e';
ble_buffer[2] = 'd';
ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkt & 0xFF);
ble_buffer[5] = (uint8_t)(pkt >> 8);
uint16_t dst_idx = 6;
uint16_t bytes_this_pkt = 0;
while (src_idx < echo.num_samples && bytes_this_pkt < cont_pkt_data_cap) {
/* Packet 1: reb: header + data merged */
ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'b'; ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(num_samples & 0xFF);
ble_buffer[5] = (uint8_t)(num_samples >> 8);
uint16_t dst_idx = BLE_REB_HEADER_LEN;
uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes;
while (src_idx < num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) {
uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF;
ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF);
ble_buffer[dst_idx++] = (uint8_t)(sample >> 8);
bytes_this_pkt += 2;
}
dr_binary_tx_safe(ble_buffer, dst_idx / 2); /* bytes to words */
dr_sd_delay_ms(BLE_PACKET_DELAY_MS); /* Inter-packet delay */
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
/* Continuation packets (red:) — only if data didn't fit in reb: */
uint16_t pkt = 0;
while (src_idx < num_samples) {
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'd'; ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkt & 0xFF);
ble_buffer[5] = (uint8_t)(pkt >> 8);
dst_idx = BLE_RED_HEADER_LEN;
while (src_idx < num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) {
uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF;
ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF);
ble_buffer[dst_idx++] = (uint8_t)(sample >> 8);
}
/* vD: Send raa: only - ree: causes subsequent packet loss */
/* DrBench now saves channel data when receiving raa: */
/* skip_raa: 0=send raa (mec), 1=skip raa (maa - caller sends final raa) */
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
pkt++;
}
/* raa: completion — skip_raa: 0=send (mec), 1=skip (maa - caller sends final raa) */
if (!skip_raa) {
ble_buffer[0] = 'r';
ble_buffer[1] = 'a';
ble_buffer[2] = 'a';
ble_buffer[3] = ':';
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
ble_buffer[0] = 'r'; ble_buffer[1] = 'a'; ble_buffer[2] = 'a'; ble_buffer[3] = ':';
ble_buffer[4] = 0x00;
ble_buffer[5] = 0x00;
dr_binary_tx_safe(ble_buffer, 3); /* 6 bytes = 3 words */
dr_binary_tx_safe(ble_buffer, 3);
}
ADC_LOG("mec v6: complete - reb + red*%u + skip_raa=%u (%u samples)", data_packets, skip_raa, echo.num_samples);
ADC_LOG("mec: reb+data merged, skip_raa=%u (%u samples)", skip_raa, num_samples);
return DR_ADC_OK;
}
@@ -831,6 +795,9 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us,
/* 채널 선택(MUX) : 1.3ms */
dr_piezo_select_channel(piezo_ch);
/* MUX 전환 후 S/H 커패시터 안정화를 위한 dummy read */
(void)spim_read_raw();
/* 채널당 반복 측정 횟수만큼 */
for (uint16_t avg_iter = 0; avg_iter < averaging; avg_iter++)
{
@@ -876,16 +843,7 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us,
}
out_channel->num_samples = num_samples;
/* Analyze echo data */
dr_adc_echo_t echo;
dr_adc_analyze_echo(out_channel->samples, num_samples, &echo, 100);
out_channel->peak_raw = echo.peak_raw;
out_channel->peak_index = echo.peak_index;
out_channel->baseline_raw = echo.baseline_raw;
/* peak, index, baseline 확인용 로그 */
//if (g_plat.log) g_plat.log("CH%u: peak=%u index=%u baseline=%u\r\n", piezo_ch, out_channel->peak_raw, out_channel->peak_index, out_channel->baseline_raw);
/* peak/baseline 분석은 PC에서 raw 데이터로 직접 수행 */
return DR_ADC_OK;
}
@@ -894,86 +852,47 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us,
dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data,
uint8_t *ble_buffer)
{
/* Debug BEFORE null check to see if we even enter the function */
dr_ble_debug(0x00FF, 0xAAAA); /* Function called marker */
if (ch_data == NULL || ble_buffer == NULL) {
dr_ble_debug(0x00FE, (ch_data == NULL ? 0x0001 : 0) | (ble_buffer == NULL ? 0x0002 : 0));
return DR_ADC_ERR_INVALID_PARAM;
}
dr_ble_debug(0x0100, ch_data->num_samples); /* Function entry - params valid */
uint16_t total_data_bytes = ch_data->num_samples * 2;
uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; /* 234 bytes */
uint16_t data_packets = (total_data_bytes + cont_pkt_data_cap - 1) / cont_pkt_data_cap;
uint16_t total_packets = 1 + data_packets;
/* Version marker: MAA_VERSION_MARKER | total_packets */
uint16_t pkt_with_ver = total_packets | MAA_VERSION_MARKER;
dr_ble_debug(0x0101, total_packets); /* Before reb: */
/* Packet 1: reb: header (14 bytes) */
ble_buffer[0] = 'r';
ble_buffer[1] = 'e';
ble_buffer[2] = 'b';
ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkt_with_ver & 0xFF);
ble_buffer[5] = (uint8_t)(pkt_with_ver >> 8);
ble_buffer[6] = (uint8_t)(ch_data->peak_raw & 0xFF);
ble_buffer[7] = (uint8_t)(ch_data->peak_raw >> 8);
ble_buffer[8] = (uint8_t)(ch_data->peak_index & 0xFF);
ble_buffer[9] = (uint8_t)(ch_data->peak_index >> 8);
ble_buffer[10] = (uint8_t)(ch_data->baseline_raw & 0xFF);
ble_buffer[11] = (uint8_t)(ch_data->baseline_raw >> 8);
ble_buffer[12] = (uint8_t)(ch_data->num_samples & 0xFF);
ble_buffer[13] = (uint8_t)(ch_data->num_samples >> 8);
/* Send reb: header */
/* Use dr_binary_tx_safe like mec? does, with dr_sd_delay_ms for SoftDevice */
dr_binary_tx_safe(ble_buffer, 7);
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
dr_ble_debug(0x0102, data_packets); /* After reb: */
/* Data packets (red:) */
uint16_t src_idx = 0;
for (uint16_t pkt = 0; pkt < data_packets; pkt++) {
ble_buffer[0] = 'r';
ble_buffer[1] = 'e';
ble_buffer[2] = 'd';
ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkt & 0xFF);
ble_buffer[5] = (uint8_t)(pkt >> 8);
uint16_t dst_idx = 6;
uint16_t bytes_this_pkt = 0;
while (src_idx < ch_data->num_samples && bytes_this_pkt < cont_pkt_data_cap) {
/* Packet 1: reb: header + data merged */
ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'b'; ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(ch_data->num_samples & 0xFF);
ble_buffer[5] = (uint8_t)(ch_data->num_samples >> 8);
uint16_t dst_idx = BLE_REB_HEADER_LEN;
uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes;
while (src_idx < ch_data->num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) {
uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF;
ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF);
ble_buffer[dst_idx++] = (uint8_t)(sample >> 8);
bytes_this_pkt += 2;
}
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
/* Continuation packets (red:) — only if data didn't fit in reb: */
uint16_t pkt = 0;
while (src_idx < ch_data->num_samples) {
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
dr_ble_debug(0x0103, pkt); /* red: packet sent */
ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'd'; ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkt & 0xFF);
ble_buffer[5] = (uint8_t)(pkt >> 8);
dst_idx = BLE_RED_HEADER_LEN;
while (src_idx < ch_data->num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) {
uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF;
ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF);
ble_buffer[dst_idx++] = (uint8_t)(sample >> 8);
}
dr_ble_debug(0x0104, 0); /* Before ree: */
/* Final packet: ree: */
ble_buffer[0] = 'r';
ble_buffer[1] = 'e';
ble_buffer[2] = 'e';
ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(total_packets & 0xFF);
ble_buffer[5] = (uint8_t)(total_packets >> 8);
dr_binary_tx_safe(ble_buffer, 3);
dr_ble_debug(0x010F, 0); /* Function complete */
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
pkt++;
}
return DR_ADC_OK;
}
@@ -1036,73 +955,46 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data,
ch_data->num_samples, compressed_size,
100.0f * compressed_size / (ch_data->num_samples * 2));
uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; /* 234 bytes */
uint16_t data_packets = (compressed_size + cont_pkt_data_cap - 1) / cont_pkt_data_cap;
uint16_t total_packets = 1 + data_packets;
/* Packet 1: rdb: header(8) + delta data merged */
ble_buffer[0] = 'r'; ble_buffer[1] = 'd'; ble_buffer[2] = 'b'; ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(ch_data->num_samples & 0xFF);
ble_buffer[5] = (uint8_t)(ch_data->num_samples >> 8);
ble_buffer[6] = (uint8_t)(compressed_size & 0xFF);
ble_buffer[7] = (uint8_t)(compressed_size >> 8);
/* Version marker for delta mode: 0xC000 | total_packets */
uint16_t pkt_with_ver = total_packets | 0xC000; /* v2 delta marker */
#define BLE_RDB_HEADER_LEN 8 /* rdb: tag(4) + num_samples(2) + compressed_size(2) */
uint16_t rdb_data_cap = BLE_MTU_SIZE - BLE_RDB_HEADER_LEN; /* 236 bytes */
/* Packet 1: rdb: header (16 bytes) - includes compressed_size */
ble_buffer[0] = 'r';
ble_buffer[1] = 'd';
ble_buffer[2] = 'b';
ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkt_with_ver & 0xFF);
ble_buffer[5] = (uint8_t)(pkt_with_ver >> 8);
ble_buffer[6] = (uint8_t)(ch_data->peak_raw & 0xFF);
ble_buffer[7] = (uint8_t)(ch_data->peak_raw >> 8);
ble_buffer[8] = (uint8_t)(ch_data->peak_index & 0xFF);
ble_buffer[9] = (uint8_t)(ch_data->peak_index >> 8);
ble_buffer[10] = (uint8_t)(ch_data->baseline_raw & 0xFF);
ble_buffer[11] = (uint8_t)(ch_data->baseline_raw >> 8);
ble_buffer[12] = (uint8_t)(ch_data->num_samples & 0xFF);
ble_buffer[13] = (uint8_t)(ch_data->num_samples >> 8);
ble_buffer[14] = (uint8_t)(compressed_size & 0xFF);
ble_buffer[15] = (uint8_t)(compressed_size >> 8);
uint16_t dst_idx = BLE_RDB_HEADER_LEN;
uint16_t src_idx = 0;
uint16_t first_chunk = (compressed_size > rdb_data_cap) ? rdb_data_cap : compressed_size;
while (src_idx < first_chunk) {
ble_buffer[dst_idx++] = delta_buffer[src_idx++];
}
/* Pad to word boundary if needed */
if (dst_idx & 1) ble_buffer[dst_idx++] = 0;
dr_binary_tx_safe(ble_buffer, 8); /* 16 bytes = 8 words */
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
/* Continuation packets (rdd:) — only if data didn't fit */
uint16_t pkt = 0;
while (src_idx < compressed_size) {
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
/* Data packets (rdd:) */
uint16_t src_idx = 0;
for (uint16_t pkt = 0; pkt < data_packets; pkt++)
{
ble_buffer[0] = 'r';
ble_buffer[1] = 'd';
ble_buffer[2] = 'd';
ble_buffer[3] = ':';
ble_buffer[0] = 'r'; ble_buffer[1] = 'd'; ble_buffer[2] = 'd'; ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(pkt & 0xFF);
ble_buffer[5] = (uint8_t)(pkt >> 8);
uint16_t dst_idx = 6;
uint16_t bytes_this_pkt = 0;
while (src_idx < compressed_size && bytes_this_pkt < cont_pkt_data_cap)
{
dst_idx = BLE_RED_HEADER_LEN;
while (src_idx < compressed_size && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) {
ble_buffer[dst_idx++] = delta_buffer[src_idx++];
bytes_this_pkt++;
}
/* Pad to word boundary if needed */
if (dst_idx & 1)
{
ble_buffer[dst_idx++] = 0;
}
if (dst_idx & 1) ble_buffer[dst_idx++] = 0;
dr_binary_tx_safe(ble_buffer, dst_idx / 2);
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
pkt++;
}
/* Final packet: rde: */
ble_buffer[0] = 'r';
ble_buffer[1] = 'd';
ble_buffer[2] = 'e';
ble_buffer[3] = ':';
ble_buffer[4] = (uint8_t)(total_packets & 0xFF);
ble_buffer[5] = (uint8_t)(total_packets >> 8);
dr_binary_tx_safe(ble_buffer, 3);
dr_sd_delay_ms(BLE_PACKET_DELAY_MS);
return DR_ADC_OK;
}
@@ -1117,8 +1009,7 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data,
/* Global async context */
static maa_async_ctx_t g_maa_ctx = { .state = MAA_ASYNC_IDLE };
/* Version marker for async MAA */
#define MAA_ASYNC_VERSION_MARKER 0xC000 /* vC: async 8-channel + raa delay fix + cleanup */
/* (version marker 제거 — reb+red merged protocol) */
/**
* @brief Capture one channel (internal helper)
@@ -1141,7 +1032,10 @@ static dr_adc_err_t maa_async_capture_channel(uint8_t ch)
}
/**
* @brief Send reb: header for current channel
* @brief Send reb: header + data merged for current channel
*
* reb: tag(4) + num_samples(2) + data(up to 238 bytes)
* 100샘플(200B) 이하면 이 패킷 하나로 채널 전송 완료
*/
static void maa_async_send_header(void)
{
@@ -1149,45 +1043,33 @@ static void maa_async_send_header(void)
uint8_t *buf = g_maa_ctx.ble_buffer;
uint16_t total_data_bytes = ch->num_samples * 2;
uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN;
g_maa_ctx.data_packets = (total_data_bytes + cont_pkt_data_cap - 1) / cont_pkt_data_cap;
g_maa_ctx.total_packets = 1 + g_maa_ctx.data_packets;
uint16_t pkts_with_ver = g_maa_ctx.total_packets | MAA_ASYNC_VERSION_MARKER;
/* reb: header (14 bytes) */
/* reb: header + data merged */
buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'b'; buf[3] = ':';
buf[4] = (uint8_t)(pkts_with_ver & 0xFF);
buf[5] = (uint8_t)(pkts_with_ver >> 8);
buf[6] = (uint8_t)(ch->peak_raw & 0xFF);
buf[7] = (uint8_t)(ch->peak_raw >> 8);
buf[8] = (uint8_t)(ch->peak_index & 0xFF);
buf[9] = (uint8_t)(ch->peak_index >> 8);
buf[10] = (uint8_t)(ch->baseline_raw & 0xFF);
buf[11] = (uint8_t)(ch->baseline_raw >> 8);
buf[12] = (uint8_t)(ch->num_samples & 0xFF);
buf[13] = (uint8_t)(ch->num_samples >> 8);
buf[4] = (uint8_t)(ch->num_samples & 0xFF);
buf[5] = (uint8_t)(ch->num_samples >> 8);
dr_binary_tx_safe(buf, 7); /* 14 bytes = 7 words */
/* 50ms BLE 대기 시간을 활용하여 raw 덤프 로그 (캡처 시간에 영향 없음)
if (g_plat.log)
{
if (g_maa_ctx.current_ch > 0) g_plat.log("\r\n");
g_plat.log("CH%u raw (%u samples):\r\n", g_maa_ctx.current_ch, ch->num_samples);
for (uint16_t i = 0; i < ch->num_samples; i++)
{
g_plat.log("%u ", ch->samples[i]);
if ((i + 1) % 16 == 0) g_plat.log("\r\n");
uint16_t dst_idx = BLE_REB_HEADER_LEN;
uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes;
uint16_t src_idx = 0;
while (src_idx < ch->num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) {
uint16_t sample = ch->samples[src_idx++];
buf[dst_idx++] = (uint8_t)(sample & 0xFF);
buf[dst_idx++] = (uint8_t)(sample >> 8);
}
if (ch->num_samples % 16 != 0) g_plat.log("\r\n");
}*/
dr_sd_delay_ms(50); /* Allow BLE stack to process TX */
dr_binary_tx_safe(buf, dst_idx / 2);
dr_sd_delay_ms(5); /* dr_binary_tx_safe 내부 재시도(40ms)로 TX 완료 보장, 최소 딜레이만 */
g_maa_ctx.current_pkt = 0;
g_maa_ctx.data_offset = 0;
g_maa_ctx.data_offset = src_idx * 2; /* 이미 전송한 바이트 수 */
/* 데이터가 첫 패킷에 다 들어갔으면 바로 다음 채널로 */
if (g_maa_ctx.data_offset >= total_data_bytes) {
g_maa_ctx.state = MAA_ASYNC_TX_DATA; /* send_data_packet()에서 false 반환 → 다음 채널 */
} else {
g_maa_ctx.state = MAA_ASYNC_TX_DATA;
}
}
/**
@@ -1207,7 +1089,7 @@ static bool maa_async_send_data_packet(void)
uint16_t pkt_idx = g_maa_ctx.current_pkt;
uint16_t remaining = total_data_bytes - g_maa_ctx.data_offset;
uint16_t chunk_size = (remaining > BLE_CONT_DATA_LEN) ? BLE_CONT_DATA_LEN : remaining;
uint16_t chunk_size = (remaining > BLE_RED_DATA_LEN) ? BLE_RED_DATA_LEN : remaining;
/* red: packet header */
buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'd'; buf[3] = ':';
+10 -14
View File
@@ -73,7 +73,7 @@
*============================================================================*/
#define DR_ADC_SCLK_MHZ 8.6f /**< SPI bit-bang SCLK frequency */
#define DR_ADC_CLOCKS_PER_SAMPLE 16 /**< ADC121S051: 16 SCLK per sample */
#define DR_ADC_ECHO_SAMPLES_MAX 100 /**< Maximum samples */
#define DR_ADC_ECHO_SAMPLES_MAX 119 /**< Maximum samples */
#define DR_ADC_ECHO_SAMPLES_DEFAULT 100 /**< Default samples */
#define DR_ADC_SAMPLE_INTERVAL_US 1.86f /**< 16 / 8.6MHz = 1.86us per sample */
#define DR_ADC_SOUND_SPEED_MM_US 1.54f /**< Sound speed in tissue (mm/us) */
@@ -311,7 +311,7 @@ void dr_adc_print_buffer(const uint16_t *buffer, uint16_t num_samples);
* @return dr_adc_err_t Error code
*
* @note Must call dr_adc_register_ble_tx() before using this function
* @note BLE packets: reb: (header), red: (data), ree: (end)
* @note BLE packets: reb: (header+data merged), red: (continuation, >119 samples only)
* @note Higher averaging reduces noise but increases measurement time (~0.3ms per avg)
*/
dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_us,
@@ -345,9 +345,6 @@ void dr_piezo_select_channel(uint8_t channel);
typedef struct {
uint16_t samples[MAA_SAMPLES_MAX]; /**< Raw sample data */
uint16_t num_samples; /**< Actual sample count */
uint16_t peak_raw; /**< Peak amplitude */
uint16_t peak_index; /**< Peak sample index */
uint16_t baseline_raw; /**< Baseline level */
} dr_maa_channel_t;
/**
@@ -373,10 +370,11 @@ dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us,
/**
* @brief Transmit captured channel data via BLE
*
* Sends previously captured channel data using reb:/red:/ree: protocol.
* Sends previously captured channel data using reb+red merged protocol.
* reb: tag(4) + num_samples(2) + data(up to 238B). red: only if > 119 samples.
*
* @param ch_data Pointer to captured channel data
* @param ble_buffer Working buffer for BLE packets (>= 240 bytes)
* @param ble_buffer Working buffer for BLE packets (>= 244 bytes)
* @return dr_adc_err_t Error code
*/
dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data,
@@ -410,7 +408,7 @@ dr_adc_err_t dr_adc_delta_compress(const uint16_t *samples, uint16_t num_samples
/**
* @brief Transmit captured channel data via BLE with delta compression
*
* Uses rdb:/rdd:/rde: protocol (delta variant of reb:/red:/ree:)
* Uses rdb+rdd merged protocol (delta variant of reb+red merged)
*
* @param ch_data Pointer to captured channel data
* @param ble_buffer Working buffer for BLE packets (>= 240 bytes)
@@ -424,8 +422,8 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data,
*
* Design: State machine driven by BLE TX complete events
* Flow:
* maa? cmd -> maa_async_start() -> capture CH0 -> TX reb: -> TX red: ...
* BLE_NUS_EVT_TX_RDY -> maa_async_continue() -> TX next packet or next channel
* maa? cmd -> maa_async_start() -> capture all CH -> TX reb:(header+data) -> TX red: (if needed)
* BLE_NUS_EVT_TX_RDY -> maa_async_on_tx_ready() -> TX next packet or next channel
* All done -> TX raa: -> state=IDLE
*============================================================================*/
@@ -433,7 +431,7 @@ dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data,
typedef enum {
MAA_ASYNC_IDLE = 0, /**< Not active */
MAA_ASYNC_CAPTURING, /**< ADC capture in progress */
MAA_ASYNC_TX_HEADER, /**< Sending reb: header */
MAA_ASYNC_TX_HEADER, /**< Sending reb: header+data merged */
MAA_ASYNC_TX_DATA, /**< Sending red: data packets */
MAA_ASYNC_NEXT_CHANNEL, /**< Preparing next channel */
MAA_ASYNC_COMPLETE /**< Sending raa: and finishing */
@@ -452,8 +450,6 @@ typedef struct {
uint16_t averaging; /**< Averaging count */
uint8_t *ble_buffer; /**< Working buffer for BLE packets */
dr_maa_channel_t channels[MAA_NUM_CHANNELS]; /**< Captured data for each channel */
uint16_t total_packets; /**< Total packets for current channel */
uint16_t data_packets; /**< Data packets for current channel */
bool pre_capture_all; /**< true: 전채널 캡처 완료 후 일괄 전송 (mbb용) */
void (*on_complete_cb)(void); /**< 비동기 캡처 완료 후 호출될 콜백 (NULL이면 미사용) */
} maa_async_ctx_t;
@@ -469,7 +465,7 @@ typedef struct {
* @param num_samples Samples per channel (1~200)
* @param cycles Burst cycles (3~7)
* @param averaging Averaging count (1~1000)
* @param ble_buffer Working buffer (>= 240 bytes)
* @param ble_buffer Working buffer (>= 244 bytes)
* @return dr_adc_err_t DR_ADC_OK if started successfully
*/
dr_adc_err_t maa_async_start(uint8_t freq_option, uint16_t delay_us,
+70 -23
View File
@@ -26,11 +26,14 @@
#include <string.h>
#include "nrf_gpio.h"
#include "debug_print.h"
#include "nrf_delay.h"
#include "dr_piezo.h"
#include "dr_util.h"
#include "main.h"
#include "dr_adc121s051.h"
#include "led_control.h"
#include "app_timer.h"
/*==============================================================================
* 외부 함수 선언부
@@ -51,7 +54,7 @@ extern void param_error(const char *cmd); /* 파라미터 오류 시 BLE로
/* BLE 바이너리 전송 함수 */
extern void single_format_data(uint8_t *buffer, const char *tag, uint16_t value); /* TAG(4바이트) + uint16 값(2바이트)을 buffer에 포맷팅 */
extern void ascii_format_data(uint8_t *buffer, const char *tag, const char *ascii, uint8_t len); /* TAG(4바이트) + ASCII 문자열을 buffer에 포맷팅 */
extern void ascii_format_data(uint8_t *buffer, const char *tag, const char *ascii, size_t len); /* TAG(4바이트) + ASCII 문자열을 buffer에 포맷팅 */
extern void dr_binary_tx_safe(const uint8_t *buffer, uint16_t length); /* BLE NUS를 통해 바이너리 데이터 전송 (length는 워드 단위) */
extern volatile bool data_tx_in_progress; /* BLE TX 진행 중 플래그 */
extern void dr_sd_delay_ms(uint32_t ms); /* SoftDevice 호환 딜레이 (BLE 스택 이벤트 처리 허용) */
@@ -64,8 +67,6 @@ extern char m_static_passkey[6]; /* BLE 정적 패스키 (6자리 숫자 문
extern uint32_t m_life_cycle; /* 디바이스 수명 카운터 (FDS에 영구 저장) */
extern void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length); /* TAG + uint16 배열을 buffer에 포맷팅 (가변 길이 응답용) */
#define DR_DEVICE_VERSION "FW25LIT2B102" /* 펌웨어 버전 문자열 - cmd_parse.c의 DEVICE_VERSION과 반드시 일치해야 함 */
/*==============================================================================
* 외부 변수 선언부
@@ -93,6 +94,7 @@ extern volatile uint16_t info_batt; /* 배터리 전압 (mV) */
extern volatile uint16_t info_temp; /* 온도 (°C x 100) */
extern volatile uint16_t info_imu[6]; /* IMU 6축 (accel XYZ + gyro XYZ) */
extern volatile bool tmp235_saadc_done; /* TMP235 SAADC 콜백 완료 플래그 */
extern volatile bool battery_saadc_done; /* 배터리 SAADC 콜백 완료 플래그 */
extern void pressure_all_level_meas(void); /* 압력 센서 전체 측정 */
extern void battery_timer_stop(void); /* 배터리 타이머 중지 */
@@ -355,9 +357,9 @@ static bool dr_parse_cmd(const uint8_t *buffer, uint8_t length, ParsedCmd *out)
* - TX/RX 전원 활성화 (mpa? / rpa:) - test용
* - TX/RX 전원 비활성화 (mpb? / rpb:) - test용
* - 단일 채널 Burst (mpc? / rpc:) - test용
* - 단일 채널 Burst + ADC -> echo capture (mec? / reb: -> red: -> ree:) : TX/RX Active -> 응답 -> TX/RX Sleep
* - 모든 채널 Burst + ADC -> echo capture (maa? / reb: -> red: -> raa:) : TX/RX Active -> 응답 -> TX/RX Sleep
* - 모든 채널 Burst + ADC -> echo capture (mbb? / reb: -> red: -> raa:) + 각종 센서 측정(배터리, IMU, 온도) : TX/RX Active -> 응답 -> TX/RX Sleep (NEW!)
* - 단일 채널 Burst + ADC -> echo capture (mec? / reb: merged -> raa:) : TX/RX Active -> 응답 -> TX/RX Sleep
* - 모든 채널 Burst + ADC -> echo capture (maa? / reb: merged -> raa:) : TX/RX Active -> 응답 -> TX/RX Sleep
* - 모든 채널 Burst + ADC -> echo capture (mbb? / reb: merged -> raa:) + 각종 센서 측정(배터리, IMU, 온도) : TX/RX Active -> 응답 -> TX/RX Sleep
*
* 삭제 명령어
* - 디바이스 활성화/슬립 : TX 전원 활성화/비활성화와 동일 기능
@@ -407,6 +409,9 @@ static int Cmd_mbb(const ParsedCmd *cmd); /* mbb? 6채널 캡처 + 센서 측
static int Cmd_mcf(const ParsedCmd *cmd); /* mcf? 피에조 파라미터 읽기 (FDS) */
static int Cmd_mcs(const ParsedCmd *cmd); /* mcs? 피에조 파라미터 쓰기 (FDS) */
/* E. LED 제어 */
static int Cmd_mls(const ParsedCmd *cmd); /* mls? LED 상태 설정 (앱 → 기기) */
/* ---- 명령 테이블 ---- */
@@ -451,6 +456,9 @@ static CmdEntry g_cmd_table[] = {
{ "mbb?", true, Cmd_mbb },
{ "mcf?", true, Cmd_mcf },
{ "mcs?", true, Cmd_mcs },
/* F. LED 제어 */
{ "mls?", true, Cmd_mls },
};
/* 명령 테이블 엔트리 수 (컴파일 타임 계산) */
@@ -664,7 +672,7 @@ static int Cmd_mss(const ParsedCmd *cmd)
static int Cmd_mfv(const ParsedCmd *cmd)
{
(void)cmd;
ascii_format_data(ble_bin_buffer, "rfv:", DR_DEVICE_VERSION, 12);
ascii_format_data(ble_bin_buffer, "rfv:", FIRMWARE_VERSION, 12);
dr_binary_tx_safe(ble_bin_buffer, 8); /* 4(TAG) + 12(버전) = 16바이트 = 8워드 */
return 1;
}
@@ -786,13 +794,12 @@ static int Cmd_mpc(const ParsedCmd *cmd)
* word 4: averaging (기본=1) - 평균화 횟수 (1~1000, 노이즈 저감용)
* word 5: piezo_ch (기본=0) - 피에조 채널 (0~7)
*
* 응답 멀티패킷 형식 (16비트 원시):
* 헤더 패킷 ("reb:"): total_pkts, peak, idx, baseline, samples
* 데이터 패킷 ("red:"): pkt_idx + 16비트 ADC 원시 데이터
* 료 패킷 ("ree:"): total_bytes_sent
* 응답 패킷 형식 (reb+red merged protocol):
* 패킷 ("reb:"): num_samples(2) + raw_data(up to 238B = 119샘플)
* 연속 패킷 ("red:"): pkt_idx(2) + raw_data(up to 238B) — 119샘플 초과 시만
* 료 패킷 ("raa:"): status(2)
*
* 16비트 포맷: 샘플당 2바이트 (Little-Endian)
* 예: 140샘플(20cm) = 280바이트 = 약 2패킷
* 100샘플 기준: reb: 1패킷(206B)으로 완료
*/
static int Cmd_mec(const ParsedCmd *cmd)
{
@@ -935,14 +942,12 @@ static int Cmd_cmd(const ParsedCmd *cmd)
* 주파수 = 1.8MHz, 딜레이 = 10us, 샘플 수 = 140
* 사이클 = 7, 평균화 = 5회
*
* 응답 형식:
* 각 채널(CH0~CH3)마다:
* reb: [총패킷수(2)] [피크(2)] [인덱스(2)] [기준선(2)] [샘플수(2)]
* red: [패킷순번(2)] [데이터...]
* 응답 형식 (reb+red merged protocol):
* 각 채널마다:
* reb: [샘플수(2)] [raw_data...] — 119샘플 이하면 단일 패킷
* red: [패킷순번(2)] [데이터...] — 119샘플 초과 시만
* 전체 완료:
* raa: [상태(2)]
*
* 버전 마커: 0xA000 (vA) = 비동기 4채널
*/
/* 피에조 캡처 파라미터: FDS(m_config)에서 로드, 앱에서 변경 가능 */
@@ -1010,19 +1015,28 @@ static void all_sensors(void)
info4 = true; /* 센서값을 전역 변수에 저장 (BLE 전송 안 함) */
/* 1. 배터리 전압 측정 → info_batt에 저장 */
DBG_PRINTF("1");
battery_saadc_done = false;
battery_level_meas();
dr_sd_delay_ms(1); /* SAADC 콜백 완료 대기 */
for (timeout_cnt = 0; !battery_saadc_done && timeout_cnt < 100; timeout_cnt++)
{
dr_sd_delay_ms(1); /* 콜백 오면 즉시 탈출, 최대 100ms */
}
DBG_PRINTF("3");
/* 2. IMU 6축 단발 읽기 → info_imu[6]에 저장 */
hw_i2c_init_once();
imu_read_direct();
DBG_PRINTF("4");
/* 3. 온도 측정 → info_temp에 저장 (TMP235는 Piezo TX/RX 전원 필요) */
if (!dr_piezo_is_power_on())
{
dr_piezo_power_on();
}
DBG_PRINTF("5");
DBG_PRINTF("6");
tmp235_saadc_done = false;
tmp235_voltage_level_meas();
@@ -1030,6 +1044,7 @@ static void all_sensors(void)
{
dr_sd_delay_ms(1); /* 콜백 오면 즉시 탈출, 최대 100ms, 타임아웃 시 이전 값 또는 쓰레기값.. */
}
DBG_PRINTF("8");
info4 = false;
@@ -1050,6 +1065,7 @@ static void all_sensors(void)
buf[19] = (uint8_t)(info_temp >> 8);
dr_binary_tx_safe(buf, 10); /* 20바이트 = 10워드 */
DBG_PRINTF("9");
/* 배터리, IMU, 온도 확인용 로그 */
//if (g_plat.log) g_plat.log("-------------------------------------------------------------------------------------\r\n[Battery] %u\r\n[IMU] %d,%d,%d,%d,%d,%d\r\n[Temperature] %u\r\n\r\n", info_batt, (int16_t)info_imu[0], (int16_t)info_imu[1], (int16_t)info_imu[2],
@@ -1060,11 +1076,11 @@ static void all_sensors(void)
/**
* @brief mbb? - 6채널 전체 캡처 + 센서 측정 (배터리/온도/IMU)
*
* 센서 측정(rbb:) → 6채널 비동기 캡처(reb:/red:/raa:) → TX/RX OFF
* 센서 측정(rbb:) → 6채널 비동기 캡처(reb: merged) → raa: → TX/RX OFF
*
* 응답 흐름:
* 1) 센서 측정: rbb: [배터리(2) + IMU 6축(12) + 온도(2)]
* 2) 각 채널(CH0~CH5): reb: [헤더] → red: [데이터...]
* 2) 각 채널: reb: [샘플수(2) + raw_data] (100샘플이면 단일 패킷)
* 3) 캡처 완료: raa: [상태]
*/
@@ -1073,6 +1089,7 @@ static int Cmd_mbb(const ParsedCmd *cmd)
dr_adc_err_t err;
all_sensors(); /* 배터리, IMU, 온도 센서 먼저 측정 */
DBG_PRINTF("10");
if (maa_async_is_busy())
{
@@ -1107,6 +1124,7 @@ static int Cmd_mbb(const ParsedCmd *cmd)
dr_piezo_power_off();
return 1;
}
DBG_PRINTF("11\n");
return 1;
}
@@ -1183,6 +1201,35 @@ static int Cmd_mcs(const ParsedCmd *cmd)
}
/**
* @brief mls? - LED 상태 설정 (앱 → 기기)
*
* 파라미터: [state(2)] - led_state_t enum 값
* 0: OFF, 4: DETACH_WARNING, 5: ALIGN_SEARCHING, 6: ALIGN_COMPLETE
* 응답: rls: [state(2)] - 에코
*/
static int Cmd_mls(const ParsedCmd *cmd)
{
if (cmd->data_len < 2) {
dr_ble_return_1("rls:", 0xFFFF);
return 1;
}
uint16_t state;
dr_get_u16(cmd, 0, &state);
if (state > LED_STATE_ERROR) {
dr_ble_return_1("rls:", 0xFFFE);
return 1;
}
led_set_state((led_state_t)state);
dr_ble_return_1("rls:", state);
return 1;
}
/*==============================================================================
* 설정: HW/시리얼 넘버 FDS 읽기/쓰기
*
@@ -7,22 +7,17 @@
******************************************************************************/
/*******************************************************************************
* [모듈 개요] 배터리 전압 및 압력센서 ADC 측정 모듈 ---> 압력 센서 미탑재로 압력 센서 부분 삭제 예정
* [모듈 개요] 배터리 전압 ADC 측정 모듈
*
* nRF52840의 SAADC(Successive Approximation ADC)를 사용하여 다음을 수행:
* 1) 배터리 전압 측정 (AIN2 채널, 1/3 프리스케일링)
* nRF52840의 SAADC(Successive Approximation ADC)를 사용하여 배터리 전압을 측정:
* - AIN2 채널, 10bit 해상도
* - 5초 주기 타이머(battery_loop)로 반복 측정
* - 저전압(3100mV 이하) 10회 연속 감지 시 자동 전원 OFF
* - 저전압(3500mV 이하) 10회 연속 감지 시 자동 전원 OFF
* - info4 모드(전체 센서 수집)에서는 info_batt에 저장 후 온도 측정으로 전환
* 2) 압력센서 2채널 측정 (AIN7=P1, AIN4=P2)
* - 12bit ADC 값을 mV로 변환하여 BLE 또는 UART로 전송
*
* 배터리 전압 변환 공식:
* 전압(mV) = ADC값 x (600mV / 1023) x 6 x 1.42 (분압 저항 보정 계수)
*
* 압력센서 전압 변환 공식:
* 전압(mV) = ADC값 x 0.805(uV/LSB) / 1000, 범위 0~3500mV 클램핑
*
* info4 모드 순서: 배터리 -> 온도(go_temp) -> IMU(motion_raw_data_enabled)
******************************************************************************/
@@ -45,18 +40,14 @@
#include "main.h"
#include "debug_print.h"
/* SAADC 내부 기준전압 600mV */
#define BATTERY_REF_VOLTAGE_IN_MILLIVOLTS 600 /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
/* SAADC 내부 기준전압 600mV (부동소수점) */
#define BATTERY_REF_VOLTAGE_IN_MILLIVOLTS 600.0f /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
/* 1/3 프리스케일링 보상 계수 (입력 전압을 1/3로 분압하므로 x3, 추가 x2 = 총 x6) */
#define BATTERY_PRE_SCALING_COMPENSATION 6 /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
/* 1/3 프리스케일링 보상 계수 (입력 전압을 1/3로 분압하므로 x3, 추가 x2 = 총 x6) (부동소수점) */
#define BATTERY_PRE_SCALING_COMPENSATION 6.0f /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
/* 10비트 ADC 최대 디지털 값 */
#define BATTERY_ADC_RES_10BITS 1023 /**< Maximum digital value for 10-bit ADC conversion. */
//#define PRESSURE_RESULT_IN_MILLI_VOLTS(adc) ((adc * 3600) / 1023)
#define PRESSURE_OFFSET_DEFAULT 0 // 압력 offset. 캘리브레이션 시 사용 가능
#define MV_PER_ADC_STEP 805 // 약 0.805mV per 1 LSB (nRF 12bit + scaling)
/* 12비트 ADC 최대 디지털 값 (부동소수점) */
#define BATTERY_ADC_RES_12BITS 4095.0f /**< Maximum digital value for 12-bit ADC conversion. */
/**@brief Macro to convert the result of ADC conversion in millivolts.
*
@@ -65,31 +56,28 @@
* @retval Result converted to millivolts.
*/
#define BATTERY_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
((((ADC_VALUE) * BATTERY_REF_VOLTAGE_IN_MILLIVOLTS) / BATTERY_ADC_RES_10BITS) * BATTERY_PRE_SCALING_COMPENSATION)
((((ADC_VALUE) * BATTERY_REF_VOLTAGE_IN_MILLIVOLTS) / BATTERY_ADC_RES_12BITS) * BATTERY_PRE_SCALING_COMPENSATION)
/* 배터리 측정용 더블 버퍼 (SAADC가 비동기로 교대 사용) */
static nrf_saadc_value_t adc_bufs[2];
/* 배터리 측정용 싱글 버퍼 (1회 측정 후 uninit하므로 더블 버퍼 불필요) */
static nrf_saadc_value_t adc_buf;
//static nrf_saadc_value_t adc_bufs[2]; // 이전: 더블 버퍼 (연속 측정용)
/* 압력센서 2채널 ADC 버퍼 [0]=AIN7(P1), [1]=AIN4(P2) */
static int16_t pressure_adc_buf[2]; //cj add 25/11/19
static uint16_t convert_adc_to_mV(int16_t raw_adc); //cj add 25/11/19
/* 배터리 모니터링 반복 타이머 정의 */
APP_TIMER_DEF(m_battery_loop_timer_id);
/* 배터리 측정 주기: 5초 (밀리초 단위) */
#define BATTERY_LOOP_INTERVAL 5000
#define BATTERY_LOOP_INTERVAL 60000
/* 저전압 체크 플래그 — battery_loop에서 true로 설정, 핸들러에서 소비 */
bool low_battery_check = false;
/* info4: 전체 센서 데이터 수집 모드 플래그 (cmd_parse에서 설정) */
/* SAADC 콜백 완료 플래그 — all_sensors()에서 배터리 측정 완료 대기용 */
volatile bool battery_saadc_done = false;
/* info4: 전체 센서 데이터 수집 모드 플래그 */
extern bool info4; // main.c
// cj add edit 25/11/24
/* info4 모드에서 압력센서 측정값을 임시 저장하는 변수 (mV 단위) */
volatile uint16_t info_p1;
volatile uint16_t info_p2;
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
@@ -120,104 +108,13 @@ extern bool motion_data_once ;
* percentage and send it to peer.
*/
/**
* @brief 압력센서 ADC 원시값을 밀리볼트(mV)로 변환
*
* 변환 공식: mV = raw_adc x 805(uV/LSB) / 1000
* 결과를 0~3500mV 범위로 클램핑하여 이상치 방지
*
* @param raw_adc SAADC에서 읽은 원시 ADC 값 (12bit)
* @return 변환된 전압값 (mV), 0~3500 범위
*/
static uint16_t convert_adc_to_mV(int16_t raw_adc)
{
/* 음수 ADC 값은 0으로 처리 (노이즈 등으로 발생 가능) */
if (raw_adc < 0)
{
raw_adc = 0;
}
/* 805 uV/LSB 스케일링: raw x 805 = uV, /1000 = mV */
int32_t mv = (int32_t)raw_adc * MV_PER_ADC_STEP; // 단위: 805 uV
mv /= 1000;
/* 0~3500mV 범위로 클램핑하여 유효 범위 보장 */
if (mv < 0)
{
mv = 0;
}
if (mv > 3500)
{
mv = 3500;
}
return (uint16_t)mv;
}
/**
* @brief 압력센서 2채널 ADC 완료 콜백
*
* SAADC 변환 완료 시 호출된다.
* AIN7(P1)과 AIN4(P2) 두 채널의 ADC 값을 mV로 변환한 뒤,
* info4 모드이면 글로벌 변수에 저장하고, 아니면 BLE/UART로 즉시 전송한다.
* 변환 후 SAADC를 해제하여 다른 ADC 측정(배터리, 온도)과 공유한다.
*/
void pressure_all_event_handler(nrf_drv_saadc_evt_t const * p_event)
{
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
{
/* 버퍼에서 2채널 ADC 원시값 추출 */
int16_t p1_adc = p_event->data.done.p_buffer[0]; // AIN7 (압력센서1)
int16_t p2_adc = p_event->data.done.p_buffer[1]; // AIN4 (압력센서2)
/* ADC 원시값 → 밀리볼트 변환 */
uint16_t p1_mV = convert_adc_to_mV(p1_adc);
uint16_t p2_mV = convert_adc_to_mV(p2_adc);
/* info4(전체 센서 수집) 모드일 때 글로벌 변수에 저장 */
if(info4 == true)
{
info_p1 = p1_mV;
info_p2 = p2_mV;
}
/* 다음 변환을 위해 버퍼 재등록 */
APP_ERROR_CHECK(nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, 2));
/* SAADC 해제 — 배터리/온도 측정과 하드웨어를 공유하므로 사용 후 반드시 해제 */
nrf_drv_saadc_uninit();
nrf_drv_saadc_channel_uninit(0);
nrf_drv_saadc_channel_uninit(1);
/* 결과 전송: 명령 소스에 따라 UART 또는 BLE로 전송 */
if(cmd_type_t == CMD_UART)
{
DBG_PRINTF("P1:%d P2:%d\r\n", p1_mV, p2_mV);
}
/* BLE 모드이고 info4가 아닌 경우(단독 압력 측정) → BLE 바이너리 전송 */
else if(cmd_type_t == CMD_BLE && info4 == false)
{
DBG_PRINTF("P1:%d P2:%d\r\n", p1_mV, p2_mV);
// uint16_t len = sprintf((char*)ble_bin_buffer,
// "rpn:%04x,%04x", p1_mV, p2_mV);
/* 2채널 압력값을 "rpn:" 헤더와 함께 바이너리 포맷으로 BLE 전송 */
uint16_t result_data[2];
result_data[0] = p1_mV;
result_data[1] = p2_mV;
format_data(ble_bin_buffer, "rpn:", result_data,2);
dr_binary_tx_safe(ble_bin_buffer,4);
}
}
}
/**
* @brief 배터리 전압 ADC 완료 콜백
*
* SAADC 변환 완료 시 호출된다.
* ADC 값을 실제 배터리 전압(mV)으로 변환하고, 동작 모드에 따라:
* - 저전압 체크 모드: 3100mV 이하 10회 연속이면 자동 전원 OFF
* - 저전압 체크 모드: 3500mV 이하 10회 연속이면 자동 전원 OFF
* - info4 모드: info_batt에 저장 후 온도 측정(go_temp)으로 전환
* - 일반 모드: BLE 또는 UART로 즉시 전송
*
@@ -231,26 +128,29 @@ void battery_event_handler( nrf_drv_saadc_evt_t const * p_event )
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
{
nrf_saadc_value_t register_val = 0;
uint16_t batt_lvl_in_milli_volt_0 = 0; /* 보정 전 전압 */
uint16_t batt_lvl_in_milli_volt_1 = 0; /* 분압 보정 후 최종 전압 */
uint32_t err_code = 0;
float batt_lvl_in_milli_volt_0 = 0; /* 보정 전 전압 (부동소수점) */
float batt_lvl_in_milli_volt_1 = 0; /* 분압 보정 후 최종 전압 (부동소수점) */
/* ADC 변환 결과 읽기 */
register_val = p_event->data.done.p_buffer[0];
/* 다음 변환을 위해 버퍼 재등록 */
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, 1);
APP_ERROR_CHECK(err_code);
//err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, 1); // 이전: 다음 변환을 위해 버퍼 재등록
//APP_ERROR_CHECK(err_code);
/* SAADC 해제 — 다른 ADC 측정(온도, 압력)과 하드웨어 공유 */
nrf_drv_saadc_uninit();
/* 1회 측정 후 해제이므로 buffer_convert(다음 버퍼 등록) 불필요 */
nrf_drv_saadc_channel_uninit(0);
nrf_drv_saadc_uninit();
/* 콜백 완료 알림 (all_sensors 대기 해제용) */
if (info4) DBG_PRINTF("2");
battery_saadc_done = true;
/* ADC값 → mV 변환 (매크로: ADC x 600/1023 x 6) */
batt_lvl_in_milli_volt_0 = BATTERY_RESULT_IN_MILLI_VOLTS(register_val);
/* 분압 저항 보정 계수 1.42 적용 → 실제 배터리 전압 */
batt_lvl_in_milli_volt_1 = (batt_lvl_in_milli_volt_0) *1.42;
batt_lvl_in_milli_volt_1 = (batt_lvl_in_milli_volt_0) *1.42f;
/* === 저전압 체크 모드 (battery_loop 타이머에서 설정) === */
if(low_battery_check == true)
@@ -273,7 +173,7 @@ void battery_event_handler( nrf_drv_saadc_evt_t const * p_event )
{
/* 아직 10회 미만 — 카운터 증가 후 경고 출력 */
low_battery_cnt++;
DBG_PRINTF("WARNING!!! low_battery cnt = %d, Batt = %d(mV)\r\n", low_battery_cnt, batt_lvl_in_milli_volt_1);
DBG_PRINTF("WARNING!!! low_battery cnt = %d, Batt = %d(mV)\r\n", low_battery_cnt, (int)batt_lvl_in_milli_volt_1);
}
}
}
@@ -289,7 +189,7 @@ void battery_event_handler( nrf_drv_saadc_evt_t const * p_event )
{
if (cmd_type_t == CMD_UART)
{
DBG_PRINTF("Tn%d\r\n\r\n", batt_lvl_in_milli_volt_1);
DBG_PRINTF("Tn%d\r\n\r\n", (int)batt_lvl_in_milli_volt_1);
}
else if (cmd_type_t == CMD_BLE)
{
@@ -312,73 +212,29 @@ void battery_event_handler( nrf_drv_saadc_evt_t const * p_event )
*/
static void battery_configure(void)
{
/* SAADC 드라이버 초기화 (기본 설정 + 배터리 이벤트 핸들러 등록) */
ret_code_t err_code = nrf_drv_saadc_init(NULL, battery_event_handler);
APP_ERROR_CHECK(err_code);
/* SAADC 드라이버 초기화 (4x 오버샘플링으로 노이즈 저감) */
nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; // 10 -> 12bit
saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X;
ret_code_t err_code = nrf_drv_saadc_init(&saadc_config, battery_event_handler);
if (err_code != NRF_SUCCESS) {
return; /* SAADC 사용 중 → 이번 측정 스킵, 다음 주기에 재시도 */
}
/* AIN2 채널 설정: 싱글엔드 입력, 1/3 프리스케일링 (기본값) */
/* AIN2 채널 설정: 싱글엔드 입력, 1/6 gain, burst + TACQ 20μs */
nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
config.burst = NRF_SAADC_BURST_ENABLED;
config.acq_time = NRF_SAADC_ACQTIME_10US;
err_code = nrf_drv_saadc_channel_init(0, &config);
APP_ERROR_CHECK(err_code);
/* 더블 버퍼 등록 — SAADC가 교대로 사용하여 데이터 손실 방지 */
err_code = nrf_drv_saadc_buffer_convert(&adc_bufs[0], 1);
/* 싱글 버퍼 등록 (1회 측정 후 uninit) */
err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_buffer_convert(&adc_bufs[1], 1);
APP_ERROR_CHECK(err_code);
}
/**
* @brief SAADC를 압력센서 2채널 측정용으로 설정
*
* AIN7(압력센서1)과 AIN4(압력센서2)를 싱글엔드 모드로 초기화한다.
* 이미 초기화된 상태(NRF_ERROR_INVALID_STATE)는 무시하여 안전하게 처리한다.
* 콜백: pressure_all_event_handler
*/
void pressure_all_configure(void)
{
ret_code_t err_code;
/* SAADC 드라이버 초기화 (이미 초기화된 경우 무시) */
err_code = nrf_drv_saadc_init(NULL, pressure_all_event_handler);
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE)
{
DBG_PRINTF("SAADC init err=%d\r\n", err_code);
return;
}
/* 채널 0: AIN7 (압력센서1, P1) */
nrf_saadc_channel_config_t ch0_cfg = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN7);
/* 채널 1: AIN4 (압력센서2, P2) */
nrf_saadc_channel_config_t ch1_cfg = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN4);
err_code = nrf_drv_saadc_channel_init(0, &ch0_cfg);
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE)
{
DBG_PRINTF("SAADC ch0 init err=%d\r\n", err_code);
return;
}
err_code = nrf_drv_saadc_channel_init(1, &ch1_cfg);
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE)
{
DBG_PRINTF("SAADC ch1 init err=%d\r\n", err_code);
return;
}
/* 2채널 ADC 버퍼 등록 ([0]=P1, [1]=P2) */
err_code = nrf_drv_saadc_buffer_convert(pressure_adc_buf, 2);
if (err_code != NRF_SUCCESS)
{
DBG_PRINTF("SAADC buf conv err=%d\r\n", err_code);
return;
}
//err_code = nrf_drv_saadc_buffer_convert(&adc_bufs[0], 1); // 이전: 더블 버퍼
//APP_ERROR_CHECK(err_code);
//err_code = nrf_drv_saadc_buffer_convert(&adc_bufs[1], 1);
//APP_ERROR_CHECK(err_code);
}
/**
@@ -396,36 +252,19 @@ void battery_level_meas(void)
APP_ERROR_CHECK(err_code);
}
/**
* @brief 압력센서 2채널 1회 측정 시작 - 압력센서 미탑재
*
* SAADC를 압력센서 2채널용으로 설정 후 샘플링을 트리거한다.
* 결과는 pressure_all_event_handler 콜백에서 비동기로 처리된다.
*/
/*
void pressure_all_level_meas(void) //add cj add 25/11/19
{
ret_code_t err_code;
pressure_all_configure();
err_code = nrf_drv_saadc_sample();
APP_ERROR_CHECK(err_code);
}
*/
/**
* @brief 배터리 모니터링 타이머 콜백 (5초 주기)
* @brief 배터리 모니터링 타이머 콜백 (5초 주기) - 주기 확인 필요(너무 짧음)
*
* 저전압 체크 플래그를 설정하고 배터리 측정을 시작한다.
* 다른 작업(IMU 등) 처리 중이면 측정을 건너뛴다.
* 그렇지 않으면 저전압 체크 플래그를 설정하고 배터리 측정을 시작한다.
*/
void battery_loop(void * p_context) /* For 1sec */
{
UNUSED_PARAMETER(p_context);
/* 다른 센서 처리 중이면 배터리 측정 스킵 (충돌 방지) */
if(processing==true)
/* 다른 센서 처리 중 또는 MBB 센서 수집 중이면 배터리 측정 스킵 (SAADC 충돌 방지) */
if (processing == true || info4 == true)
{
processing = false ; // add 20241218
//low_battery_check = true;
@@ -7,17 +7,15 @@
******************************************************************************/
/*******************************************************************************
* [헤더 개요] 배터리 전압 및 압력센서 SAADC 측정 인터페이스
* [헤더 개요] 배터리 전압 SAADC 측정 인터페이스
*
* nRF52840 SAADC를 이용한 배터리 전압 측정과 압력센서 2채널 측정의
* 외부 호출용 API를 선언한다.
* nRF52840 SAADC를 이용한 배터리 전압 측정 API를 선언한다.
*
* 주요 API:
* - battery_level_meas() : 배터리 전압 1회 측정 (AIN2)
* - pressure_all_level_meas() : 압력센서 2채널 1회 측정 (AIN7, AIN4)
* - battery_timer_init/start/stop() : 5초 주기 배터리 모니터링 타이머 제어
*
* LOW_BATTERY_VOLTAGE(3100mV) 이하가 10회 연속 감지되면 자동 전원 OFF
* LOW_BATTERY_VOLTAGE(3500mV) 이하가 10회 연속 감지되면 자동 전원 OFF
******************************************************************************/
#ifndef _BATTERY_SAADC_H_
@@ -27,10 +25,11 @@
#define LOW_BATTERY_VOLTAGE 3500 /* Low Battery 임계값 */
/** @brief 배터리 SAADC 콜백 완료 플래그 (all_sensors 대기용) */
extern volatile bool battery_saadc_done;
/** @brief 배터리 전압 1회 측정 시작 (비동기, 결과는 콜백에서 처리) */
void battery_level_meas(void);
/** @brief 압력센서 2채널(AIN7, AIN4) 1회 측정 시작 (비동기) */
//void pressure_all_level_meas(void); // 압력센서 미탑재
/** @brief 배터리 모니터링 5초 반복 타이머 시작 */
void battery_timer_start(void);
/** @brief 배터리 모니터링 타이머 정지 */
@@ -95,7 +95,7 @@ static fds_record_t const m_dummy_record =
/* 기본 설정값 상수 */
int8_t reset_status_dflt = 99; /* 리셋 상태 기본값 */
uint8_t static_passkey_dflt[6] = "123456"; /* BLE 패스키 기본값 */
uint8_t static_passkey_dflt[6] = DEFAULT_PASSKEY; /* BLE 패스키 기본값 */
/**
* @brief 기본 설정값 초기화
@@ -105,12 +105,13 @@ uint8_t static_passkey_dflt[6] = "123456"; /* BLE 패스키 기본값 */
*/
void fds_default_value_set(void)
{
/* HW Number - empty (set via BLE command) */
/* HW Number - default from HARDWARE_VERSION */
memset(m_config.hw_no, 0, 12);
memcpy(m_config.hw_no, HARDWARE_VERSION, strlen(HARDWARE_VERSION));
/* Serial Number - default from FIRMWARE_SERIAL_NO */
/* Serial Number - default from SERIAL_NUMBER */
memset(m_config.serial_no, 0, 12);
memcpy(m_config.serial_no, "VB026030000", 11);
memcpy(m_config.serial_no, SERIAL_NUMBER, strlen(SERIAL_NUMBER));
/* Static Passkey */
memcpy(m_config.static_passkey, static_passkey_dflt, 6);
@@ -124,7 +125,7 @@ void fds_default_value_set(void)
/* Device usage count */
m_config.life_cycle = 0;
/* 피에조 캡처 파라미터 기본값 */
/* 피에조 측정 파라미터 기본값 */
m_config.piezo_freq_option = 1; /* 2.1MHz */
m_config.piezo_delay_us = 10; /* 버스트 후 10us */
m_config.piezo_num_samples = 100; /* 100샘플 */
@@ -36,6 +36,25 @@
#include "nordic_common.h"
#include <stdint.h>
/* -------------------------------------------------------------------------
* 기본 버전 정보 (FDS 기본값 및 빈 필드 복구 시 사용)
*
* 하드웨어 식별 코드
* - VBTHW0100 = 개발(시험)용 Ver 1.00
* - VB0HW0100 = 양산용 Ver 1.00
*
* Firmware 식별 코드
* - VBTFW0100 = 개발(시험)용 Ver 1.00
* - VB0FW0100 = 양산용 Ver 1.00
*
* 시리얼 넘버 식별 코드
* - VBT26030001 = 개발(시험)용 26년 3월 생산 1번
* - VB026030001 = 양산용 26년 3월 생산 1번
------------------------------------------------------------------------- */
#define HARDWARE_VERSION "VBTHW0100"
#define SERIAL_NUMBER "VBT26030001"
#define DEFAULT_PASSKEY "123456"
#pragma pack(1)
typedef struct
{
@@ -132,8 +132,8 @@ echo Flashing: program → reset (NO erase, FDS preserved)
REM recover - SKIP to preserve internal flash data
REM erase - SKIP to preserve FDS/fstorage data
REM program (sector-erase only for hex regions, FDS pages untouched)
nrfutil device program --firmware hex\firmware_all.hex --serial-number %SERIALNUMBER%
REM program (hex 데이터가 있는 영역만 erase, FDS 보존)
nrfutil device program --firmware hex\firmware_all.hex --options chip_erase_mode=ERASE_RANGES_TOUCHED_BY_FIRMWARE --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 (
echo.
echo [WARNING] Program failed - device may have Readback Protection enabled.
@@ -145,7 +145,7 @@ if %errorlevel% neq 0 (
nrfutil device recover --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
echo Re-programming...
nrfutil device program --firmware hex\firmware_all.hex --serial-number %SERIALNUMBER%
nrfutil device program --firmware hex\firmware_all.hex --options chip_erase_mode=ERASE_RANGES_TOUCHED_BY_FIRMWARE --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
) else (
goto flash_fail
@@ -0,0 +1,176 @@
@echo off
setlocal enabledelayedexpansion
echo ==========================================
echo MEDiThings Bladder Patch Programming
echo (DEBUG MODE - ERASE ALL, FDS Reset)
echo ==========================================
REM -----------------------------------------------------
REM Set script directory as base path
REM -----------------------------------------------------
cd /d "%~dp0"
echo Working directory: %CD%
REM -----------------------------------------------------
REM Create hex output folder
REM -----------------------------------------------------
if not exist hex mkdir hex
REM -----------------------------------------------------
REM 1. Copy HEX files (with verification)
REM -----------------------------------------------------
echo [1/6] Copying HEX files...
set "APP_SRC=..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex"
set "BOOT_SRC=..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex"
REM Check source files exist
if not exist "%APP_SRC%" (
echo ERROR: Application HEX not found: %APP_SRC%
pause
exit /b 1
)
if not exist "%BOOT_SRC%" (
echo ERROR: Bootloader HEX not found: %BOOT_SRC%
pause
exit /b 1
)
REM Copy with /Y to overwrite without prompt
copy /Y "%APP_SRC%" hex\app.hex
if %errorlevel% neq 0 (
echo ERROR: Failed to copy app.hex
pause
exit /b 1
)
echo - app.hex copied OK
copy /Y "%BOOT_SRC%" hex\boot.hex
if %errorlevel% neq 0 (
echo ERROR: Failed to copy boot.hex
pause
exit /b 1
)
echo - boot.hex copied OK
REM -----------------------------------------------------
REM 2. Generate Bootloader DFU Settings
REM -----------------------------------------------------
echo [2/6] Generating Bootloader DFU settings...
nrfutil settings generate ^
--family NRF52840 ^
--application hex\app.hex ^
--application-version 1 ^
--bootloader-version 1 ^
--bl-settings-version 2 ^
hex\settings.hex
if %errorlevel% neq 0 (
echo ERROR: nrfutil settings failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 3. Merge HEX (SoftDevice + Application)
REM -----------------------------------------------------
echo [3/6] Merging SoftDevice + Application...
mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex hex\app.hex --output hex\part1.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex part1 failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 4. Merge HEX (Bootloader + Settings)
REM -----------------------------------------------------
echo [4/6] Merging Bootloader + Settings...
mergehex.exe --merge hex\boot.hex hex\settings.hex --output hex\part2.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex part2 failed.
pause
exit /b 1
)
REM -----------------------------------------------------
REM 5. Final HEX merge (Combine everything)
REM -----------------------------------------------------
echo [5/6] Creating final combined HEX...
mergehex.exe --merge hex\part1.hex hex\part2.hex --output hex\firmware_all.hex
if %errorlevel% neq 0 (
echo ERROR: mergehex final merge failed.
pause
exit /b 1
)
echo Final merged HEX: hex\firmware_all.hex
REM -----------------------------------------------------
REM 6. Detect device SERIAL NUMBER and Flash
REM -----------------------------------------------------
echo [6/6] Detecting device serial number...
for /f %%A in ('
powershell -Command "(nrfutil device list --json | Select-String '\"type\":\"info\"' | ConvertFrom-Json).data.devices[0].serialNumber"
') do set SERIALNUMBER=%%A
if "%SERIALNUMBER%"=="" (
echo ERROR: No serial number found.
pause
exit /b 1
)
echo Using Serial Number: %SERIALNUMBER%
echo Flashing: ERASE ALL + program + reset (FDS will be reset!)
REM program (ERASE_ALL: chip erase -> FDS/bond data all cleared)
nrfutil device program --firmware hex\firmware_all.hex --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 (
echo.
echo [WARNING] Program failed - device may have Readback Protection enabled.
echo Recover will ERASE ALL flash including FDS data.
echo.
set /p RECOVER_YN="Recover and retry? (Y/N): "
if /i "!RECOVER_YN!"=="Y" (
echo Recovering device...
nrfutil device recover --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
echo Re-programming...
nrfutil device program --firmware hex\firmware_all.hex --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
) else (
goto flash_fail
)
)
REM reset
nrfutil device reset --serial-number %SERIALNUMBER%
if %errorlevel% neq 0 goto flash_fail
goto flash_success
:flash_fail
echo ERROR: Flashing failed.
pause
exit /b 1
:flash_success
REM -----------------------------------------------------
REM NOTE: Readback Protection SKIPPED for debugging
REM -----------------------------------------------------
echo ==========================================
echo Programming Complete! (ERASE ALL)
echo No Readback Protection Applied
echo FDS/Bond data cleared (factory default)
echo %date% %time%
echo ==========================================
pause
endlocal
@@ -119,20 +119,20 @@ int setup_imu_device(struct inv_imu_serif *icm_serif)
/* IMU 드라이버 초기화 — 시리얼 인터페이스 연결 및 콜백 함수 등록 */
rc = inv_imu_init(&icm_driver, icm_serif, imu_callback);
if (rc != INV_ERROR_SUCCESS) {
printf("!!! ERROR : Failed to initialize IMU!\r\n");
DBG_PRINTF("!!! ERROR : Failed to initialize IMU!\r\n");
return rc;
}
/* WHOAMI 레지스터 읽기 — 디바이스 존재 및 통신 확인 */
rc = inv_imu_get_who_am_i(&icm_driver, &who_am_i);
if (rc != INV_ERROR_SUCCESS) {
printf("!!! ERROR : Failed to read whoami!\r\n");
DBG_PRINTF("!!! ERROR : Failed to read whoami!\r\n");
return rc;
}
/* WHOAMI 값 검증 — ICM42670P의 경우 0x67이어야 함 */
if (who_am_i != ICM_WHOAMI) {
printf("!!! ERROR : Bad WHOAMI value! Read 0x%02x, expected 0x%02x\r\n", who_am_i, ICM_WHOAMI);
DBG_PRINTF("!!! ERROR : Bad WHOAMI value! Read 0x%02x, expected 0x%02x\r\n", who_am_i, ICM_WHOAMI);
return INV_ERROR;
}
@@ -354,7 +354,7 @@ void imu_callback(inv_imu_sensor_event_t *event)
* 스케일링된 데이터를 UART로 출력
*/
if (event->sensor_mask & (1 << INV_SENSOR_ACCEL) && event->sensor_mask & (1 << INV_SENSOR_GYRO))
printf("%u: %.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f\r\n",
DBG_PRINTF("%u: %.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f\r\n",
(uint32_t)timestamp,
accel_g[0], accel_g[1], accel_g[2],
temp_degc,
@@ -381,7 +381,7 @@ void imu_callback(inv_imu_sensor_event_t *event)
/* UART 모드: "Tp" 접두사로 6축 데이터를 텍스트 형식으로 출력 */
else if(cmd_type_t == CMD_UART) {
printf("Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
//DBG_PRINTF("Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
}
/*
@@ -399,7 +399,7 @@ void imu_callback(inv_imu_sensor_event_t *event)
ssp_data[5] = (uint16_t)gyro[2];
format_data(imu_bin_buffer, "rsp:", ssp_data,12);
printf("Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
//DBG_PRINTF("Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
dr_binary_tx_safe(imu_bin_buffer,8);
if(custom_add_data==true) {
@@ -472,7 +472,10 @@ extern const nrfx_twi_t m_twi_icm42670;
#define IMU_I2C_ADDR 0x68
#define REG_ACCEL_X1 0x0B /* ACCEL_DATA_X1 — 가속도 X축 상위 바이트 레지스터 */
/* Direct IMU register read — raw I2C, no DRDY, sends rsp: via BLE */
/* --------------------------------------------------------------------------------------
* Direct IMU register read — raw I2C, no DRDY, sends rsp: via BLE
* 직접 I2C로 레지스터 읽는 방식 (인터럽트 X, IMU 드라이버 API X)
* -------------------------------------------------------------------------------------- */
int imu_read_direct(void)
{
uint8_t raw[12]; /* 가속도 6바이트 + 자이로 6바이트 */
@@ -512,12 +515,17 @@ int imu_read_direct(void)
/* ACCEL_DATA_X1(0x0B)부터 12바이트 연속 읽기 (0x0B~0x16) */
reg = REG_ACCEL_X1;
ret = icm42670_twi_tx(IMU_I2C_ADDR, &reg, 1, true); /* 레지스터 주소 전송 (STOP 없음) */
if (ret) {
if (ret)
{
DBG_PRINTF("[IMU] tx FAIL %u\r\n", ret);
return -1;
}
ret = icm42670_twi_rx(IMU_I2C_ADDR, raw, 12); /* 12바이트 데이터 수신 */
if (ret) {
if (ret)
{
DBG_PRINTF("[IMU] rx FAIL %u\r\n", ret);
return -2;
}
@@ -561,6 +569,7 @@ int imu_read_direct(void)
/* 일반 모드: "rsp:" 태그로 BLE 즉시 전송 */
format_data(imu_bin_buffer, "rsp:", ssp_data, 12);
dr_binary_tx_safe(imu_bin_buffer, 8);
DBG_PRINTF("0");
}
/* IMU 슬립 모드: PWR_MGMT0 = 0x00 → 가속도/자이로 모두 OFF (전력 절감) */
@@ -6,6 +6,13 @@
* @brief
******************************************************************************/
/*******************************************************************************
* 2026.03.26 jhChun
* 현재 이 파일은 실제 런타임에 실행되지 않고 있음
* 인터럽트 방식 대신 app_raw.c imu_read_direct()에서 직접 레지스터 읽는 방식 사용 중
* 추후 필요 여부에 따라 정리 예정
******************************************************************************/
/*******************************************************************************
* [모듈 개요] ICM42670P 메인 초기화 및 폴링 루프
*
@@ -196,8 +203,7 @@ int icm42670_init(void)
* 3) 플래그가 세팅되어 있으면 센서 데이터 읽기 (get_imu_data)
* 4) 데이터 읽기 완료 후 플래그 클리어
*
* 참고: 인터럽트 기반 폴링 방식으로, ISR에서는 플래그만 세팅하고
* 실제 I2C 통신은 메인 컨텍스트에서 수행한다.
* 참고: 인터럽트 기반 폴링 방식으로, ISR에서는 플래그만 세팅하고 실제 I2C 통신은 메인 컨텍스트에서 수행한다.
*/
void icm42670_main(void)
{
@@ -0,0 +1,292 @@
/*******************************************************************************
* @file led_control.c
* @brief LED 직접 제어 드라이버 (BSP 미사용)
* @date 2026-03-30
*
* app_timer 1개로 2색 LED(녹색/주황)의 복합 blink 패턴 구현
* 단순 on/off 상태는 타이머 없이 즉시 GPIO 제어
* 복합 패턴(에러 등)은 phase 기반 state machine으로 처리
******************************************************************************/
#include "led_control.h"
#include "nrf_gpio.h"
#include "app_timer.h"
/*==============================================================================
* 내부 상수
*============================================================================*/
#define MS_TO_TICKS(ms) APP_TIMER_TICKS(ms)
/*==============================================================================
* 색상
*============================================================================*/
#define COLOR_NONE 0
#define COLOR_GREEN 1
#define COLOR_ORANGE 2
/*==============================================================================
* 에러 패턴 상수 (No.7)
* 3Hz 깜빡 3회 = 166ms on + 166ms off × 3 = ~1초, 이후 꺼짐 1초
*============================================================================*/
#define ERROR_BLINK_ON_MS 166
#define ERROR_BLINK_OFF_MS 166
#define ERROR_BLINK_COUNT 3
#define ERROR_PAUSE_MS 1000
/*==============================================================================
* 패턴 테이블 (단순 blink 용)
*============================================================================*/
typedef struct
{
uint32_t on_ms; /* LED 켜짐 시간 (ms) */
uint32_t off_ms; /* LED 꺼짐 시간 (ms) */
uint8_t color; /* COLOR_GREEN 또는 COLOR_ORANGE */
bool repeat; /* true: 무한 반복, false: 1회 후 OFF */
} led_pattern_t;
static const led_pattern_t m_patterns[LED_STATE_COUNT] =
{
[LED_STATE_OFF] = { 0, 0, COLOR_NONE, false },
[LED_STATE_POWER_ON] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → 유지 */
[LED_STATE_POWER_OFF] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → OFF */
[LED_STATE_ADVERTISING] = { 500, 500, COLOR_GREEN, true }, /* 초록 점멸 1초 */
[LED_STATE_DETACH_WARNING] = { 1000, 3000, COLOR_GREEN, true }, /* 초록 1초 on / 3초 off */
[LED_STATE_ALIGN_SEARCHING] = { 1000, 1000, COLOR_ORANGE, true }, /* 주황 점멸 1초 */
[LED_STATE_ALIGN_COMPLETE] = { 3000, 1000, COLOR_GREEN, true }, /* 초록 3초 on / 1초 off */
[LED_STATE_ERROR] = { 0, 0, COLOR_ORANGE, true } /* 별도 state machine */
};
/*==============================================================================
* 모듈 변수
*============================================================================*/
APP_TIMER_DEF(m_led_timer);
static led_state_t m_current_state = LED_STATE_OFF;
static bool m_phase_on; /* true: LED 켜진 구간, false: 꺼진 구간 */
/* 에러 패턴 전용 */
static uint8_t m_error_blink_cnt; /* 현재까지 깜빡인 횟수 */
static uint8_t m_error_phase; /* 0: blink-on, 1: blink-off, 2: pause */
/*==============================================================================
* GPIO 헬퍼
*============================================================================*/
static inline void led_green_on(void)
{
#if LED_ACTIVE_LOW
nrf_gpio_pin_clear(LED_PIN_GREEN);
#else
nrf_gpio_pin_set(LED_PIN_GREEN);
#endif
}
static inline void led_green_off(void)
{
#if LED_ACTIVE_LOW
nrf_gpio_pin_set(LED_PIN_GREEN);
#else
nrf_gpio_pin_clear(LED_PIN_GREEN);
#endif
}
static inline void led_orange_on(void)
{
#if LED_ACTIVE_LOW
nrf_gpio_pin_clear(LED_PIN_ORANGE);
#else
nrf_gpio_pin_set(LED_PIN_ORANGE);
#endif
}
static inline void led_orange_off(void)
{
#if LED_ACTIVE_LOW
nrf_gpio_pin_set(LED_PIN_ORANGE);
#else
nrf_gpio_pin_clear(LED_PIN_ORANGE);
#endif
}
static void led_all_off(void)
{
led_green_off();
led_orange_off();
}
static void led_color_on(uint8_t color)
{
led_all_off();
if (color == COLOR_GREEN) led_green_on();
else if (color == COLOR_ORANGE) led_orange_on();
}
/*==============================================================================
* 타이머 시작 헬퍼
*============================================================================*/
static void timer_start_ms(uint32_t ms)
{
if (ms == 0) return;
app_timer_start(m_led_timer, MS_TO_TICKS(ms), NULL);
}
/*==============================================================================
* 에러 패턴 state machine (No.7)
* phase 0: LED ON (166ms) → phase 1
* phase 1: LED OFF (166ms) → cnt++ → cnt<3 ? phase 0 : phase 2
* phase 2: PAUSE (1000ms) → cnt=0, phase 0
*============================================================================*/
static void error_pattern_start(void)
{
m_error_blink_cnt = 0;
m_error_phase = 0;
led_color_on(COLOR_ORANGE);
timer_start_ms(ERROR_BLINK_ON_MS);
}
static void error_pattern_tick(void)
{
switch (m_error_phase)
{
case 0: /* ON 구간 끝 → OFF */
led_all_off();
m_error_phase = 1;
timer_start_ms(ERROR_BLINK_OFF_MS);
break;
case 1: /* OFF 구간 끝 */
m_error_blink_cnt++;
if (m_error_blink_cnt < ERROR_BLINK_COUNT)
{
/* 다시 ON */
m_error_phase = 0;
led_color_on(COLOR_ORANGE);
timer_start_ms(ERROR_BLINK_ON_MS);
}
else
{
/* 3회 완료 → pause */
m_error_phase = 2;
timer_start_ms(ERROR_PAUSE_MS);
}
break;
case 2: /* pause 끝 → 처음부터 */
m_error_blink_cnt = 0;
m_error_phase = 0;
led_color_on(COLOR_ORANGE);
timer_start_ms(ERROR_BLINK_ON_MS);
break;
default:
break;
}
}
/*==============================================================================
* 타이머 콜백
*============================================================================*/
static void led_timer_handler(void * p_context)
{
(void)p_context;
/* 에러 상태는 별도 처리 */
if (m_current_state == LED_STATE_ERROR)
{
error_pattern_tick();
return;
}
const led_pattern_t * p = &m_patterns[m_current_state];
if (m_phase_on)
{
/* ON→OFF 전환 */
led_all_off();
m_phase_on = false;
if (p->off_ms > 0)
{
timer_start_ms(p->off_ms);
}
else if (!p->repeat)
{
/* 1회성: off_ms == 0 이면 그냥 유지 (POWER_ON) 또는 끄기 (POWER_OFF) */
if (m_current_state == LED_STATE_POWER_OFF)
{
led_all_off();
m_current_state = LED_STATE_OFF;
}
/* POWER_ON: 점등 유지 상태 → 타이머 x */
}
}
else
{
/* OFF → ON 전환 */
if (p->repeat)
{
led_color_on(p->color);
m_phase_on = true;
timer_start_ms(p->on_ms);
}
/* repeat == false && off_ms > 0 인 경우는 현재 없음 */
}
}
/*==============================================================================
* 공개 함수
*============================================================================*/
void led_init(void)
{
/* GPIO 출력 설정 */
nrf_gpio_cfg_output(LED_PIN_GREEN);
nrf_gpio_cfg_output(LED_PIN_ORANGE);
led_all_off();
/* 타이머 생성 (single-shot) */
app_timer_create(&m_led_timer, APP_TIMER_MODE_SINGLE_SHOT, led_timer_handler);
m_current_state = LED_STATE_OFF;
}
void led_set_state(led_state_t state)
{
if (state >= LED_STATE_COUNT) return;
/* 이전 패턴 중단 */
app_timer_stop(m_led_timer);
led_all_off();
m_current_state = state;
m_phase_on = false;
const led_pattern_t * p = &m_patterns[state];
switch (state)
{
case LED_STATE_OFF:
/* 이미 all off */
break;
/* 에러 패턴: 별도 state machine */
case LED_STATE_ERROR:
error_pattern_start();
break;
/* 그 외: on 구간부터 시작 */
default:
led_color_on(p->color);
m_phase_on = true;
if (p->on_ms > 0)
{
timer_start_ms(p->on_ms);
}
break;
}
}
led_state_t led_get_state(void)
{
return m_current_state;
}
@@ -0,0 +1,52 @@
/*******************************************************************************
* @file led_control.h
* @brief LED 직접 제어 드라이버 (BSP 미사용)
* @date 2026-03-30
*
* 녹색(P0.12) + 주황(P0.29) 2색 LED를 app_timer 기반으로 제어
* 각 상태별 on/off 시간, 색상, 반복 패턴을 테이블로 관리
******************************************************************************/
#ifndef LED_CONTROL_H__
#define LED_CONTROL_H__
#include <stdint.h>
#include <stdbool.h>
/*==============================================================================
* LED 핀 정의
*============================================================================*/
#define LED_PIN_GREEN NRF_GPIO_PIN_MAP(0, 12) /* 녹색 LED */
#define LED_PIN_ORANGE NRF_GPIO_PIN_MAP(0, 29) /* 주황 LED */
#define LED_ACTIVE_LOW 1 /* 1 = Active Low (GPIO LOW에서 LED 켜짐) */
/*==============================================================================
* LED 상태 열거형
*============================================================================*/
typedef enum
{
LED_STATE_OFF = 0, /* 모든 LED 소등 */
LED_STATE_POWER_ON, /* 1: 전원 ON - 초록 소등 → 녹색 점등 2초 */
LED_STATE_POWER_OFF, /* 2: 전원 OFF - 초록 점등 2초 → 소등 */
LED_STATE_ADVERTISING, /* 3: 블루투스 스캐닝 - 초록 점멸 1초 간격(반복) */
LED_STATE_DETACH_WARNING, /* 4: 정상 작동중(미부착 시) - 초록 점등 1초 / 소등 3초 (반복) */
LED_STATE_ALIGN_SEARCHING, /* 5: 정렬모드(탐지중) - 주황 점멸 1초 간격 (반복) */
LED_STATE_ALIGN_COMPLETE, /* 6: 정렬모드(탐지 완료) - 초록 점등 3초 / 소등 1초 */
LED_STATE_ERROR, /* 7: 오류 발생 - 주황 3Hz 깜빡 3회 / 꺼짐 1초 (반복) */
LED_STATE_COUNT /* 배열 크기 */
} led_state_t;
/*==============================================================================
* 공개 함수
*============================================================================*/
/** @brief LED GPIO 초기화 + 타이머 생성 - main()에서 1회 호출 */
void led_init(void);
/** @brief LED 상태 변경 - 이전 패턴을 즉시 중단하고 새 패턴 시작 */
void led_set_state(led_state_t state);
/** @brief 현재 LED 상태 조회 */
led_state_t led_get_state(void);
#endif /* LED_CONTROL_H__ */
@@ -17,17 +17,12 @@
* - 전원: POWER_HOLD(P0.8) 자가유지 회로, POWER_BUTTON(P1.8) 입력
*
* [소프트웨어 부트 시퀀스 — main() 함수]
* Phase 0: 전원 유지 (power_hold_init → P0.8 HIGH)
* Phase 1: UART 초기화 (1Mbps), 로그 초기화
* Phase 2: GPIO 초기화 (DEBUG_MINIMAL_BOOT에 따라 분기)
* Phase 3: 타이머 초기화 (app_timer, 배터리, 전원 시퀀스)
* Phase 4: 설정 로드 (기본값 → FDS에서 덮어쓰기)
* Phase 5: 버튼/LED BSP 초기화
* Phase 6: BLE 스택 (SoftDevice → GAP → GATT → NUS → Advertising)
* Phase 6.5: FDS(Flash Data Storage) 초기화 및 설정 로드
* Phase 7: BLE 보안 (Peer Manager, LESC/정적 패스키)
* Phase 8: 부트 완료 → 전원 버튼 상태머신(main_s) 시작
* Phase 9: idle 루프 (nrf_pwr_mgmt_run)
* Phase 1: 하드웨어 기본 (전원유지, 로그, GPIO, 타이머, 설정, 버튼/LED)
* Phase 2: BLE 스택 (SoftDevice, 전원관리, DC-DC)
* Phase 3: FDS + 설정 로드
* Phase 4: BLE 프로토콜 (GAP, GATT, NUS, Advertising, 보안)
* Phase 5: 애플리케이션 (파서, 피에조)
* Boot 완료 → 전원 버튼 상태머신(main_s) → idle 루프
*
* [전원 버튼 상태머신 — main_s() 콜백, 5ms 간격]
* - 짧은 눌림 (<1.5초, cnt_s<150): 전원 OFF
@@ -42,8 +37,6 @@
* 5. dr_binary_tx_safe()로 CRC16 추가 후 BLE 전송 (최대 100회 재시도)
*
* [빌드 모드]
* DEBUG_MINIMAL_BOOT = 1 : 전원+BLE만 (센서/EEPROM 초기화 생략, 개발용)
* DEBUG_MINIMAL_BOOT = 0 : 전체 초기화 (양산용)
* BLE_DEV_MODE = 1 : 보안 없음 (빠른 페어링, 개발용)
* BLE_DEV_MODE = 0 : 정적 패스키 + MITM 보호 (양산용)
******************************************************************************/
@@ -97,7 +90,7 @@
#endif
/* ── 애플리케이션 모듈 헤더 ── */
#include "system_interface.h" /* 시스템 인터페이스 (센서/EEPROM 제어 추상화) */
#include "system_interface.h" /* 시스템 인터페이스 (IMU 센서 I2C 통신) */
#include "main.h" /* 메인 헤더 (전역 구조체, 상수, 외부 함수 선언) */
#include "app_raw_main.h" /* 센서 원시 데이터 처리 모듈 */
#include "main_timer.h" /* 메인 이벤트 루프 타이머 (10ms 주기) */
@@ -121,18 +114,15 @@
#include "debug_print.h" /* 디버그 출력 매크로 (DBG_PRINTF) */
#include "fstorage.h" /* Flash Storage 래퍼 (FDS 초기화/저장/로드) */
#include "dr_piezo.h" /* 피에조 초음파 드라이버 */
#include "led_control.h" /* LED 직접 제어 드라이버 (BSP 미사용) */
#define HARDWARE_VERSION "VB0HW0000"
#define FIRMWARE_VERSION "VB0FW0000"
#define FIRMWARE_SERIAL_NO "VB026030000"
/*==============================================================================
* 빌드 설정
* DEBUG_MINIMAL_BOOT 삭제 26.03.26 jhChun
*============================================================================*/
#define BLE_DEV_MODE 1 /* 1: 개발 모드 (보안 없음), 0: 양산 모드 (패스키 필수) */
#define DEBUG_MINIMAL_BOOT 1 /* 1: 최소 부팅 (전원+BLE만), 0: 전체 부팅 (센서 포함) */
/*==============================================================================
* 하드웨어 핀 정의
@@ -154,9 +144,9 @@
#define APP_ADV_DURATION 18000 /* 광고 지속시간: 18000 x 10ms = 3분 → 타임아웃 시 슬립 */
#endif
#define MIN_CONN_INTERVAL MSEC_TO_UNITS(20, UNIT_1_25_MS) /* 최소 연결 간격: 20ms */
#define MAX_CONN_INTERVAL MSEC_TO_UNITS(75, UNIT_1_25_MS) /* 최대 연결 간격: 75ms */
#define SLAVE_LATENCY 4 /* 슬레이브 지연: 0 (매 연결 이벤트마다 응답) */
#define MIN_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /* 최소 연결 간격: 30ms */
#define MAX_CONN_INTERVAL MSEC_TO_UNITS(30, UNIT_1_25_MS) /* 최대 연결 간격: 30ms */
#define SLAVE_LATENCY 0 /* 슬레이브 지연: 0 (매 연결 이벤트마다 응답) */
#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /* 연결 감독 타임아웃: 4초 */
#define FIRST_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(5000) /* 첫 파라미터 갱신 요청까지 5초 대기 */
#define NEXT_CONN_PARAMS_UPDATE_DELAY APP_TIMER_TICKS(30000) /* 이후 갱신 요청 간격: 30초 */
@@ -271,8 +261,13 @@ bool erase_bonds; /* 본딩 삭제 플래그 (부팅 시 버튼 상
volatile bool ble_connection_st; /* BLE 연결 상태 (1=연결됨, 0=미연결) */
volatile bool data_tx_in_progress = false; /* 바이너리 TX 진행 중 플래그 */
/* ── BLE TX 비동기 재시도 상태 ── */
static volatile bool s_tx_pending = false; /* TX 재시도 대기 중 */
static uint8_t s_tx_pending_buf[BLE_NUS_MAX_DATA_LEN]; /* 대기 중인 패킷 (CRC 포함) */
static uint16_t s_tx_pending_len = 0; /* 대기 중인 패킷 길이 */
char m_static_passkey[PASSKEY_LENGTH] = "123456"; /* 정적 패스키 (6자리, FDS에서 로드) */
char m_static_passkey[PASSKEY_LENGTH] = DEFAULT_PASSKEY; /* 정적 패스키 (6자리, FDS에서 로드) */
char SERIAL_NO[SERIAL_NO_LENGTH]; /* 시리얼 번호 (BLE 디바이스 이름으로 사용) */
char HW_NO[HW_NO_LENGTH]; /* 하드웨어 번호 (FDS 저장/읽기) */
bool bond_data_delete; /* 본딩 데이터 삭제 요청 플래그 */
@@ -335,38 +330,17 @@ static void power_control_handler(on_off_cont_t device_power_st)
*============================================================================*/
/**
* @brief 최소 GPIO 초기화 (DEBUG_MINIMAL_BOOT 모드용)
* @brief GPIO 초기화
*
* 전원 버튼 입력과 전원 유지 핀만 설정
* 센서 관련 GPIO는 초기화하지 않음
*/
static void minimal_gpio_init(void)
static void gpio_init(void)
{
nrf_gpio_cfg_input(POWER_BUTTON, NRF_GPIO_PIN_NOPULL);
nrf_gpio_cfg_output(POWER_HOLD);
nrf_gpio_pin_set(POWER_HOLD);
power_gpio_init();
DBG_PRINTF("[GPIO] Minimal OK (BTN=%d)\r\n", nrf_gpio_pin_read(POWER_BUTTON));
DBG_PRINTF("[GPIO] OK (BTN=%d)\r\n", nrf_gpio_pin_read(POWER_BUTTON));
}
#if !DEBUG_MINIMAL_BOOT
/**
* @brief 전체 GPIO 초기화 (양산 모드, DEBUG_MINIMAL_BOOT=0)
*
* minimal_gpio_init()를 먼저 호출한 뒤, EEPROM 전원을 OFF하여
* 불필요한 전력 소모를 방지한다.
*/
static void full_gpio_init(void)
{
minimal_gpio_init();
eeprom_control(OFF);
DBG_PRINTF("[GPIO] Full OK\r\n");
}
#endif
/*==============================================================================
* 설정 로드
* FDS(Flash Data Storage)에서 설정을 읽기 전에 기본값을 먼저 로드
@@ -382,10 +356,10 @@ static void full_gpio_init(void)
static void load_default_config(void)
{
memset(SERIAL_NO, 0, SERIAL_NO_LENGTH);
memcpy(SERIAL_NO, FIRMWARE_SERIAL_NO, strlen(FIRMWARE_SERIAL_NO));
memcpy(SERIAL_NO, SERIAL_NUMBER, strlen(SERIAL_NUMBER));
memset(m_static_passkey, 0, PASSKEY_LENGTH);
memcpy(m_static_passkey, "123456", PASSKEY_LENGTH);
memcpy(m_static_passkey, DEFAULT_PASSKEY, PASSKEY_LENGTH);
m_reset_status = 1;
bond_data_delete = 1;
@@ -419,8 +393,8 @@ static void load_flash_config(void)
if (m_config.serial_no[0] == 0 || m_config.serial_no[0] == (char)0xFF)
{
memset(m_config.serial_no, 0, SERIAL_NO_LENGTH);
memcpy(m_config.serial_no, FIRMWARE_SERIAL_NO, strlen(FIRMWARE_SERIAL_NO));
DBG_PRINTF("[CFG] S/N empty, set default: %s\r\n", FIRMWARE_SERIAL_NO);
memcpy(m_config.serial_no, SERIAL_NUMBER, strlen(SERIAL_NUMBER));
DBG_PRINTF("[CFG] S/N empty, set default: %s\r\n", SERIAL_NUMBER);
m_need_save_defaults = true;
}
@@ -466,7 +440,7 @@ static void t_power_off_timeout_handler(void * p_context)
UNUSED_PARAMETER(p_context);
APP_ERROR_CHECK(app_timer_stop(m_power_off_delay_timer_id));
DBG_PRINTF("[PWR] Off timeout\r\n");
bsp_indication_set(BSP_INDICATE_USER_STATE_OFF); /* LED OFF */
led_set_state(LED_STATE_OFF); /* LED OFF */
power_control_handler(OFF); /* P0.8 LOW → 전원 차단 */
}
@@ -637,6 +611,23 @@ static void nus_data_handler(ble_nus_evt_t * p_evt)
cmd_type_t = CMD_BLE;
ble_got_new_data = true;
/* Central이 연결 간격을 늘렸을 수 있으므로 빠른 간격 재요청 (30초에 1회) */
{
static uint32_t last_update_tick = 0;
uint32_t now_tick = app_timer_cnt_get();
if (last_update_tick == 0 ||
app_timer_cnt_diff_compute(now_tick, last_update_tick) >= APP_TIMER_TICKS(30000))
{
ble_gap_conn_params_t conn_params;
conn_params.min_conn_interval = MIN_CONN_INTERVAL;
conn_params.max_conn_interval = MAX_CONN_INTERVAL;
conn_params.slave_latency = SLAVE_LATENCY;
conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT;
sd_ble_gap_conn_param_update(m_conn_handle, &conn_params);
last_update_tick = now_tick;
}
}
/* 콜백에서는 복사만 하고, 메인 루프에서 처리 */
if (p_evt->params.rx_data.length <= BLE_NUS_MAX_DATA_LEN)
{
@@ -651,6 +642,17 @@ static void nus_data_handler(ble_nus_evt_t * p_evt)
{
maa_async_on_tx_ready();
}
/* pending 패킷 재시도 (dr_binary_tx_safe 비동기) */
else if (s_tx_pending)
{
uint16_t send_len = s_tx_pending_len;
uint32_t err = ble_nus_data_send(&m_nus, s_tx_pending_buf, &send_len, m_conn_handle);
if (err == NRF_SUCCESS)
{
s_tx_pending = false;
}
/* NRF_ERROR_RESOURCES → 다음 TX_RDY에서 다시 시도 */
}
}
}
@@ -797,7 +799,7 @@ static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
switch (ble_adv_evt)
{
case BLE_ADV_EVT_FAST:
bsp_indication_set(BSP_INDICATE_ADVERTISING);
led_set_state(LED_STATE_ADVERTISING);
DBG_PRINTF("[ADV] Fast\r\n");
break;
case BLE_ADV_EVT_IDLE:
@@ -906,10 +908,7 @@ static void pm_evt_handler(pm_evt_t const * p_evt)
uint32_t return_code;
ret_code_t err_code;
/* SDK 기본 핸들러 체인 호출 */
pm_handler_on_pm_evt(p_evt);
pm_handler_disconnect_on_sec_failure(p_evt);
pm_handler_flash_clean(p_evt);
/* SDK 기본 핸들러 + 보안 처리 (ble_quick_security 내부에서 SDK 핸들러 호출) */
ble_security_quick_pm_handler(p_evt);
switch (p_evt->evt_id)
@@ -940,32 +939,21 @@ static void pm_evt_handler(pm_evt_t const * p_evt)
}
break;
/* 보안 연결 실패 → PIN/키 누락 시 강제 재페어링 시도 */
/* 보안 연결 실패 (retry/disconnect는 ble_quick_security에서 처리) */
case PM_EVT_CONN_SEC_FAILED:
DBG_PRINTF("[PM] Sec failed\r\n");
if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING)
{
err_code = pm_conn_secure(p_evt->conn_handle, true);
if (err_code != NRF_ERROR_INVALID_STATE)
{
APP_ERROR_CHECK(err_code);
}
}
break;
/* 모든 본딩 삭제 완료 → 광고 재시작 */
/* 모든 본딩 삭제 완료 → 활성 연결이 없을 때만 광고 재시작 */
case PM_EVT_PEERS_DELETE_SUCCEEDED:
DBG_PRINTF("[PM] Bonds erased\r\n");
if (m_conn_handle == BLE_CONN_HANDLE_INVALID) {
advertising_start(false);
break;
/* 보안 설정 요청 → 재페어링 허용 */
case PM_EVT_CONN_SEC_CONFIG_REQ:
{
pm_conn_sec_config_t conn_sec_config = {.allow_repairing = true};
pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
}
break;
/* PM_EVT_CONN_SEC_CONFIG_REQ → ble_quick_security에서 처리 */
/* 피어 데이터(본딩 정보) 갱신 성공 → 피어 BLE 주소 저장 */
case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
{
@@ -1028,19 +1016,23 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
#endif
#if FEATURE_SECURE_CONNECTION
if (!ble_security_is_dev_mode()) {
pm_handler_secure_on_connection(p_ble_evt);
}
#endif
switch (p_ble_evt->header.evt_id)
{
/* BLE 연결이 끊어지는 경우 */
case BLE_GAP_EVT_DISCONNECTED:
DBG_PRINTF("[BLE] Disconnected\r\n");
DBG_PRINTF("[BLE] Disconnected (reason 0x%02X)\r\n",
p_ble_evt->evt.gap_evt.params.disconnected.reason);
ble_connection_st = 0;
m_conn_handle = BLE_CONN_HANDLE_INVALID;
m_tx_in_progress = false;
maa_async_abort(); // 비동기 측정 상태에 의한 먹통 현상 방지
s_tx_pending = false; // pending TX 클리어
if (device_status == true)
{
@@ -1065,12 +1057,18 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, m_conn_handle, 8);
err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
// 2M PHY 요청 (지원 안 되면 자동으로 1M 유지)
{
ble_gap_phys_t const phys = {
.rx_phys = BLE_GAP_PHY_2MBPS,
.tx_phys = BLE_GAP_PHY_2MBPS,
};
err_code = sd_ble_gap_phy_update(m_conn_handle, &phys);
APP_ERROR_CHECK(err_code);
}
led_set_state(LED_STATE_OFF); /* 연결 완료 → LED OFF */
#if DEBUG_MINIMAL_BOOT
DBG_PRINTF("[BLE] Minimal mode\r\n");
#endif
break;
case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
@@ -1084,6 +1082,15 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
}
break;
case BLE_GAP_EVT_PHY_UPDATE:
{
ble_gap_evt_phy_update_t const * p_phy = &p_ble_evt->evt.gap_evt.params.phy_update;
DBG_PRINTF("[BLE] PHY updated: TX=%s, RX=%s\r\n",
p_phy->tx_phy == BLE_GAP_PHY_2MBPS ? "2M" : "1M",
p_phy->rx_phy == BLE_GAP_PHY_2MBPS ? "2M" : "1M");
}
break;
case BLE_GATTC_EVT_TIMEOUT:
case BLE_GATTS_EVT_TIMEOUT:
DBG_PRINTF("[BLE] Timeout\r\n");
@@ -1098,6 +1105,14 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
NULL, NULL);
APP_ERROR_CHECK(err_code);
#else
if (ble_security_is_dev_mode()) {
err_code = sd_ble_gap_sec_params_reply(m_conn_handle,
BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP,
NULL, NULL);
APP_ERROR_CHECK(err_code);
DBG_PRINTF("DEV: Rejected security request\r\n");
}
#endif
break;
@@ -1183,7 +1198,7 @@ void bsp_event_handler(bsp_event_t event)
if (processing == false)
{
DBG_PRINTF("[BSP] Power\r\n");
bsp_indication_set(BSP_INDICATE_USER_STATE_ON);
led_set_state(LED_STATE_POWER_OFF);
go_device_power_off = true;
main_timer_start();
}
@@ -1198,14 +1213,16 @@ void bsp_event_handler(bsp_event_t event)
* 유틸리티 함수
*============================================================================*/
/** @brief BSP 버튼/LED 초기화 (부트 Phase 5) */
/** @brief BSP 버튼 초기화 + LED 직접 초기화 (부트 Phase 5) */
static void buttons_leds_init(bool * p_erase_bonds)
{
bsp_event_t startup_event;
uint32_t err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler);
uint32_t err_code = bsp_init(BSP_INIT_BUTTONS, bsp_event_handler);
APP_ERROR_CHECK(err_code);
led_init();
err_code = bsp_btn_ble_init(NULL, &startup_event);
APP_ERROR_CHECK(err_code);
@@ -1334,7 +1351,7 @@ void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
*/
void sleep_mode_enter(void)
{
bsp_indication_set(BSP_INDICATE_USER_STATE_ON);
led_set_state(LED_STATE_POWER_OFF);
DBG_PRINTF("[SYS] Sleep\r\n");
APP_ERROR_CHECK(app_timer_start(m_power_off_delay_timer_id, APP_TIMER_TICKS(POWER_OFF_DELAY), NULL));
}
@@ -1347,7 +1364,7 @@ void sleep_mode_enter(void)
*/
void device_power_off(void)
{
bsp_indication_set(BSP_INDICATE_USER_STATE_ON);
led_set_state(LED_STATE_POWER_OFF);
APP_ERROR_CHECK(app_timer_start(m_power_off_delay_timer_id, APP_TIMER_TICKS(POWER_OFF_DELAY), NULL));
}
@@ -1530,78 +1547,55 @@ void param_error(const char *cmd)
dr_binary_tx_safe(ble_bin_buffer, 3);
}
/* 기존 binary_tx_handler 함수 삭제 및 호출처 교체 - jhChun 26.03.16 */
void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length) // BLE로 바이너리 데이터를 안전하게 전송하는 함수
void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length)
{
uint32_t err_code;
static uint8_t tx_buffer[BLE_NUS_MAX_DATA_LEN] = {0};
uint16_t retry_count = 0;
const uint16_t MAX_RETRIES = 100; /* 최대 재시도 횟수 (~500ms, 5ms x 100) */
const uint16_t MAX_RETRIES = 20; /* 최대 재시도: 2ms × 20 = 40ms (기존 5ms × 100 = 500ms) */
if (ble_connection_st == 0) return; // 연결 확인
if (ble_connection_st == 0) return;
data_tx_in_progress = true;
if (length * sizeof(uint16_t) > (BLE_NUS_MAX_DATA_LEN - 2)) { // 길이 검증 (CRC 2바이트 공간 확보)
data_tx_in_progress = false;
return;
}
if (length * sizeof(uint16_t) > (BLE_NUS_MAX_DATA_LEN - 2)) return;
if (ble_connection_st == BLE_CONNECTED_ST)
{
memcpy(tx_buffer, ble_bin_buff, length * sizeof(uint16_t));
uint16_t crc = crc16_compute(tx_buffer, length * sizeof(uint16_t), NULL); // 버퍼 복사
uint16_t crc = crc16_compute(tx_buffer, length * sizeof(uint16_t), NULL);
tx_buffer[length * sizeof(uint16_t)] = (uint8_t)(crc & 0xFF);
tx_buffer[length * sizeof(uint16_t) + 1] = (uint8_t)((crc >> 8) & 0xFF); // CRC16 2바이트 추가
tx_buffer[length * sizeof(uint16_t) + 1] = (uint8_t)((crc >> 8) & 0xFF);
uint16_t total_len = length * sizeof(uint16_t) + 2;
/* 재시도 루프 - 재시도 사이에 SoftDevice가 이벤트를 처리할 수 있도록 함 */
do {
uint16_t send_len = total_len; /* 매 반복마다 리셋 필수 - ble_nus_data_send()가 값을 변경함! */
uint16_t send_len = total_len;
err_code = ble_nus_data_send(&m_nus, tx_buffer, &send_len, m_conn_handle);
if (err_code == NRF_SUCCESS) // 전송 성공 시 루프 탈출
if (err_code == NRF_SUCCESS)
{
break;
return;
}
else if (err_code == NRF_ERROR_RESOURCES) // TX 큐가 가득 찬 경우 5ms 대기 후 재시도 (최대 100회, ~500ms)
else if (err_code == NRF_ERROR_RESOURCES)
{
/* BLE TX 큐 가득 참 - 연결 이벤트가 TX를 완료할 때까지 대기 */
/* BLE 스택이 TX 완료 이벤트를 처리할 수 있도록 짧은 딜레이 */
nrf_delay_ms(5); /* ~5ms 대기하여 TX 슬롯 확보 -> 대기 중 3.2mA 전력 소모 */
nrf_delay_ms(2); /* 기존 5ms → 2ms */
retry_count++;
}
else if (err_code == NRF_ERROR_INVALID_STATE || err_code == NRF_ERROR_NOT_FOUND) // 연결 끊김, 리턴
else if (err_code == NRF_ERROR_INVALID_STATE || err_code == NRF_ERROR_NOT_FOUND)
{
DBG_PRINTF("[BLE TX] Disconnected\r\n");
data_tx_in_progress = false;
return;
}
else // 기타 에러 -> 로그 출력 후 리턴
else
{
DBG_PRINTF("[BLE TX] Err:0x%X\r\n", err_code);
data_tx_in_progress = false;
return;
}
}
while (retry_count < MAX_RETRIES);
} while (retry_count < MAX_RETRIES);
if (retry_count > 0)
if (retry_count >= MAX_RETRIES)
{
DBG_PRINTF("[BLE TX] retry=%u (+%ums)\r\n", retry_count, retry_count * 5);
}
if (retry_count >= MAX_RETRIES){ // 최대 재시도(100회) 초과 시 해당 패킷 드롭, 연결은 유지
DBG_PRINTF("[BLE TX] FAIL %u retries\r\n", retry_count);
data_tx_in_progress = false;
/* ble_connection_st = 0 설정하지 않음 - 해당 패킷만 드롭하고 연결은 유지 */
return;
}
data_tx_in_progress = false;
}
}
@@ -1627,7 +1621,7 @@ static void main_s(void * p_context)
if ((cnt_s < 150) && (m_reset_status != 2))
{
DBG_PRINTF("[BTN] Short->OFF\r\n");
bsp_indication_set(BSP_INDICATE_USER_STATE_OFF);
led_set_state(LED_STATE_OFF);
power_control_handler(OFF);
cnt_s = 0;
}
@@ -1638,7 +1632,7 @@ static void main_s(void * p_context)
nrf_delay_ms(100);
bond_data_delete = true;
m_config.bond_data_delete = (uint8_t)bond_data_delete;
const char pass_init[PASSKEY_LENGTH] = "123456";
const char pass_init[PASSKEY_LENGTH] = DEFAULT_PASSKEY;
memcpy(m_config.static_passkey, pass_init, PASSKEY_LENGTH);
config_save();
nrf_delay_ms(1000);
@@ -1653,30 +1647,13 @@ static void main_s(void * p_context)
power_control_handler(ON);
battery_timer_start();
#if DEBUG_MINIMAL_BOOT
DBG_PRINTF("[BOOT] Minimal\r\n");
#if FEATURE_SECURE_CONNECTION
advertising_start(erase_bonds);
/* DEV 모드: ble_security_quick_init()에서 이미 bond 삭제됨 → 중복 방지 */
advertising_start(!ble_security_is_dev_mode() && (erase_bonds || bond_data_delete));
#else
advertising_start();
#endif
DBG_PRINTF("[BOOT] ADV started\r\n");
#else
DBG_PRINTF("[BOOT] Full\r\n");
icm42670_init();
nrf_delay_ms(2);
#if FEATURE_SECURE_CONNECTION
advertising_start(erase_bonds);
#else
advertising_start();
#endif
m_reset_status = 1;
m_config.reset_status = m_reset_status;
config_save();
#endif
m_reset_status = 1;
DBG_PRINTF("[BOOT] Ready\r\n");
}
@@ -1687,7 +1664,7 @@ static void main_s(void * p_context)
device_reset = false;
if (cnt_s == 150) {
bsp_indication_set(BSP_INDICATE_USER_STATE_ON);
led_set_state(LED_STATE_POWER_ON);
DBG_PRINTF("[BTN] 1.5s\r\n");
}
@@ -1706,113 +1683,114 @@ int main(void)
{
bool erase_bonds_local = false;
// PHASE 0: 전원 유지
/*──────────────────────────────────────────────────────────────
* Phase 1: 하드웨어 기본 초기화 (BLE 무관)
*
* - 전원 자가유지 래치 (P0.8 HIGH)
* - RTT 로그 출력
* - GPIO (전원 버튼 입력)
* - 앱 타이머 (전원 폴링, 배터리, 메인 루프)
* - 기본 설정값 (시리얼 번호, 패스키)
* - 버튼/LED (BSP)
*────────────────────────────────────────────────────────────*/
power_hold_init();
// PHASE 1: 기본 시스템
if (power_off_duble_prohibit) return 0;
cnt_s = 0;
//uart_init(); UART 비활성화
log_init();
DBG_PRINTF("\r\n========================================\r\n");
DBG_PRINTF(" Medithings v1.17 [%s]\r\n", DEBUG_MINIMAL_BOOT ? "MIN" : "FULL");
DBG_PRINTF(" Medithings v1.17\r\n");
DBG_PRINTF("========================================\r\n");
DBG_PRINTF("[0] PWR_HOLD\r\n");
// PHASE 2: GPIO
DBG_PRINTF("[1] GPIO\r\n");
#if DEBUG_MINIMAL_BOOT
minimal_gpio_init();
#else
full_gpio_init();
#endif
DBG_PRINTF("[1] HW Init\r\n");
gpio_init();
info4 = false;
// PHASE 3: 타이머
DBG_PRINTF("[2] Timers\r\n");
timers_init();
// PHASE 4: 설정 (기본값 로드)
DBG_PRINTF("[3] Config (defaults)\r\n");
load_default_config();
// PHASE 5: 버튼/LED
DBG_PRINTF("[4] Buttons\r\n");
buttons_leds_init(&erase_bonds_local);
#if FEATURE_SECURE_CONNECTION
erase_bonds = erase_bonds_local;
#endif
DBG_PRINTF(" gpio/timer/config/btn OK\r\n");
// PHASE 6: BLE 스택
DBG_PRINTF("[5] BLE\r\n");
/*──────────────────────────────────────────────────────────────
* Phase 2: BLE 스택 초기화
*
* - 전원 관리 모듈 (idle 시 WFE/슬립)
* - SoftDevice S140 (BLE 5.0 프로토콜 스택)
* - DC-DC 컨버터 활성화 (SoftDevice 이후 필수)
*────────────────────────────────────────────────────────────*/
DBG_PRINTF("[2] BLE Stack\r\n");
power_management_init();
DBG_PRINTF(" pwr OK\r\n");
ble_stack_init();
DBG_PRINTF(" stack OK\r\n");
/* nRF52840 내장 DC-DC 활성화 — SoftDevice 초기화 후 호출 필수 */
APP_ERROR_CHECK(sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE));
DBG_PRINTF(" dcdc OK\r\n");
DBG_PRINTF(" pwr/stack/dcdc OK\r\n");
// PHASE 6.5: 내장 Flash (FDS) - BLE 스택 이후에 초기화해야 함
DBG_PRINTF("[5.5] FDS\r\n");
/*──────────────────────────────────────────────────────────────
* Phase 3: 내장 플래시 설정 (BLE 스택 이후에 초기화해야 함)
*
* - FDS(Flash Data Storage) 초기화
* - 플래시에서 저장된 설정 읽기 (시리얼, 패스키, 피에조 등)
* - 기본값 위에 플래시 값 덮어쓰기
*────────────────────────────────────────────────────────────*/
DBG_PRINTF("[3] FDS\r\n");
fs_storage_init();
config_load();
load_flash_config();
DBG_PRINTF(" fds OK\r\n");
/*──────────────────────────────────────────────────────────────
* Phase 4: BLE 프로토콜 설정
*
* - GAP: 디바이스 이름(시리얼 번호), 연결 파라미터, 패스키
* - GATT: MTU 크기 협상
* - 서비스: NUS(Nordic UART Service), QWR, DFU
* - Advertising: 광고 데이터 구성 (이름, UUID)
* - 연결 파라미터 협상 모듈
* - 보안: Peer Manager (본딩/패스키)
*────────────────────────────────────────────────────────────*/
DBG_PRINTF("[4] BLE Protocol\r\n");
gap_params_init();
DBG_PRINTF(" gap OK\r\n");
gatt_init();
DBG_PRINTF(" gatt OK\r\n");
services_init();
DBG_PRINTF(" svc OK\r\n");
advertising_init();
DBG_PRINTF(" adv OK\r\n");
conn_params_init();
DBG_PRINTF(" conn OK\r\n");
// PHASE 7: 보안
DBG_PRINTF("[6] Security\r\n");
#if FEATURE_SECURE_CONNECTION
peer_manager_init();
#endif
DBG_PRINTF(" gap/gatt/svc/adv/conn/sec OK\r\n");
// PHASE 7.5: FDS 기본값 저장 (advertising 이후)
if (m_need_save_defaults) {
DBG_PRINTF("[FDS] Saving defaults after ADV\r\n");
config_save();
m_need_save_defaults = false;
DBG_PRINTF(" fds defaults saved\r\n");
}
// PHASE 7.6: 새 파서(dr_cmd_parser) 초기화
/*──────────────────────────────────────────────────────────────
* Phase 5: 애플리케이션 초기화
*
* - 명령 파서: BLE 수신 명령 처리 (로그, BLE 전송, CRC)
* - 피에조 드라이버: 초음파 측정용 GPIO/Timer/PPI 설정
* - IMU(ICM42670P)는 여기서 초기화하지 않음
* → msp? 명령 시 imu_read_direct()가 매번 자체 설정/읽기/슬립 처리
*────────────────────────────────────────────────────────────*/
DBG_PRINTF("[5] App\r\n");
g_plat.log = log_printf;
g_plat.tx_bin = dr_binary_tx_safe;
g_plat.crc_check = true;
g_log_enable = true;
DBG_PRINTF(" parser OK\r\n");
dr_piezo_init();
DBG_PRINTF(" parser/piezo OK\r\n");
// PHASE 7.7: 피에조 드라이버 초기화 (GPIO/GPIOTE/Timer/PPI + 전원)
//dr_piezo_system_init();
dr_piezo_init(); // Piezo 초기화 (Active X)
DBG_PRINTF(" piezo OK\r\n");
// PHASE 8: 완료
/*──────────────────────────────────────────────────────────────
* Boot 완료 → 전원 버튼 상태머신 시작
*────────────────────────────────────────────────────────────*/
DBG_PRINTF("\r\n========================================\r\n");
DBG_PRINTF(" READY [%s]\r\n", SERIAL_NO);
DBG_PRINTF("========================================\r\n\r\n");
// PHASE 9: 시작
timers_start();
// MAIN LOOP
@@ -23,6 +23,17 @@
#ifndef MAIN_H__
#define MAIN_H__
/* -------------------------------------------------------------------------
* Firmware
* - VBTFW0100 = () Ver 1.00
* - VB0FW0100 = Ver 1.00
*
* Firmware Version Update History
* - VBTFW0101 : reb+red ( ), 260330 jhChun
* - VBTFW0102 : LED (msl) 260331 jhChun
------------------------------------------------------------------------- */
#define FIRMWARE_VERSION "VBTFW0102"
/*==============================================================================
*
*============================================================================*/
File diff suppressed because one or more lines are too long
@@ -3587,7 +3587,7 @@
// <7=> 7
#ifndef NRFX_SAADC_CONFIG_IRQ_PRIORITY
#define NRFX_SAADC_CONFIG_IRQ_PRIORITY 0
#define NRFX_SAADC_CONFIG_IRQ_PRIORITY 6
#endif
// <e> NRFX_SAADC_CONFIG_LOG_ENABLED - Enables logging in the module.
@@ -11555,7 +11555,7 @@
// <i> The time set aside for this connection on every connection interval in 1.25 ms units.
#ifndef NRF_SDH_BLE_GAP_EVENT_LENGTH
#define NRF_SDH_BLE_GAP_EVENT_LENGTH 6
#define NRF_SDH_BLE_GAP_EVENT_LENGTH 24
#endif
// <o> NRF_SDH_BLE_GATT_MAX_MTU_SIZE - Static maximum MTU size.
@@ -8,23 +8,17 @@
*
* [ ]
* device_activated()
* p_order=0 , power_loop
* power_loop() 20ms Step 012
* - Step 0: I2C (sw_i2c_init_once)
* - Step 1: ( )
* - Step 2: 퀀
* power_loop ( )
* (IMU) imu_read_direct()
*
* [ ]
* device_sleep_mode()
* EEPROM OFF processing
* processing
*
* []
* device_reactivated()
* I2C 퀀
*
* [EEPROM ]
* EEP_WP = P0.24 (, FDS를 )
*
* []
* app_timer, 20ms power_loop
*
@@ -43,8 +37,6 @@
#include "debug_print.h"
#include "i2c_manager.h"
/* EEPROM 쓰기 보호 핀 (P0.24) - 레거시, 현재는 FDS 사용 */
#define EEP_WP NRF_GPIO_PIN_MAP(0, 24)
/* 전원 시퀀스용 싱글샷 타이머 인스턴스 */
APP_TIMER_DEF(m_power_timer_id);
@@ -53,9 +45,6 @@ APP_TIMER_DEF(m_power_timer_id);
// 2025-12-08 change to #define POWER_LOOP_INTERVAL 30 -> 20
#define POWER_LOOP_INTERVAL 20
/* 전원 시퀀스 현재 단계 (0: I2C 초기화, 1: 예약, 2: 완료) */
static uint8_t p_order;
@@ -65,43 +54,16 @@ extern volatile bool processing;
/* 전원 시퀀스 잠금 플래그 (true = 전원 시퀀스 진행 중) */
bool lock_check = false;
/**
* @brief GPIO
*
* EEP_WP .
*/
void power_gpio_init(void)
{
nrf_gpio_cfg_output(EEP_WP);
}
/**
* @brief EEPROM (ON/OFF)
*
* @param eeprom_st ON: EEP_WP HIGH ( ), OFF: LOW ( )
* , FDS(Flash Data Storage) .
*/
void eeprom_control(on_off_cont_t eeprom_st)
{
if(eeprom_st == OFF) {
nrf_gpio_pin_clear(EEP_WP); /* LOW: EEPROM 쓰기 보호 활성화 */
}else if(eeprom_st == ON){
nrf_gpio_pin_set(EEP_WP); /* HIGH: EEPROM 쓰기 허용 */
}
}
/**
* @brief
*
* EEPROM을 OFF하고, (processing)
* (processing)
* .
*
* @return 0 ( )
*/
int device_sleep_mode(void){
int rc = 0;
eeprom_control(OFF); /* EEPROM 쓰기 보호 활성화 */
nrf_delay_ms(2);
DBG_PRINTF("Device_Sleep_Mode OK!\r\n");
nrf_delay_ms(10);
@@ -115,7 +77,6 @@ int device_sleep_mode(void){
*
* 퀀 0 ,
* power_loop() .
* EEPROM은 OFF .
*
* @return 0 ( )
*/
@@ -124,7 +85,6 @@ int device_activated(void){
p_order = 0; /* 상태머신 시작 단계 (Step 0: I2C 초기화) */
lock_check =true; /* 전원 시퀀스 진행 중 잠금 */
power_timer_start(); /* 20ms 후 power_loop() 첫 호출 */
eeprom_control(OFF); /* EEPROM OFF 상태로 시작 */
return rc;
}
@@ -149,8 +109,7 @@ int device_activated(void){
* 3) p_order < 2
* 4) p_order == 2 퀀
*
* [DEBUG_MINIMAL_BOOT ]
* (Step 2)
* imu_read_direct()
*/
void power_loop(void *p_context)
{
@@ -158,39 +117,9 @@ void power_loop(void *p_context)
power_timer_stop(); /* 현재 타이머 정지 (싱글샷 → 수동 재시작 필요) */
#if DEBUG_MINIMAL_BOOT
/* Minimal Boot: Skip sensor initialization */
/* 미니멀 부트 모드: 센서 초기화 생략하고 바로 완료 */
DBG_PRINTF("[PWR] Minimal mode - skipping sensor init\r\n");
p_order = 2; // Jump to complete
#else
/* Full Boot: Execute power sequence */
/* 풀 부트 모드: 전원 시퀀스 단계별 실행 */
switch (p_order) {
/* Step 0: I2C Initialize */
/* 단계 0: I2C 버스를 SW 모드로 초기화 (레거시 흐름) */
case 0:
sw_i2c_init_once(); /* SW I2C 초기화 (TWI 해제 → 비트뱅 모드) */
nrf_delay_ms(10); /* I2C 버스 안정화 대기 */
DBG_PRINTF("[PWR] Step %d: I2C Init\r\n", p_order);
break;
/* Step 1: Reserved */
/* 단계 1: 예약 (추후 센서 초기화 등 추가 가능) */
case 1:
DBG_PRINTF("[PWR] Step %d: (reserved)\r\n", p_order);
break;
/* Step 2: Complete */
/* 단계 2: 전원 시퀀스 완료 → 디바이스 사용 준비 완료 */
case 2:
DBG_PRINTF("[PWR] Step %d: Sequence Complete\r\n", p_order);
break;
default:
break;
}
#endif
/* 센서 초기화 불필요 — imu_read_direct()가 매 측정 시 자체 처리 */
/* 추후 전원 시퀀스 단계가 필요하면 여기에 switch(p_order) 추가 */
p_order = 2;
/* Advance to next step or finish */
/* 다음 단계로 진행하거나, 시퀀스 완료 처리 */
@@ -10,11 +10,9 @@
* 퀀 .
*
* [ ]
* power_gpio_init() : GPIO (EEP_WP )
* eeprom_control() : EEPROM ON/OFF ()
* device_activated() : power_loop
* device_sleep_mode() : (EEPROM OFF, )
* device_reactivated() : I2C 퀀
* device_sleep_mode() : ( )
* device_reactivated() : 퀀
* power_loop() : 20ms 퀀
* power_timer_start/stop : 퀀
* power_timer_init() : 퀀 ( 1)
@@ -26,13 +24,7 @@
#include "main.h"
/** @brief 전원 관련 GPIO 핀 초기화 (EEP_WP 출력 설정) */
void power_gpio_init(void);
/** @brief EEPROM 쓰기 보호 제어 (ON=허용, OFF=보호) - 레거시 */
void eeprom_control(on_off_cont_t eeprom_st);
/** @brief 디바이스 슬립 모드 진입 (EEPROM OFF, processing 해제) */
/** @brief 디바이스 슬립 모드 진입 (processing 해제) */
int device_sleep_mode(void);
/** @brief 디바이스 전원 켜기 (전원 시퀀스 상태머신 시작) */
@@ -43,8 +43,8 @@
#define TMP235_REF_VOLTAGE_IN_MILLIVOLTS 600.0f /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
/* 1/3 프리스케일링 보상 계수 x6 (부동소수점) */
#define TMP235_PRE_SCALING_COMPENSATION 6.0f /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
/* 10비트 ADC 최대값 1024 (부동소수점, 분해능 기준) */
#define TMP235_ADC_RES_10BITS 1024.0f /**< Maximum digital value for 10-bit ADC conversion. */
/* 12비트 ADC 최대값 4096 (부동소수점, 분해능 기준) */
#define TMP235_ADC_RES_12BITS 4096.0f /**< Maximum digital value for 12-bit ADC conversion. */
/**@brief Macro to convert the result of ADC conversion in millivolts.
*
@@ -52,9 +52,9 @@
*
* @retval Result converted to millivolts.
*/
/* ADC 원시값 → TMP235 출력전압(mV) 변환 매크로: ADC x (600/1024) x 6 */
/* ADC 원시값 → TMP235 출력전압(mV) 변환 매크로: ADC x (600/4096) x 6 */
#define TMP235_VOUT_IN_MILLI_VOLTS(ADC_VALUE)\
((((ADC_VALUE) * TMP235_REF_VOLTAGE_IN_MILLIVOLTS) / TMP235_ADC_RES_10BITS) * TMP235_PRE_SCALING_COMPENSATION)
((((ADC_VALUE) * TMP235_REF_VOLTAGE_IN_MILLIVOLTS) / TMP235_ADC_RES_12BITS) * TMP235_PRE_SCALING_COMPENSATION)
/* SAADC 변환 결과 저장 버퍼 (1채널) */
static nrf_saadc_value_t adc_buf;
@@ -94,7 +94,7 @@ volatile bool tmp235_saadc_done = false;
void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* TMP325 Vout reading */
{
float led_temp; /* 계산된 온도 (°C, 부동소수점) */
uint16_t led_temp_16; /* BLE 전송용 온도 (°C x 100, 정수) */
float led_temp_16; /* BLE 전송용 온도 (°C x 100, 부동소수점) */
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
{
@@ -106,8 +106,10 @@ void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* TMP325 Vout
//DBG_PRINTF("[TMP] adc=%d\r\n", adc_result);
/* SAADC 해제 — 배터리/압력센서 측정과 하드웨어 공유 */
nrf_drv_saadc_channel_uninit(0); // 채널 먼저 해제
nrf_drv_saadc_uninit();
nrf_drv_saadc_channel_uninit(0);
//nrf_drv_saadc_uninit(); // 이전: 드라이버 먼저 해제
//nrf_drv_saadc_channel_uninit(0);
/* ADC값 → TMP235 출력전압(mV) 변환 */
tmp235_voltage_in_milli_volts = TMP235_VOUT_IN_MILLI_VOLTS(adc_result);
@@ -143,7 +145,7 @@ void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* TMP325 Vout
/* info4 모드: 온도값을 정수(°C x 100)로 저장 (예: 36.50°C → 3650) */
if (info4 == true)
{
info_temp =(uint16_t)(led_temp*100);
info_temp = (uint16_t)(led_temp * 100);
}
/* UART 모드: 소수점 2자리까지 텍스트로 출력 */
else if(cmd_type_t == CMD_UART)
@@ -153,8 +155,8 @@ void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* TMP325 Vout
/* BLE 모드: °C x 100 정수를 "rso:" 헤더로 바이너리 전송 */
else if(cmd_type_t == CMD_BLE)
{
led_temp_16 = (uint16_t)(led_temp*100);
single_format_data(ble_bin_buffer, "rso:", led_temp_16);
led_temp_16 = led_temp * 100;
single_format_data(ble_bin_buffer, "rso:", (uint16_t)led_temp_16);
dr_binary_tx_safe(ble_bin_buffer,3);
@@ -162,6 +164,7 @@ void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* TMP325 Vout
// data_tx_handler(ble_tx_buffer);
}
DBG_PRINTF("7");
tmp235_saadc_done = true;
}
@@ -176,18 +179,16 @@ void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* TMP325 Vout
*/
void tmp235_init(void)
{
/* SAADC 드라이버 초기화 (기본 설정 + 온도센서 이벤트 핸들러 등록) */
ret_code_t err_code = nrf_drv_saadc_init(NULL, tmp235_voltage_handler);
/* SAADC 드라이버 초기화 (4x 오버샘플링으로 노이즈 저감) */
nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT;
saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X;
ret_code_t err_code = nrf_drv_saadc_init(&saadc_config, tmp235_voltage_handler);
APP_ERROR_CHECK(err_code);
/*
if (err_code != NRF_SUCCESS)
{
tmp235_saadc_done = true; // 대기 루프 탈출시킴
return;
}
*/
/* AIN3 채널 설정: TMP235-Q1 Vout 핀 (싱글엔드 입력) */
nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3); /* TMP235_Q1 Voltage Output Measurement */
/* AIN3 채널 설정: TMP235-Q1 Vout 핀 (싱글엔드 입력, burst 활성화) */
nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3);
config.burst = NRF_SAADC_BURST_ENABLED;
err_code = nrf_drv_saadc_channel_init(0, &config);
APP_ERROR_CHECK(err_code);