Académique Documents
Professionnel Documents
Culture Documents
Introduccin
Contenido
FUNCIONES EL API WIN32 PARA COMUNICACIONES SERIE
LNEAS DE CONTROL DE FLUJO
BUFFER DEL TRANSMISOR Y DEL RECEPTOR
LEER Y ESCRIBIR EN EL PUERTO
CLASE TWinSerCom PARA TRANSMITIR Y RECIBIR CON WIN32
UN CHAT BAJO WINDOWS
ENVIAR Y RECIBIR TRAMAS
CONTROL DEL PUERTO MEDIANTE EVENTOS
PROCESOS Y SUBPROCESOS
RECEPCIN EN SEGUNDO PLANO
10.1. FUNCIONES DEL API WIN32 PARA COMUNICACIONES SERIE
La utilizacin del las funciones del API WIN32 para trabajar con el puerto serie garantiza la
portabilidad del cdigo entre distintas versiones de WINDOWS (95/98/NT) y la estabilidad de
los programar que desarrollemos. La lista de funciones y estructuras que permiten programar el
puerto serie son ms de dos docenas, se irn explicando segun se necesiten. Trabajar con un
puerto serie se asemeja mucho al trabajo con ficheros mediante flujos, al trabajar con un puerto
serie se pueden distinguir 4 fases.
1.- Operacin de apertura: sirve para obtener un manejador o handle y comprobar si el sistema
operativo ha podido tomar el control del dispositivo para nuestra aplicacin.
2.- Configuracin: Sirve par establecer los parmetros de la comunicacin: velocidad, paridad
etc as como el tipo de acceso: mediante sondeo o mediante eventos.
3.- Acceso al puerto para leer o escribir en el: Hay que tener en cuenta que el acceso al puerto
siempre se realiza a travs de BUFFER uno para la transmisin (escritura) y otro para la
recepcin (lectura).
4.- Cierre del puerto para que otros programas puedan hace uso de l.
Las funciones que se irn presentando a continuacin han sido resumidad para dejar slo
aquellos parmetros que determinan el proceso de comunicacin.
HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDistribution,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);
ARG. ENTRADA
lpFileName
dwDesiredAcces
dwShareMode
lpSecurityAttributes
dwCreationDistribution
dwFlagsAndAttributes
hTemplateFile
TIPO SALIDA
HANDLE
DESCRIPCIN
Nombre del puerto COM1, COM2 etc
Tipo de acceso GENERIC_READ, GENERIC_WRITE etc
Modo de compartir. Debe ser 0 al no poderse compartir el
puerto serie por dos aplicaciones de forma simultnea.
Descriptor de seguridad. Debe ser 0
Especifica que accin se realizar si el fichero existe o si no
existe: CREATE_NEW, OPEN_EXISTING etc. Puesto que un
puerto siempre tiene que existir usaremos OPEN_EXISTING
Especifica el tipo de atributo del fichero/recurso, debe de ser
FILE_ATRIBUTE_NORMAL para los puertos.
Debe ser NULL
DESCRIPCIN
Manejador o Handle del puerto. Un puntero necesario para
acceder al puerto, o NULL si la operacin de apertura fall: No
existe el puerto, ocupado etc.
Si se desea conocer los tipos de datos definidos en WINDOWS para saber exactamente a que
tipos bsicos equivalen bastar con revisar el fichero WINDEF.H que se encuentra en el
directorio INCLUDE de C++ BUILDER.
Por ejemplo el siguiente fragmento abre el puerto COM1 .
..
HADLE hComPor;
hComPor=CreateFile(COM1,
GENERIC_READ|GENERIC_WRITE,
0,0,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,0 );
if (hComPor==INVALID_HANDLE_VALUE)
{
//Error de apertura del puerto
}
Una vez finalizado el trabajo con el puerto hemos de cerrar su manejador para que el sistema
operativo libere los recursos que estamos ocupando, esto se hace mediante la funcin
CloseHandle(), esta funcin devuelve TRUE si el cierre del dispostivo se hizo correctamente,
es importante no tratar de cerrar un manejador no vlido, es decir un manejador no inicializado
correctamente por CreateFile() .
ARG. ENTRADA
hObject
TIPO SALIDA
BOOL
CloseHandle()
BOOL CloseHandle(HANDLE hObject);
DESCRIPCIN
Manejador o handle genrico.
DESCRIPCIN
TRUE si la funcin se ejecut correctamente.
CAMPO
DCBlength
BaudRate
fBinary:1
fParity: 1
fOutxCtsFlow:1
TIPO
DWORD
DWORD
BIT
BIT
BIT
fOutxDsrFlow:1
BIT
fDtrControl:2
2 BITS
fDsrSensitivity:1
BIT
fTXContinueOnXoff:1
BIT
fOutX: 1
BIT
fInX: 1
BIT
fErrorChar: 1
BIT
fNull: 1
fRtsControl:2
DWORD
2 BITS
fAbortOnError:1
BIT
fDummy2:17
wReserved
XonLim
17 BITS
WORD
WORD
XoffLim
WORD
ByteSize
Parity
StopBits
XonChar
XoffChar
ErrorChar
EofChar
EvtChar
wReserved1
BYTE
BYTE
BYTE
char
char
char
char
char
WORD
DESCRIPCIN
Tamao de la estructura DCB
Velocidad en bps: CBR_100,CBR_300,CBR_600 ...CBR_560000..
Modo binario. Debe de ser TRUE
Si TRUE se chequearn errores de paridad
Si TRUE, se usa CTS para controlar el flujo entre el DTE y el DCE. Si CTS se
desactiva el DTE dejar de transmitir hasta que se active de nuevo.
Si TRUE, se usa DSRpara controlar el flujo entre el DTE y el DCE. Si DSR se
desactiva el DTE dejar de transmitir hasta que se active de nuevo.
Especifica como se manejara la lnea DTR de salida del DTE. Valores posibles:
DTR_CONTROL_ENABLE : Se activar DTR al abrir el puerto.
DTR_CONTROL_DISABLE: Se desactivar DTR al abrit el puerto
DTR_CONTROL_HANDSHAKE: Habilita el control de flujo hardware DTR-DSR
En los dos primeros casos se puede utilizar la funcin EscapeCommFunction()
para cambiar el estado de la lnea DTR
Si TRUE se descartn los datos recibidos mientras la lnea DSR (entrada al DTE)
este inactiva.
Configura el modo de operar si se llena el buffer de entrada y ya se ha transmitido
el caracter XOFF. Si FALSE la transmisin se parar despus de recibir XOFF, se
reanudar si se recibe un XON o bien se vaca el BUFFER del receptor por debajo
del lmite inferior XOnLimit.
Si TRUE habilita el control de flujo XON/XOFF durante la transmisin .La
transmisin cesar al recibirse un XOFF, se reanudar al recibirse un XON
Si TRUE habilita el control de flujo XON/XOFF durante la recepcin .Se enviar el
caracter XOFF si el BUFFER del receptor alcance el lmite XoffLimit. Se enviar
el caracter XON si el BUFFER baja se vacia por debajo de XonLimit.
Si TRUE se sustituirn todos los caracteres recibido con errores por el dato
ErrorChar definido en esta misma estructura.
Si TRUE se descartar los BYTES recibidos que sean nulos.
Configura como se manejar el control de flujo mediante la lnea RTS de salida
del DTE. Los valores posibles son:
RTS_CONTROL_DISABLE: Se mantendr RTS inactiva
RTS_CONTROL_ENABLE: Se mantendr RTS activa
RTS_CONTROL_HANDSHAKE: Habilita el control de flujo hardware RTS-CTS
RTS_CONTROL_TOGGLE: Se activa mientras el BUFFER de transmisin tenga
datos para transmitir.
En los dos primeros casos se puede utilizar la funcin EscapeCommFunction()
para cambiar el estado de la lnea RTS
Si TRUE se abortar cualquier operacin de lectura o escritura hasta que se llame
explicitamente a ClearCommError().
No se usan
No se usan
Determina el lmite inferior del BUFFER del receptor que fuerza la transmisin de
XON
Determina el lmite superiro del BUFFER del receptor que fuerza la transmisin de
XOFF
Determina el nmero de bits de datos: 4,5,6,7,8
Paridad: NOPARITY, EVENPARITY(par),MARKPARITY o ODDPARITY (impar)
Bist de Stop: ONESTOPBIT, ONE5STOPBITS,TWOSTOPBITS
Fija el caracter que se usar como XON
Fija el caracter que se usar como XOFF
Fija el caracter que se usar para sustituir un datos recibido erroneamente.
Fija el caracter que se usar para forzar el final de la transmisin
Fija el caracter que se usar para forzar el lanzamiento de un evento
Reservado
La estructura anterior permite un control total sobre la configuracin del puerto: tipo de SDU,
tipo de control de flujo, parmetros del control de flujo etc. Para trabajar con la estructura DCB
disponemos de trs funciones GetCommState() para leer la configuracin actual del puerto,
SetCommState() para configurar el puerto y BuildCommDCB() para configurar el puerto
utilizando el estilo clsico del comando MODE del DOS.
GetCommState()
BOOL GetCommState(HANDLE hFile,LPDCB lpDCB);
ARG. ENTRADA
DESCRIPCIN
hFile
Manejador del puerto o Handle devuelto por CreateFile()
lpDCB
Puntero a una estructura de tipo DCB donde se recibir la
configuracn actual del puerto.
TIPO SALIDA
DESCRIPCIN
BOOL
TRUE si la funcin se ejecut correctamente.
SetCommState()
BOOL SetCommState(HANDLE hFile,LPDCB lpDCB)
ARG. ENTRADA
DESCRIPCIN
hFile
Manejador del puerto o Handle devuelto por CreateFile()
lpDCB
Puntero a una estructura de tipo DCB que se usar para
configurar el puerto.
TIPO SALIDA
DESCRIPCIN
BOOL
TRUE si la funcin se ejecut correctamente.
BuildCommDCB()
BOOL BuildCommDCB(LPCTSTR lpDef,LPDCB lpDCB)
ARG. ENTRADA
DESCRIPCIN
lpDef
Puntero a una cadena al estilo del comando MODE con la
configuracin a fijar, por ejemplo:
baud=9600 parity=N data=8 stop=1
lpDCB
Puntero a una estructura de tipo DCB donde se recibir la
configuracin completa del puerto.
TIPO SALIDA
DESCRIPCIN
BOOL
TRUE si la funcin se ejecut correctamente.
Para probar la configuracin del puerto usaremos una aplicacin de cnsola que configure el
puerto a 9600-N-8-1. Mediante Proyect-New-Console App creamos un nuevo proyecto en un
nuevo directorio llamado PROYECT1.CPP. Para poder tener acceso a las funciones del API de
windows ser necesario incluir el fihero WINDOWS.H .
//----------------------------------#include <vcl\condefs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <iostream.h>
#pragma hdrstop
//----------------------------------USERES("Project1.res");
//----------------------------------int main(int argc, char **argv)
{
BOOL lOk;
HANDLE hCom;
// Manejador del puerto
DCB
sComCfg; // Estructura DCB
char cTec;
while (true)
{
// Abrir el puerto
hCom=CreateFile("COM1",
GENERIC_READ|GENERIC_WRITE,
0,0,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,0);
// Si error finalizar
if (hCom==INVALID_HANDLE_VALUE)
{
cout<<"ERROR al abrir el puerto";
cout<<GetLastError();
break;
}
// Leer la configuracin actual
lOk=GetCommState(hCom,&sComCfg);
if (!lOk)
{
cout<<"ERROR al leer la configuracin";
cout<<GetLastError();
break;
};
// Cambiar la configuracin a 9600-N-8-1
sComCfg.BaudRate=CBR_9600;
sComCfg.ByteSize=8;
sComCfg.Parity=NOPARITY;
sComCfg.StopBits=ONESTOPBIT;
// Escribir la nueva configuracin
lOk=SetCommState(hCom,&sComCfg);
if (!lOk)
{
cout<<"ERROR al escribir la configuracin";
cout<<GetLastError();
break;
};
cout<<"\n El puerto est abierto";
cout<<"\n y configurado a 9600-N-8-1";
cout<<"\n Pulse una tecla";
cin>>cTec;
// Salir del bucle principal
break;
}
if (hCom!=INVALID_HANDLE_VALUE)
CloseHandle(hCom);
return 0;
}
Este empieza abriendo el puerto, a continuacin se lee se lee la configuracin actual mediante
GetCommState(), se cambian los miembros de la estructura que se necesitan y se graba la
nueva configuracin en el puerto mediante SetCommState() . El bucle principal se utiliza para
cancelar la ejecucin del programa ante cualquier error. Finalmente se cierra el puerto slo si
se abri correctamente.
<FIGURA
1. SALIDA DE LA CONFIGURACIN>
La funcin GetLastError() sirve para obtener el cdigo del error que se ha producido, estos
cdigos de error se encuentran definidos en el fichero WINNT.H .
ARG. ENTRADA
No hay
TIPO SALIDA
DWORD
GetLastError()
DWORD GetLastError(VOID)
DESCRIPCIN
DESCRIPCIN
Cdigo del ltimo error producido
Para utilizar la funcin BuilCommDCB() se deber de declarar una estructur DCB y pasarla
como parmetro por referencia a la funcin. La funcin rellenar los campos de la estructura y
la podremos utilizar entonces para configurar el puerto con SetCommState().
..
HANDLE hCom;
// Manejador del puerto
DCB
sComCfg; // Estructura DCB
// Abrir el puerto con CreateFile()
CreateFile(..
// Construir una estructura DCB
if (BuidCommDCB(baud=9600 parity=N data=8 stop=1, &sComCfg) )
{
// Configurar el puerto
SetComState(hCom,&sComCfg);
};
..
Como ejemplo de uso, el siguiente cdigo activa y desactiva la lnea RTS cada segundo.
.. Abrir y configurar el puerto
lOn=FALSE;
while (!kbhit())
{
if (lOn)
EscapeCommFunction(hCom,CLRRTS);
if (!lOn)
EscapeCommFunction(hCom,SETRTS);
Sleep(1000);
lOn=!lOn;
}
.. Cerrar el puerto
Para suspender y reanudar la transmisin se puede utilizar la funcin anterior con los
parmetros SETBREAK y CLRBREAK o bien utilizar las funciones equivalentes
SetCommBreak() y ClearCommBreak(), ambas slo requieren el manejador del puerto y
retornan TRUE si la operacin se complet. Si se desean leer las lneas de estado que
proceden del DCE se puede usar la funcin GetCommModemStatus().
GetCommModemStatus()
BOOL GetCommModemStatus(HANDLE hFile,LPDWORD lpModemStat);
ARG. ENTRADA
DESCRIPCIN
hFile
Manejador del puerto o Handle devuelto por CreateFile()
lpModemStat
Un puntero a una variable DWORD donde se escribir la
configuaracin del registro de estado del mode, segn los
siguientes valores.
MS_CTS_ON
La seal CTS est activa
MS_DSR_ON
La seal DSR est activa
MS_RING_ON
La seal RING est activa
MS_RLSD_ON
La seal CD est activa
TIPO SALIDA
DESCRIPCIN
BOOL
TRUE si la funcin se ejecut correctamente.
El siguiente cdigo detecta si el DTE tiene un DCE conectado y preparado para aceptar datos.
..
DWORD dwEst;
.. Abrir y configurar el puerto
if (GetCommModemStatus(hCom,&dwErr) )
{
if ( (dwErr&MS_CTS_ON) && (dwErr&MS_DSR_ON) )
cout<<Hay un DCE preparado;
}
.. Cerrar el puerto
Se puede leer ms informacin sobre el estado del puerto serie y comprobar si se ha producido
un error y el tipo de error es ClearCommError() . Esta funcin adems de leer el tipo de error
que se ha producido sirve para resetear el flag de error del puerto, en determinados modos de
funcionamiento (fAbortOnError de la estructura DCB), el puerto deja de trabajar hasta que no
se llama a estra funcin despus de haberse producido un error.
ClearCommError()
CAMPO
fCtsHold : 1
fDsrHold : 1
fRlsdHold : 1
fXoffHold : 1;
fXoffSent : 1;
fEof : 1;
fTxim : 1;
DWORD fReserved :
25;
DWORD cbInQue;
DWORD cbOutQue;
..
COMSTAT sComSta;
DWORD
dwErr;
..
// Abrir y configuar el puerto
if( ClearCommError(hCom,&dwErr,&sComSta))
{
cout<<"Bytes en BUFFER RX:"<<sComSta.cbInQue;
if ( (dwErr&CE_BREAK)==CE_BREAK)
cout<<"\n Detectado corte en de lnea";
}
// Cerrar el puerto
El tamao de los BUFFERS de entrada y salida deber de ser acorde con el tamao de la
trama que se est manejando, si se utiliza una trama de 512 Bytes el BUFFER deber tener al
menos ese tamao.
Cuando se abre el puerto se crean los buffers de un tamao predeterminado, se puede conocer
el tamao actual de los BUFFERS mediante la funcin GetCommPropeties(), la cual
suministra una estructura COMMPROP con las propiedades del puerto dos de los campos de
esta estructura son dwCurrentTxQueue y dwCurrentRxQueue que informan del tamao total
del BUFFER del transmisor y del receptor respectivamente, esta funcin requiere que el puerto
est abierto y configurado.
GetCommProperties()
BOOL GetCommProperties(HANDLE hFile,LPCOMMPROP lpCommProp)
ARG. ENTRADA
DESCRIPCIN
hFile
Manejador del puerto o Handle devuelto por CreateFile()
lpCommProp
Puntero a un estructura COMMPROP donde la funcin
depositar informacin sobre las caractersticas del puerto.
TIPO SALIDA
DESCRIPCIN
BOOL
TRUE si la funcin se ejecut correctamente.
Podemos monitorizar los BYTES pendientes de ser enviados o los que esten pendientes de
ser leidos mediante los campos cbInQue y cbOutQue de la estructura COMSTAT vista
anteriormente. Podemos adems cambiar el tamao de estos BUFFERS mediante la funcin
SetupComm().
SetupComm()
BOOL SetupComm(HANDLE hFile,DWORD dwInQueue,DWORD dwOutQueue)
ARG. ENTRADA
DESCRIPCIN
hFile
Manejador del puerto o Handle devuelto por CreateFile()
dwInQueue
Tamao que se desea para el BUFFER del receptor
dwOutQueue
Tamao que se desea para el BUFFER del transmisor
TIPO SALIDA
DESCRIPCIN
BOOL
TRUE si la funcin se ejecut correctamente.
Esta funcin se llamar normalmente despus de la apertura del puerto antes de cualquier
operacin de lectura o escritura. El siguiente ejemplo es una aplicacin de cnsola que muestra
como usar estas funciones.
//----------------------------------#include <vcl\condefs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <iostream.h>
#include <conio.h>
#pragma hdrstop
//------------------------------------USERES("Project1.res");
//------------------------------------int main(int argc, char **argv)
{
HANDLE
hCom;
// Manejador del puerto
DCB
sComCfg;// Estructura configuracin
COMMPROP sComPro;// Estructura propiedades
COMSTAT sComSta;// Estado del puerto
DWORD
dwErr;
// Estado del error
// Bucle para controlar errores
while (true)
{
// Abrir el puerto
hCom=CreateFile("COM1",
GENERIC_READ|GENERIC_WRITE,
0,0,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,0);
// Si error finalizar
if (hCom==INVALID_HANDLE_VALUE) break;
// Leer configuracin actual
if (!GetCommState(hCom,&sComCfg)) break;
// Cambiar configuracin
sComCfg.BaudRate=CBR_9600;
sComCfg.ByteSize=8;
sComCfg.Parity=NOPARITY;
sComCfg.StopBits=ONESTOPBIT;
// Escribir nueva configuracin
if (!SetCommState(hCom,&sComCfg)) break;
// Fijar tamao de los BUFFERS del TX y RX
if (!SetupComm(hCom,2048,1024)) break;
// Leer las propiedades del puerto
if (!GetCommProperties(hCom,&sComPro)) break;
// Leer estado del puerto
if (!ClearCommError(hCom,&dwErr,&sComSta)) break;
// Mostrar tamao Buffer
cout<<"\n Tamao BUFFER TX:"<<
sComPro.dwCurrentTxQueue;
cout<<"\n Tamao BUFFER RX:"<<
sComPro.dwCurrentRxQueue;
// Mostrar tamao ocupado
cout<<"\n Ocupado BUFFER TX:"<<
sComSta.cbInQue;
cout<<"\n Ocupado BUFFER RX:"<<
sComSta.cbOutQue;
getch();
// Salir del bucle principal
break;
}
// Cerrar el puerto
if (hCom!=INVALID_HANDLE_VALUE)
CloseHandle(hCom);
return 0;
}
En el ejemplo se configura el puerto para un BUFFER del receptro a 2048 bytes y el BUFFER
del transmisor a 1024 BYTES. Este modo de operacin puede ser un problema cuando
queremos enviar un carcter de control de forma inmediata, en este caso el dato sera aadido
al final del BUFFER y se transmitira despus de los caracteres que estuvieran pendienetes.
Para el envio inmedianto de un carcter se dispone de la funcin TransmitCommChar().
TransmitCommChar()
BOOL TransmitCommChar(HANDLE hFile,char cChar);
ARG. ENTRADA
DESCRIPCIN
hFile
Manejador del puerto o Handle devuelto por CreateFile()
cChar
Carcter a envias
TIPO SALIDA
DESCRIPCIN
BOOL
TRUE si la funcin se ejecut correctamente.
Si en algn momento se desaear borrar los bytes que quedan remanentes en alguno de los dos
BUFFERS se pude utiliza la funcin PurgeComm() , esta operacin hace que se pierda el
contendido del BUFFER del transmisor sin ser transmitido realmente o que se pierda el
contenido del BUFFER del receptor sin que sea guardadp por el programa.
PurgeCommr()
BOOL PurgeComm(HANDLE hFile,DWORD dwFlags);
ARG. ENTRADA
DESCRIPCIN
hFile
Manejador del puerto o Handle devuelto por CreateFile()
dwFlags
Es uno o combinacin de los siguiente valores:
PURGE_TXABORT o PURGE_TXCLEAR para limpia el
BUFFER del transmisor
PURGE_RXABORT o PURGE_RXCLEAR para limpiar el
BUFFER del receptor
TIPO SALIDA
DESCRIPCIN
BOOL
TRUE si la funcin se ejecut correctamente.
Si queremos vaciar el BUFFER del transmisor, pero garantizando que se transmite todo su
contenido deberemos de usar la funcin FlushFileBuffers() .
10.4. LEER Y ESCRIBIR EN PUERTO
Para leer y escribir en el puerto se utilizan las funciones genricas ReadFile() y WriteFile(),
ambas son funciones genricas que permiten leer o escribir ficheros, puertos etc. En la tabla
siguiente se muestra una descripcin de cada una de ellas
ReadFile()
BOOL ReadFile(HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped);
ARG. ENTRADA
DESCRIPCIN
hFile
Manejador del puerto o Handle devuelto por CreateFile()
lpBuffer
Puntero al buffer donde la funcin depositar los datos leidos.
nNumberOfByteToRead Nmero de bytes que se desean leer
lpNumberOfByteRead
Puntero a una DWORD donde la funcin indicar los datos
que realmente se han lido.
lpOverlapped
Puntero a una estructura OVERLAPPED para acceso a
especial a puerto.
TIPO SALIDA
DESCRIPCIN
BOOL
TRUE si la funcin se ejecut correctamente.
SetCommTimeoust()
BOOL SetCommTimeouts(HANDLE hFile,LPCOMMTIMEOUTS lpCommTimeouts)
ARG. ENTRADA
DESCRIPCIN
hFile
Manejador del puerto o Handle devuelto por CreateFile()
lpCommTimeouts
Puntero a una estructura de tipo COMMTIMEOUTS que se
usar para escribir la configurar del puerto.
TIPO SALIDA
DESCRIPCIN
BOOL
TRUE si la funcin se ejecut correctamente.
El siguiente programa se utiliza una aplicacin de cnsola para transmitir por el puerto una
cadena de caracteres.
//------------------------------------#include <vcl\condefs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <iostream.h>
#include <conio.h>
#pragma hdrstop
//------------------------------------USERES("Project1.res");
//------------------------------------int main(int argc, char **argv)
{
HANDLE
hCom;
// Manejador del puerto
DCB
sComCfg;// Estructura configuracin
COMMPROP sComPro;// Estructura propiedades
COMSTAT sComSta;// Estado del puerto
DWORD
dwErr;
// Estado del error
COMMTIMEOUTS sTimOut;
BYTE
acBuf[32]; // Un buffer
DWORD
dwBytWri; //
DWORD
dwLen;
// Bucle para controlar errores
while (true)
{
// Abrir el puerto
hCom=CreateFile("COM1",
GENERIC_READ|GENERIC_WRITE,
0,0,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,0);
// Si error finalizar
if (hCom==INVALID_HANDLE_VALUE) break;
// Leer configuracin actual
if (!GetCommState(hCom,&sComCfg)) break;
// Cambiar configuracin
sComCfg.BaudRate=CBR_9600;
sComCfg.ByteSize=8;
sComCfg.Parity=NOPARITY;
sComCfg.StopBits=ONESTOPBIT;
sComCfg.fRtsControl=RTS_CONTROL_ENABLE;
sComCfg.fDtrControl=DTR_CONTROL_ENABLE;
// Escribir nueva configuracin
if (!SetCommState(hCom,&sComCfg)) break;
// Configurar TIMEOUTS
sTimOut.ReadIntervalTimeout=0;
sTimOut.ReadTotalTimeoutMultiplier=0;
sTimOut.ReadTotalTimeoutConstant=0;
sTimOut.WriteTotalTimeoutMultiplier=0;
sTimOut.WriteTotalTimeoutConstant=0;
sTimOut.WriteTotalTimeoutConstant=0;
if (!SetCommTimeouts(hCom,&sTimOut)) break;
// Contruimos la cadena a enviar
strcpy(acBuf,"Hola Mundo");
dwLen=strlen(acBuf);
while (!kbhit())
{
if ( WriteFile(hCom,acBuf,dwLen,&dwBytWri,0) )
cout<<"\nTx..:"<<acBuf ;
}
// Salir del bucle principal
break;
}
// Cerrar el puerto
if (hCom!=INVALID_HANDLE_VALUE)
CloseHandle(hCom);
return 0;
}
En este ejemplo se transmite la cadena, despus se borra y se lee del puerto un nmero de
byte igual a la cadena que se envi. Finalmente, se le aade el carcter de fin de cadena \0
para convertir el buffer leido del puerto acBuf en una cadena.
<FIGURA 4. RECIBIR>
Tal y como se ha configurado el TIMEOUT, si se ejecuta ReadFile() y no hay datos en el
BUFFER la funcin esperar de forma indefinida la llegada de nuevos datos. Si lo que
queremos es que la funcin finalice inmediatamente debemos configuar el TIMEOUT como se
muestra a continuacin.
sTimOut.ReadIntervalTimeout=MAXDWORD;
sTimOut.ReadTotalTimeoutMultiplier=0;
sTimOut.ReadTotalTimeoutConstant=0;
sTimOut.WriteTotalTimeoutMultiplier=0;
sTimOut.WriteTotalTimeoutConstant=0;
sTimOut.WriteTotalTimeoutConstant=0;
En este caso, habr que revisar el proceso de lectura, ya que la funcin finalizar
inmediatamente despus de llamarla y puede ocurrir que no haya leido ningn dato. Otra
solucin par evitar este problema es comprobar que hay datos en el BUFFER del receptor
mediante el campo cbInQue de la estructura COMSTAT antes de usar ReadFile() . Si se opta
por utilizar la primera de estas tcnicas el programa quedar como se muestra a continuacin.
//-------------------------------------------int main(int argc, char **argv)
{
HANDLE
hCom;
// Manejador del puerto
DCB
sComCfg;// Estructura configuracin
COMMPROP sComPro;// Estructura propiedades
COMSTAT sComSta;// Estado del puerto
DWORD
dwErr;
// Estado del error
COMMTIMEOUTS sTimOut;
BYTE
acBuf[32]; // Un buffer
BYTE
acBufRx[32];
DWORD
dwBytWri; // Bytes escritos
DWORD
dwBytRea; // Bytes leidos
DWORD
dwTotBytRea;
DWORD
dwLen;
// Bucle para controlar errores
while (true)
{
// Abrir el puerto
hCom=CreateFile("COM1",
GENERIC_READ|GENERIC_WRITE,
0,0,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,0);
// Si error finalizar
if (hCom==INVALID_HANDLE_VALUE) break;
// Leer configuracin actual
if (!GetCommState(hCom,&sComCfg)) break;
// Cambiar configuracin
sComCfg.BaudRate=CBR_9600;
sComCfg.ByteSize=8;
sComCfg.Parity=NOPARITY;
sComCfg.StopBits=ONESTOPBIT;
sComCfg.fRtsControl=RTS_CONTROL_ENABLE;
sComCfg.fDtrControl=DTR_CONTROL_ENABLE;
// Escribir nueva configuracin
if (!SetCommState(hCom,&sComCfg)) break;
// Configurar TIMEOUTS
// Para que la operacin de lectura
// finalice inmediatamente
sTimOut.ReadIntervalTimeout=MAXDWORD;
sTimOut.ReadTotalTimeoutMultiplier=0;
sTimOut.ReadTotalTimeoutConstant=0;
sTimOut.WriteTotalTimeoutMultiplier=0;
sTimOut.WriteTotalTimeoutConstant=0;
sTimOut.WriteTotalTimeoutConstant=0;
if (!SetCommTimeouts(hCom,&sTimOut)) break;
// Contruimos la cadena a enviar
strcpy(acBuf,"Hola Mundo");
dwLen=strlen(acBuf);
// Transmitimos la cadena
if (!WriteFile(hCom,acBuf,
dwLen,&dwBytWri,0)) break;
cout<<"\nTx..:"<<acBuf ;
strcpy(acBuf,"");
strcpy(acBufRx,"");
while (true)
{
if (!ReadFile(hCom,acBuf,
dwLen,&dwBytRea,0)) break;
// Si se ha leido algo
if (dwBytRea>0)
{
acBuf[dwBytRea]='\0';
// Cuento el total leido
dwTotBytRea=dwTotBytRea+dwBytRea;
// Encadeno la informacin leida
strcat(acBufRx,acBuf);
// Salir si se ley toda
if (dwTotBytRea>=dwLen) break;
}
}
cout<<"\nRx...:"<<acBufRx;
cout<<"\n Pulse una tecla";
getch();
// Salir del bucle principal
break;
}
// Cerrar el puerto
if (hCom!=INVALID_HANDLE_VALUE)
CloseHandle(hCom);
return 0;
}
Como se observa para recibir utilizamos llamadas repetidas a la funcin ReadFile() dentro de
un bucle, cuando una llamada lee algo del BUFFER se contruye una cadena en acBuf que se
va encadenado a la cadena total acBufRx . El bucle finaliza cuando se lee toda la cadena. Este
bucle presenta el problema de que se convierte en un bucle infinito si no se leen dwLen bytes
del puerto, esto se puede solucionar utilizando tcnicas de control de tiempo como las que se
vieron en la programacin DOS.
10.5. CLASE TWinSerCom PARA COMUNICACIONES BAJO WINDOWS
Como ejemplo de utilizacin de estas funciones se va mostrar una clase TWinSerCom
similar a la que se vi en MS-DOS para envio y recepcin de bytes y tramas. En un proyecto
nuevo creamos una nueva unidad con File-New-Unit esta la salvaremos con el nombre de
PORTCOM.CPP y estar compuesta por dos ficheros, el anterior ms el fichero PORTCOM.H.
En el fichero PORTCOM.H escribiremos el interface de la clase
//---------------------------------------// Fichero....:PORTCOM.H
// Descripcin:Declaracin clase TWINSERCOM
// Permite transmitir y recibir por sondeo
// mediante el API WIN32
// Autor......: PMR 1999
//----------------------------------------#include <windows.h>
//----------Declaracin de la clase
class TWinSerCom
{
protected :
int iNumCom;
// Nmero de puerto
bool lOpen;
// Abierto ?
HANDLE
hCom;
// Manejador del puerto
DCB
sComCfg; // Estructura configuracin
COMSTAT sComSta; // Estado del puerto
DWORD
dwErr;
// Errores del puerto
DWORD
dwTamBufIn;
DWORD
dwTamBufOut;
DWORD
dwEvtMask; // Mscara de eventos
COMMTIMEOUTS sTimOut;
public :
TWinSerCom(int iPor); // Constructor
~TWinSerCom();
// Destructor
bool Open(void);
// Abrir puerto
bool Open(int iPor); // Abrir en otro
void Close(void);
// Cerrarlo
bool IsOpen(void);
// Leer estado
bool IsDCE(void);
// Leer si DCE
bool RxByte(BYTE &bDat); // Tx BYTE
bool TxByte(BYTE bDat); // Rx BYTE
bool RxCad(char *pCad,DWORD dwLen); // Tx Cadena
bool TxCad(char *pCad);
// Rx Cadena
DWORD LeerError(void);
// Leer el error
bool SetEventos(DWORD dwDat);// Fijar eventos
DWORD EsperaEventos(void); // Esperar un evento
HANDLE LeeIde(void); // Leer Handle de COMx
};
{return hCom;}
//------------------Leer si DCE conectado
bool TWinSerCom::IsDCE(void)
{
bool lRes=FALSE;
while (true)
{
if (!lOpen) break;
if (!GetCommModemStatus(hCom,&dwErr))break;
if ((dwErr&MS_CTS_ON) && (dwErr&MS_DSR_ON))
lRes=TRUE;
break;
}
return lRes;
}
//------------------Leer si DCE conectado
DWORD TWinSerCom::LeerError(void)
{
ClearCommError(hCom,&dwErr,&sComSta);
return dwErr;
}
//------------------Fijar eventos
bool TWinSerCom::SetEventos(DWORD dwDat)
{
bool lRes=true;
dwEvtMask=dwDat;
if (!SetCommMask(hCom,dwEvtMask))
lRes=false;
return lRes;
}
//------------------Esperar evento
DWORD TWinSerCom::EsperaEventos(void)
{
// Esperar los eventos programados
// Aqu el proceso se quedar parado
// hasta que se produzca uno de los eventos
WaitCommEvent(hCom,&dwEvtMask,0);
return dwEvtMask;
}
//-------------------------Abrir el puerto
bool TWinSerCom::Open(int iPor)
{
if (lOpen) Close();
iNumCom=iPor;
return Open();
}
//-------------------------Abrir el puerto
bool TWinSerCom::Open(void)
{
char acCom[5]; // Para la cadena del puerto
while(TRUE)
{
// Si ya est abierto cancelar
if (lOpen) break;
lOpen=false;
// Abrir el puerto
sprintf(acCom,"COM%d",iNumCom);
hCom=NULL;
hCom=CreateFile(acCom,
GENERIC_READ|GENERIC_WRITE,
0,0,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,0);
// Si falla cancelar
if (hCom==INVALID_HANDLE_VALUE) break;
// Leer CFG actual
if (!GetCommState(hCom,&sComCfg)) break;
// Cambiar configuracin
sComCfg.BaudRate=CBR_9600;
sComCfg.ByteSize=8;
sComCfg.Parity=NOPARITY;
sComCfg.StopBits=ONESTOPBIT;
sComCfg.fRtsControl=RTS_CONTROL_ENABLE;
sComCfg.fDtrControl=DTR_CONTROL_ENABLE;
// Escribir nueva configuracin
if (!SetCommState(hCom,&sComCfg))
break;
if (!SetupComm(hCom,dwTamBufIn,dwTamBufOut))
break;
// Configurar TIMEOUTS
// Para que la operacin de lectura
// finalice inmediatamente
sTimOut.ReadIntervalTimeout=MAXDWORD;
sTimOut.ReadTotalTimeoutMultiplier=0;
sTimOut.ReadTotalTimeoutConstant=0;
sTimOut.WriteTotalTimeoutMultiplier=0;
sTimOut.WriteTotalTimeoutConstant=0;
sTimOut.WriteTotalTimeoutConstant=0;
if (!SetCommTimeouts(hCom,&sTimOut))
break;
lOpen=TRUE;
break;
};
return lOpen;
};
//---------------------------Cerrar el puerto
void TWinSerCom::Close(void)
{
if (hCom!=INVALID_HANDLE_VALUE)
{
// Limpiar el BUFFER
PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);
// Cerrar el puerto
CloseHandle(hCom);
}
lOpen=FALSE;
return;
};
//--------------- Transmisin
bool TWinSerCom::TxByte(BYTE bDat)
{
DWORD dwBytWri; // Bytes escritos
if (lOpen)
{
// Transmitimos un BYTE
if (WriteFile(hCom,&bDat,1,&dwBytWri,0))
if (dwBytWri==1) return TRUE;
}
return FALSE;
};
//-------------Recepcin
bool TWinSerCom::RxByte(BYTE &bDat)
{
DWORD dwBytRea; // Bytes leidos
BYTE bDatRx;
// Buffer para lo que se lea
if ( lOpen )
{
if (ReadFile(hCom,&bDatRx,1,&dwBytRea,0))
if (dwBytRea==1)
{
bDat=bDatRx;
return TRUE;
}
}
return FALSE;
};
//----------- Transmisin cadena
bool TWinSerCom::TxCad(char *pCad)
{
DWORD dwBytWri; // Bytes escritos
DWORD dwLen=strlen(pCad);
if (lOpen)
{
// Transmitimos la cadena
if (WriteFile(hCom,pCad,dwLen,&dwBytWri,0))
if (dwBytWri==dwLen) return TRUE;
}
return FALSE;
};
//-------------Recepcin cadena
bool TWinSerCom::RxCad(char *pCad,DWORD dwLen)
{
DWORD dwBytRea;
bool
lRes=FALSE;
while (TRUE)
{
// Si el puerto est cerrado cancelo
if (!lOpen) break;
// Si falla la lectura del estado cancelo
if (!ClearCommError(hCom,&dwErr,&sComSta))
break;
// Si en el BUFFER no hay todava el
// nro de bytes que se desean leer cancelo
if (sComSta.cbInQue<dwLen) break;
if (!ReadFile(hCom,pCad,dwLen,&dwBytRea,0))
break;
// Si no leo el nmero de bytes que se piden
if (dwBytRea!=dwLen) break;
// Contruir una cadena
*(pCad+dwBytRea)='\0';
lRes=TRUE;
break;
}
return lRes;
};
El constructor se encarga de guardar el puerto inicial y de asignar unos valores por defecto el
BUFFER del transmisor y del receptor. El mtodo Open() se encarga de abrir el puerto y
configurarlo a 9600-N-8-1 ,adems se configura el TIMEOUT para que las funciones de lectura
y escritura finalicen inmediatamente. El mtodo Close() se encarga de cerrar el puerto, puesto
que este mtodo es llamado por el destructor de la clase, cada vez que se borre un objeto de la
clase se cerrar el puerto, esto es util ya que si nos olvidamos de cerrar el puerto se cerrar de
forma automtica cuando finalice el programa. El mtodo TxByte() se encarga de escribir un
BYTE en el BUFFER del transmisor, si la funcin no falla retornar TRUE. El mtodo RxByte()
se encarga de leer del BUFFER del receptor un nico byte. El mtodo TxCad() recibe un
puntero a un BUFFER con la cadena a transmitir y la escribe en el BUFFER del transmisor sin
transmitir el carcter de terminacin de cadena \0. El mtodo RxCad() requiere un BUFFER
donde alojar los datos y el nmero de bytes que se desean leer, el mtodo testea el nmero de
bytes pendientes de ser leidos cbInque y si son suficientes se leen. El mtodo LeeIde()
permite leer el idendtificador del puerto con el que se trabaja, esto es muy util si deseamos
utilizar cualquier otra funcin del API de sobre un puerto ya abierto por la clase. Los mtodos
relacionados con los eventos se vern posteriormente.
Memo2->Lines->Add("");
}
//----------------------------------------void __fastcall TForm1::
RadioButton1Click(TObject *Sender)
{pCom->Open(1);}
//----------------------------------------void __fastcall TForm1::
RadioButton2Click(TObject *Sender)
{pCom->Open(2);}
//----------------------------------------void __fastcall TForm1::
RadioButton3Click(TObject *Sender)
{pCom->Open(3);}
//----------------------------------------void __fastcall TForm1::
RadioButton4Click(TObject *Sender)
{pCom->Open(4);}
//----------------------------------------void __fastcall TForm1::
Timer1Timer(TObject *Sender)
{
BYTE bDat;
int iLin;
// LED de puerto abierto
if (pCom->IsOpen())
Shape1->Brush->Color=clRed;
else
Shape1->Brush->Color=clWhite;
// LED de DCE desconectado
if (pCom->IsDCE())
Shape2->Brush->Color=clRed;
else
Shape2->Brush->Color=clWhite;
// Si se ha recibido un BYTE
if(pCom->RxByte(bDat) )
{
// Si es retorno de carro
if(bDat=='\r')
{
Memo2->Lines->Add("");
iLin++;
}
else
{
iLin=Memo2->Lines->Count-1;
Memo2->Lines->Strings[iLin]=
Memo2->Lines->Strings[iLin]+char(bDat);
}
};
}
//-------------------------------------------void __fastcall TForm1::
Memo1KeyPress(TObject *Sender, char &Key)
{
// Transmito
if (!pCom->TxByte(Key))
{
Application->MessageBox
("ERROR: No se ha podido transmitir",
"SerChat",MB_ICONERROR);
};
}
//----------------------------------------------
texto local Memo1 se controla cada pulsacin de tecla, si la transmisin falla se mostrar un
mensaje en pantalla.
<FIGURA 6.TRAMAS>
En la pantalla se puede escribir una cadena en el objeto Memo1 de la parte superior, al pulsar
el botn BitBtn1 se enviarn la cadena, entonces se esper el tiempo fijado por el objeto
TrackBar1 (este objeto tiene fijadas sus propiedades Min=100 y Max=3000) para leer el
nmero de bytes que se indican en el objeto MaskEdit1. En el ejemplo se ha conectado un
cable NULL-MODEM con FEED-BACK y se ha escrito un texto de 10 caracteres , se ha fijado
bytes de respuesta en 4 y se ha pulsado tres botn enviar. Para probar este ejemplo seguimos
los pasos indicados en el ejemplo anterior, la declaracin de la clase en UNIT.H es la
siguiente.
En este caso hay que hay que escribir la declaracin del puntero pCom y el prototipo de un
mtodo que se utilizar para refrescar los LEDS, ya que en este ejemplo no disponemos de
ningn timer.
//----------------------------------------//Programa: Unit1 del proyecto SERTRAMA.cpp
//Desc....: Envio y recepcin de tramas
//Autor...: PMR 1999
//-----------------------------------------
#include <vcl\vcl.h>
#pragma hdrstop
#include "Unit1.h"
//----------------------------------------#pragma resource "*.dfm"
TForm1 *Form1;
//-----------------------------------------__fastcall TForm1::
TForm1(TComponent* Owner): TForm(Owner)
{
pCom= new TWinSerCom(1);
Memo1->Clear();Memo2->Clear();
}
//-----------------------------------------void __fastcall TForm1::
RadioButton1Click(TObject *Sender)
{pCom->Open(1);RefrescaBotones();}
//-----------------------------------------void __fastcall TForm1::
RadioButton2Click(TObject *Sender)
{pCom->Open(2);RefrescaBotones();}
//-----------------------------------------void __fastcall TForm1::
RadioButton3Click(TObject *Sender)
{pCom->Open(3);RefrescaBotones();}
//-----------------------------------------void __fastcall TForm1::
RadioButton4Click(TObject *Sender)
{pCom->Open(4);RefrescaBotones();}
//------------------------------------------void __fastcall TForm1::
RefrescaBotones(void)
{
// LED de puerto abierto
if (pCom->IsOpen())
Shape1->Brush->Color=clRed;
else
Shape1->Brush->Color=clWhite;
// LED de DCE desconectado
if (pCom->IsDCE())
Shape2->Brush->Color=clRed;
else
Shape2->Brush->Color=clWhite;
}
//-----------------------------------------void __fastcall TForm1::
BitBtn1Click(TObject *Sender)
{
{
bool lResOk; // Respuesta OK
char acBufTx[132];
char acBufRx[132];
char acMsg[200];
int iTamTrama;
DWORD tTimIni,tEsp;
while (true)
{
// Transmitir y esperar respuesta
if(!pCom->IsOpen())
{
Application->MessageBox
("ERROR: El puerto est cerrado",
"SerTrama",
MB_ICONERROR);
break;
}
// Convertit a cadena
strcpy(acBufTx,
Memo1->Lines->Strings[0].c_str());
if(!pCom->TxCad(acBufTx)) // Transmitir
{
Application->MessageBox(
"ERROR: No se ha podido transmitir",
"SerTrama",MB_ICONERROR);
break;
}
sprintf(acMsg,"Tx..:%s",acBufTx);
Memo2->Lines->Add(acMsg);
// Esperar respuesta
iTamTrama=MaskEdit1->Text.ToInt();
if (iTamTrama<1 ||iTamTrama>131) break;
// Leer tiempo
tEsp=TrackBar1->Position;
lResOk=false;
// Bucle de espera de respuesta
strcpy(acBufRx,"");
tTimIni=GetTickCount();
while(GetTickCount()-tTimIni<tEsp)
{
if(pCom->RxCad(acBufRx,iTamTrama))
{
lResOk=true;
break;
}
};
// Testeo si hubo error al recibir
if(!lResOk)
{
Application->MessageBox(
"ERROR: No se ha podido recibir",
"SerTrama",MB_ICONERROR);
break;
}
else
{
sprintf(acMsg,"Rx..:%s",acBufRx);
Memo2->Lines->Add(acMsg);
}
Memo1->Lines->Clear();
// Salir del bucle principal
break;
}
}
}
//---------------------------------------------------------------------------
ARG. ENTRADA
void
TIPO SALIDA
DWORD
GetTickCount()
DWORD GetTickCount(VOID)
DESCRIPCIN
NINGUNO
DESCRIPCIN
Nmero de milisegundos transcurridos desde que se inici el
sistema operativo.
Mediante un bucle para controla el tiempo y una variable tipo lgica lResOk determinamos si
en el tiempo permitido se ha producido la lectura correcta.
10.8.CONTROL DEL PUERTO MEDIANTE EVENTOS
Los eventos del puerto serie son parecidos a las interruppciones en MS-DOS, se trata de no
tener que sondear el puerto para transmitir, recibir o controlar errores: que sea el puerto el que
avise al programa de estas situaciones mediante un evento o mensaje. Un programa puede
configurar el puerto para que el sistema operativo le enve un mensaje cuando se produza
algunos de los eventos que se muestran en la siguiente tabla.
Mtodo
EV_BREAK
EV_CTS
EV_DSR
EV_ERR
EV_RING
EV_RLSD
EV_RXCHAR
EV_RXFLAG
EV_TXEMPTY
El evento RLSD es util cuando se realizan comunicaciones con MODEM y se desea controlar la
posible prdida de portadora por errores de transmisin. El evento RXCHAR sirve para que el
sistema avise de hay datos pendientes de ser leidos en el buffer,si el tamao del BUFFER del
receptores uno se producir en cada carcter que llegue al puerto, no obstante, leer carcter a
carcter los bytes que van llegando al puerto es muy poco eficaz es mejor leer los datos
recibidos por bloques a travs del BUFFER de entrada. El evento RXFLAG es muy util para
recibir tramas que finalizan con un determinado carcter: suponer que deseamos recibir tramas
que finalizan con el carcter 0x0D. Si al abrir el puerto configuramos el miembro EvtChar de la
estructura DCB con dicho carcter 0x0D se producir un evento cada vez que se reciba una
trama completa.
En el API para el puerto serie existen trs funciones de aplicacin GetCommMask(),
SetCommMask() y WaitCommEvent(). Las dos primeras permiten leer o fijar la mscara de
eventos, es decir habilitar o iniohibir determinados sucesos, los eventos posibles son los
siguientes. Los prototipos de estas funciones son las siguientes.
GetCommMask()
BOOL GetCommMask(HANDLE hFile,LPDWORD lpEvtMask)
ARG. ENTRADA
DESCRIPCIN
hFile
Manejador del puerto o Handle devuelto por CreateFile()
LPDWORD
Puntero a un DWORD donde la funcin depositar la mscara
de eventos.
TIPO SALIDA
DESCRIPCIN
BOOL
TRUE si la funcin se ejecut correctamente.
SetCommMask()
BOOL SetCommMask(HANDLE hFile,DWORD lpEvtMask)
ARG. ENTRADA
DESCRIPCIN
hFile
Manejador del puerto o Handle devuelto por CreateFile()
DWORD
Un DWORD con la nueva mscara de eventos que se desea
asociar al puerto
TIPO SALIDA
DESCRIPCIN
BOOL
TRUE si la funcin se ejecut correctamente.
El siguiente programa configura la mscara de eventos para que se aada el evento EV_ERR
WaitCommEvent()
BOOL WaitCommEvent(HANDLE hFile,DWORD lpEvtMask)
ARG. ENTRADA
DESCRIPCIN
hFile
Manejador del puerto o Handle devuelto por CreateFile()
LPDWORD
Puntero a un DWORD con la mscara de los eventos que
hace que finalice la funcin
LPOverlapped
Puntero a una estructura de sobrecarga (0 si no se utiliza)
TIPO SALIDA
DESCRIPCIN
BOOL
TRUE si la funcin se ejecut correctamente.
El siguiente ejemplo es una versin bajo windows del contador serie que se vi en MS-DOS, en
la figura, la ventana DOS transmite a travs del puerto COM1 y la aplicacin WINDOWS recibe
a travs del COM4, se ha utilizado un cable NULL-MODEM entre COM1 y COM4.
break;
}; // Fin del while
pCom->Close();
};
//----------------------------------------------------
Para manejar el puerto se utiliza el puntero pCom de la clase de comunicaciones, este puntero
se inicializa en el constructor del formulario. Cuano se pulsa el botn se ejecuta el mtodo
Button1Click el cual abre el puerto seleccionado y configura la mscara de eventos para
detectar los bytes que se reciban o los errores que se produzcan. Cuando se alcanza la funcin
WaitCommEvent() el programa se queda congelado esperando uno de los dos eventos
programados, a continuacin se determina si se finaliz por error o por la entrada de un nuevo
byte. Los datos que se leen se muestran mediante la etiqueta Label1. Si se produce un error
en la recepcin se utiliza ClearCommError() para leer el tipo de error. Si se program el
miembro fAbortOnError al abrir el puerto es necesario llamar a esta funcin para que el puerto
pueda seguir trabajando. Este ejemplo sirve de base para recibir tramas completas en vez de
bytes, basta con programar el miembro fEvtChar al abrir el puerto y la mscara de
interrupciones con el bit EV_RXFLAG.
Aunque el ejemplo anterior funciona bienm tiene un grave problema y es que el programa se
quedar bloqueado en WaitCommEvent() si no se produce alguno de los eventos
programados. La solucin a este problema pasa por utilizar subprocesos,hilos o treats en los
programas.
10.9. PROCESOS Y SUBPROCESOS
El API WIN32 permite la creacin de subprocesos, hilos o Threads esta es una capacidad de
los sistemas operativos multitarea de que las aplicaciones en ejecucin (procesos) puedan
crear subprocesos que se ejecutan de forma independiente. Esta tcnica es til cuando
deseamos hacer alguna tarea en segundo plano o cuando se disponde de varios procesadores
y se desea asignar a cada uno de ellos una tarea especfica. Por ejemplo, el WORD de
Microsoft puede tener activado los subprocesos del corrector ortogrfico o del corrector
gramatical, mientras escribimos los correctores se estan ejecutando en paralelo con la edicin
del texto.
Nombre
TThread()
Execute()
Suspend()
Resume()
Termiate()
Synchronize()
Descripcin
Es el contructor de la clase, admite un parmetro
lgico para indicar si el subproceso se crea activado
o no
Cdigo que se ejecutar como si fuese una
aplicacin independiente. El constructor de la clase
llama a este mtodo de forma automtica.
Suspende temporalmente la ejecucin de un
proceso. En este estado el proceso no hace uso del
microprocesador.
Reanuda la ejecucin de un subproceso
Finaliza un subproceso y libera los recusos que
tuviese asignados. Cuando un proceso finaliza (al
cerrar el
programa) se terminan de forma
automticas todos sus subprocesos
Permite ejecutar una funcin con aceso a cualquier
objeto de la VCL.
La clase no se encuentra encapsulada como un componente de la VCL, sino que para utilizarla
debemos de crear un nuevo proyecto y despues aadir una nueva unidad con File-NewThreadObject .
Como ejemplo de subprocesos vamos a crear una aplicacin que utilizar un contador
asociado a un subproceso. Crearemos un un nuevo proyecto PROCNT que guardaremos como
PROCNT.MAK y UNIT1.CPP. Sobre la unidad depositamos los componentes que se muestran
en la figura 9 y que se detallan en el listado UNIT1.H.
// Fichero: Unit2.h
// Desc...: Definicin de la clase CntHilo
// Autor..: PMR 1999
//-----------------------------------#ifndef Unit2H
#define Unit2H
//-----------------------------------#include <vcl\Classes.hpp>
//-----------------------------------class CntHilo : public TThread
{
private:
protected:
void __fastcall Execute();
int iCnt;
public:
__fastcall CntHilo(bool CreateSuspended);
void __fastcall CntHilo::MostrarCnt(void);
};
//------------------------------------------#endif
// Fichero:Unit2.cpp
// Desc...: Declaracin de la clase CntHilo
// Autor..: PMR 1999
//-------------------------------------------
#include <vcl\vcl.h>
#pragma hdrstop
#include "Unit1.h" // Para acceso a Label1
//--------------------------------------------__fastcall CntHilo::CntHilo(bool CreateSuspended)
: TThread(CreateSuspended)
{iCnt=0;}
//--------------------------------------------void __fastcall CntHilo::Execute()
{
//---- Place thread code here ---while (true)
{
iCnt++;
Synchronize(MostrarCnt);
};
}
//-----------------------------------------void __fastcall CntHilo::MostrarCnt(void)
{
Form1->Label1->Caption=iCnt;
}
// Fichero:Unit2.cpp
// Desc...: Defincin clase CntHilo
// Autor..: PMR 1999
//------------------------------------------#include <vcl\vcl.h>
#pragma hdrstop
#include "Unit1.h"
//-----------------------------------------#pragma resource "*.dfm"
TForm1 *Form1;
//-----------------------------------------__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
// Creamos el subproceso
// inicialmente suspendido
poCnt=new CntHilo(true);
}
//----------------------------------------void __fastcall TForm1::
Button1Click(TObject *Sender)
{poCnt->Resume();}
//-----------------------------------------void __fastcall TForm1::
Button2Click(TObject *Sender)
{poCnt->Suspend();}
//-----------------------------------------void __fastcall TForm1::
Button3Click(TObject *Sender)
{poCnt->Terminate();}
//-------------------------------------------
Los nombres de los objetos se han cambiado para que aporten mas informacin: ButOpen,
ButClose, ButEnv, oMem etc. Se ha aadido un puntero pCom para manejar las
comunicaciones y un puntero poSerRx para manejar el subproceso. El buffer donse se
almacenarn los datos acBuf que se reciban y el objeto pCom se han puesto en la seccin
pblica para permitir el acceso desde el subproceso a estos objetos. En el proyecto hay que
oMem->Lines->Add("Puerto cerrado");
}
//--------------------Salir
void __fastcall TForm1::
ButSalClick(TObject *Sender)
{Close();}
//--------------------Enviar cadena
void __fastcall TForm1::
ButEnvClick(TObject *Sender)
{
AnsiString cTmp;
char *pCad;
// Leo la variable AnsiString del control
cTmp=oEdi->Text;
// Aado el retorno de carro
cTmp=cTmp+"\r";
// Convierto el objeto cTmp a cadena de C
pCad=cTmp.c_str();
// Transmito la cadena y la muestro
if ( pCom->TxCad(pCad))
oMem->Lines->Add(pCad );
}
//--------------------------------------
//--------------------------------------------#include <vcl\vcl.h>
#pragma hdrstop
#include "SerRx.h"
#include "unit1.h"
//--------------------------------------------__fastcall CSerRx::CSerRx(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------Cdigo asociado al subproceso
void __fastcall CSerRx::Execute()
{
DWORD dwEvt;
BYTE cDatRx;
int
iCnt;
// Bucle infinito para el subproceso
while(true)
{
// Esperar los eventos programados
// Aqu el subproceso se quedar parado
// hasta que se produzca uno de los eventos
dwEvt=Form1->pCom->EsperaEventos();
// Si se ha producido un error
if(dwEvt & EV_ERR)
{
// Resetear el error y mostrar mensaje
Form1->pCom->LeerError();
Form1->oMem->Lines->Add("Error al recibir.");
};
// Si se ha leido un byte
if (dwEvt & EV_RXCHAR)
{
// Leer TODOS los BYTES pendientes de
// ser leidos del buffer del receptor
iCnt=0;
while (Form1->pCom->RxByte(cDatRx))
{
Form1->acBuf[iCnt]=cDatRx;
iCnt++;
};
// Crear una cadena
Form1->acBuf[iCnt]=0x00;
// Mostrar en pantalla
Synchronize(MostrarRx);
};
};
}
//-----------------Mostrar cadena recibida
void __fastcall CSerRx::MostrarRx(void)
{
const char *pCad;
pCad=Form1->acBuf;
Form1->oMem->Lines->Add(AnsiString(pCad));
}
#pragma hdrstop
//--------------------------------------USEFORM("Unit1.cpp", Form1);
USERES("SEREVT2.res");
USEUNIT("\DATOS\LIBRO_CI\fuentes\win\PortCom\PortCom.cpp");
USEUNIT("SerRx.cpp");
//---------------------------------------WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
//-----
EJERCICIOS Y ACTIVIDADES
Teclear la clase TWinSerCom y hacer un pequeo programa que la use para enviar y recibir un
carcter.
Los ejercicios planteados en el captulo 6 con la clase de MS-DOS CPortCom reescribirlos en
WINDOWS utilizando la clase TWinSerCom.
Teclear el ejemplo del MODEM y verificar las respuestas para los comandos HAYES ms
habituales.
Desarrollar un programa que reciba datos del puerto serie mediante eventos. Los datos
recibidos se almacenarn en un fichero, si se produce un evento de error se mostrar un
mensaje y se cancelar la recepcin de datos.