LP5562 requires 2.7V min but NFC energy harvesting produces only 1.8V. New architecture: SAMD21 drives 6 red LEDs directly via TCC0/TCC1 hardware PWM through current-limiting resistors. Key changes: - Add TCC PWM driver (src/led/pwm.rs) for 6 GPIO-direct LED channels - Rewrite pattern engine: software waveform LUTs (sine/triangle/square/ heartbeat) replace LP5562 hardware execution engines - Add XBLK v2 EEPROM format with 16-byte pattern entries + playlist - Add TC4 50Hz ISR for animation, power governor for current budget - Add NTAG5 EH provisioning: persistent config + session trigger - Critical finding: SRAM passthrough in persistent EEPROM blocks all NFC access when MCU unpowered — CONFIG_1 must be session-only - Add provision_eh.py PCSC tool for ACR1552 reader - Update CLAUDE.md and STATUS.md for new architecture Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
129 lines
6.5 KiB
Markdown
129 lines
6.5 KiB
Markdown
# xBlink Development Guide
|
|
|
|
Embedded Rust firmware for a battery-free, NFC-powered LED implant. The NTAG5Link harvests energy from an NFC field (1.8V) and provides EEPROM storage for LED patterns. A SAMD21E MCU reads patterns from EEPROM, drives 6 red LEDs directly via TCC hardware PWM through current-limiting resistors, and updates duty cycles from a 50Hz timer ISR. The MCU stays in IDLE sleep during animation.
|
|
|
|
## Build Commands
|
|
|
|
```bash
|
|
# 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
|
|
- LEDs: 6x low-Vf red LEDs (Kingbright APTD1608, Vf ~1.7V) driven via TCC PWM + resistors
|
|
- NFC: NTAG5Link Click board (MikroElektronika)
|
|
- LP5562: removed from design (requires 2.7V min, incompatible with 1.8V EH)
|
|
|
|
## 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, EEPROM load, TC4 ISR animation, sleep/wake loop
|
|
led/
|
|
mod.rs # LED module re-exports
|
|
pwm.rs # TCC0/TCC1 hardware PWM driver for 6 GPIO-direct LEDs
|
|
lp5562.rs # LP5562 driver (legacy, kept for reference)
|
|
ntag5/ # NTAG5Link I2C slave driver
|
|
pattern/ # XBLK v2 format, software pattern engine, waveform LUTs
|
|
```
|
|
|
|
## 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.
|
|
|
|
## LED PWM Architecture
|
|
|
|
6 LEDs driven directly by SAMD21 TCC hardware PWM through current-limiting resistors (10-47 ohm).
|
|
|
|
| LED | TCC | Channel | SAMD21E Pin | XIAO Pin |
|
|
|-----|------|---------|-------------|----------|
|
|
| 0 | TCC0 | WO[0] | PA04 | A1 |
|
|
| 1 | TCC0 | WO[1] | PA05 | A2 |
|
|
| 2 | TCC0 | WO[2] | PA06 | A3* |
|
|
| 3 | TCC0 | WO[3] | PA07 | A4* |
|
|
| 4 | TCC1 | WO[0] | PA10 | D2 |
|
|
| 5 | TCC1 | WO[1] | PA11 | D3 |
|
|
|
|
*PA06/PA07 conflict with I2C on XIAO M0. Dev board uses 4 LEDs (0,1,4,5).
|
|
|
|
- **Animation**: TC4 ISR at 50Hz computes brightness from PatternEngine, writes TCC CC registers
|
|
- **Power governor**: Proportional scaling ensures total LED current stays within NTAG5 EH budget
|
|
- **Sleep**: IDLE mode during animation (~0.5mA MCU), STANDBY when no pattern active (~2uA)
|
|
|
|
## Key I2C Address
|
|
|
|
I2C bus (A4/A5) is used for NTAG5 only (LP5562 removed from design).
|
|
|
|
| Device | Address | Notes |
|
|
| --------- | ------- | --------------------------------- |
|
|
| NTAG5Link | 0x54 | Default NTP53x2 I2C slave address |
|
|
|
|
## NTAG5Link I2C Register Map (MCU-side)
|
|
|
|
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 |
|
|
| -------- | --------------------- | ------------------------------------------- |
|
|
| A1 (PA04)| LED 0 + resistor | TCC0/WO[0] PWM output |
|
|
| A2 (PA05)| LED 1 + resistor | TCC0/WO[1] PWM output |
|
|
| A4 (SDA) | NTAG5 SDA | I2C data |
|
|
| A5 (SCL) | NTAG5 SCL | I2C clock |
|
|
| D2 (PA10)| LED 4 + resistor | TCC1/WO[0] PWM output |
|
|
| D3 (PA11)| LED 5 + resistor | TCC1/WO[1] PWM output |
|
|
| A1 (PA04)| NTAG5 FD pin | Field detect / SRAM write indication (EIC) |
|
|
| TBD | Hall sensor output | EIC wake, pattern cycling |
|
|
|
|
## Testing
|
|
|
|
- **Hardware**: XIAO M0 + NTAG5Link Click + 4 LEDs with resistors on A1/A2/D2/D3
|
|
- **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 (legacy reference)
|
|
- `../ntag5sensor/` — NTAG5Link command reference (`vicinity/ntag5link.py`), I2C patterns (`vicinity/i2cbase.py`)
|