18г. RFID-считыватель RC522: MFRC522 по SPI, поиск карты, UID, антиколлизия, чтение блоков MIFARE Classic
Раздел 18 разобрал шину SPI на флеш-памяти, SD-карте и сдвиговых регистрах, разделы 18а–18в — звук. Здесь ещё одно устройство на SPI, самое частое в наборах для начинающих: модуль RC522 — считыватель бесконтактных карт и брелоков на 13,56 МГц. Прошивка с ним говорит на двух уровнях: по SPI — с регистрами микросхемы MFRC522, а через неё по радио — с картой. Метка с другой стороны, которую читает телефон, была в разделе 94а, считыватель NFC в Flipper Zero — в разделе 94б, ближняя зона на 13,56 МГц — в разделе 89.5.
Опоры: RM0008 (DocID13902 Rev 17): APB2 работает на полной частоте до
72 МГц, с. 48; бит SPI1EN, с. 112; настройка выводов SPI, таблица 25,
с. 166; SPI1 без ремапа — NSS/PA4, SCK/PA5, MISO/PA6, MOSI/PA7, с. 185;
SPI_CR1 — BR[2:0], CPOL, CPHA, с. 742–743; флаги BSY, TXE,
RXNE, с. 745–746. DocID13587 Rev 17 (STM32F103x8/xB): PA4–PA7 и PB0 —
без допуска 5 В, с. 29. Документа на MFRC522 и стандарта ISO 14443 в
проекте нет: адреса регистров, биты и команды микросхемы взяты по
документации производителя и не проверены, команды карты — общеизвестные.
Все числа ниже — прогон драйвера _verify/rfid18e/rfid18e.c на компьютере
(gcc 12.2.0) со своей моделью MFRC522 и карт на Python 3.11; прошивка
собрана armclang 6.24 под Cortex-M3.
Как это называется в документации
Оригинал Перевод PCD — proximity coupling device считыватель PICC — proximity card карта, брелок, метка UID — unique identifier номер карты, 4, 7 или 10 байт REQA / WUPA запрос карт в покое / разбудить и усыплённые ATQA — answer to request ответ на запрос: 2 байта anticollision, cascade level антиколлизия, каскад BCC — block check character XOR четырёх байт UID SAK — select acknowledge ответ на выбор: тип карты HLTA усыпить карту sector trailer трейлер сектора: ключи и биты доступа Transceive команда «передать и принять»
18г.1 Модуль RC522
На плате модуля — микросхема MFRC522, кварц, согласующие детали и антенна: катушка, вытравленная по краю платы. Микросхема сама формирует поле 13,56 МГц, модулирует его командами и разбирает ответ карты по ISO 14443A. Скорость в эфире — 106 кбит/с: бит длится 128 периодов несущей, 128 / 13,56 МГц = 9,44 мкс. Карта своей батарейки не имеет — её питает поле считывателя (раздел 89.5), поэтому читает модуль на сантиметрах.
Наружу у микросхемы три интерфейса: SPI, I²C и UART; на модулях RC522 обычно разведён SPI, его здесь и берём. Внутри — регистры с шестибитным адресом, до 64 штук, FIFO на 64 байта, таймер, сопроцессор CRC и блок шифра Crypto1 для карт MIFARE Classic. Прошивка кладёт кадр в FIFO, даёт команду — микросхема передаёт его, ждёт ответ и складывает его в тот же FIFO.
Номер версии микросхемы лежит в регистре VersionReg (0x37). Драйвер
принимает 0x91 и 0x92 — версии 1.0 и 2.0 по документации производителя
(не проверено); модель отвечает 0x92. Если там 0x00 или 0xFF, связи
по SPI нет.
18г.2 Подключение к F103: SPI1, адресный байт, сброс, питание
SPI1 без ремапа сидит на PA4–PA7 (с. 185). Вывод SDA модуля — это выбор
микросхемы, его ведём сами обычным выходом на PA4 (программный CS, как
в разделе 18.4). RST — на PB0. SPI1 висит на APB2, а та работает на
полной частоте — 72 МГц (с. 48); делитель 16 (BR = 011, с. 743) даёт
4,5 МГц. Режим 0: CPOL = 0, CPHA = 0, старшим битом вперёд — режим
MFRC522 по документации производителя, не проверено. Свой предел частоты
SPI у микросхемы есть, но документа нет — 4,5 МГц взяты с запасом.
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 вверх — конец транзакции */
}
Адресный байт. Первый байт каждой транзакции — адрес регистра, но не
как есть: бит 7 — чтение (1) или запись (0), биты 6:1 — адрес, бит 0 —
ноль. Адрес сдвигают на 1 влево. Чтение VersionReg (0x37) начинается с
байта 0xEE, запись в CommandReg (0x01) — с байта 0x02. При чтении
данные приходят со вторым байтом: пока ведущий шлёт адрес, ведомый ещё
не знает, что отвечать. Модель стенда проверяет бит 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 — мусор, данные приходят со вторым */
}
Байт на 4,5 МГц — 1,778 мкс, чтение регистра — два байта, 3,56 мкс.
Сброс и настройка. rc_init опускает RST на 1 мс, ждёт 50 мс, даёт
программный сброс SoftReset и читает версию. Потом — таймер, который
ограничивает ожидание ответа карты: TPrescaler = 0xA9 даёт 13,56 МГц /
(2 · 169 + 1) = 40 кГц, перезагрузка 1000 — 25,025 мс (формула — по
документации производителя, не проверено; модель считает по ней).
ModeReg = 0x3D задаёт начальное значение CRC 0x6363. Последний шаг —
включить антенну: после сброса TxControlReg равен 0x80, выводы антенны
выключены, после rc_init — 0x83. В модели вся настройка — 12
транзакций SPI, 24 байта и 51,04 мс, почти всё — ожидание после сброса.
Питание — 3,3 В. MFRC522 — микросхема на 3,3 В, и выводы F103 PA4–PA7 и PB0 тоже без допуска 5 В (с. 29). Вывод 3.3V модуля — к 3,3 В платы, не к 5 В.
18г.3 Регистры, FIFO, Transceive и CRC
Обмен с картой — одна функция. Она останавливает микросхему, снимает
флаги, очищает FIFO, кладёт туда кадр, пишет в BitFramingReg, сколько бит
в последнем байте и с какого бита класть ответ, даёт команду Transceive и
бит StartSend. Дальше — опрос ComIrqReg: приём (0x20), простой (0x10)
или таймер (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;
}
FIFO пишется и читается одной транзакцией: при записи один адресный байт и дальше все данные подряд, при чтении — адрес FIFODataReg столько раз, сколько байт нужно, и в конце ноль.
Опрос стоит дороже, чем данные. Пустое поле: REQA уходит, отвечать некому, и драйвер 7063 раза читает ComIrqReg, пока таймер не скажет «тишина», — 25,141 мс и 14 142 байта по SPI. С картой REQA занимает 102 опроса и 0,407 мс. Опрос можно заменить прерыванием: у MFRC522 есть вывод IRQ, но в этом драйвере он не используется.
CRC_A. Кадры SELECT, READ и HLTA кончаются двумя байтами CRC_A:
многочлен x¹⁶ + x¹² + x⁵ + 1, начальное значение 0x6363, младший байт
идёт первым. Для исходящих кадров драйвер зовёт сопроцессор микросхемы
(команда CalcCRC), ответ карты проверяет своей функцией crc_a — без
лишних транзакций SPI. Сверка в прогоне: сопроцессор (в модели — побитный
расчёт на Python) и crc_a на Си дали одно и то же на кадре SELECT
(28 C1), HLTA 50 00 (57 CD), READ 30 04 (26 EE), 60 04
(D1 3D), 00 00 (A0 1E) и 12 34 (26 CF) и на 200 случайных
кадрах длиной от 1 до 18 байт — на всех 200. CRC по кадру вместе с его CRC даёт 0 — так проверяют
принятый кадр. Это самосогласованность трёх расчётов, а не сверка со
стандартом: его на диске нет.
18г.4 Поиск карты: REQA, антиколлизия, SELECT, SAK
REQA и ATQA. Поиск начинается с короткого кадра в 7 бит: REQA (26)
будит карты, которые лежат в поле в покое. Каждая отвечает двумя байтами
ATQA; у карты A в модели — 04 00. WUPA (52) будит ещё и усыплённые
командой HLTA.
Антиколлизия. Дальше считыватель шлёт 93 20: «каскад 1, я знаю 0 бит
вашего UID, отвечайте». Байт 20 — NVB: старшая тетрада — сколько целых
байт в кадре (2: сама команда и NVB), младшая — сколько ещё бит. Одна
карта отвечает 40 битами: 4 байта UID и BCC — XOR этих четырёх байт. У
карты A: 3A 9F 12 C4, BCC 73. Драйвер проверяет BCC и шлёт SELECT —
93 70, UID, BCC и CRC 28 C1; карта отвечает SAK 08 с CRC. Вся
процедура для одной карты в модели — 558 транзакций SPI, 1137 байт,
2,021 мс.
Две карты. Если в поле две карты, они отвечают одновременно, и где
их биты расходятся, микросхема видит коллизию и пишет её номер в CollReg.
UID 3A 9F 12 C4 и 3A 9F 52 C4 расходятся в бите 23 — модель так и
сообщила. Драйвер ставит этот бит в 1, считает известными 23 бита и шлёт
их: 93 47 3A 9F 52 — 39 бит, NVB 47: 4 целых байта и 7 бит. Теперь
отвечает только карта с единицей в бите 23, и её ответ ложится в FIFO с
бита 7 — это поле выравнивания в 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;
}
Выбранную карту драйвер усыпляет HLTA и снова шлёт REQA — теперь отвечает
только вторая. Скан двух карт нашёл сначала 3A 9F 52 C4, потом
3A 9F 12 C4, с одной коллизией, за 81,675 мс. Из них 76,0 мс — три
ожидания по 25 мс: на HLTA карта не отвечает, и «успех» здесь — это
таймаут, а последний REQA ждёт, что больше никого нет. После скана обе
карты спят: REQA — тишина, WUPA — ATQA 04 00.
Каскады. UID бывает 4, 7 и 10 байт, а кадр антиколлизии несёт 4. У
карты с UID 7 байт в каскаде 1 идёт байт CT (88) и три байта UID, SAK
04 значит «UID не весь», и драйвер повторяет всё с 95 — каскадом 2.
Карта B (04 52 8A 1B 3C 5D 80) отдала 88 04 52 8A, BCC 54, SAK 04,
потом 1B 3C 5D 80, BCC FA, SAK 00. С картами A и B вместе
коллизия случилась дважды: в ATQA (04 00 и 44 00 расходятся в бите 7)
и в каскаде 1 — на бите 2, где 88 встречается с 3A. Коллизию в ATQA
драйвер пропускает — UID всё равно разберёт антиколлизия.
SAK говорит, что за карта: 08 — MIFARE Classic 1K, 00 — карта без
шифра вроде MIFARE Ultralight (значения общеизвестные, не проверены по
документу). В модели карта B — такая: страницы 4–7 она отдаёт командой READ
без всяких ключей, а аутентификация на ней кончается таймаутом.
18г.5 MIFARE Classic: сектор, блок, ключ A, чтение
Память MIFARE Classic 1K — 16 секторов по 4 блока по 16 байт, 1024 байта. Блок 0 — заводской: UID, BCC, SAK, ATQA. Последний блок каждого сектора — трейлер: ключ A (6 байт), биты доступа (4 байта), ключ B (6 байт). Читать блок можно только после аутентификации в его секторе.
Аутентификацию ведёт сама микросхема — команда MFAuthent: в FIFO кладут
60 (ключ A), номер блока, 6 байт ключа и последние 4 байта UID. Если
ключ подошёл, в Status2Reg поднимается бит MFCrypto1On, и дальше обмен с
картой идёт под шифром — прошивка этого не видит, микросхема шифрует и
расшифровывает сама. Ключ FF FF FF FF FF FF — заводской, общеизвестный;
новые карты обычно продают с ним.
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;
}
В модели: аутентификация в блоке 4 — 1,993 мс, чтение блока 4 — 2,107 мс,
582 транзакции SPI и 1185 байт; в блоке 16 байт Hello, STM32F103.
Трейлер того же сектора (блок 7) читается так: на месте ключа A нули —
ключ A из карты не прочитать, — затем биты доступа FF 07 80 69 и ключ B
(поведение модели повторяет то, что описано для карт, — не проверено).
Блок 0 из другого сектора карта не отдала: вместо 18 байт пришли 4 бита
отказа. С неверным ключом карта замолкает — таймаут 25,424 мс, — и
дальше не отвечает и на READ: её снова будят WUPA, выбирают и
аутентифицируют заново.
18г.6 Почему это не защита
Ключ MIFARE Classic — 48 бит, шифр Crypto1 — собственный алгоритм NXP. Его давно восстановили и опубликовали, нашли слабости и в самом шифре, и в генераторе случайных чисел карты; с тех пор известны способы достать ключи сектора, имея на руках карту или подслушав обмен. Как именно — не тема этого раздела. Вывод для прошивки простой: MIFARE Classic и RC522 не годятся для защиты — замка, пропуска, денег.
Ещё слабее схема «пускаем по UID». UID карта отдаёт до всякой аутентификации любому считывателю — в 18г.4 он пришёл в первом же ответе. Продаются карты с перезаписываемым блоком 0, на которые UID копируется. UID — это номер для опознания, а не пароль.
Для учёта, игрушек, выбора профиля или «приложи брелок — включи светильник» RC522 хорош. Для защиты нужны карты с проверенной стойкой криптографией и считыватели, которые её поддерживают; о криптографии в микроконтроллере — раздел 43.
18г.7 Грабли
Версия читается как 0x00 или 0xFF. Нет связи по SPI: не тот
режим, перепутаны MISO и MOSI, SDA не опускается или модуль без питания.
Драйвер в этом случае дальше не идёт.
Модуль от 5 В. MFRC522 — на 3,3 В, выводы F103 PA4–PA7 и PB0 без допуска 5 В (с. 29). Питать модуль от 3,3 В.
Карта не находится никогда. После сброса антенна выключена:
TxControlReg = 0x80. В модели без её включения REQA кончается
таймаутом, скан находит 0 карт; после включения (0x83) та же карта
находится за 53,024 мс.
Длинные провода SPI. Провода в десятки сантиметров на макетке дают сбои битов. В модели каждый бит MISO с вероятностью p переворачивается, по 200 чтений блока:
| p | проверка CRC | верно | ошибка CRC | прочие ошибки | неверные данные |
|---|---|---|---|---|---|
| 10⁻⁴ | да | 159 | 4 | 37 | 0 |
| 10⁻⁴ | нет | 165 | 0 | 33 | 2 |
| 10⁻⁵ | да | 195 | 0 | 5 | 0 |
| 10⁻⁵ | нет | 195 | 0 | 5 | 0 |
Чтение блока — 9480 бит по SPI, полезных из них 144 (18 байт ответа): остальное — опрос и служебные регистры, и большинство сбоев попадает туда. CRC ответа проверяет данные на всём пути — в эфире и на проводах: с проверкой неверных данных 0, без неё — 2 из 200. Лечится короче проводами и меньшей частотой SPI.
Две карты в поле. Обе отвечают одновременно. Без антиколлизии драйвер получил бы смесь битов; с ней выбирается одна, а остальные находятся после HLTA (18г.4).
HLTA «не работает». На HLTA карта не отвечает — драйвер ждёт таймаут и считает это успехом. Каждая HLTA — 25,454 мс.
Карта больше не отвечает после неверного ключа. Она вышла из выбранного состояния: WUPA, SELECT и аутентификация заново.
18г.8 Итоги
- RC522 — MFRC522 на 13,56 МГц, ISO 14443A, 106 кбит/с; бит — 9,44 мкс.
- SPI1 на F103: PA4 — SDA (CS вручную), PA5 — SCK, PA6 — MISO, PA7 — MOSI, режим 0, 72 МГц / 16 = 4,5 МГц; питание 3,3 В.
- Адресный байт: бит 7 — чтение, биты 6:1 — адрес, бит 0 — 0;
VersionReg —
0xEE, ответ —0x92в модели. - После сброса антенна выключена: TxControlReg
0x80→0x83. - Таймер 40 кГц × 1000 — 25,025 мс; пустое поле — 7063 опроса.
- REQA → ATQA
04 00;93 20→ UID3A 9F 12 C4, BCC73; SELECT с CRC28 C1→ SAK08; одна карта — 2,021 мс. - Две карты: коллизия на бите 23, обе найдены за 81,675 мс, из них 76,0 мс — таймауты.
- MIFARE Classic: 16 секторов × 4 блока × 16 байт; ключ A по умолчанию
FF FF FF FF FF FF; чтение блока — 2,107 мс. - Crypto1 взломан, UID не секрет: для защиты RC522 и MIFARE Classic не годятся.
Полные исходники
rfid18e.h — регистры и команды MFRC522, команды карты, интерфейс драйвера.
/* 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 — драйвер без регистров STM32: общая часть прошивки и стенда.
/* 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: SPI1, RST, вывод в 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);
}
}
Дальше: раздел 18д «NFC-модуль PN532: кадры по I2C/SPI/UART, ACK, чтение UID и NTAG2xx, эмуляция и отличие от RC522» — тот же диапазон 13,56 МГц, но микросхема со своей прошивкой: команды вместо регистров, три шины на выбор и метки NTAG с NDEF.