> For the complete documentation index, see [llms.txt](https://docs.mtoolstec.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.mtoolstec.com/hardware/rfid-nfc-and-uhf-hardware/grovenfc/iic-operation-guide.md).

# IIC Operation Guide

Use the IIC interface to configure GroveNFC, exchange NFC frames, and manage tag-emulation data. The host controls the module through 16-bit register addresses.

Before starting, follow the [connection instructions](/hardware/rfid-nfc-and-uhf-hardware/grovenfc.md#connecting-a-host). SDA and SCL use 3.3V logic. Firmware-dependent features are described in Section 8.

## 1. Bus and Data Format

| Item                 | Specification                                                |
| -------------------- | ------------------------------------------------------------ |
| Module role          | I²C slave on RP2040 I2C0                                     |
| 7-bit device address | `0x48`; on-wire write/read address bytes are `0x90` / `0x91` |
| Firmware SDA / SCL   | GPIO16 / GPIO17; these do not establish connector pin order  |
| Initialization rate  | 100 kHz for initial host configuration                       |
| Register address     | 16 bits, high byte first                                     |
| 16-bit register data | Little-endian, low byte first                                |
| Data buffer          | 256 bytes at `0x0100`                                        |
| Emulated tag RAM     | 4096 bytes; write window `0x1000–0x1FFF`                     |

Register write: `START → 0x48(W) → address high byte → address low byte → data → STOP`.

Register read: `START → 0x48(W) → address high byte → address low byte → repeated START → 0x48(R) → read N bytes → NACK → STOP`.

Use a combined transaction with a repeated START for reads: the transmit counter resets on RESTART. An SMBus interface with a one-byte command address cannot replace this sequence.

For example, setting Mode to `0x0100` uses the write payload `00 04 00 01`: the first two bytes select address `0x0004`, and the final two encode the value in little-endian order.

The firmware notifies the other core to process a write after STOP. There is only one pending-command slot and one shared transaction buffer. Serialize host operations and allow processing time between writes. An I²C ACK confirms byte reception, not completion of NFC or Flash processing.

## 2. System Registers

Each system register is 2 bytes wide.

| Address  | Name        | Purpose / default                                              |
| -------- | ----------- | -------------------------------------------------------------- |
| `0x0000` | VerHardware | Hardware version; `0x6123` in the reference configuration      |
| `0x0002` | VerFirmware | Firmware version; read from the device                         |
| `0x0004` | Mode        | Operating mode; default `0x0000`                               |
| `0x0006` | TagAddr     | Flash tag offset or file-mode slot number                      |
| `0x0008` | RfConfig    | Reader encoding in the high byte, Tag encoding in the low byte |
| `0x000A` | FWI         | Receive-wait parameter; default `0x0009`                       |
| `0x000C` | TxCrcEn     | Transmit CRC configuration; default 0                          |
| `0x000E` | RxCrcEn     | Receive CRC configuration; default 0                           |
| `0x0010` | NFCStatus   | NFC status; set to BUSY when sending                           |
| `0x0012` | RxLen       | Received data length                                           |
| `0x0014` | Command     | Shortcut command                                               |

VerHardware, VerFirmware, and RxLen are read-only host interfaces. Write 0 to NFCStatus to clear the previous transaction status before starting an exchange.

## 3. Miscellaneous Registers

Each register below is 1 byte wide.

| Address         | Name      | Values / purpose                                                             |
| --------------- | --------- | ---------------------------------------------------------------------------- |
| `0x0020`        | RFOn      | 0 off, 1 on; reader activation requires Mode=`0x0100`                        |
| `0x0021`        | TxLastBit | 0 means all 8 bits of the last byte; 1–7 specify valid bits in the last byte |
| `0x0022`        | Thru      | 0 disabled, 1 enabled; availability depends on firmware                      |
| `0x0023`        | EGT       | ISO14443B parameter; range 0–7                                               |
| `0x0024`        | Slot      | FeliCa parameter; range 0–7                                                  |
| `0x0025–0x0028` | Resv0–3   | Reserved                                                                     |
| `0x0029`        | EpWrite   | 1 saves RAM to Flash; 2 loads RAM from Flash                                 |

Treat miscellaneous registers as write controls. Do not depend on miscellaneous-register readback or EpWrite polling with the 20261001 reference firmware; see the compatibility notes below.

## 4. Reader Mode, RF Configuration, and CRC

Mode values: default `0x0000`, reader `0x0100`, Flash tag `0x0200`, MIFARE1 Reader `0x0300`, and file tag `0x0400`. Check the firmware compatibility notes before selecting tag-emulation modes.

Use these RF combinations with the reference IIC reader interface:

| Protocol              | RfConfig | CRC bit  |
| --------------------- | -------- | -------- |
| ISO14443A 106K        | `0x0101` | `0x0001` |
| ISO14443B 106K        | `0x0505` | `0x0002` |
| FeliCa 212K           | `0x0909` | `0x0004` |
| ISO15693 ASK100 + ASK | `0x0B0B` | `0x0008` |
| ISO15693 ASK100 + FSK | `0x0B0C` | `0x0008` |
| ISO15693 ASK10 + ASK  | `0x0C0B` | `0x0008` |
| ISO15693 ASK10 + FSK  | `0x0C0C` | `0x0008` |

Other RF rates are defined in the API, but availability depends on the installed firmware. Use the combinations above for the reference IIC interface.

ISO14443A uses RxCrcEn to control receive CRC checking. For 14B, FeliCa, and ISO15693, enabling the corresponding TxCrcEn bit also enables receive CRC checking; RxCrcEn does not independently control these exchanges. When CRC validation succeeds, RxLen excludes the final two CRC bytes. The buffer still contains those bytes; the host should read only RxLen bytes.

With CRC enabled, FeliCa also removes the initial `B2 4D` synchronization bytes. ISO15693 has a special send-EOF branch using TxCrcEn bit8. Use this extension only with firmware that supports the required tag command sequence.

FWI is protocol-dependent: it controls ISO14443A frame timing and ISO14443B waiting behavior. In the reference IIC interface, 14A uses `FWI + 9`, 14B uses FWI, and FeliCa / ISO15693 use their protocol-specific receive settings. It is not a common millisecond timeout.

## 5. NFC Status and Transaction Sequence

| Bit value           | Meaning                                                |
| ------------------- | ------------------------------------------------------ |
| `0x8000`            | BUSY                                                   |
| `0x4000`            | Receive timeout                                        |
| `0x2000`            | CRC error                                              |
| `0x1000`            | Bit / synchronization error                            |
| `0x0800` / `0x0400` | MIFARE1 authentication success / failure (definitions) |
| `0x0200` / `0x0100` | MIFARE1 write success / failure (definitions)          |
| `0x0080`            | PN532 complete (definition)                            |
| `0x0001`            | Receive complete                                       |
| `0x0002`            | No response                                            |

General exchange sequence:

1. Set Mode=`0x0100`, then configure RF, FWI, CRC, and the last-byte bit count.
2. Write RFOn=1 in a separate transaction and allow the RF field to stabilize.
3. Clear NFCStatus, then write the NFC frame to `0x0100`. STOP triggers transmission and reception.
4. Poll NFCStatus until it is nonzero and BUSY is clear, with a host-side deadline. A short operation may complete before the host observes BUSY; do not require observing BUSY first.
5. Check completion or error status. On completion, read RxLen and then read that many bytes from `0x0100`.
6. Finish with a separate RFOn=0 write. When changing protocols, turn the field off using the old RF configuration, change RfConfig, and turn it on again.

Use separate writes for registers with side effects. Firmware dispatches processing by the transaction's **starting address**; writing across Mode, RFOn, or other fields does not trigger their handlers in sequence.

The buffer capacity is 256 bytes. When firmware appends a two-byte CRC, the transmit payload must not exceed 254 bytes. The host must also validate receive lengths. Keep every access within the documented register and buffer ranges.

## 6. ISO14443A REQA Example

REQA is the 7-bit value `0x26` and requires no CRC. A successful response contains two ATQA bytes. This example demonstrates card discovery only; it does not implement UID anticollision, selection, authentication, or tag-memory access.

The following Linux Python example uses `smbus2`. Adjust the bus number for your system. The example uses fixed configuration delays because the interface does not provide a configuration-completion handshake. Adjust these delays for the installed firmware and host.

```python
import time
from smbus2 import SMBus, i2c_msg

ADDR = 0x48

def write_reg(bus, reg, payload):
    bus.i2c_rdwr(i2c_msg.write(ADDR, reg.to_bytes(2, "big") + bytes(payload)))

def write16(bus, reg, value):
    write_reg(bus, reg, value.to_bytes(2, "little"))

def read_reg(bus, reg, length):
    req = i2c_msg.write(ADDR, reg.to_bytes(2, "big"))
    reply = i2c_msg.read(ADDR, length)
    bus.i2c_rdwr(req, reply)
    return bytes(reply)

def read16(bus, reg):
    return int.from_bytes(read_reg(bus, reg, 2), "little")

with SMBus(1) as bus:
    print("hardware:", hex(read16(bus, 0x0000)))
    print("firmware:", hex(read16(bus, 0x0002)))
    for reg, value in [(0x0004, 0x0100), (0x0008, 0x0101),
                       (0x000A, 9), (0x000C, 0), (0x000E, 0)]:
        write16(bus, reg, value)
        time.sleep(0.005)
    write_reg(bus, 0x0021, [7])
    time.sleep(0.005)
    write_reg(bus, 0x0020, [1])
    time.sleep(0.02)
    try:
        write16(bus, 0x0010, 0)
        time.sleep(0.005)
        write_reg(bus, 0x0100, [0x26])
        deadline = time.monotonic() + 1.0
        while True:
            status = read16(bus, 0x0010)
            if status and not (status & 0x8000):
                break
            if time.monotonic() >= deadline:
                raise TimeoutError("GroveNFC did not complete before the host timeout")
            time.sleep(0.002)
        if status != 0x0001:
            raise RuntimeError(f"NFC status: 0x{status:04X}")
        length = read16(bus, 0x0012)
        if length != 2:
            raise RuntimeError(f"Unexpected ATQA length: {length}")
        print("ATQA:", read_reg(bus, 0x0100, length).hex(" "))
    finally:
        write_reg(bus, 0x0020, [0])
```

Before sending subsequent full-byte frames, reset TxLastBit to 0 and configure CRC as required by the NFC command. ATQA is not the UID.

## 7. Tag Emulation and Flash Data

Tag-type low-byte definitions: NTAG213/215/216 use `01/02/03`; MIFARE1 4B1K, 7B1K, 4B4K, and 7B4K use `04/05/06/07`; iSLI, iSLIX, iSLIX2, and iSLI\_L use `08/09/0A/0B`; Tag-it HF-I Plus uses `0C`; generic ISO15 uses `0F`; CHINA\_II uses `20`.

The Flash tag area reserves 64 KiB in sixteen 4 KiB slots. TagAddr must be one of `0x0000, 0x1000, …, 0xF000`.

To save a tag image:

1. Stop reader / emulation activity and set TagAddr.
2. Write a complete 4096-byte tag image in chunks to `0x1000 + offset`. Each chunk must be at most 256 bytes and remain within `0x1FFF`. Allow processing time between chunks.
3. Write EpWrite=`1` at `0x0029`. Core0 stops Core1, erases and programs the entire 4096-byte sector, clears EpWrite, and restarts Core1.
4. Interrupts are disabled during Flash processing, so IIC may not respond. Allow for sector erase and programming time; erase alone can take up to 400 ms in the reference implementation. Do not issue further transactions while storage is in progress. EpWrite completion polling is unavailable on the affected reference firmware.

Writing only part of RAM saves the remaining old contents as well. Writes to the `0x1000` window only update RAM; they do not immediately program Flash. Reading this window has no corresponding safe handler and should not be used.

Flash-backed tag mode is defined as `0x0200 | type`. File-backed tag mode is defined as `0x0400 | type`. TagAddr selects a Flash offset in Flash mode and a slot number in file mode. The 20261001 reference firmware has a Flash-mode selection incompatibility; see Section 8 before using it.

Use an image that matches the selected tag family and the firmware's image format. Tag-image layouts and file-slot loading are specific to the installed firmware. Writing Mode=`0x0000` exits tag mode and resets several configuration registers; configure the next operation again.

## 8. Firmware Compatibility

GroveNFC product firmware and custom firmware built with the development API can expose different features. Check the installed firmware version before using emulation, shortcut commands, or extended rates.

| Area                                      | 20261001 reference firmware behavior                                                                                                                |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Miscellaneous register reads              | Readback is unreliable; use these registers as write controls                                                                                       |
| Flash-backed emulation selection          | Handler accepts the type-only value instead of the documented `0x0200 \| type`; custom firmware must align its handler with the register definition |
| MIFARE1 emulation                         | The application dispatch has no active MIFARE1 emulation call; use product firmware that implements this feature                                    |
| ISO14443B tag emulation                   | The CHINA\_II activation path is inactive in the reference interface                                                                                |
| MIFARE1 Reader and key-recovery shortcuts | The reference IIC interface does not provide complete command handlers                                                                              |
| Thru and extended rates                   | Do not assume availability from constants alone                                                                                                     |
| ISO14443A timeout reporting               | Some failed exchanges may be reported as no response; handle both statuses as an unsuccessful exchange                                              |

The [API Header Reference](/hardware/rfid-nfc-and-uhf-hardware/grovenfc/api-header-reference.md) lists available identifiers. An identifier does not by itself guarantee that a custom firmware build implements the corresponding feature.

## 9. Troubleshooting

| Symptom                            | Action                                                                                                   |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Device does not acknowledge        | Check common ground, connector labels, 3.3V logic levels, bus selection, and 7-bit address `0x48`        |
| Version data appears swapped       | Decode 16-bit register data as little-endian; send register addresses high byte first                    |
| Register reads return stale data   | Use a combined transaction with repeated START                                                           |
| REQA times out                     | Check reader Mode, RfConfig=`0x0101`, RFOn=1, TxLastBit=7, CRC disabled, and tag position                |
| UID is missing                     | ATQA is a discovery response; perform anticollision and selection to obtain the UID                      |
| Status remains BUSY                | Enforce the host deadline and verify that the firmware supports the selected RF configuration or command |
| Configuration writes are ignored   | Send side-effect registers separately and allow processing time between writes                           |
| Communication pauses during saving | Wait for Flash erase/programming to finish before sending the next command                               |
