2013-08-07 31 views
31

Tôi đang cố gắng để gửi/nhận dữ liệu qua cổng USB bằng FTDI, vì vậy tôi cần xử lý giao tiếp nối tiếp bằng C/C++. Tôi đang làm việc trên Linux (Ubuntu).Đọc thêm/viết số

Về cơ bản, tôi được kết nối với thiết bị đang nghe lệnh đến. Tôi cần gửi các lệnh đó và đọc phản hồi của thiết bị. Cả hai lệnh và phản hồi là ký tự ASCII.

Mọi thứ hoạt động tốt bằng GtkTerm nhưng khi tôi chuyển sang lập trình C, tôi gặp sự cố.

Dưới đây là mã của tôi:

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

gì xảy ra là read() trả về 0 (không byte đọc ở tất cả) hoặc chặn cho đến khi thời gian chờ (VTIME). Tôi giả định điều này xảy ra bởi vì write() không gửi bất cứ điều gì. Trong trường hợp đó, thiết bị sẽ không nhận được lệnh và tôi không thể nhận phản hồi. Trong thực tế, tắt thiết bị trong khi chương trình của tôi bị chặn khi đọc thực sự được thu hút trong việc nhận phản hồi (thiết bị sẽ gửi thứ gì đó trong khi tắt).

Lạ một điều là rằng việc thêm này

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

ngay sau khi write() cuộc gọi, tôi nhận được:

I've written 6 bytes 

đó là chính xác những gì tôi mong đợi. Chỉ chương trình của tôi không hoạt động như mong muốn, như thiết bị của tôi không thể nhận được những gì tôi thực sự đang viết trên cổng.

Tôi đã thử những thứ và giải pháp khác nhau, cũng liên quan đến kiểu dữ liệu (tôi đã thử sử dụng std :: string, chẳng hạn như cmd = "INIT \r" hoặc const char) nhưng không có gì thực sự hiệu quả.

Ai đó có thể cho tôi biết tôi đang ở đâu sai?

Cảm ơn bạn trước.

EDIT: Trước đó phiên bản của mã này sử dụng

unsigned char cmd[] = "INIT \n"

và cũng cmd[] = "INIT \r\n". Tôi đã thay đổi nó vì lệnh sintax cho thiết bị của tôi được báo cáo là

<command><SPACE><CR>.

Tôi cũng đã cố gắng tránh cờ O_NONBLOCK để đọc, nhưng sau đó tôi chỉ chặn cho đến mãi mãi. Tôi đã thử sử dụng select() nhưng không có gì xảy ra. Chỉ cần cho một thử, tôi đã tạo ra một vòng lặp chờ đợi cho đến khi dữ liệu có sẵn, nhưng mã của tôi không bao giờ thoát khỏi vòng lặp. Btw, chờ đợi hoặc usleep() là điều tôi cần tránh. Báo cáo một trong những chỉ là một đoạn trích của mã của tôi. Mã hoàn chỉnh cần làm việc trong môi trường thời gian thực (cụ thể là OROCOS) vì vậy tôi không thực sự muốn chức năng giống như giấc ngủ.

Trả lời

54

Tôi đã giải quyết được sự cố của mình, vì vậy tôi đăng ở đây mã đúng trong trường hợp ai đó cần nội dung tương tự.

mở Cảng

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

Set thông số

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; 
} 

Viết

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

Đó là chắc chắn không phải cần thiết để viết byte mỗi byte, cũng int n_written = write(USB, cmd, sizeof(cmd) -1) hoạt động tốt.

Cuối cùng, đọc:

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; 
} 

một này làm việc cho tôi. Cảm ơn tất cả!

+0

Dòng của bạn 'response.append (&buf);' không biên dịch cho tôi. Không có 'while (buf! = '\ R' && n> 0);'. Là những lỗi chính tả hay tôi thiếu một cái gì đó? – josaphatv

+0

Mã bạn gợi ý cho việc gửi 'int n_written = write (USB, cmd, sizeof (cmd) -1)' không thực sự làm việc cho các bộ đệm lớn. Trong trường hợp đó, bạn cần một vòng lặp giống như bạn đã trình bày ở trên, nhưng với 'n_written = write (USB, cmd + spot, command_length - spot) 'Ngoài ra bạn phải sử dụng độ dài để chấm dứt vòng lặp, thay vì kiểm tra từng ký tự riêng lẻ – Fritz

+0

' cfmakeraw() 'đã thiết lập một loạt các thuộc tính. không cần những dòng này: 'tty.c_cflag & = ~ CSIZE; tty.c_cflag | = CS8; tty.c_cflag & = ~ PARENB;' – rumpel

0

Một số người nhận mong đợi EOL chuỗi, thường là hai nhân vật \r\n, vì vậy hãy thử trong mã của bạn thay thế dòng

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

với

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

BTW, cách trên có lẽ là hiệu quả hơn. Không cần phải trích dẫn mọi nhân vật.

+0

Ngoài ra, một số ổ USB có ghi nhanh bộ nhớ đệm có nghĩa là nó nằm trong bộ đệm trước khi thực sự được viết phải không? – Magn3s1um

+0

Cảm ơn bạn đã biết mẹo, nhưng '\ r \ n' không phải là vấn đề của tôi. Tôi đã thử nó và không có gì thực sự thay đổi. Những ý tưởng khác? – Lunatic999

+0

@ Lunatic999 Tôi chỉ đọc mã của bạn một lần nữa và dòng 'int n_written = write (USB, cmd, sizeof (cmd) -1);' loại phiền tôi. Bạn có sẵn lòng thử nghiệm bằng cách sử dụng 'int n_written = write (USB, cmd, strlen (cmd));'? Đó là những gì tôi có trong một chương trình tôi đã làm việc, thiết lập tương tự như của bạn, giao tiếp thông qua một ttyUSB. – radarhead

0

1) Tôi muốn thêm/n sau init. tức là: ghi (USB, "init \ n", 5);

2) Kiểm tra kỹ cấu hình cổng nối tiếp. Tỷ lệ cược là một cái gì đó là không chính xác trong đó. Chỉ vì bạn không sử dụng^Q/^ S hoặc điều khiển lưu lượng phần cứng không có nghĩa là phía bên kia không mong đợi nó.

3) Nhiều khả năng: Thêm một "usleep (100000); sau write() Các tập tin mô tả được thiết lập không để chặn hoặc chờ đợi, phải bao nhiêu thời gian thực hiện.? Nó phải được nhận và đệm bởi hạt nhân, thông qua các ngắt phần cứng hệ thống, trước khi bạn có thể đọc() nó.) Bạn đã cân nhắc sử dụng chọn() để đợi cho một cái gì đó để đọc()? Có lẽ với thời gian chờ?

Đã chỉnh sửa để thêm:

Bạn có cần dòng DTR/RTS không? Kiểm soát lưu lượng phần cứng cho phía bên kia gửi dữ liệu máy tính? ví dụ.

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

Cảm ơn bạn đã trả lời! Tôi đã thử những gì bạn đề xuất nhưng không có gì thay đổi. Tôi đã chỉnh sửa câu hỏi của mình với nhiều thông tin hơn ... Bất kỳ ý tưởng nào khác? Cảm ơn bạn một lần nữa – Lunatic999

+0

usleep() chỉ dành cho gỡ lỗi/thử nghiệm. Khi nó hoạt động, bạn có thể chuyển sang select() để chờ đợi thời gian cần thiết cho một phản hồi. – guest

+0

Bạn đã viết 7 byte như thế nào? Đó là "INIT \ r" là 6 byte ... Bạn có đang viết ký tự NULL sau không? Một số hệ thống giải thích một ký tự NULL như end-of-communication ... – guest

Các vấn đề liên quan