Autor Tema: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)  (Leído 89644 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5552
    • Picmania by Redraven
Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
« Respuesta #45 en: 14 de Marzo de 2008, 19:42:44 »
Extraordinaria, Diego.

Gracias a tí, Manolo, que si no aún estaría poniendo vías y vías y vías. Ja, ja, ja  :D :D :D
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
« Respuesta #46 en: 15 de Marzo de 2008, 17:13:47 »
....
Código: [Seleccionar]

////////////////////////////////////////////////////////////////////////////////////
//
// Moviendo 8 servos con un solo timer
//
//     
// Propósito: Mover hasta 8 servos eficientemente
//
// Conectar 8 servos al PORT_B
//
////////////////////////////////////////////////////////////////////////////////////


#include <16f876.h>
#fuses HS,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOLVP,NOCPD,NODEBUG,NOWRT
#use delay(clock=20000000)      //Cambiar segun el cristal utilizado

#define VAL_TIMER1 53000        // 65536-53000=12536 * 0,0002 = 2,5 milisegundos

float const uSxTick = 0.2;           // Microsegundos por Tick de TMR1 a 20 Mhz
int8 servo=0;                            // Numero de servo
int1 flag_timer1=FALSE;   


#int_timer1
void handle_timer1_int()               // Se activa cada 2,5 ms
{
    flag_timer1=TRUE;
    set_timer1(VAL_TIMER1);          // 65536-53000=12536*0,0002 = 2,5 ms
}



#int_ccp2                                   // Interrupción timer1 modo compare
void handle_ccp2_int()
{
    bit_clear(*6,servo);              // Pone en bajo el servo. Igual que output_low(PIN_Bx) pero le puedo pasar un parametro.  *6=PORTB
}




////////////////////////////////////////////////////////////////////////////////////
//
// PROGRAMA PRINCIPAL
//
////////////////////////////////////////////////////////////////////////////////////

void main()
{
    ////////////////////////////////////////// INICIALIZACIONES GENERALES
    delay_ms(333);                           // Espero a que todo se estabilice e ...
    disable_interrupts(global);              // Inicializo el Micro y ...
    disable_interrupts(int_timer1);          // deshabilitando todo lo no necesario ...
    disable_interrupts(int_rda);
    disable_interrupts(int_ext);
    setup_adc_ports(NO_ANALOGS);
    setup_adc(ADC_OFF);
    setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
    setup_timer_2(T2_DISABLED,0,1);

    setup_ccp2(CCP_COMPARE_INT);            // Configura CCP2 en modo COMPARE
    set_timer1(VAL_TIMER1);                      // 65536-53000=12536*0,0002 = 2,5 ms


    // Habilito las interrupciones necesarias
    enable_interrupts(int_timer1);          //Int timer 1
    enable_interrupts(int_ccp2);             // Habilito la interrpción INT_CCP2
    enable_interrupts(global);              //Habilita interrupciones

    do
    {
        if(flag_timer1==TRUE)              // Cuando se activa la int_timer1 -> cada 2,5 ms   
        {
            servo++;                            //Controla secuencialmente cada servo
            if (servo>=9) servo=1;       
   
            bit_set(*6,servo);              // Pongo en alto el servo.  Igual que output_high(PIN_Bx) pero le puedo pasar un parametro.  *6=PORTB
   
           
            CCP_2 = VAL_TIMER + pos_servo(servo);         // Cargo CCP con el tiempo que tiene que estar en alto el servo
                                                                  // La funcion pos_servo() debe devolver la posicion del servo en microsegundos

            flag_timer1=FALSE;       
        }

    }
    while (TRUE);
}


En primer lugar quería agradecer a juanelete, muy buen código, es sencillo para analizarlo y bien hecho, solo que tengo unas pequeñas dudas para ver si alguién me las podría aclarar, en la línea:

Código: [Seleccionar]
CCP_2 = VAL_TIMER + pos_servo(servo);
1.- VAL_TIMER es igual a get_timer1() cierto??
y
2.- pos_servo(servo) es igual a 2500 si queremos un ciclo útil de 2,5ms cierto??

Gracias de Antemano.

// ***************** EDITADO POSTERIORMENTE ***************** \\

....lo que pasa es que no sé cuánto tiempo se tarda una instrucción en C ó mejor dicho no sé cómo medirla, alguién sabe como hacer? ...

Me respondo, ya sé cómo hacerlo y es con un plugin del ccs para mplab, lo pueden descargar aqui
simplemente es una pasada  :-/ en verdad me dió mucha alegría poder encontrar esto porque ahora si puedo hacer mediciones de tiempo en las instrucciones y así poder trabajar lo más exacto posible en cuánto a ejercicios como éste que requieren precisión en el tiempo.

Me gustaría aclarar que gracias a una intervención de Modulay fué que me enteré de la existencia del mismo.

Espero que les sea de útilidad a otras personas también.

Saludos.
« Última modificación: 15 de Marzo de 2008, 17:26:50 por gu1llermo »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
« Respuesta #47 en: 15 de Marzo de 2008, 20:03:51 »
// Editado ***

Mejor dicho creo que logré el objetivo de mover 16 servos con el pic, éste es el código

Código: [Seleccionar]
#include <16f877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT,PUT
#use delay (clock=20000000)
#USE RS232 (baud=9600,bits=8,parity=N,xmit=PIN_C6,rcv=PIN_C7)

#byte RCSTA=             0x018
#bit  CREN=              RCSTA.4      // Se activa para recepción continua
#bit  OERR=              RCSTA.1      // Error de Overrun

#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(E)

#byte PORTA      = 0x05
#byte PORTB      = 0x06
#byte PORTC      = 0x07
#byte PORTD      = 0x08
#byte PORTE      = 0x09

#bit servo_01     = PORTB.0
#bit servo_02     = PORTB.1
#bit servo_03     = PORTB.2
#bit servo_04     = PORTB.3
#bit servo_05     = PORTB.4
#bit servo_06     = PORTB.5
#bit servo_07     = PORTB.6
#bit servo_08     = PORTB.7

#bit servo_09     = PORTD.0
#bit servo_10     = PORTD.1
#bit servo_11     = PORTD.2
#bit servo_12     = PORTD.3
#bit servo_13     = PORTD.4
#bit servo_14     = PORTD.5
#bit servo_15     = PORTD.6
#bit servo_16     = PORTD.7

#define n 16// número de servos, para otro número de servos hay que hacer mediciones de tiempo

#define max_theCount 50//
#define derecha 37
#define izquierda 18//

int8 servo[n];
int8 i=0;// es el índice exclusivo de servo[] en la recepción serial

// Recepción de datos
#int_rda
void rda_isr(void){//Está listo para recibir 16 bytes

    servo[i]=getc();
    if (servo[i]>derecha)// Nos aseguramos que el dato recibido esté en los limites
     {
        servo[i]=derecha;
     };
     if (servo[i]<izquierda)
     {
         servo[i]=izquierda;
     };
     i++;
     if (i>(n-1)) i=0;
}


/////////////////////////////////////// PRINCIPAL /////////////////////////////////////

void main()
{
   int8   theCount = 0;
   int    k;
   
   set_tris_b(0x00);
   set_tris_c(0b10000000);
   set_tris_d(0x00);
   
   PORTB=0x00;
   PORTD=0x00;
   
   port_b_pullups(FALSE);
   
   for(k=0;k<=(n-1);k++)
      servo[k]=izquierda;//

    setup_adc_ports(NO_ANALOGS);
    setup_adc(ADC_OFF);
    setup_timer_1(T1_DISABLED);
    setup_timer_2(T2_DISABLED,0,1);

    enable_interrupts(int_rda);
    enable_interrupts(global); 

   while (1)
   {
  delay_us(13);
      #asm
      nop
      nop
      #endasm

      servo_01= (servo[0] >= theCount);// Cada 50us llega aqui de forma cíclica
      servo_02= (servo[1] >= theCount);
      servo_03= (servo[2] >= theCount);
      servo_04= (servo[3] >= theCount);
      servo_05= (servo[4] >= theCount);
      servo_06= (servo[5] >= theCount);
      servo_07= (servo[6] >= theCount);
      servo_08= (servo[7] >= theCount);
      servo_09= (servo[8] >= theCount);
      servo_10= (servo[9] >= theCount);
      servo_11= (servo[10] >= theCount);
      servo_12= (servo[11] >= theCount);
      servo_13= (servo[12] >= theCount);
      servo_14= (servo[13] >= theCount);
      servo_15= (servo[14] >= theCount);
      servo_16= (servo[15] >= theCount);
 
      theCount++;
      if (theCount>max_theCount) theCount=0;
   }
}


Adjunto una simulación en proteus.

PD1: Estoy alegre!!! jejeje  :-/ :-/ :-/ :-/ :-/ :-/ me picaba el mosquito de hacerlo y lo hice  :mrgreen: porfavor serán que lo pueden chequear. Gracias!.

PD2: Ésta es una primera aproximación del programa.

PD3: Considero que se pueden mover X cantidad de servos con un mismo pic, solo es cuestión de medir los tiempos necesarios.



Saludos.
« Última modificación: 15 de Marzo de 2008, 21:03:20 por gu1llermo »

Desconectado AKENAFAB

  • Colaborador
  • DsPIC30
  • *****
  • Mensajes: 3227
Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
« Respuesta #48 en: 15 de Marzo de 2008, 21:00:41 »
Hola Guillermo

Felicitaciones!!!   lo trato de probar ahora :-/


Pero tengo una duda, como se envia la informacion?

¿Como envío la trama de datos ?

Servo,posicion???

Posicion, SErvo??

otro dato , en la simulacion modifica las caracteristicas del servo.



Asi tendras un a simulacion mas precisa, pero en fin, esta muy padre

Felicitaciones de nuevo guillermo.!!!


Saludos

 Atentamente
  Akenafab
« Última modificación: 15 de Marzo de 2008, 21:10:50 por AKENAFAB »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
« Respuesta #49 en: 15 de Marzo de 2008, 21:05:13 »
Gracias :mrgreen: :-/ :-/
Bueno la trama que me imaginé fué que el primer byte que le mandes corresponde al ciclo útil del primero servo, el 2do byte enviado al 2do servo y así, eso me lo imaginé en un principio por hacer algo, pero sería bueno implementar lo que tu dices, suena más lógico  :lol: :mrgreen:

Que buen dato!!! con razón no entendía el comportamiento y el porque tenía que cambiar los valores que ya había determinado previamente, voy hacer los cambios que me dices a ver.
« Última modificación: 15 de Marzo de 2008, 21:14:22 por gu1llermo »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
2da Aproximación
« Respuesta #50 en: 15 de Marzo de 2008, 22:01:43 »
Antes que nada, quisiera preguntarle a a RedPic si está bien que siga posteando mis avances aquí ó abro otro hilo.

Mientras espero la confirmación aqui tengo una 2da aproximación del código:

Código: [Seleccionar]
#include <16f877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT,PUT
#use delay (clock=20000000)
#USE RS232 (baud=9600,bits=8,parity=N,xmit=PIN_C6,rcv=PIN_C7)

#byte RCSTA=             0x018
#bit  CREN=              RCSTA.4      // Se activa para recepción continua
#bit  OERR=              RCSTA.1      // Error de Overrun

#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(E)

#byte PORTA      = 0x05
#byte PORTB      = 0x06
#byte PORTC      = 0x07
#byte PORTD      = 0x08
#byte PORTE      = 0x09

#bit servo_01     = PORTB.0
#bit servo_02     = PORTB.1
#bit servo_03     = PORTB.2
#bit servo_04     = PORTB.3
#bit servo_05     = PORTB.4
#bit servo_06     = PORTB.5
#bit servo_07     = PORTB.6
#bit servo_08     = PORTB.7

#bit servo_09     = PORTD.0
#bit servo_10     = PORTD.1
#bit servo_11     = PORTD.2
#bit servo_12     = PORTD.3
#bit servo_13     = PORTD.4
#bit servo_14     = PORTD.5
#bit servo_15     = PORTD.6
#bit servo_16     = PORTD.7

#define n 16// número de servos, para otro número de servos hay que hacer mediciones de tiempo

#define max_theCount 50//
#define derecha 45
#define izquierda 8//

int8 duty[n];
int8 i=0;// es el índice exclusivo de servo[] en la recepción serial
int1 band=0;


// Recepción de datos
#int_rda
void rda_isr(void){//1er byte recibido indica el servo y el 2do byte el ciclo

    if (band==0){
       i=getc();
       band=1;
     }
    else{
       band=0;
       duty[i]=getc();   
    }
}


/////////////////////////////////////// PRINCIPAL /////////////////////////////////////

void main()
{
   int8   theCount=0;
   int    k;
   
   set_tris_b(0x00);
   set_tris_c(0b10000000);
   set_tris_d(0x00);
   
   PORTB=0x00;
   PORTD=0x00;
   
   port_b_pullups(FALSE);
   
   for(k=0;k<=(n-1);k++)
      duty[k]=izquierda;//

    setup_adc_ports(NO_ANALOGS);
    setup_adc(ADC_OFF);
    setup_timer_1(T1_DISABLED);
    setup_timer_2(T2_DISABLED,0,1);

    enable_interrupts(int_rda);
    enable_interrupts(global); 

   while (1)
   {
  delay_us(13);
      #asm
      nop
      nop
      #endasm

      servo_01= (duty[0] >= theCount);// Cada 50us llega aqui de forma cíclica
      servo_02= (duty[1] >= theCount);
      servo_03= (duty[2] >= theCount);
      servo_04= (duty[3] >= theCount);
      servo_05= (duty[4] >= theCount);
      servo_06= (duty[5] >= theCount);
      servo_07= (duty[6] >= theCount);
      servo_08= (duty[7] >= theCount);
      servo_09= (duty[8] >= theCount);
      servo_10= (duty[9] >= theCount);
      servo_11= (duty[10] >= theCount);
      servo_12= (duty[11] >= theCount);
      servo_13= (duty[12] >= theCount);
      servo_14= (duty[13] >= theCount);
      servo_15= (duty[14] >= theCount);
      servo_16= (duty[15] >= theCount);
 
      theCount++;
      if (theCount>max_theCount) theCount=0;
   }
}


La trama es así: 1er Byte enviado indicas el servo [0..15] y el 2do Byte enviado el ciclo útil que va desde 8 (Extremo izquierdo) y 45 (Extremo Derecho), en resumen: (Servo, Posición)

El programa está respondiendo bien, pero después de enviar varios datos noto que otros servos se mueven también, eso es por el tiempo que se tarda en recibir los datos vía serial hace un desplazamiento de tiempo  :? esto lo empecé a notar después que ajusté la sensibilidad del servo a 0.5m y 2.5m, en estos momentos no sé como podría solucionar eso, en una primera opción sería usar un servo menos sensible  :lol: pero sé que esa no es la solución correcta.

PD1: No sé exactamente que es y cómo solucionarlo, pero sucede cuando le cambio el ciclo a varios de ellos, voy a chequear.

Adjunto archivos.

Saludos.

// Editado posteriormente \\

Ya creo haber identificado el problema, estoy trabajando para corregirlo.  :wink: , es un problema de tiempo en la recepción de datos que descuadra todo, no sabía que esto de los servos había que ser taaan precisos, ahora entiendo a RedPic con los 20ms exactos  :shock:  :lol:

RECOMENDACIÓN: No montar esto en físico hasta que esté bien depurado y probado su correcto funcionamiento en proteus.

Saludos.
« Última modificación: 15 de Marzo de 2008, 23:56:03 por gu1llermo »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
« Respuesta #51 en: 15 de Marzo de 2008, 23:31:22 »
Según el análisis que e realizado lo mínimo que podría hacer girar estos servo es a 4,5º cada uno, pero eso es muy complicado y requiere más tiempo, así que voy a hacerlo girar a 9º que es un poco más sencillo (relativamente).
« Última modificación: 16 de Marzo de 2008, 01:15:26 por gu1llermo »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Ahora si!
« Respuesta #52 en: 16 de Marzo de 2008, 00:47:33 »
Bueno ahora si está funcionando como debe ser, a menos de que alguién le consiga un detalle, aquí está el código resultante:

Código: [Seleccionar]
#include <16f877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT,PUT
#use delay (clock=20000000)
#USE RS232 (baud=9600,bits=8,parity=N,xmit=PIN_C6,rcv=PIN_C7)

#byte RCSTA=             0x018
#bit  CREN=              RCSTA.4      // Se activa para recepción continua
#bit  OERR=              RCSTA.1      // Error de Overrun

#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(E)

#byte PORTA      = 0x05
#byte PORTB      = 0x06
#byte PORTC      = 0x07
#byte PORTD      = 0x08
#byte PORTE      = 0x09

#bit servo_00     = PORTB.0
#bit servo_01     = PORTB.1
#bit servo_02     = PORTB.2
#bit servo_03     = PORTB.3
#bit servo_04     = PORTB.4
#bit servo_05     = PORTB.5
#bit servo_06     = PORTB.6
#bit servo_07     = PORTB.7

#bit servo_08     = PORTD.0
#bit servo_09     = PORTD.1
#bit servo_10     = PORTD.2
#bit servo_11     = PORTD.3
#bit servo_12     = PORTD.4
#bit servo_13     = PORTD.5
#bit servo_14     = PORTD.6
#bit servo_15     = PORTD.7

#define n 16// número de servos, para otro número de servos hay que hacer mediciones de tiempo

#define max_theCount 200// 200*100us=20ms
#define derecha  25// 25*100us=2,5ms
#define centro   15// 15*100us=1,5ms
#define izquierda 5// 5*100us =0,5ms

/////////////////////////////////////// PRINCIPAL /////////////////////////////////////

void main()
{
   int8    theCount=1;
   int     k;
   int8    duty[n];
   int8    i=0;// es el índice exclusivo de duty[] en la recepción serial
   int1    band=0;
   
   set_tris_b(0x00);
   set_tris_c(0b10000000);
   set_tris_d(0x00);
   
   PORTB=0x00;
   PORTD=0x00;
   
   port_b_pullups(FALSE);
   
   for(k=0;k<=(n-1);k++)
      duty[k]=izquierda;//

    setup_adc_ports(NO_ANALOGS);
    setup_adc(ADC_OFF);
    setup_timer_1(T1_DISABLED);
    setup_timer_2(T2_DISABLED,0,1);

    disable_interrupts(global); 

   while (1)
   {
     delay_us(70);
      #asm
      nop
      nop
      nop
      nop
      #endasm
// Cada 100us llega aqui de forma cíclica
     
      if (duty[0] >= theCount)
      {
         servo_00=1;
      }
      else
      {
         servo_00=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[1] >= theCount)
      {
         servo_01=1;
      }
      else
      {
         servo_01=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[2] >= theCount)
      {
         servo_02=1;
      }
      else
      {
         servo_02=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[3] >= theCount)
      {
         servo_03=1;
      }
      else
      {
         servo_03=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[4] >= theCount)
      {
         servo_04=1;
      }
      else
      {
         servo_04=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[5] >= theCount)
      {
         servo_05=1;
      }
      else
      {
         servo_05=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[6] >= theCount)
      {
         servo_06=1;
      }
      else
      {
         servo_06=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[7] >= theCount)
      {
         servo_07=1;
      }
      else
      {
         servo_07=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[8] >= theCount)
      {
         servo_08=1;
      }
      else
      {
         servo_08=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[9] >= theCount)
      {
         servo_09=1;
      }
      else
      {
         servo_09=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[10] >= theCount)
      {
         servo_10=1;
      }
      else
      {
         servo_10=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[11] >= theCount)
      {
         servo_11=1;
      }
      else
      {
         servo_11=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[12] >= theCount)
      {
         servo_12=1;
      }
      else
      {
         servo_12=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[13] >= theCount)
      {
         servo_13=1;
      }
      else
      {
         servo_13=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[14] >= theCount)
      {
         servo_14=1;
      }
      else
      {
         servo_14=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[15] >= theCount)
      {
         servo_15=1;
      }
      else
      {
         servo_15=0;
         #asm
            nop
         #endasm
      }
     
      theCount=theCount+1;
      if (theCount>max_theCount)
      {
         theCount=1;
      }
      else
      {
         delay_us(1);
         #asm
            nop
         #endasm
      }

// Recepción de datos
// Las rutinas delays y los nop son para normalizar los tiempos
      if (kbhit()){
         if (band==0){
           i=getc();
           band=1;
           delay_us(1);
           #asm
             nop
             nop
           #endasm
         }
         else{
           duty[i]=getc();
           band=0;   
         }
      }
      else{
         delay_us(3);
         #asm
          nop
         #endasm
      }
   }
}

Adjunto archivos.

La trama de envío es (Servo, Posición) dos bytes, el extremo izquierdo se logra con una posición igual a 5 y el extremo derecho con una posición igual a 25, y para el centro=15.

PD: Alguién me podría decir para que quieren controlar tantos servos? y perdonen mi desconocimiento.

Saludos. :-/ :-/ :-/
« Última modificación: 16 de Marzo de 2008, 01:19:37 por gu1llermo »

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5552
    • Picmania by Redraven
Re: Ahora si!
« Respuesta #53 en: 16 de Marzo de 2008, 04:20:11 »

PD: Alguién me podría decir para que quieren controlar tantos servos? y perdonen mi desconocimiento.


Para esto y/o esto. Y aún faltan mas cosas como varios motores D.C. o PaP. Y una larga lista de sensores, fines de carrera, sensores de posición, actuadores lineales ...   :mrgreen:
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
« Respuesta #54 en: 16 de Marzo de 2008, 09:32:31 »
Ahhh!!! Guao  :shock: tremendo proyecto no!? bueno si se quieren controlar más servos me avisas porque yo creo que se pueden adaptar más servos todavía a éste pic, mejor dicho no creo, estoy seguro que si se puede solo es cuestión de medir tiempos y ya, con respecto a los sensores, fines de carrerars y demás supongo que ya los tienes solventado ó algo ya tienes  :lol:, lo que pasa es que a mi me gusta programar y esto lo hago por gusto  :mrgreen: y por reto  :wink:

Volviendo a cuántos servos máximo podría controlar éste pic, considero que hasta 15 más, para dar un total de 31 servos  :wink: a dedicación exclusiva de éste aparatito, perdón, PIC  :D :mrgreen:

Cualquier cosa que necesites, solo plantealo de forma sencilla, jejeje bueno eso está de más decirlo  :lol: y me lo haces llegar, todo lo que tenga que ver con programación que si te puedo hechar una mano con gusto.

Alguién podría probar éste código en real? con sus servos, y me dice que le parece? es que hice el código lo más exacto posible en cuánto a tiempo se refiere y sospecho que debe funcionar muy pero muy bien, pero como no tengo servos ni nada para probar más que el proteus y mplab. También sería interesante medir las señales con un osciloscopio previamente y verificar que el ciclo completo sea de 20ms y los extremos izquierdo y derecho 0,5ms y 2,5ms exactamente.

El el programa de juanalete le agrega un retardo de 333ms antes de empezar todo, esto es necesario hacerlo? que recomendaciones me podrían dar al respecto?

Saludos.

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5552
    • Picmania by Redraven
Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
« Respuesta #55 en: 16 de Marzo de 2008, 10:40:41 »
En cuanto tenga mi nueva PCB lista tu código será probado en vivo y en directo.  :mrgreen:



P.D.: Ya están publicados todos los pines libres, lo que no están directamente asignados a un servo.
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
« Respuesta #56 en: 16 de Marzo de 2008, 16:12:55 »
...
Así que me he tirado a diseñar una PCB para 16 Servos y a ver qué resulta de ello, veremos si soy capaz de abrir un hilo titulado  Algoritmo para controlar hasta 16 servos con una sola interrupción en C (o dos si son pequeñas)
...
Está montado alrededor de un 18F4550 con conexión USB y/o TTL Serie. Para los servos he destinado los puertos B y D completos, y he puesto otros de A y del C para poder recibir info de sensores y cosas así.
...

No sé porqué pasé de alto éste detalle que vas a usar el pic18, solo resaltó el número de servos 16, bueno para éste pic18 ya la cosa es un poco diferente y si además quiere que el mismo pic haga otras funciones lo ideal sería haciendo uso de las interrupciones como lo has planteado, por mi parte creo oportuno que yo abra otro post en donde exponga éste código y unos que otros comentarios y/u observaciones para adaptarlo a X cantidad de servos, según las I/O  que disponga nuestro PIC.

...
Pensando con detenimiento creo que también puedo manejar esto con interrupciones y adaptarlo al pic 18 con comunicación USB y Serial a la vez, pero no sé como lo tienes pensado, los sensores son los encoders a los que haces referencia? cuántos encoders son? cuántos sensores son? cuántos finales de carreras? Para los encoders sería bueno aprovechar la interrupción por RB0, RB1  ó RB2 por flanco descendente, ya sé que dedicastes todo el puerto B para manejar los servos, pero fácilmente se puede usar cualquier otro pin para controlar un servo, mientras que las interrupciones por flancos descendente nop. Piensas eliminar el rebote de los pulsadores por software o por hardware?

No sé si estoy preguntando mucho para el tiempo que nos conocemos, pero bueno son solo algunas ideas y unas que otras interrogantes, además para eso estamos nop!?

PD1: Que bien te ha quedado la placa que has hecho. :wink:

PD2: Aunque te confieso que atender a las señales de un encoder y a la vez manejar estos servo al mismo tiempo con un mismo pic no se vé cosa fácil, será cuestión de ponerse a probar, si el período de la señal del encoder es del orden de los ms sería mejor.

PD3: Creo que la respuesta a todas estas interrogantes deben estar planteadas en el proyecto barco Vela, lo que pasa es que es un poco largo el hilo y para leerse todo  :z)

Saludos.
« Última modificación: 16 de Marzo de 2008, 16:36:10 por gu1llermo »

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5552
    • Picmania by Redraven
Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
« Respuesta #57 en: 16 de Marzo de 2008, 17:18:53 »
Amigo guillermo:

Por ahora el tema electrónico esta aún verde. Estamos preparando módulos generales, no concretos y específicos porque aún no sabemos qué elementos nos van a hacer falta para cada maniobra. Otro amigo está con la parte mecánica y cuando este un poco mas avanzado comenzaremos con las maniobras concretas.

Así sabremos que para esta nos hace falta un servo o dos o tres, o un DC multivuelta con posicionador o un sólo un PaP. Esto para todas y cada una de las maniobras posibles, que son decenas distintas.

Yo ahora lo que estoy haciendo es lo que llamo "afilar el lápiz antes de ponerme a dibujar". Con esta expresión quiero decir que me estoy dedicando a probar, testear, desarrollar cada una de las posibles funciones que voy a necesitar, adquirir el conocimiento del "como" para después, cuando haga falta tal o cual función o servicio o actuación solo tener que implementarla, no investigarla desde el principio.

Por eso he hecho, se puede ver en otros hilos de este mismo foro, pruebas como motores DC y con Servos y con Emisores-Receptores RF y ....

... y seguimos acumulando conocimientos para aplicarlos allá donde hagan falta en el momento en que hagan falta. Hay tantos cientos de cosas por hacer que lo que estamos haciendo es acometerlas de una en una, en una sucesión ininterrumpida, sin prisas pero sin pausas, dando pasos cortos pero seguros.

Que como dijo Mao Tse Tung: La gran Marcha empezó con un primer paso.  :mrgreen:

Proyecto Barco a Vela RC con PIC
Algoritmo para controlar hasta 8 servos
¿Conocéis módulos RF?
Esquema mínimo de driver para un Motor DC 12V con un L298N
Receptor RC asistido por PIC

Todos estos hilos tienen que ver con este tema y apuntan todos en el mismo sentido.  :mrgreen:
« Última modificación: 16 de Marzo de 2008, 17:22:28 por RedPic »
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Algoritmo para controlar 16!!! servos con una sola interrupción (en C)
« Respuesta #58 en: 16 de Marzo de 2008, 18:41:28 »
Ah!! oka, oka, como dicen: "Divide y vencerás".

Bueno RedPic con tu permiso, me gustaría publicar el código para manejar 16 servos con una interrupción del timer0, lo que pasa es que tu hilo está muy completo y abrir otro considero que sería innecesario, además duplicaría las referencias, en total, esto tiene que ver con lo que estás haciendo, además ya tienes esa parte hecha y puedes seguir con el resto  :wink: y lo podrías considerar como tu hijo porque aquí fué donde nació. ¿qué te parece?
(Pensando en voz alta) ... como que me puse filosófico al final  :roll:  :mrgreen:

Código: [Seleccionar]
#include <16f877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT,PUT
#use delay (clock=20000000)
#USE RS232 (baud=9600,bits=8,parity=N,xmit=PIN_C6,rcv=PIN_C7)

#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(E)

#byte PORTA      = 0x05
#byte PORTB      = 0x06
#byte PORTC      = 0x07
#byte PORTD      = 0x08
#byte PORTE      = 0x09

#bit servo_00     = PORTB.0
#bit servo_01     = PORTB.1
#bit servo_02     = PORTB.2
#bit servo_03     = PORTB.3
#bit servo_04     = PORTB.4
#bit servo_05     = PORTB.5
#bit servo_06     = PORTB.6
#bit servo_07     = PORTB.7

#bit servo_08     = PORTD.0
#bit servo_09     = PORTD.1
#bit servo_10     = PORTD.2
#bit servo_11     = PORTD.3
#bit servo_12     = PORTD.4
#bit servo_13     = PORTD.5
#bit servo_14     = PORTD.6
#bit servo_15     = PORTD.7

#define n 16// número de servos
            //para otro número de servos hay que hacer mediciones de tiempo

#define max_theCount 200// 200*100us=20ms
#define derecha  26// Éste valor fué determinado simulando el proceso
// en el proteus pero en teoría debería ser (25) 25*100us=2,5ms
// Sería cuestión de probar con servos reales y ajustar valores ;-)
#define centro   15// 15*100us=1,5ms
#define izquierda 5// 5*100us =0,5ms

#define val_timer0 88

int8    theCount=1;
int8    duty[n];

#INT_TIMER0
void isr_timer0(){// Cada 100us llega aqui de forma cíclica
   #asm
     nop
   #endasm
   if (duty[0] >= theCount)
      {
         servo_00=1;
      }
      else
      {
         servo_00=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[1] >= theCount)
      {
         servo_01=1;
      }
      else
      {
         servo_01=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[2] >= theCount)
      {
         servo_02=1;
      }
      else
      {
         servo_02=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[3] >= theCount)
      {
         servo_03=1;
      }
      else
      {
         servo_03=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[4] >= theCount)
      {
         servo_04=1;
      }
      else
      {
         servo_04=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[5] >= theCount)
      {
         servo_05=1;
      }
      else
      {
         servo_05=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[6] >= theCount)
      {
         servo_06=1;
      }
      else
      {
         servo_06=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[7] >= theCount)
      {
         servo_07=1;
      }
      else
      {
         servo_07=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[8] >= theCount)
      {
         servo_08=1;
      }
      else
      {
         servo_08=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[9] >= theCount)
      {
         servo_09=1;
      }
      else
      {
         servo_09=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[10] >= theCount)
      {
         servo_10=1;
      }
      else
      {
         servo_10=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[11] >= theCount)
      {
         servo_11=1;
      }
      else
      {
         servo_11=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[12] >= theCount)
      {
         servo_12=1;
      }
      else
      {
         servo_12=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[13] >= theCount)
      {
         servo_13=1;
      }
      else
      {
         servo_13=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[14] >= theCount)
      {
         servo_14=1;
      }
      else
      {
         servo_14=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[15] >= theCount)
      {
         servo_15=1;
      }
      else
      {
         servo_15=0;
         #asm
            nop
         #endasm
      }
     
      theCount=theCount+1;
      if (theCount>max_theCount)
      {
         theCount=1;
      }
      else
      {
         delay_us(1);
         #asm
            nop
         #endasm
      }
      set_timer0(val_timer0);
}

/////////////////////////////////////// PRINCIPAL /////////////////////////////////////

void main()
{
   int     k;
   int8    i=0;// es el índice exclusivo de duty[] en la recepción serial
   int1    band=0;
   
   set_tris_b(0x00);
   set_tris_c(0b10000000);
   set_tris_d(0x00);
   
   PORTB=0x00;
   PORTD=0x00;
   
   port_b_pullups(FALSE);
   
   for(k=0;k<=(n-1);k++)
      duty[k]=izquierda;//

    setup_adc_ports(NO_ANALOGS);
    setup_adc(ADC_OFF);
    setup_timer_1(T1_DISABLED);
    setup_timer_2(T2_DISABLED,0,1);
    setup_timer_0(RTCC_DIV_2);

    set_timer0(val_timer0);
   
    enable_interrupts(INT_TIMER0);
    enable_interrupts(GLOBAL); 

   while (1)
   {
// Recepción de datos
      if (kbhit()){
         if (band==0){
           i=getc();
           band=1;
         }
         else{
           duty[i]=getc();
           band=0;   
         }
      }
   }
}

Para pasarlo al pic18 habría que medir los tiempos nuevamente y hacer los ajustes, mientras aquí está éste adelanto para el pic16f877  :-/ por algo se empieza nop?

PD: Fíjense que le quité la palabra "hasta" porque podemos controlar más servos todavía  :mrgreen:

Saludos.
« Última modificación: 19 de Marzo de 2008, 16:54:11 por gu1llermo »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Algoritmo para controlar 16 servos (en C) pic18f4550
« Respuesta #59 en: 17 de Marzo de 2008, 01:39:00 »
Bueno aquí está el código para controlar 16 servos con el pic18f4550, le podemos enviar comandos tanto vía serial RS232 como vía USB, la trama es (SERVO,POSICIÓN), SERVO=[0..15] y POSICIÓN=[5..25], donde 5=extremo izquierdo y 25=extremo derecho.

Código: [Seleccionar]
#include <18F4550.h>
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN,NOPBADEN,NOBROWNOUT
#use delay(clock=48000000)//48MHz

#USE RS232 (baud=9600,bits=8,parity=N,xmit=PIN_C6,rcv=PIN_C7)

//#build(reset=0x000800,interrupt=0x000808:0x000818)
// Necesario para indicarle al compilador a partir de donde alojar mi programa
// Si se quiere usar bootloader se pueden habilitar estás dos líneas
//#ORG 0x000, 0x07FF {} // Espacio reservado para alojar el bootloader

#byte PORTA = 0xF80// Son las direcciones de memoria de los puertos en este PIC
#byte PORTB = 0xF81
#byte PORTC = 0xF82
#byte PORTD = 0xF83
#byte PORTE = 0xF84

/////////////////////////////////////////////////////////////////////////////
//
// CCS Library dynamic defines.  For dynamic configuration of the CCS Library
// for your application several defines need to be made.  See the comments
// at usb.h for more information
//
/////////////////////////////////////////////////////////////////////////////
#define USB_HID_DEVICE     FALSE             //deshabilitamos el uso de las directivas HID
#define USB_EP1_TX_ENABLE  USB_ENABLE_BULK   //turn on EP1(EndPoint1) for IN bulk/interrupt transfers
#define USB_EP1_RX_ENABLE  USB_ENABLE_BULK   //turn on EP1(EndPoint1) for OUT bulk/interrupt transfers
#define USB_EP1_TX_SIZE    2                 //size to allocate for the tx endpoint 1 buffer
#define USB_EP1_RX_SIZE    2                 //size to allocate for the rx endpoint 1 buffer

/////////////////////////////////////////////////////////////////////////////
//
// If you are using a USB connection sense pin, define it here.  If you are
// not using connection sense, comment out this line.  Without connection
// sense you will not know if the device gets disconnected.
//       (connection sense should look like this:
//                             100k
//            VBUS-----+----/\/\/\/\/\----- (I/O PIN ON PIC)
//                     |
//                     +----/\/\/\/\/\-----GND
//                             100k
//        (where VBUS is pin1 of the USB connector)
//
/////////////////////////////////////////////////////////////////////////////
//#define USB_CON_SENSE_PIN PIN_B2  //CCS 18F4550 development kit has optional conection sense pin

/////////////////////////////////////////////////////////////////////////////
//
// Include the CCS USB Libraries.  See the comments at the top of these
// files for more information
//
/////////////////////////////////////////////////////////////////////////////
#include <pic18_usb.h>     //Microchip PIC18Fxx5x Hardware layer for CCS's PIC USB driver
#include <PicUSB.h>        //Configuración del USB y los descriptores para este dispositivo
#include <usb.c>           //handles usb setup tokens and get descriptor reports

#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)
#use fast_io(E)

#bit servo_00     = PORTB.0
#bit servo_01     = PORTB.1
#bit servo_02     = PORTB.2
#bit servo_03     = PORTB.3
#bit servo_04     = PORTB.4
#bit servo_05     = PORTB.5
#bit servo_06     = PORTB.6
#bit servo_07     = PORTB.7

#bit servo_08     = PORTD.0
#bit servo_09     = PORTD.1
#bit servo_10     = PORTD.2
#bit servo_11     = PORTD.3
#bit servo_12     = PORTD.4
#bit servo_13     = PORTD.5
#bit servo_14     = PORTD.6
#bit servo_15     = PORTD.7

#define i_usb     recibe[0]// es el índice que se recibe vía usb
#define pos_usb   recibe[1]// es la posición que se recibe vía usb

#define n 16// número de servos
            //para otro número de servos hay que hacer mediciones de tiempo

#define max_theCount 200// 200*100us=20ms
#define derecha  25// 25*100us=2,5ms
#define centro   15// 15*100us=1,5ms
#define izquierda 5// 5*100us =0,5ms

#define val_timer0 127

int8    theCount=1;
int8    duty[n];

#INT_TIMER0
void isr_timer0(){
   delay_cycles(5);
   if (duty[0] >= theCount)// Cada 100us llega aqui de forma cíclica
      {
         servo_00=1;
      }
      else
      {
         servo_00=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[1] >= theCount)
      {
         servo_01=1;
      }
      else
      {
         servo_01=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[2] >= theCount)
      {
         servo_02=1;
      }
      else
      {
         servo_02=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[3] >= theCount)
      {
         servo_03=1;
      }
      else
      {
         servo_03=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[4] >= theCount)
      {
         servo_04=1;
      }
      else
      {
         servo_04=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[5] >= theCount)
      {
         servo_05=1;
      }
      else
      {
         servo_05=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[6] >= theCount)
      {
         servo_06=1;
      }
      else
      {
         servo_06=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[7] >= theCount)
      {
         servo_07=1;
      }
      else
      {
         servo_07=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[8] >= theCount)
      {
         servo_08=1;
      }
      else
      {
         servo_08=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[9] >= theCount)
      {
         servo_09=1;
      }
      else
      {
         servo_09=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[10] >= theCount)
      {
         servo_10=1;
      }
      else
      {
         servo_10=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[11] >= theCount)
      {
         servo_11=1;
      }
      else
      {
         servo_11=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[12] >= theCount)
      {
         servo_12=1;
      }
      else
      {
         servo_12=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[13] >= theCount)
      {
         servo_13=1;
      }
      else
      {
         servo_13=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[14] >= theCount)
      {
         servo_14=1;
      }
      else
      {
         servo_14=0;
         #asm
            nop
         #endasm
      }
     
      if (duty[15] >= theCount)
      {
         servo_15=1;
      }
      else
      {
         servo_15=0;
         #asm
            nop
         #endasm
      }
     
      theCount=theCount+1;
      if (theCount>max_theCount)
      {
         theCount=1;
      }
      else
      {
         delay_us(1);
         #asm
            nop
         #endasm
      }
      set_timer0(val_timer0);
}

/////////////////////////////////////// PRINCIPAL /////////////////////////////////////

void main()
{
    int     k;
    int8    i=0;// es el índice exclusivo de duty[] en la recepción serial
    int1    band=0;
    int8    recibe[2];// Buffer de recepción vía usb                 
  //int8    envia[2];// Habilitar en caso de necesitar
   
    set_tris_b(0x00);
    set_tris_c(0b10000000);
    set_tris_d(0x00);
   
    PORTB=0x00;
    PORTD=0x00;
   
    port_b_pullups(FALSE);
   
    for(k=0;k<=(n-1);k++)
      duty[k]=centro;//

    setup_adc_ports(NO_ANALOGS);
    setup_adc(ADC_OFF);

    setup_timer_0(RTCC_DIV_8 | RTCC_8_BIT);

    set_timer0(val_timer0);
   
    enable_interrupts(INT_TIMER0);
    enable_interrupts(GLOBAL);
   
    usb_init();//inicializamos el USB

    usb_task();//habilita periférico usb e interrupciones
    usb_wait_for_enumeration();//esperamos hasta que el PicUSB sea configurado por el host

   while (TRUE)
   {
      if(usb_enumerated())//si el PicUSB está configurado
         //if (TRUE)
      {
// Recepción de datos via rs232
         if (kbhit()){
            if (band==0){
               i=getc();
               band=1;
            }
            else{
               duty[i]=getc();
               band=0;   
            }
         }
// Recepción vía USB         
         if (usb_kbhit(1))//El endpoint de salida contiene datos del host (EP1)?
         {
            usb_get_packet(1, recibe, 2);// Recibimos los 2 bytes: (servo,pos)
            duty[i_usb]=pos_usb;
         }
      }
   }
}

Adjunto archivos de simulación en Proteus, Driver y Aplicación.

Adicionalmente realicé una sencilla interfaz (Gracias a J1M) para controlar los servos vía usb, aquí una imagen funcionando:



Hola Guillermo

Felicitaciones!!!   lo trato de probar ahora :-/
... esta muy padre
...

Gracias nuevamente  :lol: en verdad es muy agradable cuando reconocen nuestro trabajo y no cualquier trabajo sino el que se hace con esfuerzo, empeño y por supuesto con cariño también, así como muchos foristas que todavía no me sé todos los nombres porque soy relativamente nuevo en el mismo pero si me pidieran nombres estos son los que me suenan (no hay ningún orden en específico): Palitroquez, Nocturno, RedPic, Maunix, J1M, JLIGUAL, MGLSOFT, PICmouse, Modulay, entre otros destacados y a todos los foristas en general porque cada día hacemos que éste foro sea el mejor foro en internet en cuanto a pic y un poco más ;-), saludos a todos, pero absolutamente a todos y gracias!, muchas gracias!!!

Que la pasen bien.
« Última modificación: 17 de Marzo de 2008, 11:38:38 por gu1llermo »


 

anything