Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
а затем, последовательно проанализировав восемь бит старшего её байта,
Команды цикла также отнимают время: только блок команд FOR-NEXT отнимает втрое больше времени, по сравнению с командой WXOR.
предложенный алгоритм позволяет избавиться от команд вложенного цикла FOR-NEXT
вследствие словной или байтовой организации аккумулятора арифметико-логического устройства, результирующие битовые операции со словами/байтами в нём выполняются гораздо дольше операций со словами/байтами целиком, а в отдельных устройствах эти операции и вовсе не поддерживаются, тогда как алгоритм подразумевает выполнение восьми циклов с такими операциями.
public static byte[] Crc16( byte[] bytes )
{
int hi = 0xFF, lo = 0xFF;
foreach ( var t in bytes )
{
var b = ( byte ) ( lo ^ t );
lo = hi; hi = b;
if ( ( hi & 0x01 ) != 0 ) { hi ^= 0x02; lo ^= 0x40; }
if ( ( hi & 0x02 ) != 0 ) { hi ^= 0x04; lo ^= 0x80; }
if ( ( hi & 0x04 ) != 0 ) hi ^= 0x09;
if ( ( hi & 0x08 ) != 0 ) hi ^= 0x12;
if ( ( hi & 0x10 ) != 0 ) hi ^= 0x24;
if ( ( hi & 0x20 ) != 0 ) hi ^= 0x48;
if ( ( hi & 0x40 ) != 0 ) hi ^= 0x90;
if ( ( hi & 0x80 ) != 0 ) { hi ^= 0x20; lo ^= 0x01; }
}
return new [] { ( byte ) lo, ( byte ) hi };
}
/*
@echo off && cls
set dotnet=%windir%\Microsoft.NET\Framework
for %%n in (2.0.50727,3.5,4.0.30319) do if exist "%dotnet%\v%%n\csc.exe" set csc="%dotnet%\v%%n\csc.exe"
( %csc% /nologo /out:"%~0.exe" %0 && "%~0.exe" ) && del "%~0.exe"
exit
*/
using System;
using System.Collections.Generic;
class Program
{
public static byte[] Crc16( byte[] bytes )
{
int hi = 0xFF, lo = 0xFF;
foreach ( var t in bytes )
{
var b = ( byte ) ( lo ^ t );
lo = hi; hi = b;
if ( ( hi & 0x01 ) != 0 ) { hi ^= 0x02; lo ^= 0x40; }
if ( ( hi & 0x02 ) != 0 ) { hi ^= 0x04; lo ^= 0x80; }
if ( ( hi & 0x04 ) != 0 ) hi ^= 0x09;
if ( ( hi & 0x08 ) != 0 ) hi ^= 0x12;
if ( ( hi & 0x10 ) != 0 ) hi ^= 0x24;
if ( ( hi & 0x20 ) != 0 ) hi ^= 0x48;
if ( ( hi & 0x40 ) != 0 ) hi ^= 0x90;
if ( ( hi & 0x80 ) != 0 ) { hi ^= 0x20; lo ^= 0x01; }
}
return new [] { ( byte ) lo, ( byte ) hi };
}
static void Main( string[] args )
{
var query = new List<byte> { 0x01, 0x03, 0x00, 0x02, 0x00, 0x01 };
query.AddRange( Crc16( query.ToArray() ) );
Console.WriteLine( BitConverter.ToString( query.ToArray() ) );
Console.ReadKey();
}
}
01-03-00-02-00-01-25-CAВерсия из мира C99. nData — массив до 255 байт
uint16_t MODBUS_CRC16(const uint8_t *nData, uint16_t wLength){
union{
uint8_t u8[2];
uint16_t u16;
}val;
val.u16 = 0xFFFF;
for(uint_least8_t i=0;i<wLength;i++){ //до 255 элементов при подсчете CRC, иначе другой тип i
uint_least8_t b = val.u8[0] ^ nData[i];
val.u8[0] = val.u8[1]; val.u8[1] = b;
if ( ( val.u8[1] & 0x01 ) != 0 ) { val.u8[1] ^= 0x02; val.u8[0] ^= 0x40;}
if ( ( val.u8[1] & 0x02 ) != 0 ) { val.u8[1] ^= 0x04; val.u8[0] ^= 0x80; }
if ( ( val.u8[1] & 0x04 ) != 0 ) val.u8[1] ^= 0x09;
if ( ( val.u8[1] & 0x08 ) != 0 ) val.u8[1] ^= 0x12;
if ( ( val.u8[1] & 0x10 ) != 0 ) val.u8[1] ^= 0x24;
if ( ( val.u8[1] & 0x20 ) != 0 ) val.u8[1] ^= 0x48;
if ( ( val.u8[1] & 0x40 ) != 0 ) val.u8[1] ^= 0x90;
if ( ( val.u8[1] & 0x80 ) != 0 ) { val.u8[1] ^= 0x20; val.u8[0] ^= 0x01; }
}
return val.u16;
}Пример использования:
uint8_t nData[] = {0x01, 0x06, 0x03, 0xE9, 0x00, 0x10, 0x59, 0xB6};
uint8_t wLength = 8;
uint16_t myCRC = MODBUS_CRC16(nData,wLength);
printf("0x%04x\n", n);/*
@echo off && cls
set dotnet=%windir%\Microsoft.NET\Framework
for %%n in (2.0.50727,3.5,4.0.30319) do if exist "%dotnet%\v%%n\csc.exe" set csc="%dotnet%\v%%n\csc.exe"
( %csc% /nologo /out:"%~0.exe" %0 && "%~0.exe" ) && del "%~0.exe"
exit
*/
using System;
using System.Collections.Generic;
class Program
{
public static byte[] Crc16( byte[] bytes )
{
int hi = 0xFF, lo = 0xFF;
foreach ( var b in bytes )
{
var t = ( byte ) ( lo ^ b );
lo = hi; hi = t;
// Место флага (старшего разряда) занимает появляющийся при сдвиге влево нулевой разряд.
// Его комбинация (XOR) с младшим разрядом полинома 0x1021 должна всегда давать единицу.
// Поскольку флаг всегда равен единице, то мы должны обнулить младший разряд полинома,
// чтобы получить результат итерации аналогичный другим методам.
// По-простому: мы двигаем на каждой из 8 итераций не текущее значение crc, а полином, но
// в обратную сторону. Причём сдвиг циклический, т.к. в оригинале при сдвиге влево каждый
// раз получается один нулевой битик. Мы суём его на место старшего разряда, который нас
// безвременно покидает.
// 0001000000100001 1021 - полином
// Расчёт констант:
// 0000 1000 0001 0000 (0x1021 >> 1) & 0x7FFF = 0x0810
// 0000 0100 0000 1000 (0x1021 >> 2) & 0xBFFF = 0x0408
// 0000 0010 0000 0100 (0x1021 >> 3) & 0xDFFF = 0x0204
// 0000 0001 0000 0010 (0x1021 >> 4) & 0xEFFF = 0x0102
// 0000 0000 1000 0001 (0x1021 >> 5) & 0xF7FF = 0x0081
// 1000 0000 0100 0000 (0x1021 >> 6) & 0xFBFF = 0x8040
// 0100 0000 0010 0000 (0x1021 >> 7) & 0xFDFF = 0x4020
// 0010 0000 0001 0000 (0x1021 >> 8) & 0xFEFF = 0x2010
if ( ( hi & 0x80 ) != 0 ) { hi ^= 0x08; lo ^= 0x10; }
if ( ( hi & 0x40 ) != 0 ) { hi ^= 0x04; lo ^= 0x08; }
if ( ( hi & 0x20 ) != 0 ) { hi ^= 0x02; lo ^= 0x04; }
if ( ( hi & 0x10 ) != 0 ) { hi ^= 0x01; lo ^= 0x02; }
if ( ( hi & 0x08 ) != 0 ) { hi ^= 0x00; lo ^= 0x81; }
if ( ( hi & 0x04 ) != 0 ) { hi ^= 0x80; lo ^= 0x40; }
if ( ( hi & 0x02 ) != 0 ) { hi ^= 0x40; lo ^= 0x20; }
if ( ( hi & 0x01 ) != 0 ) { hi ^= 0x20; lo ^= 0x10; }
}
return new [] { ( byte ) lo, ( byte ) hi };
}
static void Main( string[] args )
{
var query = new List<byte> { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 };
query.AddRange( Crc16( query.ToArray() ) );
// Вывод: 31-32-33-34-35-36-37-38-39-29-B1
Console.WriteLine( BitConverter.ToString( query.ToArray() ) );
Console.ReadKey();
}
}#include
static const uint16_t crc16_table_4bit[16] = {
0x0000, 0xA001, 0xC002, 0x6003,
0x4004, 0xE005, 0x8006, 0x2007,
0x0008, 0xA009, 0xC00A, 0x600B,
0x400C, 0xE00D, 0x800E, 0x200F
};
uint16_t modbus_crc16_4bit(const uint8_t *data, uint32_t length)
{
uint16_t crc = 0xFFFF;
while (length--) {
crc ^= *data++;
// Process low nibble
uint8_t idx = crc & 0x0F;
crc >>= 4;
crc ^= crc16_table_4bit[idx];
// Process high nibble
idx = crc & 0x0F;
crc >>= 4;
crc ^= crc16_table_4bit[idx];
}
return crc;
}
Простой и эффективный расчёт Modbus CRC16 в ПЛК и МК без побитового сдвига и таблиц