mikrocontroller.net

Forum: PC-Programmierung Qt SerialPort - Daten lesen geht schief


Autor: QtNewbie87 (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich verbringe nun schon ungefähr eine Woche damit, von einem "serial 
device" mit Hilfe der Qt Klasse <QSerialPort> Daten zu empfangen.

Das Gerät hat einen Loopback Modus und sendet das empfangene Byte 
einfach wieder zurück.
Diese Funktion habe ich mit einer Konsole (putty) getestet, 
funktioniert. :-)

Ich versuche mal das Problem kurz und anschaulich zu erklären:

Ich schreibe ein "abcdefg" auf den seriellen Bus, dies geschieht in der 
main function mit arty.writeCharacter("abcdefg");, siehe:
#include <QCoreApplication>
#include <QDebug>
#include "serial.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    SerialPort arty;

    // Open serial port
    if(arty.openSerialPort() == false)
    {
        qDebug() << "Could not open serial port";
        return 0;
    }


    for(int i=0; i<10; i++)
    {
        arty.writeCharacter("abcdefg");
    }

    qDebug() << "End application!";
    return a.exec();
}


Die Ausgabe in der Konsole sieht wie folgt aus: (siehe console.PNG)

Und zu guter letzt die Implementierung:
#include "serial.h"
#include <QtSerialPort/QSerialPortInfo>
#include <QDebug>


SerialPort::SerialPort(QObject *parent) :
    QObject(parent)
{
    serial = new QSerialPort(this);

    connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this,
            SLOT(handleError(QSerialPort::SerialPortError)));

    connect(serial, SIGNAL(readyRead()), this, SLOT(readData()));
}


SerialPort::~SerialPort()
{

}


bool SerialPort::openSerialPort()
{
    QSerialPortInfo portToUse;

    serial->setPortName("com7");
    serial->setBaudRate(QSerialPort::Baud115200);
    serial->setDataBits(QSerialPort::Data8);
    serial->setParity(QSerialPort::NoParity);
    serial->setStopBits(QSerialPort::OneStop);
    serial->setFlowControl(QSerialPort::SoftwareControl);
    serial->setReadBufferSize(5000); // Aenderung !!!

    if (serial->open(QIODevice::ReadWrite))
    {
        qDebug() << "Connected to serial port";
        return true;
    }
    else
    {
        qCritical() << "Serial Port error:" << serial->errorString();
        return false;
    }
}


void SerialPort::closeSerialPort()
{
    serial->close();
    qDebug() << tr("Disconnected");
}


// private slot
void SerialPort::writeData(const QByteArray &data)
{
    serial->write(data, qstrlen(data));
}


void SerialPort::readData()
{
    qDebug() << "Daten empfangen" << serial->readAll();
    serial->waitForReadyRead(500);
}


void SerialPort::handleError(QSerialPort::SerialPortError error)
{
    if (error == QSerialPort::ResourceError) {
        qCritical() << "Serial Port error:" << serial->errorString();
        closeSerialPort();
    }
}

// Public method to write byte 
void SerialPort::writeCharacter(const QByteArray &data)
{
    serial->write(data);
    serial->waitForBytesWritten(-1);
}


Ich habe mit diversen Delays und Parametern (FlowControl) 
herumexperimentiert, leider ohne Erfolg.

Über Ratschläge und Denkanstöße wäre ich sehr dankbar! :-)

QtNewbie87

Autor: Kai (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

Um was für ein serial device handelt es sich ?

Von Atmel ein Explained Board ?
Wenn ja, dann: Rtf User Guide

Gruß

Autor: QtNewbie87 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Kai,

es handelt sich um ein Arty Board von Digilent mit einem Xilinx Artix 
FPGA drauf.

Ich habe lediglich RX mit TX gekreuzt um eine Loopback zu bekommen. 
Diese habe ich mit putty verifiziert.


Unter Qt habe ich Schwierigkeiten. Irgendwo scheint da ein Denkfehler zu 
sein. Ich sehe ihn nur nicht.


Gruß,
QtNewbie87

Autor: Noch einer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Putty funktioniert? Hast du schon mal geschaut, wie putty das Device 
konfiguriert?

Während der Terminalemulator läuft die Device-Parameter auslesen, 
beenden, Device-Parameter wieder setzen und das eigene Programm starten.

Autor: QtNewbie87 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ja, ich habe die Parameter genauso gesetzt wie putty (FlowControl, 
Parity, Stop-Bit, etc.)

Mir kam schon der Gedanke, ob man für gleichzeitige Lese- und 
Schreibzugriffe wohl besser einen Thread verwendet, allerdings habe ich 
das in dem dazugehörigen "terminal" example von Qt nicht gesehen.

Das funktioniert übrigens auch tadellos ...

Mein Beispiel ist davon abgeleitet.

Autor: Noch einer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du benutzt 2 Threads?

Hatte unter Linux einen ähnlichen Effekt - Mit 2 getrennten Programmen 
für Ein- und Ausgabe würden die gelesenen Zeichen endlos wiederholt.

Mit dem readyRead() Signal kannst du doch in ein paar Minuten einen Test 
mit einem Thread schreiben.

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielleicht braucht QSerialPort eine Eventloop, um richtig zu 
funktionieren. Die startest du ja erst, nachdem du alle deine 
Sendeversuche schon abgeschlossen hast.

Autor: QtNewbie87 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

danke für eure zahlreichen Antworten und Denkanstöße.

Die Idee mit einem  Thread Schreib- und Lesevorgänge zu regeln hört sich 
gut an. Ich werde das gleich morgen versuchen.

Der Vorschlag mit der Eventloop geht ja denke ich auch in diese 
Richtung.

Falls Jemand weiß, wo man sich ein Beispiel für eine Implementierung 
anschauen kann, freue ich mich natürlich :-)

Vielen Dank euch allen!

Sobald ich eine funktionierende Lösung habe, lasse ich euch daran 
selbstverständlich teilhaben.

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kleiner Tip:
Das "a.exec();" startet die "Eventloop", die so lange läuft, wie die 
QApplication nicht beendet wird!

Da braucht man keine anderen Threads. Die "Signals" werden ebenfalls in 
der Eventloop abgehandelt. Daher kann auch kein Datenempfang 
funktionieren, solange die nicht läuft.

Setz die "Schreibschleife" in einen Slot, der mit dem "timeout()" von 
einem QTimer verbunden ist und konfigurier den auf z.B. 1000ms 
SingleShot.
Den startetst Du vor dem Aufruf von "a.exec()". Dann sollte der String 
vollständig durchgehen.

Das mit der Eventloop und den Signals ist ein wenig knifflig.
(Weil nicht offensichtlich)
Da ist auch mein Kollege schon drüber gestolpert ;-)

Die Abhandlung über QTHreads kann u.U. hilfreich sein:

http://blog.debao.me/2013/08/how-to-use-qthread-in...

Das "a.exec()" startet den "MainThread"mit der Eventloop, die dann alle 
Events und "Signals" (zumindest die, welche im Hintergrundüber Events 
laufen) bearbeitet.

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Matthias schrieb:
> Da braucht man keine anderen Threads. Die "Signals" werden ebenfalls in
> der Eventloop abgehandelt.

Nur bei "queued connection", die man braucht, wenn man über 
Thread-Grenzen hinweg Signale mit Slots verbinden will. Innerhalb eines 
Thread hat die Eventloop per Default nichts mit der Verbindung zu tun.

Autor: Noch einer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Setz die "Schreibschleife" in einen Slot

Falls trotzdem die Schreibpuffer überlaufen: QSerialPort erbt auch ein 
bytesWritten() Signal.

>Die Abhandlung über QTHreads kann u.U. hilfreich sein

Würde ich eher von abraten. Alles im "MainThread" mit der Eventloop 
machen und nur wenn ein Slot den MainThread zu lange blockiert über 
Threads nachdenken.

>Nur bei "queued connection"

Mit Hilfe eines QTimer kann man auch innerhalb des selben Threads ein 
Signal über den Eventloop senden. Bzw. an das Ende der Eventqueue 
stellen.

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Nur bei "queued connection", die man braucht, wenn man über

Ich gehe auch davon aus, dass bei QSerialPort in der Backend-Library der
Datenempfang über einen RX-Thread abgehandelt wird. Und dann muss das 
"ReadyRead()"-Signal zwangsläufig über eine Queued-Connection und die 
EventLoop behandelt werden.

>Würde ich eher von abraten. Alles im "MainThread" mit der Eventloop

Ich meinte eigentlich nicht, dass er weitere Threads aufmachen soll.
In der verlinkten Doku wird das Thema Threads nur etwas erläutert und
auch das Thema "MainThread" erwähnt. Ich hoffe das der TO das nicht
so wie Du verstanden hat.

>Falls trotzdem die Schreibpuffer überlaufen: QSerialPort erbt auch ein
>bytesWritten() Signal.

Die Buffer sind per Default auf einem Wert um die 1024..2048 Byte. Die 
kleine Schleife schreibt da gerade mal 70 Bytes rein. Der TO möchte ja 
auch erstmal was zum Laufen bringen. Er sollte sich natürlich auch 
Gedanken machen, was da effektiv an Bytes pro Sekunde über die Leitung 
geht (Sicherheitsmarge nicht vergessen) und einen möglichen 
Buffer-Overflow im Vorfeld vermeiden.

Autor: ratazong (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

die eventqeue kann man auch manuell in main.c triggern (also vor a.exec)

a->processEvents();

das vorweg.

Ich mache nach einem write() immer ein flush() (am einfachsten bei Dir 
in der routine serialport::writedata())

speziell bei Windows bewirkte das manchmal Wunder. Nach flush() schrieb 
er wirklich auf den serialport, sonst nur in irgendeinen Buffer.

Grüße
-Martin

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.