CAPÍTULO 9 PROGRAMANDO LA INTERFAZ SERIAL La programación del puerto serial de E/S, la reconoce el DOS como COM1 y COM2. Esto se puede realizar de varias formas: • Usando las funciones open, read , write y close, proporcionadas por el lenguaje C. • Con las funciones de la interrupción lógica 0x14 (int 14h) • Programando los puertos de E/S en forma directa. 9.1. ACCESO DE LA LÍNEA SERIE USANDO FUNCIONES DE ARCHIVO Esto consiste en abrir un archivo asociado al periférico (COM1 o COM2). este método tiene el inconveniente de que nada le indica al PC si el dato está disponible en la línea serie. Si en el momento en que se efectúa la operación read hay un dato disponible, esta operación no produce ningún retraso, pues se realiza inmediatamente. Si no existe dato disponible, es sistema permanecerá en escucha, dejando de lado cualquier otra actividad. Ej 9.1 Acceso a la línea serie. #include<fcntl.h> #include<io.h> #include<stdlib.h> #include<stdio.h> #include<string.h> #include<conio.h> void main() { int fn; char c[100]; fn=open("COM1",O_BINARY|O_RDWR); if(fn==-1) exit(0); cprintf("¿Cual es su mensaje? :"); gets(c); write(fn,c,strlen(c)); read(fn,c,1); /* Quedará esperando respuesta */ } 9.2. NORMA DE COMUNICACIONES RS-232C Es una norma de comunicación serial, utilizada para la comunicación entre modems, terminales, impresoras, computadores. Fue definida como estándar por la Asociación de Industrias Electrónicas (EIA). 76 Preparado por Juan Ignacio Huircán El modem (Equipo de comunicación de datos o DCE) dispone de un conector hembra y el terminal de un conector macho. De las veinte señales definidas generalmente se ocupan nueve y a menudo sólo tres son las más usadas, puesto que la norma establecida no específica cuantas deben ser usadas. las señales de los pin 4, 5, 6, 8, 20, son diálogo, utilizadas entre el modem y el términal, mientras que los pines 15,17 y 24 se utilizan para modem de alta velocidad. El pin 22 indica que el modem ha detectado una señal de llamada en la línea telefónica y es utilizada por el sistema para responder automáticamente. La norma define los niveles de tensión entre -3v y -15v para el estado lógico "1" y de +3v a +15v para el estado "0". Para compatibilizar estos niveles con TTL, es necesario utilizar circuitos integrados diseñados para tal efecto, tal es el caso de los chip MAX232, MC1488 y MC1489. Respecto de las conexiones El conector para RS-232 tiene 25 terminales (pines), y se conoce como DB25. En la siguiente tabla se describen la mayoría de las patillas. Patilla 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Abreviación TD RD RTS CTS DSR DCD DTR Descripción Masa de protección Transmisión desde el terminal Recepción desde el modem Petición de envío Borrado de envío Dato preparado Masa de señal Detector de portadora Reservado Reservado Sin asignación Detector secundario de portadora Borrado de envío secundario Transmisión de datos secundarios Reloj de transmisión desde el DCE Recepción de datos secundario Reloj de recepción Sin Asignación Petición de envío secundario Terminal de datos preparado Detector de calidad de señal Indicador de llamada Selector de velocidad de datos Sin asignación Tabla 9.1. Descripción de Señales DB25. Las señales más utilizadas son las siguientes: Apuntes de Herramientas de Programación 77 Señales Descripción TX, transmisión (PIN 2) Señal de salida de información serial desde el terminal (DTE). PIN 3 en modem (DCE). RX, recepción (PIN 3) Señal de entrada de información serial en terminal (DTE). PIN 2 en modem. Data Carrier Detect (DCD, PIN 8) Indica que el modem ha recibido señal portadora del modem remoto. Terminal de datos preparado (DTR, PIN 20) Señal desde el teminal al modem, que indica que el terminal de datos (DTE) está listo para enviar datos al modem (DCE). Modem encedido y preparado (DSR, PIN 6) Señal desde el modem al terminal que indica que el modem está listo para la transferencia de información. Petición de envío (RTS, PIN 4) Señal desde el terminal al modem que indica que el terminal quiere enviar datos al modem. Modem Preparado para Tx (CTS, PIN 5) Señal desde el modem al terminal que indica que el terminal puede enviar datos al modem. Tabla 9.2. Descripción de Señales de control de modem. El diálogo con el modem para la transmisión de caracteres desde el teminal será esquematizado en los siguiente diagrama de flujo: Inicio Inicio Activar DTR Activar DTR SI SI NO NO NO DSR Wait Tiempo Error TX NO DSR SI Wait Tiempo SI Fin SI Activar RTS SI CTS NO CARAC NO Error TX Wait Tiempo Fin NO Error TX NO Wait Tiempo Error TX SI SI Fin Fin Leer caracter TX dato Desactivar DTR Desactivar DTR, RTS Fin Fin (a) (b) Figura 9.1. (a) Diagrama de flujo para TX. (b) Diagrama de flujo para RX. 78 Preparado por Juan Ignacio Huircán Diagrama de conexiones Como la norma no establece el número de señales que deben utilizarse para la comunicación, pueden existir diferentes formas de comunicación entre los dispositivos. El caso más general corresponde a la conexión de las ocho lineas más utilizadas, en este caso la conexión debe ser directa, pues el terminal está configurado como DTE y el modem como DCE. PC TX RX RTS CTS DSR GND DCD DTR ________________ ________________ ________________ ________________ ________________ ________________ ________________ ________________ 2 3 4 5 6 7 8 20 MODEM 2 3 4 5 6 7 8 20 Figura 9.2. Conexión PC-MODEM. Es posible establecer una comunicación entre dos DTE sin la intervención de modems (distancias cortas), con lo cual la conexión puede hacerse de la siguiente forma: PC 1 TX RX GND RTS CTS DCD DSR DTR 2 3 7 4 5 8 6 20 _______________ _______________ _______________ __ __| __ __| PC 2 3 2 7 4__ 5__| 8 __ 6 |__ 20 RX TX GND RTS CTS DCD DSR DTR Figura 9.3. Conexión PC-PC. Una conexión con tres terminales permitirá intercambiar mensajes y archivos entre máquinas, contando con un programa de comunicaciones adecuado. Puede utilzar solamente los terminales 2, 3, 7. Dentro de los aspectos prácticos es necesario mencionar que existe un conector RS-232 de 9 pines (DB9). La equivalencia entre este conector y el de 25 pin es la siguiente: 9 PINS 25 PINS Patilla 1 2 3 4 5 6 7 8 9 8 3 2 20 7 6 4 5 22 DCD RD TD DTR SG (SIGNAL GROUND) DSR RTS CTS RING INDICATOR Tabla 9.3. Equivalencia de terminales entre el conecto DB9 y DB25 Apuntes de Herramientas de Programación 79 Lo anterior puede no cumplirse, es conveniente revisar los manuales correspondientes en el caso de querer realizar alguna conexión. 9.3. ACCESO DE LA INTERFAZ SERIAL UTILIZANDO LA INT 14H También podemos utilizar el BIOS para tener un acceso cómodo al puerto de E/S serial, sin preocuparnos del harware. Para realizar esto usamos interrupción 14h (int 0x14). esta proporciona cuatro funciones, las que pueden ser accesadas a través de AH. AH 0 Función Registros Inicializa puerto Entrada: DX = # del puerto serie (0 ó 1) de comunicaciones AL = Parámetros de Inicialización. (ver siguiente tabla 9.6.) 1 2 3 Salida : AX Envia caracter Entrada: DX sobre la linea AL Salida : AH Bit 7 Leer caracter de Entrada: DX la línea Salida: AL AH Si AH no es Leer palabra de Entrada: DX estado del puerto Salida : AX de comunicaciones AH = Estado = # del puerto (0 ó 1) = Caracter a enviar = Estado de la linea: =1, si caracter no tx = # del puerto (0 ó 1) = Caracter leido = Estado de la linea 0 es que ha ocurrido error = Número puerto = Palabra de estado = Estado de la línea AL= Estado del modem Tabla 9.4. Parámetros de inicialización para int 14h. Los parámetros de inicialización son codificados en una palabra de 8 bits Bits b7-b5 Contenido Velocidad (baudios): 000=110 001=150 010=300 011=600 100=1200 101=2400 110=4800 111=9600 b4-b3 Paridad: X0 = ninguna 01 = impar 11 = par Tabla 9.5. Registro de inicialización. 80 Preparado por Juan Ignacio Huircán b2 Bits de stop: 0 = 1 bits 1 = 1.5 bits si el largo del caracter es 5 1 = 2 bits si el largo del caracter es 6, 7 u 8. largo del caracter: 00=5 b1-b0 01=6 10=7 11=8 Tabla 9.6. (Continuación) Registro de inicialización. El estado se codifica de la siguiente forma: Bits de AH b7 b6 b5 b4 b3 b2 b1 b0 Contenido Bits de AL Tiempo excedido Registro desplazamiento vacío Registro TXvacío Interrupción (BREAK) Error de formato (framimg) Error de paridad Error de Sobrecarga (Overrun) Dato preparado (Listo) b7 b6 b5 b4 b3 b2 b1 b0 Tabla 9.7. Contenido de AX . Ej 9.2 Programando la RS-232 para TX y RX utilizando la int 14h. #include #include #include #include <dos.h> <stdio.h> <stdlib.h> <conio.h> union REGS rent, rsal; void ini_rs232() { rent.h.ah=0x00; /* Inicialización */ rent.x.dx=0x00; /* COM1 */ /*1200 bps, sin par, 1 bit stop, 8 bit datos */ rent.h.al=0x83; int86(0x14,&rent,&rsal); } Contenido DCD (Data Carrier Detect) RI (Ring indicator) DSR (Data Set Ready) CTS ( Clear to Send) Delta DCD Delta RI Delta DSR Delta CTS Apuntes de Herramientas de Programación 81 void escribe(char c) { /* Función escribir */ rent.h.ah=0x01; rent.x.dx=0x00; /* COM1 */ rent.h.al=c; /* Escribe caracter c */ int86(0x14,&rent,&rsal); } int test_rs() { /* Lee status de línea */ rent.h.ah=0x03; rent.x.dx=0x00; /* COM1 */ int86(0x14,&rent,&rsal); /* Retorna bits menos significativo */ return(rsal.h.ah & 0x01); } char leer_rs() { /* Función Leer dato */ rent.h.ah=0x02; rent.x.dx=0x00; /* COM1 */ int86(0x14,&rent,&rsal); /* Retorna el dato en AL */ return(rsal.h.al); } void main() { int st; char c; /* Inicialización RS-232 */ ini_rs232(); while(1) { st=test_rs(); /* Lee estado de la línea */ if(st) { c=leer_rs(); /* Lee dato */ putch(c); } if(kbhit()) { c=getch(); if(c==0x1b) exit(0); /* Escribe Caracter */ escribe(c); } } } /* main */ Note que no se han declarado las funciones. Esto no es necesario cuando es escribe el main() al final. 82 Preparado por Juan Ignacio Huircán 9.4. PROGRAMANDO RUTINAS DE INTERRUPCIÓN PARA LA COMUNICACIÓN SERIAL Si bien el BIOS del IBM-PC nos proporciona la ayuda para la transmisión y recepción serial entre los dispositivos, esta tiene sus limitaciones: ### La velocidad máxima de transmisión es de 9600 bits/seg. Generalmente se cree que este es el valor máximo de la interfaz de comunicación, sin embargo es sólo una limitación del BIOS. Para aumentar le velocidad es necesario programar en forma directa del dispositivo de transmisión UART (Unidad Asincrónica De Transmisión Y Recepción), a través de sus puertos de programación. ### La recepción de caracteres debe hacerse por encuesta al registro de estado del dispositivo de recepción (UART) El BIOS al no disponer de servicios de interrupción (hardware) para transmisión y recepción de caracteres no justifica una velocidad de más de 9600 bits/seg. Aun a esta velocidad se requiere de un programa pequeño y eficiente que permita rápidamente la lectura de los caracteres de la UART, antes de que llegue el próximo dato. Si se quieren mejorar las características de transmisión, por ejemplo su velocidad y tiempo de atención (por interrupción) necesariamente debemos programar en forma directa los registros del UART. Para realizarlo en lenguaje C se deben utilizar las instrucciones para manejo de los puertos de Entrada/Salida. El UART o 8250 posee las siguientes características: ü ü ü Divisor programable de 16 bits. Programación de todas las características de comunicación serial (caracteres, bits de stop, paridad, etc). Programación en modo loop (para test) Una programación de los parámetros habituales se efectúa enviando una secuencia de bytes a los puertos correspondientes, los puertos asignados en el IBM-PC son los siguientes: COM2 COM1 BIT 0 1 2 3 4 5 6 7 DLAB=0 2F8 3F8 DLAB=0 2F8 3F8 REG TX REG RX (WR only) (RD only) D0 D1 D2 D3 D4 D5 D6 D7 D0 D1 D2 D3 D4 D5 D6 D7 DLAB=0 2F9 3F9 2FA 3FA 2FB 3FB 2FC 3FC 2FD 3FD 2FE 3FE REG ENABLE INT REG ID LINE CTRL MODEM CTRL LINE STATUS MODEM STATUS INT RX DATO INT TX HL INT RX LIN INT ST MOD 0 0 0 0 INT PEND INT ID INT ID 0 0 0 0 0 LINEA DTR RTS OUT 1 OUT 2 LOOP 0 0 0 DATO LISTO ERROR OVERRUN ERROR PARIDAD ERROR TRAMA BREAK INT TX HOLD VACIO THSHT VACIO 0 Delta CTS LARGO DATA LARGO DATA STOP BIT HABILITA PARIDAD TIPO PARIDAD FIJAR PARIDAD SET BREAK DIV. LATCH ACCESS BIT (DLAB) Delta DSR Delta RING Delta RLSD CTS DSR RING RLSD Tabla 9.8. Descripción de registros del puerto de comunicaciones. Si el DLAB =1 (Divisor latch access bit), entonces es posible programar el divisor para la inicialización de la velocidad. Para hacer el DLAB=1, debe poner en 1 el bit más significativo del registro 0x3fb (o 0x2fb). Apuntes de Herramientas de Programación COM2 2F8 2F9 COM1 3F8 3F9 D7 B7 B15 DIV LS DIV MS 83 D6 B6 B14 D5 B5 B13 D4 B4 B12 D3 B3 B11 D2 B2 B10 D1 B1 B9 D0 B0 B8 Tabla 9.9. Direcciones para la programación del divisor. Para la programación de la velocidad además de accesar el divisor (DLAB=1), se debe conocer el reloj externo que es aplicado al 8250. Existen dos posibilidades. ck1=1.8432 ck2=3.072 MHz MHz En las siguientes tablas se encuentra el divisor adecuado para cada una de las velocidades y reloj utilizado. CLOCK = 1.8432MHz DESIRED BAUD RATE DIVISOR USED TO GENERATE 16XCLOCK 1047 768 384 192 96 48 24 12 6 3 2 110 150 300 600 1200 2400 4800 9600 19200 38400 56000 CLOCK=3.072MHz DESIRED BAUD DIVISOR USED RATE TO GENERATE 16XCLOCK 110 1795 150 1280 300 6404 600 3202 1200 1606 2400 80 4800 40 9600 20 19200 10 38400 5 56000 3 Tabla 9.10.Constantes para programar la 8250. Ej 9.3. Como utilizar la tabla 9.10. Para una velocidad de 1200 baudios, se debe calcular la frecuencia necesaria para generar 16 veces la frecuencia de la tasa de baudios. Usando un clock =1.8432, para una velocidad de 1200 baudios: 1. 8432 x10 6 = 96 1200 x16 La conexión del UART en el IBM-PC es: 84 Preparado por Juan Ignacio Huircán OUT2 IRQ3 IRQ4 UART CONTROL. INTERR. 8250 8259 INT Figura 9.4. Conexión del UART al Controlador de Interrupciones. Luego para que la UART interrumpa al PC debemos habilitar la señal de interrupción a través de la señal OUT2 y además programar la interrupción en el registro relativo #1(0x3f9 o 0x2f9) del UART. Otra consideración especial es que se debe programar el controlador de interrupciones (8259) para habilitar la interrupción ya sea por COM1 o COM2, además de enviar un EOI (End Interrupt) al final de la RSI. De acuerdo a esto, se envía un byte de habilitación de interrupción al puerto 0x21, dicho byte tiene la siguiente estructura B7 IRQ7 B6 B5 IRQ6 IRQ5 B4 B3 IRQ4 COM1 IRQ3 COM2 B2 IRQ2 B1 B0 IRQ1 IRQ0 Luego para habilitar cualquiera de las interrupciones, se debe hacer un SET o un RESET al bit correspondiente, es decir. 0: Para habilitar 1: Para deshabilitar Ej 9.4. Programación de un puerto de 8250. La programación del chip para 8 bits de datos, 1 bits de stop, y paridad par, se debe enviar el valor 0x1B al puerto 0x3fb. #include<dos.h> #define BASE 0x3f8 /* Para COM1 void main() { outportb(BASE+3, 0x1b); } , 0x2f8 para COM2 */ /* Prog. el 0x3fb */ Ej 9.5. Habilitando y deshabilitando las interrupciones. outportb(0x21,0x00); outportb(0x21,0xff); /* Habilita todas las interrupciones /* Deshabilita todas las interrupcione Si se desea mayor comprensión acerca del CHIP 8250, puede consultar la referencia técnica . */ */ Apuntes de Herramientas de Programación 85 Ej 9.6. El siguiente programa prepara el dispositivo serial para recibir datos vía interrupción: #include <dos.h> #include <stdio.h> #include <conio.h> #define INT 0x0C /* Vector C, IRQ4 */ void interrupt (*ant_irq)(); /* Puntero a funcion */ void interrupt rx(); void prog_8250(); int flag=0; unsigned static int dato=0; void main(void) { clrscr(); outportb(0x21,0xff); /* Deshabilita todas las interrupciones */ prog_8250(); ant_irq=getvect(INT); setvect(INT,rx); outportb(0x21,0x00); /* Habilita todas las interrupciones */ enable(); do { if(flag==1) { printf("%d ",dato); flag=0; } if(kbhit()) break; } while(1); setvect(INT,ant_irq); /* Restaura el vector de interrupción */ getch(); } void prog_8250() { outport(0x3fb,0x80); outport(0x3f8,0x06); outport(0x3f9,0x00); outport(0x3fb,0x07); outport(0x3fc,0x08); outport(0x3f9,0x01); } void interrupt rx() { flag=1; dato=inportb(0x3f8); outportb(0x20,0x20); } /* /* /* /* /* /* DLAB=1 */ divisor (Low) =0x06 vel 19200 baudios */ divisor (High)=0x00 vel 19200 baudios */ 8 bits ,2 stop, sin paridad */ out 2 = 1 */ Interrumpe cuando llega dato */ /* EOI */