使用QSerialPort无法读取任何内容

时间:2017-05-20 19:43:35

标签: c++ qt

我在QSerialPort周围创建了一个包装器库。我想与我的设备通信。首先,我将list命令发送到我的设备,它应该返回该设备支持的命令列表。但是,在调试我的代码时,我发现list命令正在发送到设备并且设备返回正确的响应(我使用串行流量嗅探器Linux工具调试)。但是,我没有使用QSerialPort从我的设备获得任何响应(而串行流量嗅探器工具被禁用)。经过多次测试后,我无法使其正常工作。

我的Serial.h:

class Serial : public Print {

public:
    Serial();
    explicit Serial(const char *dev_path);
    ~Serial();

    int begin(unsigned long baudrate);
    int begin(unsigned long baudrate, uint8_t cfg);
    void end(void);

    int available(void) const;
    bool availableForWrite(void) const;
    void flush(void);
    bool isError(void) const;
    void reset(void);

    unsigned long write(uint8_t c);
    unsigned long write(uint8_t *p_data, unsigned long maxSize);
    int read(void);

    void close();

    QSerialPort &getPort()
    {
        return *_p_port;
    }

public slots:
    void readyBe(void);

private:
    QSerialPort *_p_port;
    unsigned long _baudrate;
};

我的Serial.cpp:

Serial::Serial()
{
    _p_port = new QSerialPort();
    if (_p_port == nullptr)
        throw std::runtime_error("Can't allocate memory");
}

Serial::Serial(const char *dev_path)
{
    _p_port = new QSerialPort(QString(dev_path), QApplication::instance());
    if (_p_port == nullptr)
        throw std::runtime_error("Can't allocate memory");
    // _p_port->setPortName(QString(dev_path));
    if (_p_port->open(QIODevice::ReadWrite) == false) {
        throw std::runtime_error("Can't open the serial _p_port");
        delete _p_port;
    }
    _p_port->setBaudRate(QSerialPort::Baud9600);
    _p_port->setDataBits(QSerialPort::Data8);
    _p_port->setParity(QSerialPort::NoParity);
    _p_port->setStopBits(QSerialPort::OneStop);
    _p_port->setFlowControl(QSerialPort::NoFlowControl);
}

Serial::~Serial()
{
    if (_p_port != nullptr) {
        end();
        delete _p_port;
    }
}

int Serial::begin(unsigned long baudrate)
{
    if (_p_port->setBaudRate(baudrate, QSerialPort::AllDirections) == false)
        return -1;
    _baudrate = baudrate;
    return 0;
}

void Serial::end()
{
    if (_p_port->isOpen())
        _p_port->close();
}

int Serial::available(void) const
{
    int num_bytes = _p_port->bytesAvailable();
    return num_bytes;
}

bool Serial::availableForWrite(void) const
{
    if (_p_port->isWritable())
        return true;
    return false;
}

void Serial::flush()
{
    _p_port->flush();
}

unsigned long Serial::write(uint8_t c)
{
    if (_p_port->putChar(c))
        return 1;
    return 0;
}

unsigned long Serial::write(uint8_t *p_data, unsigned long maxSize)
{
    return _p_port->write(reinterpret_cast<const char *>(p_data), (qint64)maxSize);
}

int Serial::read(void)
{
    char c;
    _p_port->getChar(&c);
    return c;
}

void Serial::reset(void)
{
    _p_port->clear(QSerialPort::AllDirections);
    _p_port->clearError();
}

bool Serial::isError(void) const
{
    if (_p_port->error() ==  QSerialPort::NoError)
        return false;
    return true;
}

我的main.cpp:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    // w.show();

    Serial serial("ttyACM0");
    if (serial.begin(115200))
        std::cout << "Failed to set Baud rate" << std::endl;
    std::cout << "Sending data" << std::endl;
    // QObject::connect(&(serial.getPort()), SIGNAL(readyRead()), &serial, SLOT(readyBe()));
    serial.print("list\r");
    serial.flush();

    while (true) {
        while (true) {
            while (serial.available() == 0) {
                if (serial.isError()) {
                    std::cout << "Error" << std::endl;
                    // serial.reset();
                }
            }

            char c = serial.read();
            std::cout << c;
            if (c == '\n')
                break;
        }
        std::cout << std::endl;
    }

    return a.exec();

}

1 个答案:

答案 0 :(得分:1)

您几乎错过了此代码所需的一切:事件循环。现实生活中的I / O是异步的。你不能只是阅读&#34;来自端口,没有一些方法在数据可用时获得通知,并且实际上让异步I / O请求得到处理。是的,有一些遗留的API允许你这样做,但它们主要导致意大利面条代码,浪费的线程和糟糕的性能。

while (serial.available() == 0)循环是无操作。它实际上没有让available()返回任何其他值。 available()内部执行的所有内容都是读取类的整数成员。您永远不会运行任何可以更新存储在该成员中的值的代码。即使您将此转换为serial.waitForReadyRead(),其中 更新可用字节数,您仍然没有旋转事件循环,因此您不会成为能够处理超时,或对应用程序可能需要做出反应的任何其他事件做出反应。 QIODevice::waitForReadyRead只是为了做一件事:当readyRead信号发射时返回。它不会处理任何其他事件,并且它是用于移植阻塞代码的拐杖,并不真正用于生产用途。

您应该将代码重新设计为异步,并由来自QSerialPort的信号驱动。然后,工作将在QCoreApplication::exec内完成 - 您不会拥有自己的循环。这种反转控制对于使异步I / O工作至关重要。

参见例如this answer是一个非常简单的异步方法示例,this answer是一个更完整的方法。