viernes, 16 de noviembre de 2018

TMR1 Control de Servomotores


  Control de Servomotor con TMR1


Un gran saludo para todos los visitantes  que comparten interés en este mundo de la electrónica y en especial para los aficionados a la programación de microcontroladores PIC. En esta ocasión quiero mostrar como hacer uso del modulo Temporizador TMR1 para controlar mas de un servomotor.
Si no tienes claro de como funciona el modulo TMR1 del microcontrolador PIC16F, te recomiendo revises la siguiente entrada, en donde explico con mayor detalle la operación del modulo:  <TMR1 Configuración y Uso >.
El objetivo en esta sección es utilizar mas de un servomotor, técnicamente hasta ocho servomotores con un solo PIC16F, en donde cada servomotor trabajara en su rango de tiempo asignado para realizar el movimiento, de 0 hasta los 180 grados. 
La programación se realizara utilizando MPLABX y el compilador XC8 ambos disponibles en la pagina de microchip. Aquí dejo los enlaces de las versiones utilizadas para nuestro ejemplo:  <<MPLABX v6.20>>   <<XC8 v2.50>>
Mencionar también que es necesario contar con conocimientos mínimos sobre programación en lenguaje C y el uso de microcontroladores PIC16.

Acerca del Servomotor
Un servo esta compuesto por un motor DC, una caja reductora y un circuito electrónico de control que nos permite ubicar un eje de accionamiento en cualquier posición dentro del rango para el cual fue diseñado, en general de 0 a 180° grados. 
Suele contar con tres lineas de conexión, dos corresponden a la alimentación de +V / GND y uno para la señal de control que consiste en un pulso digital con modulación del ancho variable entre 1 y 2 milisegundos a 50 Hz de frecuencia. Observe la siguiente figura que muestra la relación del ancho que tiene el pulso digital con la posición del servomotor.


Fig1. Señal de Control para Servo

La señal de control de un servomotor es prácticamente del tipo PWM (Pulse Width Modulation) y tenemos mente utilizar un microcontrolador como el PIC16F debemos considerar los siguientes aspectos:
  • Por general los Microcontroladores, salvo algunos del tipo DSP poseen una cantidad de pines PWM limitadas, como es el caso del PIC16F88x que solo cuenta solo con dos salidas, y estos comparten el mismo recurso de temporización, por lo que no siempre se ajustaran a ciertas necesidades.
  • Los modulo PWM de un PIC16F, están diseñados para señales PWM con ciclos de trabajo que van de 0 al 100%, mientras que un servo solo requiere para el control un ciclo de trabajo entre el 5 y 10%.
  • El modulo CCP del PIC16F no se diseño para trabajar en baja frecuencia, de tal manera que para una salida de 50Hz, la frecuencia del oscilador no deberia superar los 1MHz.
Tomando en cuenta lo citado previamente vemos que una alternativa al uso del un modulo dedicado como el CCP, es utilizar un temporizador y la interrupción, de esta manera sera posible controlar hasta ocho servomotores.
 
Señal de control PWM
Ahora veremos como se genera la señal de control PWM para un servomotor aplicando el uso del temporizador TMR1 y su interrupción.
La figura 2 nos muestra la distribución del pulso S1 dentro del periodo de control fijo que tiene el servomotor, donde el ancho del pulso dura entre entre 1 y 2 ms con un periodo fijo de 20 ms.

Fig2. Periodo de la señal PWM
 
Para establecer una salida PWM, nuestro programa utilizara las siguientes variables de control, junto a los cálculos que se muestran en la ecuación 1, estos valores pueden ajustarse en el archivo de cabecera tservo.h
  • tsvnum. Establece la cantidad de salidas PWM a utilizar, de 1 a 8.
  • tsvslot. Es la fracción del contador de tiempo o ranura dentro del periodo en la cual debe enviarse el pulso de control PWM de cada salida, si el contador se ajusta para incrementos de 1uS, entonces este valor representara el tiempo en us, necesarios para el desbordamiento del TMR1 y su calculo obedece a la siguiente ecuación.
Ec1. Calculo de ranura de tiempo de control por salida
  • TSV.svxpos. Este valor decimal corresponde a un numero que representa la duración del ancho de pulso PWM en cada salida. Considerando la ecuación 1 donde el incremento del contador es cada 1uS, el valor de posición svxpos multiplicado por 10 conseguirá en el servo movimientos de 0*10=0us para 0° , 50*10=500us para 90° y 100*100=1000us a 180°.
Si bien es posible hacer uso de cualquier modulo temporizador que incremente en pasos de un microsegundo (us), el TMR1 posibilita contabilizar hasta los 20000us que tiene el periodo de la señal PWM del servomotor, gracias a su registro de 16-bits. Para determinar los pasos en un PIC16F debemos considerar la relación que tiene el ciclo de instrucción (Tcy) con respecto a la frecuencia de operación de un PIC16F es: 
Luego de calcular la ecuación anterior, vemos que para una frecuencia de  8MHZ, el Tcy = 0.5us, por lo que necesitaremos asignar una preescala 1:2 al temporizador para conseguir incrementos de un 1.0us. (2 * 0.5u).
Para llevar a cabo la programación de control para los servomotores, he creado la librería tservo.c, que nos facilitara aplicar el proceso de señalización a todas la salidas de control PWM. Describiré como hacer uso de las dos funciones que contiene esta librería, que son:
  • TSERVOSetup(): Configura la cantidad de pines requeridos para la señalización PWM, cada pin esta asociado al puerto definido en TSVPORT, se numera desde 1 a 8, siendo 1 el pin0 y 8 el pin7.
  • TSERVOHandler(): Esta función es invocada desde la rutina de servicio a la interrupción ISR, cuando finaliza el temporizador y permite controlar el estado de la señalización para cada pin PWM, al final devuelve el valor de tiempo que se requiere para actualizar el temporizador.
Si quisiéramos duplicar la resolución de control PWM en cada salida, podríamos ajustar el temporizador para incrementos de 0.5uS,  y hacer algunos cambios a las definiciones en la librería (tservo.c / tservo.h), por lo que dejare pendiente la configuracion para una futura actualización de esta entrada.
El uso de estas funciones dentro del programa principal se resume en las siguientes secciones de código:
 Sección 1: Cabecera tservo.h

/* USER PORT DEFINITION */
#define TSVNUM 2
//Numero de servos a conectar, Máximo 8. 20ms/8 > 2ms.
#define TSVSLOT (65536UL -(20000U/TSVNUM))
//Se puede adicionar un offset +30
#define TSVTIME0 (65536UL - 900U)
//Time offset for -90
#define TSVPORT PORTD
//Puerto servo control <posn>..<pos2><pos1>
#define TSVTRIS TRISD
//Tris Puerto servo
/* END USER PORT DEFINITIOS*/

Sección 2: Programa principal

#include "tservo.h" //Cabecera de la librería servo
void setupMCU(void);
//Prototipo de función
void main(void)
{
    setupMCU();
//Configuración del PIC
    TSERVOSetup();
//Configuración del SERVO
    TSV.sv1pos = 10;
//Ajusta servo al 10%
    TSV.sv2pos = 80;
//Ajusta servo al 80%
    while(1)
    {
        if(tickms)
//Intervalo para la concurrencia
        {
            tickms = 0;
//Limpia bandera
           
//Código del programa principal
                                           
        }
    }   
}
void setupMCU(void)
//Procedimiento de configuracion del MCU
{
    OSCCONbits.IRCF = 0b111;
//Ajusta frecuencia a 8MHz
    while(OSCCONbits.HTS == 0) {};
   
 
    /* CONFIGURACIÓN TIMER1 8MHZ PASOS DE 1US */
    T1CONbits.T1CKPS = 1;  
//Pre-escala 1:2
    T1CONbits.TMR1CS = 0;  
//Modo temporizador
    T1CONbits.TMR1ON = 1;  
//Activa el modulo
    PIR1bits.TMR1IF = 0;   
//Limpia bandera
    PIE1bits.TMR1IE = 1;   
//Activa interrupción timer1
   
INTCONbits.PEIE = 1;
    INTCONbits.GIE = 1;
}
 
Sección 3: Rutina de Interrupción.

void __interrupt() isr(void) //Rutina de servicio de interrupción
{
    uint16_t res;
    if(PIR1bits.TMR1IF)
//Evento cuando finaliza el temporizador
    {
        res = TSERVOHandler();
//Función de control PWM
        T1CONbits.TMR1ON = 0;
//Para el temporizador
        TMR1L = (uint8_t) res;
//Recarga el temporizador
        TMR1H = (uint8_t) (res >> 8);
        PIR1bits.TMR1IF = 0; 
//Limpia la bandera
        T1CONbits.TMR1ON = 1;
//Inicia el temporizador
    } 
}

 
La función de control TSERVOHandler() administra el estado de cada pin en función del numero de servomotores definidos en TSVNUM,  determinando de forma automática en cambio de estado logico con base a los limites establecidos para a cada ranura de tiempo dentro del periodo PWM. Con esta técnica es posible asignar entre uno y ocho ranuras de tiempo dentro del periodo de 20ms que utiliza un servomotor. Comprenderá mejor este punto observando la distribución de tiempos que se ilustran en las siguientes figuras.

Fig3. Ranuras de tiempo PWM para 2 Servomotores

Fig4. Ranuras de tiempo PWM para 4 Servomotores

Fig5. Ranuras de tiempo PWM para 8 Servomotores

Conexión del Servo 
Como mencionamos anteriormente un servo suele contar con tres lineas de conexión, dos corresponden a la alimentación de energía y una para el control de posición. La siguiente figura ilustra la disposición de los pines y colores utilizados por algunos fabricantes.
Fig6. Puerto de conexión servo

Mas allá de la disposición de pines para un servomotor, es importante considerar aspectos técnicos que son proporcionados por el fabricante y que deben considerarse durante la etapa de diseño de un circuito electrónico.
 
Programa de control PIC16F
En este apartado veremos como crear un programa que nos permite controlar de forma independiente la posición de dos servomotores utilizando dos potenciómetros. El código del programa principal contiene procedimientos adicionales necesarios para el funcionamiento de este programa, estos incluyen la lectura ADC y la concurrencia de tareas con el TMR0.

El programa principal ingresa en un bucle continuo para ejecutar tareas cada milisegundo, estas tareas permiten destellar un LED y hacer lectura de los dos canales analógico ADC para cargar la variable de control de posición sv1pos y sv2pos. Cabe resaltar que ninguna de estas tareas representa un proceso bloqueante, la codificación utiliza una técnica de programación por estados, si quieres conocer mas detalles al respecto puedes revisar la siguiente publicación.
Fig7. Circuito para control de dos servomotores

Alternativamente a los potenciómetros que se observan en el esquema de circuito de la figura 7, podemos utilizar un modulo joystick y una estructura de movimiento de dos ejes como el que se observa en la figura 8.

Fig8. Joystick y Servo para 2-ejes
 

Conectado estos modulo a nuestro PIC16F887 conseguiremos ensayar el funcionamiento del programa de manera simple y rápida, observe como quedo el circuito de prueba.

Fig9. Circuito de Prueba PIC16F887

Aquí dejo el enlace para descargar el programa, mismo que se encuentra en formato gzip, para descomprimir y abrirlo desde MPLABX:
Si quieres ver como compilar e implementar los proyectos de este blog, en MPLABX mira este vídeo.

Conclusiones y Recomendaciones
Pudimos ver mediante un ejemplo como hacer uso del TMR1 para controlar hasta ocho servomotores utilizando las funciones presentes en la librería, adicionalmente comentar los siguientes aspectos:
  • Si bien se utilizan múltiplos de 4MHZ para la frecuencia del cristal, es posible emplear valores diferentes como 10MHZ o 20MHz, donde el control de pasos no siempre serán múltiplos exactos de 1us, considere que la frecuencia de control PWM para el servomotor es de 50Hz, pero pueden operar desde los 40Hz, hasta mas allá de 100Hz.
  • Si bien los servomotores vistos en esta sección, se diseñan para operar en un rango de movimiento que va de los 0 a 180 grados, es recomendable no alcanzar la limitaciones de movimiento para reducir la fatiga estructural o falla en el servomotor.
  • La latencia de las interrupciones puede provocar oscilaciones en el movimiento del servomotor cuando la rutina ISR atiende otras fuentes de interrupcion. Debe analizar a detalle la implicación que conlleva atender cada unos de los eventos.
Sin mas que mencionar agradezco tu visita al blog y espero que el ejemplo visto pueda ser útil en tu formación y el proyecto que desarrollas.

Atentamente, Pablo Zárate Arancibia  email: pablinza@me.com / pablinzte@gmail.com, @pablinzar

Santa Cruz de la Sierra - Bolivia