INTRODUCCIÓN
Como ya dije, voy a exponer la forma de controlar un servo, pero en este caso usando solo un timer.
par ello usaré el Timer 1, que dispone de una mayor resolución en su contador. Al ser un contador de 16 bits podremos llegar a contar los 20ms aunque coloquemos una resolución del timer pequeña.
par ello usaré el Timer 1, que dispone de una mayor resolución en su contador. Al ser un contador de 16 bits podremos llegar a contar los 20ms aunque coloquemos una resolución del timer pequeña.
FUNCIONAMIENTO
Colocaremos un divisor de Timer 1 de 2, por lo que la frecuencia del tic de timer quedará en 500000 Hz con la frecuencia de PIC a 4Mhz, que es la de su oscilador interno.
TARJETA PROTOTIPO
PROGRAMA
El programa no es muy complejo. Lo podemos dividir en tres secciones.
Se ha creado una interrupción en timer 1 para generar los pulsos del servo en dos pasos. El primero realizar la espera a nivel alto y el segundo la espera a nivel bajo del ciclo de onda de mando del servo.
Para calcular los tiempos y esperas necesarias he creado unas constantes con los calculos necesarios. Está en constantes en programa para no consumir memoria.
Constantes importantes para control del servo:
MAX y MIN que declaran los valores extremos de posición que vamos a permitir mover el servo. En este caso -180 y 180,pues trabajamos en medios grados.
PIN_SERVO que declara la salida usada para control del servo.
RESOLUTION que declara el número de posiciones a considerar en los 180º de recorrido del servo. En este caso indica que hay 360 posiciones, que nos darán 0,5 grados de precisión.
SHORT_TIME, CENTER_TIME , LONG_TIME y WAVE_TIME definen los tiempos de control para el servo en concreto. En este caso son los de un Parallax Standard (#900-00005)
MAIN.C
/* Main.c
* Control Servo Tunel Viento Blanco
* Autor José Angel Moneo Fdez
* Created: lunes 14 de Abril de 2015
* Processor: PIC16F882
* Compiler: CCS for PIC
* Versión 2.0
*/
#include <16F882.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES NOPUT //No Power Up Timer
#FUSES MCLR //Master Clear pin enabled
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOCPD //No EE protection
#FUSES NOBROWNOUT //No brownout reset
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOWRT //Program memory not write protected
#FUSES BORV40 //Brownout reset at 4.0V
#FUSES RESERVED //Used to set the reserved FUSE bits
#use delay(clock=4000000)
#include "GLCD.C"
#define Max 180
#define Min -180
#define UP PIN_A3 // PULSADOR UP
#define DOWN PIN_A2 // PULSADOR DOWN
#define LED_UP PIN_A5
#define LED_DOWN PIN_A4
#define PIN_SERVO PIN_C7
///////////////////////////////////////////////////////////////////////////
//// Valores extremos del impulso del servo
//// Unidades en segundos
///////////////////////////////////////////////////////////////////////////
#define SHORT_TIME 0.00075 // Punto mínimo
#define CENTER_TIME 0.0015 // Punto central
#define LONG_TIME 0.00225 // Punto maximo
#define WAVE_TIME 0.0200 // Ciclo total
#define RESOLUTION 360 // NUMERO DE PUNTOS A DEFINIR ENTRE SHORT Y LONG TIME Contado de centro a extremo
//////////////////////////////////////////////////////////////////////////
//define las los tics necesarios segun la resolución elegida
///////////////////////////////////////////////////////////////////////////
#ifndef TIMER_RATE
#define TIMER_RATE getenv("CLOCK") / 4 / 2 //Frecuencia seleccionada de timer1 ->500000
#endif
#define LONG_TICKS (int16)((float)TIMER_RATE * LONG_TIME)
#define SHORT_TICKS (int16)((float)TIMER_RATE * SHORT_TIME-100 ) // 10O TICS MENOS PARA COMPENSAR EL TIEMPO DE PROCESO
#define CENTER_TICKS (int16)((float)TIMER_RATE * CENTER_TIME)-100 // 10O TICS MENOS PARA COMPENSAR EL TIEMPO DE PROCESO Pulsos de reloj para punto central -> 750
#define WAVE_TICKS (int16)((float)TIMER_RATE * WAVE_TIME )// 10000 // (int16)((float)TIMER_RATE * WAVE_TIME ) //PULSOS CICLO DE ciclo total -> 10000
#define TIMER_VALUE (int16)(65536-WAVE_TICKS) //55536// (int16)(65536-WAVE_TICKS) //valor a programar timer1 para ciclo total -> 55536
#define POS_TICKS (int16)((float)(LONG_TICKS-SHORT_TICKS)*100/RESOLUTION)
#define pin_on output_high
#define pin_off output_low
long ticks_servo;
//INTERRUPCIÓN TIMER1
//PORPOSITO: Inicia el pulso Hight del servo
#int_TIMER1
void TIMER1_isr(void)
{//int mini;
static int secuencia=0;
disable_interrupts(INT_TIMER1);
switch (secuencia)
{
case 0: //Inicio del pulso eleva la señal de todos los servos y selecciona el tiempo del menor
//busca el menor de los tiempos entre los servos definidos y eleva las señales de los servos validos
output_High(PIN_SERVO);
set_timer1(65536-ticks_servo); //asigna la tiempo de servo
secuencia=1;
break;
case 1: //Baja la señal del actual y asigna el tiempo
output_low(PIN_SERVO); // descende la señal del mando de los servos con el mismo valor que el mínimo actual
set_timer1(TIMER_VALUE+ticks_servo); //Asigna como tiempo final el del ancho del pulso - el tiempo del servo.
secuencia=0;
break;
}
enable_interrupts(INT_TIMER1);
}
///////////////////////////////////////////////////////////////////////////
// Purpose: Inicializa timer1,
// y coloca servos a 0.
// Debe ser llamado antes de set_servo()
void init_servos()
{ ticks_servo=CENTER_TICKS;
setup_timer_1(T1_DIV_BY_2 | T1_INTERNAL);
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
}
// Proposito: Programa la posición del servo
// Entradas: 1) posición
void set_pos_servo(signed long posicion)
{long p;
// se hace la operación en dos casos para no sobrepasar la
// operación en la m,ultiplicacion con signo
p=abs(posicion);
if(posicion>0) ticks_servo =CENTER_TICKS+p*POS_TICKS/100;
else ticks_servo =CENTER_TICKS-p*POS_TICKS/100;
}
/*******itoa especifico para esta aplicación
Convierte un número entero en texto en texto en base, considerando un bit de decimal.
Este bit es enviado por separado para colocar el ".5"
*********************************************/
char* itoa(signed int16 num, char* str, int base,int1 decimal)
{
int i = 0,h=0,j=0;
char temp[10];
if (num == 0)
{
str[i++] = '0';
str[i] = '\0';
return str;
}
// Si es negativo pone el signo y lo convierte en positivo
if (num<0) { str[i++]="-";
num=abs(num);
}
// Traducimos digito a digito
while (num != 0)
{
int rem = num % base;
temp[h++] = (rem > 9)? (rem-10) + 'a' : rem + '0';
num = num/base;
}
for (j=h-1;(j+1)>0;j--)
str[i++]=temp[j];
if (decimal==0) {
str[i++]=".";
str[i++]="5";
}
str[i] = '\0'; // añadimos el final de cadena
return str;
}
// refresca los inicadores de permiso de botonera
void led_rfsh (signed long pos)
{ if(POS<Max) pin_on(LED_UP);
else pin_off(LED_UP);
if(POS>Min) pin_on(LED_DOWN);
else pin_off(LED_DOWN);
}
// envía orden de posición al servo y refresca los pulsadores y el display
void set_servo(signed long pos)
{ char Texto[10]="";
char grado[10]="Grados";
if (pos<=Max && pos>=Min) set_pos_servo(pos);
itoa(pos/2, Texto,10,(pos==(pos/2)*2));
//prepara pantalla
glcd_fillScreen(OFF);
glcd_text57(30,45,grado,2,ON);
glcd_text57(10,10,Texto,4,ON);
led_rfsh(pos);
}
//INICIALIZACIONES
void Setup()
{
setup_wdt(WDT_OFF);
//comparadores
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
set_tris_a(0b00001100);
set_tris_C(0x00);
set_tris_b(0x00);
glcd_init(ON);
glcd_fillScreen(OFF); // Must initialize the LCD
init_servos();
enable_interrupts(GLOBAL);
}
void main() {
signed long POS=0; // posición central
// int1 upold,downold;
int8 cuenta_pulsador=0; //cuenta los avances de pulsador para aumentar su velociad
Setup();
set_servo(0);
//COMIENZO DE CICLO
while(TRUE)
{
//****************Por pulsacion mantenida************
// permite mantener pulsado el mando de forma que vaya subiendo progresivamente la
// escala de incremento.
// de esta forma se podrá pasar de números bajos a altos de forma rápida.
if(POS<Max){cuenta_pulsador=1;
while (!input(UP))
{ if (POS==Max) break;
if ((POS+cuenta_pulsador)<Max)
POS+= cuenta_pulsador;
else
{ POS++; cuenta_pulsador=1;}
set_servo(POS);
cuenta_pulsador+=1;
}
}
if (POS>Min) {cuenta_pulsador=1;
while (!input(DOWN))
{ if (POS==Min) break;
if ((POS-cuenta_pulsador)>Min)
POS-= cuenta_pulsador;
else
{ POS--; cuenta_pulsador=1;}
set_servo(POS);
cuenta_pulsador+=1;
}
}
}
}
No hay comentarios :
Publicar un comentario