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

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

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Versión 2 (solo pic18f)
« Respuesta #15 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:



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.
« Última modificación: 19 de Marzo de 2008, 23:45:02 por gu1llermo »

Desconectado mandoanaiz

  • PIC10
  • *
  • Mensajes: 47
Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
« Respuesta #16 en: 20 de Marzo de 2008, 07:52:57 »
Excelente trabajo :-/ :-/
Andaba yo pienseando algo parecido, gracias por compartirlo. :lol: :lol:

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción del timer0 (PIC18 y PIC1
« Respuesta #17 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.

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
« Respuesta #18 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.

« Última modificación: 20 de Marzo de 2008, 15:12:00 por gu1llermo »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Excelentes noticias!, lo he logrado
« Respuesta #19 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:



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. :)
« Última modificación: 21 de Marzo de 2008, 02:58:27 por gu1llermo »


Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
« Respuesta #21 en: 21 de Marzo de 2008, 15:16:25 »
Respuesta a pocher

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



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.
« Última modificación: 21 de Marzo de 2008, 18:44:35 por gu1llermo »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
« Respuesta #22 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.
« Última modificación: 21 de Marzo de 2008, 15:36:22 por gu1llermo »

Desconectado pocher

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 2568
Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
« Respuesta #23 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.

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
« Respuesta #24 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:


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.
« Última modificación: 22 de Marzo de 2008, 05:45:09 por gu1llermo »

Desconectado pocher

  • Moderador Local
  • DsPIC30
  • *****
  • Mensajes: 2568
Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
« Respuesta #25 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.

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
« Respuesta #26 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)
« Última modificación: 22 de Marzo de 2008, 15:04:17 por gu1llermo »

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
« Respuesta #27 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.
« Última modificación: 24 de Marzo de 2008, 20:16:38 por gu1llermo »

Desconectado Cryn

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 4169
Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
« Respuesta #28 en: 13 de Abril de 2008, 00:57:26 »
 :shock: guay qeu bonito trabajo haz hecho gu1llermo :-/

felicidades ha qeudado muy bueno :mrgreen:
.

Desconectado gu1llermo

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 217
Re: Programa para controlar X servos con 1 interrupción (PIC18 y PIC16)
« Respuesta #29 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)
« Última modificación: 13 de Abril de 2008, 05:14:39 por gu1llermo »


 

anything