Sobre entrada de datos y circuitos eléctricos (Puerto paralelo II)


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:

Valor de los pines del puerto. Extraido de CodeProject
Valor de los pines del puerto. Extraido de CodeProject

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 0x378 para el puerto de datos, 0x379 para el puerto de status y 0x380 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(0x379) para ver el estado de ese pin.

switch simple

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:

Switch dobleEn 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.

switch doble breadboard
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:

switch pulldown

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:

switch pullup

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

switch pulldown breadboardswitch pullup breadboard

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í, lo 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.


11 respuestas a “Sobre entrada de datos y circuitos eléctricos (Puerto paralelo II)”

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

  2. 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 🙂

  3. 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. [email protected]

  4. Hola que bien, en el colegio me dejaron este trabajo pero bajo el lenguaje visual basic, por favor me pueden colaborar, gracias

  5. Primero muchas gracias por estos dos post acerca del puerto paralelo me han sido de gran ayuda. Por otra parte me podrias explicar la sentencia dentro del if ‘((input & BOTON1) ==0)’ que compara o que es lo que hace, ‘amp’ es una variable ¿o como?. De antemano muchas gracias.

  6. Me alegra que me hagas esa pregunta, Josue, porque en realidad ese «amp;» se debe a un error de codificación de la página y no me había dado cuenta hasta que lo has mencionado :). En ese punto debería salir simplemente un «&».

    Creo que ya lo he arreglado, así que echale un nuevo vistazo al código.

    Un saludo.

  7. las resistencias deberian ser desde 2,2K a 10K?? si le pones una menor seria demasiada intensidad teniendo en cuenta que la I de entrada max es de aprox 2,4mA??

  8. Hola!!
    Sabes que tengo una duda, estoy haciendo un proyecto con microcontrolador picaxe y necesito que medinante tres estados logicos de un selector 100,010,001 pueda leer un mensaje en pantalla por medio de puerto paralelo, queria saber si esto se puede lograr mediante java (NETBEANS…

  9. ¡Excelente! ¡Excelente! Espero mañana empezar a usar todo esto. Una preguntísima. Para generar los 5 volts puedo usar algún pin de salida?

  10. estaba haciendo pruebas con el puerto de estado recibiendo datos, todo iba bien pero ahora ya no me lee datos que puede ser, no hice corto circuitar nada. de lo bien que estaba funcionando ya no funciono, que puede ser???
    gracias por sus respuestas