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

Morkai

Grünschnabel
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
 
Zeig mal deinen Code. Meiner Erfahrung nach werden die Daten mit ReadFile einwandfrei gelöscht. D.h. einmal ausgelesen sind die Daten weg.
 
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
 
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.
 
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
 
Zuletzt bearbeitet:
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.
 
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
 
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.
 
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.
 
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
 

Neue Beiträge

Zurück