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 ?
hier noch ein kurzer übergang aus der programmausgabe: h: 2 l: 255 raw_temp = 767 Temp = 24.9 Grad h: 0 l: 0 raw_temp = 0 Temp = -50.0 Grad
verdächtig: 3 ist zufällig CTRL-C. Ob das trotz O_NOCTTY und deinen .c_lflag doch stört? Ich verwende für binäre Daten .c_lflag = 0
1 | comm_params.c_iflag = IGNBRK | IGNPAR; |
2 | comm_params.c_oflag = 0; |
3 | comm_params.c_cflag = CREAD | CLOCAL | bits | parity | stopbits | baud; |
4 | comm_params.c_lflag = 0; |
5 | comm_params.c_line = 0; |
6 | comm_params.c_cc[VMIN] = 1; |
7 | comm_params.c_cc[VTIME] = 0; |
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
ah ok das mit dem i+=seek klingt schon logisch irgendwie. sonst bringt mir ja das seek au net wirklich was wenn ichs net mit einbezieh
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
:
Bearbeitet durch User
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 | int init_UART(char *path) |
2 | {
|
3 | |
4 | int fd = open(path, ... ); |
5 | if ( fd < 0 ) |
6 | perror("init_UART: open"); |
7 | else { |
8 | struct termios UART; |
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 | return fd; |
20 | }
|
21 | close(fd); |
22 | }
|
23 | return -1; |
24 | }
|
Das Hauptprogramm könnte dann so aussehen:
1 | int main() |
2 | {
|
3 | int fd = init_UART(UPATH); |
4 | if ( fd < 0 ) |
5 | return -1; |
6 | else { |
7 | while (1) { |
8 | float result; |
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 | return 0; |
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 | int read_TSIC(int fd, float *result) |
2 | {
|
3 | char get_TSIC[] = {0x01}; |
4 | char buffer[8]; |
5 | int n, 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 | return 0; |
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?
:
Bearbeitet durch User
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
dabei lässt er "86 d" aus, was wieder 26,3 grad entspricht. "86 e" stellt er dann wieder da
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
und vorrausgesetz ich bringe das read dazu keine fehler mehr zu produzieren, damit ich überhaupt ne vernünftige ausgabe aus der funktion habe
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
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 ?
blacksmoke schrieb: > gibt es dafür eine lösung ? kann man das read zeitlich begrenzen ? man 2 select
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
damit ich zumindest sicherstellen kann, dass das programm am read nicht ewig hängen kann
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.
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.