puerto serie

44
EL API DEL PUERTO SERIE Introducción Contenido FUNCIONES EL API WIN32 PARA COMUNICACIONES SERIE LÍNEAS 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 RECEPCIÓN EN SEGUNDO PLANO 10.1. FUNCIONES DEL API WIN32 PARA COMUNICACIONES SERIE La utilización del las funciones del API WIN32 para trabajar con el puerto serie garantiza la portabilidad del código 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 más de dos docenas, se irán 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.- Operación de apertura: sirve para obtener un manejador o handle y comprobar si el sistema operativo ha podido tomar el control del dispositivo para nuestra aplicación. 2.- Configuración: Sirve par establecer los parámetros de la comunicación: 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 través de BUFFER uno para la transmisión (escritura) y otro para la recepción (lectura). 4.- Cierre del puerto para que otros programas puedan hace uso de él.

Upload: sucheful-ly

Post on 17-Sep-2015

19 views

Category:

Documents


6 download

DESCRIPTION

manual api puerto serie

TRANSCRIPT

PROGRAMACION PUERTO SERIE

EL API DEL PUERTO SERIE

Introduccin

ContenidoFUNCIONES EL API WIN32 PARA COMUNICACIONES SERIELNEAS DE CONTROL DE FLUJO BUFFER DEL TRANSMISOR Y DEL RECEPTORLEER Y ESCRIBIR EN EL PUERTOCLASE TWinSerCom PARA TRANSMITIR Y RECIBIR CON WIN32UN CHAT BAJO WINDOWSENVIAR Y RECIBIR TRAMASCONTROL DEL PUERTO MEDIANTE EVENTOSPROCESOS Y SUBPROCESOSRECEPCIN 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.

1.- Apertura y cierre del puerto

Para abrir el puerto se utiliza la funcin CreateFile() , esta funcin es una funcin genrica que permite acceder a recursos de naturaleza diversa: puertos de comunicaciones, ficheros, directorios, discos, consolas etc.

CreateFile()

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

ARG. ENTRADADESCRIPCIN

lpFileNameNombre del puerto COM1, COM2 etc

dwDesiredAccesTipo de acceso GENERIC_READ, GENERIC_WRITE etc

dwShareModeModo de compartir. Debe ser 0 al no poderse compartir el puerto serie por dos aplicaciones de forma simultnea.

lpSecurityAttributesDescriptor de seguridad. Debe ser 0

dwCreationDistributionEspecifica 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

dwFlagsAndAttributesEspecifica el tipo de atributo del fichero/recurso, debe de ser FILE_ATRIBUTE_NORMAL para los puertos.

hTemplateFileDebe ser NULL

TIPO SALIDADESCRIPCIN

HANDLEManejador 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() .

CloseHandle()

BOOL CloseHandle(HANDLE hObject);

ARG. ENTRADADESCRIPCIN

hObjectManejador o handle genrico.

TIPO SALIDADESCRIPCIN

BOOLTRUE si la funcin se ejecut correctamente.

2.- Configuracin del puerto

Para leer y escribir la configuracin el puerto se utilizan las funciones GetCommState() y SetCommState() ambas utilizan como argumento una estructura del tipo DCB. Esta estructura encapsula en sus campos la configuracin de un puerto serie como se muestra en la tabla siguiente, se encuentra disponible en el fichero WINBASE.H del directorio INCLUDE\WIN32 del C++ BUILDER.

MIEMBROS DE LA ESTRUCTURA DCB

CAMPOTIPODESCRIPCIN

DCBlengthDWORDTamao de la estructura DCB

BaudRateDWORDVelocidad en bps: CBR_100,CBR_300,CBR_600 ...CBR_560000..

fBinary:1BITModo binario. Debe de ser TRUE

fParity: 1BITSi TRUE se chequearn errores de paridad

fOutxCtsFlow:1BITSi 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.

fOutxDsrFlow:1BITSi 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.

fDtrControl:22 BITSEspecifica 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 puertoDTR_CONTROL_HANDSHAKE: Habilita el control de flujo hardware DTR-DSREn los dos primeros casos se puede utilizar la funcin EscapeCommFunction() para cambiar el estado de la lnea DTR

fDsrSensitivity:1BITSi TRUE se descartn los datos recibidos mientras la lnea DSR (entrada al DTE) este inactiva.

fTXContinueOnXoff:1BITConfigura 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.

fOutX: 1BITSi TRUE habilita el control de flujo XON/XOFF durante la transmisin .La transmisin cesar al recibirse un XOFF, se reanudar al recibirse un XON

fInX: 1BITSi 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.

fErrorChar: 1BITSi TRUE se sustituirn todos los caracteres recibido con errores por el dato ErrorChar definido en esta misma estructura.

fNull: 1DWORDSi TRUE se descartar los BYTES recibidos que sean nulos.

fRtsControl:22 BITSConfigura 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 inactivaRTS_CONTROL_ENABLE: Se mantendr RTS activaRTS_CONTROL_HANDSHAKE: Habilita el control de flujo hardware RTS-CTSRTS_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

fAbortOnError:1BITSi TRUE se abortar cualquier operacin de lectura o escritura hasta que se llame explicitamente a ClearCommError().

fDummy2:1717 BITSNo se usan

wReservedWORDNo se usan

XonLimWORDDetermina el lmite inferior del BUFFER del receptor que fuerza la transmisin de XON

XoffLimWORDDetermina el lmite superiro del BUFFER del receptor que fuerza la transmisin de XOFF

ByteSizeBYTEDetermina el nmero de bits de datos: 4,5,6,7,8

ParityBYTEParidad: NOPARITY, EVENPARITY(par),MARKPARITY o ODDPARITY (impar)

StopBitsBYTEBist de Stop: ONESTOPBIT, ONE5STOPBITS,TWOSTOPBITS

XonCharcharFija el caracter que se usar como XON

XoffCharcharFija el caracter que se usar como XOFF

ErrorCharcharFija el caracter que se usar para sustituir un datos recibido erroneamente.

EofCharcharFija el caracter que se usar para forzar el final de la transmisin

EvtCharcharFija el caracter que se usar para forzar el lanzamiento de un evento

wReserved1WORDReservado

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. ENTRADADESCRIPCIN

hFileManejador del puerto o Handle devuelto por CreateFile()

lpDCBPuntero a una estructura de tipo DCB donde se recibir la configuracn actual del puerto.

TIPO SALIDADESCRIPCIN

BOOLTRUE si la funcin se ejecut correctamente.

SetCommState()

BOOL SetCommState(HANDLE hFile,LPDCB lpDCB)

ARG. ENTRADADESCRIPCIN

hFileManejador del puerto o Handle devuelto por CreateFile()

lpDCBPuntero a una estructura de tipo DCB que se usar para configurar el puerto.

TIPO SALIDADESCRIPCIN

BOOLTRUE si la funcin se ejecut correctamente.

BuildCommDCB()

BOOL BuildCommDCB(LPCTSTR lpDef,LPDCB lpDCB)

ARG. ENTRADADESCRIPCIN

lpDefPuntero a una cadena al estilo del comando MODE con la configuracin a fijar, por ejemplo: baud=9600 parity=N data=8 stop=1

lpDCBPuntero a una estructura de tipo DCB donde se recibir la configuracin completa del puerto.

TIPO SALIDADESCRIPCIN

BOOLTRUE 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 #include #include #include #include #include #pragma hdrstop//-----------------------------------USERES("Project1.res");//-----------------------------------int main(int argc, char **argv){BOOL lOk;HANDLE hCom; // Manejador del puertoDCB sComCfg; // Estructura DCBchar 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) { coutOpen(ComboBox1->ItemIndex+1); // Si est abierto if (!pCom->IsOpen()) { Memo1->Lines->Add("ERROR al abrir el puerto"); break; } Memo1->Lines->Add("Abierto "+sCom); // Fijar la mscara de eventos dwEvtMask=EV_RXCHAR|EV_ERR; if (!SetCommMask(pCom->LeeIde(),dwEvtMask)) break;; // Esperar el evento for(iCnt=1;iCntValue;iCnt++) { Memo1->Lines->Add ("Esperando byte nro.."+String(iCnt) ); // Esperar los eventos programados WaitCommEvent(pCom->LeeIde(),&dwEvtMask,0); // Si se ha producido un error if(dwEvtMask & EV_ERR) { ClearCommError(pCom->LeeIde(),&dwErr,&sComSta); Memo1->Lines->Add("Error al recibir."); }; // Si se ha leido un byte if (dwEvtMask & EV_RXCHAR) { // Leer el Byte del Buffer if (pCom->RxByte(cDatRx)); { Memo1->Lines->Add("Recibido un byte " ); Label1->Caption=AnsiString(cDatRx); Label1->Refresh(); }; }; }; // Fin del for

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.

FIGURA 8. PROCESOS EN EJECUCIN

Del mismo modo que en el escritorio de WINDOWS podemos terner abiertos varios programas (procesos) simultneamnete, dentro de una aplicacin podremos crear tantos subprocesos como necesitemos. Los procesos permiten por tanto implementar la multitarea a nivel de programa. No hay que abusar del nmero de subprocesos, ya que cada uno de ellos consume tiempo del procesosadr como si se tratase de un programa independiente. En comunicaciones los subprocesos se utilizan para recibir o transmitir en segundo plano mientras se atiende al interface del usuario.

El API Win32 incluye una serie de funciones para manejar los procesos (GetThreadPriority(), ResumeThread(), SetThreadPriority(), CreateThreat(), SuspedThreat() etc) estas funciones pueden ser utilizadas como las que se han vista anteriormente, no obstante el C++ Builder encapsula y facilita el uso de subprocesos mediante la clase TThread. La tabla siguiente muestra un resumen del interface de la clase.

La clase TThread

NombreDescripcin

TThread()Es el contructor de la clase, admite un parmetro lgico para indicar si el subproceso se crea activado o no

Execute()Cdigo que se ejecutar como si fuese una aplicacin independiente. El constructor de la clase llama a este mtodo de forma automtica.

Suspend()Suspende temporalmente la ejecucin de un proceso. En este estado el proceso no hace uso del microprocesador.

Resume()Reanuda la ejecucin de un subproceso

Termiate()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

Synchronize()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-New-ThreadObject .

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.

FIGURA 9. EN CONTADOR CON SUBPROCESO

A continuacin creamos un objeto Thread con File-New-ThreadObject como nombre de clase utilizamos CntHilo, al aceptar se crear la unidad UNIT2. En esta unidad se declara (UNIT2.H) y define (UNIT2.CPP) la clase CntHilo tal y como se muestra a continuacun.

// Fichero: Unit2.h// Desc...: Definicin de la clase CntHilo// Autor..: PMR 1999//------------------------------------#ifndef Unit2H#define Unit2H//------------------------------------#include //------------------------------------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 #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;}

En la clase la variable iCnt se utiliza como contador, se inicializa en el constructor de la clase y se actualiza constantemente mientras el proceso est activo mediante el mtodo Execute(). Al observar que el cuerpo de este mtodo se puede pensar que la aplicacin se quedar colgada al ser un bucle infinito, esto sera cierto si se ejecutase ese cdigo en el hilo principal de la aplicacin. Como se encuentra en un subproceso el cdigo se est ejecutando en paralelo con el cdigo del programa principal. La lnea Synchronize(MostrarCnt) permite hacer que se muestre el contador en la pantalla a travs de la etiqueta Label1. Esta objeto Label es miembro de clase Form1 declarada en UNIT1.H, es por ello que se debe de incluir esta unidad en el fichero UNIT2.CPP.

Para utilizar la clase debemos de declarar un objeto poCnt de la clase CntHilo en la declaracin de la clase Form1 e inicializarlo al crear el formulario, despus asignamos los mtodos a los botones segn se desprende del cdigo.

// Fichero: Unit1.cpp// Desc...: Declaracin clase Form1// ejemplo multihilo// Autor..: PMR 1999//--------------------------------------#ifndef Unit1H#define Unit1H//-------------------------------------#include #include #include #include #include #include "unit2.h"//---------------------------------------class TForm1 : public TForm{__published:// IDE-managed Components TLabel *Label1; TButton *Button1; TButton *Button2; TButton *Button3; TMaskEdit *MaskEdit1; void __fastcall Button1Click(TObject *Sender); void __fastcall Button2Click(TObject *Sender); void __fastcall Button3Click(TObject *Sender);private:// User declarations CntHilo *poCnt;public:// User declarations __fastcall TForm1(TComponent* Owner);};//-------------------------------------------extern TForm1 *Form1;//-------------------------------------------#endif

// Fichero:Unit2.cpp// Desc...: Defincin clase CntHilo// Autor..: PMR 1999//-------------------------------------------#include #pragma hdrstop#include "Unit1.h"//------------------------------------------#pragma resource "*.dfm"TForm1 *Form1;//------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner): TForm(Owner){// Creamos el subproceso// inicialmente suspendidopoCnt=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();}//-------------------------------------------

En tiempo de ejecucin, al activar el subproceso veremos el contador funcionando y al mismo tiempo podemos manejar el ratn, editar el control de edicin, pulsar un botn etc. Hemos contruido una aplicacin multitarea que hace dos cosas a la vez, en primer plano atiende los controles del formulario Form1,en paralelo y en segundo plano hay un contador que se muestra en una etiqueta.

10.10. RECEPCIN EN SEGUNDO PLANO

Los subprocesos combinados con los eventos es el mecanismo ms rpido y eficaz para realizar comunicaciones a travs del puerto serie, mientras que el proceso principal atiende la interface del usuario, un proceso en segundo plano atiende al puerto: monitorizacin, lectura, escritura etc. En este ejemplo se va ha disear un programa que permita enviar ordenes a un modem y observar sus respuestas. El envio de las ordenes se realizar en primer plano y la recepcin de las respuestas en segundo plano. El aspecto del programa se muestra en la figura siguiente.

FIGURA 10. CONTROL DEL MODEM

En este programa , una vez abierto el puerto cualquier carcter que se reciba se mostrar en el control TMemo de la parte izquierda, para enviar una cadena al puerto se debe teclear en el control TMaskEdit y pulsar enviar, en el ejemplo se ha marcado un nmero de telfono inexistente con el comando ATDP y despus de unos segundos se ha producido la respuesta NO CARRIER indicando que en el otro extremo no existe ninguna portadora.

Para poner en prctica este programa hay que crear un proyecto nuevo sobre el que insertaremos los componentes que se desprenden de UNIT1.H.

//--------------------------------------------// Fichero....: Unit1.H// Descripcin: Declaracin de la clase Form1// Autor......: PMR Julio 1999//---------------------------------------------#ifndef Unit1H#define Unit1H#include #include #include #include #include #include #include "..\PortCom\PortCom.h"#include "SerRx.h"#include #include //---------------------------------------------class TForm1 : public TForm{__published:// IDE-managed Components TPanel *Panel1; TLabel *Label2; TMaskEdit *oEdi; TGroupBox *GroupBox1; TMemo *oMem; TPanel *Panel2; TButton *ButSal; TButton *ButEnv; TButton *ButOpen; TButton *ButClose; TComboBox *CBCom; TLabel *Label1; void __fastcall ButOpenClick(TObject *Sender); void __fastcall ButCloseClick(TObject *Sender); void __fastcall ButSalClick(TObject *Sender); void __fastcall ButEnvClick(TObject *Sender);private:// User declarations // Para el subproceso CSerRx *poSerRx;public:// User declarations // Objetos de comunicaciones // pblico para tener acceso // desde el subproceso TWinSerCom *pCom; // Para datos recibidos BYTE acBuf[1024]; // Constructor __fastcall TForm1(TComponent* Owner);};//--------------------------------------------extern TForm1 *Form1;//--------------------------------------------#endif

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 incluir la unidad PORTCOM.CPP node se encuentra la definicin del la clase TWinSerCom necesario para el objeto pCom. La definicin de la clase se encuentra en UNIT1.CPP

//-------------------------------------------// Fichero....: Unit1.CPP// Descripcin: Definicin de la clase Form1// Autor......: PMR Julio 1999//-------------------------------------------#include #pragma hdrstop#include "Unit1.h"#pragma resource "*.dfm"TForm1 *Form1;//-----------------Constructor del formulario__fastcall TForm1::TForm1(TComponent* Owner): TForm(Owner){CBCom->ItemIndex=0;// Crear el objeto de comunicacionespCom= new TWinSerCom(1);// Crear el objeto del subprocesopoSerRx=new CSerRx(false);// Estado de los botonesButOpen->Enabled=true;ButClose->Enabled=false;ButEnv->Enabled=false;CBCom->Enabled=true;}//------------Abrir Puertovoid __fastcall TForm1:: ButOpenClick(TObject *Sender){// Bucle para control de erroreswhile(true) { oMem->Lines->Clear(); // Abrir el puerto pCom->Open(CBCom->ItemIndex+1);

if (!pCom->IsOpen()) { oMem->Lines->Add("ERROR al abrir el puerto"); break; }; // Fijar la mscara de eventos if (!pCom->SetEventos(EV_RXCHAR|EV_ERR)) { oMem->Lines->Add("ERROR al configurar"); break;; } // Activar el proceso en segundo plano // para la recepcin de datos poSerRx->Resume(); // Estado de los botones ButOpen->Enabled=false; ButClose->Enabled=true; ButEnv->Enabled=true; CBCom->Enabled=false; oMem->Lines->Add("Puerto abierto"); break; };}//-----------Cerrar el puertovoid __fastcall TForm1:: ButCloseClick(TObject *Sender){//Cerrar puertopCom->Close();// Bloquear proceso en segundo planopoSerRx->Suspend();// Estado de los botonesButOpen->Enabled=true;ButClose->Enabled=false;ButEnv->Enabled=false;CBCom->Enabled=true;oMem->Lines->Add("Puerto cerrado");}//--------------------Salirvoid __fastcall TForm1:: ButSalClick(TObject *Sender){Close();}//--------------------Enviar cadenavoid __fastcall TForm1:: ButEnvClick(TObject *Sender){AnsiString cTmp;char *pCad;// Leo la variable AnsiString del controlcTmp=oEdi->Text;// Aado el retorno de carrocTmp=cTmp+"\r";// Convierto el objeto cTmp a cadena de CpCad=cTmp.c_str();// Transmito la cadena y la muestroif ( pCom->TxCad(pCad)) oMem->Lines->Add(pCad );}//--------------------------------------

En el constructor se crean los objetos de comunicaciones y subproceso y se inicializan algunas variables y estados de los componentes del formulario. Al pulsar al botn ButOpen se abre el puerto y se activa el subproceso, a partir de ese momento cualquier carcter que llegue al puerto ser mostrado en el control memo. Observar que cuando se abre el puerto se programa la mscara de eventos de la clase TWinSerCom mediante el mtodo SetEventos() . Al pulsar el botn ButClose se cierra el puerto y se suspende el subproceso. Al pulsar el botn ButEnv se lee el texto que se encuentra en el control oEdi y se envia al puerto mediante el mtodo TxCad() .

Para crear el subproceso utilizamos File-New-Threath y como nombre de clase utilizamos CSerRx, esta clase la salvamos en SERRX.CPP y SERRX.H.

//----------------------------------------// Fichero....: SerRx.H// Descripcin: Definicin de la clase CSerRx// para crear un subproceso que lea del puerto// mediante eventos.// Autor......: PMR - Julio 1999//-----------------------------------------#ifndef SerRxH#define SerRxH//-----------------------------------------#include //-----------------------------------------class CSerRx : public TThread{private:protected:void __fastcall Execute();public:__fastcall CSerRx(bool CreateSuspended); void __fastcall MostrarRx(void);};//------------------------------------------#endif

//--------------------------------------------// Fichero....: SerRx.CPP// Descripcin: Declaracin de la clase CSerRx// para crear un subproceso que lea del puerto// mediante eventos.// Autor......: PMR - Julio 1999//---------------------------------------------#include #pragma hdrstop#include "SerRx.h"#include "unit1.h"//---------------------------------------------__fastcall CSerRx::CSerRx(bool CreateSuspended): TThread(CreateSuspended){}//---------Cdigo asociado al subprocesovoid __fastcall CSerRx::Execute(){DWORD dwEvt;BYTE cDatRx;int iCnt;

// Bucle infinito para el subprocesowhile(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 recibidavoid __fastcall CSerRx::MostrarRx(void){const char *pCad;pCad=Form1->acBuf;Form1->oMem->Lines->Add(AnsiString(pCad));}

El mtodo execute() de la clase disponde de un bucle infinito que se ejecuta en un subproceso en paralelo con el proceso principal, en este mtodo se esperan que ocurran los eventos programados mediante el mtodo EsperarEventos() . Si no se produce error se pasa a leer todos los bytes pendientes en el BUFFER del receptor, recordar que el evento de EV_RXCHAR se dispara cuando hay datos pendientes de ser leidos, pero no se garantiza que se dispare el evento en cada byte recibido. Los datos recibidos se almacenan en el buffer acBuf y se muestran en el control oMem del formulario principal. A continuacin se muestra el archivo de proyecto final SEREVT2.MAK que queda como resultado .

// Fichero SEREVT2.MAK// Descripcin: Proyecto modem// Fecha..: PMR Julio 1999//---------------------------------------#include #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.

fin del captulo--