2013-08-07 13 views
31

FTDI kullanarak bir USB Portu üzerinden veri gönderiyorum/almak için veriyorum, bu yüzden C/C++ kullanarak seri iletişimi kullanmam gerekiyor. Linux (Ubuntu) üzerinde çalışıyorum. Temel olarak, gelen komutları dinleyen bir aygıta bağlandım. Bu komutları göndermem ve cihazın cevabını okumam gerek. Her iki komut ve yanıt ASCII karakterleri'dur.Linux C Seri Port Okuma/Yazma

GtkTerm kullanarak her şey iyi çalışıyor, ancak C programlamaya geçtiğimde sorunla karşılaşıyorum. Zaman aşımı (VTIME) kadar

#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); 

ne olur read() döner 0 (byte hiç okuma) olmasıdır veya blok:

İşte benim kod. Bunun olduğunu varsayıyorum, çünkü write() hiçbir şey göndermiyor. Bu durumda, cihaz komut almazdı ve yanıt alamıyorum. Aslında, programımı bloke ederken cihazımı kapatırken aslında yanıt almayı başardı (cihaz kapanırken bir şey gönderir).

Garip bir şey olduğunu bu

cout << "I've written: " << n_written << "bytes" << endl; 

sağ write() çağrısından sonra, Aldığım ekleyerek:

tam beklediğim budur
I've written 6 bytes 

. Sadece benim programım gerektiği gibi çalışmıyor, gibi cihazım aslında portta yazdığım şeyi alamıyor.

Veri türleri ile ilgili farklı şeyler ve çözüm denedim (örneğin, cmd = "INIT \r" veya const char gibi std :: string'i kullanarak denedim) ama gerçekten işe yaramadı.

Birisi bana nerede yanlış olduğumu söyleyebilir mi?

Önceden teşekkür ederiz.

DÜZENLEME: Bu kodun Önceden sürüm

unsigned char cmd[] = "INIT \n"

ve ayrıca cmd[] = "INIT \r\n"

kullandı. Cihazım için komut sintaksı

<command><SPACE><CR> olarak bildirildiği için değiştirdim.

Ayrıca okumada O_NONBLOCK bayrağından kaçınmayı denedim, ancak daha sonra sadece sonsuza dek bloke ediyorum. select() kullanmayı denedim ama hiçbir şey olmuyor. Sadece bir deneme için, veriler uygun olana kadar bir bekleme döngüsü oluşturdum, fakat kodum asla döngüden çıkmıyor. Btw, beklemek veya usleep() kaçınmak gereken bir şey. Bildirilen bir tanesi sadece kodumun bir alıntıdır. Tam kodun, gerçek zamanlı bir ortamda çalışmak üzere (özellikle OROCOS) çalışması gerekiyor, bu yüzden gerçekten uyku benzeri bir işlev istemiyorum.

cevap

54

Sorunlarımı çözdüm, bu yüzden birilerinin benzer şeylere ihtiyacı olması durumunda doğru kodu buraya gönderiyorum.

Açık Liman

int USB = open("/dev/ttyUSB0", O_RDWR| O_NOCTTY); 

parametrelerini ayarlayın

struct termios tty; 
struct termios tty_old; 
memset (&tty, 0, sizeof tty); 

/* Error Handling */ 
if (tcgetattr (USB, &tty) != 0) { 
    std::cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << std::endl; 
} 

/* Save old tty parameters */ 
tty_old = tty; 

/* Set Baud Rate */ 
cfsetospeed (&tty, (speed_t)B9600); 
cfsetispeed (&tty, (speed_t)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_cc[VMIN] = 1;     // 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 

/* Make raw */ 
cfmakeraw(&tty); 

/* Flush Port, then applies attributes */ 
tcflush(USB, TCIFLUSH); 
if (tcsetattr (USB, TCSANOW, &tty) != 0) { 
    std::cout << "Error " << errno << " from tcsetattr" << std::endl; 
} 

Yaz

unsigned char cmd[] = "INIT \r"; 
int n_written = 0, 
    spot = 0; 

do { 
    n_written = write(USB, &cmd[spot], 1); 
    spot += n_written; 
} while (cmd[spot-1] != '\r' && n_written > 0); 

Kesinlikle değildi bayt başına bayt yazmak için gerekli, ayrıca int n_written = write(USB, cmd, sizeof(cmd) -1) iyi çalıştı.

Sonunda, okuyun: Bu seferki benim için çalıştı

int n = 0, 
    spot = 0; 
char buf = '\0'; 

/* Whole response*/ 
char response[1024]; 
memset(response, '\0', sizeof response); 

do { 
    n = read(USB, &buf, 1); 
    sprintf(&response[spot], "%c", buf); 
    spot += n; 
} while(buf != '\r' && n > 0); 

if (n < 0) { 
    std::cout << "Error reading: " << strerror(errno) << std::endl; 
} 
else if (n == 0) { 
    std::cout << "Read nothing!" << std::endl; 
} 
else { 
    std::cout << "Response: " << response << std::endl; 
} 

. Hepinize teşekkür ederim!

+0

Sizin satırınız response.append (&buf); 'benim için derleme yapmıyor. Ne yapmıyorsa (buf! = '\ R' && n> 0);' Bu yazım hataları mı yoksa bir şey mi eksik? – josaphatv

+0

int n_written = write (USB, cmd, sizeof (cmd) -1) 'i gerçekten önermek için büyük önbellekler için işe yaramaz. Bu durumda, yukarıda gördüğünüz gibi bir döngüye ihtiyacınız olacaktır, ancak n_written ile = Yazma (USB, cmd + nokta, command_length - spot) Ayrıca, her karakteri ayrı ayrı kontrol etmek yerine, döngüyü sonlandırmak için uzunluğu kullanmanız gerekir: – Fritz

+0

'cfmakeraw()' zaten bir grup özniteliği ayarlar. şu satırlara gerek yoktur: , tty.c_cflag & = ~ CSIZE; tty.c_cflag | = CS8; tty.c_cflag & = ~ PARENB; – rumpel

0

Bazı alıcılar genellikle iki karakter \r\n olan EOL dizisi, bekliyoruz, bu yüzden BTW, yukarıdaki yolu muhtemelen daha verimlidir

unsigned char cmd[] = "INIT\r\n"; 

çizgiyi

unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '\r', '\0'}; 

yerine kodunuzda deneyin. Her karakteri alıntılamaya gerek yok.

+0

Ayrıca, bazı USB sürücüler hızlı yazma önbelleğe alma özelliğine sahiptir; – Magn3s1um

+0

İpucu için teşekkürler, ama '\ r \ n' benim sorunum değil. Denedim ve hiçbir şey gerçekten değişmedi. Diğer fikirler? – Lunatic999

+0

@ Lunatic999 Sadece kodunuzu tekrar okuyorum ve int n_written = yazma (USB, cmd, sizeof (cmd) -1); tür beni rahatsız ediyor. Int n_written = write (USB, cmd, strlen (cmd)) kullanarak test etmek ister misiniz? Çalıştığım bir programda var, benimki gibi çok benzer bir kurulum, bir ttyUSB aracılığıyla iletişim. – radarhead

0

1) Init sonra a/n ekleyebilirim. yani yazımı (USB, "init \ n", 5);

2) Seri bağlantı noktası yapılandırmasını iki kez kontrol edin. Oranlar orada yanlış bir şeydir. Sadece^Q/^ S kullanmıyorsanız veya donanım akış kontrolü diğer tarafın beklemediği anlamına gelmez.

3) Büyük olasılıkla: bir "usleep (100000) ekleyin; yazılmasından sonra() dosya tanımlayıcısı doğru ne kadar sürer, engellemek ya da beklemek değil ayarlanır.? Bir yanıtı geri almak için önce okuyabilirsiniz? (oku() 'den önce), sistem donanım kesmeleri aracılığıyla çekirdeğin alınması ve tamponlanması gerekir. select()'u kullanmayı düşündünüz mü? için bir şey okumak için()? Belki bir zaman aşımı ile?

Düzenlendi Düzenleme:

DTR/RTS satırlarına ihtiyacınız var mı? Diğer tarafa bilgisayar verilerini göndermesini söyleyen donanım akış denetimi? Örneğin.

int tmp, serialLines; 

cout << "Dropping Reading DTR and RTS\n"; 
ioctl (readFd, TIOCMGET, & serialLines); 
serialLines &= ~TIOCM_DTR; 
serialLines &= ~TIOCM_RTS; 
ioctl (readFd, TIOCMSET, & serialLines); 
usleep(100000); 
ioctl (readFd, TIOCMGET, & tmp); 
cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl; 
sleep (2); 

cout << "Setting Reading DTR and RTS\n"; 
serialLines |= TIOCM_DTR; 
serialLines |= TIOCM_RTS; 
ioctl (readFd, TIOCMSET, & serialLines); 
ioctl (readFd, TIOCMGET, & tmp); 
cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl; 
+0

Cevabınız için teşekkür ederiz! Önerdiğin şeyi denedim ama hiçbir şey değişmedi. Sorumu daha fazla bilgi ile düzenledim ... Başka bir fikir var mı? Tekrar teşekkürler – Lunatic999

+0

usleep() sadece hata ayıklama/test içindir. Çalışmaya başladıktan sonra, bir yanıt için gerekli zamanı beklemek üzere() öğesini seçebilirsiniz. – guest

+0

7 bayt nasıl yazdınız? 6 bayt olan "INIT \ r" ... Takip eden NULL karakterini mi yazıyorsunuz? Bazı sistemler NULL karakterini iletişim sonu olarak yorumluyor ... – guest