mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik UART mit LIN-Protokoll


Autor: Amir B-a (sticky)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

ISR (USART_RX_vect) {    //Die Interupt Service Routine wird ausgeführt wenn ein zeichen empfangen wird
  

              
  uint16_t TMPchecksum = 0;
  uint8_t checksum;      //Hier steht die Enhanced Checksumme
  uint8_t ID;          //Hier steht die vom Master versandte ID (ohne Paritätsbits)
  uint8_t PID;        //Hier steht die vom Master versandte ID (mit Paritätsbits)
  uint8_t ID_adr;        //Hier stehen die oberen drei Bits der ID mit der die Baugruppe angesprochen wird
  uint8_t ID_func;      //Hier stehen die unteren drei Bits der ID mit der die Funktionen ausgeführt werden
  uint8_t SyncFeld;
  uint8_t Sync_Break;

  //LIN KOMMUNIKATION STARTET HIER
  Sync_Break=Receive_Byte();

  if (Sync_Break){        //Ein Frame Error wird durch ein Sync-Break hervorgerufen


    if (SyncFeld==0x55){        


      PID    = Receive_Byte();
      ID    = (PID & 00111111);            //ID (ohne Paritätsbits)
      ID_adr  = (ID >> 3);
      ID_func = (PID & 00000111); 

      if(Paritaet_Berechnen(PID)) {

        if ((ID_func==0) && (Slave_Address==ID_adr)){        

          for (uint8_t i=0; i<=6; i++){
            Send_Byte(Daten[i]);          //gesammelte Daten senden
            TMPchecksum= TMPchecksum + Daten[i];  //Checksumme berechnen (CARRY-BIT fehlt noch)!!!!!!!
          }
        
          TMPchecksum = TMPchecksum+PID;        //Enhanced Checksumme!  
          //Low-Byte der checksumme + High-Byte der checksumme
          checksum  = (uint8_t) ((TMPchecksum & 0x00FF) + (TMPchecksum >> 8));        
          checksum= ~checksum;        //Checksumme invertieren
          Send_Byte(checksum);        //anschließend die Checksumme senden

        }

        if ((ID_func == 1) && (Slave_Address==ID_adr)){    
          Set_RelaisPB1(1);          //Relais1 einschalten
        }

        if ((ID_func == 2) && (Slave_Address==ID_adr)){    
          Set_RelaisPB1(0);          //Relais1 ausschalten
        }        

        if ((ID_func == 3) && (Slave_Address==ID_adr)){    
          Set_RelaisPB2(1);          //Relais2 einschalten
        }

        if ((ID_func == 4) && (Slave_Address==ID_adr)){    
          Set_RelaisPB2(0);          //Relais2 ausschlaten
        }

        if ((ID_func == 5) && (Slave_Address==ID_adr)){    
          Set_RelaisPB1(1);
          Set_RelaisPB2(1);          //Relais1+2 einschlaten
        }

        if ((ID_func == 6) && (Slave_Address==ID_adr)){    
          Set_RelaisPB1(0);
          Set_RelaisPB2(0);          //Relais1+2 ausschlaten
        }

        if ((ID_func == 7) && (Slave_Address==ID_adr)){    
          SW_Reset();              //Reset auslösen
        }
      }//parität
      
        else{ 
        DDRC = (1 << DDC4);
        PORTC |= (1<<PC4);}             //gelbe LED eischalten, somit liegt ein Paritätsfehler vor
    }//syncfeld
  }//FE
}//ISR

Autor: Johnny (Gast)
Datum:

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

SyncFeld = Receive_Byte();

Autor: Amir B-a (sticky)
Datum:

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

Autor: Dennis (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
ID    = (PID & 0b00111111);            //ID (ohne Paritätsbits)
ID_adr  = (ID >> 3);
ID_func = (PID & 0b00000111);

Autor: Amir B-a (sticky)
Datum:

Bewertung
0 lesenswert
nicht 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.

ISR (USART_RX_vect) {    //Die Interupt Service Routine wird ausgeführt wenn ein zeichen empfangen wird
  

              
  uint16_t TMPchecksum = 0;
  uint8_t checksum;      //Hier steht die Enhanced Checksumme
  uint8_t ID;          //Hier steht die vom Master versandte ID (ohne Paritätsbits)
  uint8_t PID;        //Hier steht die vom Master versandte ID (mit Paritätsbits)
  uint8_t ID_adr;        //Hier stehen die oberen drei Bits der ID mit der die Baugruppe angesprochen wird
  uint8_t ID_func;      //Hier stehen die unteren drei Bits der ID mit der die Funktionen ausgeführt werden
  uint8_t SyncFeld;
  uint8_t Sync_Break;
  
  
  //LIN KOMMUNIKATION STARTET HIER
  Sync_Break=Receive_Byte();

  if (Sync_Break){        //Ein Frame Error wird durch ein Sync-Break hervorgerufen
    
    UCSR0A &= 0b11100011;      //Frame Error Flag löschen;

    SyncFeld=Receive_Byte();
    SyncFeld= ~SyncFeld;    //0xAA wird empfangen. Durch Invertieren erhält man 0x55
                  //Grund: LIN sendet MSB zuerst und steht im Empfangsregister der UART im LSB
    if (SyncFeld==0x55){      

        DDRC = (1 << DDC4);    //LED ein!
        PORTC |= (1<<PC4);

      PID    = Receive_Byte();
      ID    = (PID & 0b00111111);            //ID (ohne Paritätsbits)
      ID_adr  = (ID >> 3);
      ID_func = (PID & 0b00000111); 

      if(Paritaet_Berechnen(PID)) {
  


        if ((ID_func==0) && (Slave_Address==ID_adr)){        

          for (uint8_t i=0; i<=6; i++){
            Send_Byte(Daten[i]);          //gesammelte Daten senden
            TMPchecksum= TMPchecksum + Daten[i];  //Checksumme berechnen (CARRY-BIT fehlt noch)!!!!!!!
          }
        
          TMPchecksum = TMPchecksum+PID;        //Enhanced Checksumme!  
          //Low-Byte der checksumme + High-Byte der checksumme
          checksum  = (uint8_t) ((TMPchecksum & 0x00FF) + (TMPchecksum >> 8));        
          checksum= ~checksum;        //Checksumme invertieren
          Send_Byte(checksum);        //anschließend die Checksumme senden

        }

        if ((ID_func == 1) && (Slave_Address==ID_adr)){    
          Set_RelaisPB1(1);          //Relais1 einschalten
        }

        if ((ID_func == 2) && (Slave_Address==ID_adr)){    
          Set_RelaisPB1(0);          //Relais1 ausschalten
        }        

        if ((ID_func == 3) && (Slave_Address==ID_adr)){    
          Set_RelaisPB2(1);          //Relais2 einschalten
        }

        if ((ID_func == 4) && (Slave_Address==ID_adr)){    
          Set_RelaisPB2(0);          //Relais2 ausschlaten
        }

        if ((ID_func == 5) && (Slave_Address==ID_adr)){    
          Set_RelaisPB1(1);
          Set_RelaisPB2(1);          //Relais1+2 einschlaten
        }

        if ((ID_func == 6) && (Slave_Address==ID_adr)){    
          Set_RelaisPB1(0);
          Set_RelaisPB2(0);          //Relais1+2 ausschlaten
        }

        if ((ID_func == 7) && (Slave_Address==ID_adr)){    
          SW_Reset();              //Reset auslösen
        }
      }//parität
      
        else{ 
        DDRC = (1 << DDC4);
        PORTC |= (1<<PC4);}             //gelbe LED eischalten, somit liegt ein Paritätsfehler vor
    }//syncfeld
  }//FE
}//ISR

Autor: Amir B-a (sticky)
Datum:

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

Autor: rene (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Johnny (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Amir B-a (sticky)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ganz oben ist der Sourcecode angehangen!

Danke fürs anschauen

Autor: Amir B-a (sticky)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Eine neuere Version!
uint8_t Receive_Byte(){

uint8_t status;

DDRC = (1 << DDC3);       
PORTC |= (1<<PC3);         //rote LED einschalten (receive)

while (!(UCSR0A & (1<<RXC0)))  // warten bis Zeichen verfuegbar
        ;
status = UCSR0A;
if (status & (1<<FE0)){      //wurde ein Break empfangen?
return 1;
}

else{
return UDR0;          // Zeichen aus UDR an Aufrufer zurueckgeben
}

DDRC = (0 << DDC3);
PORTC &= (0<<PC3);         //rote LED ausschalten (receive)
}

Autor: Amir B-a (sticky)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hilfe?

Autor: Frederik Krämer (n0ll4k)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Amir B-a (sticky)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Autor: Frederik Krämer (n0ll4k)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Amir B-a (sticky)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Frederik Krämer (n0ll4k)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Bogumil (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Frederik Krämer (n0ll4k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dafür wollte ich die Takterkennung des LIN Buses anhand des 
Synchonisationsfelds einbauen.

Autor: Bogumil (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kannst du denn den Uart des Prozessors so fein "granulieren"?

Autor: Amir B-a (sticky)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Frederik Krämer (n0ll4k)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.