RS232 veya RS485 bağlantısı üzerinden basit ve güvenilir bir iletişim sağlamak için bir kütüphane üzerinde çalışıyorum. Bu kodun bir kısmı, hat gürültüsünden kaynaklanan bozulmayı tespit etmek için veriler üzerinde bir CRC16 sağlama toplamı kullanmayı içeriyor. CRC16 sağlama toplamını hesaplamak için bir işlev oluşturdum, ancak doğru değerleri çıktılamıyor gibi görünüyor.
Yazdığım ilgili kod aşağıdadır (ayrıca burada da bulunabilir).
#include <stdint.h>
#define CRC16 0x8005
uint16_t gen_crc16(const uint8_t *data, uint16_t size)
{
uint16_t out = 0;
int bits_read = 0, bit_flag;
/* Sanity check: */
if(data == NULL)
return 0;
while(size > 0)
{
bit_flag = out >> 15;
/* Get next bit: */
out <<= 1;
out |= (*data >> (7 - bits_read)) & 1;
/* Increment bit counter: */
bits_read++;
if(bits_read > 7)
{
bits_read = 0;
data++;
size--;
}
/* Cycle check: */
if(bit_flag)
out ^= CRC16;
}
return out;
}
Çıktımı bu çevrimiçi CRC hesaplayıcısı ile kontrol ediyorum.
Ya CRC16'nın nasıl hesaplanacağına dair anlayışımın yanlış olduğu ya da çevrimiçi hesap makinesinin yanlış olduğu sonucuna vardım (ilki daha olası görünüyor). Birisi bana nerede yanlış yapıyor olabileceğimi söyleyebilir mi?
Belirli bir CRC uygulaması için 'eşleştirmeniz' gereken birkaç ayrıntı vardır - aynı polinomu kullansanız bile, veri bitlerinin nasıl işlendiğindeki küçük farklılıklar, CRC için belirli bir başlangıç değeri kullanılması (bazen sıfır, bazen 0xffff) ve / veya CRC bitlerinin ters çevrilmesi nedeniyle farklı sonuçlar olabilir. Örneğin, bazen bir uygulama veri baytlarının düşük sıralı bitlerinden yukarı doğru çalışırken, bazen de yüksek sıralı bitlerden aşağı doğru çalışır (sizinkinin şu anda yaptığı gibi).
Ayrıca, tüm veri bitlerini çalıştırdıktan sonra CRC'nin son bitlerini 'dışarı itmeniz' gerekir.
CRC algoritmalarının donanımda uygulanmak üzere tasarlandığını unutmayın, bu nedenle bit sıralamasının nasıl ele alındığının bir kısmı yazılım açısından çok mantıklı olmayabilir.
CRC16'yı lammertbies.nl CRC hesaplayıcı sayfasında gösterildiği gibi 0x8005 polinomu ile eşleştirmek istiyorsanız, CRC işlevinizde aşağıdaki değişiklikleri yapmanız gerekir:
Yani, fonksiyonunuz şöyle görünebilir:
#define CRC16 0x8005
uint16_t gen_crc16(const uint8_t *data, uint16_t size)
{
uint16_t out = 0;
int bits_read = 0, bit_flag;
/* Sanity check: */
if(data == NULL)
return 0;
while(size > 0)
{
bit_flag = out >> 15;
/* Get next bit: */
out <<= 1;
out |= (*data >> bits_read) & 1; // item a) work from the least significant bits
/* Increment bit counter: */
bits_read++;
if(bits_read > 7)
{
bits_read = 0;
data++;
size--;
}
/* Cycle check: */
if(bit_flag)
out ^= CRC16;
}
// item b) "push out" the last 16 bits
int i;
for (i = 0; i < 16; ++i) {
bit_flag = out >> 15;
out <<= 1;
if(bit_flag)
out ^= CRC16;
}
// item c) reverse the bits
uint16_t crc = 0;
i = 0x8000;
int j = 0x0001;
for (; i != 0; i >>=1, j <<= 1) {
if (i & out) crc |= j;
}
return crc;
}
Bu fonksiyon "123456789"
girdiğimde benim için 0xbb3d
döndürüyor.
Burada crc16 CCITT'yi hesaplamak için çalışan bir kod bulunmaktadır. Test ettim ve sonuçlar http://www.lammertbies.nl/comm/info/crc-calculation.html tarafından sağlananlarla eşleşti.
unsigned short crc16(const unsigned char* data_p, unsigned char length){
unsigned char x;
unsigned short crc = 0xFFFF;
while (length--){
x = crc >> 8 ^ *data_p++;
x ^= x>>4;
crc = (crc << 8) ^ ((unsigned short)(x << 12)) ^ ((unsigned short)(x <<5)) ^ ((unsigned short)x);
}
return crc;
}