Guten Tag!
Ich möchte an der seriellen Schnittstelle ankommende Daten empfangen und
in eine Datei schreiben. Baudrate ist 38400. Es kommen ASCII-Strings mit
einer Länge zwischen 100 und 150 Bytes und mit einer Frequenz von 5Hz
an. Jeder String endet mit einem <cr><lf>.
Meiner Ansicht nach ist für meine Anwendung der Canonical Mode mit der
Erkennung des <lf> am Ende des Strings geeignet.
Hier nun das Problem:
Es passiert immer wieder, das einzelne Zeichen des Strings "verloren"
gehen und somit auch in der Logdatei fehlen. Das <cr><lf> am Ende jedes
Strings ist immer vorhanden. Dieser Fehler passiert unregelmässig und
kann von mir nicht reproduziert werden. Die fehlenden Zeichen sind schon
bei der Ausgabe mit printf im Terminal sichtbar, also noch vor dem
Schreiben in die Datei.
Original:
aabbccdd 123456 123456 123456 123456 123456 123456 123456 123456<cr><lf>
Aufgezeichnet:
aabbccdd 123456 12346 123456 123456 123456 123456 123456 123456<cr><lf>
^^^
Hier mein C Programm, das weitgehend von Minicom-Beispiel übernommen
wurde:
Hans Gruber schrieb:> char buf[255];
...
> res = read(fd,buf,255);> buf[res]=0;
Hat vermutlich nichts mit dem Problem zu tun, ist aber schon mal ein
Buffer Overflow falls tatsächlich mal 255 Zeichen in einem Rutsch
gelesen werden.
Hans Gruber schrieb:> if((write(fh, &buf, res)) == -1)> {> perror("Fehler bei write");> }
Und falls write() mal weniger als res Bytes schreibt, geht der Rest
verloren. Außerdem sollte es entweder buf oder &buf[0] sein.
Hallo,
ich glaube das Problem hatte ich auch, allerdings weiß ich keine Details
mehr. Allerdings habe ich das Lesen in einem Thread realisiert, weil ich
mir nicht sicher war, ob diverse printf oder write mir die Schnittstelle
blockieren:
..in irgendeiner Initialisierung:
// Öffnen der seriellen Schnittstelle
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
// Sichern der alten Konfiguration
/// todo Die Konfiguration sollte beim Beenden wird überspielt werden.
tcgetattr(fd,&oldtio); /* save current port settings */
// Konfiguration der Schnittstelle
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
// set input mode (non-canonical, no echo,...)
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 1; /* inter-character timer unused */
newtio.c_cc[VMIN] = 0; /* blocking read until 5 chars received
*/
// Konfiguration übertragen
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
..und dann in einem Thread, der alle 20ms aufgerufen wird...
res = read(fd,buf,255); /* returns after 5 chars have been input */
buf[res]=0; /* so we can printf... */
Spontan hätte ich das Flag [VTIME] in Verdacht:
http://www.unixwiz.net/techtips/termios-vmin-vtime.html
Vielleicht hilft es...
Gruß
Marvol
Andreas B. schrieb:> Hans Gruber schrieb:>> char buf[255];> ...>> res = read(fd,buf,255);>> buf[res]=0;>> Hat vermutlich nichts mit dem Problem zu tun, ist aber schon mal ein> Buffer Overflow falls tatsächlich mal 255 Zeichen in einem Rutsch> gelesen werden.>> Hans Gruber schrieb:>> if((write(fh, &buf, res)) == -1)>> {>> perror("Fehler bei write");>> }>> Und falls write() mal weniger als res Bytes schreibt, geht der Rest> verloren. Außerdem sollte es entweder buf oder &buf[0] sein.
Hallo Andreas,
da Du eh schon in C programmierst erlaube ich mir Die nur einen Hinweis
zu geben was Du mal recherchieren und testen könntest.
Schau Die mal die Funktion "select" in verbindung mit Filedescriptoren
an.
Ich bin mir ziemlich sicher dass das dein problem löst.
Danke schonmal für die Antworten!
Ich hab den Code jetzt nochmal umgeschrieben und ein select mit
eingebaut. Schön langsam komme ich dem Problem näher. Es passiert jetzt
folgendes:
Die Strings werden in die Datei geschrieben. Nach einiger Zeit kann ich
an der Console mit Minicom einen unvollständigen String entdecken,
gefolgt von einem "1 input overrun(s)" oder auch "2 input overrun(s)".
Es scheint also so, das der Hardware Puffer überläuft.
Zur Hardware: Ein Embedded Linux System mit einem ATMEL AT91RM9200. An
einem Seriellen Port kommen die Daten rein, am anderen hängt der
Entwicklungsrechner an der Console.
Hier noch der neue Code:
1
#include<sys/time.h>
2
#include<sys/types.h>
3
#include<unistd.h>
4
#include<termios.h>
5
#include<fcntl.h>
6
7
#define MODEMDEVICE "/dev/ttyS0" // setting port
8
#define BAUDRATE B38400 // setting baudrate
9
10
main()
11
{
12
intfd1,res;
13
fd_setreadfs;/* file descriptor set */
14
intmaxfd;/* maximum file desciptor used */
15
intloop=1;/* loop while TRUE */
16
structtermiosoldtio,newtio;
17
charbuf[255];
18
19
fd1=open(MODEMDEVICE,O_RDWR|O_NOCTTY);
20
if(fd1<0)
21
{
22
perror(MODEMDEVICE);
23
exit(-1);
24
}
25
26
tcgetattr(fd1,&oldtio);// save current serial port settings
27
bzero(&newtio,sizeof(newtio));// clear struct for new port settings
Welche Möglichkeiten hab ich noch das Problem zu lösen? Ich hab schon
diverse Optionen und Kombinationen des File Descriptors versucht
(O_NONBLOCK, O_NDELAY, V_TIME, V_MIN), alle ohne Erfolg.
Grüsse,
Hans
Hallo,
vielleicht liegt es garnicht an Deinem Programm, sondern an der
Einstellung von Minicom. Schau mal unter 14.10...
http://www.linuxhaven.de/dlhp/HOWTO/DE-Modem-HOWTO-14.html
Ich nehme einfach mal an, dass Dein Entwicklungsrechner mit Linux läuft.
Gruß
Marvol
@Mar Vol!
An Minicom kann es nicht liegen. Dies wird ja nur zum Monitoring
verwendet. Der Empfang und das Schreiben der Daten selbst findet auf dem
Embedded Linux System. Minicom gibt mir nur zur Kontrolle die Strings
über die Verbindung zur Console zum Embedded System aus.
Bin echt am Verzweifeln...
@all
Ich habe gestern noch ein paar Versuche gemacht. Ich hab mir ein
Shell-Script gebastelt, das ebenfalls die Daten empfängt und dann in
eine Datei schreibt. Hier konnte ich den gleichen Effekt beobachten.
Ich denke also das es an irgendeiner Einstellung für den Port selbst
liegt. Gibt es eine Möglichkeit in C oder per Shell den Hardware-Puffer
zu leeren, sobald ein (oder 5) vollständiger String ausgelesen und
geschrieben wurde? Oder ist das rein vom Timing her nicht machbar?
Folgende Idee:
Hans
Schreib doch mal in ein tmpfs statt auf einen Datenträger (vermutlich
eine SD-Karte, oder?). Eventuell blockiert einfach das Schreiben den
seriellen Empfang, weil der Treiber zu lange Interrupts blockiert und
der UART viel zu wenig Pufferspeicher hat, um das zu überbrücken.
Andreas hat doch ein gute Idee, schreib die einen kleinen Interpreter:
1. Funktion: Minicom schickt die Daten an das Board.
2. Funktion: Das Board speichert die Daten im Speicher.
3. Funktion: Minicom frägt die Speicherdaten ab.
Beispiel mit Interpretation vom ersten Zeichen:
>s aabbccdd 123456 123456 123456 123456 123456 123456 123456 123456<cr><lf>
// Board sieht "s" und speichert den Rest in den Speicher.
>r
// Board sieht "r": Daten werden vom Board zurückgeliefert.
Einfach mal die Schreibzyklen weglassen.
Viel Glück
Marvol
Was ist denn die sendende Gegenstelle, kannst Du mit der ev. ein
Handshake machen? Hintergedanke wäre dass Du das Handshake z.B. den CTS
auf 0 setzt so bald Du mit der Verarbeitung von Daten beschäftigt bist
und somit nicht wirklich empfangsbereit. Wenn der FIFO Puffer des
Hardwarebausteins z.B 15 Bytes aufnimmt und das Handshake Signal schon
nach 10 Bytes weggeht könnte ein oder mehrere noch gesendete Bytes immer
noch vom FIFO aufgenommen werden. Ich muß aber dazu sagen dass ich es
selbst noch nie gemacht habe die Variante mit den Descriptoren und
select haben mir eigentlich immer geholfen.