Files
xblink/CLAUDE.md
michael e4195d2583 Add LP5562 driver, M1 smoke test, and milestone-based plan
- Integrate LP5562 driver from sibling project (ntag5-samd21-lp562)
  with full doc comments restored
- Update main.rs with complete M1 test: I2C init, GPIO EN, RGBW cycle
- Rewrite DEVELOPMENT_PLAN.md from waterfall to milestone-based groups
  organized by hardware availability (Groups A-E)
- Rewrite STATUS.md with milestone checklist tracking
- Add LP5562 timing constraints and flash script docs to CLAUDE.md
- Add flash_when_ready.sh for auto-flash dev workflow
- Add brainstorm design doc for plan redesign rationale

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 10:26:24 -08:00

5.0 KiB

xblink Development Guide

Embedded Rust firmware for a battery-free, NFC-powered LED implant. The NTAG5Link harvests energy from an NFC field and provides EEPROM storage for LED patterns. A SAMD21E MCU reads patterns from EEPROM, programs LP5562 execution engines, then sleeps while the LP5562 autonomously drives RGBW LEDs.

Build Commands

# Build release firmware
cargo build --release

# Flash via UF2 bootloader (XIAO M0: double-tap RST to enter bootloader)
cargo hf2 --release

# Auto-flash: watches for bootloader USB device, flashes when detected
./flash_when_ready.sh

Target

  • Architecture: thumbv6m-none-eabi (ARM Cortex-M0+)
  • Dev board: Seeed XIAO M0 (SAMD21G18A) — will port to bare SAMD21E18A later
  • LED driver: LP5562EVM (TI evaluation module, RGBW, I2C addr 0x30)
  • NFC: NTAG5Link Click board (MikroElektronika)

Dependencies

Crate Version Purpose
xiao_m0 0.13 Board support package (dev board phase)
panic-halt 0.2 Minimal panic handler for no_std
cortex-m 0.7 Cortex-M runtime, critical-section-single-core feature
embedded-hal 1.0.0 Hardware abstraction traits (I2C, GPIO, delay)

When porting to bare SAMD21E: replace xiao_m0 with atsamd-hal = { version = "0.17", features = ["samd21e"] } and add cortex-m-rt = "0.7".

Project Structure

src/
  main.rs          # Entry point: boot, pattern load, sleep/wake loop
  led/
    mod.rs         # LP5562 re-exports (LedController trait added later, M10)
    lp5562.rs      # LP5562 driver (from ../ntag5-samd21-lp562/)
  ntag5/           # NTAG5Link I2C slave driver (M4+)
  pattern/         # Pattern format and engine builder (M5+)

Architecture Conventions

  • no_std only: No heap allocation. Fixed-size arrays and stack-only data structures.
  • embedded-hal generics: All hardware drivers must be generic over embedded_hal::i2c::I2c (1.0 traits). Never use concrete HAL types in driver code.
  • Hardware-first: Get hardware working before extracting abstractions. Traits and formats are designed after hands-on experience, not before.
  • Error types: Use Error<E> enum wrapping underlying I2C error with From<E> impl (see LP5562 driver pattern).
  • Register addresses: Place in a reg submodule as pub const values.
  • const fn: Prefer const fn where possible (see EngineCommand builder in LP5562 driver).
  • No unsafe: Avoid unless absolutely necessary for hardware access.

LP5562 Timing Constraints

The LP5562 driver does NOT enforce timing delays internally — the caller is responsible:

  • >= 500 us after enable() before any other commands
  • >= 488 us between consecutive ENABLE register writes (engine exec changes)
  • >= 153 us between consecutive OP_MODE register writes (engine mode changes)
  • When transitioning from Run, set exec to Hold first, then change mode

Key I2C Addresses

Device Address Notes
LP5562 0x30 ADDR_SEL pins both low (LP5562EVM default)
NTAG5Link 0x54 Default NTP53x2 I2C slave address

The MCU accesses NTAG5Link as a standard I2C slave — plain register read/write, NOT the NFC-side ISO15693 custom commands used by the Python ntag5sensor tooling.

Region Address Range Size Notes
Session registers 0x00-0x06 7 bytes Volatile, runtime config
Configuration 0x37-0x3F Varies Persistent, EEPROM-backed
EH config 0x3D 1 byte Energy harvesting voltage/current
SRAM 0xF8-0xFF 256 bytes 64 x 4-byte blocks, volatile
EEPROM user memory blocks 0x00-0x1FF 2048 bytes 512 x 4-byte blocks

Key config constants (from ../ntag5sensor/vicinity/ntag5link.py):

  • CONFIG_1_USE_CASE_CONF_I2C_SLAVE = 0 << 4 — NTAG5 as I2C slave (our mode)
  • CONFIG_1_ARBITER_MODE_SRAM_PASSTHROUGH = 2 << 2 — SRAM pass-through for NFC↔I2C
  • CONFIG_1_SRAM_ENABLE = 1 << 1 — Enable SRAM access
  • CONFIG_0_EH_MODE_LOW_FIELD_STRENGTH = 2 << 2 — EH mode for phone NFC

Hardware Wiring (Dev Board)

XIAO Pin Connection Function
A4 (SDA) LP5562 SDA, NTAG5 SDA Shared I2C data
A5 (SCL) LP5562 SCL, NTAG5 SCL Shared I2C clock
D0/A0 LP5562 EN Hardware enable (GPIO, active high)
TBD NTAG5 FD pin Field detect / SRAM write indication (EIC wake)
TBD Hall sensor Recovery mode trigger (EIC wake)

Testing

  • Hardware: XIAO M0 + LP5562EVM + NTAG5Link Click (when jumpers available)
  • PCSC reader: Use ../ntag5sensor/ Python tooling with ACR1552 reader
  • Phone NFC: VivoKey RawNFC app for SRAM mailbox testing
  • Phone app: DT NFC Identifier for basic tag info

Sibling Projects

  • ../ntag5-samd21-lp562/ — Original LP5562 driver + smoke test (xblink's src/led/lp5562.rs is synced from here)
  • ../ntag5sensor/ — NTAG5Link command reference (vicinity/ntag5link.py), I2C patterns (vicinity/i2cbase.py)