В статье рассказывается как написать код для формирования и проверки 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