# ADC 캡처 데이터가 전부 0으로 나온 상황 정리 작성일: 2026-06-11 ## 배경 ADC echo 파형이 그래프 앞쪽으로 압축되어 보이는 문제가 있었다. 원인 후보로 `src/drivers/echo_adc/echo_adc.c`에서 2바이트 샘플마다 `nrfx_spim_xfer()`를 한 번씩 호출하는 구조가 지목되었다. 이 구조는 샘플마다 드라이버 호출/대기/CS 처리 오버헤드가 붙기 때문에 실제 샘플 간격이 예상보다 커질 수 있다. 그래서 샘플 간격을 줄이기 위해, 기존 레퍼런스 펌웨어처럼 SPIM 레지스터를 직접 만지는 hot loop 방식으로 바꾸는 실험을 했다. ## 현재 정상 동작 기준 현재 정상 동작하는 기준 코드는 샘플 1개마다 아래 경로를 사용한다. ```c nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_RX(buf, len); nrfx_spim_xfer(&echo_spim, &xfer, 0); ``` 그리고 `echo_adc_init()`에서는 CS 핀을 SPIM의 hardware SS 핀으로 넘긴다. ```c 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)); ``` 실패한 실험 뒤에는 이 정상 동작 기준으로 원복했다. ## 실패 실험 1: SPIM SS를 끊고 CS를 GPIO로 수동 제어 `echo_adc_init()`에서 SPIM이 CS 핀을 소유하지 않도록 바꿨다. ```c NRF_SPIM_PIN_NOT_CONNECTED ``` 즉 원래 있던 아래 설정을 제거했다. ```c gpio_abs_pin(&echo_cs) ``` 그 뒤 CS를 GPIO 출력으로 직접 설정하고, `nrfx_spim_xfer()`와 direct capture loop 양쪽에서 수동으로 CS를 내리고 올렸다. ```c cs_port->OUTCLR = cs_mask; nrfx_spim_xfer(&echo_spim, &xfer, 0); cs_port->OUTSET = cs_mask; ``` 캡처 루프도 아래처럼 SPIM 레지스터 직접 접근 방식으로 바꿨다. ```c spim->RXD.MAXCNT = 2U; for (...) { cs_port->OUTCLR = cs_mask; spim->RXD.PTR = (uint32_t)&raw_capture[i * 2U]; spim->EVENTS_END = 0; spim->TASKS_START = 1; while (!spim->EVENTS_END) { } cs_port->OUTSET = cs_mask; } ``` 결과/위험: - ADC 데이터가 전부 0으로 나올 수 있다. - 가능한 원인은 CS/SPIM 소유권 불일치, RX-only direct start가 `nrfx_spim_xfer()`와 다르게 동작하는 문제, 또는 nrfx가 내부적으로 준비하던 SPIM 상태가 빠진 문제다. - 이 단순한 "SS 끊기 + direct loop" 패치는 그대로 쓰면 안 된다. ## 실패 실험 2: Hardware SS를 유지한 채 같은 CS를 수동 토글 `echo_cs`는 계속 SPIM hardware SS 핀으로 설정해둔 상태에서, `echo_adc_capture()` 내부만 direct register loop로 바꿨다. 예시: ```c NRF_SPIM_Type *spim = echo_spim.p_reg; uint32_t cs_mask = BIT(echo_cs.pin); spim->RXD.MAXCNT = 2U; for (...) { NRF_P0->OUTCLR = cs_mask; spim->RXD.PTR = (uint32_t)&raw_capture[i * 2U]; spim->EVENTS_END = 0; spim->TASKS_START = 1; while (!spim->EVENTS_END) { } NRF_P0->OUTSET = cs_mask; } ``` 결과/위험: - 이 경우도 ADC 데이터가 전부 0으로 나올 수 있다. - 이유는 hardware SS가 켜진 상태에서 펌웨어가 같은 CS 핀을 직접 `NRF_P0->OUTCLR/OUTSET`으로 건드리기 때문이다. - 즉 SPIM hardware SS 제어와 수동 GPIO CS 제어가 같은 핀에서 겹쳐 ADC 프레임 타이밍이 깨질 수 있다. - hardware SS를 켠 상태에서 같은 CS 핀을 수동으로 토글하면 안 된다. ## 핵심 정리 레퍼런스 펌웨어의 direct SPIM loop를 이 Zephyr/nrfx 포팅 코드에 내부 루프만 바꿔서 그대로 넣으면 안 된다. 더 빠른 캡처 경로가 필요하면, 아래를 포함한 별도 low-level SPIM 경로로 제대로 구성해야 한다. - CS 소유권을 명확히 정하기: hardware SS 또는 manual GPIO 중 하나만 사용 - SPIM PSEL 상태 확인 - SCLK 생성을 보장하기 위한 RXD/TXD 설정 확인 - `RXD.MAXCNT = 2` - `EVENTS_END` 클리어 및 polling - CS 타이밍 검증 그 전까지는 정상 동작이 확인된 `nrfx_spim_xfer()` 경로를 유지한다.