18г. RFID reader RC522: MFRC522 over SPI, card search, UID, anticollision, reading MIFARE Classic blocks
Section 18 covered the SPI bus with flash memory, an SD card and shift registers, sections 18а–18в covered sound. Here is one more SPI device, the most common one in beginner kits: the RC522 module, a reader of contactless cards and key fobs at 13.56 MHz. The firmware talks to it on two levels: over SPI with the registers of the MFRC522 chip, and through the chip over radio with the card. The tag on the other side, read by a phone, was in section 94а, the NFC reader in the Flipper Zero in section 94б, the near field at 13.56 MHz in section 89.5.
Sources: RM0008 (DocID13902 Rev 17): APB2 runs at full speed up to
72 MHz, p. 48; the SPI1EN bit, p. 112; SPI pin configuration, table 25,
p. 166; SPI1 without remap is NSS/PA4, SCK/PA5, MISO/PA6, MOSI/PA7, p. 185;
SPI_CR1: BR[2:0], CPOL, CPHA, pp. 742–743; the BSY, TXE,
RXNE flags, pp. 745–746. DocID13587 Rev 17 (STM32F103x8/xB): PA4–PA7 and
PB0 are not 5 V tolerant, p. 29. The project has no MFRC522 document and no
ISO 14443 standard: the register addresses, bits and commands of the chip
are taken from the manufacturer's documentation and not checked, the card
commands are the well-known ones. All numbers below come from a run of the
driver _verify/rfid18e/rfid18e.c on a computer (gcc 12.2.0) with our own
model of the MFRC522 and the cards in Python 3.11; the firmware is built by
armclang 6.24 for the Cortex-M3.
What it is called in the documentation
Original Meaning PCD — proximity coupling device reader PICC — proximity card card, key fob, tag UID — unique identifier card number, 4, 7 or 10 bytes REQA / WUPA request idle cards / wake up halted ones too ATQA — answer to request answer to the request: 2 bytes anticollision, cascade level anticollision, cascade level BCC — block check character XOR of the four UID bytes SAK — select acknowledge answer to select: the card type HLTA put the card to sleep sector trailer sector trailer: keys and access bits Transceive the "transmit and receive" command
18г.1 The RC522 module
The module board carries the MFRC522 chip, a crystal, matching parts and the antenna: a coil etched along the edge of the board. The chip itself creates the 13.56 MHz field, modulates it with commands and decodes the card's answer per ISO 14443A. The air rate is 106 kbit/s: a bit lasts 128 carrier periods, 128 / 13.56 MHz = 9.44 µs. The card has no battery of its own: it is powered by the reader's field (section 89.5), so the module reads at centimetres.
The chip has three host interfaces: SPI, I²C and UART; RC522 modules usually wire up SPI, and that is what we use. Inside there are registers with a six-bit address, up to 64 of them, a 64-byte FIFO, a timer, a CRC coprocessor and a Crypto1 cipher block for MIFARE Classic cards. The firmware puts a frame into the FIFO and gives a command; the chip sends it, waits for the answer and puts it into the same FIFO.
The chip version is in the VersionReg register (0x37). The driver
accepts 0x91 and 0x92, versions 1.0 and 2.0 per the manufacturer's
documentation (not checked); the model answers 0x92. If it reads 0x00
or 0xFF, there is no SPI link.
18г.2 Connecting to the F103: SPI1, address byte, reset, supply
SPI1 without remap sits on PA4–PA7 (p. 185). The module's SDA pin is chip
select; we drive it ourselves as a plain output on PA4 (software CS, as
in section 18.4). RST goes to PB0. SPI1 is on APB2, which runs at full
speed, 72 MHz (p. 48); the divider 16 (BR = 011, p. 743) gives 4.5 MHz.
Mode 0: CPOL = 0, CPHA = 0, MSB first; this is the MFRC522 mode per the
manufacturer's documentation, not checked. The chip has its own SPI
frequency limit, but there is no document: 4.5 MHz is taken with margin.
void rc_spi(const uint8_t *tx, uint8_t *rx, uint32_t n)
{
GPIOA->BRR = 1u << 4; /* SDA (CS) вниз */
for (uint32_t i = 0; i < n; i++) {
while (!(SPI1->SR & SPI_SR_TXE)) {
}
SPI1->DR = tx[i];
while (!(SPI1->SR & SPI_SR_RXNE)) {
}
rx[i] = (uint8_t)SPI1->DR; /* полный дуплекс: байт за байт */
}
while (SPI1->SR & SPI_SR_BSY) {
}
GPIOA->BSRR = 1u << 4; /* CS вверх — конец транзакции */
}
The address byte. The first byte of every transaction is the register
address, but not as is: bit 7 is read (1) or write (0), bits 6:1 are the
address, bit 0 is zero. The address is shifted left by 1. Reading
VersionReg (0x37) starts with the byte 0xEE, writing to CommandReg
(0x01) with the byte 0x02. On a read the data comes with the second
byte: while the master sends the address, the slave does not yet know what
to answer. The bench model checks bit 0 of every address byte: over the
whole run there was not a single such byte.
void rc_write(uint8_t reg, uint8_t v)
{
uint8_t tx[2] = {(uint8_t)((reg << 1) & 0x7Eu), v}, rx[2];
rc_spi(tx, rx, 2);
}
uint8_t rc_read(uint8_t reg)
{
uint8_t tx[2] = {(uint8_t)(0x80u | ((reg << 1) & 0x7Eu)), 0}, rx[2];
rc_spi(tx, rx, 2);
return rx[1]; /* первый байт MISO — мусор, данные приходят со вторым */
}
A byte at 4.5 MHz takes 1.778 µs, a register read is two bytes, 3.56 µs.
Reset and setup. rc_init pulls RST low for 1 ms, waits 50 ms, issues
the SoftReset command and reads the version. Then comes the timer that
limits the wait for the card's answer: TPrescaler = 0xA9 gives
13.56 MHz / (2 · 169 + 1) = 40 kHz, reload 1000 gives 25.025 ms (the
formula is per the manufacturer's documentation, not checked; the model
computes by it). ModeReg = 0x3D sets the CRC start value 0x6363. The
last step is turning the antenna on: after reset TxControlReg is 0x80,
the antenna pins are off, after rc_init it is 0x83. In the model the
whole setup is 12 SPI transactions, 24 bytes and 51.04 ms, almost all of
it the wait after reset.
Supply is 3.3 V. The MFRC522 is a 3.3 V chip, and the F103 pins PA4–PA7 and PB0 are not 5 V tolerant either (p. 29). The module's 3.3V pin goes to the board's 3.3 V, not to 5 V.
18г.3 Registers, FIFO, Transceive and CRC
An exchange with the card is one function. It stops the chip, clears the
flags, flushes the FIFO, puts the frame there, writes to BitFramingReg how
many bits are in the last byte and from which bit to store the answer,
issues the Transceive command and the StartSend bit. Then it polls
ComIrqReg: receive (0x20), idle (0x10) or timer (0x01).
int rc_transceive(rc_t *r, const uint8_t *tx, uint32_t n, uint8_t tx_last,
uint8_t *rx, uint32_t *rx_n, uint8_t *rx_last, uint8_t rx_align)
{
uint8_t irq = 0;
*rx_n = 0;
rc_write(RC_COMMAND, RC_CMD_IDLE);
rc_write(RC_COMIRQ, 0x7Fu); /* снять все флаги */
rc_write(RC_FIFOLEVEL, 0x80u); /* очистить FIFO */
fifo_write(tx, n);
rc_write(RC_BITFRAMING, (uint8_t)((rx_align << 4) | tx_last));
rc_write(RC_COMMAND, RC_CMD_TRANSCEIVE);
rc_write(RC_BITFRAMING, (uint8_t)(0x80u | (rx_align << 4) | tx_last)); /* StartSend */
for (r->polls = 1; r->polls <= POLL_MAX; r->polls++) {
irq = rc_read(RC_COMIRQ);
if (irq & 0x31u) /* приём, простой или таймер */
break;
}
rc_write(RC_BITFRAMING, 0);
if (!(irq & 0x30u))
return RC_TIMEOUT; /* таймер: карта не ответила (или POLL_MAX) */
uint8_t err = rc_read(RC_ERROR);
if (err & 0x13u)
return RC_ERR; /* FIFO переполнен, чётность, протокол */
uint32_t k = rc_read(RC_FIFOLEVEL) & 0x7Fu;
if (k > 64u)
return RC_ERR;
fifo_read(rx, k);
*rx_n = k;
*rx_last = rc_read(RC_CONTROL) & 0x07u;
if (err & 0x08u) { /* коллизия: где? */
uint8_t c = rc_read(RC_COLL);
if (c & 0x20u)
return RC_ERR;
r->coll_pos = (c & 0x1Fu) ? (c & 0x1Fu) : 32u;
r->colls++;
return RC_COLLISION;
}
return RC_OK;
}
The FIFO is written and read in one transaction: on a write, one address byte and then all the data in a row; on a read, the FIFODataReg address as many times as there are bytes to read, and a zero at the end.
Polling costs more than the data. An empty field: REQA goes out, no one answers, and the driver reads ComIrqReg 7063 times until the timer says "silence": 25.141 ms and 14 142 bytes over SPI. With a card REQA takes 102 polls and 0.407 ms. Polling can be replaced by an interrupt: the MFRC522 has an IRQ pin, but this driver does not use it.
CRC_A. The SELECT, READ and HLTA frames end with two CRC_A bytes: the
polynomial x¹⁶ + x¹² + x⁵ + 1, start value 0x6363, low byte first. For
outgoing frames the driver calls the chip's coprocessor (the CalcCRC
command); the card's answer it checks with its own function crc_a, with
no extra SPI transactions. The check in the run: the coprocessor (in the
model, a bitwise calculation in Python) and crc_a in C gave the same on
the SELECT frame (28 C1), HLTA 50 00 (57 CD), READ 30 04 (26 EE),
60 04 (D1 3D), 00 00 (A0 1E) and 12 34 (26 CF), and on 200
random frames from 1 to 18 bytes long, on all 200. The CRC over a frame
together with its CRC gives 0; that is how a received frame is checked.
This is the self-consistency of three calculations, not a check against
the standard: it is not on the disk.
18г.4 Finding a card: REQA, anticollision, SELECT, SAK
REQA and ATQA. The search starts with a short 7-bit frame: REQA (26)
wakes the cards that lie idle in the field. Each answers with the two bytes
of ATQA; card A in the model gives 04 00. WUPA (52) also wakes the
cards put to sleep by HLTA.
Anticollision. Next the reader sends 93 20: "cascade 1, I know 0 bits
of your UID, answer". The byte 20 is NVB: the high nibble is how many
whole bytes are in the frame (2: the command itself and NVB), the low
nibble is how many more bits. One card answers with 40 bits: 4 UID bytes
and BCC, the XOR of those four bytes. Card A has 3A 9F 12 C4, BCC 73.
The driver checks BCC and sends SELECT: 93 70, the UID, BCC and CRC
28 C1; the card answers with SAK 08 and a CRC. The whole procedure for
one card in the model is 558 SPI transactions, 1137 bytes, 2.021 ms.
Two cards. With two cards in the field they answer at the same time,
and where their bits differ the chip sees a collision and writes its
position into CollReg. The UIDs 3A 9F 12 C4 and 3A 9F 52 C4 differ in
bit 23, and the model reported exactly that. The driver sets this bit to 1,
counts 23 bits as known and sends them: 93 47 3A 9F 52, 39 bits, NVB
47: 4 whole bytes and 7 bits. Now only the card with a one in bit 23
answers, and its answer goes into the FIFO from bit 7: that is the
alignment field in BitFramingReg.
for (;;) {
uint32_t nb = known / 8u, nbit = known % 8u;
buf[1] = (uint8_t)(((2u + nb) << 4) | nbit); /* NVB: сколько байт и бит уходит */
int st = rc_transceive(r, buf, 2u + nb + (nbit ? 1u : 0u), (uint8_t)nbit, rx, &n, &last, (uint8_t)nbit);
if (st != RC_OK && st != RC_COLLISION)
return st;
r->rounds++;
uint8_t keep = (uint8_t)((1u << nbit) - 1u); /* младшие биты первого байта — наши */
for (uint32_t i = 0; i < n && 2u + nb + i < 7u; i++)
buf[2u + nb + i] = i ? rx[i] : (uint8_t)((buf[2u + nb] & keep) | (rx[0] & (uint8_t)~keep));
if (st == RC_OK)
break;
uint32_t pos = nb * 8u + r->coll_pos; /* номер бита в CLn, от 1 */
if (pos <= known || pos > 32u)
return RC_ERR;
buf[2u + (pos - 1u) / 8u] |= (uint8_t)(1u << ((pos - 1u) % 8u)); /* идём по ветке «1» */
known = pos;
if (r->rounds > 40u)
return RC_ERR;
}
The driver puts the selected card to sleep with HLTA and sends REQA again:
now only the second one answers. The scan of two cards found 3A 9F 52 C4
first, then 3A 9F 12 C4, with one collision, in 81.675 ms. Of that,
76.0 ms are three waits of 25 ms: the card does not answer HLTA, and the
"success" here is a timeout, while the last REQA waits to see that no one
is left. After the scan both cards sleep: REQA gets silence, WUPA gets
ATQA 04 00.
Cascades. A UID is 4, 7 or 10 bytes, while an anticollision frame
carries 4. A card with a 7-byte UID sends in cascade 1 the CT byte (88)
and three UID bytes, SAK 04 means "the UID is not complete", and the
driver repeats it all with 95, cascade 2. Card B (04 52 8A 1B 3C 5D 80)
gave 88 04 52 8A, BCC 54, SAK 04, then 1B 3C 5D 80, BCC FA, SAK
00. With cards A and B together there were two collisions: in ATQA
(04 00 and 44 00 differ in bit 7) and in cascade 1, at bit 2, where
88 meets 3A. The driver lets a collision in ATQA pass: the
anticollision will sort out the UID anyway.
SAK tells what the card is: 08 is MIFARE Classic 1K, 00 is a card
without a cipher such as MIFARE Ultralight (well-known values, not checked
against a document). In the model card B is such a card: it gives pages 4–7
to the READ command without any keys, and authentication on it ends with a
timeout.
18г.5 MIFARE Classic: sector, block, key A, reading
The memory of a MIFARE Classic 1K is 16 sectors of 4 blocks of 16 bytes, 1024 bytes. Block 0 is the factory block: UID, BCC, SAK, ATQA. The last block of each sector is the trailer: key A (6 bytes), access bits (4 bytes), key B (6 bytes). A block can be read only after authentication in its sector.
Authentication is done by the chip itself, with the MFAuthent command: the
FIFO gets 60 (key A), the block number, 6 key bytes and the last 4 bytes
of the UID. If the key fits, the MFCrypto1On bit rises in Status2Reg, and
from then on the exchange with the card is encrypted; the firmware does
not see it, the chip encrypts and decrypts by itself. The key
FF FF FF FF FF FF is the factory one, well known; new cards are usually
sold with it.
int rc_read_block(rc_t *r, uint8_t block, uint8_t out[16])
{
uint8_t buf[4] = {MF_READ, block}, rx[64], last = 0;
uint32_t n = 0;
if (rc_calc_crc(buf, 2, buf + 2) != RC_OK)
return RC_TIMEOUT;
int st = rc_transceive(r, buf, 4, 0, rx, &n, &last, 0);
if (st != RC_OK)
return st;
if (n == 1 && last == 4)
return RC_NAK; /* 4 бита вместо 18 байт: карта отказала */
if (n != 18 || last != 0)
return RC_ERR;
if (!r->no_crc_check && crc_a(rx, 16) != (uint16_t)(rx[16] | rx[17] << 8))
return RC_CRC; /* ловит и помехи в эфире, и сбой на проводах SPI */
memcpy(out, rx, 16);
return RC_OK;
}
In the model: authentication in block 4 takes 1.993 ms, reading block 4
takes 2.107 ms, 582 SPI transactions and 1185 bytes; the block holds the 16
bytes Hello, STM32F103. The trailer of the same sector (block 7) reads
like this: zeros in place of key A (key A cannot be read from the card),
then the access bits FF 07 80 69 and key B (the model's behaviour
follows what is described for the cards, not checked). Block 0 from
another sector the card did not give: instead of 18 bytes came 4 bits of
refusal. With a wrong key the card falls silent, a timeout of 25.424 ms,
and after that does not answer READ either: it is woken again with WUPA,
selected and authenticated anew.
18г.6 Why this is not security
The MIFARE Classic key is 48 bits, the Crypto1 cipher is NXP's own algorithm. It was reconstructed and published long ago, and weaknesses were found both in the cipher itself and in the card's random number generator; since then there are known ways to get the sector keys with the card in hand or by eavesdropping on the exchange. How exactly is not the topic of this section. The conclusion for the firmware is simple: MIFARE Classic and the RC522 are not fit for security: a lock, an access pass, money.
Weaker still is the scheme "let in by UID". The card gives its UID to any reader before any authentication; in 18г.4 it came in the very first answer. Cards with a rewritable block 0 are on sale, and a UID can be copied onto them. A UID is a number for identification, not a password.
For inventory, toys, picking a profile or "tap the fob to switch on the lamp" the RC522 is good. Security needs cards with proven strong cryptography and readers that support it; on cryptography in a microcontroller, see section 43.
18г.7 Pitfalls
The version reads as 0x00 or 0xFF. There is no SPI link: the wrong
mode, MISO and MOSI swapped, SDA not going low, or the module unpowered.
The driver goes no further in this case.
The module on 5 V. The MFRC522 is a 3.3 V part, the F103 pins PA4–PA7 and PB0 are not 5 V tolerant (p. 29). Power the module from 3.3 V.
A card is never found. After reset the antenna is off: TxControlReg =
0x80. In the model without turning it on REQA ends with a timeout, the
scan finds 0 cards; after turning it on (0x83) the same card is found in
53.024 ms.
Long SPI wires. Wires tens of centimetres long on a breadboard give bit errors. In the model each MISO bit flips with probability p, 200 block reads each:
| p | CRC check | correct | CRC error | other errors | wrong data |
|---|---|---|---|---|---|
| 10⁻⁴ | yes | 159 | 4 | 37 | 0 |
| 10⁻⁴ | no | 165 | 0 | 33 | 2 |
| 10⁻⁵ | yes | 195 | 0 | 5 | 0 |
| 10⁻⁵ | no | 195 | 0 | 5 | 0 |
A block read is 9480 bits over SPI, of which 144 are useful (the 18 bytes of the answer): the rest is polling and service registers, and most errors land there. The CRC of the answer checks the data over the whole path, in the air and on the wires: with the check wrong data is 0, without it 2 out of 200. The cure is shorter wires and a lower SPI frequency.
Two cards in the field. Both answer at once. Without anticollision the driver would get a mix of bits; with it one card is selected, and the others are found after HLTA (18г.4).
HLTA "does not work". The card does not answer HLTA: the driver waits for the timeout and counts it as success. Each HLTA takes 25.454 ms.
The card no longer answers after a wrong key. It has left the selected state: WUPA, SELECT and authentication anew.
18г.8 Summary
- RC522 is an MFRC522 at 13.56 MHz, ISO 14443A, 106 kbit/s; a bit is 9.44 µs.
- SPI1 on the F103: PA4 is SDA (CS by hand), PA5 SCK, PA6 MISO, PA7 MOSI, mode 0, 72 MHz / 16 = 4.5 MHz; supply 3.3 V.
- Address byte: bit 7 is read, bits 6:1 the address, bit 0 is 0;
VersionReg is
0xEE, the answer is0x92in the model. - After reset the antenna is off: TxControlReg
0x80→0x83. - Timer 40 kHz × 1000 is 25.025 ms; an empty field costs 7063 polls.
- REQA → ATQA
04 00;93 20→ UID3A 9F 12 C4, BCC73; SELECT with CRC28 C1→ SAK08; one card takes 2.021 ms. - Two cards: a collision at bit 23, both found in 81.675 ms, of which 76.0 ms are timeouts.
- MIFARE Classic: 16 sectors × 4 blocks × 16 bytes; the default key A is
FF FF FF FF FF FF; a block read takes 2.107 ms. - Crypto1 is broken, the UID is no secret: the RC522 and MIFARE Classic are not fit for security.
Full sources
rfid18e.h — MFRC522 registers and commands, card commands, the driver interface.
/* rfid18e.h — раздел 18г: драйвер считывателя MFRC522 (модуль RC522) без регистров STM32.
* Драйвер говорит с микросхемой через три функции платформы: rc_spi, rc_rst, rc_delay_ms.
* В прошивке fw18e.c их даёт SPI1 и GPIO STM32F103, в стенде host18e.c — модель model18e.py.
* Адреса регистров, команды и биты MFRC522 — по документации производителя (в проекте её нет, не проверено);
* команды карты — ISO 14443A и MIFARE Classic, общеизвестные. */
#ifndef RFID18E_H
#define RFID18E_H
#include <stdint.h>
/* ── регистры MFRC522 ─────────────────────────────────────────────────── */
#define RC_COMMAND 0x01u
#define RC_COMIRQ 0x04u /* флаги: 0x01 таймер, 0x02 ошибка, 0x10 простой, 0x20 приём */
#define RC_DIVIRQ 0x05u /* 0x04 — CRC посчитан */
#define RC_ERROR 0x06u /* 0x01 протокол, 0x02 чётность, 0x04 CRC, 0x08 коллизия, 0x10 FIFO */
#define RC_STATUS2 0x08u /* 0x08 — шифрование Crypto1 включено */
#define RC_FIFODATA 0x09u
#define RC_FIFOLEVEL 0x0Au /* сколько байт в FIFO; запись 0x80 — очистить */
#define RC_CONTROL 0x0Cu /* биты 2:0 — сколько бит в последнем принятом байте */
#define RC_BITFRAMING 0x0Du /* 0x80 начать передачу, 6:4 выравнивание приёма, 2:0 бит в последнем байте */
#define RC_COLL 0x0Eu /* 4:0 — номер бита первой коллизии, 0x20 — номер неверен */
#define RC_MODE 0x11u
#define RC_TXCONTROL 0x14u /* биты 1:0 — антенна (выводы TX1, TX2) */
#define RC_TXASK 0x15u
#define RC_CRCRESULT_H 0x21u
#define RC_CRCRESULT_L 0x22u
#define RC_TMODE 0x2Au
#define RC_TPRESCALER 0x2Bu
#define RC_TRELOAD_H 0x2Cu
#define RC_TRELOAD_L 0x2Du
#define RC_VERSION 0x37u
/* ── команды MFRC522 ──────────────────────────────────────────────────── */
#define RC_CMD_IDLE 0x00u
#define RC_CMD_CALCCRC 0x03u
#define RC_CMD_TRANSCEIVE 0x0Cu
#define RC_CMD_MFAUTHENT 0x0Eu
#define RC_CMD_SOFTRESET 0x0Fu
/* ── команды карты ────────────────────────────────────────────────────── */
#define PICC_REQA 0x26u /* короткий кадр 7 бит: ответят карты в покое */
#define PICC_WUPA 0x52u /* 7 бит: ответят и усыплённые HLTA */
#define PICC_SEL1 0x93u /* каскад 1; 0x95 — 2, 0x97 — 3 */
#define PICC_CT 0x88u /* в каскаде 1: «UID длиннее, дальше каскад 2» */
#define PICC_HLTA 0x50u
#define MF_AUTH_A 0x60u
#define MF_READ 0x30u
/* ── ответ драйвера ───────────────────────────────────────────────────── */
enum { RC_OK, RC_TIMEOUT, RC_COLLISION, RC_ERR, RC_CRC, RC_NAK, RC_NOCHIP, RC_BCC };
typedef struct {
uint8_t uid[10];
uint8_t len; /* 4, 7 или 10 байт */
uint8_t sak; /* SAK последнего каскада */
uint8_t atqa[2];
} rc_card_t;
typedef struct {
uint8_t version; /* VersionReg после сброса */
uint8_t coll_pos; /* номер бита последней коллизии в CLn, от 1 */
uint8_t rounds; /* шагов антиколлизии в последнем rc_select */
uint8_t no_crc_check; /* 1 — не проверять CRC ответа (для опыта в 18г.7) */
uint32_t polls; /* чтений ComIrqReg в последнем обмене */
uint32_t colls; /* всего коллизий */
} rc_t;
/* ── платформа: даёт прошивка или стенд ───────────────────────────────── */
void rc_spi(const uint8_t *tx, uint8_t *rx, uint32_t n); /* CS вниз, n байт, CS вверх */
void rc_rst(int level); /* вывод RST модуля */
void rc_delay_ms(uint32_t ms);
/* ── драйвер ──────────────────────────────────────────────────────────── */
void rc_write(uint8_t reg, uint8_t v);
uint8_t rc_read(uint8_t reg);
int rc_init(rc_t *r, int antenna);
uint16_t crc_a(const uint8_t *d, uint32_t n); /* CRC_A программно: для сверки с микросхемой */
int rc_calc_crc(const uint8_t *d, uint32_t n, uint8_t out[2]);
int rc_transceive(rc_t *r, const uint8_t *tx, uint32_t n, uint8_t tx_last, /* rx — 64 байта */
uint8_t *rx, uint32_t *rx_n, uint8_t *rx_last, uint8_t rx_align);
int rc_request(rc_t *r, uint8_t cmd, uint8_t atqa[2]);
int rc_select(rc_t *r, rc_card_t *c);
int rc_halt(rc_t *r);
int rc_auth(rc_t *r, uint8_t block, const uint8_t key[6], const rc_card_t *c);
void rc_stop_crypto(void);
int rc_read_block(rc_t *r, uint8_t block, uint8_t out[16]);
int rc_scan(rc_t *r, rc_card_t *cards, int max);
#endif
rfid18e.c — the driver without STM32 registers: shared by the firmware and the bench.
/* rfid18e.c — раздел 18г: драйвер MFRC522 — регистры, FIFO, Transceive, CRC_A, REQA, антиколлизия,
* SELECT по каскадам, HLTA, аутентификация ключом A и чтение блока MIFARE Classic. Без регистров STM32. */
#include <string.h>
#include "rfid18e.h"
#define POLL_MAX 40000u /* предохранитель: таймер MFRC522 (25 мс) сработает раньше */
/* ── регистры: адресный байт — бит 7 чтение, биты 6:1 адрес, бит 0 — ноль ── */
void rc_write(uint8_t reg, uint8_t v)
{
uint8_t tx[2] = {(uint8_t)((reg << 1) & 0x7Eu), v}, rx[2];
rc_spi(tx, rx, 2);
}
uint8_t rc_read(uint8_t reg)
{
uint8_t tx[2] = {(uint8_t)(0x80u | ((reg << 1) & 0x7Eu)), 0}, rx[2];
rc_spi(tx, rx, 2);
return rx[1]; /* первый байт MISO — мусор, данные приходят со вторым */
}
static void fifo_write(const uint8_t *d, uint32_t n)
{
uint8_t tx[65], rx[65];
tx[0] = (uint8_t)((RC_FIFODATA << 1) & 0x7Eu);
memcpy(tx + 1, d, n);
rc_spi(tx, rx, n + 1); /* один адрес — все байты подряд уходят в FIFO */
}
static void fifo_read(uint8_t *d, uint32_t n)
{
uint8_t tx[65], rx[65];
memset(tx, (int)(0x80u | ((RC_FIFODATA << 1) & 0x7Eu)), n);
tx[n] = 0; /* последний адрес — 0: «больше не читаю» */
rc_spi(tx, rx, n + 1);
memcpy(d, rx + 1, n);
}
int rc_init(rc_t *r, int antenna)
{
memset(r, 0, sizeof *r);
rc_rst(0); /* жёсткий сброс выводом RST */
rc_delay_ms(1);
rc_rst(1);
rc_delay_ms(50); /* запуск генератора — с запасом */
rc_write(RC_COMMAND, RC_CMD_SOFTRESET);
for (uint32_t i = 0; i < 100u && (rc_read(RC_COMMAND) & 0x10u); i++)
rc_delay_ms(1); /* бит PowerDown: микросхема ещё просыпается */
r->version = rc_read(RC_VERSION);
if (r->version != 0x91u && r->version != 0x92u)
return RC_NOCHIP; /* 0x00 или 0xFF — нет связи по SPI */
rc_write(RC_TMODE, 0x80u); /* TAuto: таймер стартует в конце передачи */
rc_write(RC_TPRESCALER, 0xA9u); /* 13,56 МГц / (2 · 169 + 1) = 40 кГц */
rc_write(RC_TRELOAD_H, 0x03u); /* 1000 отсчётов — 25 мс на ответ карты */
rc_write(RC_TRELOAD_L, 0xE8u);
rc_write(RC_TXASK, 0x40u); /* модуляция 100 % ASK, как требует ISO 14443A */
rc_write(RC_MODE, 0x3Du); /* CRC-сопроцессор начинает с 0x6363 */
rc_write(RC_COLL, 0x00u); /* биты после коллизии — нули */
if (antenna)
rc_write(RC_TXCONTROL, rc_read(RC_TXCONTROL) | 0x03u); /* после сброса антенна выключена */
return RC_OK;
}
uint16_t crc_a(const uint8_t *d, uint32_t n)
{
uint16_t crc = 0x6363u;
for (uint32_t i = 0; i < n; i++) {
uint8_t b = (uint8_t)(d[i] ^ (crc & 0xFFu));
b = (uint8_t)(b ^ (b << 4));
crc = (uint16_t)((crc >> 8) ^ ((uint16_t)b << 8) ^ ((uint16_t)b << 3) ^ (b >> 4));
}
return crc;
}
int rc_calc_crc(const uint8_t *d, uint32_t n, uint8_t out[2])
{
rc_write(RC_COMMAND, RC_CMD_IDLE);
rc_write(RC_DIVIRQ, 0x04u); /* снять флаг «CRC готов» */
rc_write(RC_FIFOLEVEL, 0x80u);
fifo_write(d, n);
rc_write(RC_COMMAND, RC_CMD_CALCCRC);
for (uint32_t i = 0; i < POLL_MAX; i++) {
if (rc_read(RC_DIVIRQ) & 0x04u) {
rc_write(RC_COMMAND, RC_CMD_IDLE);
out[0] = rc_read(RC_CRCRESULT_L); /* в эфир младший байт идёт первым */
out[1] = rc_read(RC_CRCRESULT_H);
return RC_OK;
}
}
return RC_TIMEOUT;
}
int rc_transceive(rc_t *r, const uint8_t *tx, uint32_t n, uint8_t tx_last,
uint8_t *rx, uint32_t *rx_n, uint8_t *rx_last, uint8_t rx_align)
{
uint8_t irq = 0;
*rx_n = 0;
rc_write(RC_COMMAND, RC_CMD_IDLE);
rc_write(RC_COMIRQ, 0x7Fu); /* снять все флаги */
rc_write(RC_FIFOLEVEL, 0x80u); /* очистить FIFO */
fifo_write(tx, n);
rc_write(RC_BITFRAMING, (uint8_t)((rx_align << 4) | tx_last));
rc_write(RC_COMMAND, RC_CMD_TRANSCEIVE);
rc_write(RC_BITFRAMING, (uint8_t)(0x80u | (rx_align << 4) | tx_last)); /* StartSend */
for (r->polls = 1; r->polls <= POLL_MAX; r->polls++) {
irq = rc_read(RC_COMIRQ);
if (irq & 0x31u) /* приём, простой или таймер */
break;
}
rc_write(RC_BITFRAMING, 0);
if (!(irq & 0x30u))
return RC_TIMEOUT; /* таймер: карта не ответила (или POLL_MAX) */
uint8_t err = rc_read(RC_ERROR);
if (err & 0x13u)
return RC_ERR; /* FIFO переполнен, чётность, протокол */
uint32_t k = rc_read(RC_FIFOLEVEL) & 0x7Fu;
if (k > 64u)
return RC_ERR;
fifo_read(rx, k);
*rx_n = k;
*rx_last = rc_read(RC_CONTROL) & 0x07u;
if (err & 0x08u) { /* коллизия: где? */
uint8_t c = rc_read(RC_COLL);
if (c & 0x20u)
return RC_ERR;
r->coll_pos = (c & 0x1Fu) ? (c & 0x1Fu) : 32u;
r->colls++;
return RC_COLLISION;
}
return RC_OK;
}
int rc_request(rc_t *r, uint8_t cmd, uint8_t atqa[2])
{
uint8_t rx[64], last = 0;
uint32_t n = 0;
int st = rc_transceive(r, &cmd, 1, 7, rx, &n, &last, 0); /* короткий кадр: 7 бит */
if (st == RC_COLLISION)
st = RC_OK; /* разные ATQA у двух карт — не беда, UID разберёт антиколлизия */
if (st != RC_OK)
return st;
if (n != 2 || last != 0)
return RC_ERR;
atqa[0] = rx[0];
atqa[1] = rx[1];
return RC_OK;
}
/* Один каскад: антиколлизия до 40 известных бит CLn (4 байта и BCC), потом SELECT. */
static int cascade(rc_t *r, uint8_t sel, uint8_t cl[5], uint8_t *sak)
{
uint8_t buf[9], rx[64], last = 0;
uint32_t known = 0, n = 0;
memset(buf, 0, sizeof buf);
buf[0] = sel;
for (;;) {
uint32_t nb = known / 8u, nbit = known % 8u;
buf[1] = (uint8_t)(((2u + nb) << 4) | nbit); /* NVB: сколько байт и бит уходит */
int st = rc_transceive(r, buf, 2u + nb + (nbit ? 1u : 0u), (uint8_t)nbit, rx, &n, &last, (uint8_t)nbit);
if (st != RC_OK && st != RC_COLLISION)
return st;
r->rounds++;
uint8_t keep = (uint8_t)((1u << nbit) - 1u); /* младшие биты первого байта — наши */
for (uint32_t i = 0; i < n && 2u + nb + i < 7u; i++)
buf[2u + nb + i] = i ? rx[i] : (uint8_t)((buf[2u + nb] & keep) | (rx[0] & (uint8_t)~keep));
if (st == RC_OK)
break;
uint32_t pos = nb * 8u + r->coll_pos; /* номер бита в CLn, от 1 */
if (pos <= known || pos > 32u)
return RC_ERR;
buf[2u + (pos - 1u) / 8u] |= (uint8_t)(1u << ((pos - 1u) % 8u)); /* идём по ветке «1» */
known = pos;
if (r->rounds > 40u)
return RC_ERR;
}
if ((uint8_t)(buf[2] ^ buf[3] ^ buf[4] ^ buf[5]) != buf[6])
return RC_BCC; /* BCC — XOR четырёх байт */
buf[1] = 0x70u; /* NVB = 7 байт: SELECT */
if (rc_calc_crc(buf, 7, buf + 7) != RC_OK)
return RC_TIMEOUT;
int st = rc_transceive(r, buf, 9, 0, rx, &n, &last, 0);
if (st != RC_OK)
return st;
if (n != 3 || last != 0)
return RC_ERR;
if (!r->no_crc_check && crc_a(rx, 1) != (uint16_t)(rx[1] | rx[2] << 8))
return RC_CRC;
memcpy(cl, buf + 2, 5);
*sak = rx[0];
return RC_OK;
}
int rc_select(rc_t *r, rc_card_t *c)
{
uint8_t cl[5], sak = 0;
r->rounds = 0;
c->len = 0;
for (uint8_t lvl = 0; lvl < 3u; lvl++) {
int st = cascade(r, (uint8_t)(PICC_SEL1 + 2u * lvl), cl, &sak);
if (st != RC_OK)
return st;
if (sak & 0x04u) { /* UID не весь: CT и три байта, дальше следующий каскад */
if (cl[0] != PICC_CT)
return RC_ERR;
memcpy(c->uid + c->len, cl + 1, 3);
c->len = (uint8_t)(c->len + 3u);
continue;
}
memcpy(c->uid + c->len, cl, 4);
c->len = (uint8_t)(c->len + 4u);
c->sak = sak;
return RC_OK;
}
return RC_ERR;
}
int rc_halt(rc_t *r)
{
uint8_t buf[4] = {PICC_HLTA, 0x00}, rx[64], last = 0;
uint32_t n = 0;
if (rc_calc_crc(buf, 2, buf + 2) != RC_OK)
return RC_TIMEOUT;
int st = rc_transceive(r, buf, 4, 0, rx, &n, &last, 0);
return st == RC_TIMEOUT ? RC_OK : RC_ERR; /* на HLTA карта молчит: успех — это таймаут */
}
int rc_auth(rc_t *r, uint8_t block, const uint8_t key[6], const rc_card_t *c)
{
uint8_t buf[12], irq = 0;
buf[0] = MF_AUTH_A;
buf[1] = block;
memcpy(buf + 2, key, 6);
memcpy(buf + 8, c->uid + c->len - 4u, 4); /* последние 4 байта UID */
rc_write(RC_COMMAND, RC_CMD_IDLE);
rc_write(RC_COMIRQ, 0x7Fu);
rc_write(RC_FIFOLEVEL, 0x80u);
fifo_write(buf, 12);
rc_write(RC_COMMAND, RC_CMD_MFAUTHENT);
for (r->polls = 1; r->polls <= POLL_MAX; r->polls++) {
irq = rc_read(RC_COMIRQ);
if (irq & 0x11u)
break;
}
if (!(irq & 0x10u))
return RC_TIMEOUT; /* ключ не тот: карта замолчала */
if (rc_read(RC_ERROR) & 0x1Bu)
return RC_ERR;
return (rc_read(RC_STATUS2) & 0x08u) ? RC_OK : RC_ERR;
}
void rc_stop_crypto(void)
{
rc_write(RC_STATUS2, rc_read(RC_STATUS2) & (uint8_t)~0x08u);
}
int rc_read_block(rc_t *r, uint8_t block, uint8_t out[16])
{
uint8_t buf[4] = {MF_READ, block}, rx[64], last = 0;
uint32_t n = 0;
if (rc_calc_crc(buf, 2, buf + 2) != RC_OK)
return RC_TIMEOUT;
int st = rc_transceive(r, buf, 4, 0, rx, &n, &last, 0);
if (st != RC_OK)
return st;
if (n == 1 && last == 4)
return RC_NAK; /* 4 бита вместо 18 байт: карта отказала */
if (n != 18 || last != 0)
return RC_ERR;
if (!r->no_crc_check && crc_a(rx, 16) != (uint16_t)(rx[16] | rx[17] << 8))
return RC_CRC; /* ловит и помехи в эфире, и сбой на проводах SPI */
memcpy(out, rx, 16);
return RC_OK;
}
int rc_scan(rc_t *r, rc_card_t *cards, int max)
{
int n = 0;
while (n < max) {
uint8_t atqa[2];
if (rc_request(r, PICC_REQA, atqa) != RC_OK)
break; /* в поле больше никого: все найденные усыплены */
if (rc_select(r, &cards[n]) != RC_OK)
break;
cards[n].atqa[0] = atqa[0];
cards[n].atqa[1] = atqa[1];
rc_halt(r); /* усыпить: на следующий REQA ответят остальные */
n++;
}
return n;
}
fw18e.c — STM32F103 firmware: SPI1, RST, output to USART1.
/* fw18e.c — раздел 18г: считыватель RC522 (MFRC522) на STM32F103 (BluePill), 72 МГц.
* SPI1 без ремапа: PA4 — SDA (выбор микросхемы, ведём сами), PA5 — SCK, PA6 — MISO, PA7 — MOSI; PB0 — RST.
* SPI — режим 0 (CPOL = 0, CPHA = 0), старшим битом вперёд, 72 МГц / 16 = 4,5 МГц.
* Модуль питается от 3,3 В. UID и блок 4 карты MIFARE Classic — в USART1 (PA9, 115200).
* Тактирование до 72 МГц — как в разделе 6 (здесь не повторяется); APB2 = 72 МГц. */
#include "stm32f10x.h"
#include "rfid18e.h"
#define CPU_HZ 72000000u
#define SPI_BR (SPI_CR1_BR_0 | SPI_CR1_BR_1) /* BR = 011: fPCLK / 16 */
_Static_assert(SPI_BR == 0x18u, "BR[2:0] = 011 — биты 5:3 SPI_CR1");
_Static_assert(CPU_HZ / 115200u == 625u, "USART1: BRR = 625 — 115 200 бод");
static const uint8_t key_ff[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; /* заводской ключ A — общеизвестный */
/* ── платформа драйвера ───────────────────────────────────────────────── */
void rc_spi(const uint8_t *tx, uint8_t *rx, uint32_t n)
{
GPIOA->BRR = 1u << 4; /* SDA (CS) вниз */
for (uint32_t i = 0; i < n; i++) {
while (!(SPI1->SR & SPI_SR_TXE)) {
}
SPI1->DR = tx[i];
while (!(SPI1->SR & SPI_SR_RXNE)) {
}
rx[i] = (uint8_t)SPI1->DR; /* полный дуплекс: байт за байт */
}
while (SPI1->SR & SPI_SR_BSY) {
}
GPIOA->BSRR = 1u << 4; /* CS вверх — конец транзакции */
}
void rc_rst(int level)
{
GPIOB->BSRR = level ? 1u : 1u << 16;
}
void rc_delay_ms(uint32_t ms)
{
uint32_t t0 = DWT->CYCCNT;
while (DWT->CYCCNT - t0 < ms * (CPU_HZ / 1000u)) {
}
}
/* ── вывод в USART1 ───────────────────────────────────────────────────── */
static void put(char c)
{
while (!(USART1->SR & USART_SR_TXE)) {
}
USART1->DR = (uint8_t)c;
}
static void puts_(const char *s)
{
while (*s)
put(*s++);
}
static void hex(const uint8_t *d, uint32_t n)
{
static const char h[] = "0123456789ABCDEF";
for (uint32_t i = 0; i < n; i++) {
put(h[d[i] >> 4]);
put(h[d[i] & 15u]);
}
}
static void board_init(void)
{
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN
| RCC_APB2ENR_SPI1EN | RCC_APB2ENR_USART1EN;
GPIOA->BSRR = 1u << 4; /* CS вверх до настройки вывода */
GPIOA->CRL = (GPIOA->CRL & 0x0000FFFFu)
| 0x3u << 16 /* PA4 — выход 50 МГц: CS */
| 0xBu << 20 /* PA5 — SCK, альтернативная функция */
| 0x4u << 24 /* PA6 — MISO, вход без подтяжки */
| 0xBu << 28; /* PA7 — MOSI, альтернативная функция */
GPIOA->CRH = (GPIOA->CRH & ~(0xFu << 4)) | 0xBu << 4; /* PA9 — USART1_TX */
GPIOB->CRL = (GPIOB->CRL & ~0xFu) | 0x3u; /* PB0 — RST модуля */
GPIOC->CRH = (GPIOC->CRH & ~(0xFu << 20)) | 0x2u << 20; /* PC13 — светодиод платы */
SPI1->CR1 = SPI_CR1_MSTR | SPI_BR | SPI_CR1_SSM | SPI_CR1_SSI; /* ведущий, режим 0, NSS программный */
SPI1->CR1 |= SPI_CR1_SPE;
USART1->BRR = CPU_HZ / 115200u;
USART1->CR1 = USART_CR1_UE | USART_CR1_TE;
}
int main(void)
{
static rc_t r;
rc_card_t c;
uint8_t atqa[2], blk[16];
board_init();
if (rc_init(&r, 1) != RC_OK) {
puts_("MFRC522: no answer, VersionReg=");
hex(&r.version, 1);
puts_("\r\n");
for (;;) {
GPIOC->ODR ^= 1u << 13; /* частое мигание: нет связи по SPI */
rc_delay_ms(100);
}
}
for (;;) {
if (rc_request(&r, PICC_REQA, atqa) == RC_OK && rc_select(&r, &c) == RC_OK) {
puts_("UID ");
hex(c.uid, c.len);
puts_(" SAK ");
hex(&c.sak, 1);
if (c.sak == 0x08u && rc_auth(&r, 4, key_ff, &c) == RC_OK) { /* MIFARE Classic 1K */
if (rc_read_block(&r, 4, blk) == RC_OK) {
puts_(" block4 ");
hex(blk, 16);
}
}
puts_("\r\n");
rc_halt(&r); /* карта уснула: пока лежит, второй раз не ответит на REQA */
rc_stop_crypto(); /* после HLTA: после аутентификации он уходит под шифром */
GPIOC->ODR ^= 1u << 13;
}
rc_delay_ms(200);
}
}
Next: section 18д "NFC module PN532: frames over I2C/SPI/UART, ACK, reading the UID and NTAG2xx, emulation and the difference from RC522" — the same 13.56 MHz band, but a chip with its own firmware: commands instead of registers, three buses to choose from and NTAG tags with NDEF.