En esta práctica aprenderemos a controlar un servomotor mediante Miuva 18, un servomotor es un motor en el cual podremos controlar la posición de su eje, moviéndolo una cantidad determinada de grados para que posteriormente se mantenga fijo en esa posición.
El control de los servomotores es relativamente sencillo, se le tiene que enviar un pulso que puede durar desde 1 hasta 2ms, la frecuencia mínima que se necesita es de 20ms. Como se muestra en el diagrama siguiente:
En esta practica definiremos 3 posiciones diferentes, 0°, 90° y 180° (dado en tiempos de pulso 1, 1.5 y 2ms). Para enviar estos pulsos utilizaremos las interrupciones por desborde del timer0 del PIC.
Interrupciones
Las interrupciones en los PICs se activan cuando sucede alguna determinada acción, (por ejemplo cuando se desborda un timer) y lo que hacen es pausar el programa en la línea en la que está para realizar alguna acción definida en la función de interrupción para que después de realizar la acción definida regrese a la línea donde estaba.
Timer0
Los timer son recursos que tienen los microcontroladores, su funcionamiento es muy sencillo, solamente cuentan hasta el 255 o hasta el 65535 dependiendo si es de 8 o 16 bits respectivamente. Cuando llegan a su límite se dice que se desbordan por lo tanto el tiempo de desborde estará dado por diferentes factores, y se puede calcular con la siguiente fórmula:
tiempo_desborde = (4/Fosc)*Prescaler*Número a contar
Fosc es la frecuencia del oscilador, la cual definimos en las primeras líneas de cada código, el prescaler es un valor que podremos definir nosotros en el código, y el número a contar se define dependiendo de cuantos bits es el timer. Por ejemplo, si se utiliza una frecuencia de oscilador de 48MHz, un prescaler de 8 y el timer de 16 bits el tiempo de desborde será:
tiempo_desborde = (4/48000000)*8*65535 = 43.69ms
Podemos jugar con estos valores para obtener alguna cantidad de tiempo deseada. En este caso, recordemos que el periodo de la señal enviada al servomotor debe ser de 20ms, por lo tanto necesitamos ajustar los valores para ajustar el tiempo de desborde a cada posición.
Por ejemplo para 0°:
Señal en alto (1ms) -> Señal en bajo (19ms)
Para 90°:
Señal en alto (1.5ms) -> Señal en bajo (18.5ms)
Para 180°:
Señal en alto (2ms) -> Señal en bajo (18ms)
Entonces tendremos que predefinir el tiempo de desborde para los 3 casos, esto se puede hacer modificando el número en el que va a comenzar a contar el timer, esto quiere decir que por ejemplo si tarda en desbordarse 43.69ms y quisiéramos un tiempo de 21.84ms, el timer tendría que contar la mitad, entonces en lugar de comenzar en 0 tendría que comenzar en 32767 (la mitad de 65535 para 16 bits).
Para los casos mostrados anteriormente se tendría que comenzar en:
37035 -> 19ms
tiempo_desborde = (4/48000000)*8*(65535-37035) = 19m
37785 -> 18.5ms
38535 -> 18ms
Para realizar esto se utilizan las siguientes funciones:
setup_timer_0(T0_INTERNAL|T0_DIV_8);
Definimos que el timer trabajará con el oscilador interno y tendrá un prescaler de 8.
enable_interrupts(int_TIMER0); //Habilitamos las interrupciones por desborde de timer0 enable_interrupts(GLOBAL);
set_timer0(38535); //Precargar el timer0
Con esta función le damos el valor en el que queremos que comience a contar el timer0.
Es importante en este punto definir también la función de interrupción, dicho de otra forma, lo que se tiene que realizar cuando se desborde el timer. En este caso, mandar el pulso indicado al servomotor. Esto se hace con las siguientes líneas, las cuales deben de estar antes de la función principal.
#int_TIMER0 //Función de interrupción void TIMER0_isr(void){ //Aquí se describe lo que realiza la función de interrupción }
Una vez entrando a esta función vamos a declarar el envío del pulso correspondiente, por ejemplo, para el pulso de 1ms, quedaría de la siguiente forma:
output_high(PIN_C1); //Encender PWM delay_us(1000); //Esperar 1ms output_low(PIN_C1); //Apagar PWM set_timer0(37035); //Precargar el timer0
Por último, crearemos una variable que llamaremos “a”, en la cual almacenaremos si el servomotor tendría que ir a la posición 1, 2 o 3 (0°, 90° o 180°). En nuestra función principal tendríamos que definir que si detecta que el pulsador se presionó entonces aumente el valor de a y en dado caso que su valor sea mayor a 3 entonces lo reinicie a 1.
if (input_state(PIN_C0)){ //Si se presionó el pulsador delay_ms(200); //Retardo de 200ms a++; //Aumenta 1 if (a >= 4){ //Si a es mayor o igual a 4 a = 1; //Reiniciamos el valor de a } }
En la función de interrupción deberemos verificar el valor de a para saber a que posición tiene que enviarlo, esto se puede realizar con un ciclo if.
#int_TIMER0 //Función de interrupción void TIMER0_isr(void){ if (a == 1){ //Si el boton está en 1 //Indicaciones para el pulso de 1ms } else if (a == 2){ //Si el botón está en 2 //Indicaciones para el pulso de 1.5ms } else{ //Si el botón está en 3 //Indicaciones para el pulso de 2ms } }
Con esto podemos crear el siguiente código.
Código del programa
#include <18f46K22.h> // la librería del PIC #device ADC = 10 // la librería del PIC #fuses HSM,NOWDT,PUT,BROWNOUT, MCLR,NOLVP,NOXINST,NOWRT,PLLEN #use delay (clock=48M) //Seleccionamos la frecuencia de reloj de 48MHz #use standard_io(c) //Preconfiguración del puerto C #use standard_io(b) //Preconfiguración del puerto B int a = 1; //Variable para saber cuantas veces se presionó el pulsador #int_TIMER0 //Función de interrupción void TIMER0_isr(void){ if (a == 1){ //Si el boton está en 1 output_high(PIN_C1); //Encender PWM delay_us(1000); //Esperar 1ms output_low(PIN_C1); //Apagar PWM set_timer0(37035); //Precargar el timer0 } else if (a == 2){ //Si el botón está en 2 output_high(PIN_C1); //Encender PWM delay_us(1500); //Esperar 1.5ms output_low(PIN_C1); //Apagar PWM set_timer0(37785); //Precargar el timer0 } else{ //Si el botón está en 3 output_high(PIN_C1); //Encender PWM delay_us(2000); //Esperar 2ms output_low(PIN_C1); //Apagar PWM set_timer0(38535); //Precargar el timer0 } } void main(){ //Funcion principal set_tris_C(0b00000001); //PIN C0 como entrada setup_timer_0(T0_INTERNAL|T0_DIV_8); //Timer0 con osc. interno y Prescaler = 8 // VELOCIDAD_TIMER0 = 4/48000000*8 = 0.666ms enable_interrupts(int_TIMER0); //Habilitar interrupciones enable_interrupts(GLOBAL); set_timer0(0x8AD0); //Precargar el timer0 while(true){ //Bucle infinito if (input_state(PIN_C0)){ //Si se presionó el pulsador delay_ms(200); //Retardo de 200ms a++; //Aumenta 1 if (a >= 4){ //Si a es mayor o igual a 4 a = 1; //Reiniciamos el valor de a } } } }