Forum: Mikrocontroller und Digitale Elektronik util/crc16.h, CRC-Berechnung unterschiedlich µC<->PC


von Martin K. (dschadu)


Lesenswert?

Hi,

ich nutze zur Absicherung der Kommunikation zwischen Master und Slave 
die _crc16_update() Funktion aus der AVR Libc: 
http://www.nongnu.org/avr-libc/user-manual/group__util__crc.html .
Aktuell bin ich noch dran, Slave und Master getrennt zu entwickeln. Ich 
kopiere mir also einfach den vom Master erzeugen String inklusive 
Checksumme (zum beispiel #DO,001,02,12345,01,01556,29610,63191\n), pack 
das ganze in ein Terminalprogramm und schick es dem Slave. Das 
funktioniert. Änder ich die Checksumme (der Letzte Abschnitt) oder die 
Daten, akzeptiert der Slave die Übertragung nicht. Das funktioniert 
also.
Der vollständigkeithalber, hier der Code vom Master:
1
void UART_SendDataToSlave(char* data)
2
{
3
  uint8_t len = strlen(data);
4
  uint16_t CRC = 0xffff;
5
  char buffer[7];
6
  
7
   for (uint8_t i = 1; i < len; i++)      //CRC berechnen
8
   {
9
     CRC = _crc16_update(CRC, data[i]);
10
  }
11
  
12
  strcat(data, ",");
13
  utoa(CRC, buffer, 10);        //CRC anhängen
14
  strcat(data, buffer);  //CRC anhängen
15
  strcat(data, "\n");    //Kommando-Ende
16
  
17
  strcpy(UART_LastTransmit, data);      //Letzte Übertragung sichern
18
  
19
  uart_puts(data);
20
  UARTWaitForACKSet;
21
}

Der Slave (UART_Data ist der komplette String, inkl \n)
1
//CRC Kontrolle:
2
        uint8_t CRClen = strlen(UART_Data);
3
        CRClen -= 6;
4
        char cCRC[6];
5
        strncpy(cCRC, UART_Data+CRClen, 5);
6
        
7
        uint16_t CRC = 0xffff;        
8
        for (uint8_t i = 1; i < CRClen-1; i++)      //CRC berechnen
9
        {
10
          CRC = _crc16_update(CRC, UART_Data[i]);
11
        }
12
        if (atol(cCRC) == CRC)
13
          ReturnVal = 1;      //CRC Ok
14
        else
15
          ReturnVal = 4;      //CRC Falsch


Da aber auch der PC mit dem Master kommunizieren soll und es mir vor 
allem das Debuging einfacher macht, wollte ich mir einen kleinen 
CRC-Calculator am PC bauen. Allerdings komm ich weder mit den Online-CRC 
Rechnern auf das Ergebnis wie der Controller, noch mit einem kleinen 
Visual C# Programm, das auf dem Code aus der <util/crc16.h> beruht.
Die CRC16 mit dem Programm von unten lautet: 20274.

PC-Programm:
1
static int crc16_update(int crc, int a)
2
        {
3
            int i;
4
5
            crc ^= a;
6
            for (i = 0; i < 8; ++i)
7
            {
8
                if ((crc & 1) == 1)
9
                    crc = (crc >> 1) ^ 0xA001;
10
                else
11
                    crc = (crc >> 1);
12
            }
13
14
            return crc;
15
        }
16
17
        private void b_calculate_Click(object sender, EventArgs e)
18
        {
19
            string CRCString = tb_string.ToString();
20
            int len = CRCString.Length;
21
            int CRC = 0xffff;
22
23
            for (int i = 1; i < len; i++)      //CRC berechnen
24
            {
25
                CRC = crc16_update(CRC, CRCString[i]);
26
            }
27
28
            tb_crc16.Text = CRC.ToString();
29
        }


Weiß jemand Rat? Was überseh ich? Oder ein Prinzipfehler?


Controller: AVR-Familie (Mega32L auf dem Testboard, später dann 328p-au)
Compiler: GCC 4.7.2 (AVR Studio 6.1)
PC: .NET4 (Visual Studio C# 2010 Express)

von Stefan F. (Gast)


Lesenswert?

Ersetze mal int durch einen Datentyp mit klar definierter Größe, z.B. 
int16_t.

von Martin K. (dschadu)


Lesenswert?

Hi, ich habe das Programm entsprechend geändert
1
static UInt16 crc16_update(UInt16 crc, char a)
2
        {
3
            UInt16 i;
4
5
            crc ^= a;
6
            for (i = 0; i < 8; ++i)
7
            {
8
                if ((crc & 1) == 1)
9
                    crc = (UInt16)((crc >> 1) ^ 0xA001);
10
                else
11
                    crc = (UInt16)(crc >> 1);
12
            }
13
14
            return crc;
15
        }
16
17
        private void b_calculate_Click(object sender, EventArgs e)
18
        {
19
            string CRCString = tb_string.ToString();
20
            int len = CRCString.Length;
21
            UInt16 CRC = 0xffff;
22
23
            for (int i = 1; i < len; i++)      //CRC berechnen
24
            {
25
                CRC = crc16_update(CRC, CRCString[i]);
26
            }
27
28
            tb_crc16.Text = CRC.ToString();
29
        }

Ergebnis bleibt 20274.

von Stefan E. (sternst)


Lesenswert?

In C würde der gezeigte Code auf jeden Fall in die Hose gehen. Ob auch 
in C# hängt von diversen Fragen ab, wie z.B.:
Wie groß ist char in C#?
Welche Signedness hat char in C#?
Hat C# die selben Promotion-Rules wie C?
Ich kenne die Antworten nicht, denn ich kann kein C#.
Ich vermute aber mal, dass es in C# wegen der gleichen (oder ähnlicher) 
Gründe in die Hose geht.

Mache a unsigned.

von Achim K. (aks)


Lesenswert?

Im MikroController ist
1
uint16_t
2
    crc16_update(uint16_t crc, uint8_t a)
3
    {
4
        int i;
5
6
        crc ^= a;
7
        for (i = 0; i < 8; ++i)
8
        {
9
            if (crc & 1)
10
                crc = (crc >> 1) ^ 0xA001;
11
            else
12
                crc = (crc >> 1);
13
        }
14
15
        return crc;
16
    }

"a" unsigned. Wenn es für die 16 Bit Operation "crc ^= a;" erweitert 
wird ("crc" hat ja 16bit), wird oben 0 ergänzt.

Bei Deiner
1
 
2
static UInt16 crc16_update(UInt16 crc, char a)
3
        {
4
            UInt16 i;
5
6
            crc ^= a;
7
            for (i = 0; i < 8; ++i)
8
            {
9
                if ((crc & 1) == 1)
10
                    crc = (UInt16)((crc >> 1) ^ 0xA001);
11
                else
12
                    crc = (UInt16)(crc >> 1);
13
            }
14
15
            return crc;
16
        }

ist nicht ganz klar, ob "a" signed oder unsigned ist und wie viele Bit 
es hat. Wenn man 8 Bit signed annimmt, dann würde die Erweiterung auf 16 
Bit nicht nur 0 sondern bei Werten mit gesetztem höchsten Bit 0xff oben 
ergänzen. Damit würde sich dann das abweichende Ergebnis erklären.

von Martin K. (dschadu)


Lesenswert?

Stefan Ernst schrieb:
> In C würde der gezeigte Code auf jeden Fall in die Hose gehen.
Warum?

> Wie groß ist char in C#?
16-Bit Unsigned

> Mache a unsigned.
Auch mit byte (8-Bit Unsigned) kommt das selbe raus.


Ich hab mir das wohl zu einfach vorgestellt mit Copy&Paste.
Was mich aber wundert, warum in den Online-CRC-Rechnern auch überall 
meist vollständig andere Ergebnisse bei raus kommen?
Zum beispiel:
http://www.zorc.breitbandkatze.de/crc.html

von Tom (Gast)


Lesenswert?

Martin K. schrieb:
> Ich hab mir das wohl zu einfach vorgestellt mit Copy&Paste.

Mit der Erkenntnis stehst du nicht alleine.

Cx sind Programmiersprachen, bei denen man genau wissen muss, was man 
tut, insbesondere wenn das ganze Konstrukt hinterher auch noch auf 
verschiedenen Systemen ohne Änderungen funktionieren soll.

von Stefan E. (sternst)


Lesenswert?

Martin K. schrieb:
> Stefan Ernst schrieb:
>> In C würde der gezeigte Code auf jeden Fall in die Hose gehen.
> Warum?

Das "auf jeden Fall" war etwas übertrieben. Wenn man dem Compiler sagt, 
dass char per Default unsigned zu sein hat, dann nicht (wäre dann aber 
immer noch sehr schlechter Stil). Was ansonsten passiert, hat Achim doch 
schon erklärt.

Martin K. schrieb:
>> Wie groß ist char in C#?
> 16-Bit Unsigned

Aha. Und welchen Datentyp hat string[x]? Und was steht in diesem char 
dann konkret drin? Sind das wirklich ASCII-Codes?

Hast du überhaupt mal ganz grundsätzlich überprüft, ob auch in beiden 
Fällen wirklich identische Inputdaten an die CRC-Routinen verfüttert 
werden? Und damit meine ich jetzt nicht, was du irgendwo als Text 
eingibst, sondern, was als binäre Daten in der CRC-Routine ankommt.

Martin K. schrieb:
> Was mich aber wundert, warum in den Online-CRC-Rechnern auch überall
> meist vollständig andere Ergebnisse bei raus kommen?

Was sollen wir dazu sagen? Hier wissen wir ja noch weniger darüber, was 
genau du da eingestellt/eingegeben hast.

von Martin K. (dschadu)


Lesenswert?

Stefan Ernst schrieb:
> Was sollen wir dazu sagen? Hier wissen wir ja noch weniger darüber, was
> genau du da eingestellt/eingegeben hast.

CRC-CCITT
CRC order 16
CRC polynom A001
Initial value ffff (direct)
Final XOR value 0

Result: 0x6133


Ich hab noch ein wenig mit Visual C# gespielt, komm aber auf kein 
Ergebnis. Hier und da gibt es Routinen im Netz für CRC16-CCITT, auch mit 
dem Polynom A001. Mit einer Variante war ich seltsamerweise immer exakt 
91 UNTER den vom µC berechneten werden. Sehr frustrierend grade. Vllt. 
seh ich auch den Wald vor lauter bäumen nicht mehr.

: Bearbeitet durch User
von Stefan E. (sternst)


Lesenswert?

Martin K. schrieb:
> Stefan Ernst schrieb:
>> Was sollen wir dazu sagen? Hier wissen wir ja noch weniger darüber, was
>> genau du da eingestellt/eingegeben hast.
>
> CRC-CCITT
> CRC order 16
> CRC polynom A001
> Initial value ffff (direct)
> Final XOR value 0
>
> Result: 0x6133

Und was hast du als Inputdaten eingegeben, und was als Ergebnis 
erwartet?

von Martin K. (dschadu)


Lesenswert?

Sorry, natürlich den String von oben:

#DO,001,02,12345,01,01556,29610

Erwartete Checksumme: 63191 (0xF6D7)

von Stefan E. (sternst)


Lesenswert?

Martin K. schrieb:
> Sorry, natürlich den String von oben:
>
> #DO,001,02,12345,01,01556,29610
>
> Erwartete Checksumme: 63191 (0xF6D7)

Warum das '#'? Deine eigenen Routinen ignorieren doch das erste Zeichen, 
der Online-Rechner aber natürlich nicht.

Der Online-Rechner scheint in die andere Richtung zu shiften. Wenn du 
das Polynom umdrehst (A001->8005) und die beiden Reverse-Häkchen setzt, 
kommt bei "DO,001,02,12345,01,01556,29610" genau das raus:
> Erwartete Checksumme: 63191 (0xF6D7)

von Martin K. (dschadu)


Lesenswert?

Stefan Ernst schrieb:
> Warum das '#'? Deine eigenen Routinen ignorieren doch das erste Zeichen,
> der Online-Rechner aber natürlich nicht.

Oh verdammt, das ist mir gar nicht aufgefallen... ich stell mich ins Eck 
und schäm mich! ;)

Vielen Dank für deine Hilfe. Denke damit komm ich erst mal weiter :)

von Andreas (Gast)


Lesenswert?

Hallo,

Ich hatte ähnliche Probleme mit der (bzw. einer) CCITT-CRC und wollte 
übereinstimmende Ergebnisse zwischen srec_cat, der avr-libc und der 
Breitbandkatze erhalten.

Hab es erst kürzlich hinbekommen und die screenshots (breitbandkatze), 
den Example-Code und das script-file f. srec_cat in ein File 
zusammengepackt (am Ende des Threads). Wie erwähnt: ist halt eine CCITT 
und nicht CRC16.

Vielleicht hilft es mal jemandem weiter:
TitelBeitrag "CRC generierung mittels srec_cat"

Gruß,
Andreas

von Harper B. (harper)


Lesenswert?

Achim K. schrieb:
> Bei Deiner
>
1
> static UInt16 crc16_update(UInt16 crc, char a)
2
>         {
3
> ...
4
>         }
5
>
>
> ist nicht ganz klar, ob "a" signed oder unsigned ist und wie viele Bit
> es hat. Wenn man 8 Bit signed annimmt, dann würde die Erweiterung auf 16

Wenn man ein Byte benötigt, würde ich den Datentype "byte" empfehlen. 
Der ist 8 bit unsigned.

von Martin K. (dschadu)


Lesenswert?

Ich hab mich noch mal dran gesetzt, aber kam nicht weiter. Mit diversen 
Routinen aus dem Netz hab ich es versucht, aber ich kam nicht auf mein 
Ergebnis.
Nach mehreren Stunden ohne weiter zu kommen hab ich das jetzt erst mal 
aufgegeben und bin grad an einer anderen Stelle vom Projekt.
Vielleicht hab ich beim nächsten Versuch ja eine Eingebung :)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.