# c++ / serielle Schnittstelle / WinXP / not overlapped mode



## Morkai (7. April 2007)

Hallo,
ich bin neu hier bei Euch im Forum und habe auch gleich eine Frage. Als Erstes möchte ich Euch aber für die schöne Seite, die interessanten Tutorials und das informative Forum gratulieren.
Nun zu meiner Frage. Wie aus dem Titel schon zu erkennen beschäftige ich mich mit der Kommunikation über die serielle Schnittstelle (WinXP). Durch regen gebrauch der Suchfunktionen im Netz und diesem Forum bin ich soweit gekommen, dass ich Daten senden und prinzipiell auch empfangen kann (im not overlapped Mode). Beim Empfangen hakt es aber nach wie vor ein wenig.

Zu den Details:
1.	als Erstes wird ein Handle mit CreateFile erzeugt.
2.	dann wird die Struktur DCB mit entsprechenden Werten initialisiert
3.	WriteFile und ReadFile funktionen werden ausgeführt
Auf der Gegenseite wird Hyperterminal verwendet um das Senden/ Empfangen zu testen.

Das eigentliche Programm läuft in einer Endlosschleife. Wie oben erwähnt funktioniert das Senden tadellos. Beim Empfangen werden alle Zeichen korrekt empfangen, der unerwünschte Effekt ist, so lange kein neues Zeichen von Hyperterminal aus gesendet wird, „empfängt“ mein Programm immer das zuletzt empfangene Zeichen. (ReadFile liefert mit jedem Aufruf das zuletzt empfangene Zeichen.) D.h. also die Daten im Empfangsspeicher, auf den ich über den Handle ja keinen Zugriff habe außer mit ReadFile, werden durch ein auslesen nicht gelöscht.
Meine Frage ist, gibt es eine Möglichkeit eine Abfrage zu implementieren, um die Empfangsspeicher nur dann auszulesen, wenn neue Daten empfangen wurden. (Z.B. vergleichbar dem Zugriff auf Interrupts oder den UART direkt, was unter DOS ja leicht möglich war.)

Eine zweite Frage am Rande. Ich bin bei meiner Such u.a. auch über die Struktur COMMPROP gestolpert. Was sind die Unterschiede zur DCB Struktur bzw. genauer gefragt, wann sollte man welche Struktur verwenden?

Vielen Dank für Eure Hilfe.

Gruß
Morkai


----------



## Endurion (8. April 2007)

Zeig mal deinen Code. Meiner Erfahrung nach werden die Daten mit ReadFile einwandfrei gelöscht. D.h. einmal ausgelesen sind die Daten weg.


----------



## Morkai (8. April 2007)

Hallo,
hier der Code. Ich habe letztendlich vor, das Ganze als Klasse umzusetzten. Ich denke, durch die beschränkte Funktionalität ist es aber leicht zu verstehen.

Als erstes poste ich die Klasse und den dazugehörigen Quelltext und als letztes die Main-Funktion, welche zu Testzwecken erstellt wurde. Die relevanten Passagen habe ich Fett markiert!

_class SerComClass
{
	private:
	HANDLE hComPort;
	DCB dcb;
	COMMTIMEOUTS commtimeouts;


// Definition der private methods

	bool SCC_GetHandle(char *);
	bool SCC_GetDCB();
	bool SCC_SetDCB();
	bool SCC_SetCommTimeOuts();


	public:

// Definition der public methods

	// Konstruktor und Destructor
	SerComClass(void);
	SerComClass(char *);
	~SerComClass();

	// Ändern der Portkonfiguration
	bool SCC_SetBaudRate(unsigned long);
	bool SCC_SetByteSize(BYTE);
	bool SCC_SetParity(BYTE);
	bool SCC_SetStopBit(BYTE);
	bool SCC_SetHandshake(bool);					//TRUE = Handshake ON; FALSE = Handshake OFF

	// Ändern der Timeout-Settings
	bool SCC_SetReadIntervalTimeout(DWORD);
	bool SCC_SetReadTotalTimeoutMultiplier(DWORD);
	bool SCC_SetReadTotalTimeoutConstant(DWORD);
	bool SCC_SetWriteTotalTimeoutMultiplier(DWORD);
	bool SCC_SetWriteTotalTimeoutConstant(DWORD);

	// Sende und Empfangsfunktionen
	bool SCC_WriteByte(char *, DWORD);
	bool SCC_ReadByte(char *, DWORD);
};_

_*SerComClass::SerComClass(void)
{
	memset(&dcb,0,sizeof(dcb));
	SCC_GetHandle("COM1:");
	SCC_SetDCB();
	SCC_SetCommTimeOuts();
}*
SerComClass::SerComClass(char *ptr_c_Port)
{
	memset(&dcb,0,sizeof(dcb));
	SCC_GetHandle(ptr_c_Port);
}

*SerComClass::~SerComClass()
{
	CloseHandle(hComPort);
}*
*bool SerComClass::SCC_GetHandle(char *ptr_c_Port)
{
	hComPort = CreateFile(ptr_c_Port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, NULL);

	if (hComPort == INVALID_HANDLE_VALUE)
	{
		return(0);								//im Fehlerfall wird 0 zurückgeliefert
	}
	return(1);									//1 wird zurückgeliefert wenn alles i.O.
}*
bool SerComClass::SCC_GetDCB()
{
	return((bool)GetCommState(hComPort,&dcb));
}

*bool SerComClass::SCC_SetDCB()
{
	dcb.DCBlength = sizeof(DCB);				// sizeof(DCB) 
    dcb.BaudRate = CBR_9600;					// current baud rate 
    dcb.fBinary = TRUE;							// binary mode, no EOF check 
    dcb.fParity = FALSE;							// enable parity checking 
    dcb.fOutxCtsFlow = FALSE;					// CTS output flow control 
    dcb.fOutxDsrFlow = FALSE;					// DSR output flow control 
    dcb.fDtrControl = DTR_CONTROL_ENABLE;		// DTR flow control type 
    dcb.fDsrSensitivity = FALSE;				// DSR sensitivity 
    dcb.fTXContinueOnXoff = FALSE;				// XOFF continues Tx 
    dcb.fOutX = FALSE;							// XON/XOFF out flow control 
    dcb.fInX = FALSE;							// XON/XOFF in flow control 
    dcb.fErrorChar = FALSE;						// enable error replacement 
    dcb.fNull = FALSE;							// enable null stripping 
    dcb.fRtsControl = RTS_CONTROL_ENABLE;		// RTS flow control 
    dcb.fAbortOnError = FALSE;					// abort reads/writes on error 
//    dcb.fDummy2 = fDummy2;					// reserved 
//    dcb.wReserved = wReserved;				// not currently used 
    dcb.XonLim = 0;								// transmit XON threshold 
    dcb.XoffLim = 0;							// transmit XOFF threshold 
    dcb.ByteSize = 8;							// number of bits/byte, 4-8 
    dcb.Parity = NOPARITY;						// 0-4=no,odd,even,mark,space 
    dcb.StopBits = ONESTOPBIT;					// 0,1,2 = 1, 1.5, 2 
    dcb.XonChar = 0;							// Tx and Rx XON character 
    dcb.XoffChar = 0;							// Tx and Rx XOFF character 
    dcb.ErrorChar = 0;							// error replacement character 
    dcb.EofChar = 0;							// end of input character 
    dcb.EvtChar = 0;							// received event character 
//    dcb.wReserved1 = wReserved1;				// reserved; do not use 

	if(!SetCommState(hComPort, &dcb))
	{
		return(0);								//im Fehlerfall wird 0 zurückgeliefert
	}
	return(1);									//1 wird zurückgeliefert wenn alles i.O.
}*
*bool SerComClass::SCC_SetCommTimeOuts()
{
	commtimeouts.ReadIntervalTimeout = MAXDWORD;
	commtimeouts.ReadTotalTimeoutConstant = 0;
	commtimeouts.ReadTotalTimeoutMultiplier = 0;
	commtimeouts.WriteTotalTimeoutConstant = 0;
	commtimeouts.WriteTotalTimeoutMultiplier = 0;

	if(!SetCommTimeouts(hComPort,&commtimeouts))
	{
		return(0);
	}
	return(1);
}*
bool SerComClass::SCC_SetBaudRate(unsigned long ulBaudRate)
{
	switch(ulBaudRate)
	{
		case 110:
			dcb.BaudRate = CBR_110;
			break;
		case 300:
			dcb.BaudRate = CBR_300;
			break;
		case 600:
			dcb.BaudRate = CBR_600;
			break;
		case 1200:
			dcb.BaudRate = CBR_1200;
			break;
		case 2400:
			dcb.BaudRate = CBR_2400;
			break;
		case 4800:
			dcb.BaudRate = CBR_4800;
			break;
		case 9600:
			dcb.BaudRate = CBR_9600;
			break;
		case 14400:
			dcb.BaudRate = CBR_14400;
			break;
		case 19200:
			dcb.BaudRate = CBR_19200;
			break;
		case 38400:
			dcb.BaudRate = CBR_38400;
			break;
		case 56000:
			dcb.BaudRate = CBR_56000;
			break;
		case 57600:
			dcb.BaudRate = CBR_57600;
			break;
		case 115200:
			dcb.BaudRate = CBR_115200;
			break;
		case 128000:
			dcb.BaudRate = CBR_128000;
			break;
		case 256000:
			dcb.BaudRate = CBR_256000;
			break;
		default:
			return(0);							//wenn die Eingaben nicht den oberen Angaben entspricht
	}

	if(!SetCommState(hComPort, &dcb))
	{
		return(0);								//im Fehlerfall wird 0 zurückgeliefert
	}
	return(1);									//1 wird zurückgeliefert wenn alles i.O.
}

bool SerComClass::SCC_SetByteSize(BYTE bybytelength)
{
	if(bybytelength>4 && bybytelength<9)		//mögliche Eingaben 5, 6, 7 oder 8
	{
		dcb.ByteSize = bybytelength;
	}

	if(!SetCommState(hComPort, &dcb))
	{
		return(0);								//im Fehlerfall wird 0 zurückgeliefert
	}
	return(1);									//1 wird zurückgeliefert wenn alles i.O.
}

bool SerComClass::SCC_SetParity(BYTE byParity)
{
	switch(byParity)
	{
		case 0:
			dcb.Parity = NOPARITY;
			dcb.fParity = FALSE;
			break;
		case 1:
			dcb.Parity = ODDPARITY;
			dcb.fParity = TRUE;
			break;
		case 2:
			dcb.Parity = EVENPARITY;
			dcb.fParity = TRUE;
			break;
		case 3:
			dcb.Parity = MARKPARITY;
			dcb.fParity = TRUE;
			break;
		case 4:
			dcb.Parity = SPACEPARITY;
			dcb.fParity = TRUE;
			break;
		default:
			return(0);							//wenn die Eingabe nicht im Bereich 0-4 liegt
	}

	if(!SetCommState(hComPort, &dcb))
	{
		return(0);								//im Fehlerfall wird 0 zurückgeliefert
	}
	return(1);		
}

bool SerComClass::SCC_SetStopBit(BYTE byStopBit)
{
	switch(byStopBit)
	{
		case 0:
			dcb.StopBits = ONESTOPBIT;
			break;
		case 1:
			dcb.StopBits = ONE5STOPBITS;
			break;
		case 2:
			dcb.StopBits = TWOSTOPBITS;
			break;
		default:
			return(0);							//wenn die Eingabe nicht im Bereich 0-2 liegt
	}

	if(!SetCommState(hComPort, &dcb))
	{
		return(0);								//im Fehlerfall wird 0 zurückgeliefert
	}
	return(1);		
}

bool SerComClass::SCC_SetHandshake(bool bHandshake)
{
	if(bHandshake == TRUE)						//Handshake einschalten
	{
		dcb.fOutxCtsFlow = bHandshake;
		dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
	}else										//Handshake ausschalten
	{
		dcb.fOutxCtsFlow = bHandshake;
		dcb.fRtsControl = RTS_CONTROL_DISABLE;
	}

	if(!SetCommState(hComPort, &dcb))
	{
		return(0);								//im Fehlerfall wird 0 zurückgeliefert
	}
	return(1);		
}

bool SerComClass::SCC_SetReadIntervalTimeout(DWORD dwReadIntervalTimeout)
{
	commtimeouts.ReadIntervalTimeout = dwReadIntervalTimeout;

	if(!SetCommTimeouts(hComPort,&commtimeouts))
	{
		return(0);
	}
	return(1);
}

bool SerComClass::SCC_SetReadTotalTimeoutMultiplier(DWORD dwReadTotalTimeoutMultiplier)
{
	commtimeouts.ReadTotalTimeoutMultiplier = dwReadTotalTimeoutMultiplier;

	if(!SetCommTimeouts(hComPort,&commtimeouts))
	{
		return(0);
	}
	return(1);
}

bool SerComClass::SCC_SetReadTotalTimeoutConstant(DWORD dwReadTotalTimeoutConstant)
{
	commtimeouts.ReadTotalTimeoutConstant = dwReadTotalTimeoutConstant;

	if(!SetCommTimeouts(hComPort,&commtimeouts))
	{
		return(0);
	}
	return(1);
}

bool SerComClass::SCC_SetWriteTotalTimeoutMultiplier(DWORD dwWriteTotalTimeoutMultiplier)
{
	commtimeouts.WriteTotalTimeoutMultiplier = dwWriteTotalTimeoutMultiplier;

	if(!SetCommTimeouts(hComPort,&commtimeouts))
	{
		return(0);
	}
	return(1);
}

bool SerComClass::SCC_SetWriteTotalTimeoutConstant(DWORD dwWriteTotalTimeoutConstant)
{
	commtimeouts.WriteTotalTimeoutConstant = dwWriteTotalTimeoutConstant;

	if(!SetCommTimeouts(hComPort,&commtimeouts))
	{
		return(0);
	}
	return(1);
}

*bool SerComClass::SCC_WriteByte(char *ptrcWriteData, DWORD dwBytesToWrite)
{
	DWORD wBytesWritten = 0;

	if(!WriteFile(hComPort,ptrcWriteData,dwBytesToWrite,&wBytesWritten,NULL))
	{
		return(0);
	}
	return(1);
}*

*bool SerComClass::SCC_ReadByte(char *ptrcReceiveData, DWORD dwBytesToRead)
{
	DWORD dwBytesRead = 0;
	char *cData;

	if(!ReadFile(hComPort,ptrcReceiveData,dwBytesToRead,&dwBytesRead,NULL))
	{
		return(0);
	}
	return(1);
}*_

_*void main(void)
{
	SerComClass clComPort;
	char cZeichen = '0';
	bool error;

	clComPort.SCC_WriteByte("Kommunikation ist initialisiert", sizeof("Kommunikation ist initialisiert\n"));

	while(!kbhit())
	{
		error = clComPort.SCC_ReadByte(&cZeichen,1);
		if(cZeichen>96 && cZeichen<123)
		{
			cout<<cZeichen;
		}
		if(!error)
		{
			cout<<"Empfangsfehler"<<endl;
		}
	}
	cout<<endl<<"ENDE";

}*_

Gruß und vielen Dank für das Interesse!
Morkai


----------



## Endurion (8. April 2007)

Du solltest bei ReadFile den Wert in dwBytesRead prüfen. Gerade bei der seriellen Schnittstelle wird das oft von der max. zu lesenden Anzahl abweichen.


----------



## Morkai (8. April 2007)

Hi,
dwBytesRead is bei/ nach jedem Aufruf 1. Das habe ich schon geprüft. Trotzdem Danke! Fällt Dir vielleicht noch etwas anderes ein? Was ist denn mit WaitCommEvent?

Gruß
Morkai


----------



## Endurion (9. April 2007)

Die WaitCommEvent-Funktion ist nur eine Unterstützung, wenn man mit Events arbeiten möchte. Da kannst du Windows mitteilen, dass es bei einer Änderung am COM-Port (Daten eingetroffen oder ähnliches) ein Event setzt. Ist praktisch für einen CPU-sparenden Ablauf. Das ändert dann aber nicht wirklich etwas am ReadFile-Verhalten.

Ach ich Blindfisch: Du setzt BytesToRead auf 0. D.h. du sagst Windows, lies mir bitte 0 Bytes von der seriellen Schnittstelle. Das tut es dann ja auch 

Setz den mindestens auf 1.

Kleiner Haken: ReadFile blockiert beim allerersten Aufruf, wenn kein Byte empfangen wurde (zumindest bei mir hat es das getan). Wenn das ein Problem ist, müsstest du dann mit overlapped IO arbeiten.


----------



## Morkai (9. April 2007)

Guten Morgen,
ich danke Dir dass Du Dich mit dem Problem auseinander setzt, ich scheine auch wirklich der einzige zu sein der dieses Problem hat.  

Aber dwBytesToRead ist auf 1 gesetzt. Ich glaube Du hast das mit dwBytesRead verwechselt!

Schönen Ostermontag!

Gruß
Morkai


----------



## Endurion (9. April 2007)

Ach flixt, ich sollte vor dem Kaffee nix posten 

Ich kann morgen nochmal nachsehen, hab im Büro gerade mit sowas zu tun, aber den Source nicht hier.


----------



## Endurion (10. April 2007)

Blöde Frage, hast du evtl. nur die Ausgabe auf std::cout falsch?
Ich hab mir deinen Code mal kompiliert. Sobald ein Zeichen empfangen wurde, wird das vermutlich auch ewig ausgegeben. In dem Code prüfst du den Rückgabewert "error" gar nicht. D.h. es wird ewig das letzte empfangene Zeichen ausgegeben, obwohl es gar nicht mehr empfangen wurde.

Einfach den "error" prüfen, und wenn alles geklappt hat, das Zeichen auch ausgeben.

Mit anderen Worten, dein Empfangs-Code von der seriellen Schnittstelle ist sauber.


----------



## Morkai (10. April 2007)

Danke für die Antwort!

Ich denke Deine Vermutung ist richtig. Ich habe gestern abend schon ein wenig in diese Richtung probiert und bin auch zu der Überzeugung gelangt, dass der Fehler hier liegt.

Man, das ist ja fast peinlich! :-( 

Deine Frage ist also gar nicht blöd!!

Hast Du eine Idee, wann man die DCB Strucktur verwendet/ verwenden sollte und wann die COMMPROP Struktur?

Gruß
Morkai


----------



## Endurion (11. April 2007)

COMMPROP kenne ich gar nicht, ich verwende immer das DCB Struct. Da kann ich dir also gar nicht helfen


----------

