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

时间:2010-08-06 10:42:44

标签: c linux serial-port signals

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

问题是没有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;
} 

2 个答案:

答案 0 :(得分:4)

  • 您不应该使用SIGIO。我从未找到任何使用它做任何事情的好文档,并且有更好的方法。有民意调查选择 epoll 系列功能,可以更好地为此工作。在我看来,你的程序正在忙着等待数据变得可用,这意味着它占用了CPU时间。当没有任何事情要做时,替代方案让你的程序进入睡眠状态。您可以使用 pause 来获得此效果,程序将进入睡眠状态,直到收到信号(如SIGIO)。您也可以使用在阻止模式下打开tty文件,让读取阻止您直到读取内容。

  • 您不应该同时使用信号处理程序和常规代码中的printf。通常,您根本不应该使用信号处理程序中的 stdio 操作,因为它可以休眠,但您通常可以在测试期间使用它。从信号处理程序和常规代码(或多个信号处理程序,如果一个信号没有阻止其他信号)中使用它是不好的,因为那时函数将访问相同的数据。您的常规代码中基本上printf可能会被SIGIO中断,然后调用printf,并且printf都希望访问FILE stdout的内部数据 - 无论是那个还是锁(用于阻止阻塞不同线程,而不是第二次调用同一个线程)都将阻止 SIGIO 处理程序调用{​​{1}},这将阻止整个程序。这里的问题被称为竞争条件,可能会或可能不会发生,具体取决于时间。

  • 在代码中:

_

printf

_

即使您已经填充了缓冲区的一部分,也会不断地传递255作为所需的长度。如果您是第一次通过循环,则会获得254个字节的数据,然后再次调用再次请求255个字节并获得超过1个字节的溢出缓冲区。此外,您永远不会将do { res = read(fd,buf+offset,255); offset += res; } while (offset < bytes_expected); 放回offset,因此它会增长和增长,因此如果您的程序读取的字节数超过255个,则缓冲区溢出。

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

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

  • 您只在read中设置一个值,但它有其他字段。这些应该被设置为某种东西,或者你只是将随机位作为标志和掩码投入。

_

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

在设置其他标志之前。这样,您可以只使用已经存在的值,而不是明确设置的任何字段。

此外,您似乎正在设置所有 BAUDRATE 标志。

tcgetattr(fd,&oldtio); /* save current port settings */

memcpy(&newtio, oldtio, sizeof(struct termios) );  /* !!  <-- This line !!  */

这很不可能。 /* set new port settings for canonical input processing */ newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; 用于屏蔽这样的波特码:     tcflag_t baud_code = tio.c_cflag&amp; BAUDRATE;

现在BAUDRATE只有与波特集相对应的位,没有设置baud_codeCRTSCTS的东西。您应该使用其中一个类似CLOCALB9600的波特码而不是BAUDRATE,除非您想:

B115200

只会改变波特率并留下其余的标志。这是tcflag_t non_baud_flags = tio.c_cflag | ~BAUDRATE; tcflag_t new_flags = non_baud_flags | B9600; 的用途。

  • 我也不确定你做某些事情的顺序。我认为在设置串口之前,您可能会自己接收 SIGIO ,这可能会让您获得一些垃圾数据。应该这样做:

_

int cfsetspeed(struct termios *termios_p, speed_t speed);

答案 1 :(得分:0)

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

像这样的循环有什么问题?

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

你的问题究竟是什么?如何找到退出循环的好测试? 在经过一段时间后收到一些字符时,您想退出吗?

关于len的可能值的答案,您可以查看tcgetattr手册页。 也许你的tty选项不适合你想要实现的目标? 对不起,但你的问题和代码都不清楚,也许你可以先描述你希望用你的程序实现的目标。