Hallo Gemeinde, in gewisser Weise seid ihr meine letzte Hoffnung !
Seit vielen Monaten(!) jage ich ein seltsames Problem mit RS232 dass
immer mal wieder sporadisch auftritt und das ich bisher nicht lösen
konnte, da es schon sehr schwierig ist das Problem überhaupt zu
reproduzieren. Grundaufbau: Eine PC Anwendung kommuniziert mit einem
Mikrocontroller System und transferiert Daten über RS232 (echte serielle
Schnittstelle kein USB->RS232 Konverter oder ähnliches). Die PC
Anwendung und das Microcontroller System benutzen ein einfaches
Protokoll zum Datenaustausch. PC seitig werden Funktionen der Win32 API
(unter Windows XP) zur Ansteuerung der seriellen Schnittstelle
verwendet. Der Mikrocontroller (oder besser DSP) ist ein TMS320F283xx.
Als Level-Shifter wird ein MAX3243 verwendet.
Der Fehler tritt leider nur sporadisch auf und ist sehr schwer zu
reproduzieren. Es sind meist über 100000 Transfers in dem Protokoll
nötig bis der Fehler mal auftritt. Er führt PC-seitig dazu, dass die
Funktion ReadFile() mit einem Timeout abbricht. Vermutlich weil die PC
Anwendung noch Daten erwartet die aber nicht ankommen (Vermutung: für's
Protokoll relevante Zeichen gehen verloren).
Nun hatte ich zunächst die Mikrocontroller-Software im Verdacht. Habe
dort die gesamte diesbezügliche I/O Funktionalität komplett
neugeschrieben: Vorher war Polling und Waiting angesagt nun sind Receive
und Transmit interrupt-basiert mit je 512 byte RX und TX FIFO Puffern.
Der Fehler ist dadurch aber nicht verschwunden. Nun habe ich mal mit
Portmon so eine Kommunikations-Session mitgeloggt und bin auf Fragmente
wie dieses hier gestoßen:
Man beachte die Lücke in diesem Log, die wird auch in Portmon so
angezeigt und ist mir deshalb unerklärlich. Danach scheinen Zeichen
doppelt zu kommen, da hier eigentlich das Wort "Success" empfangen
werden sollte.
Diese Stellen sind in der Tat häufer im Log:
Hier scheinen bei der "Lücke" gleich ca 10 Zeichen zu fehlen.
Deshalb glaube ich langsam dass es ein Windows Problem ist. Das
Protokoll zwischen Mikrocontroller und PC benutzt ein ASCII-0 um
anzuzeigen, dass der Mikrocontroller fertig mit senden ist. Für die PC
Seite heißt das: Wenn ein ASCII-0 empfangen wurde keine weiteren
ReadFile() Aufrufe mehr und nächstes Kommando senden, dann wieder auf
Antwort warten. Ich vermute nun dass das Timeout auftritt wenn ein
ASCII-0 auf oben dargestellte Weise "verschluckt" wurde und die PC Seite
erwartet noch weitere Zeichen zu erhalten, der Mikrocontroller aber
bereits alles gesendet hat.
Die Konfiguration der seriellen Schnittstelle laut Portmon ist übrigens:
1
0.00004734 tclsh85.exe IRP_MJ_CREATE Serial1 SUCCESS Options: Open WriteThrough
Gerry E. schrieb:> Ich weiß es nicht genau, aber es könnte sich um einen Framing-Error> handeln.> Hast Du den im Windowsprogramm abgefangen?
Nein, müßte ich mal prüfen. Einfach mal mit ClearCommError() schauen ob
PC-seitig irgendwelche Fehler erkannt werden ?
Hallo,
rufst du ReadFile für jedes ASCII-Zeichen einzeln auf? Oder nur einmal
bis zu NUL?
Ob Windows schuld ist, könntest du ja prüfen, indem du einen externen
Monitor zum Vergleich mitlaufen lässt, z.B. einen Protokoll-Analysator
direkt an der RS232C-Leitung.
Gruss Reinhard
Reinhard Kern schrieb:> Hallo,>> rufst du ReadFile für jedes ASCII-Zeichen einzeln auf? Oder nur einmal> bis zu NUL?>
Ich rufe ReadFile() für jedes einzelne Zeichen auf. Das Protokoll wird
sozusagen zeichenweise verarbeitet. Eine ASCII-0 markiert das Ende einer
Antwort vom Mikrocontroller System.
> Ob Windows schuld ist, könntest du ja prüfen, indem du einen externen> Monitor zum Vergleich mitlaufen lässt, z.B. einen Protokoll-Analysator> direkt an der RS232C-Leitung.>
Guter Vorschlag. Werde Montag aber erstmal checken ob ich im Timeout
Fall von ReadFile() Windows ein paar Details mit ClearCommError()
entlocken kann.
Vor einem Protokoll-Analyzer habe ich bisher zurückgeschreckt weil es
sind halt einfach extrem viele Transaktionen (mehrere 100k) nötig damit
das Problem überhaupt mal auftritt. Und jede Transaktion besteht aus
dutzenden Zeichen. Die zu analysierende Datenmenge ist deshalb schon
gewaltig. Habe auch festgestellt, dass es durchaus von Computer zu
Computer anders ist. An meinem Rechner tritt es z.B. praktisch gar nicht
auf, am Rechner eines Kollegen hingegen häufiger.
Ich hatte bei mir ähnliche Probleme. Zeichen fehlten. Zuviele oder
zuwenige Zeichen. Es gibt zu einen zwei sehr schöne OCX (Active X)
Controls dafür.
Einmal die MSCOMM32 von Microsoft, wird bei Visual C mitgeliefert. Und
mein persönlicher Favorit ist Comcontrol von www.jspayne.com. Damit habe
ich dann herausgefunden das mein Timing nicht passt. Kommen schicke
Fehlermeldungen raus.. Der BRG (Baud Rate Generator) im Microcontroller
lief nicht genau genug. Quarz und Teilerfaktor geändert und seither
keine Probleme mehr.
Thomas B. schrieb:> Es gibt zu einen zwei sehr schöne OCX (Active X)> Controls dafür.
für was sollte man den ocx für die Com schnittstelle verwenden? Das
ganze sind och nur ein paar api funktionen die aus jeder sprache
ausführbar sind?
Man sollte ein Protokol verwenden, dass mit einem fehlendes Zeichen
umgehen kann. Nullterminiert ... ist unguenstig. Besser ist die genaue
Anzahl zeichen von vorneweg mitzuschicken und die herunterzuzaehlen.
Einen Timeout sollte man dann auch haben. Im Fehlerfall wird das Packet
neu angefordert.
Ziff schrieb:> Einen Timeout sollte man dann auch haben. Im Fehlerfall wird das Packet> neu angefordert.
Hallo,
genau. Zwar findet er den Fehler dann nie, aber das braucht er auch
nicht mehr. Ich habe noch nie eine ungesicherte Übertragung verwendet,
wahrscheinlich kann man auch mit dem grössten Aufwand die Roh-Fehlerrate
nicht auf exakt Null bringen. Ist ja eigentlich auch seit
Teletype-Zeiten und Pionieren wie Shannon bekannt.
Uraltes, aber unumstössliches Dogma: es GIBT Übertragungsfehler.
Gruss Reinhard
Ziff schrieb:> Man sollte ein Protokol verwenden, dass mit einem fehlendes Zeichen>> umgehen kann.
Es gibt bei mir die Besonderheit, dass das Mikrocontroller Programm auch
über eine Terminal aus direkt bedienbar sein soll. Deshalb hat das
Protokoll weder Längen- noch CRC-Bytes. Das ASCII-0 wird deshalb
verwendet, da es im ANSI Modus des Terminals für den menschlichen
Benutzer unsichtbar ist.
Habe jetzt mit ClearCommError() herausgefunden, dass CE_OVERRUN Fehler
auftreten.
Das Windows SDK meint zu CE_OVERRUN:
> A character-buffer overrun has occurred. The next character is lost.
Was bedeutet das genau? Es gibt nämlich auch noch CE_RXOVER, was so viel
heißt wie:
> An input buffer overflow has occurred. There is either no room in the> input buffer, or a character was received after the end-of-file (EOF)> character.
Was ist der Unterschied zwischen dem character-buffer und dem input
buffer ?
klaus schrieb:> Was ist der Unterschied zwischen dem character-buffer und dem input> buffer ?
char buffer betrifft (wahrscheinlich) den UART selbst. Ein empfangenes
Zeichen wurde nicht abgeholt bevor das nächste eingetroffen war.
input buffer betrifft (wahrscheinlich) die API-Funktion. Du hast z.B.
eine Bufferlänge von 1 für den Empfang angegeben, aber es sind 2 Zeichen
eingetroffen. Deshalb habe ich ja auch gefragt, ob bzw. warum du nicht
einfach den ganzen String bis /0 empfängst. Eine gute Empfangsroutine
sollte mit 0, 1, vielen Zeichen zurechtkommen.
Da der Rechner wahrscheinlich nicht insgesamt zu langsam für den Empfang
ist, müsste sich beides durch Buffer abfangen lassen. UARTs haben heute
fast immer ein FIFO, an das man aber mit der API u.U. nicht rankommt,
ausserdem kann man für COM-Schnittstellen überhaupt die Länge des
Softwarebuffers beim Einrichten angeben.
Gruss Reinhard
Reinhard Kern schrieb:> input buffer betrifft (wahrscheinlich) die API-Funktion. Du hast z.B.> eine Bufferlänge von 1 für den Empfang angegeben, aber es sind 2 Zeichen> eingetroffen. Deshalb habe ich ja auch gefragt, ob bzw. warum du nicht> einfach den ganzen String bis /0 empfängst. Eine gute Empfangsroutine> sollte mit 0, 1, vielen Zeichen zurechtkommen
Du meinst warum ich nicht in Puffern Lese, also z.B. 1k Blöcken? Das ist
deswegen weil die Antworten unterschiedlich lang sein können. Da es kein
Längenbyte gibt kann man die Länge vorher nicht wissen und ReadFile()
würde dann afaik jedes mal erst durch einen Timeout zurückkehren.
Deshalb byte-weises leses bis ASCII-0.
Leider war ich bei der Fehlersuche bisher nicht sehr erfolgreich. Es ist
tatsächlich so, dass das Problem nur am Computer eines Kollegen
auftritt. An meinem kann ich es egal was ich anstelle nicht
reproduzieren. Die Interrupt-Triggerschwelle habe ich bereits ohne
Erfolg verändert. Ebenso ohne Erfolg war der Versuch je 100us Delay
zwischen die Ausgabe jedes Zeichens im Mikrocontroller-Programm zu
setzen. Dies geschah in der Annahme Windows käme nicht rechtzeitig dazu
den Hardware FIFO Puffer zu lesen. Auch am Kabel liegt es nicht.
Außerdem konnte man jetzt beobachten dass teilweise sogar Zeichenmüll
ankommt. Aber wie gesagt nur am Computer meines Kollegen
klaus schrieb:> Du meinst warum ich nicht in Puffern Lese, also z.B. 1k Blöcken? Das ist> deswegen weil die Antworten unterschiedlich lang sein können. Da es kein> Längenbyte gibt kann man die Länge vorher nicht wissen und ReadFile()> würde dann afaik jedes mal erst durch einen Timeout zurückkehren.> Deshalb byte-weises leses bis ASCII-0.
Das ist nicht so. Man kann durchaus empfangen bis /0, zumindest lässt
das WIN32-API das zu. Welche Software du verwendest, hast du uns glaube
ich noch nicht verraten. Nicht mal ob es Windows ist.
ceterum censeo: Datenübertragungen müssen gegen Fehler gesichert werden.
Gruss Reinhard
Reinhard Kern schrieb:> Welche Software du verwendest, hast du uns glaube> ich noch nicht verraten. Nicht mal ob es Windows ist.klaus schrieb:> Die PC>> Anwendung und das Microcontroller System benutzen ein einfaches> Protokoll zum Datenaustausch. PC seitig werden Funktionen der Win32 API> (unter Windows XP) zur Ansteuerung der seriellen Schnittstelle> verwendet.Reinhard Kern schrieb:> Das ist nicht so. Man kann durchaus empfangen bis /0, zumindest lässt> das WIN32-API das zu.
Geht das über das Member "EofChar" in der DCB Datenstruktur? Ist jetzt
das einzige was ich gefunden habe
klaus schrieb:> Reinhard Kern schrieb:>> Das ist nicht so. Man kann durchaus empfangen bis /0, zumindest lässt>> das WIN32-API das zu.>> Geht das über das Member "EofChar" in der DCB Datenstruktur? Ist jetzt> das einzige was ich gefunden habe
Genau. Ich organisiere meine Protokolle meist zeilenweise und gebe da CR
an (LF wird total missachtet). Andere Protokolle haben ETX. Bei dir
müsstest du NUL eintragen, aber das schaltet die Funktion ab - einer der
Gründe, warum es keine so gute Idee ist, NUL als Steuerzeichen zu
verwenden. Was NUL betrifft, war meine Aussage auch falsch, aber mit
jedem anderen Zeichen geht es.
Ein Problem sind die Timeouts bei Handbetrieb. Die Frage ist, darf man
Kaffee trinken gehen und dann die Zeile zu Ende tippen. Bei
automatischen Übertragungen stellt sich die Frage nicht, µC trinken
keinen Kaffee, da reichen Sekundenbruchteile als Timeout.
Ob dir blockweises Lesen nun weiterhilft, ist eine ganz andere Frage.
ceterum censeo: Datenübertragungen müssen gegen Fehler gesichert werden.
Gruss Reinhard