Control de un motor DC con el PIC 16F876A, 16 bits de resolución, por Bucle.

En el apartado Encoder por Software se explicó cómo hacer que un microcontrolador (PIC) cuente pulsos, ahora vamos a concentrarnos en el control del motor. El PIC va a hacer tres cosas a la vez. La primera es leer los pulsos y transformarlo en la "posición actual del motor"; la segunda es cargar la posición final, que es la posición a la que queremos llevar el motor (número de vueltas o fracciones de ésta); y la tercera es el control del motor y avisar cuando esté posicionado.

Aunque veas muchas líneas en el esquema en realidad es muy sencillo de montar porque dos bits son para leer el encoder [RA0 y RA1], otros dos para controlar el motor [RA2 y RA3], un bit para un led [RA4] y todos los demás son los bits en paralelo de entrada de datos [RB0..RB7=D0..D7] y [RC0..RC7=D8..D15]. Como puedes comprobar todo va seguido y con un orden lógico.

El control del motor es muy simple. Se trata de comparar la posición actual (es la posición que está el motor en ese instante) con la posición final. Entonces podemos tener tres resultados: Puede ser mayor, menor o igual. Si la posición actual es mayor que la posición final el motor ha de girar en un sentido; si fuera menor ha de girar en sentido contrario; y si es igual el motor ha de parar. Para el control del motor sólo vamos a usar dos bits, el que indica "menor que" (RA2) y "mayor que" (RA3). El bit de "igual" (RA4) de momento le vamos a poner un LED para indicar que el motor ya ha llegado a la posición final.

Observa el programa:

Haz clic aquí para ver el código en versión Proton IDE.

Código fuente en CCS C.

The translation could modify the code. Use the code without translating.
#include <16F876A.h>
#FUSES NOWDT, XT, NOPUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT
#use delay(clock=4000000)
#byte porta = 0x05      // Asignamos PortA.
#byte portb = 0x06      // Asignamos PortB.
#byte portc = 0x07      // Asignamos PortC.
void main()
{
   port_b_pullups(false);      // Configuramos todo digital. 
   setup_adc_ports(NO_ANALOGS);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   set_tris_a(0b100011);
   set_tris_b(0b11111111);
   set_tris_c(0b11111111);
   signed int32   x=0;  // Declaramos el valor de X con 32 bits, posición actual.
   signed int32   p=0;  // Declaramos el valor de P con 32 bits, posición final.
   int8    aux=0;       // Se almacenará el valor anterior de RA0 y RA1, hasta la siguiente
                           comparación.
   int8    enc=0;       // Se almacenará el valor actual de RA0 y RA1, hasta la siguiente
                           comparación.
   
   while(true)          // Bucle infinito.
   {
      aux = enc;        // Igualamos 'AUX' y 'ENC' para luego comparar cuando cambie 'ENC'.               
      enc = porta & 3;  // Aislamos RA0 y RA1 como un número de 2 bits y se carga en la variable
                           'ENC'.
    
      if(aux == 2 && enc == 3) {x++;} // Si en la comparación hay flanco de subida, incrementamos X.                                    
      if(aux == 3 && enc == 2) {x--;} // Si en la comparación hay flanco de bajada, decrementamos X.
         
      // ---------------- Comparación para el control del Motor. ----------------
    
      if(x == p)                  // Si X=P, motor parado.
      {
         output_low (PIN_A2);     // Motor parado.
         output_low (PIN_A3);
         
         p=make16(portc,portb);   // Carga PortC como byte alto y PortB como byte bajo en la
                                     variable P.
          
         output_low (PIN_A4);     // Apaga el LED indicando que ya ha llegado.     
      }
    
      output_high (PIN_A4);       // Enciende el LED indicando que se está posicionando.
    
      if(x > p)                   // Si x>p, motor hacia atrás.
      {      
          output_high (PIN_A2);   // Motor hacia atrás.
          output_low  (PIN_A3);
      }
     
      if(x < p)                   // Si x<p, motor hacia adelante.
      {      
         output_low   (PIN_A2);   // Motor hacia adelante.
         output_high  (PIN_A3);
      }
   }
}

Habrás observado que las variables que intervienen para contar y posicionar (x y p) están declaradas con 32 bits (con signo) en vez de 16. Esto es así para que el contador pueda contabilizar un poco más allá de 65535 (última posición posible con 16 bits) porque por inercia el motor siempre cuenta unos pulsos de más y necesitamos que pueda corregirse para alcanzar esa última posición. Por otra parte, están declaradas con signo para que pueda parar en la posición cero.

Etapa de potencia para controlar el motor.

En la etapa de potencia uso un L293, pero puedes usar cualquier puente en H que se corresponda con la potencia del motor que vayas a usar. Lo único que has de tener presente es que cuando le entre un "11" tenga protección para evitar que se auto-cortocircuite. En puentes en H con protección, un "11" actúa como un "00", es decir, motor parado. El L293 tiene esta protección, al menos con la construcción que presento. Tiene dos puentes en H y uso los dos para un solo motor poniéndolos en paralelo, de esta forma soporta el doble de corriente.

Antes de ponerlo en marcha y otras cuestiones:

  • Cuando pongas en marcha el circuito, todos los bits de entrada de datos han de estar a cero.
  • Si al poner en marcha el circuito, el motor gira sin parar, significa que la polaridad del motor está invertida por tanto has de cambiar la polaridad del motor. Y si al alimentar el circuito el motor se pone en marcha sólo un tiempo y luego se detiene, es debido a que hay algún bit de entrada a uno. Al alimentar el circuito el motor siempre ha de estar parado si todos los bits de entrada de datos están a cero.
  • Los PICs son de tecnología CMOS, eso significa que la entrada de datos no pueden estar al aire porque crearían bits aleatorios (bits falsos). Si vas a probar este circuito poniendo a mano la información de posición con cables o pulsadores, has de añadir en la entrada de datos una red de resistencias para polarizarlas a positivo, como en la imagen de abajo. Puedes usar red de resistencias integradas para que quede todo más compacto. Observa que los pulsadores son "normalmente cerrados", es decir, que normalmente ha de haber un 0 en cada bit hasta que se pulse el (o los) pulsador(es) para poner a 1 el(o los) bit(s) correspondiente(s).
  • Cuando el circuito está montado en una protoboard es importante tener el motor fijo a una superficie. Si el motor está suelto, la propia inercia de moverse y pararse mueve los cables; en una protoboard hay que evitar esto a toda costa porque no hay soldaduras. Puedes usar plastilina para fijar el motor a la mesa o a una superficie. Cualquier movimiento de cables o malas conexiones puede crear ruido y convertirse en falso conteo.
  • Si por la razón que sea necesitas que el motor gire en sentido contrario a como lo hace normalmente, has de enrocar (intercambiar) las entradas del encoder, y también, la polaridad del motor.
  • Debido a que es un encoder incremental, en la práctica necesitarás que cuando se ponga en marcha el PIC lo primero que haga sea llevar el motor (con reductora) a la posición cero real (en este sentido sería como cuando controlas un motor paso a paso) porque, si no, considerará la posición cero allá donde esté en el momento de poner en marcha el circuito. Pero te aconsejo que antes de añadir esta parte primero hagas las pruebas sin este detalle (tal como lo presento). Cuando todo te funcione bien entonces le añades unas líneas de programación para que el PIC encuentre la posición cero real. Puedes aprovechar la patilla RA5 para poner el sensor de posición de cero real.
  • Puedes aumentar la resolución del encoder hasta un factor de por cuatro. Para ver cómo se hace cliquea aquí.
  • En las pruebas que realicé usé encoders de 2 a 12 pulsos por revolución del motor y funcionó perfectamente bien. Luego probé con un motor con encoder de 334 pulsos por revolución del motor y con ese no funcionaba porque la frecuencia del cristal es de sólo 4MHz y al PIC no le da tiempo de contar con el método bucle infinito. De todas formas, aunque funcionase con esa resolución, no se puede aplicar este método porque hay tanta resolución que el motor vibraría una vez que llegase a la posición donde ha de parar debido a que el espacio para hacerlo es ínfimo. Para encoders de mucha resolución es necesario un control PID.
  • Los dos transistores que ves en el esquema y no tienen identificación están ahí para hacer de acondicionador de las señales del encoder óptico. Puedes usar cualquier transistor que tenga una Beta o hFE mayor de 100 (si lo deseas puedes poner Darlingtons). Yo utilicé transistores BC549, pero tú usa lo que tengas más a mano, teniendo presente lo que dije antes. En este caso, para ayudar a saturar a los transistores puse resistencias relativamente bajas (de 1K) así se consigue disminuir en lo posible el tiempo de latencia entre el corte y saturación de los transistores.