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:
108
src/main.rs
108
src/main.rs
@@ -19,6 +19,7 @@ mod ntag5;
|
|||||||
mod pattern;
|
mod pattern;
|
||||||
use led::lp5562::{ClockSource, Lp5562, DEFAULT_ADDRESS};
|
use led::lp5562::{ClockSource, Lp5562, DEFAULT_ADDRESS};
|
||||||
use ntag5::Ntag5Link;
|
use ntag5::Ntag5Link;
|
||||||
|
use ntag5::sram::MailboxState;
|
||||||
use pattern::LedMode;
|
use pattern::LedMode;
|
||||||
|
|
||||||
/// Blink the on-board LED n times (active-low: low=on, high=off)
|
/// Blink the on-board LED n times (active-low: low=on, high=off)
|
||||||
@@ -59,6 +60,9 @@ fn main() -> ! {
|
|||||||
lp_en.set_high().unwrap();
|
lp_en.set_high().unwrap();
|
||||||
delay.delay_ms(10u32); // Let LP5562 power stabilize
|
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
|
// I2C on A4 (SDA) / A5 (SCL) at 400 kHz
|
||||||
let i2c = bsp::i2c_master(
|
let i2c = bsp::i2c_master(
|
||||||
&mut clocks,
|
&mut clocks,
|
||||||
@@ -113,6 +117,9 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
delay.delay_ms(500u32);
|
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
|
// Try reading XBLK library header from upper 1K
|
||||||
let mut header_buf = [0u8; pattern::HEADER_SIZE];
|
let mut header_buf = [0u8; pattern::HEADER_SIZE];
|
||||||
let eeprom_ok = ntag.read_memory(pattern::LIBRARY_BASE_BLOCK, &mut header_buf).ok()
|
let eeprom_ok = ntag.read_memory(pattern::LIBRARY_BASE_BLOCK, &mut header_buf).ok()
|
||||||
@@ -131,20 +138,56 @@ fn main() -> ! {
|
|||||||
|
|
||||||
match eeprom_pattern {
|
match eeprom_pattern {
|
||||||
Some(pat) => {
|
Some(pat) => {
|
||||||
// Reclaim I2C for LP5562
|
|
||||||
let i2c = ntag.release();
|
|
||||||
let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS);
|
|
||||||
|
|
||||||
// EEPROM pattern loaded — 3 fast blinks
|
// EEPROM pattern loaded — 3 fast blinks
|
||||||
blink(&mut led, &mut delay, 3, 80);
|
blink(&mut led, &mut delay, 3, 80);
|
||||||
|
|
||||||
if pattern::load_pattern(&mut lp, &pat, led_current, &mut delay).is_err() {
|
// Load pattern into LP5562 (swap I2C to LP5562 then back)
|
||||||
blink(&mut led, &mut delay, 10, 50);
|
{
|
||||||
|
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 {
|
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 => {
|
None => {
|
||||||
@@ -219,17 +262,52 @@ fn main() -> ! {
|
|||||||
let loaded = ntag.read_memory(pat_base_block, &mut pat_buf).ok()
|
let loaded = ntag.read_memory(pat_base_block, &mut pat_buf).ok()
|
||||||
.and_then(|()| pattern::parse_pattern_entry(&pat_buf));
|
.and_then(|()| pattern::parse_pattern_entry(&pat_buf));
|
||||||
|
|
||||||
let i2c = ntag.release();
|
// Load into LP5562 (swap I2C), then swap back for mailbox
|
||||||
let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS);
|
{
|
||||||
|
let i2c = ntag.release();
|
||||||
if let Some(pat) = loaded {
|
let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS);
|
||||||
if pattern::load_pattern(&mut lp, &pat, led_current, &mut delay).is_err() {
|
if let Some(pat) = loaded {
|
||||||
blink(&mut led, &mut delay, 10, 50);
|
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 {
|
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 {
|
} else {
|
||||||
// Provisioning failed — fall back to hardcoded cycle
|
// Provisioning failed — fall back to hardcoded cycle
|
||||||
|
|||||||
Reference in New Issue
Block a user