如何在Linux上正确设置串口通信

时间:2013-04-08 23:38:27

标签: c linux embedded serial-port termios

我正在尝试从FPGA板读取数据和向FPGA板写入数据。电路板本身附带了一个驱动器,无论何时插入电路板,都会创建一个名为ttyUSB0的终端设备。在FPGA上,实现了异步接收器和发送器,它们似乎有效。

但是,C方似乎存在问题。我一直在使用一些测试向量来测试FPGA是否正在输出正确的信息。我注意到了一些事情:

  1. 设备有时无法正常打开
  2. 终端属性有时无法检索或设置。
  3. 读取有时是非阻塞的,无法检索正确的值。
  4. 下面是我如何设置终端和文件描述符选项。其中大部分来自这里:http://slackware.osuosl.org/slackware-3.3/docs/mini/Serial-Port-Programming

    关于程序失败原因的任何建议或意见都会有很大帮助。

    #include <stdio.h>   // Standard input/output definitions
    #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
    
    int open_port(void){
    
        int fd;    // File descriptor for the port
        fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);
    
        if (fd == -1){
            fprintf(stderr, "open_port: Unable to open /dev/ttyUSB0 %s\n",strerror(errno));
            exit(EXIT_FAILURE);
        }
    
        return (fd);
    }
    
    int main(void){
    
        int fd = 0;              // File descriptor
        struct termios options;  // Terminal options
    
        fd = open_port();    // Open tty device for RD and WR
    
        fcntl(fd, F_SETFL);            // Configure port reading
        tcgetattr(fd, &options);       // Get the current options for the port
        cfsetispeed(&options, B230400);    // Set the baud rates to 230400
        cfsetospeed(&options, B230400);
    
        options.c_cflag |= (CLOCAL | CREAD);    // Enable the receiver and set local mode
        options.c_cflag &= ~PARENB;             // No parity bit
        options.c_cflag &= ~CSTOPB;             // 1 stop bit
        options.c_cflag &= ~CSIZE;              // Mask data size
        options.c_cflag |=  CS8;                // Select 8 data bits
        options.c_cflag &= ~CRTSCTS;            // Disable hardware flow control  
    
        // Enable data to be processed as raw input
        options.c_lflag &= ~(ICANON | ECHO | ISIG);
    
        // Set the new attributes
        tcsetattr(fd, TCSANOW, &options);
    
        ////////////////////////////////////
        // Simple read and write code here//
        ////////////////////////////////////
    
        // Close file descriptor & exit
        close(fd)
        return EXIT_SUCCESS
    }  
    

    更新 我根据第一个答案修改了我的代码。这就是我现在所拥有的:

    #include <errno.h>      // Error number definitions
    #include <stdint.h>     // C99 fixed data types
    #include <stdio.h>      // Standard input/output definitions
    #include <stdlib.h>     // C standard library
    #include <string.h>     // String function definitions
    #include <unistd.h>     // UNIX standard function definitions
    #include <fcntl.h>      // File control definitions
    #include <termios.h>    // POSIX terminal control definitions
    
    // Open usb-serial port for reading & writing
    int open_port(void){
    
        int fd;    // File descriptor for the port
        fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);
    
        if (fd == -1){
            fprintf(stderr, "open_port: Unable to open /dev/ttyUSB0 %s\n",strerror(errno));
            exit(EXIT_FAILURE);
        }
    
        return fd;
    }
    
    int main(void){
    
        int              fd = 0;     // File descriptor
        struct termios   options;    // Terminal options
        int              rc;         // Return value
    
        fd = open_port();            // Open tty device for RD and WR
    
        // Get the current options for the port
        if((rc = tcgetattr(fd, &options)) < 0){
            fprintf(stderr, "failed to get attr: %d, %s\n", fd, strerror(errno));
            exit(EXIT_FAILURE);
        }
    
        // Set the baud rates to 230400
        cfsetispeed(&options, B230400);
    
        // Set the baud rates to 230400
        cfsetospeed(&options, B230400);
    
        cfmakeraw(&options);
        options.c_cflag |= (CLOCAL | CREAD);   // Enable the receiver and set local mode
        options.c_cflag &= ~CSTOPB;            // 1 stop bit
        options.c_cflag &= ~CRTSCTS;           // Disable hardware flow control
        options.c_cc[VMIN]  = 1;
        options.c_cc[VTIME] = 2;
    
        // Set the new attributes
        if((rc = tcsetattr(fd, TCSANOW, &options)) < 0){
            fprintf(stderr, "failed to set attr: %d, %s\n", fd, strerror(errno));
            exit(EXIT_FAILURE);
        }
    
        ////////////////////////////////
            // Simple Read/Write Code Here//
            ////////////////////////////////
    
        // Close file descriptor & exit
        close(fd);
        return EXIT_SUCCESS;
    } 
    

    为了澄清,接收器和发送器使用8个数据位,1个停止位,没有校验位。

1 个答案:

答案 0 :(得分:5)

我更喜欢Serial Programming Guide for POSIX Operating Systems

你应该删除fcntl(mainfd, F_SETFL)语句,因为它不是必需的,也没有被错误地实现(F_GETFL没有事先做过,缺少第三个参数)。

尝试使用cfmakeraw设置非规范模式,因为您的初始化代码不完整:

options->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
        | INLCR | IGNCR | ICRNL | IXON);
options->c_oflag &= ~OPOST; 

对于非规范模式,您还需要定义

options.c_cc[VMIN]  = 1;
options.c_cc[VTIME] = 2;

1和2只是建议值。

所有系统调用之后添加对返回状态的测试。

rc = tcgetattr(mainfd, &options);
if (rc < 0) {
    printf("failed to get attr: %d, %s\n", mainfd, strerror(errno));
    exit (-3);
}

尝试使用较慢的波特率(例如115200甚至9600)进行测试。