Hallo liebe Leute!
Da in Linux ja alles ein "File" ist, habe ich soeben erste Erfahrungen
gemacht, eine serielle Schnittstelle mittels fopen() anzusteuern, nach
dem Prinzip, in eine Datei zu schreiben.
Hier der kleine Beispielcode:
1
#include<stdio.h>
2
3
intmain(void)
4
{
5
6
FILE*pFile;
7
pFile=fopen("/dev/ttyS0","w+b");
8
fprintf(pFile,"Test");
9
fclose(pFile);
10
}
Allerdings gibt es ja noch einige Möglichkeiten mehr, als einfach was
auf "Gut Glück" rauszuschicken.
Da denke ich zum Beispiel an die Baudrate. Wie kann ich in C eine
Baudrate einstellen, bzw. die ganzen "Terminaleinstellungen" wie
"Hardware-Handshake ON/OFF" usw. programmieren?
Oder gibt es eine elegantere Möglichkeit, mit der Seriellen
Schnittstelle zu kommunizieren?
Das Ganze soll unter Linux (Ubuntu) laufen, wie ihr es ja schon am
"dev/ttyS0" sehen könnt :)
Vielen Dank!
Ok Danke,
schreiben kann ich schonmal auf die Schnittstelle :)
Hat Linux einen Art Eingangspuffer, falls ich nicht genau in dem Moment
die Schnittstelle abfrage, wo Daten reinkommen?
Gibt es einen Art Interrupt,sobald Linux was empfangen hat?
Wenn ja, wie kann ich den in C auswerten?
Danke!
Das würde man Unix-gemäß meist so machen, daß man einfach liest.
Solange hängt das Programm - falls das nicht gewünscht ist, macht
man einen zusätzlichen Thread auf.
Der eine liest und blockiert ggf. solange, der andere läuft weiter.
Eine Variante ist select(), aber die meisten Leute mögen das nicht
von Anfang an :-)
Sendung und Empfang gehen über Interrupt-Puffer. Voraussetzung, dass
gepuffert wird, ist allerdings, dass die Schnittstelle geöffnet ist.
Jeder close/open leert den Puffer.
Klaus Wachtler schrieb:> Das würde man Unix-gemäß meist so machen, daß man einfach liest.
Unix-Stil ist wohl eher nur ein Thread, der mit select() alle offenen
Files ueberwacht...
> Eine Variante ist select(), aber die meisten Leute mögen das nicht> von Anfang an :-)
Muss man auch nicht moegen, beisst sich ueber kurz oder lang ziemlich
schnelle mit Abstraktion und OO.
Peter Stegemann schrieb:> Unix-Stil ist wohl eher nur ein Thread, der mit select() alle offenen> Files ueberwacht...
archaischer Unix-Stil; seit Kurzem werden Threads auch schon
mal verwendet :-)
Klaus Wachtler schrieb:> archaischer Unix-Stil; seit Kurzem werden Threads auch schon> mal verwendet :-)
Vor ein paar Monaten sprach mich ein Arbeitskollege an und erklaerte,
Threads seien eine veraltete Technologie der 80er Jahre. Ich kriege
gelegentlich noch ein paar Geschichten erzaehlt, wie "unser" Projekt
unter seiner Hand nun laeuft, aber ich bin zum Glueck weit weg...
Was hier Linux aber definitv fehlt, ist eine Moeglichkeit die serielle
Schnittstelle als normales File zu benutzen aber trotzdem die Parameter
einzustellen. Die "Everything is a file"-Philosophie so richtig ausleben
koennte man, wenn man z.B. "/dev/serx/115200/8/n/1" oeffnen koennte.
Peter Stegemann schrieb:> ... als normales File ...
Damit meinst du FILE* statt int fd?
Das geht doch auch, indem man sie als FILE * öffnet (z.B.
als "/dev/ttyS0" etc.) und sich den fd dazu mit fileno() holt.
Über den kann man dann die ioctl(), tcsetattr() etc. machen.
Oder umgekehrt erst mit open() öffnen, und dann einen Stream
mit fdopen() daraus machen.
Sollte beides gehen.
Peter Stegemann schrieb:> Vor ein paar Monaten sprach mich ein Arbeitskollege an und erklaerte,> Threads seien eine veraltete Technologie der 80er Jahre.
Wenn er ohne Threads eine CPU mit 4 oder 8 Kernen sinnvoll
auslastet, mag er recht haben.
Wahrscheinlich unter Windows kein Problem, weil der oder die
Virenscanner, Windows selbst und allerlei Blinkkram und Services
für jeden Pups genug Last erzeugen.
Klaus Wachtler schrieb:> Peter Stegemann schrieb:>> ... als normales File ...> Damit meinst du FILE* statt int fd?
Ob FILE* oder int fd ist egal.
> Das geht doch auch, indem man sie als FILE * öffnet (z.B.> als "/dev/ttyS0" etc.) und sich den fd dazu mit fileno() holt.> Über den kann man dann die ioctl(), tcsetattr() etc. machen.
Das hat aber nichts mit einem Zugriff zu tun, bei dem die Parameter
ueber den Pfad uebergeben werden. Denn das wuerde z.B. auch mit cat
funktionieren, das ueberhaupt keine seriellen Optionen kennt.
ach so, jetzt verstehe ich.
Dann müsste es zumindest mit einem vorgeschickten stty gehen.
Über den Namen wäre zugegebenermaßen fauler, aber da sind die
möglichen Einstellungen doch vielleicht etwas begrenzt.
Soviele Namen kann man kaum erzeugen, wie man bei RS232
Einstellungen hat.
o.k., hier ein weiterer Teil, der das lesen per select macht.
Auch wenn das von einigen als archaisch angesehen wird (o.k. das hier
ist auch schon >10 Jahre alt...), erspart es meiner Meinung nach bei
kleinen Programmen wie hier den ganzen Thread-Aufwand, und man hat nicht
die Probleme, die man sich mit Threads einhandelt...
Guten morgen Jungs!
Ich habe es jetzt erstmal mit der <termios.h> gemacht.
Die Funktion write() erfüllt schon mal ihre Dienste (schreibt mir auf
die virtuelle Schnittstelle).
mit read() habe ich wiegesagt noch ein Verständnisproblem.
Gibt es jetzt in Linux einen Eingangspuffer für die RS232 oder nicht?
Ich würde dann einfach einen Thread programmieren, der alle paar hundert
Millisekunden mal auf den Empfangspuffer lauscht.
DAnke!
LynUx schrieb:> Gibt es jetzt in Linux einen Eingangspuffer für die RS232 oder nicht?
Nein, zumindest nicht mit nennenswerter Größe.
Du solltest in dem zusätzlichen Thread nicht alle soundsolang
nachschauen, sondern einfach lesen, was geht und wegschreiben
(globaler Puffer, oder was du halt machen willst).
Ob du wartest oder gleich blockierend liest, ist doch egal.
Es hängt so oder so dieser Thread.
Wozu das Warten?
Noch dazu nach einer Endlosschleife, wo du nie ankommst?
Mach dir eher Gedanken, wie man Kollisionen beim Zugriff
auf die globale Variable vermeidet.
Solange das Abspeichern nicht atomar ist, sollte das restliche
Programm nicht lesen, während der Thread schreibt.
Das könnte dann so aussehen:
1
globale Variable Input;
2
Mutex für Input;
3
4
Threadfunktion
5
{
6
while( immerundewig )
7
{
8
lokale Variable tmp;
9
tmp = read();
10
Mutex für Input setzen;
11
Input = tmp;
12
Mutex für Input freigeben;
13
}
14
}
Das Warten passiert automatisch im read(), wenn nichts da ist.
(Es sei denn, man setzt den fd explizit auf non blocking, aber
das macht kaum Sinn.)
Mutex bedeutet doch, dass ich während ich mit meinem Thread auf die
glob. Variable zugreife, theoretisch von einem anderen Thread (oder
Routine) darauf zugreifen kann und es dabei crasht richtig?
> ... und es dabei crasht richtig?
Mit einem Mutex versucht man genau das zu verhindern.
Beispiel ohne:
1
intvariable;
2
3
voidthread1()
4
{
5
while(true)
6
{
7
variable=variable+3*(variable-5);
8
printf("%d\n",variable);
9
sleep(1);
10
}
11
}
12
13
voidthread2()
14
{
15
while(true)
16
{
17
if(es_ist_Zeit_fuer_einen_Neustart)
18
{
19
variable=1;
20
}
21
}
22
}
Hier könnte mit etwas Pech die Variable von thread2() zu 1
gesetzt werden, während thread1() mitten in der Berechnung ist.
Dann wäre der ausgegebene Wert ein Mischmasch aus neuem und altem
Wert.
Das gilt es zu vermeiden!
Stell dir den Mutex einfach als globales Flag vor, der auf true oder
false bzw. 1 oder 0 stehen kann.
Man vereinbart jetzt einfach, daß das Flag auf 0 (false) stehen soll,
solange niemand auf die Variable zugreift, und auf 1 (true), wenn
jemand zugreift.
Weiterhin hält sich jeder einfach an die Regel, nicht einfach
zuzugreifen, sondern zu warten bis das Flag auf 0 ist, es dann
auf 1 setzt, jetzt erst die Variable anfasst und dann wieder das
Flag zu 0 setzt:
1
intvariable;
2
boolflag_fuer_variable;
3
4
voidthread1()
5
{
6
while(true)
7
{
8
9
// Mutex setzen:
10
while(flag_fuer_variable)
11
{
12
// nichts tun, bis keiner auf variable zugreift
13
}
14
flag_fuer_variable=true;
15
16
// jetzt die variable verwenden (möglichst schnell, um den Mutex
17
// bzw. das Flag nicht zu lange zu blockieren):
18
variable=variable+3*(variable-5);
19
printf("%d\n",variable);
20
21
// Mutex wieder freigeben:
22
flag_fuer_variable=false;
23
24
// jetzt könnte wieder jemand anders auf variable zugreifen...
25
26
sleep(1);
27
}
28
}
29
30
voidthread2()
31
{
32
while(true)
33
{
34
if(es_ist_Zeit_fuer_einen_Neustart)
35
{
36
// Mutex setzen:
37
while(flag_fuer_variable)
38
{
39
// nichts tun, bis keiner auf variable zugreift
40
}
41
flag_fuer_variable=true;
42
43
// jetzt die variable verwenden (möglichst schnell, um den Mutex
44
// bzw. das Flag nicht zu lange zu blockieren):
45
variable=1;
46
47
// Mutex wieder freigeben:
48
flag_fuer_variable=false;
49
}
50
}
51
}
Damit hat man das Problem umgangen. Jeder der Threads wartet
vor dem Zugriff auf die Variable höflich darauf, daß der andere
damit fertig ist.
Zumindest glaubt man, das Problem umgangen zu haben - solange
man nicht genau hinsieht.
Dann würde man nämlich feststellen, daß man das Problem nur
etwas verschoben hat: es könnte thread 1() laufen, in der
Schleife feststellen, daß das Flag gerade frei ist und
mit etwas Pech genau jetzt unterbrochen werden von thread2().
Der stellt auch fest, daß es frei ist und macht also weiter,
ebenso wie thread1(), wenn er wieder Rechenzeit bekommt.
Es muß also dafür gesorgt werden, daß die Abfrage "ist der
Mutex gerade frei" und das Setzen, falls ja, in einem
atomaren Vorgang stattfindet, also nicht von einem anderen
Thread unterbrochen werden kann.
Weil es dazu in C oder C++ keinen sicheren portablen Weg
gibt, braucht man die mithilfe des OS.
Es gibt dazu passende Bibliotheken, ich verwende meist
für Threads die libpthread mit den Posix-Threads und
darin ist auch gleich ein Mutex enthalten.
Dahinter steckt nichts weiter als ein logisches Flag
ja/nein und die Möglichkeit, das atomar abzufragen und zu
setzen.
Nur heißt es dann nicht mehr Flag, sondern Mutex.
Das Setzen heißt Sperren und das Löschen heißt Freigeben.