From 822789ff822416017851189fdb5ce56f04eda0ed Mon Sep 17 00:00:00 2001 From: jhchun Date: Tue, 30 Jun 2026 18:20:39 +0900 Subject: [PATCH] =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 16 +++++--- src/command/handlers/cmd_device.c | 19 ++++++++-- src/drivers/imu/imu_i2c.h | 4 +- src/drivers/piezo/piezo.c | 6 +-- src/measurement/piezo_measure.c | 62 +++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4324e07..39ac8a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,28 +2,34 @@ cmake_minimum_required(VERSION 3.20.0) -macro(app_set_runner_args) - board_runner_args(nrfutil "--erase-mode=ranges") -endmacro() - find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(vesiscan) target_include_directories(app PRIVATE src + src/command + src/command/handlers src/ble src/drivers/battery src/drivers/echo_adc src/drivers/led src/drivers/imu src/drivers/piezo + src/measurement ) target_sources(app PRIVATE src/main.c src/app_nvs.c - src/parser.c + src/command/parser.c + src/command/cmd_common.c + src/command/cmd_table.c + src/command/handlers/cmd_device.c + src/command/handlers/cmd_info.c + src/command/handlers/cmd_piezo.c + src/command/handlers/cmd_sensor.c src/power_control.c + src/measurement/piezo_measure.c src/ble/ble_service.c src/drivers/battery/battery_adc.c src/drivers/echo_adc/echo_adc.c diff --git a/src/command/handlers/cmd_device.c b/src/command/handlers/cmd_device.c index 26b9d0c..a4b7b0b 100644 --- a/src/command/handlers/cmd_device.c +++ b/src/command/handlers/cmd_device.c @@ -15,6 +15,9 @@ #include "ble_service.h" #include "cmd_device.h" +/* + * msq: power off + */ int cmd_msq(const uint8_t *data, uint8_t data_len) { ARG_UNUSED(data); @@ -31,6 +34,9 @@ int cmd_msq(const uint8_t *data, uint8_t data_len) return 1; } +/* + * mss: reboot + */ int cmd_mss(const uint8_t *data, uint8_t data_len) { ARG_UNUSED(data); @@ -47,6 +53,9 @@ int cmd_mss(const uint8_t *data, uint8_t data_len) return 1; } +/* + * msr: clear bond data and reboot + */ int cmd_msr(const uint8_t *data, uint8_t data_len) { uint16_t status = 0x0000; @@ -62,8 +71,8 @@ int cmd_msr(const uint8_t *data, uint8_t data_len) } /* - * 현재 연결의 bond/security 정보를 먼저 지우면 같은 연결에서 rsr: 응답을 - * 보낼 수 없음. 응답 전송 완료 후 bond 삭제와 reboot 진행 필요 + * 현재 연결의 bond/security 정보를 먼저 지우면 같은 연결에서 rsr: 응답을 보낼 수 없음 + * 응답 전송 완료 후 bond 삭제와 reboot 진행 필요 */ k_msleep(100); @@ -93,7 +102,9 @@ int cmd_msr(const uint8_t *data, uint8_t data_len) return 1; } -/* msp? → IMU 1회 측정 → rsp: + accel XYZ + gyro XYZ (각 int16 빅엔디안) */ +/* + * mdf: DFU mode enter + */ int cmd_mdf(const uint8_t *data, uint8_t data_len) { ARG_UNUSED(data); @@ -101,7 +112,7 @@ int cmd_mdf(const uint8_t *data, uint8_t data_len) uint16_t status = ble_dfu_advertising_is_enabled() ? 0xFFFF : 0x0000; - /* DFU advertising 전환 중 연결이 끊겨도 전원 래치가 유지되도록 먼저 ON 상태를 확정한다. */ + /* DFU advertising 전환 중 연결이 끊겨도 전원 래치가 유지되도록 먼저 ON 상태를 확정 */ device_power_keep_on(); int tx_err = cmd_send_response_u16("rdf:", status); diff --git a/src/drivers/imu/imu_i2c.h b/src/drivers/imu/imu_i2c.h index 7a2e7c6..fefaa1b 100644 --- a/src/drivers/imu/imu_i2c.h +++ b/src/drivers/imu/imu_i2c.h @@ -76,9 +76,7 @@ int imu_fifo_start(void); * @param out_count 실제 저장한 샘플 수 * @return 0 성공, 음수 에러 */ -int imu_fifo_read_latest(uint8_t *sample_bytes, - uint16_t max_samples, - uint16_t *out_count); +int imu_fifo_read_latest(uint8_t *sample_bytes, uint16_t max_samples, uint16_t *out_count); /** * @brief Return whether IMU FIFO streaming is active. diff --git a/src/drivers/piezo/piezo.c b/src/drivers/piezo/piezo.c index 6aec34c..6746f0f 100644 --- a/src/drivers/piezo/piezo.c +++ b/src/drivers/piezo/piezo.c @@ -246,7 +246,7 @@ void piezo_burst_sw(uint8_t cycles) /* 구동 Enable 전 초기 Low 정렬 */ NRF_P0->OUTCLR = piezo_pe_mask; NRF_P1->OUT = p1_all_low; - NRF_P0->OUTSET = piezo_pe_mask; + NRF_P0->OUTSET = piezo_pe_mask; /* PE High */ for (uint8_t i = 0; i < cycles; i++) { @@ -261,10 +261,10 @@ void piezo_burst_sw(uint8_t cycles) /* 잔류 전하 방전 */ NRF_P1->OUT = p1_dmp_high; - nop_delay_32(); + nop_delay_32(); /* DMP High 폭 */ /* 출력 Low 복귀 */ NRF_P1->OUT = p1_all_low; - NRF_P0->OUTCLR = piezo_pe_mask; + NRF_P0->OUTCLR = piezo_pe_mask; /* PE Low */ irq_unlock(key); } diff --git a/src/measurement/piezo_measure.c b/src/measurement/piezo_measure.c index b5421c3..379465f 100644 --- a/src/measurement/piezo_measure.c +++ b/src/measurement/piezo_measure.c @@ -10,12 +10,14 @@ #include "app_nvs.h" #include "debug_print.h" +/* 앱에서 저장/변경 가능한 piezo 측정 기본값 */ #define PIEZO_CFG_FREQ_DEFAULT 1 #define PIEZO_CFG_CYCLES_DEFAULT 7 #define PIEZO_CFG_DELAY_DEFAULT 10 #define PIEZO_CFG_SAMPLES_DEFAULT 100 #define PIEZO_CFG_AVG_DEFAULT 3 +/* 설정값 검증 범위 */ #define PIEZO_CFG_AVG_MAX 10 #define PIEZO_CFG_SAMPLES_MIN 80 #define PIEZO_CFG_SAMPLES_MAX 117 @@ -24,10 +26,16 @@ #define PIEZO_AVG_INTER_BURST_GAP_US 500 #define PIEZO_DUMMY_CAPTURE_COUNT 5 +/* 6채널 sweep 결과 (각 채널의 평균 완료 후 최종만 저장) */ static uint16_t piezo_channels[PIEZO_NUM_CHANNELS][PIEZO_MEASURE_MAX_SAMPLES]; + +/* 단일 capture 임시 버퍼 (dummy capture와 real capture 모두 이 버퍼 재사용) */ static uint16_t echo_capture[PIEZO_MEASURE_MAX_SAMPLES]; + +/* 평균 누적 버퍼 (uint16 샘플을 avg 횟수만큼 더하므로 overflow 여유를 위해 uint32) */ static uint32_t echo_accum[PIEZO_MEASURE_MAX_SAMPLES]; +/* NVS에서 읽어 오거나 mcs? 명령으로 갱신되는 현재 측정 설정 */ static piezo_config_t g_piezo_config = { .freq = PIEZO_CFG_FREQ_DEFAULT, .cycles = PIEZO_CFG_CYCLES_DEFAULT, @@ -36,6 +44,9 @@ static piezo_config_t g_piezo_config = { .samples = PIEZO_CFG_SAMPLES_DEFAULT, }; +/* + * 저장/측정에 사용할 piezo 설정값 검증 + */ bool piezo_config_validate(const piezo_config_t *cfg) { if (cfg == NULL) @@ -77,6 +88,9 @@ const piezo_config_t *piezo_config_get(void) return &g_piezo_config; } +/* + * 부팅 시 NVS 설정 읽기 (없거나 유효하지 않으면 기본값 유지) + */ int piezo_config_init(void) { int err = app_nvs_init(&g_piezo_config, piezo_config_validate, NULL); @@ -88,6 +102,9 @@ int piezo_config_init(void) return 0; } +/* +* 런타임 설정 변경: RAM 설정을 갱신한 뒤 NVS에도 저장 +*/ int piezo_config_set(const piezo_config_t *cfg) { if (!piezo_config_validate(cfg)) @@ -99,6 +116,9 @@ int piezo_config_set(const piezo_config_t *cfg) return app_nvs_save_piezo(&g_piezo_config); } +/* + * sweep 명령이 끝난 뒤 BLE 응답에서 채널별 결과 버퍼 참조 시 사용 + */ const uint16_t *piezo_measure_channel_buffer(uint8_t channel) { if (channel >= PIEZO_NUM_CHANNELS) @@ -109,11 +129,18 @@ const uint16_t *piezo_measure_channel_buffer(uint8_t channel) return piezo_channels[channel]; } +/* + * mec?/mad? 같은 단일 채널 명령의 최종 결과 버퍼 반환 + */ const uint16_t *piezo_measure_single_buffer(void) { return echo_capture; } +/* + * piezo 측정 공통 준비 단계 + * 전원 인가, piezo GPIO 초기화, echo ADC 초기화/wake를 한 번에 수행 + */ int piezo_measure_start_session(void) { DBG_PRINTF("[MBB] piezo session start\r\n"); @@ -146,11 +173,16 @@ int piezo_measure_start_session(void) return ECHO_STATUS_OK; } +/* + * 6채널 전체 sweep + * 각 채널마다 dummy capture를 먼저 버린 뒤, cfg->avg회 real capture를 sample index별로 평균 + */ int piezo_measure_sweep(void) { const piezo_config_t *cfg = piezo_config_get(); uint16_t capture_delay_us = cfg->delay_us; + /* 너무 짧은 delay가 들어오면 burst 직후 ADC capture가 겹치므로 최소 delay 보장 */ if (capture_delay_us < PIEZO_BURST_TO_ADC_DELAY_US) { capture_delay_us = PIEZO_BURST_TO_ADC_DELAY_US; @@ -160,6 +192,10 @@ int piezo_measure_sweep(void) { DBG_PRINTF("[MBB] sweep ch=%d start\r\n", ch); + /* + * MUX 채널 선택 + * 드라이버 내부에서 1.3 ms settle을 기다리고, 아래에서 measurement layer settle 0.5 ms를 추가로 기다린다. + */ int err = piezo_select_channel(ch); if (err) { @@ -169,6 +205,7 @@ int piezo_measure_sweep(void) k_busy_wait(PIEZO_POST_SELECT_SETTLE_US); + /* ADC dummy read/wake (채널 전환 직후 첫 ADC 접근을 실제 데이터에 섞지 않기 위한 준비 단계) */ err = echo_adc_wake(); if (err) { @@ -176,6 +213,10 @@ int piezo_measure_sweep(void) return ECHO_STATUS_CAPTURE; } + /* + * Dummy burst + dummy capture + * 결과는 버리고, piezo/mux/ADC 입력의 이전 상태가 real 평균에 섞이지 않도록 함 + */ for (uint8_t dummy = 0; dummy < PIEZO_DUMMY_CAPTURE_COUNT; dummy++) { k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US); @@ -190,6 +231,10 @@ int piezo_measure_sweep(void) } } + /* 마지막 dummy capture와 첫 real capture 사이에도 같은 회복 시간을 둔다. */ + k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US); + + /* 여기부터 실제 평균에 들어갈 capture */ memset(echo_accum, 0, sizeof(echo_accum)); for (uint16_t avg = 0; avg < cfg->avg; avg++) @@ -199,6 +244,7 @@ int piezo_measure_sweep(void) k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US); } + /* burst-to-capture 사이 타이밍 흔들림을 줄이기 위해 real capture 구간만 IRQ를 잠금 */ unsigned int key = irq_lock(); piezo_burst_sw((uint8_t)cfg->cycles); k_busy_wait(capture_delay_us); @@ -211,12 +257,14 @@ int piezo_measure_sweep(void) return ECHO_STATUS_CAPTURE; } + /* 같은 sample index끼리 누적 */ for (uint16_t i = 0; i < cfg->samples; i++) { echo_accum[i] += echo_capture[i]; } } + /* 채널별 최종 평균 파형 저장 - 이후 cmd handler가 reb: 패킷으로 전송 */ for (uint16_t i = 0; i < cfg->samples; i++) { piezo_channels[ch][i] = (uint16_t)(echo_accum[i] / cfg->avg); @@ -229,6 +277,10 @@ int piezo_measure_sweep(void) return ECHO_STATUS_OK; } +/* + * 단일 채널 burst + echo capture + * mec? 디버그/테스트 명령에서 사용하며, 결과는 echo_capture에 평균 저장된다. + */ int piezo_measure_single_capture(uint8_t cycles, uint16_t delay_us, uint16_t num_samples, @@ -245,6 +297,7 @@ int piezo_measure_single_capture(uint8_t cycles, return ECHO_STATUS_CAPTURE; } + /* averaging 0은 명령 파라미터 누락/미지정으로 보고 1회 capture로 보정한다. */ if (averaging == 0U) { averaging = 1U; @@ -264,6 +317,7 @@ int piezo_measure_single_capture(uint8_t cycles, return ECHO_STATUS_CAPTURE; } + /* real capture 전에 같은 채널에서 dummy burst/capture를 수행하고 버린다. */ for (uint8_t dummy = 0; dummy < PIEZO_DUMMY_CAPTURE_COUNT; dummy++) { k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US); @@ -277,6 +331,9 @@ int piezo_measure_single_capture(uint8_t cycles, } } + /* 마지막 dummy capture와 첫 real capture 사이에도 같은 회복 시간을 둔다. */ + k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US); + memset(echo_accum, 0, sizeof(echo_accum)); for (uint16_t avg = 0; avg < averaging; avg++) @@ -311,6 +368,10 @@ int piezo_measure_single_capture(uint8_t cycles, return ECHO_STATUS_OK; } +/* + * 단일 채널 ADC-only capture. + * piezo burst 없이 현재 echo ADC 입력만 평균해서 확인할 때 사용한다. + */ int piezo_measure_adc_only_capture(uint16_t num_samples, uint16_t averaging, uint8_t channel) @@ -353,6 +414,7 @@ int piezo_measure_adc_only_capture(uint16_t num_samples, k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US); } + /* burst가 없으므로 ADC capture 자체의 간섭만 줄이기 위해 짧게 IRQ를 잠근다. */ unsigned int key = irq_lock(); err = echo_adc_capture(echo_capture, num_samples); irq_unlock(key);