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

发布于 2024-09-13 12:07:39 字数 3237 浏览 4 评论 0原文

我想读取串行 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;
} 

I want to read serial COM port and to write the data to a file in LINUX. Actually I'm sending data from hyperterminal from other PC.

The problem is without while loop I can write only one line.

But with while(1) loop I can't write anything to file.

Or else I have to send BIG file, then application exits/terminates and writes to the file.

My application should write the data (it may 2 lines or any thing); after that it has to wait for next data.

So please help me out.....

Here is my Code

=========================================

    #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;
} 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

乄_柒ぐ汐 2024-09-20 12:07:39
  • 您不应该使用 SIGIO。我从来没有找到任何好的文档来使用它来做任何事情,并且有更好的方法。有 pollselectepoll 系列函数可以更好地实现此目的。在我看来,您的程序正忙于等待数据可用,这意味着它正在占用 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 !!  */

在设置其他标志之前, 。这样,您可以只使用未明确设置的任何字段中已有的值。

此外,您似乎正在设置所有 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 &波特率;

现在,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. */
  • You shouldn't be using SIGIO. I've never found any good documentation for using it to do anything, and there are better ways. There's poll, select, and epoll families of functions that would work much better for this. It looks to me like your program is busy waiting on data to become available, which means it's eating up CPU time. The alternatives let your program go to sleep when there's nothing to do. You could use pause to get this effect though, and the program would go to sleep until it received a signal (like SIGIO). You could also just use have the tty file opened in blocking mode and let read block you until there is something read.

  • You shouldn't be using printf from both a signal handler and regular code. Usually you shouldn't ever be using stdio operations from a signal handler at all because it can sleep, but you can often get away with it during testing. Using it from both a signal handler and regular code (or in multiple signal handlers if one signal isn't blocking others) is bad because then the functions will be accessing the same data. Essentially printf in your regular code might be interrupted by SIGIO and then the printf in there is called and both printfs are wanting access to FILE stdout's internal data -- either that or the locks (which are intended to stop to block different threads, not the same thread calling a second time) will block the SIGIO handler's call to printf, which will block the whole program. The problem here is called a race condition and may or may not happen depending on timing.

  • In the code:

_

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

_

You are continually passing 255 as the length you want, even though you may already have filled part of the buffer. If you the first time through the loop you get 254 bytes of data and then call again asking for 255 bytes again and get more than 1 byte you have overrun your buffer. Additionally you never drop offset back to 0 so it just grows and grows, so so you have an overflow of the buffer if your program ever reads more than 255 bytes.

You also aren't checking that the read return value isn't -1. If it were -1 then your program does very bad things.

Also, there is no real reason (that I can see) to try to try to accumulate 255 bytes of data before you write the data you have already read back out. Even if you only want to handle the first 255 bytes that come in the serial port then you could go ahead and write the first ones as the last ones were coming in, and then exit the loop after the 255 limit had been reached.

  • You are only setting one value in struct sigaction saio, but it has other fields. These should be set to something or you are just throwing random bits in as flags and masks.

_

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.  */

_

  • You pass newtio to tcsetattr, but it is possible that you have not totally initialized it. You should have done:

_

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

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

before you set the other flags. This way you can just use the values that were already there for any fields that you weren't setting explicitly.

Additionally, you seem to be setting all of the BAUDRATE flags.

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

This isn't correct, most likely. BAUDRATE is used for masking out the baud code like this :
tcflag_t baud_code = tio.c_cflag & BAUDRATE;

And now baud_code has just the bits that correspond to the baud set, without the CRTSCTS and CLOCAL stuff set. You should be using one of the baud codes that look like B9600 and B115200 here instead of BAUDRATE, unless you want to:

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

which would only change the baud rate and leave the rest of the flags alone. This is what int cfsetspeed(struct termios *termios_p, speed_t speed); is for.

  • I'm also not sure about the order in which you are doing some things. I think that you may open yourself up to receiving SIGIO before you have set up the serial port, which will probably get you some junk data. Should probably do:

_

 /* 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. */
你的他你的她 2024-09-20 12:07:39

我不知道你从哪里得到这段代码,但对于你真正想要实现的目标来说,它看起来过于复杂。对我来说就像是复制粘贴。

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

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

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

有关 len 可能值的答案,您可以查看 tcgetattr 手册页。
也许您的 tty 选项不适合您想要实现的目标?
抱歉,您的问题和代码都不清楚,也许您可​​以首先描述您想要通过程序实现的目标。

I don't know where you got this code, but it looks over-complicated for what you actually want to achieve. It looks like copy pasting to me.

What is the problem with a loop like this ?

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

What exactly is your question ? How to find the good test to exit from the loop ?
Do you want to exit when you receive some char, after a certain time has elapsed ?

For answer on the possible value of len, you can look at the tcgetattr man page.
Maybe your tty options are not suited to what you want to achieve ?
Sorry but neither your question nor your code is clear, maybe you could first describe whet you want to achieve with your program.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文