Lesen und schreiben auf die serielle Schnittstelle in C unter Linux
Ich versuche, senden/empfangen von Daten über eine USB-Schnittstelle mittels FTDI, also ich muss um die serielle Kommunikation mit C/C++. Ich arbeite Linux (Ubuntu).
Grundsätzlich bin ich mit einem Gerät verbunden, das ist das lauschen auf eingehende Befehle. Ich brauche zu senden, die Befehle und Gerät Lesen die Antwort. Beide Befehle und Antwort-sind ASCII-Zeichen.
Alles funktioniert gut mit GtkTerm aber, wenn ich Wechsel zu C Programmierung habe ich Probleme.
Hier ist mein code:
#include <stdio.h> //standard input /output functions
#include <stdlib.h>
#include <string.h> //string function definitions
#include <unistd.h> //UNIX standard function definitions
#include <fcntl.h> //File control definitions
#include <errno.h> //Error number definitions
#include <termios.h> //POSIX terminal control definitions
/* Open File Descriptor */
int USB = open( "/dev/ttyUSB0", O_RDWR| O_NONBLOCK | O_NDELAY );
/* Error Handling */
if ( USB < 0 )
{
cout << "Error " << errno << " opening " << "/dev/ttyUSB0" << ": " << strerror (errno) << endl;
}
/* *** Configure Port *** */
struct termios tty;
memset (&tty, 0, sizeof tty);
/* Error Handling */
if ( tcgetattr ( USB, &tty ) != 0 )
{
cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << endl;
}
/* Set Baud Rate */
cfsetospeed (&tty, B9600);
cfsetispeed (&tty, B9600);
/* Setting other Port Stuff */
tty.c_cflag &= ~PARENB; //Make 8n1
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag &= ~CRTSCTS; //no flow control
tty.c_lflag = 0; //no signaling chars, no echo, no canonical processing
tty.c_oflag = 0; //no remapping, no delays
tty.c_cc[VMIN] = 0; //read doesn't block
tty.c_cc[VTIME] = 5; //0.5 seconds read timeout
tty.c_cflag |= CREAD | CLOCAL; //turn on READ & ignore ctrl lines
tty.c_iflag &= ~(IXON | IXOFF | IXANY);//turn off s/w flow ctrl
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //make raw
tty.c_oflag &= ~OPOST; //make raw
/* Flush Port, then applies attributes */
tcflush( USB, TCIFLUSH );
if ( tcsetattr ( USB, TCSANOW, &tty ) != 0)
{
cout << "Error " << errno << " from tcsetattr" << endl;
}
/* *** WRITE *** */
unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '\r', '\0'};
int n_written = write( USB, cmd, sizeof(cmd) -1 );
/* Allocate memory for read buffer */
char buf [256];
memset (&buf, '\0', sizeof buf);
/* *** READ *** */
int n = read( USB, &buf , sizeof buf );
/* Error Handling */
if (n < 0)
{
cout << "Error reading: " << strerror(errno) << endl;
}
/* Print what I read... */
cout << "Read: " << buf << endl;
close(USB);
Was passiert, ist, dass read()
gibt 0 zurück (keine bytes gelesen) oder zu blockieren, bis timeout (VTIME
). Ich gehe davon aus, dass dies geschieht, weil write()
sendet nicht nichts. In diesem Fall Gerät würde nicht erhalten Befehl, und ich kann nicht erhalten, Reaktion. In der Tat, ausschalten des Geräts, während mein Programm blockiert, auf das Lesen eigentlich hat immer eine Antwort (Gerät sendet etwas beim Herunterfahren).
Merkwürdige ist, dass das hinzufügen dieser
cout << "I've written: " << n_written << "bytes" << endl;
direkt nach write()
Aufruf, erhalte ich:
I've written 6 bytes
das ist genau das, was ich erwarte. Nur mein Programm funktioniert nicht wie es sollte, wie mein Gerät nicht empfangen kann, was ich eigentlich schreiben auf port.
Habe ich verschiedene Sachen ausprobiert und Lösung, auch in Bezug auf Daten-Typen (ich habe versucht, mit std::string, wie cmd = "INIT \r"
oder const char
), aber nichts hat wirklich funktioniert.
Kann mir jemand sagen, wo ich falsch bin?
Vielen Dank im Voraus.
EDIT:
Zuvor version von diesem code verwendet
unsigned char cmd[] = "INIT \n"
und auch cmd[] = "INIT \r\n"
. Ich geändert es weil Befehl Syntax für mein Gerät ist gemeldet
<command><SPACE><CR>
.
Ich habe auch versucht zu vermeiden die O_NONBLOCK
Flagge auf dem Lesen, aber dann habe ich nur zu blockieren, bis für immer. Ich habe versucht, mit select()
aber es passiert nichts. Nur für einen Versuch, ich ' ve erstellt eine Warteschleife bis Daten verfügbar, aber mein code nie beendet die Schleife. Btw, warten oder usleep()
ist etwas, was ich vermeiden müssen. Gemeldet ist, ist nur ein Ausschnitt aus meinem code. Komplette code muss in einer Echtzeit-Umgebung (speziell OROCOS) also ich möchte wirklich nicht schlafen-like-Funktion.
std::cout
ist C++. Allgemein gesprochen, Ihre MCVE halten sollten, um eine Sprache, es sei denn, Sie vermuten eine schlechte Interaktion. Ansonsten einige Leute beginnen zu streiten C++ ist nicht C.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Hab ich gelöst meine Probleme, deshalb poste ich hier den richtigen code, falls jemand braucht ähnliche Sachen.
Offenen Port
Parameter festlegen
Schreiben
War es definitiv nicht nötig zu schreiben byte pro byte, auch
int n_written = write( USB, cmd, sizeof(cmd) -1)
fein gearbeitet.Endlich, Lesen:
Diese funktionierte für mich. Vielen Dank Euch allen!
response.append(&buf);
nicht kompilieren bei mir. Wederwhile(buf != '\r' && n > 0);
. Sind diese Tippfehler oder bin ich etwas fehlt?int n_written = write( USB, cmd, sizeof(cmd) -1)
funktioniert nicht wirklich für große Puffer. In diesem Fall müssten Sie eine Schleife wie die, die Sie oben zeigen, aber mitn_written = write( USB, cmd + spot, command_length - spot)
. Auch Sie müssten, um zu verwenden, der Länge nach beenden der Schleife anstelle der überprüfung jedes Zeichen einzeln.cfmakeraw()
enthält bereits eine Reihe von Parametern. Sie brauchen nicht diese Zeilen:tty.c_cflag &= ~CSIZE; tty.c_cflag |= CS8; tty.c_cflag &= ~PARENB;
Manche Empfänger erwarten, dass die EOL-Sequenz, die in der Regel zwei Zeichen
\r\n
, so versuchen Sie in Ihrem code ersetzen Sie die Zeilemit
BTW, in der oben beschriebenen Weise ist wahrscheinlich effizienter. Es gibt keine Notwendigkeit zu zitieren, jeder Charakter.
\r\n
ist nicht mein problem. Ich habe es versucht und nichts ändert sich wirklich. Andere Ideen?int n_written = write( USB, cmd, sizeof(cmd) -1 );
Art stört mich. Wären Sie bereit, zu testen, mitint n_written = write(USB, cmd, strlen(cmd));
? Das habe ich in einem Programm an dem ich gearbeitet habe, sehr ähnliches setup wie du, die Kommunikation durch eine ttyUSB.1) ich möchte hinzufügen, eine /n nach dem init. also schreiben( USB, "init\n", 5);
2) überprüfen Sie die serielle port-Konfiguration. Verschiedenheit ist etwas falsch es. Nur weil Sie nicht verwenden ^F/^S oder hardware flow control bedeutet nicht, dass die andere Seite nicht erwartet.
3) die Meisten wahrscheinlich: Hinzufügen "usleep(100000); nach der write(). Der Datei-Deskriptor ist nicht zu blockieren oder zu warten, richtig? Wie lange dauert es, bis eine Antwort zurück, bevor Sie anrufen können Sie Lesen? (Es werden empfangen und zwischengespeichert, die vom kernel über system-hardware-interrupts, bevor Sie read() es.) Haben Sie sich überlegt mit wählen Sie() zu warten, dass etwas read()? Vielleicht mit einem timeout?
Bearbeitet, um Hinzufügen:
Brauchen Sie die DTR/RTS Leitungen? Hardware-flow control, der sagt, die andere Seite zu senden die Rechner Daten? z.B.
\r\n
Ende. Werde ich Bearbeiten, meine Frage. Jedenfalls habe ich alles behoben und jetzt funktioniert es. Ich werde nach dem letzten code, falls jemand die brauchen sollte etwas ähnliches. Wirklich vielen Dank für Eure Antworten!