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 (ohne A), denn sonst würde ja bei UNICODE ein falscher Parameter übergeben?!


  • Mod

    @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 (ohne A), denn sonst würde ja bei UNICODE 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 einfach ReadFile. 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 darauffolgendes FlushFileBuffers 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 als ATZ\r. Je weniger desto besser.


Anmelden zum Antworten