Embedded HandbookApps
All sections › X. Design, networks and IoT
STM32L476, STM32F103 · level intermediate · 11 min read
Open in the interactive handbook

94а. ST25DV NFC tag: the phone and the microcontroller share one memory

The device has no buttons and no screen, yet you need to configure it: an address, a threshold, a network key. Or the device is broken, and you still want to get its log out. For this there are NFC tags with two inputs. On one side, a phone accesses the tag memory over the radio; on the other, the microcontroller accesses it over I²C. ST25DV is such a tag from ST. This section is about how firmware works with it: which addresses it has on the bus, what lies at which address, why there is a password and how the tag reports that the phone has written something.

The reference values come from the ST25DV driver that ST ships in the support pack for STM32L4 for Keil (st25dv.c, st25dv.h, st25dv_reg.h). Experiment: this driver is built unchanged on a PC and connected to a tag model that prints every I²C transaction. The check is _verify/nfc_st25_check.py.

The radio interface itself is not covered here. For 13.56 MHz, the near field and powering the tag from the reader's field, see section 89.

Terms used in the documentation

Term Meaning
dual interface EEPROM an EEPROM with two inputs: radio and wired
user memory / user area user memory / a zone of it
system configuration area the system area: static settings
dynamic register a dynamic register: the current value of a setting, not the stored one
fast transfer mode, mailbox fast transfer through a mailbox in RAM
GPO — General Purpose Output the tag's interrupt output
I2C password, security session the I²C password, the session after it is presented
energy harvesting taking energy from outside; in an NFC tag, power from the reader's field brought out to a separate pin for an external circuit

94а.1 Why a tag with two inputs

An ordinary NFC tag is passive memory: the phone writes, the phone reads. ST25DV has a second input, I²C, and its memory is visible to the microcontroller. This gives three typical uses:


94а.2 Two addresses on the bus and the memory map

The tag answers on the I²C bus at two addresses. Different parts of the memory are visible through them, and the driver picks the address by one bit of the register number:

Address (8-bit) 7-bit What is behind it
0xA6 0x53 user EEPROM from 0x0000, dynamic registers 0x2000…0x2007, mailbox from 0x2008
0xAE 0x57 system area: static settings, tag identity data, password

The driver's rule for registers: if bit 0x2000 is set in the number, the request goes to 0xA6, otherwise to 0xAE. The user EEPROM is read and written by separate driver functions ReadData and WriteData (St25Dv_Drv.ReadData), and they always go to 0xA6. STM32 HAL and this driver take the address already shifted left, so the code has 0xA6, while in seven-bit notation (section 17) the same address is 0x53.

The system area registers you need:

Register Address What it holds
GPO 0x0000 which events to output on the GPO pin
ENDA1, ENDA2, ENDA3 0x0005, 0x0007, 0x0009 boundaries of the user memory zones
I2CSS 0x000B zone protection on the I²C side
MEM_SIZE 0x0014–0x0015 number of blocks minus one
BLK_SIZE 0x0016 block size minus one
ICREF 0x0017 chip code
UID 0x0018 unique number, 8 bytes
I2C_PWD 0x0900 window for the password

Dynamic: GPO_DYN 0x2000, EH_CTRL_DYN 0x2002, RF_MNGT_DYN 0x2003, I2C_SSO_DYN 0x2004 (whether the session is open), IT_STS_DYN 0x2005 (what happened), MB_CTRL_DYN 0x2006 (mailbox enable), MB_LEN_DYN 0x2007.

ST25DV: one memory, two inputs — radio for the phone and I²C for the microcontroller


94а.3 Experiment: the real driver and a tag model

Testing a driver on a real tag means seeing only the outcome: it worked or it did not. Here it is done differently. The driver files from the pack are built with gcc on a PC without a single edit, and the bus functions — Read, Write, IsReady, GetTick, which the driver receives through ST25DV_RegisterBusIO — are a tag model that prints every transaction: W or R, address, register, length and bytes.

The model exists only so that the driver has someone to talk to. Its behaviour is defined in the experiment and marked in the output as indented model remarks: an identity of 2048 bytes, a password of eight zeros, three refusals to the ready poll after every EEPROM write, refusal to write the system area without the password. The bytes on the bus are exactly what the driver produced.

The only substitution is an empty cmsis_compiler.h: the PC has no CMSIS, and the driver needs only stdint.h from it.


94а.4 Identification and size

== 1. Init: опознание
R 0xAE 0x0017 [  1] 26
Init = 0
== 2. объём памяти
R 0xAE 0x0014 [  1] FF
R 0xAE 0x0015 [  1] 01
R 0xAE 0x0016 [  1] 03
Mem_Size = 511, BlockSize = 3 -> 2048 байт

Init reads one byte — ICREF — and compares it with two driver constants: 0x24 is a 4 Kbit tag, 0x26 is a 16 or 64 Kbit tag. The size is computed as (MEM_SIZE + 1) × (BLK_SIZE + 1): 512 blocks of 4 bytes — 2048 bytes, that is 16 Kbit.

The same header also has codes 0x25 and 0x27: the macro ST25DV_AM_I_CMOS calls them CMOS variants, and ST25DV_AM_I_OPEN_DRAIN calls 0x24 and 0x26 open-drain variants. But Init accepts only 0x24 and 0x26: this driver rejects a CMOS tag during identification.


94а.5 Password and I²C session

The system area holds settings that security depends on: zone boundaries, their protection, the mailbox mode. You can write there only after presenting the password. The driver comments on such functions all say the same thing: the write takes effect only after the I²C password has been presented. In the experiment, the model answered NACK without the password:

== 3. зоны без пароля
W 0xAE 0x0005 [  1] 0F
        модель: NACK — системная область без пароля не пишется
WriteEndZonex = -102
== 4. пароль
W 0xAE 0x0900 [ 17] 00 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00 00
        модель: пароль верный, сессия I2C открыта
        опрос готовности: 1 раз, из них NACK 0
PresentI2CPassword = 0
R 0xA6 0x2004 [  1] 01
сессия = 1

The password is 64 bits. The driver builds a frame of 17 bytes: the password (8 bytes, most significant byte first), the code 0x09, and the same password once more. Changing the password is the same frame with the code 0x07, with the new password in both places, and it can be done only inside an open session. Whether the session is open is shown by the dynamic register I2C_SSO_DYN (0x2004); -102 is ST25DV_NACK.

A password in firmware is as much a secret as an encryption key: whoever reads it from Flash can reconfigure the tag.


94а.6 Memory zones

The user EEPROM can be split into four zones, each with its own protection — separately for radio and for I²C. The boundaries are stored in ENDA1… ENDA3 in units of 32 bytes: the number of the zone's last 32-byte chunk.

ST25DV_CreateUserZone(512, 512, 512, 512) in the experiment:

W 0xAE 0x0009 [  1] 3F
W 0xAE 0x0007 [  1] 3F
W 0xAE 0x0005 [  1] 3F
W 0xAE 0x0005 [  1] 0F
W 0xAE 0x0007 [  1] 1F
W 0xAE 0x0009 [  1] 2F

First all three boundaries are set to the end of memory: 2048 / 32 − 1 = 0x3F, in the order 3, 2, 1. Then the real ones are written: 512 / 32 − 1 = 0x0F, 1024 / 32 − 1 = 0x1F, 1536 / 32 − 1 = 0x2F, now in the order 1, 2, 3. On the first step the tag may answer NACK — when ENDA2 or ENDA3 becomes equal to the previous boundary. The driver comment calls such a NACK normal, and the function carries on. Lengths that are not multiples of 32 the driver rounds down.

Each of the six writes goes to EEPROM, and after each one the driver waits for ready (94а.7). The memory size is read twice along the way: once in CreateUserZone, the second time in the nested InitEndZone, six extra reads in total.

ST25DV zones: 2048 bytes split into four zones of 512, boundaries ENDA1…ENDA3 = 0x0F, 0x1F, 0x2F in chunks of 32 bytes; the driver first sets all three to the end 0x3F in the order 3, 2, 1, then into place in the order 1, 2, 3


94а.7 Writing EEPROM: chunks of 256 bytes and ready polling

== 6. запись 300 байт в EEPROM
W 0xA6 0x0000 [256] 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 …
        опрос готовности: 4 раз, из них NACK 3
W 0xA6 0x0100 [ 44] 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 …
        опрос готовности: 4 раз, из них NACK 3
WriteData = 0

The tag accepts no more than 256 bytes in one transaction, so 300 bytes went out in two chunks: 256 from address 0x0000 and 44 from 0x0100.

After each chunk the tag spends some time writing the EEPROM and does not answer at its address. The driver calls IsReady in a loop until it gets an ACK — this is ready polling (acknowledge polling). The wait limit is ST25DV_WRITE_TIMEOUT = 320 ms; a comment in the driver derives it as 256 bytes / 4 bytes per internal page × 5 ms per page. If the tag never answers, the function returns ST25DV_TIMEOUT.

Writing 300 bytes to the ST25DV EEPROM: a 256-byte chunk from address 0x0000, ready polling — three NACKs and an ACK, then a 44-byte chunk from 0x0100 and four polls again; the 320 ms wait limit is 256 bytes over a 4-byte internal page at 5 ms each

After writes to the dynamic registers and the mailbox the driver does not wait for ready: polling follows only writes to EEPROM and the system area.


94а.8 The mailbox and the GPO pin

== 7. почтовый ящик
R 0xA6 0x2006 [  1] 00
W 0xA6 0x2006 [  1] 01
W 0xA6 0x2008 [ 10] 74 65 6D 70 3D 32 33 2E 35 43
WriteMailboxData = 0
WriteMailboxData(257) = -1
== 8. вывод GPO
W 0xAE 0x0000 [  1] C8
        опрос готовности: 4 раз, из них NACK 3
ConfigIT = 0

ST25DV_SetMBEN_Dyn enables the mailbox in read-modify-write fashion: it read MB_CTRL_DYN, set the MBEN bit (0x01), and wrote it back. On a real tag this is not enough: MBEN is set only if fast transfer is allowed by the static register MB_MODE (0x000D, bit 0), and from the factory it holds 0 — so the tag does not accept the write to MB_CTRL_DYN. Therefore, once, after PresentI2CPassword, you call ST25DV_WriteMBMode(…, ST25DV_ENABLE): this is a write to the system area, with ready polling, like for GPO. The value is stored in EEPROM, and from then on SetMBEN_Dyn works at every power-up. The model in the experiment does not emulate this restriction, so it does not show up in the log. The message temp=23.5C is 10 bytes from address 0x2008. The driver does not write more than 256 bytes into the mailbox: for 257 it returned -1 (ST25DV_ERROR) without going out on the bus.

GPO is a tag pin that you can wire to an EXTI input of the microcontroller (section 7). The GPO register selects which events toggle it. By the masks from st25dv_reg.h:

Bit Mask Event
0 0x01 RFUSERSTATE — a state set over the radio
1 0x02 RFACTIVITY — activity over the radio
2 0x04 RFINTERRUPT — an interrupt requested over the radio
3 0x08 FIELDCHANGE — the field appeared or disappeared
4 0x10 RFPUTMSG — the phone put a message into the mailbox
5 0x20 RFGETMSG — the phone took a message
6 0x40 RFWRITE — a write over the radio
7 0x80 ENABLE — the pin is enabled at all

In the experiment 0xC8 = 0x80 | 0x40 | 0x08: the pin is enabled and reacts to a write by the phone and to the field. GPO is a system register, so it needs the password and ready polling. After the interrupt the firmware reads IT_STS_DYN (0x2005) to find out what exactly happened. The pulse duration is selected in ITTIME from eight values — from 302 down to 37 µs.

The GPO and IT_STS_DYN registers one above the other: the three low bits match, above them IT_STS_DYN is shifted by one, and a write by the phone is 0x40 in GPO and 0x80 in IT_STS_DYN; below, the path of the event from a radio write through the GPO pulse and EXTI to reading IT_STS_DYN


94а.9 What the phone sees

A phone without a special app reads from the tag not bytes but an NDEF message — a standard wrapper for a link, text or an app record. At the start of the tag memory lies a service file, CC (Capability Container): from it the phone learns that the tag is formatted for NDEF and how much room the message has. The driver can forbid writes to the blocks of the CC file — one or two (LOCKCCFILE, 0x000C) — so that a stray write does not break the formatting.

The byte-level format of CC and NDEF is defined by the NFC Forum specifications; this section does not cover them. In practice a device does this: it gives the first blocks to the CC and an NDEF message (for example, a link to the device's page), and keeps its own data in a separate zone closed to radio writes.


94а.10 Pitfalls

The tag does not answer on I²C while a phone is nearby. The driver comment on IsDeviceReady says exactly this: during a radio exchange I²C gets NACK. The firmware must retry the request, not fail with a bus error.

Writing settings "does not work". The system area is not written without the password (94а.5). First PresentI2CPassword, then everything else.

The wrong address. The code has 0xA6/0xAE, a bus scanner shows 0x53/0x57 (94а.2). The system registers need the second address: there is no single "tag address".

The comment lies about GPO. Above ST25DV_GetGPOStatus and ST25DV_ConfigureGPO the driver lists the bits RFBUSY = 0x02, FIELDFALLING = 0x08, FIELDRISING = 0x10, RFPUTMSG = 0x20, RFGETMSG = 0x40, RFWRITE = 0x80. This is the layout of IT_STS_DYN, not GPO: in GPO a write by the phone is 0x40, and 0x80 is the pin enable (94а.8). Trust the ST25DV_GPO_*_MASK masks.

A CMOS tag fails Init (94а.4): the driver knows its codes, but during identification compares only with 0x24 and 0x26.

Extra write cycles. EEPROM wears out, while mailbox messages live in RAM. Frequent short exchanges go through the mailbox; to EEPROM only what must survive a power-off.


94а.11 Summary

Next: section 94б "Flipper Zero inside: STM32WB55, FreeRTOS and 433 MHz radio, NFC, RFID and IR in one firmware" — an open device where four more radio paths work alongside NFC.

Open in the interactive handbook