2011-08-04 41 views
96

Tôi hơi bối rối về việc đọc và ghi vào cổng nối tiếp. Tôi có một thiết bị USB trong Linux sử dụng trình điều khiển chuyển đổi thiết bị nối tiếp USB FTDI. Khi tôi cắm nó vào, nó tạo ra:/dev/ttyUSB1.cách mở, đọc và ghi từ cổng nối tiếp trong C

Tôi nghĩ rằng việc mở và đọc/ghi từ nó trong C. là đơn giản, tôi biết tốc độ truyền và thông tin chẵn lẻ, nhưng có vẻ như không có tiêu chuẩn cho điều này?

Tôi có thiếu thứ gì đó hoặc ai đó có thể chỉ cho tôi đúng hướng không?

+14

Bạn đã xem [HOWTO lập trình nối tiếp] (http://tldp.org/HOWTO/Serial-Programming-HOWTO/) chưa? – ribram

+1

EDIT: Tôi sẽ xem liên kết của ribram. Tuy nhiên, điểm vẫn là trong khi thiết bị nối tiếp được biểu diễn dưới dạng tệp, các thiết bị thường có giao diện cụ thể hơn được triển khai thông qua các lệnh gọi hệ thống như 'ioctl' và' fcntl'. –

+0

Tôi đã không nhìn thấy như thế nào, tôi sẽ xem xét! – gnychis

Trả lời

193

Tôi đã viết điều này một thời gian dài trước đây và chỉ cần sao chép và dán các bit cần thiết vào từng dự án.

#include <errno.h> 
#include <fcntl.h> 
#include <string.h> 
#include <termios.h> 
#include <unistd.h> 

int 
set_interface_attribs (int fd, int speed, int parity) 
{ 
     struct termios tty; 
     memset (&tty, 0, sizeof tty); 
     if (tcgetattr (fd, &tty) != 0) 
     { 
       error_message ("error %d from tcgetattr", errno); 
       return -1; 
     } 

     cfsetospeed (&tty, speed); 
     cfsetispeed (&tty, speed); 

     tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;  // 8-bit chars 
     // disable IGNBRK for mismatched speed tests; otherwise receive break 
     // as \000 chars 
     tty.c_iflag &= ~IGNBRK;   // disable break processing 
     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_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl 

     tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls, 
             // enable reading 
     tty.c_cflag &= ~(PARENB | PARODD);  // shut off parity 
     tty.c_cflag |= parity; 
     tty.c_cflag &= ~CSTOPB; 
     tty.c_cflag &= ~CRTSCTS; 

     if (tcsetattr (fd, TCSANOW, &tty) != 0) 
     { 
       error_message ("error %d from tcsetattr", errno); 
       return -1; 
     } 
     return 0; 
} 

void 
set_blocking (int fd, int should_block) 
{ 
     struct termios tty; 
     memset (&tty, 0, sizeof tty); 
     if (tcgetattr (fd, &tty) != 0) 
     { 
       error_message ("error %d from tggetattr", errno); 
       return; 
     } 

     tty.c_cc[VMIN] = should_block ? 1 : 0; 
     tty.c_cc[VTIME] = 5;   // 0.5 seconds read timeout 

     if (tcsetattr (fd, TCSANOW, &tty) != 0) 
       error_message ("error %d setting term attributes", errno); 
} 


... 
char *portname = "/dev/ttyUSB1" 
... 
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC); 
if (fd < 0) 
{ 
     error_message ("error %d opening %s: %s", errno, portname, strerror (errno)); 
     return; 
} 

set_interface_attribs (fd, B115200, 0); // set speed to 115,200 bps, 8n1 (no parity) 
set_blocking (fd, 0);    // set no blocking 

write (fd, "hello!\n", 7);   // send 7 character greeting 

usleep ((7 + 25) * 100);    // sleep enough to transmit the 7 plus 
            // receive 25: approx 100 uS per char transmit 
char buf [100]; 
int n = read (fd, buf, sizeof buf); // read up to 100 characters if ready to read 

Các giá trị cho tốc độ được B115200, B230400, B9600, B19200, B38400, B57600, B1200, B2400, B4800 vv Các giá trị cho chẵn lẻ là 0 (nghĩa là không chẵn lẻ), PARENB|PARODD (kích hoạt tính chẵn lẻ và sử dụng lẻ), PARENB (cho phép tính chẵn lẻ và sử dụng đồng đều), PARENB|PARODD|CMSPAR (dấu chẵn lẻ) và PARENB|CMSPAR (khoảng trống không gian).

"Chặn" đặt xem read() trên cổng có đợi số ký tự được chỉ định đến không. Đặt không chặn có nghĩa là số tiền trả về read() tuy nhiên nhiều ký tự khả dụng mà không phải chờ thêm, tối đa giới hạn bộ đệm.


Phụ Lục:

CMSPAR là cần thiết chỉ cho việc lựa chọn nhãn hiệu và không gian chẵn lẻ, mà là không phổ biến. Đối với hầu hết các ứng dụng, nó có thể được bỏ qua. Tệp tiêu đề của tôi /usr/include/bits/termios.h chỉ cho phép định nghĩa CMSPAR chỉ khi biểu tượng tiền xử lý __USE_MISC được xác định. Định nghĩa đó xảy ra (trong features.h) với

#if defined _BSD_SOURCE || defined _SVID_SOURCE 
#define __USE_MISC  1 
#endif 

Các ý kiến ​​giới thiệu của <features.h> nói:

/* These are defined by the user (or the compiler) 
    to specify the desired environment: 

... 
    _BSD_SOURCE   ISO C, POSIX, and 4.3BSD things. 
    _SVID_SOURCE   ISO C, POSIX, and SVID things. 
... 
*/ 
+0

hoạt động hoàn hảo! cảm ơn rất nhiều vì đã chia sẻ điều này :) – gnychis

+0

Trình biên dịch của tôi phàn nàn CMSPAR là không xác định. Làm thế nào tôi có thể sửa lỗi này ? – Skeith

+0

@Skeith: Tôi đã sửa đổi câu trả lời của mình. – wallyk

27

Đối với mã demo mà phù hợp với tiêu chuẩn POSIX như mô tả trong Setting Terminal Modes ProperlySerial Programming Guide for POSIX Operating Systems, sau đây được cung cấp.
Về bản chất, nó bắt nguồn từ câu trả lời khác, nhưng các bình luận không chính xác và gây nhầm lẫn đã được sửa chữa.

#include <errno.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <termios.h> 
#include <unistd.h> 

int set_interface_attribs(int fd, int speed) 
{ 
    struct termios tty; 

    if (tcgetattr(fd, &tty) < 0) { 
     printf("Error from tcgetattr: %s\n", strerror(errno)); 
     return -1; 
    } 

    cfsetospeed(&tty, (speed_t)speed); 
    cfsetispeed(&tty, (speed_t)speed); 

    tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */ 
    tty.c_cflag &= ~CSIZE; 
    tty.c_cflag |= CS8;   /* 8-bit characters */ 
    tty.c_cflag &= ~PARENB;  /* no parity bit */ 
    tty.c_cflag &= ~CSTOPB;  /* only need 1 stop bit */ 
    tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */ 

    /* setup for non-canonical mode */ 
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); 
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 
    tty.c_oflag &= ~OPOST; 

    /* fetch bytes as they become available */ 
    tty.c_cc[VMIN] = 1; 
    tty.c_cc[VTIME] = 1; 

    if (tcsetattr(fd, TCSANOW, &tty) != 0) { 
     printf("Error from tcsetattr: %s\n", strerror(errno)); 
     return -1; 
    } 
    return 0; 
} 

void set_mincount(int fd, int mcount) 
{ 
    struct termios tty; 

    if (tcgetattr(fd, &tty) < 0) { 
     printf("Error tcgetattr: %s\n", strerror(errno)); 
     return; 
    } 

    tty.c_cc[VMIN] = mcount ? 1 : 0; 
    tty.c_cc[VTIME] = 5;  /* half second timer */ 

    if (tcsetattr(fd, TCSANOW, &tty) < 0) 
     printf("Error tcsetattr: %s\n", strerror(errno)); 
} 


int main() 
{ 
    char *portname = "/dev/ttyUSB0"; 
    int fd; 
    int wlen; 

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC); 
    if (fd < 0) { 
     printf("Error opening %s: %s\n", portname, strerror(errno)); 
     return -1; 
    } 
    /*baudrate 115200, 8 bits, no parity, 1 stop bit */ 
    set_interface_attribs(fd, B115200); 
    //set_mincount(fd, 0);    /* set to pure timed read */ 

    /* simple output */ 
    wlen = write(fd, "Hello!\n", 7); 
    if (wlen != 7) { 
     printf("Error from write: %d, %d\n", wlen, errno); 
    } 
    tcdrain(fd); /* delay for output */ 


    /* simple noncanonical input */ 
    do { 
     unsigned char buf[80]; 
     int rdlen; 

     rdlen = read(fd, buf, sizeof(buf) - 1); 
     if (rdlen > 0) { 
#ifdef DISPLAY_STRING 
      buf[rdlen] = 0; 
      printf("Read %d: \"%s\"\n", rdlen, buf); 
#else /* display hex */ 
      unsigned char *p; 
      printf("Read %d:", rdlen); 
      for (p = buf; rdlen-- > 0; p++) 
       printf(" 0x%x", *p); 
      printf("\n"); 
#endif 
     } else if (rdlen < 0) { 
      printf("Error from read: %d: %s\n", rdlen, strerror(errno)); 
     } 
     /* repeat read to get full message */ 
    } while (1); 
} 

Để làm cho chương trình xử lý dữ liệu nhận được dưới dạng mã ASCII, hãy biên dịch chương trình bằng biểu tượng DISPLAY_STRING, ví dụ:

cc -DDISPLAY_STRING demo.c 
+1

Rất nhiều thứ có thể được thay thế chỉ bằng 'cfmakeraw' đúng không? – CMCDragonkai

+0

Các ví dụ khác mà tôi đã thấy cũng mở cổng bằng 'O_NDELAY' hoặc' O_NONBLOCK'. Http://www.cmrr.umn.edu/~strupp/serial.html đề cập rằng nếu bạn mở bộ mô tả tệp bằng các cờ đó, thì 'VTIME' bị bỏ qua. Vậy sự khác nhau giữa việc chạy với bộ mô tả tập tin 'O_NONBLOCK' so với việc thực hiện nó với' VTIME' là gì? – CMCDragonkai

+0

@CMCDragonkai - Nó phức tạp hơn nhiều so với những gì bạn viết. Xem http://stackoverflow.com/questions/25996171/linux-blocking-vs-non-blocking-serial-read tham chiếu câu trả lời được chấp nhận cho câu hỏi này.BTW ngay cả khi bạn mở thiết bị đầu cuối ở chế độ không chặn, bạn vẫn có thể hoàn nguyên về chế độ chặn bằng ** fcntl() ** – sawdust

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