使用C语言的串行端口(USB-RS232)的read()问题

时间:2018-11-08 03:39:47

标签: c serial-port ubuntu-14.04 communication termios

大家好,

我刚刚开始为我的研究编程,该研究需要使用通过串行端口(USB到RS232,使用/ dev / ttyUSB0)连接的移动机制(类似于只能在3轴XYZ上移动的机械手)计算机。该计算机的操作系统为Linux 14.04,由于内核原因,我无法使用更高级的版本,并且该程序的运行臂也在程序中使用C语言。

我一直在使用的此代码是2000年以来开发的,但是最近几年一直没有使用该程序。自从我开始这个项目以来,我遇到了几个问题,但最后我总是崩溃在同一堵墙上,这是程序内部引起的永恒循环,因为C的read()函数无法正确进行读取数据来自串行端口。

/* For 232cOUT() and 232cIN() */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include "traverse.h"

rs232cIN(mojiin,status)
unsigned char *mojiin;
unsigned char *status;{
     int fdi,c,res,j,w;
     struct termios oldtio,newtio;
     struct termios oldscr;
     printf("ok0i\n");
     fdi = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); 
     if (fdi <0) {perror(MODEMDEVICE); exit(-1); }

    tcgetattr(fdi,&oldtio); /* save current port settings --> For tty device */
    tcgetattr(1,&oldscr);  /* save current port settings --> For display device */
    printf("fdi=%d\n",fdi);
    printf("ok1i\n");
    bzero(&newtio, sizeof(newtio));
    newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD | CSTOPB;
    newtio.c_iflag = IGNPAR | ICRNL;  
    newtio.c_oflag = 0;
    printf("ok2i\n");
    /* set input mode (non-canonical, no echo,...) */
    newtio.c_lflag = 0;

    newtio.c_cc[VTIME]    = 5;   /* inter-character timer unused */
    newtio.c_cc[VMIN]     = 0;   /* blocking read until 5 chars received */

    tcflush(fdi, TCIFLUSH);
    tcsetattr(fdi,TCSANOW,&newtio);
    tcsetattr(1,TCSANOW,&newtio); /* stdout settings like modem settings */

    printf("ok3i\n");
    res = read(fdi,mojiin,256);   /* return after 5 chars have been input */        
    printf("res=%d\n",res);
     printf("ok4i\n");
    mojiin[res]=0;              /* so we can printf... */
    #if 1
         printf("Data import Good!!\n\r");
         printf("Imported-MOJIRETSU:%c\n\r",mojiin[0]);   
         printf("Imported-MOJISU:%d\n\r", res);   
     #endif
     status[0]=mojiin[0];       
     //sleep(1);       /*  this command is very important */
     usleep(SLEEPUSEC2);
     tcsetattr(fdi,TCSANOW,&oldtio);
     tcsetattr(1,TCSANOW,&oldscr);

     close(fdi); 
     }

也许这段代码与我在网络上找到的其他代码相似,开放端口也与此类似,但是用read()函数代替了write()函数。

问题出在读取功能上,使用当前代码,我无法读取来自RS232的任何值。计算机可以将ASCII码发送到RS232,但是当需要接收时,它没有返回值,例如,该代码正在运行,例如,程序正在运行多个循环,但程序陷入了loopin1。

读取结果为res,MOJIRETSU和MOJISU。如果您检查fdi = 3,则表示它正在从RS232端口读取某些内容,但是当它到达res时,该值为0且MOJIRETSU不显示任何值,而MOJISU等于0。从MOJISU它将创建状态随后会在状态标志上生成错误。

==========  [Y_AXIS] Traversing START!! ========== 
Delta(Traverse) -->0.000000 
loopin1ok0o 
ok1o 
ok2o 
MOJISU=4 
ok3o 
     ZR 

MOJIOUT=ZR 
Sending OK 

ok4o 
ok0i 
fdi=3 
ok1i 
ok2i 
ok3i 
    res=0 
         ok4i 
             Data import Good!! 
Imported-MOJIRETSU: 
Imported-MOJISU:0 
status=0 
STATUS ERROR 0x0 !!!! 
STATUS FLAG=-4 
ok0o 
ok1o 
ok2o 
MOJISU=4 
ok3o 
     ZR 

MOJIOUT=ZR 
Sending OK 

ok4o 
ok0i 
fdi=3 
ok1i 
ok2i 
ok3i 
    res=0 
         ok4i 
             Data import Good!! 
Imported-MOJIRETSU: 
Imported-MOJISU:0 
status=0 
STATUS ERROR 0x0 !!!! 
STATUS FLAG=-4 
^C 

从循环中可以看到,有一条错误消息,该错误消息导致同一部分一直重复并不断重复。最奇怪的是,如果我取消此错误,则意味着我只是忽略该循环,该错误会生成其他代码的修改:信号继续进行其他循环,直到最终移动了遍历机构!即使它仍然发送与循环所示完全相同的错误。

然后您可能会问,没有循环就可以工作,但是问题是错误消息是一些标志,这些标志指示横移机制是否达到了它所站立的滑轨的极限,并且没有警告我可能会破坏遍历机制!因此,绝对地,我需要这个反馈回路来进行操作。

系统以非规范方式工作,我将VTIME调整为几个值,并且不更改输出状态。如果更改VMIN,则程序将卡住,并且仅在关闭终端后才能停止它。

即使没有循环,遍历机制也能发送错误,这确实很奇怪。因此,如果有人知道如何操作read()函数,那么我真的需要建议,这样我才能真正获得res,MOJIRETSU或MOJISU的值,并在不删除此反馈循环的情况下操作遍历机制。

1 个答案:

答案 0 :(得分:0)

您的串行终端初始化使用的编码习惯很差。清零termios结构不符合POSIX。

  

归零术语结构指的是什么?

只需在代码中搜索“零”,就会发现令人反感的语句:

    bzero(&newtio, sizeof(newtio));

请参阅Setting Terminal Modes ProperlySerial Programming Guide for POSIX Operating Systems了解初始化串行终端的正确方法。

  

该代码也可以在几年前使用,因为遍历机制已在以前的研究中使用,所以最近它无法随该代码一起使用。

这就是编写不良代码的问题。它不是便携式的,并且可能无法在其他系统或不同日期使用。


  

问题出在读取功能上,使用当前代码,我无法读取来自RS232的任何值。

与注释或文本描述不匹配的代码在某种程度上阻碍了分析。
但是,您的代码存在三个明显的问题。

如上所述,串行终端初始化不符合POSIX。

该日志表明您正在某些神秘的输出例程和您发布以供查看的输入例程之间循环。
输入例程的每次执行都会在串行终端上执行 open(),初始化和 close()序列。 大概输出例程(尚未发布)在串行终端上执行类似的 open(),初始化和 close()序列。

这种重复的打开,初始化和关闭串行终端的步骤效率极低,并且在正确的程序设计下完全没有必要。
那是第二个问题。

第三个问题是前一个问题的直接结果。
作为读取初始化的一部分,您将显式丢弃接收缓冲区中可能存在的任何数据:

    tcflush(fdi, TCIFLUSH);

后续的 read()(无论如何配置VMIN和VTIME)只能返回在刷新操作之后接收到的数据。
在串行终端未打开时和/或在刷新操作之前接收到的所有数据都会丢失到您的程序中。

如果您的程序在启动时仅一次打开并初始化串行终端以进行读写,则第三个问题将消失。