Control de Servos con PIC16F
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 >.
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 central de accionamiento en cualquier posición dentro del rango de operación para el cual fue diseñado, en general de 0 a 180° grados.
Un servo esta compuesto por un motor DC, una caja reductora y un circuito electrónico de control que nos permite ubicar un eje central de accionamiento en cualquier posición dentro del rango de operación 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.
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.
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 de tiempo o ranura dentro del periodo en la cual debe enviarse el pulso de control PWM de cada salida, su valor representa los micro-segundos necesarios para un desbordamiento del TMR1 y su calculo obedece a la siguiente ecuación.
Ec1. Calculo de ranura de tiempo en microsegundos |
- TSV.svxpos. Este valor decimal corresponde a la duración del ancho del PWM de cada salida, como valores de referencia se tiene: 0=0 grados, 50= 90 grados y 100=180 grados.
Entonces aplicando la formula anterior vemos que para una frecuencia de 8MHZ, los pasos serán de 0.5us y se necesitara un división 1:2 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.
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
/* 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;
}
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
}
}
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.
Conexión del Servo
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 tservo, Adicionalmente quiero comentar los siguientes aspectos con relación al tema:
Pudimos ver mediante un ejemplo como hacer uso del TMR1 para controlar hasta ocho servomotores utilizando las funciones presentes en la librería tservo, Adicionalmente quiero comentar los siguientes aspectos con relación al tema:
- Es posible adaptar esta técnica de control para servomotores utilizando temporizadores de 8-bit como el modulo TMR0 o TMR2, pronto actualizare la publicación con un ejemplo de uso para el caso.
- Si bien se recomienda utilizar múltiplos de 4MHZ para la frecuencia del cristal, es posible emplear valore diferentes como ser 10MHZ o 20MHz, donde el control de pasos no siempre serán múltiplos exactos de 1us, si bien la frecuencia de control PWM para el servomotor es de 50Hz, estos pueden operar en el rango de 40Hz hasta los 400Hz.
- La latencia de las interrupciones puede provocar oscilaciones en el movimiento del servomotor cuando la rutina ISR atiende otras fuentes de interrupciones. 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
No hay comentarios.:
Publicar un comentario