Forum: Mikrocontroller und Digitale Elektronik Atxmega128A1, UART liefert falsches Zeichen


von Wolfgang K. (opendcc)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe hier ein unerklärliches Problem und hoffe, es hinreichend genau 
zu beschreiben, vielleicht hat einer der mitlesenden Experten einen 
Tipp.

Ausgangssituation:
ATXmega agiert als Kommunikationsprozessor, an einem UART kommen Daten 
mit 500kBaud an, diese werden in ein FIFO geschrieben, analysiert, 
bearbeitet und weiter auf einem anderen UART wieder abgesendet. Die 
Daten kommen paketweise, Pakete beginnen mit einem Längenfeld und sind 
mit CRC gesichert.
Die UART Routine ist als ISR mit Priority high implementiert, des 
weiteren gibt es noch andere Interrupts, teilweise auch mit High 
Priority. Alles ist in C geschrieben, Compiler ist der AVRGCC aus dem 
aktuellen Studio6.

Fehlerbild: Sporadisch (so mit 0,01%) ist das Längenfeld des Paketes 
0xA0 anstatt der korrekten Länge. Zur Kontrolle habe ich Scope an den 
RX-Pin des Prozessors gehängt, dort sehe ich den Datenstrom, der 
reingeht. In der ISR habe ich nach der Zeile rec = UART.DATA eine 
Kontrollausgabe auf einen Port reingehängt:
1
rec = UART.DATA;
2
if (rec == 0xA0) { PORT_AN(); _delay_us(1) PORT_AUS();}

Und wie auf dem Bild zu sehen ist, feuert das nach einiger Zeit und zig 
normalen Übertragungen. Zusammen mit dem Fehler stelle ich fest, dass 
die Interruptroutine etwas verzögert ist, d.h. ein anderer Interrupt war 
dazwischen. (Wenn ich die Kontrolle auf (rec == 0x06) umstelle, dann 
feuert das etwas früher und immer).

Ich habe darauf alle anderen Interrupts im System von HI auf MED 
zurückgestuft - und siehe da, der Fehler ist weg. Konkret hängt es an 
einem Interrupt, dessen ISR ich hier mal einhänge:
1
TCF0.INTCTRLB = TC_CCDINTLVL_MED_gc | TC_CCCINTLVL_HI_gc | TC_CCBINTLVL_MED_gc | TC_CCAINTLVL_MED_gc;
2
                
3
ISR(TCF0_CCC_vect)
4
  {
5
    PORTE.OUTSET = (1<<my_pin_p);
6
    TCF0.INTCTRLB = (TCF0.INTCTRLB & ~TC0_CCCINTLVL_gm) | TC_CCCINTLVL_OFF_gc;   // disable Interrupt
7
8
    // timer von event abkoppeln 
9
    
10
    TCF0.CTRLD = TC_EVACT_RESTART_gc | TC_EVSEL_OFF_gc;
11
  }


Wenn ich den TC_CCCINTLVL_HI_gc auf TC_CCCINTLVL_MED_gc ändere, tritt 
der Fehler nicht auf. Aber eigentlich ist die ISR vollkommen harmlos und 
hat nichts mit dem UART zu tun - weder der gleich Port noch irgendwelche 
Variablen.

Was ist jetzt nicht verstehe - warum ist das so? Warum wird das Byte 
verändert, welches ich vom UART lese?

von Stefan F. (sfrings)


Lesenswert?

Vermutlich ist die Ausführ-Zeit der Interrupt Routinen in Summe zu groß. 
Ich schätze, du bist da knapp an der Grenze. Versuche mal eine höhere 
Taktfrequenz oer eine niedrigere Bitrate oder vereinfache die serielle 
interrupt-routine zur Probe.

von Wolfgang K. (opendcc)


Lesenswert?

Hallo,

oben habe ich einen Screenshot des Timings eingestellt. Dort sieht man 
den Puls, den ich nach einem fehlerhaften Empfang ausgebe, etwa bei 30% 
des nächsten Wortes. D.h. der UART wurde rechtzeitig gelesen. Und selbst 
wenn ich nicht rechtzeitig lesen würde, dürfte das Ergebnis nicht 
einfach 0xA0 sein, sondern ich müßte den Wert des nächsten Bytes 
erhalten.

Servus Wolfgang

von Wolfgang K. (opendcc)


Lesenswert?

Hallo,

ich habe die Ursache gefunden.

Versuchshalber habe ich mal die quasi konkurrierende ISR mit einem Delay 
um ein paar us verlängert und konnte so den Fehler öfter provozieren. 
Und diese ISR liegt zeitlich nicht wie vermutet nach dem Empfang des 
Wortes im Weg, sondern vor dem Empfang, beim Aufschalten des Empfängers.

D.h. bei dieser Halbduplex Verbindung war das Umschalten der 
Richtungsleitung nach erfolgtem TX. Wenn sich hier eine weitere ISR 
reindrängt (eben die oben zitierte Timer-ISR), dann dauert dieser 
Vorgang 2us zu lang und manchmal ist die Gegenstelle schneller mit der 
Antwort.
Folge: ich versause das Startbit, snyce erst beim Bit 3 ein (also nach 
den 2 Einsen vom 00000110) und interpretiert das Stopbit und folgendes 
Startbit als 0xA0.

Wie immer, das Problem ist vor dem Bildschirm ..

Danke und Servus

Wolfgang

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.