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
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"
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
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...
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...
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"
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
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.
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.
|