Версия для печати

Применение СRС16 в PLC (STL и SCL)

Дата: 2008-10-04

Добавлено: komatic

Тема: Step7


Что такое CRC16?
Варианты использования в промышленных контроллерах
Реализация на SCL
Реализация на STL

Что такое CRC16?

CRC - с английского cyclic redundancy code, переводится как: циклический избыточный код, иногда называется контрольной суммой. CRC является простейшей хэш-функцией.
Вкратце - берем данные произвольной длины обрабатывая их по некоторому алгоритму и получаем, какоето n-разрядное число (например 16 разрядное, или word для CRC-16), меньшее чем размер исходных данных.
Т.е. было 100 Мб данных - получили два байта результата.

Способов расчета CRC очень много, только для CRC16 существует несколько (CRC-16-CCITT, CRC-16-DNP, CRC-16-IBM).
Как и у всех хэш-функций у CRC существует вероятность коллизий - когда для разных данных вычислится одинаковая CRC. Уменьшают вероятность колизий или увеличением размера результата, например CRC32, или вычислением нескольких контрольных сумм по разным алгоритмам.

Популярность CRC обусловлена тем, что подобная проверка просто реализуема в двоичном цифровом оборудовании, для контроллера это значит использование команд без плавающей точки, а значит большее быстродействие.

Зачем все это нужно?
Результат вычисления обыно добавляется в конец блока данных непосредственно перед началом передачи или сохранения данных на каком-либо носителе информации. Впоследствии он проверяется для подтверждения её целостности.

Подробнее можно найти в литературе:
Ross N. Williams Элементарное руководство по CRCалгоритмам обнаружения ошибок .pdf 200Кб
Anarchriz/DREAD CRC, и как его восстановить .pdf 122Кб
или по ссылке wikipedia

Варианты использования в промышленных контроллерах

Классический способ: проверка правильности переданных данных между контроллерами.

Уменьшение нагрузки на сетевой обмен или пересылок данных внутри одного PLC: передавать данные только тогда когда они изменились, т.е. изменилась их контрольная сумма. (очень напрашивается например обработка таким образом медленно меняющихся рецептов).

Упрощение обработки символьных строк в контроллере: можно обрабатывать не сами строки (сравнение, пересылка и т.д.), а их контрольные суммы, которые занимают меншьше места.

Определения момента изменения данных: для запуска функции обработки данных только в момент их изменения.

Реализация на SCL

Описание функционального блока
Тип алгоритма: Табличный, инициализация полинома W#16#FFFF, в конце перестановка байт.

TYPE UDT5                       // Определение ANY в UDT
    STRUCT                      // 10 байтов ANY указателя - например P#DBx.DBX y.z BYTE n
        SyntaxId    : BYTE;     // Всегда 10 Hex 
        DataType    : BYTE;     // Код типа данных - например 01=BOOL,02=BYTE..
        DataCount   : INT;      // Количество типов данных - например 10 BOOL-ов 
        DB_Number   : WORD;     // Номер блока данных
        BytePointer : DWORD;    // Указатель байт и бит
    END_STRUCT
END_TYPE
FUNCTION_BLOCK FB51
TITLE = " СRС16"
//
// Подсчет CRC16 с заданной области
// тип:
// прямой табличный алгоритм, с начальным значением полинома FFFF и перестановкой байтов в конце
//
// возможные коды ошибок:
// W#16#8001 - тип данных BOOL не поддерживается
// W#16#8002 - нулевая длина области
VERSION: " 1.0"
AUTHOR: komatic
NAME: CRC16
VAR_INPUT
    AREA:           ANY;            // сообщение (область данных) с которого вычисляется CRC16
END_VAR
VAR_OUTPUT
    Error:          WORD;           // код ошибки ( 0 если все нормально)
    CRC_VAL:        WORD;           // результат (CRC16)
END_VAR
VAR_TEMP
    CRC_AUX:        WORD;           // вспомогательный
    i:              INT;            // для цикла
    Buffer:         UDT5;           // для обработки ANY первой области
    TempAny    AT Buffer: ANY;     
    ByteNr:         INT;            // Смещение на начало области
    ByteCount:      INT;            // Количество байтов
    TableIndex:     INT;            // Индекс в таблице
END_VAR
VAR
    Table_CRC16:                    // таблица значений полиномов
    ARRAY[0..255] OF WORD:=
       [W#16#0000, W#16#C0C1, W#16#C181, W#16#140 W#16#C301, W#16#3C0 W#16#280 W#16#C241,
        W#16#C601, W#16#6C0 W#16#780 W#16#C741, W#16#500 W#16#C5C1, W#16#C481, W#16#440,
        W#16#CC01, W#16#CC0 W#16#D80 W#16#CD41, W#16#F00 W#16#CFC1, W#16#CE81, W#16#E40,
        W#16#A00 W#16#CAC1, W#16#CB81, W#16#B40 W#16#C901, W#16#9C0 W#16#880 W#16#C841,
        W#16#D801, W#16#18C0, W#16#1980, W#16#D941, W#16#1B00, W#16#DBC1, W#16#DA81, W#16#1A40,
        W#16#1E00, W#16#DEC1, W#16#DF81, W#16#1F40, W#16#DD01, W#16#1DC0, W#16#1C80, W#16#DC41,
        W#16#1400, W#16#D4C1, W#16#D581, W#16#1540, W#16#D701, W#16#17C0, W#16#1680, W#16#D641,
        W#16#D201, W#16#12C0, W#16#1380, W#16#D341, W#16#1100, W#16#D1C1, W#16#D081, W#16#1040,
        W#16#F001, W#16#30C0, W#16#3180, W#16#F141, W#16#3300, W#16#F3C1, W#16#F281, W#16#3240,
       W#16#3600, W#16#F6C1, W#16#F781, W#16#3740, W#16#F501, W#16#35C0, W#16#3480, W#16#F441,
        W#16#3C00, W#16#FCC1, W#16#FD81, W#16#3D40, W#16#FF01, W#16#3FC0, W#16#3E80, W#16#FE41,   
        W#16#FA01, W#16#3AC0, W#16#3B80, W#16#FB41, W#16#3900, W#16#F9C1, W#16#F881, W#16#3840,
        W#16#2800, W#16#E8C1, W#16#E981, W#16#2940, W#16#EB01, W#16#2BC0, W#16#2A80, W#16#EA41,
        W#16#EE01, W#16#2EC0, W#16#2F80, W#16#EF41, W#16#2D00, W#16#EDC1, W#16#EC81, W#16#2C40,
        W#16#E401, W#16#24C0, W#16#2580, W#16#E541, W#16#2700, W#16#E7C1, W#16#E681, W#16#2640,
        W#16#2200, W#16#E2C1, W#16#E381, W#16#2340, W#16#E101, W#16#21C0, W#16#2080, W#16#E041,
        W#16#A001, W#16#60C0, W#16#6180, W#16#A141, W#16#6300, W#16#A3C1, W#16#A281, W#16#6240,
        W#16#6600, W#16#A6C1, W#16#A781, W#16#6740, W#16#A501, W#16#65C0, W#16#6480, W#16#A441,
        W#16#6C00, W#16#ACC1, W#16#AD81, W#16#6D40, W#16#AF01, W#16#6FC0, W#16#6E80, W#16#AE41,
        W#16#AA01, W#16#6AC0, W#16#6B80, W#16#AB41, W#16#6900, W#16#A9C1, W#16#A881, W#16#6840,
        W#16#7800, W#16#B8C1, W#16#B981, W#16#7940, W#16#BB01, W#16#7BC0, W#16#7A80, W#16#BA41,
        W#16#BE01, W#16#7EC0, W#16#7F80, W#16#BF41, W#16#7D00, W#16#BDC1, W#16#BC81, W#16#7C40,
        W#16#B401, W#16#74C0, W#16#7580, W#16#B541, W#16#7700, W#16#B7C1, W#16#B681, W#16#7640,
        W#16#7200, W#16#B2C1, W#16#B381, W#16#7340, W#16#B101, W#16#71C0, W#16#7080, W#16#B041,
        W#16#5000, W#16#90C1, W#16#9181, W#16#5140, W#16#9301, W#16#53C0, W#16#5280, W#16#9241,
        W#16#9601, W#16#56C0, W#16#5780, W#16#9741, W#16#5500, W#16#95C1, W#16#9481, W#16#5440,   
        W#16#9C01, W#16#5CC0, W#16#5D80, W#16#9D41, W#16#5F00, W#16#9FC1, W#16#9E81, W#16#5E40,
        W#16#5A00, W#16#9AC1, W#16#9B81, W#16#5B40, W#16#9901, W#16#59C0, W#16#5880, W#16#9841,
        W#16#8801, W#16#48C0, W#16#4980, W#16#8941, W#16#4B00, W#16#8BC1, W#16#8A81, W#16#4A40,
        W#16#4E00, W#16#8EC1, W#16#8F81, W#16#4F40, W#16#8D01, W#16#4DC0, W#16#4C80, W#16#8C41,
        W#16#4400, W#16#84C1, W#16#8581, W#16#4540, W#16#8701, W#16#47C0, W#16#4680, W#16#8641,
        W#16#8201, W#16#42C0, W#16#4380, W#16#8341, W#16#4100, W#16#81C1, W#16#8081]; //, W#16#4040];
END_VAR
LABEL
    Fail;                                           // метка для выхода по ошибке
END_LABEL
BEGIN
// из-за ограничения SCL на инициализацию только 255 значений (нужно 256 :))
Table_CRC16[255]:=W#16#4040;   
Error:=W#16#0;
// Подготавливаем указатель
TempAny:=AREA;                                      // Area теперь доступны через Buffer по UDT
// Находим длину сообщения в байтах
CASE CHAR_TO_INT(BYTE_TO_CHAR(Buffer.DataType)) OF
    1 : Error:=W#16#8001; GOTO Fail;                // тип ошибки - тип данных BOOL не поддерживается
    2 : ByteCount:=Buffer.DataCount;                // тип данных BYTE
    3 : ByteCount:=Buffer.DataCount;                // тип данных CHAR
    4 : ByteCount:=2*Buffer.DataCount;              // тип данных WORD
    5 : ByteCount:=2*Buffer.DataCount;              // тип данных INT
    6 : ByteCount:=4*Buffer.DataCount;              // тип данных DWORD
ELSE:                                                          
    Error:=W#16#8001;                               // тип ошибки - тип данных не поддерживается
    GOTO Fail;
END_CASE;
// Проверяем на нулевую длину области
IF ByteCount=0 THEN Error:=W#16#8002; GOTO Fail; END_IF // тип ошибки - нулевая длина области
// вычисляем номер байта первой области
ByteNr:=WORD_TO_INT(DWORD_TO_WORD(SHR(IN:=SHL(IN:=Buffer.BytePointer, N:=8), N:=11))); 
//----------- Алгоритм вычисления CRC16 ------------ начало
CRC_AUX:=W#16#FFFF;                                 // инициализация вспомогательной переменной
FOR i:=1 TO ByteCount BY 1 DO                       // обрабатываем каждый байт сообщения
    TableIndex:= WORD_TO_INT((WORD_TO_BLOCK_DB(Buffer.DB_Number).db[ByteNr] XOR CRC_AUX) AND W#16#FF);
    CRC_AUX:=ROR(IN:=CRC_AUX,N:=8) AND W#16#FF;
    CRC_AUX:=Table_CRC16[TableIndex] XOR CRC_AUX;
    ByteNr:=ByteNr+1;
END_FOR;
CRC_VAL:=ROR(IN:=CRC_AUX, N:=8);
//----------- Алгоритм вычисления CRC16 ------------ конец
Fail:;
END_FUNCTION_BLOCK
DATA_BLOCK DB51  FB51
BEGIN
END_DATA_BLOCK

Реализация на STL

Исходный текст был взят из интернета (автор неизвестен)

FUNCTION FC 50 : VOID
TITLE =
AUTHOR : BERMIT
FAMILY : MODBUS
NAME : CRC16
VERSION : 1.0
VAR_INPUT
  MSG_DB_NO : INT ;                    //Номер DB в котором подсчитывается CRC16 области
  MSG_DBB_NO : INT ;                    //Смещение на начало области
  MSG_LEN : INT ;                       //Длина области
  CRC_DB_NO : INT ;                    //Номер DB с таблицей CRC16
END_VAR
VAR_OUTPUT
  CRC_VAL : WORD ;                      //Вычисленное CRC16 области
END_VAR
VAR_TEMP
  MSG_POINTER : DWORD ;                //указатель на сообщение
  CRC_POINTER : DWORD ;                //указатель на таблицу с полиномами
  CRC_AUX : WORD ;                      //вспомогательная переменная для вычесления
  CHAR_CNT : WORD ;                    //счетчик байт в сообщении
  MSG_DB : WORD ;   
  CRC_DB : WORD ;   
END_VAR
BEGIN
NETWORK
TITLE =Проверка на длину области
      L     #MSG_LEN;
      L     0;
      <=I   ;
      BEC   ;                          // выход если длина области меньше 0
NETWORK
TITLE =Номера блоков данных
      L     #MSG_DB_NO;
      T     #MSG_DB;
      L     #CRC_DB_NO;
      T     #CRC_DB;
NETWORK
TITLE =Формирование указателя на область
      L     #MSG_DBB_NO;
      SLD   3;
      T     #MSG_POINTER;
      L     W#16#FFFF;                 // константа инициализации CRC
      T     #CRC_AUX;
NETWORK
TITLE =Вычисление CRC сообщения
      L     #MSG_LEN;                   //Длина области
NXT1: T     #CHAR_CNT;
      OPN   DB [#MSG_DB];               //блок данных для сообщения
      L     DBB [#MSG_POINTER];         //прочитали байт сообщения
      L     #CRC_AUX;                  // промежуточное значение
      XOW   ;                           //xor
      L     W#16#FF;
      AW    ;                           //выделили один байт из слова
      ITD   ;                           // в двойное слово
     SLD   3;                           // для адресации на байт
      SLD   1;                         // для адресации на слово
      T     #CRC_POINTER;               // получили указатель на таблицу слов
      L     #CRC_AUX;                   // сдвинули на 8
      SRW   8;
      L     W#16#FF;                    // выделили байт
      AW    ;
      T     #CRC_AUX;                   // сохранили для следующей итерации
      OPN   DB [#CRC_DB];               // открыли таблицу кфтов
      L     DBW [#CRC_POINTER];         // считали константу
      L     #CRC_AUX;
      XOW   ;
      T     #CRC_AUX;                   // сохранили для следующей итерации
      L     #MSG_POINTER;               // инкремент указателя на сообщение
      L     P#1.0;
      +D    ;
      T     #MSG_POINTER;
      L     #CHAR_CNT;
      LOOP  NXT1;                       //до тех пор пока не закончится сообщение
NETWORK
TITLE =Переставление байтов
      L     #CRC_AUX;                   // перестановка байтов
      CAW   ;
      T     #CRC_VAL;
END_FUNCTION

Тестирование на симуляторе показало, что время выполнения реализации на SCL в среднем в два раза больше чем на STL





Просмотров: 18641

Комментарии к материалу

Добавлен: r_andriy    Дата: 2009-10-29

а где UDT5 ???

Добавлен: komatic    Дата: 2009-10-29

Добавил UDT5, теперь компилируется

Добавлен: r_andriy    Дата: 2009-11-02

Да отлично компилируется.

Я потестировал реализацию на STL, она работаэт быстрее в ~10 раз чем моя написаная на STL но без использования масива.

Добавлен: r_andriy    Дата: 2009-11-02

Спасибо автору и постеру !!!

Добавлен: SHKODRAN    Дата: 2011-08-13

I tried the source written in SCL, and I must say that it only works to a certain address! Is possible to control whole db if the data changes?
Thankx.
Best regards
Shkodran.

Добавлен: Дмитрий    Дата: 2013-08-02

Вот спасибо, уже думал самому писать, скачал литературу. Начал в криптографию вникать. А тут добрые люди. Спасибо.

Добавлен: void    Дата: 2017-02-07

Неактуально.В последних Tia Portal не буя не работает.

Добавлен: Дмитрий    Дата: 2017-09-14

| Неактуально.В последних Tia Portal не буя не работает.

Так это Chalenge, надо починить.

Добавлен: Сергей    Дата: 2018-12-08

Спасибо большое автору!

Добавить комментарий

Ваше имя:

Текст комментария (4000 max):

Введите сумму с картинки: