Forum: Mikrocontroller und Digitale Elektronik UART mit LIN-Protokoll


von Amir B. (sticky)


Angehängte Dateien:

Lesenswert?

Hallo alle.

Ich möchte eine Lin-Kommunikation mit der UART des ATMega168
realisieren. habe dazu einen ATA6662 Transceiver angeschlossen.
(um den Master muss ich mich nicht kümmern)
Ich erhalte einen Master-Header, auf die ich an meinen Slave reagieren 
möchte.

Der Master ist ein BabyLIN modul von der Firma Lipowsky.
Am oziloskop sehe ich, das der Head gesendet wird. Jedoch antwortet mein 
Slave nicht.

Folgendes zum Programmablauf:
-Ich sammel am ADC Werte bis eine Nachricht anliegt.
-Dann springt das Programm in eine ISR, wo der Slave auf die Anfrage des 
Masters antworten soll.
so weit so gut.

-Ich prufe, ob ein Frame Error vor liegt (entpricht einem Sync-Break)
funzt auch noch.
-Ich lese das nächte Byte, welches eine Hex55 (dez85) also das Sync-Feld 
ist, ein. Das Funz leider nicht richtig!!!
-Nun überspringe ich das problem in dem ich fest sage es liegt eine 
hex55 vor und lese das nächte Byte ein, welches die ID ist. Aber wieder 
komm ich nicht in die Funktion.

Was mach ich falsch?

Wer kann Helfen?

Gruß Sticky
1
ISR (USART_RX_vect) {    //Die Interupt Service Routine wird ausgeführt wenn ein zeichen empfangen wird
2
  
3
4
              
5
  uint16_t TMPchecksum = 0;
6
  uint8_t checksum;      //Hier steht die Enhanced Checksumme
7
  uint8_t ID;          //Hier steht die vom Master versandte ID (ohne Paritätsbits)
8
  uint8_t PID;        //Hier steht die vom Master versandte ID (mit Paritätsbits)
9
  uint8_t ID_adr;        //Hier stehen die oberen drei Bits der ID mit der die Baugruppe angesprochen wird
10
  uint8_t ID_func;      //Hier stehen die unteren drei Bits der ID mit der die Funktionen ausgeführt werden
11
  uint8_t SyncFeld;
12
  uint8_t Sync_Break;
13
14
  //LIN KOMMUNIKATION STARTET HIER
15
  Sync_Break=Receive_Byte();
16
17
  if (Sync_Break){        //Ein Frame Error wird durch ein Sync-Break hervorgerufen
18
19
20
    if (SyncFeld==0x55){        
21
22
23
      PID    = Receive_Byte();
24
      ID    = (PID & 00111111);            //ID (ohne Paritätsbits)
25
      ID_adr  = (ID >> 3);
26
      ID_func = (PID & 00000111); 
27
28
      if(Paritaet_Berechnen(PID)) {
29
30
        if ((ID_func==0) && (Slave_Address==ID_adr)){        
31
32
          for (uint8_t i=0; i<=6; i++){
33
            Send_Byte(Daten[i]);          //gesammelte Daten senden
34
            TMPchecksum= TMPchecksum + Daten[i];  //Checksumme berechnen (CARRY-BIT fehlt noch)!!!!!!!
35
          }
36
        
37
          TMPchecksum = TMPchecksum+PID;        //Enhanced Checksumme!  
38
          //Low-Byte der checksumme + High-Byte der checksumme
39
          checksum  = (uint8_t) ((TMPchecksum & 0x00FF) + (TMPchecksum >> 8));        
40
          checksum= ~checksum;        //Checksumme invertieren
41
          Send_Byte(checksum);        //anschließend die Checksumme senden
42
43
        }
44
45
        if ((ID_func == 1) && (Slave_Address==ID_adr)){    
46
          Set_RelaisPB1(1);          //Relais1 einschalten
47
        }
48
49
        if ((ID_func == 2) && (Slave_Address==ID_adr)){    
50
          Set_RelaisPB1(0);          //Relais1 ausschalten
51
        }        
52
53
        if ((ID_func == 3) && (Slave_Address==ID_adr)){    
54
          Set_RelaisPB2(1);          //Relais2 einschalten
55
        }
56
57
        if ((ID_func == 4) && (Slave_Address==ID_adr)){    
58
          Set_RelaisPB2(0);          //Relais2 ausschlaten
59
        }
60
61
        if ((ID_func == 5) && (Slave_Address==ID_adr)){    
62
          Set_RelaisPB1(1);
63
          Set_RelaisPB2(1);          //Relais1+2 einschlaten
64
        }
65
66
        if ((ID_func == 6) && (Slave_Address==ID_adr)){    
67
          Set_RelaisPB1(0);
68
          Set_RelaisPB2(0);          //Relais1+2 ausschlaten
69
        }
70
71
        if ((ID_func == 7) && (Slave_Address==ID_adr)){    
72
          SW_Reset();              //Reset auslösen
73
        }
74
      }//parität
75
      
76
        else{ 
77
        DDRC = (1 << DDC4);
78
        PORTC |= (1<<PC4);}             //gelbe LED eischalten, somit liegt ein Paritätsfehler vor
79
    }//syncfeld
80
  }//FE
81
}//ISR

von Johnny (Gast)


Lesenswert?

Wenn das "if (Sync_Break)" überwunden ist, fehlt wahrscheinlich folgende 
anweisung, sofern ich Deine Implementation richtig verstehe:

SyncFeld = Receive_Byte();

von Amir B. (sticky)


Lesenswert?

Ja hast mich schon richtig verstanden.
Aber das ist es nicht. Ist wohl beim kopieren "verloren" gegangen.

von Dennis (Gast)


Lesenswert?

Naja, das SYNC Field sollte ja schon erkannt werden, also liegt der 
Fehler vielleicht in der Funktion Receive_Byte(). Ich weiß es nicht, 
aber kann evtl. beim UART die Bitorder ändern? Sprich bei LIN kommt ja 
nach dem Startbit das LSB auf den Bus, liest deine UART-Schnittstelle 
das verkehrt herum ein?

Ansonsten musst du bei den beiden Bitfolgen wohl noch ein "0b" davor 
setzen:
1
ID    = (PID & 0b00111111);            //ID (ohne Paritätsbits)
2
ID_adr  = (ID >> 3);
3
ID_func = (PID & 0b00000111);

von Amir B. (sticky)


Lesenswert?

Danke für die Tips.
Ich habe sowohl versucht mit 55 hex als auch AA hex zu vergleichen. hat 
beides nicht geholfen. Glaube aber das du im Grunde Recht hattest. Das 
MSB wird als erstes gesendet und somit steht es im Empfangsbuffer als 
LSB.
Wie gesagt es muss noch ein Fehler irgendwo sein.

Kann es sein, dass ich den Frame Error Flag zurück setzen muss? Denn ich 
komme nach wie vor nicht in die Schleife mit dem Sync Feld.

1
ISR (USART_RX_vect) {    //Die Interupt Service Routine wird ausgeführt wenn ein zeichen empfangen wird
2
  
3
4
              
5
  uint16_t TMPchecksum = 0;
6
  uint8_t checksum;      //Hier steht die Enhanced Checksumme
7
  uint8_t ID;          //Hier steht die vom Master versandte ID (ohne Paritätsbits)
8
  uint8_t PID;        //Hier steht die vom Master versandte ID (mit Paritätsbits)
9
  uint8_t ID_adr;        //Hier stehen die oberen drei Bits der ID mit der die Baugruppe angesprochen wird
10
  uint8_t ID_func;      //Hier stehen die unteren drei Bits der ID mit der die Funktionen ausgeführt werden
11
  uint8_t SyncFeld;
12
  uint8_t Sync_Break;
13
  
14
  
15
  //LIN KOMMUNIKATION STARTET HIER
16
  Sync_Break=Receive_Byte();
17
18
  if (Sync_Break){        //Ein Frame Error wird durch ein Sync-Break hervorgerufen
19
    
20
    UCSR0A &= 0b11100011;      //Frame Error Flag löschen;
21
22
    SyncFeld=Receive_Byte();
23
    SyncFeld= ~SyncFeld;    //0xAA wird empfangen. Durch Invertieren erhält man 0x55
24
                  //Grund: LIN sendet MSB zuerst und steht im Empfangsregister der UART im LSB
25
    if (SyncFeld==0x55){      
26
27
        DDRC = (1 << DDC4);    //LED ein!
28
        PORTC |= (1<<PC4);
29
30
      PID    = Receive_Byte();
31
      ID    = (PID & 0b00111111);            //ID (ohne Paritätsbits)
32
      ID_adr  = (ID >> 3);
33
      ID_func = (PID & 0b00000111); 
34
35
      if(Paritaet_Berechnen(PID)) {
36
  
37
38
39
        if ((ID_func==0) && (Slave_Address==ID_adr)){        
40
41
          for (uint8_t i=0; i<=6; i++){
42
            Send_Byte(Daten[i]);          //gesammelte Daten senden
43
            TMPchecksum= TMPchecksum + Daten[i];  //Checksumme berechnen (CARRY-BIT fehlt noch)!!!!!!!
44
          }
45
        
46
          TMPchecksum = TMPchecksum+PID;        //Enhanced Checksumme!  
47
          //Low-Byte der checksumme + High-Byte der checksumme
48
          checksum  = (uint8_t) ((TMPchecksum & 0x00FF) + (TMPchecksum >> 8));        
49
          checksum= ~checksum;        //Checksumme invertieren
50
          Send_Byte(checksum);        //anschließend die Checksumme senden
51
52
        }
53
54
        if ((ID_func == 1) && (Slave_Address==ID_adr)){    
55
          Set_RelaisPB1(1);          //Relais1 einschalten
56
        }
57
58
        if ((ID_func == 2) && (Slave_Address==ID_adr)){    
59
          Set_RelaisPB1(0);          //Relais1 ausschalten
60
        }        
61
62
        if ((ID_func == 3) && (Slave_Address==ID_adr)){    
63
          Set_RelaisPB2(1);          //Relais2 einschalten
64
        }
65
66
        if ((ID_func == 4) && (Slave_Address==ID_adr)){    
67
          Set_RelaisPB2(0);          //Relais2 ausschlaten
68
        }
69
70
        if ((ID_func == 5) && (Slave_Address==ID_adr)){    
71
          Set_RelaisPB1(1);
72
          Set_RelaisPB2(1);          //Relais1+2 einschlaten
73
        }
74
75
        if ((ID_func == 6) && (Slave_Address==ID_adr)){    
76
          Set_RelaisPB1(0);
77
          Set_RelaisPB2(0);          //Relais1+2 ausschlaten
78
        }
79
80
        if ((ID_func == 7) && (Slave_Address==ID_adr)){    
81
          SW_Reset();              //Reset auslösen
82
        }
83
      }//parität
84
      
85
        else{ 
86
        DDRC = (1 << DDC4);
87
        PORTC |= (1<<PC4);}             //gelbe LED eischalten, somit liegt ein Paritätsfehler vor
88
    }//syncfeld
89
  }//FE
90
}//ISR

von Amir B. (sticky)


Lesenswert?

Wie wird das Frame Error Flag zurück gesett? Da es ist Read-Only ist 
muss es doch irgendwie indirekt gehen oder?

von rene (Gast)


Lesenswert?

Aaahhhhh,
was soll das ? In einer ISR wartet man nie. Nie! Eine ISR wir aufgrund 
eines Hardware Ereignisses angesprungen. Das macht der Controller von 
selbst. Dort laesst man allenfalls eine statusmaschine laufen, aber 
wartet nie. Eine ISR mus so schnell wie nur moeglich sein, und nachher 
wird sie verlassen. Berechnungen macht man uebrigens auch nicht. Kein 
CRC ode dergleichen. Das kann man ausseerhalb rechnen. Fuer eine 
Beispiel :
http://www.ibrtses.com/embedded/avruart.html
Sollte trotzdem lesbat sein.

von Johnny (Gast)


Lesenswert?

Für weitere Abklärungen würde uns der Source von der Funktion 
Receive_Byte() weiterhelfen und eine Angabe, was jeweils von 
Receive_Byte() zurückgegeben wird.

von Amir B. (sticky)


Lesenswert?

Ganz oben ist der Sourcecode angehangen!

Danke fürs anschauen

von Amir B. (sticky)


Angehängte Dateien:

Lesenswert?

Eine neuere Version!
1
uint8_t Receive_Byte(){
2
3
uint8_t status;
4
5
DDRC = (1 << DDC3);       
6
PORTC |= (1<<PC3);         //rote LED einschalten (receive)
7
8
while (!(UCSR0A & (1<<RXC0)))  // warten bis Zeichen verfuegbar
9
        ;
10
status = UCSR0A;
11
if (status & (1<<FE0)){      //wurde ein Break empfangen?
12
return 1;
13
}
14
15
else{
16
return UDR0;          // Zeichen aus UDR an Aufrufer zurueckgeben
17
}
18
19
DDRC = (0 << DDC3);
20
PORTC &= (0<<PC3);         //rote LED ausschalten (receive)
21
}

von Amir B. (sticky)


Lesenswert?

Hilfe?

von Frederik K. (n0ll4k)


Lesenswert?

Hi mich würd mal interessieren ob es bei dir läuft, ich arbeite grad im 
Rahmen meiner Diplomarbeit auch an einer LIN Kommunikation mit dem 
ATmega168 und dem ATA6624.

Wenn deine Software immer noch den letzten Stand hat geht es ja auch 
nicht, da das SyncFeld immer gleich 0 ist da du ja kein weiteres Byte 
nach dem Framing Error empfangen hast. Wird dieser überhaupt bei dir 
erkannt und dann der Rest der ISR abgearbeitet oder wird nach dem 
erkennen des Errors nicht weitergearbeitet?

von Amir B. (sticky)


Angehängte Dateien:

Lesenswert?

Ich versteh deine Frag nicht!

Aber meine Kommunikation funzt jetzt. Siehe Code im Anhang.

Das einzige Problem ist, dass ich kein Sync-Break erkenne. Es gab 
Komplikationen diesbezüglisch. Und zwar war es mir nicht möglisch das 
Frame Error Flag zurückzusetzen. Dadurch kam ich nie in den Genuss das 
Sync-Feld zu empfangen. Deshalb ignoriere ich das sync-Break an meinen 
Slaves. Ich nutze eh einen externen Taktgeber, so das es nicht nötig ist 
sich mittels des Sync-Felds zu synchronisieren.
Die Kommunikation mit einem LIN-Master, der streng nach spezifikation 
2.0 ausgelegt ist, funktionierte tadellos. Sprich der Master hat nicht 
gemerckert.

Ich hoffe der Code kann dir Helfen.
Gruß Sticky

von Frederik K. (n0ll4k)


Lesenswert?

Ich habe den Sync Break nun am laufen. Habe es über eine Anpassung der 
Baudrate, wie in der Atmel AppNote480 gezeigt wird hinbekommen. Ob ich 
die Synchronisation einabauen werde ist noch nicht klar. Zur Zeit nutze 
ich zwar nur den internen RC Oszi aber ob das final so bleibt ist noch 
fraglich. Als Master habe ich auch einen BabyLIN hier, der momentan auch 
nicht meckert.

Funktioniert bei dir denn alles ohne State Machine? Das erkennen geht 
bei mir auch nur dank einer StateMachine, sonst hat mein Master immer 
rumgemeckert.

Die Frage bezog sich auf das zurücksetzen des Framing Errors. Ich habe 
auch erst versucht die Erkennung über einen Framing Error zu erkennen 
aber das hat nicht funktioniert.

von Amir B. (sticky)


Lesenswert?

Wenn es bei dir mit einer State-Machine funzt solltest du die dann auch 
nutzen. Ansonten hast du ja selber gesehen das es geht. Leider ohne 
Sync-Break. Wie gesagt es wird am slave einfach ignoriert, was 
unkritisch ist solange du NICHT das intere RC-Glied als taktgeber 
verwendest. (ich nutze einen 8MHz Quarz)

Gruß Sticky

von Frederik K. (n0ll4k)


Lesenswert?

Da meins für den Einsatz im Automobiel entwickelt wird und hier ja auch 
immer Kosten eine Rolle spielen würde ich den Quarz und die 
Kondensatoren gerne weglassen. Und mit der State Machine funktioniert es 
recht gut.

von Bogumil (Gast)


Lesenswert?

>Da meins für den Einsatz im Automobiel entwickelt wird und hier ja auch
>immer Kosten eine Rolle spielen würde ich den Quarz und die
>Kondensatoren gerne weglassen. Und mit der State Machine funktioniert es
>recht gut.

Wie korrigierst du dann die unvermeidliche Drift in der Taktfrequenz 
über Temperatur und Alterung?

von Frederik K. (n0ll4k)


Lesenswert?

Dafür wollte ich die Takterkennung des LIN Buses anhand des 
Synchonisationsfelds einbauen.

von Bogumil (Gast)


Lesenswert?

Kannst du denn den Uart des Prozessors so fein "granulieren"?

von Amir B. (sticky)


Lesenswert?

Frederik Krämer schrieb:
> Da meins für den Einsatz im Automobiel entwickelt wird und hier ja auch
> immer Kosten eine Rolle spielen würde ich den Quarz und die
> Kondensatoren gerne weglassen. Und mit der State Machine funktioniert es
> recht gut.

Das kannst du machen. Dafür ist LIN ja auch eigentlisch ausgelegt. du 
kannst denn quarz und die beiden Kondensatoren weglassen und mit den 
internen RC-Gliedarbeiten. musst aber jeden Slave syncronisieren.

von Frederik K. (n0ll4k)


Lesenswert?

Ich entwickle einen Slave neu, der Rest des LIN Clusters ist vorgegeben 
aber anhand der LDF kann man das ja alles sehr bequem implementieren.

Die LIN 2.0 bzw 2.1 Specification gibt die Synchronisation ja eh als 
Standard vor, ich bin mir nur nicht sicher ob der ursprüngliche Slave 
die Sychronisation unterstützt hat oder nicht, da dieser einen Quarz 
hat.

Falls ich zu einem späteren Zeitpunkt größere Probleme bekomme werde ich 
noch einen Quarz einbauen aber zur Zeit komme ich gut ohne den Quarz 
aus. Die Frage ist wie es später aussieht wenn sämtliche Funktionen 
implementiert sind.

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.