Pour travailler avec le port série sous Windows on utilisera les fonctions suivantes :
- Createfile : pour ouvrir le port ;
- Writefile : pour envoyer des données ;
- ReadFile : pour lire des données ;
- CloseHandle : fermeture du handle de communication.
Liste des fonctions annexes à utiliser.
- GetCommMask
- SetCommMask
- SetupComm
- GetCommState
- GetCommTimeouts
- BuildCommDCB
- ClearCommError
- GetOverlappedResult
Différentes définitions :
- RTS_CONTROL_HANDSHAKE = utilisation normale du handshake ;
- RTS_CONTROL_DISABLE = position fixe du signal (modifiable par programme) ;
- RTS_CONTROL_ENABLE = position fixe du signal(modifiable par programme) ;
- RTS_CONTROL_TOGGLE= souvent utilisé pour commander l'émission des modems half/duplex sur ligne multipoints.
Définition généraliste du HandShaking :
L'expression handshaking est utilisée pour décrire les procédures de mise en
accord de deux équipements connectés.
Contrôle de flux XON/XOFF :
Utilisé le plus souvent sur des périphériques tel que les imprimantes séries, ou des consoles Unix par exemple.
Ce protocole a l'avantage de n'utiliser du point de vue connectique que deux fils RX/TX (broches 2/3) , la gestion de flux étant réalisée par les caractères Xon/Xoff correspondant aux codes ASCII 17 et 19 .
Ce protocole ne doit pas être utilisé pour un transfert de fichier binaire sous peine de problèmes .
Xon:rétablit la communication lorsque le registre tampon de réception est vide.
Xoff:Interrompt la communication lorsque le registre tampon de réception est plein .L'arrêt est bilatérale.
Contrôle de flux Matériel :
RTS/CTS :
CTS :Clear To Send. PAE pour Prêt À Émettre en français. Réponse d'acceptation de l'émission, après un RTS.
RTS : Request To Send. Trame permettant à un émetteur de prévenir le récepteur qu'il veut parler. On lui répond par CTS.
Si RTS passe en position basse (tension négative), cela se traduit par : arrêtez d'envoyer.
Quand le récepteur est prêt à recevoir plus de données, il relance RTS, demandant à l'autre côté de reprendre l'envoi.
DTR/DSR :
C'est un contrôle de flux unidirectionnel uniquement.
DTR : Data Terminal Ready. Terminal de données prêt.
Indique au modem que l'ordinateur est sous tension et qu'il est prêt.
DSR : Data Set Ready. Modem Prêt.
Sert à indiquer à l'ordinateur (DTE) que le modem est sous tension et prêt .
Voici une classe de gestion du port série minimaliste avec les fonctions de base implémentées mettant en ?uvre les fonctions précitées.
Note : L'exemple qui suit utilise le flag FILE_FLAG_OVERLAPPED dans le createfile .
Ce flag active le mode de communication asynchrone parce qu'il ne faut pas faut pas travailler en mode synchrone c'est ingérable :
C'est le moyen le plus sûr de perdre des caractères en réception ou de bloquer l'application.
Le mode asynchrone est beaucoup plus souple, les opérations sont placées en file d'attente par le système et traitées dés que possible.
La gestion des fonctions WriteFile et ReadFile est un peu plus compliqué voir dans le code les détails d'implémentations.
# if ! defined ( AFX_SERIALCOM )
# define AFX_SERIALCOM
# if _MSC_VER > 1000
# pragma once
# endif
# define XON 17
# define XOFF 19
# define WM_CCOMRCV WM_USER + 100 / / réceptions sur la voie série
# define WM_CCOMEVENT WM_USER + 101 / / evénements sur la voie série .
# define WM_CCOMERROR WM_USER + 102 / / erreurs sur la voie série .
class CCom
{
public :
CCom ();
~ CCom ();
bool PortOpen (int portnumber,long baudrate,char parity,int wordlength,int stopbits);
bool PortSet (long baudrate,char parity,int wordlength,int stopbits);
bool PortClose ();
bool WriteBuffer (const char * buffer,unsigned int ucount= 0 );
int ReadBuffer (char * buffer,unsigned int ucount);
bool ReadChar (char & rchar);
bool UseXonXoff (bool bEnable= true );
bool UseRtsCts (bool bEnable= true );
bool UseDtrDsr (bool bEnable= true );
long SizeUsedInRXBuf ();
bool IsRXEmpty ();
bool WaitCommEvent (DWORD & rEvtMask);
bool SetCommMask (DWORD EvtMask);
DWORD GetCommMask ();
CString GetStringError (){ return m_StrError;}
int GetCountRead (){ return m_count;}
void SetParentNotify (CWnd * pParent){ m_pParent= pParent;}
bool PurgeCom ();
bool PurgeRx ();
bool PurgeTx ();
bool StartThread (CWnd * pParent);
bool ResumeThread ();
bool StopThread ();
bool SetTimeouts (DWORD dwRxTimeout= 5000 ,DWORD dwTxTimeout= 5000 );
virtual void OnError (DWORD dwError);
private :
CCom (const CCom & rCom){ ASSERT (0 );}
CCom & operator = (const CCom & arg){ ASSERT (0 );return * this ;}
protected :
static UINT Thread (LPVOID pParam);
DWORD GetError ();
protected :
CWnd * m_pParent;
CString m_StrError;
DWORD m_comerr;
HANDLE m_hCom;
UINT m_nInputBufferSize;
UINT m_nOutputBufferSize;
DCB m_dcb;
DWORD m_EventMask;
COMMTIMEOUTS m_ComTimeouts;
OVERLAPPED m_Ov;
unsigned int m_count;
long m_baudrate;
char m_parity;
int m_wordlength;
int m_stopbits;
CWinThread* m_pThread;
HANDLE m_hCloseCom;
HANDLE m_hArrayEvent[2 ];
bool m_bThreadExist;
} ;
# endif
|
# include "stdafx.h"
# include "SerialCom.h"
CCom:: CCom ()
{
m_hCom = NULL ;
m_Ov.hEvent= NULL ;
m_comerr= 0 ;
m_count= 0 ;
m_pParent= NULL ;
m_pThread= NULL ;
m_hCloseCom= NULL ;
m_bThreadExist= false ;
}
CCom:: ~ CCom ()
{
PortClose ( );
}
DWORD CCom:: GetError ()
{
LPVOID lpMsgBuf;
DWORD dw;
FormatMessage (
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL ,
(dw= GetLastError ()),
0 ,
(LPTSTR) & lpMsgBuf,
0 ,
NULL
);
m_StrError= CString ((LPTSTR)lpMsgBuf);
LocalFree ( lpMsgBuf );
return dw;
}
bool CCom:: PortOpen (int portnumber,long baudrate,char parity,int wordlength,int stopbits)
{
char sz[20 ];
m_nInputBufferSize= 1050 ;
m_nOutputBufferSize= 1050 ;
memset (& m_Ov,0 ,sizeof (m_Ov));
wsprintf ( sz, " \\\\.\\COM%d " , portnumber);
m_hCom = CreateFile ( sz,
GENERIC_READ | GENERIC_WRITE,
0 ,
NULL ,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL );
if ( m_hCom = = INVALID_HANDLE_VALUE )
{
GetError ();
return false ;
}
SetupComm ( m_hCom, m_nInputBufferSize, m_nOutputBufferSize );
:: GetCommMask ( m_hCom, & m_EventMask );
:: SetCommMask ( m_hCom, 0 );
m_dcb.fBinary = 1 ;
m_dcb.fParity = 0 ;
m_dcb.fNull = 0 ;
m_dcb.XonChar = XON;
m_dcb.XoffChar = XOFF;
m_dcb.XonLim = (WORD)( ( m_nInputBufferSize) / 4 );
m_dcb.XoffLim = (WORD)( ( m_nOutputBufferSize ) / 4 );
m_dcb.EofChar = 0 ;
m_dcb.EvtChar = 0 ;
m_dcb.fOutxDsrFlow = 0 ;
m_dcb.fOutxCtsFlow = 0 ;
m_dcb.fDtrControl = DTR_CONTROL_ENABLE;
m_dcb.fRtsControl = RTS_CONTROL_ENABLE;
GetCommState ( m_hCom, & m_dcb );
GetCommTimeouts ( m_hCom, & m_ComTimeouts );
m_ComTimeouts.ReadIntervalTimeout = 1000 ;
m_ComTimeouts.ReadTotalTimeoutMultiplier = 1000 ;
m_ComTimeouts.ReadTotalTimeoutConstant = 1000 ;
m_ComTimeouts.WriteTotalTimeoutMultiplier = 1000 ;
m_ComTimeouts.WriteTotalTimeoutConstant = 1000 ;
SetCommTimeouts ( m_hCom, & m_ComTimeouts );
return PortSet (baudrate,parity,wordlength,stopbits);
}
bool CCom:: SetTimeouts (DWORD dwRxTimeout ,DWORD dwTxTimeout )
{
if (m_hCom= = NULL )
{
TRACE0 (" CCom::SetTimeouts': NULL handle ! " );
return 0 ;
}
COMMTIMEOUTS commTimeOuts ;
commTimeOuts.ReadIntervalTimeout = dwRxTimeout;
commTimeOuts.ReadTotalTimeoutMultiplier = 1 ;
commTimeOuts.ReadTotalTimeoutConstant = dwRxTimeout;
commTimeOuts.WriteTotalTimeoutMultiplier = 1 ;
commTimeOuts.WriteTotalTimeoutConstant = dwTxTimeout;
SetCommTimeouts (m_hCom, & commTimeOuts ) ;
return true ;
}
bool CCom:: PortSet (long baudrate,char parity,int wordlength,int stopbits )
{
int result;
if (! m_hCom) return false ;
m_baudrate= baudrate;
m_parity= parity;
m_wordlength= wordlength;
m_stopbits= stopbits;
char * szBaud = new char [50 ];
sprintf (szBaud, " baud=%d parity=%c data=%d stop=%d " , baudrate,parity,wordlength,stopbits);
if (result= GetCommState (m_hCom,& m_dcb))
{
m_dcb.fRtsControl = RTS_CONTROL_ENABLE;
result= - 1 ;
if (BuildCommDCB (szBaud, & m_dcb)) result= SetCommState (m_hCom, & m_dcb);
}
if ( result < 0 ) GetError ();
delete szBaud;
PurgeCom ();
return ( (result > 0 ) );
}
bool CCom:: PortClose ()
{
if (! m_hCom) return false ;
if (m_pThread)
{
do
{
SetEvent (m_hCloseCom);
}
while (m_bThreadExist);
CloseHandle (m_hCloseCom);
}
if (m_Ov.hEvent) CloseHandle (m_Ov.hEvent);
m_Ov.hEvent= NULL ;
m_hCloseCom= NULL ;
m_pThread= NULL ;
EscapeCommFunction ( m_hCom, CLRDTR );
EscapeCommFunction ( m_hCom, CLRRTS );
int status= CloseHandle ( m_hCom);
m_hCom= NULL ;
if ( status ) return true ;
GetError ();
return false ;
}
bool CCom:: WriteBuffer (const char * buffer,unsigned int ucount )
{
int result;
DWORD comerr;
COMSTAT comstat;
unsigned int amounttowrite;
DWORD amountwritten;
if (! ucount) ucount= strlen (buffer);
ClearCommError ( m_hCom, & comerr, & comstat );
m_comerr | = comerr;
amounttowrite = m_nOutputBufferSize - comstat.cbOutQue;
if ( ucount < amounttowrite ) amounttowrite = ucount;
result = WriteFile ( m_hCom,
buffer,
(int ) amounttowrite,
& amountwritten,
& m_Ov );
m_count = amountwritten;
if ( result = = 0 )
{
if ( ( comerr = GetLastError () ) ! = ERROR_IO_PENDING )
{
ClearCommError ( m_hCom, & comerr, & comstat );
m_comerr| = comerr;
}
else m_count = amounttowrite;
}
if ( m_count < ucount ) return (0 );
return ( 1 );
}
int CCom:: ReadBuffer (char * buffer,unsigned int ucount)
{
int result;
COMSTAT comstat;
DWORD comerr;
DWORD countread;
DWORD counttoread;
ClearCommError ( m_hCom, & comerr, & comstat );
if ( comerr > 0 ) m_comerr | = comerr;
if ( comstat.cbInQue > 0 )
{
if ( comstat.cbInQue < ucount ) counttoread = comstat.cbInQue;
else counttoread = ucount;
result = ReadFile ( m_hCom,buffer,(int ) counttoread,& countread,& m_Ov );
m_count = countread;
if ( result = = 0 )
{
if ( ( comerr = GetLastError () ) ! = ERROR_IO_PENDING )
{
ClearCommError ( m_hCom, & comerr, & comstat );
m_comerr | = comerr;
}
}
if ( m_count < ucount )
{
if ( GetOverlappedResult ( m_hCom, & m_Ov, & countread, TRUE ) )
{
m_count = countread;
return ( 1 );
}
return ( - 1 );
}
return ( 1 );
}
else
{
m_count = 0 ;
return ( - 1 );
}
return - 1 ;
}
bool CCom:: ReadChar (char & rchar )
{
return ((ReadBuffer (& rchar,1 )= = 0 ));
}
long CCom:: SizeUsedInRXBuf ()
{
COMSTAT comstat;
DWORD comerr;
ClearCommError ( m_hCom, & comerr, & comstat );
m_comerr | = comerr;
return comstat.cbInQue;
}
bool CCom:: UseXonXoff (bool bEnable)
{
int result;
m_dcb.fInX = ( bEnable ) ? 1 : 0 ;
m_dcb.fOutX = ( bEnable ) ? 1 : 0 ;
result= SetCommState ( m_hCom, & m_dcb );
if ( result = = TRUE ) return true ;
GetError ();
return false ;
}
bool CCom:: UseRtsCts (bool bEnable)
{
int result;
m_dcb.fOutxCtsFlow = ( bEnable ) ? 1 : 0 ;
m_dcb.fRtsControl = ( bEnable) ? RTS_CONTROL_HANDSHAKE : RTS_CONTROL_DISABLE;
result= SetCommState ( m_hCom, & m_dcb );
if ( result = = TRUE ) return true ;
GetError ();
return false ;
}
bool CCom:: UseDtrDsr (bool bEnable)
{
int result;
m_dcb.fOutxDsrFlow = ( bEnable ) ? 1 : 0 ;
m_dcb.fDtrControl = ( bEnable ) ? DTR_CONTROL_HANDSHAKE : DTR_CONTROL_DISABLE;
result= :: SetCommState ( m_hCom, & m_dcb );
if ( result = = TRUE ) return true ;
GetError ();
return false ;
}
bool CCom:: WaitCommEvent (DWORD & rEvtMask)
{
if (m_hCom= = NULL )
{
TRACE0 (" CCom::WaitCommEvent': NULL handle ! " );
ASSERT (FALSE);
return false ;
}
if (! :: WaitCommEvent (m_hCom,& rEvtMask,& m_Ov))
{
TRACE1 (" \nCCom::WaitCommEvent:%d " ,GetLastError ());
GetError ();
return false ;
}
return true ;
}
DWORD CCom:: GetCommMask ()
{
if (m_hCom= = NULL )
{
TRACE0 (" CCom::GetCommMask': NULL handle ! " );
return 0 ;
}
DWORD dwMask;
:: GetCommMask ( m_hCom, & dwMask);
return dwMask;
}
bool CCom:: SetCommMask (DWORD EvtMask)
{
if (! m_Ov.hEvent)
m_Ov.hEvent= CreateEvent (NULL ,
FALSE,
FALSE,
NULL
);
SetEvent (m_Ov.hEvent);
return (:: SetCommMask (m_hCom,EvtMask)> 0 );
}
bool CCom:: PurgeRx ()
{
if (m_hCom= = NULL )
{
TRACE0 (" CCom::PurgeRx': NULL handle ! " );
return false ;
}
:: PurgeComm (m_hCom, PURGE_RXCLEAR);
return true ;
}
bool CCom:: PurgeTx ()
{
if (m_hCom= = NULL )
{
TRACE0 (" CCom::PurgeTx': NULL handle ! " );
return false ;
}
:: PurgeComm (m_hCom, PURGE_TXCLEAR);
return true ;
}
bool CCom:: PurgeCom ()
{
if (m_hCom= = NULL )
{
TRACE0 (" CCom::PurgeCom': NULL handle ! " );
return false ;
}
:: PurgeComm (m_hCom, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
return true ;
}
bool CCom:: StartThread (CWnd * pParent)
{
SetParentNotify (pParent);
if (! m_pParent | | ! PurgeCom ()) return false ;
if (m_bThreadExist)
{
do
{
SetEvent (m_hCloseCom);
}
while (m_bThreadExist);
CloseHandle (m_hCloseCom);
}
m_hCloseCom = CreateEvent (NULL , TRUE, FALSE, NULL );
m_hArrayEvent[0 ]= m_hCloseCom;
m_hArrayEvent[1 ]= m_Ov.hEvent;
if (! (m_pThread = AfxBeginThread (Thread, this ))) return false ;
TRACE (" Le Thread Démarre\n " );
return true ;
}
bool CCom:: IsRXEmpty ()
{
long n;
n = SizeUsedInRXBuf ();
if ( n < 0 )
{
OnError (GetError ());
return true ;
}
return (( n= = 0 ));
}
bool CCom:: ResumeThread ()
{
if (! m_pThread) return false ;
m_pThread- > ResumeThread ();
return true ;
}
bool CCom:: StopThread ()
{
if (! m_pThread) return false ;
m_pThread- > SuspendThread ();
return true ;
}
void CCom:: OnError (DWORD dwError)
{
if (m_pParent)
m_pParent- > PostMessage (WM_CCOMERROR,(WPARAM)this ,dwError);
}
UINT CCom:: Thread (LPVOID pParam)
{
CCom * pCom = (CCom* )pParam;
int nResult;
DWORD WaitEvent = 0 ;
DWORD dwError= 0 ;
DWORD dwMaskEvent= 0 ;
pCom- > m_bThreadExist= true ;
while (1 )
{
nResult= pCom- > WaitCommEvent (dwMaskEvent);
if (! nResult)
{
switch (dwError= GetLastError ())
{
case 87 :
case ERROR_IO_PENDING:
break ;
default :
{
pCom- > GetError ();
pCom- > OnError (dwError);
break ;
}
}
}
else
{
if (pCom- > IsRXEmpty ()) continue ;
}
WaitEvent = WaitForMultipleObjects (2 , pCom- > m_hArrayEvent, FALSE, INFINITE);
switch (WaitEvent)
{
case 0 :
pCom- > m_bThreadExist= false ;
AfxEndThread (1 );
return (0 );
case 1 :dwMaskEvent= pCom- > GetCommMask ();
if (dwMaskEvent & EV_RXCHAR)
pCom- > m_pParent- > SendMessage (WM_CCOMRCV,(WPARAM)pCom,dwMaskEvent);
if ((dwMaskEvent & EV_CTS) | |
(dwMaskEvent & EV_RXFLAG) | |
(dwMaskEvent & EV_BREAK) | |
(dwMaskEvent & EV_ERR)
| | (dwMaskEvent & EV_RING))
{
pCom- > m_pParent- > SendMessage (WM_CCOMEVENT,(WPARAM)pCom,dwMaskEvent);
}
break ;
}
} ;
return 0 ;
}
CCom com;
com.PortOpen (1 ,57600 ,' N ' ,8 ,1 );
com.UseRtsCts ();
com.SetCommMask (EV_RXCHAR);
com.WriteBuffer (" ATI7\r\n " );
DWORD EvtMask;
com.WaitCommEvent (EvtMask);
char sz[1000 ];
com.ReadBuffer (sz,sizeof (sz));
AfxMessageBox (CString (sz,com.GetCountRead ()));
|
|