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


von QtNewbie87 (Gast)


Angehängte Dateien:

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:
1
#include <QCoreApplication>
2
#include <QDebug>
3
#include "serial.h"
4
5
int main(int argc, char *argv[])
6
{
7
    QCoreApplication a(argc, argv);
8
    SerialPort arty;
9
10
    // Open serial port
11
    if(arty.openSerialPort() == false)
12
    {
13
        qDebug() << "Could not open serial port";
14
        return 0;
15
    }
16
17
18
    for(int i=0; i<10; i++)
19
    {
20
        arty.writeCharacter("abcdefg");
21
    }
22
23
    qDebug() << "End application!";
24
    return a.exec();
25
}


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

Und zu guter letzt die Implementierung:
1
#include "serial.h"
2
#include <QtSerialPort/QSerialPortInfo>
3
#include <QDebug>
4
5
6
SerialPort::SerialPort(QObject *parent) :
7
    QObject(parent)
8
{
9
    serial = new QSerialPort(this);
10
11
    connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this,
12
            SLOT(handleError(QSerialPort::SerialPortError)));
13
14
    connect(serial, SIGNAL(readyRead()), this, SLOT(readData()));
15
}
16
17
18
SerialPort::~SerialPort()
19
{
20
21
}
22
23
24
bool SerialPort::openSerialPort()
25
{
26
    QSerialPortInfo portToUse;
27
28
    serial->setPortName("com7");
29
    serial->setBaudRate(QSerialPort::Baud115200);
30
    serial->setDataBits(QSerialPort::Data8);
31
    serial->setParity(QSerialPort::NoParity);
32
    serial->setStopBits(QSerialPort::OneStop);
33
    serial->setFlowControl(QSerialPort::SoftwareControl);
34
    serial->setReadBufferSize(5000); // Aenderung !!!
35
36
    if (serial->open(QIODevice::ReadWrite))
37
    {
38
        qDebug() << "Connected to serial port";
39
        return true;
40
    }
41
    else
42
    {
43
        qCritical() << "Serial Port error:" << serial->errorString();
44
        return false;
45
    }
46
}
47
48
49
void SerialPort::closeSerialPort()
50
{
51
    serial->close();
52
    qDebug() << tr("Disconnected");
53
}
54
55
56
// private slot
57
void SerialPort::writeData(const QByteArray &data)
58
{
59
    serial->write(data, qstrlen(data));
60
}
61
62
63
void SerialPort::readData()
64
{
65
    qDebug() << "Daten empfangen" << serial->readAll();
66
    serial->waitForReadyRead(500);
67
}
68
69
70
void SerialPort::handleError(QSerialPort::SerialPortError error)
71
{
72
    if (error == QSerialPort::ResourceError) {
73
        qCritical() << "Serial Port error:" << serial->errorString();
74
        closeSerialPort();
75
    }
76
}
77
78
// Public method to write byte 
79
void SerialPort::writeCharacter(const QByteArray &data)
80
{
81
    serial->write(data);
82
    serial->waitForBytesWritten(-1);
83
}


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

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

QtNewbie87

von Kai (Gast)


Lesenswert?

Hi,

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

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

Gruß

von QtNewbie87 (Gast)


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

von Noch einer (Gast)


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.

von QtNewbie87 (Gast)


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.

von Noch einer (Gast)


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.

von Rolf M. (rmagnus)


Lesenswert?

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

von QtNewbie87 (Gast)


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.

von Matthias (Gast)


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-the-right-way-part-1/

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

von Rolf M. (rmagnus)


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.

von Noch einer (Gast)


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.

von Matthias (Gast)


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.

von ratazong (Gast)


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

von QtNewbie87 (Gast)


Lesenswert?

Vielen Dank für die vielen Ratschläge! :-)

Leider kam ich zeitlich noch nicht dazu an meinem Projekt großartig 
weiterzuarbeiten. Immer mal wieder ein bisschen eben. Ich werde die 
Lösung hier vorstellen, sobald sie ordentlich funktioniert.

Grüße,
QtNewbie87

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.