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.
@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
staticvoiddo_v24(intiFd)
2
{
3
intiOldTimeout;
4
intiStdio;
5
intok;
6
intiMaxSelect;
7
structtimevaltTimeout;
8
fd_setfdSet;
9
intiSelRet;
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",
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...
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