mikrocontroller.net

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


Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht 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...
private: System::Void timer1_Tick(System::Object^  sender, System::EventArgs^  e) {
std::ifstream fin("muster.txt");      // Eingabedatei
std::ofstream fout("test_out.txt");
std::vector< std::string > allItems;    // Dynamisches Array
std::string item;              // Einzelne Werte
static int RS232_state = 0;          // RS232-Status

if( fin.good() && (connect) ){                // Wenn die Datei geöffnet werden konnte
  fin.seekg(0L, ios::beg);        // an den Anfang der Datei springen
//while( getline( fin, item, ',')){      // Zeilenweise Auslesen, Werte einzel durch ',' getrennt
//    allItems.push_back(item);      // Zeile in Vector speichern
    switch(RS232_state){
      case sendENQ:
        ComWrite(COM4,ENQ);
richTextBoxMessage->AppendText("sendENQ\n");
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
        RS232_state = waitACK;
        break;
      case waitACK:
        if(ComRead(COM4) != -1){
          if( ComRead(COM4)== ACK ){
            RS232_state = sendSOH;
          }
          else{
            RS232_state = sendENQ;
          }
        }
richTextBoxMessage->AppendText("waitACK\n");
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
        break;
      case sendSOH:
        ComWrite(COM4,SOH);
        RS232_state = sendCONTR;
richTextBoxMessage->AppendText("sendSOH\n");
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
        break;
      case sendCONTR:
//        ComWrite(COM4,0xE0);
//        ComWrite(COM4,(atoi (allItems[0].c_str()) ) );      // String in Int umwandeln und senden
        RS232_state = sendAXIS;
richTextBoxMessage->AppendText("sendCONTR\n");
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
        break;
      case sendAXIS:
//        ComWrite(COM4,SOH);
        RS232_state = sendCHK;
richTextBoxMessage->AppendText("sendAXIS\n");
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
        break;
      case sendCHK:
//        ComWrite(COM4,SOH);
        RS232_state = waitACK_CHK;
richTextBoxMessage->AppendText("sendCHK\n");
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
        break;
      case waitACK_CHK:
        if( ComRead(COM4) != 0 ){
          if(ComRead(COM4) == ACK){
            RS232_state = sendEOT;
          }
          else{
            RS232_state = sendCONTR;
          }
        }
richTextBoxMessage->AppendText("waitACK_CHK\n");
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
        break;
      case sendEOT:
        ComWrite(COM4,EOT);
        RS232_state = sendENQ;
richTextBoxMessage->AppendText("sendEOT\n");
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
        break;
    }
//  }
}
fin.close();
fout.close();
     }

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:
int ComRead(unsigned Nr)
{
unsigned char   c;
DWORD      dwCount;


  if(Nr>=MAX_COM_PORTS)return -1;
  if(!bIsOpen[Nr])return -1;

  if(!ReadFile(hComFile[Nr],&c,1,&dwCount,0))return -1;
  if(dwCount!=1)return -1;


return c;
}

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

Autor: Arc Net (arc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Günstig wäre, das Protokoll etwas näher zu kennen.
Beispiel:
case waitACK:
        if(ComRead(COM4) != -1){
          if( ComRead(COM4)== ACK ){
            RS232_state = sendSOH;
          }
          else{
            RS232_state = sendENQ;
          }
        }

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"

Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht 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
if( ComReadCOM4) != -1 ){
  if( ComRead(COM4)== ACK ){
    RS232_state = sendSOH;
  }
  else{
    RS232_state = sendENQ;
  }
}
mit
if( ComGetReadCount(COM4) != 0 ){
  if( ComRead(COM4)== ACK ){
    RS232_state = sendSOH;
  }
  else{
    RS232_state = sendENQ;
  }
}
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...

Autor: Arc Net (arc)
Datum:

Bewertung
0 lesenswert
nicht 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
richTextBoxMessage->AppendText("sendAXIS\n");
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.win...

http://msdn.microsoft.com/en-us/library/system.win...

http://msdn.microsoft.com/en-us/library/system.io....

Beitrag "VB2008.NET - Serielle Com Port ansteuerung"

Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Arc Net (arc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Z.Z. sieht es so aus
private: System::Void timer1_Tick(System::Object^  sender, System::EventArgs^  e) {
    ...
    switch(RS232_state){
      case sendENQ:
        ComWrite(COM4,ENQ);
richTextBoxMessage->AppendText("sendENQ\n");
richTextBoxMessage->ScrollToCaret();          // Ans Ende scrollen
        RS232_state = waitACK;
        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
// delegate for Control.Invoke
delegate void RTBDelegate(void);
RTBDelegate^ RTBUpdateHandler;

void UpdateRTB() {
    // update RichTextBox here
}

Form1(void) {
    InitializeComponent();

    RTBUpdateHandler = gcnew RTBDelegate(this, &Form1::UpdateRTB);
}

private: System::Void timer1_Tick(System::Object^  sender, System::EventArgs^  e) {
    ...
    switch(RS232_state){
      case sendENQ:
        ComWrite(COM4,ENQ);
        // either direct update or invoke
        if (InvokeRequired) {
            Invoke(RTBUpdateHandler);
        } else {
            UpdateRTB();
        }
        RS232_state = waitACK;
        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.

Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht 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

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.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

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