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

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

Вопрос:

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

Прежде чем перейти на целевую систему (встроенную систему), я попытался прочитать и записать с/на последовательный порт на моем ноутбуке. Моя основная проблема заключается в том, как читать из последовательного порта. Как вы знаете, новые компьютеры и ноутбуки не имеют последовательного порта, поэтому я пытаюсь имитировать программирование последовательного порта, используя ручной кабель USB-to-serial. Когда последовательный кабель 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, и это означает, что ошибка не возникала!

Мысли?

– Саид Амраллахи Боуки

Лучший ответ:

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

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

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

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

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

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

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.nnPlease check your connectionsnand configuration and try again.»); return; } void MYPROG::on_readyRead() { // keep reading until we get a rn delimiter rxBytes.append(r_port->readAll()); qDebug()<<«raw rxBtes»<<rxBytes; if(!rxBytes.contains(«rn»)) { return; } int end = rxBytes.lastIndexOf(«rn») + 2; QStringList cmds = QString(rxBytes.mid(0, end)).split(«rn», QString::SkipEmptyParts); rxBytes = rxBytes.mid(end); foreach(QString cmd, cmds){ qDebug()<<«serial read»<<cmd; } }

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

Ответ №1

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

Ответ №2

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

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

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

Оцените статью
Добавить комментарий