Hallo liebe Mikrocontroller-Gemeinde, ich habe in den letzten Tagen versucht ein LIN-Bus Protokoll mit zwei Arduino Boards, über deren USART0 Schnittstelle, zu realisieren. Dabei stoße ich jedoch auf ein Hindernis, welches ich einfach nicht überwinden kann. Der Master überträgt seinen Frame (kontrolliert via Oszi), im Slave verwende ich nun zwei Interrupts. Der erste (INT0) erkennt den Syncbreak (13 LOW Bits) und schaltet daraufhin den Slave in Empfangsmodus. Der zweite (USART_RX) soll nun immer ausgelöst werden, wenn neue Daten im Puffer verfügbar sind. Der INT0 Interrupt wird auch tadellos ausgeführt (Kontrolle via LED) und erkennt den Syncbreak. Der USART_RX wird hingegen gar nicht getriggert. Globale Interrupts sind aktiviert, Masse ist verbunden und beide USART sind gleich konfiguriert. Mit den Arduino eigenen Funktionen klappt alles wie gewünscht. Genutzt wird AVR Atmel ATmega 328P Programmiert wird in der Arduino IDE, loop() entspricht der main(). setup() wird bei Programmstart automatisch ausgeführt. Erläuterung des Codes: Der Slave wartet auf den INT0 Interrupt um den Syncbreak zu erkennen. Ist der Syncbreak erkannt, wird der USART Empfänger aktiviert. (Zwischen Ende des Syncbreak und Eingang von Daten über dem USART vergehen 4 Bit bei 19.2k Baud. Der Empfänger soll nun Antworten wenn seine PID empfangen wird. Nach der Antwort geht der Slave wieder in Ausgangsposition zurück. Vielleicht hat jemand von euch eine Idee, was hier falsch laufen könnte. Vielen Dank im Voraus! Hier der Code des Slaves: #include <avr/io.h> // Inkludiere IO Ports #include <avr/interrupt.h> // Inkludiere Interrupt Adressen unsigned char PID = 0x92; // Enthält die geschütze ID des LIN Slave unsigned char sync = 0x55; // Enthält die Form des Syncfield unsigned char RESP = 0xFF; // Antwort des Slave volatile bool Lowedge = false; // Gibt an, ob eine Low Edge erkannt wurde volatile unsigned char data[2]; // Enthält empfangende Daten volatile int i = 0; // Index für Datenpuffer volatile bool newdata = false; // Gibt an, ob neue Daten empfangen wurden // Erkennung Syncbreak ISR(INT0_vect) // Interrupt wenn Pegelwechsel auf INT0 = RX { EICRA ^= B00000001; // Wechsel von Low auf High, bzw. High auf Low Edge Trigger if(Lowedge == false) { TCNT1 = 0; // Timer1 mit Startwert = 0 TCCR1B = B00000011; // Starte Timer1 64 Prescaler Lowedge = true; // Low Edge erkannt } else { if(TCNT1 >= 84) // Syncbreak mindestens 13 Bit { UART_config_RX(); // syncBreak erkannt -> Receiver an } TCCR1B = B00000000; // Stoppe Timer1 Lowedge = false; // Low Edge Flag reset EIMSK = B00000000; // Disable INT0 } } // Erkennung neuer Datenpakete ISR(USART_RX_vect) // Interrupt wenn im RX Buffer des USART Daten verfügbar sind { data[i++] = UDR0; // Lade die neuen Daten in das Datenarray newdata = true; // Teile mit, dass neue Daten verfügbar sind } void setup() { SREG |= B10000000; // Enable Global Interrupts EICRA = B00000010; // Falling Edge Trigger EIMSK = B00000001; // Enable INT0 TCCR1B = B00000000; // Timer1 gestoppt } void UART_config_RX() { // UART-Empfänger konfigurieren UBRR0L = 51; // 19200 Baud //Init UART UCSR0B = B11010000; // 8-Bit Data, RX + TX aktiv, TX deaktiviert, RX Interrupt aktiviert UCSR0C = B00000110; // 8-Bit Data, Keine Parity, Rising Clock } void UART_config_TX() { // UART-Sender konfigurieren UBRR0L = 51; // 19200 Baud // Init UART UCSR0B = B11001000; // 8-Bit Data, TX + RX aktiv, RX deaktiviert, TX Interrupt aktiviert UCSR0C = B00000110; // 8-Bit Data, Keine Parity, Rising Clock } void UART_ausschalten() { // Deaktiviere USART UCSR0B = B00000000; UCSR0C = B00000000; } void LIN_receive() { if((data[0] == sync) && (data[1] == PID)) { UART_ausschalten(); LIN_sendResponse(); // Sende Antwort an Master EIMSK = B00000001; // Enable INT0 - Ermöglicht erneutes empfangen eines Syncbreak data[0] = 0; data[1] = 0; } else { UART_ausschalten(); EIMSK = B00000001; // Enable INT0 - Ermöglicht erneutes empfangen eines Syncbreak data[0] = 0; // Reset data Array data[1] = 0; } } void LIN_sendResponse() { UART_config_TX(); while(!(UCSR0A & (1 << UDRE0))); // Warte bis Transmitbuffer leer ist UDR0 = RESP; // Sende Antwort = 0xFF } void loop() { if(newdata == true) // Warte auf Empfangene Daten { if((data[0] != 0) && (data[1] != 0)) // Wenn zweites Byte empfangen { LIN_receive(); // Starte Datenverarbeitung } newdata = false; // Setze NewData-Flag zurück } }
Kann es sein, daß der USART RX nicht schnell genug startet und die Daten dadurch verlorengehen? Der USART muß auch die Start- und Stopbits erkennen, ansonsten bekommst Du keine Daten. Kannst Du keine Lösung bauen, bei der der USART RX hardwareseitig immer aktiv bleibt?
Hallo Ben, auch das habe ich bereits versucht. Dabei den Syncbreak weggelassen und einfach so Daten gesendet. Auch da wurde kein Interrupt ausgelöst. Der USART hat auch mit Syncbreak etwa 200us lang Zeit. Bei 16MHz Clock mehr als genung.
:
Bearbeitet durch User
Wo ist denn dein LIN-Bus am ATmega328 angeschlossen? Schaltplan?
Aktuell möchte ich nur den LIN Frame richtig hinbekommen. Dabei sind die beiden Boards direkt über RX und TX verbunden. (TX1 -> RX2 TX2->RX1)
Am INT0 muss der LIN-Bus ja auch hängen. Sonst würde ja kein Interrupt ausgelöst werden. Deshalb meine Frage. Also hängt der LIN-Bus and INT0 und an RXD?
> Bei 16MHz Clock mehr als genung.
Wo kommt das Taktsignal her?
Der interne RC-Oszillator ist oft zu ungenau für den USART.
Ohne mit LIN jemals was gemacht zu haben: ist das Gehampel mit einem zweiten Interrupt für's Break wirklich nötig? Reicht nicht einfach ein empfangenes 00-Byte mit Frame-Error als Break-Erkennung? Im übrigen sieht deine gesamte Programmlogik sehr chaotisch aus - so wird das nie stabil laufen. PS: Sei froh, dass kein RX-Interrupt kommt - wenn ich das richtig sehe, überbügelt der den gesammten Speicher ...
An sowas habe ich auch gedacht, ähnlich wie DMX512 das macht. Dort bekommt man das Break vom USART als fehlerhaftes Datenbyte mit fehlenden Stopbits gemeldet (RX_complete mit frame error flag beim AVR). LIN ist wieder mal irgendeine KFZ-Kacke, weil wohl irgend ein paar Techniker nicht mit dem CAN-Bus klargekommen sind und sich für bereits existierende Lösungen zu fein waren. Anders kan ich mir den Bedarf nicht erklären. Allerdings weiß ich auch zu wenig über dieses LIN-Protokoll, um jetzt spontan die Auswertung mit dem USART alleine zu wissen. Und ja, die USART_RX ISR würde den Speicher zumüllen. Allerdings kenne ich auch vom Arduino zu wenig, weiß nicht wie schlau der Compiler ist. Wäre auch möglich, daß er einen Fehler meldet wenn i in den Stack hineinläuft bzw. kein weiterer Speicher für neue Variablen bzw. eine Vergrößerung des Arrays mehr verfügbar ist.
Ben B. schrieb: > LIN ist wieder mal irgendeine KFZ-Kacke, weil wohl irgend ein paar > Techniker nicht mit dem CAN-Bus klargekommen sind und sich für bereits > existierende Lösungen zu fein waren. Bei ersterem muss ich dir voll zustimmen. Der Grund wird wohl eher gewesen sein, dass CAN zu leistungsfähig (aka aufwendig und teuer) ist, für einfache Steuerungen also irgendetwas einfaches (aka billigeres) her muss. Alleine die Einsparung beim Kupfer durch den einadrigen Bus wird bei BWLern schon ein Leuchten in den Augen ausgelöst haben.
Ben B. schrieb: > Allerdings kenne > ich auch vom Arduino zu wenig, weiß nicht wie schlau der Compiler ist. Arduino hat keinen eigenen Compiler. Der verwendet den GCC.
Ben B. schrieb: > LIN ist wieder mal irgendeine KFZ-Kacke, weil wohl irgend ein paar > Techniker nicht mit dem CAN-Bus klargekommen sind und sich für bereits > existierende Lösungen zu fein waren. Anders kan ich mir den Bedarf nicht > erklären. Allerdings weiß ich auch zu wenig über dieses LIN-Protokoll, > um jetzt spontan die Auswertung mit dem USART alleine zu wissen. Es ging um Kosten. LIN ist ausdrücklich dafür gemacht, dass der Slave beispielsweise keinen Quarz braucht, sondern dafür das Synchronbyte nach dem BREAK ausmessen und die Bitrate danach einstellen kann. Dafür muss der Prozessortakt nur während eine Frames halbwegs konstant bleiben, Langzeitstabilität ist egal. Und ein Software-UART sollte auch reichen - das war Designziel. Die UARTs der AVRs sind für LIN nicht wirklich optimal. Es gibt einige Automotive-AVRs wie Tiny167, die einen speziellen LIN-UART haben. Da geht es nämlich genau und die normgerechte BREAK-Erkennung und Erzeugung. Für die gibts dann auch eine passende AppNote: https://www.microchip.com/wwwAppNotes/AppNotes.aspx?appnote=en592045 Ansonsten hat jeder PIC24/dsPIC33 und PIC32 LIN 2.2 konforme UARTs standardmäßig eingebaut. fchk
:
Bearbeitet durch User
Vielen Dank für die zahlreichen Antworten! Timo N. schrieb: > Also hängt der LIN-Bus and INT0 und an RXD? Ja, ganz genau. foobar schrieb: > ist das Gehampel mit einem > zweiten Interrupt für's Break wirklich nötig? Reicht nicht einfach ein > empfangenes 00-Byte mit Frame-Error als Break-Erkennung? Das möchte ich nun gerne versuchen, gits da vllt. einen Tipp wie man ein solchen Error-Frame provoziert? Mir kommt dazu folgendes in den Sinn: Sender schickt 9 Bit Datenwort. Empfänger ist dabei auf 8 Bit konfiguriert. Der Empfänger interpretiert dies als Stopbitfehler und meldet einen Frame-Error? Frank K. schrieb: > Die UARTs der AVRs sind für LIN nicht wirklich optimal. Es gibt einige > Automotive-AVRs wie Tiny167, die einen speziellen LIN-UART haben. Da > geht es nämlich genau und die normgerechte BREAK-Erkennung und > Erzeugung. Vielen Dank, bei dem finalen Projekt werde ich wohl lieber diese Alternative benutzen!
>> Wo kommt das Taktsignal her? > <nullstring> Danke für die Antworten...
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.