I want to receive data CONTINUOUSLY from a COM por

2019-04-17 10:50发布

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

2条回答
疯言疯语
2楼-- · 2019-04-17 11:09
  • 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. */
查看更多
萌系小妹纸
3楼-- · 2019-04-17 11:14

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.

查看更多
登录 后发表回答