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 RTU», а собрать или разобрать кадр целиком можно в инструменте Modbus RTU.
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 процессоре, порядок байтов внутри слова окажется обратным, и результат разойдётся с расчётом по байтам. Регистры блока CRC — на страницах STM32F1 и STM32F4.
Режим «Блок CRC STM32» повторяет расчёт аппаратного блока вместе с тем, как данные в него подаёт HAL. Блок STM32F1 и STM32F4 фиксирован: CRC-32 с полиномом 0x04C11DB7 по 32-битным словам (RM0008 и RM0090, раздел 4.4). У программируемого блока, например в STM32F3 (RM0316, глава 6), задаются размер полинома — 7, 8, 16 или 32 бита, — сам полином, начальное значение, разворот битов входа REV_IN по байтам, полусловам или словам и разворот результата REV_OUT. Финального XOR у блока нет — его делают программно.
| Нужный CRC | Размер и полином | Начальное | REV_IN | REV_OUT | После чтения |
|---|---|---|---|---|---|
| CRC-32 (ZIP, Ethernet) | 32 бита, 0x04C11DB7 | 0xFFFFFFFF | по байтам | да | XOR 0xFFFFFFFF |
| CRC-16/MODBUS | 16 бит, 0x8005 | 0xFFFF | по байтам | да | — |
| CRC-16/XMODEM | 16 бит, 0x1021 | 0x0000 | нет | нет | — |
| CRC-8/SMBUS | 8 бит, 0x07 | 0x00 | нет | нет | — |
Соответствие верно при формате данных HAL «байты» (CRC_INPUTDATA_FORMAT_BYTES): HAL_CRC_Calculate складывает каждые четыре байта в слово старшим байтом вперёд, а остаток дописывает 16- и 8-битными записями, так что блок видит байты в исходном порядке. Разворот REV_IN по байтам тогда равен отражению входа, а REV_OUT — отражению результата. Калькулятор показывает, какие записи уйдут в CRC_DR, и называет алгоритм каталога, если он совпадает с настройкой блока. Модель сверена с примерами STM32CubeF1 и STM32CubeF3: CRC-32 по 114 словам, CRC-7 потока байтов с полиномом 0x65, CRC-16 с разворотом по словам и CRC-8 с полиномом 0x9B.
Какой код выбрать
- Побитовый — восемь итераций на байт и ни одной таблицы. Подходит для коротких кадров 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-32 на STM32 не совпадает с онлайн-калькулятором?
Блок CRC STM32 по умолчанию считает CRC-32/MPEG-2: без отражения битов и без финального XOR, а «обычный» CRC-32 из ZIP отражает вход и результат и инвертирует его. На STM32F1 и STM32F4 блок не настраивается, и совпадения добиваются программно. У программируемого блока (например, STM32F3) включите REV_IN по байтам и REV_OUT, подавайте данные байтами и сделайте XOR результата с 0xFFFFFFFF — для строки «123456789» получится 0xCBF43926.
Табличный или побитовый расчёт 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-битных слов
- STMicroelectronics. RM0316 Rev 10. Reference manual: STM32F303xB/C/D/E, STM32F303x6/8, STM32F328x8, STM32F358xC, STM32F398xE — глава 6 — программируемый блок CRC: полином 7, 8, 16 или 32 бита, CRC_INIT, запись в CRC_DR словом, полусловом и байтом, REV_IN по байтам, полусловам и словам (пример 0x1A2B3C4D), REV_OUT (пример 0x11223344 → 0x22CC4488) — эталоны тестов
- STMicroelectronics. STM32CubeF1 и STM32CubeF3: примеры CRC и драйвер HAL (репозитории STM32CubeF1, STM32CubeF3, stm32f3xx-hal-driver) — CRC_Example (0x379E9F06 для 114 слов у F1 и F3), CRC_Bytes_Stream_7bit_CRC, CRC_Data_Reversing_16bit_CRC, CRC_UserDefinedPolynomial — эталоны тестов; HAL_CRC_Calculate складывает байты в слова старшим байтом вперёд и дописывает остаток записями по 16 и 8 бит
- Philip Koopman. Best CRC Polynomials — выбор полинома по расстоянию Хэмминга для заданной длины данных
Где пригодится
- Битовые операции в CМаски, сдвиги, установка и проверка битов, ловушки стандарта C
- Таблица ASCIIКоды символов, кириллица в CP1251, KOI8-R, CP866 и UTF-8
- DS18B20Датчик температуры 1-Wire: выводы, точность, 9–12 бит, паразитное питание
- Цифровая логикаВентили, таблицы истинности, СДНФ, карты Карно, мультиплексор, защёлка, триггер, регистр
- Датчики влажности SHT3x, HDC1080, HDC2080, BME280Точность, адреса, питание, отклик, дрейф, нагреватель, пересчёт и размещение
- CRCЦиклический избыточный код для обнаружения ошибок
Встроить калькулятор на свой сайт
Скопируйте код и вставьте его в HTML страницы, статьи или вики. Виджет бесплатный, без рекламы, счётчиков и cookie. Условие одно — ссылка на GAW под рамкой остаётся на месте. Подробнее о виджетах
<iframe src="https://gaw.ru/embed/crc/" title="Калькулятор CRC" width="100%" height="640" style="border:1px solid #e3e5e9;border-radius:8px" loading="lazy" allow="clipboard-write"></iframe>
<p style="margin:6px 0 0;font-size:14px">Калькулятор: <a href="https://gaw.ru/tools/crc/">Калькулятор CRC</a> — GAW.ru</p>Автоподстройка высоты рамки
Если сайт разрешает скрипты, добавьте этот код один раз в любом месте страницы: рамка будет менять высоту вместе с результатом расчёта.
<script>window.addEventListener("message",function(e){if(e.origin!=="https://gaw.ru"||!e.data||e.data.type!=="gaw:embed-height")return;var f=document.querySelectorAll('iframe[src^="https://gaw.ru/embed/"]');for(var i=0;i<f.length;i++)if(f[i].contentWindow===e.source)f[i].style.height=e.data.height+"px"})</script>Обновлено .
Нашли ошибку в расчёте?