From 1f1c4b0b255f1f6718993c011436560bc84bc8a7 Mon Sep 17 00:00:00 2001 From: jhChun Date: Thu, 19 Mar 2026 18:04:04 +0900 Subject: [PATCH] =?UTF-8?q?Bit-bang=20SPI(8MHz)=EC=97=90=EC=84=9C=20HW=20S?= =?UTF-8?q?PI(SPIM3,=2016MHz)=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- pc_firm/dr_adc121s051/dr_adc121s051.c | 413 +++++------------- pc_firm/dr_adc121s051/dr_adc121s051.h | 3 + pc_firm/parser.c | 149 +++++-- .../pca10056/s140/config/sdk_config.h | 2 +- 4 files changed, 226 insertions(+), 341 deletions(-) diff --git a/pc_firm/dr_adc121s051/dr_adc121s051.c b/pc_firm/dr_adc121s051/dr_adc121s051.c index 8ba5d35..2203cd3 100644 --- a/pc_firm/dr_adc121s051/dr_adc121s051.c +++ b/pc_firm/dr_adc121s051/dr_adc121s051.c @@ -5,7 +5,9 @@ * @author Charles KWON * @date 2025-12-15 * - * @details Software SPI (bit-bang) implementation for ADC121S051. + * @details Hardware SPI (nrfx_spim, SPIM2) implementation for ADC121S051. + * Replaces bit-bang SPI to eliminate __disable_irq() and prevent + * SoftDevice assertion failures during BLE connection events. * Optimized for reading envelope-detected echo signals. * * ADC121S051 Serial Interface: @@ -32,6 +34,8 @@ #include "dr_adc121s051.h" #include "dr_util.h" +#include "nrfx_spim.h" +#include "nrf52840.h" #include "nrf_gpio.h" #include "nrf_delay.h" #include /* memset */ @@ -72,17 +76,6 @@ extern void dr_piezo_power_off(void); #define DR_PIN_NUM(pin) ((pin) & 0x1F) #define DR_PIN_PORT(pin) (((pin) >> 5) & 0x01) -/* SPI bit masks for direct register access */ -#define CS_MASK (1UL << DR_PIN_NUM(DR_ADC_PIN_CS)) -#define SCLK_MASK (1UL << DR_PIN_NUM(DR_ADC_PIN_SCLK)) -#define SDATA_MASK (1UL << DR_PIN_NUM(DR_ADC_PIN_SDATA)) - -/* Get port register based on pin */ -#define DR_GET_PORT(pin) (DR_PIN_PORT(pin) ? NRF_P1 : NRF_P0) - -/* ADC timing constants */ -#define SCLK_CYCLES 16 /**< Clock cycles per conversion */ - /*============================================================================== * PRIVATE VARIABLES *============================================================================*/ @@ -90,247 +83,85 @@ extern void dr_piezo_power_off(void); static bool m_initialized = false; static uint32_t m_vref_mv = DR_ADC_VREF_MV; +/* Hardware SPI instance (SPIM3 - supports up to 32MHz, 16MHz for ADC121S051) */ +static nrfx_spim_t m_spim = NRFX_SPIM_INSTANCE(3); + /* Echo capture buffer (module-level for external access) */ static uint16_t m_echo_buffer[DR_ADC_ECHO_SAMPLES_MAX]; -/* Port registers for fast access (currently unused - using direct NRF_P0) */ -/* static NRF_GPIO_Type *m_port_cs; */ -/* static NRF_GPIO_Type *m_port_sclk; */ -/* static NRF_GPIO_Type *m_port_sdata; */ - -/*============================================================================== - * PRIVATE FUNCTION PROTOTYPES - *============================================================================*/ - -static void adc_gpio_init(void); -/* adc_transfer is always_inline, no prototype needed */ - -/*============================================================================== - * INLINE GPIO FUNCTIONS (for speed) - *============================================================================*/ - -/* cs_low() unused - using ADC_TRANSFER_16BITS macro with direct register access -static inline void cs_low(void) -{ - NRF_P0->OUTCLR = CS_MASK; -} -*/ - -static inline void cs_high(void) -{ - NRF_P0->OUTSET = CS_MASK; -} - -/* sclk_low() unused - using direct register access in macro -static inline void sclk_low(void) -{ - NRF_P0->OUTCLR = SCLK_MASK; -} -*/ - -static inline void sclk_high(void) -{ - NRF_P0->OUTSET = SCLK_MASK; -} - -/* read_sdata() unused - using direct register access in macro -static inline uint32_t read_sdata(void) -{ - return (NRF_P0->IN & SDATA_MASK) ? 1 : 0; -} -*/ - /*============================================================================== * PRIVATE FUNCTIONS *============================================================================*/ /** - * @brief Initialize GPIO pins for ADC + * @brief Perform one ADC121S051 conversion via hardware SPI (SPIM2). + * @return 12-bit ADC value (0–4095) + * + * @details ADC121S051 serial frame (16 bits, MSB first): + * Bit 15-13: Leading zeros (Z2, Z1, Z0) + * Bit 12-1: Data bits D11-D0 + * Bit 0: Don't care + * Extract: (raw16 >> 1) & 0x0FFF + * + * SPI Mode 2 (CPOL=1, CPHA=0): + * - SCLK idle HIGH + * - Data valid on SCLK falling edge (sampled by SPIM on falling edge) + * - CS falling edge starts ADC conversion */ -static void adc_gpio_init(void) +static uint16_t spim_read_raw(void) { - /* Port pointers removed - using direct NRF_P0 access for speed */ + volatile uint8_t rx_buf[2] = {0, 0}; + NRF_SPIM_Type *spim = m_spim.p_reg; - /* CS: Output, HIGH (idle) */ - nrf_gpio_cfg_output(DR_ADC_PIN_CS); - nrf_gpio_pin_set(DR_ADC_PIN_CS); + nrf_gpio_pin_clear(DR_ADC_PIN_CS); /* CS LOW */ + spim->RXD.PTR = (uint32_t)rx_buf; + spim->RXD.MAXCNT = 2; + spim->EVENTS_END = 0; + spim->TASKS_START = 1; + while (!spim->EVENTS_END) {} + nrf_gpio_pin_set(DR_ADC_PIN_CS); /* CS HIGH */ - /* SCLK: Output, HIGH (idle) - per Figure 4 */ - nrf_gpio_cfg_output(DR_ADC_PIN_SCLK); - nrf_gpio_pin_set(DR_ADC_PIN_SCLK); - - /* SDATA: Input (ADC has push-pull output) */ - nrf_gpio_cfg_input(DR_ADC_PIN_SDATA, NRF_GPIO_PIN_NOPULL); - - ADC_LOG("ADC GPIO: CS=P%d.%02d SCLK=P%d.%02d SDATA=P%d.%02d", - DR_PIN_PORT(DR_ADC_PIN_CS), DR_PIN_NUM(DR_ADC_PIN_CS), - DR_PIN_PORT(DR_ADC_PIN_SCLK), DR_PIN_NUM(DR_ADC_PIN_SCLK), - DR_PIN_PORT(DR_ADC_PIN_SDATA), DR_PIN_NUM(DR_ADC_PIN_SDATA)); + uint16_t raw16 = ((uint16_t)rx_buf[0] << 8) | rx_buf[1]; + return (raw16 >> 1) & 0x0FFF; } -/** - * @brief Perform one ADC conversion (16 clock cycles) - * @return Raw 16-bit data (includes leading zeros) - * - * @details Timing optimized for maximum speed (~500ksps). - * At 64MHz CPU: 1 NOP = 15.625ns - * - * ADC specs (@ 3.3V): - * - tACC (data access time): 40ns max - * - tH (hold time): 7ns min - * - fSCLK: 4-10 MHz - * - * MUST be inlined to eliminate function call overhead for consistent timing. - */ -/* - * ADC_TRANSFER_BIT macro - one SCLK cycle for bit-bang SPI - * Captures data on falling edge, processes on rising edge - * - * Target: 10MHz (100ns cycle) = 50ns LOW + 50ns HIGH - * ADC121S051 timing requirements: - * - tCL (SCLK low time): 40ns min - * - tCH (SCLK high time): 40ns min - * - tACC (data access): 40ns max (data valid after SCLK falling) - * - * LOW phase: OUTCLR(~15ns) + NOP(~16ns) + IN read(~30ns) = ~61ns (meets tCL, tACC) - * HIGH phase: OUTSET(~15ns) + shift/OR(~40ns) = ~55ns (meets tCH) - * Total: ~116ns (~8.6MHz) - * - * Note: Removing HIGH phase NOP to reduce cycle time - */ -#define ADC_TRANSFER_BIT(data, in_reg) \ - do { \ - NRF_P0->OUTCLR = SCLK_MASK; \ - __NOP(); \ - in_reg = NRF_P0->IN; \ - NRF_P0->OUTSET = SCLK_MASK; \ - data = (data << 1) | ((in_reg & SDATA_MASK) ? 1 : 0); \ - } while(0) - -#define ADC_TRANSFER_BIT_LAST(data, in_reg) \ - do { \ - NRF_P0->OUTCLR = SCLK_MASK; \ - __NOP(); \ - in_reg = NRF_P0->IN; \ - NRF_P0->OUTSET = SCLK_MASK; \ - data = (data << 1) | ((in_reg & SDATA_MASK) ? 1 : 0); \ - } while(0) - -/* - * ADC_TRANSFER_16BITS macro - Complete 16-bit transfer - * Use this instead of adc_transfer() function for consistent first bit timing. - * Variables data and in_reg MUST be declared and initialized before calling. - * - * IMPORTANT: Call ADC_PREHEAT(data, in_reg) before cs_low() to warm up pipeline. - */ -#define ADC_TRANSFER_16BITS(data, in_reg) \ - do { \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 15 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 14 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 13 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 12 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 11 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 10 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 9 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 8 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 7 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 6 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 5 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 4 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 3 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 2 */ \ - ADC_TRANSFER_BIT(data, in_reg); /* Bit 1 */ \ - ADC_TRANSFER_BIT_LAST(data, in_reg); /* Bit 0 */ \ - NRF_P0->OUTSET = CS_MASK; \ - __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); \ - __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); \ - __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); \ - __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); \ - } while(0) - -/* - * ADC_PREHEAT - Warm up instruction pipeline before timing-critical section - * This prevents the first SCLK cycle from being longer than others. - * - * CRITICAL: Must perform ACTUAL SCLK toggle to prime the GPIO write path! - * CS is still HIGH (idle), so this dummy toggle doesn't affect ADC. - * - * Sequence: - * 1. Dummy SCLK LOW/HIGH cycle (exact same code as ADC_TRANSFER_BIT) - * 2. SCLK remains HIGH (ready for cs_low) - */ -#define ADC_PREHEAT(data, in_reg) \ - do { \ - /* Dummy SCLK cycle - CS still HIGH, so ADC ignores this */ \ - /* Must match ADC_TRANSFER_BIT timing exactly */ \ - NRF_P0->OUTCLR = SCLK_MASK; \ - __NOP(); \ - in_reg = NRF_P0->IN; \ - NRF_P0->OUTSET = SCLK_MASK; \ - data = (data << 1) | ((in_reg & SDATA_MASK) ? 1 : 0); \ - /* Second dummy cycle for better pipeline warm-up */ \ - NRF_P0->OUTCLR = SCLK_MASK; \ - __NOP(); \ - in_reg = NRF_P0->IN; \ - NRF_P0->OUTSET = SCLK_MASK; \ - data = (data << 1) | ((in_reg & SDATA_MASK) ? 1 : 0); \ - /* Reset data after priming */ \ - data = 0; \ - } while(0) - -/* adc_transfer() function replaced by ADC_TRANSFER_16BITS macro for speed - * Kept here for reference but commented out to eliminate warning - */ -#if 0 -/** - * @brief Perform one ADC conversion (16 clock cycles) - * @param data Pre-initialized to 0 by caller - * @param in_reg Pre-declared register variable by caller - * @return Raw 16-bit data (includes leading zeros) - */ -__attribute__((always_inline)) -static inline uint16_t adc_transfer(uint16_t data, register uint32_t in_reg) -{ - /* Implementation moved to ADC_TRANSFER_16BITS macro */ - return data; -} -#endif - /*============================================================================== * PUBLIC FUNCTIONS - INITIALIZATION *============================================================================*/ dr_adc_err_t dr_adc_init(void) { - ADC_LOG("ADC121S051 init..."); - - /* Initialize GPIO */ - adc_gpio_init(); - - /* Ensure idle state (SCLK idle HIGH per Figure 4) */ - cs_high(); - sclk_high(); - - /* Wait for power stabilization */ - nrf_delay_us(10); + nrfx_err_t err; - /* Dummy read to wake up and clear stale data */ + ADC_LOG("ADC121S051 init (HW SPI, SPIM3)..."); + + nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG; + config.sck_pin = DR_ADC_PIN_SCLK; /* P0.14 */ + config.mosi_pin = NRFX_SPIM_PIN_NOT_USED;/* not used (read-only ADC) */ + config.miso_pin = DR_ADC_PIN_SDATA; /* P0.15 */ + config.ss_pin = DR_ADC_PIN_CS; /* P0.19 */ + config.frequency = NRF_SPIM_FREQ_16M; + config.mode = NRF_SPIM_MODE_3; /* CPOL=1 (idle HIGH), CPHA=1 (sample on rising - ADC presents on falling, stable on rising) */ + config.bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST; + config.ss_active_high = false; /* CS active LOW */ + + err = nrfx_spim_init(&m_spim, &config, NULL, NULL); + ADC_LOG("SPIM3 init result: 0x%08X (%s)", err, (err == NRFX_SUCCESS) ? "OK" : "FAIL"); + if (err != NRFX_SUCCESS) { - uint16_t dummy_data = 0; - register uint32_t dummy_reg = 0; - ADC_PREHEAT(dummy_data, dummy_reg); - NRF_P0->OUTCLR = CS_MASK; - __NOP(); __NOP(); __NOP(); __NOP(); /* tSU: CS setup time */ - ADC_TRANSFER_16BITS(dummy_data, dummy_reg); + return DR_ADC_ERR_NOT_INIT; } - /* Quiet time between conversions */ - __NOP(); __NOP(); __NOP(); __NOP(); - + /* Wait for ADC power stabilization */ + nrf_delay_us(10); + + /* Dummy read to wake up ADC and clear stale conversion data */ + (void)spim_read_raw(); + m_initialized = true; - - ADC_LOG("ADC121S051 ready (VREF=%dmV)", m_vref_mv); - + + ADC_LOG("ADC121S051 ready (VREF=%dmV, HW SPI)", m_vref_mv); + return DR_ADC_OK; } @@ -338,15 +169,10 @@ void dr_adc_uninit(void) { if (!m_initialized) return; - cs_high(); - sclk_high(); /* Idle HIGH per Figure 4 */ - - nrf_gpio_cfg_default(DR_ADC_PIN_CS); - nrf_gpio_cfg_default(DR_ADC_PIN_SCLK); - nrf_gpio_cfg_default(DR_ADC_PIN_SDATA); - + nrfx_spim_uninit(&m_spim); + m_initialized = false; - + ADC_LOG("ADC121S051 uninitialized"); } @@ -364,40 +190,7 @@ dr_adc_err_t dr_adc_read_raw(uint16_t *raw_value) if (!m_initialized) return DR_ADC_ERR_NOT_INIT; if (raw_value == NULL) return DR_ADC_ERR_INVALID_PARAM; - uint16_t data = 0; - register uint32_t in_reg = 0; - - /* Critical section for precise timing */ - __disable_irq(); - - /* Preheat pipeline before timing-critical section */ - ADC_PREHEAT(data, in_reg); - - /* CS LOW - starts conversion, samples VIN on this edge */ - NRF_P0->OUTCLR = CS_MASK; - __NOP(); /* Match timing with other SCLK cycles */ - - /* Transfer 16 bits + CS HIGH (macro for consistent timing) */ - ADC_TRANSFER_16BITS(data, in_reg); - - __enable_irq(); - - /* - * Extract 12-bit data from 16-bit frame: - * Bit 15-13: Leading zeros (Z2, Z1, Z0) - must be 0 - * Bit 12-1: Data bits (D11-D0) - * Bit 0: Don't care (trailing) - * - * Result = (data >> 1) & 0x0FFF - */ - - /* Verify leading zeros (upper 3 bits should be 0) */ - if ((data & 0xE000) != 0) - { - ADC_LOG("ADC frame error: leading zeros invalid (0x%04X)", data); - } - - *raw_value = (data >> 1) & 0x0FFF; + *raw_value = spim_read_raw(); return DR_ADC_OK; } @@ -444,37 +237,32 @@ dr_adc_err_t dr_adc_read_averaged(dr_adc_result_t *result, uint16_t num_samples) dr_adc_err_t dr_adc_capture_echo(uint16_t *buffer, uint16_t num_samples) { + volatile uint8_t rx_buf[2]; + uint32_t cs_mask = (1UL << DR_PIN_NUM(DR_ADC_PIN_CS)); + uint16_t raw16; + uint16_t i; + NRF_SPIM_Type *spim = m_spim.p_reg; + if (!m_initialized) return DR_ADC_ERR_NOT_INIT; if (buffer == NULL) return DR_ADC_ERR_INVALID_PARAM; if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) return DR_ADC_ERR_INVALID_PARAM; - uint16_t data = 0; - register uint32_t in_reg = 0; + spim->RXD.MAXCNT = 2; - /* Disable interrupts for continuous high-speed capture */ - __disable_irq(); - - /* Preheat pipeline once before the loop */ - ADC_PREHEAT(data, in_reg); - - for (uint16_t i = 0; i < num_samples; i++) + for (i = 0; i < num_samples; i++) { - data = 0; /* Reset for each sample */ + NRF_P0->OUTCLR = cs_mask; + spim->RXD.PTR = (uint32_t)rx_buf; + spim->EVENTS_END = 0; + spim->TASKS_START = 1; + while (!spim->EVENTS_END) {} + NRF_P0->OUTSET = cs_mask; - /* CS LOW - starts conversion, samples VIN */ - NRF_P0->OUTCLR = CS_MASK; - __NOP(); /* Match timing with other SCLK cycles */ - - /* Transfer 16 bits + CS HIGH (macro for consistent timing) */ - ADC_TRANSFER_16BITS(data, in_reg); - - /* Store 12-bit value */ - buffer[i] = (data >> 1) & 0x0FFF; + raw16 = ((uint16_t)rx_buf[0] << 8) | rx_buf[1]; + buffer[i] = (raw16 >> 1) & 0x0FFF; } - __enable_irq(); - return DR_ADC_OK; } @@ -1485,6 +1273,9 @@ dr_adc_err_t maa_async_start(uint8_t freq_option, uint16_t delay_us, uint16_t num_samples, uint8_t cycles, uint16_t averaging, uint8_t *ble_buffer) { + dr_adc_err_t err; + uint8_t ch; + if (g_maa_ctx.state != MAA_ASYNC_IDLE) { ADC_LOG("maa_async_start: busy"); @@ -1510,16 +1301,25 @@ dr_adc_err_t maa_async_start(uint8_t freq_option, uint16_t delay_us, ADC_LOG("maa_async_start: freq=%u delay=%u samples=%u cycles=%u avg=%u", freq_option, delay_us, num_samples, g_maa_ctx.cycles, g_maa_ctx.averaging); - /* Capture CH0 */ + /* 전채널 캡처: BLE 전송 없이 6채널 전부 캡처 완료 후 TX 시작 */ g_maa_ctx.state = MAA_ASYNC_CAPTURING; - dr_adc_err_t err = maa_async_capture_channel(0); - - if (err != DR_ADC_OK) + for (ch = 0; ch < MAA_NUM_CHANNELS; ch++) { - ADC_LOG("maa_async_start: CH0 capture failed (%d)", err); - maa_async_send_completion(0xFFF0); - return err; + err = maa_async_capture_channel(ch); + if (err != DR_ADC_OK) + { + //ADC_LOG("maa_async_start: CH%u capture failed (%d)", ch, err); + if (g_plat.log) g_plat.log("[maa] maa_async_start: CH%u capture failed (%d)", ch, err); + maa_async_send_completion(0xFFF0 | ch); + return err; + } } + //ADC_LOG("maa_async_start: all channels captured, starting TX"); + if (g_plat.log) g_plat.log("[maa] maa_async_start: all channels captured, starting TX"); + + /* 캡처 완료 → 피에조 전원 OFF (BLE 전송 중 불필요) */ + dr_piezo_power_off(); + g_maa_ctx.auto_powered = false; /* 이미 껐으므로 완료 후 중복 OFF 방지 */ /* Send CH0 header - this will trigger TX_RDY for subsequent packets */ maa_async_send_header(); @@ -1551,17 +1351,8 @@ bool maa_async_on_tx_ready(void) } else { - /* Capture next channel */ + /* 이미 전채널 캡처됨, 바로 헤더 전송 */ g_maa_ctx.state = MAA_ASYNC_CAPTURING; - dr_adc_err_t err = maa_async_capture_channel(g_maa_ctx.current_ch); - - if (err != DR_ADC_OK) - { - ADC_LOG("maa_async: CH%u capture failed", g_maa_ctx.current_ch); - maa_async_send_completion(0xFFF0 | g_maa_ctx.current_ch); - return false; - } - /* Send header for new channel */ maa_async_send_header(); } } @@ -1589,6 +1380,11 @@ bool maa_async_is_busy(void) return (g_maa_ctx.state != MAA_ASYNC_IDLE); } +void maa_async_set_pre_capture_all(bool on) +{ + g_maa_ctx.pre_capture_all = on; +} + maa_async_state_t maa_async_get_state(void) { return g_maa_ctx.state; @@ -1612,3 +1408,4 @@ void maa_async_set_on_complete(void (*cb)(void)) g_maa_ctx.on_complete_cb = cb; } + diff --git a/pc_firm/dr_adc121s051/dr_adc121s051.h b/pc_firm/dr_adc121s051/dr_adc121s051.h index a298516..a9f4f16 100644 --- a/pc_firm/dr_adc121s051/dr_adc121s051.h +++ b/pc_firm/dr_adc121s051/dr_adc121s051.h @@ -455,6 +455,7 @@ typedef struct { uint16_t total_packets; /**< Total packets for current channel */ uint16_t data_packets; /**< Data packets for current channel */ bool auto_powered; /**< true: 자동 전원 ON → 완료 후 OFF */ + bool pre_capture_all; /**< true: 전채널 캡처 완료 후 일괄 전송 (mbb용) */ void (*on_complete_cb)(void); /**< 비동기 캡처 완료 후 호출될 콜백 (NULL이면 미사용) */ } maa_async_ctx_t; @@ -507,6 +508,8 @@ void maa_async_abort(void); * @brief 자동 전원 플래그 설정 (완료 후 자동 power off) */ void maa_async_set_auto_power(bool on); +void maa_async_set_pre_capture_all(bool on); + /** * @brief 비동기 캡처 완료 콜백 설정 diff --git a/pc_firm/parser.c b/pc_firm/parser.c index 76679fe..2991158 100644 --- a/pc_firm/parser.c +++ b/pc_firm/parser.c @@ -408,6 +408,8 @@ static int Cmd_mpc(const ParsedCmd *cmd); /* mpc? 피에조 버스트 발생 ( static int Cmd_mec(const ParsedCmd *cmd); /* mec? 피에조 버스트 + 에코 캡처 (16비트 원시) */ static int Cmd_maa(const ParsedCmd *cmd); /* maa? 6채널 전체 캡처 (비동기 전송) */ static int Cmd_mbb(const ParsedCmd *cmd); /* mbb? 6채널 캡처 + 센서 측정 (배터리 + 온도 + IMU) */ +static int Cmd_mcf(const ParsedCmd *cmd); /* mcf? 피에조 파라미터 읽기 (FDS) */ +static int Cmd_mcs(const ParsedCmd *cmd); /* mcs? 피에조 파라미터 쓰기 (FDS) */ /* ---- 명령 테이블 ---- */ @@ -451,6 +453,8 @@ static CmdEntry g_cmd_table[] = { { "mec?", true, Cmd_mec }, { "maa?", true, Cmd_maa }, { "mbb?", true, Cmd_mbb }, + { "mcf?", true, Cmd_mcf }, + { "mcs?", true, Cmd_mcs }, }; /* 명령 테이블 엔트리 수 (컴파일 타임 계산) */ @@ -838,12 +842,10 @@ 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개 파라미터 순서대로 추출 (데이터 부족 시 기본값 유지) */ @@ -884,13 +886,20 @@ static int Cmd_mec(const ParsedCmd *cmd) if (g_plat.log && g_log_enable) { g_plat.log("[Cmd_mec] result=%d\r\n", err); + + /* raw 데이터 덤프 */ + const uint16_t *buf = dr_adc_get_echo_buffer(); + g_plat.log("[mec] CH%u raw (%u samples):\r\n", piezo_ch, num_samples); + for (uint16_t i = 0; i < num_samples; i++) { + g_plat.log("%u ", buf[i]); + if ((i + 1) % 16 == 0) g_plat.log("\r\n"); + } + if (num_samples % 16 != 0) g_plat.log("\r\n"); } - /* 자동으로 켰으면 완료 후 전원 OFF */ - if (auto_powered) { - if (g_plat.log) g_plat.log("[Cmd_mec] TX/RX Active -> Sleep\r\n"); - dr_piezo_power_off(); - } + /* 측정 완료 후 무조건 전원 OFF */ + if (g_plat.log) g_plat.log("[Cmd_mec] TX/RX Active -> Sleep\r\n"); + dr_piezo_power_off(); return 1; } @@ -1007,14 +1016,11 @@ 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) { + /* 피에조 전원: OFF일 경우 ON → 완료 후 OFF */ + if (!dr_piezo_is_power_on()) + { if (g_plat.log) g_plat.log("[Cmd_maa] TX/RX Sleep -> Active\r\n"); dr_piezo_system_init(); - auto_powered = true; } /*======================================================================= @@ -1038,12 +1044,13 @@ static int Cmd_maa(const ParsedCmd *cmd) 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(); + dr_piezo_power_off(); return 1; } - /* 자동 전원 플래그 설정 → 비동기 완료 시 자동 OFF */ - maa_async_set_auto_power(auto_powered); + /* 측정 완료 시 무조건 전원 OFF */ + //maa_async_set_auto_power(true); + //dr_piezo_power_off(); /* 즉시 반환 → 비동기 전송 진행 중 */ /* 전체 완료 시 상태 머신이 "raa:" 응답 + 자동 power off 처리 */ @@ -1086,7 +1093,7 @@ static void all_sensors(void) dr_sd_delay_ms(50); /* SAADC 콜백 완료 대기 */ info4 = false; - + /* rbb: 패킷 조립 및 전송 : [TAG 4B] [배터리 2B] [IMU 12B] [온도 2B] = 20바이트 = 10워드 */ uint8_t *buf = ble_bin_buffer; @@ -1094,7 +1101,8 @@ static void all_sensors(void) buf[4] = (uint8_t)(info_batt & 0xFF); buf[5] = (uint8_t)(info_batt >> 8); - for (int i = 0; i < 6; i++) { + for (int i = 0; i < 6; i++) + { buf[6 + i * 2] = (uint8_t)(info_imu[i] & 0xFF); buf[6 + i * 2 + 1] = (uint8_t)(info_imu[i] >> 8); } @@ -1119,11 +1127,18 @@ static void all_sensors(void) * 2) 각 채널(CH0~CH5): reb: [헤더] → red: [데이터...] * 3) 캡처 완료: raa: [상태] */ + static int Cmd_mbb(const ParsedCmd *cmd) { dr_adc_err_t err; - /* 파라미터 5개 수신 시 FDS에 저장 — 디버그 중 비활성화 + if (g_plat.log) g_plat.log("[Cmd_mbb] params updated: freq=%u cyc=%u avg=%u delay=%u samples=%u\r\n", + m_config.piezo_freq_option, m_config.piezo_cycles, + m_config.piezo_averaging, m_config.piezo_delay_us, + m_config.piezo_num_samples); + + // 파라미터 5개 수신 시 FDS에 저장 — 디버그 중 비활성화 + /* if (cmd->data_len >= 10) { uint16_t freq, cycles, averaging, delay_us, num_samples; dr_get_u16(cmd, 0, &freq); @@ -1138,23 +1153,19 @@ static int Cmd_mbb(const ParsedCmd *cmd) m_config.piezo_delay_us = delay_us; m_config.piezo_num_samples = num_samples; config_save(); - - if (g_plat.log) g_plat.log("[Cmd_mbb] params updated: freq=%u cyc=%u avg=%u delay=%u samples=%u\r\n", - m_config.piezo_freq_option, m_config.piezo_cycles, - m_config.piezo_averaging, m_config.piezo_delay_us, - m_config.piezo_num_samples); - } - */ + }*/ all_sensors(); + dr_sd_delay_ms(20); /* SoftDevice 이벤트 정리 후 캡처 시작 */ + if (maa_async_is_busy()) { dr_ble_return_1("raa:", 0xFFFE); return 1; } - /* 피에조 전원: OFF일 경우 ON → 완료 후 OFF */ + /* 피에조 전원: OFF일 경우 ON */ if (!dr_piezo_is_power_on()) { if (g_plat.log) g_plat.log("[Cmd_mbb] TX/RX Sleep -> Active\r\n"); @@ -1166,6 +1177,9 @@ static int Cmd_mbb(const ParsedCmd *cmd) m_config.piezo_averaging, m_config.piezo_delay_us, m_config.piezo_num_samples); + /* 전채널 선캡처 모드: 모든 채널 캡처 완료 후 BLE 전송 */ + maa_async_set_pre_capture_all(true); + /* 비동기 6채널 캡처 시작 (m_config 파라미터 사용) */ err = maa_async_start( m_config.piezo_freq_option, @@ -1185,8 +1199,81 @@ static int Cmd_mbb(const ParsedCmd *cmd) return 1; } - /* 완료 시 무조건 전원 OFF (전체 측정 사이클) */ - maa_async_set_auto_power(true); + /* 완료 시 자동 전원 OFF */ + //maa_async_set_auto_power(true); + + return 1; +} + + +/** + * @brief mcf? - 피에조 파라미터 읽기 (FDS) + * + * 응답: rcf: [freq(2)] [cycles(2)] [avg(2)] [delay_us(2)] [num_samples(2)] = 14바이트 = 7워드 + */ +static int Cmd_mcf(const ParsedCmd *cmd) +{ + (void)cmd; + + uint8_t *buf = ble_bin_buffer; + buf[0] = 'r'; buf[1] = 'c'; buf[2] = 'f'; buf[3] = ':'; + buf[4] = m_config.piezo_freq_option; buf[5] = 0; + buf[6] = m_config.piezo_cycles; buf[7] = 0; + buf[8] = (uint8_t)(m_config.piezo_averaging & 0xFF); buf[9] = (uint8_t)(m_config.piezo_averaging >> 8); + buf[10] = (uint8_t)(m_config.piezo_delay_us & 0xFF); buf[11] = (uint8_t)(m_config.piezo_delay_us >> 8); + buf[12] = (uint8_t)(m_config.piezo_num_samples & 0xFF); buf[13] = (uint8_t)(m_config.piezo_num_samples >> 8); + dr_binary_tx_safe(buf, 7); /* 14바이트 = 7워드 */ + + if (g_plat.log) g_plat.log("[Cmd_mcf] freq=%u cyc=%u avg=%u delay=%u samples=%u\r\n", + m_config.piezo_freq_option, m_config.piezo_cycles, + m_config.piezo_averaging, m_config.piezo_delay_us, + m_config.piezo_num_samples); + + return 1; +} + + +/** + * @brief mcs? - 피에조 파라미터 쓰기 (FDS) + * + * 파라미터: [freq(2)] [cycles(2)] [avg(2)] [delay_us(2)] [num_samples(2)] = 10바이트 + * 응답: rcs: [저장된 5개 값] = 14바이트 = 7워드 + */ +static int Cmd_mcs(const ParsedCmd *cmd) +{ + if (cmd->data_len < 10) { + if (g_plat.log) g_plat.log("[Cmd_mcs] missing params (data_len=%u)\r\n", cmd->data_len); + dr_ble_return_1("rcs:", 0xFFFF); + return 1; + } + + uint16_t freq, cycles, averaging, delay_us, num_samples; + dr_get_u16(cmd, 0, &freq); + dr_get_u16(cmd, 1, &cycles); + dr_get_u16(cmd, 2, &averaging); + dr_get_u16(cmd, 3, &delay_us); + dr_get_u16(cmd, 4, &num_samples); + + m_config.piezo_freq_option = (uint8_t)freq; + m_config.piezo_cycles = (uint8_t)cycles; + m_config.piezo_averaging = averaging; + m_config.piezo_delay_us = delay_us; + m_config.piezo_num_samples = num_samples; + config_save(); + + if (g_plat.log) g_plat.log("[Cmd_mcs] saved: freq=%u cyc=%u avg=%u delay=%u samples=%u\r\n", + m_config.piezo_freq_option, m_config.piezo_cycles, + m_config.piezo_averaging, m_config.piezo_delay_us, + m_config.piezo_num_samples); + + uint8_t *buf = ble_bin_buffer; + buf[0] = 'r'; buf[1] = 'c'; buf[2] = 's'; buf[3] = ':'; + buf[4] = m_config.piezo_freq_option; buf[5] = 0; + buf[6] = m_config.piezo_cycles; buf[7] = 0; + buf[8] = (uint8_t)(m_config.piezo_averaging & 0xFF); buf[9] = (uint8_t)(m_config.piezo_averaging >> 8); + buf[10] = (uint8_t)(m_config.piezo_delay_us & 0xFF); buf[11] = (uint8_t)(m_config.piezo_delay_us >> 8); + buf[12] = (uint8_t)(m_config.piezo_num_samples & 0xFF); buf[13] = (uint8_t)(m_config.piezo_num_samples >> 8); + dr_binary_tx_safe(buf, 7); /* 14바이트 = 7워드 */ return 1; } @@ -1319,8 +1406,6 @@ static int Cmd_mqz(const ParsedCmd *cmd) return 1; } -/* 2026-03-17: mxz?, myz? 삭제 */ - /*============================================================================== * IMU: 6축 원시 데이터 (단발 읽기) *============================================================================*/ 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 30671df..1c6c362 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 @@ -3673,7 +3673,7 @@ #ifndef NRFX_SPIM3_ENABLED -#define NRFX_SPIM3_ENABLED 0 +#define NRFX_SPIM3_ENABLED 1 #endif // NRFX_SPIM_EXTENDED_ENABLED - Enable extended SPIM features