guill.net - La page des réseaux

Communication RS232 en C++ Builder
Auteur : Xavier Brun de l'ESEO, le 30/09/00


Première partie : Gestion des entrées/Sorties de périphériques

L’une des grandes force Windows NT et de Windows 95 réside dans le nombre de périphériques que ces environnements savent gérer. D’un façon générale, nous pouvons définir un périphérique comme quelque chose permettant une communication. Voici une liste décrivant leur usage habituel :
 
Périphériques Usage
Fichier Stockage de données
Répertoire Attributs et compression de fichiers
Disque logique Formatage
Disque physique Table de partitionnement
Port série Transmission de données en série
Port parallèle Envoi de données en parallèle
Console Une fenêtre texte

Win32 tente de masquer les différences entre périphériques afin de faciliter la tâche du développeur d’application. Ainsi, lorsque vous avez ouvert un périphérique, les fonctions Win32 qui permettent de lire ou d’écrire des données, sont les même quel que soit le mode de communication employé.

Cette partie présente les différents mécanismes disponibles pour lire et écrire des données sur un périphérique. Nous nous intéresserons plus particulièrement à la manipulation de ces fonctions pour des périphérique de type port série.

Il faut noter que bien que les fonctions utilisées soient communes à tout type de périphérique leur utilisation et paramétrage diffère d’un périphérique à l’autre. Par exemple nous pouvons régler la vitesse de transmission sur une ligne série, mais ce paramètre n’a aucun sens pour un fichier.

Ouvrir et fermer un périphérique

Avant de pouvoir effectuer une quelconque opération d’entrée/sortie, vous devez ouvrir le périphérique et obtenir un handle. D’une façon générale la plupart des périphériques sont ouvert avec la fonction CreateFile, dont la description est donnée ci après :

HANDLE CreateFile(

    LPCTSTR lpFileName,
    DWORD dwDesiredAccess,
    DWORD dwShareMode,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    DWORD dwCreationDistribution,
    DWORD dwFlagsAndAttributes,
    HANDLE hTemplateFile
   );

Le paramètre lpFileName identifie le type de périphérique ainsi que l’instance spécifique de ce périphérique.  LpFileName aura pour valeur :

Le paramètre dwDesiredAccess précise comment vous souhaitez transférer les données avec le périphérique. Quatre valeurs sont possibles :
 
 
Valeur Signification
0 Vous ne souhaitez ni Lire ni écrire sur le périphérique. 
Vous souhaitez juste changer sa configuration et ses réglages
GENERIC_READ Permet des accès en lecture seule sur le périphérique
GENERIC_WRITE Permet des accès en écriture seule sur le périphérique.
GENERIC_READ| GENERIC_WRITE Permet des accès en lecture et en écriture sur le périphérique

Le paramètre dwShareMode détermine les privilèges de partage. Sous Windows 95 et Windows NT, un périphérique peut être employé par plusieurs ordinateurs à la fois ou par plusieurs processus en même temps. Il devient alors important de pouvoir réduire les droits d’accès des autres systèmes et processus sur le périphérique que vous utilisez. Les différentes valeurs possibles pour dwShareMode sont :
 
Valeur Signification
Vous souhaitez un accès exclusif à ce périphérique. S’il est déjà ouvert, CreateFile échoue. 
Si vous l’ouvrez et qu’un autre appel CreateFile est effectué ensuite, cet appel échouera.
FILE_SHARE_READ Vous souhaitez utiliser le périphérique en lecture seul. S’il est déjà ouvert en écriture, CreateFile échouera. 
Si vous l’ouvrez et qu’un autre appel CreateFile est effectué en écriture, cet appel échouera. 
FILE_SHARE_WRITE Vous souhaitez uniquement écrire dans le périphérique. S’il est déjà ouvert en lecture, CreateFile échouera. 
Si vous l’ouvrez et qu’un autre appel CreateFile est effectué en lecture, cet appel échouera.
FILE_SHARE_READ| FILE_SHARE_WRITE Vous souhaitez utiliser le périphérique en lecture et en écriture. 
Si le périphérique est déjà ouvert pour un accès exclusif, CreateFile échouera. 
Si vous l’ouvrez et qu’un autre appel  CreateFile est effectué en lecture ou en écriture, cet appel échouera.

Le paramètre lpSecurityAttributes, pointe sur une structure SECURITY_ATTRIBUTES qui vous permet de préciser les informations de sécurité de l’objet du noyau associé au périphérique. Vous définissez également si le handle obtenu en retour sera héritable ou non ( permettant ainsi à des processus enfant d’accéder au périphérique). Le paramètre est  NULL si vous souhaiter mettre en œuvre la sécurité par défaut et un handle non héritable.

Le paramètre dwCreationDistribution est utile lorsque CreateFile ouvre un fichier. Les différentes valeurs dwCreationDistribution sont :
 
 
Valeur Signification
CREATE_NEW  Demande à CreateFile de créer un nouveau fichier. Echec si un fichier de même nom existe.
CREATE_ALWAYS  Demande à CreateFile de créer un fichier sans vérification de l’existance ou non de l’existance d’un fichier de même nom.
OPEN_EXISTING  Demande à CreateFile d’ouvrir un fichier existant. Erreur si le fichier n’existe pas.
OPEN_ALWAYS  Demande à CreateFile d’ouvrir un fichier s’il existe ou de le créer s’il n’existe pas encore.
TRUNCATE_EXISTING  Demande à CreateFile d’ouvrir un fichier existant et de ramener sa taille à 0 octet. 
Echec si le fichier n’existe pas déjà. Le drapeau GENERIC_WRITE doit être également utilisé.

Lorsque vous appelez CreateFile pour ouvrir un périphérique qui n’est pas un fichier, vous devez utiliser OPEN_EXISTING pour le paramètre dwCreationDistribution.

Le paramètre dwFlagsAndAttributes vous permet de définir des drapeaux affinant la communication avec le périphérique et si le périphérique est un fichier vous obtenez ses attributs. Dans le cas de la communication qui nous intéresse nous n’utiliserons pas ce paramètre : valeur = 0. Il faut noter que si vous souhaitez utiliser le périphérique de manière asynchrone, il faut utiliser le drapeau FILE_FLAG_OVERLAPPED. Ce drapeau indique au système que vous voulez accéder au fichier de manière asynchrone ( par défaut l’ouverture est synchrone).
Si vous communiquer en mode synchrone, le programme est suspendu dans l’attente des informations devant être lues. Une fois la lecture terminée, le programme reprend le contrôle et continue sont exécution.
Théoriquement les entrées/sorties sur les périphériques étant très lentes par rapport à la plupart des opérations du processeur, il est préférable de communiquer avec un périphérique de façon asynchrone. Dans le principe, vous appelez une fonction demandant au système d’exploitation de lire ou d’écrire des données mais cette fonction rend la main immédiatement, sans attendre la fin de l’opération. Le système d’exploitation traite votre demande de façon autonome et vous renvoie un message lorsque le traitement est terminé. Pendant ce temps votre application a continué à s’exécuter. Nous ne traiterons pas ce cas dans notre exemple.

Le paramètre hTemplateFile permet de spécifier les attributs du fichier. Il aura pour valeur NULL dans notre cas

Nous allons maintenant aborder la lecture et l’écriture de donnée synchrone sur un périphérique

Lecture et écriture synchrone sur un périphérique :

Cette section aborde les fonctions de Win32 qui permettent des lectures et des écritures synchrones sur des périphériques ( nous ne traiterons pas les lecture asynchrones ). Rappel un périphérique peut  être un fichier, un canal, un port …

La méthode la plus simple et la plus facile à mettre en œuvre pour lire et écrire des fichiers passe par les deux fonctions :

BOOL ReadFile(

    HANDLE hFile, // handle of file to read
    LPVOID lpBuffer, // address of buffer that receives data
    DWORD nNumberOfBytesToRead, // number of bytes to read
    LPDWORD lpNumberOfBytesRead, // address of number of bytes read
    LPOVERLAPPED lpOverlapped  // address of structure for data
   );

BOOL WriteFile(

    HANDLE hFile, // handle to file to write to
    LPCVOID lpBuffer, // pointer to data to write to file
    DWORD nNumberOfBytesToWrite, // number of bytes to write
    LPDWORD lpNumberOfBytesWritten, // pointer to number of bytes written
    LPOVERLAPPED lpOverlapped  // pointer to structure needed for overlapped I/O
   );

bien que chacune de ces fonctions possèdent le mot File dans son nom, elles peuvent être utilisées avec n’importe quel type de périphérique.

Le paramètre hFile identifie le handle du périphérique auquel vous souhaitez accéder.

Le paramètre lpBuffer pointe sur une mémoire tampon qui servira lors des échanges d’écriture/lecture entre le périphérique et l’application.

Le paramètre nNumberOfBytesToWrite et nNumberOfBytesToRead indique à ReadFile et WriteFile combien d’octets doivent être lus ou écrits sur le périphérique.

Le paramètre  lpNumberOfBytesRead et lpNumberOfBytesWritten indique l’adresse d’un DWORD contenant le nombre d’octets réellement transmis.

Le paramètre lpOverlapped doit être NULL lors d’opération synchrone.

Les fonctions ReadFile et WriteFile renvoient TRUE en cas de succès. ReadFile ne peut être utilisée qu’avec des périphériques ouvert avec le drapeau GENERIC_READ. De même WriteFile ne peut être utilisée que si le périphérique est ouvert avec le drapeau GENERIC_WRITE.

Configuration du périphérique de communication

Le fichier étant ouvert il ne nous reste plus qu’à configurer le port de communication. Pour cela nous utilisons la fonction SetCommSate :

BOOL SetCommState(

    HANDLE hFile, // handle of communications device
    LPDCB lpDCB // address of device-control block structure
   );

Le paramètre hFile identifie le handle de communication retourné par la fonction CreateFile

Le paramètre lpDCB indique l’adresse de la structure DCB contenant les informations de configuration spécifique au port de communication.

Description de la structure DCB

La structure DCB permet de configurer le port serie. Cette structure est définie comme suit :

typedef struct _DCB { // dcb
    DWORD DCBlength;           // sizeof(DCB)
    DWORD BaudRate;            // current baud rate
    DWORD fBinary: 1;          // binary mode, no EOF check
    DWORD fParity: 1;          // enable parity checking
    DWORD fOutxCtsFlow:1;      // CTS output flow control
    DWORD fOutxDsrFlow:1;      // DSR output flow control
    DWORD fDtrControl:2;       // DTR flow control type
    DWORD fDsrSensitivity:1;   // DSR sensitivity

    DWORD fTXContinueOnXoff:1; // XOFF continues Tx
    DWORD fOutX: 1;            // XON/XOFF out flow control
    DWORD fInX: 1;             // XON/XOFF in flow control
    DWORD fErrorChar: 1;       // enable error replacement
    DWORD fNull: 1;            // enable null stripping
    DWORD fRtsControl:2;       // RTS flow control
    DWORD fAbortOnError:1;     // abort reads/writes on error
    DWORD fDummy2:17;          // reserved
    WORD wReserved;            // not currently used

    WORD XonLim;               // transmit XON threshold
    WORD XoffLim;              // transmit XOFF threshold
    BYTE ByteSize;             // number of bits/byte, 4-8
    BYTE Parity;               // 0-4=no,odd,even,mark,space
    BYTE StopBits;             // 0,1,2 = 1, 1.5, 2
    char XonChar;              // Tx and Rx XON character
    char XoffChar;             // Tx and Rx XOFF character
    char ErrorChar;            // error replacement character

    char EofChar;              // end of input character
    char EvtChar;              // received event character
    WORD wReserved1;           // reserved; do not use
} DCB;

Le paramètre DCBlength donne la taille en octets de la structure.

Le paramètre BaudRate spécifie la vitesse de transmission sur le port série. La valeur peut être n’importe quelle valeur ou une des valeurs standards définies ci après :
CBR_110
CBR_300
CBR_600
CBR_1200
CBR_2400
CBR_4800
CBR_9600
CBR_14400
CBR_19200
CBR_38400
CBR_56000
CBR_57600
CBR_115200
CBR_128000
CBR_256000

FBinary  doit être à TRUE pour des application Windows.

Le paramètre fParity spécifie la vérification ou non de la parité. TRUE la parité est vérifiée et une erreur est générée si nécessaire.

Les paramètres suivants ne sont pas utilisés en communication 3 fils. Il sont uniquement à configurer dans le cas de protocole XON/XOFF et permettent de contrôler les signaux DTR ( terminal prêt), CTS(Voie Libre), DSR ( jeu de donnée prêt), RTS (demande pour émettre) . Nous ne rentrerons donc pas dans le détail de ces paramètres. De façon générale, et dans le cadre de cette présentation, nous leur affecterons la valeur 0.

FOutxCtsFlow
Specifies whether the CTS (clear-to-send) signal is monitored for output flow control. If this member is TRUE and CTS is turned off, output is suspended until CTS is sent again.

fOutxDsrFlow
Specifies whether the DSR (data-set-ready) signal is monitored for output flow control. If this member is TRUE and DSR is turned off, output is suspended until DSR is sent again.

fDtrControl
Specifies the DTR (data-terminal-ready) flow control. This member can be one of the following values:
 
 
Value Meaning
DTR_CONTROL_DISABLE Disables the DTR line when the device is opened and leaves it disabled.
DTR_CONTROL_ENABLE Enables the DTR line when the device is opened and leaves it on..
DTR_CONTROL_HANDSHAKE Enables DTR handshaking. If handshaking is enabled, it is an error for 
the application to adjust the line by using the EscapeCommFunction function.

fDsrSensitivity
Specifies whether the communications driver is sensitive to the state of the DSR signal. If this member is TRUE, the driver ignores any bytes received, unless the DSR modem input line is high.

fTXContinueOnXoff
Specifies whether transmission stops when the input buffer is full and the driver has transmitted the XoffChar character. If this member is TRUE, transmission continues after the input buffer has come within XoffLim bytes of being full and the driver has transmitted the XoffChar character to stop receiving bytes. If this member is FALSE, transmission does not continue until the input buffer is within XonLim bytes of being empty and the driver has transmitted the XonChar character to resume reception.

fOutX
Specifies whether XON/XOFF flow control is used during transmission. If this member is TRUE, transmission stops when the XoffChar character is received and starts again when the XonChar character is received.

fInX
Specifies whether XON/XOFF flow control is used during reception. If this member is TRUE, the XoffChar character is sent when the input buffer comes within XoffLim bytes of being full, and the XonChar character is sent when the input buffer comes within XonLim bytes of being empty.

fErrorChar
Specifies whether bytes received with parity errors are replaced with the character specified by the ErrorChar member. If this member is TRUE and the fParity member is TRUE, replacement occurs.

fNull
Specifies whether null bytes are discarded. If this member is TRUE, null bytes are discarded when received.

fRtsControl
Specifies the RTS (request-to-send) flow control. If this value is zero, the default is RTS_CONTROL_HANDSHAKE. This member can be one of the following values:
 
 
Value Meaning
RTS_CONTROL_DISABLE  Disables the RTS line when the device is opened and leaves it disabled.
RTS_CONTROL_ENABLE Enables the RTS line when the device is opened and leaves it on.
RTS_CONTROL_HANDSHAKE Enables RTS handshaking. The driver raises the RTS line when the "type-ahead" (input) 
buffer is less than one-half full and lowers the RTS line when the buffer is more than 
three-quarters full. If handshaking is enabled, it is an error for the application to adjust 
the line by using the EscapeCommFunction function.
RTS_CONTROL_TOGGLE  Specifies that the RTS line will be high if bytes are available for transmission. 
After all buffered bytes have been sent, the RTS line will be low.

fAbortOnError
Specifies whether read and write operations are terminated if an error occurs. If this member is TRUE, the driver terminates all read and write operations with an error status if an error occurs. The driver will not accept any further communications operations until the application has acknowledged the error by calling the ClearCommError function.

fDummy2
Reserved; do not use.

wReserved
Not used; must be set to zero.

XonLim
Specifies the minimum number of bytes allowed in the input buffer before the XON character is sent.

XoffLim
Specifies the maximum number of bytes allowed in the input buffer before the XOFF character is sent. The maximum number of bytes allowed is calculated by subtracting this value from the size, in bytes, of the input buffer.

XonChar
Specifies the value of the XON character for both transmission and reception.

XoffChar
Specifies the value of the XOFF character for both transmission and reception.

ErrorChar
Specifies the value of the character used to replace bytes received with a parity error.

EofChar
Specifies the value of the character used to signal the end of data.

EvtChar
Specifies the value of the character used to signal an event.

wReserved1
Reserved; do not use.

Le paramètre ByteSize définit le nombre de bits de l’octet

Le paramètre Parity définit le type de parité utilisé dans le protocole. Les valeurs possibles sont :
 
Valeur Description
EVENPARITY Parité paire
NOPARITY Pas de parité
ODDPARITY Parité impaire

Le paramètre StopBits définit le nombre de stop bit. Les valeurs possibles sont :
 
Valeur Description
ONESTOPBIT 1 stop bit
ONE5STOPBITS 1.5 stop bits
TWOSTOPBITS 2 stop bits

Définition des TimeOut de communication

La définition des timeouts de communication est utilisée pour arrêter le process de communication au bout de x (ms). Dans notre cas un timeout sera utilisé pour sortir de la boucle de réception étant donnée que l’on ne veut pas gérer la taille des trames reçue. Nous fixerons donc une taille de buffer de réception fixe que nous traiterons en fin de time out.

La gestion des timeouts se fait à l’aide des commandes SetCommTimeouts pour la configuration, GetCommTimeouts pour la lecture de la configuration et de la structure COMMTIMEOUTS contenant la description des timeouts :

Fonction SetCommTimeouts : utilisée pour configurer les timeOuts

BOOL SetCommTimeouts(
    HANDLE hFile,                                          // handle of communications device
    LPCOMMTIMEOUTS lpCommTimeouts // address of communications time-out structure
   );

Le paramètre hFile identifie le port de communication retourné par la fonction CreateFile

Le paramètre lpCommTimeouts  pointe sur une structure COMMTIMEOUTS qui contient les nouvelles valeurs de time out

La valeur de retour est différente de 0 en cas de succès.

Fonction GetCommTimeouts : utilisée pour lire les timeouts

BOOL GetCommTimeouts(
    HANDLE hFile,                                           // handle of communications device
    LPCOMMTIMEOUTS lpCommTimeouts  // address of comm. time-outs structure
   );

Le paramètre hFile identifie le port de communication retourné par la fonction CreateFile

Le paramètre lpCommTimeouts pointe une structure contenant la configuration actuelle des timeouts.

La valeur de retour est différente de 0 en cas de succès.

La structure COMMTIMEOUTS : utilisée pour définir les timeouts

typedef struct _COMMTIMEOUTS {   // ctmo
    DWORD ReadIntervalTimeout;
    DWORD ReadTotalTimeoutMultiplier;
    DWORD ReadTotalTimeoutConstant;
    DWORD WriteTotalTimeoutMultiplier;
    DWORD WriteTotalTimeoutConstant;
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

ReadIntervalTimeout
Specifies the maximum time, in milliseconds, allowed to elapse between the arrival of two characters on the communications line. During a ReadFile operation, the time period begins when the first character is received. If the interval between the arrival of any two characters exceeds this amount, the ReadFile operation is completed and any buffered data is returned. A value of zero indicates that interval time-outs are not used.
A value of MAXDWORD, combined with zero values for both the ReadTotalTimeoutConstant and ReadTotalTimeoutMultiplier members, specifies that the read operation is to return immediately with the characters that have already been received, even if no characters have been received.

ReadTotalTimeoutMultiplier
Specifies the multiplier, in milliseconds, used to calculate the total time-out period for read operations. For each read operation, this value is multiplied by the requested number of bytes to be read.

ReadTotalTimeoutConstant
Specifies the constant, in milliseconds, used to calculate the total time-out period for read operations. For each read operation, this value is added to the product of the ReadTotalTimeoutMultiplier member and the requested number of bytes.
A value of zero for both the ReadTotalTimeoutMultiplier and ReadTotalTimeoutConstant members indicates that total time-outs are not used for read operations.

WriteTotalTimeoutMultiplier
Specifies the multiplier, in milliseconds, used to calculate the total time-out period for write operations. For each write operation, this value is multiplied by the number of bytes to be written.

WriteTotalTimeoutConstant
Specifies the constant, in milliseconds, used to calculate the total time-out period for write operations. For each write operation, this value is added to the product of the WriteTotalTimeoutMultiplier member and the number of bytes to be written.
A value of zero for both the WriteTotalTimeoutMultiplier and WriteTotalTimeoutConstant members indicates that total time-outs are not used for write operations.

Remarks

If an application sets ReadIntervalTimeout and ReadTotalTimeoutMultiplier to MAXDWORD and sets ReadTotalTimeoutConstant to a value greater than zero and less than MAXDWORD, one of the following occurs when the ReadFile function is called:
- If there are any characters in the input buffer, ReadFile returns immediately with the characters in the buffer.
- If there are no characters in the input buffer, ReadFile waits until a character arrives and then returns immediately.
- If no character arrives within the time specified by ReadTotalTimeoutConstant, ReadFile times out.


Suite

Retour