C++, Problem bei Auslesen von COM-Port

Gast #4610670
Lesenswert?

Hallo zusammen,

ich habe leider ein kleines Problem beim Auslesen eines virtuellen 
Com-Ports.

Folgende Sachlage: Ich habe von Microsoft ein kleines Codebeispiel 
genommen und es entsprechend meinen Anforderungen zum Testen angepasst 
(siehe Code im anhang):

https://msdn.microsoft.com/de-de/library/windows/desktop/aa363201(v=vs.85).aspx

Wenn ich beispielsweise meinen Arduino Nano an den PC anschließe und das 
Programm starte, bleibt er in der ReadFile Funktion in Zeile 27 hängen 
und liest nichts aus.

Wenn ich jetzt mit einem Terminalprogramm (zB HTerm) einmal mit dem COM 
verbinde und die Verbindung wieder trenne, funktioniert mein Code 
hingegen einwandfrei. Also: Damit der Code funktioniert, muss ich zuerst 
mit einem anderen Programm einmal auf den COM zugreifen. Danach klappt 
alles ohne Probleme, bis ich a) den Rechner neustarte oder b) den Nano 
aus- und wieder einstecke.

Das Ganze ist reproduzierbar und tritt bei jedem beliebigen Com-Port auf 
mehreren Rechnern so auf.

Hat jemand von euch eine Idee, was im angehängten Code noch fehlt?


Noch eine kleine Anmerkung: Getestet wurde auf W7, 64 Bit.

Gruß
Nik
Angehängte Dateien:
#4610689
Lesenswert?

Über den DCB stellst Du sehr viel mehr ein als Baudrate/Parity/Stop, 
z.B. Flusskontrolle. Wenn Du die vorherige Einstellung einfach so 
übernimmst (GetCommState), bekommst Du eben auch das was vorher war.

Initialisiere Deinen DCB vollständig, dann funktioniert es.
Gast #4610739
Lesenswert?

Es werden zunächst einmal 1024 Byte gesendet, damit ich an der Arudino 
Nano Rx-LED sehe, ob was ankommt (und ja, das tut es). Dass da keine 
Nutzdaten, sondern nur die Start- und Stopbits übertragen werden, ist 
nicht schlimm, da der Arduino eh nichts mit den Daten macht.

Danach lese ich zyklisch den Buffer aus.

Der Arduino sendet ca. viermal pro Sekunde eine 42 raus.

Das Ergebnis ist auf dem Bild im Anhang zu sehen.

Wie gesagt, der Code ist erstmal nur zu Testzwecken, bis es mal so läuft 
wie es soll.
Angehängte Dateien:
Gast #4610861
Lesenswert?

Okay, da lag wohl das Problem.

Also die Read Funktion scheint ohne vorherige Verbindung eines anderen 
Programms mit dem COM-Port erst zurückzukehren, wenn das komplette 
Bufferarray voll ist, so wie ich das gerade sehe. Je nach Größe kann das 
dann entsprechend lang dauern.

Sobald HTerm einmal lief und ich meinen Code dann wieder ausführe, kehrt 
die Readfunktion direkt zurück und schreibt mir in nBytesRead die Anzahl 
der neuen Bytes, welche ich dann aus dem Buffer auslesen kann.

Lässt sich dieses Verhalten irgendwie erklären?
#4610864
Lesenswert?

Die COM Schnittstele benutzt auch noch die Struktur COMMTIMEOUTS.

Darin ist festgelegt wie lange Pause zwischen den Zeichen sein darf, 
kann also sein das du immer in einen Timeout läufst.

lass dir mal das Ergebnis von ReadFile ausgeben ...
1
  bool result;
2

3
  result = ReadFile( .......);
4
  if ( ! result )
5
  {
6
      // error handling
7
      LPTSTR lpMsgBuf;
8

9
      FormatMessage( 
10
       FORMAT_MESSAGE_ALLOCATE_BUFFER | 
11
       FORMAT_MESSAGE_FROM_SYSTEM | 
12
       FORMAT_MESSAGE_IGNORE_INSERTS,
13
       NULL,
14
       GetLastError(),
15
       0, // Default language
16
       (LPTSTR) &lpMsgBuf,
17
       0,
18
       NULL 
19
     );
20

21
     LogMsg(_T("Port Read Error: %s"),lpMsgBuf);
22
     LocalFree( lpMsgBuf );  
23

24
  }

LogMsg ist eine Funktion von Mir das musst du anpassen.
#4610885
Lesenswert?

Die angesprochenen COMMTIMEOUTS werden das Phänomen erklären, dass Dir 
HTerm Dein Programm "repariert". Wenn Du sowieso im Sekundentakt pollen 
willst, kannst Du auch vor derm ReadFile-Aufruf einfach mal 
ClearCommError aufrufen:

https://msdn.microsoft.com/en-us/library/windows/desktop/aa363180(v=vs.85).aspx

In der von der Funktion ausgefüllten COMSTAT Struktur kannst Du dann 
sehen, wie viele Bytes es gerade zu lesen gibt (cbInQue). ReadFile wird 
dann halt nur noch aufgerufen, wenn cbInQue > 0 ist und nicht mehr nur 
mit sizeof(buffer), sondern mit min(stat.cbInQue, sizeof(buffer)).
Gast #4610911
Lesenswert?

VIELEN DANK! Das konnte das Problem nun beheben. Habe den Code jetzt mit 
Hilfe der ClearCommError Funktion zum laufen bekommen.

So wird jetzt immer geprüft, wie viele neue Bytes empfangen wurden und 
dann die Anzahl, sollte sie größer 0 sein, als maximale Byteanzahl der 
Read-Funktion übergeben.

Jetzt ist es nur noch verbesserungswürdig, dass der gesamte Codeablauf 
durch die Sleep Funktion gebremst wird. Wenn parallel noch was empfangen 
werden soll, wird das unschön. Ohne Sleep verbraucht das Programm 
allerdings 25% meiner CPU-Ressourcen. Gibts da noch Tricks, das 
anzupassen bzw. ein Stichwort, nach was man da mal schauen könnte? Habe 
leider bisher nur etwas Programmiererfahrung in C.
Angehängte Dateien:
Gast #4610922
Lesenswert?

Nik schrieb:
> Jetzt ist es nur noch verbesserungswürdig, dass der gesamte Codeablauf
> durch die Sleep Funktion gebremst wird.

vergiss den Quatsch mit dem ClearCommError.

Wenn du nicht weißt wie viele Byte zum Lesen da sind, dann ist das 
einfachste immer 1 Byte zu lesen. Und schon funktioniert alles.
Gast #4610932
Lesenswert?

Nik schrieb:
> Über ClearCommError bekomme ich in cbInQue die Anzahl der zur Verfügung
> stehenden Bytes geschrieben. Funktioniert eigentlich bestens.

ja, nur das es sinnlos CPU-Zeit kostet. weil viel zu oft  keine Daten da 
sind.

Bei deinem Code sehe ich keinen Nachteil immer ein Byte zu lesen. dann 
geht die CPU auf 0% und der kleine Overhead wegen dem mehrfach lesen 
speilt bei COM keine Rolle.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren