Files
fromost 86373a2d33 Add read/write for felica
felica types refactor
2026-08-27 10:02:50 +08:00

447 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PN532 — Byte-Level Communication Protocol
The PN532 is a "dumb" NFC front-end: the host drives it by writing *command frames*
and reading back *response frames*. This document describes those bytes — the
framing, checksums, acknowledgements, error codes and command set — independent of
the physical transport (HSU/UART, I²C or SPI). The transport only changes *how the
bytes are clocked in/out*; the frame content is identical everywhere.
> Sources: NXP *PN532 User Manual* UM0701-02 (the host protocol) and NXP
> `PN532_C1.pdf` (the datasheet, for transport framing and the low-level CIU
> command set).
---
## 1. The Information Frame
Every command and every response is one *information frame* of bytes:
```
| Preamble | Start code | LEN | LCS | TFI | DATA ... | DCS | Postamble |
| 00 | 00 FF | | | | | | 00 |
```
| Field | Size | Value / meaning |
|-------|------|-----------------|
| Preamble | 1 | `0x00` (see §1.4 for length rules) |
| Start code | 2 | `0x00 0xFF` |
| LEN | 1 | Length of the *data field* = 1 (TFI) + N (payload bytes). `0x01..0xFF`. |
| LCS | 1 | Length Checksum — `(0x100 LEN) & 0xFF`, i.e. `LEN + LCS == 0` (mod 256). |
| TFI | 1 | Frame identifier: `0xD4` host→PN532, `0xD5` PN532→host. |
| DATA | N | Command code (PD0) followed by parameters (PD1…PDn). |
| DCS | 1 | Data Checksum — `(0x100 (TFI + Σ DATA)) & 0xFF`, i.e. `TFI + Σ DATA + DCS == 0` (mod 256). |
| Postamble | 1 | `0x00` (see §1.4) |
**Checksums in one line (two's complement):**
```
LCS = (!LEN) + 1
DCS = (!(TFI + DATA0 + DATA1 + ...)) + 1
```
A frame is rejected (no ACK is returned) if `LEN + LCS != 0` or the data checksum
does not sum to zero.
### 1.1 Extended information frame (payload > 255 bytes)
The firmware supports up to **264** data bytes (265 including TFI). Beyond the normal
frame's 255-byte limit it uses an *extended* frame:
```
00 00 FF FF FF LENM LENL LCS TFI PD0 ... PDn DCS 00
└─┬─┘ └──┬──┘ │
LEN = FF LCS = FF │ ← both fixed to 0xFF (normally an "error" LEN/LCS pair)
LCS ← lower byte of [LENM + LENL + LCS] = 0x00
```
- `LEN` and `LCS` are fixed to `0xFF`.
- Real length: `LENGTH = LENM × 256 + LENL` = number of bytes in TFI + payload.
- `LCS` satisfies `LENM + LENL + LCS == 0` (mod 256).
- The host *may* use the extended frame for short frames too; the PN532 always picks
the right form (normal ≤ 255, extended > 255).
### 1.2 Frame Identifier (TFI)
| TFI | Direction |
|-----|-----------|
| `0xD4` | Host → PN532 |
| `0xD5` | PN532 → Host |
| `0x7F` | Error frame (PN532 → host, §2) |
### 1.3 Response command byte
The response echoes the command code **+ 1**:
| Command (host→PN532) | Response (PN532→host) |
|----------------------|----------------------|
| `GetFirmwareVersion` `0x02` | `0x03` |
| `SAMConfiguration` `0x14` | `0x15` |
| `InListPassiveTarget` `0x4A` | `0x4B` |
| `InDataExchange` `0x40` | `0x41` |
| … | `command + 1` |
### 1.4 Preamble / Postamble length rules
They are **not** always a single `0x00` byte:
- **Host → PN532** (HSU and I²C): preamble and postamble may be `0..n` bytes; the
value has no impact on frame processing. The PN532 only synchronises on the
`0x00 0xFF` start code.
- **Host → PN532** (SPI): preamble and postamble **must** be exactly one `0x00` byte.
- **PN532 → Host**: always a single `0x00` byte. This can be disabled entirely with
`SetParameters` flag `fRemovePrePostAmble` (bit 6) to save 2 bytes per frame.
---
## 2. Acknowledge / NACK / Error Frames
```
ACK : 00 00 FF 00 FF 00 ← PN532 ↔ host: "frame received OK"
NACK : 00 00 FF FF 00 00 ← host → PN532 only: "resend your last response"
Error: 00 00 FF 01 FF 7F 81 00 ← PN532 → host: "syntax error at application level"
```
- The **ACK** has two roles: acknowledging a received frame, and (when sent by the
host during command processing) **aborting** the current process.
- The **NACK** is used *only* by the host, to ask the PN532 to retransmit its last
response (after a corrupt/absent response). The PN532 never sends NACK — it just
stays silent on a data-link error.
- The **Error frame** is returned when the PN532 sees an *unknown command code* or
*incorrect parameters* in an otherwise valid frame.
### 2.1 Dialog structure
The host is always the master:
```
host ── command frame ──▶ PN532
host ◀── ACK ──────────── PN532 (must arrive within 15 ms; else host resends)
... PN532 executes ...
host ◀── response frame ─ PN532
host ── (optional ACK) ─▶ PN532
```
- **15 ms rule (HSU)**: the ACK must follow the command within 15 ms. If the host
sees no ACK, it resends the command.
- **Abort**: a new command, or a bare ACK, aborts the current process; the PN532 then
answers only the last command received.
- **Data-link errors** that silence the PN532: LCS error, DCS error, framing error
(HSU stop bit = 0), HSU timeout (frame not fully received within ~4× a 256-byte
frame; e.g. **89 ms** at 115200 baud, 44 ms at 230400, 8 ms at 1.288 M).
---
## 3. Status Byte and Error Codes
RF commands (`InDataExchange`, `TgGetData`, `InListPassiveTarget`, …) return a
status byte as the first payload byte:
```
7 6 5 .. 0
NADPresent MI Error code
```
- bit 7 `NADPresent` — payload contains a NAD byte (DEP / ISO14443-4 PCD).
- bit 6 `MI` — More Information (chaining) in progress.
- bits 05 — error code (`0x00` = success).
Error code list:
| Code | Cause |
|------|-------|
| `0x00` | OK |
| `0x01` | Time out — target did not answer |
| `0x02` | CRC error detected by the CIU |
| `0x03` | Parity error detected by the CIU |
| `0x04` | Erroneous bit count during anticollision (14443-3 Type A / 18092 106 k) |
| `0x05` | Framing error during MIFARE operation |
| `0x06` | Abnormal bit-collision during bitwise anticollision at 106 k |
| `0x07` | Communication buffer size insufficient |
| `0x09` | RF buffer overflow (CIU_Error BufferOvfl) |
| `0x0A` | RF field not switched on in time by counterpart (active mode) |
| `0x0B` | RF protocol error |
| `0x0D` | Temperature error — antenna drivers switched off |
| `0x0E` | Internal buffer overflow |
| `0x10` | Invalid parameter (range / format) |
| `0x12` | DEP: unsupported command received from initiator |
| `0x13` | DEP / MIFARE / 14443-4: data format does not match spec |
| `0x14` | MIFARE: authentication error |
| `0x23` | ISO14443-3: UID check byte wrong |
| `0x25` | DEP: invalid device state |
| `0x26` | Operation not allowed in this configuration |
| `0x27` | Command not acceptable in current context (unknown target number, …) |
| `0x29` | Target released by its initiator |
| `0x2A` | 14443-3B: card ID mismatch (wrong card) |
| `0x2B` | 14443-3B: previously activated card disappeared |
| `0x2C` | NFCID3 mismatch (initiator vs target) in DEP 212/424 passive |
| `0x2D` | Over-current detected |
| `0x2E` | NAD missing in DEP frame |
---
## 4. Transport-Specific Bytes
### 4.1 HSU / UART
- Full-duplex, up to **1.288 Mbaud** (default 115200), 8 data bits, LSB first, 1 stop bit.
- Frames are sent as-is (see §1), preamble/postamble may be 0..n bytes.
- Hardware preamble filter strips `00 00 FF` from incoming frames.
### 4.2 I²C
- **Address**: 7-bit `0x24`; 8-bit write `0x48`, read `0x49` (`SLV+W = 0x48`, `SLV+R = 0x49`).
- Fast mode up to 400 kHz.
- The frame is "slightly modified": on **reads**, a **status byte** is prepended. On
**writes** (commands) the frame is written verbatim (no status byte).
```
RDY status byte: bit 0 = RDY (bits 7..1 reserved)
RDY = 0 → no frame available
RDY = 1 → frame follows
```
Read sequence: START → read 1 status byte → if `RDY == 0`, STOP and retry; if
`RDY == 1`, keep reading the whole frame before STOP. A STOP before the full frame
discards the remaining bytes.
```
RDY ... RDY RDY frame
```
### 4.3 SPI
- Slave, SCK up to **5 MHz**.
- Every transfer starts with a **direction byte** (2 LSBs):
| First byte | Operation |
|-----------|-----------|
| `xxxx xx01` (`0x01`) | Data write (host → PN532) |
| `xxxx xx10` (`0x02`) | Status read (PN532 → host) |
| `xxxx xx11` (`0x03`) | Data read (PN532 → host) |
- Status register (1 byte): **bit 0 = RDY**. Poll `0x02` until `RDY == 1`, then read
with `0x03`.
- SPI preamble/postamble **must** be exactly one `0x00` byte.
- Optionally use the `P70_IRQ` pin (handshake) to skip status polling.
---
## 5. Host Command Set
Each command is the first payload byte (PD0) after the `0xD4` TFI. `In` = initiator,
`Tg` = target.
| Command | Code | Parameters |
|---------|------|-----------|
| `Diagnose` | `0x00` | NumTst (1), [InParam…] |
| `GetFirmwareVersion` | `0x02` | — |
| `GetGeneralStatus` | `0x04` | — |
| `ReadRegister` | `0x06` | Address (2, big-endian) |
| `WriteRegister` | `0x08` | Address (2) + Value (1…n) |
| `ReadGPIO` | `0x0C` | — |
| `WriteGPIO` | `0x0E` | P3, P7 |
| `SetSerialBaudRate` | `0x10` | Baud rate (1) |
| `SetParameters` | `0x12` | Flags (1) |
| `SAMConfiguration` | `0x14` | Mode (1), Timeout (1), [IRQ (1)] |
| `PowerDown` | `0x16` | WakeUpEnable (1), GenerateIRQ (1) |
| `RFConfiguration` | `0x32` | CfgItem (1), … |
| `RFRegulationTest` | `0x58` | TxMode (1) |
| `InJumpForPSL` | `0x46` | ActPSL (1), … |
| `InJumpForDEP` | `0x56` | ActPass (1), … |
| `InListPassiveTarget` | `0x4A` | MaxTg (1), BrTy (1), [InitiatorData] |
| `InATR` | `0x50` | — |
| `InPSL` | `0x4E` | Tg (1) |
| `InDataExchange` | `0x40` | Tg (1), [DataOut…] |
| `InCommunicateThru` | `0x42` | DataOut… |
| `InDeselect` | `0x44` | Tg (1) |
| `InRelease` | `0x52` | Tg (1) |
| `InSelect` | `0x54` | Tg (1) |
| `InAutoPoll` | `0x60` | PollNr (1), … |
| `TgInitAsTarget` | `0x8C` | Mode (1), … |
| `TgGetData` | `0x86` | — |
| `TgSetData` | `0x8E` | DataIn… |
| `TgGetInitiatorCommand` | `0x88` | — |
| `TgResponseToInitiator` | `0x90` | Data… |
| `TgGetTargetStatus` | `0x8A` | — |
| `TgSetGeneralBytes` | `0x92` | Data… |
| `TgSetMetaData` | `0x94` | Data… |
---
## 6. Worked Examples
### 6.1 GetFirmwareVersion (`0x02`)
Request:
```
00 00 FF 02 FE D4 02 2A 00
└─preamble+start─┘ │ │ │ │ └ postamble
LEN=02 ───────┘ │ │ └ DCS=0x2A
LCS=FE ──────────┘ └ command 0x02
TFI=0xD4 ───────────┘
```
Response (`IC=0x32` PN532, firmware 1.6, rev 0x06, support 0x07):
```
00 00 FF 06 FA D5 03 32 01 06 07 E8 00
│ │ │ │ │ │ └ DCS
│ │ │ │ │ └ support (0x07)
│ │ │ │ └ rev
│ │ │ └ firmware version (0x01 = 1.6)
│ │ └ IC = 0x32 (PN532)
│ └ cmd+1 = 0x03
└ TFI = 0xD5
```
`support` bitmask: `0x01` ISO/IEC 14443A, `0x02` ISO/IEC 14443B, `0x04` ISO/IEC 18092 (NFCIP-1).
### 6.2 SAMConfiguration (`0x14`)
Params: Mode=`0x01` (normal), Timeout=`0x14` (20 × 50 ms = 1 s), IRQ=`0x01` (drive P70_IRQ).
```
00 00 FF 05 FB D4 14 01 14 01 02 00
│ │ │ │ │ └ DCS
│ │ │ └ IRQ
│ │ └ Timeout (LSB 50 ms; 0x00 = no timeout)
│ └ Mode (0x01 normal, 0x02 virtual card, 0x03 wired card, 0x04 dual card)
└ command 0x14
```
Response:
```
00 00 FF 02 FE D5 15 16 00
```
### 6.3 InListPassiveTarget (`0x4A`)
Params: MaxTg=`0x01`, BrTy=`0x00` (106 kbps ISO/IEC 14443A):
```
00 00 FF 04 FC D4 4A 01 00 E1 00
│ │ │ └ BrTy (0x00 A, 0x01/0x02 FeliCa 212/424, 0x03 B, 0x04 Jewel)
│ │ └ MaxTg (max 2)
│ └ command 0x4A
```
Response (`0x4B`) for a MIFARE card with 4-byte UID `DE AD BE EF`:
```
00 00 FF 0C F4 D5 4B 01 01 04 00 08 04 DE AD BE EF 96 00
│ │ │ │ │ │ │ │ └── NFCID1 ──┘ └ DCS
│ │ │ │ │ │ │ └ NFCID length (4)
│ │ │ │ │ │ └ SEL_RES
│ │ │ │ └── SENS_RES (04 00)
│ │ └ target number (Tg = 0x01)
│ └ NbTg = 0x01
└ TFI 0xD5
```
### 6.4 InDataExchange (`0x40`)
Params: Tg=`0x01`, then the raw card command — MIFARE Classic READ block 4 (`0x30 0x04`):
```
00 00 FF 05 FB D4 40 01 30 04 B7 00
│ │ │ └─────┘ └ DCS
│ │ └ MIFARE READ (0x30) block 04
│ └ target Tg = 0x01 (bit 6 = MI for DEP chaining)
└ command 0x40
```
Success response (`0x41`): Status=`0x00`, then 16 data bytes (`00..0F`):
```
00 00 FF 13 ED D5 41 00 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 72 00
│ │ └ status 0x00 = success
│ └ cmd+1 = 0x41
└ TFI 0xD5
```
### 6.5 ReadRegister (`0x06`)
Params: register address (2 bytes, big-endian). Reading `CIU_RxSel` (`0x6307`):
```
00 00 FF 04 FC D4 06 63 07 BC 00
```
Response (`0x07`) with value `0x84`:
```
00 00 FF 03 FD D5 07 84 A0 00
```
---
## 7. MIFARE / ISO/IEC 14443A Card Bytes
These are the payload bytes passed inside `InDataExchange` / `InCommunicateThru`.
| Operation | Card command |
|-----------|--------------|
| Authenticate key A / B | `0x60` / `0x61` + block + UID + key (6) |
| Read block | `0x30` + block |
| Write block | `0xA0` + block + 16 data bytes |
| Write (Ultralight) | `0xA2` + page + 4 data bytes |
| Transfer (write commit) | `0xB0` + block |
| Decrement | `0xC0` + block + 4-byte value |
| Increment | `0xC1` + block + 4-byte value |
| Restore | `0xC2` + block |
FeliCa payload codes: Polling `0x00`, Request Service `0x02`, Request Response `0x04`,
Read Without Encryption `0x06`, Write Without Encryption `0x08`, Request System Code `0x0C`.
---
## 8. SetParameters (`0x12`) flags
```text
D4 12 Flags
```
| Bit | Flag | Meaning |
|-----|------|---------|
| 0 | `fNADUsed` | Use NAD in DEP / 14443-4 PCD |
| 1 | `fDIDUsed` | Use DID (DEP) / CID (14443-4 PCD) |
| 2 | `fAutomaticATR_RES` | Auto-generate ATR_RES in target mode |
| 4 | `fAutomaticRATS` | Auto-send RATS after selecting 14443-4 card |
| 5 | `fISO14443-4_PICC` | Emulate ISO14443-4 PICC |
| 6 | `fRemovePrePostAmble` | Omit preamble + postamble in frames sent to host |
---
## 9. CIU Command Set (low-level, from datasheet §8.6.20)
The firmware drives a *Contactless Interface Unit* (CIU) whose commands live in the
`CIU_Command` register. These are **not** host commands — they surface only via
`WriteRegister`/`ReadRegister`. Listed for completeness:
| Command | Code | Action |
|---------|------|--------|
| Idle | `0000` | No action; cancel current command |
| Config | `0001` | Configure CIU for FeliCa / MIFARE / NFCIP-1 |
| GenerateRandomID | `0010` | Generate 10-byte random ID |
| CalcCRC | `0011` | Run CRC coprocessor (or self-test) |
| Transmit | `0100` | Transmit data from FIFO |
| NoCmdChange | `0111` | Modify `CIU_Command` bits without changing command |
| Receive | `1000` | Activate receiver |
| SelfTest | `1001` | Activate self-test |
| Transceive | `1100` | Transmit then auto-receive (initiator) or vice-versa |
| AutoColl | `1101` | FeliCa polling / MIFARE anticollision (card mode) |
| MFAuthent | `1110` | MIFARE Classic authentication |
| SoftReset | `1111` | Reset the CIU |
---
## 10. Minimum Startup Sequence
1. **Reset** — pulse `RSTPD_N` (high → low → wait ~400 ms → high), let the PN532 boot.
2. **GetFirmwareVersion** (`0x02`) — sanity-check the link and chip.
3. **SAMConfiguration** (`0x14`, mode `0x01`) — enable the SAM in normal (reader)
mode; required before any RF command.
4. Poll with **InListPassiveTarget** (`0x4A`), then transact with **InDataExchange** (`0x40`).