Como controlar 8 Servos RC por el puerto LPT...

Controlar un servo RC por el puerto LPT (paralelo) del ordenador?

 Pues si, debido a la compatibilidad de tecnoligias de un servo RC y un ordenador, es muy sencillo el controlar un servo por el puerto paralelo del mismo.
Para ello debemos tener bien claro lo que es el puerto lpt de un ordenador. si no lo sabeis, mirad el articulo que hay en mi pagina relacionado al puerto paralelo y como programarlo. Ademas, podeis ver el articulo relacionado con el control de un motor paso a paso mediante el puerto paralelo ya que este ultimo es mas practico que el primero...

A partir de ahora, solo necesitamos un poco de teoria relacionada con los servos RC y su funcionamiento. Vamos a ello.

Un servo RC es un dispositivo electro-mecanico muy practico, sencillo y barato que sirve para hacer casi cualquier cosa que seamos capaces de imaginar. Debido al gran desarrollo que estos aparatitos han alcanzado, su uso se ha pensado en que sea lo mas sencillo posible simplificando al maximo todos sus componentes y por consiguiente, reduciendo las posibilidades de fallo de las partes del mismo. Asi el mecanismo se convierte en algo muy fiable y de gran precision.

Pues, vamos a ver lo que es un servo por dentro:

Un Servo de Futaba S-148

Como podemos ver, hay cinco cosas en un servo:
    1: La carcasa exterior.
    2: Un grupo de engranajes que sirven como reductora.
    3: Un motor electrico.
    4: Un potensiometro que hace de sensor de posicion.
    5: Un circuito electronico.

Para que sirven cada una de sus partes:

El servo se invento para mover cosas a posiciones definidas con gran exactitud y velocidad. Es por ello que la base del sistema esta en un motor que es capaz de realizar tal movimiento. Pero para ello es necesario controlar ese movimiento, pues para ello se ha puesto un potenciometro que sirve de sensor de la posicion de la palanca del servo, asi la electronica es capaz de controlar el motor dependiendo de la posicion que deseamos alcanzar y la posicion en la que se encuentra ahora la palanca. Pero como necesitamos que el servo tenga bastante "potencia", se le ha puesto un grupo reductor que hace que el servo tenga gran capacidad de torque llegando a alcanzar los 2.5kg/cm los servos mas economicos.

Y ahora, como controlamos todo esto???????????

Pues muy facil!!!!!!  Los inganieros se las han pensao para que el control sea lo mas sencillo posible y para ello se ha resuelto el problema con solo tres cables: uno que es para la alimentacion negativa, otro que es para la alimentacion positiva, y un tercero que sirve para mandar las ordenes de forma secuencial.

Para que lo tengamos claro, un servo trabaja con corriente continua en el rango de 4-6V y sucede que un ordenador tambien... Asi, pasamos a explicar como se envian las ordenes por ese tercer cable famoso.

Por el cable de señal (que suele ser blanco) lo que se envia es un tren de pulsos que varia de 0v a 5v y que va segun el esquema siguiente:

Esto que significa? pues que por el cable de señal va un tren de pulsos que se repite cada 20 milisegundos aproximadamente y que la informacion de la posicion deseada va en relacion al tiempo que dura el estado de 5v en el cable. Como se ve en la figura anterior, la duracion del pulso esta comprendida entre 1 milisegundo y 2 milisegundos para cada una de los extremos del movimiento del servo.
Ya con esto, si somos capaces de generar una señal de 5V que este comprendida dentro de estos parametros, seremos capaces de controlar un servo RC.

Resumiendo, en la figura siguiente podemos ver la relacion del pulso y la posicion del sevo:

Como vereis, ahora el rango de duracion del pulso esta comptrendido entre 0.5ms y 2.5ms. Esto es asi ya que despues de estudiar mas en profundidad el tema, he podido comprobar que algunos servos son capaces de reconocer este rango mas amplio, por lo que he preferido utilizar este ultimo criterio que es el que engloba la totalidad de los servosRC.

Yo realice unas lecturas de mi emisora de 4 canales antigua y obtuve esto:

    Intervalo de señal de 22.59ms

    Amplitud maxima para 1.92ms

    Amplitud minima de 1.03ms
 
Si quereis el programa con el que he hecho las lecturas, lo teneis en la pagina de descarga...

COMO SE CONECTAN LOS 8 SERVOS AL ORDENADOR...

Lo de controlar 8 servos no es una casulaidad, sucede que el puerto paralelo del pc tiene, entre otras cosas, 8 salidas binarias. si usamos cada una de ellas para un servo, podemos controlar 8 servos de forma sencilla (ya que las salidas son de 5v, no hace falta ningun tipo de adaptacion para los servos).

Pasamos a explicar cuales son esas 8 salidas del puerto paralelo:


mirando el ordenador por detras...

Pues, si nos hemos leido los articulos que os recomende, sabreis que significa esto, y como no pienso repetir las cosas, sucede que los 8 pines de salida son los que tienen la letra "D" estando numerados del 0 al 7 (8 salidas de señal).

Ya en este punto, paso a decir como funciona el programa y posteriormente el esquema de como conectarlo.

EL PROGRAMA

El programa esta hecho para MS-DOS y esta pensado que se pueda utilizar en ordenadores antiguos (386, 486, etc) que seran los que usemos para los experimentos que vayamos a realizar con los servos...

Para compilar el programa he usado el compilador DJGPP que es de licencia libre y se puede bajar desde internet. El motivo por el que he usado este compilador es que es el unico que me permite controlar intervalos de tiempos pequeños como para poder generar la señal descripta anteriormente, asi me es posible el controlar intervalos de hata 1 microsegundo (la millonesima parte de un segundo). Y a partir de ahi, la filosofia del programa es sencilla. El programa entra en un bucle infinito en el que cada 20 ms envia por cada una de sus 8 salidas la señal comprendida entre 0.5 ms y 2.5ms a cada uno de los 8 servos.

El aspecto del programa es asi de feo, pero os aseguro que funciona...


Para descargarlo solo tienes que pinchar en la imagen...





Si vemos, el programa empieza posicionando los servos en la posicion 50 que es la que corresponde a 0.5ms a lo que es lo mismo -90º. A partir de ahi, podemos cambiar la posicion de cada servo con el somando "s". Este comando tiene la siguiente estructura:

s2 150(enter)

Esto significa que queremos cambiar el servo 2 a la posicion 150. Observad que hay un espacio entre el comando y la posicion.y asi para cada uno de los 8 servos. Tened en cuenta que los servos empiezan a numerarse desde el 0 al 7 y que los ramgos de posiciones van desde el 50 al 250. Y la "s"  debe ser en minusculas...

El comando "p" lo que hace es cambiar el puerto paralelo por el que se envian las señales. Esto solo se usa si tenemos mas de un puerto paralelo en el PC. La estructura de la orden es:

p1(enter)

Esto significa que usamos el puerto lpt 1. El rango de puertos va desde el 1 al 3 y la "p" es minuscula...

Para salir del programa debemos pulsar la "x" minuscula y ya esta.
 
 

PEROOOOOOO... ASI NO ES MUY UTIL ESTO!!!!!!!

Ya, por eso el programa es capaz de leer un fichero de posiciones que hayamos escrito antes. El fichero es de texto y tiene la estructura siguiente:

TiempoDuracion ,PosServ_0, PosServ_1,PosServ_2 ,PosServ_3, PosServ_4,PosServ_5 ,PosServ_6, PosServ_7

Cada linea debe tener estos 9 datos separados por comas y sin espacios.
 

La estructura de la orden de cada linea es la siguiente:

TiempoDuracion    -    Es un valor entero que es la centesima parte de un segundo. Esto quiere decir que si ponemos 100 las posiciones especificadas en esa linea duraran 1 segundo, 200 = 2 segundos, etc...

PosServ_0    -    Aqui se pone la posicion que queremos que tenga el servo. en este caso hablamos del servo 0 y debe ser un valor entero comprandido entre 50 y 250 para los dos extremos del movimiento del servo.

El fichero debe acabar con el numero -1 en la ultima linea para especificarle al programa que se ha finalizado la ejecucion. No olvidarlo...

Asi quedaria un fichero con una secuencia de posiciones:

10,50,60,70,80,90,100,110,120
50,250,240,230,220,210,200,190,180
200,150,151,152,153,154,155,156,157
-1

Para cargar un fichero se usa el comando "f" seguido del nombre del fichero que debe estar en la misma carpeta del programa.

f 1.fps(enter)

Esto significa que debe ejecutar el fichero "1.fps". Observad que el nombre del fichero va separado de la f por un espacio.
 

ALERTA!!!!!
Si no quereis tener problemas, debeis tratar el programa con mucho cuidado ya que no he invertido ni un segundo de tiempo en ponerle protecciones ante posibles burradas de uso. Os aseguro que funciona perfectamente si hacemos las cosas como hay que hacerlas y sin complicarle las cosas al programa.
 

 

CODIGO FUENTE DEL PROGRAMA...
Para los que querais saber como es y si os atreveis a mejorarlo, aqui teneis el codigo fuente del programa para que hagais con el lo que querais...

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/farptr.h>
#include <go32.h>

int gaiPortInst[3] = {0, 0, 0};
int giPortID;
int sPos[8] = {50,50,50,50,50,50,50,50};

int   giFileOpen = 0;
FILE* gpfFileIn;
int   giDelayFCmd = -100;

/**********************************************************************/
/*Busca los puertos paralelos instalados en el PC*/
int iFunFindLPTPort (void) {
 int iT;
 int iTmp = 0;
 unsigned uTmp;

 for (iT=0; iT<3; iT++) {
  uTmp = (unsigned) _farpeekw(_dos_ds, 0x408 + (iT * 2));
  if (uTmp != 0) {
 gaiPortInst[iT] = (int) uTmp;
 iTmp++;
  } else {
 gaiPortInst[iT] = 0;
  }
 }
 return (iTmp);
}

/**********************************************************************/
int iExeCmd(char *acBuf) {
 int iRet = 1;

 printf("\n  Procesando comando: %s", acBuf);
 switch(acBuf[0]) {
  case 's':
   printf("\n  Estableciendo posiciones de servos...");
   switch(acBuf[1]) {
    case '0': sPos[0] = atoi(&acBuf[3]); if(sPos[0]<50) sPos[0] = 50; if(sPos[0]>250) sPos[0] = 250; break;
    case '1': sPos[1] = atoi(&acBuf[3]); if(sPos[1]<50) sPos[1] = 50; if(sPos[1]>250) sPos[1] = 250; break;
    case '2': sPos[2] = atoi(&acBuf[3]); if(sPos[2]<50) sPos[2] = 50; if(sPos[2]>250) sPos[2] = 250; break;
    case '3': sPos[3] = atoi(&acBuf[3]); if(sPos[3]<50) sPos[3] = 50; if(sPos[3]>250) sPos[3] = 250; break;
    case '4': sPos[4] = atoi(&acBuf[3]); if(sPos[4]<50) sPos[4] = 50; if(sPos[4]>250) sPos[4] = 250; break;
    case '5': sPos[5] = atoi(&acBuf[3]); if(sPos[5]<50) sPos[5] = 50; if(sPos[5]>250) sPos[5] = 250; break;
    case '6': sPos[6] = atoi(&acBuf[3]); if(sPos[6]<50) sPos[6] = 50; if(sPos[6]>250) sPos[6] = 250; break;
    case '7': sPos[7] = atoi(&acBuf[3]); if(sPos[7]<50) sPos[7] = 50; if(sPos[7]>250) sPos[7] = 250; break;
   }
   break;

  case 'p':
   printf("\n  Estableciendo puerto de comunicaciones...");
   switch(acBuf[1]) {
    case '1': if(gaiPortInst[0] != 0) giPortID = gaiPortInst[0]; break;
    case '2': if(gaiPortInst[1] != 0) giPortID = gaiPortInst[1]; break;
    case '3': if(gaiPortInst[2] != 0) giPortID = gaiPortInst[2]; break;
   }
   break;

  case 'x':
   printf("\n  Finalizando Programa...");
   iRet = -1;
   break;

  case 'f':
   if (giFileOpen == 1) fclose(gpfFileIn);
   printf("\n  Cargando fichero de comandos...");
   if ((gpfFileIn = fopen(&acBuf[2], "r")) == NULL) {
    printf ("\n    ERROR: Nombre de fichero %s erroneo...", &acBuf[3]);
    giDelayFCmd = -100;
    giFileOpen = 0;
   } else {
    giDelayFCmd = -50;
    giFileOpen = 1;
   }
   break;

  default:
   printf("\n  ERROR: Comando Erroneo...");
   break;
 }

 printf("\n");
 printf("\n  Puerto LPT: 0x%3x", giPortID);
 printf("\n  Posiciones: %3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d", sPos[0], sPos[1], sPos[2], sPos[3], sPos[4], sPos[5], sPos[6], sPos[7]);
 printf("\n  Comando (s-servo_pos, p->puerto, x->salir): ");

 return iRet;
}

/**********************************************************************/
int main() {
 uclock_t start_time;
 int SigSend;
 uclock_t ctTmp;
 uclock_t ctTmpp;
 char acBuf[80] = "";
 int  icBuf = 0;
 char cCar;
 int iRun = 1;
 int iStartDelayFCmd;

 if (iFunFindLPTPort() == 0) {
  printf("\nERROR: No se han encontrado Puertos instalados en su PC...");
  return(0);
 }
 giPortID = gaiPortInst[0];
 outportb(giPortID, 0x00);

 printf("\nPrograma MS-DOS para controlar 8 Servos RC por LPT (FPR-07/2002)\n");
 printf("\n  Genera PWM entre 0.5ms y 2.5ms por el puerto de DATOS");
 printf("\n  Puerto LPT: 0x%3x", giPortID);
 printf("\n  Posiciones: %3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d", sPos[0], sPos[1], sPos[2], sPos[3], sPos[4], sPos[5], sPos[6], sPos[7]);
 printf("\n  Comando (s-servo_pos, p->puerto, x->salir): ");

 start_time = uclock();
 outportb(giPortID, 0xff);
 ctTmp = 0;

 while(iRun) {
  SigSend = 0x00;
  ctTmpp = ctTmp;
  ctTmp = (uclock() - start_time) / 10;
  if (ctTmpp < ctTmp && ctTmp < 500) {
   if (ctTmp < sPos[0]) SigSend += 0x01;
   if (ctTmp < sPos[1]) SigSend += 0x02;
   if (ctTmp < sPos[2]) SigSend += 0x04;
   if (ctTmp < sPos[3]) SigSend += 0x08;
   if (ctTmp < sPos[4]) SigSend += 0x10;
   if (ctTmp < sPos[5]) SigSend += 0x20;
   if (ctTmp < sPos[6]) SigSend += 0x40;
   if (ctTmp < sPos[7]) SigSend += 0x80;
   outportb(giPortID, SigSend);
  } else {
   if (kbhit()) {
    switch(cCar = getche()) {
     case '\r':
      acBuf[icBuf] = '\0';
      icBuf = 0;
      if ((iExeCmd(acBuf)) == -1) iRun = 0;
      break;

     case 8: /* Tecla Delete */
      if (icBuf > 0) icBuf--;
      break;

     default: /* Tecla cualquiera */
      acBuf[icBuf] = cCar;
      icBuf++;
      break;
    }
   }

   if (giDelayFCmd == -50) {
    iStartDelayFCmd = clock();
    giDelayFCmd = 0;
   }
   if (giDelayFCmd != -100) {
    if (feof(gpfFileIn) == 1) {
     giDelayFCmd = -100;
     fclose(gpfFileIn);
     printf("\n  FCMD->Finalizado Fichero de Comandos...");
     printf("\n  Comando (s-servo_pos, p->puerto, x->salir): ");
    } else {
     if ((clock() - iStartDelayFCmd) > giDelayFCmd) {
      iStartDelayFCmd = clock();
      fscanf(gpfFileIn, "%d,", &giDelayFCmd);
      if (giDelayFCmd < 0) {
       giDelayFCmd = -100;
       fclose(gpfFileIn);
       printf("\n  FCMD->Finalizado Fichero de Comandos...");
       printf("\n  Comando (s-servo_pos, p->puerto, x->salir): ");
       continue;
      }
      if (giDelayFCmd < 10) giDelayFCmd = 10;
      fscanf(gpfFileIn, "%d,%d,%d,%d,%d,%d,%d,%d", &sPos[0], &sPos[1], &sPos[2], &sPos[3], &sPos[4], &sPos[5], &sPos[6], &sPos[7]);
      if(sPos[0]<50) sPos[0] = 50; if(sPos[0]>250) sPos[0] = 250;
      if(sPos[1]<50) sPos[1] = 50; if(sPos[1]>250) sPos[1] = 250;
      if(sPos[2]<50) sPos[2] = 50; if(sPos[2]>250) sPos[2] = 250;
      if(sPos[3]<50) sPos[3] = 50; if(sPos[3]>250) sPos[3] = 250;
      if(sPos[4]<50) sPos[4] = 50; if(sPos[4]>250) sPos[4] = 250;
      if(sPos[5]<50) sPos[5] = 50; if(sPos[5]>250) sPos[5] = 250;
      if(sPos[6]<50) sPos[6] = 50; if(sPos[6]>250) sPos[6] = 250;
      if(sPos[7]<50) sPos[7] = 50; if(sPos[7]>250) sPos[7] = 250;
      printf("\n  FCMD->Delay %3d - Posiciones: %3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d", giDelayFCmd, sPos[0], sPos[1], sPos[2], sPos[3], sPos[4], sPos[5], sPos[6], sPos[7]);
     }
    }
   }

  }
  if (ctTmp > 2000) {
   start_time = uclock();
   outportb(giPortID, 0xff);
   ctTmp = 0;
  }

 }

 outportb(giPortID, 0x00);
 if (giFileOpen == 1) fclose(gpfFileIn);

 printf("\n\n");
 printf("\nPrograma MS-DOS para controlar 8 Servos RC por LPT (FPR-07/2002)");
 printf("\n*FIN DE EJECUCION DEL PROGRAMA - COMPILADO CON DGJPP");
 printf("\n*FRANCISCO PANTANO RUBI¥O - WWW.TERRA.ES/PERSONAL/FRANPR");

 return 0;
}
 

COMO CONECTAR LOS SERVOS AL PUERTO LPT...

ALERTA!!!!!
Mucho cuidado ya que estamos manipulando el puerto paralelo del ordenador sin ningun tipo de proteccion. Esto equivale a decir que no hay posibilidad de error, que cualquier cosa mal hecha puede costaros la placa base del ordenador como minimo. Asi es que os recomiendo mucha cautela y si es posible le instalais un puerto paralelo extra al pc para evitaros daños mayores.
No me hago responsable de lo que pueda pasar. A mi me funciona perfectamente y eso es garantia mas que suficiente para avalar este proyecto...

Bueno, despues del rollo anterior, el esquema de conexion es el de la figura:

Como se puede ver, solo se trata de hacer una placa con pines de conexion para las ocho clavijas de los servos. Para ello he utilizado una placa universal de vaquelita, esa que viene con los agujeritos hechos y tienes que ir puenteando por debajo con el estaño, y una regleta de pines de 2.54mm de separacion para hacer las conexiones de los servos.


visto el ordenador por detras...

Como podeis ver, es muy simple, solo hay que puentear todos los negativos de los servos con el negativo de la alimentacion, hacer lo mismo con el positivo, y luego ir soldando cada uno de los pines de señal del servo a los correspondientes pines de la clavija del puerto paralelo (segun esquema anterior de los pines del puerto paralelo).
La masa de alimentacion va conectada tambien a la masa del puerto lpt, que son los pines de color
VERDE.

Y ya esta!!!!!!!!! con esto podeis hacer ya casi todo lo que se os ocurra. Os pido que me mandeis fotos de los inventos que hagais con esto para ver si esto sirve para algo...

Espero que lo disfruteis, y aqui os pongo una demostracion de lo que he hecho para comprobar su funcionamiento...