Accediendo al mundo real: El puerto paralelo


Si hay una tecnología que se está extinguiendo ahora mismo, que se va desechando lenta pero inexorablemente, esa es la del puerto paralelo. Y es una lástima. No sólo porque este conector ya estuviera presente en el primer PC que fabricó IBM en 1981, también porque el puerto paralelo ha sido uno de los mayores aliados del hacking. Con sus 25 pines, no es precisamente el conector más discreto que puedes encontrar en un ordenador . Ni el más rápido. Pero tiene una cosa que lo hace muy atractivo para el aficionado: Es muy fácil de usar. Casi trivial.

El puerto paralelo según Wikipedia
El puerto paralelo según Wikipedia

Básicamente, lo que podemos hacer con estos pines es ponerlos a 0 o 5 voltios. A voluntad. Puede que esto no parezca gran cosa, pero es suficiente para activar un relé que, a su vez, active una radio, una bombilla, una lavadora, etc. Así que  va a permitir, de una manera muy directa, que nuestro ordenador manipule el mundo exterior.

O sea, lo mismo que un microcontrolador como Arduino. Sólo que, claro, no tienes que gastarte 30 pavos en el dichoso Arduino y la potencia y la memoria del PortatilQueTeRegalóTuTíoPorqueEstáViejoYDeTodasFormasIbaATirarlo siguen siendo cientos de veces mayores que las de cualquier micro que puedas comprar con dinero. Cierto que un Arduino cabe en la palma de la mano, pero para «aplicaciones estáticas» (esto es, para chismes que no tengan que ir cosidos a la ropa o bajo el sillín de una bici) yo diría que usar directamente el puerto paralelo es bastante más práctico.

Pero empecemos por el principio. Para ir cogiéndole el tranquillo a esto te propongo empezar con unos sencillos juegos de luces como estos:


Controlando 8 leds con el puerto paralelo on Vimeo.

Aprenderemos para qué sirven los distintos pines del puerto paralelo y cómo sacar datos a traves de ellos (leer datos es ligeramente más complicado y lo dejaremos para futuras entregas) . Todo ello en un estupendo entorno linux, pero si tienes Windows puedes ver cómo hacer esto mismo aquí. Si tienes un Mac…  Bueno, no creo que los Mac sigan montando de éstos. No son lo bastante bonitos, supongo.

Los Puertos

Como ya hemos dicho, el conector del puerto paralelo consta de 25 pines. Lo que no hemos dicho aún es que esos pines pertenecen en realidad a tres puertos distintos.

Valor de los pines del puerto. Extraido de CodeProject
Valor de los pines del puerto. Extraído de CodeProject

El puerto de datos (DATA), permite transmitir un byte (8 bits) de datos. Cada uno de los bits se corresponde con uno de los pines D0 a D7. Por tanto, en conjunto, se pueden transmitir valores entre 0 y 255 en formato binario. Por ejemplo, para transmitir el 139, 10001011 en binario, se pondrán a 5Voltios los pines D7, D3, D1 y D0. Este puerto puede configurarse para servir tanto de entrada como de salida de datos.

El puerto de control (CONTROL)  es sólo de salida y se corresponde con los pines C0 a C3, con lo que en principio nos permite trasmitir valores entre 0 y 16. En un principio este puerto se utilizaba para pasarle comandos inmediatos a la impresora como el salto de línea o el reset y  los pines conservan los nombres que se les dieron en esa época. Y por razones históricas tambien, C0, C1 y C3 están invertidos, lo que quiere decir que invierten la salida y, al contrario que los pines normales, representan el 0 con 5 voltios y el 1 con 0 voltios. Por ejemplo, el 6 es 0110 en binario, pero como los bits 0, 1 y 3 están invertidos tendríamos C3, C2 y C0 a 5 voltios y C1 a 0.

Por último tenemos el puerto de estado (STATUS). Se usaba originalmente para recibir el estado de la impresora, también ha heredado los nombres de los pines de aquella época y también presenta algún pin invertido. Es un puerto sólo de entrada, así que no lo vamos a usar por el momento. Pero vete familiarizándote con él.

¿Me sigues, no? ¿No? bueno, es un poco confuso así contado,  pero espero que esta tabla te aclare un poco las cosas.

Signal BIT Modo Direction
-Strobe C0 Invertido Salida
-Auto Feed C1 Invertido Salida
-Initialize C2 Normal Salida
-Select C3 Invertido Salida
-Error S3 Normal Entrada
+Select In S4 Normal Entrada
+Paper End S5 Normal Entrada
-Acknowledge S6 Normal Entrada
+Busy S7 Invertido Entrada

¿Y el resto de los pines? ¿Los que están en verde en el esquema? Pues están conectados a tierra y nos van a dar el voltaje de referencia de 0 voltios

Enchufando cosas.

Bueno, ya sabemos lo que puede hacer cada agujerete, pero ¿cómo enchufamos ahí nuestros inventos?

De momento, y como todavía estamos hablando de aprender a manejar todo esto y de experimentar, te recomiendo que construyas un adaptador para placa de pruebas (protoboard) como éste:

Adaptador puerto paralelo

Sólo tienes que pinchar el adaptador a la placa de pruebas para poder acceder a los pines del puerto paralelo directamente desde tus prototipos, lo que va a facilitar muchísimo la experimentación. Y aparte del cable paralelo (que seguro que tienes por ahí de alguna antigua impresora), sólo necesitas un trozo de placa perforada, un conector DB-25 macho y cable unifilar de varios colores (el de pinchar en la protoboard).

Adaptador pp por delante (conector)

Lo que vamos a hacer es sacar un cable de cada pin (salvo de los pines conectados a tierra) y ordenar los extremos en línea, usando un color en cada tipo de puerto para distinguirlos fácilmente.

Adaptador pp por delante (cableado)
En rojo el puerto de control, en amarillo el de datos y en verde el de estado. Tambíen se ven, en negro, los dos cables conectados a tierra.

Tienes que dejar como medio cm de cable pelado en dicho extremo antes de soldarlo para que sobresalga por debajo de la placa y se pueda pinchar fácilmente en la protoboard (Posiblemente tendrás que enderezar las puntas un poco para que encajen bien).

Adaptador pp por detras (detalle de los pines)
Detalle de los extremos de los cables por debajo de la placa.

Un último consejo: Suelda todos lo pines conectados a tierra entre sí y a la placa perforada. Luego conéctales un par de cables, pero no pongas los extremos a la misma altura que los otros. Déjalos más cerca del conector, de manera que cuando pinches el adaptador a tu protoboard los cables de tierra se conecten a las tiras largas verticales mientras el resto de los cables se conecta a las cortas horizontales.

Adaptador pp por detras (general)
Los pines de tierra estan soldados a los dos cables que sobresalen cerca del centro de la placa. (Haz click en la imagen para verla ampliada).

Nuestro primer experimento.

Para nuestro primer contacto con el puerto paralelo simplemente vamos a conectar 8 leds cada uno a un pin de datos. De este modo tendremos feddback visual inmediato de lo que está pasando en cada momento. Para realizar la conexión puedes seguir este esquema:

esquema-conexion-leds-puerto-paralelo
Los cuadrados con el 470 escrito representan resistencias de 470 ohmnios. Extraído de CodeProject.

Las resistencias que aparecen en el esquema sirven para limitar la corriente que pasará por cada led. Aunque en general son imprescindibles porque, si no, se queman los leds, resulta que cada pin del puerto paralelo sólo puede suministrar 40mA como máximo. Una corriente suficientemente baja para que la aguanten la mayoría de los leds.

Si has usado el adaptador que hicimos antes y has pasado de las resistencias en montaje debería tener esta pinta:

Conexión de los leds
El cable negro largo conecta los leds a tierra cerrando el circuito.

Con esto ya podemos ponernos a programar. Hasta aquí todo lo que te he contado es válido para cualquier sistema operativo. A partir de este momento nos centraremos en Linux, pero te recuerdo que puedes ver cómo hacer esto mismo en un  ordenador con Windows aquí.

En concreto vamos a programar en C. ¿Por qué C? Porque es el lenguaje de programación estandar en Linux y digamos que está muy integrado con el sistema operativo. Nuestro primer programa va a ser muy sencillo, simplemente nos irá pidiendo números por consola y los mostrará en binario a través de los leds. Pero vete echándole un vistazo al código ya que estamos:

#include 
#include 
//asm/io.h es donde están definidas las funciones ioperm() y outb()
#include 

/* Esta es la direccion más frecuente para el puerto paralelo*/
#define DATAPORT 0x378

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 un bucle infinito
  while(1)
  {
    int input;

    //Le pedimos al usuario que introduzca un número
    printf("Introduce un número entre 0 y 255 (-1 para salir)\n");
    //Leemos el valor introducido y lo guardamos en "input"
    scanf("%d", &input);
    //Si "input" vale "-1" salimos del bucle
    if(input==-1) break;
    //Si "input" no está entre "0" y "255" (y no era "-1" no nos
    //interesa, así que volvemos directamente al comienzo del bucle
    if(input < 0 || input > 255) continue;

    //Si hemos llegado hasta aqui es que "input" vale entre "0" y
    //"255" y podemos sacarlo por el puerto paralelo
    outb(input, DATAPORT);
  }

  //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;
}

Para compilar un programa en C como éste, guárdalo en un archivo de texto cuyo nombre termine en «.c» como «parporttest.c» y compílalo mediante gcc (gcc es el compilador de C que viene por defecto con casi todas las distros de Linux, probablemente ya lo tengas instalado aunque no lo sepas) escribiendo «gcc Wall parporttest.c -o parportest» en la consola. Esto generará un fichero ejecutable que se llama «parportest» y que podrás ejecutar cuando quieras desde consola escribiendo «./parportest».

Pero volvamos al código. Nos interesan dos funciones, sobre todo:

ioperm();
outb();

La primera función  modifica los permisos de acceso de los puertos. Recibe tres enteros. El primero le indica la dirección del primer puerto cuyos permisos queremos modificar, el segundo es el número de puertos y el tercero debe ser «1» para conceder el permiso y «0» para quitarlo. De esta manera, para tener permiso de acceso al puerto de datos y a los dos siguientes (que son STATUS y CONTROL) ponemos:

ioperm(DATAPORT, 3, 1);

y, al contrario,  para dejarlo todo como estaba al final del programa (quitando los permisos) ponemos:

ioperm(DATAPORT, 3, 0);

La segunda función es la que hace la magia. Se encarga de mandar un byte de datos a un determinado puerto. Recibe dos enteros, el primero es el byte que se quiere enviar (debe estar entre 0 y 255) y el segundo es la dirección del puerto. La dirección del puerto de datos, en la gran mayoría de los ordenadores es «0x378», por eso al principio del programa hemos puesto:

#define DATAPORT 0x378

Ahora, para enviar el contenido de la variable «input» al puerto de datos (cuya dirección está en «DATAPORT») usamos:

outb(input, DATAPORT);

Si queremos sacar el dato por el puerto de control, cuya dirección es igual a la dirección del puerto de datos más dos, tenemos que poner:

outb(input, DATAPORT+2);

Fácil ¿verdad? Pues ejecuta el programa a ver qué pasa. Por cierto. Importante. Hace falta tener privilegios de root para que «ioperm» funcione correctamente, así que acuerdate de ejecutar el programa desde una cuenta de administrador.

salida-puerto-datos
Salida del puerto de datos para 13 (00001101 en binario) y 167 (10100111 en binario)

Prueba unos cuantos números y observa el comportamiento de los leds. Cuando te canses puedes intentar modificar el programa y las conexiones para usar el puerto de control  en lugar de el de datos. No debería ser muy difícil con lo que ya sabes, y así practicas.

salida-puerto-control
Salida del puerto de control para 0 (0000 en binario) y 13 (1101 en binario).

¿Ves lo que te decía de los pines invertidos?

Deberes

Y para terminar (que ya va tocando) aquí te dejo el código del programa con el que hice el video del principio. Tampoco debería darte muchos problemas a estas alturas.

#include 
#include 
#include 

// Esta es la direccion más frecuente para el puerto paralelo
#define DATAPORT 0x378 /* lpt1 */

//Duración de cada paso en microsegundos
#define DELAY 150000

//Numero de veces que se repite cada secuencia
#define REPEAT 4

void Blink();
void Alternate();
void ZigZag();

int main()
{
  //Obtenemos permiso de acceso para la direccion de DATAPORT y
  //las 2 siguientes
  if (ioperm(DATAPORT, 3, 1)) {perror("ioperm"); return 1;}

  //mode indique que secuencia es la que se esta ejecutando
  int mode=0;
  while(1)
  {
    int i;
    for(i = 0 ; i < REPEAT; i++)
    {
      int reset=0;
      //Comprueba el valor de mode
      switch (mode)
      {
        //Si vale 0 toca parpadeo
        case 0:
          Blink();
          break;
        //Si vale 1 toca alternar pares e impares
        case 1:
          Alternate();
          break;
        //Si vale 2 toca hacer un ZigZag
        case 2:
          ZigZag();
          break;
        //Si vale otra cosa, vovemos a empezar. Para ello
        // ponemos mode a -1
        default:
          mode=-1;
      }
    }
    //Despues de repetir REPEAT veces una secuencia pasamos 
    //a la siguiente sumandole 1 a mode
    mode++;
  }

  //Volvemos a cerrar los puertos
  if (ioperm(DATAPORT, 3, 0)) {perror("ioperm"); return 1;}

  return 0;
}

//Esta función hace parpadear los leds.
void Blink()
{
  //Ponemos todos los led a 1
  outb(255,DATAPORT);
  //Y esperamos 2*DELAY microsegundos
  usleep(2*DELAY);

  //Luego ponemos todos los leds a 0
  outb(0,DATAPORT);
  //Y volvemos a esperar
  usleep(2*DELAY);
}
//Esta función enciende alternativamente los leds pares y los impares
void Alternate()
{
  int i;
  //repetimos cuatro veces
  for(i=0;i<4;i++)
  {
    int output = 85;

    //Primero ponemos 85 que es 01010101 en binario,
    //con lo que se iluminan los leds impares, y esperamos 2*DELAY
    outb(output,DATAPORT);
    usleep(2*DELAY);

    //Desplazamos los bits de output un lugar a la izquierda,
    //con lo que nos queda "10101010"
    output = output << 1;

    //Al pasarlo al puerto paralelo se iluminan los leds pares
    outb(output,DATAPORT);
    usleep(2*DELAY);
  }
}

//Esta función enciende ulas luces al estilo del coche fantastico
void ZigZag()
{
  //output solo tiene el último bit distinto de 0
  int output = 1;
  //direction indica el sentido de movimiento de la luz: 1 izquierda,
  // 0 derecha. Empezamos hacia la derecha
  int direction = 1;
  while (output !=0)
  {
    //Sacamos output por el puerto
    outb(output, DATAPORT);
    usleep(DELAY);

    //Si dirección es distinto de cero
    //movemos los bits de output hacia la izquierda
    if(direction)
      output = output << 1;
    //Si no, los movemos hacia la derecha
    else
      output = output >> 1;

    //Si output vale 256 es que se nos ha salido
    //la luz por la izquierda así que cambiamos la dirección
    if(output==256)
    {
      output = output >> 1;
      direction = !direction;
    }
  }
}

Fíjate en que cada patrón de movimiento de las luces está definido en una función distinta: Blink(), Alternate() y ZigZag(). Si te ves con fuerzas te propongo que modifiques alguna de esas funciones para crear tu propia coreografía.

Y si mola, nos la pegas en los comentarios.


38 respuestas a “Accediendo al mundo real: El puerto paralelo”

  1. Hola,
    Me encanta vuestra página y la veo muy a menudo. La verdad llevaba mucho tiempo intentando encontrar algo como esto por la red, porque llevo bastante tiempo interesado en hacer algo como lo que exponéis aquí.
    Me gustaría saber cómo se podría hacer esto pero con el puerto USB (que me imagino que será más complicado al transmitir los datos en serie con la consiguiente necesidad de registros), ya bien ampliando el artículo que exponéis, o indicándome dónde puedo encontrar algo así por la red.
    Muchas gracias y seguid así

    Atentamente, un estudiante de ingeniería de la Carlos III

  2. Hombre… siempre te quedara el rs232 y a una mala (que muchos portátiles ya no lo llevan) un 18F2550 o 18F4550 y ya tienes conexión con el mundo exterior (por usb :P)

  3. Buen ariculo. Lo que no entiendo es porque se informa repetidamente sobre «21 pines», cuando – si no me equivoco – son 25 pines.

  4. En cuanto al USB, como dicen en:
    http://www.lvr.com/usbfaq.htm
    el USB es un puerto en un bus compartido, lo que significa que no puedes acceder directamente al puerto, tienes que acceder a cada uno de los dispositivos que haya conectados individualmente.

    A la hora de fabricar tus própios dispositivos esto se traduce en que tienen que ser capaces de «presentarse» adecuadamente cuando los conectas, pasandole un identificador al ordenador. Luego también tienen que ser capaces de saber qué paquetes de datos son los que van dirigidos a ellos.

    Total, mucho jaleo. Puedes utilizar un microcontrolador, como dice lowtech, para que se encargue de la comunicación entre el USB y el resto de tu dispositivo, pero añadirá mucha complejidad al proyecto.

    Para empezar con esto, es mucho más sencillo comprar uno de esos conversores de USB a puerto paralelo que venden para poder usar las impresoras viejas. Con eso puedes trabajar como con un puerto paralelo normal.

    Es poco elegante, quizá, pero tremendamente práctico.

  5. Comparar un portatil con un microcontrolador es casi tan absurdo como comparar la potencia de un portatil con la potencia de un ordenador de sobremesa.

    Un microcontrolador sirve para lo que sirve y te aseguro que pocas lavadoras, pocos coches, pocos telefonos, pocas maquinas de todo tipo llevan dentro un portatil viejo con windows o linux. Lo extraordinario de un ucontrolador es que en un chip de bajo coste tienes la versatibilidad de un sistema montado con un cpu memoria y perifericos en un chip.

    Bueno y si quieres potencia pues para eso están los dsp o directante los FPGA. Abre un televisor de plasma e identificame cuantos portatiles harian falta para sustituir los FPGA que llevan dentro.

    Enhorabuena por el tuto que seguro que despierta a mucha gente la curiosidad.

    1 saludo

  6. Hombre, elmasvital, no saques las cosas de contexto.
    Aquí estamos hablando de proyectos y prototipos caseros, no de producción industrial. Y lo que digo es que para un proyecto casero que no vaya a ser portable puedes usar el puerto paralelo de tu ordenador y evitarte las limitaciones de velocidad y de memoria que tienen los micros (y ahorrar algo de pasta).

    Ten en cuenta, además, que no hay mucha gente que tenga FPGAs por casa (mucho menos un programador de FPGAs) pero casi todo el mundo tiene puerto paralelo en su sobremesa.

    Y tampoco hay mucha gente que se esté construyendo un televisor de plasma casero. Ojalá.

  7. Muy bueno. Me acabas de dar muchas ideas. Y yo creyendo que el puerto paralelo estaba acabado.

    Un saludo y esperamos más temas.

  8. Hola amigo que buen proyecto..
    .no se si me puedas ayudar. eske mediante un programa que realice en C me genera valores y con esos valores genero una señal senoidal…pero lo ke kiero es sacar estos valores por el puerto paralelo….como lo haria??…te agradeceria tu respuesta…

  9. Bueno Pablo, el mayor problema es que el puerto de datos solo puede trabajar con valores enteros entre 0 y 256. Tendrás que convertir primero tu señal para que se ajuste a esta restricción, pero una vez hecho eso sóolo tienes que ir sacando cada valor consecutivamente con

    outb(valor,DIRECCION);

    como en los programas de ejemplo del artículo.

  10. Hola, ante todo, declarar que soy novato en el tema electrónico. A pesar de ello, he construdio un dispositivo que a traves de 8 pins de datos de puerto paralelo y los 4 pins de control, me controla 32 leds. La pregunta es: ¿se puede hacer lo mismo con el puerto usb, y cómo?
    Gracias adelantadas

  11. Hola Santi. manu ya nos preguntó sobre eso en el primer comentario y puedes leer respuestas en los comentarios 2 y 5.

  12. Yo tuve que incluir:
    #include

    Por cierto, ya estoy esperando el artículo en el que se explica la introducción de datos 😉

  13. Ey sebas, tienes razón. Parece que en algunos sistemas hay que sustituir el
    «#include <asm/io.h>»
    del ejemplo por:
    «#include <sys/io.h>»

    En cuanto al artículo sobre recogida de datos: Estoy en ello, pero mientras duren los talleres en Medialab me va a faltar tiempo para vivir.

    No se puede estar a todo, me temo 🙁

  14. Por cierto, parece que wordpress interpreta todo lo que hay entre «<» y «>» como una etiqueta html, por eso el primer comentario de Sebas salió mal.

    Si queréis que «<» y «>» aparezcan correctamente en los comentarios tenéis que poner «&lt;» y «&gt;» en su lugar.

    Bastante engorroso, ya lo se 🙁

  15. Hey esto si Mola compañero.
    Men encanto tu Post. Te felicito esta muy facil de entender y me ha servido mucho para aprender a usar el paralelo.
    Ahora a programar.
    GRACIAS!!

  16. He descubierto la página por casualidad y estoy flipando.Yo estoy preparando para mis niños una controladora , pero voy a usar el LOGO , el objetivo es estudiar el sistema de numeración binario y pasárnoslo bien en el recreo .Un saludo

  17. Cogollo:
    Queria comentarte que a mi me ha servido pero con un pequeño cambio: en vez de usar la libreria utilice .
    Utilizo ubunto 9.10 y el compilador g++.
    Te vuelvo a agradecer tu post y tambien la continuación de este donde lees el paralelo.

    Te comento que me ha servido mucho para cierto proyecto que estoy realzando. Muy facil de entender. Gracias

  18. Perdo las librerias no salieron pero eran:
    la que no me sirvio asm/io.h y la que si me sirvio sys/io.h

  19. Hola. No habia leido los comentarios anteriores. Ya estaba lo de la libreria en sys en vez de asm.

  20. Pues para los que no tenemos casi idea de programación, y nos tenemos que conformar con cosillas ocmo visualbas.. digo.. Gambas… el puerto paralelo es una bbendición. Para controlar telescopios (aficiones raras que tiene uno), por ejemplo, es genial: fácil de prorgamar, y fácil para añadirle cosillas (y mira que yose soldar muuy poco!).

  21. Hola , estoy intentando ejecutar los programas en una debian , y al ejecuatarlo me dice que no encuentra el fichero asm/io ,que supongo nos da los comandos para manejar los puertos.He ejecutado el programa como root , qué debo de hacer, he de compilar el programa en algún un directorio en especial,…

    Un saludo

  22. gracias quisiera saber como puedo conectar mi lavadora casera que es digital con mi computadora para dar la señal de enter gracias

  23. Muy buen tutorial. ¿Tendrás alguno donde hagas lectura de los pines de estado?
    Una aclaración: En los pines de estado dijiste que puedes mandar un número entre 0 y 16. Si son 4 bits, es de 0 a 15. ¡Saludos!

  24. Muy bueno el artículo!!!! LLevo dias buscando como cacharrear con una placa pentium y LPT…para aplicar proyectos de la misma forma como en arduino, pinguino, o raspberry.
    Gracias por la entrada y si alguien sabe de proyectos similares, por favor que postee….
    >>>a favoritos va>>>>