Forum: PC-Programmierung [C++] RS232 Zeichen empfangen, mit/ohne Thread?


von Gelöscht (kami89)


Lesenswert?

Hallo zusammen,

Ich arbeite an einer C++ Klasse für die Kommunikation per RS232. Diese 
soll sich auch gleich um mein "eigenes" Protokoll kümmern (Header, 
CRC,...) und direkt in meine (zukünftigen) Projekte integriert werden 
können.

Das Senden von Daten ist auch kein Problem. Aber das Empfangen ist so 
eine Sache, man hat halt nicht so einen genialen Interrupt wie auf den 
AVRs. Da kam ich auf die Idee dass die Klasse im Constructor ihren 
eigenen Thread startet, der sich dann ums Empfangen kümmert. Ist ein 
ganzes Datenpaket angekommen, soll per Funktionszeiger eine Funktion 
ausserhalb der Klasse (also im Projekt des Anwenders) aufgerufen und die 
Daten übergeben werden.

Das habe ich nun mal so gemacht, und es funktioniert grundsätzlich auch 
nicht schlecht. Da mein Programm (mit QT-Creator erstellt) aber von Zeit 
zu Zeit mal abstürzt, bin ich auf den Hinweis gestossen, dass man beim 
QT-Creator viele Sachen nicht aus einem Thread heraus machen darf. Vor 
allem auf GUI-Elemente dürfe man nicht zugreifen, und genau das mache 
ich momentan.

Ich will nämlich die empfangenen Daten gleich in einer Liste auflisten 
lassen, und ich wüsste nicht wie ich das machen soll, wenn es nicht aus 
dem Thread heraus gemacht werden darf.

Wie kann ich nun also aus dem Hauptthread heraus eine gewisse Funktion 
aufrufen sobald ein Datenpaket oder ein Byte empfangen wurde?
Oder wie macht "man" das sonst?
Natürlich sollte nicht eine Verzögerung von 2 Sekunden eintreten :-)

Es sollte auch eine möglichst universelle Lösung sein, damit die Klasse 
möglichst universell einsetzbar ist. Ausserdem möchte ich die Klasse für 
Linux und Windows programmieren, um plattformunabhängig zu sein. Vorerst 
ist aber vorallem die Linux-Variante wichtig, um den Windows-Kram würde 
ich mich später kümmern.

Ich hoffe ihr habt da ein paar gute Tipps :-)

freundliche Grüsse
Urban

von Peter II (Gast)


Lesenswert?

Urban B. schrieb:
> Wie kann ich nun also aus dem Hauptthread heraus eine gewisse Funktion
> aufrufen sobald ein Datenpaket oder ein Byte empfangen wurde?

du schickt einfach aus denen Thread eine WindowsMessage das jetzt neue 
Daten das sind. Ich kenn QT aber nicht, aber irgendwo gibt es bei jeder 
windowsanwendung eine Hauptschleife wo alles nachrichten verarbeitet 
werden, dort musst du die Nachricht dann auswerten.

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?


von Gelöscht (kami89)


Lesenswert?

Hi,

Peter II schrieb:
> du schickt einfach aus denen Thread eine WindowsMessage das jetzt neue
> Daten das sind. Ich kenn QT aber nicht, aber irgendwo gibt es bei jeder
> windowsanwendung eine Hauptschleife wo alles nachrichten verarbeitet
> werden, dort musst du die Nachricht dann auswerten.

Ich glaube eben das macht QT irgendwie "im Hintergrund". Jedenfalls gibt 
es im ganzen Code keine Hauptschleife. Und ob das universell einsetzbar 
ist wäre ich mir auch nicht so sicher...

Μαtthias W. schrieb:
> http://stackoverflow.com/questions/638251/how-to-e...

Das würde vermutlich funktionieren, aber ich möchte eigentlich eine 
Lösung haben, die auch ausserhalb von Qt funktioniert. Wenn ich mal eine 
Konsolenanwendung mache, bei der ich meine Klasse einbinden will, habe 
ich den ganzen Qt-Kram ja nicht.

mfg

von Peter II (Gast)


Lesenswert?

Urban B. schrieb:
> Ich glaube eben das macht QT irgendwie "im Hintergrund". Jedenfalls gibt
> es im ganzen Code keine Hauptschleife.

google liefert:

http://doc.qt.nokia.com/latest/qwidget.html#winEvent

QWidget::winEvent

von Markus M. (mark_m)


Lesenswert?

Du empfängst die Daten von der RS232 in einem Lese-Thread. Die 
empfangenen Daten legst Du z.B. in einem LIFO ab. Von dort holt es dann 
ein Verarbeitungs-Thread ab, der die Daten weiter verarbeitet. Somit hat 
Du Lese-Thread und Verabeitungs-Thread entkoppelt.

Im Lese-Thread würde ich keinerlei Datenbearbeitung betrieben. Nur Daten 
lesen und weiterreichen.

Die Funktionalität kannst Du komplett in der Modellschicht unterbringen. 
Das ist dann auch portierbar.

Grüsse

von Gelöscht (kami89)


Lesenswert?

Peter II schrieb:
> google liefert:
>
> http://doc.qt.nokia.com/latest/qwidget.html#winEvent
>
> QWidget::winEvent

Ist aber auch wieder eine reine Qt-Lösung, oder kann man das auch ohne 
Qt auf diese Weise machen?

Markus M. schrieb:
> Du empfängst die Daten von der RS232 in einem Lese-Thread. Die
> empfangenen Daten legst Du z.B. in einem LIFO ab. Von dort holt es dann
> ein Verarbeitungs-Thread ab, der die Daten weiter verarbeitet. Somit hat
> Du Lese-Thread und Verabeitungs-Thread entkoppelt.
>
> Im Lese-Thread würde ich keinerlei Datenbearbeitung betrieben. Nur Daten
> lesen und weiterreichen.
>
> Die Funktionalität kannst Du komplett in der Modellschicht unterbringen.
> Das ist dann auch portierbar.

Dann könnte ich aber auch gleich die Empfangsfunktion von ausserhalb der 
Klasse aufrufen lassen, dann kann ich mir die Threads gleich komplett 
sparen. Das puffern übernimmt ja eh schon das Betriebssystem. Ich finde 
es aber halt irgendwie ziemlich mühsam, wenn der Anwender dann noch 
irgend ein Timer oder ähnliches braucht, um die Daten zu empfangen. Wenn 
die Klasse das alles automatisch übernimmt, und mir meldet wenn was 
reinkommt, wäre das doch echt komfortabel.

Scheint aber wohl nicht so einfach zu sein. Dann muss ich halt doch noch 
eine Art Timer in meine Programme einbinden...
Gibts vielleicht Tipps, wie ich diesen Timer am besten realisiere?

von Peter II (Gast)


Lesenswert?

Urban B. schrieb:
> Ist aber auch wieder eine reine Qt-Lösung, oder kann man das auch ohne
> Qt auf diese Weise machen?
ja auch QT zu verzichten. Etweder macht du QT oder nicht.

> Dann muss ich halt doch noch
> eine Art Timer in meine Programme einbinden
der Dann wieder ein Thread ist? oder doch ein QT-Timer - was dann wieder 
QT ist.

von Gelöscht (kami89)


Lesenswert?

Peter II schrieb:
> Urban B. schrieb:
>> Ist aber auch wieder eine reine Qt-Lösung, oder kann man das auch ohne
>> Qt auf diese Weise machen?
> ja auch QT zu verzichten. Etweder macht du QT oder nicht.
>
>> Dann muss ich halt doch noch
>> eine Art Timer in meine Programme einbinden
> der Dann wieder ein Thread ist? oder doch ein QT-Timer - was dann wieder
> QT ist.

Du hast mein Problem erkannt :-)

Nein im Ernst, gibt es denn keine gescheite Lösung, die auch unabhängig 
von Qt oder sonstwas ist? Halt etwas, das nur die C++ Werkzeuge 
verwendet. Ich möchte nicht für jedes ach so kleines Testprogramm, das 
ein bisschen mit meinen AVRs plaudern soll, das Rad neu erfinden. Ob ich 
nun einen Lastwagen oder ein Cabriolet baue, ob aus Stahl oder 
Kunststoff, ist doch egal, die Räder sind doch immer die gleichen :-)

von Peter II (Gast)


Lesenswert?

Urban B. schrieb:
> Nein im Ernst, gibt es denn keine gescheite Lösung, die auch unabhängig
> von Qt oder sonstwas ist? Halt etwas, das nur die C++ Werkzeuge
> verwendet.

Du hast dich aber schon auf QT festgelegt, damit musst du dich auch an 
die Regeln von QT halten. Wenn du nur C++ verwenden willst warum hast du 
dann QT verwendet?

QT nimmt man ja weil man das Rad nicht neu erfinden will, also verzichte 
auf QT und man es mit den Betriebssystem funktionen oder nutze mit 
Möglichkeiten von QT.

von Gelöscht (kami89)


Lesenswert?

Peter II schrieb:
> Du hast dich aber schon auf QT festgelegt, damit musst du dich auch an
> die Regeln von QT halten. Wenn du nur C++ verwenden willst warum hast du
> dann QT verwendet?
>
> QT nimmt man ja weil man das Rad nicht neu erfinden will, also verzichte
> auf QT und man es mit den Betriebssystem funktionen oder nutze mit
> Möglichkeiten von QT.

Du weisst aber schon was Qt, bzw. der Qt Creator ist oder? Damit mache 
ich halt einfach die GUI, weil das, Zitat: "Mit den Betriebssystem 
funktionen", reeelativ mühsam werden kann :-)

Das Programm wird ja trotzdem in C++ geschrieben, mit all den C++ 
Features die es halt so gibt. Qt ist quasi eine Erweiterung um die GUI 
zu gestalten.

Wenn du mir was Besseres empfehlen kannst, immer her damit! Ich nutze Qt 
erst seit kurzem, von dem was ich so gesehen habe scheint das eine 
ziemlich Gute Sache zu sein, vorallem halt auch um plattformunabhängig 
zu programmieren. Und jetzt komm bitte nicht mit Java, das kann ich 
nicht, und habe momentan auch nicht die Zeit um es zu lernen :-)

von Rolf M. (rmagnus)


Lesenswert?

Urban B. schrieb:
> Du weisst aber schon was Qt, bzw. der Qt Creator ist oder?

Weißt du es denn?

> Das Programm wird ja trotzdem in C++ geschrieben, mit all den C++
> Features die es halt so gibt. Qt ist quasi eine Erweiterung um die GUI
> zu gestalten.

Qt besteht aus einer ganzen Reihe von Bibliotheken, und eine davon ist 
für GUI-Programmierung. Es bietet aber viel mehr als nur GUI und hat 
auch die Möglichkeit, Programme komplett ohne GUI zu schreiben.
Man kann natürlich Qt nur für die GUI verwenden und für den ganzen Rest 
was anderes, aber dann sollte man dafür auch einen guten Grund haben.

von Gelöscht (kami89)


Lesenswert?

Ja, ich weiss schon dass Qt noch eine ganze Menge mehr kann. Ich habe ja 
auch nie behauptet, dass ich nur die GUI von Qt verwende und sonst 
nichts.

Wenn ich aber eine C++ Klasse für die Kommunikation per RS232 schreibe, 
möchte ich die entweder mit oder ohne Qt einsetzen können. Das soll ja 
die Klasse nicht kümmern, ob ich ausserhalb der Klasse Qt verwende oder 
nicht, die soll einfach tun was sie tun soll.

Dabei soll sie aber eine Schnittstelle nach aussen haben, um eben ein 
empfangenes Paket weiterleiten zu können. Die Frage ist jetzt aber, wie 
ich diese Schnittstelle möglichst universell realisiere, um flexibel zu 
sein.

von Peter II (Gast)


Lesenswert?

Urban B. schrieb:
> Wenn ich aber eine C++ Klasse für die Kommunikation per RS232 schreibe,
> möchte ich die entweder mit oder ohne Qt einsetzen können. Das soll ja
> die Klasse nicht kümmern, ob ich ausserhalb der Klasse Qt verwende oder
> nicht, die soll einfach tun was sie tun soll.
im C++ Standard gibt es keine Threads keine Serielle schnittstelle. Also 
must du ohne QT zwangsläufig Betriebystem funktionen nutzen und bist 
nicht universell

> Dabei soll sie aber eine Schnittstelle nach aussen haben, um eben ein
> empfangenes Paket weiterleiten zu können. Die Frage ist jetzt aber, wie
> ich diese Schnittstelle möglichst universell realisiere, um flexibel zu
> sein.

ist ja auch kein Prblem - hast du ja schon gelöst. Nur wenn du jetzt 
ebend QT nutzen willst musst du auch nach den Regel von QT arbeiten. 
Also nutze die QT möglichkeiten die Info an den Haupthtread 
weiterzuleiten.

von Nur Ein Gast (Gast)


Lesenswert?

Ich mach das mit Signal in Thread und Slot im Hauptprogram.

von Markus M. (mark_m)


Lesenswert?

Es gibt die POSIX Thread Library (Pthreads) und die Boost Theads 
Library.

Grüsse

von Gelöscht (kami89)


Lesenswert?

Peter II schrieb:
> im C++ Standard gibt es keine Threads keine Serielle schnittstelle. Also
> must du ohne QT zwangsläufig Betriebystem funktionen nutzen und bist
> nicht universell

Mit universell meine ich nicht plattformunabhängig, sondern die Art und 
Weise, wie ich die Klasse nutze. Dass ich die Serielle Schnittstelle und 
die Threads für jedes OS einzeln programmieren muss, ist mir egal. Das 
muss ich ja auch nur einmal machen. Ob ich dann mein Programm aber im 
Notepad, in Eclipse oder im QT-Creator schreibe, das soll keine Rolle 
spielen. Das meine ich mit universell.

Peter II schrieb:
> ist ja auch kein Prblem - hast du ja schon gelöst. Nur wenn du jetzt
> ebend QT nutzen willst musst du auch nach den Regel von QT arbeiten.
> Also nutze die QT möglichkeiten die Info an den Haupthtread
> weiterzuleiten.

Das wäre natürlich eine Möglichkeit, dass ich wie bisher einen Thread 
fürs Empfangen verwende, und für Qt dann extra irgendwas "bastle", damit 
der Hauptthread dann die Daten bekommt. Ist dann aber eben nicht mehr so 
universell wie ich das gerne gehabt hätte :-)

Ich dachte halt, es gibt bestimmt irgendwas, das ich noch nicht kenne. 
Ich programmiere erst seit ca. 3 Jahren C und seit einem halben Jahr 
C++.
Aber so wie es hier ausschaut geht das wohl nicht viel komfortabler. Das 
ist irgendwie doch traurig, dass so etwas simples auf einem uC 
stinkeinfach, und auf einem PC ziemlich mühsam ist?!

von Gelöscht (kami89)


Lesenswert?

Nur Ein Gast schrieb:
> Ich mach das mit Signal in Thread und Slot im Hauptprogram.

Und dann sind Signal und Slot also quasi voneinander entkoppelt? Das 
wäre vielleicht eine Möglichkeit, die bei Verwendung von Qt-Creator 
funktionieren könnte. Und wenn es ohne Qt sein soll, dann halt einfach 
mit dem Thread, der eine Funktion per Pointer aufruft.

Markus M. schrieb:
> Es gibt die POSIX Thread Library (Pthreads) und die Boost Theads
> Library.
>
> Grüsse

ääh und was willst du nun damit sagen? :-D Momentan verwende ich einen 
POSIX Thread.

von NurEinGast (Gast)


Lesenswert?

> die bei Verwendung von Qt-Creator funktionieren

QtCreator ist doch "nur" eine Oberfläche, die die Verwendung der 
Funktionen der QT Library erleichtert. QtCreator nimmt auch nur Dein 
Programm, jagt es durch einen Compiler und bindet als Library halt ( 
meisstens ) die Qt Lib dazu. Und deshalb kannst Du die QT Funktionen und 
Dienste nutzen.

Boost bietet auch solche Dienste an.....

Posix Thread holst Du Dir doch auch aus einer Library  ....
( Ich nenne das jetzt einfach mal zusammengefasst als Schlagwort 
"Library")

Du willst eine Funktion nutzen, die eine wie auch immer geartete Lib 
bietet. Irgendwann hast Du dich dann halt für die Lib entschieden, die 
Dir für Dich am besten geeignet scheint. Ok Und nun willst Du, dass das 
ganze auch für alle anderen Libs portabel sein soll ? Irgendwie ist das 
zu viel verlangt.

> Das
> ist irgendwie doch traurig, dass so etwas simples auf einem uC
> stinkeinfach, und auf einem PC ziemlich mühsam ist?!

Nein - nicht wirklich.
Auch der PC verwendet im Endefekt einen UART und seine Interrupts.
Nur möchtest Du das auf dem Microcontroller "von Hand" bearbeiten.
Ok so weit.

Auf dem PC geht das natürlich auch.
Unter DOS kann man das genau so machen.
Wenn Du aber eine Betriebssystem verwenden möchtest, dass für Dich die 
Hardware versteckt, und noch Multitasking macht und noch ne GUI hat und 
das ganze Buffern übernimmt usw. usw. - dann wird halt nicht mehr sooo 
übersichtlich und auf den ersten Blick verständlich.

Wenn Du auf dem Microcontroller ein Betriebsystem laufen lässt, dann 
sieht es dort doch ganze ähnlich aus. Nimm nen LPC3131 mit Linux, oder 
nen ARM mit Embedded Windows oder QNX oder was auch immer. Von mir aus 
PicoOS auf nem AVR. Und schon hast Du die gleichen "Probleme" wie auf 
dem PC.

Du hast Dich für ein System entschieden, dass Dir Arbeit abnehmen soll - 
und diese Systeme sind halt nicht immer alle vollständig untereinder 
kompatibel. Sonst wären sie ja alle gleich

Ich denke es ist nicht abhängig von Microcontroller / PC. Ok - PC 
Hardware
ist komlexer wie Microcontroller Hardware.

von NurEinGast (Gast)


Lesenswert?

> Ob ich dann mein Programm aber im
> Notepad, in Eclipse oder im QT-Creator schreibe, das soll keine Rolle
> spielen. Das meine ich mit universell.

Das spielt keine Rolle.
Ich schreibe meine QT Programm manchmal mit QtCreator, aber auch mit VI, 
Joe, Notepad .... Geht auch mit Eclipse.

Das eigentlich wichtige ist, wie Du es durch dem Compiler jagst und 
welche Lib's Du verwendest und dazu linkst.

Ob Du das mit QtCreator machst, oder mit VI und Makefile, oder gar mit 
EdLin und dann den Compiler und Linker - von mir aus - mit den 
entsprechenden Parametern von Hand aufrufst .... gehen tut das alles.
Das eine ist bequemer, das andere nicht.
Aber die Funktion Deines Programmes und der verwendeten Libraries wird 
sich dadurch nicht ändern.

von Gelöscht (kami89)


Lesenswert?

OK Ihr habt ja alle recht :-)

Ich kann mich aber irgendwie nicht so richtig mit dem Gedanken 
anfreunden, dass ich für ein winzig kleines Kommandozeilenprogramm auch 
noch die Qt-Libs verwenden muss, einzig und allein nur um meine eigene 
RS232-Klasse nutzen zu können. Das scheint mir irgendwie ein bisschen 
wie mit Kanonen auf Spatzen geschossen.

Naja, dann mache ich es halt doch so, dass das eigene Programm die 
RS232-Klasse ständig pollen muss um Zeichen empfangen zu können. Dann 
brauche ich keine Threads und kein Qt.
Dann muss ich jetzt schauen wie ich bei meinen Qt-Programmen am besten 
einen Timer realisiere, der nicht als eigenständiger Thread arbeitet, 
sondern vom Hauptthread aufgerufen wird.

mfg

von Tom K. (ez81)


Lesenswert?

Du könntest deiner Kommunikationsklasse eine Callback-Funktion (oder 
besser ein Objekt) mitgeben, das sie aufruft, wenn sie eine Nachricht 
empfangen hat.
Nur ganz grob skizziert, will gleich noch weg:
1
class Callback
2
{
3
   virtual HandleMessage(const MyMessageType& bla) = 0;
4
};
5
6
class RS232Receiver
7
{
8
   RS232Receiver(Callback& c) : m_foo(c)
9
   {
10
   }
11
12
   Callback& m_foo;
13
14
   void ThreadFunc()
15
   {
16
      ...
17
      if (completeMessageReceived)
18
         m_foo.HandleMessage(someMessage);
19
      ...
20
   }
21
};

Du  könntest dann eine Klasse von Callback ableiten, die mit der 
Nachricht entweder direkt etwas tut (simples Konsolenprogramm) oder sie 
einfach in ein QT-Signal übersetzt und weiterschubst. Die Experten a la 
Karl Heinz Buchegger werden morgen sicherlich eine viel elegantere 
Lösung parat haben ;)

von Gelöscht (kami89)


Lesenswert?

Tom K. schrieb:
> Du könntest deiner Kommunikationsklasse eine Callback-Funktion (oder
> besser ein Objekt) mitgeben, das sie aufruft, wenn sie eine Nachricht
> empfangen hat.

Ja, so ähnlich habe ich es ja bisher gemacht, halt einfach mit einem 
Funktionszeiger, den ich meiner Klasse übergebe.

Wie "gefährlich" sind eigentlich Threads allgemein, also ohne den 
Einsatz von Qt? Muss man da auch darauf achten dass verschiedene Threads 
nicht auf die gleichen Variablen zugreifen dürfen? Muss man wie beim uC 
einfach volatile Variablen einsetzen?

Wenn die Threads eh ziemlich gefährlich werden können, verzichte ich 
dann wohl doch lieber darauf. Schlussendlich kann "der Anwender" ja mit 
dem Funktionszeiger machen was er will, was dann gefährlich werden 
könnte. Ständiges pollen wäre zwar mühsamer für den Anwender, dafür aber 
auch sicherer.

von Gelöscht (kami89)


Lesenswert?

Jetzt bin ich noch auf eine ganz andere Idee gekommen.
Angenommen, ich schreibe statt einer einzelnen Klasse gleich ein ganzes 
Terminalprogramm. Dieses Programm liest per std::cin die Befehle ein und 
gibt empfangene Datenpakete, Fehlermeldungen usw. per std::cout aus. Das 
Empfangen könnte dann ohne Probleme mit einem Thread erfolgen. Dann 
könnte ich in meinen Programmen diese Anwendung starten, Befehle 
übergeben und deren Ausgaben einlesen.

Wäre das eine gute Idee?
Bei Linuxanwendungen sieht man ja häufig, dass für reine 
Terminalanwendungen eine optionale GUI erhältlich ist, das funktioniert 
doch auch nach diesem Prinzip, oder?

Ein Vorteil dabei wäre auch, dass ich diese Anwendung unter einem 
bestimmten Pfad abspeicheren kann, und alle meine Programme dort die 
Konsolenanwendung starten. Gibt es nun mal eine neue Version vom 
Konsolenprogramm, muss ich lediglich diese eine Datei überschreiben, und 
alle meine Anwendungen haben gleich die neuste Version :-)

mfg

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.