如何在Linux上正确设置串口通讯 [英] How to properly set up serial communication on Linux

查看:172
本文介绍了如何在Linux上正确设置串口通讯的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


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


  1. 设备有时无法正确打开

  2. 终端属性有时无法被检索或设置。

  3. 读取有时是非阻塞的,不能检索正确的值。

下面是我如何设置终端和文件描述符选项。其中大部分是从这里获取的: http://slackware.osuosl.org/slackware- 3.3 / docs / mini /串口编程



有关为什么程序可能失败的任何建议或意见将会非常有帮助。 >

  #include< stdio.h> //标准输入/输出定义
#include< string.h> // String function definitions
#include< unistd.h> // UNIX标准函数定义
#include< fcntl.h> //文件控制定义
#include< errno.h> //错误号码定义
#include< termios.h> // POSIX终端控件定义

int open_port(void){

int fd; //端口的文件描述符
fd = open(/ dev / ttyUSB0,O_RDWR | O_NOCTTY);

if(fd == -1){
fprintf(stderr,open_port:Unable to open / dev / ttyUSB0%s\\\
,strerror(errno));
exit(EXIT_FAILURE);
}

return(fd);
}

int main(void){

int fd = 0; //文件描述符
struct termios选项; //终端选项

fd = open_port(); //为RD和WR打开tty设备

fcntl(fd,F_SETFL); //配置端口读取
tcgetattr(fd,& options); //获取端口
cfsetispeed(& options,B230400)的当前选项; //将波特率设置为230400
cfsetospeed(& options,B230400);

options.c_cflag | =(CLOCAL | CREAD); //启用接收器并设置本地模式
options.c_cflag& =〜PARENB; //无奇偶校验位
options.c_cflag& =〜CSTOPB; // 1 stop bit
options.c_cflag& =〜CSIZE; // Mask data size
options.c_cflag | = CS8; //选择8个数据位
options.c_cflag& =〜CRTSCTS; //禁用硬件流控制

//允许以原始输入处理数据
options.c_lflag& =〜(ICANON | ECHO | ISIG);

//设置新属性
tcsetattr(fd,TCSANOW,& options);

////////////////////////////////
//简单的读写代码//
//////////////////////////////// $
$ b //关闭文件描述符&退出
关闭(fd)
返回EXIT_SUCCESS
}

UPDATE
我已经根据第一个答案修改了我的代码。这是我现在所在:

  #include< errno.h> //错误号定义
#include< stdint.h> // C99固定数据类型
#include< stdio.h> //标准输入/输出定义
#include< stdlib.h> // C标准库
#include< string.h> // String function definitions
#include< unistd.h> // UNIX标准函数定义
#include< fcntl.h> //文件控制定义
#include< termios.h> // POSIX终端控制定义

//打开usb-serial端口读取&写
int open_port(void){

int fd; //端口的文件描述符
fd = open(/ dev / ttyUSB0,O_RDWR | O_NOCTTY);

if(fd == -1){
fprintf(stderr,open_port:Unable to open / dev / ttyUSB0%s\\\
,strerror(errno));
exit(EXIT_FAILURE);
}

return fd;
}

int main(void){

int fd = 0; //文件描述符
struct termios选项; //终端选项
int rc; //返回值

fd = open_port(); //为RD和WR打开tty设备

//获取端口的当前选项
if((rc = tcgetattr(fd,& options))< 0){
fprintf(stderr,无法获取attr:%d,%s\\\
,fd,strerror(errno));
exit(EXIT_FAILURE);
}

//将波特率设置为230400
cfsetispeed(& options,B230400);

//将波特率设置为230400
cfsetospeed(& options,B230400);

cfmakeraw(& options);
options.c_cflag | =(CLOCAL | CREAD); //启用接收器并设置本地模式
options.c_cflag& =〜CSTOPB; // 1 stop bit
options.c_cflag& =〜CRTSCTS; //禁用硬件流控制
options.c_cc [VMIN] = 1;
options.c_cc [VTIME] = 2;

//设置新属性
if((rc = tcsetattr(fd,TCSANOW,& options))< 0){
fprintf(stderr,failed to set attr:%d,%s\\\
,fd,strerror(errno));
exit(EXIT_FAILURE);
}

////////////////////////////
/ /简单读/写代码//
////////////////////////////

//关闭文件描述符&退出
close(fd);
return EXIT_SUCCESS;
}

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

解决方案

我更喜欢 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\\\
,mainfd,strerror(errno));
exit(-3);
}

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


I'm attempting to read and write data from and to an FPGA board. The board itself came with a driver that create a terminal device called ttyUSB0 whenever the board is plugged in. On the FPGA, an asynchronous receiver and transmitter were implemented, and they seem to work.

However, there seems to be an issue on C side of things. I've been using some test vectors to test if the FPGA is outputting the proper information. I've noticed a few things things:

  1. The device sometimes does not open correctly
  2. The terminal attributes sometimes fail to be retrieved or set.
  3. The read is sometimes non-blocking and doesn't retrieve the proper value.

Below is how I set up terminal and file descriptor options. Much of it was taken from here: http://slackware.osuosl.org/slackware-3.3/docs/mini/Serial-Port-Programming

Any advice or comments as to why the program may be failing would be greatly helpful.

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

UPDATE I've modified my code based on the first answer. This is what I have now:

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

Just to clarify, the receiver and transmitter use 8 data bits, 1 stop bit, and no parity bit.

解决方案

I prefer Serial Programming Guide for POSIX Operating Systems.

You should delete the fcntl(mainfd, F_SETFL) statement, since it's not required and incorrectly implemented (F_GETFL not done prior and missing third argument).

Try using cfmakeraw to setup non-canonical mode, since your initialization code is incomplete:

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

For non-canonical mode, you also need to define

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

1 and 2 are just suggested values.

Add testing of return status after all system calls.

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

Try testing with slower baudrates (e.g. 115200 or even 9600).

这篇关于如何在Linux上正确设置串口通讯的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆