Forum: Mikrocontroller und Digitale Elektronik Arduino / ATmega328P UART RX Interrupt LIN


von Gooniebert (Gast)


Lesenswert?

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
  }
}

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

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?

von Max B. (gooniebert)


Lesenswert?

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
von Timo N. (tnn85)


Lesenswert?

Wo ist denn dein LIN-Bus am ATmega328 angeschlossen?
Schaltplan?

von Max B. (gooniebert)


Lesenswert?

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)

von Timo N. (tnn85)


Lesenswert?

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?

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

> Bei 16MHz Clock mehr als genung.
Wo kommt das Taktsignal her?

Der interne RC-Oszillator ist oft zu ungenau für den USART.

von foobar (Gast)


Lesenswert?

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 ...

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

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.

von Wolfgang (Gast)


Lesenswert?

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.

von Wolfgang (Gast)


Lesenswert?

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.

von Frank K. (fchk)


Lesenswert?

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
von Max B. (gooniebert)


Lesenswert?

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!

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

>> 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
Noch kein Account? Hier anmelden.