TODOPIC
Bienvenido(a), Visitante. Por favor, ingresa o regístrate.
¿Perdiste tu email de activación?
03 de Septiembre de 2010, 05:29:19

Ingresar con nombre de usuario, contraseña y duración de la sesión
Buscar:     Búsqueda Avanzada
257111 Mensajes en 28437 Temas por 27916 Usuarios
Último usuario: zororyuzaki
* Inicio Ayuda Buscar Calendario Ingresar Registrarse
Buscar en TodoPIC
+  TODOPIC
|-+  Microcontroladores PIC
| |-+  Lenguaje C para microcontroladores PIC (Moderadores: Modulay, pikman, pocher, vszener, Suky)
| | |-+  Ejemplito 16F876A: Controlando un SERVO con el PIC desde nuestro PC
0 Usuarios y 1 Visitante están viendo este tema. « anterior próximo »
Páginas: [1] 2 Marcar como favorito Imprimir
Autor Tema: Ejemplito 16F876A: Controlando un SERVO con el PIC desde nuestro PC  (Leído 4465 veces)
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« : 14 de Enero de 2006, 02:25:00 »

(O todo lo que siempre quiso saber sobre el Servo y nunca se atrevió a preguntar)

Pero como decía Jack El Destripador: ¡Vayamos por partes!

Y para empezar un poco de teoria, que a ninguno de nosotros nos va a venir mal. Un Servomotor es un cacharro, entre otros muchos, que puede manejarse inyectándole un señal PWM.

Y si me preguntáis qué es esto os respondo que es un método de control que consiste en enviar un tren de pulsos, cada uno de ellos con un periodo de tiempo en alto, a 5V, y otro en bajo, a 0V; separados cada uno del siguiente un tiempo constante y que podemos variarle la respectivas duraciones que permanece en alto y bajo, o como su propio nombre indica: Pulse Width Modulation, que dicho para entendernos significa Modulación de Ancho de Pulso.

Un servo es un motor controlado por una electronica que lee el PWM y que se encarga de mover al motor dependiendo de lo que ha leído.

El servo, o mejor dicho la electrónica del servo colococa al motor en cada posición dependiendo del tiempo en que el pulso que le inyectamos permanece en alto. Si el tiempo que dura en estado alto dura exactamente 1.5 milisegundos entonces el Servo va y se coloca en el centro de su recorrido, si dura exactamente 0.5 milisegundos el servo retrocede desde el punto medio unos 90º y se coloca en su extremo izquierdo y si, por último, dura exactamente 2.5 milisegundos el servo avanza desde el punto medio unos 90º y se coloca en su extremo derecho. Al tiempo en que permanece en alto un pulso le llamamos Duty Cicle.

Con duraciones intermedias del tiempo en que permanece el pulso en alto, o Duty Cicle, el servo se posiciona en puntos intermedios de su recorrido.

Para que el servo responda correctamente a estos distintos Duty Cicles los pulsos deben llegarle al servo con una periodicidad, o frecuencia constante, uno tras otro, separados 20 milisegundos cada uno uno del siguiente, cada flanco de subida debe estar separado del siguiente flanco de subida los mismos 20 milisegundos; por lo tanto cada ciclo alto-bajo dura siempre exactamente 20 milisegundos y lo que variamos es la relación entre el tiempo que está en alto y en bajo.

Decir que los pulsos estan separados unos de otros 20 milisegundos es exactamente lo mismo que decir que se envían con una frecuencia de 50 Herzios, ya que 50 hz son 50 pulsos por segundo y por lo tanto 1000 milisegundos (que tiene un segundo) dividido entre 50 son exactamente eso: 20 milisegundos. O sea aplicamos la formula f (frecuencia en Herzios) = 1 / t (Periodo en Segundos).

En el fondo todo este asunto no es distinto de encender y apagar nuestro famoso led, que es algo por lo que empezamos todos cuando comenzamos a trastear con los PIC"s, pero controlando muy exactamente los tiempos durante los que que está encendido y apagado.

Esto podemos verlo de forma mas fácil y clara en la imagen esquema siguiente:



Ahora lo que tenemos que hacer es saber cómo podemos controlar estos tiempos en nuestro PIC para poner en alto (disparar el pulso) y en bajo (apagarlo) con la cadencia adecuada, siguiendo la tabla de tiempos descrita mas arriba.

Para ello voy a echar mano del socorrido TIMER0 del PIC que me va a servir de reloj para saber cuándo y durante cuánto tiempo tengo que tener mi pulso en alto. Como soy el mas listo de la clase he elegido un divisor, o preescaler, del TIMER0 de 1:16 (mas adelante os contaré el por qué de este divisor).

Asumiendo que tenemos nuestro PIC funcionando con un cristal de 4.00Mhz entonces el TIMER0 funcionando a 1:16 hace saltar la Interrupción por Desbordamiento de Timer, tambien conocida como RTCC, cada 4.096 milisegundos.

Esto es lo mismo que decir que TIMER0 tarda 4.096 milisegundos en contar desde 0 a 255 y que al llegar a 255 pasar de nuevo a 0 hace saltar la RTCC.

Esto significa que cada paso de contador del TIMER0, a lo que llamamos un tick de reloj, tarda 4.096 / 256 = 0.016 milisegundos. Esto me da una pauta bastante facil de calcular que consiste en que cada 5 RTCC completas tengo 5 * 4.096 = 20.48 milisegundos que es un poco más de lo que necesito, que son 20 milisegundos exactos:

Esto lo podemos conseguir contando 4 RTC"s completas, a 4.096 milisegundos cada una, y otra más un poco mas corta. No podemos hacer que la RTCC se acabe antes de la cuenta, pero si que podemos, y es lo que vamos a hacer, que empiece a contar un poco mas tarde, que no empiece a contar desde 0 sino desde 30: esto se explica porque 30 * 0.016 = 0.48 milisegundos menos que va contar esta última RTCC, al haber empezado desde un valor de 30 en lugar de 0, luego 4.096 - 0.48 = 3.616 milisegundos para la última RTCC.

Concluyendo: tengo 4 RTCC"s completas a 4.096 milisegundos y una capada a 3.616 luego 4 * 4.096 + 3.616 = 20 milisegundos. Lo que realmente voy a hacer es contar 1 RTC completa, 2 RTCC completas, 3 RTCC completas, 4 RTCC completas yy pongo el contador de TIMER0 a 30, y por fin 5 RTCC completas luego ya han pasado 20 milisegundos exactamente. Ya tenemos fijada la frecuencia que necesitamos.

A estas 5 RTCC"s les llamo flagRTCC que sólo voy a activar cuando se completen las 5 RTCC"s (4 completas y otra mas incompleta).

Además sabiendo que cada tick de reloj ocupa 0.016 milisegundos podemos traducir los tiempos de anchos de pulsos descritos anteriormente en ticks de nuetro reloj particular: así 0.5 milisegundos son lo mismo que esperar 31 ticks de reloj, 1.5 milisegundos equivalen a 93 ticks de reloj y 2.5 milisegundos son 155 ticks de reloj. (Recordad que llamamos tick de reloj al tiempo que tarda TIMER0 en contar 1 más).

La imagen anterior podemos ahora convertirla en esta siguiente en la que hemos cambiado los tiempos por RTCC"s y Ticks:



Como vemos en el cronograma superior: cada vez que se produce un super RTCC, de 4 RTC"s y pico a la que llamamos flagRTCC, ponemos en alto el PIN de la señal del Servo, debemos recordar que esto siempre va a ocurrir en el tránsito del contador TIMER0 entre los valores de 255 y 0, por lo que flagRTCC siempre va a coincidir con TIMER0=0.

Ahora entonces solo debemos esperar el número suficiente de ticks para volver a poner nuestro pin a bajo. Si deseamos que el Servo se posicione en su centro debemos mantener el PIN en alto durante 93 ticks de TIMER0 o, lo que es lo mismo, esperar 1.5 milisegundos para bajar el pulso.

La secuencia queda entonces de la siguiente manera:

- RTCC corre alocadamente, una tras otra, dedicandose exclusivamente a contar cuántas de ellas han pasado; si es la cuarta pone TIMER0 a 30 para que la quinta sea mas corta, si es la quinta pone en alto flagRTCC para lo que sea necesario y comienza de nuevo.

- En el programa principal detectamos que flagRTCC se ha activado así que lo desactivamos y ponemos en alto el PIN y marcamos, con flagSERVO1, que acabamos de activarlo.

- A continuación, y siempre que flagSERVO1 esté activado, comprobamos el valor de TIMER0 que si es mayor que el que deseamos, en nuestro caso 93, y cuando lo alcancemos ponemos a bajo el PIN y lo marcamos desactivando flagSERVO1. Y hemos acabado.

Cada 20 milisegundos activamos el pulso, y transcurridos 1.5 milisegundos lo desactivamos, que es exactamente lo que queríamos hacer.

El valor de TIMER0, con el comparamos para controlar la duración de cada pulso, la tenemos guardada en tSERVO1, que inicialmente cargamos con el número de ticks necesarios para colocar el Servo en su punto medio, ticks_PULSO_MEDIO. El valor de tSERVO1 lo podemos cambiar dinámicamente mediante la recepción de comandos a traves de la RS232.

De esta forma con los comandos "1", "2" y "3" podemos cambiar el valor de tSERVO1 a ticks_PULSO_MINIMO, ticks_PULSO_MEDIO y ticks_PULSO_MAXIMO respectivamente; y con los comandos "+" y "-" vamos incrementando o decrementando su valor. Con "r" le pedimos al PIC que nos envíe su valor actual.

Ahora solo nos quedaría implementar todo esto en un programa en C que queda de la siguiente forma:

Codigo:


// servo_pwm_232

// Ejemplo con un servo FUTABA S3003
// Alimentación y pulsos a 5V
// Cuadro de Tiempos :
//    Periodo 20 ms (Frecuencia 50 Hz)
//    Ancho Pulso minimo 0.5 ms
//    Ancho pulso medio  1.5 ms
//    Ancho pulso maximo 2.5 ms
//    TMR0 a 1:16 -> 1 RTCC cada 4.096 ms
//                -> 1 Tick cada 0.096 / 256 = 0.016 ms
//                -> 20 ms = (4 x RTCC completas) + (1 * RTCC - 30 ticks)
//    Ancho Pulso minimo 0.5 ms ->  31 ticks de TMR0
//    Ancho pulso medio  1.5 ms ->  93 ticks de TMR0
//    Ancho pulso maximo 2.5 ms -> 155 ticks de TMR0

#include <16f876a.h>
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT
#use delay(clock=4000000)
#use standard_io(b)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

#define PIN_SERVO1 PIN_B0

const int AJUSTE_FINO_DE_RTCC =  30;
const int ticks_PULSO_MINIMO  =  31;
const int ticks_PULSO_MEDIO   =  93;
const int ticks_PULSO_MAXIMO  = 155;

int1 flagRTCC     = 0;
int   contRTCC    = 0;
int1 flagSERVO1 = 0;
int   tSERVO1     = ticks_PULSO_MEDIO;
char Keypress   =0x00;

void eco_servos(void);
void ajusta_servo(void);

#int_rda
void rda_isr() {
   Keypress=0x00;
   if(kbhit()){
      Keypress=getc();
   }
}

#int_RTCC
RTCC_isr(){
   ++contRTCC;
   if(contRTCC==4){
      set_TIMER0(AJUSTE_FINO_DE_RTCC);
   }
   if(contRTCC==5){
      flagRTCC=1;
      contRTCC=0x00;
   }
}

void main() {

   int ValTIMER0;

   setup_counters(RTCC_INTERNAL,RTCC_DIV_16);
   enable_interrupts(int_rda);
   enable_interrupts(global);
   printf("
SERVO Commander

" );
   eco_servos();
   set_TIMER0(0);
   enable_interrupts(INT_RTCC);
   do {
      // DISPARO DEL PULSO PWM
      if(flagRTCC==1){
         flagRTCC=0;
         output_high(PIN_SERVO1);
         flagSERVO1=1;    
      }
      // CONTROL DE ANCHO DEL PULSO PWM
      if(flagSERVO1==1){
         valTIMER0 = get_TIMER0();
         if(valTIMER0>tSERVO1){
            flagSERVO1=0;
            output_low(PIN_SERVO1);
         }  
      }
      // CONTROL DESDE LA RS-232
      if(Keypress!=0x00){
           ajusta_servo();   
         Keypress=0x00;
      }
   } while (TRUE);
}

void ajusta_servo(void){

  switch(Keypress){
    // Periodos Prefijados
    case "1": tSERVO1=ticks_PULSO_MINIMO;
              break;
    case "2": tSERVO1=ticks_PULSO_MEDIO;
              break;
    case "3": tSERVO1=ticks_PULSO_MAXIMO;
              break;
    // Inc Dec Periodo
    case "+": if(++tSERVO1>ticks_PULSO_MAXIMO){
                tSERVO1=ticks_PULSO_MAXIMO;
              }
              break;
    case "-": if(--tSERVO1<ticks_PULSO_MINIMO){
                tSERVO1=ticks_PULSO_MINIMO;
              }
              break;
    // Dame Periodo actual
    case "r": eco_servos();
              break;
  }
}

void eco_servos(void){
   printf("S=%u
",tSERVO1);
}




Descargar código aqui

Como podéis ver en el comentario inicial del código, todo esto está montado para la familia de servos compatibles FUTABA S3003, HiTec HS-300 CW, HOBBICO COMMAND CS-51,   que son de los mas usados por los aficionados al Radiocontrol.

Para ajustar este código a otros servos solo hay que calcular el preescaler y los ticks necesarios para ajustarse a las caracteristicas de éste. Hay servos que funcionan a 400hz en lugar de 50hz y con anchos de pulso ligeramente distintos a los utilizados aquí. Es normal anchos de pulso en los extremos de 1.00 y 2.00 milisegundos respectivamente. El punto medio en 1.5 milisegundos es muy común.

Otro corolario de este ejemplito estriba en la posibilidad de manjear varios servos. Utilizando la misma estructura de PIN_SERVO1 y tSERVO1 podemos habilitar el control indistinto de tantos servos como deseemos, teniendo así PIN_SERVO2 y tSERVO2, PIN_SERVO3 y tSERVO3 ... etc controlando cada uno de ellos de forma absolutamente similar.

Espero que os guste.



Aqui para ver la imagen en grande.

P.D. Muchas gracias al amigo dogflu por sus inteligentes y acertadas observaciones sobre la redaccion de este ejemplito.


En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
Nocturno
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
España España

Mensajes: 12825



WWW
« Respuesta #1 : 15 de Enero de 2006, 01:51:00 »

Deberías escribir un libro llamado "Programación PIC para torpes"
Con tus explicaciones es imposible no entenderlo.
En línea

Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ
tougteno
PIC10
*
Desconectado Desconectado

Mensajes: 28


« Respuesta #2 : 17 de Enero de 2006, 02:34:00 »

Que buena explicación RedPic! La veradad es que no tenia intensiones de leerlo, pero la corriente me fue llevandoGiño
En línea
JRpropo
PIC10
*
Desconectado Desconectado

Mensajes: 21


« Respuesta #3 : 28 de Febrero de 2006, 09:57:00 »

Hola Redpic,

MUY BUENA tu pagina WEB.
Estaba analizando como se pueden controlar varios servos con tu programa, pero de insertar mas rutinas para mas de un servo se rompería la concepción de 20mseg de frame para cada servo. Como modificarías tu programa para lograr al menos incluir 4 servos ?

También estoy un poco confundido cuando dices "El TIMER0 funcionando a 1:16 hace saltar la Interrupción por Desbordamiento de Timer, también conocida como RTCC, cada 4.096 milisegundos". Como sacas esta cuenta si el cristal es de 4MHz ?

Gracias,
JRpropo
En línea
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #4 : 28 de Febrero de 2006, 10:09:00 »

Inicio del artículo de mi página Los Cristales y El Tiempo:

Cálculo de tiempos según el Cristal oscilador que usamos

   En la sección El Rincón del C dedicábamos un artículo al uso de la Interrupción RTCC, en el que mostrábamos una tabla de tiempos que tardaba el TIMER0 en dar una vuelta de manivela completa, que dependía del Preescaler seleccionado. Está tabla de tiempos adjuntaba como nota que el Cristal oscilador utilizado era de 4 Mhz.
 
 
   Sin embargo soy capaz de imaginar a cualquiera de vosotros, amables Picmaníacos, con los ojos cerrados metiendo la mano en vuestro saco de cristales. Sacando uno al azar. La probabilidad de que dicho cristal sea de 4 Mhz es calculable. Es directamente proporcional al numero de cristales de 4 Mhz que haya en vuestro saco e inversamente proporcional al número de otros tipos de cristales que tengáis en tan heterogénea mezcla. Una pequeña locura.
 
 
   ¿Que hacemos, entonces, si vais a utilizar un cristal cuyo valor este alejado, o muy alejado, de nuestros 4 Mhz de referencia? Pues fácil y sencillo como juego de chiquillo: Calculamos el Tiempo de RTCC en función del Cristal que vamos a usar, o sea el tema de este artículo.
 
 
   La primera fórmula que vamos a ver nos da el Tiempo que tarda RTCC en dispararse, sin Preescaler, comenzando TIMER0 en 00h y terminando en FFh, o sea desbordamiento completo:
 
 
Time = (256 * 4) / FOSC  (1)
 
donde Time es el tiempo en segundos (S) que tarda RTCC en saltar y FOSC es la frecuencia de oscilación de nuestro cristal en Hercios (Hz)
 
continúa ...

En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #5 : 28 de Febrero de 2006, 10:23:00 »

Para dos servos, por ejemplo, solo tienes que duplicar donde corresponda todo lo que aparezca con la palabra SERVO1 ...

Codigo:


...

#define PIN_SERVO1 PIN_B0
#define PIN_SERVO2 PIN_B1

...

int1 flagSERVO1 = 0;
int   tSERVO1     = ticks_PULSO_MEDIO;
int1 flagSERVO2 = 0;
int   tSERVO2     = ticks_PULSO_MEDIO;
...

      // DISPARO DEL PULSO PWM
      if(flagRTCC==1){
         flagRTCC=0;
         output_high(PIN_SERVO1);
         flagSERVO1=1;    
         output_high(PIN_SERVO2);
         flagSERVO2=1;    
      }
      // CONTROL DE ANCHO DEL PULSO PWM
      if(flagSERVO1==1){
         valTIMER0 = get_TIMER0();
         if(valTIMER0>tSERVO1){
            flagSERVO1=0;
            output_low(PIN_SERVO1);
         }  
      if(flagSERVO2==1){
         valTIMER0 = get_TIMER0();
         if(valTIMER0>tSERVO2){
            flagSERVO2=0;
            output_low(PIN_SERVO2);
         }  
      }

...

    // Periodos Prefijados
    case "1": tSERVO1=ticks_PULSO_MINIMO;
              break;
    case "2": tSERVO1=ticks_PULSO_MEDIO;
              break;
    case "3": tSERVO1=ticks_PULSO_MAXIMO;
              break;
    // Inc Dec Periodo
    case "+": if(++tSERVO1>ticks_PULSO_MAXIMO){
                tSERVO1=ticks_PULSO_MAXIMO;
              }
              break;
    case "-": if(--tSERVO1<ticks_PULSO_MINIMO){
                tSERVO1=ticks_PULSO_MINIMO;
              }
              break;
    case "a": tSERVO2=ticks_PULSO_MINIMO;
              break;
    case "b": tSERVO2=ticks_PULSO_MEDIO;
              break;
    case "c": tSERVO2=ticks_PULSO_MAXIMO;
              break;
    // Inc Dec Periodo
    case "m": if(++tSERVO2>ticks_PULSO_MAXIMO){
                tSERVO2=ticks_PULSO_MAXIMO;
              }
              break;
    case "n": if(--tSERVO2<ticks_PULSO_MINIMO){
                tSERVO2=ticks_PULSO_MINIMO;
              }
              break;

...

void eco_servos(void){
   printf("S1=%u S2=%u
",tSERVO1,tSERVO2);
}




En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
elesep
Colaborador
PIC10
*****
Desconectado Desconectado

Mensajes: 42


« Respuesta #6 : 28 de Febrero de 2006, 11:22:00 »


Muy Bien explicado estoy seguro que va a ser de mucha ayuda para las personas que recien empiezan, y que puedan entender con facilidad, Ya que esta muy bien redactado.

Muy buen aporte
En línea
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #7 : 01 de Marzo de 2006, 10:32:00 »

Gracias elesep por tus palabras de ánimo. Son la gasolina que mueve este motor. Rebotado

En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
Ojosdeluna
PIC10
*
Desconectado Desconectado

Mensajes: 5


« Respuesta #8 : 09 de Marzo de 2006, 03:21:00 »

Hola ! Sonrisa

      La explicacion que dio Redpic con los servos salvo mi proyecto de empezar con el pie inzquierdo mucahs gracias Sonrisa

     Sin embargo la parte donde dices el ciclo de trabajo y ello me dio la ideaRollEyes de poque no utilizar un PWM como el TL494 para controlar el ciclo de trabajo a una frecuencia dada por :
                                   fosc=1.1/RC
                          haciendo fosc=1/T tenemos que f=50 hz
                          y proponiendo .1microF tenemor una R de 220K
                          perfectamente  comercial si se desea ajustar.

     Asi tenemos una frecuencia constante y para variar los ciclos de trabajo podriamos utilizar un pic y un adc por resistencias, esto con el objetivo de que el programa del pic pueda ocuparse de otros mandos e instrucciones y solo escribir en un puerto la posicion, si el puerto se ocupada para otras cosas pues solo hacer una rutina para controlar un latch D o dispositivo a fin que almacene la posicion.

     Ocuparia mas espacio tal ves en una placa, pero podria entonces ocuparme de otras ciclos para hacer un programa tal ves mas complejo o que se ocupe de sensores o "x"  y dejar que la pocision dada en el latch lo haga de forma automatica no? Bueno es una idea jejejeAvergonzado

      Ahora dos preguntitas para redpic, el servo cambia de posicion a la izquierda al aplicarle pulsos de 1.5 ms 90 grados, pero 90 grados de su posicion actual (puede ke sea izquierda o derecha) o desde su posicion central? Y si ya llego a su pocicion digamos Izquierda y sigo aplicando los pulsos este seguira intentando llegar a la izquierda o se queda en esa posición?Rebotado
En línea
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #9 : 05 de Noviembre de 2006, 09:41:34 »

Tal como ha descubierto el amigo Aitopes hay un error de transcripción en el código fuente de este ejemplo.

Pongo a continuación el código original:

Código
GeSHi (c):
  1. // servo_pwm_232
  2.  
  3. // Ejemplo con un servo FUTABA S3003
  4. // Alimentación y pulsos a 5V
  5.  
  6. // Cuadro de Tiempos :
  7.  
  8. //    Periodo 20 ms (Frecuencia 50 Hz)
  9. //    Ancho Pulso minimo 0.5 ms
  10. //    Ancho pulso medio  1.5 ms
  11. //    Ancho pulso maximo 2.5 ms
  12.  
  13. //    TMR0 a 1:16 -> 1 RTCC cada 4.096 ms
  14. //                -> 1 Tick cada 0.096 / 256 = 0.016 ms
  15. //                -> 20 ms = (4 x RTCC completas) + (1 * RTCC - 30 ticks)
  16.  
  17. //    Ancho Pulso minimo 0.5 ms ->  31 ticks de TMR0
  18. //    Ancho pulso medio  1.5 ms ->  93 ticks de TMR0
  19. //    Ancho pulso maximo 2.5 ms -> 155 ticks de TMR0
  20.  
  21.  
  22. #include <16f876a.h>
  23. #fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT
  24. #use delay(clock=4000000)
  25. #use standard_io(b)
  26. #use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
  27.  
  28. #define PIN_SERVO1 PIN_B0
  29.  
  30. const int AJUSTE_FINO_DE_RTCC =  30;
  31. const int ticks_PULSO_MINIMO  =  31;
  32. const int ticks_PULSO_MEDIO   =  93;
  33. const int ticks_PULSO_MAXIMO  = 155;
  34.  
  35. int1 flagRTCC   = 0;
  36. int  contRTCC   = 0;
  37. int1 flagSERVO1 = 0;
  38. int  tSERVO1    = ticks_PULSO_MEDIO;
  39.  
  40. char Keypress=0x00;
  41.  
  42. void eco_servos(void);
  43.  
  44. #int_rda
  45. void rda_isr() {
  46.  
  47.   Keypress=0x00;
  48.   if(kbhit()){
  49.      Keypress=getc();
  50.   }
  51. }
  52.  
  53. #int_RTCC
  54. RTCC_isr(){
  55.  
  56.   ++contRTCC;
  57.   if(contRTCC==4){
  58.      set_TIMER0(AJUSTE_FINO_DE_RTCC);
  59.   }
  60.   if(contRTCC==5){
  61.      flagRTCC=1;
  62.      contRTCC=0x00;
  63.   }
  64. }
  65.  
  66. void main() {
  67.  
  68.   int ValTIMER0;
  69.  
  70.   setup_counters(RTCC_INTERNAL,RTCC_DIV_16);
  71.  
  72.   enable_interrupts(int_rda);
  73.   enable_interrupts(global);
  74.  
  75.  
  76.   printf("\r\n\SERVO Commander\r\n\r\n");
  77.   eco_servos();
  78.  
  79.   set_TIMER0(0);
  80.  
  81.   enable_interrupts(INT_RTCC);
  82.  
  83.  
  84.   do {
  85.  
  86.      // DISPARO DEL PULSO PWM
  87.  
  88.      if(flagRTCC==1){
  89.         flagRTCC=0;
  90.         output_high(PIN_SERVO1);
  91.         flagSERVO1=1;    
  92.      }
  93.  
  94.      // CONTROL DE ANCHO DEL PULSO PWM
  95.      if(flagSERVO1==1){
  96.         valTIMER0 = get_TIMER0();
  97.         if(valTIMER0>tSERVO1){
  98.            flagSERVO1=0;
  99.            output_low(PIN_SERVO1);
  100.         }  
  101.      }
  102.  
  103.      // CONTROL DESDE LA RS-232
  104.      
  105.      if(Keypress!=0x00){
  106.         switch(Keypress){
  107.            // Periodos Prefijados
  108.  
  109.            case '1': tSERVO1=ticks_PULSO_MINIMO;
  110.                      break;
  111.            case '2': tSERVO1=ticks_PULSO_MEDIO;
  112.                      break;
  113.            case '3': tSERVO1=ticks_PULSO_MAXIMO;
  114.                      break;
  115.            case '+': if(++tSERVO1>ticks_PULSO_MAXIMO){
  116.                        tSERVO1=ticks_PULSO_MAXIMO;
  117.                      }
  118.                      break;
  119.            case '-': if(--tSERVO1<ticks_PULSO_MINIMO){
  120.                        tSERVO1=ticks_PULSO_MINIMO;
  121.                      }
  122.                      break;
  123.            case 'r': eco_servos();
  124.                      break;
  125.        }
  126.        
  127.         Keypress=0x00;
  128.      }
  129.  
  130.  
  131.   } while (TRUE);
  132.  
  133. }
  134.  
  135. void eco_servos(void){
  136.   printf("S=%u\r\n",tSERVO1);
  137. }
  138.  

En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
Marttyn
Colaborador
PIC24H
*****
Desconectado Desconectado

Sexo: Masculino
Argentina Argentina

Mensajes: 1280


Las cucarachas dicen "No al debugger!"


WWW
« Respuesta #10 : 19 de Marzo de 2007, 03:27:38 »

Código:
   do {
      // DISPARO DEL PULSO PWM
      if(flagRTCC==1){
         flagRTCC=0;
         output_high(PIN_SERVO1);
         flagSERVO1=1;   
      }
      // CONTROL DE ANCHO DEL PULSO PWM
      if(flagSERVO1==1){
         valTIMER0 = get_TIMER0();
         if(valTIMER0>tSERVO1){
            flagSERVO1=0;
            output_low(PIN_SERVO1);
         }   
      }
      }
   } while (TRUE);

estoy iniciandome con los servos y me base en esta rutina para hacer funcionar uno, pero sin haberlo llevado a la practica encuentro algo que no se si ira bien. en el bucle infinito, los primeros 20ms de funcionamiento no ocurrira nada, ya que flagRTCC es 0 y flagServo1 tambien, entonces no entrara en ninguno de los dos IF hasta que no ocurran 5 desbordamientos. esta claro que para esta aplicacion en concreto esto no plantea un problema, pero para otras cosas tal vez seria mejor declarar
Código:
int1 flagRTCC = 1
asi el servo comenzara a moverse desde el primer ms, no? Smile

por cierto, un servo al que se le esta enviando constantemente un pulso igual al anterior, osea, se le dice que se mantenga quieto donde esta, y que no tiene nada que haga fuerza para quitarlo del sitio, tiene mucho consumo? porque por este motivo me planteo encender un servo solo cuando lo tengo que mover. y si no se le envia señal alguna, pero tiene Vcc? consume?
En línea

La gente ve las cosas que existen y se pregunta por qué.
Yo prefiero imaginar lo que no existe y preguntarme por qué no.
Marttyn
Colaborador
PIC24H
*****
Desconectado Desconectado

Sexo: Masculino
Argentina Argentina

Mensajes: 1280


Las cucarachas dicen "No al debugger!"


WWW
« Respuesta #11 : 29 de Marzo de 2007, 05:27:58 »

otra vez yo estoy molestando con el tema de los servos, aunque nadie me responda...  Sad
me estaba preguntando yo, porque esperar al 5º rtcc para quitarle los ms que sobran si se le pueden quitar a todos los rtcc lo mismo sin tener que comprobar si estamos en la 4º rtcc o cosas asi...
lo que digo es que si queremos 20ms, simplemente contamos 5 rtcc de 4ms (en vez de 4 de 4.096ms y 1 de 3.616ms).
entonces en vez de sacarle 30 a la ultima habria que sacarle (30 ticks / 5 rtcc) 6 ticks de reloj a cada rtcc, que nos coincide casualmente para que las rtcc se produzcan cada 4ms EXACTOS  Mr. Green

Código:
#int_RTCC
void RTCC_isr(void){

   contRTCC = contRTCC + 1;
   set_TIMER0(6);    //en vez de 6 podemos poner "ajuste_fino_RTCC" y declararlo como constante arriba... No te lo tomes a mal RedPic, es broma  :-)

   if(contRTCC==5){
      flag5RTCC=1;
      contRTCC=0;
   }
}

ahora, para que todo siga funcionando correctamente habria que hacer una pequeña modificacion que es:

Código:
valTIMER0 = get_TIMER0() - 6;

es decir, cuando necesitamos saber por que tick esta el timer le restamos los 6 ticks que le sumamos antes.
lo admito, es probable que muchos piensen que soy medio gilip.... o cosas asi por estar dandole vueltas a algo que ya esta solucionado, pero es que me gusta que las cosas sean redondas, simetricas, simples y etc...  lol
salu2 y espero que haya alguien que no piense que realmente soy $x#!&? y esto le resulte util
En línea

La gente ve las cosas que existen y se pregunta por qué.
Yo prefiero imaginar lo que no existe y preguntarme por qué no.
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #12 : 30 de Marzo de 2007, 08:05:21 »

Querido Marttyn:

1º.- No molestas con este tema de los servos, ni con cualquier otro parecido siempre y cuando tenga que ver con la temática de este foro.  Mr. Green

2º.- El que te respondan o no es como el tiempo meteorológico, unas veces llueve, otras hace sol y aún otras ocurren las dos cosas al mismo tiempo. Hay veces que un hilo se llena de post en segundos y otras que duerme el sueño de los justos durante meses. He vivido ambas situaciones y se lo que se siente cuando nadie te hace caso. Ten paciencia y discúlpalos, discúlpanos, por no atender todos los hilos, hay veces que no podemos o no queremos  entrar en todos los temas.  Mr. Green

3º.- El tema de este hilo es un "Ejemplito". Así en diminutivo, escrito para ejemplificar cómo se podría controlar un servo con un PIC, no intentan sentar cátedra de cómo manejar un servo con un PIC. Imagino que habrá tantas formas como amigos se pongan a hacerlo, y la mayoría de ellas serán mas sólidas, eficaces, seguras y controladas que ésta que yo propuse en su día.  Mr. Green

4º.- Este hilo en concreto lo abrí  cuando aún era un bisoño en esto de los PIC's y me propuse con ellos aprender y publicar lo que iba aprendiendo para que otros pudiesen compartir mis experiencias. Estoy seguro de que si hoy lo hiciese de nuevo lo haría de forma muy distinta. En aquel momento simplemente no sabía muy bien lo que estaba haciendo. Aunque funcionaba.  Mr. Green

5º.- Estoy seguro que lo que planteas es cierto y supone una mejora sustancial a lo que yo hice en su día, te lo agradezco y considero que los amigos visitantes a partir de los post que has realizado tendrán un mejor conocimiento de qué hacer y cómo si desean controlar un servo desde un PIC.  Mr. Green

6º.- Ya me ha salido otra de mis novelas. En vez de decirte: "Sí, Gracias" me tiro a explicarte todas las circunstancias ... y tal ... y tal ...  Mr. Green

Venga. Continuamos.
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
Marttyn
Colaborador
PIC24H
*****
Desconectado Desconectado

Sexo: Masculino
Argentina Argentina

Mensajes: 1280


Las cucarachas dicen "No al debugger!"


WWW
« Respuesta #13 : 01 de Abril de 2007, 09:06:19 »

redpic lo que dije sobre que nadie hacia caso a mis post era por decir algo... realmente no me importaba. de hecho puse el post aun sabiendo que probablemente nadie me fuera a responder... al fin y al cabo era un hilo que se podia considerar "cerrado"
si alguien alguna vez quiere trabajar con servos, es de parada obligada este ejemplito tuyo. y el objetivo de mis posts es que alguno de esos curiosos que aprende de tu sabiduria pues tambien pueda aprender algo de mis pequeñas experiencias, que no son ni muy extensas ni muy elaboradas, pero que pueden aportar algo...  Smile
un saludo
En línea

La gente ve las cosas que existen y se pregunta por qué.
Yo prefiero imaginar lo que no existe y preguntarme por qué no.
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #14 : 02 de Abril de 2007, 02:34:03 »

Claro que sí Marttyn.   Razz

Y de nuevo te agradezco tus aportes. Y si te parece bien podrías montar un post con el ejemplo completo que incluya tus correcciones y avances. Así nuestros amigos y visitantes tendrían disponible mas "jugo de cerebro" disponible.

Esto es lo que me gusta del foro, que es una cosa compartida, que entre todos hacemos que crezca día a día.  Smile

Un saludo.


En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
aitopes
Moderadores
DsPIC33
*****
Desconectado Desconectado

Sexo: Masculino
Mensajes: 5068


Ariel Palazzesi www.ucontrol.com.ar


WWW
« Respuesta #15 : 01 de Septiembre de 2007, 04:14:04 »

Diego, no sabes con que ganas he hagarrado estos "viejos" hilos tuyos, ahora que por fin me decidi por aprender CCS.

¡Gracias por escribirlos!
En línea

RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #16 : 01 de Septiembre de 2007, 07:44:01 »

Asi me gusta  Mr. Green

Para eso fueron creados, ¡que te aproveche!  ja ja ja lol  lol lol
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
firepic
Colaborador
PIC24H
*****
Desconectado Desconectado

Sexo: Masculino
Venezuela Venezuela

Mensajes: 1074


Javier Chang


« Respuesta #17 : 15 de Febrero de 2008, 10:16:22 »

Saludos a todos! comencé a trabajar con servos hace un tiempo, y también a programar pics con C (antes usaba ensamblador)...  y sus comentarios me han servido de mucho... en especial RedPic, eres un maestro eh!! Muchas gracias por tus aportes... aunque yo no estoy controlando los pics desde la pc, sino desde el CA/D... pero la teoría sobre el PWM es igual de aplicable...
De nuevo gracias! cuando termine algo bueno trataré de publicarlo...
Que tengan paz!! sigan divirtiéndose programando pics!!  Mr. Green
En línea

"Por la presunción solo se ocasiona una lucha, pero con los que consultan juntos hay sabiduría" (Proverbios 13:10).
RedPic
Administrador
DsPIC33
*******
Desconectado Desconectado

Sexo: Masculino
Tibet Tibet

Mensajes: 4876



WWW
« Respuesta #18 : 16 de Febrero de 2008, 04:59:47 »

Muchas gracias firepic. Yo también hice un experimento de CAD y generar el PMW en El Termo-Servo o un servo controlado por temperatura.Mr. Green
En línea

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania
firepic
Colaborador
PIC24H
*****
Desconectado Desconectado

Sexo: Masculino
Venezuela Venezuela

Mensajes: 1074


Javier Chang


« Respuesta #19 : 22 de Febrero de 2008, 05:22:50 »

Epale!
He estado trabajando con servos y me han ayudado mucho los trabajos de Redpic... y no tengo nada en contra de hacerlo así, pero me surgió una idea... y me gustaría que vieran qué les parece...
Con la lógica de Redpic, usamos las interrupciones de RTCC para contar los 20ms que es el período de trabajo del servo...
Y para fijar el ancho del pulso, el el valor del timer y lo compara, así:

Citar
#
        valTIMER0 = get_TIMER0();
#
        if(valTIMER0>tSERVO1){
#
           flagSERVO1=0;
#
           output_low(PIN_SERVO1);
#
        }


Me puse a hacer varios experimentos... y según lo que han comentado otros, no es a mí no mas que me pasa... resulta que a veces hay un pequeño movimiento indeseado en el servo, pequeño, pero lo hay... me puse a pensar... será que el tiempo en alto es crítico y al evaluar el valor del timer con el "if" la cosa no resulta tan exacta?
Y me vino una idea... ¿Qué tal si uso la interrupción por RTCC para contar ese tiempo?
Aquí les dejo un grafiquito como los de Redpic para mostrar lo que estoy diciendo:



De esta forma, la lógica de programación cambia un poquito:

1- Inicialmente pongo el pin que está conectado al cable "control" del servo (lo llamaré salida de ahora en adelante, por flojera) en 1, y el timer se carga con el valor correspondiente al ancho del pulso (aquí hay que tomar en cuenta el oscilador y bla, bla, bla... si... ya ustedes saben el resto del cuento)...

2- Cuando ocurre la primera interrupción, quiere decir que pasó el tiempo que yo quiero que esté el pulso en alto. Entonces pongo la salida a 0 y pongo a 1 una bandera que indica que ya terminó de contarse el "tiempo en alto".

3- Hay que hacer un cálculo sencillísimo: dividir el tiempo en ticks que el timer debería contar para hacer 20ms (con un cristal de 20M y la pre-escala de 1:64 esto es 1563) entre la precarga que le estamos colocando al timer. ¿Para qué? Pues claro, para saber la cantidad de interrupciones que deben ocurrir hasta el siguiente ciclo ¿sencillo verdad? Sí, si, ya sé... no siempre van a ser exactamente los 20ms lindos y bellos... pero ¿qué se va a hacer? Nada es perfecto jajaja... bueno esa no es la respuesta técnica... lo cierto es que según estuve investigando y por los experimentos que hice...puedo ratificar que el tiempo entre pulso y pulso no es tan crítico en los servos... entre 15ms y 30ms funciona para la mayoría... con esta lógica siempre estará entre 19ms y 21ms... ¿es un error pasable no creen?

4- Finalmente, mientras la bandera flag_pulso está en cero (la salida todavía está en 1) no hago nada... esto asegura que tenga un pulso seguro y firme siempre... cuando la bandera cambia a 1 entonces hago los cálculos por si deseo cambiar el ancho de pulso, en este ejemplo lo que tengo que hacer es usar el CAD para leer el valor de un potenciómetro y luego adecuar ese valor al que debe cargarse al timer para que cuente el ancho de pulso adecuado.

5- No hay más que decir... como dice el conejo Box: esto es todo amigos!!

Bien aquí les dejo el código... a mí me funciona perfectamente... pero POR FAVOR, todos, y especialmente ustedes, amigos sabios del ccs en este foro, me dicen sus opiniones!!  Smile
La configuración del circuito, como se darán cuenta en el código es:
PIC16F876 (barato y fácil de conseguir jejeje)
Cristal 20MHz
Potenciómetro conectado a Canal 0 (RA0) [puede ser otro dispositivo apropiado, como algún sensor; yo lo hice inicialmente con un potenciómetro sólo de prueba]
Control del Servo conectado a RC4
Servo HS-300.

Paz y bien para todos!
Nos leemos en el foro!  Mr. Green

Código:
//////////////////////////////////////////////////
// Lee el canal 0 del pic y modifica      //
//       PWM para control de servo            //
//////////////////////////////////////////////////

//Definiciones generales previas:
//PIC16F876
//Cristal de 20MHz
//Pin donde está conectado la línea de control del servo
//El programa está hecho para un servo, pero esta lógica
//puede ampliarse de forma sencilla para abarcar más servos.

#include <16f876.h>
#device PIC16F876 ADC=10
#fuses HS,NOWDT,NOPROTECT,NOLVP,PUT,NOBROWNOUT
#use delay(CLOCK=20000000)
#define servo_1 PIN_C4

int canal_adc=0; //para elegir qué servo mover (elige el potenciómetro a leer)
int ancho_pulso=0;     //el valor que se va a cargar en el timer0 y que representa el ancho del pulso
int n=0;      //la cantidad de veces que se
int flags=0;
int i=0;
int16 j=0;
int16 valor=0;

#bit flag_pulso = flags.0


const int tmr_pulso_minimo = 39;
const int tmr_pulso_medio  = 117;
const int tmr_pulso_maximo = 195;
const int16 periodo = 1563;

void conf_conversion (int canal) //Función que configura el CAD del pic y fija canal a leer.
{
setup_adc(ADC_CLOCK_INTERNAL);
setup_adc_ports(RA0_RA1_RA3_ANALOG);
set_adc_channel(canal);
delay_us(20);
}

int16 conversion()          //Esta función es la conversión en sí misma, devuelve
{ //el valor de 10 bits correspondiente a la lectura del canal analógico.
int16 value=0;
value=read_adc();
return(value);
}

void ajusta_pwm () //Esta función fija los parámetros para el siguiente pulso de la PWM.
{
float escala=0;
valor=conversion(); //Lee el valor analógico del dispositivo conectado al pic (ej: potenciómetro).
escala=1024/(tmr_pulso_maximo-tmr_pulso_minimo);//Escala para convertir a un valor que se cargará a timer0
ancho_pulso=(int)(valor/escala);        //y es proporcional al ancho de pulso deseado.
ancho_pulso=256-tmr_pulso_minimo-ancho_pulso;     //El valor se carga en el timer.
n=(int)(periodo/ancho_pulso); //Calcula la cantidad de interrupciones necesarias
      //para completar un período de PWM (aprox. 20ms)
}

#INT_RTCC //Función de interrupción por sobreflujo en timer0.
RTCC_isr ()
{
if(i==1)         //La ocurrencia de la primera interrupción indica al pic
{   //que ha transcurrido el tiempo del ancho de pulso deseado,
output_low(servo_1); //de manera que la señal debe fijarse a cero (fin del pulso).
flag_pulso=1;     //y fijo a 1 una bandera que indica que el pulso ya terminó (flag_pulso).
}
if(i==n)         //Si ha interrumpido la cantidad de veces necesarias
{    //para completar un período:
i=0;   // -Aclaro el contador.
output_high(SERVO_1);  // -La señal a 1 (comienza próxima ciclo, comienza nuevo pulso).
flag_pulso=0;      // -Aclaro bandera (flag_pulso) que indica que el pulso no ha terminado.
}
i++; //Cuenta cada vez que ocurre interrupción por sobreflujo en timer 0.
set_rtcc(ancho_pulso); //Y vuelvo a cargar el valor deseado al timer, que es proporcional al ancho de pulso.
}

main()
{
int canal_an=0; //Indica qué canal del CAD voy a leer.
setup_counters(RTCC_INTERNAL,RTCC_DIV_64);//Configura el timer0 (RTCC), aquí uso pre-escala de 1:64
enable_interrupts(INT_RTCC); //Habilita interrupción por sobreflujo en timer0
enable_interrupts(GLOBAL);   //Habilita interrupciones globales
conf_conversion(canal_an);   //Llama a la función para configurar el CAD
ajusta_pwm(); //Llama a función para preparar primer ciclo de PWM
i=n;       //inicializa i en n, de manera que
RTCC_isr(); //cuando entre en esta función coloque la señal a 1 (inicia pulso).
while(TRUE) //Un ciclo infinito.
{
if(flag_pulso==1)ajusta_pwm(); //Si ya pasó el pulso, ajusta PWM (de este modo el pic no hará nada
} //durante el tiempo que dure el pulso, el cual es crítico
} //en lo que respecta a controlar el servo.
« Última modificación: 22 de Febrero de 2008, 05:32:16 por firepic » En línea

"Por la presunción solo se ocasiona una lucha, pero con los que consultan juntos hay sabiduría" (Proverbios 13:10).
TODOPIC
   

 En línea
Páginas: [1] 2 Imprimir 
« anterior próximo »
Ir a:  

Impulsado por MySQL Impulsado por PHP Powered by SMF 1.1.11 | SMF © 2006-2008, Simple Machines LLC XHTML 1.0 válido! CSS válido!
Página creada en 0.23 segundos con 22 consultas.