Forum: PC-Programmierung COM Puffer auslesen unter C, Linux. Verständnisproblem.


von Falko K. (falko1985)


Angehängte Dateien:

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

von Simon B. (nomis)


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

von Peter II (Gast)


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)

von Falko K. (falko1985)


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

von Peter II (Gast)


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.

von Falko K. (falko1985)


Angehängte Dateien:

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)

von Konrad S. (maybee)


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.

von A.H. (Gast)


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.

von Falko K. (falko1985)


Lesenswert?

Danke,
es hat jetzt funktioniert. Ich konnte den Pin aktivieren mit
1
ioctl(fd_ser, TIOCMSET, TIOCM_DTR);

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.