Serial port bedienen
-
@Knackwurst sagte in Serial port bedienen:
-TODO: Wie erkenne ich das alles gesendet ist bzw. das es nicht funktioniert weil das Endgerät z.B. nichts einliest?
Das geht nur beim Hardware-Handshake, wenn die Funktion in den Timeout läuft.
Dann sollte BytesWritten nicht mit strlen(cmd) übereinstimmen.Ansonsten werden die Daten einfach raus geschrieben und du musst auf Antwort hoffen.
Cool wäre natürlich, wenn das Gerät SCPI kann.
-
@Martin-Richter sagte in Serial port bedienen:
hComm = CreateFileA(_T("\\\\.\\COM4"), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0)
Du meinst wohl
CreateFile
(ohneA
), denn sonst würde ja beiUNICODE
ein falscher Parameter übergeben?!
-
@Th69 sagte in Serial port bedienen:
@Martin-Richter sagte in Serial port bedienen:
hComm = CreateFileA(_T("\\\\.\\COM4"), GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0)
Du meinst wohl
CreateFile
(ohneA
), denn sonst würde ja beiUNICODE
ein falscher Parameter übergeben?!Du hast natürlich vollkommen recht. Typischer Copy & Paste Fehler. Korrigiert!
-
@Martin-Richter sagte in Serial port bedienen:
Anmerkung: Dir fehlen gravierende Basics, was die WinAPI Entwicklung betrifft.
Das hast Du leider recht, aber irgendwo muss man ja mit irgendwas anfangen zu lernen...
-
@Martin-Richter sagte in Serial port bedienen:
zu 5.
Wäre auch die Frage auf overlapped i/o umzustellen. Dann hast Du mehr Möglichkeiten mit Timeouts, oder teilweise gelesenen Daten.
Ansonsten (non-overlapped) kommt ReadFile nicht zurück bis eben ein Fehler auftritt oder eben alle Daten, die Du wolltest da sind.Ich dachte die Entscheidung Overlapped (Asynchron) oder Nonoverlapped (Synchron) würde nur beim öffnen des File-Handles gemacht und nicht bei einzelnen Funktionen? Heißt das ich muss das bei jeder IO-Funktion angeben?
Wenn ich beim CreateFile kein Flag angebe gilt ja Nonoverlapped IO, d.H. ich müsste dort beim 6. Parameter FILE_FLAG_OVERLAPPED angeben, richtig? Das heißt doch das ich im Synchronen Modus arbeite.
-
Hier mal mein aktueller Code (müsste Nonoverlapped IO sein):
HKEY hk; long h; // Enumerate COM ports h = RegOpenKeyExA(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, KEY_QUERY_VALUE, &hk); if (h == ERROR_SUCCESS) { DWORD cSubKeys; DWORD cValues; cSubKeys = 0; cValues = 0; RegQueryInfoKeyA(hk, NULL, NULL, NULL, &cSubKeys, NULL, NULL, &cValues, NULL, NULL, NULL, NULL); if (cValues == 0) { printf("DEBUG: No devices found\n"); break; } printf("DEBUG: Number of devices found: %ld\n", cValues); // Iterate through all COM ports found long rc; LPTSTR lpValueName = new TCHAR[50]; DWORD lpcchValueName = 50; DWORD dwIndex = 0; LPBYTE lpData = new BYTE[50]; DWORD lpcbData = 50; while ( (rc = RegEnumValueA(hk, dwIndex, lpValueName, &lpcchValueName, NULL, NULL, lpData, &lpcbData)) == ERROR_SUCCESS) { //printf("DEBUG: item #%d: %s (len %d) %s (len %d)\n", dwIndex, lpData, lpcbData, lpValueName, lpcchValueName); printf("DEBUG: Found device %s\n", lpData); dwIndex++; lpcchValueName = 50; lpcbData = 50; // open port HANDLE hComm; LPSTR gszPort = new TCHAR(50); sprintf(gszPort, "\\\\.\\%s", lpData); // create valid "filename" from COM port name hComm = CreateFileA( gszPort, // friendly name of port (cast to LPBYTE) GENERIC_READ | GENERIC_WRITE, // Read/Write Access 0, // No Sharing, ports cant be shared 0, // No Security OPEN_EXISTING, // Open existing port only //0, NULL, // Non Overlapped I/O 0 // Null for Comm Devices ); if (hComm == INVALID_HANDLE_VALUE) { printf("ERROR opening port %s\n", gszPort); continue; } //handle ERROR_FILE_NOT_FOUND? //Setting the Parameters for the SerialPort DCB dcbSerialParams = { 0 }; // Initializing DCB structure dcbSerialParams.DCBlength = sizeof(dcbSerialParams); //retreives the current settings if ( ! GetCommState(hComm, &dcbSerialParams)) { printf("ERROR getting the COM state\n"); CloseHandle(hComm);//Closing the Serial Port continue; } dcbSerialParams.BaudRate = CBR_38400; //BaudRate = 38400 dcbSerialParams.ByteSize = 8; //ByteSize = 8 dcbSerialParams.StopBits = ONESTOPBIT; //StopBits = 1 dcbSerialParams.Parity = NOPARITY; //Parity = None if ( ! SetCommState(hComm, &dcbSerialParams)) { printf("ERROR setting serial params\n"); CloseHandle(hComm);//Closing the Serial Port continue; } //Setting Timeouts COMMTIMEOUTS timeouts = { 0 }; //Initializing timeouts structure timeouts.ReadIntervalTimeout = 5; timeouts.ReadTotalTimeoutConstant = 5; timeouts.ReadTotalTimeoutMultiplier = 1; timeouts.WriteTotalTimeoutConstant = 5; timeouts.WriteTotalTimeoutMultiplier = 1; if (SetCommTimeouts(hComm, &timeouts) == FALSE) { printf("ERROR setting timeouts\n"); CloseHandle(hComm);//Closing the Serial Port continue; } //Writing data to Serial Port char cmd[] = "ATZ\r\n"; DWORD BytesWritten = 0; // No of bytes written to the port if ( ! WriteFile(hComm,// Handle to the Serialport cmd, // Data to be written to the port strlen(cmd), // sizeof(SerialBuffer), // No of bytes to write into the port &BytesWritten, // No of bytes written to the port NULL)) { int err = GetLastError(); printf("ERROR write %zu bytes to %s failed (error code is %d)\n", strlen(cmd), gszPort, err); CloseHandle(hComm);//Closing the Serial Port continue; } if (BytesWritten != strlen(cmd)) { printf("ERROR Only %ld bytes of %zu written to %s\n", BytesWritten, strlen(cmd), gszPort); CloseHandle(hComm);//Closing the Serial Port continue; } printf("DEBUG: Command '%s' (%ld bytes) written\n", cmd, BytesWritten); //Setting Receive Mask if ( ! SetCommMask(hComm, EV_RXCHAR)) { printf("ERROR Setting CommMask\n"); CloseHandle(hComm);//Closing the Serial Port continue; } // Wait for answer DWORD dwEventMask; // Event mask to trigger if ( ! WaitCommEvent(hComm, &dwEventMask, NULL)) { printf("ERROR in WaitCommEvent()\n"); CloseHandle(hComm);//Closing the Serial Port continue; } // read answer from device int loop = 0; DWORD NoBytesRead; char ReadData; char SerialBuffer[64] = { 0 }; printf("DEBUG read bytes from device...\n"); do { if ( ! ReadFile(hComm, &ReadData, sizeof(ReadData), &NoBytesRead, NULL)) { printf("ERROR in ReadFile()\n"); CloseHandle(hComm);//Closing the Serial Port break; } SerialBuffer[loop] = ReadData; ++loop; } while (NoBytesRead > 0); loop--; printf("Read from device: "); int index = 0; for (index = 0; index < loop; ++index) { printf("%c", SerialBuffer[index]); } printf("\n"); // close port printf("DEBUG: close port %s\n", gszPort); CloseHandle(hComm);//Closing the Serial Port } // check if loop ends because of an error if (rc != ERROR_NO_MORE_ITEMS) { if (rc == ERROR_MORE_DATA) { printf("ERROR - Buffer for 'lpData' or 'lpValueName' too small\n"); } else { printf("ERROR - Unknown error %ld\n", rc); } break; } } else { printf("ERROR - Can't open registry\n"); }
Leider bleibt er nach dem senden des ATZ zum ersten (falschen) COM stehen, kein Timeout, kein Fehler, einfach eingefroren.
-
@Knackwurst
Lösch den Teil raus://Setting Receive Mask if ( ! SetCommMask(hComm, EV_RXCHAR)) { printf("ERROR Setting CommMask\n"); CloseHandle(hComm);//Closing the Serial Port continue; } // Wait for answer DWORD dwEventMask; // Event mask to trigger if ( ! WaitCommEvent(hComm, &dwEventMask, NULL)) { printf("ERROR in WaitCommEvent()\n"); CloseHandle(hComm);//Closing the Serial Port continue; }
Und mach statt dessen das rein:
FlushFileBuffers(hComm);
WaitCommEvent
bricht nicht nach einem Timeout ab. Das wartet ggf. ewig.Und du brauchst es nicht. Stell das Timeout das du willst mit
SetCommTimeouts
ein, und dann verwende einfachReadFile
. Das bricht dann nämlich nach dem eingestellten Timeout ab.-TODO: Wie erkenne ich das alles gesendet ist bzw. das es nicht funktioniert weil das Endgerät z.B. nichts einliest?
Erkennen ob etwas gesendet wurde kann man halbwegs zuverlässig. Ich sag' mal wenn
WriteFile
und ein darauffolgendesFlushFileBuffers
beide keinen Fehler gemeldet haben, dann kannst du mit halbwegs guter Sicherheit davon ausgehen dass die Daten gesendet wurden.Ob es von der Gegenstelle aber auch empfangen wurde ist ne ganz andere Frage. Das kannst du bei seriellen Schnittstellen im Allgemeinen nicht erkennen. Im Speziellen u.U. schon, nämlich z.B. dadurch dass das Gerät antwortet.
Wobei ich allgemein hinterfragen würde ob es gut ist einfach an unbekannte Geräte ein
ATZ\r
zu schicken. Musst du wirklich alle Ports scannen? Wäre es nicht möglich statt dessen den Benutzer die Port-Nummer eingeben zu lassen?
-
@hustbaer sagte in Serial port bedienen:
Und mach statt dessen das rein:
FlushFileBuffers(hComm);
WaitCommEvent
bricht nicht nach einem Timeout ab. Das wartet ggf. ewig.JA, das funktioniert!
-
@hustbaer sagte in Serial port bedienen:
Wobei ich allgemein hinterfragen würde ob es gut ist einfach an unbekannte Geräte ein ATZ\r zu schicken. Musst du wirklich alle Ports scannen? Wäre es nicht möglich statt dessen den Benutzer die Port-Nummer eingeben zu lassen?
Ich hatte mir das so eingebildet, weil ich es besonders komfortabel haben wollte. Leider antwortet das Gerät welches ich identifizieren will nicht von selbst beim öffnen der Verbindung sondern erwartet aktiv ein Kommando. Anstelle ATZ könnte man auch was anderes senden was evtl. nicht so invasiv wäre? Ein AT oder ATV.
Das Ziel ist einen gültigen Adapter zu erkennen und es könnte theoretisch mehr als einer dran sein. Und was will man sonst anzeigen? In der Reg hat man ja nur den COM und den Devicepfad. Ggf. könnte man noch die Treibereigenschaften ermitteln, aber über das Device sagt das alles nichts aus. Eine Liste würde also auch nur "COM5, COM8, COM12" anzeigen und der User würde sich testweise durchklickern, was dann ja zum gleichen Ergebnis führt nicht wahr?
So schicke ich ein ATZ und erhalte im korrekten Fall ein "ELM327 v%d.%d" zurück auf das ich teste.
-
IIRC gibt es manche serielle Geräte die sich von sich aus melden wenn man bestimmte Signale über die Handshake-Leitungen sendet. Das wäre weniger invasiv. Ich weiss bloss leider nicht mehr wie das genau geht. Wenn's dich interessiert kannst du ja mal danach googeln - vielleicht findest du irgendwo ne Beschreibung.
Und ich weiss auch nicht ob Modems das unterstützen. Ich weiss dass serielle Mäuse es unterstützen und dass Windows diese Detection verwendet (bzw. bis zumindest Windows 7 verwendet hat). Weiss ich deswegen, weil wir das in meiner alten Firma extra im .inf File vom Treiber deaktivieren mussten damit Windows eben nicht die Handshake-Leitungen jedes mal beim Booten ansteuert. (Wir hatten die Handshake-Leitungen als digitale Ein- und Ausgänge misbraucht, und da konnten wir das Rumspielen von Windows gar nicht brauchen.)
ps:
Und ja,AT\r
ist natürlich weniger invasiv alsATZ\r
. Je weniger desto besser.