CRC (циклический избыточный код) — контрольная сумма, которую передатчик дописывает в конец кадра, а приёмник пересчитывает по принятым данным. Математически это остаток от деления сообщения как двоичного многочлена на порождающий полином. CRC обнаруживает любую одиночную ошибку и любой пакет ошибок не длиннее разрядности CRC, а если полином делится на x + 1 — ещё и все ошибки с нечётным числом искажённых битов. Двойные ошибки CRC ловит гарантированно, только пока кадр короче периода полинома, поэтому полином выбирают под длину данных.
Параметры алгоритма
Любой распространённый CRC описывается шестью параметрами модели Rocksoft (Р. Уильямс, 1993):
| Параметр | Что задаёт | CRC-16/MODBUS |
|---|---|---|
| Width | разрядность CRC, бит | 16 |
| Poly | порождающий полином без старшего бита | 0x8005 |
| Init | начальное значение регистра | 0xFFFF |
| RefIn | обрабатывать ли каждый байт младшим битом вперёд | да |
| RefOut | перевернуть ли биты результата | да |
| XorOut | с чем сложить по модулю 2 результат | 0x0000 |
Полином записывают без старшего бита: 0x8005 означает x¹⁶ + x¹⁵ + x² + 1. Отражённый алгоритм работает с перевёрнутым полиномом и сдвигает регистр вправо — поэтому в коде Modbus фигурирует 0xA001, а в коде CRC-32 — 0xEDB88320.
Одного полинома мало: CRC-16/CCITT-FALSE, CRC-16/KERMIT и CRC-16/XMODEM используют один и тот же 0x1021, но дают разные результаты. Для проверки служит контрольное значение — CRC от строки «123456789»; у каждого пресета калькулятор сверяет его с каталогом CRC RevEng.
Популярные алгоритмы
| Алгоритм | Poly | Init | RefIn/RefOut | XorOut | Check | Где используется |
|---|---|---|---|---|---|---|
| CRC-8/SMBUS | 07 | 00 | нет | 00 | F4 | SMBus |
| CRC-8/MAXIM-DOW | 31 | 00 | да | 00 | A1 | 1-Wire, DS18B20 |
| CRC-8/NRSC-5 | 31 | FF | нет | 00 | F7 | датчики Sensirion |
| CRC-16/MODBUS | 8005 | FFFF | да | 0000 | 4B37 | Modbus RTU |
| CRC-16/IBM-3740 (CCITT-FALSE) | 1021 | FFFF | нет | 0000 | 29B1 | многие протоколы |
| CRC-16/XMODEM | 1021 | 0000 | нет | 0000 | 31C3 | XMODEM, карты SD |
| CRC-16/KERMIT (CCITT) | 1021 | 0000 | да | 0000 | 2189 | Kermit, Bluetooth |
| CRC-15/CAN | 4599 | 0000 | нет | 0000 | 059E | кадр CAN 2.0 |
| CRC-32 (ISO-HDLC) | 04C11DB7 | FFFFFFFF | да | FFFFFFFF | CBF43926 | ZIP, PNG, Ethernet |
| CRC-32C | 1EDC6F41 | FFFFFFFF | да | FFFFFFFF | E3069283 | iSCSI, ext4 |
| CRC-32/MPEG-2 | 04C11DB7 | FFFFFFFF | нет | 00000000 | 0376E6E7 | MPEG-2, блок CRC в STM32 |
Все значения в шестнадцатеричном виде.
Modbus RTU
В Modbus RTU CRC считают по всем байтам кадра, начиная с адреса устройства. Регистр начинается с FFFF, на каждый байт выполняется XOR и восемь сдвигов вправо с полиномом A001, результат дописывают младшим байтом вперёд. Запрос «прочитать 10 регистров хранения, начиная с нулевого, у устройства 1» выглядит так: 01 03 00 00 00 0A C5 CD.
Приёмнику не нужно сравнивать CRC отдельно: если посчитать CRC-16/MODBUS по всему кадру вместе с двумя последними байтами, при правильной передаче получится ноль. Про физический уровень Modbus — в статье о RS-485.
1-Wire и датчики
У 1-Wire последний байт 64-битного ROM-кода — CRC-8 полинома X⁸ + X⁵ + X⁴ + 1 (0x31) по первым семи байтам; той же CRC защищён блокнот DS18B20. Байты обрабатываются младшим битом вперёд, поэтому в каталоге это CRC-8/MAXIM-DOW с отражением. Датчики Sensirion используют тот же полином, но без отражения и с начальным значением FF: по даташиту SHT3x CRC от байтов BE EF равна 0x92. Подробнее о шине — в статье об 1-Wire.
STM32 и порядок байтов
Аппаратный блок CRC у STM32 по умолчанию считает с полиномом 0x04C11DB7 и начальным значением 0xFFFFFFFF без отражения и финального XOR — это параметры CRC-32/MPEG-2, а не «обычного» CRC-32 из ZIP. К тому же блок принимает 32-битные слова и обрабатывает каждое старшим битом вперёд. Если записывать в него байты потока словами на little-endian процессоре, порядок байтов внутри слова окажется обратным, и результат разойдётся с расчётом по байтам. Сверяйте результат по контрольному значению или подавайте данные побайтно, если блок это позволяет.
Какой код выбрать
- Побитовый — восемь итераций на байт и ни одной таблицы. Подходит для коротких кадров Modbus и 1-Wire на маленьком МК.
- Табличный — один поиск в таблице на байт. Таблица на 256 значений занимает 256 байт для CRC-8, 512 байт для CRC-16 и 1 КБ для CRC-32 во флеш-памяти.
Сгенерированный код проверен: все пресеты калькулятора в обоих вариантах собраны компилятором MSVC с предупреждениями уровня 4 как ошибками и дают контрольное значение каталога. Функция принимает указатель на данные и длину и возвращает готовую CRC — с отражением и финальным XOR, если они нужны.
Если протокол свой, полином лучше брать из таблиц Ф. Купмана: он подобрал полиномы с наибольшим расстоянием Хэмминга для каждой длины данных.
Частые вопросы
Как посчитать CRC-16 для Modbus RTU?
Выберите CRC-16/MODBUS и вставьте байты кадра без CRC, например 01 03 00 00 00 0A. Получится 0xCDC5, а в кадр байты идут младшим вперёд: 01 03 00 00 00 0A C5 CD. Начальное значение регистра — FFFF, полином в отражённом виде — A001, финального XOR нет. Если посчитать CRC по всему принятому кадру вместе с двумя байтами CRC, должен получиться ноль.
Чем CRC-16/CCITT-FALSE отличается от CRC-16/CCITT?
У обоих полином 0x1021, но остальные параметры разные. CCITT-FALSE (в каталоге — CRC-16/IBM-3740) начинается с FFFF и обрабатывает биты старшим вперёд, контрольное значение 0x29B1. CRC-16/CCITT, он же KERMIT, начинается с нуля и работает с отражёнными байтами, контрольное значение 0x2189. Есть ещё XMODEM — ноль в начале без отражения, 0x31C3. Названия в разных библиотеках путают, поэтому сверяйте контрольное значение для строки «123456789».
Что такое контрольное значение check?
Это CRC от девяти ASCII-символов «123456789» — байты 31 32 33 34 35 36 37 38 39. По нему однозначно проверяют, что реализация и параметры совпадают с эталоном: у CRC-32 это 0xCBF43926, у CRC-16/MODBUS — 0x4B37. Калькулятор показывает check для выбранных параметров и сверяет его с каталогом CRC RevEng.
Что такое отражение входа и результата?
Отражение (RefIn) означает, что каждый байт обрабатывается младшим битом вперёд — так передаёт UART, поэтому отражённые CRC естественны для последовательных протоколов. RefOut переворачивает биты готового результата. В программе отражённый алгоритм сдвигает регистр вправо и использует перевёрнутый полином: для 0x8005 это 0xA001, для CRC-32 — 0xEDB88320.
Табличный или побитовый расчёт CRC — что выбрать?
Побитовый алгоритм — несколько строк без таблицы, но 8 итераций цикла на каждый байт. Табличный обрабатывает байт за одну операцию, зато таблица занимает 256 значений: 256 байт для CRC-8, 512 для CRC-16 и 1 КБ для CRC-32 во флеш-памяти. На маленьком МК с короткими кадрами Modbus хватает побитового варианта, для потоков данных и CRC-32 выгоднее таблица.
Источники
- Ross N. Williams. A Painless Guide to CRC Error Detection Algorithms. 1993 — модель параметров Width, Poly, Init, RefIn, RefOut, XorOut; прямой и табличный алгоритмы
- Greg Cook. Catalogue of parametrised CRC algorithms (CRC RevEng) — параметры, псевдонимы и контрольные значения всех пресетов калькулятора
- Modbus Organization. MODBUS over Serial Line: Specification and Implementation Guide V1.02 — CRC в режиме RTU: начальное значение FFFF, полином A001, младший байт первым
- Maxim Integrated. Application Note 27: Understanding and Using Cyclic Redundancy Checks with Maxim 1-Wire and iButton Products — CRC-8 полинома X⁸ + X⁵ + X⁴ + 1 в ROM-коде 1-Wire
- Sensirion. Datasheet SHT3x-DIS, version 7, 2022 — CRC-8: полином 0x31, начальное значение 0xFF, пример CRC(0xBEEF) = 0x92
- STMicroelectronics. AN4187: Using the CRC peripheral in the STM32 family, rev. 2, 2022 — аппаратный блок CRC: полином 0x04C11DB7, начальное значение 0xFFFFFFFF, обработка 32-битных слов
- Philip Koopman. Best CRC Polynomials — выбор полинома по расстоянию Хэмминга для заданной длины данных
Обновлено . Нашли ошибку в расчёте? Напишите нам.