Реализация расчета CRC16 Modbus RTU на C#

  • Михаил
  • 25 мин. на прочтение
  • 1237
  • 27 Aug 2023
  • 27 Aug 2023

Начнём с основ, так что такое modbus rtu. Modbus RTU (Remote Terminal Unit) – это протокол, используемый для последовательной связи и является наиболее распространенной реализацией, доступной для Modbus. Modbus RTU использует компактное двоичное представление данных для протокольной связи. Формат RTU следует за командами / данными с контрольной суммой циклической проверки избыточности в качестве механизма проверки ошибок для обеспечения надежности данных. При передачи любых данных возникает проблема в ошибках возникающих при передачи, с этой целью при передачи используют CRC. Снова возвращаемся к истокам. Циклическая проверка избыточности (CRC)-это код обнаружения ошибок, обычно используемый в цифровых сетях и устройствах хранения данных для обнаружения случайных изменений необработанных данных. Блоки данных, поступающие в эти системы, получают короткое контрольное значение, основанное на остатке полиномиального деления их содержимого. CRC16 Modbus RTU — это алгоритм циклического избыточного кода (CRC), который широко применяется в протоколе Modbus RTU для обнаружения ошибок передачи данных. CRC с 16-битным значением, использующий многочлен x^16+x^15+x^2+1. Эта формула является стандартной для протокола Modbus RTU и может быть использована для обнаружения ошибок в любых данных, которые передаются через этот протокол.

Перед началом расчета CRC16 Modbus RTU нужно понимать, что алгоритм является чрезвычайно простым и основан на базовых операциях побитового сравнения и сдвига. Основными шагами алгоритма являются:

1. Инициализация регистра CRC16 значением 0xFFFF.
2. Обработка каждого байта передаваемых данных, начиная с первого.
3. Вычисление нового значения CRC16 для каждого байта.
4. Использование финального значения CRC16 для обнаружения ошибок.

Рассмотрим каждый из этих шагов более подробно.

1. Инициализация регистра CRC16 значением 0xFFFF.

Перед началом обработки данных наш CRC16 регистр требуется инициализировать значением 0xFFFF. Это необходимо для обеспечения корректного функционирования алгоритма. Это значение было выбрано потому, что оно имеет высокую эффективность при обработке случайных данных.

2. Обработка каждого байта передаваемых данных, начиная с первого.

Каждый байт данных передается по-битово. Поэтому первым шагом при обработке данных является извлечение каждого бита входного байта. Это можно сделать с помощью операции побитового сдвига и побитового логического И.

3. Вычисление нового значения CRC16 для каждого байта.

После того, как мы извлекли каждый бит входного байта, мы должны вычислить новое значение CRC16, используя значения, извлеченные из этого байта. Это можно сделать с помощью цикла, который выполняется от 0 до 7 (так как каждый байт состоит из 8 бит).

В этом цикле мы осуществляем побитовое сравнение текущего значения CRC16 и значения из входного байта. Если они различаются, то мы выполняем "XOR" операцию между текущим значением CRC16 и 0xA001. Иначе мы выполняем обычное "XOR" текущего значения CRC16 с нулем.

4. Использование финального значения CRC16 для обнаружения ошибок.

После завершения обработки всех байтов входных данных мы получаем значения CRC16. Чтобы обнаружить ошибку передачи данных, мы должны сравнить это значение с CRC16, полученной устройством-получателем. Если значения отличаются, то мы можем сделать вывод о том, что произошла ошибка передачи данных.

Реализуем в коде.

       public static ushort Modbus(byte[] buf)
       {
           ushort crc = 0xFFFF;
           int len = buf.Length;
           for (int pos = 0; pos < len; pos++)
           {
               crc ^= buf[pos];
               for (int i = 8; i != 0; i--)
               {
                   if ((crc & 0x0001) != 0)
                   {
                       crc >>= 1;
                       crc ^= 0xA001;
                   }
                   else
                       crc >>= 1;
               }
           }
           // Возврат в виде lo-hi
           //return crc;
           // или
           // Возврат в виде hi-lo
           return (ushort)((crc >> 8) | (crc << 8));
       }

В этой реализации мы используем «ushort» для хранения значения текущего CRC16. Мы начинаем с его инициализации значениями 0xFFFF и затем обрабатываем каждый байт данных, вычисляя новое значение CRC16 для каждого из них.

Иногда необходимо рассчитать CRC16 Modbus RTU для данных, которые не являются кратными восьми битам. В этом случае мы можем добавить дополнительный байт данных, чтобы сделать размер входящих данных кратным восьми. Мы должны убедиться в том, что этот байт имеет значение 0, так как он не должен влиять на вычисление CRC16.

Здесь же реализуем конвертацию строки в массив

       public static byte[] GetBytesFromHexString(string strInput)
       {
           Byte[] bytArOutput = new Byte[] { };
           if (!string.IsNullOrEmpty(strInput) && strInput.Length % 2 == 0)
           {
               SoapHexBinary hexBinary = null;
               try
               {
                   hexBinary = SoapHexBinary.Parse(strInput);
                   if (hexBinary != null)
                   {
                       bytArOutput = hexBinary.Value;
                   }
               }
               catch (Exception ex)
               {
                   
               }
           }
           return bytArOutput;
       }

Конечно-же использование

           string stringHex = "FF 10 00 03 00 02 04 00 02 00 1E";
           ushort crc = Modbus(GetBytesFromHexString(stringHex));
           Console.WriteLine("CRC 2: " + crc + " | Hex: " + crc.ToString("X"));

В заключении, чтобы рассчитать CRC16 Modbus RTU на C#, мы используем простой алгоритм, основанный на побитовых операциях. Этот алгоритм может использоваться для обнаружения ошибок передачи данных в протоколе Modbus RTU и других протоколах, использующих CRC.