我想从COM端口连续接收数据,同时要写入文件

我想读取串行COM端口并将数据写入LINUX中的文件。 其实我是从其他电脑的超级terminal发送数据。

问题是没有while循环,我只能写一行。

但while(1)循环,我不能写任何文件。

否则我必须发送BIG文件,然后应用程序退出/终止并写入文件。

我的应用程序应该写入数据(可能是2行或任何东西); 之后它必须等待下一个数据。

所以请帮我解决…..

这是我的代码

========================================= #include <termios.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <sys/signal.h> #include <sys/types.h> #include <assert.h> #include <string.h> #include <time.h> #define BAUDRATE B115200 #define MODEMDEVICE "/dev/ttyS0" #define _POSIX_SOURCE 1 /* POSIX compliant source */ #define FALSE 0 #define TRUE 1 volatile int STOP=FALSE; void signal_handler_IO (int status); /* definition of signal handler */ int wait_flag=TRUE; /* TRUE while no signal received */ struct timeval timeout; char n; fd_set rdfs; /*Some variables*/ int num, offset = 0, bytes_expected = 255; main() { int fd,c, res,i; char In1; struct termios oldtio,newtio; struct sigaction saio; /* definition of signal action */ char buf[255]; FILE *fp; /* open the device to be non-blocking (read will return immediatly) */ fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fd == -1) { perror("open_port: Unable to open /dev/ttyS0 - "); return 1; } else { fcntl(fd, F_SETFL, 0); n = select(fd + 1, &rdfs, NULL, NULL, &timeout); } fp = fopen("/root/Desktop/gccAkash/OutputLOG.txt","a+"); assert(fp != NULL); /* install the signal handler before making the device asynchronous */ saio.sa_handler = signal_handler_IO; sigemptyset(&saio.sa_mask); saio.sa_flags = 0; saio.sa_restorer = NULL; sigaction(SIGIO,&saio,NULL); /* allow the process to receive SIGIO */ fcntl(fd, F_SETOWN, getpid()); fcntl(fd, F_SETFL, FASYNC); tcgetattr(fd,&oldtio); /* save current port settings */ /* set new port settings for canonical input processing */ newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; newtio.c_iflag = IGNPAR | ICRNL; newtio.c_oflag = 0; newtio.c_lflag = ICANON; newtio.c_cc[VMIN]=0; //it will wait for one byte at a time. newtio.c_cc[VTIME]=10; // it will wait for 0.1s at a time. tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); while (STOP==FALSE) { if (wait_flag==FALSE) //if input is available { do { res = read(fd,buf+offset,255); offset += res; } while (offset < bytes_expected); if (offset!=0) { for (i=0; i<offset; i++) //for all chars in string { In1 = buf[i]; fputc ((int) In1, fp); printf("charecter:%c\n\r",In1); } //EOFor }//EOIf buf[res]=0; printf("Received Data is:%s\n\r",buf); if (res==0) STOP=TRUE; //stop loop if only a CR was input wait_flag = TRUE; // wait for new input } } //while stop==FALSE while(readchar) /* restore old port settings */ tcsetattr(fd,TCSANOW,&oldtio); close(fd); } /************************************************** ************************* * signal handler. sets wait_flag to FALSE, to indicate above loop that * * characters have been received. * ************************************************** *************************/ void signal_handler_IO (int status) { printf("received SIGIO signal.\n"); wait_flag = FALSE; } 

Solutions Collecting From Web of "我想从COM端口连续接收数据,同时要写入文件"

  • 你不应该使用SIGIO。 我从来没有发现使用它做任何事情的好文档,有更好的方法。 有民意测验选择epoll家庭的功能,这将更好地工作。 它看起来像你的程序正在等待数据变得可用,这意味着它正在消耗CPU时间。 当没有任何事情做时,选择让你的程序进入睡眠状态。 你可以使用暂停来获得这个效果,程序会进入休眠状态,直到它接收到一个信号(如SIGIO)。 你也可以使用在阻塞模式下打开tty文件,让读取阻止你,直到有东西读取。

  • 您不应该使用信号处理程序和常规代码中的printf 。 通常情况下,你不应该使用信号处理程序的stdio操作,因为它可以睡眠,但是在测试过程中你经常可以逃脱。 在信号处理程序和常规代码中使用它(或者在多个信号处理程序中,如果一个信号不阻止其他信号处理程序的话)使用它是不好的,因为那么函数将访问相同的数据。 本质上,普通代码中的printf可能会被SIGIO中断,然后printf被调用,并且printf都想访问FILE stdout的内部数据 – 无论是锁还是锁(用来阻止不同的线程,而不是同一个线程再次调用)将阻止SIGIO处理程序对printf的调用,这将阻塞整个程序。 这里的问题被称为竞赛条件,可能或不会发生取决于时间。

  • 在代码中:

_

 do { res = read(fd,buf+offset,255); offset += res; } while (offset < bytes_expected); 

_

即使您已经填充了缓冲区的一部分,您仍然不断地传递255个长度。 如果你第一次通过循环,你得到254字节的数据,然后再次调用再次请求255个字节,并得到超过1个字节,你已经超过了你的缓冲区。 此外,您永远不会将offset降回到0所以它只是增长和增长,所以如果您的程序读取超过255个字节,则会有缓冲区溢出。

您也没有检查read返回值不是-1。 如果它是-1,那么你的程序做非常糟糕的事情。

另外,在写入已经读出的数据之前,没有真正的原因(我可以看到)试图累积255个字节的数据。 即使你只想处理串行端口中的前255个字节,也可以继续写入最后一个进入的第一个字节,然后在达到255限制后退出循环。

  • 你只是在struct sigaction saio设置一个值,但它有其他的字段。 这些应该被设置为某种东西,或者你只是随机的位标记和掩码。

_

 struct sigaction saio = {0}; saio.sa_handler = signal_handler_IO; /* Now you don't have to worry about the flags and mask b/c those both are zeroed out and I'm pretty sure those are decent values for your purposes, but you could still set them explicitly. */ 

_

  • 你把newtio传给tcsetattr ,但是你可能没有完全初始化它。 你应该做的:

_

 tcgetattr(fd,&oldtio); /* save current port settings */ memcpy(&newtio, oldtio, sizeof(struct termios) ); /* !! <-- This line !! */ 

在设置其他标志之前。 这样你就可以使用那些已经存在的值来显示你没有设置的字段。

另外,你似乎正在设置所有的BAUDRATE标志。

 /* set new port settings for canonical input processing */ newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; 

这是不正确的,最有可能的。 BAUDRATE用于屏蔽波特代码,如下所示:tcflag_t baud_code = tio.c_cflag&BAUDRATE;

而现在baud_code只有波特组对应的位,没有设置CRTSCTSCLOCAL 。 你应该在这里使用一个看起来像B9600B115200的波特代码,而不是BAUDRATE,除非你想:

 tcflag_t non_baud_flags = tio.c_cflag | ~BAUDRATE; tcflag_t new_flags = non_baud_flags | B9600; 

这只会改变波特率而只剩下其他的标志。 这是什么int cfsetspeed(struct termios *termios_p, speed_t speed); 是为了。

  • 我也不确定你在做什么的顺序。 我想你可能会打开自己的SIGIO,然后再设置串口,这可能会给你一些垃圾数据。 应该可能做到:

_

  /* I swapped these two lines */ tcsetattr(fd,TCSANOW,&newtio); tcflush(fd, TCIFLUSH); /* Now the settings are correct before you flush, so no junk */ /* And moved these from above */ sigaction(SIGIO,&saio,NULL); /* There was no real reason for this to be up there, but no real harm, either. It just goes better here, but does need to be set before you set the file as async. */ /* allow the process to receive SIGIO */ fcntl(fd, F_SETOWN, getpid()); fcntl(fd, F_SETFL, FASYNC); /* Now SIGIO won't be called on behalf of this file until after the serial port has be set up and flushed. */ 

我不知道你在哪里得到这个代码,但是它看起来过于复杂,你实际上想要达到什么目的。 它看起来像复制粘贴给我。

这样的循环有什么问题?

 while() { len = read(serialfd, somebuf, wanted_len); write to file } 

你的问题到底是什么? 如何找到退出循环的好测试? 在一段时间过后,你是否想要退出?

为了回答len的可能值,你可以看看tcgetattr手册页。 也许你的tty选项不适合你想要达到的目的? 对不起,但你的问题和你的代码都不清楚,也许你可以先描述你想用你的程序来达到什么目的。