Wire SRAM mailbox polling into main idle loop (M6)

- Add FD pin (A1/PA04) as pull-up input for SRAM write detection
- Configure NTAG5 FD pin for SRAM-write-by-RF indication at boot
- Replace both idle loops with 200ms FD pin polling
- On command received: process via sram::process_command, then
  reload active pattern from EEPROM into LP5562
- I2C bus swapped between NTAG5 and LP5562 as needed (zero-cost
  move via release()/new())

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
michael
2026-03-05 16:19:39 -08:00
parent 8dfe9117af
commit 3469944209

View File

@@ -19,6 +19,7 @@ mod ntag5;
mod pattern;
use led::lp5562::{ClockSource, Lp5562, DEFAULT_ADDRESS};
use ntag5::Ntag5Link;
use ntag5::sram::MailboxState;
use pattern::LedMode;
/// Blink the on-board LED n times (active-low: low=on, high=off)
@@ -59,6 +60,9 @@ fn main() -> ! {
lp_en.set_high().unwrap();
delay.delay_ms(10u32); // Let LP5562 power stabilize
// NTAG5 FD pin on A1 (PA04) — input with pull-up (FD is open-drain)
let fd_pin = pins.a1.into_pull_up_input();
// I2C on A4 (SDA) / A5 (SCL) at 400 kHz
let i2c = bsp::i2c_master(
&mut clocks,
@@ -113,6 +117,9 @@ fn main() -> ! {
}
delay.delay_ms(500u32);
// Configure FD pin for SRAM-write-by-RF indication
let _ = ntag.configure_fd_sram_write(); // Best-effort, non-fatal
// Try reading XBLK library header from upper 1K
let mut header_buf = [0u8; pattern::HEADER_SIZE];
let eeprom_ok = ntag.read_memory(pattern::LIBRARY_BASE_BLOCK, &mut header_buf).ok()
@@ -131,20 +138,56 @@ fn main() -> ! {
match eeprom_pattern {
Some(pat) => {
// Reclaim I2C for LP5562
let i2c = ntag.release();
let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS);
// EEPROM pattern loaded — 3 fast blinks
blink(&mut led, &mut delay, 3, 80);
if pattern::load_pattern(&mut lp, &pat, led_current, &mut delay).is_err() {
blink(&mut led, &mut delay, 10, 50);
// Load pattern into LP5562 (swap I2C to LP5562 then back)
{
let i2c = ntag.release();
let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS);
if pattern::load_pattern(&mut lp, &pat, led_current, &mut delay).is_err() {
blink(&mut led, &mut delay, 10, 50);
}
let i2c = lp.release();
ntag = Ntag5Link::new(i2c, ntag5::DEFAULT_ADDRESS);
}
// LP5562 runs autonomously — MCU idles (future: STANDBY)
// Mailbox polling loop — LP5562 runs autonomously,
// MCU polls FD pin for NFC commands
let mut mbox = MailboxState::new();
loop {
delay.delay_ms(60000u32);
delay.delay_ms(200u32);
if fd_pin.is_low().unwrap_or(false) {
let cmd_ok = ntag5::sram::process_command(
&mut ntag, &mut mbox, &mut delay,
);
if matches!(cmd_ok, Ok(true)) {
// Reload LP5562 from current EEPROM state
let mut hdr_buf = [0u8; pattern::HEADER_SIZE];
if ntag.read_memory(pattern::LIBRARY_BASE_BLOCK, &mut hdr_buf).is_ok() {
if let Some(hdr) = pattern::parse_header(&hdr_buf) {
led_current = hdr.current;
let pat_block = pattern::LIBRARY_BASE_BLOCK
+ (pattern::HEADER_SIZE as u16 / 4)
+ (hdr.active_index as u16
* (pattern::PATTERN_ENTRY_SIZE as u16 / 4));
let mut pat_buf = [0u8; pattern::PATTERN_ENTRY_SIZE];
if ntag.read_memory(pat_block, &mut pat_buf).is_ok() {
if let Some(new_pat) = pattern::parse_pattern_entry(&pat_buf) {
// Swap I2C to LP5562 for reprogramming
let i2c = ntag.release();
let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS);
let _ = pattern::load_pattern(
&mut lp, &new_pat, led_current, &mut delay,
);
let i2c = lp.release();
ntag = Ntag5Link::new(i2c, ntag5::DEFAULT_ADDRESS);
}
}
}
}
}
}
}
}
None => {
@@ -219,17 +262,52 @@ fn main() -> ! {
let loaded = ntag.read_memory(pat_base_block, &mut pat_buf).ok()
.and_then(|()| pattern::parse_pattern_entry(&pat_buf));
let i2c = ntag.release();
let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS);
if let Some(pat) = loaded {
if pattern::load_pattern(&mut lp, &pat, led_current, &mut delay).is_err() {
blink(&mut led, &mut delay, 10, 50);
// Load into LP5562 (swap I2C), then swap back for mailbox
{
let i2c = ntag.release();
let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS);
if let Some(pat) = loaded {
if pattern::load_pattern(&mut lp, &pat, led_current, &mut delay).is_err() {
blink(&mut led, &mut delay, 10, 50);
}
}
let i2c = lp.release();
ntag = Ntag5Link::new(i2c, ntag5::DEFAULT_ADDRESS);
}
// Mailbox polling loop (same as EEPROM-loaded path)
let mut mbox = MailboxState::new();
loop {
delay.delay_ms(60000u32);
delay.delay_ms(200u32);
if fd_pin.is_low().unwrap_or(false) {
let cmd_ok = ntag5::sram::process_command(
&mut ntag, &mut mbox, &mut delay,
);
if matches!(cmd_ok, Ok(true)) {
let mut hdr_buf = [0u8; pattern::HEADER_SIZE];
if ntag.read_memory(pattern::LIBRARY_BASE_BLOCK, &mut hdr_buf).is_ok() {
if let Some(hdr) = pattern::parse_header(&hdr_buf) {
led_current = hdr.current;
let pat_block = pattern::LIBRARY_BASE_BLOCK
+ (pattern::HEADER_SIZE as u16 / 4)
+ (hdr.active_index as u16
* (pattern::PATTERN_ENTRY_SIZE as u16 / 4));
let mut pat_buf2 = [0u8; pattern::PATTERN_ENTRY_SIZE];
if ntag.read_memory(pat_block, &mut pat_buf2).is_ok() {
if let Some(new_pat) = pattern::parse_pattern_entry(&pat_buf2) {
let i2c = ntag.release();
let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS);
let _ = pattern::load_pattern(
&mut lp, &new_pat, led_current, &mut delay,
);
let i2c = lp.release();
ntag = Ntag5Link::new(i2c, ntag5::DEFAULT_ADDRESS);
}
}
}
}
}
}
}
} else {
// Provisioning failed — fall back to hardcoded cycle