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

Problems with read() of a Serial Port (USB-RS232) using C language

大家好,

我刚开始为我的研究编程,它需要使用通过串行端口(USB 到 RS232,使用 /dev/ttyUSB0) 到计算机。电脑的OS是Linux14.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); 
     }

也许这段代码与我在网上找到的其他代码相似,Open Port 也与此相似,但不是 read() 函数而是 write()。

问题出在读取函数上,使用当前代码我无法读取来自 RS232 的任何值。电脑可以向RS232发送一个ASCII码,但是需要接收的时候,没有return值,以程序为例运行就是这段代码,程序循环了好几遍,但是程序卡在 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 

正如您从循环中看到的那样,有一条错误消息导致同一部分重复并一直重复自己。最奇怪的是,如果我取消这个错误,这意味着我只是忽略错误生成的循环修改其他 codes:The 信号继续到其他循环,直到它最终移动遍历机制!即使它仍然发送与循环显示完全相同的错误。

然后你可能会问,没有循环工作,但问题是错误消息是指示遍历机制是否达到其所处轨道极限的标志,如果没有警告我可能会破坏遍历机制!所以我肯定需要这个反馈回路来运行。

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

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

您的串行终端初始化使用了糟糕的编码实践。将 termios 结构清零不符合 POSIX。

what do you refer with Zeroing the termios structure?

只需在您的代码中搜索 "zero",您就会找到有问题的语句:

    bzero(&newtio, sizeof(newtio));

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

Also the code use to work some years ago, because the traverse mechanism was used in previous research, more recently it hasn't been able to move with this code.

这就是代码编写不当的问题。它不可移植,并且可能无法在不同的系统或不同的日期工作。


The problem comes here at the read function, where using the current code I can't read any value coming from the RS232.

与评论或您的文字描述不匹配的代码在一定程度上阻碍了分析。
但是您的代码存在三个明显的问题。

如前所述,串行终端初始化不 POSIX 兼容。

日志表明您正在一些神秘的输出例程和您执行的输入例程之间循环 post 以供审查。
输入例程的每次执行都会在串行终端上执行 open()、初始化和 close() 序列。 据推测,输出例程(尚未 posted)执行类似的 open()、初始化和 close() 序列在串行终端上。

这种重复打开、初始化和关闭串行终端的顺序非常低效,并且在适当的程序设计下完全没有必要。
这是第二个问题。

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

    tcflush(fdi, TCIFLUSH);

你后续的read()(不管你怎么配置VMIN和VTIME)只能returnflush操作后收到的数据
在刷新操作之前串行终端未打开 and/or 时,您的程序会丢失所有接收到的数据。

如果您的程序在启动时只打开并初始化串行终端一次以进行读取和写入,那么第三个问题就会消失。