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

标签 c linux serial-port signals

我想读取串行COM端口并将数据写入Linux中的文件。实际上我是从其他电脑的超级终端发送数据。
问题是没有while循环,我只能写一行。
但是在while(1)循环中,我不能向文件中写入任何内容。
否则我必须发送大文件,然后应用程序退出/终止并写入文件。
我的应用程序应该写入数据(可能是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;
} 

最佳答案

你不应该用sigio。我从来没有找到任何好的文档来使用它来做任何事情,而且有更好的方法。有一些poll、select和epoll函数族可以更好地实现这一点。在我看来,你的程序正忙着等待数据可用,这意味着它正在占用CPU时间。其他的选择让你的程序在无事可做时进入睡眠状态。不过,您可以使用pause来获得这种效果,程序将进入睡眠状态,直到收到信号(如sigio)。您也可以使用在阻塞模式下打开tty文件,让read阻塞您,直到有东西被读取为止。
不应该同时使用信号处理程序和常规代码中的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 !!  */

在你设置其他标志之前。这样,您就可以对任何未显式设置的字段使用已经存在的值。
此外,您似乎正在设置所有波特率标志。
/* set new port settings for canonical input processing */
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

这很可能是不对的。BAUDRATE用于屏蔽波特码,如下所示:
t cflag_t baud_code=tio.c_cflag&baudrate;
现在baud_code只有对应于波特率集的位,没有CRTSCTSCLOCAL数据集。您应该在这里使用一个看起来像B9600B115200的波特率代码,而不是波特率,除非您想:
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. */

关于c - 我想连续从COM端口接收数据,同时想写入文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3423002/

相关文章:

c++ - 使用 select() 和 fgets() 从串行端口访问信息

c - 在内存中查找进程页面(linux-kernel)

c - 如何从字符串中确定文件的大小

c++ - 如何将 13 位值映射到 4 位代码?

c - 为什么具有相同处理器(intel x86_64)的不同PC上的相同C代码的汇编语言代码不同

java - 我如何确认 _JAVA_OPTS 正在执行?

c - 串口通信问题(C代码)

c - 通过网络 Ubuntu 和 C 发送声音

Linux 与 Windows 在 MatLab 中执行 lsqcurvefit 和 importdata

python - Pyserial 非轮询读取