简体   繁体   中英

How to receive complete data from serial port in linux

I am trying to do a serial communication application. I have a device which is running angstrom qt linux image. I need to write the serial code application for that device. Because the cross compiler is set for QT4.8.7, so it do not include the QSerialPort . So I am following this link for serial communication and I have also found many good examples on it.

Below is the code:

void MainWindow::SerialOpen(QString PORT)
{
fd = open(PORT.toStdString().c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
    perror("open_port: Unable to open /dev/ttyLP0\n");
    exit(1);
}

saio.sa_handler = signal_handler_IO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);

fcntl(fd, F_SETFL, FNDELAY);
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL,  O_ASYNC );

tcgetattr(fd,&termAttr);
cfsetispeed(&termAttr,B9600);
cfsetospeed(&termAttr,B9600);
termAttr.c_cflag &= ~PARENB;
termAttr.c_cflag &= ~CSTOPB;
termAttr.c_cflag &= ~CSIZE;
termAttr.c_cflag |= CS8;
termAttr.c_cflag |= (CLOCAL | CREAD);
termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
termAttr.c_oflag &= ~OPOST;
tcsetattr(fd,TCSANOW,&termAttr);
qDebug("Serial Port configured....\n");
}

and for reading I am using:

void signal_handler_IO (int status)
{
  char buf [100];
  int n = read (fd, buf, sizeof buf);
  if(n > 0)
  {
    buf[n] = '\0';
    printf("Receive OK %s\n",buf);
  }
}

I am using event based serial read. I am wiriting it in QT creator. The problem I am facing is that whenever I am sending characters like A or B or any other single char. It receives it. But when I send the complete string, it breaks automatically. Have a look at the below image:

在此处输入图片说明

As you can see in the image, it receives char like p l h but then I sent hello world . It breaks it and first received hello and then world . I again sent the hello world it receives the he then llo then worl and then d . Why is this showing this behaviour. Please help. Thanks.

You are receiving your data in small chunks. Try running the read function in a while loop to append all chunks into one message like so:

#include <cstring> // needed for strcat.
#include <errno.h> // needed for strerror and errno
void signal_handler_IO (int status)
{
    char buf [100];
    char msg[1024];
    int n;
    int length = 0;
    while((n = read (fd, buf, (sizeof buf)-1)) > 0)
    {
        buf[n] = '\0';
        // you can add a check here to see if (length+n) < 1024
        strcat(msg, buf);
        length += n;
    }
    if(n<0)
    {
        //error handling
        //EDIT: adding error handling
        fprintf(stderr, "Error while receiving message: %s\n", strerror(errno));
        return;
    }
    printf("Receive OK: '%s'\n", msg);
}

The signal handling needs to be turned off for the time of reading data so that the signal handler doesn't get called for each new chunk of data. After we're done reading the handler needs to be restored. EDIT : Thanks to @MarkPlotnick for giving a better example on masking the signal instead of turning it off (could cause race condition). Add these two lines before calling sigaction to MainWindow::SerialOpen to mask the signal so that the handler doesn't get called again while the signal is being handled:

sigemptyset(&saio.sa_mask);
sigaddset(&saio.sa_mask, SIGIO);

The size of msg is just an example, you can set it to any value that will fit the message. Also, if you're using c++ then it would be better to use std::string instead of an array for msg .

Note the (sizeof buf)-1 , if you read all characters there would be no room for the '\\0' at the end of the array.

ANOTHER EDIT : After our chat conversation it turned out that read is blocking when there are no more characters to read (even though open was called with O_NDELAY flag for non-blocking reading). To fix this issue one can first check how many bytes are available for reading:

while(true)
{ 
    size_t bytes_avail; 
    ioctl(fd, FIONREAD, &bytes_avail); 
    if(bytes_avail == 0) 
        break; 
    int n = read (fd, buf, (sizeof buf)-1); 
    if(n<0) 
    { 
        //error handling
        fprintf(stderr, "Error while receiving message: %s\n", strerror(errno)); 
        return; 
    } 
    buf[n] = '\0'; 
    // you can add a check here to see if (length+n) < 1024 
    strcat(msg, buf); 
    length += n; 
} 

QT4.8.7, so it do not include the QSerialPort

You can build QSerialPort from sources and to use it. Please read Wiki: https://wiki.qt.io/Qt_Serial_Port

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM