вторник, 18 февраля 2014 г.

В помощь разработчику: 5 реализаций Modbus CRC +CRC16 и 14 заповедей RS-485


Все реализации подсчета контрольной суммы Modbus-пакета отработаны и проверены в промышленных приложениях:

1. Подсчет до заданного байта в пакете (используется, когда в пакет входит и контрольная сумма)
function ModbusCRC1(BufferData: array of byte; max: integer): word;
var
  Data: byte;
  i,j: integer;
const
  polinomio:word= $A001;
begin
  result:= $FFFF;
  for i:= 0 to max do
  begin
    data:= BufferData[i];
    for j:=1 to 8 do
    begin
      if (((data xor result) and $0001) = 1) then
         result:=(result shr 1) xor polinomio
      else
         result:=result shr 1;
      data:= data shr 1;
    end;
  end;
end;
2. Подсчет по всей длине пакета через исключающее ИЛИ и маску
function ModbusCRC2(BufferData: array of byte): word;
var
  Data: byte;
  i,j: integer;
const
  polinomio:word= $A001;
begin
  result:= $FFFF;
  for i:=low(BufferData) to high(BufferData) do
  begin
    data:= BufferData[i];
    for j:=1 to 8 do
    begin
      if (((data xor result) and $0001) = 1) then
         result:=(result shr 1) xor polinomio
      else
         result:=result shr 1;
      data:= data shr 1;
    end;
  end;
end;
3. Подсчет по всей длине пакета с определением четности через MOD
function ModbusCRC3(BufferData: array of byte): word;
var
  i,j : byte;
  bCRC : word;
begin
bCRC:=$FFFF;
for i:= 0 to LENgth(BufferData)-1 do
  begin
   bCRC:=bCRC xor BufferData[i];
   for j:=1 to 8 do
     begin
     if bCRC mod 2 > 0 then begin
     bCRC:=bCRC shr 1;
     bCRC:=bCRC xor $A001;
     end else bCRC:= bCRC shr 1;
     end;
  end;
 ModbusCRC3:= bCRC;
end;
4. Через таблицу (самый быстрый способ)
var tableCRC16:array [0..$FF] of Word =
 ($0000,$C0C1,$C181,$0140,$C301,$03C0,$0280,$C241,
  $C601,$06C0,$0780,$C741,$0500,$C5C1,$C481,$0440,
  $CC01,$0CC0,$0D80,$CD41,$0F00,$CFC1,$CE81,$0E40,
  $0A00,$CAC1,$CB81,$0B40,$C901,$09C0,$0880,$C841,
  $D801,$18C0,$1980,$D941,$1B00,$DBC1,$DA81,$1A40,
  $1E00,$DEC1,$DF81,$1F40,$DD01,$1DC0,$1C80,$DC41,
  $1400,$D4C1,$D581,$1540,$D701,$17C0,$1680,$D641,
  $D201,$12C0,$1380,$D341,$1100,$D1C1,$D081,$1040,
  $F001,$30C0,$3180,$F141,$3300,$F3C1,$F281,$3240,
  $3600,$F6C1,$F781,$3740,$F501,$35C0,$3480,$F441,
  $3C00,$FCC1,$FD81,$3D40,$FF01,$3FC0,$3E80,$FE41,
  $FA01,$3AC0,$3B80,$FB41,$3900,$F9C1,$F881,$3840,
  $2800,$E8C1,$E981,$2940,$EB01,$2BC0,$2A80,$EA41,
  $EE01,$2EC0,$2F80,$EF41,$2D00,$EDC1,$EC81,$2C40,
  $E401,$24C0,$2580,$E541,$2700,$E7C1,$E681,$2640,
  $2200,$E2C1,$E381,$2340,$E101,$21C0,$2080,$E041,
  $A001,$60C0,$6180,$A141,$6300,$A3C1,$A281,$6240,
  $6600,$A6C1,$A781,$6740,$A501,$65C0,$6480,$A441,
  $6C00,$ACC1,$AD81,$6D40,$AF01,$6FC0,$6E80,$AE41,
  $AA01,$6AC0,$6B80,$AB41,$6900,$A9C1,$A881,$6840,
  $7800,$B8C1,$B981,$7940,$BB01,$7BC0,$7A80,$BA41,
  $BE01,$7EC0,$7F80,$BF41,$7D00,$BDC1,$BC81,$7C40,
  $B401,$74C0,$7580,$B541,$7700,$B7C1,$B681,$7640,
  $7200,$B2C1,$B381,$7340,$B101,$71C0,$7080,$B041,
  $5000,$90C1,$9181,$5140,$9301,$53C0,$5280,$9241,
  $9601,$56C0,$5780,$9741,$5500,$95C1,$9481,$5440,
  $9C01,$5CC0,$5D80,$9D41,$5F00,$9FC1,$9E81,$5E40,
  $5A00,$9AC1,$9B81,$5B40,$9901,$59C0,$5880,$9841,
  $8801,$48C0,$4980,$8941,$4B00,$8BC1,$8A81,$4A40,
  $4E00,$8EC1,$8F81,$4F40,$8D01,$4DC0,$4C80,$8C41,
  $4400,$84C1,$8581,$4540,$8701,$47C0,$4680,$8641,
  $8201,$42C0,$4380,$8341,$4100,$81C1,$8081,$4040);
 

function ModbusCRC4(BufferData: array of byte): Word;
var
   b: Byte;
   i: Integer;
begin
   Result := $FFFF;
   for i := LOW(BufferData) to HIGH(BufferData) do
   begin
     b:= BufferData[i] xor Result;
     Result:= Result shr 8 xor tableCRC16[b];
   end;
end;
5. Через указатель на массив и длину массива (способ чуть медленнее табличного, но быстрее всех остальных)
function ModbusCRC5(BufferData: pointer; size: integer): word;
var
  t: ^byte;
  Data: byte;
  i,j: integer;
const
  polinomio:word= $A001;
begin
 t:= BufferData;

  result:= $FFFF;
  for i:= 0 to size-1 do
  begin
    data:= t^;
    for j:=1 to 8 do
    begin
      if (((data xor result) and $0001) = 1) then
         result:=(result shr 1) xor polinomio
      else
         result:=result shr 1;
      data:= data shr 1;
    end;
    inc(t)  end;
end;
Сборный модуль ModbusCRC с бонусом подсчета CRC16 и CRC32 прилагаю.

Ресурсы по тематике
  1. Удаленный промышленый терминал-индикатор по Modbus RTU
  2. Промышленный MODBUS/M-Link/OPC.WEB сервер-шлюз терминала оператора
  3. Libmodbus. Библиотека Modbus for Linux, Mac OS X, FreeBSD, QNX and Win32
  4. PascalScada. Библиотека для работы с ПЛК под Lazarus (Windows/Linux/FreeBSD)
  5. Библиотеки и классы протокола Modbus TCP/IP for C#/Delphi/Lazarus/PHP/Python
  6. Использование ПЛК в самогоноварении

Комментариев нет:

Отправить комментарий

В комментариях уважайте собеседника, внимательно читайте посты и не додумывайте. Просьбы и предложения из разряда: «можно ваш Skype/Viber/телефон», «напишите мне в vk/FB», а также другие им подобные — игнорируются. Выход новых версий ПО, внешняя ссылка, переставшая работать с течением времени и т.п. не является основанием для претензий. Желающие спокойно подискутировать и высказаться — Welcome. Желающие спонсировать блог — Donate. Нарушение этих простых правил ведет к бану и удалению комментариев без предупреждения.