COM Puffer auslesen unter C, Linux. Verständnisproblem.

#2252526
Lesenswert?

Ich habe ein merkwürdiges Problem beim Auslesen von einer seriellen 
Schnittstelle.
Das Gerät funktioniert einwandfrei, die Übertragung ist physikalisch 
auch in Ordnung, es muss ein Fehler in meinem Programm sein.
Ich möchte die erwartete 38 Byte lange Antwort gerne in der Konsole 
anzeigen lassen. Doch das geschieht nie. Die Anfrage wird zwar gesendet, 
aber ich habe scheinbar beim Auslesen einen Fehler gemacht.
Wenn ich die Schnittstelle mit einem Tool überwache, sehe ich, dass die 
Antwort wie erwartet am Port ankommt.
Kann jemand meinen Fehler finden? Ich komme eigentlich aus der 
High-Level-Programmierung, daher gestehe ich mir ein, dass ich nicht 
jeden benutzten Befehl richtig tiefgreifend verstehe.

 Vielen Dank

Quelltext hängt an
Angehängte Dateien:
#2252545
Lesenswert?

Falko Klepp schrieb:
> Kann jemand meinen Fehler finden? Ich komme eigentlich aus der
> High-Level-Programmierung, daher gestehe ich mir ein, dass ich nicht
> jeden benutzten Befehl richtig tiefgreifend verstehe.

Ich habe das Programm nicht ausprobiert und getestet, von daher können 
meine Tips ins Leere gehen. Aber ein paar Hinweise:

Nach dem write() wartest Du zwei Sekunden und machst dann ein select() 
mit einem timeout von 0. Das ist problematisch, weil die serielle 
Schnittstelle nicht besonders lange für Dich puffert. D.h. Du gibst ihm 
zwei Sekunden Zeit dass die serielle Antwort ins Nirvana rauschen kann 
und willst dann aber möglichst genau zu dem Zeitpunkt wo Du das select() 
machst die Antwort haben.

Es ist deutlich besser, auf das sleep() zu verzichten und dem select 
einen timeout von 2s zu geben. Dann wartet das select() bis zu max. 2s 
(je nach Timeout-Argument) auf die Verfügbarkeit von Daten, die du dann 
unmittelbar im Anschluss mit dem read() abholen kannst.

Und - eher generische Tips:

* arbeite an der Quellcodeformatierung, das Hauptprogramm ist ganz 
schlecht lesbar.

* Filedeskriptoren sind normale Integers (im Gegensatz zu den 
FILE-Pointern der libc), man braucht die nicht by reference zu 
übergeben.

* Ich persönlich verwende lieber poll() statt select(), das ist aber 
eine Geschmacksfrage.

Viele Grüße,
        Simon
Gast #2252547
Lesenswert?

wenn ich das richtig sehen, gehst du davon aus das alle Zeichen 
gleichzeitig kommen, das muss aber nicht so sein. Du musst so lange 
lesen bei bis du alles hast oder das ende an irgendetwas erkennen( z.b. 
\n, fest länge, timeout)
#2252584
Lesenswert?

Danke schonmal,
die Änderung von sleep() auf ein Timeout hat leider nichts bewirkt.
Wo sind die Daten denn überhaupt, wenn ich sie bisher scheinbar nicht 
einlesen kann? Sie müssten ja im Puffer des Geräts sein, ohne abgeholt 
zu werden, oder?

Wie würde ich den Befehl denn schreiben, dass er warten soll, bis 38 
Byte angekommen sind?

Danke
Gast #2252588
Lesenswert?

Falko Klepp schrieb:
> Wie würde ich den Befehl denn schreiben, dass er warten soll, bis 38
> Byte angekommen sind?

anzahl = 0;
while ( anzahl < 38 ) {
   read_zeichen
   anzahl++;
}


aber ob das eine gute idee ist? Was ist wenn mal nicht 38 zeichen kommen 
oder durch einen Fehler du ein versatz bekommst? wird bestimmt ein \n 
nach jeden Datensatz gesendet auf das solltest du warten.
#2253960
Lesenswert?

Ich habe mein Programm nun modifiziert, es funktioniert jetzt besser als 
vorher, aber noch nicht so, wie ich es will.
Wenn ich es starte, wird zwar alles gesendet, aber nichts empfangen. 
Wenn ich eine Instanz meines Programms laufen habem und noch eine zweite 
Instanz starte, funktioniert es aber, und die ankommenden Bytes werden 
dann mehr oder weniger zufällig verteilt von beiden Instanzen abgeholt 
und gelesen (je nachdem welcher Thread gerade dran ist).

Ich habe aber eine Vermutung:
Mein Gerät zieht über die serielle Schnittstelle noch Strom über die 
DTR-Leitung.  Wie kann ich sicher stellen, dass dieser Pin immer an ist?
Ich weiß z.B., dass man unter .NET 4 den DTR-Pin manuell aktivieren und 
deaktivieren kann. Dann müsste es in C ja erst Recht eine derartige Flag 
oder ähnliches geben.

Ich habe mal extern alles protokolliert, was auf dem Port passiert. 
Einmal wenn ich die Daten mit einem Tool schreibe und lese (was immer 
funktioniert), und einmal mit meinem Programm (wo ich nichts empfange).

Die meisten Informationen darin sind wahrscheinlich Overkill, aber 
vielleicht erkennt jemand das Problem.

Ich bin immer noch zuversichtlich das Problem zu lösen.

Gruß

(sorry, habe meinen Quellcode zweimal angehängt, ist beides gleich)
Angehängte Dateien:
#2255025
Lesenswert?

Falko Klepp schrieb:
> Ich habe mein Programm nun modifiziert, es funktioniert jetzt besser als
> vorher, aber noch nicht so, wie ich es will.

Mir fällt auf, dass deine "struct termios" nicht vollständig 
initialisiert ist. Was wäre denn der Wert von c_cc[VTIME]?
Du solltest mit memset() für klare Verhältnisse sorgen.

Da du die alten Einstellungen der seriellen Schnittstelle bei 
Programm-Ende nicht wiederherstellst, kannst du nach einem 
Programmaufruf mit "stty -a /dev/ttyS1" prüfen, was du denn eigentlich 
(an- oder) eingestellt hast.

Ich würde dir empfehlen, zunächst im "non-canonical mode" mit 
"c_cc[VMIN] = 1" und "c_cc[VTIME] = 0" zu arbeiten. Da liest du Zeichen 
für Zeichen ein, verpasst keines und verbrätst wenig Rechenleistung. 
Dafür kannst du aber nichts nebenher machen.
Wenn dann alle Daten ankommen ist zu ggf. überlegen, wie das Einlesen 
der Daten nebenher erfolgen kann, ohne den Rest vom Programm von der 
Arbeit abzuhalten.
Gast #2255050
Lesenswert?

Falko Klepp schrieb:
> Mein Gerät zieht über die serielle Schnittstelle noch Strom über die
> DTR-Leitung.  Wie kann ich sicher stellen, dass dieser Pin immer an ist?

Die Seriellen Zusatzleitungen werden unter Linux mit speziellen ioctl(2) 
Aufrufen manipuliert. "man 2 ioctl" für den generellen Aufruf sollte was 
bringen, "man tty_ioctl" in den Abschnitten TIOCMGET, TIOCMSET, TIOCMBIC 
und TIOCMBIS für das Holen, Setzen bzw. bitweise Setzen und Löschen.

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