Hallo, ich habe Probleme bei dem Ansprechen einer seriellen Schnittstelle (Linux, SUSE 10.3). Es handelt sich hier um eine Motoransteuerung, es kommen 6Byte von der Motorsteuerung und es werden zwei Byte zur Motorsteuerung gesendet (19200N2). Der Datenstrom von der Motoransteuerung kommt kontinuierlich. Das Senden und Empfangen klappt erstmal zuverlässig. Allerdings bekomme ich immer 24Byte auf einmal. Ich polle auf die serielle Schnittstelle und meist liegen 0Byte im Empfangsspeicher und dann auf einmal 24Byte. Ich habe es schon den Realtime-Kernel, der die Timeslots verkleinert hat, versucht. Keine wirkliche Besserung (Hier liegen dann 16Byte im Speicher). Unter Dos klappt es hervorragend. ;-) Kann ich irgendwie die serielle Schnittstelle Parametrieren, dass sie mir ab einer bestimmten Anzahl von empfangenen Bytes diese Daten zur Verfügung stellt? Gruß Samuel Schmidt
Das Problem bei den 6-Byte-Nachrichten besteht darin, dass der 16550-Schnittstellenbaustein mit seinen 16-Byte-Hardwarepuffer normalerweise so konfiguriert ist, dass er erst bei fast vollem Hardwarepuffer, d.h. bei einem Füllstand von 14 Bytes einen Interrupt auslöst. Dies bedeutet, dass frühestens während des Eintreffens der dritten 6-Byte-Nachricht ein Interrupt ausgelöst wird. Um zu vermeiden, dass einzelne Bytes ewig ungelesen im Hardwarepuffer verbleiben, fragt der Treiber zusätzlich alle etwa 10 ms den Puffer ab. Dies bedeutet aber, dass kurze Nachrichten im ungünstigsten Fall erst nach 10 ms an die Anwendung übergeben werden. Will man nicht so lange warten, kann man das Low-Latency-Flag im Treiber setzen. Dann wird die Abfrageperiode auf etwa 1 ms reduziert. Die Reaktionszeiten werden dadurch besser, allerdings wächst die Prozessorbelastung durch die häufigeren Abfragen. Das Low-Latency-Flag kann entweder in der Anwendung oder mit dem Setserial-Tool gesetzt werden: setserial /dev/ttyS0 low_latency Eine andere Möglichkeit besteht darin, den Pufferfüllstand, bei dem ein Interrupt ausgelöst wird, von 14 auf einen kleineren Wert herabzusetzen. Der 16550 unterstützt Werte von 14, 8, 4 und 1. Wie man dies auf anständige Weise umstellt, weiß ich nicht. Der Parameter rx_trigger von setserial funktioniert für den 16550 leider nicht. Ein Work-Around besteht darin, dem Treiber einen 8250 mit 1-Byte- Puffer statt des 16550 vorzugaukeln. Dies geht mit setserial /dev/ttyS0 uart 8250 Damit wird schon beim ersten empfangenen Byte ein Interrupt ausgelöst, so dass die Anwendung augenblicklich reagieren kann. Ein Nachteil besteht in der höheren Anzahl ausgelöster Interrupts, die bei großen übertragenen Datenmengen die Prozessorbelastung merklich erhöhen können, was aber bei deiner Anwendung wahrscheinlich keine große Rolle spielt. Dadurch, dass durch den 8250-Trick der Empfangspuffer von 16 auf 1 Byte reduziert wird, steigt die Gefahr, dass eintreffende Datenbytes verloren gehen, wenn die Interrupts nicht rechtzeitig bearbeitet werden können, weil bspw. gleichzeitig viele andere Interrupts (Timer, Ethernet usw.) eintreffen. Aber auch das sollte bei den heutigen schnellen Prozessoren und üblichen Baudraten bis 115200 nicht zu Problemen führen. Ich würde an deiner Stelle einfach die beiden genannten Optionen jeweils alleine und in Kombination ausprobieren und schauen, wie die Ergebnisse ausfallen.
Hi, vielen Dank für die ausführliche Antwort. Werde versuchen das morgen Abend umzusetzen und werden dann berichten ob es klappt. Viele Grüße Samuel
Warum die serielle Schnittstelle pollen? Das erzeugt nur unnötig Prozessorlast. Unter Linux/Unix macht man sowas mit "select"...
@Bernhard [quote]Unter Linux/Unix macht man sowas mit "select"[\quote] Äh, geht's etwas ausführlicher? und wenn es bloss nen link ist, der einem weiterhilft - weil "google doch mal nach SELECT" iss glaub ich nicht wirklich witzig... ragosti
Hi, "select" ist ein Standard Unix Systemaufruf (mache mal "man select"). Damit kann man auf ein Ereignis bei einem (oder mehreren) Filedeskriptor warten, und gleichzeitig einen Timeout angeben (und nachdem in Unix praktisch alles ein File ist, geht der fast immer...). Das Programm "steht" dann solange, bis ein Ereignis eintrifft oder der Timeout abgelaufen ist, dann kehrt der "select" Aufruf zurück. Der Vorteil gegenüber polling ist, daß es keine Rechenzeit braucht, da der Kernel die Kontrolle in der Zeit hat. Hier mal ein Beispiel aus einem Programm von mir, das mit einem 8051 Kontrollerboard über RS232 kommuniziert und dabei einzelne Tastendrücke und die serielle Kommunikation abhandelt. D.h. in der Routine wartete die "select" auf V24 und auf Tastendruck und reagiert entsprechend.... Ich könnte Dir auch das ganze Programm schicken, das müsste ich nur erst etwas aufräumen (die ganzen "#if 0" Blöcke rausnehmen...). Gruß, Bernhard
1 | static void do_v24 (int iFd) |
2 | {
|
3 | int iOldTimeout; |
4 | int iStdio; |
5 | int ok; |
6 | int iMaxSelect; |
7 | struct timeval tTimeout; |
8 | fd_set fdSet; |
9 | int iSelRet; |
10 | |
11 | /* "open" terminal -> get filedescriptor to it */
|
12 | gfStdio = fopen (ctermid (NULL), "r+"); |
13 | if (gfStdio == NULL) { |
14 | fprintf (stderr, "\nCan not open terminal (errno: %s) !\n", strerror (errno)); |
15 | return; |
16 | }
|
17 | |
18 | iStdio = fileno (gfStdio); |
19 | if (iStdio < 0) |
20 | {
|
21 | fprintf (stderr, "Error: Invalid stream for terminal (errno %d: %s)!\n", |
22 | errno, strerror (errno)); |
23 | return; |
24 | }
|
25 | |
26 | tcgetattr (fileno (gfStdio), &gstTermOld); |
27 | |
28 | gstTerminal = gstTermOld; |
29 | gstTerminal.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON); |
30 | |
31 | /* Set maximum wait time on input */
|
32 | gstTerminal.c_cc[VTIME] = 0; |
33 | |
34 | /* Set minimum bytes to read */
|
35 | gstTerminal.c_cc[VMIN] = 0; |
36 | |
37 | tcsetattr (fileno (gfStdio), TCSAFLUSH, &gstTerminal); |
38 | |
39 | /* set new timeout on V24, we want responsive system */
|
40 | iOldTimeout = fnSetTimeout (iFd, 0); /* no wait */ |
41 | |
42 | ok = TRUE; |
43 | |
44 | do
|
45 | {
|
46 | FD_ZERO (&fdSet); |
47 | FD_SET (iFd, &fdSet); |
48 | FD_SET (iStdio, &fdSet); |
49 | iMaxSelect = (iFd > iStdio) ? iFd : iStdio; |
50 | |
51 | /* -- set max. waittime -- */
|
52 | tTimeout.tv_sec = 1; |
53 | tTimeout.tv_usec = 500000; |
54 | |
55 | errno = 0; |
56 | |
57 | iSelRet = select (iMaxSelect + 1, &fdSet, NULL, NULL, &tTimeout); |
58 | |
59 | if (iSelRet > 0) |
60 | {
|
61 | /* -- we got something on stdin ?? -- */
|
62 | if (FD_ISSET (iStdio, &fdSet)) |
63 | {
|
64 | /* stdin: someone hacked the keyboard */
|
65 | ok = fnHandleKeyboard (gfStdio, iFd); |
66 | }
|
67 | |
68 | /* -- we got something from serial line -- */
|
69 | if (FD_ISSET (iFd, &fdSet)) |
70 | {
|
71 | /* handle V24 input here */
|
72 | fnHandleInput (iFd, gfStdio); |
73 | }
|
74 | }
|
75 | else if (iSelRet < 0) |
76 | {
|
77 | /*
|
78 | ** Check if SIGINT was received. This is handled further down,
|
79 | ** so we ignore the error from select in that case.
|
80 | */
|
81 | if (errno != EINTR) |
82 | {
|
83 | fprintf (stderr, "Error reading from select: (%d) %s\n", |
84 | errno, strerror (errno)); |
85 | break; |
86 | }
|
87 | }
|
88 | else
|
89 | {
|
90 | /* just timeout */
|
91 | wasEsc = 0; |
92 | }
|
93 | } while (ok && gRun); |
94 | |
95 | /* reset old timeout */
|
96 | fnSetTimeout (iFd, iOldTimeout); |
97 | |
98 | tcsetattr (iStdio, TCSAFLUSH, &gstTermOld); |
99 | fclose (gfStdio); |
100 | }
|
Oder man benutzt poll ("man poll"), was im Gegensatz zu seinem Namen auch nicht pollt, sondern so ziemlich das selbe macht wie select. CU
bzw. nur ein Wrapper um select ist....aber sobald man auf mehrere Ereignisse reagieren will braucht man eh select.
> Wrapper ??? Das ist aber schon ne Ewigkeit her. > mehrere Ereignisse ??? Wo ist das Problem? http://www.rt.com/man/poll.2.html
Irgendwie hatte ich die poll-syntax anders im Hinterkopf.... Na ja, nie selbst benutzt... Hat das einen Vorteil gegenüber select, ausser daß der Code vielleicht kürzer wird? Wie ist das Verhalten bei Signalen? Gibts das auf allen Unixen (ich muß normalerweise konform zu Linux, HP-UX, OSF1 und AIX arbeiten)?
Bernhard M. wrote: > bzw. nur ein Wrapper um select ist....aber sobald man auf mehrere > Ereignisse reagieren will braucht man eh select. Ist eher anders herum... poll unterstützt beliebig viele Handles auf die gleichzeitig gewartet werden kann, select ist da wg. den Bitlisten-Parametern meistens auf handles < 1024 beschränkt. Und inzwischen (seit kernel 2.5.44) gibts noch "epoll", was nochmal ne ecke besser als poll sein soll...
Wie kommt es zu den 24 Bytes, die zu Beginn von snowyrain als Blockgröße genannt wurden ?
Hallo vielen Dank für die vielen Antworten. Ich habe bei dem jetzigen Problem mit "setserial /dev/ttyS0 low_latency" gearbeitet. Sehe allerdings ein, das ich mich ganz dringend mit dem Select Befehl auseinandersetzen muss. Das wichtigen Zeilen aus dem Beispiel von "Bernhard M." meine ich Verstanden zuhaben. Vielleicht werde ich meinen jetzigen Code auch umbauen, aber ich muss zwischendurch auch noch arbeiten ;-). Die 24Byte kann ich mir auch nicht erklären. Mein Linux System ist allerdings sehr schlecht von mir administriert. Vielleicht greifen da andere Tasks zwischen. Ich habe keine wirkliche Ahnung von Linux und dessen genauen Interna. Vielen Dank für super Antworten Gruß Snowyrain
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.