Forum: PC-Programmierung RS232 Problem


von klaus (Gast)


Lesenswert?

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:
1
0.00106039  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: =  
2
0.00000138  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: 0  
3
0.00000080  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: x  
4
0.00000083  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: f  
5
0.00000081  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: f  
6
0.00000080  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: f  
7
0.00000083  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: f  
8
0.00000081  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: .  
9
0.00065484  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: .  
10
0.00000109  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: #  
11
0.00000082  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: 0  
12
0.00000082  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: :  
13
0.00000082  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1:    
14
0.00000083  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: S  
15
0.00000081  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: u  
16
0.00000081  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: c  
17
0.00065553  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: c  
18
0.00000088  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: e  
19
0.00000082  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: s  
20
0.00000000  tclsh85.exe  IRP_MJ_READ  Serial1           Length 1  
21
0.00000102  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: e  
22
0.00000085  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: s  
23
0.00000088  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: s  
24
0.00000083  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: .  
25
0.00000082  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: .  
26
0.00000082  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: .

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:
1
0.00000096  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: >  
2
0.00000081  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: m  
3
0.00000080  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: e  
4
0.00000080  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: m  
5
0.00000079  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: r  
6
0.00000080  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: e  
7
0.00000082  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: a  
8
0.00000080  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: d  
9
0.00000081  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1:    
10
0.00033035  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: 2  
11
0.00000093  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: 0  
12
0.00000081  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: 9  
13
0.00000085  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: 7  
14
0.00000081  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: 2  
15
0.00000079  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: 1  
16
0.00000084  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: 6  
17
0.00000000  tclsh85.exe  IRP_MJ_READ  Serial1           Length 1  
18
0.00065764  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: c  
19
0.00000136  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: e  
20
0.00000090  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: s  
21
0.00000085  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: s  
22
0.00000087  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: .  
23
0.00000084  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: .  
24
0.00000085  tclsh85.exe  IRP_MJ_READ  Serial1  SUCCESS  Length 1: .

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   
2
0.00000113  tclsh85.exe  IOCTL_SERIAL_GET_BAUD_RATE  Serial1  SUCCESS    
3
0.00000042  tclsh85.exe  IOCTL_SERIAL_GET_LINE_CONTROL  Serial1  SUCCESS    
4
0.00000039  tclsh85.exe  IOCTL_SERIAL_GET_CHARS  Serial1  SUCCESS    
5
0.00000037  tclsh85.exe  IOCTL_SERIAL_GET_HANDFLOW  Serial1  SUCCESS    
6
0.00000042  tclsh85.exe  IOCTL_SERIAL_GET_BAUD_RATE  Serial1  SUCCESS    
7
0.00000029  tclsh85.exe  IOCTL_SERIAL_GET_LINE_CONTROL  Serial1  SUCCESS    
8
0.00000029  tclsh85.exe  IOCTL_SERIAL_GET_CHARS  Serial1  SUCCESS    
9
0.00000029  tclsh85.exe  IOCTL_SERIAL_GET_HANDFLOW  Serial1  SUCCESS    
10
0.00000723  tclsh85.exe  IOCTL_SERIAL_SET_BAUD_RATE  Serial1  SUCCESS  Rate: 115200  
11
0.00000309  tclsh85.exe  IOCTL_SERIAL_SET_RTS  Serial1  SUCCESS    
12
0.00000314  tclsh85.exe  IOCTL_SERIAL_SET_DTR  Serial1  SUCCESS    
13
0.00000178  tclsh85.exe  IOCTL_SERIAL_SET_LINE_CONTROL  Serial1  SUCCESS  StopBits: 1 Parity: NONE WordLength: 8  
14
0.00000048  tclsh85.exe  IOCTL_SERIAL_SET_CHAR  Serial1  SUCCESS  EOF:0 ERR:0 BRK:0 EVT:0 XON:11 XOFF:13  
15
0.00000183  tclsh85.exe  IOCTL_SERIAL_SET_HANDFLOW  Serial1  SUCCESS  Shake:1 Replace:40 XonLimit:2048 XoffLimit:512  
16
0.00000034  tclsh85.exe  IOCTL_SERIAL_GET_TIMEOUTS  Serial1  SUCCESS    
17
0.00000031  tclsh85.exe  IOCTL_SERIAL_SET_TIMEOUTS  Serial1  SUCCESS  RI:0 RM:100 RC:3000 WM:100 WC:3000  
18
0.00000036  tclsh85.exe  IOCTL_SERIAL_SET_BREAK_OFF  Serial1  SUCCESS    
19
0.00000151  tclsh85.exe  IOCTL_SERIAL_PURGE  Serial1  SUCCESS  Purge: TXABORT RXABORT TXCLEAR RXCLEAR  
20
0.00000062  tclsh85.exe  IOCTL_SERIAL_GET_COMMSTATUS  Serial1  SUCCESS

Hat jemand schon einmal ein ähnlich gelagertes Problem ?

Was könnten mögliche Erklärungen sein ?


Viele Grüße
klaus

von Gerry E. (micky01)


Lesenswert?

Ich weiß es nicht genau, aber es könnte sich um einen Framing-Error 
handeln.
Hast Du den im Windowsprogramm abgefangen?

von klaus (Gast)


Lesenswert?

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 ?

von Reinhard Kern (Gast)


Lesenswert?

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

von klaus (Gast)


Lesenswert?

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.

von Thomas B. (Gast)


Lesenswert?

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.

von Peter (Gast)


Lesenswert?

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?

von Ziff (Gast)


Lesenswert?

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.

von Reinhard Kern (Gast)


Lesenswert?

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

von klaus (Gast)


Lesenswert?

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.

von klaus (Gast)


Lesenswert?

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 ?

von Reinhard Kern (Gast)


Lesenswert?

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

von klaus (Gast)


Lesenswert?

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

von Reinhard Kern (Gast)


Lesenswert?

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

von klaus (Gast)


Lesenswert?

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

von Reinhard Kern (Gast)


Lesenswert?

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

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.