TODOPIC

Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: gu1llermo en 17 de Marzo de 2008, 18:29:00

Título: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: gu1llermo en 17 de Marzo de 2008, 18:29:00
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 (http://www.todopic.com.ar/foros/index.php?topic=20335.0) 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 (http://www.ccsinfo.com/downloads.php)
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):
(http://img393.imageshack.us/img393/2291/imagen1px3.jpg)

[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

(http://img132.imageshack.us/img132/4733/imagen2ke3.jpg)

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):

(http://img371.imageshack.us/img371/3669/imagen3im3.jpg)

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

(http://img355.imageshack.us/img355/1151/imagen5kc7.jpg)

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:

(http://img329.imageshack.us/img329/472/imagen6kk1.jpg)

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:

(http://img329.imageshack.us/img329/9770/imagen7tp6.jpg)

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:

(http://img355.imageshack.us/img355/8016/imagen8qe0.jpg)

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;
         }
      }
   }
}
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
Publicado por: gu1llermo en 17 de Marzo de 2008, 18:30:05
Captura de la imagen del PIC16F877 funcionando:

(http://img407.imageshack.us/img407/5369/servong2.jpg)

Captura del PIC18F4550 funcionando:

(http://img134.imageshack.us/img134/3444/pic18servo16pq7.jpg)

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

Archivos:

Servo16 (http://www.todopic.com.ar/foros/index.php?action=dlattach;topic=20335.0;attach=6441)

pic18_servo16 (http://www.todopic.com.ar/foros/index.php?action=dlattach;topic=20335.0;attach=6412)

Driver (http://www.todopic.com.ar/foros/index.php?action=dlattach;topic=20335.0;attach=6413)

Aplicación (http://www.todopic.com.ar/foros/index.php?action=dlattach;topic=20335.0;attach=6418)

Otras lecturas recomendadas:
Proyecto PicUSB (http://www.todopic.com.ar/foros/index.php?topic=2260.0)
8 LED RGB PWM Controller (http://www.todopic.com.ar/foros/index.php?topic=20574.0)

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.
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC16)
Publicado por: Nocturno en 18 de Marzo de 2008, 04:41:07
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?
Título: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC16)
Publicado por: gu1llermo 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?
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
Publicado por: jfh900 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
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
Publicado por: gu1llermo 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.
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC16)
Publicado por: Nocturno en 18 de Marzo de 2008, 13:08:10
Lo ideal sería una rutina con nº de servos / resolución configurables  :mrgreen:
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
Publicado por: gu1llermo en 18 de Marzo de 2008, 13:29:46
jejeje sip, pero bueno vamos piano a piano  :D :D :D buena :idea:
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
Publicado por: gu1llermo 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.
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC16)
Publicado por: Nocturno en 18 de Marzo de 2008, 15:42:08
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.
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
Publicado por: gu1llermo 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.
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
Publicado por: pocher 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.
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
Publicado por: gu1llermo 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.
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
Publicado por: pocher 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
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
Publicado por: gu1llermo 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.
Título: Versión 2 (solo pic18f)
Publicado por: gu1llermo en 19 de Marzo de 2008, 23:39:01
Bueno en vista de tantos detalles decidí hacer una segunda versión para que la chequeen y me digan sus observaciones u opiniones, ésta versión es muuuy distinta a la anterior, se podría decir que es una fusión entre esta y la de RedPic:

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 byte_alto_usb  recibe[1]//
#define byte_bajo_usb  recibe[2]//

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

#define periodo   60000//
#define derecha    7416//
#define centro     4417//
#define izquierda  1417//
#define inicio     7416

int8    i_int=1;// Índice de la interrupción del TIMER1
int8    x=0;// Número de interrupciones
int16   VTI_Servo[n+1], VTI[n+1];
// VTI_Servo[] contiene el estado de los servos y VTI los ticks de las proximas
// interrupciones
int16   TICK_dif[n];
int16   TICK[]={inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio};
int1    band_inicio=0;

#INT_TIMER1
void isr_timer1(){

   servo_00=bit_test(VTI_Servo[i_int],0);
   servo_01=bit_test(VTI_Servo[i_int],1);
   servo_02=bit_test(VTI_Servo[i_int],2);
   servo_03=bit_test(VTI_Servo[i_int],3);
   servo_04=bit_test(VTI_Servo[i_int],4);
   servo_05=bit_test(VTI_Servo[i_int],5);
   servo_06=bit_test(VTI_Servo[i_int],6);
   servo_07=bit_test(VTI_Servo[i_int],7);
   servo_08=bit_test(VTI_Servo[i_int],8);
   servo_09=bit_test(VTI_Servo[i_int],9);
   servo_10=bit_test(VTI_Servo[i_int],10);
   servo_11=bit_test(VTI_Servo[i_int],11);
   servo_12=bit_test(VTI_Servo[i_int],12);
   servo_13=bit_test(VTI_Servo[i_int],13);
   servo_14=bit_test(VTI_Servo[i_int],14);
   servo_15=bit_test(VTI_Servo[i_int],15);

   set_timer1(VTI[i_int]);
   i_int++;
}

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

void main()
{
    int     k, kk;
    int8    i=0;// es el índice exclusivo de duty[] en la recepción serial
    int8    dato=0;
    int8    recibe[3];// Buffer de recepción vía usb                 
  //int8    envia[2];// Habilitar en caso de necesitar
    int16   aux;
    int8    byte_alto, byte_bajo;// Son los baytes alto y bajo de la recepción
                                 // Serial rs232
   
    set_tris_b(0x00);
    set_tris_c(0b10000000);
    set_tris_d(0x00);
   
    PORTB=0x00;
    PORTD=0x00;
   
    port_b_pullups(FALSE);
   
    disable_interrupts(global);

    setup_adc_ports(NO_ANALOGS);
    setup_adc(ADC_OFF);

    setup_timer_1(T1_INTERNAL | T1_DIV_BY_4);

    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)
      {

         if (i_int>x){// Esto se repite al terminar un ciclo completo
         // y la 1era vez que se ejecuta éste código también
         
            // Hacemos una copia de TICK[] en TICK_dif[]
            for (k=0; k<=n-1; k++) TICK_dif[k]=TICK[k];
            // A continuación ordenamos TICK_dif[] de menor a mayor
            for (k=0; k<=n-1; k++){
               for (kk=k+1; kk<=n-1; kk++){
                  if (TICK_dif[k]>TICK_dif[kk]){
                     aux=TICK_dif[kk];
                     TICK_dif[kk]=TICK_dif[k];
                     TICK_dif[k]=aux;
                  }
               }
            }// Aqui ya terminamos de ordenar de menor a mayor TICK_dif[]
      // Ahora vamor a eliminar los repetidos y nos quedamos con los diferentes
      // solamente y a la vemos sabemos cuántos son
            k=0;
            for (kk=1; kk<=n-1;kk++){
               if (TICK_dif[k]!=TICK_dif[kk]){
                  k++;
                  TICK_dif[k]=TICK_dif[kk];
               }
            }
            x=k+1; // (x+1) es el número de interrupciones que se hacen
            i_int=0; // índice de interrupciones
            // Generamos el vector con los tiempos de las interrupciones
           
            VTI[0]=65535-TICK_dif[0];//
            // Una vez que se entre en la 1era interrupción por desborde del
            // timer1 éste será el valor con que se cargará dicho contador
            // para que ocurra la próxima interrupción.
            VTI[x]=65535-periodo+TICK_dif[x-1];// periodo=60000
            // Con éste valor se cargará el contador la última vez que entra
            // en el manejo de interrupciones en un ciclo, cada ciclo es de
            // 20ms, por lo tanto será el tiempo que esperará para que se
            // cumpla dicho tiempo
            // 60000ticks*(1/12000000)*4=20ms
            // a éste valor le resto lo que ha transcurrido previamente
            // Próximanete genramos los tiempos de duración de cada interrupción
            for (k=1; k<=x-1; k++){
            VTI[k]=65535-(TICK_dif[k]-TICK_dif[k-1]);
          }
           
            // Ahora vamos a generar el vector de los estados de los pines
            // en las interrupciones VTI_Servo[]
            for (k=0; k<=x-1;k++){
               for (kk=0; kk<=n-1; kk++){
                  if (TICK_dif[k]<=TICK[kk]){
                     bit_set(VTI_Servo[k], kk);
                  }
                  else bit_clear(VTI_Servo[k], kk);
               }
            }
         }
           
// Recepción de datos via rs232//         
         if (kbhit()){
            switch (dato){
               
               case 0:
                  i=getc();
                  dato=1;
               break;
               
               case 1:
                  byte_alto=getc();// recibe el byte alto
                  dato=2;
               break;
               
               case 2:
                  byte_bajo=getc();// recibe el byte bajo
                  TICK[i]=256*byte_alto+byte_bajo;
                  dato=0;
               break;
            }
         }
// Recepción vía USB         
         if (usb_kbhit(1))//El endpoint de salida contiene datos del host (EP1)?
         {
            usb_get_packet(1, recibe, 3);// Recibimos los 3 bytes:
            // (servo,byte_alto_usb, byte_bajo_usb)
            TICK[i_usb]=256*byte_alto_usb+byte_bajo_usb;
         }
         
         if (band_inicio==0){// Se ejecuta una sola vez
            set_timer1(65535-60000);// 65535-60000
                             // espero 20ms para empezar a mover los servos
            enable_interrupts(INT_TIMER1);
            enable_interrupts(GLOBAL);
            band_inicio=1;
         }
      }
   }
}

También hice otro módulo para probar el usb, pero recuerdo que tiene comunicación serial rs232 también, la trama ahora es de 3 bytes, el 1er byte enviado indica el servo [0..15], el 2do byte enviado corresponde al byte alto y el 3er byte enviado al byte bajo, que unidos nos dá los 16bits, esto es: TICKs=256*byte_alto+byte_bajo, para resumir, la trama es (servo, byte_alto, byte_bajo)

Imagen funcionando en donde se vé la aplicación y el proteus:

(http://img171.imageshack.us/img171/4808/pic18servover02mh2.jpg)

Adjunto Simulación, código y aplicación, el driver es el mismo que está publicado más arriba.

Nota: No he probado esto en físico, ya que no tengo servos, solo simulación en proteus.

Saludos.
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
Publicado por: mandoanaiz en 20 de Marzo de 2008, 07:52:57
Excelente trabajo :-/ :-/
Andaba yo pienseando algo parecido, gracias por compartirlo. :lol: :lol:
Título: Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
Publicado por: gu1llermo en 20 de Marzo de 2008, 11:00:58
Simulando un poco observé que en esta versión hay solapamiento de tiempo, me explico, si la diferencia entre dos Ticks consecutivos es menor que 300 entonces podemos observar como otros servos se ven afectado en su posición, sufriendo estos un pequeño desplazamiento, y como la diferencia en ticks del extremo derecho y el extremo izquierdo es de 6000 Ticks (según pruebas que e realizado posteriormente) tenemos que 6000/300=20 pasos  :shock: jejeje voy a soñar con ese 20 :D :D :D total total son 20 pasos por servos sin que el otro se vea afectado, que curioso nop, esto es para una configuración del timer1 igual a T1_DIV_BY_4 lo que quiere decir que todavía hay esperanza configurandolo a T1_DIV_BY_1 que la lógica nos dice que así obtendríamos los 80 pasos previamente discutidos. Asombroso y curioso nop, dos vías y la misma llegada.

Saludos.
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: gu1llermo en 20 de Marzo de 2008, 12:47:49
Bueno ya realicé el cambio con la esperanza de obtener buenos resultados pero lamento informarles que no he podido, sin embargo publicaré el código final, en el que traté de mejorar la precisión por si alguién le interesa saber hasta donde llegué:

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 byte_alto_usb  recibe[1]//
#define byte_bajo_usb  recibe[2]//

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

#define ventana    29827//
#define derecha    29827//
#define centro     17827//
#define izquierda   5827//
#define inicio      17827

#define ventana_ciclos 26176// Valor determinado en simulaciones

int8    i_int=0;// Índice de la interrupción del TIMER1
int8    x=0;// Número de interrupciones
int16   VTI_Servo[n+1], VTI[n+1];
// VTI_Servo[] contiene el estado de los servos y VTI los ticks de las proximas
// interrupciones
int16   TICK_dif[n];
int16   TICK[]={inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio,inicio};
int1    band_inicio=0;
int8    ciclos=9;

#INT_TIMER1
void isr_timer1(){
   int8 parte_baja, parte_alta;
   
   if (i_int>x){
      ciclos++;
      set_timer1(65535-ventana_ciclos);//
   }
   else {
      parte_baja=make8(VTI_Servo[i_int], 0);
      parte_alta=make8(VTI_Servo[i_int], 1);

      if (parte_baja & 0b00000001){
         servo_00=1;
      }
      else {
         servo_00=0;
         delay_cycles(1);
      }
   
      if (parte_baja & 0b00000010){
         servo_01=1;
      }
      else {
         servo_01=0;
         delay_cycles(1);
      }
   
      if (parte_baja & 0b00000100){
         servo_02=1;
      }
      else {
         servo_02=0;
         delay_cycles(1);
      }
   
      if (parte_baja & 0b00001000){
         servo_03=1;
      }
      else {
         servo_03=0;
         delay_cycles(1);
      }
   
      if (parte_baja & 0b00010000){
         servo_04=1;
      }
      else {
         servo_04=0;
         delay_cycles(1);
      }
   
      if (parte_baja & 0b00100000){
         servo_05=1;
      }
      else {
         servo_05=0;
         delay_cycles(1);
      }
   
      if (parte_baja & 0b01000000){
         servo_06=1;
      }
      else {
         servo_06=0;
         delay_cycles(1);
      }
   
      if (parte_baja & 0b10000000){
      servo_07=1;
      }
      else {
         servo_07=0;
         delay_cycles(1);
      }
   
      if (parte_alta & 0b00000001){
         servo_08=1;
      }
      else {
         servo_08=0;
         delay_cycles(1);
      }
   
      if (parte_alta & 0b00000010){
         servo_09=1;
      }
      else {
         servo_09=0;
         delay_cycles(1);
      }
   
      if (parte_alta & 0b00000100){
         servo_10=1;
      }
      else {
         servo_10=0;
         delay_cycles(1);
      }
   
      if (parte_alta & 0b00001000){
         servo_11=1;
      }
      else {
         servo_11=0;
         delay_cycles(1);
      }
   
      if (parte_alta & 0b00010000){
         servo_12=1;
      }
      else {
         servo_12=0;
         delay_cycles(1);
      }
   
      if (parte_alta & 0b00100000){
         servo_13=1;
      }
      else {
         servo_13=0;
         delay_cycles(1);
      }
   
      if (parte_alta & 0b01000000){
         servo_14=1;
      }
      else {
         servo_14=0;
         delay_cycles(1);
      }
   
      if (parte_alta & 0b10000000){
         servo_15=1;
      }
      else {
         servo_15=0;
         delay_cycles(1);
      }

      set_timer1(VTI[i_int]);
      i_int++;
   }
}

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

void main()
{
    int     k, kk;
    int8    i=0;// es el índice exclusivo de duty[] en la recepción serial
    int8    dato=0;
    int8    recibe[3];// Buffer de recepción vía usb                 
  //int8    envia[2];// Habilitar en caso de necesitar
    int16   aux;
    int8    byte_alto, byte_bajo;// Son los baytes alto y bajo de la recepción
                                 // Serial rs232
   
    set_tris_b(0x00);
    set_tris_c(0b10000000);
    set_tris_d(0x00);
   
    PORTB=0x00;
    PORTD=0x00;
   
    port_b_pullups(FALSE);
   
    disable_interrupts(global);

    setup_adc_ports(NO_ANALOGS);
    setup_adc(ADC_OFF);

    setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);

    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)
      {

         if (ciclos>8){// Esto se repite al terminar un ciclo completo de 20ms
         // y la 1era vez que se ejecuta éste código también
            ciclos=1;
            // Hacemos una copia de TICK[] en TICK_dif[]
            for (k=0; k<=n-1; k++) TICK_dif[k]=TICK[k];
            // A continuación ordenamos TICK_dif[] de menor a mayor
            for (k=0; k<=n-1; k++){
               for (kk=k+1; kk<=n-1; kk++){
                  if (TICK_dif[k]>TICK_dif[kk]){
                     aux=TICK_dif[kk];
                     TICK_dif[kk]=TICK_dif[k];
                     TICK_dif[k]=aux;
                  }
               }
            }// Aqui ya terminamos de ordenar de menor a mayor TICK_dif[]
      // Ahora vamor a eliminar los repetidos y nos quedamos con los diferentes
      // solamente y a la vemos sabemos cuántos son
            k=0;
            for (kk=1; kk<=n-1;kk++){
               if (TICK_dif[k]!=TICK_dif[kk]){
                  k++;
                  TICK_dif[k]=TICK_dif[kk];
               }
            }
            x=k+1; // (x+1) es el número de interrupciones que se hacen
            i_int=0; // índice de interrupciones
            // Generamos el vector con los tiempos de las interrupciones
           
            VTI[0]=65535-TICK_dif[0];//
            // Una vez que se entre en la 1era interrupción por desborde del
            // timer1 éste será el valor con que se cargará dicho contador
            // para que ocurra la próxima interrupción.
            VTI[x]=65535-ventana+TICK_dif[x-1];// ventana=2,5ms
            // Con éste valor se cargará el contador la última vez que entra
            // en el manejo de interrupciones en un ciclo, cada ciclo es de
            // 2,5ms, por lo tanto será el tiempo que esperará para que se
            // cumpla dicho tiempo
            // 30000ticks*(1/12000000)=2,5ms
            // a éste valor le resto lo que ha transcurrido previamente
            // Próximanete genramos los tiempos de duración de cada interrupción
            for (k=1; k<=x-1; k++){
            VTI[k]=65535-(TICK_dif[k]-TICK_dif[k-1]);
          }
           
            // Ahora vamos a generar el vector de los estados de los pines
            // en las interrupciones VTI_Servo[]
            for (k=0; k<=x-1;k++){
               for (kk=0; kk<=n-1; kk++){
                  if (TICK_dif[k]<=TICK[kk]){
                     bit_set(VTI_Servo[k], kk);
                  }
                  else bit_clear(VTI_Servo[k], kk);
               }
            }
         }
           
// Recepción de datos via rs232//         
         if (kbhit()){
            switch (dato){
               
               case 0:
                  i=getc();
                  dato=1;
               break;
               
               case 1:
                  byte_alto=getc();// recibe el byte alto
                  dato=2;
               break;
               
               case 2:
                  byte_bajo=getc();// recibe el byte bajo
                  TICK[i]=256*byte_alto+byte_bajo;
                  dato=0;
               break;
            }
         }
// Recepción vía USB         
         if (usb_kbhit(1))//El endpoint de salida contiene datos del host (EP1)?
         {
            usb_get_packet(1, recibe, 3);// Recibimos los 3 bytes:
            // (servo,byte_alto_usb, byte_bajo_usb)
            TICK[i_usb]=256*byte_alto_usb+byte_bajo_usb;
         }
         
         if (band_inicio==0){// Se ejecuta una sola vez
            set_timer1(65535-ventana);//
                             // espero 20ms para empezar a mover los servos
            enable_interrupts(INT_TIMER1);
            enable_interrupts(GLOBAL);
            band_inicio=1;
         }
      }
   }
}

Adjunto archivos con una nueva interface, en la que modifiqué los límites permitidos.

Título: Excelentes noticias!, lo he logrado
Publicado por: gu1llermo en 21 de Marzo de 2008, 02:55:02
Lo logré!!!!  :-/ :-/ :-/ :-/ :-/ :mrgreen: ya logré mejorar la precisión a 81 pasos por servo en el pic18f, éste es el código resultante:

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
#include <math.h>

#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]//

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

#define max_cont 101//
#define derecha   101//
#define centro    60//
#define izquierda 20// 20*25us =0,5ms

#define val_timer1 65497//65535-75, 1 tick=(1/12000000)*4
#define resto      13047//65535-52500

int8    cont=0;
int8    duty[n];

#INT_TIMER1
void isr_timer1(){
   delay_cycles(1);
   if (duty[0] >= cont)// Cada 25us llega aqui de forma cíclica por 2,5ms nada
                       // más, luego en 17,5ms
      {
         servo_00=1;
      }
      else
      {
         servo_00=0;
         delay_cycles(1);
      }
     
      if (duty[1] >= cont)
      {
         servo_01=1;
      }
      else
      {
         servo_01=0;
         delay_cycles(1);
      }
     
      if (duty[2] >= cont)
      {
         servo_02=1;
      }
      else
      {
         servo_02=0;
         delay_cycles(1);
      }
     
      if (duty[3] >= cont)
      {
         servo_03=1;
      }
      else
      {
         servo_03=0;
         delay_cycles(1);
      }
     
      if (duty[4] >= cont)
      {
         servo_04=1;
      }
      else
      {
         servo_04=0;
         delay_cycles(1);
      }
     
      if (duty[5] >= cont)
      {
         servo_05=1;
      }
      else
      {
         servo_05=0;
         delay_cycles(1);
      }
     
      if (duty[6] >= cont)
      {
         servo_06=1;
      }
      else
      {
         servo_06=0;
         delay_cycles(1);
      }
     
      if (duty[7] >= cont)
      {
         servo_07=1;
      }
      else
      {
         servo_07=0;
         delay_cycles(1);
      }
     
      if (duty[8] >= cont)
      {
         servo_08=1;
      }
      else
      {
         servo_08=0;
         delay_cycles(1);
      }
     
      if (duty[9] >= cont)
      {
         servo_09=1;
      }
      else
      {
         servo_09=0;
         delay_cycles(1);
      }
     
      if (duty[10] >= cont)
      {
         servo_10=1;
      }
      else
      {
         servo_10=0;
         delay_cycles(1);
      }
     
      if (duty[11] >= cont)
      {
         servo_11=1;
      }
      else
      {
         servo_11=0;
         delay_cycles(1);
      }
     
      if (duty[12] >= cont)
      {
         servo_12=1;
      }
      else
      {
         servo_12=0;
         delay_cycles(1);
      }
     
      if (duty[13] >= cont)
      {
         servo_13=1;
      }
      else
      {
         servo_13=0;
         delay_cycles(1);
      }
     
      if (duty[14] >= cont)
      {
         servo_14=1;
      }
      else
      {
         servo_14=0;
         delay_cycles(1);
      }
     
      if (duty[15] >= cont)
      {
         servo_15=1;
      }
      else
      {
         servo_15=0;
         delay_cycles(1);
      }

      set_timer1(val_timer1);
      cont++;
}

/////////////////////////////////////// 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_1(T1_INTERNAL | T1_DIV_BY_4);

    set_timer1(resto);
   
    enable_interrupts(INT_TIMER1);
    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;
         }
// Análisis
         if (cont>max_cont+1){
            cont=1;
            set_timer1(resto);// Lo suficiente para que en 20ms exactos
                              // se produzca la próxima interrupción
         }
         
      }
   }
}

Lo interesante de éste código es que es sencillo, en comparación con el anterior y éste si maneja correctamente todos los servos sin desfasarse nada, todo está al pelo, solo echenle un vistazo por encima al código para que vean lo sencillo que es  :mrgreen:

Aquí está una imagen funcionando:

(http://img517.imageshack.us/img517/9413/definitivooe9.jpg)

Ahora si lo dejo hasta aquí, hice lo que quería hasta los momentos  ;-)

Adjunto interface para controlarlo vía USB.

PD: Para esta precisión no creo que se le puedan adaptar muchos servos, pero para la versión en la que se la precisión es de 20 pasos sip.


Saludos. :)
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: Nocturno en 21 de Marzo de 2008, 03:02:55
Enhorabuena Guillermo  8)
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: gu1llermo en 21 de Marzo de 2008, 15:16:25
Respuesta a pocher (http://www.todopic.com.ar/foros/index.php?topic=20960.msg160625#msg160625)

Yo no e querido implementar la recepción de datos por interrupción porque pienso que se puede perder la linealidad en los tiempos, esos servos son muy sensibles y si le llega la hora de apagarse ó encenderse su señal, les gusta que se les preste atención en el momento justo porque sino hacen lo que ellos quieran, por otro lado, siguiendo tus recomendaciones le agregué un control de overrun, para evitar cuelgues en caso de recibir varios datos a la vez, también realicé una sencilla interfaz rs232 que la podemos visualizar a través del virtual terminal y funciona de la siguiente forma:

Código: [Seleccionar]
Servo= pulsamos el 0...
Código: [Seleccionar]
Servo= 0pulsamos ENTER
Código: [Seleccionar]
Servo= 0
Pos=
pulsamos el 2...
Código: [Seleccionar]
Servo= 0
Pos= 2
pulsamos el 6
Código: [Seleccionar]
Servo= 0
Pos= 26
pulsamos ENTER
Código: [Seleccionar]
Servo= 0
Pos= 26
Dato enviado!

Coloqué en un cuadro para diferenciar lo que vemos por el virtual terminal.

Esto es para el caso de que quisieramos mover el Servo 0 a la posición 26.

También tiene manejo de datos inválidos, es decir fuera de rango, Sevo [0..15] y Pos [20..101] son los valores permitidos solamente.

En la imagen podemos ver tanto el virtual terminal como el PicUSB mandándole comandos al Pic18f

(http://img177.imageshack.us/img177/1170/pic18vl2.jpg)

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)

#byte RCSTA=             0xFAB       // Registro de recepcion de la usart
#byte RCREG=             0xFAE       // Aquí se recibe el dato de la usart

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

//#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
#include <string.h>

#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]//

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

#define max_cont 101//
#define derecha   101//
#define centro    60//
#define izquierda 20// 20*25us =0,5ms

#define val_timer1 65497// 1 tick=(1/12000000)*4
#define resto      13047//

int8    cont=0;
int8    duty[n];

#INT_TIMER1
void isr_timer1(){
   delay_cycles(1);
   if (duty[0] >= cont)// Cada 25us llega aqui de forma cíclica por 2,5ms nada
                       // más, luego en 17,5ms
      {
         servo_00=1;
      }
      else
      {
         servo_00=0;
         delay_cycles(1);
      }
     
      if (duty[1] >= cont)
      {
         servo_01=1;
      }
      else
      {
         servo_01=0;
         delay_cycles(1);
      }
     
      if (duty[2] >= cont)
      {
         servo_02=1;
      }
      else
      {
         servo_02=0;
         delay_cycles(1);
      }
     
      if (duty[3] >= cont)
      {
         servo_03=1;
      }
      else
      {
         servo_03=0;
         delay_cycles(1);
      }
     
      if (duty[4] >= cont)
      {
         servo_04=1;
      }
      else
      {
         servo_04=0;
         delay_cycles(1);
      }
     
      if (duty[5] >= cont)
      {
         servo_05=1;
      }
      else
      {
         servo_05=0;
         delay_cycles(1);
      }
     
      if (duty[6] >= cont)
      {
         servo_06=1;
      }
      else
      {
         servo_06=0;
         delay_cycles(1);
      }
     
      if (duty[7] >= cont)
      {
         servo_07=1;
      }
      else
      {
         servo_07=0;
         delay_cycles(1);
      }
     
      if (duty[8] >= cont)
      {
         servo_08=1;
      }
      else
      {
         servo_08=0;
         delay_cycles(1);
      }
     
      if (duty[9] >= cont)
      {
         servo_09=1;
      }
      else
      {
         servo_09=0;
         delay_cycles(1);
      }
     
      if (duty[10] >= cont)
      {
         servo_10=1;
      }
      else
      {
         servo_10=0;
         delay_cycles(1);
      }
     
      if (duty[11] >= cont)
      {
         servo_11=1;
      }
      else
      {
         servo_11=0;
         delay_cycles(1);
      }
     
      if (duty[12] >= cont)
      {
         servo_12=1;
      }
      else
      {
         servo_12=0;
         delay_cycles(1);
      }
     
      if (duty[13] >= cont)
      {
         servo_13=1;
      }
      else
      {
         servo_13=0;
         delay_cycles(1);
      }
     
      if (duty[14] >= cont)
      {
         servo_14=1;
      }
      else
      {
         servo_14=0;
         delay_cycles(1);
      }
     
      if (duty[15] >= cont)
      {
         servo_15=1;
      }
      else
      {
         servo_15=0;
         delay_cycles(1);
      }

      set_timer1(val_timer1);
      cont++;
}

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

void main()
{
    int     k;
    int8    recibe[2];// Buffer de recepción vía usb                 
  //int8    envia[2];// Habilitar en caso de necesitar
    int8    i=0, i_old=0;// es el índice exclusivo de duty[] en la recepción serial
                         // i_old es una variable auxiliar, para saber si ya
                         // pase por el análisis del buffer
    char    buffer[32];// buffer de recepeción serial
    int8    servo, pos;
    int8    aux;// sirve para manejar el overrun, vacía buffer del pic
    int1    band=0;// band=0 => recibir índice del servo
    int1    mensj1=0, mensj2=0;// Son banderas para saber si se escribieron
                           // los mensajes 'Servo= ' y 'Pos= '                     
    int1    inicio=0;

    set_tris_b(0x00);
    set_tris_c(0b10000000);
    set_tris_d(0x00);
   
    disable_interrupts(INT_TIMER1);
    disable_interrupts(GLOBAL);   

    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_INTERNAL | T1_DIV_BY_4);

   
   
    printf("*** Programa para controlar 16 Sevos ***\r\n");
    printf("*** Via Serial y USB al mismo tiempo ***\r\n");
    printf("*** Realizado por Guillermo Nunez    ***\r\n");
    printf("*** Para el foro TODOPIC ;-)         ***\r\n");
    printf("*** Fecha: 21/03/08                  ***\r\n");
    printf("\r\n");
    printf("Servo=   [0..15]\r\n");
    printf("Pos=   [20..101]\r\n");
    printf("Espere mientras se incializa el PicUSB...\r\n");
   
   
    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
   
    printf(">>> Listo!\r\n");

   while (TRUE)
   {
      if(usb_enumerated())//si el PicUSB está configurado
         //if (TRUE)
      {
// Si se reciben más de 2 bytes sin haberlos procesado, se produce un error
// de Overrun. En este caso, se borra el error reiniciando CREN para que nuestro pic
// no se cuelgue
         

         // Análisis
         if (cont>max_cont+1){
            cont=1;
            //delay_cycles(25);
            set_timer1(resto);// Lo suficiente para que en 20ms exactos
                              // se produzca la próxima interrupción
         }
         if (OERR)
         {
            aux=RCREG;
            aux=RCREG;
            aux=RCREG;
            CREN=0;
            CREN=1;
            printf("Error:\r\n");
            printf("Envíe de nuevo los datos\r\n");
            i=0;
         }
// Recepción de datos via rs232
         if (kbhit()){
               buffer[i]=getc();
               i++;
         }
// Escribe los mensajes si no lo has hecho
         if (band==0){
            if (mensj1==0){
               printf("\r\nServo= ");
               mensj1=1;
            } 
         }
         else{
            if (mensj2==0){
               printf("\r\nPos= ");
               mensj2=1;
            }
         }
         
// Procesa los datos recibidos
         if (i_old!=i){
            if ((!isdigit(buffer[i-1])) && (buffer[i-1]!=13)) { //Valida los datos
               i--;// Lo sacamos del buffer y no se escribe en pantalla
            }
            else{
               if (band==0){// Estamos recibiendo el índice del Servo
                  if (i>1){// si ya se recibieron 2 datos
                     i=0;                     
                     if (buffer[1]!=13){// Pregunto si el segundo dato es
                                        // Distinto a ENTER
                        putc(buffer[1]);
                        servo=servo*10+buffer[1]-0x30;
                        if (servo>15){
                           printf("\r\nIngrese un valor entre 0 y 15\r\n");
                           mensj1=0;
                        }
                        else{
                           band=1;
                           mensj2=0;
                        }
                     }
                     else{// Si el 2do dato es ENTER
                        band=1;
                        mensj2=0;
                     }
                  }
                  else{// Se ha recibido 1 dato
                     if (buffer[0]!=13){// Verificamos que no sea ENTER
                        servo=buffer[0]-0x30;
                        putc(buffer[0]);
                     }
                     else i=0;// No nos interesa un ENTER, así que lo sacamos
                              // del buffer
                  }
               }
               else{// Estamos recibiendo la posición
                  switch (i){
                 
                     case 1:// Hemos recibido 1 dato
                        if (buffer[0]!=13){// Verificamos que no sea ENTER
                           pos=buffer[0]-0x30;
                           putc(buffer[0]);
                        }
                        else i=0;// No nos interesa un ENTER, así que lo sacamos
                                 // del buffer
                     break;
                     
                     case 2:// Hemos recibido 2 datos
                       
                        if (buffer[1]!=13){// Verificamos que no sea ENTER
                           pos=pos*10+buffer[1]-0x30;
                           putc(buffer[1]);
                        }
                        else{  // Terminamos de recibir datos
                           printf("\r\nIngrese un valor entre 20 y 101\r\n");
                           i=0;
                           mensj2=0;
                        }
                     break;
                     
                     case 3:// Hemos recibido 3 datos
                       
                        i=0;
                        if (buffer[2]!=13){// Verificamos que no sea ENTER
                           putc(buffer[2]);
                           pos=pos*10+buffer[2]-0x30;
                           if ((pos>=20) && (pos<=101)){
                              duty[servo]=pos;
                              mensj1=0;
                              band=0;
                              printf("\r\nDato enviado! \r\n");
                           }
                           else{
                              printf("\r\nIngrese un valor entre 20 y 101\r\n");
                              mensj2=0;
                           }
                        }
                        else{
                           if (pos>=20){
                              duty[servo]=pos;
                              mensj1=0;
                              band=0;
                              printf("\r\nDato enviado! \r\n");
                           }
                           else{
                              printf("\r\nIngrese un valor entre 20 y 101\r\n");
                              mensj2=0;
                           }
                        }
                     break;
                  }
               }
            }
            i_old=i;
         }

         
// 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;
         }
         if (inicio==0){
            inicio=1;
            set_timer1(resto);
            enable_interrupts(INT_TIMER1);
          enable_interrupts(GLOBAL);
         
        }
         
      }
   }
}


Saludos.
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: gu1llermo en 21 de Marzo de 2008, 15:32:57
Enhorabuena Guillermo  8)

Gracias! Nocturno  :lol:, ahora falta la prueba de fuego que es en real  8) esa parte creo que RedPic la va hacer pero cuando termine la placa que está haciendo y tenga tiempo claro.

Saludos.
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: pocher en 22 de Marzo de 2008, 03:48:16
Yo también me sumo a Enhorabuena Guillermo.

Lo acabo de probar. Has hecho un programa para torpes que son como me gustan a mí. Perfecto.
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: gu1llermo en 22 de Marzo de 2008, 05:42:35
Y lo mejoré  :shock: :mrgreen: :mrgreen: :-/ :-/ :-/ ahora sip, prueben éste, solo que le quité el usb y lo dejé solo con comunicación serial.

Imagen funcionando:
(http://img80.imageshack.us/img80/4005/sinusbkz4.jpg)

Adjunto archivos .c, .hex y .dsn comprimidos con winrar.

PD: Me acabo de dar cuenta que en la interface se me olvidó quitar que es por comunicación serial nada más  :lol:

Saludos.
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: pocher en 22 de Marzo de 2008, 06:17:24
Ah pájaro, pájaro ahora sí que para 59 saca 0º clavados y el cochecito podrá ir recto, je, je.

Todo es cuestión de corregir los tiempos ... y probar.
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: gu1llermo en 22 de Marzo de 2008, 11:32:09
Analizando el código, midiendo los tiempos y viendo las otras salidas que restan en nuestro pic, puedo decir con seguridad que puedo manejar hasta 28 servos con ésta misma precisión y estabilidad, los puertos son PORTA 6 pines [0..5], PORTB 8 pines, PORTC 3 pines [0..2], PORTD 8 pines, PORTE 3 pines [0..2].

Vistes! la estabilidad de los servos? ahora si no veo ninguna tembladera ni nada por el estilo, sumado a esto la precisión  :wink:  :mrgreen: lo que restaría es manejar los tiempos como dices, pero advierto que no es cosa sencilla ésta.

PD: Hay un pensamiento que me gusta mucho y dice: "Si he visto más allá que los demás, es porque estaba parado en hombros de gigantes" -- Isaac Newton, nunca me cansaré de agradecer y reconocer que he podido lograr esto gracias a todos los que aportamos conocimientos y experiencias en éste foro y en la internet en general, en especial a RedPic que fué el que tuvo la idea y la tomé como mía también, por su algoritmo y su teoría, sus enlaces y todo lo que aporta que ya es bastante (gracias por eso también), y a todo el foro en general, porque fuí tomando ideas de aquí y de allá, recomendaciones, sugerencias, comentarios, etc... gracias, mil gracias!

Saludos.

(Gracias Padre por todo lo que he logrado, todo es gracias a tí Señor)
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: gu1llermo en 24 de Marzo de 2008, 12:24:28
Le hice una modificación y mejora al código, ahora es un poco más flexible, por los momentos el período es de 20ms, y podemos fijar las posiciones izquierda y derecha desde el encabezado del programa, para lograr diferentes frecuencias, me explico, actualmente izquierda=20 y derecha=100, esto es 20*25us=500us=0,5ms (cada 25us es que ocurre una interrupción) pero si queremos 0,3ms esto es 300us/25us=12, asi que le asignaríamos a izquierda el valor de 12, lo mismo para derecha que actualmente vale 100, 100*25us=2500us=2,5ms, pero si quisieramos 2,3ms sería 2300us/25us=92, por lo que derecha=92.

Esto lo hice porque ví que los futaba van de 0,3ms a 2,3ms, y otros servos pueden ir de 1ms a 2ms, para un período de 20ms.

De forma resumida trataré de explicar como fué diseñado:

Usa el principio de control de pwm de los LEDs RGB, cada 25us (exactos) ocurre una interrupción e incrementa un contador, cada servo tiene asociado un valor de ciclo útil que es comparado con éste contador, si es mayor o menor se activa su señal o se desactiva, luego pasados 2,5ms cambio el valor del timer1 para que cada 5,461333ms ocurra una interrupción, dandole respiro al micro por ese tiempo para que haga otras funciones, esto lo hace 3 veces y luego hace una última interrupción de 1.116ms para lograr los 20ms exactos de su período, de aquí en adelante todo vuelve a empezar, es decir, cada 25us una interrupción, esto lo hace 100 veces (2,5ms) luego cada 5ms por 3 veces, luego el resto del tiempo que necesite para llegar a los 20ms.

Adicionalmente e de decir que lo hice más robusto que el anterior, por lo tanto lo recomiendo ampliamente.

Saludos y espero que le sirva a alguién.
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: Cryn en 13 de Abril de 2008, 00:57:26
 :shock: guay qeu bonito trabajo haz hecho gu1llermo :-/

felicidades ha qeudado muy bueno :mrgreen:
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: gu1llermo en 13 de Abril de 2008, 05:05:19
Gracias Cryn, no fué fácil pero allí está. Voy a colocar aquí mismo el código para que nadie tenga que estar descargandolo sino que lo copie y lo pegue en donde quiera:

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)

#byte RCSTA=             0xFAB       // Registro de recepcion de la usart
#byte RCREG=             0xFAE       // Aquí se recibe el dato de la usart
#byte TMR1L=             0xFCE// Byte bajo del timer1


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

//#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

#include <string.h>

#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 n 16// número de servos
            //para otro número de servos hay que hacer mediciones de tiempo

#define max_cont  100// 100*25us=2,5 siempre dejar fijo éste valor,
                     // con esto se logra 20ms de período
int8 derecha=100;    // 100*25us=2.5ms
                     // Si queremos 2,3ms colocamos derecha=92, 2300/25=92
int8 centro=60;
int8 izquierda=20;   // 20*25us =0,5ms
                     // Si queremos 0,3ms colocamos izquierda=92, 300/25=12

#define val_timer1  65389// Valor determinado por tanteo
#define resto       52819//Este valor sirve para lograr los 20ms del período total
                         // También determinado por tanteo, usando el mplab

int8    cont=0;
int8    duty[n];
int8    cont_old=0;// variable auxiliar

#INT_TIMER1
void isr_timer1(){

   int8  cap_timer1;
   
   cap_timer1=TMR1L;// Capturamos el valor bajo del timer1
   
   if (cap_timer1==42){delay_cycles(4);}// Valor del timer1_low determinado por simulaciones
                                        // usando el mplab
   else delay_cycles(4);
   // la idea es que aqui siempre sea la misma cantidad de ciclos en cada entrada
   // esto fue determinado por tanteo
   // cada 25us llegue aqui de forma cíclica, variando "val_timer1"
   if (duty[0] > cont){servo_00=1;}
   else{servo_00=0;delay_cycles(1);}
     
   if (duty[1] > cont){servo_01=1;}
   else{servo_01=0; delay_cycles(1);}
     
   if (duty[2] > cont){servo_02=1;}
   else{servo_02=0;delay_cycles(1);}
     
   if (duty[3] > cont){servo_03=1;}
   else{servo_03=0;delay_cycles(1);}
     
   if (duty[4] > cont){servo_04=1;}
   else{servo_04=0;delay_cycles(1);}
     
   if (duty[5] > cont){servo_05=1;}
   else{servo_05=0;delay_cycles(1);}
     
   if (duty[6] > cont){servo_06=1;}
   else{servo_06=0;delay_cycles(1);}
     
   if (duty[7] > cont){servo_07=1;}
   else{servo_07=0;delay_cycles(1);}
     
   if (duty[8] > cont){servo_08=1;}
   else{servo_08=0;delay_cycles(1);}
     
   if (duty[9] > cont){servo_09=1;}
   else{servo_09=0;delay_cycles(1);}
     
   if (duty[10] > cont){servo_10=1;}
   else{servo_10=0;delay_cycles(1);}
     
   if (duty[11] > cont){servo_11=1;}
   else{servo_11=0;delay_cycles(1);}
     
   if (duty[12] > cont){servo_12=1;}
   else{servo_12=0;delay_cycles(1);}
     
   if (duty[13] > cont){servo_13=1;}
   else{servo_13=0;delay_cycles(1);}
     
   if (duty[14] > cont){servo_14=1;}
   else{servo_14=0;delay_cycles(1);}
     
   if (duty[15] > cont){servo_15=1;}
   else{servo_15=0;delay_cycles(1);}

   set_timer1(val_timer1);
   cont++;
// Análisis
      if (cont>max_cont){
         if (cont_old!=cont){
            cont_old=cont;
            if ((cont-max_cont)<4){
               set_timer1(0);// 5,461333ms+
            }
            else {
               set_timer1(resto);// 20ms-5,461333ms*3-2,5ms=1.116ms
                                 // => 13392 Ticks, 65535-13392=52143
               cont=0;
            }
         }
      }   
}

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

void main()
{
    int     k;
    int8    i=0, i_old=0;// es el índice exclusivo de duty[] en la recepción serial
                         // i_old es una variable auxiliar, para saber si ya
                         // pase por el análisis del buffer
    char    buffer[16];// buffer de recepeción serial
    int8    servo, pos;
    int8    aux;// sirve para manejar el overrun, vacía buffer del pic
    int1    band=0;// band=0 => recibir índice del servo
    int1    mensj1=0, mensj2=0;// Son banderas para saber si se escribieron
                           // los mensajes 'Servo= ' y 'Pos= '                     
    int1    inicio=0;

    set_tris_b(0x00);
    set_tris_c(0b10000000);
    set_tris_d(0x00);
   
    disable_interrupts(INT_TIMER1);
    disable_interrupts(GLOBAL);   

    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_INTERNAL | T1_DIV_BY_1);

    printf("*** Programa para controlar 16 Sevos ***\r\n");
    printf("*** Via Serial RS232                 ***\r\n");
    printf("*** Realizado por Guillermo Nunez    ***\r\n");
    printf("*** Para el foro TODOPIC ;-)         ***\r\n");
    printf("*** Fecha: 24/03/08                  ***\r\n");
    printf("\r\n");
    printf("Version 1.3\r\n");
    printf("Servo=   [0..15]\r\n");
    printf("Pos=   [%d..%d]\r\n", izquierda, derecha);


   while (TRUE)
   {
// Si se reciben más de 2 bytes sin haberlos procesado, se produce un error
// de Overrun. En este caso, se borra el error reiniciando CREN para que nuestro pic
// no se cuelgue
         if (OERR)
         {
            aux=RCREG;
            aux=RCREG;
            aux=RCREG;
            CREN=0;
            CREN=1;
            printf("Error:\r\n");
            printf("Envíe de nuevo los datos\r\n");
            i=0;
            mensj1=0;
            band=0;
         }
// Recepción de datos via rs232
         if (kbhit()){
               buffer[i]=getc();
               i++;
         }
// Escribe los mensajes si no lo has hecho
         if (band==0){
            if (mensj1==0){
               printf("\r\nServo= ");
               mensj1=1;
            } 
         }
         else{
            if (mensj2==0){
               printf("\r\nPos= ");
               mensj2=1;
            }
         }
         
// Procesa los datos recibidos
         if (i_old!=i){
            if ((!isdigit(buffer[i-1])) && (buffer[i-1]!=13)) { //Valida los datos
               i--;// Lo sacamos del buffer y no se escribe en pantalla
            }
            else{
               if (band==0){// Estamos recibiendo el índice del Servo
                  if (i>1){// si ya se recibieron 2 datos
                     i=0;                     
                     if (buffer[1]!=13){// Pregunto si el segundo dato es
                                        // Distinto a ENTER
                        putc(buffer[1]);
                        servo=servo*10+buffer[1]-0x30;
                        if (servo>15){
                           printf("\r\nIngrese un valor entre 0 y 15\r\n");
                           mensj1=0;
                        }
                        else{
                           band=1;
                           mensj2=0;
                        }
                     }
                     else{// Si el 2do dato es ENTER
                        band=1;
                        mensj2=0;
                     }
                  }
                  else{// Se ha recibido 1 dato
                     if (buffer[0]!=13){// Verificamos que no sea ENTER
                        servo=buffer[0]-0x30;
                        putc(buffer[0]);
                     }
                     else i=0;// No nos interesa un ENTER, así que lo sacamos
                              // del buffer
                  }
               }
               else{// Estamos recibiendo la posición
                  switch (i){
                 
                     case 1:// Hemos recibido 1 dato
                        if (buffer[0]!=13){// Verificamos que no sea ENTER
                           pos=buffer[0]-0x30;
                           putc(buffer[0]);
                        }
                        else i=0;// No nos interesa un ENTER, así que lo sacamos
                                 // del buffer
                     break;
                     
                     case 2:// Hemos recibido 2 datos
                       
                        if (buffer[1]!=13){// Verificamos que no sea ENTER
                           pos=pos*10+buffer[1]-0x30;
                           putc(buffer[1]);
                        }
                        else{  // Terminamos de recibir datos
                           printf("\r\nIngrese un valor entre %d y %d\r\n", izquierda, derecha);
                           i=0;
                           mensj2=0;
                        }
                     break;
                     
                     case 3:// Hemos recibido 3 datos
                       
                        i=0;
                        if (buffer[2]!=13){// Verificamos que no sea ENTER
                           putc(buffer[2]);
                           pos=pos*10+buffer[2]-0x30;
                           if ((pos>=izquierda) && (pos<=derecha)){
                              duty[servo]=pos;
                              mensj1=0;
                              band=0;
                              printf("\r\nDato enviado! \r\n");
                           }
                           else{
                              printf("\r\nIngrese un valor entre %d y %d\r\n", izquierda, derecha);
                              mensj2=0;
                           }
                        }
                        else{
                           if (pos>=izquierda){
                              duty[servo]=pos;
                              mensj1=0;
                              band=0;
                              printf("\r\nDato enviado! \r\n");
                           }
                           else{
                              printf("\r\nIngrese un valor entre %d y %d\r\n", izquierda, derecha);
                              mensj2=0;
                           }
                        }
                     break;
                  }
               }
            }
            i_old=i;
         }

         if (inicio==0){
            inicio=1;
            set_timer1(resto);
            enable_interrupts(INT_TIMER1);
            enable_interrupts(GLOBAL);
        }
   }
}

La precisión actual es de 2,25º para cada servo, aunque se puede llegar hasta 1,8º colocando la interrupción cada 20us, pero no sé para que serviría  :?

PD1: Éste código lo diseñé yo Guillermo Núñez, y ahora es propiedad de todo el mundo y en especial del foro Todopic, así que sientanse libres de agregar, quitar, copiar ó hacer lo que quieran con él, tampoco hace falta que me nombren o mencionen en ningún lado, mi satisfacción ya está cumplida y fué el poder mover estos servos y compartirlo con todos ustedes, pero si me quieren nombrar no problem, adelante.

PD2: Cuando tenga tiempo colocaré, como quedaría el código para manejar más servos, siguiendo el mismo principio.

PD3: Toda prueba que hagan con él es bajo su propio riesgo, hasta los momentos no ha sido verificado en real, solo en simulación.

PD4: Como todo programa en sus inicios, este seguro que tiene sus detalles, cualquier comentario u observación hacermelo saber, gracias.

Saludos. 8)
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: JLIGUAL en 18 de Abril de 2008, 18:32:56
Estimado Gu1llermo:


Este si que es el espiritu del foro, crear pòr diversion y ponerlo a disposicion del publico en general sin ni siquiera pedir que se incluya tu nombre junto a tu creaccion, espero poder disfrutar en breve de tu creacion y si incluire tu nombre junto al programa de ejecucion.

un saludo

Juan Luis Igual
 
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: juankho en 06 de Mayo de 2009, 17:32:08
buenas tardes compañeros, estoy trabajando sobre este explendido programa.  mi necesidad es controlar simplemete dos servos por usb para un brazo robotico; pero con solo dos servos no consigo que me trabaje bien, tengo claro que es por a quello de cuadras los tiempos,, pero no logro consegurilo.
estoy algo verde para esto  :mrgreen:
gracias a cualque compañero del foro que me pueda colaborar
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: sirias52 en 23 de Octubre de 2011, 20:09:45
Hola que tal guillermo, tengo una pequeña duda sobre tu programa (el que logras 81 pasos para el servo con el 18F4550) por que divides (1/12MHZ)*4 no deberia ser entre 48MHZ?
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: adriandroid en 23 de Septiembre de 2012, 22:32:22
olle bro un preguntota cuano lo hago funcionar con las lines para respetar el botloader en la simulacion no me funcionana los servos solo lo hacen son bootloader   si lo programa con bootloader si funcionara?
Título: Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: Thulsa Doom en 25 de Marzo de 2015, 19:18:10
Hola a todos, sé que es un tema viejo pero buscando la manera de hacer los tiempos más precisos llegué hasta aquí y la verdad que lo que he leído está muy pero que muy bien, lo que faltan son las fotos.

a ver si alguien puede recuperar este manual con las imágenes para verlo más claro.

gracias de ante mano
Título: Re:Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
Publicado por: akrs en 12 de Octubre de 2015, 04:04:15
los link con la simulacion ya no funcionan podrias resubirlos ?