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
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
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)
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
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.
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)
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.