Найти в Дзене
Ilya Engineer

CRC 16 код для ModBus на C#

В статье рассказывается как написать код для формирования и проверки CRC кода при обмене пакетами по протоколу ModBus

Для чего нужен CRC и как с ним работать если Вас заинтересовала данная статья, думаю Вы уже знаете.

Здесь я приведу пример как генерировать CRC код для ModBus протокола в программах написанных на С sharp

В примере будет приведен код класса CRC созданного мной и позволяющего генерировать ModBus из строки символов.

Первая функция класса CRC_calc:

public static int CRC_calc(string datas)
{
int k = 0;
int length_command = datas.Length;
int i;
int crc_result = 0xFFFF;
while (length_command > 0)
{
length_command--;
crc_result ^= (ushort)datas[k];
k++;
for (i = 0; i < 8; i++)
{
if ((crc_result & 0x01) == 1)
crc_result = (crc_result >> 1) ^ 0xA001;
else crc_result = (crc_result >> 1);
}
}
return crc_result;
}

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

int crc1 = CRC.CRC_calc(Data);
Data = Data + (char)(crc1 & 0xFF);
Data = Data + (char)(crc1 >> 8);

Переменная Data Содержит строку полезных данных, которые мы хотим передать. Вычисляем CRC для этой строки, добавляем к строке и можем передавать.

Если же мы хотим проверить строку, которая к нам пришла и в которой уже есть CRC, то необходимо вычислять код уже не от всей строки, следует отбросить 2 последних символа. Фактически функция такая же, только в одной строке добавится -2

public static int CRC_calc_check(byte[] datas)
{
int k = 0;
int length_command = datas.Length - 2;
int i;
int crc_result = 0xFFFF;
while (length_command > 0)
{
length_command--;
crc_result ^= (ushort)datas[k];
k++;
for (i = 0; i < 8; i++)
{
if ((crc_result & 0x01) == 1)
crc_result = (crc_result >> 1) ^ 0xA001;
else crc_result = (crc_result >> 1);
}
}
return crc_result;
}

Обратите внимание что здесь я уже работаю с массивом байт (входные данные, подчеркнуто), в общем тут как Вам будет удобнее, можно заменить на строку:

public static int CRC_calc_check(string datas)
{
int k = 0;
int length_command = datas.Length - 2;
int i;
int crc_result = 0xFFFF;
while (length_command > 0)
{
length_command--;
crc_result ^= (ushort)datas[k];
k++;
for (i = 0; i < 8; i++)
{
if ((crc_result & 0x01) == 1)
crc_result = (crc_result >> 1) ^ 0xA001;
else crc_result = (crc_result >> 1);
}
}
return crc_result;
}

Код не изменится, а всё зависит от входных данных с которыми Вам удобнее работать.

А имея первую функцию, можно упростить так:

public static int CRC_calc_check(byte[] data)
{
string datas ="";
for (int n = 0; n < (data.Length - 2); n++) datas = datas + (char)data[n];
return CRC_calc(datas);
}

Применение функции:

byte[] asciiBytes = Encoding.GetEncoding(1251).GetBytes(indata);
int crc = CRC.CRC_calc_check(asciiBytes);
if (crc == (asciiBytes[asciiBytes.Length - 1] * 256 +
asciiBytes[asciiBytes.Length - 2])
) {здесь делаем что-то с полученными данными, теперь мы знаем, что они пришли без ошибок;}

В функции indata – полученные данные. Обратите внимание мы преобразуем их из строки в массив байт в кодировке CP-1251. Это очень важно, иначе все символы с кодом более 127 будут не верно расшифровываться.

Данные я получаю из COM-порта, и выглядит это вот так:

public voidDataReceivedHandler(
object sender,
SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
indata = indata+(sp.ReadExisting());
}

Как я уже говорил переменная indata строковая.

Вот и всё в следующей статье я расскажу, как получать данные из COM порта и отправлять их в порт.

Подписывайтесь на канал, ставьте палец вверх, а я буду рассказывать Вам как создавать собственные устройства на Arduino и не только и управлять ими с компьютера, телефона и других умных гаджетов.

Если у Вас возникли вопросы, пишите мне на почту engineer.ilya@yandex.ru