C++ Ubuntu select() 如果串行接口有异步读取的数据
C++ Ubuntu select() if serial interface has data on asynchronous read
我正在使用 C++ 和 termios 为 Ubuntu 编写异步串行数据 reader class,但我在检查是否有可用数据时遇到困难。
这是我的代码:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
class MySerialClass {
public:
MySerialClass(std::string port);
virtual ~MySerialClass();
void openSerial();
void closeSerial();
void configureSerial();
void writeSerial(std::vector<char> data);
void readSerial(std::vector<char> &data, unsigned int numBytes);
private:
int fd = 0; // The serial file descriptor
fd_set fdset; // The set to check on select
std::string portName = "";
};
MySerialClass::MySerialClass(std::string port) : portName(port) {}
MySerialClass::~MySerialClass() {}
void MySerialClass::openSerial()
{
fd = open(portName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
}
void MySerialClass::closeSerial()
{
close(fd);
}
void MySerialClass::configureSerial()
{
struct termios config = { 0 };
tcgetattr(fd, &config);
config.c_iflag = IGNPAR | ICRNL;
config.c_oflag = 0;
config.c_lflag = ICANON;
config.c_cc[VINTR] = 0; /* Ctrl-c */
config.c_cc[VQUIT] = 0; /* Ctrl-\ */
config.c_cc[VERASE] = 0; /* del */
config.c_cc[VKILL] = 0; /* @ */
config.c_cc[VEOF] = 4; /* Ctrl-d */
config.c_cc[VTIME] = 0; /* inter-character timer unused */
config.c_cc[VMIN] = 1; /* blocking read until 1 character arrives */
config.c_cc[VSWTC] = 0; /* '[=12=]' */
config.c_cc[VSTART] = 0; /* Ctrl-q */
config.c_cc[VSTOP] = 0; /* Ctrl-s */
config.c_cc[VSUSP] = 0; /* Ctrl-z */
config.c_cc[VEOL] = 0; /* '[=12=]' */
config.c_cc[VREPRINT] = 0; /* Ctrl-r */
config.c_cc[VDISCARD] = 0; /* Ctrl-u */
config.c_cc[VWERASE] = 0; /* Ctrl-w */
config.c_cc[VLNEXT] = 0; /* Ctrl-v */
config.c_cc[VEOL2] = 0; /* '[=12=]' */
speed_t sp = B9600;
config.c_cflag |= CSIZE;
config.c_cflag |= CS8;
cfsetispeed(&config, sp);
cfsetospeed(&config, sp);
tcsetattr(fd, TCSAFLUSH, &config);
}
void MySerialClass::writeSerial(std::vector<char> data)
{
char buffer[1024];
if (data.size() > 1024)
return;
int index = 0;
for (char &item : data)
buffer[index++] = item;
unsigned int size = data.size();
write(fd, &buffer[0], size);
}
void MySerialClass::readSerial(std::vector<char> &data, unsigned int numBytes)
{
char buffer[1024];
data.clear();
if (numBytes > 1024)
return;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
int ret = select(fd + 1, 0, 0, 0, &tv);
std::cout << "Select returns: " << ret << std::endl;
if (!ret)
return;
read(fd, &buffer[0], numBytes);
for (unsigned int i = 0; i < numBytes; i++)
data.push_back(buffer[i]);
}
int main()
{
MySerialClass serial("/dev/ttyS1");
serial.openSerial();
serial.configureSerial();
while(1)
{
std::vector<char> retData;
serial.readSerial(retData, 100);
std::string retString(retData.begin(), retData.end());
if (retString == "END")
{
serial.closeSerial();
break;
}
}
}
它编译得很好,但它从不接收数据,因为 select()
语句总是 returns 零。带有阻塞选项但没有 select()
的代码工作正常(只需注释 select() 行并从 open()
中删除 O_NODELAY
)。
我很确定这个问题与 select()
的使用方式有关(这是我第一次使用 select()
)。
有人可以帮我解决吗?该代码可在 Coliru here
顺便说一句:我对 select()
的另一个疑问是这个 class 将在多线程环境中使用。我需要确保每个 class 实例只检查其端口繁忙(它自己的 fd
),没有其他线程端口繁忙。
没有指定要阅读的fd_set。试试这个:
fd_set readfs; /* file descriptor set */
FD_ZERO(&readfs); /* clear the set */
FD_SET(fd, &readfs); /* put the fd in the set */
int ret = select(fd + 1, &readfs, 0, 0, &tv);
编辑:这也应该可以解决您的其他问题。每个 select 只查看您告诉它查看的文件描述符。
哎呀。英语语法不好,但更正后看起来更糟。
我正在使用 C++ 和 termios 为 Ubuntu 编写异步串行数据 reader class,但我在检查是否有可用数据时遇到困难。
这是我的代码:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
class MySerialClass {
public:
MySerialClass(std::string port);
virtual ~MySerialClass();
void openSerial();
void closeSerial();
void configureSerial();
void writeSerial(std::vector<char> data);
void readSerial(std::vector<char> &data, unsigned int numBytes);
private:
int fd = 0; // The serial file descriptor
fd_set fdset; // The set to check on select
std::string portName = "";
};
MySerialClass::MySerialClass(std::string port) : portName(port) {}
MySerialClass::~MySerialClass() {}
void MySerialClass::openSerial()
{
fd = open(portName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
}
void MySerialClass::closeSerial()
{
close(fd);
}
void MySerialClass::configureSerial()
{
struct termios config = { 0 };
tcgetattr(fd, &config);
config.c_iflag = IGNPAR | ICRNL;
config.c_oflag = 0;
config.c_lflag = ICANON;
config.c_cc[VINTR] = 0; /* Ctrl-c */
config.c_cc[VQUIT] = 0; /* Ctrl-\ */
config.c_cc[VERASE] = 0; /* del */
config.c_cc[VKILL] = 0; /* @ */
config.c_cc[VEOF] = 4; /* Ctrl-d */
config.c_cc[VTIME] = 0; /* inter-character timer unused */
config.c_cc[VMIN] = 1; /* blocking read until 1 character arrives */
config.c_cc[VSWTC] = 0; /* '[=12=]' */
config.c_cc[VSTART] = 0; /* Ctrl-q */
config.c_cc[VSTOP] = 0; /* Ctrl-s */
config.c_cc[VSUSP] = 0; /* Ctrl-z */
config.c_cc[VEOL] = 0; /* '[=12=]' */
config.c_cc[VREPRINT] = 0; /* Ctrl-r */
config.c_cc[VDISCARD] = 0; /* Ctrl-u */
config.c_cc[VWERASE] = 0; /* Ctrl-w */
config.c_cc[VLNEXT] = 0; /* Ctrl-v */
config.c_cc[VEOL2] = 0; /* '[=12=]' */
speed_t sp = B9600;
config.c_cflag |= CSIZE;
config.c_cflag |= CS8;
cfsetispeed(&config, sp);
cfsetospeed(&config, sp);
tcsetattr(fd, TCSAFLUSH, &config);
}
void MySerialClass::writeSerial(std::vector<char> data)
{
char buffer[1024];
if (data.size() > 1024)
return;
int index = 0;
for (char &item : data)
buffer[index++] = item;
unsigned int size = data.size();
write(fd, &buffer[0], size);
}
void MySerialClass::readSerial(std::vector<char> &data, unsigned int numBytes)
{
char buffer[1024];
data.clear();
if (numBytes > 1024)
return;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
int ret = select(fd + 1, 0, 0, 0, &tv);
std::cout << "Select returns: " << ret << std::endl;
if (!ret)
return;
read(fd, &buffer[0], numBytes);
for (unsigned int i = 0; i < numBytes; i++)
data.push_back(buffer[i]);
}
int main()
{
MySerialClass serial("/dev/ttyS1");
serial.openSerial();
serial.configureSerial();
while(1)
{
std::vector<char> retData;
serial.readSerial(retData, 100);
std::string retString(retData.begin(), retData.end());
if (retString == "END")
{
serial.closeSerial();
break;
}
}
}
它编译得很好,但它从不接收数据,因为 select()
语句总是 returns 零。带有阻塞选项但没有 select()
的代码工作正常(只需注释 select() 行并从 open()
中删除 O_NODELAY
)。
我很确定这个问题与 select()
的使用方式有关(这是我第一次使用 select()
)。
有人可以帮我解决吗?该代码可在 Coliru here
顺便说一句:我对 select()
的另一个疑问是这个 class 将在多线程环境中使用。我需要确保每个 class 实例只检查其端口繁忙(它自己的 fd
),没有其他线程端口繁忙。
没有指定要阅读的fd_set。试试这个:
fd_set readfs; /* file descriptor set */
FD_ZERO(&readfs); /* clear the set */
FD_SET(fd, &readfs); /* put the fd in the set */
int ret = select(fd + 1, &readfs, 0, 0, &tv);
编辑:这也应该可以解决您的其他问题。每个 select 只查看您告诉它查看的文件描述符。
哎呀。英语语法不好,但更正后看起来更糟。