Проблемы при чтении с последовательного порта с использованием 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(¬ifAreaPort,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).