Autor Tema: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)  (Leído 38407 veces)

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

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Bueno decidí abrir éste post aparte para ahondar en detalles sobre como podríamos lograr éste objetivo de controlar X cantidad de servos ó señales PWM con un mismo PIC mediante una sola interrupción del TIMER0, la idea original es de RedPic allí pueden conseguir un poco de teoría y ver como nació éste algoritmo, que ahora es de todos nosostros.

Para entender todo lo que a continuación se explicará es necesario de varias herramientas:
1.- Mplab + Plug-in del Ccs
2.- CCS Compiler
3.- Proteus (7.2 SP2)
4.- Y ganas de aprender  :wink:

Después de tener todo instalado, abrimos el MPLAB y creamos un nuevo proyecto, elegimos nombre y ubicación, y le damos OK (yo elegí por ejemplo: pic_pwm):


[PIC16F877]

Luego vamos a File --> New y en la ventana que se nos abre copiamos éste 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)

#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;   
         }
      }
   }
}

Lo guardaremos en la misma carpeta con el nombre pic_pwm.c (pueden elegir el que quiera claro)
Luego en la ventana: pic_pwm.mcp, hacemos click derecho eliginedo "Add Files..." buscamos elegimos el archivo creado pic_pwm.c



Nos vamos a: Configure --> Select Device... y elegimos el PIC16F877

Luego en Debugger --> Select Tool, seleccionamos MPLAB SIM

En Debugger --> Settings..., colocamos 20Mhz a Processor Frecuency

En Project --> Select Language Toolsuite... elegimos CCS C Compiler for PIC... (aquí mismo en la sección de Location: "C:\Archivos de programa\PICC\Ccsc.exe" vá la ruta del compilador que en mi caso es ésta) y le damos OK

En éste punto podemos salvar nuestro proyecto.

Ahora bien compilamos y abrimos el StopWatch que está en Debugger, acomodando las dos ventanas para visualizarlas al mismo tiempo y a su vez aprovechamos de colocar un BreakPoint en éste punto del programa (Ver imagen):



Le damos a run y esta debe ser la imagen resultante:



Podemos observar que tardó 129us en llegar aquí la primera vez, es decir con una sola vez que le hemos dado a RUN, ahora si limpiamos con el botón "Zero" del StopWatch y le volvemos a dar veremos la siguiente imagen:



Podemos observar que en la segunda corrida llega en 99,8us, volvemos a colocar a cero el contador del StopWatch con el botón "Zero" y corremos nuevamente:



Podemos observar que llegó a los 100us exactos y de ahora en adelante siempre que le demos al botón RUN obtendremos 100us exactos en ese punto, y eso lo podemos extrapolar para cualquier otra línea que esté en ese código que maneja la interrupción del TIMER0.

Una de las tantas preguntas que se podrían estar haciendo en éste momento ¿Y para qué me sirve esto?

Bueno el truco está en mantener esos 100us si agregamos o quitamos servos (ó generar señales mediante PWM)

¿Cómo?

Vamos a fijarnos en ésta parte del programa:



El índice de duty[], nos indica el valor del ciclo útil que queremos obtener en un pin, en este caso el pin RB0 (servo_00=PORTB.0)

En éste ciclo utilizamos una variable llamada "theCount" cuyo valor máximo es 200 y es inicializada en 1, como siempre entramos cada 100us eso implica que si aumentamos de uno en uno ésta variable, al cabo de 200 pasadas equivaldrían en tiempo a 20ms (200*100us=20.000us=20ms) ahora bien si queremos un ciclo útil de 0,5ms en el pin RB0, cargamos duty[0] con 5 y la comparación
Código: [Seleccionar]
if (duty[0] >= theCount) será cierta 5 veces 100us lo que es igual a 500us=0,5ms por lo tanto el pin RB0  estará en alto ese tiempo y el resto en bajo, facilmente podemos deducir que para 2,5ms habría que cargar duty[0] con 25 ;-)

Ok, todo bien hasta ahora.

La otra pregunta que se estarían haciendo (entre otras) sería: ¿Para que esos nop y esos delays?

La respuesta es la siguiente: si analizamos el código equivalente a la instrucción if en ASM nos daremos cuenta que si la condición es cierta tarda 1 ciclo más de instrucción que en el caso de que sea falsa, y esos desequilibrios son indeseados, por lo tanto agregando ese nop en la instrucción else logramos hacer que si pasa por if (true) como por else se tarde siempre la misma cantidad de ciclos y de esta forma logramos igualdad en tiempo para ambos casos.

Cómo verán esas líneas de código se repiten para tantos señales de pwm queramos generar, así que podemos agregar o quitar señales pero siempre respetando que cada 100us exactos ocurra nuestra interrupción por TIMER0, quizás no en las 2 primeras corridas (como hemos visto anteriormente) lo logremos pero si en las sucesivas y de allí en adelante.
Esto último lo podemos lograr variando el valor del timer "val_timer0=88" y agregando unos cuantos nop ó delay_cycles() en la rutina de interrupción, preferiblemente al entrar en ella.

Nota: Yo no aconsejaría utilizar otras interrupciones en éste pic, porque se generarían desequilibrios de tiempo indeseados.

El mismo análisis aplica para el PIC18F4550, ajustando los valores que sean necesarios para lograr que con una interrupción del TIMER0 cada 100us se ejecute ésta línea de código
Código: [Seleccionar]
if (duty[0] >= theCount)
Éste es el código para el 18f4550:

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;
         }
      }
   }
}
« Última modificación: 20 de Marzo de 2008, 12:34:05 por gu1llermo »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
« Respuesta #1 en: 17 de Marzo de 2008, 18:30:05 »
Captura de la imagen del PIC16F877 funcionando:



Captura del PIC18F4550 funcionando:



También está una aplicación sencilla que hice en visual studio para mandarle datos al pic18

Archivos:

Servo16

pic18_servo16

Driver

Aplicación

Otras lecturas recomendadas:
Proyecto PicUSB
8 LED RGB PWM Controller

Nota1: En Debugger --> Settings..., colocar 48Mhz a Processor Frecuency para el caso del pic18

Nota2: Para medir los tiempos en el MPLAB para el caso del PIC18 tienen que comentar está línea:
Código: [Seleccionar]
usb_wait_for_enumeration();colocar:
Código: [Seleccionar]
//usb_wait_for_enumeration();porque sino el pic se queda allí en el MPLAB
También esta otra, en vez de estar así
Código: [Seleccionar]
if(usb_enumerated())//si el PicUSB está configurado
         //if (TRUE)

que esté así:
Código: [Seleccionar]
//if(usb_enumerated())//si el PicUSB está configurado
         if (TRUE)

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

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18310
    • MicroPIC
Me gusta mucho tu planteamiento, Guillermo. La exactitud del MPLAB demuestra que la aplicación debería funcionar. ¿La has probado con servos de verdad?

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC16)
« Respuesta #3 en: 18 de Marzo de 2008, 10:58:40 »
Bueno sip, la exactitud de tiempo que logré en el MPLAB en teoría debería funcionar y muy bien, lamentablemente no tengo ningún servo para hacer las pruebas, bueno e de confesar que hace poco supe lo que era un servo y su funcionamiento gracias a RedPic y a ahona sería cuestión de que alguién se atreviera a probarlo y verificar su funcionamiento, claro a su propio riesgo, también sería una buena idea medir las señales con un osciloscopio previamente en real y así estar más seguro todavía, yo en estos momentos estoy de vacaciones de la uni y allá es donde tengo acceso a uno, será cuestión de esperar un poco hasta llegar y observar la señal de salida en los pines con el mismo, claro pero si antes lo hiciese otra persona, mejor todavía  ;-)

Saludos y gracias!!!.

Que la pasen bien.

PD: Alguién le gustaría medir estas señales?

Desconectado jfh900

  • Moderadores
  • DsPIC30
  • *****
  • Mensajes: 3595
Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
« Respuesta #4 en: 18 de Marzo de 2008, 12:27:09 »
La idea es buena, pero la precisión es muy pequeña. Solo 20 posiciones por servo. Lo normal son 256 posiciones por servo.

Un saludo
* Cuando hables, procura que tus palabras sean mejores que el silencio.
* 'Todos somos ignorantes, lo que ocurre es que no todos ignoramos las mismas cosas.' Albert Einstein.
* No hay nada peor que un experto para evitar el progreso en un campo
* "La vida es como una novela. No importa que sea larga, sino que esté bien narrada" Seneca
* La vida no se vive por las veces que respiras, sino por los momentos que dejan sin aliento.
* Dios dijo: ∇·E=ρ/ε0 ; ∇·B=0 ; ∇xE=-dB/dt ; ∇xB= μ0ε0dE/dt..y la luz se hizo..!!..

Desde España Jesús

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
« Respuesta #5 en: 18 de Marzo de 2008, 12:33:40 »
Ah! ok, buen dato, no sabía, gracias por informarme, veré que se puede hacer al respecto.

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18310
    • MicroPIC
Lo ideal sería una rutina con nº de servos / resolución configurables  :mrgreen:

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
« Respuesta #7 en: 18 de Marzo de 2008, 13:29:46 »
jejeje sip, pero bueno vamos piano a piano  :D :D :D buena :idea:

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
« Respuesta #8 en: 18 de Marzo de 2008, 13:44:55 »
La idea es buena, pero la precisión es muy pequeña. Solo 20 posiciones por servo. Lo normal son 256 posiciones por servo.

Un saludo

Lo más que podría llegar es a 80 posiciones por servo, para los 16 que pretendo controlar, sirve? es decir vale la pena desarrollarlo? Y nada más para el pic18 que trabaja a 48MHz.

Y lograr un desplazamiento de 2,25º para cada uno.

Para lograr esto estaríamos hablando de una interrupción cada 25us y trabajar con contadores de 2Bytes.
« Última modificación: 18 de Marzo de 2008, 13:50:23 por gu1llermo »

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18310
    • MicroPIC
80 posiciones por servo parece una cifra bastante buena. De hecho, dependiendo de la aplicación, 20 posiciones ya serían suficientes. Por ejemplo, para girar el timón del barco no creo que sea necesaria mayor resolución.
Por eso te decía que lo ideal sería que la aplicación fuera configurable, y que cada uno balancee hacia nº de servos o nº de posiciones según sus necesidades.

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
« Respuesta #10 en: 18 de Marzo de 2008, 17:34:34 »
Bueno me puse analizar y lograr una precisión de 80 posiciones por servo tendría que reprogramar toda la rutina del manejo de interrupción del timer0 en ASM  :? y trabajar con los contadores de 2 bytes  :shock: pero repito: todo esto en assembler y ya en éste punto lo considero un poco impráctico, por los momentos dejo la rutina como está.

Lo de la opción de crear una aplicación configurable, indicándole nº de servos y resolución, no lo veo sencillo, porque las dos variables varían mucho, en un primer lugar pensé en diseñar una tabla, pero son muchas las combinaciones posibles, lo mejor es analizarlo, hacer mediciones de tiempo y configurarlo manualmente, si esto último no es sencillo imaginense programarlo, la idea está buena, pero en estos momentos no me anoto a ella.

Para Concluir:

Podemos decir que es posible controlar varios servos con una sola interrupción del timer0 generando un pwm por software, pero pagamos en precisión, así que si la precisión no es una limitante y el manejar 30 servos es necesario, éste sería el método ideal, pero en cambio si se desea mejor precisión y controlar hasta 8 servos entonces el algoritmo de RedPic es el más idóneo, por decir algo, pero en verdad está excelente, se puede obtener una precisión de 14bits (10.000Ticks) ,si no me equivoco, que traducido en grados 180/10000=0.018º, mejor precisión que esa  :shock: que más se puede pedir, nuevamente felicitaciones por tan buen código RedPic.

Saludos y que la pasen bien esta semana santa.
« Última modificación: 18 de Marzo de 2008, 18:46:49 por gu1llermo »

Desconectado pocher

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 2569
Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
« Respuesta #11 en: 19 de Marzo de 2008, 04:13:31 »
Guillermo en el programa que has adjuntado para el PIC16F877 no aparece la interrupción del TMR0.

Esta tarde si tengo un poco de tiempo me miraré el programa para el PIC18F4550.

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
« Respuesta #12 en: 19 de Marzo de 2008, 12:57:37 »
Hola pocher, no entiendo a que te refieres exactamente, que crees que le hace falta? porque la interrupción por timer0 está activada y empieza así la rutina encargada de atenderla:

Código: C
  1. #INT_TIMER0
  2. void isr_timer0(){// Cada 100us llega aqui de forma cíclica

y en el programa principal:

Código: [Seleccionar]
    setup_timer_0(RTCC_DIV_2);

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

Y disculpa por no entenderte.

Saludos.

Desconectado pocher

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 2569
Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
« Respuesta #13 en: 19 de Marzo de 2008, 13:45:06 »
Hola Guillermo.

Esta mañana he estado descargando los archivos .rar y me he puesto a provarlos. El programa que corresponde al PIC16F877 (Servo16) le falta la interrupción del TMR0 y es diferente al programa que explicas. Debes de haberlo traspapeleado al copiar - pegar.

Ahora mismo acabo de provar el del PIC18F4550 y funciona bien en el simulador. Yo como los niños, primero probar, luego entender.

Te felicito Guillermo, buen trabajo.

Un saludo

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
« Respuesta #14 en: 19 de Marzo de 2008, 16:47:32 »
Ah! ok, sip jejeje es muuuy posible eso, lo que pasa es que empiezo a modificar y se me olvida cambiarle el nombre y guardar el original, con decir que a veces tengo que buscar el código aquí de nuevo en internet para empezar a hacerles cambios de nuevo :D jejeje disculpa eso.

Gracias! por las felicitaciones  :mrgreen: y por tu observación, voy a corregirlo.

// Editado...

Ya lo corregí, efectivamente esa era una versión anterior la que había colocado, ahora si está todo bien. :mrgreen:

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