Add read/write for felica
felica types refactor
This commit is contained in:
@@ -0,0 +1,446 @@
|
||||
# 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 0–5 — 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`).
|
||||
Reference in New Issue
Block a user