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

5
我正在尝试从FPGA板读写数据。每当插入该板时,板子本身带有一个创建名为ttyUSB0的终端设备的驱动程序。在FPGA上,实现了一个异步接收器和发送器,它们似乎都工作正常。
然而,在C语言方面似乎存在问题。我一直在使用一些测试向量来测试FPGA是否输出正确的信息。我注意到了一些事情:
1. 设备有时无法正确打开。 2. 终端属性有时无法被检索或设置。 3. 读取有时是非阻塞的,并且不能检索到正确的值。
以下是我设置终端和文件描述符选项的方法。其中很多内容来自于这里: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个回答

5
我更喜欢POSIX操作系统串行编程指南
你应该删除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)。

我已经更新并发布了我的当前代码。由于某种原因,程序无法从板上读取任何内容,它只是在读取函数上等待。有什么想法吗? - sj755
1
你不需要使用 c_cflag &= ~PARENB,因为 cfmakeraw() 已经处理了这个。你可能确实需要 c_cflag &= ~CSTOPBc_cflag &= ~CRTSCTS,因为它们已被移除!其中任何一个都可能导致读取失败。 - sawdust
1
如果这些编辑正确完成,那么我就没有问题。也许你可以将这些更改应用到 Q 中的“更新”中?读取代码可能会有问题。如果你有一个 COM 端口或 USB 转 RS232 适配器,你可以隔离问题:将 TxD 连接到 RxD (即短接 DB9 的引脚 2 和 3),然后修改代码在读取之前发送一些数据以测试。你还可以将你的代码与这个经过测试的代码进行比较,尽管该端口是为非阻塞 I/O 打开的。 - sawdust
我已经更新了问题,包括编辑内容。将TxD绑定到RxD引脚需要一些时间,因为连接必须在硬件描述中进行,并且我必须实现一个新模块。 - sj755
嘿,我进行了更多的调试。程序不再停止接收数据,但它表现出一些奇怪的行为。 - sj755
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接