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
Ü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.
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
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
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.
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.
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?
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?
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
boolresult;
2
3
result=ReadFile(.......);
4
if(!result)
5
{
6
// error handling
7
LPTSTRlpMsgBuf;
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.
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)).
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.
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.
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.