MODBUS - generera CRC-16 ?

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

MODBUS - generera CRC-16 ?

Inlägg av jesse »

Jag såg att CRC-16 diskuterades i tråden om "skicka många databytes" men tänkte att den här frågan ändå är värd en egen tråd.

Jag försöker tolka ett RS485-protokoll på kinesiska för en laddare (BestGo) och har lyckats översätta det mesta till begriplig information. Men i slutet av dokumentet har de en funktion för beräkning av CRC enligt MODBUS-300, men jag fattar inte varför de blandar in två tabeller med en massa siffror???

Är det inte så att all MODBUS CRC genereras på samma sätt (standard) eller har BestGo sin egen variant? De hänvisar ju till MODBUS-300 som referens :humm:

Normalt kan man ju generera CRC enkelt utan tabeller. Eller har de bara valt att göra det på ett annorlunda sätt? Jag kan inte se att deras funktion skulle vara effektivare på något vis. Jag har inte testat om den ger samma resultat som MODBUS standard-CRC.

I MODBUS 300 (sidan 112) finns beskrivet hur CRC genereras:
A procedure for generating a CRC is:

1, Load a 16–bit register with FFFF hex (all 1’s). Call this the CRC register.

2. Exclusive OR the first 8–bit byte of the message with the low–order byte
of the 16–bit CRC register, putting the result in the CRC register.

3. Shift the CRC register one bit to the right (toward the LSB), zero–filling the
MSB. Extract and examine the LSB.

4. (If the LSB was 0): Repeat Step 3 (another shift).
(If the LSB was 1): Exclusive OR the CRC register with the polynomial
value A001 hex (1010 0000 0000 0001).

5. Repeat Steps 3 and 4 until 8 shifts have been performed. When this is
done, a complete 8–bit byte will have been processed.
CRC-16.gif
och i protokollet för laddaren:

Kod: Markera allt

// CRC-beräkning, funktion:

WORD ModbusCRC(BYTE * pData, BYTE len)
{
	BYTE byCRCHi = 0xff;
	BYTE byCRCLo = 0xff;
	BYTE byIdx;
	WORD crc;

	while(len--)
	{
		byIdx = byCRCHi ^* pData++;
		byCRCHi = byCRCLo ^ gabyCRCHi[byIdx];
		byCRCLo = gabyCRCLo[byIdx];
	}
	
	crc = byCRCHi;
	crc <<= 8;
	crc += byCRCLo;
	return crc;
}

// Hög byte av CRC-kod tabell

BYTE gabyCRCHi[] =
{
	0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,
	0x80,0x41,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
	0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,
	0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
	0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x00,0xc1,
	0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,
	0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x00,0xc1,
	0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
	0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,
	0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,
	0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,
	0x81,0x40,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
	0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,
	0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,
	0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x01,0xc0,
	0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
	0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,
	0x80,0x41,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
	0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,
	0x80,0x41,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
	0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x01,0xc0,
	0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,
	0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,
	0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
	0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,
	0x80,0x41,0x00,0xc1,0x81,0x40
};

// Hög byte av CRC-kod, tabell (felskrivet? ska vara låg byte)

BYTE	gabyCRCLo[] = 
{
	0x00,0xc0,0xc1,0x01,0xc3,0x03,0x02,0xc2,0xc6,0x06,
	0x07,0xc7,0x05,0xc5,0xc4,0x04,0xcc,0x0c,0x0d,0xcd,
	0x0f,0xcf,0xce,0x0e,0x0a,0xca,0xcb,0x0b,0xc9,0x09,
	0x08,0xc8,0xd8,0x18,0x19,0xd9,0x1b,0xdb,0xda,0x1a,
	0x1e,0xde,0xdf,0x1f,0xdd,0x1d,0x1c,0xdc,0x14,0xd4,
	0xd5,0x15,0xd7,0x17,0x16,0xd6,0xd2,0x12,0x13,0xd3,
	0x11,0xd1,0xd0,0x10,0xf0,0x30,0x31,0xf1,0x33,0xf3,
	0xf2,0x32,0x36,0xf6,0xf7,0x37,0xf5,0x35,0x34,0xf4,
	0x3c,0xfc,0xfd,0x3d,0xff,0x3f,0x3e,0xfe,0xfa,0x3a,
	0x3b,0xfb,0x39,0xf9,0xf8,0x38,0x28,0xe8,0xe9,0x29,
	0xeb,0x2b,0x2a,0xea,0xee,0x2e,0x2f,0xef,0x2d,0xed,
	0xec,0x2c,0xe4,0x24,0x25,0xe5,0x27,0xe7,0xe6,0x26,
	0x22,0xe2,0xe3,0x23,0xe1,0x21,0x20,0xe0,0xa0,0x60,
	0x61,0xa1,0x63,0xa3,0xa2,0x62,0x66,0xa6,0xa7,0x67,
	0xa5,0x65,0x64,0xa4,0x6c,0xac,0xad,0x6d,0xaf,0x6f,
	0x6e,0xae,0xaa,0x6a,0x6b,0xab,0x69,0xa9,0xa8,0x68,
	0x78,0xb8,0xb9,0x79,0xbb,0x7b,0x7a,0xba,0xbe,0x7e,
	0x7f,0xbf,0x7d,0xbd,0xbc,0x7c,0xb4,0x74,0x75,0xb5,
	0x77,0xb7,0xb6,0x76,0x72,0xb2,0xb3,0x73,0xb1,0x71,
	0x70,0xb0,0x50,0x90,0x91,0x51,0x93,0x53,0x52,0x92,
	0x96,0x56,0x57,0x97,0x55,0x95,0x94,0x54,0x9c,0x5c,
	0x5d,0x9d,0x5f,0x9f,0x9e,0x5e,0x5a,0x9a,0x9b,0x5b,
	0x99,0x59,0x58,0x98,0x88,0x48,0x49,0x89,0x4b,0x8b,
	0x8a,0x4a,0x4e,0x8e,0x8f,0x4f,0x8d,0x4d,0x4c,0x8c,
	0x44,0x84,0x85,0x45,0x87,0x47,0x46,0x86,0x82,0x42,
	0x43,0x83,0x41,0x81,0x80,0x40
};
Jag får väl koda in det och kolla om det ger samma resultat. Men det är ju korkat om jag måste ha en unik CRC-rutin för bara den laddaren. :|
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46989
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: MODBUS - generera CRC-16 ?

Inlägg av TomasL »

Kika i modbus-standarden, där är det utförligt beskrivet.
Vanligen använder man en uppslagstabell istället (två stycken) eftersom CRC'n är två byte.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: MODBUS - generera CRC-16 ?

Inlägg av sodjan »

> mmen jag fattar inte varför de blandar in två tabeller med en massa siffror

När det gäller tabeller rent generellt så är det en (vanlig?) metod
att snabba upp CRC beräkningar.
Användarvisningsbild
vfr
EF Sponsor
Inlägg: 3515
Blev medlem: 31 mars 2005, 17:55:45
Ort: Kungsbacka

Re: MODBUS - generera CRC-16 ?

Inlägg av vfr »

Jag har byggt CRC-funktioner med både bitfipplande och tabeller. Ska man ha en vanlig 8-bitars mikrokontroller så är väl 8-bits tabeller, alltså 256 värden a 16 bitar, den vanliga metoden. Tabellmetoden tar lite större plats men är avsevärt snabbare. Till PIC har jag också använt en 4-bitars variant. Att den är särskilt effektiv på just PIC, i det här fallet PIC16, beror på att man där har 'SWAPF'-instruktionen som gör det enkelt att få ut 4-bitars indexvärden utan quadrupla skift/rotate. Man får då en hybridlösning som är hyffsat snabb men ändå tar lite minne eftersom tabellen bara har 16 värden.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46989
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: MODBUS - generera CRC-16 ?

Inlägg av TomasL »

Jesse gå in på modbus hemsida, och tanka ned den riktiga standarden, i stället för det där gamla 300-dokumentet.
Användarvisningsbild
Korken
Inlägg: 2230
Blev medlem: 3 februari 2006, 19:19:36
Ort: Luleå, Porsön

Re: MODBUS - generera CRC-16 ?

Inlägg av Korken »

Och för oss som inte orkar skriva en egen CRC rutin: http://www.tty1.net/pycrc/index_en.html ;)
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46989
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: MODBUS - generera CRC-16 ?

Inlägg av TomasL »

Modbus använder inte standard CRC-16 utan en egen implementation.

Dessutom finns koden i modus-dokumentationen.
Användarvisningsbild
vfr
EF Sponsor
Inlägg: 3515
Blev medlem: 31 mars 2005, 17:55:45
Ort: Kungsbacka

Re: MODBUS - generera CRC-16 ?

Inlägg av vfr »

Då är det väl inte en CRC-16, eller?

"CRC-16" är ett väldefinierat polynom, liksom "CRC-CCITT". Däremot kan man ju kalla alla dessa för 16-bitars CRC. Om dom nu använder ett eget polynom i modbus så skulle jag inte kalla det för CRC-16 av risken för sammanblandningar.
danwi
Inlägg: 386
Blev medlem: 16 oktober 2008, 17:00:59
Ort: Linköping

Re: MODBUS - generera CRC-16 ?

Inlägg av danwi »

Nej, CRC-16 innebär inte ett speciellt polynom utan det specar bara längden.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46989
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: MODBUS - generera CRC-16 ?

Inlägg av TomasL »

Både ja och nej
sixteen conflicting definitions of CRC-16

Kod: Markera allt

Name 						Polynomial 																																																			


CRC-16-IBM 			x^{16} + x^{15} + x^2 + 1 (Bisync, Modbus, USB, ANSI X3.28, many others; also known as CRC-16 and CRC-16-ANSI) 	
CRC-16-CCITT 		x^{16} + x^{12} + x^5 + 1 (X.25, V.41, HDLC, XMODEM, Bluetooth, PACTOR, SD, many others; known as CRC-CCITT) 		
CRC-16-T10-DIF 	x^{16} + x^{15} + x^{11} + x^{9} + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 (SCSI DIF) 															
CRC-16-DNP 			x^{16} + x^{13} + x^{12} + x^{11} + x^{10} + x^8 + x^6 + x^5 + x^2 + 1 (DNP, IEC 870, M-Bus) 										
CRC-16-DECT 		x^{16} + x^{10} + x^8 + x^7 + x^3 + 1 (cordless telephones)[23] 																								
CRC-16-ARINC 		x^{16} + x^{15} + x^{13} + x^5 + x^3 + x + 1 (ACARS applications[24]) 																					
CRC-16-Fletcher Not a CRC; see Fletcher's checksum 	Used in Adler-32 A & B CRCs
Dessutom, Modbus är Big Endian (dock behöver själva data inte vara det).
Användarvisningsbild
exile
EF Sponsor
Inlägg: 496
Blev medlem: 21 oktober 2005, 23:32:07

Re: MODBUS - generera CRC-16 ?

Inlägg av exile »

Om du använder AVR kan jag tipsa om crc16 för AVR. Den är ganska snabb och liten. :)
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: MODBUS - generera CRC-16 ?

Inlägg av jesse »

(har varit ute på landet några dagar - utan internet)
TomasL skrev:Jesse gå in på modbus hemsida, och tanka ned den riktiga standarden, i stället för det där gamla 300-dokumentet.
Nu är det ju så att tillverkaren av laddaren hänvisar till just MODBUS-300, så jag antar att det är den standarden de själva har följt. Problemet är att jag inte har laddaren hemma att prova på, annars hade det ju varit lätt att kolla hur det fungerar.. Jag får hoppas att CRC-funktionen är densamma.

Jag testade CRC-funktionen i laddarens datablad, men den gav en helt annan kod som svar än standard MODBUS gav. Frågan är om det är fel i deras datablad eller om de har implementerat en egen variant av CRC (och varför i så fall kan man ju undra?)

Nu är jag bara hemma ett par dagar, men måste jobba, så jag hinner inte med att sitta och koda CRC-generatorer för att jämföra just nu. Får se om jag får lite tid över i kväll eller i helgen.

exile: Jo, den är enkel och bra. Borde väl gå att modifiera om det skulle behövas. Fördelen med den funktionen är att den räknar ut CRC för varje mottagen byte - på så vis är CRC färdigt i samma ögonblick som sista byte är inläst. De flesta andra kodexempel på nätet loopar igenom en array av data efter att all data lästs in. (EDIT: Den är förstås skriven i assembler - C-koden var bara "ekvivalent"... då är det ju inte lika lätt att modifiera. :humm: Men med tanke på hur bra optimeringen för C-kod fungerar så blir den nog lika effektiv även om man skriven den själv i C)
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: MODBUS - generera CRC-16 ?

Inlägg av jesse »

Nu har jag implementerat fyra kodexempel - och alla fungerar (fast låga och höga byten blandas ihop ...).
Ett av dem var AVR-exemplet som exile postade ovan.

data = "123456789"; // nio ASCII-tecken.

Resultat: (Code::Blocks)

Kod: Markera allt

kines CRC 0x374B                (kodexemplet i kina-laddarens dokument)
annan CRC 0x4B37                (hittad på nätet nånstans)
MB300 CRC 0x374B                (kodexempel i MODBUS 300 protokollet)
AVR   CRC 0x4B37                (Atmel's kodexempel i deras bibliotek)

Process returned 0 (0x0)   execution time : 0.031 s
Press any key to continue.
Facit har jag här: On-line CRC calculation and free library
On-line CRC calculation and free library

"123456789"
1 byte checksum 221
CRC-16 0xBB3D
CRC-16 (Modbus) 0x4B37
CRC-16 (Sick) 0x56A6
CRC-CCITT (XModem) 0x31C3
CRC-CCITT (0xFFFF) 0x29B1
CRC-CCITT (0x1D0F) 0xE5CC
CRC-CCITT (Kermit) 0x8921
CRC-DNP 0x82EA
CRC-32 0xCBF43926
Hela koden här: (jag har bytt ut datatyperna så de passar kompilatorn. uint16_t ==> unsigned short och uint8_t ==> unsigned char)

Kod: Markera allt

#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

unsigned short BestGoModbusCRC16(unsigned char * pData, unsigned char len);
unsigned short OTHER_CRC16func (const unsigned char *nData, unsigned short wLength);
unsigned short MODBUS300_CRC16 (puchMsg, usDataLen);
unsigned short AVRcrc16_update(unsigned short crc, unsigned char a);

int main()
{
    int antal = 9;
    char data[] = "123456789";

    /*data[0] = '1';
    data[1] = 0;
    data[2] = 0;
    data[3] = 0;
    data[4] = '5';
    data[5] = 'a';
    data[6] = 'n';
    data[7] = 'd';
    data[8] = 'e';
    data[9] = 250;*/

    printf ("kines CRC 0x%X\n", (int)BestGoModbusCRC16(data,antal) & 0xffff);
    printf ("annan CRC 0x%X\n", (int)OTHER_CRC16func(data,antal) & 0xffff);
    printf ("MB300 CRC 0x%X\n", (int)MODBUS300_CRC16(data,antal) & 0xffff);
    unsigned short AVRCRC = 0xFFFF; // start
    int i;
    for (i=0; i< antal; i++) {
        AVRCRC = AVRcrc16_update(AVRCRC, data[i]);
    }
    printf ("AVR  CRC 0x%X\n", (int)AVRCRC & 0xffff);
    return 0;
}

// *********************** KINALADDARENS KOD ***********************
unsigned short BestGoModbusCRC16(unsigned char * pData, unsigned char len)
{
    // Hög byten av CRC-koden tabell

    const unsigned char gabyCRCHi[] =
    {
        0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,
        0x80,0x41,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
        0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,
        0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
        0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x00,0xc1,
        0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,
        0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x00,0xc1,
        0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
        0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,
        0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,
        0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,
        0x81,0x40,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
        0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,
        0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,
        0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x01,0xc0,
        0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,
        0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,
        0x80,0x41,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
        0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,
        0x80,0x41,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
        0x01,0xc0,0x80,0x41,0x00,0xc1,0x81,0x40,0x01,0xc0,
        0x80,0x41,0x00,0xc1,0x81,0x40,0x00,0xc1,0x81,0x40,
        0x01,0xc0,0x80,0x41,0x01,0xc0,0x80,0x41,0x00,0xc1,
        0x81,0x40,0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,
        0x00,0xc1,0x81,0x40,0x01,0xc0,0x80,0x41,0x01,0xc0,
        0x80,0x41,0x00,0xc1,0x81,0x40
    };

    // Hög byten av CRC-koden tabell (LÅG)

    const unsigned char	gabyCRCLo[] =
    {
        0x00,0xc0,0xc1,0x01,0xc3,0x03,0x02,0xc2,0xc6,0x06,
        0x07,0xc7,0x05,0xc5,0xc4,0x04,0xcc,0x0c,0x0d,0xcd,
        0x0f,0xcf,0xce,0x0e,0x0a,0xca,0xcb,0x0b,0xc9,0x09,
        0x08,0xc8,0xd8,0x18,0x19,0xd9,0x1b,0xdb,0xda,0x1a,
        0x1e,0xde,0xdf,0x1f,0xdd,0x1d,0x1c,0xdc,0x14,0xd4,
        0xd5,0x15,0xd7,0x17,0x16,0xd6,0xd2,0x12,0x13,0xd3,
        0x11,0xd1,0xd0,0x10,0xf0,0x30,0x31,0xf1,0x33,0xf3,
        0xf2,0x32,0x36,0xf6,0xf7,0x37,0xf5,0x35,0x34,0xf4,
        0x3c,0xfc,0xfd,0x3d,0xff,0x3f,0x3e,0xfe,0xfa,0x3a,
        0x3b,0xfb,0x39,0xf9,0xf8,0x38,0x28,0xe8,0xe9,0x29,
        0xeb,0x2b,0x2a,0xea,0xee,0x2e,0x2f,0xef,0x2d,0xed,
        0xec,0x2c,0xe4,0x24,0x25,0xe5,0x27,0xe7,0xe6,0x26,
        0x22,0xe2,0xe3,0x23,0xe1,0x21,0x20,0xe0,0xa0,0x60,
        0x61,0xa1,0x63,0xa3,0xa2,0x62,0x66,0xa6,0xa7,0x67,
        0xa5,0x65,0x64,0xa4,0x6c,0xac,0xad,0x6d,0xaf,0x6f,
        0x6e,0xae,0xaa,0x6a,0x6b,0xab,0x69,0xa9,0xa8,0x68,
        0x78,0xb8,0xb9,0x79,0xbb,0x7b,0x7a,0xba,0xbe,0x7e,
        0x7f,0xbf,0x7d,0xbd,0xbc,0x7c,0xb4,0x74,0x75,0xb5,
        0x77,0xb7,0xb6,0x76,0x72,0xb2,0xb3,0x73,0xb1,0x71,
        0x70,0xb0,0x50,0x90,0x91,0x51,0x93,0x53,0x52,0x92,
        0x96,0x56,0x57,0x97,0x55,0x95,0x94,0x54,0x9c,0x5c,
        0x5d,0x9d,0x5f,0x9f,0x9e,0x5e,0x5a,0x9a,0x9b,0x5b,
        0x99,0x59,0x58,0x98,0x88,0x48,0x49,0x89,0x4b,0x8b,
        0x8a,0x4a,0x4e,0x8e,0x8f,0x4f,0x8d,0x4d,0x4c,0x8c,
        0x44,0x84,0x85,0x45,0x87,0x47,0x46,0x86,0x82,0x42,
        0x43,0x83,0x41,0x81,0x80,0x40
    };

	unsigned char byCRCHi = 0xff;
	unsigned char byCRCLo = 0xff;
	int byIdx;
	unsigned short crc;

	while(len--)
	{
		byIdx = byCRCHi ^* pData++;
		byCRCHi = byCRCLo ^ gabyCRCHi[byIdx];
		byCRCLo = gabyCRCLo[byIdx];
	}

	crc = byCRCHi;
	crc <<= 8;
	crc += byCRCLo;
	return crc;
}


/****************************** ANNAN CRC-FUNKTION *******************************/
unsigned short OTHER_CRC16func (const unsigned char *nData, unsigned short wLength)
{
    static const unsigned short wCRCTable[] = {
        0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241,
        0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440,
        0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40,
        0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841,
        0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40,
        0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,
        0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,
        0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,
        0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,
        0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,
        0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,
        0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,
        0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,
        0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,
        0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,
        0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,
        0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,
        0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,
        0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,
        0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,
        0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,
        0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,
        0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,
        0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,
        0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,
        0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,
        0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,
        0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,
        0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,
        0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,
        0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,
        0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 };

    unsigned char nTemp;
    unsigned short wCRCWord = 0xFFFF;

    while (wLength--)
    {
        nTemp = *nData++ ^ wCRCWord;
        wCRCWord >>= 8;
        wCRCWord ^= wCRCTable[nTemp];
    }
    return wCRCWord;

}

/************************** MODBUS 300 CRC KOD ********************
CRC Generation (Continued)
Example (Continued)
The function takes two arguments:
unsigned char *puchMsg ;  A pointer to the message buffer containing
binary data to be used for generating the CRC
unsigned short usDataLen ;The quantity of bytes in the message buffer.
The function returns the CRC as a type unsigned short.   **/

/*********** CRC Generation Function ****************/

unsigned short MODBUS300_CRC16 (puchMsg, usDataLen)

unsigned char *puchMsg ; /* message to calculate CRC upon */
unsigned short usDataLen ; /* quantity of bytes in message */

{

    /* Table of CRC values for high–order byte */
    static unsigned char auchCRCHi[] = {
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
        0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
        0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
        0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
        0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
        0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
        0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
        0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
        0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
        0x40  } ;

    /* Table of CRC values for low–order byte */
    static char auchCRCLo[] = {
        0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
        0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
        0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
        0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
        0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
        0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
        0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
        0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
        0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
        0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
        0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
        0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
        0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
        0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
        0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
        0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
        0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
        0x40  } ;


    unsigned char uchCRCHi = 0xFF ; /* high byte of CRC initialized */
    unsigned char uchCRCLo = 0xFF ; /* low byte of CRC initialized */
    unsigned uIndex ; /* will index into CRC lookup table */

    while (usDataLen--) /* pass through message buffer */
    {
        uIndex = uchCRCHi ^ *puchMsg++ ; /* calculate the CRC */
        uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ;
        uchCRCLo = auchCRCLo[uIndex] ;
    }
    return (uchCRCHi << 8 | uchCRCLo) ;
}

/****************** AVR CRC16 **********************/

unsigned short AVRcrc16_update(unsigned short crc, unsigned char a)
    {
        int i;

        crc ^= a;
        for (i = 0; i < 8; ++i)
        {
            if (crc & 1)
                crc = (crc >> 1) ^ 0xA001;
            else
                crc = (crc >> 1);
        }

        return crc;
    }
(AVR-rutinen går säkert att optimera rejält genom att sluta skyffla CRC-koden fram och tillbaks som argument till funktonen utan istället lägger man den som en global variabel.)
Skriv svar