Sobre entrada de datos y circuitos eléctricos (Puerto paralelo II)
8 de Septiembre de 2009 por cogollo
Hace unos meses ya hablamos de la salida de datos a través del puerto paralelo, pero dejé para más tarde hablar de la entrada. Bien, supongo que ya es lo bastante tarde.
Para que quede claro, veremos qué hay que hacer para poder leer datos digitales a través del puerto paralelo del PC, mediante un programa en C y en un entorno Linux (aunque los circuitos que usaremos son validos para cualquier entorno).
Recapitulemos: el conector del puerto paralelo tiene 25 pines, que se corresponden con tres lineas de datos diferentes (Data, Control y Status) según este esquema:
Además tenemos que Data es de entrada/salida, Control es sólo de salida y Status sólo de entrada. Y que en un determinado pin 0V representan un 0 lógico y 5V un 1 lógico, salvo en los pines que están invertidos (Y este puede ser un buen momento para repasar el primer artículo).
Vimos también que para sacar un byte a través de un puerto hay que usar la función:
outb(Byte,Puerto);
donde Byte es el byte de datos que queremos mostrar y Puerto es el numero del puerto que queremos usar y que en la mayoría de los casos es 0×378 para el puerto de datos, 0×379 para el puerto de status y 0×380 para el puerto de control.
Pues bien, para la entrada de datos tenemos una función similar:
inb(Puerto);
que devuelve un byte de datos leidos a traves de Puerto. Por ejemplo, para leer desde el puerto de status y guardarlo en la variable input pondríamos:
short int input = inb(0x379);
Y eso es todo amigos, pero antes de ponernos con un programa de ejemplo deja que aclare una cosilla sobre circuitos eléctricos.
Breve anotación sobre los circuitos eléctricos
En un circuito eléctrico, un cable que no está conectado a nada no está a 0V. Vale, puede que para ti esto sea una perogrullada, pero si no estas familiarizado con la electricidad, lo más inmediato es pensar que un cable que no esta conectado, por el que no pasa electricidad, no tiene un voltaje, o sea que está a 0V.
Pero no es tan sencillo. Y por lo que he podido comprobar mucha gente tiene esa creencia, que por otra parte causa muchos problemas a la hora de intentar leer datos digitales.
Te pondré un ejemplo: fíjate en el esquema de aquí abajo a la izquierda. Uno podría pensar en usar un montaje como éste para detectar la pulsación de un boton. Si Entrada es uno de los pines del puerto de estatus podemos usar inb(0×379) para ver el estado de ese pin.

Cuando pulsamos en interruptor el circuito se cierra,el pin de entrada está a 5V y leemos un 1 en ese bit. Cuando se suelta el interruptor el circuito se abre de nuevo y el pin de entrada queda “al aire”, con lo que debe de estar a 0V y leemos un 0 en su bit, ¿no? Pues más bien no.
En primer lugar desconocemos cómo es el circuito detrás de ese pin. Dentro de tu ordenador el mismo circuito que se encarga de la lectura del pin podría estar induciendo ahí un cierto voltaje desconocido para nosotros. Podría ser 0, podría ser 5, podría ser cualquier valor entre 0 y 5. You never know.
Eso ya es bastante malo. Pero es que además la configuración del circuito podría hacer que éste actuase como un diminuto condensador y cualquier carga igualmente diminuta que quede atrapada en él produciría también un voltaje desconocido para nosotros. Incluso puede darse el caso de que el cable que estemos usando para la entrada actúe como una antena de radio y nuestra entrada se vea afectada por todo tipo de interferencias.
Para que nos entendamos, y reformulando la primera sentencia para un caso más concreto: Un pin de entrada que no está conectado a nada no está a 0V, tendrá un voltaje arbitrario y desconocido para nosotros.
¿Y cómo salimos de ésta pues? No te dejes los pines al aire. Por ejemplo: En lugar del interruptor simple de antes podemos usar un selector como el del siguiente esquema:
En este caso, cuando el selector está arriba tenemos la entrada conectada a los 5V (1 lógico) y cuando está abajo la entrada pasa inmediatamente a estar a 0V (0 lógico). Aquí cada estado del selector está asociado inequívocamente a un valor del bit leído. Puedes encontrar este tipo de selectores bajo los botones del ratón de tu ordenador. De los tres pines que tienen, el que se encuentra bajo el pulsador está normalmente conectado al del otro extremo y cuando se presiona el pulsador pasa a estar conectado al del centro.

Ejemplo de conexión de un selector. En esta imagen y en las siguientes el cable azul está conectado a tierra (0V) y el naranja a 5V.
Otra forma de mantener la entrada siempre a un voltaje conocido es usar una resistencia pull-up o pull-down. A continuación tienes el esquema del montaje de una resistencia pull-down:

Se emplea una resistencia de valor intermedio. Lo más habitual es que sea de 1 o 10 KΩ. Cuando el interruptor está cerrado, la resistencia que ofrece la rama del circuito que va a los 5 V es mucho menor que la que va hasta los 0V así que el voltaje en el nodo donde confluyen las tres ramas del circuito es prácticamente 5V. Lo bueno es que aquí, cuando el interruptor se abre, el pin de entrada no queda aislado, sino que sigue conectado a tierra a través de la resistencia. Cualquier carga que hubiera quedado atrapada en el circuito se descarga rápidamente a través de la resistencia y la entrada se pone a 0V.
Una resistencia pull-up funciona igual, pero se conecta a los 5V como en este esquema:

En este caso con interruptor cerrado la rama de tierra tiene mucha menos resistencia que la de 5V y el voltaje en el nodo es de (prácticamente) 0V. Al abrirse el interruptor, la resistencia mantiene la entrada conectada a los 5V y el voltaje de entrada sube hasta ese valor.
Aquí abajo puedes ver como sería la entrada con una resistencia pull-dow (izquierda) y otra pull-up (derecha).
De vuelta al software
Bien, ahora que ya sabes como conectar tus sensores al puerto paralelo veamos un programita que lea el valor de esos sensores. Este ejemplo lee el valor de dos interruptores conectados a los pines 3 y 4 del puerto de estatus. Los interruptores usan resistencias pull-up por lo que la lectura de un 0 lógico en uno de esos pines representa una pulsación en su interruptor asociado. Un mensaje en pantalla dice qué interruptor se está pulsando.
//Este programa monitoriza el valor estado de 2 pulsadores conectados //a las lineas de STATUS y muestra por pantalla si se ha pulsado alguno de ellos #include #include //sys/io.h es donde estan definidas las funciones ioperm() y inb() #include // Esta es la direccion mas frecuente para el puerto paralelo #define DATAPORT 0x378 //Y esta es la direccion de las lineas de Status #define STATUSPORT DATAPORT+1 #define TIEMPOCONPULSACION 300000 #define TIEMPOSINPULSACION 10000 //Mascaras para los pines 3 y 4 del puerto de Status #define BOTON1 8 #define BOTON2 16 int main() { //Obtenemos permiso de acceso para la direccion de DATAPORT y //las 2 siguientes if (ioperm(DATAPORT, 3, 1)) {perror("ioperm"); return 1;} //Entramos en el bucle principal del programa while(1) { //input va a guardar el valor leido desde el puerto short int input=0; //pulsacion nos va a decir que boton se ha pulsado short int pulsacion = 0; //Leemos el byte de las lineas de Status y lo guardamos en input input=inb(STATUSPORT); //Si input tiene el bit BOTON1 a cero es que se ha pulsado el boton correspondiente if((input & BOTON1) ==0) //Ponemos pulsacion a 1 para indicarlo y sacamos un mensaje por pantalla pulsacion=1; //Si tiene a cero el bit BOTON2 es que se ha pulsado el otro boton else if((input & BOTON2) ==0) pulsacion=2; //Si pulsacion !=0 es que se ha pulsado algun boton. //Esperamos TIEMPOCONPULSACION microsegundos para no inundar la pantalla de texto if(pulsacion) { printf("Pulsando boton %d\n",pulsacion); fflush(stdout); usleep(TIEMPOCONPULSACION); } //En caso contrario solo esperamos TIEMPOSINPULSACION microsegundos else usleep(TIEMPOSINPULSACION); } //Antes de terminar el programa dejamos los permisos de acceso //a los puertos como estaban if (ioperm(DATAPORT, 3, 0)) {perror("ioperm"); return 1;} //El programa termina sin errores return 0; }
Esto debería dejar más o menos claro cómo leer datos en las líneas de estatus, pero ¿qué pasa con las líneas de datos? ¿No habíamos quedado que son de entrada y salida? Bueno sí, los son. Al menos en cualquier ordenador razonablemente moderno, pero en sus primeras versiones el puerto de datos era sólo de salida.
Suponiendo que tu ordenador tenga menos de 15 años puedes configurar las líneas de datos para entrada poniendo a 1 el bit 5 del puerto de control (este bit no se corresponde con ningún pin “físico” de puerto de control, sólo está ahi para seleccionar el modo del puerto de datos). Es decir:
outb(0x10,0x380);
Después de eso ya puedes leer la entrada como hicimos antes, solo tienes que cambiar la dirección del puerto.
short int input = inb(0x378);
Otra vuelta de tuerca
Para terminar te propongo un programíta que usa tanto entrada como salida de datos por el puerto paralelo. Este programa detecta la pulsación de dos botones como el ejemplo anterior, pero ya no dice por pantalla qué botón se ha pulsado. Ahora tenemos 8 LEDs conectados a las 8 líneas de datos y uno de ellos está encendido. Lo que hace el programa es cambiar el LED que se enciende una posición a la derecha cuando se pulsa el botón derecho y una posición a la izquierda cuando se pulsa el botón izquierdo. Aunque mejor mira el vídeo que tenemos a continuación, que no deja lugar a dudas.
Bueno, pues el programa que hace eso en realidad es muy similar al primero. Aquí lo tienes:
//Este programa monitoriza el valor estado de 2 pulsadores conectados //a las lineas de STATUS y cambia la posicion del led que esta iluminado #include #include #include //Direccion del puerto paralelo #define DATAPORT 0x378 #define STATUSPORT DATAPORT+1 #define TIEMPOCONPULSACION 300000 #define TIEMPOSINPULSACION 10000 #define BOTON1 8 #define BOTON2 16 int main() { //Obtenemos permiso de acceso para DATAPORT if (ioperm(DATAPORT, 3, 1)) {perror("ioperm"); return 1;} //light va aguardar el estado de la salida del puerto. //Lo hacemos igual a 00000001 en binario unsigned short int light=1; //y lo sacamos por el puerto paralelo para que se encienda el primer led outb(light, DATAPORT); //Bucle principal while(1) { short int input=0; short int pulsacion = 0; input=inb(STATUSPORT); if((input & BOTON1) ==0) //Se ha pulsado el boton 1 pulsacion=1; else if((input & BOTON2) ==0) //Se ha pulsado el boton 2 pulsacion=2; if(pulsacion==1) { //Si light es (00000001)b lo convertimos en (10000000)b if(light==1) light=128; //Si no, desplazamos el 1 un lugar a la derecha else light>>=1; //Sacamos el nuevo valor por el puerto outb(light, DATAPORT); usleep(TIEMPOCONPULSACION); } else if(pulsacion==2) { //Si light es (10000000)b lo convertimos en (00000001)b if(light==128) light=1; //Si no, desplazamos el 1 un lugar a la izquierda else light<<=1; //Sacamos el nuevo valor por el puerto outb(light, DATAPORT); usleep(TIEMPOCONPULSACION); } else usleep(TIEMPOSINPULSACION); } //Restauramos los permisos de acceso al puerto if (ioperm(DATAPORT, 3, 0)) {perror("ioperm"); return 1;} //El programa termina sin errores return 0; }
Ale, pues ya está, con esto terminamos la “parte teórica” del puerto paralelo. En el futuro intentaré hablar de cómo interactuar con cosas concretas (motores, pantallas LCD, disqueteras…), pero ésa es otra historia y debe ser contada en otro momento.
Publicado en Hackeos e ideas, Nuestros hacks, Open Source |
5 Comentarios »



8 de Septiembre de 2009 a las 18:15
Hola, justamente en unos días me iva a poner con esto, pero te agradezco que me hayas ahorrado ponerme a probar a ver cómo lo hacía.
Por cierto, ya que estamos con puerto paralelo te muestro el robot que he hecho con el puerto paralelo: http://www.youtube.com/watch?v=UBoGiuFIPLM
No es gran cosa, pero es curioso. También tengo algunos vídeos controlando servos y demás, si alguien le interesa le puedo ayudar.
9 de Septiembre de 2009 a las 20:06
Hombre, pues me alegro de que estas cosillas sean útiles después de todo.
Por cierto que veo por el vídeo que tu también eres de la hermandad de la cinta adhesiva
7 de Noviembre de 2009 a las 6:47
Hola, pilotox, me gustaria que me ayudaran con la lectura de datos del puerto paralelo, Solo se usar entorno Windows y tengo instalado el Borland. Me uno a la Hermandad xD .. desde Mexico. electronicaure@hotmail.com
17 de Noviembre de 2009 a las 1:49
Hola que bien, en el colegio me dejaron este trabajo pero bajo el lenguaje visual basic, por favor me pueden colaborar, gracias
3 de Diciembre de 2009 a las 16:10
Buenas.
…y por ejemplo, una aplicación real a día de hoy, construirte un interfaz para conectar controles de máquina recreativa al ordenador. Si no recuerdo mal, hasta 64 entradas por un par de euros: http://www.retrovicio.com/tutoriales/interfaz-puerto-paralelo