serielle Schnittstelle unter Linux - NMEA

#1149555
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
 }
Gast #1150041
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ß,
Persönliche Seite #1152158
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.
Gast #1153006
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ß,

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren