使用select()进行非阻塞串口通信

10

我正在开发一个项目,需要从串口读写数据,而且这必须是非阻塞的,出于某些原因。select()函数看起来像是我想要使用的,但我正在努力实现它。

在open_port()函数中,我定义了端口的设置以及它是非阻塞的。 在其他select()函数中,我将描述符赋给open_port()并尝试读取。 函数末尾还有一个1秒的休眠调用,以尝试避免读取速度对硬件造成影响。

运行时,在发送消息之前每秒都会打印一条“无可用数据”的消息。 发送消息后,它会将其打印出来,但通常是碎片化的,并带有二进制字符。例如,当发送单词“buffer”时,它将打印出“ffer”,然后是一个二进制字符。

我几乎没有关于termios或select的经验,所以任何建议都将不胜感激。

#include <iostream>
#include "stdio.h"
#include "termios.h"
#include "errno.h"
#include "fcntl.h"
#include "string.h"
#include "time.h"
#include "sys/select.h"

using namespace std;

int open_port(){
struct termios oldtio,newtio;
int serial_fd;
if ((serial_fd = open("/dev/ttyS0", O_RDWR | O_EXCL | O_NDELAY)) == -1) {
    cout << "unable to open" << endl;
    return -1;
}
if (tcgetattr(serial_fd, &oldtio) == -1) {
    cout << "tcgetattr failed" << endl;
    return -1;
}
cfmakeraw(&newtio); // Clean all settings
newtio.c_cflag = (newtio.c_cflag & ~CSIZE) | CS8 | B115200; // 8 databits
newtio.c_cflag |= (CLOCAL | CREAD);
newtio.c_cflag &= ~(PARENB | PARODD); // No parity
newtio.c_cflag &= ~CRTSCTS; // No hardware handshake
newtio.c_cflag &= ~CSTOPB; // 1 stopbit
newtio.c_iflag = IGNBRK;
newtio.c_iflag &= ~(IXON | IXOFF | IXANY); // No software handshake
newtio.c_lflag = 0;
newtio.c_oflag = 0;
newtio.c_cc[VTIME] = 1;
newtio.c_cc[VMIN] = 60;
if (tcsetattr(serial_fd, TCSANOW, &newtio) == -1) {
    cout << "tcsetattr failed" << endl;
    return -1;
}
tcflush(serial_fd, TCIOFLUSH); // Clear IO buffer
return serial_fd;
}

void otherselect(){
fd_set readfs;
timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
char * buffer = new char[15];
int _fd = open_port();
FD_ZERO(&readfs);
FD_SET(_fd, &readfs);
select(_fd+1, &readfs, NULL, NULL, &tv /* no timeout */);
if (FD_ISSET(_fd, &readfs))
{
    int r = read(_fd, buffer, 15);
    if(r == -1){
        cout << strerror(errno) << endl;
    }
    cout << buffer << endl;
}
else{
    cout << "data not available" << endl;
}
close(_fd);
sleep(1);
}

int main() {
    while(1){
        otherselect();
    }
}

由于这是C++,您是否考虑使用boost::asio来完成这个任务? - moooeeeep
你不需要在 open() 调用中加入 O_NONBLOCK 才能实现非阻塞吗?编辑:如果我没记错,O_NDELAY 只是跳过等待DCD,而 O_NONBLOCK 则实际上是在没有等待任何输入的情况下继续执行。 - favoretti
1
O_NDELAY在fcntl.h中被设置为O_NONBLOCK,它们是相等的。我还没有研究过boost::asio,现在我会去做,但我真的很喜欢在使用select()时被给予如何处理数据的选项。 - Kevin Corder
1
read()函数不会添加终止符'\0',那么你的cout << buffer如何知道字符串在哪里结束呢? - Alan Curry
1
你不仅需要担心数组中的最后一个单元格。你不能假设因为read()没有返回-1,它就必须返回你要求的全部内容。这不是read()的工作方式。它可以返回从1到你缓冲区大小的任何内容,或者0表示EOF,-1表示错误。你需要告诉cout只打印缓冲区中前r个字符。我不是C++专家,不知道如何做到这一点。 - Alan Curry
显示剩余6条评论
1个回答

1
当您使用read()时,您不会得到一个以null结尾的字符串,因此

cout<<buffer<<endl

这显然是一个糟糕的想法。 做a,

buffer[r]='\0'  #(provided r<15)

在打印之前,请确保检查好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接