Hi, und zwar bin ich grad dabei einen Temperatursensor, der über einen
atmel abgefragt wird mit Linux über UART zu steuern.
der atmel funktioniert soweit und sendet auch. Er sendet den 11-bit wert
des sensors zerteilt auf ein high-byte und ein low-byte. dies
funktioniert auch (serial terminal).
Leider will das Linux Programm nicht so recht mitspielen. kriege es zwar
teils zum laufen, aber ab einem temperaturwert von 24,9/ 25 grad
bzw übergang von h-byte=2, l-byte=255 -> h-byte=3, l-byte=0
schmeißt er nur noch misst raus also 0, -25, -50 grad
als UPATH wird /dev/ttyUSB0 verwendet
könnt ihr mir weiterhelfen ?
wie man sieht, ignoriere ich auch den alten Zustand komplett. Ich finde
die Idee völlig falsch, warum soll mein Programm in irgendeiner Form
alte Einstellungen übernehmen? Je nachdem, was vorher gelaufen ist,
passen die oder nicht.
Wie greifst Du denn auf den Port zu?
Die Verwendung eines seriellen Ports unter Unix/Linux ist nicht ganz so
trivial, wie das sonst mit Dateien zu erwarten ist. Das liegt daran,
dass der serielle Port ursprünglich für den Anschluss von
Nutzerterminals gedacht war. Dazu implementiert der Treiber unter
anderem einfache Edier- und Prozesssteuerfunktionen, die auf bestimmte
Steuerzeichen reagieren: BS, DEL, Ctrl-C, Ctrl-Z zum Beispiel. Sollen
binäre Daten übertragen werden, so müssen diese Sonderfunktionen vorher
sauber abgeschaltet werden, sonst gibt es mit Sicherheit frühe oder
später Probleme. Google mal nach termios.
Alternativ kann man sich auf ASCII-Text beschränken. Dann aber daran
denken, dass der serielle Treiber eine komplette Eingabezeile bis zum
Zeilenendezeichen puffert, bevor er sie an das lesende Programm
weiterleitet.
wow also mit folgenden einstellungen scheint es ganz gut zu laufen jetzt
UART.c_iflag = IGNBRK|IGNPAR|IGNCR;
UART.c_cflag = CLOCAL|CREAD|CS8;
UART.c_oflag = 0;
UART.c_lflag &= ~(ISIG|ICANON|IEXTEN|ECHO|CLOCAL);
UART.c_cc[VMIN] = 1;
UART.c_cc[VTIME] = 0;
zur weiteren Information. die werte sollen dann später in eine Datei
geschrieben werden und per PHP ausgelesen werden und per webserver
angezeigt werden
vielen dank schon mal :-)
ich kenne termios. und kenne mich auch etwas mit treiberprogrammierung
aus also so is es net. nur die flags sind mir bei termios nicht ganz
geläufig.
das weiß ich selbst das es nicht nur ne datei is sondern n Block-device
wir haben termios im letzten semester bereits benutzt für ne art
home-automatisierung eben auch in die richtung nur das es verschiedene
hauptknoten gab, die über das Filesystem die Sensordaten untereinander
tauschen konnten
Sorry, ich hatte übersehen, dass der Quellcode schon dabei ist. Das
hätte meine Frage natürliche beantwortet. Ich sehe aber noch einige
potentielle Probleme. Das dringendste scheint mit die Leseschleife zu
sein:
1
for(i=0;i<2;i++)
2
{
3
seek=read(fd,(&raw_data[i]),sizeof(raw_data));
4
if(seek == -1){printf("Error reading data from UART\n"); return -1;}
5
}
Offensichtlich versuchst Du, zwei Zeichen einzulesen, in jedem Durchlauf
eines.
1. Problem: Beim zweite Durchlauf ist der Eingabepuffer ein Zeichen
kleiner, Du gibst aber immer noch die gleiche Puffergröße an. Das ist
definitiv ein Fehler der potentiell zu einem Buffer-Overflow führen
kann.
2. Problem: Es ist keinesfalls garantiert, dass read bei jedem Aufruf
ein einzelnes Zeichen zurückliefert. Jede beliebige Anzahl <=
sizeof(raw_data) ist möglich, je nach dem, wie viele Zeichen gerade
verfügbar sind. Da das O_NDELAY Flag beim Öffnen gesetzt wurde, ist die
Null explizit eingeschlossen. Es ist also alles zwischen 0-2 möglich.
Die Anzahl tatsächlich gelesener Zeichen wird als Ergebnis
zurückgeliefert, aber nicht ausgewertet. Es müsste also mindestens so
aussehen:
1
for(i=0;i<2;i+=seek)
2
{
3
seek=read(fd,(&raw_data[i]),sizeof(raw_data)-i);
4
if(seek == -1){printf("Error reading data from UART\n"); return -1;}
5
}
Auf das O_NDELAY kann man auf jeden Fall verzichten, es macht im gegeben
Fall – soweit ich das beurteilen kann – ohnehin keinen Sinn. Dann könnte
man read alternativ auch einfach mit der Puffergröße 1 aufrufen, was
aber deutlich weniger elegant ist.
also als größe gebe ich an sizeof(raw_data[i] damit sollte ja immer 1
byte gemeint sein und damit ok.
was das ONDELAY macht weiß ich ehrlich gesagt gar nicht. ich hab nur das
was wir letztes semester benutzt haben wieder hergenommen.
deshalb hab ich mich ja ans forum gewandt, weil mehr augen sehen mehr
als ich allein.
ganz versteh ich die probleme noch nicht die du sonst beschrieben hast,
aber das immer mal n byte verloren gehn kann leuchtet mir noch ein
Zwei Bücher wären vielleicht hilfreich: ISBN 978-0139376818 und ISBN
978-3446152021 . Beides gute Bücher, sie enthalten Grundlagen und
Details zu den Tiefen der Unix-Programmierung.
> wie man sieht, ignoriere ich auch den alten Zustand komplett. Ich finde> die Idee völlig falsch, warum soll mein Programm in irgendeiner Form> alte Einstellungen übernehmen? Je nachdem, was vorher gelaufen ist,> passen die oder nicht.
Normalerweise werden Programme aus einer Shell gestartet. Standard-Ein-
und Ausgabe der Shell sind mit einem Terminal verbunden. Die Anwendung
läuft in einem Child-Prozess und erbt als solcher die File-Deskriptoren
der Shell. Nach dem Start tritt die Shell in den Hintergrund und
überlässt der Anwendung das Terminal zur Ein-/Ausgabe. Möchte die
Anwendung z.B. interaktiv arbeiten, so muss sie die Einstellungen des
Terminal ändern. Das ist schon bei jedem einfachen Text-Editor der Fall.
Wird eine Anwendung beendet, ohne die geänderten Einstellungen zurück
gesetzt zu haben, so bleiben diese auch über das Ende der Anwendung
hinaus erhalten. Geerbte File-Deskriptoren werden quasi zwischen den
Prozessen geteilt. Die Shell, die dann mit dem Terminal weiterarbeitet,
weiß aber nichts von den Änderungen, was mit ziemlicher Sicherheit zu
Problemen führt. Das ist auch der Grund, warum sich ein Terminal nach
einem Programmabsturz manchmal merkwürdig verhält. Gute Programme merken
sich die Einstellungen und tun alles in Ihrer Macht stehende, um sie vor
dem Programmende wiederherzustellen, selbst wenn das Programm abstürzen
oder von außen terminiert werden sollte. (Signal-Handler helfen hier
weiter, lediglich ein kill -9 kann unter keinen Umständen abgefangen
werden. Ein stty sane hilft dann weiter.)
was ich grad auch festestellt habe, wenn ich das programm starte is der
erste wert ein fehler. nehm ich aber das NONBLOCK raus, gibt es keine
fehler.
das bringt mich zu der Frage warum ? wartet das read bei BLOCK solange
bis ein zeichen kommt ? so hab ich das nämlich immer verstanden
> das bringt mich zu der Frage warum ? wartet das read bei BLOCK solange> bis ein zeichen kommt ? so hab ich das nämlich immer verstanden
Das hast Du (fast) richtig verstanden. Ein blockierendes read wartet bis
mindestens (!) ein Zeichen eingegangen ist.
Hast Du die Schleife wie oben beschrieben geändert? Dann sollte es
eigentlich egal sein. Ein nicht blockierendes read zählt den
Schleifenindex bei fehlenden Zeichen ja nicht weiter und wird einfach so
lange wiederholt, bis die geforderte Anzahl Zeichen gelesen wurde. Ist
nur unökonomisch.
Der oben gepostete Link gibt noch den Hinweis, dass O_NDELAY bei
Problemen mit den Handshake-Ltg. helfen könnte. Das sehe ich aber eher
als Hack, besser wäre es, den Handshake selber sauber hinzukriegen. Das
dürfte bei USB aber eh nicht das Thema sein.
naja wenn ich das NONBLOCK rausnehme hängt er irgendwann. ich denke er
wartet auf ein zeichen obwohl keins mehr kommt.
das for schleife hab ich geändert
> naja wenn ich das NONBLOCK rausnehme hängt er irgendwann. ich denke er> wartet auf ein zeichen obwohl keins mehr kommt.
Ich habe ohnehin noch nicht ganz verstanden, wie die while-Schleife in
main() jemals verlassen werden soll? Einzige Möglichkeit, die ich bisher
sehe: Da in jedem Durchlauf ein neuer File-Deskriptor geöffnet, aber
nicht wieder geschlossen wird (die entsprechende Zeile im Programm ist
auskommentiert!) werden diese irgendwann verbraucht sein und das Öffnen
wird mit einem Fehler zum Abbruch führen. (Die Anzahl verfügbarer Fds
ist implementationsabhängig.) Das kann aber nicht gewollt sein und hat
auch nichts mit blockend oder nicht-blockend zu tun. Also, wie soll das
Programm grundsätzlich beendet werden?
also was das angeht, das hab ich in meinem code schon geändert.
init_UART wird nur noch einmal vor der while(1) schleife aufgerufen.
ich denke das es später das beste sein wird mit main-args oder so zu
arbeiten.
wann ich da momentan allerdings das close machen soll weiß ich noch
nicht
darfst Du auf
raw_temp zugreifen wenn Du die Variable garnicht an float read_TSIC()
übergibst? Oder anders herum: Warum ist die überhaupt ganz oben angelegt
worden wenn man Sie doch nirgends sonst benötigt?
Ich sollte mir wohl auch einmal eines dieser Bücher kaufen...
Gute Nacht
Hauspapa
du hast natürlich recht. raw_temp gehört in read_TSIC() rein.
aber das erklärt dennoch nicht warum es mit NONBLOCK nur zu 90 % sauber
läuft
hier die ersten 3 zeilen:
Error reading data from UART
Error reading data from UART
Temp = -100.0 Grad
danach geht es ganz normal weiter. zwischendrin kann es auch sein das es
mal zu fehlern kommt
und warum es ohne NONBLOCK schon nach kurzer zeit hängen bleibt
Poste doch mal eine aktuelle Version des Programms, es scheint sich ja
inzwischen einiges geändert zu haben. Die Frage, wann und wie das
Programm beendet werden soll, ist auch noch offen.
Sende doch zum Synchronisieren am Anfang ein bestimmte charsequenz, wenn
du die komplett empfangen hast stellst du auf den 2-byte Empfang um.
Dein Problem wird nämlich werden die beiden Bytes zu unterscheiden, nach
einem Reconnect / neustarten des Rechners. Du kannst nicht garantieren,
dass die Bytes in der richtigen Reihenfolge (wenn das 2 byte der letzten
Übertragung als nächstes ankommt) im Ringspeicher deines BS landen.
Gruß Jonas
ich hab zwar den Code vom Raspberry grad nicht da weil ich nicht daheim
bin, aber er sollte etwa so sein momentan wie angehängt.
oder ich arbeite wieder mit so ne art Framestart wie ich es schon mal
benutzt hab. also das höchste Bit jedes Bytes als Synchronisierung für
den Start benutzen um h-byte bzw. l-byte zu kennzeichnen. Den Platz dazu
hätte ich, da ich bei 2 bytes nur 11 bit brauche.
sollte halt erst mal stabil laufen, dann kann ich mich um das drum herum
kümmern. evtl bietet sich an das als kleine lib zu benutzen also
1. init_UART
2. read_TSIC
3. writeback termios, close(fd)
es soll ja vielleicht noch erweiterbar sein. dafür haben wir letztes
semester auch ein protokoll fürs uart benutzt, dass unter anderem den µC
mit ID identifiziert,...
andererseits soll es halt relativ leichtgewichtig bleiben und nicht
durch eine recht lange kette von Apps verlangsamt werden, wie es bei uns
der fall war.
>...oder ich arbeite wieder mit so ne art Framestart wie ich es schon mal>benutzt hab.
Genau, so mein ich dass:
>>Sende doch zum Synchronisieren am Anfang ein bestimmte charsequenz>dafür haben wir letztes semester auch ein protokoll fürs uart benutzt, dass >unter anderem den µC mit ID identifiziert,...
Naja, ein kleiner Overhead musst du schon in Kauf nehmen, ohne
Protokoll(man glaubt selber nicht wie schnell man ein solches erstellt,
ohne es zu merken) gibts halt chaos.
Gruß Jonas
ich weiß ich fand das letztes semester ganz nett mal bewusst n protokoll
zur kommunikation mit mehreren µC mit jeweils mehreren auch
verschiedenartigen sensoren zu entwickeln. vielleicht nehm ich besser
das als grundlage wieder her.
Naja eigentlich ist das ganze doch relativ einfach zu überblicken.
Einmal gibts die MC- und die PC-Seite.
Wenn wir ohne Bestätigungsnachrichten arbeiten wollen, müssen wir eine
Synchronisation einbauen, damit der Empfänger(PC) weiss wo der
Datenstrom beginnt. Zum Beispiel senden wir immer ein "AABB" vor jedem
neuen Datenpaket.
Ein Datenpacket besteht also aus "AAAA" + deinen 2 Nutzbytes.
Die PC-Seite ignoriert also einfach alle empfangenen Bytes bis er die
Reihenfolge "AABB" vollstängig empfangen hat. Nun weiss sie, die
nächsten 2 Bytes sind meine Nutzdaten, diese muss ich interpretieren und
das Ergebnis dann weiterverarbeiten. Eigentlich recht simpel oder?
Gruß Jonas
so nun folgendes szenario:
µC: das erste bit jedes bytes ist jetz für framestart reserviert. also
beim h-byte = 1 beim l-byte = 0
die linux app ist nun die angehängte.
soweit leider keine änderungen.
habe einen zähler eingebaut beim read des 2. bytes läuft und bei über
1000 durchläufen einen fehler schmeißt.
dieser fehler kommt komischerweise immer bei den folgenden temperaturen:
26,3 Grad (h-byte=00000110 l-byte = 00001101)
26,4 Grad (h-byte=00000110 l-byte = 00001110)
das h-byte ist natürlich um 1 nach links verschoben, da das LSB noch das
MSB von l-byte ist.
die werte darüber und darunter passen.
ansonsten, wenn ich den zählerwert runtersetzte krieg ich ganz am anfang
noch 2x nen fehler wie bisher auch.
Ich glaube, wir sollten mal einiges aufräumen. Fangen wir mal mit der
init-Funktion an:
Was passiert, wenn open() fehlschlägt? Dann kommt die Initialisierung
mit einer -1 zurück und Du versuchst in main() einen FD zu schließen,
der nie gültig war. Das ist schon mal schlecht. Eine init-Funktion
sollte immer ein gültiges Objekt zurückgeben oder gar nichts. Kommt
nichts zurück, so gibt es für den Aufrufer auch keinen Grund, irgend
etwas aufzuräumen. Was nach einem gescheiterten Initialisierungsversuch
aufzuräumen ist, sollte eine init-Funktion selbst aufräumen. (Sie ist
ohnehin die einzige die weiß, was aufzuräumen ist.) Der Aufrufer kümmert
sich nur um die ihm übergebenen gültigen Objekte.
Ich sehe auch keinen Grund, den FD global zu machen, also versuche doch
mal so etwas:
1
intinit_UART(char*path)
2
{
3
4
intfd=open(path,...);
5
if(fd<0)
6
perror("init_UART: open");
7
else{
8
structtermiosUART;
9
memset(&UART,0,sizeof(UART));
10
if(tcgetattr(fd,&UART)!=0)
11
perror("init_UART: tcgetattr");
12
else{
13
UART.c_iflag=...;
14
...
15
cfsetospeed(&UART,B9600);
16
if(tcsetattr(fd,TCSANOW,&UART)!=0)
17
perror("init_UART: tcsetattr");
18
else
19
returnfd;
20
}
21
close(fd);
22
}
23
return-1;
24
}
Das Hauptprogramm könnte dann so aussehen:
1
intmain()
2
{
3
intfd=init_UART(UPATH);
4
if(fd<0)
5
return-1;
6
else{
7
while(1){
8
floatresult;
9
if(read_TSCI(fd,&result)==0)
10
printf("Temp = %4.1f Grad\n",result);
11
else
12
printf("faild read TSIC\n");
13
usleep(1000*100);
14
}
15
close(fd);// in diesem Fall sinnlos, würde aber wenn dann hier hingehören
16
}
17
return0;
18
}
Der FD wird natürlich als Parameter übergeben und read_TSCI liefert als
Return-Wert nur noch den Status, das eigentliche Ergebnis wird
call_by_reference übertragen. (Eine Gleitkommavariable auf Gleichheit zu
testen ist mindestens schlechter Stil.) Für den Augenblick würde ich
mich darauf beschränken, einfach mal zu schauen, was da ankommt. Die
ganze Temperaturrechnerei kommt dann später. Also etwa:
1
intread_TSIC(intfd,float*result)
2
{
3
charget_TSIC[]={0x01};
4
charbuffer[8];
5
intn,i=0;
6
7
while(i<sizeof(get_TSIC))
8
if((n=write(fd,get_TSIC+i,sizeof(get_TSIC)-i)<0){
9
perror("read_TSIC: write");
10
return-1;
11
}
12
else
13
i+=n;
14
for(i=0,i<100,++i)
15
if((n=read(fd,buffer,sizeof(buffer))<0){
16
perror("read_TSIC: read");
17
return-1;
18
}
19
else{
20
char*j=buffer;
21
printf("%n:",n);
22
while(n--)
23
printf(" %02x",*j++);
24
printf("\n");
25
}
26
*result=0.0;
27
return0;
28
}
Probiere das mal aus und teile uns mit, was passiert.
Auf das Speichern der alten termios-Einstellungen kannst Du zunächst
verzichten, Du nutzt sie sowieso nicht. Da das Hauptprogramm eine
Endlosschleife ist, die nur durch ein externes Signal beendet werden
kann, müsstest Du einen Signalhändler installieren, um dass zu ändern.
Das versuche wir jetzt aber nicht. Ist wohl auch nicht wirklich nötig,
da an Deinem seriellen Port keine weitere Applikation hängen dürfte.
Prüfe aber bitte, ob das so wirklich so ist! Der Hinweis von bernd_d56
ist absolut richtig. Normalerweise erwartet ein Linux-System dass
Nutzerterminals an den seriellen Ports angeschlossen sind und startet
für jeden Port einen Prozess (getty), der auf einen Login-Versuch
wartet. Wenn zwei Prozesse vom selben Port lesen ist Datensalat
vorprogrammiert. Also, /etc/inittab auf Deinen Port prüfen, ggfs.
auskommentieren und init neu starten (kill -HUP 1 sollte init auch ohne
Reboot veranlassen, die geänderte Konfiguration einzulesen.)
blacksmoke schrieb:>> soweit leider keine änderungen.> habe einen zähler eingebaut beim read des 2. bytes läuft und bei über> 1000 durchläufen einen fehler schmeißt.>> dieser fehler kommt komischerweise immer bei den folgenden temperaturen:>> 26,3 Grad (h-byte=00000110 l-byte = 00001101)> 26,4 Grad (h-byte=00000110 l-byte = 00001110)>> das h-byte ist natürlich um 1 nach links verschoben, da das LSB noch das> MSB von l-byte ist.>> die werte darüber und darunter passen.
Und was ist der Fehler? Sollen andere Werte kommen? Stoppt die
Kommunikation?
Ja is ziemlich nötig aufzuräumen. Wenn ich heim komm werd ich das
nochmal sauber schreiben. Wenn es später nur ne einzelne funktion wird,
die von wo anders aufgerufen wird is es vielleicht schon sinnvoll wieder
am schluss sauber zu machen. Ich will eigentlich schon in richtung
modular und erweiterbar. Vielleicht sollen die aufrufintervalle und all
sowas später aus ner config datei kommen um konfortabler auch einstellen
zu können. Ich werde dann weiter berichten.
ah8 schrieb:> Die Verwendung eines seriellen Ports unter Unix/Linux ist nicht ganz so> trivial,
Oh je, wenn ich deinen Beitrag lese, wird mir schlecht.
Ist das WIRKLICH so?
Es gibt also unter Unix/Linux keinerlei Chance, einen seriellen Port
schlicht und einfach zu öffnen, zu benutzen und wieder zu schließen,
weil Dinge wie z.B. das Editieren auf einfachster Stufe und zeilenweises
Arbeiten in den Treiber eingearbeitet sind, wo sie definitiv NICHT
hingehören.
Der Treiber betreibt also nicht nur das Transportieren und
Zwischenpuffern der Daten, sondern auch noch eine (rudimentäre)
Datenverarbeitung?
Wie kann man nur ein BS derart abstrus konstruieren?
Als Windows-Programmierer ist man es gewöhnt, daß ein serieller Port
eine Standard-Einstellung (Baud, Handshake usw.) hat und daß beim Öffnen
des Ports eben dieses als Default voreingestellt ist - egal was
irgendeine andere Anwendung zuvor damit angestellt hat. Ebenso erwartet
man, daß das Betriebssystem keinerlei Datenvorverarbeitung tut, weil es
ja überhaupt nicht wissen kann, was die Daten konkret bedeuten und was
jeweilige Anwendung mit den Daten anstellen will.
Naja, wieder mal was dazugelernt - wenngleich auch was negatives.
Und da hatte mal dieser Torvalds in einem Anflug von Größenwahn gesagt,
wenn er mal Bill Gates treffen würde, dann würde er ihm erklären, wie
man ein Betriebssystem RICHTG schreibt...
W.S.
Da trennt sich mal wieder die gemeinde in Windows und Linux. Ich kenne
den Treiber jetz war nicht bis ins Detail, aber ich denke das wird schon
alles seinen sinn haben. Und nur weil ne funktionalität da is, heißt es
ja nicht das man sie unbedingt benutzen muss. Der Zugriff auf Geräte is
halt trotzdem relativ einheitlich (open,read,ioctl). In den letzten
Jahren bin ich immer mehr zum Linux Fan geworden, was einfach auch mit
meiner Studienrichtung (Technische Informatik) und eigenem Interesse zu
tun hat. Und ein serieller Port hat ja unter Linux auch
Standardeinstellungen wie Baud, Parity,...
Im gegenzug kann man da halt ein serielles terminal aufmachen seine
einstellungen machen und anschlißend per c-programm auch auslesen. so
hat es ein Mitstudent letztes mal getan.
W.S. schrieb:> Oh je, wenn ich deinen Beitrag lese, wird mir schlecht.> Ist das WIRKLICH so?
"Mein lieber W.S."..., meinst du nicht, dass du mit deinen Rumgesülze
nervst?
W.S. schrieb:> Der Treiber betreibt also nicht nur das Transportieren und> Zwischenpuffern der Daten, sondern auch noch eine (rudimentäre)> Datenverarbeitung?
naja, man merkt du hast Ahnung! Ich hoffe du hast verstanden, was du
hier geschrieben hast...
W.S. schrieb:> Als Windows-Programmierer ist man es gewöhnt, daß ein serieller Port> eine Standard-Einstellung (Baud, Handshake usw.) hat und daß beim Öffnen> des Ports eben dieses als Default voreingestellt ist - egal was> irgendeine andere Anwendung zuvor damit angestellt hat.
...und glaube mir (auch HIER kann ich aus fast 20 Jahre währender
Erfahrung sprechen...;-)) --> auch unter Linux reicht ein:
* open
* write/read
* close
Der Rest (Einstellung der Parameter) ist analog Windows (siehe
entsprechende Tutorials!).
Grüße Uwe
PS.: wie weit mit du schon mit der Gleitkommaerweiterung gekommen?
W.S. schrieb:> Der Treiber betreibt also nicht nur das Transportieren und> Zwischenpuffern der Daten, sondern auch noch eine (rudimentäre)> Datenverarbeitung?> Wie kann man nur ein BS derart abstrus konstruieren?
Weil Terminals und Consolen dort angeschlossen werden können.
Windows reicht z.b. ALT-F4 und den Klick auf das Kreuz rechts oben auch
nicht 1:1 an die Anwendung weiter.
> Es gibt also unter Unix/Linux keinerlei Chance, einen seriellen Port> schlicht und einfach zu öffnen, zu benutzen und wieder zu schließen,> weil Dinge wie z.B. das Editieren auf einfachster Stufe und zeilenweises> Arbeiten in den Treiber eingearbeitet sind, wo sie definitiv NICHT> hingehören.
Man darf dabei nicht vergessen, dass der serielle Port unter Unix nicht
einfach nur eine Datenleitung ist, sondern eines der wichtigsten und
elementarsten Instrumente, eine Unix-System zu kontrollieren. Jeder
Admin wird z.B. seine Konsole zu schätzen wissen, die ihm oft auch dann
noch Zugriff auf das System gewährt, wenn (fast) alles andere schon
nicht mehr geht und bei anderen Betriebssystemen nur noch der
Reset-Knopf bleibt. Jeder Nutzer, der einen hängenden Prozess mit Ctrl-C
beendet, nutzt dabei Funktionalität des seriellen Treibers. Das bedingt
natürlich eine gewisse Komplexität. Sicher würde man vieles, was
historisch gewachsen ist, heute anders machen, im Grundsatz ist das
ganze System aber schon sehr durchdacht. Für den Nutzer, der einfach nur
ein paar Daten übertragen will, ergibt das damit natürlich erst einmal
das Problem, den seriellen Port von einem komplexen Steuerinstrument auf
eine einfache Datenleitung downzugraden. Das muss man natürlich zunächst
einmal wissen. Ich gebe aber gerne zu, dass auch ich mir schon
einfachere Wege gewünscht habe, dieses zu erreichen.
Übrigens open/read/write/close funktioniert genauso, wenn ich um die
zusätzlichen Funktionalitäten des seriellen Treibers weiß und sie in
meinem Protokoll berücksichtige.
mit deiner variante vom code bekomme ich nix anderes raus:
read_TSIC:read: Resource temporarily unavailable
faild read TSIC
2: 85 5f
das ganze wiederholt sich dann
genau an dieser Stelle bleibt er auch hängen wenn ich O_NONBLOCK
entferne. vom temperatursensor kommt dieser wert aber raus, da ich
direkt am µC ein LCD hängen hab, dass mir die temperatur zusätzlich
ausgibt
ok ich glaub das mit dem d hat sich erledigt. beim zweiten mal hinkucken
kam mir das verdächtig nach CR vor
und siehe termios-attributes: UART.c_iflag = IGNBRK|IGNPAR|IGNCR;
er ignoriert also mit diesen einstellungen CR alias 0x0d
wiedermal ein ganz dummer fehler den man erst mal sehen muss. mit dem
was da rauskommt kann ich denk ich jetz soweit leben. ich werde nun den
mikrocontroller wieder wie letztes semester aufbauen, damit er Modular
ist und das protokoll dazu auch wieder implementieren.
(enthält FS, länge, befehl, daten) und hat recht zuverlässig
funktioniert
vielen dank auf jeden fall für eure hilfe. Auf einen sauberen code zu
achten wird mir in zukunft wichtiger sein g
blacksmoke schrieb:> genau an dieser Stelle bleibt er auch hängen wenn ich O_NONBLOCK> entferne.
Die Fehlermeldung kommt dann aber nicht mehr, oder? Lies mal in *man 2
read*, wie sich read im nicht-blockierenden Modus verhält, wenn keine
Daten verfügbar sind. (Und verlasse Dich dabei nicht auf dass, was ich
oben geschrieben habe, da steckt ein Fehler drin.) Das sollte das
Verhalten erklären. Wenn Du weiter darauf bestehst, nicht-blockierenden
zu arbeiten, wirst Du diese Meldung entsprechend behandeln müssen.
> vom temperatursensor kommt dieser wert aber raus, da ich> direkt am µC ein LCD hängen hab, dass mir die temperatur zusätzlich> ausgibt
Ich weiß zwar nicht genau, wie Deine HW aussieht, ich kann mir aber
nicht vorstellen, das dieses LCD in der seriellen Leitung hängt.
Folglich würde ich das als Beweis nicht durchgehen lassen, dass
tatsächlich etwas über die Leitung kommt. Fall doch etwas ankommt,
kannst Du ausschließen, dass noch ein weiterer Prozess versucht, vom
gleichen Port zu lesen?
also das mit dem read funktioniert trotzdem noch nicht so 100 prozentig
read_TSIC:read: Resource temporarily unavailable
faild read TSIC
ich würde schon auf blockierenden betrieb umstellen, wenn ich
sicherstellen kann das er dann nicht bis zu ende der zeit irgendwo hängt
ok also habe jetz das O_NONBLOCK raus uns lasse 2 byte blockierend
empfangen.
nochmal zum update nun folgenden code der angehängt ist.
nachdem ich IGNCR raus genommen habe bleibt er nicht mehr hängen.
und zu deiner frage, wenn ich blockierend lese, krieg ich auch keine
fehlermeldung
blacksmoke schrieb:> das LCD ist ja nur ein beweiß dafür, das aus dem sensor was raus kommt.> es hängt natürlich nicht am UART, sondern am Port des µC
Hast Du ein Null-Modem-Kabel und einen Rechner mit zwei seriellen Ports?
(Zwei Rechner mit je einem Port tun's auch.) Dann verbinde doch diese
beiden Ports mit dem Null-Modem-Kabel, schreibe Dir zwei kleine
Programme, eines das liest, eines das schreibt, und prüfe, ob die Daten,
die Du auf der einen Seite schreibst auf der anderen Seite richtig
ankommen. Dann – und nur dann – kanst Du sicher sein, dass Dein
Port-Interface funktioniert.
ich benutze einen usb-to-seriell-converter, der mir auch bisher immer
treue dienste gleistet hat.
nun kann ich ich das programm auch umschreiben, bei jeder abfrage die
datei zuerst öffnen, abragen, und dann wieder schließen, was vorher auch
nicht funktioniert hat, was aber scheinbar ne recht schlechte
abfragegeschwindigkeit nach sich zieht (ich denke durch das öffnen,
setzen der attribute,...)
aber soweit funktioniert jetzt alles auch ohne eine einzige
fehlermeldung zu bekommen
das mit 2 pcs verbinden über seriell, haben wir im Studium schon mal
gemacht, allerdings mit echo > und cat
blacksmoke schrieb:> ich würde schon auf blockierenden betrieb umstellen, wenn ich> sicherstellen kann das er dann nicht bis zu ende der zeit irgendwo hängt
Das ist der falsche Ansatz für ein richtig erkanntes Problem. Wenn Du
nicht-blockierend arbeitest betreibst Du im Prinzip Polling, und das
frisst unnötig jede Menge CPU-Zeit. Es ist ja auch noch offen, wie Du
Dein Programm sauber beendest. Ctrl-C ist auf Dauer sicher keine gute
Lösung. Hast Du Dir schon mal select (man 2 select) angeschaut? Damit
kannst Du sowohl gleichzeitig auf Tastatureingaben reagieren als auch
einen Timout setzen.
blacksmoke schrieb:> ich benutze einen usb-to-seriell-converter, der mir auch bisher immer> treue dienste gleistet hat.
Kein Problem. Die USB-Seite steckt im PC nehme ich an? Dann bräuchtest
Du natürlich zwei davon und ein Null-Modem-Kabel. Genügend frei
USB-Ports sind mit einem Hub ja kein Problem. (Ich unterstelle mal, dass
man USB2seriell-Konverter einfach so zusammenschließen kann, sollten ja
ganz normale serielle Leitungen sein. Probiert habe ich das allerdings
nie.)
> das mit 2 pcs verbinden über seriell, haben wir im Studium schon mal> gemacht, allerdings mit echo > und cat
Auf einer Seite kannst Du das machen. Zum Einstellen der
Leitungsparameter gibt es da noch stty. Auf der anderen Seite geht es
Dir ja aber gerade um Deine *read*-Routine.
also nochmal zum stand:
mein programm arbeitet jetzt blockieren, und es gibt auch keine
fehlermeldungen mehr.
wenn ich später eine lib draus mach, kann ich init_UART aufrufen,
read_TSIC und anschließend die datei wieder schließen.
das funktioniert auch und habe ich grad getestet.
das problem, warum das blockierende read hängen geblieben ist, war ja
das wegen falsch eingestellten attributen, 0x0d (CR) ignoriert wurde,
und so das read unendlich warten musste.
nachdem das attribut korrigiert ist, funktioniert alles wunderbar.
und sollte damit auch sauber gelöst sein oder nicht ?
Im angehängten code siehst du in der main n paar auskommentierte zeilen,
dies habe ich zum testen benutzt, um die datei zu öffnen (init)
read_TSIC aufzurufen und anschließend die datei wieder zu schließen.
Ich denke das war grad nur ein Missverständniss ;-)
ah8 schrieb:> ch unterstelle mal, dass> man USB2seriell-Konverter einfach so zusammenschließen kann, sollten ja> ganz normale serielle Leitungen sein. Probiert habe ich das allerdings> nie.
Im Studim haben wir genau die konfiguration schon mal getestet mit 2
convertern. funktioniert einwandfrei
Übrigens, der Code für die Lese-Routine war nur zum testen gedacht,
nicht für die produktive Lösung. Zum Beispiel wird bei der Rückgabe
nicht auf n==0 geprüft. Das zeigt ein Dateiende an. Ob das bei einem
seriellen Port überhaupt möglich ist, kann ich nicht genau sagen und
dürfte stark davon abhängen, ob und wie der Handshake implementiert ist.
Dabei werden oft Abstriche gemacht. Das kann ein Grund sein, warum
häufig O_NDELAY verwendet wird. Ein fehlerhaft implementiertes Handshake
kann blockierendes Lesen ewig warten lassen. Ich würde die Möglichkeit
eines Dateiendes aber in jedem Fall ins Kalkül ziehen.
was das read noch zum stehen bringen kann ist, wenn ich während das
programm läuft den Sensor abstecke. dann wartet das read natürlich und
kehrt nie zurück.
gibt es dafür eine lösung ? kann man das read zeitlich begrenzen ?
weil, das wäre ja genau was ich bräuchte. er schickt nen befehl hin,
wartet ne gewisse zeit, wenn nichts kommt, muss er abbrechen und nochmal
versuchen
blacksmoke schrieb:> nachdem das attribut korrigiert ist, funktioniert alles wunderbar.> und sollte damit auch sauber gelöst sein oder nicht ?
Bis dahin ist es, glaube ich, noch ein lange Weg :-)
1. Wie wird das Programm sauber beendet?
2. Was passiert, wenn Dir Zeichen verloren gehen oder der uC sich
aufhängt?
3. Dein Programm stürzt ab oder wird beendet, es sind aber noch Zeichen
im Treiber gepuffert. Ist das möglich? Wenn ja, kann Dein Programm beim
nächsten Start darauf reagieren?
4. Kann es sein, dass noch alte Messwerte irgendwo im Puffer hängen,
wenn Dein Programm startet? Brauchst Du vielleicht einen Zeitstempel, um
Abfrage und Antwort zu synchronisieren?
Das sind nur einige Fragen, die mir einfallen. Ich kann auch nicht alle
beantworten, aber Du solltest es können, wenn Du eine saubere Lösung
willst.