Forum: PC-Programmierung serielle Schnittstelle unter Linux - NMEA


von Phil J. (sunflower_seed)


Lesenswert?

Hallo,

ich habe hier neben mir einen Navi*** GPS-Empfänger mit TTL-Output.

4800baud
8N1

Im Hyperterminal unter Wind****f funktioniert das auslesen der NMEA 
Datensätze prächtig.
Jetzt möchte ich die Datensätze aber über eine serielle Schnittstelle 
(TTL) unter Linux empfangen.
Ich habe mir dazu das folgende Programm zusammengebastelt.
Allerdings habe ich folgendes Problem: In der Konsole erscheint 
lediglich "0", aber periodisch, wahrscheinlich passend mit dem 
Eintreffen eines neuen NMEA Datensatzes.
Was mach ich falsch?
1
# include <stdio.h>
2
# include <unistd.h>
3
# include <fcntl.h>
4
# include <termios.h>
5
# include <sys/select.h>
6
7
# define TERM_DEVICE "/dev/ttyUSB0"    /* = COM1 */
8
# define TERM_SPEED B4800        /* Bit/Sek */
9
10
int main()
11
 {
12
  int fd, old_flags;
13
  ssize_t length;
14
  char buffer[16];
15
  struct termios term_attr;
16
  fd_set input_fdset;
17
18
  if ((fd = open(TERM_DEVICE, O_RDWR)) == -1)
19
   {
20
    perror("terminal: Can't open device " TERM_DEVICE);
21
    return(1);
22
   }
23
            /* RS232 konfigurieren */
24
  if (tcgetattr(fd, &term_attr) != 0)
25
   {
26
    perror("terminal: tcgetattr() failed");
27
    return(1);
28
   }
29
  term_attr.c_cflag = TERM_SPEED | CS8 | CRTSCTS | CLOCAL | CREAD;
30
  term_attr.c_iflag = 0;
31
  term_attr.c_oflag = OPOST | ONLCR;
32
  term_attr.c_lflag = 0;
33
  if (tcsetattr(fd, TCSAFLUSH, &term_attr) != 0)
34
    perror("terminal: tcsetattr() failed");
35
36
            /* Std.-Eingabe anpassen */
37
  if (tcgetattr(STDIN_FILENO, &term_attr) != 0)
38
   {
39
    perror("terminal: tcgetattr() failed");
40
    return(1);
41
   }
42
            /* alte Einst. sichern */
43
  old_flags = term_attr.c_lflag;
44
  term_attr.c_lflag &= ~(ICANON | ECHO);
45
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_attr) != 0)
46
    perror("terminal: tcsetattr() failed");
47
48
  while (1)
49
   {
50
    FD_ZERO(&input_fdset);
51
    FD_SET(STDIN_FILENO, &input_fdset);
52
    FD_SET(fd, &input_fdset);
53
    if (select(fd+1, &input_fdset, NULL, NULL, NULL) == -1)
54
      perror("terminal: select() failed");
55
    if (FD_ISSET(STDIN_FILENO, &input_fdset))
56
     {
57
      if ((length = read(STDIN_FILENO, buffer, 16)) == -1)
58
        perror("terminal: read() failed");
59
      else
60
        if (buffer[0] == '\33')     /* Abbruch mit ESC */
61
      break;
62
    else
63
          write(fd, buffer, length);
64
     }
65
    if (FD_ISSET(fd, &input_fdset))
66
     {
67
      if ((length = read(fd, buffer, 16)) == -1)
68
        perror("terminal: read() failed");
69
      else
70
        write(STDOUT_FILENO, buffer, length);
71
     }
72
   }
73
            /* Std.-Eingabe wie vorher */
74
  term_attr.c_lflag = old_flags;
75
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_attr) != 0)
76
    perror("terminal: tcsetattr() failed");
77
78
  printf("Aborted.\n");
79
  close(fd);
80
  return(0);
81
 }

von Gast3 (Gast)


Lesenswert?

Mach erst mal, dass es mit einer richtigen Schnittstelle geht.
Dann kannst Du den naechsten Schritt mit USB versuchen.

Gast3

von Phil J. (sunflower_seed)


Lesenswert?

Ich kann das Ding auch an ttyS1 hängen. Alle Schnittstellen an dem 
Linuxboard sind als Logik-Level ausgelegt.
Das Ergebnis ist das selbe.

von Detlef _. (detlef_a)


Lesenswert?

>>Jetzt möchte ich die Datensätze aber über eine serielle Schnittstelle
>>(TTL) unter Linux empfangen.

Was Fertiges nehmen: http://de.wikipedia.org/wiki/Minicom
Schlank und schnell und einfach und zuverlässig.

Gute Nacht
Detlef

von Superberti (Gast)


Lesenswert?

Hallo,

Du bist sicher, dass du Hardware-Handshake benutzt?

term_attr.c_cflag = TERM_SPEED | CS8 | CRTSCTS | CLOCAL | CREAD;

Das du mit select() arbeitest würde ich noch

term_attr..c_cc[VMIN]=1;
term_attr..c_cc[VTIME]=0;

setzen (s.struct termios)


Außerdem würde ich keinen Fehler werfen, wenn beim read()==-1 errno 
EAGAIN oder EWOULDBLOCK ist:

int res = read(fd, buffer, 16);

  if (res<0)
  {
    if (errno==EAGAIN || errno==EWOULDBLOCK)
    {
      usleep(1000);
      continue;
    }
    else
      perror(...);
  }

Gruß,

von Phil J. (sunflower_seed)


Lesenswert?

Hardware-Handshake vor den kopfschlag
CRTSCTS ist jetzt rausgenommen.
Aber irgendwie ist das Ergebnis noch nicht befriedigend.
mehr als "{" bekomm ich nicht zu Gesicht
Werd noch mal in Ruhe Code lesen

von Thomas P. (tpircher) Benutzerseite


Lesenswert?

1
term_attr.c_cflag = TERM_SPEED | CS8 | CRTSCTS | CLOCAL | CREAD;
Ich weiss nicht ob das dein Problem ist, aber ich wuerde davon absehen, 
die Baudrate in c_cflag zu setzen.
Nimm eher cfsetispeed/cfsetospeed anstatt TERM_SPEED. Das ist portabler.

Zur Diagnose: nimm minicom zur Initialisierung der seriellen 
Schnittstelle und lies diese dann mit stty -a aus (waehrend minicom 
laeuft).

Thomas

EDIT: oder cutecom anstelle von minicom.

von Phil J. (sunflower_seed)


Lesenswert?

Das mit Minicom ist nicht so einfach.
Das ganze muss auf das Foxboard von ACME angepasst werden, habs bis 
jetzt noch nicht geschafft das vernünftig zu kompilieren.

von Thomas P. (tpircher) Benutzerseite


Lesenswert?

Hmm, warum kannst du den seriellen Driver nicht unter einem Linux 
Desktop testen und dann auf deine Foxboard portieren (kopieren)?

von Phil J. (sunflower_seed)


Lesenswert?

Das wär ne Idee.
;-)

von Superberti (Gast)


Lesenswert?

Hi nochmal,

vielleicht hilft es Dir ja weiter, wie ich meine Schnittstellen 
initialisiere. Ich lese immer mit select() und Timeouts und dieser Code 
hat eigentlich immer gut funktioniert (der Code kommt aus meiner 
seriellen Klasse und kompiliert so natürlich nicht):

void Open()
{
  mHandle = open( "/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK);
  if (mHandle<0)
    throw TOSErr("Could not open com port",errno);

  int BaudRate=19200;
  struct termios newtio;
  newtio.c_cflag = CS8 | CLOCAL | CREAD;
  newtio.c_iflag = IGNPAR | IGNBRK ;
  newtio.c_oflag = 0;
  newtio.c_lflag &= ~ (ICANON | ECHO | ECHOE | ISIG);
  newtio.c_cc[VMIN]=1;
  newtio.c_cc[VTIME]=0;
  if(cfsetospeed(&newtio, BaudRate))
    throw TOSErr("cfsetospeed failed",errno);
  if (cfsetispeed(&newtio, BaudRate))
    throw TOSErr("cfsetispeed failed",errno);

  tcflush(mHandle, TCIFLUSH);
  if (tcsetattr(mHandle,TCSANOW,&newtio))
    throw TOSErr("tcsetattr failed",errno);
}

und beim Lesen dann:

void Read(char * const inBuf, const int BytesToRead, int & BytesRead, 
const bool aNoThrow)
{
  if (mHandle<0)
    throw TErr("com port not opened:");
  if (BytesToRead<=0)
    return;
  BytesRead=0;
  int BytesLeft=BytesToRead;
  fd_set AcceptSet;
  struct timeval timeout;
  int rounds=0;
  while (BytesLeft)
  {
    // Warten mit select()
    // Achtung:Timeout-Struktur wird unter Linux in select() verändert
    rounds++;

    timeout.tv_sec=mSerInfo.Timeout_ms/1000;
    timeout.tv_usec=(mSerInfo.Timeout_ms*1000)%1000000;
    FD_ZERO(&AcceptSet);
    FD_SET(mHandle,&AcceptSet);
    int st=select(mHandle+1,&AcceptSet,NULL,NULL,&timeout);
    if (st==0)
    {
      // Timeout
      if (!aNoThrow)
        throw TErr("Read command timeout. Read only 
"+ToString(BytesRead)+" of "+ToString(BytesToRead)+" bytes.");
      else
        return;
    }
    else if (st<0)
    {
      throw TOSErr("TSerIO::Read select failed",errno);
    }
    int res = read(mHandle, inBuf+BytesRead, BytesLeft);
    if (res<0)
    {
      if (errno==EAGAIN || errno==EWOULDBLOCK)
      {
        usleep(1000);
        continue;
      }
      else
        throw TOSErr("Read command failed. Read only 
"+ToString(BytesRead)+" of "+ToString(BytesToRead)+" bytes.",errno);
    }
    BytesLeft-=res;
    BytesRead+=res;
  }
}

Gruß,

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.