如果串行接口具有异步读取数据,则 C++ Ubuntu select()

Posted

技术标签:

【中文标题】如果串行接口具有异步读取数据,则 C++ Ubuntu select()【英文标题】:C++ Ubuntu select() if serial interface has data on asynchronous read 【发布时间】:2015-06-17 00:18:30 【问题描述】:

我正在使用 C++ 和 termios 为 Ubuntu 编写一个异步串行数据读取器类,我在检查是否有可用数据时遇到了困难。

这是我的代码:

#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;     /* '\0' */
    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;     /* '\0' */
    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;     /* '\0' */

    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() 语句总是返回零。带有阻塞选项且不带select() 的代码工作正常(只需注释select() 行并从open() 中删除O_NODELAY)。

我很确定这个问题与 select() 的使用方式有关(这是我第一次使用 select())。

有人可以帮我解决这个问题吗?代码可在Coliru here获得

顺便说一句:我对select() 的另一个疑问是这个类将用于多线程环境。我需要确保每个类实例只检查其端口繁忙(它自己的fd),没有其他线程端口繁忙。

【问题讨论】:

【参考方案1】:

没有指定要读取的 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);

编辑:这也应该解决您的其他问题。每个选择只查看您告诉它查看的文件描述符。

呸。糟糕的英语语法,但它看起来更糟。

【讨论】:

谢谢。工作正常。

以上是关于如果串行接口具有异步读取数据,则 C++ Ubuntu select()的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Linux 上通过 C++ 中的串行接口与 Arduino 通信?

使用FileReader接口读取文件内容

xadc输出是串行还是并行

C++ 从事件处理程序中获取数据并传递给另一个类的方法?

在 Python 中使用 Qt Designer 接口实时读取串行数据

具有多个事件的连续串行端口读取