Controlar un servomotor con el PC

Supongo que muchos estaréis familiarizados con los servos. No es mi caso. Por unas cosas u otras nunca me he dedicado al modelismo ni a la robótica así que para mí estos motores como si no existieran. Sin embargo hace unas semanas vi uno barato en DealExtreme y pensé que algún día puedo necesitarlo. Y para entonces mejor saber cómo se usa. Así que esta primera prueba no va a ser nada elaborado, solamente un servo, un PIC, y un PC para dar las órdenes.

He utilizado el modelo TowerPro MG995. Según dicen, y he podido comprobar, tiene sus ventajas y sus inconvenientes.

Ventajas
  • Es barato. O relativamente barato comparado con otros modelos.
  • Potente. Tiene bastante torque.
  • Engranajes metálicos.
Inconvenientes
  • Tiene una zona muerta muy estrecha, y le cuesta quedarse quieto.
  • Los engranajes metálicos lo hacen lento y con inercia, por lo que a veces se pasa y tiene que retroceder.
  • Con la inercia que tiene consume mucho al arrancar (más de 1A) y mete mucho ruido en la alimentación. Así que si no desacopláis bien el micro se os va a reiniciar.
  • Es muy sensible a las caídas de tensión aunque sean breves. Cuando lo alimentamos con 5V hay ocasiones que la electrónica interna se reinicia y el motor se mueve sólo.

En esta página de la Wikipedia se explica cómo se controla y en esta otra explican cómo funcionan interiormente. Hay cientos de páginas por toda Internet, de aficionados a la robótica sobre todo, que tienen mucha información.


Como ya os he dicho nunca había manejado un servo y no tenía muy claro cómo son los pulsos que hay que enviar. Según la Wikipedia es así:


Pero los valores de 1ms y 2ms son orientativos y dependen del modelo de servo. Y como yo no he podido encontrar el datasheet del MG995 pues me he preparado un PIC para probar con varios.

Programa del PIC

Como de costumbre usaremos un 12F683 y un programita en C que podéis compilar con el compilador de CCS. Y por si no lo tenéis también os dejo al final del artículo un enlace al código fuente y al fichero compilado.

Utilizaremos el puerto serie para la transmisión, si vuestro ordenador ya no tiene puerto serie necesitaréis un conversor USB-RS232. Como este.

Tras presentar una cabecera se queda esperando comandos. Los comandos constan de una letra y un número. La letra puede ser p si queremos cambiar el periodo u o si queremos cambiar la duración del pulso. El número que sigue son los microsegundos de duración.

Si por ejemplo queremos cambiar el tiempo del pulso a 1ms, que son 1000us pondremos
o1000

Tal que así:

Prueba de servo.
Escribe oXXXX para variar el tiempo On
o pXXXX para variar el periodo

>o1000
Ton = 1000
Periodo = 200000
>p15000
Ton = 1000
Periodo = 200000
>

Variar el periodo no debería infuir en la posición del servo, pero es útil para hacer pruebas.

Varias cosas en cuanto a este código:

Utilizamos un Timer para gestionar el pulso. Habría sido más fácil con un bucle y un par de delays, pero no podríamos hacer nada más mientras tanto. Usando un timer nos quitamos el problema del bucle principal y lo delegamos a la interrupción. Y si luego más adelante quisiéramos manejar más de un servo con el mismo PIC ya tenemos un paso avanzado.

He rescrito la función atol. Mi versión es menos potente que la que trae el compilador en sus librerías pero también ocupa menos espacio en memoria. No necesito convertir números hexadecimales ni negativos en este programa.

También he pasado de gets por dos motivos. El primero es que no tiene límite de caracteres, y se puede cargar otras cosas de la memoria si escribo una cadena demasiado larga. Tampoco me preocupa mucho porque sólo son pruebas lo que estoy haciendo. Pero el motivo principal es que get_string tiene eco remoto. Eso quiere decir que en el PC verás en el terminal lo que tecleas mientras escribes. Cosa que gets no hace.


/*****************************************************************************/
/*   Primera prueba de servos.
/*   Recibe los parámetros desde el PC para ver cuales son los que
/*   mejor se ajustan al motor.
/*
/*   14/12/2010
/*****************************************************************************/

#include "prueba1.h"
// Prefiero get_string() en input.c a gets() de stdlib.h
// porque se le puede poner un límite de caracteres
// y además tiene echo remoto (se ve lo que envías mientras escribes)
#include <input.c>


short activo =     0;   // indica si el pin está activo
                        // si lo hiciera con una instruccion bit
                        // no podría cambiarlo en el #define fácilmente
long Ton     =  1000;   // tiempo de duración del pulso (us)
long Periodo = 20000;   // duracion del periodo (us)

long atol(char *);

#int_TIMER1
void  TIMER1_isr(void) 
{
 /* Lo activamos y plantamos la interrupción
    para que se desactive al rato 
    Los valores de offset estan calculados con el simulador */
 if (!activo) {
  activo = 1;
  output_high(PIN_Servo);
  set_timer1(65535 - Ton + 60);
 }
 else {
  activo = 0;
  output_low(PIN_Servo);
  set_timer1(65535 - Periodo + Ton + 65);
 }
}



void main()
{
 setup_oscillator(OSC_4MHZ);
 
    setup_adc_ports(NO_ANALOGS|VSS_VDD);
 setup_adc(ADC_OFF);
 
 /* Timer1 con resolución de 1us,
    desbordamiento en 65.535ms a 4MHz */
 setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);

 printf("Prueba de servo.\r\n");
 printf("  Escribe oXXXX para variar el tiempo On\r\n");
 printf("  o pXXXX para variar el periodo\r\n");

 enable_interrupts(INT_TIMER1);
 enable_interrupts(GLOBAL);


 for(;;) {
  char string[10];
  char var;
  long valor;

  get_string(string, sizeof(string));
  //strcpy(string,"o1234"); (DEBUG)
  var   = string[0];
  valor = atol(string+1);
  
  if (valor <= 0) {
   puts("Valor no valido.");
  }
  else { 
   if (var == 'o') {
    if (valor > Periodo) {
     puts("Valor no permitido pata Ton.");
    } 
    else { 
     Ton = valor;
    }
   }
   else if (var == 'p') {
    Periodo = valor;
   }
   else {
    puts("Comando no reconocido."); 
   }
   
   printf("\nTon = %Lu\r\n", Ton);
   printf("Periodo = %Lu\r\n", Periodo);
  }
 } 
}


/* Versión reducida de atol:
   Esta versión sólo trabaja con positivos y en decimal.
   Y para cadenas de hasta 256 caracteres. */
long atol(char *s)
{
 long result;
 char indice;
 char c;

 indice = 0;
 result = 0;
 
 c = s[indice];
 while (c >= '0' && c <= '9') {
   result = 10*result + (c - '0');
   indice++;
   c = s[indice];
 }

 return(result);
}

Por si teneis curiosidad, el fichero prueba1.h es así

#include <12F683.h>

#device adc=16

#FUSES NOWDT                  //No Watch Dog Timer
#FUSES INTRC_IO               //Internal RC Osc, no CLKOUT
#FUSES NOCPD                  //No EE protection
#FUSES NOPROTECT              //Code not protected from reading
#FUSES NOMCLR                 //Master Clear pin used for I/O
#FUSES NOPUT                  //No Power Up Timer
#FUSES NOBROWNOUT             //No brownout reset
#FUSES IESO                   //Internal External Switch Over mode enabled
#FUSES FCMEN                  //Fail-safe clock monitor enabled

#define PIN_Servo   PIN_A2 // Salida para el servo
#define PIN_SrTX    PIN_A0 // TX serie para conectar al PC
#define PIN_SrRX    PIN_A1 // RX serie para conectar al PC

#use delay(clock=4000000)
#use rs232(baud=9600,INVERT,DISABLE_INTS,parity=E,xmit=PIN_SrTX,rcv=PIN_SrRX,bits=8)


Salida del PIC

Vamos a usar la tarjeta de sonido que nos curramos en otra entrada (Medir valores lógicos con una tarjeta de sonido) para ver cómo son los pulsos que genera el programa y cómo cambian cuando tecleamos los comandos.


La imagen es una captura de pantalla del Audacity. En la primera pista vemos pulsos de 500us con un periodo de 30ms. En la segunda el periodo es el mismo pero los pulsos son de 2.5ms (los hemos cambiado enviando el comando o2500) y en la tercera mantenemos la duración del pulso pero el periodo cambia a 20ms (comando p20000).

Fijaos que aquí se aprecia muy bien el efecto de Gibbs del que ya habíamos hablado en otro artículo debido al filtrado de las altas frecuencias. Se ve mucho porque hay una resistencia de 1k en serie con la entrada de la tarjeta y porque la frecuencia de muestreo de esta última es baja. A mayor corriente menos se nota la oscilación. Pero la corriente de salida del PIC es finita.

Resultado

En este servo, el cable marrón va a masa, el rojo a positivo y el naranja es la señal de posición.

Los valores del periodo válidos van desde los 5ms a más de 35ms (que es el tope que se puede probar con el programa anterior, porque Timer1 se desborda).

En cuanto la de duración del pulso he visto que va entre 0.5ms y 2.5ms. Por debajo de 0.5ms el servo se coloca en posiciones aleatorias. Por encima de 2.5ms permanece en la posición de 180º sin moverse.

<0.5ms ->   ?   (indeterminado)
 0.5ms ->   0º
 1.0ms ->  45º
 1.5ms ->  90º (centro)
 2.0ms -> 135º
 2.5ms -> 180º
>2.5ms -> 180º (inmóvil)

A partir de ahí basta extrapolar para saber cuánto tiene que durar el pulso para situar el motor en la posición que queramos. Luego habrá que tener en cuenta la velocidad de giro.


Esto es todo por ahora. Os dejo el enlace a los archivos aquí.
Artículo completo >>

Dimmer controlado por mando a distancia: el hardware

A este proyecto le he dedicado otras entradas en el blog. Se trata de diseñar y construir un circuito para regular la intensidad de luz de una lámpara utilizando un mando a distancia.

Hoy voy a presentaros el hardware. Para que luego sea más fácil entender el software. He aquí el esquema:


No difiere mucho de los esquemas que encontraréis en otras páginas. Como cabe esperar el corazón del circuito es el microcontrolador que se ve a la derecha. Yo he usado un 12F683 porque es el que tengo para las pruebas pero vosotros podeis usar otros modelos si lo preferís. Para examinar el esquema lo dividimos en cinco partes.

Microcontrolador
No es necesario escribir mucho sobre esta parte.

Se trata del PIC IC1, es el cerebro y a él van conectadas el resto de elementos. Sí es interesante que tenga bajo consumo para no sobrecargar la fuente de alimentación. También conviene contar con un par de temporizadores para hacer más sencilla la rutina de recepción NEC.


Fuente de alimentación
Si queremos que el circuito sea lo más sencillo posible no podemos contar con una fuente de alimentación completa, ni con transformador ni conmunatada. Además se supone que el consumo va a ser muy bajo así que nos decantamos por una fuente de alimentación sin transformador. En inglés se conoce como Transformerless Power Supply y encontraréis abundante información y esquemas en Google. Por mi parte os recomiendo esta Nota de Aplicación de Microchip: Transformerless Power Supplies: Resistive and Capacitive.

Hablamos de R1, R4, R5, R7, D1, D2, C1, C2 y C3. Vamos a entender brevemente cuál es la función de cada componente.

La tensión de red (220V 50Hz en España) se aplica a los terminales X1-1 y X1-2. R5 es un fusible que protege al resto por si hubiera un cortocircuito interno.

C1 es el corazón de esta parte. Se trata de un condensador de tipo X2. Eso significa dos cosas, por un lado que está dimensionado para la tensión de red entre 150V y 250V AC. Y por otro que ante un fallo el condensador queda abierto. Otros modelos pueden fallar y quedar los terminales en corto, aplicando la tensión de red a todo el circuito. Cuando trabajamos con doce voltios nos da igual, pero con 220V hay que cuidar estos detalles o nos podemos llevar una buena hostia. Como dicen en la nota anterior: "si el condensador queda en corto puede reventar algo... literalmente".

R1 limita la intensidad que pasa por C1 cuando este se encuentra descargado al suministrar tensión. Y otro elemento de protección es R4 que garantiza que el condensador no se quede cargado cuando desenchufemos el circuito. Un condensador cargado a 220V da sustos muy desagradables. Es habitual cuando cargamos condensadores a esas tensiones no dejarlos cargados, ya lo vimos también en esta entrada.

Para la tensión alterna C1 representa una resistencia, más baja cuanto mayor su capacidad, sólo que a diferencia de una resistencia C1 no disipa calor (ni consume). El diodo zener D1 estabiliza la tensión en 5V que nos viene estupendamente para alimentar el PIC. D2, C2 y C3 forman un rectificador de media onda, que es apropiado sólo para bajos consumos.

Mientras que C2 es un condensador electrolítico que elimina el rizado de alterna, C3 es un condensador de poliester, de menor capacidad pero de reacción mucho más rápida. Nos suministrará los picos de corriente necesarios por ejemplo para disparar el optotriac sin afectar al voltaje de alimentación del resto del circuito.

R7 es un varistor que deriva los picos de la red que excedan cierto voltaje para que no lleguen al circuito. Es opcional aunque muy recomendable.

Sensor de paso por cero
Una vez tenemos el PIC alimentado necesitamos saber cuando la tensión de red pasa por cero para disparar el triac con un retardo acorde con la energía que queramos.

Mirad el datasheet de un PIC como el 12F683 y fijaos cómo son por dentro las entradas de tipo IO entrada/salida:


La imagen no está sacada del datasheet, sino de esta Nota de Aplicación donde viene más simplificado: Interfacing to AC Power Lines. El caso es que tienen dos diodos limitadores. Lo que quiere decir que aunque apliquemos 220V directamente al pin del PIC no se dañará siempre y cuando limitemos la intensidad. R2 es una resistencia de un valor muy elevado, suficiente para elevar la tensión hasta nivel alto, pero con una intensidad muy baja para no destruir los diodos. La tensión en el puerto GP2 oscilará entre 0 cuando la tensión de red pase por el semiciclo negativo hasta 5V como máximo en el semiciclo positivo. En cualquiera de las dos transiciones sabemos que la tensión acaba de pasar o va a pasar inmediatamente por cero.

Conviene utilizar una entrada de tipo Schmitt trigger (ST) para que la transición sea limpia. De lo contrario los transitorios producidos por el encendido de motores y electrodomésticos pueden causar que el circuito conmute varias veces antes de tiempo y por tanto que no funcione bien.

Disparador
Aunque veréis muchos esquemas que utilizan un triac de baja corriente de disparo (Logic level and sensitive gate triacs) aquí he usado un circuito clásico de triac-optotriac. Es cuestión de gustos, a mi personalmente no me gusta excitar directamente el triac con el micro, y menos aún cuando la fuente no está aislada eléctricamente como sí lo estaría si usásemos una fuente con transformador. Creo en el aislamiento galvánico y los optoacopladores me parecen la mejor opción; pero esto, repito, es solamente mi opinión personal y no quiero decir que de la otra forma no sea igual de válido.

En cuanto al optotriac, hay dos tipos: con detección de paso por cero y sin ella.

En lo que tienen detección de cruce por cero los más comunes son los modelos MOC30XY, donde X indica la máxima tensión nominal:
MOC 3041 -> 400V
MOC 3061 -> 600V
MOC 3081 -> 800V

y la Y indica la corriente que necesita el LED para garantizar el disparo:
MOC 3040 -> 30mA (no existen todos los modelos)
MOC 3041 -> 15mA
MOC 3042 -> 10mA
MOC 3043 ->  5mA

Aunque en el esquema está puesto como tal, no nos interesa un optotriac con detección de paso por cero. Pues no nos permitiría hacer el disparo cuando nosotros queramos. Así que será mejor emplear otros integrados como el MOC3020 o el MOC3030 que no incorporan esa característica. Los primeros son muy importantes para hacer una intermitencia o en general un control de encendido/apagado (por tiempo, por temperatura, etc) pero no precisamente un dimmer.

Además como el disparo lo vamos a hacer con un microcontrolador no es preciso que sea del modelo sensible (los acabados en 3) porque lo que haremos será enviar un pulso muy breve, que será suficiente para encender el triac pero al durar muy poco tiempo no agotará el condensador de la fuente de alimentación. La cosa sería muy distinta si en lugar de un PIC hiciésemos una intermitencia utilizando un timer como por ejemplo un 555 que deja la salida a nivel alto durante todo el periodo que dure la luz encendida.

El TRIAC puede ser cualquiera siempre y cuando soporte la tensión de la red y la carga que vayáis a usar. En este caso al ser una lámpara la carga es resistiva pura, pero si queréis controlar un motor tened cuidado que la cosa se vuelve compleja.

Conectores auxiliares
Son dos, el primero, indicado como SL1 es a donde va conectado el módulo receptor de infrarrojos (hemos hablado de él en otras entradas), y será el que reciba la señal del mando.

Para terminar he incorporado un conector externo que no he usado pero podría venir bien por ejemplo para conectar un LED y que el dimmer se regule automáticamente con la luz ambiente. Mirad esta entrada para ver como se hace un sensor utilizando un simple LED: Sensor óptico sencillo con amplio rango dinámico. En su momento yo usaba esta patilla para depuración conectándola al puerto serie.

Lo bueno de usar microcontroladores es que el circuito es muy simple y siempre se puede ampliar por software. Así que si os sobra un puerto no lo dejéis sin conectar porque el día de mañana se os puede ocurrir una utilidad para él. Si ya tenéis el conector sólo os hace falta reprogramar el chip y no tenéis que hacer otra placa.


Montaje

Esta es la PCB con las pistas vistas por el lado de cobre:




Y así es como quedaría el circuito ya montado:


Observad que el condensador C1 no es de 47nF sino de 100nF. En realidad con un consumo tan bajo no es crítico, eso sí cuanta mayor capacidad más margen de maniobra tendremos.


Conexión del circuito

La tensión de red va conectada a la clema gris de la izquierda. El terminal del centro es común, el de abajo es la entrada de la tensión y el de arriba es la salida hacia la bombilla.

A la derecha está el conector de tres pines a donde va conectado el módulo IR. Se hace así para que el circuito pueda estar escondido y tan sólo quede expuesto el receptor.

Se trata de un circuito lo suficientemente pequeño como para que quepa dentro del plafón o de la caja de registro más cercana.



Por último os dejo los esquemas (para Eagle), las imágenes y un PDF con las pistas en este enlace.
El software está publicado en esta entrada.
Artículo completo >>