Hallo an alle. Ich habe ein LIN-Slavemodul gebaut auf dem ein ATMega168 und ein ATA6662 (LIN-Transceiver) sitzt. Nutze auserdem AVRStudio mit gcc Ich will mein LIN-Slavemodul auf eine LIN-Header-Nachricht des Masters synchronisieren. Dazu habe ich folgendes Datenblatt gefunden. http://www.atmel.com/dyn/resources/prod_documents/doc7653.pdf ich versteh nur leider nicht wie ich es anwenden muss. Kann mir jemand bei der Synchronisation meines Slaves helfen? Ich danke im voraus.
>Ich will mein LIN-Slavemodul auf eine LIN-Header-Nachricht des Masters >synchronisieren. Die Application Note bezieht sich - nach kurzer Sichtung - mehr auf das Autobauding, d.h. Abgleich der Slave-Baudrate auf den Master. Insbesondere erforderlich bei Eisatz von RC-Oszillatoren. Du meinst wahrscheinlich mehr die grundsätzliche Synchronisation auf die LIN-Nachricht, oder?
Falls Du die grundsätzliche Synchronisation meinst, geht das wie folgt: - Im Receive-Interrupt als erstes prüfen, ob ein Framing-Error vorliegt (Verdacht auf Break) - Prüfen, ob das empfangene Byte = 0 ist. - Ich prüfe zusätzlich zu diesem Zeitpunkt noch, ob der Portpin RXD an dieser Stelle ebenfalls noch '0' ist. Nur empfehlenswert bei Interrupt-basierender Verarbeitung. - Empfangs-State-Machine an dieser Stelle auf jeden Fall zurücksetzen, ein Break hat immer Vorrang. - Nächstes Byte sollte ein 0x55 sein (An dieser Stelle kann man einige Prozessoren auf das Autobauding mittels 0x55 Character vorbereiten, z.B. PIC) - Als nächstes Byte die ID empfangen. Parität prüfen und feststellen, ob ID für einen selbst bestimmt ist. - Je nach geplanter Frame Datenrichtung nun 1..8 Datenbytes senden oder empfangen. - Checksumme empfangen und prüfen oder Checksumme berechnen und senden. - Fertig. Die Prüfung mittels Framing Error mag einigen Usern nicht genügen, sie hätte theoretisch noch einige Unzulänglichkeiten. In der Praxis machen das aber viele Implementationen so. Ich auch.
Harald wrote: > Du meinst wahrscheinlich mehr die grundsätzliche Synchronisation auf die > LIN-Nachricht, oder? ja! mein code
1 | ISR (USART_RX_vect) { //Die Interupt Service Routine wird ausgeführt wenn ein zeichen empfangen wird |
2 | //(LIN KOMMUNIKATION STARTET HIER)
|
3 | uint16_t TMPchecksum = 0; |
4 | uint8_t checksum; |
5 | uint8_t ID; |
6 | |
7 | Receive_Byte(); |
8 | //(LIN KOMMUNIKATION STARTET HIER)
|
9 | if (FE0==1){ //Ein Frame Error wird durch ein Sync-Break hervorgerufen |
10 | Receive_Byte(); |
11 | if (SyncFeld==0x55){ |
12 | |
13 | PID = Receive_Byte(); |
14 | //ID = PID
|
15 | |
16 | |
17 | if (ID ==0 && Slave_Address==0){ |
18 | |
19 | for (uint8_t i=0; i<=6; i++){ |
20 | Send_Byte(Daten[i]); //gesammelte Daten senden |
21 | TMPchecksum= TMPchecksum + Daten[i]; //Checksumme berechnen (CARRY-BIT fehlt noch)!!! |
22 | }
|
23 | |
24 | |
25 | TMPchecksum = TMPchecksum+PID; //Enhanced Checksumme! |
26 | |
27 | //Low-Byte der checksumme + High-Byte der checksumme
|
28 | checksum = (uint8_t) ((TMPchecksum & 0x00FF) + (TMPchecksum >> 8)); |
29 | |
30 | checksum= ~checksum; //Checksumme invertieren |
31 | Send_Byte(checksum); //anschließend die Checksumme senden |
32 | }
|
33 | |
34 | if (ID == 1 && Slave_Address==0){ |
35 | Set_Relais(0,1); //Relais1 einschalten |
36 | }
|
37 | |
38 | if (ID == 2 && Slave_Address==0){ |
39 | Set_Relais(1,0); //Relais2 einschalten |
40 | }
|
41 | |
42 | if (ID == 3 && Slave_Address==0){ |
43 | Set_Relais(1,1); //Relais1 und 2 einschlaten |
44 | }
|
45 | |
46 | if (ID == 4 && Slave_Address==0){ |
47 | SW_Reset(); //Reset auslösen |
48 | }
|
49 | }
|
50 | }
|
51 | |
52 | }//ISR |
das ist was ich soweit habe. doch muss ich nicht dafür sorgen, dass sich der slave synchronisiert? oder reicht es das ich einen Quarz nutze ?
Wenn Du einen Quarz nutzt, sehe ich bei festgelegter Baudrate (nicht Norm-konform) für Synchronisation keinen Bedarf. Sonst müsste sich ja jede beliebige UART-Schnittstelle auf geeignete Art und Weise synchronisieren. Im großen Stückzahl-Bereich tut man sicherlich alles dafür, um auf einen Quarz verzichten zu können. Ganz nebenbei sollte ein RC-Oszillator auch weniger Ausfallrisiko haben (Quarzbruch etc.). Für ein privates Projekt oder Kleinserie indes völlig unerheblich.
Sichte gerade deien Code. Du solltest nicht ernsthaft im Interrupt auf neue Bytes warten, das frisst ja deine ganze Rechenzwit auf. Viel besser baust Du eine State-Machine auf, stark vereinfacht dargestellt: if (FramingError) state = 1; switch (state) { case 0 : break; // Standby, nichts zu tun case 1 : Empfangsbyte==0x55 --> nein= state=0, ja=state++ case 2 : Empfangsbyte==ID ? --> nein=state=0, ja=state++ case 3 : Bytes in Schleife empfangen/senden case 4 : Checksumme senden bzw. prüfen --> Gültig=state++ / ungültig=state=0 case 5 : Frame an Applikation übergeben, state=0 }
Is es notwendig es in einer statemashine zu machen? Denn ich sammel mit meinem Modul Daten mit den ADC und wandle diese um. Das tue ich solange bis ich ein Anfrage vom Master erhalte die gesammelten Daten an ihn (Master) zu senden. gibt es eine bessere Möglischkeit?
Wie gesagt, ich würde den Code auf State-Machine umstellen. Die meisten UART sind ja double-buffered, d.h. ein nachfolgend empfangenes Byte schlägt im nächsten Durchlauf auf. In deinem Code gibst Du aber dem Break nicht die höchste Priorität. Wenn sich der Master den Abbruch des Frames überlegt (ist - glaube ich - in der Norm vorgesehen) würde dein Code das nicht berücksichtigen bzw. durcheinander kommen.
steht denn im Empfangsregister das Syncbreak solange bis ich es mittels receiveByte() abrufe? Erhalte ich beim 2. Aufruf von receiveByte() dann das Sync-Field? tut mir leid die fragen mögen sehr trivial und dumm erscheinen.
1 | uint8_t Receive_Byte(){ |
2 | DDRC = (1 << DDC3); //rote LED einschalten (receive) |
3 | while (!(UCSR0A & (1<<RXC0))) // warten bis Zeichen verfuegbar |
4 | ;
|
5 | return UDR0; // Zeichen aus UDR an Aufrufer zurueckgeben |
6 | DDRC = (0 << DDC3); //rote LED ausschalten (receive) |
7 | }
|
Ich bin jetzt beim AVR nicht so im Thema. Bei einigen Prozessoren muss man nach einem Framing Error erstmal alle Fehler zurücksetzen, bevor es weiter geht. In diesem Fall würde es so nicht funktionieren. Baue deine Interrupt-Routine so auf, dass immer nur ein Byte verarbeitet wird. Dann veranlasst Du alles notwendige und bereitest den Interrupt auf den Empfang des nächsten Bytes vor.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.