Hallo Zusammen,
nutzen der Forensuche ergab nicht das gewünschte Ergebnis ;-(
Ich habe ein Problem mit UART Interupts.
Ich habe ein Gerät, welches 2 UARTS verwendet. An der einen
Schnittstelle kommt ca alle 5 sec. ein Protokoll an und der Wert wird
gespeichert (UART1)
Die andere Schnittstelle (UART2) dient zur Kommunikation mit dem PC.
Beides funktioniert grundsätzlich auch ganz gut. In Kombination treten
jedoch sporadisch Fehler auf.
Im Anhang befindet sich die Interuptrutine der 2ten Schnittstelle. Die
der Ersten sieht im Prinzip genau so aus.
Der Fehler entsteht offensichtlich dann, wenn ein Interrupt von der
ersten Schnittstelle auftritt, während in der 2ten gerade gearbeitet
wird. Der Fehler tritt nicht auf, wenn ich den Interrupt in der
initialisierung der ersten Schnittstelle herausnehme. Die kann dann nur
leider nicht verwenden...
Verarbeitet wird das Protokoll, sobald 8 Bytes angekommen sind. Dies
geschieht nicht, wenn der Empfang zu lange dauert, wodurch ich wenn die
Interrupt Routine der zweiten Schnittstelle nicht korekt zuende
ausgeführt wird ein Timeout bekomme und dadurch das ergebnis für mich
unbrauchbar ist.
In meiner persöhnlichen prioritätsliste ist die 2te Schnittstelle
wichtiger als die erste, da nihct viel passiert, wenn ich hier einen
Wert übersehe.
Aus dem Grund habe ich versucht alle Interrupts wärend der
Interrupsroutine auszuschalten ( _DINT() _EINT() ) dies hat jedoch
keinen effekt gehabt.
Hat jemand eine Idee, was ich ncoh probieren könnte? Ist die einzige
Lösung UART0 und UART1 zu tauschen und den fehler bei der momentan 1sten
Schnittstelle machen zu lassen?
Klingt eher nach Workaround als nach Lösung.
Gruß Daniel
Dein Quelltext ist mega stressig zu lesen!
Wie hast du denn die ganzen Einrückungen hibekommen? :)
Außerdem kann man ja garnicht sehen, wie du die initialisiert hast.
Woher sind die zwei UARTs? Von zwei verschiedenen
Kommunikationsschnittstellen oder benutzt du beide von einer
Schnittstelle?
wo ist denn der Stressig zu lesen? der ist Struktoriert.
Einrücken tue ich mit Tabs ;-)
Ich verwende 2 Schnittstellen, sie sind also auch physikalisch einzeln.
Hier die beiden Initialisierungen.
Ach ja, tauschen der Uarts ist nicht möglich, da sich die beschaltung
unterscheidet und die Platinenlayouts schon zum Hersteller raus sind.
Grundsätzlich läuft die Übertragung im Übrigen extrem stabil, ahbe eben
10000 Übertragungszyklen also etwa 60-80k Protokolle verschickt und
nicht einen Fehler bekommen, nur dier interrupt ist ein Problem.
Wird dieser nicht initialisieret oder einfach nicht angeschlossen
funktioniert alles ohne Problem. Diesen in Teilen des Codes
auszuschalten bringt nichts.
Habe den Interrupt für Schnittstelle1 in dem Interrupt für
Schnittstelle2 und allem was irgendwie mit übertragung zu tun hat
deaktiviert. Das Problem bleibt.
IE1 &= ~URXIE0;
Ausgeschaltet habe ich ihn so.
Gruß Daniel
Ich habe mal ausführungszeiten gemessen.
Leere interrupt routine benötigt ca 5us meine Routine etwa 130us.
Es wird pro Protokoll aber nur eine ausgelöst. Also sagen wir so ich
kann einen Auschlag messen je 500ms ( ich schalte einen Ausgang ein und
aus, ein beim eingang in die Funktion und aus beim verlassen) Protokolle
werden alle 500ms gesendet also ein Interrupt je Protokoll. Es werden
jedoch alle Zeichen ordentlich empfangen ist das Richtig so?
Verwendeter Controler ist der MSP430F149
Ein "funktioniert fast immer" Szenario ist typisch für sogenannte "race
conditions" - Fälle, in denen Operationen durch einen Interrupt
unterbrochen werden, die nicht unterbrochen werden dürfen. Was nur
selten geschieht. Dann liegt das Problem aber nicht im
Interrupt-Handler, sondern im übrigen Code.
So, das dürfte in etwa alles sein was von interesse ist und in diesem
Fall ausgeführt werden kann.
Fast immer heißt im übrigen in etwa 99,3% das macht es sehr nervraubend
das ganze zu debuggen.
ich habe jetzt den Code für andere Betriebszustände weg gelassen, in
diese wird in meinem Fall jedoch nicht gewechselt. Dort gibt es auch
keine weiteren Interruptfähigen Funktionen.
Ohne das nun im Einzelnen untersucht zu haben: Überleg die mal bei jeder
Zeile in ubGetMessageUart1, was passiert wenn darin ein Interrupt
ebendieser UART auftritt.
Fiese Kombination, ich Programmiere in Crossworks und habe die Schnipsel
hier fix in Visualstudio zusammen Kopiert, das gibt manchmal komische
verschiebungen. Die sind in Crossworks nicht so groß in VS auch nicht
@ A.K. es sollte eigentlich nciht viel passieren. ubUart1Receive[][]
funktioniert als Ringpuffer. Selbst wenn ich nicht wieder in die
Funktion zurück springe habe ich unterschiedliche wete für
ubReadUart1Buf und ubWriteUart1Buf, so dass im schlimmsten Fall 2 mal
Kopiert werden würde.
Außerdem kann zu diesem Zeitpunkt kein Interupt auftretten, da der
Kommunikationspartner auf eine Antwort wartet.
Aber wie gesagt selbst wenn wird eigentlich normal weiter verfahren,
außer ich täusche mich massiv, was natürlich nach einigen Tagen ncihts
anderes angucken durchaus möglich ist.
Ich habe die selben Funktionen auf einem System laufen, bei dem nur eine
UART verwendet wird. hier kommt es NIE zu Fehlern.
"NIE" im Sinne von während meiner Tests also zu 1,5 Mio. abfragen.
Daniel G. schrieb:> Außerdem kann zu diesem Zeitpunkt kein Interupt auftretten, da der> Kommunikationspartner auf eine Antwort wartet.
Du kannst selbstverständlich versuchen, mich davon zu überzeugen, dass
das Programm eigentlich garantiert funktioniere müsste. Aber wie du oben
selber schon festgestellt hast... Drum kann es nicht schaden, mal die
eine oder andere Annahme in Frage zu stellen. Zumal grad diese Annahme
eine böse Tretmine ist, falls sich später mal was ändert.
So kannst du mal versuchsweise neuralgische Punkte absichern,
beispielsweise den "Have we a message" Block mit abgeschalteten
Interrupts durchfahren. Mag vielleicht noch mehr davon geben - jeder
Code, der die gleichen Variablen wie ein Interrupt-Handler verwendet,
ist ein potentialler Kandidat.
Was ich versucht habe, ist dass ich den Inhalt des Interupt Handler für
UART0 auskommentiert habe. So läuft es Problem frei.
Übertragung über UART1 läuft fehlerfrei mit allen weiteren Interrupts,
was der von UART1 und ein Timer Interrupt ist.
Sobald ich den Interrupt für UART0 einbinde kommt es gelegentlich zu
Fehlern. Diesen aus zu schalten mit
E1 &= ~URXIE0;
hat jedoch nciht gebracht. Ich habe ihn ausgeschaltet wärend des
Interrupts von UART1 und über diesen Bereich
1
vCheckTelegramTimeout();
2
ubTelegramState=ubGetMessageUart1();
3
4
5
6
if(ubSystemState==STATE_SOFTWARE)
7
{
8
if(ubTelegramState==UART_TELEGRAM_IO)
9
{
10
vSoftwareHandler();
11
}
12
}
also immer wen irgend etwas mit Datenübertragung stattfindet.
Wie du richtig erkannt hast ist die Abfrage bei "Have we a message" die
einzige Stelle in der empfangs routine in der eine Variable verwendet
wird, die auch in dem Interrupt verwedentet wird.
Stelle ich an dieser stelle alle interrupts aus, so haben ich keine
änderung des Verhaltens.
Da du bislang nie jenen Code gezeigt hast, der die tatsächlichen
Probleme enthält, sondern immer nur bestimmte Häppchen, kannst du kaum
mehr als Allgemeinplätze erwarten.
Du schreibst schon mehrfach, dass das Problem nur auftritt, wenn beide
UARTs in Betrieb sind, zeigt aber seltsamerweise stets nur Code, der
eine einzige UART verwendet. Wie stellst du dir das vor? So fehlen auch
die Variablendeklarationen - zwar klingt das bisher nicht nach den
üblichen "volatile" Problem, aber diese Strategie des information hiding
führt einfach nicht weiter. Wenn der Fehler exakt da wäre, wo du ihn
suchst, hättest du ihn vielleicht schon selbst gefunden.
Wenn das zu gross und zu unhandlich ist, dann reduzier den Kram soweit,
bis es entweder lesbar oder das Problem bei Eingrenzung verschwunden
ist.
Ok, dann nochmal beide Uarts Zusammenhängend. UART0 wird nur zum gelesen
nie gesendet.
habe das ganze noch ein wenig reduziert und hoffentlich alles zusammen
gepackt was wichtig ist.
Viel mehr wird nicht gemacht, habe nur die Port initialisierung und
sowas weg gelassen.
@ A.K. es tut mir sehr leid wenn ich dir in meinem bemühen das ganze
lesbar zu machen vor den Kopf gestoßen habe, das war nie meine Absicht.
Ich hoffe du nimmst es mir nicht al zu übel. Das Programm ist über
einige Dateien verteilt. deshalb ahbe ich die Wichtigen Teile zusammen
kopiert und möglicherweise Teile ausgelassen, die evtl. wichtig sein
könnten.
Kurzes Googlen ergab volatile verhindert, das isrgendwo Kopien einer
Variablen erstellt werden oder diese weg optimiert werden.
Es schadet also nicht, bis auf , dass ich die optimierung meines
Compilers aushebele.
Ich habe jetzt mal an allem was irgendwie benutzt wird ein volatile
davor geschrieben. es ändert am verhalten leider nichts.
Ich denke den "Fehler" gefunden zu haben. Es wird sich wohl um eine
Timing Problem gehandelt haben. nachdem ich die Checksummen überprüfung
ausgelagert habe hat sich die Fehler anfälligkeit mindestens drastisch
reduziert.
Eine frage hätte ich noch. Ich bin im Moment noch unzufrieden mit deisem
Konstrukt
1
if(RXBUF1==AUSSEN_TEMP||RXBUF1==RE_START_ANLERN
2
||RXBUF1==RE_NEW_ADRESS
3
||RXBUF1==RE_QUIT_ANLERN
4
||RXBUF1==RE_STATUS_MELDEN
5
||RXBUF1==RE_STATUS_AENDERN
6
||RXBUF1==RE_ERROR_STATE
7
||RXBUF1==SW_GET_TEMP
8
||RXBUF1==SW_GET_INPUT
9
||RXBUF1==SW_GET_ALLOCATION
10
||RXBUF1==SW_GET_PERIODE)
wenn ich daraus eine switch anweisung mache, die immer das gleiche
macht, gewinne ich dadurch Zeit? im falle von "RXBUF1 == SW_GET_PERIODE"
muss der Controler ja erst alle anderen abfrage. Da an dieser stelle
noch einige Möglichkeiten hinzu kommen werden würde hier mit sicherheit
einige Zeit verloren gehen.
Unsicher bin ich, ob der Compiler aus der switch anweisung intern wieder
eine geschachtelte Abfrage macht. Hat hier jemand erfahrungswerte und
möchte die mit mir teilen?
Wenn RXBUF1 ein I/O-Register ist, dann ist diese Anweisung definitiv
ineffizient, denn dann wird für jeden einzelnen Vergleich der Port neu
ausgelesen (weil volatile). RXBUF1 sollte vorneweg einmal in eine lokale
Variable ausgelesen werden.
Ein Switch-Statement wird wahrscheinlich effizienter sein als solch ein
Stapel Vergleiche. Meist erzeugt ein Compiler aus einem nicht zu
winzigen switch Statement je nach Werteverteilung entweder eine Tabelle
[O(1)] oder einen Vergleichsbaum [O(log N)] an Stelle deiner linearen
Suche [O(N)]. Ja nach Werteverteilung kannst du auch selbst eine Tabelle
verwenden.
Wieso das?
Die variante mit der Switch anweisung bringt leider gar nichts, die
Zeiten sind identisch mit denen beim benutzen der Schachtel if
Konstruktion.
Das verwenden eine Variable statt dem RXBUF war jedoch viel
versprechend. hier gewinne ich 36us bei 192us bzw jetzt 156us eine ganze
Menge wie ich finde.
Mal sehen was ich noch so an Möglichkeiten finde
>PS: Wenn RXBUF1 ein UART-typischer Transferpuffer ist, also einer der>sich beim Auslesen automatisch leert, dann wundert mich nix mehr.
Da leert sich gar nix! Nur das IRQ-Flag (IFG) wird zurückgesetzt.
>hier gewinne ich 36us bei 192us bzw jetzt 156us eine ganze>Menge wie ich finde.>Mal sehen was ich noch so an Möglichkeiten finde
Ich sehe keinen driftigen Grund, warum man in der ISR jedes empfangene
Byte komplett auf alle möglichen Bedingungen hin untersuchen sollte?
Ein Rahmen mit BeginOfFile und EndOfFile-Kennung reicht doch völlig aus.
Der empfange String wird dann irgendwo in main() auf Gültigkeit und Sinn
untersucht!
Stefan schrieb:>>PS: Wenn RXBUF1 ein UART-typischer Transferpuffer ist, also einer der>>sich beim Auslesen automatisch leert, dann wundert mich nix mehr.> Da leert sich gar nix! Nur das IRQ-Flag (IFG) wird zurückgesetzt.
Ok, ob sich der Inhalt ändert oder nicht mag unterschiedlich sein und
hängt sicherlich auch von der Puffertiefe ab. Vielleicht ist das beim
MSP430 ja anders, aber üblicherweise werden solche Pufferregister genau
ein einziges Mal pro empfangenem Byte ausgelesen.
>Ich sehe keinen driftigen Grund, warum man in der ISR jedes empfangene>Byte komplett auf alle möglichen Bedingungen hin untersuchen sollte?
Die abfrage wird nur beim ersten Byte eines Potentiellen Protokolls
durch geführt. Die folgenden Bytes werden nur eingelesen.
Dies hat den Grund, dass ich nciht garantieren kann, dass das erste
empfangene Byte immer der anfang eines Protokolls ist. Diese Abfrage
dient dazu, dass der Controler, für den Fall das er den Anfang der
Protokolle aus irgend einem Grund verliert, wieder bei einem Ersten Byte
anfängt zu lesen und so wieder in einen normalen empfangszyklus zurück
kehrt.
Daniel G. schrieb:>>Ich sehe keinen driftigen Grund, warum man in der ISR jedes empfangene>>Byte komplett auf alle möglichen Bedingungen hin untersuchen sollte?>> Die abfrage wird nur beim ersten Byte eines Potentiellen Protokolls> durch geführt. Die folgenden Bytes werden nur eingelesen.> Dies hat den Grund, dass ich nciht garantieren kann, dass das erste> empfangene Byte immer der anfang eines Protokolls ist. Diese Abfrage> dient dazu, dass der Controler, für den Fall das er den Anfang der> Protokolle aus irgend einem Grund verliert, wieder bei einem Ersten Byte> anfängt zu lesen und so wieder in einen normalen empfangszyklus zurück> kehrt.
Deshalb ja auch die Nutzdaten in <BOF> ... <EOF> einschließen.
Dann muss man nur nach BOF und nach EOF suchen ;-)
>Deshalb ja auch die Nutzdaten in <BOF> ... <EOF> einschließen.>Dann muss man nur nach BOF und nach EOF suchen ;-)
Ich glaube damit bekomme ich das hin Interupt zeit liegt jetzt bei ca
104us bis jetzt 48K Protokolle ohne Fehler. Ich werde das heute Nacht
ncohmal testen. Habe allerdings ein 0xFF verwendet, da dieses Zeichen
nur unter monströs unwahrscheinlichen Umständen als Checksumme vorkommen
aknn und das müsste genau dann passieren, wenn gerade ein Fehler
vorliegt.
Soweit so gut und vorerst Danke für eure Hilfe