Bit-bang SPI(8MHz)에서 HW SPI(SPIM3, 16MHz)로 변경

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jhChun
2026-03-19 18:04:04 +09:00
parent ce81cd949f
commit 1f1c4b0b25
4 changed files with 226 additions and 341 deletions

View File

@@ -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 <string.h> /* 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 (04095)
*
* @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;
}

View File

@@ -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 비동기 캡처 완료 콜백 설정

View File

@@ -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축 원시 데이터 (단발 읽기)
*============================================================================*/