Проблемы при чтении с последовательного порта с использованием QSerialPort

Мне нужно разработать программу на C++ для встроенной процессорной системы на базе FriendlyARM. Я использую Qt Creator 3.0.0 (на основе Qt 5.2.0) для настольного компьютера. Моя программа должна быть в состоянии читать с последовательного порта на процессоре Mini2440 FriendlyARM.

Прежде чем перейти к целевой системе (встроенной системе), я пытался читать и записывать данные с / на последовательный порт моего ноутбука. Моя главная проблема - как читать с последовательного порта. Как вы знаете, на новых компьютерах и ноутбуках нет последовательного порта, поэтому я пытаюсь смоделировать программирование последовательного порта, используя кабель-переходник USB-последовательный порт. Когда последовательный USB-кабель подключен, он распознается как "/dev/ttyUSB0" в Ubuntu. Кажется, хорошо работает. Обратите внимание, что другой конец кабеля (последовательный порт) не подключен ни к чему.

Мой первый вопрос: это нормально, чтобы настроить кабель, как это, или я должен подключить его к другому устройству? Я пытаюсь записывать в / dev / ttyUSB0 каждые 10 секунд и читать данные. Я закончил следующий код:

void MainWindow::refreshNotificationArea()
{
    generateNotifAreaData(); // a typical random data-generator
    QList<QSerialPortInfo> L = QSerialPortInfo::availablePorts();
    for (auto e : L)
        qDebug() << e.portName() << '\n'; // it prints 1 serial port: :ttyUSB0
    // write to the port
    QSerialPort notifAreaPort;
    // 1. set properties
    notifAreaPort.setBaudRate(QSerialPort::Baud9600); 
    notifAreaPort.setStopBits(QSerialPort::OneStop);
    notifAreaPort.setParity(QSerialPort::NoParity); 
    notifAreaPort.setDataBits(QSerialPort::Data8);
    notifAreaPort.setFlowControl(QSerialPort::NoFlowControl); 
    QObject::connect(&notifAreaPort,SIGNAL(error(QSerialPort::SerialPortError)),
    this, SLOT(errorReport(QSerialPort::SerialPortError)));
    notifAreaPort.setPortName(serial_device.c_str());
    // 2. open port
    notifAreaPort.open(QIODevice::ReadWrite);
    if (!notifAreaPort.isOpen())
        qDebug() << "Open failed"; // open is OK, no error message printed

    string s  = convertNotifAreadData2Str();
    qDebug() << "Generated data " << s.c_str(); // OK
    int a = notifAreaPort.write(s.c_str()); // write done
    qDebug() << "Write count" << a; // OK

    // now read the info

    QByteArray ba = notifAreaPort.readLine(3); // read failed
    QSerialPort::SerialPortError err = notifAreaPort.error();
    qDebug() << "Error code" << err;
    qDebug() << "What? " << notifAreaPort.errorString();
    qDebug() << "Read count " << ba.size(); // 0

    notifAreaPort.close();
}

void MainWindow::errorReport(QSerialPort::SerialPortError error)
{
    if(error!=0)
        qDebug()<<"ERROR:"<<endl<<error; // nothing printed
}

Запись в последовательный порт в порядке. но чтение иногда выдает "Нет такого файла или каталога"! иногда "Файл временно недоступен! Странно, что notifAreaPort.error() возвращает 0, и это означает, что ошибки не произошло!

Мысли?

- Саид Амроллахи Бёюки

3 ответа

Решение

Вы не можете написать, а затем прочитать с QSerialPort в той же функции.

Есть два метода, которые я использую для QSerialPort обработка:

METHOD ONEСоздать и открыть свой QSerialPort объект. Настроить QTimer с таймаутом около 50 мс или около того (зависит от аппаратного обеспечения).

Подключите readyRead() сигнал в слот, который в основном просто читает все данные в ваш буфер (QByteArray идеально подходит для этого). Слот останавливает QTimer, читает все данные, доступные с readAll(), а затем перезапускает QTimer и возвращается.

Подключите timeout сигнал о QTimer к функции для обработки прочитанных байтов ввода.

Предпосылка здесь заключается в том, что в конечном итоге все данные будут получены и QTimer timeout, после чего у вас будут все ваши данные в буфере для обработки.

METHOD TWOСлот, который обрабатывает readyRead() Сигнал может проверить все данные в буфере на наличие какого-либо "маркера", который означает, что какой-то кусок данных полностью прибыл. Многие устройства используют 0x0D или же 0x0d0x0A как разносчик. Другие используют NULL 0x00 или какой-то другой байт.

Оцените буфер на каждой итерации readyRead() слот обработчика.

Этот пример показывает второй вариант, и он хорошо работает для небольших чтений.

r_port = new QSerialPort(this);
r_port->setPortName("COM3");
r_port->setBaudRate(QSerialPort::Baud9600);
r_port->setDataBits(QSerialPort::Data8);
r_port->setParity(QSerialPort::NoParity);
r_port->setStopBits(QSerialPort::OneStop);
r_port->setFlowControl(QSerialPort::NoFlowControl);
if (r_port->open(QSerialPort::ReadWrite))
{
    connect(r_port, &QSerialPort::readyRead, this, &MYPROG::on_readyRead);
    connect(r_port, &QSerialPort::errorOccurred, this, &MYPROG::breakCaught);
}
else
{
    QMessageBox::critical(this, "SERIAL PORT NOT CONNECTED", "Unable to connect to the radio.\n\nPlease check your connections\nand configuration and try again.");
    return;
}

void MYPROG::on_readyRead()
{
 // keep reading until we get a \r\n delimiter
    rxBytes.append(r_port->readAll());
    qDebug()<<"raw rxBtes"<<rxBytes;
    if(!rxBytes.contains("\r\n"))
    {
        return;
    }
    int end = rxBytes.lastIndexOf("\r\n") + 2;
    QStringList cmds = QString(rxBytes.mid(0, end)).split("\r\n", QString::SkipEmptyParts);
    rxBytes = rxBytes.mid(end);
    foreach(QString cmd, cmds){
        qDebug()<<"serial read"<<cmd;
    }
}

Это позволяет читать QSerialPort при поступлении данных, а затем программа может вернуться в цикл обработки событий, чтобы графический интерфейс пользователя не отвечал на запросы. Типичные последовательные чтения являются небольшими и короткими по времени, поэтому при использовании этих методов замораживание пользовательского интерфейса редко происходит.

В вашем коде есть пара проблем, но я выделю наиболее важные из них:

  • Вы устанавливаете параметры перед открытием. Это должно произойти после открытия. Вот как печально был разработан API, но мы находимся в процессе его модернизации.

  • Вы должны примеры командной строки для чтения, что я добавил в 5.2? Кажется, что вы не знаете, как читать, и это даст вам простой пример. Короче говоря: вы в основном пытаетесь читать до того, как запись потенциально закончится.

"Hand-made USB to serial adapter" - звучит интересно. Вы уверены, что это работает правильно? Я думаю, что это хорошая идея - соединить PIN 2(Rx) и 3(Tx), чтобы вы получали данные. Теперь вы можете протестировать ваше устройство с любым другим программным обеспечением терминала. Я использую для последовательных портов всегда сигнал readyRead() и проверяю перед чтением с помощью port->bytesAvailable(). И я открываю свой порт с помощью port->open(QIODevice::ReadWrite | QIODevice::Unbuffered).

Другие вопросы по тегам