【问题标题】:How to properly set up serial communication on Linux如何在 Linux 上正确设置串行通信
【发布时间】:2013-04-08 23:38:26
【问题描述】:

我正在尝试在 FPGA 板上读写数据。该板本身带有一个驱动程序,该驱动程序在插入该板时创建一个名为 ttyUSB0 的终端设备。在 FPGA 上,实现了异步接收器和发送器,它们似乎可以工作。

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

  1. 设备有时无法正确打开
  2. 有时无法检索或设置终端属性。
  3. 读取有时是非阻塞的,不会检索到正确的值。

以下是我如何设置终端和文件描述符选项。大部分内容来自这里:http://slackware.osuosl.org/slackware-3.3/docs/mini/Serial-Port-Programming

任何关于程序失败原因的建议或 cmet 都会非常有帮助。

#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 个停止位,并且没有奇偶校验位。

【问题讨论】:

    标签: c linux embedded serial-port termios


    【解决方案1】:

    我更喜欢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)进行测试。

    【讨论】:

    • 我进行了更新并发布了我当前的代码。由于某种原因,程序无法从板上读取任何内容,它只是等待读取功能。有什么想法吗?
    • 您不需要c_cflag &amp;= ~PARENB,因为cfmakeraw() 会处理这个问题。您可能确实需要 c_cflag &amp;= ~CSTOPBc_cflag &amp;= ~CRTSCTS,它们已被删除!这些都可能扼杀阅读。
    • 我已经添加了 1 个停止位标志并禁用了硬件流控制,但程序仍然等待读取功能。原始代码往往会导致程序崩溃,但有时它确实从板上正确读取,因此板肯定在传输某些东西。还有其他想法吗?
    • 我刚刚在硬件上做了一些调试,看来板子内部产生了正确的结果。我希望我设计的发射器控制器可以正常工作,并且 PC 有时会收到正确的结果。在这一点上,我 90% 确定问题完全出在 C 代码上。
    • 如果这些编辑正确完成,那么我看不出有问题。也许您可以将这些更改应用于 Q 中的“更新”?读取的代码可能是个问题。如果您有 COM 端口或 USB-to-RS232 适配器,您可以隔离问题:进行环回将 TxD 绑定到 RxD(即 DB9 的短引脚 2 和 3)。然后在阅读之前破解您的代码以发送一些数据。您也可以将您的代码与this tested code 进行比较,尽管该端口已为非阻塞 I/O 打开。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-12
    • 1970-01-01
    • 2010-10-17
    • 1970-01-01
    • 2017-10-15
    • 2011-04-21
    • 2012-10-19
    相关资源
    最近更新 更多