简体   繁体   English

如何在Linux中从串行端口接收完整数据

[英]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. 我有一台正在运行qq linux映像的设备。 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 . 因为交叉编译器设置为QT4.8.7,所以它不包括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. 我在QT创作者中撰写。 The problem I am facing is that whenever I am sending characters like A or B or any other single char. 我面临的问题是,无论何时我发送诸如AB或任何其他单个字符的字符。 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 . 如您在图像中看到的,它像p l h一样接收char,但是随后我向hello world发送了消息。 It breaks it and first received hello and then world . 它打破了它,首先收到了hello ,然后是world I again sent the hello world it receives the he then llo then worl and then d . 我再次向hello world发送了消息,它收到了he然后是llo然后是worl ,然后是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: 尝试在while循环中运行read函数,以将所有块附加到一条消息中,如下所示:

#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). 编辑 :感谢@MarkPlotnick提供了一个更好的屏蔽信号的示例,而不是将其关闭(可能导致竞态)。 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: 在调用sigactionMainWindow::SerialOpen掩盖信号之前,添加以下两行,以便在处理信号时不会再次调用处理程序:

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. msg的大小仅是示例,您可以将其设置为适合该消息的任何值。 Also, if you're using c++ then it would be better to use std::string instead of an array for msg . 另外,如果您使用的是c ++,那么最好使用std::string代替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. 注意(sizeof buf)-1 ,如果您读取了所有字符,则在数组末尾没有空格来容纳'\\0'

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). 另一个编辑 :在我们的聊天对话之后,事实证明,当没有更多字符要读取时, read正在阻塞(即使使用O_NDELAY标志调用open来进行非阻塞读取)。 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 QT4.8.7,因此它不包含QSerialPort

You can build QSerialPort from sources and to use it. 您可以从源代码构建QSerialPort并使用它。 Please read Wiki: https://wiki.qt.io/Qt_Serial_Port 请阅读Wiki: https : //wiki.qt.io/Qt_Serial_Port

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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