crc 16 modbus algoritması nasıl oluyor

Başlatan erkan614, 18 Haziran 2009, 18:46:22

erkan614

iyi günler,
seriporttan aldığım 01 03 01 00 00 01 85 f6 hex datası
01 03 01 00 00 01 kısmı komut
85 f6  kısmı crc
burdanda görüldüğü gibi crc 0xf685 crc sini hesaplanmış.

benim karşıtarafa cevap verebilmem için crc hesaplamam gerekiyor yalnız bu hesaplamanın algoritmasını basitce anlatan bi yazı bulamadım yardımlarınızı bekliyorum



yufuk

CRC-16 basit olduğu kadar çokta karışık bir algoritma. Çünkü bir tane CRC-16 yok. Bir kaçtane farklı sonuclara ulaşılan CRC-16 hesaplama yöntemleri var. Burada fark lullanılan bir sabit sayıdan kaynaklanıyor bildiğim kadarıyla. Ben şimdiye kadar MODBUS RTU ile çalıştığım için
bu protokolde kullanılan CRC-16 yöntemini anlatabilirim. Diğerleri için araştırman lazım. MODBUS RTU'daki CRC-16 hesaplaması şu şekildedir:

Önce 16 bit'lik bir CRC değişkeni oluştur.


1. CRC değişkenine FFFF(hex) değerini yükle.
2. Mesajın ilk byte(8-bit)'ını, 16-bitlik CRC değişkeninin düşük değerlikli byte'ı ile Exclusive OR(özel veya) işlemine sok. Sonucu CRC değişkenine yaz.
3. CRC değişkenini 1-bit sağa kaydır(LSB'ye doğru).  MSB'yi "0" ile doldur. LSB'yi denetle.
4. Eğer LSB "0" ise: 3.Adımı tekrarla(Yani; CRC değişkenini sağa kaydır). Eğer LSB "1" ise:  CRC değişkenini 40961(0xA001 hex) rakamıyla  Exclusive OR işlemine sok.
5. 3. ve 4. adımları 8 defa tekrarla. Tekrarlama bittiğinde 1-byte işlemi tamamlanmış olacak.
6. Mesajın ikinci byte'ı  için 2. Ve 5. Adımların arasını tekrarla. Mesajın tüm byte'ları bitene kadar bu işlemleri tekrarla.
7. Bu işlemlerin sonunda CRC değişkeninin içeriği CRC değeri olacaktır.
8. CRC değeri mesajın sonuna eklernirken; yüksek ve düşük byte'ları yer değiştirilip eklenecektir.

NOT:


Aşağıdaki 2 adet "HEX" formatndaki rakamlar dizisi gerçek cihazlardan alınmış örnek mesajlardır. Eğer heaplama sonucunda CRC-16 değeri doğru çıkarsa başardın demektir.
"0B030006000224A0" -- " 0B0300060002" mesaj ,"24A0" CRC-16 değeri.
0B-03-00-06-00-02-24-A0  Cizgi arasındaki her hex değeri 1byte.


"0B0304000042C86105" -- "0B0304000042C8" mesaj, "6105" CRC-16 değeri
0B-03-04-00-00-42-C8-61-05  Cizgi arasındaki her hex değeri 1byte.




DİKKAT: Mesajı CRC-16 algoritmasına soktuğunuzda son 2 rakamı bunun dışında tutacaksınız. Çünkü son 2 rakam CRC-16 değeri. Sadece mesaj kısmını hesaplayıp bu son 2 rakamla karşılaştıracaksınız. Eğer aynı sonuca ulaşıyorsanız mesaj doğru demektir.






-------------------------------------------------------------------------------
Aşağıdaki kodlar VB6 kodları.


Private Sub Command1_Click()
Dim CRC As Long
Dim MESAJ As String
Dim MESAJ_BYTE As String
Dim MESAJ_BYTE_SAYI As Byte
Dim MESAJ_BOYU As Byte
Dim K As Byte
Dim I As Byte


CRC = 65535                 'CRC'YE İLK DEĞER AKTARILIYOR.
MESAJ = Trim(Text1.Text)    'TEXT KUTUSU MESAJ DEĞİŞKENİNE AKTARILIYOR.
MESAJ_BOYU = Len(MESAJ)     'MESAJIN BOYU BULUNUYOR.
MESAJ_BOYU = MESAJ_BOYU - 4 'MESAJ BOYUNDAN CRC DEĞERİ ÇIKARILIYOR.

For K = 1 To MESAJ_BOYU Step 2               'CRC16 KODUNU ÜRETMEK İÇİN DÖNGÜYE GİRİYOR.2 KARAKTER 1 BYTE YAPTIĞI İÇİN İKİŞER SAYIYOR.
   MESAJ_BYTE = Mid(MESAJ, K, 2)            'MESAJIN BİR BYTE'I ALINIYOR.
   MESAJ_BYTE_SAYI = Val("&H" & MESAJ_BYTE) 'ALINAN 2 KARAKTER HEX RAKAMA ÇEVRİLİYOR.
   CRC = CRC Xor MESAJ_BYTE_SAYI            'MESAJ BYTE'I CRC İLE XOR'LANIYOR. SONUC CRC DEĞİŞKENİNE YAZILIYOR.
   For I = 1 To 8                           'BU İŞLEMİ 8 KERE YAPMAK İÇİN DÖNGÜYE GİRİLİYOR.
       If (CRC And &H1) = 1 Then            'EĞER CRC'NİN LSB'Sİ "1" İSE
           CRC = CRC \ 2                    'CRC'Yİ 1 SAĞA KAYDIR.(2'YE BÖLMEK 1 SAĞA KAYDIRMAK DEMEKTİR).
           CRC = CRC Xor 40961              'CRC'Yİ HEX(A001) İLE XOR'LA.
       Else
           CRC = CRC \ 2                    'CRC'Yİ 1 SAĞA KAYDIR.(2'YE BÖLMEK 1 SAĞA KAYDIRMAK DEMEKTİR).
       End If
   Next
Next
   Label1.Caption = Hex(CRC)
End Sub
------------------------------------------------------------------------------
Aşağıdakilerde microBASIC kodları:

'//////////////////////////////////////////////////////////////////////////////
'CRC16 Hesaplama
Sub Procedure CRC_Hesap()
  CRC16 = $FFFF                'CRC'YE İLK DEĞER AKTARILIYOR.
  For SAY1 = 0 To 17          'Gönderilecek Byte kadar tekrarla.(Bu projede toplam 18 byte mesaj uzunluğu var. Sizin mesajınızın boyu ne kadarsa SAY1 değişkenine o kadar değer girin.)
      CRC16 = CRC16 Xor SONUC[SAY1]
      For SAY2 = 1 To 8
          If (CRC16 And $0001) = 1 Then
             CRC16 = CRC16 >> 1
             CRC16 = CRC16 Xor $A001
          Else
             CRC16 = CRC16 >> 1
          End If
      Next SAY2
  Next SAY1
  CRC16_L = CRC16 And $FF
  CRC16_H = Word((CRC16 And $FF00) >> 8 )
End Sub
'//////////////////////////////////////////////////////////////////////////////

mihri

CCS' de yazdığım modbus rtu kütüphanesi için crc16 hesabı alt programı aşağıda kolay gelsin.

/*=================================================================================
/*                        CRC HESABI İÇİN ALT PROGRAM
/*================================================================================*/
/* CRC High byte look up table */ 
const char Table_CRC_Hi[256] = { 
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 
} ; 

/*CRC Low byte look up table*/ 
const char Table_CRC_Lo[256] = { 
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 
};

long int CRC16(char *message, int length) 
{ 
   char CRC_Hi = 0xFF;             
   char CRC_Lo = 0xFF;          
   long int CRC;            
   int index=0;               
   while (length--) 
   { 
      index = CRC_Hi ^ *message++;          
      CRC_Hi = CRC_Lo ^ Table_CRC_Hi[index]; 
      CRC_Lo = Table_CRC_Lo[index]; 
   } 
   CRC = CRC_Hi;                   
   CRC = (CRC<<8) | CRC_Lo; 
   return CRC; 
}
/*--------------------------------------------------------------------------------------*/
"Eppur si muove!"

yufuk

Bu sitede Web Sayfası üzerinde tüm CRC algoritmalarını hesaplama programı var. Sonuçlarını karşılaştırıp, doğru sonuca ulaşıp ulaşmadığından emin olmak için kullanabilirsin.

http://www.lammertbies.nl/comm/info/crc-calculation.html

camby

#6
Alıntı yapılan: yufuk - 18 Haziran 2009, 21:58:07
NOT:


Aşağıdaki 2 adet "HEX" formatndaki rakamlar dizisi gerçek cihazlardan alınmış örnek mesajlardır. Eğer heaplama sonucunda CRC-16 değeri doğru çıkarsa başardın demektir.
"0B030006000224A0" -- " 0B0300060002" mesaj ,"24A0" CRC-16 değeri.
0B-03-00-06-00-02-24-A0  Cizgi arasındaki her hex değeri 1byte.


"0B0304000042C86105" -- "0B0304000042C8" mesaj, "6105" CRC-16 değeri
0B-03-04-00-00-42-C8-61-05  Cizgi arasındaki her hex değeri 1byte.

DİKKAT: Mesajı CRC-16 algoritmasına soktuğunuzda son 2 rakamı bunun dışında tutacaksınız. Çünkü son 2 rakam CRC-16 değeri. Sadece mesaj kısmını hesaplayıp bu son 2 rakamla karşılaştıracaksınız. Eğer aynı sonuca ulaşıyorsanız mesaj doğru demektir.


CRC'de" hello world" olarak kullandım bunu :) Teşekkürler

camby

#7
--