Forum: Mikrocontroller und Digitale Elektronik µC - UART - Linux


von blacksmoke (Gast)


Angehängte Dateien:

Lesenswert?

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 ?

von blacksmoke (Gast)


Lesenswert?

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

von gnd3 (Gast)


Lesenswert?

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.

von ah8 (Gast)


Lesenswert?

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.

von blacksmoke (Gast)


Lesenswert?

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 :-)

von blacksmoke (Gast)


Lesenswert?

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

von blacksmoke (Gast)


Lesenswert?

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

von ah8 (Gast)


Lesenswert?

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.

von blacksmoke (Gast)


Lesenswert?

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

von blacksmoke (Gast)


Lesenswert?

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

von ah8 (Gast)


Lesenswert?

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.

von (Gast) (Gast) (Gast) (Gast) (Gast) (Gast) (Gast) (Gast)


Lesenswert?


von ah8 (Gast)


Lesenswert?

> 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.)

von blacksmoke (Gast)


Lesenswert?

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

von ah8 (Gast)


Lesenswert?

> 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.

von blacksmoke (Gast)


Lesenswert?

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

von ah8 (Gast)


Lesenswert?

> 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?

von blacksmoke (Gast)


Lesenswert?

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

von hauspapa (Gast)


Lesenswert?

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

von blacksmoke (Gast)


Lesenswert?

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

von ah8 (Gast)


Lesenswert?

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.

von Jonas B. (jibi)


Lesenswert?

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

von blacksmoke (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Jonas B. (jibi)


Lesenswert?

>...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

von blacksmoke (Gast)


Lesenswert?

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.

von Jonas B. (jibi)


Lesenswert?

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
von Bernd D. (Firma: ☣ ⍵ ☣) (bernd_d56) Benutzerseite


Lesenswert?

Was ist mit getty?
Nicht das der stört.
Typischerweise lauscht der auf tty1-6

von blacksmoke (Gast)


Angehängte Dateien:

Lesenswert?

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.

von ah8 (Gast)


Lesenswert?

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.)

von Achim K. (aks)


Lesenswert?

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?

von blacksmoke (Gast)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von blacksmoke (Gast)


Lesenswert?

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.

von Uwe B. (boerge) Benutzerseite


Lesenswert?

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
von Tok (Gast)


Lesenswert?

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.

von ah8 (Gast)


Lesenswert?

> 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.

von blacksmoke (Gast)


Lesenswert?

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

von blacksmoke (Gast)


Lesenswert?

dabei lässt er "86 d" aus, was wieder 26,3 grad entspricht.
"86 e" stellt er dann wieder da

von blacksmoke (Gast)


Lesenswert?

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

von blacksmoke (Gast)


Lesenswert?

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

von ah8 (Gast)


Lesenswert?

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?

von blacksmoke (Gast)


Lesenswert?

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

von blacksmoke (Gast)


Lesenswert?

und vorrausgesetz ich bringe das read dazu keine fehler mehr zu 
produzieren, damit ich überhaupt ne vernünftige ausgabe aus der funktion 
habe

von blacksmoke (Gast)


Lesenswert?

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

von blacksmoke (Gast)


Angehängte Dateien:

Lesenswert?

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

von ah8 (Gast)


Lesenswert?

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.

von blacksmoke (Gast)


Lesenswert?

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

von ah8 (Gast)


Lesenswert?

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.

von ah8 (Gast)


Lesenswert?

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.

von blacksmoke (Gast)


Angehängte Dateien:

Lesenswert?

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 ;-)

von blacksmoke (Gast)


Lesenswert?

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

von ah8 (Gast)


Lesenswert?

Ü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.

von blacksmoke (Gast)


Lesenswert?

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 ?

von ah8 (Gast)


Lesenswert?

blacksmoke schrieb:

> gibt es dafür eine lösung ? kann man das read zeitlich begrenzen ?

man 2 select

von blacksmoke (Gast)


Lesenswert?

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

von blacksmoke (Gast)


Lesenswert?

damit ich zumindest sicherstellen kann, dass das programm am read nicht 
ewig hängen kann

von ah8 (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.