/******************************************************************************* * @file echo_adc.c * @brief ADC121S051 echo capture driver using nrfx SPIM3 ******************************************************************************/ #include #include #include #include #include #include #include #include "echo_adc.h" #include "debug_print.h" #define ECHO_SCLK_NODE DT_NODELABEL(echo_sclk) #define ECHO_MISO_NODE DT_NODELABEL(echo_miso) #define ECHO_CS_NODE DT_NODELABEL(echo_cs) /* SPIM END event 대기 최대 반복 횟수 */ #define ECHO_ADC_SPIM_END_TIMEOUT_LOOPS 100000U /* ADC SPI 핀 */ static const struct gpio_dt_spec echo_sclk = GPIO_DT_SPEC_GET(ECHO_SCLK_NODE, gpios); static const struct gpio_dt_spec echo_miso = GPIO_DT_SPEC_GET(ECHO_MISO_NODE, gpios); static const struct gpio_dt_spec echo_cs = GPIO_DT_SPEC_GET(ECHO_CS_NODE, gpios); /* SPIM3 레지스터 인스턴스 */ static nrfx_spim_t echo_spim = NRFX_SPIM_INSTANCE(NRF_SPIM3); static bool echo_adc_initialized; /* ADC 원본 수신 버퍼 */ static uint8_t raw_capture[ECHO_ADC_MAX_SAMPLES * 2]; static NRF_GPIO_Type *gpio_reg_for_spec(const struct gpio_dt_spec *spec) { return (spec->port == DEVICE_DT_GET(DT_NODELABEL(gpio1))) ? NRF_P1 : NRF_P0; } /* 포트 포함 절대 핀 번호 */ static uint32_t gpio_abs_pin(const struct gpio_dt_spec *spec) { uint32_t port = (spec->port == DEVICE_DT_GET(DT_NODELABEL(gpio1))) ? 1U : 0U; return NRF_GPIO_PIN_MAP(port, spec->pin); } /* SPIM RX 전송: ADC wake/dummy read용 nrfx 경로 */ static int echo_adc_transfer_rx(uint8_t *buf, size_t len) { nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_RX(buf, len); int err = nrfx_spim_xfer(&echo_spim, &xfer, 0); if (err != 0) { DBG_PRINTF("[ECHO] xfer fail err=%d\r\n", err); return -EIO; } return 0; } /* 12비트 샘플 추출 */ static void echo_adc_unpack_samples(uint16_t *samples, uint16_t num_samples) { for (uint16_t i = 0; i < num_samples; i++) { uint16_t raw16 = ((uint16_t)raw_capture[i * 2U] << 8) | (uint16_t)raw_capture[i * 2U + 1U]; samples[i] = (raw16 >> 1) & 0x0FFF; } } /* 샘플마다 CS를 직접 제어하고 SPIM RX 2바이트를 수행 */ static int echo_adc_capture_fast(uint16_t *samples, uint16_t num_samples) { NRF_SPIM_Type *spim = echo_spim.p_reg; NRF_GPIO_Type *cs_port = gpio_reg_for_spec(&echo_cs); uint32_t cs_mask = BIT(echo_cs.pin); /* SPIM 직접 제어 */ nrf_spim_enable(spim); spim->RXD.MAXCNT = 2U; for (uint16_t i = 0; i < num_samples; i++) { spim->RXD.PTR = (uint32_t)&raw_capture[i * 2U]; spim->EVENTS_END = 0; cs_port->OUTCLR = cs_mask; spim->TASKS_START = 1; /* 전송 완료 대기: SPIM이 꼬인 경우 IRQ lock 상태로 영구 정지하지 않도록 timeout 처리 */ uint32_t timeout = ECHO_ADC_SPIM_END_TIMEOUT_LOOPS; while (!spim->EVENTS_END) { if (--timeout == 0U) { spim->EVENTS_STOPPED = 0; spim->TASKS_STOP = 1; cs_port->OUTSET = cs_mask; spim->EVENTS_END = 0; DBG_PRINTF("[ECHO] SPIM END timeout sample=%u\r\n", i); return -ETIMEDOUT; } } cs_port->OUTSET = cs_mask; } echo_adc_unpack_samples(samples, num_samples); return 0; } int echo_adc_init(void) { // 중복 초기화 방지 if (echo_adc_initialized || nrfx_spim_init_check(&echo_spim)) { echo_adc_initialized = true; return 0; } // GPIO 준비 확인 const struct gpio_dt_spec *pins[] = { &echo_sclk, &echo_miso, &echo_cs }; for (size_t i = 0; i < ARRAY_SIZE(pins); i++) { if (!device_is_ready(pins[i]->port)) { DBG_PRINTF("[ECHO] GPIO device not ready (%d)\r\n", (int)i); return -ENODEV; } } /* ADC SPI 설정 */ nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG( gpio_abs_pin(&echo_sclk), NRF_SPIM_PIN_NOT_CONNECTED, gpio_abs_pin(&echo_miso), gpio_abs_pin(&echo_cs)); config.frequency = NRFX_MHZ_TO_HZ(16); config.mode = NRF_SPIM_MODE_3; config.bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST; config.orc = 0x00; config.miso_pull = NRF_GPIO_PIN_NOPULL; config.ss_active_high = false; nrfx_err_t err = nrfx_spim_init(&echo_spim, &config, NULL, NULL); if (err != 0) { DBG_PRINTF("[ECHO] init fail err=%d\r\n", err); return -EIO; } echo_adc_initialized = true; //DBG_PRINTF("[ECHO] SPIM3 init OK\r\n"); return 0; } int echo_adc_wake(void) { /* 더미 읽기 웨이크업 */ int err = echo_adc_init(); if (err) { return err; } uint8_t discard[2]; return echo_adc_transfer_rx(discard, sizeof(discard)); } int echo_adc_capture(uint16_t *samples, uint16_t num_samples) { // 입력값 검사 if (samples == NULL) { return -EINVAL; } if ((num_samples == 0U) || (num_samples > ECHO_ADC_MAX_SAMPLES)) { return -EINVAL; } int err = echo_adc_init(); if (err) { return err; } return echo_adc_capture_fast(samples, num_samples); }