Forum: PC-Programmierung C# Serielles Telegramm mit Antwort vom µC


von Roblue (Gast)


Lesenswert?

Hallo Freunde,

bisher hab ich mich fast ausschließlich mit der µC-Programmierung 
beschäftigt. Nun hab ich mich ein wenig in die PC-Programmierung mit C# 
eingelesen und habe mir recht schnell eine Klasse zur Kommunikation mit 
dem µC geschrieben, inkl. GUI per WinForms.
Nun brauche ich einen kleinen Denkanstoß, denn ich finde, so wie ich es 
derzeit mache, ist reinster Pfusch.

Vorweg: Momentan funktioniert alles einwandfrei. Wenn serielle Kommandos 
allerdings recht schnell wiederholt von der GUI gesendet werden, gibt es 
oft "Kollisionen". Mein µC sendet nach jedem erkannten Kommando eine 
eigene Bestätigungssequenz.
Wenn diese Sequenz erkannt wird, schalte ich einen Bool-Typ auf true, 
welcher anschließend wieder auf false gesetzt wird.


Hier mal ein Auszug der das veranschaulicht:
1
try
2
{
3
  MyComPort.Open();
4
  if (MyComPort.IsOpen == true)
5
  {
6
    Befehl(BefehlVerbinden);
7
    while (rx_antwort == false)
8
      ;
9
    Befehl(BefehlConfig);
10
  }
11
}
12
catch (Exception ex)
13
{
14
  MessageBox.Show("Fehler beim öffnen des Ports!\r\n" + ex.Message, "Fehler!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
15
}

Wie man erkennen kann, sitzt hier eine while-Schleife dazwischen die nur 
darauf wartet, endlich eine Antwort zu bekommen, damit der nächste 
Befehl gesendet werden kann. Bis die Antwort kommt, steht das Programm 
aber. Das haut zwar hin nur frage ich mich, wie man das schöner lösen 
könnte.

Hat da vielleicht jemand einen Tipp?

PS: Empfangene Daten werden per SerialDataReceivedEventArgs und 
anschließender Auswertung bearbeitet.

LG,
roblue

von Joe (Gast)


Lesenswert?

Du könntest das Ganze z.B. in einen Thread packen, dadurch funktioniert 
das GUI und die dazugehörigen Funktionen auch während man auf die 
Antwort wartet.
Ist natürlich fraglich ob das in deiner Anwendung Sinn ergibt, aber 
vllt. hilft's dir ja.

MfG

von Roblue (Gast)


Lesenswert?

Hallo Joe!

Danke für die schnelle Antwort! Bin bereits auf dieses Thema gestoßen. 
In meinem C# Handbuch hab ich schon ein wenig über die Threads gelesen. 
Habe auch schon mal einen Versuch damit gemacht, allerdings hatte ich 
zum Schluss unzählige laufende Threads offen, sodass sich die Anwendung 
erst aufgehängt hat...

Muss mir das noch sehr genau ansehen. Im Prinzip bräuchte ich ja nur 
meine "BefehlSenden" Methode in einem eigenen Thread. Allerdings weiß 
ich noch nicht, wie das mit einer seperaten Klasse funktioniert.

LG,
roblue

von Roblue (Gast)


Lesenswert?

Hallo zusammen!

Habe nun ein paar Beispiele mit Threads programmiert, allerdings nicht 
ganz zufriedenstellend. Was haltet ihr davon:
1
// Lokale Methode zum schreiben der Befehle
2
private void BefehlSenden(byte[] Befehl)
3
{
4
  int timeoutZeit = 100;
5
  int lokalerCounter = 0;
6
7
  if (MyComPort.IsOpen)
8
  {
9
    for (int i = 0; i <= Befehl.Length - 1; i++)
10
    {
11
      MyComPort.BaseStream.WriteByte(Befehl[i]);
12
    }
13
    while (rx_antwort == false)
14
    {
15
      lokalerCounter++;
16
      Thread.Sleep(10);
17
18
      if (lokalerCounter >= timeoutZeit)
19
      {
20
        MessageBox.Show("Keine Verbindung zur Steuerung!\r\n", "Fehler!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
21
        break;
22
      }
23
    }
24
  }
25
}


Mit Thread.Sleep(10) lasse ich in der Schleife das Programm für 10ms 
anhalten und einen Counter hochzählen, bis die Timeout-Zeit von 1sek. 
erreicht wurde.

Das Programm darf von mir aus ruhig kurzfristig in eine Schleife fallen. 
Hab das nun ein paar mal versucht und das dürfte wohl funktionieren. Die 
Frage, die ich mir stelle ist aber, ob das andere Programmierer auch so 
machen bzw. machen würden?

LG,
roblue

von Tastkopf (Gast)


Lesenswert?

ob erfahrene Windowsprogrammierer das so machen würden ist schwer zu 
sagen, ich versteh ja nichmal warum du dieses Heckmeck machst. Ich hab 
zwar schon öfter Programme geschrieben, welche die Serielle 
Schnittstelle nutzen um mit einem uC zu Kommunizieren. Allerdings fällt 
mir kein Einsatzzweck ein welcher deine Art der Kommunikation brauchen 
würde.
Erklär mal was deine Schnittstelle da tut, vielleicht kann dir dann 
jemand sagen ob man das hätte besser machen können.

von Sven H. (dsb_sven)


Lesenswert?

Ich würde vermutlich eher eine Art Watchdog basteln, der anschlägt, wenn 
eine gewisse Zeit lang nichts empfangen wurde und nicht eine Zeit lang 
gucken, ob etwas da ist.

von Roblue (Gast)


Lesenswert?

Es geht um die Auswertung eines seriellen Telegramms. Die Daten kommen 
von einer Steuerung mit Mikrocontroller. Der sendet nun Daten im 
folgenden Format:
BYTE1 = Telegrammstart
BYTE2 = Geräte-ID
BYTE3 = Anzahl der Bytes im Telegramm
BYTE4 = Funktion
BYTE5 = Daten 0
BYTE6 = Daten 1
usw...

Das sendet er nur nach Aufforderung, also ich muss sagen welche Daten 
ich haben will bzw. welche ich übermitteln will. Zudem wird immer noch 
eine Bestätigung in folgender Form mitgeschickt:
BYTE1 = Telegrammstart
BYTE2 = Geräte-ID
BYTE3 = Anzahl der Bytes im Telegramm
BYTE4 = 0x90
BYTE5 = 0x01

Und genau diese Antwort muss ich abwarten, bis ich den nächsten Befehl 
an die Steuerung senden darf. Deswegen auch die Schleife, die auf die 
Antwort wartet.

von bluppdidupp (Gast)


Lesenswert?

Ich würde das irgendwie so machen:
https://gist.github.com/70a4a5e5435fbd88908c

Der Button packt nen Kommando in eine Queue, und die wiederum wird von 
einem BackgroundWorker sequentiell abgearbeitet.
Bei Klick auf den Button blockiert die GUI dann nicht.

BackgroundWorker kann man direkt auf eine Form ziehen und stellt quasi 
eine Wrapper-Klasse um einen Thread dar:
- In DoWork() läuft der Thread ab.
- Mit .RunWorkerAsync() wird der Thread gestartet
- Mit .ReportProgress() kann der Thread der GUI Infos mitteilen
- Mit .CancelAsync() kann die GUI dem Thread via .CancellationPending 
mitteilen, dass er mal Feierabend machen soll ;D

Mit:
1
lock(this)
2
{
3
    // Synchronisierte Code
4
}
...sorgt man dafür der darin verbaute Code immer nur von einem Thread 
gleichzeitig betreten werden darf (d.h. im Beispiel kann .Enqueue() und 
.Dequeue() nicht gleichzeitig laufen)
Solche Abschnitte sollte man möglichst kurz halten: Wenn 2 Threads 
gleichzeitig einen solchen Abschnitt betreten wollen, muss einer von 
beiden warten - Im Falle des GUI-Threads würde die GUI dann solange 
einfrieren.

Zu beachten bei Windows.Forms: GUI-Elemente (Textboxen, Labels, etc.) 
dürfen in DoWork() nicht verändert werden, da sollte man immer den Umweg 
über .ReportProgress() nehmen.

von Roblue (Gast)


Lesenswert?

Hallo bluppdidupp!

Vielen Dank für die ausführliche Erklärung. Funktioniert prima und ist, 
so wie ich finde, eine recht solide Lösung.

LG,
Roblue

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.