Files
VesiScan-Basic_Zephyr/src/drivers/echo_adc/echo_adc.c
T
2026-07-06 11:14:35 +09:00

195 lines
4.8 KiB
C

/*******************************************************************************
* @file echo_adc.c
* @brief ADC121S051 echo capture driver using nrfx SPIM3
******************************************************************************/
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/devicetree.h>
#include <zephyr/sys/util.h>
#include <nrfx_spim.h>
#include <hal/nrf_spim.h>
#include <errno.h>
#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);
}