write und read mit select-call
-
Erstmal Danke für die beiden Tipps.
Vielleicht hab ich mich was missverständlich ausgedrückt.
Es soll erstmal, quasi als lokales Echo, das gelesene auf der Konsole ausgeben werden.
Das möchte ich auf jeden Fall per write machen, da diese Funktion später mal abgeändert wird.Die Select-Funktion selber läuft nicht als Schleife.
So hab mal versucht, das eben schnell zu basteln.
Ist nicht lauffähig und noch nicht komplett.#include <stdio.h> /* Standard input/output definitions */ #include <string.h> /* String function definitions */ #include <unistd.h> /* UNIX standard function definitions */ #include <fcntl.h> /* File control definitions */ #include <errno.h> /* Error number definitions */ #include <termios.h> /* POSIX terminal control definitions */ #include <sys/select.h> /* define the timeval structure */ int n, echo; int max_fd; fd_set input; struct timeval timeout; int fd, fn, rd, rn, i; struct termios options; char buf[10]; char buff[8] = "Antwort"; int inialisieren() { fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { /* Could not open the port.*/ perror("open_port: Unable to open /dev/ttyS0 - "); } else { fcntl(fd, F_SETFL, 0); } /* Get the current options for the port...*/ if (tcgetattr(fd, &options) != 0) { printf("Could not read current Parameters!\n"); } else { printf("Read current Parameters (serial)!\n"); } /* Set the baud rates to 38400...*/ cfsetispeed(&options, B38400); cfsetospeed(&options, B38400); /* Enable the receiver and set local mode...*/ options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; /* Set the new options for the port...*/ if (tcsetattr(fd, TCSANOW, &options) != 0) { printf("Could not set Parameters!\n"); //return 0; } else { printf("Set Parameters(serial)!\n"); } //return (fd); //int fn; /* File descriptor for the port */ fn = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY); if (fn == -1) { /* * Could not open the port. */ perror("open_port: Unable to open /dev/ttyUSB0 - "); } else { fcntl(fn, F_SETFL, 0); } /* * Get the current options for the port... */ if (tcgetattr(fn, &options) != 0) { printf("Could not read current Parameters!\n"); } else { printf("Read current Parameters!\n"); } /* * Set the baud rates to 38400... */ cfsetispeed(&options, B38400); cfsetospeed(&options, B38400); /* * Enable the receiver and set local mode... */ options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; /* * Set the new options for the port... */ if (tcsetattr(fn, TCSANOW, &options) != 0) { printf("Could not set Parameters!\n"); return 0; } else { printf("Set Parameters!\n"); } return 0; } void read_serial() { rd = read(fd, buf, sizeof(buf)); printf("%s\n", buf); } void read_usb() { rn = write(fn, buff, sizeof(buff)); printf("%s\n", buff); } void write_serial() { static struct termios old, new; /* Initialize new terminal i/o settings */ void initTermios(int echo) { tcgetattr(0, &old); /* grab old terminal i/o settings */ new = old; /* make new settings same as old settings */ new.c_lflag &= ~ICANON; /* disable buffered i/o */ new.c_lflag &= echo ? ECHO : ~ECHO; /* set echo mode */ tcsetattr(0, TCSANOW, &new); /* use these new terminal i/o settings now */ } /* Restore old terminal i/o settings */ void resetTermios(void) { tcsetattr(0, TCSANOW, &old); } /* Read 1 character - echo defines echo mode */ char getch_(int echo) { char ch; initTermios(echo); ch = getchar(); resetTermios(); return ch; } /* Read 1 character without echo */ char getch(void) { return getch_(0); } /* Read 1 character with echo */ char getche(void) { return getch_(1); } } void select_fd() { /* Initialize the input set */ FD_ZERO(&input); FD_SET(fd, &input); FD_SET(fn, &input); max_fd = (fn > fd ? fn : fd) + 1; /* Initialize the timeout structure */ timeout.tv_sec = 10; timeout.tv_usec = 0; /* Do the select */ n = select(max_fd, &input, NULL, NULL, &timeout); /* See if there was an error */ if (n < 0) perror("select failed"); else if (n == 0) puts("TIMEOUT"); else { /* We have input */ if (FD_ISSET(fd, &input)) { read_serial(); } if (FD_ISSET(fn, &input)) { read_usb(); } } } void main() { inialisieren(); while (1) { select_fd(); write_serial(); } }
Jetzt müsste ich ja, zu jedem File Deskriptor eine Read und eine Write-Funktion erstellen oder?
So, wie im Code schon fürs die Read-Funktion geschehen?Das Einrücken hat Eclipse für mich übernommen
-
Mach mal
max_fd = (fn > fd ? fn : fd) + 1; FD_SET(max_fd, &input);
sieht besser aus
void main() { inialisieren(); while (1) { // HIER ist die Schleife für das select. select_fd(); // write_serial(); // siehe unten } }
Das lesen/schreiben kannst du auch in die select Auswertung nehmen:
... n = select(max_fd, &input, NULL, NULL, &timeout); /* See if there was an error */ if (n < 0) perror("select failed"); else if (n == 0) puts("TIMEOUT"); else { /* We have input */ if (FD_ISSET(fd, &input)) { do_serial(fd); // do_serial kümmert sich um lesen/schreiben } if (FD_ISSET(fn, &input)) { do_usb(fn); // do_usb machen lassen } } ...
Und ein paar Rückgabewerte aus den Funktionen sind auch nicht schlecht.
Irgendwie muss das Programm auch zum Ende kommen können
-
BF0911 schrieb:
#include <unistd.h> /* UNIX standard function definitions */
Was ist denn das?
-
@ mngbd
das implementiert die systemcalls.
Danke für die Tipps.
Ich werd die morgen früh versuchen, die direkt in die Tat umzusetzen und werd mich dann melden.
-
BF0911 schrieb:
@ mngbd
das implementiert die systemcalls.
Aha. Was ist denn das?
-
[hier viele boshafte Fragen und fragwürdige Antworten einsetzen]
Solche Dinge wären im Unix-Forum vielleicht besser aufgehoben:
http://www.c-plusplus.net/forum/f5
-
BF0911 schrieb:
das implementiert die systemcalls.
Sind eigentlich die Unix-Leute schon so verwirrt, dass sie die Implementationen mittlerweile in die Header schreiben?
-
-
So, hatte es hinbekommen.
Nun hab ich den Code so geschrieben, dass ich ihm mit einer Schnittstelle als Argument starten kann.Hier scheint aber nun die read-Funktion zu zicken.
Dort bleibt das Programm hängen, mit und auch ohne die Select-Funktion.#include <stdio.h> /* Standard input/output definitions */ #include <string.h> /* String function definitions */ #include <unistd.h> /* UNIX standard function definitions */ #include <fcntl.h> /* File control definitions */ #include <errno.h> /* Error number definitions */ #include <termios.h> /* POSIX terminal control definitions */ #include <sys/select.h> /* define the timeval structure */ #include <pthread.h> /* threads in Unix */ int n, echo; int max_fd; fd_set input; struct timeval timeout; int fn, i; struct termios options; int inialisieren(char *serial[]) { int fd; fd = open(*serial, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { /* Could not open the port.*/ perror("open_port: Unable to open port - "); } else { fcntl(fd, F_SETFL, 0); } /* Get the current options for the port...*/ if (tcgetattr(fd, &options) != 0) { printf("Could not read current Parameters!\n"); } else { printf("Read current Parameters %s!\n",*serial); } /* Set the baud rates to 38400...*/ cfsetispeed(&options, B38400); cfsetospeed(&options, B38400); /* Enable the receiver and set local mode...*/ options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; /* Set the new options for the port...*/ if (tcsetattr(fd, TCSANOW, &options) != 0) { printf("Could not set Parameters!\n"); //return 0; } else { printf("Set Parameters %s!\n",*serial); } return (fd); } int select_fd(int filedes) { /* Initialize the input set */ FD_ZERO(&input); FD_SET(filedes, &input); // FD_SET(fn, &input); // max_fd = (fn > fd ? fn : fd) + 1; max_fd = filedes + 1; FD_SET(max_fd, &input); /* Initialize the timeout structure */ timeout.tv_sec = 10; timeout.tv_usec = 0; /* Do the select */ n = select(max_fd, &input, NULL, NULL, &timeout); /* See if there was an error */ if (n < 0) perror("select failed"); else if (n == 0) puts("TIMEOUT"); else { /* We have input */ if (FD_ISSET(filedes, &input)) { do_port(filedes); // do_port kümmert sich um lesen/schreiben } // if (FD_ISSET(fn, &input)) { // do_port(fn); // do_port kümmert sich um lesen/schreiben // } } return 0; } int do_port(int filedescriptor) { int j, i; char p[3]; char buf[3]; char t; i = 0; static struct termios old, new; /* Initialize new terminal i/o settings */ void initTermios(int echo) { tcgetattr(0, &old); /* grab old terminal i/o settings */ new = old; /* make new settings same as old settings */ new.c_lflag &= ~ICANON; /* disable buffered i/o */ new.c_lflag &= echo ? ECHO : ~ECHO; /* set echo mode */ tcsetattr(0, TCSANOW, &new); /* use these new terminal i/o settings now */ } /* Restore old terminal i/o settings */ void resetTermios(void) { tcsetattr(0, TCSANOW, &old); } /* Read 1 character - echo defines echo mode */ char getch_(int echo) { char ch; initTermios(echo); ch = getchar(); resetTermios(); return ch; } /* Read 1 character without echo */ char getch(void) { return getch_(0); } // /* Read 1 character with echo */ // char getche(void) { // return getch_(1); // } while (1) { printf("zei"); t = getch(); printf("zeichen gelesen %c size: %d",t,sizeof(t)); write(filedescriptor,&t,sizeof(t)); read(filedescriptor, buf, sizeof(buf)); p[i] = *buf; if (i == 2) { write(filedescriptor, p, sizeof(p)); for (j = 0; j <= i; j++) { } i = 0; } else { printf("Beginn der Else-Anweisung\n"); printf("aktuelles Element %c\n", p[i]); i++; } } return (filedescriptor); } int main(int argc, char *argv[]) { int fd; if (argc >= 2) { printf("Parameter: %s\n", argv[1]); fd = inialisieren(&argv[1]); select_fd(fd); } return 0; }
Jetzt sagte mir ein Kollege, ich soll das mal mit Threads ausprobieren.
Threads hab ich noch nie unter C genutzt.
Hat vielleicht Jemand einen guten Link, ob sich in das Thema einzulesen?
-
Du hast in do_port eine while{1} ohne break oder return. Das bleibt da Programm dann gefangen.
Ich weiß nicht ob du select verstanden hast?
Es zeigt bei dir an, das Daten zum lesen da sind. Wenn du die gelesen und verarbeitet (mit write ausgegeben) hast, musst du aus do_port auch wieder raus, und mit select wieder auf neue Daten warten.
Abgesehen davon, was macht denn das
void initTermios(int echo) { tcgetattr(0, &old); /* grab old terminal i/o settings */ new = old; /* make new settings same as old settings */ new.c_lflag &= ~ICANON; /* disable buffered i/o */ new.c_lflag &= echo ? ECHO : ~ECHO; /* set echo mode */ tcsetattr(0, TCSANOW, &new); /* use these new terminal i/o settings now */ }
und das
void resetTermios(void) {...}
und ...
in int do_port(int filedescriptor) { ?
Funktionen in Funktionen ist in C nicht.
-
Ah, ok.
Dann muss die While-Schleife vor die Select-Funktion, ja?!
static struct termios old, new; /* Initialize new terminal i/o settings */ void initTermios(int echo) { tcgetattr(0, &old); /* grab old terminal i/o settings */ new = old; /* make new settings same as old settings */ new.c_lflag &= ~ICANON; /* disable buffered i/o */ new.c_lflag &= echo ? ECHO : ~ECHO; /* set echo mode */ tcsetattr(0, TCSANOW, &new); /* use these new terminal i/o settings now */ } /* Restore old terminal i/o settings */ void resetTermios(void) { tcsetattr(0, TCSANOW, &old); } /* Read 1 character - echo defines echo mode */ char getch_(int echo) { char ch; initTermios(echo); ch = getchar(); resetTermios(); return ch; } /* Read 1 character without echo */ char getch(void) { return getch_(0); }
Mit dem oben stehenden Code stell ich das Echo der Konsole aus.
und ...
in int do_port(int filedescriptor) { ?
int filedescriptor soll ich eigentlich nur eine Variable sein.
-
BF0911 schrieb:
Ah, ok.
Dann muss die While-Schleife vor die Select-Funktion, ja?!
Ja, wie in meinem ersten Post:
Initialisieren() while {nichtEnde} FD_... select() // warten auf daten lesedaten() machwasmitDaten() SchreibeDaten() endwhile aufraeumen()
Das dürfen alles unterschiedliche Funktionen sein, aber du mußt halt immer wieder select aufrufen. select wartet auf ein Ereignis bei mehreren Kanälen.
BF0911 schrieb:
static struct termios old, new; /* Initialize new terminal i/o settings */ void initTermios(int echo) { ...
Mit dem oben stehenden Code stell ich das Echo der Konsole aus.
und ...
in int do_port(int filedescriptor) { ?
int filedescriptor soll ich eigentlich nur eine Variable sein.
Du hast in der Definition der Funktion do_port(....) { noch andere Funktionen definiert. Anders ... die Funktion do_port umschließt initTermios, resetTermios, getch_, .... . Das ist in C nicht erlaubt.Oder dein Copy&Paste war falsch.
Und do_port sollte nicht filedescriptor zurückgeben sondern einen Status, etwa 0 für Ok, negative Zahlen für Fehler, positive für Aktionen die in der Main Loop gemacht werden sollen (etwa Programmende).
-
So, nachdem alles nicht so klappte, wie ich mir das vorgestellt habe, habe ich mich in Threads eingelesen und das Programm angepasst.
Hier erstmal der Code:
#include <stdlib.h> /* general purpose standard library */ #include <stdio.h> /* Standard input/output definitions */ #include <string.h> /* String function definitions */ #include <unistd.h> /* UNIX standard function definitions */ #include <fcntl.h> /* File control definitions */ #include <errno.h> /* Error number definitions */ #include <termios.h> /* POSIX terminal control definitions */ #include <sys/select.h> /* define the timeval structure */ #include <pthread.h> /* threads in Unix */ int echo; fd_set input; struct timeval timeout; struct termios options; static struct termios old, new; /* Initialisiere neue Terminal i/o-Einstellungen */ void initTermios(int echo) { tcgetattr(0, &old); /* Hole alte Terminal i/o-Einstellungen */ new = old; /* Setze neue Einstellungen gleich alten Einstellungen */ new.c_lflag &= ~ICANON; /* deaktiviere buffering i/o */ new.c_lflag &= echo ? ECHO : ~ECHO; /* Setze echo mode */ tcsetattr(0, TCSANOW, &new); /* Nutze die neuen Terminal-Einstellungen*/ } /* Stelle alte Terminal i/o-Einstellung wieder her */ void resetTermios(void) { tcsetattr(0, TCSANOW, &old); } /* Lese 1 Zeichen - echo definiert echo-mode */ char getch_(int echo) { char ch; initTermios(echo); ch = getchar(); resetTermios(); return ch; } /* Lese 1 Zeichen ohne Echo */ char getch(void) { return getch_(0); } /* Lese 1 Zeichen mit Echo */ char getche(void) { return getch_(1); } pthread_cond_t cond; int inialisieren(char *serial[]) { int fd; fd = open(*serial, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { /* Port kann nicht geöffnet werden*/ perror("open_port: Unable to open port - "); } else { fcntl(fd, F_SETFL, 0); } /* Hole die aktuelle Konfiguration des Ports*/ if (tcgetattr(fd, &options) != 0) { printf("Could not read current Parameters!\n"); } else { printf("Read current Parameters %s!\n", *serial); } /* Setze Baudrate auf 38400...*/ cfsetispeed(&options, B38400); cfsetospeed(&options, B38400); /* Enable the receiver and set local mode...*/ options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; /* Setze die neue Konfiguration für den Port*/ if (tcsetattr(fd, TCSANOW, &options) != 0) { printf("Could not set Parameters!\n"); } else { printf("Set Parameters %s!\n", *serial); } return fd; } void * read1(int *filedes) { char buf[10]; while (1) { /* Lese Schnittstelle und gebe gelesenes Zeichen auf Konsole aus */ read((int*) filedes, buf, sizeof(buf)); printf("%s\n", buf); } } void * write1(int *tmpfd) { int n, i, j; char p[10]; i = 0; printf("bitte geben Sie Ihre Zeichen ein\n"); while (1) { /* Zeichen in Array p an Position [i] schreiben */ p[i] = getch(); printf("%c\n", p[i]); /* wenn Array p voll ist, betrete IF-Schleife */ if (i == 9) { printf("betrete If-Schleife\n"); printf("%s", p); /* Schreibe Array p auf Schnittstelle */ n = write((int*) tmpfd, p, sizeof(p)); /* Zähl-Variable i wieder auf Null setzen */ for (j = 0; j <= i; j++) { } i = 0; } /* Wenn Array p noch Zeichen fehlen, wird die Zählvariable um 1 erhöht */ else { printf("Beginn der Else-Anweisung\n"); printf("aktuelles Element %c\n", p[i]); i++; } } } int main(int argc, char *argv[]) { int fd; /* Thread inialisieren */ pthread_t tr, tw; /*Funktion "inialisieren" wird mit Startparameter gestartet */ fd = inialisieren(&argv[1]); /* Erstelle Thread */ pthread_create(&tr, NULL, read1, (int*) fd); pthread_create(&tw, NULL, write1, (int*) fd); /* Gehe in Thread */ pthread_join(tr, NULL); pthread_join(tw, NULL); return 0; }
Folgendes Problem tritt auf:
Wenn thread mit dem Parameter /dev/ttyUSB0 aufrufe, funktioniert alles perfekt.
Rufe ich Thread mit /dev/ttyS0 auf, wird das Array p nicht auf die Schnittstelle geschrieben.Das kann ich aber nicht ganz nach vollziehen, die Funktionen ja exakt die selben sind, nur halt mir einer anderen Schnittstelle.
-
Du solltest dir unbedingt nochmal call by value und call by reference anschauen.
Und beachte die Warnungen vom Compiler. Wenn du keine hast, schalte sie ein.Mal sehen, also
*argv[] ist ein Array of pointer to char. argv[1] ist demnach ein pointer auf char und das reicht zum initialsieren.
Auch einen void* muss man mit return zurückgeben. Wenn du von der Funktion nichts zurückgeben willst ist es ein einfaches void. (read1 und writel)
Pointer (call by reference) werden bei Arrays benutzt und wenn du modifizierte Werte zurückgeben willst. Sonst reicht ein call by value. (read1 und writel)
Schreib das mal hinter deine globalen Variablen und beachte die Fehler/Warnungen.
int inialisieren(char serial[]); void read1(int filedes); void write1(int tmpfd);
Ich weiß zwar nicht was
pthread_create(&tr, NULL, read1, (int*) fd);
macht, aber (int) fd* sieht nicht gut aus bei int fd
Du kannst aus einem File Deskriptor kein Zeiger auf int machen.
Ansonsten ist Thread-Programmierung kein Thema mehr von Ansi-C.