Forum: PC-Programmierung C++, Problem bei Auslesen von COM-Port


von Nik (Gast)


Angehängte Dateien:

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

von René K. (king)


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.

von Nik (Gast)


Angehängte Dateien:

Lesenswert?

Danke für deine schnelle Antwort!

Die Intitialisierung sollte nun vollständig sein (siehe Code im Anhang). 
Leider hat es das Problem nicht behoben.

Gibt es noch andere Fehlerquellen?

Gruß
Nik

von Hans-Georg L. (h-g-l)


Lesenswert?

char buffer[1024] = { 0 };
...
WriteFile(hCom, buffer, sizeof(buffer), &nBytesRead, NULL);
...
ReadFile(hCom, buffer, sizeof(buffer), &nBytesRead, NULL);
...

Drei mal darfst raten was da passiert ...

von René K. (king)


Lesenswert?

Zusätzlich zu dcb.Parity musst Du noch dcb.fParity auf FALSE setzen.

Übrigens, hat jetzt mit dem Problem nichs zu tun, aber schreibe mal 
lieber TEXT("\\\\.\\COM2") anstelle von TEXT("COM2"), und zwar darum:
https://support.microsoft.com/en-us/kb/115831

von Hans-Georg L. (h-g-l)


Lesenswert?

Hans-Georg L. schrieb:
> char buffer[1024] = { 0 };
> ...
> WriteFile(hCom, buffer, sizeof(buffer), &nBytesRead, NULL);
> ...
> ReadFile(hCom, buffer, sizeof(buffer), &nBytesRead, NULL);
> ...
>
> Drei mal darfst raten was da passiert ...

Schickst du auch genug Zeichen ?

von Nik (Gast)


Angehängte Dateien:

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.

von Nik (Gast)


Lesenswert?

Danke für die Tipps. Er sträubt sich leider immernoch :\.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Nik schrieb:
> Der Arduino sendet ca. viermal pro Sekunde eine 42 raus.

Und wie lange hast Du der Angelegenheit schon zugesehen? Das dürfte 250 
Sekunden dauern, bis die von ReadFile() angeforderten 1000 Bytes 
zusammenkommen.

von René K. (king)


Lesenswert?

Bei 4 Bytes in der Sekunde dauert es 255 Sekunden, bis ReadFile 
zurückkehrt. Zuzüglich der einen Sekunde beim Sleep davor brauchst Du in 
Summe 256 Sekunden bis was passiert. Wartest Du das auch ab?

von Nik (Gast)


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?

von Hans-Georg L. (h-g-l)


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.

: Bearbeitet durch User
von René K. (king)


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

von Nik (Gast)


Angehängte Dateien:

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.

von Peter II (Gast)


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.

von Nik (Gast)


Lesenswert?

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

von Peter II (Gast)


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.

von Nik (Gast)


Lesenswert?

Okay, danke für den Tipp. Das hilft in der Tat :)

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.