Skip to content

nRF 플랫폼 가이드

Nordic Semiconductor nRF 마이크로컨트롤러용 Xylolabs SDK 통합 가이드이다.


지원 대상

MCU 코어 클럭 RAM 연결성 오디오 지원
nRF52840 Cortex-M4F 64 MHz 256 KB BLE 5.0, 802.15.4 2ch XAP @48kHz (DSP 보조)
nRF9160 Cortex-M33 64 MHz 256 KB LTE-M / NB-IoT 센서 전용

하드웨어 구성

SPI 센서 연결 — LIS2DH12 가속도계 (nRF52840)

SDK 예제는 SPI로 연결된 LIS2DH12 3축 가속도계를 사용한다. Zephyr 디바이스 트리 별칭 st_lis2dh가 이 장치에 매핑되며, 드라이버가 SPI 프레이밍과 레지스터 접근을 처리한다.

nRF52840 GPIO          LIS2DH12
──────────────────     ─────────────────────
P0.27 (SPI_SCK)  ───── SCL / SPC
P0.26 (SPI_MOSI) ───── SDA / SDI
P0.06 (SPI_MISO) ───── SDO
P0.08 (GPIO CS)  ───── CS (active low)
3V3              ───── VDD / VDDIO
GND              ───── GND
GND              ───── INT1 (미사용 시 GND에 연결)

LIS2DH12는 I2C도 지원한다 (SA0 low → 0x18, SA0 high → 0x19). DTS 오버레이에서 compatible 속성을 알맞게 지정하면 된다.

/* boards/nrf52840dk_nrf52840.overlay */
&spi1 {
    status = "okay";
    cs-gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;

    lis2dh12: lis2dh12@0 {
        compatible = "st,lis2dh12", "st,lis2dh";
        reg = <0>;
        spi-max-frequency = <8000000>;
        label = "LIS2DH12";
    };
};

센서 버스

버스 핀 (nRF52840-DK) 주요 장치
SPI1 P0.27/P0.26/P0.06 + CS LIS2DH12, MAX31865
I2C0 (TWI0) P0.26 (SDA), P0.27 (SCL) BME280, MPU6050
ADC (SAADC) AIN0–AIN7 아날로그 센서, 배터리 전압

전송 모드

BLE GATT (nRF52840)

XMBP 패킷은 BLE GATT 노티피케이션 형태로 게이트웨이 장치(ESP32, 라즈베리파이 등)에 전송되며, 게이트웨이가 WiFi 또는 이더넷을 통해 Xylolabs API로 중계한다. nRF52840은 인터넷에 직접 연결하지 않는다.

nRF52840 (이 장치)
  → BLE GATT 노티피케이션 (XMBP 패킷, 2M PHY)
    → 게이트웨이 (ESP32 / RPi)
      → HTTP POST → Xylolabs API

주요 BLE 파라미터:

파라미터 기본값 범위
연결 인터벌 15 ms (12 × 1.25 ms) 7.5 ms – 4 s
MTU 247 바이트 23 – 247 바이트
PHY 2M 1M / 2M / Coded
TX 출력 0 dBm −20 dBm ~ +8 dBm

협상된 MTU보다 큰 XMBP 패킷은 플랫폼 레이어에서 시퀀스 헤더를 붙여 여러 노티피케이션으로 자동 분할한다. 게이트웨이는 전달 전에 이를 재조립한다.

초기화 시 BLE 전송 설정:

// Rust (Embassy) - 권장
use nrf_softdevice::ble::{gatt_server, peripheral};
use nrf_softdevice::Softdevice;

// 초기화 시 BLE 전송 설정
let sd = Softdevice::enable(&softdevice_config());
let server = XylolabsGattServer::new(sd).unwrap();

// 15ms 연결 인터벌, MTU 247로 광고 시작
let adv_config = peripheral::Config {
    interval: 160, // 100ms 광고 인터벌
    ..Default::default()
};
let conn = peripheral::advertise_connectable(sd, adv_config, &adv_data).await.unwrap();
gatt_server::run(&conn, &server, |event| { /* XMBP 노티피케이션 처리 */ }).await;
Legacy C equivalent
platform_nrf_config_t cfg = {
    .transport      = NRF_TRANSPORT_BLE_GATT,
    .wdt_timeout_ms = 8000,
    .conn_interval  = 12,   /* 15 ms */
    .mtu            = 247,
    .apn            = NULL,
};
platform_nrf_init(&platform, &cfg);
platform_nrf_ble_start_advertising();

LTE 모뎀 (nRF9160)

nRF9160 SiP는 Cortex-M33 애플리케이션 코어와 LTE 전용 모뎀을 하나의 다이에 통합한다. 애플리케이션 코어는 공유 메모리 + 인터럽트 방식의 IPC 링크를 통해 모뎀과 통신한다. Zephyr의 nrf_modem 라이브러리와 소켓 API가 모뎀 AT 명령을 추상화한다.

// Rust (Embassy) - 권장
use nrf_modem::{ConnectionPreference, SystemMode};

// LTE 모뎀 전송 설정 (nRF9160)
nrf_modem::init(SystemMode {
    lte_support: true,
    nbiot_support: true,
    gnss_support: false,
    preference: ConnectionPreference::Lte,
}).await.unwrap();

// APN 설정 및 연결
nrf_modem::send_at("AT+CGDCONT=1,\"IP\",\"your.apn.here\"").await.unwrap();
let socket = nrf_modem::TcpStream::connect(host, port).await.unwrap();
Legacy C equivalent
platform_nrf_config_t cfg = {
    .transport      = NRF_TRANSPORT_LTE_MODEM,
    .wdt_timeout_ms = 30000,
    .conn_interval  = 0,
    .mtu            = 1280,
    .apn            = "your.apn.here",  /* NULL이면 자동 설정 */
};
platform_nrf_init(&platform, &cfg);

SDK는 LTE 모뎀 위에서 Zephyr BSD 소켓 API(zsock_connect, zsock_send, zsock_recv)를 사용한다. 애플리케이션 코어 부하를 줄이기 위해 모뎀으로의 TLS 오프로딩도 지원한다.


Rust 빌드 (권장)

# 의존성 설치
rustup target add thumbv7em-none-eabihf  # nRF52840용
rustup target add thumbv8m.main-none-eabihf  # nRF9160용
cargo install probe-rs-tools

# 빌드
cd sdk/examples/nrf52840-ble
cargo build --release

# J-Link를 통해 플래시
cargo run --release

모든 nRF 예제는 sdk/examples/를 참고한다.


레거시 C 빌드 시스템

Zephyr / nRF Connect SDK (west)

SDK는 Zephyr 모듈로 패키징되어 있다. 애플리케이션의 west.yml에 추가한다:

# west.yml
manifest:
  projects:
    - name: xylolabs-sdk
      url: https://github.com/your-org/xylolabs-sdk
      path: modules/xylolabs
      revision: main

또는 Zephyr find_package 전에 CMakeLists.txt에서 로컬로 참조할 수 있다:

cmake_minimum_required(VERSION 3.20)

# SDK를 Zephyr 모듈로 추가
list(APPEND ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/../sdk/c/nrf)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(my_xylolabs_device)

target_sources(app PRIVATE
    src/main.c
    src/platform_impl.c
)
target_compile_definitions(app PRIVATE NRF_PLATFORM)

빌드 명령어

# nRF Connect SDK(ncs) 설치 및 west 워크스페이스 초기화
west init -m https://github.com/nrfconnect/sdk-nrf --mr v2.6.0 ncs
cd ncs && west update

# nRF52840-DK 빌드 (BLE 전송)
west build -b nrf52840dk/nrf52840 -- -DCONF_FILE=prj_ble.conf

# nRF9160-DK 빌드 (LTE 전송)
west build -b nrf9160dk/nrf9160/ns -- -DCONF_FILE=prj_lte.conf

# 플래시
west flash

# 시리얼 콘솔
minicom -D /dev/ttyACM0 -b 115200

Kconfig 설정 (prj.conf)

BLE용 (nRF52840):

# prj_ble.conf
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_GATT_NOTIFY_MULTIPLE=y
CONFIG_BT_L2CAP_TX_MTU=247
CONFIG_BT_BUF_ACL_TX_SIZE=251
CONFIG_BT_BUF_ACL_RX_SIZE=251

CONFIG_SPI=y
CONFIG_SENSOR=y
CONFIG_LIS2DH=y

CONFIG_XYLOLABS_TRANSPORT_BLE=y
CONFIG_XYLOLABS_AUDIO_CHANNELS=2

LTE용 (nRF9160):

# prj_lte.conf
CONFIG_NRF_MODEM_LIB=y
CONFIG_LTE_LINK_CONTROL=y
CONFIG_MODEM_INFO=y
CONFIG_NET_SOCKETS_OFFLOAD=y
CONFIG_NET_SOCKETS_POSIX_NAMES=y

CONFIG_XYLOLABS_TRANSPORT_LTE=y
CONFIG_XYLOLABS_AUDIO_CHANNELS=0


XAP 오디오 — nRF52840

지원 사양

nRF52840 Cortex-M4F는 단정밀도 FPU를 갖추고 64 MHz로 동작한다. DSP 보조 LC3 스펙트럼 연산 (XAP 내부 사용) 기준 처리 가능 사양은 다음과 같는다:

구성 CPU 사용률 지원 여부
2ch XAP @48kHz ~22% (64 MIPS 중 14 MIPS) 지원
4ch XAP @96kHz ~88% (64 MIPS 중 56 MIPS) 미지원 — 여유 없음
1ch XAP @16kHz ~5% 지원 (음성 품질)

nRF52840에서 4ch @96kHz는 지원하지 않는다. 64 MHz M4F는 BLE 스택 + 센서 관리 + XAP를 해당 속도로 동시에 처리할 여유가 없다. 4채널 배포에는 ESP32-S3(240 MHz, 듀얼 코어, 벡터 확장)를 사용한다.

PDM 마이크 입력

nRF52840은 MEMS 마이크용 PDM(Pulse-Density Modulation) 인터페이스를 내장한다. 디지털 PDM 마이크(예: MP34DT01-M)를 PDM 주변장치에 연결한다:

nRF52840 GPIO          PDM 마이크 (예: MP34DT01-M)
──────────────────     ──────────────────────────────────
P0.13 (PDM_CLK)  ───── CLK
P0.14 (PDM_DIN)  ───── DATA
3V3              ───── VDD
GND              ───── GND
GND              ───── SEL (좌 채널)
3V3              ───── SEL (우 채널, 두 번째 마이크)

Kconfig 설정:

CONFIG_AUDIO_PDM_NRFX=y
CONFIG_AUDIO_CODEC_LC3=y
CONFIG_XYLOLABS_AUDIO_CHANNELS=2
CONFIG_XYLOLABS_AUDIO_SAMPLE_RATE=48000

XAP 메모리 사용량 (2ch @48kHz)

영역 크기
LC3 인코더 상태 (2ch) ~2 KB
오디오 링 버퍼 16 KB
LC3 배치 버퍼 2 KB
오디오 총 사용량 ~20 KB

메모리 예산

nRF52840 — BLE 센서 비콘 (센서 전용)

영역 크기
BLE SoftDevice (S140) ~40 KB
Zephyr 커널 ~16 KB
XMBP 패킷 버퍼 4 KB
센서 샘플 버퍼 ~2 KB
애플리케이션 스택 ~4 KB
총 사용량 ~66 KB
여유 ~190 KB

센서 전용 비콘 권장 설정:

// Rust (Embassy) - 권장
// 센서 전용 비콘 설정 — build.rs 또는 Cargo.toml features에서 설정
const AUDIO_CHANNELS: usize = 0;
const AUDIO_RING_SIZE: usize = 0;
const XMBP_BUF_SIZE: usize = 4096;
const HTTP_BUF_SIZE: usize = 512;
const SENSOR_CHANNELS: usize = 4;
const MOTOR_CHANNELS: usize = 0;
const SENSOR_RATE_HZ: u32 = 50;
const META_BATCH_MS: u32 = 2000;

Legacy C equivalent
#define XYLOLABS_AUDIO_CHANNELS    0
#define XYLOLABS_AUDIO_RING_SIZE   0
#define XYLOLABS_XMBP_BUF_SIZE   4096
#define XYLOLABS_HTTP_BUF_SIZE     512
#define XYLOLABS_SENSOR_CHANNELS   4
#define XYLOLABS_MOTOR_CHANNELS    0
#define XYLOLABS_SENSOR_RATE_HZ   50
#define XYLOLABS_META_BATCH_MS   2000

nRF52840 — BLE 오디오 노드 (2ch XAP @48kHz)

영역 크기
BLE SoftDevice (S140) ~40 KB
Zephyr 커널 ~16 KB
LC3 인코더 상태 (2ch) ~2 KB
오디오 링 버퍼 16 KB
LC3 배치 버퍼 2 KB
XMBP 패킷 버퍼 8 KB
애플리케이션 스택 ~6 KB
총 사용량 ~90 KB
여유 ~166 KB

nRF9160 — LTE 센서 노드

영역 크기
nrf_modem 라이브러리 ~32 KB
Zephyr 커널 + 네트워크 스택 ~24 KB
XMBP 패킷 버퍼 4 KB
TLS 자격증명 저장소 ~4 KB
센서 버퍼 ~2 KB
애플리케이션 스택 ~4 KB
총 사용량 ~70 KB
여유 ~186 KB

소비 전력

모드 nRF52840 nRF9160
활성 (센서 동작, M4F 실행 중) ~3 mA ~3 mA
BLE TX (0 dBm, 2M PHY) ~5 mA 해당 없음
LTE-M TX (피크) 해당 없음 ~220 mA
LTE-M RX 해당 없음 ~6 mA
슬립 (BLE 연결 유지) ~1.5 µA 해당 없음
PSM (LTE, RRC 유휴) 해당 없음 ~2.5 µA
System OFF (nRF52840) ~0.5 µA 해당 없음

BLE 연결을 유지하면서 슬립 상태의 nRF52840은 평균 약 1.5 µA를 소모한다. 저듀티 사이클 센서 비콘으로 동작할 경우 CR2032 코인 셀로 수년간 운용이 가능하다.

eDRX 및 PSM (nRF9160)

배치 전송 사이 LTE-M 소비 전력을 최소화하려면 eDRX와 PSM을 설정한다:

// Rust (Embassy) - 권장
use nrf_modem::send_at;

// PSM 활성화: 주기적 TAU 1시간, 활성 윈도우 10초
send_at("AT+CPSMS=1,,,\"00100001\",\"00000101\"").await.unwrap();

// eDRX 활성화: 40.96초 페이징 주기
send_at("AT+CEDRXS=2,4,\"0101\"").await.unwrap();
Legacy C equivalent
/* PSM 활성화: 주기적 TAU 1시간, 활성 윈도우 10초 */
lte_lc_psm_req(true);

/* eDRX 활성화: 40.96초 페이징 주기 */
lte_lc_edrx_req(LTE_LC_LTE_MODE_LTEM, "0101");

PSM이 활성화되면 예약된 전송 사이에 모뎀이 전원을 끕니다. 애플리케이션 코어도 다음 배치 인터벌까지 Zephyr의 k_sleep()을 통해 저전력 상태로 진입한다.


예제 코드 개요

참조 예제는 sdk/c/nrf/examples/nrf52840_ble.c에 있으며, 다음 내용을 다룹니다:

  1. XMBP 배치 인코딩 — 가속도계 샘플 100개를 모아 BLE 전송에 최적화된 간결한 바이너리 배치로 인코딩한다.
  2. 다중 속도 샘플링 — 가속도계를 50 Hz(20 ms 간격), 다이 온도를 1 Hz로 측정하며, Zephyr의 k_uptime_get()으로 정밀한 타이밍을 구현한다.
  3. BLE 단편화 — 큰 XMBP 패킷을 MTU 크기의 GATT 노티피케이션으로 분할하고, 게이트웨이에서 재조립할 수 있도록 시퀀스 헤더를 포함한다.
  4. Zephyr 센서 API — LIS2DH12 드라이버에 대해 sensor_sample_fetch() + sensor_channel_get()을 사용하며, m/s²에서 g 단위로 변환한다.
  5. 협력적 전력 관리 — 샘플링 이벤트 사이 k_sleep()으로 CPU를 반납하고, BLE SoftDevice와 Zephyr 스케줄러가 웨이크업을 처리한다.

예제의 스트림 정의:

// Rust (Embassy) - 권장
let streams = [
    StreamDef::new(0, "accel_x",     ValueType::F32, "g",       50.0),
    StreamDef::new(1, "accel_y",     ValueType::F32, "g",       50.0),
    StreamDef::new(2, "accel_z",     ValueType::F32, "g",       50.0),
    StreamDef::new(3, "temperature", ValueType::F32, "celsius",  1.0),
];
Legacy C equivalent
static const xylolabs_stream_def_t streams[] = {
    { 0, "accel_x",     XMBP_VT_F32, "g",       50.0f },
    { 1, "accel_y",     XMBP_VT_F32, "g",       50.0f },
    { 2, "accel_z",     XMBP_VT_F32, "g",       50.0f },
    { 3, "temperature", XMBP_VT_F32, "celsius",  1.0f },
};

플랫폼 초기화:

// Rust (Embassy) - 권장
use nrf_softdevice::ble::peripheral;

let sd = Softdevice::enable(&softdevice_config());
let server = XylolabsGattServer::new(sd).unwrap();

// 설정: 15ms 연결 인터벌, MTU 247, 8초 워치독
let adv = peripheral::ConnectableAdvertisement::ScannableUndirected {
    adv_data: &ADV_DATA,
    scan_data: &SCAN_DATA,
};
let conn = peripheral::advertise_connectable(sd, adv, &Default::default()).await.unwrap();
Legacy C equivalent
platform_nrf_config_t cfg = {
    .transport      = NRF_TRANSPORT_BLE_GATT,
    .wdt_timeout_ms = 8000,
    .conn_interval  = 12,   /* 15 ms */
    .mtu            = 247,
    .apn            = NULL,
};
int ret = platform_nrf_init(&platform, &cfg);
platform_nrf_ble_start_advertising();

문제 해결

증상 원인 해결 방법
BLE 노티피케이션 누락 주변기기와 게이트웨이 간 MTU 불일치 bt_gatt_exchange_mtu()로 명시적 MTU 협상; 게이트웨이가 247바이트를 요청하는지 확인
부팅 시 LIS2DH12 미검출 DTS 오버레이 미적용 또는 SPI CS 핀 오류 west build 오버레이 경로 확인; cs-gpios가 실제 배선과 일치하는지 검토
LC3 인코딩 지연 급등 인코딩 중 BLE SoftDevice가 M4F 선점 LC3 스레드 우선순위 상향; 인코딩 중 Zephyr 협력적 스케줄링으로 블록
nRF9160 모뎀 초기화 타임아웃 SIM 미삽입 또는 APN 설정 오류 SIM 및 APN 확인; UART 쉘에서 AT+CEREG?로 등록 상태 조회
슬립 중 전류 과다 GPIO 누설 또는 주변장치 미정지 SPI CS를 HIGH로 유지하고, 슬립 전 LIS2DH12를 파워다운 모드로 전환
XMBP 버퍼 오버플로 배치 플러시 인터벌이 너무 긴 경우 XYLOLABS_META_BATCH_MS 감소 또는 XYLOLABS_XMBP_BUF_SIZE 증가
워치독 리셋 (LTE 연결 중) 콜드 스타트 시 LTE 연결에 30초 이상 소요 첫 부팅 시 wdt_timeout_ms를 60000으로 늘리고, 모뎀 초기화 중 WDT 갱신