Forum: PC-Programmierung Linux Serial Port zu langsam


von Peter P. (jre)


Angehängte Dateien:

Lesenswert?

Hallo

Im Anhang ist der Quellcode meine Serial Port Cpp und h Datei.
Ich versuche über einen Mikrocontroller Daten einzulesen.

Dazu sende ich 2 Byte (A101) an den uController. Dieser sendet dann 
sofort 27 Byte zurück.

Mit Hterm habe ich das bereits getestet. Das Funktioniert wunderbar und 
schnell.

Mit dem Cpp Programme bekomme ich nur die Bytes nur eingelesen, wenn ich 
zuvor eine ganze Weile warte uslseep(count * 400000) steht in der 
readByte methode.

Ist die Zeit kürzer werden immer wenigier Byte empfangen. Das kann aber 
nicht richtig sein. Die Daten müssen nach wenigen Millisekunden da sein. 
Jetzt sind das ja fast 4 bis 5 Sekunden die ich warten muss um alle 
Daten lesen zu können. In Hterm klappt das in unter 40ms.

Kann mir da jemaden helfen? Danke!

: Verschoben durch Moderator
von Klaus W. (mfgkw)


Lesenswert?

Eine harte Zeit zu warten und nur dann einmal das Lesen probieren ist 
auch nicht der richtige Weg.
Entweder nimmt man wie du select(), dann aber in einer Schleife bis 
etwas kommt (dann braucht man auch kein sleep vorher - select wartet 
doch selbst, solange nichts passiert.
Oder man liest in einem eigenen Thread blockierend.

von Klaus W. (mfgkw)


Lesenswert?

Jan Remmert schrieb:
> Ist die Zeit kürzer werden immer wenigier Byte empfangen.

Fehlen dann die ersten Bytes oder die letzten?
Wenn die letzten fehlen, dann ist nicht der Linuxrechner zu langsam, 
sondern die Daten tröpfeln halt erst nach und nach ein.
Seriell ist halt nicht so schnell, und wenn du zu früh liest, bekommst 
du natürlich nur die bis dahin übertragenen Daten zu sehen.

In jedem Fall musst du natürlich sehen, ob du genug gelesen hast (auch 
wenn du wie von mir vorgeschlagen select() in einer schleife nimmst), 
und ggf. weiter lesen bis Zeilenende oder was auch immer das Ende deines 
Datensatzes signalisiert.

von Nils (Gast)


Lesenswert?

select wartet nicht bis alle bytes empfangen wurden, sondern nur bis 
etwas empfangen wurde. Dies ist bei einer seriellen Schnittstelle 
meistens nur ein Byte. Durch das Warten hast Du sichergestellt, dass 
alle Bytes die dich interessiert haben auch empfangen waren.

Das Problem ist, dass du ein "framing" brauchst, mit dem deine 
Read-Methode erkennen kann, wann eine komplette Antwort empfangen wurde. 
Möglichkeiten: fixe Länge, Länge zuerst gesendet oder Schlusszeichen 
(z.B. CR).
Dann musst Du nur in einer Schleife mit select empfangen bis die 
komplette Antwort zusammen ist. (und evtl zuviel empfangene Daten für 
später aufbewahren.)

Nils

von Rolf Magnus (Gast)


Lesenswert?

Nils schrieb:
> Das Problem ist, dass du ein "framing" brauchst, mit dem deine
> Read-Methode erkennen kann, wann eine komplette Antwort empfangen wurde.
> Möglichkeiten: fixe Länge, Länge zuerst gesendet oder Schlusszeichen
> (z.B. CR).

Wirklich sinnvoll ist davon eigentlich nur das Schlusszeichen (das dann 
auch in den restlichen Daten nicht vorkommen sollte - zur Not per 
escaping entfernen). Andernfalls hat man keine Möglichkeit, sich zu 
synchronisieren. Wenn dann nur ein einziges Byte verlorengeht, kommt 
danach nur noch Datenmüll an.

von Peter II (Gast)


Lesenswert?

Es sollte auch ohne select gehen, denn du wartest beim select ja eh nur 
auf eine Filehandle. Da kannst du gleich read machen das warten auch 
nur.

Dann kommt mir der code schon ein wenig merkwürdig vor.

> this->max_fileDiscriptor = this->fileDiscriptor+1;

1. Warum die ganzen Variabeln nicht lokal in der funktino machen, was 
sollen sie denn in den Objekt?

2. max_fileDiscriptor sollte doch bestimmt eine anzahl sein, dan kann 
man doch nicht das filehandler reinrechnen.

3. das this finde ich schrecklich, lass es weg und machen am Variabeln 
namen kenntlich das es sich um eine membervariabel handelt.

von Klaus W. (mfgkw)


Lesenswert?

Peter II schrieb:
> 2. max_fileDiscriptor sollte doch bestimmt eine anzahl sein, dan kann
> man doch nicht das filehandler reinrechnen.

select(() braucht einen Wert, der um 1 höher ist als der max. 
vorkommende fd, also hier eins höher als der einzige fd.
Es ist also keine Anzahl.

Trotzdem fände ich ich es ohne Variable sinnvoller; man kann es 
tatsächlich einfach beim Aufruf berechnen lassen (den Rest macht der 
Optimierer :-).

von Klaus W. (mfgkw)


Lesenswert?

Peter II schrieb:
> Es sollte auch ohne select gehen, denn du wartest beim select ja eh nur
> auf eine Filehandle. Da kannst du gleich read machen das warten auch
> nur.

Erstmal richtig; bei select() kann man aber sinnvollerweise noch ein 
Timeout mit angeben und so auf eine unterbrochene Verbindung sinnvoll 
reagieren.
Ein blocking read() würde ewig warten.

von Stefan MM (Gast)


Lesenswert?

Doch das passt so. Schau mal in die manpage zu select.

http://linux.die.net/man/2/select

Es geht um nfds:

nfds is the highest-numbered file descriptor in any of the three sets, 
plus 1.

Beste Grüße,

Stefan

von Peter II (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Erstmal richtig; bei select() kann man aber sinnvollerweise noch ein
> Timeout mit angeben und so auf eine unterbrochene Verbindung sinnvoll
> reagieren.

kann man etwa unter linux keine Timeout für das Filehandle(Comport) 
festlegen?

Klaus Wachtler schrieb:
> select(() braucht einen Wert, der um 1 höher ist als der max.
> vorkommende fd, also hier eins höher als der einzige fd.
> Es ist also keine Anzahl.

ist ja unpraktisch, gut ich arbeite immer mit Poll dort muss die anzahl 
rein.

von Klaus W. (mfgkw)


Lesenswert?

Peter II schrieb:
> Klaus Wachtler schrieb:
>> Erstmal richtig; bei select() kann man aber sinnvollerweise noch ein
>> Timeout mit angeben und so auf eine unterbrochene Verbindung sinnvoll
>> reagieren.
>
> kann man etwa unter linux keine Timeout für das Filehandle(Comport)
> festlegen?

nein, dafür nimmt man genau select()

>
> Klaus Wachtler schrieb:
>> select(() braucht einen Wert, der um 1 höher ist als der max.
>> vorkommende fd, also hier eins höher als der einzige fd.
>> Es ist also keine Anzahl.
>
> ist ja unpraktisch, gut ich arbeite immer mit Poll dort muss die anzahl
> rein.

unpraktisch für den Aufrufer, aber effizienter, weil select() nicht 
soviel tun muß.
Die fd_set sind (Bit-?) Vektoren, und durch die Angabe des maximalen 
Werts muß select() nicht den ganzen Vektor abgrasen, sondern kann bei 
dem Wert aufhören.
Schließlich werden typischerweise nur die untersten der vielen möglichen 
Werte genutzt.

von Peter P. (jre)


Lesenswert?

Danke für eure vielen Antworten

Wenn die Zeit Kürzer ist, fehlen die letzten Daten. Dies liegt aber 
nicht an der Hardware oder dem mikrocontroller, da mit Hterm die Daten 
innerhalb von 40ms vollständig vorhanden sind.

Die Zeit, die ich zu beginn warten muss beträgt wie gesagt fast 4 
Sekunden. Das kann ja nicht sein.

Die Framelänge übergebe ich der Funktion
Ich lese im Polling. Ginge ein Byte verloren müsste die read funktion 
nur irgendwann ein timeout zurück geben.

Wie würde denn jetzt eine Optimierte Read Methode mit select in 
while-chleife und max_filedescriptor ohne Variable aussehen? Die 
While-Schleife muss so lange durchlaufen werden bis count daten gelesen 
wurden oder nach einer Zeit von ca 2 sec abbrechen.

von Klaus W. (mfgkw)


Lesenswert?

Jan Remmert schrieb:
> bis count daten gelesen

das ist wie bereits gesagt nicht sinnvoll, weil du dich nach einem 
kaputten Byte nicht wieder einkriegst.
Ringe dich zu einem Zeilenvorschub durch, dann schreibe ich dir die 
select()-Variante :-)

von Peter P. (jre)


Lesenswert?

Ich übertrage Messdaten als Rohdaten. Diese können jeden wert von 0 bis 
255 annehmen. Daher kann nicht sichergestellt werden, dass ein 
Zeilenvorschub nur am Ende übertragen wird.

von Klaus W. (mfgkw)


Lesenswert?

na, es soll nicht mein Problem sein, wenn etwas aus dem Tritt kommt.

Dann halt so:
1
::std::vector<unsigned char> serialPort::readByte(int count)
2
{
3
  ::std::vector<unsigned char> receivedBytes;
4
5
  fd_set  fd_set_input;
6
  struct timeval timeout;
7
  timeout.tv_sec  = 2;
8
  timeout.tv_usec = 0;
9
10
  while( receivedBytes.size()<count )
11
  {
12
    FD_ZERO( &fd_set_input );
13
    FD_SET( this->fileDiscriptor, &fd_set );
14
15
    int   ret_select = select( this->fileDiscriptor+1, &fd_set_input , NULL, NULL, &timeout );
16
17
    if( ret_select<0 )
18
    {
19
      // select error: TODO handling...
20
    }
21
    else if( ret_select==0 )
22
    {
23
      // timeout expired: TODO handling...
24
    }
25
    else
26
    {
27
      // read:
28
29
      char   read_char;
30
      if( read( this->fileDiscriptor, &read_char, 1 )==1 )
31
      {
32
        receivedBytes.push_back( read_char );
33
      }
34
      else
35
      {
36
        // error reading byte; TODO handling ...
37
      }
38
    }
39
  }
40
41
  return receivedBytes;
42
}

(ungetestet, aus dem Stegreif geschrieben)

von Klaus W. (mfgkw)


Lesenswert?

PS: Wenn man auf mehrere fd warten würde, müsste man nach select vor dem 
read noch prüfen, welcher überhaupt heiß ist.
Weil wir aber nur einen fd eintragen, kann es auch nur einer nach select 
sein.

von Peter P. (jre)


Lesenswert?

Danke sehr für die Mühe.
Es gibt aber Probleme in der Zeile:
FD_SET( this->fileDiscriptor, &fd_set );
mit dem &fd_set beim Compilieren;

von Klaus W. (mfgkw)


Lesenswert?

Muß ja auch &fd_set_input heißen, sorry.

Wie gesagt ungetestet...

von Peter II (Gast)


Lesenswert?

Klaus Wachtler schrieb:

>     FD_ZERO( &fd_set_input );
>     FD_SET( this->fileDiscriptor, &fd_set );

ich würde die dinger noch aus der while schleife rausziehen, macht wenig 
sinn jedes mal neu einzulegen.

Eigentlich müsste man auch noch die Gesamtzeit berücksichtigen, wenn 
jedes byte nach 1,5s kommt, dann hängt das Programm zu lange. Es sollen 
ja alle bytes in 2s gelesen sien.

von frame (Gast)


Lesenswert?

Ich glaube, das Problem ist ein anderes.

Die Schnittstelle wird standardmäßig für die Verwendung als Terminal
initialisiert, wartet also auf CR oder ein timeout.

Anbei ein leicht gekürzter Beispielcode, der jedes empfangene Byte
sofort zurückliefert:
1
int  openReceiver(int iSerLine, int iBaudrate)
2
{
3
    int  iBaud;
4
5
    iBaud = getBaudRateIndex(iBaudrate);
6
    if (iBaud == 0)
7
    {
8
        iBaud = B4800;
9
    }
10
11
    /* open port */
12
    if ((iFD = openLine(iSerLine)) == -1)
13
    {
14
        printf ("FATAL: serial GPS interface open error !\n");
15
        return RETURN_ERROR;
16
    }
17
18
    /* Configure port reading; block read(), until data arrive */
19
    fcntl(iFD, F_SETFL, 0);
20
21
    /* Get the current options for the port */
22
    tcgetattr(iFD, &oldoptions);
23
    memcpy (&options, &oldoptions, sizeof (struct termios));
24
25
    /* Set the baud rates to the defined value */
26
    cfsetispeed(&options, iBaud);
27
    cfsetospeed(&options, iBaud);
28
29
    /* Enable the receiver and set local mode */
30
    options.c_cflag |= (CLOCAL | CREAD);
31
    options.c_cflag &= ~PARENB;                 /* Mask the character size to 8 bits, no parity */
32
    options.c_cflag &= ~CSTOPB;
33
    options.c_cflag &= ~CSIZE;
34
    options.c_cflag |=  CS8;                    /* Select 8 data bits */
35
    options.c_cflag &= ~CRTSCTS;                /* Disable hardware flow control */
36
    /* options.c_cflag |= CRTSCTS;  */          /* enable hardware flow control */
37
38
    /* Enable data to be processed as raw input */
39
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
40
    options.c_iflag &= ~(ICRNL | INLCR | IXON | IXOFF);
41
    options.c_oflag &= ~OPOST;
42
43
    options.c_cc[VTIME] = 0;                    /* no inter-character timer */
44
    options.c_cc[VMIN]  = 1;                    /* number of characters expected */
45
    /* options.c_cc[VEOF] = 4; */
46
47
    /* enable the new options for the port, return o.k. */
48
    tcsetattr(iFD, TCSANOW, &options);
49
    fprintf (stderr, "receiver successful opened\n");    /* Debug */
50
51
    return 0;
52
}
53
54
int  closeReceiver(void)
55
{
56
    tcsetattr(iFD, TCSANOW, &oldoptions);  /* Portrekonfiguration rueckgaengig*/
57
    tcflush(iFD, TCIFLUSH);                    
58
    close(iFD);                            /* seriellen Port schliessen */
59
    return 0;
60
}

So funktioniert es zumindest bei mir.

von Klaus W. (mfgkw)


Lesenswert?

Peter II schrieb:
> Klaus Wachtler schrieb:
>
>>     FD_ZERO( &fd_set_input );
>>     FD_SET( this->fileDiscriptor, &fd_set );
>
> ich würde die dinger noch aus der while schleife rausziehen, macht wenig
> sinn jedes mal neu einzulegen.

Nein, das geht schief (zumindest, wenn man mit mehreren fd arbeitet).
Grund: select() ändert das Feld, indem nach dem Aufruf nur die fds noch 
gesetzt sind, von denen man lesen (bzw. im anderen Feld auf die man 
schreiben kann).
In diesem Fall ist es egal, weil es ja nur einen Eintrag gibt und genau 
der nach select() auch wieder gesetzt sein wird (außer bei timeout, wo 
es keinen mehr interessiert).

Ich hatte es es erst außerhalb der Schleife, dann aber nochmal schnell 
editiert, damit nicht jemand anderes das mal nimmt und mit mehreren fd 
dann auf die Klappe fällt.

>
> Eigentlich müsste man auch noch die Gesamtzeit berücksichtigen, wenn
> jedes byte nach 1,5s kommt, dann hängt das Programm zu lange. Es sollen
> ja alle bytes in 2s gelesen sien.

Ja, wenn man es so will, kann immer die bisherige Zeit von der 
Gesamtzeit abziehen und nur den Rest übergeben.
In aller Regel gehe ich aber davon aus, daß die 4 Byte zügig 
hereinpurzeln und nur am Anfang einmalig verzögert wird, oder es hängt 
komplett; insofern ist das wohl das kleinste Problem.

von Peter P. (jre)


Lesenswert?

Bei mir hängt er jetzt in der Select schleife. es kommt kein Timeout.

Das Beispiel von Frame ist zu stark verkürzt. Weiß da leider nicht was 
wie gemacht wurde.

Noch einmal zur Übersicht was ich brauche. Vieleicht hat jemand einen 
ganz anderen Code:

Beim öffnen die Baudrate und das Device als Sting übergeben.

Meine Sende Methode funktioniert. (Es wird als ein Hexcode als String 
übergeben und es wird alles übermittelt)

Eine Definierte Anzahl an Bytes möglichst schnell lesen und in einem 
unsigned char vector zurück geben. Kommen innerhalb von 2 Sekunden nicht 
genug Bytes einen timeout erzeugen und einen Leeren Vector zurück geben.

von Simon B. (nomis)


Lesenswert?

Peter II schrieb:
> Klaus Wachtler schrieb:
>
>>     FD_ZERO( &fd_set_input );
>>     FD_SET( this->fileDiscriptor, &fd_set );
>
> ich würde die dinger noch aus der while schleife rausziehen, macht wenig
> sinn jedes mal neu einzulegen.

Das darf nicht passieren!

FD_ZERO legt nicht etwa eine Variable an, sondern löscht insbesonders 
auch die Flags für die Filedeskriptoren. Und das muss nach jedem 
select() neu passieren.

Ich finde ja das select()-API total gruselig und würde immer die 
Verwendung von poll() stattdessen empfehlen, da sieht das dann etwa so 
aus:
1
int main (int argc, char *argv[])
2
{
3
  int fd;
4
  struct pollfd pfd[1];
5
6
  fd = open ("/dev/ttyS0", O_RDWR);
7
  if (fd < 0) {
8
    perror ("open");
9
    return -1;
10
  }
11
12
  pfd[0].fd = fd;
13
  pfd[0].events = POLLIN;
14
15
  while (1) {
16
    if (poll (pfd, 1, -1) > 0) {
17
      if (pfd[0].revents & POLLIN)
18
        ; /* read serial port input */
19
    }
20
  }
21
22
  return 0;
23
}

Viele Grüße,
        Simon

von Klaus W. (mfgkw)


Lesenswert?

Jan Remmert schrieb:
> Bei mir hängt er jetzt in der Select schleife. es kommt kein Timeout.

Dann hast du wohl etwas falsch gemacht.

Wenn man select() ein Timeout übergibt, gehe ich jede Wette ein, daß
es auch berücksichtigt wird.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Simon Budig schrieb:

> Ich finde ja das select()-API total gruselig und würde immer die
> Verwendung von poll() stattdessen empfehlen

select() war halt seinerzeit der erste Versuch, IO-Multiplexing
überhaupt zu implementieren.  Davor konnte man nur blockend arbeiten
und musste zwei separate Prozesse für Senden und Empfangen nutzen
(Threads gab's da auch noch keine).  UUCP arbeitete noch auf diese
Weise.

poll() ist nun mittlerweile von Posix standardisiert, insofern
spricht da alles dafür, das zu bevorzugen.

> , da sieht das dann etwa so
> aus:

Aber nicht vergessen, das "canonical input processing" noch
auszuschalten, sonst passiert bei binären Eingabedaten allerlei
Unfug (\x13 wird zu \x10 umgemappt, \x15 löscht den Eingabepuffer
etc. pp.):

Beitrag "Re: Linux Serial Port zu langsam"

von Klaus W. (mfgkw)


Lesenswert?

Simon Budig schrieb:
> Ich finde ja das select()-API total gruselig

naja, schön ist es nicht.
Aber wenn man es verstanden hat auch wieder nicht besonders schwierig.
Zudem halt sehr effizient.

von Simon B. (nomis)


Lesenswert?

Klaus Wachtler schrieb:
> [select()-API]
> Aber wenn man es verstanden hat auch wieder nicht besonders schwierig.
> Zudem halt sehr effizient.

Auch wenn wir uns hier am Rande eines Glaubenskrieges bewegen...   :)

select() ist in der libc AFAIK mittels poll() implementiert. Die 
Friemelei mit den Bitfeldern ist total unbequem und fehlerträchtig 
(siehe oben), besonders wenn man Filedeskriptoren simultan auf 
Lesbarkeit und Schreibbarkeit überwachen will etc. pp.

Ein Array von Poll-Deskriptoren setzt man einmal außerhalb der 
poll()-Schleife auf und kann es dann immer wieder verwenden ohne es neu 
initialisieren zu müssen. Nur wenn sich an der Menge der 
Filedeskriptoren etwas ändert muss man das nochmal anfassen.

Aber egal, jeder verwende das was er mag  :)

Viele Grüße,
         Simon

von Simon B. (nomis)


Lesenswert?

Klaus Wachtler schrieb:
> Ja, wenn man es so will, kann immer die bisherige Zeit von der
> Gesamtzeit abziehen und nur den Rest übergeben.

Das ist übrigens auch noch eine select()-Falle: select() aktualisiert 
das timeout-Argument (Posix: "may"), so dass anschließend die Restzeit 
drinsteht. Wenn man das also vor einem erneuten select()-Aufruf nicht 
wieder den Originalwert reinschreibt, hat man automatisch das Verhalten 
was hier in diesem Fall gewünscht ist, im allgemeinen Fall aber häufig 
lästig ist.

Viele Grüße,
        Simon

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Simon Budig schrieb:
> Das ist übrigens auch noch eine select()-Falle: select() aktualisiert
> das timeout-Argument (Posix: "may"), so dass anschließend die Restzeit
> drinsteht.

Übrigens nur unter Linux.  (Daher auch das "may".)  BSD (wo select()
herkommt) hat das nie getan, und entsprechend auch alle anderen Clones
davon nicht.

von Peter II (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Übrigens nur unter Linux.  (Daher auch das "may".)  BSD (wo select()
> herkommt) hat das nie getan, und entsprechend auch alle anderen Clones
> davon nicht.

ich habe mir auch gerade mal die windows doku durchgelesen, da ist der 
parameter sogar const und wird auch nicht aktualisiert.

(Das Linux immer alles anders machen muss...)

von Peter P. (jre)


Lesenswert?

Ich bekomme das alles nicht hin. Verstehe von dem was ihr hier 
diskutiert auch nur noch die Hälfte. Es wäre super, wenn mir jemand eine 
.cpp und .h Datei mit Funktionierendem Code uploaden kann. Ihr scheint 
euch ja gut auszukennen.

Die startReading methode brauche ich nicht.
Die anderen Motoden sollten von der Schnittstelle her so bleiben wie sie 
sind.

Die ReadByte methode soll eine bestimmte Anzahl Bytes (count) lesen und 
als unsigned char vector zurück geben. Wenn nach 2 Sekunden nicht alle 
Daten gelesen werden konnten soll ein leerer vector zurück gegeben 
werden.

Wäre echt schön wenn mir jemand das macht. Vielen Dank.

von Rolf Magnus (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Simon Budig schrieb:
>> Das ist übrigens auch noch eine select()-Falle: select() aktualisiert
>> das timeout-Argument (Posix: "may"), so dass anschließend die Restzeit
>> drinsteht.
>
> Übrigens nur unter Linux.  (Daher auch das "may".)  BSD (wo select()
> herkommt) hat das nie getan, und entsprechend auch alle anderen Clones
> davon nicht.

Leider, denn eigentlich ist das recht praktisch. Typisches Problem ist, 
daß select wie jeder Systemcall beim Eintreten eines Signals vorzeitig 
zurückkehrt (mit errno=EAGAIN). Wenn man zuverlässig mit einem 
bestimmten Timeout warten will, ist es angenehm, wenn select einem 
gleich die noch zu wartende Zeit einträgt, die man dann so zum nächsten 
Aufruf weiterverwenden kann. Ohne dieses Feature muß man sich vorher mit 
gettimeofday den Startzeitpunkt merken, nach Rückkehr von select prüfen, 
ob die gewünschte Zeit schon abgelaufen ist, Restzeit ausrechnen und die 
an den nächsten select-Aufruf übergeben, und das finde ich nicht 
sonderlich elegant.

von Rolf Magnus (Gast)


Lesenswert?

Rolf Magnus schrieb:
> daß select wie jeder Systemcall beim Eintreten eines Signals vorzeitig
> zurückkehrt (mit errno=EAGAIN)

Sorry, meinte natürlich EINTR.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rolf Magnus schrieb:
> Ohne dieses Feature muß man sich vorher mit
> gettimeofday den Startzeitpunkt merken, nach Rückkehr von select prüfen,
> ob die gewünschte Zeit schon abgelaufen ist, Restzeit ausrechnen und die
> an den nächsten select-Aufruf übergeben, und das finde ich nicht
> sonderlich elegant.

Wobei die Linux-Implementierung letztlich nicht viel anderes tun wird
als genau dies.  Dafür, dass sie das nun nicht-portabel macht, sich
der Aufrufer also ohnehin nicht drauf verlassen kann und den ganzen
Krams nochmal selbst implementieren muss, damit er portabel ist, war
der Aufwand dann für die Katz'.

Letztlich hat poll() das alles überholt (und dabei keinerlei eigene
Vorkehrungen für die Restzeit mitgebracht).

von Peter P. (jre)


Angehängte Dateien:

Lesenswert?

So ich habe endlich eine Klasse geschrieben, die dass macht was ich 
möchte und gut Funktioniert.

Zumindest in der der Testumgebung:
der uController hat bei Epmfang von A1 27 bytes hochgezählt und 
gesendet.
Die 27 Bytes habe ich alle Empfangen.

Das Programm hängt sich auch nicht auf, wenn nichts empfangen wird. 
Allerdings dauert es dann ziemlich lange, da für jedes Byte ein eigener 
timeout abläuft.

Seltsamer weise klappt es noch nicht mit dem richtigen Programm im 
uRechner.
Dem werde ich aber morgen auf den Grund gehen.

von Peter P. (jre)


Lesenswert?

Ich muss mich leider nocheinmal melden. Meine neu geschriebene Klasse 
funktioniert auch nicht richtig.

Es werden manchmal leere Bytes eingelesen, und dadurch verschieben sich 
sie anderen.

von Peter II (Gast)


Lesenswert?

Jan Remmert schrieb:
> Es werden manchmal leere Bytes eingelesen, und dadurch verschieben sich
> sie anderen.

es gibt keine leeren bytes - was meinst du damit?

von Peter P. (jre)


Lesenswert?

Es werden 00 eingelesen, obwohl ich nichts sende

von Peter II (Gast)


Lesenswert?

ist ja klar

[c]
unsigned char ComPort::readByte()
{
  unsigned char buffer[1];
  int n;
    n = read(this->fileDiscriptor,&buffer,1);
  if (n>0)
  {
    return buffer[0];
  }
  else
  {
    return NULL;
  }
}

[\c]

wie willst du die beiden zustände unterscheiden (NULL oder return 
buffer[0])?

von Peter P. (jre)


Lesenswert?

OK Sry ich hatte noch was geändert bzw nutze diese Funktion
1
::std::vector<unsigned char> ComPort::readBytes(int count)
2
{
3
  int n;
4
  unsigned char buffer[1];
5
  ::std::vector<unsigned char> vbuffer;
6
  for(int i = 0; i < count; i++)
7
  {
8
    n = read(this->fileDiscriptor,&buffer,1);
9
    if (n>0)
10
    {
11
      vbuffer.push_back(buffer[0]);
12
    }
13
  }
14
  return vbuffer;
15
}

von Klaus W. (mfgkw)


Lesenswert?

ja, und?

Wenn read() scheitert (z.B. weil etwas zu früh gelesen wird), dann
fehlen dir ein paar Bytes im Feld.

Und warum? Weil du nicht mit select() (oder wie auch immer) darauf 
wartest, daß die Bytes auch gelesen werden können.

Dann hast du aus voller Überzeuigung immer noch keine Synchronisation, 
worauf sich alles weitere verschiebt.

Das wolltest du doch so haben, oder? :-)

von Peter P. (jre)


Lesenswert?

Die Bytes die ich lesen möchte kommen ja direkt hinternander. Wenn es 
nicht zu Störungen kommt müssen auch alle gelesen werden mit dieser 
Funktion. Sonst wird sie irgendwann abbrechen und der zurück gegebene 
Vektor ist zu kurz. Dann werden die Daten verworfen. Dies darf hin und 
wieder vorkommen.

Das Programm soll aus einem Mikrokontroller zyklisch, so schnell es geht 
Messwerte von Batterien und Temepraturfühlern auslesen. Um neue 
Messwerte zu empfangen wird A0,01 an den uRechner gesendet. Darauf hin 
sendet dieser sofart 27 Byte Daten. Byte 3 und 4 beinhalten sogar eine 
Prüfsumme, um die Richtigkeit der Daten sicherzustellen. Unter Windows 
geschiet das einige male in der Sekunde. Dann macht es auch nichts wenn 
hin und wieder Daten verworfen werden. Unter Linux klappt das noch 
nicht.

von Simon B. (nomis)


Lesenswert?

Jan Remmert schrieb:
> Die Bytes die ich lesen möchte kommen ja direkt hinternander. Wenn es
> nicht zu Störungen kommt müssen auch alle gelesen werden mit dieser
> Funktion. Sonst wird sie irgendwann abbrechen und der zurück gegebene
> Vektor ist zu kurz. Dann werden die Daten verworfen. Dies darf hin und
> wieder vorkommen.

Du schreibst leider nirgends, wie Du readBytes() denn nun verwendest. 
Deine Funktion liest im Normalfall weniger als (count) Bytes ein, da Du 
den seriellen Port nonblocking öffnest, d.h. das read() wird regelmäßig 
nichts lesen, da dein Programm die Schleife schneller durchlaufen wird, 
als der Port Daten liefern kann.

Wenn Die aufrufende Funktion also nicht vorsichtig ist und checkt, ob 
genügend Daten zurückgeliefert wurden, können da auch schonmal Nullbytes 
drinstehen.

Zudem wird - da Du Dich ja weigerst select() oder poll() zu verwenden - 
das Programm Busy-Waiting machen, d.h. der Prozess verbraucht jede Menge 
Rechenzeit um nichts zu tun.

Entweder du verwendest select() und poll(), oder Du machst ein blocking 
read. In letzterem Fall blockiert dann das Programm aber solange bis 
tatsächlich ein Zeichen kommt.

Viele Grüße,
         Simon

von Peter P. (jre)


Lesenswert?

Also weniger Zeichen als er soll liest er eigendlich nicht ein.

Das Programm soll auch blocken, da ich erst dann weiter machen will wenn 
alle Daten da sind.

von Simon B. (nomis)


Lesenswert?

Jan Remmert schrieb:
> Das Programm soll auch blocken, da ich erst dann weiter machen will wenn
> alle Daten da sind.

Dann mach auf jeden Fall das O_NDELAY-Flag aus dem open() weg.

Viele Grüße,
        Simon

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.