PROGRAMACION DEL PUERTO SERIE DEL PC
El canal serie del PC es uno de los recursos mas comunes para la conexion de perifericos, como pueden ser dispositivos de puntero (mouse) o de comunicacion (modem, cables de conexion entre PCs). Esta compuesto por un integrado del tipo 16550 en modelos actuales, mientras que en la "antiguedad" (hace 4 dias) se hablaba del 8250. Os suena la palabra UART verdad?? pos se referia a este tipo de chipset. Vamos a tocar la UART!!!
La siguente tabla nos muestra el ejemplo de 4 puertos COM tipicos. Lo mas normal es que tanto el COM1 como el COM2 esten ahora integrados en la placa base, o puestos en una tarjeta controladora. Los otros 2 se suelen configurar con el modem (interno)
Com | Direccion Base | IRQ |
Com1 | 3F8 | 4 |
Com2 | 2F8 | 3 |
Com3 | 3E8 | 4 |
Com4 | 2E8 | 3 |
Ten en cuenta que el Com1 y el Com3 comparten la misma IRQ. Lo mismo pasa con Com2 y Com4.
Configuracion del puerto serie
Cada uno de los puertos COM tiene 7 registros a comentar:
Registro base+0: tiene 3 funciones
Registro base+1: tiene 2 funciones
Registro base+2:
Registro base+3:
Registro base+4:
Registro base+5:
Registro base+6:
Algunos de los registros antes esmentados no los usaremos. Tan solo son importantes LCR, BRDH, BRDL, LSR, THR y RDR.
Debemos configurar correctamente el puerto serie ANTES de trabajar con el. Y para configurarlo yo hago lo siguiente:
Primero configuro el LCR, pero poniendo el bit DLAB a 1. Luego configuro el BRDL, seguidamente el BRDH y finalmente vuelvo a configurar el LCR con los mismos bits que antes, pero esta vez el bit DLAB a 0.
Explicacion de los Registros
Con este registro definimos (configuramos) los parametros de la comunicacion serie. DLAB es el bit de mas peso y Num(1) el de menos peso.
DLAB | Break | Stick | Tipo | Paridad | Stop | Num(2) | Num(1) |
Num (1 y 2): Indica el numero de bits de datos en cada palabra que vamos a utilizar. Se configura con la siguente tabla:
Num2 | Num1 | Nš Bits |
0 | 0 | 5 |
0 | 1 | 6 |
1 | 0 | 7 |
1 | 1 | 8 |
Stop: Indica el numero de bits de stop que enviara (o esperara) del puerto. Lo normal es poner un 0 en este bitpara conseguir un bit de Stop. Pero si ponemos un 1, el canal usara 1,5 bits de stop si el numero de bits de la palabra (configurada en num) es de 5. En caso de que sean mas de 5, se usaran 2 Stop bits. Puede que en determinados momentos nos sea de utilidad, pero yo lo unico que veo es que envia 10 bits en lugar de 9, cosa que implica mas tiempo en el envio, asi que yo siempre pongo un 0 (1 stop bit).
Paridad: Indica si hay paridad en la comunicacion serie. Con un 0 le diremos que no queremos paridad, y con un 1 que si. La paridad es una manera de detectar errores, pero debemos programar nuestro programa para que lo detecte. Asi que de momento pondremos un 0... ;)
Tipo: Este bit nos indicara ell tipo de Paridad que vamos a usar. Con un 0 le indicaremos que vamos a mirar la paridad de modo impar, mientras que con un 1 miraremos la paridad de modo par.
Stick: Indica el nivel que usaremos para la paridad: si ponemos un 0, contaremos el numero de '1' para la paridad. Y si ponemos un '1' contaremos el numero de '0' para la paridad.
Break: Fuerza un corte de la comunicacion. Si lo dejamos a 0 no pasara nada, pero si lo ponemos a 1 cortamos la comunicacion y forzamos la salida a '0'
DLAB: Bit interno. Desconozco su uso, pero no es decisivo. Lo que hago (asi me lo enseņaron) es configurar una vez el puerto poniendo a 1 este bit, configurar la velocidad (los 2 registros) y luego volver a configurar el puerto poniendo un 0.
Ejemplo:
Si queremos una configuracion de 8 bits, 1 stop bit, sin paridad para un COM2 lo que tendremos que hacer es enviarle a la direccion base mas 3 (este caso como la direccion base es 2F8, nos dirigimos a 2F8+3=2FB) le enviaremos un 10000011 (0x83, con el DLAB a 1) configuramos la velocidad y volvemos a configurar el puerto pero con DLAB a 0 (00000011, 0x03).
BRDH y BRDL (Baud Rate Divisor, BRD)
Es un registro de 16 bits compuesto por BRDH y BRDL que define la velocidad de la comunicacion del canal de comunicacion. El valor a cargarle lo obtendremos al aplicar la siguiente formula:
Debemos tener en cuenta lo siguente: que para un XT el clock era de 1,78 MHz, y en un AT es de 1,84 MHz.
Para un AT, si queremos 1200 baudios debemos hace la siguiente operacion:
1,84M / (16*1200) = 95,83
Ese resultado lo redondeamos, 96 (0x60 en hexadecimal). ese es el resultado, 00 96, lo que hay ke enviarle. En BRDL el 96 y el 00 en BRDH. Juntando con el ejemplo de antes, una configuracion de COM2 de 1200 baudios, sin paridad, 8 bits de datos y 1 bit de stop:
outportb (0x2FB,0x83);
outportb (0x2F8,0x60);
outportb (0x2F9,0x00);
outportb (0x2FB,0x03);
Enviar datos por el puerto serie
Para enviar una palabra por el puerto COM debemos enviar el dato a THR (a la direccion de Hardware base del puerto). Si hablamos del COM2, deberemos hacer:
outportb (0x2F8,dato);
El dato sera enviado por el puerto serie, SIEMPRE QUE ESTE LIBRE Y NO OCUPADO. Para sabe si esta ocupado debemos mirarlo en el LSR (Line Status Register, 2FD) de la siguiente manera
Si ((LSR&0x20) es igual a 0x20) entonces podremos enviar el dato. Si hablamos del COM2, la pregunta en 'C' seria:
if ((outportb (0x2FD)&0x20)== 0x20)
Poner un getch, un scanf o kualkier tipo de funcion de consola tipica (esperar que el usuario introduzca un dato) puede ser muy perjudicial para el programa, porque si ese momento alguien envia un dato, no se va a leer! va a llegar, pero si durante el tiempo ke el usuario esta dudando ke poner llega otro dato por el canal serie la informacion anterior seria machacada por el nuevo dato, y perderiamos parte de la informacion!!!
Leer datos del puerto serie
Para leer una palabra del puerto COM debemos leer del registro RDR (de la direccion base del puerto). Si hablamos del COM2 deberemos hacer:
inport (0x2F8);
Claro esta que la lectura deberemos guardarla en algun sitio (un char seria lo mas indicado).
Pero como en el caso de la escritura en el puerto, tambien nos interesaria que el puerto nos avisara cuando el puerto tiene un dato nuevo. Deberemos saberlo de la siguiente manera:
Si ((LSR&0x01) es igual a 0x01) entonces podremos recibir el dato. Si hablamos del COM2, la pregunta en 'C' seria:
if ((outportb (0x2FD)&0x01)== 0x01)
Un ejemplo de lo que he contado hasta ahora: Un programa que configura el puerto serie COM2, envia todo el rato el dato 'A' por el canal, y en caso de que reciba algo lo muestra por pantalla.
//By Stealth Programa de muetra del
funcionamiento de envio/Recibo del canal serie
//Code start
#include <stdio.h>
#include <dos.h>
void main (void)
{
outportb (0x2FB,0x83);
outportb (0x2F8,0x60);
outportb (0x2F9,0x00);
outportb (0x2FB,0x03);
// Puerto configurado para 1200 baudios, 1 stop bit, 8 bits de datos y no paridadfor(;;)
{if ((outportb (0x2FD)&0x01)== 0x01) //en caso de recibir
{
printf ("%c",inportb(0x2F8)); // Lo sakamos por pantalla
}if ((outportb (0x2FD)&0x20)== 0x20) //en caso de poder enviar
{
outportb (0x2F8,'A'); // Lo mandamos por el puerto
}}
}
//Code ends