//! Transport abstraction and the I2C implementation. //! //! The PN532 communicates over a framed protocol regardless of the physical //! transport (I2C, SPI or HSU). The [`Interface`] trait mirrors that boundary: //! the driver builds logical commands, the interface handles framing and the //! acknowledgement/response handshake. use core::convert::Infallible; use embedded_hal::delay::DelayNs; use embedded_hal::digital::{ErrorType, InputPin, OutputPin}; use embedded_hal::i2c::{I2c, Operation}; use crate::commands::{ PN532_ACK_WAIT_TIME_MS, PN532_HOST_TO_PN532, PN532_I2C_ADDRESS, PN532_PN532_TO_HOST, PN532_POSTAMBLE, PN532_PREAMBLE, PN532_STARTCODE1, PN532_STARTCODE2, }; use crate::error::Error; const ACK: [u8; 6] = [0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00]; /// Maximum response data length supported (matches the PN532's 64-byte packet buffer). const MAX_DATA_LEN: usize = 64; /// Maximum length of an outbound command frame. const WRITE_FRAME_CAPACITY: usize = 64; /// Marker type for when no reset pin is connected. #[derive(Clone, Copy, Debug, Default)] pub struct NoReset; impl ErrorType for NoReset { type Error = Infallible; } impl OutputPin for NoReset { fn set_low(&mut self) -> Result<(), Self::Error> { panic!("NoReset should not be used") } fn set_high(&mut self) -> Result<(), Self::Error> { panic!("NoReset should not be used") } } /// Marker type for when no IRQ pin is connected. #[derive(Clone, Copy, Debug, Default)] pub struct NoIrq; impl ErrorType for NoIrq { type Error = Infallible; } impl InputPin for NoIrq { fn is_high(&mut self) -> Result { panic!("NoIrq should not be used"); } fn is_low(&mut self) -> Result { panic!("NoIrq should not be used"); } } /// A framed transport to the PN532. /// /// Implementations are responsible for the frame/ack handshake described in /// the PN532 user manual (UM0701-02). pub trait Interface { /// The error type produced by the underlying physical transport. type TransportError; /// Initialise the hardware: pulse the reset pin (if provided) and wait for /// the PN532 to become ready. fn begin(&mut self) -> Result<(), Error>; /// Write a command frame (`header` + optional `body`) and wait for the ACK. fn write_command( &mut self, header: &[u8], body: &[u8], ) -> Result<(), Error>; /// Read a response frame, returning the length of the payload (excluding /// the `TFI` and command bytes) placed in `buf`. fn read_response( &mut self, buf: &mut [u8], timeout_ms: u16, ) -> Result>; } /// [`Interface`] implementation over a blocking `embedded_hal::i2c::I2c` bus. /// /// `RST` and `IRQ` are the optional reset and IRQ pins; use [`NoReset`] / /// [`NoIrq`] (the defaults) or the relevant `embedded_hal` pins via the /// [`I2cInterface::with_reset`] / [`I2cInterface::with_reset_irq`] constructors. pub struct I2cInterface { i2c: I2C, delay: D, reset: RST, irq: IRQ, command: u8, } impl I2cInterface { /// Create an interface from an already-configured I2C bus and a delay /// source, without a reset or IRQ pin. pub fn new(i2c: I2C, delay: D) -> Self { Self { i2c, delay, reset: NoReset, irq: NoIrq, command: 0, } } } impl I2cInterface { /// Create an interface with a reset pin (`RSTPD_N`). pub fn with_reset(i2c: I2C, delay: D, reset: RST) -> Self { Self { i2c, delay, reset, irq: NoIrq, command: 0, } } } impl I2cInterface { /// Create an interface with an IRQ pin but no reset pin. pub fn with_irq(i2c: I2C, delay: D, irq: IRQ) -> Self { Self { i2c, delay, reset: NoReset, irq, command: 0, } } } impl I2cInterface { /// Create an interface with both a reset pin and an IRQ pin. pub fn with_reset_irq(i2c: I2C, delay: D, reset: RST, irq: IRQ) -> Self { Self { i2c, delay, reset, irq, command: 0, } } } impl I2cInterface { /// Consume the interface and return the underlying I2C bus. pub fn release(self) -> I2C { self.i2c } } impl Interface for I2cInterface where I2C: I2c, D: DelayNs, RST: OutputPin, IRQ: InputPin, { type TransportError = I2C::Error; fn begin(&mut self) -> Result<(), Error> { // Pulse RSTPD_N: high -> low -> wait -> high -> wait. This mirrors the // Adafruit library's begin() reset sequence. self.reset.set_high().unwrap(); self.reset.set_low().unwrap(); self.delay.delay_ms(400); self.reset.set_high().unwrap(); // Let the PN532 boot after the reset is released. The Adafruit library // waits ~10 ms + a 500 ms wakeup here; give it a full 500 ms. self.delay.delay_ms(500); Ok(()) } fn write_command( &mut self, header: &[u8], body: &[u8], ) -> Result<(), Error> { self.command = header[0]; let data_len = header.len() + body.len() + 1; // TFI + payload if data_len > 0xFF { return Err(Error::NoSpace); } // PREAMBLE + STARTCODE1 + STARTCODE2 + LEN + LCS + TFI + payload + DCS + POSTAMBLE let frame_len = 6 + header.len() + body.len() + 2; let mut frame = [0u8; WRITE_FRAME_CAPACITY]; if frame_len > frame.len() { return Err(Error::NoSpace); } frame[0] = PN532_PREAMBLE; frame[1] = PN532_STARTCODE1; frame[2] = PN532_STARTCODE2; frame[3] = data_len as u8; frame[4] = (!(data_len as u8)).wrapping_add(1); frame[5] = PN532_HOST_TO_PN532; let mut sum = PN532_HOST_TO_PN532; let mut idx = 6; for &b in header.iter().chain(body.iter()) { frame[idx] = b; idx += 1; sum = sum.wrapping_add(b); } frame[idx] = (!sum).wrapping_add(1); frame[idx + 1] = PN532_POSTAMBLE; crate::debug!("pn532: write cmd=0x{:02X} len={}", header[0], frame_len); self.i2c .write(PN532_I2C_ADDRESS, &frame[..frame_len]) .map_err(Error::Transport)?; crate::debug!("pn532: write ACKed, reading ACK frame"); self.read_ack_frame() } fn read_response( &mut self, buf: &mut [u8], timeout_ms: u16, ) -> Result> { // Wait for the PN532 to signal data is ready. self.wait_ready(timeout_ms)?; // Read the full response frame in a single transaction, stripping the // RDY byte. Frame layout: PREAMBLE(3) + LEN + LCS + TFI + CMD + payload + DCS + POSTAMBLE. let frame_len = buf.len() + 9; let mut frame = [0u8; MAX_DATA_LEN + 10]; if frame_len > frame.len() { return Err(Error::NoSpace); } self.i2c .transaction( PN532_I2C_ADDRESS, &mut [ Operation::Read(&mut [0]), Operation::Read(&mut frame[..frame_len]), ], ) .map_err(Error::Transport)?; if frame[0] != PN532_PREAMBLE || frame[1] != PN532_STARTCODE1 || frame[2] != PN532_STARTCODE2 { return Err(Error::InvalidFrame); } let length = frame[3] as usize; if frame[3].wrapping_add(frame[4]) != 0 { return Err(Error::InvalidFrame); } let cmd = self.command.wrapping_add(1); if frame[5] != PN532_PN532_TO_HOST || frame[6] != cmd { return Err(Error::InvalidFrame); } let data_len = length.saturating_sub(2); if data_len > buf.len() { return Err(Error::NoSpace); } let mut sum = frame[5].wrapping_add(frame[6]); for i in 0..data_len { buf[i] = frame[7 + i]; sum = sum.wrapping_add(frame[7 + i]); } if sum.wrapping_add(frame[7 + data_len]) != 0 { return Err(Error::InvalidFrame); } Ok(data_len) } } impl I2cInterface where I2C: I2c, D: DelayNs, IRQ: InputPin, { /// Wait until the PN532 signals data is ready. /// /// With an IRQ pin this blocks until the pin goes low; without one it polls /// the I2C status byte. Either way the status byte is consumed, so the next /// read returns the frame itself. fn wait_ready(&mut self, timeout_ms: u16) -> Result<(), Error> { let mut elapsed = 0u16; loop { match self.irq.is_low() { Ok(true) => { // IRQ asserted (active-low): data is ready. Poll the status // byte to confirm and consume it so the next read gets the // frame itself. let mut status = [0u8; 1]; match self.i2c.read(PN532_I2C_ADDRESS, &mut status) { Ok(()) if status[0] & 1 == 1 => return Ok(()), _ => { self.delay.delay_ms(1); elapsed += 1; if timeout_ms != 0 && elapsed >= timeout_ms { return Err(Error::Timeout); } } } } Ok(false) | Err(_) => { // IRQ connected but not asserted yet (or a read error): // keep waiting. self.delay.delay_ms(1); elapsed += 1; if timeout_ms != 0 && elapsed >= timeout_ms { return Err(Error::Timeout); } } } } } fn read_ack_frame(&mut self) -> Result<(), Error> { self.wait_ready(PN532_ACK_WAIT_TIME_MS)?; let mut ack = [0u8; 6]; self.i2c .transaction( PN532_I2C_ADDRESS, &mut [ Operation::Read(&mut [0]), Operation::Read(&mut ack), ], ) .map_err(Error::Transport)?; if ack == ACK { Ok(()) } else { Err(Error::InvalidAck) } } }