Forum: PC-Programmierung C++ Comtools RX-Buffer automatisch löschen??


von Patrick B. (p51d)


Lesenswert?

Hallo miteinander

Ich bin gerade dabei für ein kleines Schulprojekt auf der PC Seite ein 
Frame-Protokoll für die RS232-Schnittstelle aufzubauen.
Nach ein paar Anfangsschwierigkeiten konnte ich Daten senden und auch 
empfangen, doch irgendwie macht das Programm nicht das, wass es sollte:

sorry wegen der Formatierung, aber das ganze ist noch im 
Anfangsstadium...
1
private: System::Void timer1_Tick(System::Object^  sender, System::EventArgs^  e) {
2
std::ifstream fin("muster.txt");      // Eingabedatei
3
std::ofstream fout("test_out.txt");
4
std::vector< std::string > allItems;    // Dynamisches Array
5
std::string item;              // Einzelne Werte
6
static int RS232_state = 0;          // RS232-Status
7
8
if( fin.good() && (connect) ){                // Wenn die Datei geöffnet werden konnte
9
  fin.seekg(0L, ios::beg);        // an den Anfang der Datei springen
10
//while( getline( fin, item, ',')){      // Zeilenweise Auslesen, Werte einzel durch ',' getrennt
11
//    allItems.push_back(item);      // Zeile in Vector speichern
12
    switch(RS232_state){
13
      case sendENQ:
14
        ComWrite(COM4,ENQ);
15
richTextBoxMessage->AppendText("sendENQ\n");
16
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
17
        RS232_state = waitACK;
18
        break;
19
      case waitACK:
20
        if(ComRead(COM4) != -1){
21
          if( ComRead(COM4)== ACK ){
22
            RS232_state = sendSOH;
23
          }
24
          else{
25
            RS232_state = sendENQ;
26
          }
27
        }
28
richTextBoxMessage->AppendText("waitACK\n");
29
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
30
        break;
31
      case sendSOH:
32
        ComWrite(COM4,SOH);
33
        RS232_state = sendCONTR;
34
richTextBoxMessage->AppendText("sendSOH\n");
35
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
36
        break;
37
      case sendCONTR:
38
//        ComWrite(COM4,0xE0);
39
//        ComWrite(COM4,(atoi (allItems[0].c_str()) ) );      // String in Int umwandeln und senden
40
        RS232_state = sendAXIS;
41
richTextBoxMessage->AppendText("sendCONTR\n");
42
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
43
        break;
44
      case sendAXIS:
45
//        ComWrite(COM4,SOH);
46
        RS232_state = sendCHK;
47
richTextBoxMessage->AppendText("sendAXIS\n");
48
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
49
        break;
50
      case sendCHK:
51
//        ComWrite(COM4,SOH);
52
        RS232_state = waitACK_CHK;
53
richTextBoxMessage->AppendText("sendCHK\n");
54
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
55
        break;
56
      case waitACK_CHK:
57
        if( ComRead(COM4) != 0 ){
58
          if(ComRead(COM4) == ACK){
59
            RS232_state = sendEOT;
60
          }
61
          else{
62
            RS232_state = sendCONTR;
63
          }
64
        }
65
richTextBoxMessage->AppendText("waitACK_CHK\n");
66
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
67
        break;
68
      case sendEOT:
69
        ComWrite(COM4,EOT);
70
        RS232_state = sendENQ;
71
richTextBoxMessage->AppendText("sendEOT\n");
72
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
73
        break;
74
    }
75
//  }
76
}
77
fin.close();
78
fout.close();
79
     }

Theoretisch sollte das Programm immer dann bei einem Case stehenbleiben, 
wenn es ein ACK vom MCU erwarte, tut es aber nur gerade beim ersten mal:
Zu Debugg-Zwecken gebe ich jeweils die Case-Position noch an ein 
Textfeld aus, und das sieht so aus:
> sendENQ
> waitACK
> waitACK
> waitACK
> waitACK
> waitACK
> waitACK
> waitACK
> sendSOH
> sendCONTR
> sendAXIS
> sendCHK
> waitACK_CHK
> sendEOT
> sendENQ
> waitACK
> sendSOH
> sendCONTR
> sendAXIS
> sendCHK
> waitACK_CHK
> sendEOT
> sendENQ
> waitACK
> sendSOH
> sendCONTR
> sendAXIS
> sendCHK
> waitACK_CHK
> sendEOT
> sendENQ
> waitACK
> .....

Meine Vermutung ist, da ich die ACK's vom MCU über einen Tastendrück 
sende, dass bei der Abfrage "ComRead(COM4) != 0" der Buffer nie gelehrt 
wird, denn so hat er immer ein ACK im RX-Buffer.

Die ganze Comtool-Read-Funktion sieht so aus:
1
int ComRead(unsigned Nr)
2
{
3
unsigned char   c;
4
DWORD      dwCount;
5
6
7
  if(Nr>=MAX_COM_PORTS)return -1;
8
  if(!bIsOpen[Nr])return -1;
9
10
  if(!ReadFile(hComFile[Nr],&c,1,&dwCount,0))return -1;
11
  if(dwCount!=1)return -1;
12
13
14
return c;
15
}

Kann mir da jemand weiterhelfen??
MFG und danke im voraus für die Hilfe
Patrick

von Arc N. (arc)


Lesenswert?

Günstig wäre, das Protokoll etwas näher zu kennen.
Beispiel:
1
case waitACK:
2
        if(ComRead(COM4) != -1){
3
          if( ComRead(COM4)== ACK ){
4
            RS232_state = sendSOH;
5
          }
6
          else{
7
            RS232_state = sendENQ;
8
          }
9
        }

D.h. das vor dem ACK irgendetwas gesendet werden muss, bevor es 
weitergeht.
Wird nur ACK gesendet, wird nie der nächste Zustand erreicht.
Bei waitACK_CHK: muss zuerst 0x00 empfangen werden.
Die Frage ist, soll das vom Protokoll so sein, oder nicht?
Andere Baustelle: Das Update von UI-Elementen muss im Haupt/UI-Thread 
gemacht werden, nicht in einem anderen (Timer/BackgroundWorker, 
DataReceived etc).
Siehe Beitrag "VB2008.NET - Serielle Com Port ansteuerung"

von Patrick B. (p51d)


Lesenswert?

Zum Protokoll:
PC sendet ENQ und der MCU antwortet mit ACK (MCU bereit für Daten).
Jetzt sendet der PC SOH, Controll-Byte, Achsen, CRC und wartet, ob die 
übertragung Richtigt war, wenn ja, antwortet der MCU mit ACK und der PC 
quitiert mit EOT. ansonsten muss noch mal vom Controll-Byte aus alles 
gesendet werden.

Ok, werde mir mal den Tread anschauen.
MFG
Patrick

von Patrick B. (p51d)


Lesenswert?

sry, doppelpost.

hab noch vergessen dass ComRead -1 ausgibt, wenn keine Daten vorhanden 
sind. also wenn Daten da sind, soll überprüft werden was empfangen 
wurde...

von Patrick B. (p51d)


Lesenswert?

Ich hab das Problem beheben können.

Der Fehler lag nicht beim C++ sondern beim C auf dem MCU...
Die ACK's wurden mittels Tastendruck gesendet -> Entprellen vergessen. 
so hat der MCu auch bei kurzem Druck ca 50 ACK's gesendet... welche alle 
schön abgearbeitet wurden...
Das CPP Programm lief einfach schön die ACK's im Buffer ab...

Ich habe noch das
1
if( ComReadCOM4) != -1 ){
2
  if( ComRead(COM4)== ACK ){
3
    RS232_state = sendSOH;
4
  }
5
  else{
6
    RS232_state = sendENQ;
7
  }
8
}
mit
1
if( ComGetReadCount(COM4) != 0 ){
2
  if( ComRead(COM4)== ACK ){
3
    RS232_state = sendSOH;
4
  }
5
  else{
6
    RS232_state = sendENQ;
7
  }
8
}
abgeändert.

Arc Net schrieb:
> Andere Baustelle: Das Update von UI-Elementen muss im Haupt/UI-Thread
> gemacht werden, nicht in einem anderen (Timer/BackgroundWorker,
> DataReceived etc).

was meinst du damit??? im main?
wie soll ich dann das Senden Zeitlich steuern??
Das ganze Frame sollte so maximal alle 30us gesendet werden. es werden 
aber auch noch Verzögerungen benötigt...

soll ich beim Timerevent den Tag setzen und dann aus dem Main heraus 
darauf abfragen damit ich das ENQ zeitlich gesteuert senden kann? der 
rest wird dann einfach automatisch gesendet...

von Arc N. (arc)


Lesenswert?

Patrick B. schrieb:
> Arc Net schrieb:
>> Andere Baustelle: Das Update von UI-Elementen muss im Haupt/UI-Thread
>> gemacht werden, nicht in einem anderen (Timer/BackgroundWorker,
>> DataReceived etc).
>
> was meinst du damit??? im main?

Nein, das Update der Controls. Z.Z. steht z.B. in Timer_Tick
1
richTextBoxMessage->AppendText("sendAXIS\n");
2
richTextBoxMessage->ScrollToCaret();
Das Problem ist, das die Controls nicht thread-safe sind d.h. greift man 
aus einem anderen Thread (das kann ein BackgroundWorker-Thread sein, 
kann aber u.a. auch ein Thread sein, der von einem System.Timer genutzt 
wird) auf ein Control zu, gibt es eine Exception.

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired.aspx

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.checkforillegalcrossthreadcalls.aspx

http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx

Beitrag "VB2008.NET - Serielle Com Port ansteuerung"

von Patrick B. (p51d)


Lesenswert?

Arc Net schrieb:
> Das Problem ist, das die Controls nicht thread-safe sind d.h. greift man
> aus einem anderen Thread (das kann ein BackgroundWorker-Thread sein,
> kann aber u.a. auch ein Thread sein, der von einem System.Timer genutzt
> wird) auf ein Control zu, gibt es eine Exception.

=> soll das soviel heissen wie timerEvent greifft auf richTextBox zu...
wie kann ich dies unterbinden?? so dass ich beim timerEven ein "Flag" 
setzte auf welches ich aus der richTextBox abfrage und entsprechendes 
setze...


korriegiere mich, wenn ich falsch liege..
MFG
Patrick

von Arc N. (arc)


Lesenswert?

Z.Z. sieht es so aus
1
private: System::Void timer1_Tick(System::Object^  sender, System::EventArgs^  e) {
2
    ...
3
    switch(RS232_state){
4
      case sendENQ:
5
        ComWrite(COM4,ENQ);
6
richTextBoxMessage->AppendText("sendENQ\n");
7
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
8
        RS232_state = waitACK;
9
        break;
d.h. innerhalb des Timer.Tick-Events wird auf die RichTextBox 
zugegriffen.
Was schief gehen kann.
Lösen kann man das z.B. so
1
// delegate for Control.Invoke
2
delegate void RTBDelegate(void);
3
RTBDelegate^ RTBUpdateHandler;
4
5
void UpdateRTB() {
6
    // update RichTextBox here
7
}
8
9
Form1(void) {
10
    InitializeComponent();
11
12
    RTBUpdateHandler = gcnew RTBDelegate(this, &Form1::UpdateRTB);
13
}
14
15
private: System::Void timer1_Tick(System::Object^  sender, System::EventArgs^  e) {
16
    ...
17
    switch(RS232_state){
18
      case sendENQ:
19
        ComWrite(COM4,ENQ);
20
        // either direct update or invoke
21
        if (InvokeRequired) {
22
            Invoke(RTBUpdateHandler);
23
        } else {
24
            UpdateRTB();
25
        }
26
        RS232_state = waitACK;
27
        break;
http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx
http://msdn.microsoft.com/en-us/library/ms171728.aspx

p.s. bei der Gelegenheit, sollte man auch das Datei-Handling aus dem 
Timer herausnehmen, u.a. deshalb, da falls dort irgendetwas zu lange 
dauert, es durchaus passieren kann, dass Timer.Tick erneut aufgerufen 
wird bevor der letzte Durchlauf beendet ist (mit allen Konsequenzen)

p.p.s. wie hier im Forum (und auch anderenorts) häufiger dargelegt, ist 
jede Sprache != C++ besser, wenn man für das .NET Framework entwickelt.
Also vllt auch nochmal überlegen, ob es wirklich C++ bleiben soll/muss.

von Patrick B. (p51d)


Lesenswert?

Arc Net schrieb:
> p.s. bei der Gelegenheit, sollte man auch das Datei-Handling aus dem
> Timer herausnehmen, u.a. deshalb, da falls dort irgendetwas zu lange
> dauert, es durchaus passieren kann, dass Timer.Tick erneut aufgerufen
> wird bevor der letzte Durchlauf beendet ist (mit allen Konsequenzen)
Das ist sowieso eine themoräre Lösung: später sollte das ganze sehr viel 
schneller laufen, das ENQ sollte maximal alle 30us gesendet werden, was 
aber mit einem Faktor mutlipliziert wird, je nach Differenz zweier 
Punkte in der TXT-Datei.
Nochmals später sollte das ganze über den d2xx.dll Treiber von FTDI 
laufen.


> p.p.s. wie hier im Forum (und auch anderenorts) häufiger dargelegt, ist
> jede Sprache != C++ besser, wenn man für das .NET Framework entwickelt.
> Also vllt auch nochmal überlegen, ob es wirklich C++ bleiben soll/muss.
Ok, nur so zu meiner momentanen Situation:
- Ich habe seit 3 Jahren C in der Schule
- Seit dem Sommer sind wir an C++ (Borland VCL),
  Zuhause aus eigenem Interesse mit Visual C++ Express 2008
- Hobbymässig für eine Homepage PHP, HTML und JS, nutze ich jetzt aber 
nicht
  mehr oft.

Bei C++ kann ich sehr viel aus dem C übernehmen, es sind einfach noch 
komplexe Syntaxe und Klassen...
Was würdes du empfehlen??

MFG
Patrick

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.