Forum: PC-Programmierung How to flush serial output buffer on linux


von Fump (Gast)


Lesenswert?

OS: Debian 5.0

Ich beobachte bei meinem Programm, das häufig eine Situation eintritt wo 
ein einzelnes Zeichen, das mit write auf /dev/ttyS0 geschrieben wird, 
nicht tatsächlich am Ausgang erscheint. Da auf dieses Zeichen (von einem 
uC) eine Antwort erwartet wird, hängt mein Programm, bzw. läuft durch 
die Retry-Schleife.

Ich meine nun, mal gehört zu haben das die Ausgabe nicht zwangsweise 
sofort während write bzw. kurz danach erfolgt, sondern das sich das 
Zeichen noch eine Weile im Output-Buffer aufhalten kann.

Habt Ihr das auch mal beobachtet?

Ich habe hier http://www.easysw.com/~mike/serial/serial.html#3_1_1 
gelesen, das es einige ...FLUSH... attribute für die tcs Funktionen gibt 
bzw. eine Posix Funktion namens tcflush.
Allerdings steht in der Beschreibung was von "discard", was ich als 
"verwerfen" übersetzen würde. Irgendwie habe ich das im Zusammenhang mit 
Dateien mal so gelernt das alle noch nicht geschriebenen Änderungen 
geschrieben werden aber nicht verworfen. Ist da ein Unterschied zwischen 
Dateien und den Devices?

Deute ich "discard" falsch oder muss ich mein serielles Device anders 
öffnen?

Hier der Code:
1
int InitControllerSerialInterface () {
2
  // fprintf (stderr, "Initialise serial interface to controller\n");
3
  fdc = open (UC_INTERFACE, O_RDWR | O_NOCTTY | O_NDELAY);
4
      
5
  if (fdc == -1)
6
  {
7
    perror("open_port: Unable to open /dev/ttyS0 - ");
8
    return -1;
9
  }
10
  else {
11
    fcntl (fdc, F_SETFL, FNDELAY);
12
13
    struct termios options;
14
15
    tcgetattr (fdc, &options); // Get the current options for the port...
16
17
    cfsetispeed (& options, B9600);  // set baud rate
18
    cfsetospeed (& options, B9600);
19
20
    options.c_cflag |= (CLOCAL | CREAD);  // Enable the receiver and set local mode...
21
    /* 8N1 no parity */
22
    options.c_cflag &= ~PARENB;
23
    options.c_cflag &= ~CSTOPB;
24
    options.c_cflag &= ~CSIZE;
25
    options.c_cflag |= CS8;
26
27
    // options.c_cflag &= ~(CCTS_OFLOW | CRTS_IFLOW);  // no hardware flow control
28
    options.c_cflag &= ~CRTSCTS;
29
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);  // raw input
30
    options.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL);
31
    options.c_oflag &= ~OPOST;  // raw output
32
    tcsetattr (fdc, TCSANOW, & options);  // Set the new options for the port
33
    
34
    return 0;
35
  }
36
}

In diesem Zusammenhang beobachte ich auch, das die glib funktionen zum 
logging ab und zu einfach hängen und nach einer Weile bzw. nach einigen 
neutralen Manipulationen am Terminal wieder gehen.

Hat das auch schon jemand beobachtet? Irgendwelche Hinweise?

von Jonas M. (jonen)


Lesenswert?

ggf. tut es ein "fsync(fdc);" nach dem schreiben

von Fump (Gast)


Lesenswert?

>ggf. tut es ein "fsync(fdc);" nach dem schreiben

Das ist wohl das was ich mal (vor Jahren) als flushen kennengelernt 
habe. Laut Doku ist das: "fsync() transfers ("flushes") all modified 
in-core data of (i.e., modified buffer cache pages for) the file 
referred to by the file descriptor fd to the disk device (or other 
permanent storage device) where that file resides. "

Also wohl nicht auf Zeichendevices anwendbar.

von Fump (Gast)


Lesenswert?

Hmm. Verwirrend. Manche sagen, das fsynch nur mit Dateien oder nur mit 
Block devices geht. Manche empfehlen es auch für Character devices.
Ich kann es ja mal probieren. Aber eine dedizierte Funktion dafür wäre 
mir natürlich lieber.

von Gerry E. (micky01)


Lesenswert?

Fump schrieb:
> Hmm. Verwirrend. Manche sagen, das fsynch nur mit Dateien oder nur mit
> Block devices geht. Manche empfehlen es auch für Character devices.
> Ich kann es ja mal probieren. Aber eine dedizierte Funktion dafür wäre
> mir natürlich lieber.

Dann schau mal nach tcdrain(), vielleicht hilft das ja weiter.
Dein Problem könnte auch damit zusammenhängen, dass Du einen USB zu 
RS232 Konverter hast?!

von Fump (Gast)


Lesenswert?

@ Gerry

>Dann schau mal nach tcdrain(), vielleicht hilft das ja weiter.
Hmm. Könntest Du das bitte erläutern? Nach der Doku blockt das mein 
Programm bis das Zeichen gesendet ist.
Wie ich nun sehe, habe ich versäumt zu schreiben, das ich ein Timeout 
habe, das dann auch zuschlägt. D.h. das Zeichen ist nach 2 bis 5 
Sekunden noch nicht draussen.
Ob ich also blocke bis das Zeichen draussen ist oder warte, ist doch vom 
Programm aus gesehen, das selbe, oder?
Ich bin der Meinung, das das Problem ist, dass das Zeichen nicht 
unmittelbar oder jedenfalls kurzfristig (weniger als 1s) rausgeht.

Hier auch nochmal der Hinweis, dass das nicht grundsätzlich so ist. Es 
gibt Zeiten wo etwa eine Viertelstunde lang das Zeichen sofort rausgeht. 
Dann wieder geht es einige Zeit lang nicht.

>Dein Problem könnte auch damit zusammenhängen, dass Du einen USB zu
>RS232 Konverter hast?!

Nein. Ich benutze keinen USB-RS232-Konverter. Das Gerät hängt direkt an 
einer RS232-Schnittstelle auf dem Motherboard.

von Gerry E. (micky01)


Lesenswert?

tcdrain wartet, bis das letzte Zeichen gesendet wurde.
Anhand des Zeichenrahmens und der Baudrate kann man ausrechnen, wie 
lange das mindestens dauert. Aber das hilft hier anscheinend nicht 
weiter.

Das /dev/ttyS0 oben hatte ich überlesen, sorry.

Was ich mir denken könnte ist dass noch ein zweites Programm die 
Schnittstelle geöffnet hat, zb ein getty. Mach mal ein "lsof | grep 
ttyS0".
Wenn Du das ausschließen kannst käme noch "setserial" in Frage. Mal die 
Einstellungen ausgeben lassen und mit den Settings im BIOS vergleichen.

Mehr fällt mir im Moment nicht ein, vielleicht hat ja sonst noch einer 
ne Idee...

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Gerry E. schrieb:
> Mehr fällt mir im Moment nicht ein, vielleicht hat ja sonst noch einer
> ne Idee...
Hardware Handshake mit floatendem CTS/RTS?

von Gerry E. (micky01)


Lesenswert?

Läubi .. schrieb:
> Gerry E. schrieb:
>> Mehr fällt mir im Moment nicht ein, vielleicht hat ja sonst noch einer
>> ne Idee...
> Hardware Handshake mit floatendem CTS/RTS?

Ja, auch möglich, obwohl: Fump (geiler Name) hat ja den Handshake 
abgeschaltet.

 options.c_cflag &= ~CRTSCTS;

Trotzdem kann es natürlich von Vorteil sein, unbenutzte Eingänge auf ein 
festes Potential zu legen.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Gerry E. schrieb:
>  options.c_cflag &= ~CRTSCTS;
>
> Trotzdem kann es natürlich von Vorteil sein, unbenutzte Eingänge auf ein
> festes Potential zu legen.
Ja das steht im Quellcode... aber weiß das z.B. auch die Gegenstelle? 
Und kriegt es auch der Treiber mit?
Ich hab (unter Windows) auch ab und an das Problem gehabt das er Zeichen 
einfach nicht raussendet wenn ich nur einzelne Zeichen absende...

von Fump (Gast)


Lesenswert?

@ Gerry & Läubi

Das noch ein zweites Programm die serielle Schnittstelle öffnet kann ich 
ausschliessen. Es könnte höchstens sein, dass ich das fragliche Programm 
selbst ab und zu mal mit Ctrl-C abgebrochen habe, so das die 
Schnittstelle an sich noch offen war als ich es noch einmal gestartet 
habe. Allerdings habe ich trotzdem wieder Zeichen empfangen können. Das 
wäre doch dann auch nicht gegangen, oder? Muss direkt mal probieren ob 
open dann fehlschlägt.

setserial kann ich mal probieren. Im Bios steht aber doch nur die 
I/O-Adresse und der Interrupt. setserial gibt ja wesentlich mehr aus. 
Kannst Du bitte kurz erklären, was das bringen kann?

Tatsächlich sind auf PC-Seite die Handshake-Signale nirgendwo 
angeschlossen. Habe aber auch noch nie gehört, das floatende 
Handshake-Signale bei disabletem CTS/RTS Probleme machen. Selbst wenn, 
habe ich ja keine +- 15V am RS232-Stecker um sie auf die 
Handshake-Anschlüsse zu legen.? Kann ich ja mal probieren mit externem 
Netzteil, aber seltsam irgendwie.

Die Gegenstelle kann die Handshakesignale nicht beeinflussen. Sie ist 
nur mit Gnd, TX und RX verbunden und hat selbst keine Handshake-Signale. 
Ist ein ATMega mit Max dran.

von Fump (Gast)


Lesenswert?

Ach ich seh, grad, das ich ja dann eine Fehlermeldung bekomen hätte wenn 
open fehlgeschlagen wäre. Hmmm.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Du kannst einfach RTS und CTS am Stecker zusammenschalten.
Probier mal aus was passiert wenn du einfach fröhlich weitersendest 
(z.B. immer 100 bytes) und dann auf Empfang gehst.

von Oliver (Gast)


Lesenswert?

Hmm,
so wie ich das sehen (hab nett ales durchgelesen ;( ) existiert das 
Problem noch
also bei mir (Debian-5.0.1 / 2.6.26-2-686) kommt alles sofort am Seriell 
raus !
Mit Strg-C brech ich fast immer ab, und der Port bleibt frei !

Ich würde erstmal 'minicom -s' starten den Port einstellen,
und es darüber mal versuchen (zum Fehler ausschließen).
Achso, hast du den Ausgang mal per Oszi oder LED geprüft,
vielleicht liegt es ja an der AVR-Software der Gegenstelle !?!

Hmm, ......

DBD
 Olli

PS: Mein init:
1
  int fd;
2
  if ((fd = open(mdevice, O_RDWR )) >= 0) {
3
4
    tcgetattr(fd,&oldtio); /* save current port settings */
5
    bzero(&newtio, sizeof(newtio));
6
    newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
7
    newtio.c_iflag = IGNPAR;
8
//    newtio.c_oflag = 0;
9
//    newtio.c_lflag = ICANON;
10
11
    newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */
12
    newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
13
    newtio.c_cc[VERASE]   = 0;     /* del */
14
    newtio.c_cc[VKILL]    = 0;     /* @ */
15
    newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */
16
    newtio.c_cc[VTIME]    = 5;     /* inter-character timer unused */
17
    newtio.c_cc[VMIN]     = 1;     /* blocking read until 1 character arrives */
18
    newtio.c_cc[VSWTC]    = 0;     /* '\0' */
19
    newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */
20
    newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
21
    newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
22
    newtio.c_cc[VEOL]     = 0;     /* '\0' */
23
    newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
24
    newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
25
    newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
26
    newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
27
    newtio.c_cc[VEOL2]    = 0;     /* '\0' */
28
    tcflush(fd, TCIFLUSH);
29
    tcsetattr(fd, TCSANOW, &newtio);

von Fump (Gast)


Lesenswert?

@  Läubi

>Du kannst einfach RTS und CTS am Stecker zusammenschalten.

Das kann ich mal probieren.

>Probier mal aus was passiert wenn du einfach fröhlich weitersendest
>(z.B. immer 100 bytes) und dann auf Empfang gehst.

Was vielleicht aus meiner Beschreibung nicht klar hervorgeht:
Es ist so, das ich einmal das Programm starte und es funktioniert 
klaglos. Einzelne Zeichen werden gesendet. Ich erhalte auch die 
passenden Antworten. Das geht so einige Zeit lang, bis ich dann (aus 
anderen Gründen, bin gerade dabei die SW im Zusammenhang mit einer 
Anlage zu testen) anhalte.
Dann starte ich das Programm nochmal und es geht nicht. Ich probier hin 
und her ohne was zu ändern und auf einmal geht es wieder. Ohne das ich 
irgendwas an der SW oder HW geändert habe.

Man könnte vermuten, das das System gerade mit irgendwas ganz dringendem 
beschäftigt ist und für so Kinkerlitzchen wie einzelne Zeichen keine 
Zeit hat.

Ich weiss daher gerade nicht, was die 100 Zeichen bringen sollen. Kannst 
Du das bitte erläutern?

von Gerry E. (micky01)


Lesenswert?

Oliver schrieb:
...
> Achso, hast du den Ausgang mal per Oszi oder LED geprüft,
> vielleicht liegt es ja an der AVR-Software der Gegenstelle !?!

Auch möglich, hast Recht!
Gerade wo ich obigen Beitrag von Fump sehe.

ZB ist es möglich, dass sich die Software des AVR aus dem Tritt bringen 
lässt, wenn die PC-Seite "unterbrochen" wird...

Man kann übrigens solche Fehlersituationen sehr leicht dadurch 
herbeiführen, indem man während der laufenden Kommunikation den 
RS-232-Stecker zieht! Eine gute Software zeichnet sich dadurch aus, dass 
dann eine Fehlermeldung kommt und nach dem Wiederanstöpseln die 
Geschichte weiterläuft als wäre nichts gewesen...

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.