diff --git a/data/CODE_REVIEW_STATUS.md b/data/CODE_REVIEW_STATUS.md index a3c235c..840a244 100644 --- a/data/CODE_REVIEW_STATUS.md +++ b/data/CODE_REVIEW_STATUS.md @@ -165,6 +165,75 @@ cat_interface.c 삭제(EEPROM/AES 코드 전체 삭제). TWI 인스턴스(m_twi) | c_addr[6] | main.c:187 | 삭제 | **미완료** | | led_pd_dac_v[LED_NUM] | 참조 다수 | 삭제 | 확인 필요 | +### 2.3.1 cmd_parse.c → parser.c 통합 현황 + +현재 구조: main.c → `received_command_process()` [cmd_parse.c] → `dr_cmd_parser()` [parser.c] → 실패 시 레거시 if-else 체인 [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?` | 활성 (stub) | AGC gain — HW 제거됨 | param_error만 응답 | +| `sfz?` | 활성 (stub) | AGC gain — HW 제거됨 | param_error만 응답 | +| `ssv?` | `if(0&&)` 비활성 | 펌웨어 버전 | → parser.c `mfv?`로 대체됨 | +| `ssz?` | `if(0&&)` 비활성 | 시리얼번호 쓰기 | → parser.c `mws?`로 대체됨 | +| `srz?` | `if(0&&)` 비활성 | 시리얼번호 읽기 | → parser.c `mrs?`로 대체됨 | +| `siz?` | `if(0&&)` 비활성 | HW번호 읽기 | → parser.c `mrh?`로 대체됨 | +| `shz?` | `if(0&&)` 비활성 | HW번호 쓰기 | → parser.c `mwh?`로 대체됨 | + +#### 통합 남은 작업 + +| # | 작업 | 현재 상태 | +|---|------|----------| +| 1 | parser.c에 누락 명령어 추가 (mpz/mqz/mxz/myz) | **완료** | +| 2 | received_command_process() 래퍼를 main.c로 이동 | **미완료** | +| 3 | 전역 변수 (SERIAL_NO, m_static_passkey 등) main.c로 이동 | **미완료** | +| 4 | cmd_parse.h include 정리 (7개 파일) | **미완료** | +| 5 | cmd_parse.c / cmd_parse.h 삭제 | **미완료** | +| 6 | Keil 프로젝트 파일 업데이트 | **미완료** | + ### 2.4 주석 처리된 코드 | 위치 | 내용 | 판정 | 현재 상태 | @@ -448,10 +517,11 @@ BLE_GAP_EVT_CONNECTED/DISCONNECTED 핸들러에 IMU 타이머 start/stop 로직 | 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 | 미사용 전역변수 + HW_NO 삭제 (2.2-2.3절) | **미완료** | -| 9 | DEBUG_MINIMAL_BOOT 조건 제거 (3절) | **미완료** | -| 10 | Keil uvprojx 소스 참조 제거 (4절) | 확인 필요 | -| 11 | 주석 처리된 코드 삭제 (2.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차 정리 (저전력 최적화) diff --git a/data/CODE_REVIEW_STATUS.pdf b/data/CODE_REVIEW_STATUS.pdf deleted file mode 100644 index a9654c9..0000000 Binary files a/data/CODE_REVIEW_STATUS.pdf and /dev/null differ diff --git a/data/CODE_REVIEW_STATUS_0316.pdf b/data/CODE_REVIEW_STATUS_0316.pdf new file mode 100644 index 0000000..3eb93f3 Binary files /dev/null and b/data/CODE_REVIEW_STATUS_0316.pdf differ diff --git a/pc_firm/dr_adc121s051/dr_adc121s051.c b/pc_firm/dr_adc121s051/dr_adc121s051.c index 4210092..50b318b 100644 --- a/pc_firm/dr_adc121s051/dr_adc121s051.c +++ b/pc_firm/dr_adc121s051/dr_adc121s051.c @@ -49,6 +49,9 @@ extern void dr_piezo_burst_sw_20mhz(uint8_t cycles); extern void dr_piezo_burst_sw_17mhz(uint8_t cycles); extern void dr_piezo_burst_sw_22mhz(uint8_t cycles); extern void dr_piezo_burst_sw_19mhz(uint8_t cycles); +extern void dr_piezo_power_off(void); + +#include "parser.h" /*============================================================================== * DEBUG CONFIGURATION @@ -791,7 +794,7 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u if (cycles < 3 || cycles > 9) cycles = 5; /* Valid range: 3~9, default 5 */ if (averaging == 0) averaging = 1; /* Minimum 1 */ if (averaging > 1000) averaging = 1000; /* Maximum 1000 */ - if (piezo_ch > 7) piezo_ch = 0; /* Validate piezo channel: 0~7 jhChun 26.01.29 */ + if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0; /* 채널 범위 검증 */ dr_adc_echo_t echo; echo.peak_raw = 0; @@ -1325,6 +1328,8 @@ static dr_adc_err_t maa_async_capture_channel(uint8_t ch) { if (ch > (MAA_NUM_CHANNELS - 1)) return DR_ADC_ERR_INVALID_PARAM; + if (g_plat.log) g_plat.log("[maa] capturing CH%u\r\n", ch); + return dr_adc_capture_channel_only( g_maa_ctx.freq_option, g_maa_ctx.delay_us, @@ -1434,6 +1439,13 @@ static void maa_async_send_completion(uint16_t status) dr_binary_tx_safe(buf, 3); + /* 자동으로 켰으면 완료 후 전원 OFF */ + if (g_maa_ctx.auto_powered) { + if (g_plat.log) g_plat.log("[Cmd_maa] TX/RX Active -> Sleep\r\n"); + dr_piezo_power_off(); + g_maa_ctx.auto_powered = false; + } + g_maa_ctx.state = MAA_ASYNC_IDLE; ADC_LOG("maa_async: complete, status=0x%04X", status); } @@ -1551,3 +1563,8 @@ void maa_async_abort(void) } } +void maa_async_set_auto_power(bool on) +{ + g_maa_ctx.auto_powered = on; +} + diff --git a/pc_firm/dr_adc121s051/dr_adc121s051.h b/pc_firm/dr_adc121s051/dr_adc121s051.h index 6c129c3..c79afee 100644 --- a/pc_firm/dr_adc121s051/dr_adc121s051.h +++ b/pc_firm/dr_adc121s051/dr_adc121s051.h @@ -336,7 +336,7 @@ void dr_piezo_select_channel(uint8_t channel); * @brief 8-channel echo buffer for maa? command * Memory: 140 samples × 2 bytes × 8 channels = 2,240 bytes */ -#define MAA_NUM_CHANNELS 8 /* 4 -> 8 jhChun 26.02.12*/ +#define MAA_NUM_CHANNELS 6 /* 4 -> 8 -> 6 jhChun 26.03.17*/ #define MAA_SAMPLES_MAX 200 /** @@ -454,6 +454,7 @@ typedef struct { 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 auto_powered; /**< true: 자동 전원 ON → 완료 후 OFF */ } maa_async_ctx_t; /** @@ -501,5 +502,10 @@ maa_async_state_t maa_async_get_state(void); */ void maa_async_abort(void); +/** + * @brief 자동 전원 플래그 설정 (완료 후 자동 power off) + */ +void maa_async_set_auto_power(bool on); + #endif /* DR_ADC121S051_H */ diff --git a/pc_firm/parser.c b/pc_firm/parser.c index 4574aae..3684690 100644 --- a/pc_firm/parser.c +++ b/pc_firm/parser.c @@ -111,6 +111,7 @@ extern config_data_t m_config; /* 전체 설정 구조체 (FDS 저장 대 /* 피에조 초음파 트랜스듀서 제어 함수 */ extern void dr_piezo_power_on( void ); /* 피에조 회로 전원 ON (TX/RX 보드) */ extern void dr_piezo_power_off( void ); /* 피에조 회로 전원 OFF */ +extern bool dr_piezo_is_power_on( void ); /* 피에조 전원 상태 확인 */ extern void dr_piezo_burst_sw(uint8_t cycles); /* 2.1MHz 버스트 펄스 발생 (기본 주파수) */ extern void dr_piezo_burst_sw_18mhz(uint8_t cycles); /* 1.8MHz 버스트 펄스 발생 */ extern void dr_piezo_burst_sw_20mhz(uint8_t cycles); /* 2.0MHz 버스트 펄스 발생 */ @@ -436,7 +437,7 @@ static CmdEntry g_cmd_table[] = { /* D. 각종 센서 측정 */ { "msn?", true, Cmd_msn }, - { "mso?", false, Cmd_mso }, + { "mso?", true, Cmd_mso }, { "msp?", true, Cmd_msp }, { "msi?", true, Cmd_msi }, @@ -511,7 +512,7 @@ static int dr_cmd_dispatch(const ParsedCmd *cmd) int dr_cmd_parser(const uint8_t *buf, uint8_t len) { ParsedCmd cmd; - g_plat.log("parser!!!!!!!!"); + g_plat.log("parser!!!!!!!!\r\n"); if (g_plat.log) g_plat.log("[PARSER] in len=%u crc=%u\r\n", len, g_plat.crc_check); @@ -614,9 +615,7 @@ static int Cmd_msq(const ParsedCmd *cmd) { uint16_t val = 0; dr_get_u16(cmd, 0, &val); - if (g_plat.log && g_log_enable) { - g_plat.log("[Cmd_msq] Power off\r\n"); - } + if (g_plat.log) g_plat.log("[Cmd_msq] Power off val=%u\r\n", val); single_format_data(ble_bin_buffer, "rsq:", val); dr_binary_tx_safe(ble_bin_buffer, 2); go_device_power_off = true; /* 메인 루프에서 전원 OFF 실행 예약 */ @@ -637,9 +636,7 @@ static int Cmd_msr(const ParsedCmd *cmd) { uint16_t val = 0; dr_get_u16(cmd, 0, &val); - if (g_plat.log && g_log_enable) { - g_plat.log("[Cmd_msr] Bond delete + reset\r\n"); - } + if (g_plat.log) g_plat.log("[Cmd_msr] Bond delete + reset val=%u\r\n", val); single_format_data(ble_bin_buffer, "rsr:", val); dr_binary_tx_safe(ble_bin_buffer, 2); @@ -665,9 +662,7 @@ static int Cmd_mss(const ParsedCmd *cmd) { uint16_t val = 0; dr_get_u16(cmd, 0, &val); - if (g_plat.log && g_log_enable) { - g_plat.log("[Cmd_mss] Device reset\r\n"); - } + if (g_plat.log) g_plat.log("[Cmd_mss] Device reset val=%u\r\n", val); single_format_data(ble_bin_buffer, "rss:", val); dr_binary_tx_safe(ble_bin_buffer, 2); @@ -712,8 +707,7 @@ static int Cmd_mpa(const ParsedCmd *cmd) g_plat.log("[Cmd_mpa] Piezo Activation\n"); } - dr_piezo_power_on(); /* 피에조 보드 전원 ON (LDO 활성화) */ - dr_piezo_system_init(); /* 피에조 시스템 초기화 (DAC/MUX/ADC 설정) */ + dr_piezo_system_init(); /* 내부에서 power_on + init 수행 */ if (g_plat.tx_bin) { single_format_data(ble_bin_buffer, "rpa:", 1); @@ -777,8 +771,8 @@ static int Cmd_mpc(const ParsedCmd *cmd) (void)dr_get_u16(cmd, 1, &freq_option); /* word 1: 주파수 옵션 */ (void)dr_get_u16(cmd, 2, &piezo_ch); /* word 2: 피에조 채널 */ - /* 채널 범위 검증: 0~7 유효, 초과 시 0으로 리셋 */ - if (piezo_ch > 7) piezo_ch = 0; + /* 채널 범위 검증: 초과 시 0으로 리셋 */ + if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0; if (g_plat.log && g_log_enable) { const char *freq_str = (freq_option == 0) ? "1.8MHz" : @@ -851,6 +845,14 @@ static int Cmd_mec(const ParsedCmd *cmd) uint16_t averaging = 1; /* 기본 1 (평균화 없음), 최대 1000 */ uint16_t piezo_ch = 0; /* 기본 채널 0 (유효: 0~7) */ + /* 피에조 전원이 꺼져 있으면 자동으로 켜기 */ + bool auto_powered = false; + if (!dr_piezo_is_power_on()) { + if (g_plat.log) g_plat.log("[Cmd_mec] TX/RX Sleep -> Active\r\n"); + dr_piezo_system_init(); + auto_powered = true; + } + /* 6개 파라미터 순서대로 추출 (데이터 부족 시 기본값 유지) */ (void)dr_get_u16(cmd, 0, &freq_option); /* word 0: 주파수 옵션 */ (void)dr_get_u16(cmd, 1, &delay_us); /* word 1: 버스트→ADC 딜레이 */ @@ -863,8 +865,8 @@ static int Cmd_mec(const ParsedCmd *cmd) if (averaging == 0) averaging = 1; if (averaging > 1000) averaging = 1000; - /* 피에조 채널 범위 검증: 0~7 */ - if (piezo_ch > 7) piezo_ch = 0; + /* 피에조 채널 범위 검증 */ + if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0; if (g_plat.log && g_log_enable) { const char *freq_str = (freq_option == 0) ? "1.8MHz" : @@ -877,18 +879,10 @@ static int Cmd_mec(const ParsedCmd *cmd) freq_option, freq_str, delay_us, num_samples, cycles, averaging, piezo_ch); } - /* 통합 버스트+캡처+전송 함수 호출 - * 내부 동작 순서: - * 1. ADC 전원 ON - * 2. MUX로 피에조 채널 선택 (0~7) - * 3. 주파수별 버스트 펄스 발생 (freq_option에 따라) - * 4. delay_us 후 ADC 캡처 (averaging 횟수만큼 반복) - * 5. 캡처된 샘플 평균화 (노이즈 저감) - * 6. BLE 멀티패킷 전송 (패킷 간 타이밍 자동 관리) - */ + /* 통합 버스트+캡처+전송 함수 호출 */ dr_adc_err_t err = dr_adc_burst_capture_transmit( (uint8_t)freq_option, delay_us, num_samples, (uint8_t)cycles, - (uint16_t)averaging, (uint8_t)piezo_ch, ble_bin_buffer, 0); /* 0=raa 태그로 전송 */ + (uint16_t)averaging, (uint8_t)piezo_ch, ble_bin_buffer, 0); /* 에러 발생 시 에러 코드 + 요청 샘플 수 응답 */ if (err != DR_ADC_OK) { @@ -899,6 +893,12 @@ static int Cmd_mec(const ParsedCmd *cmd) g_plat.log("[Cmd_mec] result=%d\r\n", err); } + /* 자동으로 켰으면 완료 후 전원 OFF */ + if (auto_powered) { + if (g_plat.log) g_plat.log("[Cmd_mec] TX/RX Active -> Sleep\r\n"); + dr_piezo_power_off(); + } + return 1; } @@ -981,7 +981,7 @@ static int Cmd_cmd(const ParsedCmd *cmd) */ #define MAA_FREQ_OPTION 1 /* 기본 2.1MHz */ #define MAA_DELAY_US 10 /* 버스트 후 10us 딜레이 */ -#define MAA_NUM_SAMPLES 140 /* 140샘플 (약 25cm 거리) */ +#define MAA_NUM_SAMPLES 100 /* 140샘플 (약 25cm 거리) */ #define MAA_CYCLES 7 /* 7사이클 버스트 */ #define MAA_AVERAGING 5 /* 5회 평균화 */ @@ -1005,11 +1005,22 @@ static int Cmd_maa(const ParsedCmd *cmd) return 1; } + /* 피에조 전원이 꺼져 있으면 자동으로 켜기 */ + bool auto_powered = false; + bool pwr = dr_piezo_is_power_on(); + if (g_plat.log) g_plat.log("[Cmd_maa] piezo_power=%u\r\n", pwr); + if (!pwr) { + if (g_plat.log) g_plat.log("[Cmd_maa] TX/RX Sleep -> Active\r\n"); + dr_piezo_system_init(); + auto_powered = true; + } + /*======================================================================= - * 비동기 4채널 캡처 시작 + * 비동기 6채널 캡처 시작 * - maa_async_start(): CH0 캡처 실행 + 첫 헤더 패킷 전송 * - 이후 패킷은 BLE_NUS_EVT_TX_RDY 콜백에서 자동 전송 * - 블로킹 딜레이 없음 → SoftDevice 이벤트 정상 처리 가능 + * - auto_powered=true면 완료 후 자동 전원 OFF *=======================================================================*/ err = maa_async_start( (uint8_t)MAA_FREQ_OPTION, @@ -1021,12 +1032,19 @@ static int Cmd_maa(const ParsedCmd *cmd) ); if (err != DR_ADC_OK) { - /* 시작 실패 → maa_async_start 내부에서 에러 응답 이미 전송됨 */ + /* 시작 실패 → 에러 응답 전송 (앱 타임아웃 방지) */ + if (g_plat.log) g_plat.log("[Cmd_maa] start failed err=%d\r\n", err); + single_format_data(ble_bin_buffer, "raa:", (uint16_t)(0xFF00 | err)); + dr_binary_tx_safe(ble_bin_buffer, 3); + if (auto_powered) dr_piezo_power_off(); return 1; } + /* 자동 전원 플래그 설정 → 비동기 완료 시 자동 OFF */ + maa_async_set_auto_power(auto_powered); + /* 즉시 반환 → 비동기 전송 진행 중 */ - /* 전체 완료 시 상태 머신이 "raa:" 응답을 자동 전송 */ + /* 전체 완료 시 상태 머신이 "raa:" 응답 + 자동 power off 처리 */ return 1; } @@ -1119,10 +1137,7 @@ static int Cmd_mrs(const ParsedCmd *cmd) /*============================================================================== - * 패스키 / 수명 카운터 (FDS) - * * 패스키: BLE 페어링 시 사용하는 6자리 숫자 (예: "123456") - * 수명 카운터: 디바이스 사용 횟수 추적 (uint32, 상위16+하위16으로 분할 전송) *============================================================================*/ /* mpz? - BLE 패스키 FDS에 쓰기 @@ -1154,6 +1169,8 @@ static int Cmd_mqz(const ParsedCmd *cmd) (void)cmd; memcpy(m_static_passkey, m_config.static_passkey, 6); + if (g_plat.log) g_plat.log("[mqz] Passkey read: %.6s\r\n", m_static_passkey); + ascii_format_data(ble_bin_buffer, "rqz:", m_static_passkey, 6); dr_binary_tx_safe(ble_bin_buffer, 5); return 1; diff --git a/project/ble_peripheral/ble_app_bladder_patch/battery_saadc.h b/project/ble_peripheral/ble_app_bladder_patch/battery_saadc.h index 60e9eaa..20c21f4 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/battery_saadc.h +++ b/project/ble_peripheral/ble_app_bladder_patch/battery_saadc.h @@ -3,19 +3,39 @@ * @author CandyPops Co. * @version V1.0.0 * @date 2022-09-05 - * @brief + * @brief + ******************************************************************************/ + +/******************************************************************************* + * [헤더 개요] 배터리 전압 및 압력센서 SAADC 측정 인터페이스 + * + * nRF52840 SAADC를 이용한 배터리 전압 측정과 압력센서 2채널 측정의 + * 외부 호출용 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 ******************************************************************************/ #ifndef _BATTERY_SAADC_H_ #define _BATTERY_SAADC_H_ +/* 저전압 판정 임계값 (mV) — 이 값 이하가 10회 연속이면 자동 전원 OFF */ #define LOW_BATTERY_VOLTAGE 3100 /* Low Battery 임계값 */ +/** @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 배터리 모니터링 타이머 정지 */ void battery_timer_stop(void); +/** @brief 배터리 모니터링 타이머 초기화 (앱 시작 시 1회 호출) */ void battery_timer_init(void); #endif //_BATTERY_SAADC_H_ diff --git a/project/ble_peripheral/ble_app_bladder_patch/debug_print.h b/project/ble_peripheral/ble_app_bladder_patch/debug_print.h index 1f513db..886cc96 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/debug_print.h +++ b/project/ble_peripheral/ble_app_bladder_patch/debug_print.h @@ -1,13 +1,32 @@ // file: debug_print.h +/******************************************************************************* + * [한국어 설명] 디버그 출력 매크로 (조건부 컴파일) + * + * SEGGER RTT(Real Time Transfer)를 이용한 디버그 출력 매크로. + * J-Link 디버거를 통해 실시간으로 로그를 PC에 전송한다. + * UART를 사용하지 않으므로 시스템 타이밍에 미치는 영향이 적다. + * + * === 조건부 컴파일 === + * ENABLE_PRINTF = 1: DBG_PRINTF가 SEGGER_RTT_printf(채널0)로 치환됨 + * -> 실제 로그 출력 (디버깅 시 사용) + * ENABLE_PRINTF = 0: DBG_PRINTF가 빈 매크로로 치환됨 + * -> 코드에서 완전히 제거 (릴리스 빌드 시 사용) + * + * === 사용법 === + * DBG_PRINTF("값: %d\r\n", value); // printf와 동일한 포맷 문자열 + * 출력은 SEGGER RTT Viewer 또는 J-Link RTT Client에서 확인. + ******************************************************************************/ #ifndef DEBUG_PRINT_H #define DEBUG_PRINT_H -#define ENABLE_PRINTF 1 // Set to 0 to disable globally +#define ENABLE_PRINTF 1 /* 1=디버그 출력 활성화, 0=전역 비활성화 */ #if ENABLE_PRINTF #include "SEGGER_RTT.h" + /* SEGGER RTT 채널 0으로 포맷 문자열 출력 */ #define DBG_PRINTF(...) SEGGER_RTT_printf(0, __VA_ARGS__) #else + /* 빈 매크로: 컴파일러가 호출 코드를 완전히 제거 */ #define DBG_PRINTF(...) // Do nothing #endif diff --git a/project/ble_peripheral/ble_app_bladder_patch/fstorage.c b/project/ble_peripheral/ble_app_bladder_patch/fstorage.c index f8376a8..9232239 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/fstorage.c +++ b/project/ble_peripheral/ble_app_bladder_patch/fstorage.c @@ -2,6 +2,36 @@ TEST medi50 Dec 23 ******************************************************************************/ +/** + * @file fstorage.c + * @brief FDS(Flash Data Storage) 기반 설정 저장 모듈 + * + * 외부 EEPROM을 대체하여 nRF52840 내장 플래시에 장치 설정을 저장/로드한다. + * + * [레코드 관리] + * - CONFIG_FILE = 0x8010, CONFIG_REC_KEY = 0x7010 으로 단일 레코드를 관리한다. + * + * [config_data_t 구조체 필드] + * - magic(4B) : 포맷 확인용 매직 넘버 (MAGIC = 0x20231226) + * - hw_no(12B) : 하드웨어 번호 (BLE 명령으로 설정) + * - serial_no(12B) : 시리얼 번호 (기본값: "VB026030000") + * - passkey(6B) : BLE 페어링용 정적 패스키 + * - bond_delete(1B) : 본딩 데이터 삭제 플래그 + * - reset_status(1B) : 리셋 상태 값 + * - pd_adc_cnt : 포토다이오드 ADC 측정 횟수 + * - pd_delay_us : 포토다이오드 측정 간 지연 시간(us) + * - life_cycle : 장치 사용 횟수 + * + * [매직 넘버 검증] + * - 플래시에서 로드한 데이터의 magic 값이 0x20231226과 일치하는지 확인하여 + * 유효한 설정인지 판별한다. 불일치 시 기본값으로 초기화한다. + * + * [FDS 이벤트 후처리] + * - FDS 쓰기/업데이트 완료 이벤트 수신 후, 대기 중인 후처리를 수행한다: + * 전원 OFF (go_device_power_off), 슬립 진입 (go_sleep_mode_enter), + * 시스템 리셋 (go_NVIC_SystemReset) + */ + #include "sdk_config.h" #include @@ -27,23 +57,29 @@ #include "debug_print.h" -/* File ID and Key used for the configuration record. */ +/* FDS 레코드 식별자: 파일 ID와 레코드 키로 단일 설정 레코드를 관리 */ #define CONFIG_FILE (0x8010) #define CONFIG_REC_KEY (0x7010) +/* 매직 넘버: 플래시에 저장된 데이터가 유효한 설정인지 판별하는 데 사용 */ #define CONFIG_MAGIC_NUMBER_VALUE (0x20231226) + +/* 전역 설정 데이터 구조체 인스턴스 */ config_data_t m_config; -extern bool go_device_power_off; -extern bool go_sleep_mode_enter; -extern bool go_NVIC_SystemReset; +/* FDS 쓰기 완료 후 수행할 후처리 플래그 (main.c에서 선언) */ +extern bool go_device_power_off; /* 전원 OFF 요청 */ +extern bool go_sleep_mode_enter; /* 슬립 모드 진입 요청 */ +extern bool go_NVIC_SystemReset; /* 시스템 리셋 요청 */ -/* Flag to check fds initialization. */ +/* FDS 초기화 완료 여부 플래그 (fds_evt_handler에서 true로 설정) */ static bool volatile m_fds_initialized; +/* FDS 쓰기 진행 중 플래그: true이면 쓰기 완료 대기 중 */ bool fds_flag_write = false; -/* A record containing dummy configuration data. */ + +/* FDS에 기록할 레코드 템플릿 (m_config 데이터를 가리킴) */ static fds_record_t const m_dummy_record = { .file_id = CONFIG_FILE, @@ -54,9 +90,16 @@ static fds_record_t const m_dummy_record = }; -int8_t reset_status_dflt = 99; -uint8_t static_passkey_dflt[6] = "123456"; +/* 기본 설정값 상수 */ +int8_t reset_status_dflt = 99; /* 리셋 상태 기본값 */ +uint8_t static_passkey_dflt[6] = "123456"; /* BLE 패스키 기본값 */ +/** + * @brief 기본 설정값 초기화 + * + * m_config 구조체의 각 필드를 공장 초기값으로 설정한다. + * 플래시에 유효한 설정이 없거나 매직 넘버가 불일치할 때 호출된다. + */ void fds_default_value_set(void) { /* HW Number - empty (set via BLE command) */ @@ -84,8 +127,19 @@ void fds_default_value_set(void) } +/* 마지막 FDS 이벤트 ID 저장 (디버깅용) */ static volatile uint8_t fds_last_evt = 0xFF; +/** + * @brief FDS 이벤트 콜백 핸들러 + * + * FDS 내부에서 비동기 작업이 완료될 때 호출된다. + * - FDS_EVT_INIT : FDS 초기화 완료 → m_fds_initialized 플래그 설정 + * - FDS_EVT_WRITE : 새 레코드 쓰기 완료 → fds_flag_write 해제 + * - FDS_EVT_UPDATE : 레코드 업데이트 완료 → fds_flag_write 해제 후 + * 대기 중인 전원 OFF / 슬립 진입 / 시스템 리셋 수행 + * - FDS_EVT_DEL_RECORD / FDS_EVT_DEL_FILE / FDS_EVT_GC : 현재 미사용 + */ static void fds_evt_handler( fds_evt_t const *p_evt ) { fds_last_evt = p_evt->id; @@ -140,7 +194,13 @@ static void fds_evt_handler( fds_evt_t const *p_evt ) } -/**@brief Wait for fds to initialize. */ +/** + * @brief FDS 초기화 완료 대기 + * + * m_fds_initialized 플래그가 true가 될 때까지 대기한다. + * 최대 3초(3000ms) 타임아웃이 설정되어 있으며, + * 타임아웃 시 에러 로그를 출력하고 반환한다. + */ static void wait_for_fds_ready( void ) { uint32_t timeout = 0; @@ -157,6 +217,20 @@ static void wait_for_fds_ready( void ) } +/** + * @brief FDS에서 설정 로드 + * + * 플래시에서 CONFIG_FILE/CONFIG_REC_KEY 레코드를 검색하여 m_config에 로드한다. + * + * 동작 흐름: + * 1. fds_record_find()로 레코드 검색 (실패 시 최대 10회 재시도, 100ms 간격) + * 2. 레코드 발견 시: + * - fds_record_open()으로 열기 (CRC 에러 시 삭제 후 기본값으로 재생성) + * - 데이터를 m_config로 복사 + * - 매직 넘버 불일치 시 기존 레코드 삭제 → 기본값 설정 → 재기록 + * 3. 레코드 미발견 시: + * - 기본값으로 새 레코드 생성 후 다시 로드 + */ void config_load( void ) { ret_code_t rc; @@ -264,6 +338,21 @@ void config_load( void ) } +/** + * @brief 현재 설정을 FDS에 저장 + * + * m_config의 내용을 플래시에 기록한다. + * + * 동작 흐름: + * 1. 이전 FDS 쓰기 작업이 진행 중이면 최대 3초 대기 + * 2. 매직 넘버가 올바르지 않으면 보정 + * 3. 기존 레코드가 있으면 fds_record_update()로 갱신 + * - 플래시 공간 부족 시 GC(가비지 컬렉션) 수행 후 재시도 + * 4. 기존 레코드가 없으면 fds_record_write()로 새로 생성 + * + * 참고: 쓰기 완료는 fds_evt_handler()에서 비동기로 처리되며, + * 완료 후 전원 OFF/슬립/리셋 등의 후처리가 수행될 수 있다. + */ void config_save( void ) { ret_code_t rc; @@ -334,11 +423,25 @@ void config_save( void ) } +/** + * @brief config_load()의 래퍼 함수 + * + * 외부 모듈에서 설정 로드를 요청할 때 사용한다. + */ void fs_set_value(void) { config_load(); } +/** + * @brief FDS 초기화 + * + * 부팅 시 호출되어 FDS 모듈을 초기화한다. + * 1. fds_register()로 이벤트 핸들러 등록 + * 2. fds_init()로 FDS 초기화 시작 + * 3. wait_for_fds_ready()로 초기화 완료 대기 (최대 3초) + * 4. fds_stat()로 플래시 상태 확인 + */ void fs_storage_init(void) { ret_code_t rc; diff --git a/project/ble_peripheral/ble_app_bladder_patch/fstorage.h b/project/ble_peripheral/ble_app_bladder_patch/fstorage.h index e5b8129..a4d27f0 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/fstorage.h +++ b/project/ble_peripheral/ble_app_bladder_patch/fstorage.h @@ -3,7 +3,29 @@ * @author CandyPops Co. * @version V1.0.0 * @date 2022-09-05 - * @brief + * @brief FDS(Flash Data Storage) 기반 설정 저장 모듈 인터페이스 + ******************************************************************************* + * + * [헤더 개요] + * nRF52840 내장 플래시에 디바이스 설정을 저장/로드하는 FDS 모듈의 공용 API. + * 외부 EEPROM을 대체하며, SoftDevice와 공존하여 플래시를 안전하게 관리한다. + * + * [config_data_t 구조체] (45바이트, 패킹됨) + * magic_number(4B): 포맷 확인용 (0x20231226) + * hw_no(12B): 하드웨어 번호 + * serial_no(12B): 시리얼 번호 (BLE 디바이스 이름으로도 사용) + * static_passkey(6B): BLE 페어링 패스키 (숫자 6자리) + * bond_data_delete(1B): 본딩 삭제 플래그 + * reset_status(1B): 리셋 상태 코드 + * pd_adc_cnt(1B): ADC 샘플링 횟수 + * pd_delay_us(2B): PD 안정화 딜레이 (마이크로초) + * life_cycle(4B): 디바이스 사용 횟수 + * + * [주요 API] + * fs_storage_init(): FDS 초기화 (부트 시 1회) + * config_load(): FDS에서 설정 로드 (없으면 기본값 생성) + * config_save(): 현재 설정을 FDS에 저장 + * ******************************************************************************/ #ifndef IHP_FSTORAGE_H_ diff --git a/project/ble_peripheral/ble_app_bladder_patch/i2c_manager.c b/project/ble_peripheral/ble_app_bladder_patch/i2c_manager.c index fc79152..e82794d 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/i2c_manager.c +++ b/project/ble_peripheral/ble_app_bladder_patch/i2c_manager.c @@ -1,6 +1,28 @@ /******************************************************************************* * @file i2c_manager.c * @brief Reliable HW↔SW I2C Switching Logic (with Mode Set Logging) + ******************************************************************************* + * + * [모듈 개요] + * I2C 버스의 HW(하드웨어 TWI) / SW(소프트웨어 비트뱅) 모드 전환을 관리하는 모듈. + * + * - HW I2C: nRF52840 내장 TWI 하드웨어 주변장치를 사용 (ICM42670P IMU 센서 통신용, 400kHz) + * - SW I2C: GPIO 비트뱅 방식의 소프트웨어 I2C (현재 사용하지 않는 레거시 코드) + * + * [핀 설정] + * SCL = P1.14 (ICM42670_I2C_SCL_PIN) + * SDA = P1.15 (ICM42670_I2C_SDA_PIN) + * + * [주요 함수] + * hw_i2c_init_once() : SW→HW 전환 또는 HW 초기화 (이미 HW 모드면 중복 초기화 방지) + * sw_i2c_init_once() : HW→SW 전환 (TWI 해제 후 SW 모드 진입, 레거시) + * i2c_reset_state() : 모든 I2C 모드 플래그를 초기화 (HW/SW 모두 false) + * + * [동작 원리] + * HW_I2C_FRQ, SW_I2C_FRQ 두 개의 bool 플래그로 현재 모드를 추적하며, + * 한 번에 하나의 모드만 활성화되도록 상호 배제(mutex) 방식으로 관리한다. + * 모드 전환 시 기존 모드의 리소스를 먼저 해제한 후 새 모드를 초기화한다. + * ******************************************************************************/ #include "i2c_manager.h" @@ -12,40 +34,52 @@ #include "boards.h" #include "system_interface.h" -bool HW_I2C_FRQ = true; -bool SW_I2C_FRQ = false; +/* 현재 I2C 모드 상태 플래그 (true = 해당 모드 활성화) */ +bool HW_I2C_FRQ = true; /* HW TWI 모드 활성 여부 (기본값: true, 초기 상태는 HW) */ +bool SW_I2C_FRQ = false; /* SW 비트뱅 모드 활성 여부 */ +/* TWI 인스턴스 번호 (nRF52840은 TWI0, TWI1 두 개 지원) */ #define TWI_INSTANCE 0 /* TWI (I2C) 하드웨어 인스턴스 : IMU 드라이버에서 사용 - jhChun 26.03.16 */ const nrfx_twi_t m_twi = NRFX_TWI_INSTANCE(TWI_INSTANCE); /* TWI (I2C) 해제 - jhChun 26.03.16 */ +/* TWI 하드웨어를 비활성화하고 초기화 해제하여 GPIO 핀을 반환한다 */ static void twi_uninitialize(void){ - nrfx_twi_disable(&m_twi); - nrfx_twi_uninit(&m_twi); + nrfx_twi_disable(&m_twi); /* TWI 주변장치 비활성화 */ + nrfx_twi_uninit(&m_twi); /* TWI 초기화 해제 (핀 리소스 반환) */ } /* TWI (I2C) 하드웨어 초기화 (SCL/SDA핀, 400kHz) - jhChun 26.03.16 */ +/* SCL, SDA 핀을 설정하고 400kHz Fast Mode로 TWI를 초기화 및 활성화한다 */ static void twi_initialize(void){ ret_code_t err_code; + /* TWI 설정 구조체: 핀 번호, 클럭 속도, 인터럽트 우선순위 지정 */ const nrfx_twi_config_t twi_config = { - .scl = ICM42670_I2C_SCL_PIN, - .sda = ICM42670_I2C_SDA_PIN, - .frequency = NRF_TWI_FREQ_400K, - .interrupt_priority = APP_IRQ_PRIORITY_HIGH, + .scl = ICM42670_I2C_SCL_PIN, /* SCL 핀 (P1.14) */ + .sda = ICM42670_I2C_SDA_PIN, /* SDA 핀 (P1.15) */ + .frequency = NRF_TWI_FREQ_400K, /* 400kHz Fast Mode */ + .interrupt_priority = APP_IRQ_PRIORITY_HIGH, /* 높은 인터럽트 우선순위 */ }; + /* TWI 초기화 (이벤트 핸들러 NULL = 블로킹 모드) */ err_code = nrfx_twi_init(&m_twi, &twi_config, NULL, NULL); APP_ERROR_CHECK(err_code); - nrfx_twi_enable(&m_twi); + nrfx_twi_enable(&m_twi); /* TWI 주변장치 활성화 → I2C 통신 가능 */ } /* -------------------------------------------------------------------------- */ /* HW (TWI) 초기화 */ /* -------------------------------------------------------------------------- */ +/* + * HW I2C 모드로 전환하거나 초기화하는 함수. + * - SW 모드가 활성화되어 있으면 SW 플래그를 해제하고 HW로 전환 + * - 이미 HW 모드이면 중복 초기화를 방지하여 바로 리턴 + * - 최초 HW 초기화 시 twi_initialize()를 호출하여 TWI 하드웨어 설정 + */ void hw_i2c_init_once(void) { // SW 모드일 경우 강제 해제 후 HW 전환 @@ -55,9 +89,10 @@ void hw_i2c_init_once(void) // SW 리소스 해제 (필요 시 추가) SW_I2C_FRQ = false; - nrf_delay_ms(2); + nrf_delay_ms(2); /* 모드 전환 안정화 대기 (2ms) */ } + /* 이미 HW 모드가 활성화되어 있으면 중복 초기화 방지를 위해 즉시 리턴 */ // 이미 HW면 스킵 if (HW_I2C_FRQ) { @@ -65,10 +100,12 @@ void hw_i2c_init_once(void) return; } + /* HW TWI 하드웨어 초기화 수행 (SCL/SDA 핀 설정, 400kHz, 활성화) */ // 실제 HW 초기화 twi_initialize(); - nrf_delay_ms(2); + nrf_delay_ms(2); /* 초기화 후 안정화 대기 (2ms) */ + /* 모드 플래그 갱신: HW 활성, SW 비활성 */ HW_I2C_FRQ = true; SW_I2C_FRQ = false; @@ -78,6 +115,12 @@ void hw_i2c_init_once(void) /* -------------------------------------------------------------------------- */ /* SW (Port Bang-Bang) 초기화 */ /* -------------------------------------------------------------------------- */ +/* + * SW I2C(비트뱅) 모드로 전환하는 함수. (현재 레거시, 사용하지 않음) + * - HW 모드가 활성화되어 있으면 TWI를 해제하고 SW로 전환 + * - 이미 SW 모드이면 중복 초기화를 방지하여 바로 리턴 + * - power_control.c의 power_loop()에서 Step 0에서 호출됨 + */ void sw_i2c_init_once(void) { // HW 모드일 경우 강제 해제 후 SW 전환 @@ -85,9 +128,9 @@ void sw_i2c_init_once(void) { //DBG_PRINTF("[I2C]HW→SW\r\n"); - nrfx_twi_disable(&m_twi); - nrfx_twi_uninit(&m_twi); - nrf_delay_ms(2); + nrfx_twi_disable(&m_twi); /* TWI 비활성화 */ + nrfx_twi_uninit(&m_twi); /* TWI 초기화 해제 */ + nrf_delay_ms(2); /* 모드 전환 안정화 대기 (2ms) */ HW_I2C_FRQ = false; } @@ -99,10 +142,12 @@ void sw_i2c_init_once(void) return; } + /* TWI 라인 완전 해제 후 SW 비트뱅 모드 진입 */ // 실제 SW 초기화 twi_uninitialize(); // TWI 라인 해제 - nrf_delay_ms(1); + nrf_delay_ms(1); /* 해제 후 안정화 대기 (1ms) */ + /* 모드 플래그 갱신: SW 활성, HW 비활성 */ SW_I2C_FRQ = true; HW_I2C_FRQ = false; @@ -112,9 +157,14 @@ void sw_i2c_init_once(void) /* -------------------------------------------------------------------------- */ /* 전체 리셋 */ /* -------------------------------------------------------------------------- */ +/* + * 모든 I2C 모드 플래그를 초기화하는 함수. + * HW/SW 모두 비활성 상태로 만들어, 다음 init 호출 시 강제로 재초기화되도록 한다. + * 주로 시스템 리셋이나 에러 복구 시 사용. + */ void i2c_reset_state(void) { - HW_I2C_FRQ = false; - SW_I2C_FRQ = false; + HW_I2C_FRQ = false; /* HW 모드 플래그 초기화 */ + SW_I2C_FRQ = false; /* SW 모드 플래그 초기화 */ DBG_PRINTF("Flags reset\r\n"); } diff --git a/project/ble_peripheral/ble_app_bladder_patch/i2c_manager.h b/project/ble_peripheral/ble_app_bladder_patch/i2c_manager.h index c913b33..5100725 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/i2c_manager.h +++ b/project/ble_peripheral/ble_app_bladder_patch/i2c_manager.h @@ -1,6 +1,16 @@ /******************************************************************************* * @file i2c_manager.h * @brief Common header for HW/SW I2C mutex control + ******************************************************************************* + * + * [헤더 개요] + * I2C 버스 HW/SW 모드 전환 관리자의 공용 인터페이스 헤더. + * + * - HW_I2C_FRQ: HW TWI 모드 활성 여부 (true = HW I2C 사용 중) + * - SW_I2C_FRQ: SW 비트뱅 모드 활성 여부 (true = SW I2C 사용 중) + * + * 두 플래그는 상호 배제적으로 동작하며, 동시에 true가 되지 않도록 관리된다. + * ******************************************************************************/ #ifndef __I2C_MANAGER_H__ #define __I2C_MANAGER_H__ @@ -8,11 +18,33 @@ #include #include "app_error.h" -extern bool HW_I2C_FRQ; -extern bool SW_I2C_FRQ; +/* I2C 모드 상태 플래그 (외부 참조용) */ +extern bool HW_I2C_FRQ; /* HW TWI 모드 활성 여부 */ +extern bool SW_I2C_FRQ; /* SW 비트뱅 모드 활성 여부 */ +/** + * @brief HW I2C(TWI) 모드 초기화 (중복 초기화 방지) + * + * SW 모드가 활성화되어 있으면 해제 후 HW로 전환한다. + * 이미 HW 모드이면 아무 동작 없이 리턴한다. + * ICM42670P IMU 센서 통신 전에 호출하여 HW I2C를 준비한다. + */ void hw_i2c_init_once(void); + +/** + * @brief SW I2C(비트뱅) 모드 초기화 (레거시, 현재 미사용) + * + * HW 모드가 활성화되어 있으면 TWI를 해제한 후 SW로 전환한다. + * 이미 SW 모드이면 아무 동작 없이 리턴한다. + */ void sw_i2c_init_once(void); + +/** + * @brief I2C 모드 플래그 전체 초기화 + * + * HW_I2C_FRQ, SW_I2C_FRQ를 모두 false로 리셋한다. + * 다음 init 호출 시 강제로 재초기화가 수행된다. + */ void i2c_reset_state(void); #endif diff --git a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw.h b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw.h index 956f1cd..3e4dc9e 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw.h +++ b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw.h @@ -3,7 +3,20 @@ * @author CandyPops Co. * @version V1.0.0 * @date 2022-09-05 - * @brief + * @brief + ******************************************************************************/ + +/******************************************************************************* + * [헤더 개요] ICM42670P IMU 드라이버 상위 레이어 선언 + * + * IMU 센서의 초기화, 설정, 데이터 읽기를 위한 함수 프로토타입과 + * 동작 모드 설정 매크로를 정의한다. + * + * 주요 설정 매크로: + * SERIF_TYPE - 통신 인터페이스 (UI_I2C) + * USE_LOW_NOISE_MODE - 1:저잡음(800Hz), 0:저전력(100Hz) + * USE_HIGH_RES_MODE - 1:20비트 고해상도, 0:16비트 표준 + * USE_FIFO - 1:FIFO 사용, 0:레지스터 직접 읽기 ******************************************************************************/ #ifndef _APP_RAW_H_ @@ -16,64 +29,71 @@ #include "inv_imu_driver.h" -/*** Example configuration ***/ +/*** 설정 매크로 ***/ -/* - * Select communication link between SmartMotion and IMU +/* + * MCU와 IMU 간 통신 인터페이스 선택 + * UI_I2C: I2C 통신 사용 (기본) */ #define SERIF_TYPE UI_I2C /* - * Set power mode flag - * Set this flag to run example in low-noise mode. - * Reset this flag to run example in low-power mode. - * Note: low-noise mode is not available with sensor data frequencies less than 12.5Hz. + * 전원 모드 설정 + * 1: 저잡음 모드 — 800Hz ODR, 높은 정밀도, 높은 전력 소모 + * 0: 저전력 모드 — 100Hz ODR, 낮은 전력 소모 + * 주의: 12.5Hz 미만 ODR에서는 저잡음 모드 사용 불가 */ #define USE_LOW_NOISE_MODE 1 /* - * Select Fifo resolution Mode (default is low resolution mode) - * Low resolution mode: 16 bits data format - * High resolution mode: 20 bits data format - * Warning: Enabling High Res mode will force FSR to 16g and 2000dps + * FIFO 해상도 모드 선택 + * 0: 저해상도 — 16비트 데이터 (기본) + * 1: 고해상도 — 20비트 데이터 (FSR이 16g/2000dps로 강제 고정됨) */ #define USE_HIGH_RES_MODE 0 /* - * Select to use FIFO or to read data from registers + * 데이터 읽기 방식 선택 + * 0: 레지스터 직접 읽기 (현재 사용 중) + * 1: FIFO에서 읽기 */ #define USE_FIFO 0 /** - * \brief This function is in charge of reseting and initializing IMU device. It should - * be successfully executed before any access to IMU device. + * \brief IMU 디바이스를 리셋하고 초기화한다. WHOAMI 확인 포함. + * 다른 IMU 접근 함수 호출 전에 반드시 성공적으로 실행되어야 한다. * - * \return 0 on success, negative value on error. + * \return 0=성공, 음수=에러 */ int setup_imu_device(struct inv_imu_serif *icm_serif); /** - * \brief This function configures the device in order to output gyro and accelerometer. - * \return 0 on success, negative value on error. + * \brief 자이로 및 가속도계 출력을 위한 디바이스 설정을 수행한다. + * FSR, ODR, 전원 모드, FIFO 설정 등을 적용한다. + * \return 0=성공, 음수=에러 */ int configure_imu_device(void); /** - * \brief This function extracts data from the IMU FIFO. - * \return 0 on success, negative value on error. + * \brief FIFO 또는 레지스터에서 IMU 데이터를 추출한다. + * 내부적으로 imu_callback()이 호출되어 데이터를 처리한다. + * \return 0=성공, 음수=에러 */ int get_imu_data(void); /** - * \brief This function is the custom handling packet function. - * \param[in] event structure containing sensor data from one packet + * \brief 센서 데이터 수신 콜백. 마운팅 매트릭스 적용 후 + * info4/BLE/UART 모드에 따라 데이터를 출력한다. + * \param[in] event 하나의 센서 데이터 패킷을 담은 구조체 */ void imu_callback(inv_imu_sensor_event_t *event); /** - * \brief Direct IMU register read — bypasses DRDY, sends rsp: via BLE. - * \return 0 on success, negative value on error. + * \brief 드라이버 API를 우회한 직접 I2C 레지스터 읽기. + * DRDY 인터럽트 없이 즉시 센서 데이터를 읽어 BLE로 전송한다. + * 읽기 후 IMU를 슬립 모드로 전환하여 전력을 절감한다. + * \return 0=성공, 음수=에러 */ int imu_read_direct(void); diff --git a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw_main.h b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw_main.h index a0c2183..b7127be 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw_main.h +++ b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw_main.h @@ -3,16 +3,29 @@ * @author CandyPops Co. * @version V1.0.0 * @date 2022-09-05 - * @brief + * @brief + ******************************************************************************/ + +/******************************************************************************* + * [헤더 개요] ICM42670P 메인 초기화/폴링 루프 선언 + * + * ICM42670P IMU 센서의 전체 초기화 및 메인 루프 함수를 선언한다. + * - icm42670_init() : 전체 초기화 (MCU설정 → IMU초기화 → 센서설정 → 인터럽트 활성화) + * - icm42670_main() : 메인 폴링 루프 (INT1 인터럽트 확인 → 데이터 읽기) + * - icm42670_uninit() : 해제 (프로토타입만 선언, 구현은 별도) ******************************************************************************/ #ifndef _APP_RAW_MAIN_H_ #define _APP_RAW_MAIN_H_ #include "sdk_config.h" +/* ICM42670P 전체 초기화 — MCU I2C 설정 → IMU 드라이버 초기화 → 센서 설정 → 인터럽트 활성화 */ int icm42670_init(void); + +/* ICM42670P 메인 폴링 루프 — INT1 인터럽트 플래그 확인 후 센서 데이터 읽기 */ void icm42670_main(void); + +/* ICM42670P 해제 (프로토타입 선언) */ int icm42670_uninit(void); #endif /* !_APP_RAW_MAIN_H_ */ - diff --git a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.c b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.c index 2500960..0cec044 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.c +++ b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.c @@ -3,7 +3,29 @@ * @author CandyPops Co. * @version V1.0.0 * @date 2022-09-05 - * @brief + * @brief + ******************************************************************************/ + +/******************************************************************************* + * [모듈 개요] ICM42670P IMU 센서 I2C 통신 인터페이스 + * + * nRF52840의 TWI(I2C) 하드웨어를 사용하여 ICM42670P IMU 센서와 통신하는 + * 저수준 인터페이스 모듈이다. + * + * - I2C 슬레이브 주소: 0x68 (ICM42670P 기본 주소) + * - I2C 핀 설정: SCL=P1.14, SDA=P1.15 (system_interface.h에서 정의) + * - TWI 인스턴스: NRFX_TWI_INSTANCE(0) 사용 + * - 통신 속도: 100kHz (NRF_TWI_FREQ_100K) + * + * 주요 함수 흐름: + * inv_io_hal_init() → I2C 또는 SPI 초기화 (현재 I2C만 구현) + * inv_io_hal_read_reg() → 레지스터 읽기 (TX로 주소 전송 → RX로 데이터 수신) + * inv_io_hal_write_reg() → 레지스터 쓰기 (주소+데이터를 한번에 TX) + * + * 에러 처리: 모든 I2C 읽기/쓰기에서 실패 시 1회 재시도 후 에러 출력 + * + * 참고: SPI4 인터페이스 코드 경로도 존재하지만 현재 미구현 상태이며, + * 실제 운용에서는 I2C(UI_I2C)만 사용한다. ******************************************************************************/ /* board driver */ @@ -21,18 +43,31 @@ #include "system_interface.h" #include "nrf_delay.h" -/* I2C number and slave address for INV device */ +/* ICM42670P I2C 슬레이브 주소 및 직렬 쓰기 최대 바이트 수 */ #define ICM_I2C_ADDR 0x68 #define INV_MAX_SERIAL_WRITE 16 -/* TWI instance. */ +/* TWI(I2C) 인스턴스 생성 — system_interface.h의 ICM42670_I2C_INSTANCE(0)을 사용 */ const nrfx_twi_t m_twi_icm42670 = NRFX_TWI_INSTANCE(ICM42670_I2C_INSTANCE); +/* + * inv_i2c_master_uninitialize() + * I2C 버스를 비활성화하고 TWI 인스턴스를 해제한다. + * 슬립 모드 진입 전 또는 재초기화 전에 호출된다. + */ void inv_i2c_master_uninitialize(void){ nrfx_twi_disable(&m_twi_icm42670); nrfx_twi_uninit(&m_twi_icm42670); } +/* + * inv_i2c_master_initialize() + * nRF52840 TWI 하드웨어를 초기화하고 활성화한다. + * - SCL: P1.14, SDA: P1.15 + * - 속도: 100kHz + * - 인터럽트 우선순위: 최고 (APP_IRQ_PRIORITY_HIGH) + * - 이벤트 핸들러 없음 (블로킹 모드로 동작) + */ void inv_i2c_master_initialize(void){ ret_code_t err_code; @@ -43,50 +78,75 @@ void inv_i2c_master_initialize(void){ .interrupt_priority = APP_IRQ_PRIORITY_HIGH, }; + /* TWI 드라이버 초기화 (이벤트 핸들러=NULL → 블로킹 모드) */ err_code = nrfx_twi_init(&m_twi_icm42670, &twi_icm42670_config, NULL, NULL); APP_ERROR_CHECK(err_code); + /* TWI 하드웨어 활성화 — 이후 tx/rx 가능 */ nrfx_twi_enable(&m_twi_icm42670); } +/* + * icm42670_twi_tx() + * I2C 전송 래퍼 함수. nrfx_twi_tx를 호출하여 데이터를 송신한다. + * no_stop=true이면 STOP 컨디션을 보내지 않음 (Repeated START를 위해 사용) + */ uint32_t icm42670_twi_tx( uint8_t device_id, uint8_t const * p_data, uint8_t length, bool no_stop) { - ret_code_t ret; - ret = nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop); + ret_code_t ret; + ret = nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop); return ret; } +/* + * icm42670_twi_rx() + * I2C 수신 래퍼 함수. nrfx_twi_rx를 호출하여 데이터를 수신한다. + */ uint32_t icm42670_twi_rx( uint8_t device_id, uint8_t * p_data, uint8_t length) { - ret_code_t ret; + ret_code_t ret; ret = nrfx_twi_rx(&m_twi_icm42670, device_id, p_data, length); return ret; } +/* + * inv_i2c_master_read_register() + * ICM42670P의 특정 레지스터에서 데이터를 읽는다. + * + * 동작 순서: + * 1) TX: 레지스터 주소 1바이트 전송 (no_stop=true → Repeated START 준비) + * 2) RX: 지정된 길이만큼 데이터 수신 + * + * 에러 처리: TX, RX 각각 실패 시 1회 재시도한다. + */ static unsigned long inv_i2c_master_read_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, unsigned char *RegisterValue){ //ret_code_t ret; uint32_t ret; uint8_t addr8 = (uint8_t)RegisterAddr; + /* 1단계: 읽을 레지스터 주소를 전송 (STOP 없이 → Repeated START 사용) */ ret = icm42670_twi_tx(Address, &addr8, 1, true); if(ret != NRF_SUCCESS) { + /* 실패 시 1회 재시도 */ ret = icm42670_twi_tx(Address, &addr8, 1, true); if(ret != NRF_SUCCESS) { printf("ERR! i2c read-1\r\n"); } } - ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen); + /* 2단계: 해당 레지스터에서 데이터 수신 */ + ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen); if(ret != NRF_SUCCESS) { - ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen); + /* 실패 시 1회 재시도 */ + ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen); if(ret != NRF_SUCCESS) { printf("ERR! i2c read-2\r\n"); } @@ -95,14 +155,28 @@ static unsigned long inv_i2c_master_read_register(unsigned char Address, unsigne return ret; } +/* + * inv_i2c_master_write_register() + * ICM42670P의 특정 레지스터에 데이터를 쓴다. + * + * 동작 순서: + * 1) 버퍼[0]에 레지스터 주소, 버퍼[1~N]에 쓸 데이터를 배치 + * 2) TX: 주소+데이터를 한번에 전송 (no_stop=false → STOP 컨디션 포함) + * + * 에러 처리: 실패 시 1회 재시도한다. + */ static unsigned long inv_i2c_master_write_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, const unsigned char *RegisterValue){ uint32_t ret; - uint8_t buffer[1 + INV_MAX_SERIAL_WRITE]; /* Addr + data */ + uint8_t buffer[1 + INV_MAX_SERIAL_WRITE]; /* 레지스터 주소(1) + 데이터(최대 16바이트) */ + /* 버퍼 구성: [레지스터 주소][데이터 바이트들] */ buffer[0] = (uint8_t)RegisterAddr; memcpy(buffer+1, RegisterValue, RegisterLen); + + /* 주소+데이터를 한번에 전송 */ ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false); if(ret != NRF_SUCCESS) { + /* 실패 시 1회 재시도 */ ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false); if(ret != NRF_SUCCESS) { printf("ERR! i2c write\r\n"); @@ -114,7 +188,12 @@ static unsigned long inv_i2c_master_write_register(unsigned char Address, unsign - +/* + * inv_io_hal_init() + * IMU 드라이버가 사용하는 시리얼 인터페이스(I2C 또는 SPI)를 초기화한다. + * serif->serif_type에 따라 분기하며, 현재는 I2C만 구현되어 있다. + * 반환값: 0=성공, -1=지원하지 않는 인터페이스 타입 + */ int inv_io_hal_init(struct inv_imu_serif *serif) { @@ -122,13 +201,14 @@ int inv_io_hal_init(struct inv_imu_serif *serif) switch (serif->serif_type) { case UI_SPI4: { + /* SPI4 초기화 — 현재 미구현 (I2C만 사용) */ break; } case UI_I2C: inv_i2c_master_initialize(); break; - + default: return -1; } @@ -137,12 +217,17 @@ int inv_io_hal_init(struct inv_imu_serif *serif) } +/* + * inv_io_hal_read_reg() + * IMU 드라이버 콜백: 지정된 레지스터에서 데이터를 읽는다. + * 시리얼 타입에 따라 I2C 또는 SPI 읽기를 수행한다. + */ int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen) { switch (serif->serif_type) { case UI_SPI4: return 0; - + case UI_I2C: return inv_i2c_master_read_register(ICM_I2C_ADDR, reg, rlen, rbuffer); @@ -151,12 +236,17 @@ int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuf } } +/* + * inv_io_hal_write_reg() + * IMU 드라이버 콜백: 지정된 레지스터에 데이터를 쓴다. + * 시리얼 타입에 따라 I2C 또는 SPI 쓰기를 수행한다. + */ int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen) { switch (serif->serif_type) { case UI_SPI4: return 0; - + case UI_I2C: return inv_i2c_master_write_register(ICM_I2C_ADDR, reg, wlen, wbuffer); @@ -164,20 +254,30 @@ int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t return -1; } } + +/* + * cat_read() + * 범용 I2C 읽기 함수 (디버그/레거시용). + * 8바이트를 읽어 첫 번째 바이트를 반환하고, 읽은 데이터를 콘솔에 출력한다. + * 참고: 현재 실제 운용에서는 사용되지 않으며, 디버그 목적으로 남아 있다. + */ uint8_t cat_read(uint8_t device_id, uint8_t address, uint8_t *data) { - + uint8_t read_data = 0; char adata[8]; ret_code_t err_code; //address = 1|(address<<1); address = (address & 0xFF); + + /* 레지스터 주소 전송 (STOP 없이, Repeated START 준비) */ err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, &address, 1, true); if (err_code != NRF_SUCCESS) { // Handle error // return; } + /* 8바이트 데이터 수신 */ err_code = nrfx_twi_rx(&m_twi_icm42670, device_id, data, 8); if (err_code != NRF_SUCCESS) { // Handle error @@ -187,41 +287,48 @@ uint8_t cat_read(uint8_t device_id, uint8_t address, uint8_t *data) memcpy(adata,data,8); printf("Data %s . \r\n", adata); return read_data; - - - - - + + + + + } +/* + * cat_write() + * 범용 I2C 쓰기 함수 (디버그/레거시용). + * 주소 1바이트 + 데이터 1바이트를 전송한다. + * 참고: buffer에 6바이트를 복사하지만, 실제 전송은 2바이트만 수행한다. + */ void cat_write(uint8_t device_id, uint8_t address, uint8_t *data){ - + uint8_t buffer[7]={0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - + address = (address & 0xFF); - + buffer[0] = (address); //buffer[1] =(data & 0xFF); memcpy(buffer+1,data,6); ret_code_t err_code; //err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, 0x00, 1, false); + + /* 주소(1바이트) + 데이터(1바이트) = 2바이트 전송 */ err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, buffer, 2, false); // err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 2, false); // nrfx_twi_rx(&m_twi_icm42670, device_id, p_data, length); -// nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop); +// nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop); printf("Data %x %x %x %x. \r\n", buffer[0], buffer[1], buffer[2], buffer[3]); - + //err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 6, false); if (err_code != NRF_SUCCESS) { - + printf("TWI Error."); - + } - + } - diff --git a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.h b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.h index 76ae19f..b98505b 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.h +++ b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.h @@ -3,7 +3,22 @@ * @author CandyPops Co. * @version V1.0.0 * @date 2022-09-05 - * @brief + * @brief + ******************************************************************************/ + +/******************************************************************************* + * [헤더 개요] ICM42670P I2C 통신 인터페이스 선언 + * + * nRF52840 TWI 하드웨어를 통해 ICM42670P IMU 센서와 통신하기 위한 + * 핀 정의, 함수 프로토타입을 선언한다. + * + * 핀 배치: + * - I2C SCL : P1.14 + * - I2C SDA : P1.15 + * - INT1 : P1.13 (데이터 준비 인터럽트) + * - INT2 : P0.26 (보조 인터럽트, 현재 미사용) + * + * TWI 인스턴스: 0번 사용 ******************************************************************************/ #ifndef _SYSTEM_INTERFACE_H_ @@ -17,25 +32,42 @@ #endif -#define ICM42670_I2C_INSTANCE 0 /**< I2C instance index. */ -#define ICM42670_I2C_SDA_PIN NRF_GPIO_PIN_MAP(1,15) -#define ICM42670_I2C_SCL_PIN NRF_GPIO_PIN_MAP(1,14) -#define ICM42670_INT1_PIN NRF_GPIO_PIN_MAP(1,13) -#define ICM42670_INT2_PIN NRF_GPIO_PIN_MAP(0,26) +#define ICM42670_I2C_INSTANCE 0 /**< I2C(TWI) 인스턴스 인덱스 */ +#define ICM42670_I2C_SDA_PIN NRF_GPIO_PIN_MAP(1,15) /**< SDA 핀: P1.15 */ +#define ICM42670_I2C_SCL_PIN NRF_GPIO_PIN_MAP(1,14) /**< SCL 핀: P1.14 */ +#define ICM42670_INT1_PIN NRF_GPIO_PIN_MAP(1,13) /**< INT1 핀: P1.13 (데이터 준비 인터럽트) */ +#define ICM42670_INT2_PIN NRF_GPIO_PIN_MAP(0,26) /**< INT2 핀: P0.26 (보조, 현재 미사용) */ + +/* I2C 전송 래퍼 — no_stop=true이면 Repeated START를 위해 STOP 컨디션 생략 */ uint32_t icm42670_twi_tx( uint8_t device_id, uint8_t const * p_data, uint8_t length, bool no_stop); + +/* I2C 수신 래퍼 */ uint32_t icm42670_twi_rx( uint8_t device_id, uint8_t * p_data, uint8_t length); + +/* 범용 I2C 읽기 (디버그/레거시용) — 8바이트를 읽어 첫 바이트 반환 */ uint8_t cat_read (uint8_t device_id, uint8_t address, uint8_t *data); -void cat_write (uint8_t device_id, uint8_t address, uint8_t *data); + +/* 범용 I2C 쓰기 (디버그/레거시용) — 주소+데이터 2바이트 전송 */ +void cat_write (uint8_t device_id, uint8_t address, uint8_t *data); + +/* I2C 하드웨어 해제 (슬립 또는 재초기화 전 호출) */ void inv_i2c_master_uninitialize(void); + +/* I2C 하드웨어 초기화 (100kHz, 블로킹 모드) */ void inv_i2c_master_initialize(void); + +/* IMU 드라이버용 시리얼 인터페이스 초기화 (I2C/SPI 분기) */ int inv_io_hal_init(struct inv_imu_serif *serif); + +/* IMU 드라이버 콜백: 레지스터 읽기 */ int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen); + +/* IMU 드라이버 콜백: 레지스터 쓰기 */ int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen); #endif /* !_SYSTEM_INTERFACE_H_ */ - diff --git a/project/ble_peripheral/ble_app_bladder_patch/main.c b/project/ble_peripheral/ble_app_bladder_patch/main.c index 6670842..365b110 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/main.c +++ b/project/ble_peripheral/ble_app_bladder_patch/main.c @@ -608,11 +608,15 @@ static void nrf_qwr_error_handler(uint32_t nrf_error) extern bool maa_async_on_tx_ready(void); extern bool maa_async_is_busy(void); +/* 2026-03-17: BLE 콜백에서 블로킹 방지 — 명령을 버퍼에 저장 후 메인 루프에서 처리 */ +static volatile uint8_t pending_cmd_buf[BLE_NUS_MAX_DATA_LEN]; +static volatile uint8_t pending_cmd_len = 0; + /** * @brief NUS(Nordic UART Service) 데이터 수신 핸들러 * * BLE를 통해 스마트폰 앱에서 데이터를 수신했을 때 호출됨 - * - RX_DATA 이벤트: 수신된 명령을 received_command_process()로 전달 + * - RX_DATA 이벤트: 수신 데이터를 버퍼에 복사 (메인 루프에서 dr_cmd_parser 호출) * - TX_RDY 이벤트: BLE TX 버퍼에 공간이 생겼을 때 비동기 MAA 전송 계속 */ static void nus_data_handler(ble_nus_evt_t * p_evt) @@ -622,7 +626,12 @@ static void nus_data_handler(ble_nus_evt_t * p_evt) cmd_type_t = CMD_BLE; ble_got_new_data = true; DBG_PRINTF("[NUS] RX len=%d\r\n", p_evt->params.rx_data.length); - dr_cmd_parser(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length); + + /* 콜백에서는 복사만 하고, 메인 루프에서 처리 */ + if (p_evt->params.rx_data.length <= BLE_NUS_MAX_DATA_LEN) { + memcpy((void *)pending_cmd_buf, p_evt->params.rx_data.p_data, p_evt->params.rx_data.length); + pending_cmd_len = p_evt->params.rx_data.length; + } } else if (p_evt->type == BLE_NUS_EVT_TX_RDY) { @@ -1771,6 +1780,13 @@ int main(void) // MAIN LOOP for (;;) { + /* BLE 콜백에서 수신된 명령이 있으면 여기서 처리 (블로킹 방지) */ + if (pending_cmd_len > 0) { + uint8_t len = pending_cmd_len; + pending_cmd_len = 0; + dr_cmd_parser((const uint8_t *)pending_cmd_buf, len); + } + idle_state_handle(); } } diff --git a/project/ble_peripheral/ble_app_bladder_patch/main_timer.h b/project/ble_peripheral/ble_app_bladder_patch/main_timer.h index 3029238..e0a7e21 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/main_timer.h +++ b/project/ble_peripheral/ble_app_bladder_patch/main_timer.h @@ -3,15 +3,35 @@ * @author CandyPops Co. * @version V1.0.0 * @date 2022-09-05 - * @brief + * @brief + ******************************************************************************* + * + * [헤더 개요] + * 메인 이벤트 루프 타이머의 공용 인터페이스 헤더. + * + * 10ms(일반) 또는 80ms(디테일) 간격의 싱글샷 타이머를 사용하여 + * main_loop() 콜백에서 센서 데이터 수집 및 시스템 제어를 수행한다. + * + * [주요 함수] + * main_timer_start() : 타이머 시작 (싱글샷, 수동 재시작 필요) + * main_timer_stop() : 타이머 정지 + * main_timer_init() : 타이머 초기화 (앱 시작 시 1회 호출) + * ******************************************************************************/ #ifndef TIMER_ROUTINE_H__ #define TIMER_ROUTINE_H__ +/** @brief 메인 루프 타이머 시작 (싱글샷, MAIN_LOOP_INTERVAL 후 main_loop 호출) */ void main_timer_start(void); + +/** @brief 지정된 간격(ms)으로 메인 루프 타이머 시작 */ +void main_timer_start_ms(uint32_t interval_ms); + +/** @brief 메인 루프 타이머 정지 */ void main_timer_stop(void); + +/** @brief 메인 루프 타이머 초기화 (앱 시작 시 1회, 싱글샷 모드로 생성) */ void main_timer_init(void); #endif //TIMER_ROUTINE_H__ - diff --git a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvoptx b/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvoptx index 26232f3..cf05330 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvoptx +++ b/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvoptx @@ -225,7 +225,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -241,7 +241,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -305,7 +305,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -321,7 +321,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -369,7 +369,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -385,7 +385,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -401,7 +401,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -417,7 +417,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -433,7 +433,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -449,7 +449,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -465,7 +465,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -481,7 +481,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -497,7 +497,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -513,7 +513,7 @@ 0 0 0 - ..\..\..\cmd_parse.c + C:\jhChun\VesiScan-Basic_0313\VesiScan-Basic\project\ble_peripheral\ble_app_bladder_patch\cmd_parse.c @@ -1207,30 +1207,6 @@ 1 8 - 1 - 0 - 0 - 0 - ..\..\..\cmd_parse.c - cmd_parse.c - 0 - 0 - - - 1 - 9 - 5 - 0 - 0 - 0 - ..\..\..\cmd_parse.h - cmd_parse.h - 0 - 0 - - - 1 - 10 5 0 0 @@ -1242,7 +1218,7 @@ 1 - 11 + 9 1 0 0 @@ -1254,7 +1230,7 @@ 1 - 12 + 10 5 0 0 @@ -1266,7 +1242,7 @@ 1 - 13 + 11 1 0 0 @@ -1278,7 +1254,7 @@ 1 - 14 + 12 5 0 0 @@ -1290,7 +1266,7 @@ 1 - 15 + 13 1 0 0 @@ -1302,7 +1278,7 @@ 1 - 16 + 14 1 0 0 @@ -1314,7 +1290,7 @@ 1 - 17 + 15 5 0 0 @@ -1326,7 +1302,7 @@ 1 - 18 + 16 1 0 0 @@ -1338,7 +1314,7 @@ 1 - 19 + 17 5 0 0 @@ -1350,7 +1326,7 @@ 1 - 20 + 18 1 0 0 @@ -1362,7 +1338,7 @@ 1 - 21 + 19 5 0 0 @@ -1374,7 +1350,7 @@ 1 - 22 + 20 1 0 0 @@ -1386,7 +1362,7 @@ 1 - 23 + 21 1 0 0 @@ -1398,7 +1374,7 @@ 1 - 24 + 22 5 0 0 @@ -1418,7 +1394,7 @@ 0 2 - 25 + 23 1 0 0 @@ -1438,7 +1414,7 @@ 0 3 - 26 + 24 1 0 0 @@ -1450,7 +1426,7 @@ 3 - 27 + 25 1 0 0 @@ -1470,7 +1446,7 @@ 0 4 - 28 + 26 1 0 0 @@ -1484,13 +1460,13 @@ nRF_BLE - 0 + 1 0 0 0 5 - 29 + 27 1 0 0 @@ -1502,7 +1478,7 @@ 5 - 30 + 28 1 0 0 @@ -1514,7 +1490,7 @@ 5 - 31 + 29 1 0 0 @@ -1526,7 +1502,7 @@ 5 - 32 + 30 1 0 0 @@ -1538,7 +1514,7 @@ 5 - 33 + 31 1 0 0 @@ -1550,7 +1526,7 @@ 5 - 34 + 32 1 0 0 @@ -1562,7 +1538,7 @@ 5 - 35 + 33 1 0 0 @@ -1574,7 +1550,7 @@ 5 - 36 + 34 1 0 0 @@ -1586,7 +1562,7 @@ 5 - 37 + 35 1 0 0 @@ -1598,7 +1574,7 @@ 5 - 38 + 36 1 0 0 @@ -1610,7 +1586,7 @@ 5 - 39 + 37 1 0 0 @@ -1622,7 +1598,7 @@ 5 - 40 + 38 1 0 0 @@ -1634,7 +1610,7 @@ 5 - 41 + 39 1 0 0 @@ -1646,7 +1622,7 @@ 5 - 42 + 40 1 0 0 @@ -1658,7 +1634,7 @@ 5 - 43 + 41 1 0 0 @@ -1670,7 +1646,7 @@ 5 - 44 + 42 1 0 0 @@ -1682,7 +1658,7 @@ 5 - 45 + 43 1 0 0 @@ -1694,7 +1670,7 @@ 5 - 46 + 44 1 0 0 @@ -1706,7 +1682,7 @@ 5 - 47 + 45 1 0 0 @@ -1718,7 +1694,7 @@ 5 - 48 + 46 1 0 0 @@ -1738,7 +1714,7 @@ 0 6 - 49 + 47 1 0 0 @@ -1758,7 +1734,7 @@ 0 7 - 50 + 48 1 0 0 @@ -1770,7 +1746,7 @@ 7 - 51 + 49 1 0 0 @@ -1782,7 +1758,7 @@ 7 - 52 + 50 1 0 0 @@ -1794,7 +1770,7 @@ 7 - 53 + 51 1 0 0 @@ -1806,7 +1782,7 @@ 7 - 54 + 52 1 0 0 @@ -1818,7 +1794,7 @@ 7 - 55 + 53 1 0 0 @@ -1830,7 +1806,7 @@ 7 - 56 + 54 1 0 0 @@ -1842,7 +1818,7 @@ 7 - 57 + 55 1 0 0 @@ -1854,7 +1830,7 @@ 7 - 58 + 56 1 0 0 @@ -1866,7 +1842,7 @@ 7 - 59 + 57 1 0 0 @@ -1878,7 +1854,7 @@ 7 - 60 + 58 1 0 0 @@ -1890,7 +1866,7 @@ 7 - 61 + 59 1 0 0 @@ -1902,7 +1878,7 @@ 7 - 62 + 60 1 0 0 @@ -1914,7 +1890,7 @@ 7 - 63 + 61 1 0 0 @@ -1926,7 +1902,7 @@ 7 - 64 + 62 1 0 0 @@ -1938,7 +1914,7 @@ 7 - 65 + 63 1 0 0 @@ -1950,7 +1926,7 @@ 7 - 66 + 64 1 0 0 @@ -1962,7 +1938,7 @@ 7 - 67 + 65 1 0 0 @@ -1974,7 +1950,7 @@ 7 - 68 + 66 1 0 0 @@ -1986,7 +1962,7 @@ 7 - 69 + 67 5 0 0 @@ -2006,7 +1982,7 @@ 0 8 - 70 + 68 1 0 0 @@ -2018,7 +1994,7 @@ 8 - 71 + 69 1 0 0 @@ -2030,7 +2006,7 @@ 8 - 72 + 70 1 0 0 @@ -2042,7 +2018,7 @@ 8 - 73 + 71 1 0 0 @@ -2054,7 +2030,7 @@ 8 - 74 + 72 1 0 0 @@ -2066,7 +2042,7 @@ 8 - 75 + 73 1 0 0 @@ -2078,7 +2054,7 @@ 8 - 76 + 74 1 0 0 @@ -2090,7 +2066,7 @@ 8 - 77 + 75 1 0 0 @@ -2102,7 +2078,7 @@ 8 - 78 + 76 1 0 0 @@ -2114,7 +2090,7 @@ 8 - 79 + 77 1 0 0 @@ -2126,7 +2102,7 @@ 8 - 80 + 78 1 0 0 @@ -2138,7 +2114,7 @@ 8 - 81 + 79 1 0 0 @@ -2150,7 +2126,7 @@ 8 - 82 + 80 1 0 0 @@ -2162,7 +2138,7 @@ 8 - 83 + 81 1 0 0 @@ -2174,7 +2150,7 @@ 8 - 84 + 82 1 0 0 @@ -2186,7 +2162,7 @@ 8 - 85 + 83 1 0 0 @@ -2198,7 +2174,7 @@ 8 - 86 + 84 1 0 0 @@ -2210,7 +2186,7 @@ 8 - 87 + 85 1 0 0 @@ -2222,7 +2198,7 @@ 8 - 88 + 86 1 0 0 @@ -2234,7 +2210,7 @@ 8 - 89 + 87 1 0 0 @@ -2246,7 +2222,7 @@ 8 - 90 + 88 1 0 0 @@ -2258,7 +2234,7 @@ 8 - 91 + 89 1 0 0 @@ -2270,7 +2246,7 @@ 8 - 92 + 90 1 0 0 @@ -2282,7 +2258,7 @@ 8 - 93 + 91 1 0 0 @@ -2294,7 +2270,7 @@ 8 - 94 + 92 1 0 0 @@ -2306,7 +2282,7 @@ 8 - 95 + 93 1 0 0 @@ -2318,7 +2294,7 @@ 8 - 96 + 94 1 0 0 @@ -2330,7 +2306,7 @@ 8 - 97 + 95 1 0 0 @@ -2342,7 +2318,7 @@ 8 - 98 + 96 1 0 0 @@ -2362,7 +2338,7 @@ 0 9 - 99 + 97 1 0 0 @@ -2374,7 +2350,7 @@ 9 - 100 + 98 1 0 0 @@ -2386,7 +2362,7 @@ 9 - 101 + 99 1 0 0 @@ -2398,7 +2374,7 @@ 9 - 102 + 100 1 0 0 @@ -2410,7 +2386,7 @@ 9 - 103 + 101 1 0 0 @@ -2430,7 +2406,7 @@ 0 10 - 104 + 102 1 0 0 @@ -2442,7 +2418,7 @@ 10 - 105 + 103 1 0 0 @@ -2454,7 +2430,7 @@ 10 - 106 + 104 1 0 0 @@ -2474,7 +2450,7 @@ 0 11 - 107 + 105 1 0 0 @@ -2486,7 +2462,7 @@ 11 - 108 + 106 1 0 0 @@ -2498,7 +2474,7 @@ 11 - 109 + 107 1 0 0 @@ -2518,7 +2494,7 @@ 0 12 - 110 + 108 1 0 0 @@ -2530,7 +2506,7 @@ 12 - 111 + 109 1 0 0 @@ -2542,7 +2518,7 @@ 12 - 112 + 110 1 0 0 @@ -2554,7 +2530,7 @@ 12 - 113 + 111 1 0 0 @@ -2574,7 +2550,7 @@ 0 13 - 114 + 112 1 0 0 @@ -2586,7 +2562,7 @@ 13 - 115 + 113 1 0 0 @@ -2598,7 +2574,7 @@ 13 - 116 + 114 1 0 0 @@ -2610,7 +2586,7 @@ 13 - 117 + 115 1 0 0 @@ -2622,7 +2598,7 @@ 13 - 118 + 116 1 0 0 @@ -2634,7 +2610,7 @@ 13 - 119 + 117 1 0 0 @@ -2646,7 +2622,7 @@ 13 - 120 + 118 1 0 0 @@ -2658,7 +2634,7 @@ 13 - 121 + 119 1 0 0 @@ -2670,7 +2646,7 @@ 13 - 122 + 120 1 0 0 @@ -2682,7 +2658,7 @@ 13 - 123 + 121 1 0 0 @@ -2694,7 +2670,7 @@ 13 - 124 + 122 1 0 0 @@ -2706,7 +2682,7 @@ 13 - 125 + 123 1 0 0 @@ -2718,7 +2694,7 @@ 13 - 126 + 124 1 0 0 @@ -2730,7 +2706,7 @@ 13 - 127 + 125 1 0 0 @@ -2750,7 +2726,7 @@ 0 14 - 128 + 126 1 0 0 @@ -2762,7 +2738,7 @@ 14 - 129 + 127 1 0 0 @@ -2774,7 +2750,7 @@ 14 - 130 + 128 1 0 0 @@ -2786,7 +2762,7 @@ 14 - 131 + 129 1 0 0 @@ -2798,7 +2774,7 @@ 14 - 132 + 130 1 0 0 @@ -2810,7 +2786,7 @@ 14 - 133 + 131 1 0 0 @@ -2822,7 +2798,7 @@ 14 - 134 + 132 1 0 0 @@ -2834,7 +2810,7 @@ 14 - 135 + 133 1 0 0 @@ -2846,7 +2822,7 @@ 14 - 136 + 134 1 0 0 @@ -2858,7 +2834,7 @@ 14 - 137 + 135 1 0 0 @@ -2870,7 +2846,7 @@ 14 - 138 + 136 1 0 0 @@ -2882,7 +2858,7 @@ 14 - 139 + 137 1 0 0 @@ -2894,7 +2870,7 @@ 14 - 140 + 138 1 0 0 @@ -2914,7 +2890,7 @@ 0 15 - 141 + 139 1 0 0 @@ -2926,7 +2902,7 @@ 15 - 142 + 140 1 0 0 @@ -2938,7 +2914,7 @@ 15 - 143 + 141 1 0 0 @@ -2950,7 +2926,7 @@ 15 - 144 + 142 1 0 0 @@ -2962,7 +2938,7 @@ 15 - 145 + 143 1 0 0 @@ -2974,7 +2950,7 @@ 15 - 146 + 144 1 0 0 @@ -2986,7 +2962,7 @@ 15 - 147 + 145 1 0 0 @@ -3006,7 +2982,7 @@ 0 16 - 148 + 146 1 0 0 @@ -3018,7 +2994,7 @@ 16 - 149 + 147 1 0 0 @@ -3030,7 +3006,7 @@ 16 - 150 + 148 1 0 0 @@ -3050,7 +3026,7 @@ 0 17 - 151 + 149 4 0 0 @@ -3070,7 +3046,7 @@ 0 18 - 152 + 150 1 0 0 @@ -3082,7 +3058,7 @@ 18 - 153 + 151 1 0 0 @@ -3094,7 +3070,7 @@ 18 - 154 + 152 1 0 0 @@ -3114,7 +3090,7 @@ 0 19 - 155 + 153 4 0 0 @@ -3134,7 +3110,7 @@ 0 20 - 156 + 154 1 0 0 @@ -3146,7 +3122,7 @@ 20 - 157 + 155 1 0 0 @@ -3158,7 +3134,7 @@ 20 - 158 + 156 1 0 0 @@ -3170,7 +3146,7 @@ 20 - 159 + 157 1 0 0 @@ -3182,7 +3158,7 @@ 20 - 160 + 158 1 0 0 @@ -3194,7 +3170,7 @@ 20 - 161 + 159 1 0 0 @@ -3206,7 +3182,7 @@ 20 - 162 + 160 1 0 0 @@ -3218,7 +3194,7 @@ 20 - 163 + 161 1 0 0 @@ -3230,7 +3206,7 @@ 20 - 164 + 162 1 0 0 @@ -3242,7 +3218,7 @@ 20 - 165 + 163 1 0 0 diff --git a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvprojx b/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvprojx index 9fdfb58..50ea965 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvprojx +++ b/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvprojx @@ -418,16 +418,6 @@ 1 ..\..\..\fstorage.c - - cmd_parse.c - 1 - ..\..\..\cmd_parse.c - - - cmd_parse.h - 5 - ..\..\..\cmd_parse.h - debug_print.h 5 @@ -4631,16 +4621,6 @@ 1 ..\..\..\fstorage.c - - cmd_parse.c - 1 - ..\..\..\cmd_parse.c - - - cmd_parse.h - 5 - ..\..\..\cmd_parse.h - debug_print.h 5 diff --git a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/config/sdk_config.h b/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/config/sdk_config.h index 25633d6..30671df 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/config/sdk_config.h +++ b/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/config/sdk_config.h @@ -11473,7 +11473,7 @@ // or this value is actually used. It depends on which one is bigger. #ifndef SEGGER_RTT_CONFIG_BUFFER_SIZE_UP -#define SEGGER_RTT_CONFIG_BUFFER_SIZE_UP 512 +#define SEGGER_RTT_CONFIG_BUFFER_SIZE_UP 1024 // 512 -> 1024 jhChun 26.03.17 #endif // SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS - Maximum number of upstream buffers. diff --git a/project/ble_peripheral/ble_app_bladder_patch/power_control.c b/project/ble_peripheral/ble_app_bladder_patch/power_control.c index ec040aa..acf4162 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/power_control.c +++ b/project/ble_peripheral/ble_app_bladder_patch/power_control.c @@ -1,6 +1,33 @@ /******************************************************************************* TEST medi50 Dec 23 //=========power_control.c==================== + ******************************************************************************* + * + * [모듈 개요] + * 디바이스 전원 시퀀스를 관리하는 모듈 (전원 켜기 / 끄기 / 슬립). + * + * [전원 켜기 흐름] + * device_activated() + * → p_order=0으로 초기화, power_loop 타이머 시작 + * → power_loop() 상태머신이 20ms 간격으로 Step 0→1→2 순서대로 실행 + * - Step 0: I2C 초기화 (sw_i2c_init_once) + * - Step 1: 예약 (현재 미사용) + * - Step 2: 전원 시퀀스 완료 + * + * [슬립 모드] + * device_sleep_mode() + * → EEPROM OFF → processing 플래그 해제 + * + * [재활성화] + * device_reactivated() + * → I2C 재초기화 후 전원 시퀀스를 처음부터 다시 시작 + * + * [EEPROM 쓰기 보호 핀] + * EEP_WP = P0.24 (레거시, 현재는 FDS를 사용하므로 실질적으로 미사용) + * + * [타이머] + * 싱글샷 모드 app_timer, 20ms 간격으로 power_loop 호출 + * ******************************************************************************/ #include #include @@ -15,50 +42,89 @@ #include "app_timer.h" #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); +/* 전원 시퀀스 상태머신의 타이머 간격 (20ms) */ // 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; + +/* 데이터 처리 중 플래그 (외부 모듈에서 선언) */ 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); + nrf_gpio_pin_clear(EEP_WP); /* LOW: EEPROM 쓰기 보호 활성화 */ }else if(eeprom_st == ON){ - nrf_gpio_pin_set(EEP_WP); + nrf_gpio_pin_set(EEP_WP); /* HIGH: EEPROM 쓰기 허용 */ } } +/** + * @brief 디바이스 슬립 모드 진입 + * + * EEPROM을 OFF하고, 데이터 처리 플래그(processing)를 해제하여 + * 메인 루프가 더 이상 센서 데이터를 처리하지 않도록 한다. + * + * @return 0 (항상 성공) + */ int device_sleep_mode(void){ int rc = 0; - eeprom_control(OFF); + eeprom_control(OFF); /* EEPROM 쓰기 보호 활성화 */ nrf_delay_ms(2); DBG_PRINTF("Device_Sleep_Mode OK!\r\n"); nrf_delay_ms(10); - processing = false; + processing = false; /* 데이터 처리 플래그 해제 → 센서 처리 중단 */ return rc; } +/** + * @brief 디바이스 전원 켜기 (전원 시퀀스 시작) + * + * 전원 시퀀스 단계를 0으로 초기화하고, 잠금 플래그를 설정한 뒤 + * 타이머를 시작하여 power_loop() 상태머신을 구동한다. + * EEPROM은 OFF 상태로 시작한다. + * + * @return 0 (항상 성공) + */ int device_activated(void){ int rc = 0; - p_order = 0; - lock_check =true; - power_timer_start(); - eeprom_control(OFF); + p_order = 0; /* 상태머신 시작 단계 (Step 0: I2C 초기화) */ + lock_check =true; /* 전원 시퀀스 진행 중 잠금 */ + power_timer_start(); /* 20ms 후 power_loop() 첫 호출 */ + eeprom_control(OFF); /* EEPROM OFF 상태로 시작 */ return rc; } @@ -74,32 +140,49 @@ int device_activated(void){ * 1: (reserved) * 2: Complete */ +/* + * 전원 시퀀스 상태머신 (20ms 싱글샷 타이머 콜백). + * + * [실행 흐름] + * 1) 타이머 콜백 진입 → 타이머 정지 (싱글샷이므로) + * 2) 현재 p_order에 해당하는 초기화 단계 실행 + * 3) p_order < 2이면 다음 단계로 진행하고 타이머 재시작 + * 4) p_order == 2이면 전원 시퀀스 완료 + * + * [DEBUG_MINIMAL_BOOT 모드] + * 센서 초기화를 건너뛰고 즉시 완료 단계(Step 2)로 점프 + */ void power_loop(void *p_context) { UNUSED_PARAMETER(p_context); - power_timer_stop(); + 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(); - nrf_delay_ms(10); + 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; @@ -110,31 +193,49 @@ void power_loop(void *p_context) #endif /* Advance to next step or finish */ + /* 다음 단계로 진행하거나, 시퀀스 완료 처리 */ if (p_order < 2) { - p_order++; - power_timer_start(); + p_order++; /* 다음 단계로 이동 */ + power_timer_start(); /* 20ms 후 다음 단계 실행 */ } else { + /* 전원 시퀀스 전체 완료 */ DBG_PRINTF("[PWR] Device Activated OK!\r\n"); } } +/** + * @brief 디바이스 재활성화 (슬립 복귀 시) + * + * I2C를 재초기화하고, 전원 시퀀스를 처음부터 다시 시작한다. + * 슬립 모드에서 깨어날 때 호출된다. + * + * @return 0 (항상 성공) + */ int device_reactivated(void){ int rc = 0; - sw_i2c_init_once(); - nrf_delay_ms(10); - lock_check = true; - p_order = 0; - power_timer_start(); + sw_i2c_init_once(); /* I2C 버스 재초기화 */ + nrf_delay_ms(10); /* 안정화 대기 */ + lock_check = true; /* 전원 시퀀스 진행 중 잠금 */ + p_order = 0; /* 상태머신을 Step 0부터 재시작 */ + power_timer_start(); /* 20ms 후 power_loop() 시작 */ return rc; } +/** + * @brief 전원 시퀀스 타이머 시작 + * + * 싱글샷 모드로 POWER_LOOP_INTERVAL(20ms) 후 power_loop()를 호출한다. + */ void power_timer_start(void) { APP_ERROR_CHECK(app_timer_start(m_power_timer_id, APP_TIMER_TICKS(POWER_LOOP_INTERVAL), NULL)); } +/** + * @brief 전원 시퀀스 타이머 정지 + */ void power_timer_stop(void) { APP_ERROR_CHECK(app_timer_stop(m_power_timer_id)); @@ -142,6 +243,12 @@ void power_timer_stop(void) } +/** + * @brief 전원 시퀀스 타이머 초기화 (앱 시작 시 1회 호출) + * + * 싱글샷 모드 타이머를 생성하고, 콜백으로 power_loop()를 등록한다. + * 싱글샷이므로 매 단계마다 power_timer_start()로 수동 재시작해야 한다. + */ void power_timer_init(void) //active start { APP_ERROR_CHECK(app_timer_create(&m_power_timer_id, APP_TIMER_MODE_SINGLE_SHOT, power_loop)); diff --git a/project/ble_peripheral/ble_app_bladder_patch/power_control.h b/project/ble_peripheral/ble_app_bladder_patch/power_control.h index bba0237..773b069 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/power_control.h +++ b/project/ble_peripheral/ble_app_bladder_patch/power_control.h @@ -3,7 +3,22 @@ * @author CandyPops Co. * @version V1.0.0 * @date 2022-09-05 - * @brief + * @brief + ******************************************************************************* + * + * [헤더 개요] + * 디바이스 전원 시퀀스 관리 모듈의 공용 인터페이스 헤더. + * + * [주요 함수 요약] + * power_gpio_init() : 전원 관련 GPIO 초기화 (EEP_WP 핀) + * eeprom_control() : EEPROM 쓰기 보호 ON/OFF (레거시) + * device_activated() : 전원 켜기 → power_loop 상태머신 시작 + * device_sleep_mode() : 슬립 모드 진입 (EEPROM OFF, 처리 중단) + * device_reactivated() : 슬립 복귀 → I2C 재초기화 후 전원 시퀀스 재시작 + * power_loop() : 20ms 간격 전원 시퀀스 상태머신 콜백 + * power_timer_start/stop : 전원 시퀀스 타이머 제어 + * power_timer_init() : 전원 시퀀스 타이머 초기화 (앱 시작 시 1회) + * ******************************************************************************/ #ifndef _POWER_CONTROL_H_ @@ -11,14 +26,31 @@ #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 해제) */ int device_sleep_mode(void); + +/** @brief 디바이스 전원 켜기 (전원 시퀀스 상태머신 시작) */ int device_activated(void); + +/** @brief 디바이스 재활성화 (슬립 복귀 시 I2C 재초기화 후 시퀀스 재시작) */ int device_reactivated(void); + +/** @brief 전원 시퀀스 상태머신 (20ms 타이머 콜백, Step 0→1→2) */ void power_loop(void * p_context); /* For x ms */ + +/** @brief 전원 시퀀스 타이머 시작 (20ms 싱글샷) */ void power_timer_start(void); + +/** @brief 전원 시퀀스 타이머 정지 */ void power_timer_stop(void);; + +/** @brief 전원 시퀀스 타이머 초기화 (앱 시작 시 1회 호출) */ void power_timer_init(void); #endif //_POWER_CONTROL_H_ diff --git a/project/ble_peripheral/ble_app_bladder_patch/pulse_gen.c b/project/ble_peripheral/ble_app_bladder_patch/pulse_gen.c index 0ef1942..40f3fb3 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/pulse_gen.c +++ b/project/ble_peripheral/ble_app_bladder_patch/pulse_gen.c @@ -6,6 +6,26 @@ * @date 2025-10-13 ******************************************************************************/ +/******************************************************************************* + * [한국어 설명] PWM 펄스 생성기 + * + * === 현재 상태 === + * SKIP_PWM 매크로가 정의되어 있어 PWM 기능이 비활성화됨. + * init/start/stop 함수는 모두 Stub(빈 껍데기) 구현으로, + * 로그만 출력하고 실제 PWM 동작은 하지 않는다. + * 빌드 호환성을 위해 함수 인터페이스만 유지. + * + * === 실제 구현 (SKIP_PWM 미정의 시) === + * nrfx_pwm 드라이버 + app_timer를 사용하여 3핀(P28/P29/P30) PWM 제어. + * PWM 인스턴스 0, 16MHz 클럭, TOP=8, 개별 채널 로드 모드. + * 6단계 시퀀스로 채널별 듀티 사이클 조절. + * app_timer(10ms)로 주기적 반복 재생. + * + * === 비활성화 사유 === + * 현재 프로젝트에서는 피에조 구동을 dr_piezo 드라이버가 담당하므로 + * 이 PWM 펄스 생성기는 사용하지 않음. + ******************************************************************************/ + #define SKIP_PWM // ? ?? ???? PWM ?? ??? #include "pulse_gen.h" @@ -15,57 +35,72 @@ #ifdef SKIP_PWM /* ========================================================================== */ -/* PWM ?? ???? Stub ?? */ +/* PWM 비활성화 모드: Stub 구현 (로그만 출력, 실제 동작 없음) */ /* ========================================================================== */ +/* Stub 초기화: 아무 동작 없이 성공 반환 */ ret_code_t pulse_gen_init(void) { DBG_PRINTF("[PWM] skipped init\r\n"); return NRF_SUCCESS; } +/* Stub 시작: 아무 동작 없음 */ void pulse_gen_start(void) { DBG_PRINTF("[PWM] skipped start\r\n"); } +/* Stub 정지: 아무 동작 없음 */ void pulse_gen_stop(void) { DBG_PRINTF("[PWM] skipped stop\r\n"); } #else /* ==================================================================== */ -/* ?? PWM ?? ?? (SKIP_PWM ?? ?? ? ???) */ +/* 실제 PWM 구현 코드 (SKIP_PWM 미정의 시에만 컴파일됨) */ /* ========================================================================== */ #include "nrfx_pwm.h" -/* --- Configuration --- */ -#define PULSE_PIN_1 28 -#define PULSE_PIN_2 29 -#define PULSE_PIN_3 30 -#define PULSE_DELAY_MS 10 +/* --- 설정값 --- */ +#define PULSE_PIN_1 28 /* PWM 출력 핀 1 (P0.28) */ +#define PULSE_PIN_2 29 /* PWM 출력 핀 2 (P0.29) */ +#define PULSE_PIN_3 30 /* PWM 출력 핀 3 (P0.30) */ +#define PULSE_DELAY_MS 10 /* 반복 재생 간격 (ms) */ -#define PWM_INSTANCE_ID 0 -#define PWM_TOP_VALUE 8 -#define SEQUENCE_LENGTH 6 +#define PWM_INSTANCE_ID 0 /* PWM 하드웨어 인스턴스 번호 */ +#define PWM_TOP_VALUE 8 /* PWM 카운터 최대값 (듀티 사이클 분해능) */ +#define SEQUENCE_LENGTH 6 /* PWM 시퀀스 단계 수 */ -static nrfx_pwm_t m_pwm = NRFX_PWM_INSTANCE(PWM_INSTANCE_ID); -APP_TIMER_DEF(m_pulse_delay_timer_id); -static volatile bool m_pulses_running = false; +static nrfx_pwm_t m_pwm = NRFX_PWM_INSTANCE(PWM_INSTANCE_ID); /* PWM 인스턴스 */ +APP_TIMER_DEF(m_pulse_delay_timer_id); /* 반복 재생용 앱 타이머 */ +static volatile bool m_pulses_running = false; /* 펄스 생성 동작 중 플래그 */ +/* PWM 시퀀스 데이터: 6단계에 걸쳐 3채널의 듀티 사이클을 개별 설정 */ static nrf_pwm_values_individual_t m_pulse_seq_values[SEQUENCE_LENGTH]; static nrf_pwm_sequence_t const m_pulse_sequence = { .values.p_individual = m_pulse_seq_values, .length = NRF_PWM_VALUES_LENGTH(m_pulse_seq_values), - .repeats = 0, - .end_delay = 0 + .repeats = 0, /* 각 단계 반복 없음 */ + .end_delay = 0 /* 시퀀스 종료 후 추가 지연 없음 */ }; /* -------------------------------------------------------------------------- */ -/* Local Handlers */ +/* 내부 핸들러 함수 */ /* -------------------------------------------------------------------------- */ + +/* + * PWM 시퀀스 값 초기화 + * 6단계에 걸쳐 CH0/CH1/CH2의 듀티 사이클을 교대 설정: + * 단계0: CH0=100%, CH1=0, CH2=0 + * 단계1: CH0=0, CH1=100%, CH2=0 + * 단계2: CH0=100%, CH1=0, CH2=0 + * 단계3: CH0=0, CH1=100%, CH2=0 + * 단계4: CH0=0, CH1=0, CH2=0 (모두 OFF) + * 단계5: CH0=0, CH1=0, CH2=100% + */ static void prepare_pulse_sequence(void) { m_pulse_seq_values[0].channel_0 = PWM_TOP_VALUE; m_pulse_seq_values[0].channel_1 = 0; m_pulse_seq_values[0].channel_2 = 0; @@ -76,6 +111,7 @@ static void prepare_pulse_sequence(void) m_pulse_seq_values[5].channel_0 = 0; m_pulse_seq_values[5].channel_1 = 0; m_pulse_seq_values[5].channel_2 = PWM_TOP_VALUE; } +/* 타이머 타임아웃 핸들러: m_pulses_running이면 PWM 시퀀스 1회 재생 후 타이머 재시작 */ static void pulse_delay_timeout_handler(void * p_context) { if (m_pulses_running) @@ -86,8 +122,14 @@ static void pulse_delay_timeout_handler(void * p_context) } /* -------------------------------------------------------------------------- */ -/* Public Functions */ +/* 공개 함수 */ /* -------------------------------------------------------------------------- */ + +/* + * PWM 초기화: PWM 인스턴스 설정 + 앱 타이머 생성 + * PWM: 16MHz 클럭, UP 카운트 모드, TOP=8, 개별 채널 로드 + * 타이머: 단발(SINGLE_SHOT) 모드, 10ms 후 시퀀스 재생 + */ ret_code_t pulse_gen_init(void) { ret_code_t err_code; @@ -121,18 +163,20 @@ ret_code_t pulse_gen_init(void) return err_code; } +/* 펄스 생성 시작: 플래그 설정 후 타임아웃 핸들러를 직접 호출하여 즉시 첫 재생 */ void pulse_gen_start(void) { - if (m_pulses_running) return; + if (m_pulses_running) return; /* 이미 동작 중이면 무시 */ m_pulses_running = true; - pulse_delay_timeout_handler(NULL); + pulse_delay_timeout_handler(NULL); /* 첫 번째 재생 즉시 시작 */ } +/* 펄스 생성 정지: 플래그 해제 + 타이머 정지 + PWM 즉시 정지 */ void pulse_gen_stop(void) { m_pulses_running = false; app_timer_stop(m_pulse_delay_timer_id); - nrfx_pwm_stop(&m_pwm, true); + nrfx_pwm_stop(&m_pwm, true); /* true = 즉시 정지 (현재 시퀀스 중단) */ } #endif /* SKIP_PWM */ diff --git a/project/ble_peripheral/ble_app_bladder_patch/pulse_gen.h b/project/ble_peripheral/ble_app_bladder_patch/pulse_gen.h index a76c585..5eadf41 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/pulse_gen.h +++ b/project/ble_peripheral/ble_app_bladder_patch/pulse_gen.h @@ -1,3 +1,15 @@ +/******************************************************************************* + * [한국어 설명] PWM 펄스 생성기 헤더 + * + * PWM 기반 펄스 생성 모듈의 공개 인터페이스. + * 현재 SKIP_PWM 매크로로 비활성화되어 있으며, Stub 구현만 동작함. + * 실제 구현 시 nrfx_pwm + app_timer로 3핀(P28/P29/P30) PWM 제어. + * + * 비활성화 사유: 피에조 구동은 dr_piezo 드라이버가 담당하므로 + * 이 PWM 모듈은 현재 프로젝트에서 사용하지 않음. + * 빌드 호환성을 위해 인터페이스만 유지. + ******************************************************************************/ + #ifndef PULSE_GEN_H__ #define PULSE_GEN_H__ @@ -6,20 +18,23 @@ #include "sdk_errors.h" /** - * @brief Initializes the PWM peripheral and app_timer for pulse generation. + * @brief PWM 주변장치 및 app_timer를 초기화한다. + * (SKIP_PWM 정의 시 아무 동작 없이 NRF_SUCCESS 반환) * * @return NRF_SUCCESS on successful initialization, otherwise an error code. */ ret_code_t pulse_gen_init(void); /** - * @brief Starts the repeating pulse generation. - * Generates a burst of pulses, waits a few ms, and repeats. + * @brief 반복 펄스 생성을 시작한다. + * 버스트 펄스를 생성하고, 수 ms 대기 후 반복. + * (SKIP_PWM 정의 시 아무 동작 없음) */ void pulse_gen_start(void); /** - * @brief Stops the repeating pulse generation. + * @brief 반복 펄스 생성을 정지한다. + * (SKIP_PWM 정의 시 아무 동작 없음) */ void pulse_gen_stop(void); diff --git a/project/ble_peripheral/ble_app_bladder_patch/tmp235_q1.c b/project/ble_peripheral/ble_app_bladder_patch/tmp235_q1.c index 379fd8d..1692aee 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/tmp235_q1.c +++ b/project/ble_peripheral/ble_app_bladder_patch/tmp235_q1.c @@ -97,6 +97,7 @@ void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* TMP325 Vout /* ADC 변환 결과 읽기 */ adc_result = p_event->data.done.p_buffer[0]; + DBG_PRINTF("[TMP] adc=%d\r\n", adc_result); /* SAADC 해제 — 배터리/압력센서 측정과 하드웨어 공유 */ nrf_drv_saadc_uninit(); diff --git a/project/ble_peripheral/ble_app_bladder_patch/tmp235_q1.h b/project/ble_peripheral/ble_app_bladder_patch/tmp235_q1.h index 2e2ddc5..536c118 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/tmp235_q1.h +++ b/project/ble_peripheral/ble_app_bladder_patch/tmp235_q1.h @@ -3,13 +3,28 @@ * @author CandyPops Co. * @version V1.0.0 * @date 2022-09-05 - * @brief + * @brief + ******************************************************************************/ + +/******************************************************************************* + * [헤더 개요] TMP235-Q1 아날로그 온도센서 드라이버 인터페이스 + * + * TMP235-Q1의 아날로그 전압 출력을 SAADC(AIN3)로 읽어 + * 온도(°C)로 변환하는 기능의 외부 호출용 API를 선언한다. + * + * 주요 API: + * - tmp235_init() : SAADC 초기화 + 즉시 측정 시작 (내부 사용) + * - tmp235_voltage_level_meas() : 온도 1회 측정 (외부 호출용 래퍼) + * + * 온도 변환: Vout(mV) → Ta(°C) = (Vout - 500) / 10.0 (0~100°C 구간) ******************************************************************************/ #ifndef _TMP235_Q1_H_ #define _TMP235_Q1_H_ +/** @brief TMP235 SAADC 초기화 및 측정 시작 (AIN3 채널) */ void tmp235_init(void); +/** @brief 온도 1회 측정 외부 호출 함수 (내부적으로 tmp235_init 호출) */ void tmp235_voltage_level_meas(void); #endif /* !_TMP235_Q1_H_ */ diff --git a/project/ble_peripheral/dr_piezo/dr_piezo.c b/project/ble_peripheral/dr_piezo/dr_piezo.c index 3b5f80a..753fe0f 100644 --- a/project/ble_peripheral/dr_piezo/dr_piezo.c +++ b/project/ble_peripheral/dr_piezo/dr_piezo.c @@ -3,35 +3,88 @@ * @brief Piezo Transducer Driver (2MHz Signal Generator) * @author Charles KWON * @date 2025-12-09 - * + * * @details Uses Timer2 + GPIOTE + PPI for CPU-free 2MHz waveform generation - * + * * Timing Diagram (???): - * + * * |<----------- PE HIGH ----------->| * PE ___/?????????????????????????????????\___ * P_OUT ___/?\_/?\_/?\_/?\_/?\________________\___ * N_OUT ___\_/?\_/?\_/?\_/?\_/________________\___ * DMP _________________________/?????\_________ * |<-- 3~5 cycles -->| || - * + * * P_OUT? N_OUT? ?? ?? ?? (???) - * + * * 2MHz = 500ns period = 250ns half-period * Timer @ 16MHz: 1 tick = 62.5ns * Half-period = 250ns = 4 ticks * Full-period = 500ns = 8 ticks ******************************************************************************/ -#include "dr_piezo.h" -#include "nrf_gpio.h" -#include "nrf_timer.h" -#include "nrf_gpiote.h" -#include "nrf_ppi.h" -#include "nrf_delay.h" -#include "power_control.h" -#include "app_util_platform.h" +/******************************************************************************* + * [한국어 설명] 피에조 초음파 트랜스듀서 드라이버 + * + * === 개요 === + * 방광 측정용 2MHz 초음파 송신 신호를 생성하는 드라이버. + * nRF52840의 하드웨어 주변장치(Timer2 + GPIOTE + PPI)를 활용하여 + * CPU 개입 없이 정밀한 2MHz 파형을 자동으로 생성한다. + * + * === 초음파 TX 시퀀스 === + * 1단계: PE(Pulse Enable) = HIGH -> MOSFET 드라이버 활성화 + * 2단계: P_OUT/N_OUT 교번 펄스 생성 (2MHz, 3~7 사이클) + * - P_OUT과 N_OUT은 역상(반대 위상)으로 동작 + * - 피에조 소자 양단에 +/-20V 교번 전압 인가 + * 3단계: DMP(Dump) = HIGH -> 펄스 완료 후 피에조에 남은 잔류 에너지 방전 + * 4단계: DMP = LOW -> 방전 완료 + * 5단계: PE = LOW -> MOSFET 드라이버 비활성화, 유휴 상태 복귀 + * + * === 하드웨어 아키텍처 === + * [Timer2] --- CC[0](반주기=4틱) ---> [PPI CH8,9] ---> [GPIOTE CH4,5] -> P_OUT/N_OUT 토글 + * |-- CC[1](전체주기=8틱) --> [PPI CH10,11] --> [GPIOTE CH4,5] -> P_OUT/N_OUT 토글 + * |-- CC[2](전체주기=8틱) --> 타이머 자동 클리어 + 인터럽트(잔여 사이클 카운트) + * + * - Timer2: 16MHz 클럭, 16비트 모드 + * - CC[0] = 4틱(250ns) -> 반주기 시점에서 P_OUT/N_OUT 토글 + * - CC[1] = 8틱(500ns) -> 전체주기 시점에서 P_OUT/N_OUT 토글 + * - CC[2] = 8틱(500ns) -> 인터럽트 발생 + 타이머 자동 클리어(SHORT) + * + * - GPIOTE CH4: P_OUT 핀 토글 모드 + * - GPIOTE CH5: N_OUT 핀 토글 모드 + * + * - PPI CH8: CC[0] 이벤트 -> P_OUT 토글 태스크 + * - PPI CH9: CC[0] 이벤트 -> N_OUT 토글 태스크 + * - PPI CH10: CC[1] 이벤트 -> P_OUT 토글 태스크 + * - PPI CH11: CC[1] 이벤트 -> N_OUT 토글 태스크 + * + * === 전원 === + * DR_PIEZO_PWR_EN(P1.9) -> DC/DC 컨버터 활성화 -> +/-20V 고전압 생성 + * + * === 소프트웨어 버스트 모드 (dr_piezo_burst_sw 계열) === + * Timer/PPI 대신 CPU에서 직접 GPIO를 제어하는 방식. + * 인터럽트를 비활성화(__disable_irq)하고 NOP 명령어로 정밀 타이밍 생성. + * 포트 레지스터(NRF_P1->OUT)에 직접 접근하여 여러 핀을 동시에 제어. + * 주파수별(1.7/1.8/1.9/2.0/2.1/2.2 MHz) NOP 개수가 다름. + * + * NOP 타이밍 계산법: + * CPU 클럭 64MHz -> 1 NOP = 15.625ns + * 목표 반주기(ns) = 1,000,000 / (목표주파수MHz * 2) + * 필요 NOP 수 = (목표 반주기 - 레지스터 쓰기 시간(~30ns)) / 15.625 + * 루프 오버헤드(~47ns = 3 NOP)는 두 번째 반주기에서 차감 + ******************************************************************************/ +/* 헤더 포함 */ +#include "dr_piezo.h" +#include "nrf_gpio.h" /* GPIO 제어 (핀 설정, 출력) */ +#include "nrf_timer.h" /* 타이머 주변장치 제어 */ +#include "nrf_gpiote.h" /* GPIOTE (GPIO Tasks and Events) 제어 */ +#include "nrf_ppi.h" /* PPI (Programmable Peripheral Interconnect) 제어 */ +#include "nrf_delay.h" /* 지연(딜레이) 함수 */ +#include "power_control.h" /* 전원 관리 */ +#include "app_util_platform.h" /* 인터럽트 우선순위 등 플랫폼 유틸리티 */ + +/* 조건부 디버그 출력: FEATURE_PRINTF 정의 시 SEGGER RTT로 출력 */ #ifdef FEATURE_PRINTF #include "debug_print.h" #else @@ -39,62 +92,76 @@ #endif /*============================================================================== - * HARDWARE RESOURCE ALLOCATION + * 하드웨어 리소스 할당 + * - Timer2: 2MHz 파형 생성의 시간 기준 (16MHz 클럭) + * - GPIOTE CH4/5: P_OUT/N_OUT 핀의 하드웨어 토글 + * - PPI CH8~11: 타이머 비교 이벤트 -> GPIOTE 토글 태스크 자동 연결 *============================================================================*/ -#define PIEZO_TIMER NRF_TIMER2 -#define PIEZO_TIMER_IRQn TIMER2_IRQn -#define PIEZO_TIMER_IRQ_PRIORITY 6 +#define PIEZO_TIMER NRF_TIMER2 /* 사용할 타이머 인스턴스 */ +#define PIEZO_TIMER_IRQn TIMER2_IRQn /* 타이머2 인터럽트 번호 */ +#define PIEZO_TIMER_IRQ_PRIORITY 6 /* 인터럽트 우선순위 (6 = 중간) */ -/* GPIOTE channels */ -#define GPIOTE_CH_P_OUT 4 -#define GPIOTE_CH_N_OUT 5 +/* GPIOTE 채널 할당 - 핀 토글 제어용 */ +#define GPIOTE_CH_P_OUT 4 /* P_OUT(양극 출력) 토글용 GPIOTE 채널 */ +#define GPIOTE_CH_N_OUT 5 /* N_OUT(음극 출력) 토글용 GPIOTE 채널 */ -/* PPI channels */ -#define PPI_CH_P_OUT_TOGGLE_0 8 -#define PPI_CH_N_OUT_TOGGLE_0 9 -#define PPI_CH_P_OUT_TOGGLE_1 10 -#define PPI_CH_N_OUT_TOGGLE_1 11 +/* PPI 채널 할당 - 타이머 이벤트 -> GPIOTE 태스크 연결 */ +#define PPI_CH_P_OUT_TOGGLE_0 8 /* CC[0](반주기) -> P_OUT 토글 */ +#define PPI_CH_N_OUT_TOGGLE_0 9 /* CC[0](반주기) -> N_OUT 토글 */ +#define PPI_CH_P_OUT_TOGGLE_1 10 /* CC[1](전체주기) -> P_OUT 토글 */ +#define PPI_CH_N_OUT_TOGGLE_1 11 /* CC[1](전체주기) -> N_OUT 토글 */ /*============================================================================== - * TIMING CONSTANTS + * 타이밍 상수 + * Timer2 클럭: 16MHz -> 1틱 = 62.5ns + * 2MHz 신호: 주기 500ns(8틱), 반주기 250ns(4틱) *============================================================================*/ -#define TIMER_FREQ_MHZ 16 -#define TICK_NS (1000 / TIMER_FREQ_MHZ) /* 62.5ns */ +#define TIMER_FREQ_MHZ 16 /* 타이머 클럭 주파수 (MHz) */ +#define TICK_NS (1000 / TIMER_FREQ_MHZ) /* 1틱 = 62.5ns */ -/* 2MHz: period = 500ns, half = 250ns */ -#define PERIOD_TICKS_2MHZ 8 /* 500ns / 62.5ns = 8 */ -#define HALF_PERIOD_TICKS 4 /* 250ns / 62.5ns = 4 */ +/* 2MHz 기준 타이밍: 주기 = 500ns, 반주기 = 250ns */ +#define PERIOD_TICKS_2MHZ 8 /* 전체 주기: 500ns / 62.5ns = 8틱 */ +#define HALF_PERIOD_TICKS 4 /* 반주기: 250ns / 62.5ns = 4틱 */ /*============================================================================== - * PIEZO OPERATING FREQUENCY CONFIGURATION + * 피에조 동작 주파수 설정 *============================================================================*/ /* - * Target piezo frequency: 1.8 MHz + * 목표 피에조 주파수: 2.1 MHz (하드웨어 버스트 모드용) * - * Timing (hardcoded in dr_piezo_burst_sw for stability): - * 1.8 MHz: 556ns period, 278ns half-period - * At 64MHz CPU (1 NOP = 15.625ns): - * First half: 15 NOPs (~234ns) - * Second half: 12 NOPs (~188ns) + loop overhead + * 소프트웨어 버스트 모드의 타이밍은 각 주파수별 함수에 NOP 개수로 하드코딩됨. + * 안정적인 파형 생성을 위해 컴파일 타임에 고정. * - * To change frequency, modify the NOP counts in dr_piezo_burst_sw(): - * 2.0 MHz: First=14, Second=11 - * 2.1 MHz: First=13, Second=10 - * 1.5 MHz: First=18, Second=15 + * NOP 기반 타이밍 계산 (CPU 64MHz, 1 NOP = 15.625ns): + * 1.7 MHz: 반주기 294ns -> 첫 반주기 18 NOP, 둘째 반주기 10 NOP + 루프 오버헤드 + * 1.8 MHz: 반주기 278ns -> 첫 반주기 17 NOP, 둘째 반주기 11 NOP + 루프 오버헤드 + * 1.9 MHz: 반주기 263ns -> 첫 반주기 15 NOP, 둘째 반주기 9 NOP + 루프 오버헤드 + * 2.0 MHz: 반주기 250ns -> 첫 반주기 15 NOP, 둘째 반주기 10 NOP + 루프 오버헤드 + * 2.1 MHz: 반주기 238ns -> 첫 반주기 14 NOP, 둘째 반주기 9 NOP + 루프 오버헤드 + * 2.2 MHz: 반주기 227ns -> 첫 반주기 13 NOP, 둘째 반주기 8 NOP + 루프 오버헤드 + * + * 주파수를 변경하려면 dr_piezo_burst_sw_XXmhz() 함수의 NOP 수를 수정할 것. */ -#define PIEZO_FREQ_MHZ 2.1f +#define PIEZO_FREQ_MHZ 2.1f /* 기본 동작 주파수 (MHz) */ /*============================================================================== - * STATIC VARIABLES + * 정적 변수 *============================================================================*/ -static volatile bool m_tx_active = false; -static volatile uint8_t m_remaining_cycles = 0; -static uint32_t m_period_ticks = PERIOD_TICKS_2MHZ; -static bool m_power_enabled = false; -static bool m_initialized = false; +static volatile bool m_tx_active = false; /* TX 송신 중 플래그 (인터럽트에서 변경) */ +static volatile uint8_t m_remaining_cycles = 0; /* 남은 펄스 사이클 수 (인터럽트에서 감소) */ +static uint32_t m_period_ticks = PERIOD_TICKS_2MHZ; /* 현재 주기 (타이머 틱 단위) */ +static bool m_power_enabled = false; /* DC/DC 컨버터 전원 상태 */ +static bool m_initialized = false; /* 드라이버 초기화 완료 여부 */ /*============================================================================== - * INTERRUPT HANDLER + * 타이머2 인터럽트 핸들러 + * 매 주기(CC[2])마다 호출되어 잔여 사이클을 감소시킨다. + * 잔여 사이클이 0이 되면: + * 1) 타이머 정지 및 클리어 + * 2) GPIOTE 비활성화 (P_OUT/N_OUT 토글 중단) + * 3) GPIO를 출력 모드로 재설정 후 LOW로 초기화 + * 4) DMP 펄스 발생 (피에조 잔류 에너지 방전) + * 5) PE = LOW (MOSFET 드라이버 비활성화) *============================================================================*/ void TIMER2_IRQHandler(void) { @@ -140,9 +207,12 @@ void TIMER2_IRQHandler(void) } /*============================================================================== - * POWER CONTROL FUNCTIONS + * 전원 제어 함수 + * DC/DC 컨버터(±20V)를 ON/OFF하여 피에조 구동 전압을 제어한다. + * 전원 안정화에 약 10ms 필요. *============================================================================*/ +/* 피에조 전원 ON: DC/DC 컨버터 활성화 → ±20V 생성 */ void dr_piezo_power_on(void) { nrf_delay_ms(20); @@ -158,6 +228,7 @@ void dr_piezo_power_on(void) DBG_PRINTF("[DR_PIEZO] Power ON: +/-20V ready\r\n"); } +/* 피에조 전원 OFF: TX 비활성화 후 DC/DC 컨버터 차단 */ void dr_piezo_power_off(void) { dr_piezo_disable(); @@ -169,10 +240,17 @@ void dr_piezo_power_off(void) DBG_PRINTF("[DR_PIEZO] Power OFF\r\n"); } +/* 피에조 전원 상태 확인 */ +bool dr_piezo_is_power_on(void) +{ + return m_power_enabled; +} + /*============================================================================== - * PRIVATE FUNCTIONS + * 내부(private) 초기화 함수 *============================================================================*/ +/* GPIO 초기화: 모든 신호 핀을 출력 모드로 설정하고 LOW(유휴)로 초기화 */ static void dr_piezo_gpio_init(void) { nrf_gpio_cfg_output(DR_PIEZO_PIN_PE); @@ -192,6 +270,7 @@ static void dr_piezo_gpio_init(void) DBG_PRINTF("[DR_PIEZO] GPIO init done\r\n"); } +/* GPIOTE 초기화: P_OUT/N_OUT을 토글 모드로 설정 (PPI 연결 대상) */ static void dr_piezo_gpiote_init(void) { /* P_OUT: Toggle mode, initial LOW */ @@ -215,6 +294,12 @@ static void dr_piezo_gpiote_init(void) DBG_PRINTF("[DR_PIEZO] GPIOTE init done\r\n"); } +/* + * 타이머 초기화: 16MHz 클럭, 16비트 모드 + * CC[0]=반주기(4틱): 반주기 시점 토글 이벤트 + * CC[1]=전체주기(8틱): 전체주기 시점 토글 이벤트 + * CC[2]=전체주기(8틱): 인터럽트 발생 + 타이머 자동 클리어(SHORT) + */ static void dr_piezo_timer_init(void) { nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_STOP); @@ -258,6 +343,12 @@ static void dr_piezo_timer_init(void) DBG_PRINTF("[DR_PIEZO] Timer init done (period=%d ticks)\r\n", m_period_ticks); } +/* + * PPI 초기화: 타이머 비교 이벤트 → GPIOTE 토글 태스크 연결 (4채널) + * CC[0] 이벤트(반주기) → P_OUT 토글 + N_OUT 토글 + * CC[1] 이벤트(전체주기) → P_OUT 토글 + N_OUT 토글 + * 이로써 P_OUT과 N_OUT은 항상 역상으로 동작함. + */ static void dr_piezo_ppi_init(void) { /* @@ -302,9 +393,10 @@ static void dr_piezo_ppi_init(void) } /*============================================================================== - * TX DRIVER FUNCTIONS + * TX 드라이버 공개 함수 *============================================================================*/ +/* TX 드라이버 초기화: GPIO → GPIOTE → Timer → PPI 순서로 설정 */ void dr_piezo_init(void) { DBG_PRINTF("[DR_PIEZO] Initializing TX driver...\r\n"); @@ -321,6 +413,7 @@ void dr_piezo_init(void) DBG_PRINTF("[DR_PIEZO] TX driver ready (2MHz)\r\n"); } +/* TX 드라이버 해제: 타이머 정지, PPI/GPIOTE 비활성화, 모든 핀 LOW */ void dr_piezo_uninit(void) { nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_STOP); @@ -346,6 +439,13 @@ void dr_piezo_uninit(void) DBG_PRINTF("[DR_PIEZO] TX driver uninitialized\r\n"); } +/* + * 하드웨어 기반 버스트 송신 (Timer + PPI + GPIOTE 사용) + * 1) GPIOTE를 재설정: P_OUT=HIGH 시작, N_OUT=LOW 시작 (역상) + * 2) PE = HIGH → MOSFET 드라이버 활성화 + * 3) 타이머 시작 → PPI가 자동으로 P_OUT/N_OUT 토글 + * 4) 인터럽트 핸들러에서 잔여 사이클 관리 및 종료 처리 + */ void dr_piezo_burst(uint8_t cycles) { if (m_tx_active) @@ -409,11 +509,13 @@ void dr_piezo_burst(uint8_t cycles) nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_START); } +/* 기본 사이클 수(5)로 버스트 송신 */ void dr_piezo_pulse(void) { dr_piezo_burst(DR_PIEZO_DEFAULT_CYCLES); } +/* TX 출력 활성화: 수동으로 초기 상태 설정 (PE=HIGH, P_OUT=HIGH, N_OUT=LOW) */ void dr_piezo_enable(void) { nrf_gpio_pin_set(DR_PIEZO_PIN_P_OUT); @@ -423,6 +525,7 @@ void dr_piezo_enable(void) DBG_PRINTF("[DR_PIEZO] TX enabled\r\n"); } +/* TX 출력 비활성화: 모든 신호 핀 LOW로 복귀 (유휴 상태) */ void dr_piezo_disable(void) { nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); @@ -432,11 +535,13 @@ void dr_piezo_disable(void) DBG_PRINTF("[DR_PIEZO] TX disabled\r\n"); } +/* TX 송신 중 여부 확인 (인터럽트 핸들러에서 false로 전환) */ bool dr_piezo_is_busy(void) { return m_tx_active; } +/* 동작 주파수 변경 (100kHz~4MHz): 타이머 CC 레지스터 재설정 */ void dr_piezo_set_frequency(uint32_t freq_hz) { if (freq_hz < 100000 || freq_hz > 4000000) @@ -456,9 +561,14 @@ void dr_piezo_set_frequency(uint32_t freq_hz) } /*============================================================================== - * MUX CONTROL FUNCTION + * MUX 제어 함수 + * 8채널 아날로그 MUX로 피에조 에코 신호 경로를 선택한다. + * MUXA: CH0~CH3 담당, MUXB: CH4~CH7 담당 + * SEL0, SEL1: MUX 내부 채널 주소 선택 + * High Drive(H0H1) 모드: MUX IC의 빠른 스위칭을 위해 강한 출력 구동력 사용 *============================================================================*/ +/* MUX 제어 핀 초기화: 4개 핀 모두 High Drive 출력으로 설정, 기본값 LOW */ void dr_piezo_mux_init(void) { /* Configure MUX control pins as outputs */ @@ -522,9 +632,18 @@ void dr_piezo_mux_init(void) } +/* + * 피에조 채널 선택 (0~7) + * 채널 매핑 (EN_MUXA, EN_MUXB, SEL0, SEL1): + * CH0 = MUXA 입력0 (1,0,0,0) CH4 = MUXB 입력0 (0,1,1,1) + * CH1 = MUXA 입력2 (1,0,1,0) CH5 = MUXB 입력1 (0,1,0,1) + * CH2 = MUXA 입력1 (1,0,0,1) CH6 = MUXB 입력2 (0,1,1,0) + * CH3 = MUXA 입력3 (1,0,1,1) CH7 = MUXB 입력3 (0,1,0,0) + * 채널 전환 후 MUX 안정화 대기 시간(1.3ms) 필요. + */ void dr_piezo_select_channel(uint8_t channel) { - channel = channel & 0x07; /* Mask to 0-7 */ + channel = channel & 0x07; /* 0~7 범위로 마스킹 */ switch (channel) { // EN_A EN_B SEL0 SEL1 @@ -566,6 +685,7 @@ void dr_piezo_select_channel(uint8_t channel) nrf_delay_us(DR_PIEZO_MUX_SETTLING_US); } +/* 핀 테스트: 각 신호 핀을 순서대로 HIGH/LOW 토글 (오실로스코프 확인용) */ void dr_piezo_test_pins(void) { DBG_PRINTF("[DR_PIEZO] Pin test...\r\n"); @@ -606,9 +726,10 @@ void dr_piezo_test_pins(void) } /*============================================================================== - * SYSTEM FUNCTIONS + * 시스템 함수 (전원 + TX 드라이버 통합 제어) *============================================================================*/ +/* 시스템 전체 초기화: 전원 ON → TX 드라이버 초기화 */ void dr_piezo_system_init(void) { DBG_PRINTF("[DR_PIEZO] System init...\r\n"); @@ -617,6 +738,7 @@ void dr_piezo_system_init(void) DBG_PRINTF("[DR_PIEZO] System ready\r\n"); } +/* 시스템 전체 종료: TX 드라이버 해제 → 전원 OFF */ void dr_piezo_system_uninit(void) { dr_piezo_uninit(); @@ -624,6 +746,7 @@ void dr_piezo_system_uninit(void) DBG_PRINTF("[DR_PIEZO] System shutdown\r\n"); } +/* 전원 확인 후 버스트 송신 (블로킹: 송신 완료까지 대기) */ void dr_piezo_transmit(uint8_t cycles) { if (!m_power_enabled) @@ -638,60 +761,74 @@ void dr_piezo_transmit(uint8_t cycles) } /*============================================================================== - * SOFTWARE-BASED BURST + * 소프트웨어 기반 버스트 모드 * 2025-12-11 Charles KWON *============================================================================== - * - * Timing Diagram: - * - * |<-margin->|<----- pulses ----->|<-- DMP -->|<-margin->| - * + * + * Timer/PPI/GPIOTE 하드웨어 대신 CPU에서 직접 GPIO 레지스터를 조작하여 + * 초음파 펄스를 생성하는 방식. 인터럽트를 비활성화하여 정확한 타이밍 보장. + * + * === 타이밍 다이어그램 === + * + * |<-마진->|<----- 펄스들 ----->|<-- DMP -->|<-마진->| + * * PE ___/--------------------------------------------------\___ * P_OUT ___________/-\_/-\_/-\_/-\_/-\____________________\_______ * N_OUT ___________\_/-\_/-\_/-\_/-\_/____________________\_______ * DMP __________________________________/----------\____________ - * - * Signal Description: - * - PE (Pulse Enable): Wraps entire sequence with margin before and after - * - P_OUT: Positive output, starts LOW, toggles at 2MHz, opposite phase to N_OUT - * - N_OUT: Negative output, starts LOW, toggles at 2MHz, opposite phase to P_OUT - * - DMP (Dump): Activates after pulses complete, simultaneous with N_OUT falling edge - * - * Timing Specifications: - * - Frequency: 2MHz (500ns period, 250ns half-period) - * - CPU Clock: 64MHz (1 NOP = 15.625ns) - * - Duty Cycle: 50:50 - * - DMP Pulse Width: ~500ns - * - PE Margin: ~3 NOPs before pulses and after DMP - * - * Pin Mapping (Port P1): - * - P1.02: N_OUT (Negative output) - * - P1.03: P_OUT (Positive output) - * - P1.05: PE (Pulse Enable) - * - P1.09: DMP (Dump control) - * - * Note: Direct register access (NRF_P1->OUT) is used for simultaneous - * multi-pin control to ensure precise timing and phase alignment. + * + * === 신호 설명 === + * - PE (Pulse Enable): 전체 시퀀스를 감싸는 활성화 신호 (전후 마진 포함) + * - P_OUT: 양극 출력, N_OUT과 역상으로 2MHz 토글 + * - N_OUT: 음극 출력, P_OUT과 역상으로 2MHz 토글 + * - DMP (Dump): 펄스 완료 후 피에조 잔류 에너지 방전 + * + * === 동작 원리 === + * 1) __disable_irq()로 인터럽트 차단 → 타이밍 흔들림 방지 + * 2) PE ON (P0.25 OUTSET 레지스터 사용 → 다른 P0 핀 영향 없음) + * 3) NOP 마진 후 for 루프로 P_OUT/N_OUT 교번 출력 + * 4) DMP 펄스 (32 NOP ≒ 500ns) + * 5) PE OFF 후 __enable_irq()로 인터럽트 복원 + * + * === 포트 레지스터 직접 접근 === + * NRF_P1->OUT 레지스터에 미리 계산된 비트 마스크를 직접 기록. + * 이 방식으로 P_OUT, N_OUT, DMP를 동시에 제어하면서도 + * 채널 선택 핀(MUX SEL)은 보존한다 (P1_CTRL_MASK로 제어 핀만 변경). + * PE는 P0 포트에 있으므로 OUTSET/OUTCLR 레지스터로 별도 제어. *============================================================================*/ +/* 핀 번호에서 포트 내 비트 위치 추출 (하위 5비트 = 0~31) */ #define PIN_NUM(pin) ((pin) & 0x1F) -/* Bit masks for P1 port pins - derived from dr_piezo.h definitions - * - * WARNING: Never hardcode pin numbers! - * Hardcoding may save a developer's time momentarily, - * but it will also shorten their lifespan +/* P1 포트 핀의 비트 마스크 - dr_piezo.h의 핀 정의에서 자동 생성 + * + * 경고: 핀 번호를 절대 하드코딩하지 말 것! + * 하드코딩은 개발자의 시간을 일시적으로 절약해줄 수 있지만, + * 동시에 개발자의 수명을 단축시킬 것이다. * - Charles KWON + * + * 각 마스크는 해당 핀의 포트 레지스터 내 비트 위치를 나타낸다. + * NRF_P1->OUT에 직접 쓸 때 사용되며, 여러 핀을 동시에 제어 가능. */ -#define P_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_P_OUT)) /* P1.03 -> P1.07 */ -#define N_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_N_OUT)) /* P1.02 -> P1.06 */ -#define PE_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_PE)) /* P1.05 -> P0.25 */ -#define DMP_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_DMP)) /* P1.09 -> P1.00 */ +#define P_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_P_OUT)) /* P1.07 양극 출력 */ +#define N_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_N_OUT)) /* P1.06 음극 출력 */ +#define PE_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_PE)) /* P0.25 펄스 활성화 */ +#define DMP_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_DMP)) /* P1.00 방전 제어 */ -/* Combined mask for all piezo control signals (excluding channel select) */ +/* P1 포트에서 피에조 제어에 사용하는 핀들의 결합 마스크 (채널 선택 핀 제외) */ #define P1_CTRL_MASK (P_OUT_MASK | N_OUT_MASK | DMP_MASK) +/* + * 소프트웨어 버스트 - 기본 주파수 2.1MHz + * + * NOP 타이밍 계산: + * 2.1MHz → 주기 476ns, 반주기 238ns + * CPU 64MHz → 1 NOP = 15.625ns + * 첫 반주기: 14 NOP(≒219ns) + 레지스터 쓰기(≒30ns) = ≒249ns + * 둘째 반주기: 9 NOP(≒141ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒218ns + * 합계: ≒436~476ns (≒2.1MHz) + */ void dr_piezo_burst_sw(uint8_t cycles) { /* Clamp cycles to valid range (1-20) */ @@ -838,6 +975,13 @@ void dr_piezo_burst_sw(uint8_t cycles) * First half: 15 NOPs (~234ns) * Second half: 12 NOPs (~188ns) + loop overhead */ +/* + * 소프트웨어 버스트 - 1.8MHz + * NOP 타이밍: 반주기 278ns + * 첫 반주기: 17 NOP(≒266ns) + 레지스터 쓰기(≒30ns) = ≒296ns + * 둘째 반주기: 11 NOP(≒172ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒249ns + * 합계: ≒499~556ns (≒1.8MHz) + */ void dr_piezo_burst_sw_18mhz(uint8_t cycles) { /* Clamp cycles to valid range (1-20) */ @@ -960,6 +1104,13 @@ void dr_piezo_burst_sw_18mhz(uint8_t cycles) * Second half: 11 NOPs (~172ns) + loop overhead (~47ns) = ~219ns * Total: ~468-500ns per cycle */ +/* + * 소프트웨어 버스트 - 2.0MHz + * NOP 타이밍: 반주기 250ns + * 첫 반주기: 15 NOP(≒234ns) + 레지스터 쓰기(≒30ns) = ≒264ns + * 둘째 반주기: 10 NOP(≒156ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒233ns + * 합계: ≒468~500ns (≒2.0MHz) + */ void dr_piezo_burst_sw_20mhz(uint8_t cycles) { /* Clamp cycles to valid range (1-20) */ @@ -1083,6 +1234,13 @@ void dr_piezo_burst_sw_20mhz(uint8_t cycles) * Second half: 11 NOPs (~172ns) + loop overhead (~47ns) = ~219ns * Total: ~468-500ns per cycle */ +/* + * 소프트웨어 버스트 - 1.9MHz + * NOP 타이밍: 반주기 263ns + * 첫 반주기: 15 NOP(≒234ns) + 레지스터 쓰기(≒30ns) = ≒264ns + * 둘째 반주기: 9 NOP(≒141ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒218ns + * 합계: ≒468~500ns (≒1.9MHz) + */ void dr_piezo_burst_sw_19mhz(uint8_t cycles) { /* Clamp cycles to valid range (1-20) */ @@ -1208,6 +1366,13 @@ void dr_piezo_burst_sw_19mhz(uint8_t cycles) * Second half: 11 NOPs (~172ns) + loop overhead (~47ns) = ~219ns * Total: ~452ns per cycle (~2.21 MHz) */ +/* + * 소프트웨어 버스트 - 2.2MHz + * NOP 타이밍: 반주기 227ns + * 첫 반주기: 13 NOP(≒203ns) + 레지스터 쓰기(≒30ns) = ≒233ns + * 둘째 반주기: 8 NOP(≒125ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒202ns + * 합계: ≒435~454ns (≒2.2MHz) + */ void dr_piezo_burst_sw_22mhz(uint8_t cycles) { /* Clamp cycles to valid range (1-20) */ @@ -1327,6 +1492,13 @@ void dr_piezo_burst_sw_22mhz(uint8_t cycles) * Second half: 14 NOPs (~219ns) + loop overhead (~47ns) = ~266ns * Total: ~532-588ns per cycle */ +/* + * 소프트웨어 버스트 - 1.7MHz + * NOP 타이밍: 반주기 294ns + * 첫 반주기: 18 NOP(≒281ns) + 레지스터 쓰기(≒30ns) = ≒311ns + * 둘째 반주기: 10 NOP(≒156ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒233ns + * 합계: ≒532~588ns (≒1.7MHz) + */ void dr_piezo_burst_sw_17mhz(uint8_t cycles) { /* Clamp cycles to valid range (1-20) */ diff --git a/project/ble_peripheral/dr_piezo/dr_piezo.h b/project/ble_peripheral/dr_piezo/dr_piezo.h index f87a57b..00d31d1 100644 --- a/project/ble_peripheral/dr_piezo/dr_piezo.h +++ b/project/ble_peripheral/dr_piezo/dr_piezo.h @@ -3,20 +3,60 @@ * @brief Piezo Transducer Driver (2MHz Signal Generator) * @author Charles KWON * @date 2025-12-09 - * + * * @note Hardware: nRF52840 + MD1822K6-G MOSFET Driver + TC7920K6-G MOSFET - * Output: �20V at 2MHz, 3~5 cycles - * + * Output: +/-20V at 2MHz, 3~5 cycles + * * @details Timing Sequence: * 1. PE = HIGH (enable) * 2. P_OUT/N_OUT = 2MHz pulses (3~5 cycles) * 3. DMP = HIGH (dump) * 4. DMP = LOW * 5. PE = LOW (disable) - * + * * All signals (P_OUT, N_OUT, DMP) operate within PE HIGH period. ******************************************************************************/ +/******************************************************************************* + * [한국어 설명] 피에조 초음파 트랜스듀서 드라이버 헤더 + * + * === 개요 === + * 방광 측정용 초음파 송신기의 핀 할당, 설정값, 함수 선언을 정의. + * nRF52840 + MD1822K6-G(MOSFET 드라이버) + TC7920K6-G(MOSFET) 하드웨어 구성. + * 출력: +/-20V, 2MHz, 3~7 사이클 버스트. + * + * === 핀 할당 === + * 전원 제어: + * - DR_PIEZO_PWR_EN (P1.9): DC/DC 컨버터 활성화 -> +/-20V 고전압 생성 + * + * TX 신호 핀 (MOSFET 드라이버 제어): + * - PE (P0.25): Pulse Enable - 전체 시퀀스 활성화/비활성화 + * - DMP (P1.0): Dump - 펄스 후 피에조 잔류 에너지 방전 + * - P_OUT (P1.7): Positive Output - 피에조 양극 구동 + * - N_OUT (P1.6): Negative Output - 피에조 음극 구동 (P_OUT과 역상) + * + * MUX 제어 핀 (8채널 에코 신호 경로 선택): + * - EN_MUXA (P0.21): MUXA 활성화 (CH0~CH3 담당) + * - EN_MUXB (P0.23): MUXB 활성화 (CH4~CH7 담당) + * - SEL0 (P1.10): MUX 내부 채널 주소 비트 0 + * - SEL1 (P0.28): MUX 내부 채널 주소 비트 1 + * + * === MUX 채널 매핑 (8채널) === + * CH0 = MUXA 입력0: EN_A=1, EN_B=0, SEL0=0, SEL1=0 + * CH1 = MUXA 입력2: EN_A=1, EN_B=0, SEL0=1, SEL1=0 + * CH2 = MUXA 입력1: EN_A=1, EN_B=0, SEL0=0, SEL1=1 + * CH3 = MUXA 입력3: EN_A=1, EN_B=0, SEL0=1, SEL1=1 + * CH4 = MUXB 입력0: EN_A=0, EN_B=1, SEL0=1, SEL1=1 + * CH5 = MUXB 입력1: EN_A=0, EN_B=1, SEL0=0, SEL1=1 + * CH6 = MUXB 입력2: EN_A=0, EN_B=1, SEL0=1, SEL1=0 + * CH7 = MUXB 입력3: EN_A=0, EN_B=1, SEL0=0, SEL1=0 + * + * === 두 가지 버스트 모드 === + * 1) 하드웨어 버스트 (dr_piezo_burst): Timer2 + PPI + GPIOTE 사용, CPU 비의존적 + * 2) 소프트웨어 버스트 (dr_piezo_burst_sw_XXmhz): CPU NOP 기반 정밀 타이밍 + * - 주파수별 전용 함수: 1.7/1.8/1.9/2.0/2.1/2.2 MHz + ******************************************************************************/ + #ifndef DR_PIEZO_H #define DR_PIEZO_H @@ -25,7 +65,8 @@ #include "nrf_gpio.h" /*============================================================================== - * POWER CONTROL PINS (DC/DC Converter +/-20V) + * 전원 제어 핀 (DC/DC 컨버터 +/-20V) + * DR_PIEZO_PWR_EN: HIGH로 설정 시 DC/DC 컨버터가 +/-20V 고전압 생성 *============================================================================*/ //#define DR_PIEZO_PWR_SHDN NRF_GPIO_PIN_MAP(0, 21) /**< SHDN_VPP/VNN (LT3463) */ // P0.21 : PZT_EN_MUXA jhChun 0128 //#define DR_PIEZO_PWR_EN_10V NRF_GPIO_PIN_MAP(0, 22) /**< EN_+10V (MCP1804) */ // P0.22 : NIRS PIN @@ -33,7 +74,12 @@ #define DR_PIEZO_PWR_EN NRF_GPIO_PIN_MAP(1, 9) /** Power Enable jhChun 0128 */ /*============================================================================== - * TX SIGNAL PINS (MOSFET Driver Control) + * TX 신호 핀 (MOSFET 드라이버 제어) + * PE: Pulse Enable - 전체 TX 시퀀스 활성화/비활성화 + * DMP: Dump - 펄스 후 피에조 잔류 에너지 방전용 + * P_OUT: Positive Output - 피에조 양극 구동 (N_OUT과 역상) + * N_OUT: Negative Output - 피에조 음극 구동 (P_OUT과 역상) + * 주의: 이전 핀 할당(주석 처리)에서 새 보드 레이아웃으로 변경됨 (jhChun 0128) *============================================================================*/ //#define DR_PIEZO_PIN_PE NRF_GPIO_PIN_MAP(1, 5) /**< Pulse Enable */ //#define DR_PIEZO_PIN_DMP NRF_GPIO_PIN_MAP(1, 9) /**< Dump control */ @@ -46,7 +92,11 @@ #define DR_PIEZO_PIN_N_OUT NRF_GPIO_PIN_MAP(1, 6) /**< Negative output */ // P1.2 -> P1.6 jhChun 0128 /*============================================================================== - * MUX CONTROL PINS (Echo Signal Path Selection) + * MUX 제어 핀 (에코 신호 경로 선택) + * 8채널 아날로그 MUX로 피에조 센서 채널을 선택한다. + * MUXA(CH0~CH3)와 MUXB(CH4~CH7) 두 개의 4채널 MUX 사용. + * EN_MUXA/EN_MUXB: 각 MUX 활성화 (동시에 하나만 HIGH) + * SEL0/SEL1: MUX 내부 4채널 중 하나를 선택하는 주소 비트 *============================================================================*/ //#define DR_PIEZO_MUX_SEL1 NRF_GPIO_PIN_MAP(1, 13) /**< MUX Select 1 (HIGH) */ //#define DR_PIEZO_MUX_SEL2 NRF_GPIO_PIN_MAP(1, 12) /**< MUX Select 2 (LOW) */ @@ -59,7 +109,11 @@ #define DR_PIEZO_MUX_SEL1 NRF_GPIO_PIN_MAP(0, 28) /**< MUX Select 1 */ /*============================================================================== - * CONFIGURATION + * 설정값 + * DR_PIEZO_FREQ_HZ: 목표 주파수 (실제 동작 주파수는 dr_piezo.c에서 결정) + * DR_PIEZO_DEFAULT_CYCLES: 기본 버스트 사이클 수 (5) + * DR_PIEZO_MIN/MAX_CYCLES: 허용 사이클 범위 (3~7) + * DR_PIEZO_MUX_SETTLING_US: MUX 채널 전환 후 아날로그 경로 안정화 대기 시간 *============================================================================*/ /** * @note Actual operating frequency is defined in dr_piezo.c as PIEZO_FREQ_MHZ. @@ -73,7 +127,8 @@ #define DR_PIEZO_MUX_SETTLING_US 1300 /**< MUX settling delay (us) */ /*============================================================================== - * POWER CONTROL FUNCTIONS + * 전원 제어 함수 + * DC/DC 컨버터(+/-20V)를 ON/OFF하여 피에조 구동 고전압을 제어한다. *============================================================================*/ /** @@ -86,8 +141,15 @@ void dr_piezo_power_on(void); */ void dr_piezo_power_off(void); +/** + * @brief 피에조 전원 상태 확인 + * @return true: 전원 ON, false: 전원 OFF + */ +bool dr_piezo_is_power_on(void); + /*============================================================================== - * TX DRIVER FUNCTIONS + * TX 드라이버 함수 + * 초음파 송신 관련: 초기화, 버스트 송신, 활성화/비활성화, 주파수 설정 *============================================================================*/ /** @@ -156,7 +218,12 @@ void dr_piezo_mux_init(void); void dr_piezo_select_channel(uint8_t channel); /*============================================================================== - * SYSTEM FUNCTIONS (Power + TX combined) + * 시스템 함수 (전원 + TX 통합 제어) + * 전원 ON/OFF와 TX 드라이버 초기화/해제를 한 번에 수행하는 편의 함수. + * 소프트웨어 버스트(burst_sw) 계열: CPU NOP 기반 정밀 타이밍. + * - Timer/PPI 없이 CPU에서 직접 GPIO를 제어 + * - 인터럽트 비활성화 상태에서 동작하여 타이밍 정확도 보장 + * - 주파수별 전용 함수 제공 (NOP 개수가 다름) *============================================================================*/ /**