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
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
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
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이 활성화되면 예약된 전송 사이에 모뎀이 전원을 끕니다. 애플리케이션 코어도 다음 배치
인터벌까지 Zephyr의 k_sleep()을 통해 저전력 상태로 진입한다.
예제 코드 개요¶
참조 예제는 sdk/c/nrf/examples/nrf52840_ble.c에 있으며, 다음 내용을 다룹니다:
- XMBP 배치 인코딩 — 가속도계 샘플 100개를 모아 BLE 전송에 최적화된 간결한 바이너리 배치로 인코딩한다.
- 다중 속도 샘플링 — 가속도계를 50 Hz(20 ms 간격), 다이 온도를 1 Hz로 측정하며, Zephyr의
k_uptime_get()으로 정밀한 타이밍을 구현한다. - BLE 단편화 — 큰 XMBP 패킷을 MTU 크기의 GATT 노티피케이션으로 분할하고, 게이트웨이에서 재조립할 수 있도록 시퀀스 헤더를 포함한다.
- Zephyr 센서 API — LIS2DH12 드라이버에 대해
sensor_sample_fetch()+sensor_channel_get()을 사용하며, m/s²에서 g 단위로 변환한다. - 협력적 전력 관리 — 샘플링 이벤트 사이
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
플랫폼 초기화:
// 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
문제 해결¶
| 증상 | 원인 | 해결 방법 |
|---|---|---|
| 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 갱신 |