Probleme mit UART

Gast #423213
Lesenswert?

Hi,

ich hab Probleme bei der UART Übertragung. Empfangen alleine klappt, 
senden alleine auch, nur beides zusammen haut nicht hin.

Das empfangen mach ich per Daten-empfangen-Interrupt. Das senden ohne 
Interrupt in der main in der main(). Controller Atmega48

Hab leider den kompletten Code jetzt nicht zur Hand, sonst würd ich den 
auch mal schnell posten.

Hier nur ein paar Ausschnitte:

void uart_init()
{

DDRB |= (1 << PB2) | (1 << PB4) | (1<<PB1);

/* Aktivieren des Empfängers, des Senders und des "Daten 
empfangen"-Interrupts */
UCSR0B = (1<<RXCIE0) | (1<<RXEN0)|(1<<TXEN0);

/* baud rate*/
UBRR0H = (unsigned char) (UBRR>>8);
UBRR0L = (unsigned char) UBRR;

/* frame format: 8data, 2stop bit*/
UCSR0C =  (1 << USBS0) | (3 << UCSZ00);
}


void uart_transmit()
{
while(!(UCSR0A & (1<<UDRE0)))
{
}
UDR0 = AD_WERT_AKKU;
}


ISR(USART_RX_vect)
{
static unsigned char state = STATE_IDLE;

  c = UDR0;

  switch (state)
         ..............
         ...............
         ............usw.
}


Irgendwie scheinen die sich gegenseitig zu stören.... Sieht jemand den 
Fehler?



Gast #423235
Lesenswert?

In ca. ner Stunde bin ich Zuhause, dann poste ich den ganzen Code.

Können die(empfangen und senden) sich denn irgendwie gegenseitig stören?

Vielleicht liegt es an meiner STATE, die ist ziemlich lang. Wenn ich ihm 
nur zwei Byte über die serielle schicke klappt auch beides zusammen.

Nur bei 16Bytes vom einen Controller zum anderen tauchen die Probleme 
auf. Und dabei sieht es so aus, das die ersten 16Byte empfangen werden, 
danach aber nix mehr.
#423244
Lesenswert?

ALte Regel:
Interrupt Service Funktionen so kurz wie möglich. Während eine ISR
läuft sind alle Interrupts gesperrt.

Normalerweise holt man in der ISR einfach nur das Zeichen ab und
stellt es in einen Buffer (Queue). Verarbeitet werden die Daten
dann erst in der Hauptschleife in der main().

Wenn du dich an dieses einfache Schema hältst, kann eigentlich nichts
schief gehen.
#423260
Lesenswert?

1. Wie schauts mit der Gegenseite aus ? Auch ein Mega oder was hängt 
dort dran?

2. Probier mal, wenn du vor dem Senden den RX-Int abdrehst, TXCIE an und 
auf Tx-Complete wartest, und dann den RX-Int wie an, Tx aus.

Könnte sein, dass wenn ein Interrupt von RX kommt, das dir die Daten von 
UDRE verschmeisst.
Gast #424326
Lesenswert?

Ja, das ist anscheinend das Problem. Der UDR0 Wert.

Wenn ich Daten Empfange schreibe ich ja das erste Byte aus UDR0 in eine 
Variable und verarbeite diese mit einer STATE. Beim zweiten Empfangen 
Interrupt springt er dann in die entsprechende STATE und verarbeitet 
wieder UDR0. Das passiert 16 mal, also 16 empfangene Bytes.

Zwischendurch wird aber mal die Senden Routine aufgerufen. Da dort auch 
UDR0 verarbeitet wird, stimmen dann die Werte in der Empfangen Routine 
nicht mehr.

Könnte höchstens mit einem Interrupt arbeiten, welches am Anfang des 
Interrupts ausgeschaltet und nach 16 empfangenen Bytes wieder 
eingeschaltet wird.

Oder gibt es noch ne andere Möglichkeit?
Gast #424332
Lesenswert?

Dass der Inhalt des "Sende-UDR0" nichts (aber auch überhaupt nichts!) 
mit dem Inhalt des "Empfangs-UDR0" zu tun hat, ist dir klar, oder?
Das sind zwei Register, die über die gleiche Adresse angesprochen 
werden. Wobei man das eine nur beschreiben und das andere nur lesen 
kann.

Eine State-Machine gehört nicht in eine ISR! Dafür sollte die 
Hauptschleife genug Platz und Zeit bieten. (Schliesse mich somit 
Karl-Heinz an.)
Dass dein gepostetes Programm etwas spärlich ausfällt, hat Karl-Heinz ja 
auch schon "bemängelt".

Was soll dein Programm machen bzw. wie sieht dein Protokoll aus?
Je nach Protokoll würde ich in der Empfangs-ISR ein Flag setzen, dass 
entweder bei jedem empfangenen Byte gesetzt wird, oder dann gesetzt 
wird, wenn alle 16 Bytes empfangen wurden. Die zweite Variante erfordert 
auf jeden Fall einen Puffer... Dann könnte die State-Machine aber auch 
anders aussehen...
Gast #424344
Lesenswert?

>Dass der Inhalt des "Sende-UDR0" nichts (aber auch überhaupt nichts!)
>mit dem Inhalt des "Empfangs-UDR0" zu tun hat, ist dir klar, oder?

Doch doch, eigentlich schon. Aber das Programm funktioniert nur nicht, 
sobald ich in der Sende-Routine UDR0 veränderen.

Hab es jetzt mal mit nem Senden_Interrupt versucht. Haut aber auch nicht 
hin.

Das Programm ist nun dabei. ist wahrscheinlich dann einfacher 
nachzuvollziehen.
Angehängte Dateien:
Gast #424353
Lesenswert?

>UCSR0B = (1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0) | (1 << UDRIE0);
Wenn du UDRIE0 setzt, springt der Controller dauernd in die 
USART-Transmit-ISR.

Besser:
.
.
.

AD_WERT_AKKU = ADCH;
UDR0 = AD_WERT_AKKU;
UCSR0B |= (1 << UDRIE0);
.
.
.

ISR(USART_UDRE_vect)
{
  UCSR0B &= ~(1 << UDRIE0);
}

Und Kommentare würden die Lesbarkeit erhöhen...
Gast #424359
Lesenswert?

Ja, mit Kommentaren hab ich es nicht so. Werd ich aber gleich mal 
ändern.

Klappt so auch nicht, sobald UDR0 mit dem AD-Wert überschrieben wird.

Wenn ich diese Zeile "//UDR0 = AD_WERT_AKKU; "ausblende, dann geht der 
Empfang. Wenn sie mit ausgeführt wird, läuft er nicht.
Gast #424393
Lesenswert?

"c" würde ich noch ein "voltile" bei der Deklaration spendieren.
Eigentlich solltest du allen Variablem, die du in ISR und woanders 
benutzt, das volatile spendieren.

Wenn du den Funktionen c als Parameter übergeben würdest, wäre die 
State-Abfrage etwas kürzer.... (eher ein Schönheitsproblem...)
#424462
Lesenswert?

Eher nicht.
Was du siehst sind die Auswirkungen eines anderen Problems.
Ich steig aber noch nicht komplett durch den Code durch.
Werde mich in der Nacht mal dazusetzen und das mit realer Hardware
durchprobieren. Ich hab allerdings keinen Mega48, nur einen Mega16.
Sollte aber keinen Unterschied machen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren