Files
lora-card-reader/pn532/src/interface.rs
T
fromost 86373a2d33 Add read/write for felica
felica types refactor
2026-08-27 10:02:50 +08:00

346 lines
11 KiB
Rust

//! 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<bool, Self::Error> {
panic!("NoIrq should not be used");
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
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<Self::TransportError>>;
/// Write a command frame (`header` + optional `body`) and wait for the ACK.
fn write_command(
&mut self,
header: &[u8],
body: &[u8],
) -> Result<(), Error<Self::TransportError>>;
/// 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<usize, Error<Self::TransportError>>;
}
/// [`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, D, RST = NoReset, IRQ = NoIrq> {
i2c: I2C,
delay: D,
reset: RST,
irq: IRQ,
command: u8,
}
impl<I2C, D> I2cInterface<I2C, D, NoReset, NoIrq> {
/// 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<I2C, D, RST: OutputPin> I2cInterface<I2C, D, RST, NoIrq> {
/// 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<I2C, D, IRQ: InputPin> I2cInterface<I2C, D, NoReset, IRQ> {
/// 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<I2C, D, RST: OutputPin, IRQ: InputPin> I2cInterface<I2C, D, RST, IRQ> {
/// 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<I2C, D, RST, IRQ> I2cInterface<I2C, D, RST, IRQ> {
/// Consume the interface and return the underlying I2C bus.
pub fn release(self) -> I2C {
self.i2c
}
}
impl<I2C, D, RST, IRQ> Interface for I2cInterface<I2C, D, RST, IRQ>
where
I2C: I2c,
D: DelayNs,
RST: OutputPin,
IRQ: InputPin,
{
type TransportError = I2C::Error;
fn begin(&mut self) -> Result<(), Error<Self::TransportError>> {
// 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::TransportError>> {
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<usize, Error<Self::TransportError>> {
// 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<I2C, D, RST, IRQ> I2cInterface<I2C, D, RST, IRQ>
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<I2C::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<I2C::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)
}
}
}