www.mikrocontroller.net

Forum: PC-Programmierung serielle Schnittstelle unter Linux - NMEA


Autor: Phil J. (sunflower_seed)
Datum:

Bewertung
0 lesenswert
nicht 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?
# include <stdio.h>
# include <unistd.h>
# include <fcntl.h>
# include <termios.h>
# include <sys/select.h>

# define TERM_DEVICE "/dev/ttyUSB0"    /* = COM1 */
# define TERM_SPEED B4800        /* Bit/Sek */

int main()
 {
  int fd, old_flags;
  ssize_t length;
  char buffer[16];
  struct termios term_attr;
  fd_set input_fdset;

  if ((fd = open(TERM_DEVICE, O_RDWR)) == -1)
   {
    perror("terminal: Can't open device " TERM_DEVICE);
    return(1);
   }
            /* RS232 konfigurieren */
  if (tcgetattr(fd, &term_attr) != 0)
   {
    perror("terminal: tcgetattr() failed");
    return(1);
   }
  term_attr.c_cflag = TERM_SPEED | CS8 | CRTSCTS | CLOCAL | CREAD;
  term_attr.c_iflag = 0;
  term_attr.c_oflag = OPOST | ONLCR;
  term_attr.c_lflag = 0;
  if (tcsetattr(fd, TCSAFLUSH, &term_attr) != 0)
    perror("terminal: tcsetattr() failed");

            /* Std.-Eingabe anpassen */
  if (tcgetattr(STDIN_FILENO, &term_attr) != 0)
   {
    perror("terminal: tcgetattr() failed");
    return(1);
   }
            /* alte Einst. sichern */
  old_flags = term_attr.c_lflag;
  term_attr.c_lflag &= ~(ICANON | ECHO);
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_attr) != 0)
    perror("terminal: tcsetattr() failed");

  while (1)
   {
    FD_ZERO(&input_fdset);
    FD_SET(STDIN_FILENO, &input_fdset);
    FD_SET(fd, &input_fdset);
    if (select(fd+1, &input_fdset, NULL, NULL, NULL) == -1)
      perror("terminal: select() failed");
    if (FD_ISSET(STDIN_FILENO, &input_fdset))
     {
      if ((length = read(STDIN_FILENO, buffer, 16)) == -1)
        perror("terminal: read() failed");
      else
        if (buffer[0] == '\33')     /* Abbruch mit ESC */
      break;
    else
          write(fd, buffer, length);
     }
    if (FD_ISSET(fd, &input_fdset))
     {
      if ((length = read(fd, buffer, 16)) == -1)
        perror("terminal: read() failed");
      else
        write(STDOUT_FILENO, buffer, length);
     }
   }
            /* Std.-Eingabe wie vorher */
  term_attr.c_lflag = old_flags;
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_attr) != 0)
    perror("terminal: tcsetattr() failed");

  printf("Aborted.\n");
  close(fd);
  return(0);
 } 

Autor: Gast3 (Gast)
Datum:

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

Gast3

Autor: Phil J. (sunflower_seed)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Detlef _a (detlef_a)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Superberti (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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ß,

Autor: Phil J. (sunflower_seed)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Thomas Pircher (tpircher) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Phil J. (sunflower_seed)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Thomas Pircher (tpircher) Benutzerseite
Datum:

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

Autor: Phil J. (sunflower_seed)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das wär ne Idee.
;-)

Autor: Superberti (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.