TODOPIC

Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: RedPic en 03 de Febrero de 2008, 00:14:35

Título: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 03 de Febrero de 2008, 00:14:35
Preámbulo:

Primero deciros que estaba dudando entre postear este hilo aquí, en el subforo de C, o hacerlo en el de Robótica ya que en el fondo lo que deseo describir es un algoritmo, una idea de cómo hacer algo mas que una implementación de esa idea en un idioma concreto, pero al final me he decidido por éste ya que es mi entorno habitual y el foro de Robótica lo frecuento poco, mas que nada por mi desconocimiento del tema aunque poco a poco iré entrando mas en él.

Por ahí tengo ya posteados algunos hilos dedicados a este tema de los servos y los PWM's, concretamente recuerdo Controlando un SERVO con el PIC desde nuestro PC (http://www.todopic.com.ar/foros/index.php?topic=4629.0),  El Termo-Servo o un servo controlado por temperatura. (http://www.todopic.com.ar/foros/index.php?topic=4746.0) o Proyecto Radiocontrol : Receptor RC asistido por PIC (http://www.todopic.com.ar/foros/index.php?topic=20232.0) ...

Todos ellos han tratado de un único servo, apuntando algunas ideas para poder manejar mayor cantidad de ellos, pero sin concretar nada sobre dicha posibilidad. Como alguno de ustedes sabéis estoy empeñado en cierto proyecto de temas de RC (radiocontrol) cuyas repercusiones en el foro las tenéis en los hilos Proyecto de Barco a Vela radiocontrolado (http://www.todopic.com.ar/foros/index.php?topic=19703.0) o el ¿Conocéis módulos RF? (http://www.todopic.com.ar/foros/index.php?topic=19938.0). En cualquiera de ellos se prevee manejar no uno sino muchos servos, así que ahora ha llegado el momento de acometer el tema.

La idea de cómo hacerlo la he tomado prestada de la gente de IEArobotics (http://www.iearobotics.com/) y quiero dejar aquí constancia de ello y de mi público agradecimiento.

Introducción:

No vamos a entrar en una descripción detallada de cómo manejar un servo, para ello a quien le interese puede visitar uno de los hilos anteriormente mencionados, pero vamos a dar al menos un somero repaso al tema.

Un servo, de los que usamos los hobbystas, se alimenta con unos 5V y se controla mediante una señal PWM con una frecuencia de 50 Hz, o sea con un periodo de 20 ms, y con un Duty Cycle que varía entre el 6% y el 15%, o sea entre un periodo en alto de unos 0.5 ms a 2.5 ms (estos valores pueden cambiar según el modelo concreto de servo que utilicemos, los Hitec van desde los 0.5 a los 2.5 ms y los Futaba desde los 0.3 a los 2.3 ms aproximadamente)

Con un pulso de unos 0.7 ms el servo se posiciona en un extremo de su recorrido, con un pulso de unos 2.3 ms el servo se posiciona en el extremo contrario y con un pulso intermedio, de unos 1.5 ms, se posiciona en el centro de su recorrido.

Aquí tenéis un cronograma de cómo reacciona un servo ante estos tipos de pulsos PWM:

(http://picmania.garcia-cuervo.net/images/servos_x_8_en_c_cronograma_1_min_max.jpg)

Implementar la generación de un solo pulso PWM no es cuestión difícil y en los hilos mencionados antes tenéis formas sencillas de hacerlo.

La cosa sin embargo se complica conforme vamos acumulando una tras otra la necesidad  de controlar mas y mas servos. Y todo es un problema de tiempos, conforme mas servos vamos intentando controlar nuestras rutinas se van complicando, ocupando mas tiempo de proceso, y empiezan a perder la precisión necesaria en la generación de los pulsos de cada uno de los servos. Éstos empiezan a temblar y a hacer cosas "raras", vibran, tiemblan, dan saltos ... y todo producido por una inestabilidad en los anchos de los pulsos que le llegan.

Es mi intención en este hilo la de presentaros una forma económica en recursos y tiempos de proceso, en el PIC para controlar hasta 8 servos de forma estable y precisa, haciendo además uso de una única interrupción: La del desborde del Timer1 (rodando a 16 bits de precisión).

Descripción del algoritmo:

Para describiros el algoritmo tenemos que hacer un uso intensivo de la calculadora. El primer dato a tener en cuenta es la frecuencia del pulso PWM de los servos, que como dijimos anteriormente es de 50 Hz. Esto significa que cada pulso a cada servo tiene un periodo, o tiempo que transcurre entre dos flancos sucesivos:

[ latex ]T=\frac{1}{F}=\frac{1}{50}=0,02s=20ms[ /latex ].

Este periodo lo podemos dividir entre 8 para obtener lo que vamos a denominar una "ventana" para cada uno de los servos.

[ latex ]W=\frac{20}{8}=2.5ms[ /latex ].

Dentro de cada una de estas "ventanas" de tiempo de 2.5 ms de duración vamos a controlar un solo servo, pero uno tras otro hasta completar los 8 que son nuestro objetivo. Esto significa que independientemente del tiempo que esté en alto el pulso de un servo, recordad que decíamos que podía ser entre 0.7 ms y 2.3 ms, cada 2.5 ms ponemos en alto el siguiente servo, y como han transcurrido 2.5 ms desde la puesta en alto del anterior éste ya debe de haber sido bajado porque ningún pulso en alto puede sobrepasar los 2.3 ms.

Esto hace que dentro de cada 2.5 ms se pone en alto y después en bajo la señal de un servo. Como son 8 servos y por lo tanto 8 "ventanas" de 2.5 ms, a cada servo le tocará de nuevo ponerle la señal en alto 8 * 2.5 ms = 20 ms. O sea el periodo correspondiente a la frecuencia de 50 Hz que necesita.

Esto puede verse de forma mucho mas clara mediante el siguiente cronograma:

(http://picmania.garcia-cuervo.net/images/servos_x_8_en_c_cronograma_8.jpg)

Fijaos cómo las señales de cada uno de los servos se van generando una tras otra, de forma sucesiva, pero separadas cada una de la siguiente los mismos 2.5 ms hasta completar los 20 ms con los 8 servos, y cómo independiente de en qué momento se ponga en alto uno de ellos individualmente, a los 20 ms vuelve a tocarle a ese mismo ponerse en alto.

Ahora la cuestión pasa por generar los "timmings" necesarios para ser capaces de producir este tren de pulsos de ancho controlado.

Recursos a utilizar:

Como os puse mas arriba el principal recurso a utilizar es una única interrupción, la del desborde del Timer1. Pero antes debemos tratar otro tema relacionado con éste.

Una idea que a nadie se le debe escapar, pero que por mas obvia que sea no voy a dejar de mencionar, es la de que hace falta un PIC razonablemente rápido. Con un pulso al extremo, de los 2.3 ms, vamos a tener sólo 0.2 ms para detectar el fin del pulso y ponerlo a bajo. No es mi intención calcular qué frecuencia FOSC mínima podríamos utilizar, he escogido un PIC que puede volar a 20 Mhz con el que tenemos mas que suficiente y si deseáis probar con frecuencias menores os rogaría que me hicieseis partícipe de los resultados.

Con un cristal de 20 Mhz y el Timer1 configurado para como contador de 16 bits, con prescaler a 1:1 tenemos los siguientes "timmings" :

(http://picmania.garcia-cuervo.net/images/servos_x_8_en_c_calculo_timer1.gif)

El dato fundamental es el del tiempo de incremento de un Tick del Timer1, que resulta ser de 0.2 uS, o lo que es lo mimo 0.0002 ms.

Podemos así expresar en esta unidad de Ticks los tiempos en alto de cada uno de los servos. Si todos tuviesen que estar en el centro de su recorrido sus PWM deberían de ser de 1.5 ms en alto:

Tx=\frac{1.5ms}{0,0002ms}=7.500Ticks

Así si habilitamos la interrupción por desbordamiento del Timer1 la primera vez que desborde ponemos en alto el pin correspondiente al Servo número 1 y precargamos el valor del Timer1 con 7.500 ticks antes del desborde, que se produce a los 65.535 ticks, con lo que ponemos el Timer1 a 65.535 - 7.500 = 58.035, de esta forma obtendremos la siguiente interrupción cuando transcurran esos 7.500 ticks.

Cuando la interrupción salta de nuevo y entramos por segunda vez tenemos que prefijar entonces el tiempo que ha de transcurrir para completar la ventana de 2.5 ms hasta el comienzo del control del siguiente servo. Como en nuestro ejemplo hemos utilizado un valor de 1.5 ms ó 7.500 Ticks tendremos ahora que esperar 2.5 ms - 1.5 ms = 1 ms por lo que vamos a precargar de nuevo el Timer1 con 65.535 - 5.000 = 60.535 y nuestra interrupción saltará de nuevo transcurrido 1 ms.

Ahora le toca al siguiente Servo con el que procederemos de la misma forma: ponemos en alto el pin del servo 2, precargamos el Timer1 restando al desborde en numero de ticks correspondiente a su ancho de pulso, esperamos la siguiente interrupción en la que ponemos en bajo el pin del servo y precargamos de nuevo con los ticks necesarios para completar los 2.5 ms de su ventana y de nuevo a empezar pero con el siguiente servo.

Esta es la idea fundamental. Cada dos interrupciones se controla completamente un Servo y entre ambas transcurre exactamente 2.5 ms, independiente del estado en alto que tenga que tener cada servo, cada 16 interrupciones se vuelve de nuevo al principio, al primer servo, y han transcurrido exactamente 20 ms.

Con una simple tabla de 8 enteros de 16 bits para guardar el número de ticks que tiene que estar en alto cada pulso de cada servo y con dos simples cálculos aritméticos de suma y resta en una única interrupción tenemos perfectamente controlados hasto 8 servos.

Implementación en CCS C:

Para realizar la implementación de este algoritmo en C comenzaremos por comentar algunos detalles importantes.

Vamos a usar la directiva #use fast_io(X) y fijar el funcionamiento de los pines con el set_tris_x(). Esto hace que el compilador no incluya los set_tris() automáticamente cada vez que se encuentra un output_low() o output_high() con lo que quitaremos instrucciones innecesarias de en medio y ganaremos en velocidad y precisión en nuestro programa.

En el ejemplo que he preparado interactuo con el PIC mediante el canal serie del mismo con un MAX232. Así que he habilitado también la interrupción por recepción serie. Como los tiempos, "timmings" les llamábamos, son fundamentales en esta aplicación vamos también a usar la directiva #priority timer1,rda que nos va a hacer prioritaria la interrupción del timer1 sobre la de recepción serie. Ante la duda el Timer1 gana y nosotros con él en estabilidad y precisión.

He creado una tabla int16 Servo_PWM[8] en la que guardo los Ticks de ancho que tienen que durar cada uno de los pulsos de cada uno de los servos. Son los necesarios para restárselos a 65.535 tras inicio de cada pulso y para sumárselos al ancho de la ventana tras su final para colocar la interrupción al inicio del siguiente pulso.

Como el inicio de un pulso de servo siempre llega con una interrupción impar y el final de ese mismo pulso llega con la interrupción par me he creado un int1 flag_Phase que va a hacerme saber si estoy al comienzo o al final de cada pulso.

Si estoy al final, flag_Phase=1, entonces incremento el número del siguiente Servo que me toca tratar y que será al que le tengo que levantar la señal en la siguiente interrupción. Si al incrementar el siguiente servo es mayor que el último vuelvo a ponerlo a 0 y recomienzo de nuevo con el primero.

El programa queda tal como sigue:

Código: C#
  1. #include <18f1320.h>
  2. #fuses HS,NOMCLR,PUT,NOWDT,NOPROTECT,BROWNOUT,BORV45,NOLVP,NOCPD,NODEBUG,NOWRT
  3. #use delay(clock=20000000)
  4. #use rs232(baud=115200, xmit=PIN_B1, rcv=PIN_B4)
  5. #use fast_io(A)
  6. #use fast_io(B)
  7.  
  8. #priority timer1,rda
  9.  
  10. #define SERVO1 PIN_B5
  11. #define SERVO2 PIN_B3
  12. #define SERVO3 PIN_A0
  13. #define SERVO4 PIN_A4
  14. #define SERVO5 PIN_B2
  15. #define SERVO6 PIN_A3
  16. #define SERVO7 PIN_B0
  17. #define SERVO8 PIN_B5
  18.  
  19. const int16  Ticks4Window  = 12500;  // PWM Window for servo = 2.5 ms x 8 = 20 ms
  20. const int16  Ticks4Minimum =  3500;  // PWM High for Minimum Position = 0.7 ms  
  21. const int16  Ticks4Center  =  7500;  // PWM High for Center  Position = 1.5 ms  
  22. const int16  Ticks4Maximum = 11500;  // PWM High for Maximum Position = 2.3 ms  
  23.  
  24. static char  command;
  25. static int16 Servo_PWM[8]={Ticks4Center,Ticks4Center,Ticks4Center,Ticks4Center,0,0,0,0};
  26. static int8  Servo_Idx=0;
  27. static int1  SERVO1_ON=1;
  28. static int1  SERVO2_ON=1;
  29. static int1  SERVO3_ON=1;
  30. static int1  SERVO4_ON=1;
  31. static int1  SERVO5_ON=0;
  32. static int1  SERVO6_ON=0;
  33. static int1  SERVO7_ON=0;
  34. static int1  SERVO8_ON=0;
  35. static int1  flag_Phase;
  36. static int16 Ticks4NextInterrupt=53036;
  37.  
  38. #int_rda
  39. void serial_isr(void){
  40.  
  41.    if(kbhit()){
  42.       command=getc();
  43.    }
  44. }
  45.  
  46. #int_timer1
  47. void timer1_isr(void){
  48.  
  49.    if(flag_Phase==0){
  50.       if(Servo_Idx==0 && SERVO1_ON) output_high(SERVO1);
  51.       if(Servo_Idx==1 && SERVO2_ON) output_high(SERVO2);
  52.       if(Servo_Idx==2 && SERVO3_ON) output_high(SERVO3);
  53.       if(Servo_Idx==3 && SERVO4_ON) output_high(SERVO4);
  54.       if(Servo_Idx==4 && SERVO5_ON) output_high(SERVO5);
  55.       if(Servo_Idx==5 && SERVO6_ON) output_high(SERVO6);
  56.       if(Servo_Idx==6 && SERVO7_ON) output_high(SERVO7);
  57.       if(Servo_Idx==7 && SERVO8_ON) output_high(SERVO8);
  58.       Ticks4NextInterrupt = 65535 - Servo_PWM[Servo_Idx];
  59.       set_timer1(Ticks4NextInterrupt);
  60.    }
  61.    if(flag_Phase==1){
  62.       if(Servo_Idx==0 && SERVO1_ON) output_low(SERVO1);
  63.       if(Servo_Idx==1 && SERVO2_ON) output_low(SERVO2);
  64.       if(Servo_Idx==2 && SERVO3_ON) output_low(SERVO3);
  65.       if(Servo_Idx==3 && SERVO4_ON) output_low(SERVO4);
  66.       if(Servo_Idx==4 && SERVO5_ON) output_low(SERVO5);
  67.       if(Servo_Idx==5 && SERVO6_ON) output_low(SERVO6);
  68.       if(Servo_Idx==6 && SERVO7_ON) output_low(SERVO7);
  69.       if(Servo_Idx==7 && SERVO8_ON) output_low(SERVO8);
  70.       Ticks4NextInterrupt = 65535 - Ticks4Window + Servo_PWM[Servo_Idx];
  71.       set_timer1(Ticks4NextInterrupt);
  72.       if(++Servo_Idx>7) Servo_Idx=0;
  73.    }
  74.    ++flag_Phase;
  75. }
  76.  
  77. void pres_menu(void){
  78.  
  79.    printf("\r\nA 18F1320 listen on RS-232");
  80.    printf("\r\nEight Servos Control Algorithm");
  81.    printf("\r\n");
  82.    printf("\r\n[?] This menu");
  83.    printf("\r\n[I] All to Minimum");
  84.    printf("\r\n[C] All to Center");
  85.    printf("\r\n[X] All to Maximum");
  86.    printf("\r\n[+] Step to front");
  87.    printf("\r\n[-] Step to back");
  88.    printf("\r\n\n>");
  89.  
  90. }
  91.  
  92. void main(void) {
  93.  
  94.    int1 valid_command;
  95.    int8 i;
  96.  
  97.    disable_interrupts(global);
  98.    setup_adc_ports(NO_ANALOGS);
  99.    setup_adc(ADC_OFF);
  100.    setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
  101.    setup_timer_0(RTCC_OFF);
  102.    setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
  103.    setup_timer_2(T2_DISABLED,0,1);
  104.    setup_timer_3(T3_DISABLED);
  105.    port_b_pullups(FALSE);
  106.  
  107.    set_tris_a(0b00000000);
  108.    set_tris_b(0b00010000);
  109.  
  110.    output_low(SERVO1);
  111.    output_low(SERVO2);
  112.    output_low(SERVO3);
  113.    output_low(SERVO4);
  114.    output_low(SERVO5);
  115.    output_low(SERVO6);
  116.    output_low(SERVO7);
  117.    output_low(SERVO8);
  118.  
  119.    delay_ms(1000);
  120.  
  121.    command='\0';
  122.  
  123.    enable_interrupts(int_rda);
  124.    set_timer1(Ticks4NextInterrupt);
  125.    enable_interrupts(int_timer1);
  126.    enable_interrupts(global);
  127.  
  128.    pres_menu();
  129.  
  130.    do {
  131.       // Comandos serie
  132.       if(command!='\0'){
  133.          command=toupper(command);
  134.          valid_command=0;
  135.          printf("%c\r\n>",command);
  136.  
  137.          if(command=='?'){
  138.             pres_menu();
  139.             valid_command=1;
  140.          }
  141.          if(command=='I'){
  142.             printf("> All to Minimum\r\n>");
  143.             for(i=0;i<4;i++) Servo_PWM[i]=Ticks4Minimum;
  144.             valid_command=1;
  145.          }
  146.          if(command=='C'){
  147.             printf("> All to Center\r\n>");
  148.             for(i=0;i<4;i++) Servo_PWM[i]=Ticks4Center;
  149.             valid_command=1;
  150.          }
  151.          if(command=='X'){
  152.             printf("> All to Maximum\r\n>");
  153.             for(i=0;i<4;i++) Servo_PWM[i]=Ticks4Maximum;
  154.             valid_command=1;
  155.          }
  156.          if(command=='+'){
  157.             printf("> Step to front\r\n>");
  158.             for(i=0;i<4;i++) Servo_PWM[i]+=80;
  159.             valid_command=1;
  160.          }
  161.          if(command=='-'){
  162.             printf("> Step to back\r\n>");
  163.             for(i=0;i<4;i++) Servo_PWM[i]-=80;
  164.             valid_command=1;
  165.          }
  166.  
  167.          if(!valid_command) printf("?\r\n>");
  168.          command='\0';
  169.       }
  170.  
  171.    } while (TRUE);
  172. }

Mi montaje funcionando:

Solo tengo 4 servos disponibles así que solo 4 le he conectado físicamente, pero el programa contempla los 8 aunque los 4 últimos no los secuencia en dos interrupciones sino que una de ellas salta inmediatamente y es la segunda la que hace todo el recorrido de la ventana de esos servos.

(http://picmania.garcia-cuervo.net/images/servos_x_8_en_c_foto.jpg)

Y este es el pequeño menú que me he preparado para controlar su funcionamiento:

(http://picmania.garcia-cuervo.net/images/servos_x_8_en_c_rs232_dump.gif)

Os aseguro que los servos se desplazan suavemente, sin vibraciones ni temblores, entre ambos extremos siguiendo puntualmente las ordenes que les mando desde el PC.

En este menú, como véis, manejo todos los servos a la vez, de hecho lo que hago es solo cargar la tabla Servo_PWM con los valores extremos y central y dejo que la interrupción haga el resto. Y la verdad es que va absolutamente de lujo.

Era una espinita que tenía clavada hace tiempo y que por fin he podido quitarme. Y quería compartirlo con todos ustedes.

Ea, ya está bien por hoy. Mañana más.  :mrgreen:








Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Thanathos en 03 de Febrero de 2008, 01:13:26
Ante todo saludos estimado REDPIC, si a alguien debo lo que se es a ti.
Definitivamente otra vez me dejaste sin palabras...

Me voy a poner a analizar tu codigo, lo mire asi rapidito y tiene cosas de verdad interezantes...

1 pregunta, ¿Que Porcentaje de maquina consume el programa?, es que hace unos dias hice un programa igual para 8 servos con un 877 y la verdad gastaba maquina.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: darklord en 03 de Febrero de 2008, 02:43:23
Muy bueno!!!  :shock:

He seguido tus avances en tu página Diego, y me gusta mucho tu forma de escribir tanto el código como los artículos, todo muy bien estructurado. Estaba buscando algoritmos para controlar servos y di con tu artículo, voy a analizarlo, pero ante todo, gracias por compartirlo.

No te ha pasado por la cabeza la idea de un controlador de servos USB?

Recibe un coordial saludo.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Nocturno en 03 de Febrero de 2008, 05:04:40
Don Diego, felicidades por un nuevo magnífico artículo.

Dices que cuando se solapan varios servos en el mismo pulso ascendente, para luego bajar cuando a cada uno le corresponda, has observado vibraciones extrañas.
Aunque me encanta la forma de resolverlo que has tenido, reservando una parcela de tiempo exclusiva para cada uno, yo sospecho que la velocidad del PIC es más que suficiente para manejar con soltura una situación como la planteada.
¿No crees que existe la posibilidad de que la causa fuera otra?, ¿estuviste peleando mucho con esa idea hasta abandonarla?
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 03 de Febrero de 2008, 07:32:12
El tema tal como lo tenía anteriormente planteado tenía un fallo evidente con el ejemplo simple de todos los servos a la misma posición. Tenía la interrupción de Timer1 que solo servía para generar un pulso cada 20 ms, precargando el timer con el valor correspondiente, y poniendo en alto todas las señales de todos los servos.

Y después en el main() e independiente de la velocidad a la que corriese el PIC tenia la batería de comparaciones de tiempos transcurridos para cada servo, en caso de sobrepasar su valor debía ponerlos a bajo. Esto llevaba a la paradoja de que si todos los servos estaban en la misma posición todos las señales debían caer al mismo tiempo, cosa a todas luces imposible.

Y también, si los servos tenían posiciones cercanas pero no iguales, lo suficientemente cerca como para depender del tiempo de proceso del que le precedía o del que le sucedía en el flujo del programa resultaba que la estabilidad absoluta de un servo dependía de la posición de sus vecinos.

No, no intenté seguir por ahí porque me daba cuenta de que jamás iba a conseguir precisión.

Deberías ver ahora como responden estos al aumento tick a tick del ancho de pulso, es casi inapreciable el movimiento que produce, suave y firme.  :mrgreen:

Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 03 de Febrero de 2008, 07:36:48

No te ha pasado por la cabeza la idea de un controlador de servos USB?


Si, claro que sí. Pero el tema es que el método por que le haga llegar las posiciones, o mejor dicho lo valores de las posiciones a la memoria del PIC es irrelevante y no necesito una velocidad tan grande como el USB me puede brindar.

Además la aplicación para la que estamos investigando todo esto de los servos, y otras cosas como los motores DC o PaP, es para ser radiocontrolados en un modelo naval y ahí va a ser difícil usar el USB.  :mrgreen: 
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Nocturno en 03 de Febrero de 2008, 07:47:57
Abundando en la idea, y sin ánimo de darte la coña más, porque ya lo tienes solucionado.

Imagina que en el momento de levantar las señales haces un "if" con condición TRUE por cada servo lo cual los levantará todos, pero con un miniretraso entre ellos que corresponde al tiempo que tarda el pic en pasar de cada uno al siguiente.

Y ahora en el momento de bajarlas haces lo mismo, un "if" para cada servo que introducirá a cada uno un miniretraso equivalente al que usaste al levantarlas.

En teoría, sería como si pones ese diagrama de tiempos tan chulo que has pintado, pero con las señales "casi" superpuestas, sólo decaladas por ese miniretraso que comentaba antes.

¿No crees que funcionaría?
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 03 de Febrero de 2008, 08:02:11
Si, debería funcionar perfectamente. De echo es similar a lo que estoy haciendo con la interrupción salvo que la "ventana" la definen los if() consecutivos de subida y bajada.

Pero y es el único pero que le encuentro, esta en el resto del programa. Me decidí a poner la interrupción  enlazada servo tras servo para tener todo el main() para mi solo, como la cama cuando mi esposa se va a ver a la nieta.

Así en el main() puedo hacer y deshacer cálculos todo lo complejo que quiera sabiendo siempre que los servos están atendidos correctamente. Cuanto termino un cálculo actualizo el doble-byte de ese servo y sé que cuando le toque se irá a la posición adecuada, o puedo estar sensando otros puertos o dedicado al tema de las comunicaciones mientras el sistema de pulsos PWM está en "automático".
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Nocturno en 03 de Febrero de 2008, 08:12:38
Perfecto. Que sepas que has conseguido incrementar en una unidad mi pila de insatisfacciones, en forma de proyectos que me gustaría hacer y no tengo tiempo.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 03 de Febrero de 2008, 08:21:00
Tú bien sabes que yo tampoco tengo tiempo, pero esto lo hago como medida terapéutica. O lo hago a plena satisfacción o caigo enfermo. De vez en cuando hay que hacer cosas como esta, no todo pueden ser Iacedeses finde tras finde.  :mrgreen:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: stk500 en 03 de Febrero de 2008, 08:26:12
Felicitaciones Diego por tu gran Clase como siempre  :-/ :-/

estupendo Servo Tutorial nos has dados muchas gracias  :-/ :-/
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: jfmateos2 en 03 de Febrero de 2008, 08:33:23
Enhorabuena RedPIC.

Me surge una duda al leer tu código. En lugar de usar este tipo de if consecutivos de los que, obviamente, no pueden ser ciertos más de uno a la vez, ¿en los PICs también sería más eficiente recurrir a estructuras switch...case o if...else if?

Código: [Seleccionar]
      if(Servo_Idx==0 && SERVO1_ON) output_high(SERVO1);
      if(Servo_Idx==1 && SERVO2_ON) output_high(SERVO2);
      if(Servo_Idx==2 && SERVO3_ON) output_high(SERVO3);
      if(Servo_Idx==3 && SERVO4_ON) output_high(SERVO4);
      if(Servo_Idx==4 && SERVO5_ON) output_high(SERVO5);
      if(Servo_Idx==5 && SERVO6_ON) output_high(SERVO6);
      if(Servo_Idx==6 && SERVO7_ON) output_high(SERVO7);
      if(Servo_Idx==7 && SERVO8_ON) output_high(SERVO8);
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 03 de Febrero de 2008, 09:01:36
Enhorabuena RedPIC.

Me surge una duda al leer tu código. En lugar de usar este tipo de if consecutivos de los que, obviamente, no pueden ser ciertos más de uno a la vez, ¿en los PICs también sería más eficiente recurrir a estructuras switch...case o if...else if?

Código: [Seleccionar]
      if(Servo_Idx==0 && SERVO1_ON) output_high(SERVO1);
      if(Servo_Idx==1 && SERVO2_ON) output_high(SERVO2);
      if(Servo_Idx==2 && SERVO3_ON) output_high(SERVO3);
      if(Servo_Idx==3 && SERVO4_ON) output_high(SERVO4);
      if(Servo_Idx==4 && SERVO5_ON) output_high(SERVO5);
      if(Servo_Idx==5 && SERVO6_ON) output_high(SERVO6);
      if(Servo_Idx==6 && SERVO7_ON) output_high(SERVO7);
      if(Servo_Idx==7 && SERVO8_ON) output_high(SERVO8);

Si. Así lo tenía antes.

Pero de este modo lo veía como mas bonito, mas estético.  :oops: :oops: :oops:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Nocturno en 03 de Febrero de 2008, 09:05:23
Me suena que se compilan igual los switch/case que los if/else.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 03 de Febrero de 2008, 09:09:07
No lo he comprobado pero la diferencia debe ser mínima.

Ademas el Switch solo switcheaba entre los servos y dentro de cada case tenía que poner un if() para saber si el servo ese estaba o no activo, asi que decidi poner solo el if().

Ademas asi la rutina siempre tarda lo mismo, mientras que con el switch() tarda menos con el primero y todo con el último. Que no se si tendrá importancia o no pero ahí está.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Marttyn en 04 de Febrero de 2008, 15:19:52
Perfecto Diego! Tu siempre con tu faceta de profesor haces que los demas aprendamos de manera amena. La verdad te felicito!
Bueno, creo que todos por aqui sabeis que los servos me envantan! el 50% de mis post son sobre ellos  :mrgreen:
Yo resolvi una situacion similar de manera similar... en uno de los interminables post sobre servos alguien habia hablado (supongo que sera a quien le das las gracias al principio) sobre lo que tu comentas de dividir en 8 el timer para poder controlar efectivamente cada servo...
A mi me ocurria lo mismo que a ti, los muy cabr**** vibraban!! llevo un año peleandome casi dia a dia con ellos, y lo que tu comentas es la mejor solucion que encontre hasta el momento.
La unica diferencia es que hay algo me libera al timer1 de tanta tarea, algo que tambien comentaban en ese post, y es el uso del CCP1 (que por supuesto aprendi a usar en uno de tus ejemplitos  :D)
El codigo lo tengo de la siguiente manera: el timer 1 esta dividido en 8 partes como el tuyo, pero SIEMPRE ocurre el overflow 2,5mS despues de haber comenzado, no le cargo la duracion del pulso en alto, PERO SI le cargo al CCP (en modo compare) el valor correspondiente para que el pulso de ese servo dure lo indicado. luego la interrupcion CCP se encarga de bajar el pulso cuando correspode.
en resumen, el timer1 sube el pulso y el ccp lo baja...
no se si esta manera tiene alguna desventaja o ventaja, pero por lo visto los a los dos nos funciona todo sin problemas, con una solucion muy parecida.
Un saludo y espero que sigas instruyendonos como hasta ahora!
Gracias otr vez!
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: jfh900 en 04 de Febrero de 2008, 15:31:52
Pues hay controladoras de 30 servos funcionando como está:

http://www.jvmbots.com/viewtopic.php?t=431&postdays=0&postorder=asc&start=0

Y creo que funcionan como comentaba Manolo. De la estabilidad no dice nada.

¿Manolo tu has implementado lo que comentaste?

Un saludo

Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Nocturno en 04 de Febrero de 2008, 15:38:58
No, no lo he probado, sólo pensaba en voz alta.
Pero conozco la controladora del amigo Julio, lo que demuestra que es posible.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Marttyn en 04 de Febrero de 2008, 15:59:07
por cierto diego, me haz levantado la curiosidad. despues de mas de un año metido en esto de los pics nunca he hecho nada con comunicacion rs232. asi que inmediatamente me meti en tu post Lo minimo para comunicaciones RS232 Hardware y software (http://www.todopic.com.ar/foros/index.php?topic=4620.0) mire lo que se necesita y ya me estoy llendo a la tienda de electronica para comprar un max232, 4 condensadores de 1uF y uno de 10uF  :mrgreen:
a ver que sale de todo esto!
algo es seguro... mientras mas cosas aprendo, mas ideas tengo, y mas cosas quiero hacer... y con la pila de cosas pendientes que tengo NO ES BUENO seguir aprendiendo cosas!  :D
salu2
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 04 de Febrero de 2008, 17:14:46
Ahora el amigo Pocher me ha mandado un par de códigos que el tenía para 8 y 16 Servos. El de 8 es también una forma de hacer lo que yo he hecho con el Timer1 escrito de otra forma. El de 16 tengo que estudiarlo para ver cual es su modo de funcionamiento.

Marttyn, si he visto tus progresos con esto de los servos y sabía que te interesaban hasta la neurosis. Sin embargo tu hilo de los servos "vibrantes" solo lo seguí al principio y no llegué a ver tu solución. Si lo deseas postea aquí tú codigo y una explicación de como lo haces con el CCP Compare.

Juanfran, mi código no sirve para mas de 8 servos, pero quiero probar con dos o tres interrupciones a 8 servos cada una hasta alcanzar 16 o 24 servos a ver que pasa con ellos. El problema es que físicamente solo tengo 5 servos y voy a tener que hacer como con lo de los 8 y que es ir moviéndolos por distintos pines para ver como soportan cada uno de ellos y comprobar si "tiemblan" o no.

Y hablando de "temblar". De los cinco Servos tengo uno que tiembla en el extremo de máxima señal a 2.3 ms. No es el canal al que lo conecte ya que intercambiándolo con cualquier otro sigue vibrando siempre el mismo y no el canal al que lo tenía puesto. Imagino que el pobre ya está viejo ya que tiene mas de 15 años y estuvo algunos años montado en un avión. Le tengo mucho cariño.   :mrgreen:

Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Marttyn en 04 de Febrero de 2008, 18:51:29
Y hablando de "temblar". De los cinco Servos tengo uno que tiembla en el extremo de máxima señal a 2.3 ms. No es el canal al que lo conecte ya que intercambiándolo con cualquier otro sigue vibrando siempre el mismo y no el canal al que lo tenía puesto. Imagino que el pobre ya está viejo ya que tiene mas de 15 años y estuvo algunos años montado en un avión. Le tengo mucho cariño.   :mrgreen:

yo tengo unos cuantos servos, algunos baratillos y otros de los caros, todos nuevos.... y hay varios (tanto caros como baratos) que vibran si el pulso esta en un extremo... yo creo que si un servo tiene un extremo teorico en 2.3mS, en la practica algunos lo tienen en 2.29mS y otros en 2.31mS... con lo que si nos pasamos de lo que el servo puede dar es cuando empieza a vibrar. o tambien, internamente tienen un tope, y si se le da un pulso que lleva el servo mas alla del tope lo que hace es chocar contra ese tope una y otra vez intentando moverse mas alla de el y es lo que genera la vibracion.
esto son solo teorias, porque la verdad que como ya dije tengo unos cuantos servos y no creo que vibren en el extremo porque esten viejos, sino porque estan mas alla de su limite, fisico o electronico.

a ver si tengo un momentito y pongo el codigo con el CCP compare, que lo tengo dentro de un programa mas grande y esta lleno de cosas que no interesan.
salu2!
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Marttyn en 05 de Febrero de 2008, 03:21:25
ya hice el circuito para comunicacion RS232, y WOOOOOW!!!  :shock:
esto eleva la programacion, depuracion y prueba de circuitos a otro nivel completamente distinto!
ahora jugar y programar servos va a ser cosa de niños!  :D
ya sabia que manolo tiene la capacidad de contagiarme las ganas con todos sus proyectos, veo que tu tambien diego!  :mrgreen:
salu2
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 05 de Febrero de 2008, 04:20:43
Me alegra muchísimo el poder haberte sido útil. Y tienes razón, con algún medio para poder monitorizar o controlar lo que ocurre en el PIC es todo mucho mas fácil. Es como una pequeña cucharada de azúcar en el café, lo hace mas digerible.  :mrgreen: :mrgreen: :mrgreen:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: J1M en 05 de Febrero de 2008, 11:14:05
Lo primero, enhorabuena por esa peazo controladora!!! Y pensar que tengo en mi casa 6 mini servos y una cámara esperando para ser juntados en armonía!!!

Una dudilla y aviso que la hago desde el más profundo de los desconocimientos en el tema. Sería posible generar las señales para controlar los servos de la misma forma en que se controlan los leds RGB por software? Aparece en una APPNote de Microchip pero no la encuentro, básicamente es el mismo método que utiliza Manolo en su artículo del led RGB. Supongo que sabéis al que me refiero, que ventajas tiene? inconvenientes?

Saludos y graciasssss por aguantar la plastada! :p
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Marttyn en 05 de Febrero de 2008, 15:04:29
Y tienes razón, con algún medio para poder monitorizar o controlar lo que ocurre en el PIC es todo mucho mas fácil.

Es como si supiera lo que piensa mi cucaracha en cada momenoto  :mrgreen:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 05 de Febrero de 2008, 17:06:52
Amigo Jaime:

Nunca he manejado los dichosos LEd's RGB pero debe ser mas sencillo que esto de los servos, ya que creo, supongo, barrunto, imagino que no están constreñidos a los dichosos 50 Hz de los sevos y se puede tener mas libertad al mandarles los PWM.

Pero doctores tiene la iglesia y seguro que nos iluminarán con su sapiencia.  :mrgreen:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Gorgonas en 05 de Febrero de 2008, 23:00:47
Pues los led RGB la esencia es parecida, pero el periodo se divide en los 256 valores posibles del color, que es el tiempo que esta en alto la onda, y eso multiplicado por 3 y aun por encima para que no produzca destellos, cuando el tiempo en alto de la onda es muy pequeño, hay que poner una frecuencia algo alta...
yo tuve que usar un reloj de 16 Mz para poder encender el led adecuadamente sin destellos, usar un lcd y controlar el menu. Con un reloj de menos frecuencia, el micro no daba a basto para atender las interrupciones del timer y controlar el resto de cosas adecuadamente.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 06 de Febrero de 2008, 03:12:52
Y tienes razón, con algún medio para poder monitorizar o controlar lo que ocurre en el PIC es todo mucho mas fácil.

Es como si supiera lo que piensa mi cucaracha en cada momenoto  :mrgreen:

Pues si trabajas con los 18F2550 ó 18F4550 rodando a 48 Mhz y hablando con el PC por USB a 2.0 Mhz de lo que eres capaz de ver. Es una pasada.  :mrgreen:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: jfh900 en 06 de Febrero de 2008, 10:16:43
Yo todas las controladoras que he visto llevan un micro dedicado solo para el control de los servos. Creo que sería una buena idea que hicierais lo mismo (no es tanto el coste y el proyecto lo merece).

Un saludo
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 06 de Febrero de 2008, 16:12:34
Si, Jesús, esa es mi idea. Un PIC-Satélite para cada 8 servos, dedicado en exclusiva a su control y gestión con sus propias funciones "macro" por si tiene que ejecutar movimientos complejos con cualquier grupo de ellos y con su batería de sensores, conectado al PCI-Cerebro mediante I2C o SPI en modo Esclavo-Maestro que se encarga de las comunicaciones de las funciones de alto nivel.  :mrgreen:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: juanelete en 09 de Febrero de 2008, 07:28:25
Hola a tod@s

Creo que se puede simplificar un poco el codigo utilizando bit_set() y bit_clear, en vez de output_high() y output_low() para poner el servo en alto y en bajo respectivamente, siempre que se disponga de un puerto completo para ello.

Ademas si el micro lo permite, creo que es mejor utilizar #int_CCPx para controlar el tiempo que esta en alto el servo, ya que hace que no sea tan critica la velocidad del micro y se pueden uilizar micros mas lentos.

Un ejemplo de como yo lo hago habitualmente:

Código: [Seleccionar]

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


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

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

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


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



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




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

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

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


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

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

            flag_timer1=FALSE;       
        }

    }
    while (TRUE);
}


Saludos a tod@s
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: AKENAFAB en 09 de Febrero de 2008, 08:38:18
HOla Redpic !

y HOla todos!!  :mrgreen:


Hace tiempo que realice mi controladora de servos , controlo de igual manera 8 servos , atendiendo cada servo de forma individual y de 8 pues sabemos qu eno da mas por el periodo.

Por falta de tiempo no he podido depurar el programa y realizar otro soft para poder manejar "X" servos.

Bueno aqui la idea:

Utilizar el método de la burbuja(consiste en ordenar n numeros de forma consecutiva, sea 1-2-3-15 'o 20-19-3-2-1),


ordenar el ciclo activo del servo de menor a mayor.
cargar el timer con el ciclo activo menor
esperar interrupción y verificar si 1 ó mas registros de posición de los servos son iguales y poner la señal de esos mismos a estado bajo.
tomar el siguiente valor y restarle el que termino.
repetir pasos.

No he programado nada espero tener un rato libre.

habría que checar también que tanto demora ese método en acomodar los datos y pues abríamos si es factible con nuestra controladora o esta muy fumado.

Hasta luego

Que estén bien!!

Atentamente
  Akenafab

Agregando

En teoria  :D , bueno bueno en mi mente soñadora  :D ... tenemos 2.5 ms por maximo que estarian trabajando los servos y 17.5 ms libres para realizar todas las operaciones necesarias.

Estoy zombie    :shock:...
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 09 de Febrero de 2008, 08:48:00
Muy interesante tu análisis, Akenafab, habrá que estudiarlo con mas detalle.

Lo único que se me ocurre, así sin pensar demasiado, es la situación en la que todos o casi todos los servos estén en valores muy próximos, sin ser iguales, y se requiera mucho proceso para ordenar dichos valores y haya muy poco tiempo para hacerlo, precisamente porque los servos estás como agrupados.

Pero la idea como te digo es muy interesante.  :mrgreen:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 09 de Febrero de 2008, 08:52:12

Creo que se puede simplificar un poco el codigo utilizando bit_set() y bit_clear, en vez de output_high() y output_low() para poner el servo en alto y en bajo respectivamente, siempre que se disponga de un puerto completo para ello.



Si, Juanelete, esa fué mi primera intención. Y como tú dices "siempre que se disponga de un puerto completo para ello", incluso llegué a sacar mi RRBOARD2 (http://www.todopic.com.ar/foros/index.php?topic=2300.0) en la que tengo puertos completos disponibles.

Pero me dije, y la verdad es que la realidad me dió después la razón, que lo normal es tener pines dispersos disponibles y que cualquier solución debía pasar por poder usar cualquier combinación de pines disponibles.

Otra cosa sería tener una variable de 8 bits con los estados de cada servo y en vez de moverla a un puerto invocar una función que mueva bit a bit a los pines que correspondan, pero para eso preferí el mucho mas simple output_high y output_low.  :mrgreen:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 12 de Marzo de 2008, 20:38:35
Bueno, pues resulta que el amigo Juan Luis Igual, el del     
Proyecto Barco a Vela RC con PIC (http://www.todopic.com.ar/foros/index.php?topic=19703.0), me ha mandado un pila de servos para pruebas, que junto a mi propia pila de servos hacen una pila al cuadrado de servos. Y la ocasión hay que aprovecharla.  :mrgreen:

Así que me he tirado a diseñar una PCB para 16 Servos y a ver qué resulta de ello, veremos si soy capaz de abrir un hilo titulado  Algoritmo para controlar hasta 16 servos con una sola interrupción en C (o dos si son pequeñas)

El caso es que el diseño está listo, voy a ver cómo fabrico la PCB que para mi es lo peor de todo este invento.

Está montado alrededor de un 18F4550 con conexión USB y/o TTL Serie. Para los servos he destinado los puertos B y D completos, y he puesto otros de A y del C para poder recibir info de sensores y cosas así.

(http://picmania.garcia-cuervo.net/images/Sexteen_Servs_PCB_Schema.jpg)

(http://picmania.garcia-cuervo.net/images/Sexteen_Servs_PCB_Rutes.JPG)

(http://picmania.garcia-cuervo.net/images/Sexteen_Servs_PCB_Top.jpg) (http://picmania.garcia-cuervo.net/recursos/RedPicPCBs/Sixteen_Servs_Top.pdf)

(http://picmania.garcia-cuervo.net/images/Sexteen_Servs_PCB_Bottom.jpg) (http://picmania.garcia-cuervo.net/recursos/RedPicPCBs/Sixteen_Servs_Bottom.pdf)

Paciencia si las imágenes tardan en cargar, el servidor casero tiene estas cosas.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Nocturno en 13 de Marzo de 2008, 02:07:00
Si me permites un consejo, Don Diego, coloca los componentes SMD en la cara bottom, y te ahorrarás un huevo de vías.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 13 de Marzo de 2008, 06:06:19
Si me permites un consejo, Don Diego, coloca los componentes SMD en la cara bottom, y te ahorrarás un huevo de vías.

 :shock: :shock: :shock: :shock: :shock:

Nunca "me se" hubiera "oscurrido"

Lo haré. Gracias Maestro.  :mrgreen:

Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 13 de Marzo de 2008, 20:17:07
Cuando digo que no se qué sería de mí sin ustedes es cierto hasta límites insospechados. En este caso, otro más, el consejo del Maestro de Maestros Don Manuel Nocturno ha dado sus frutos de forma espectacular.  :mrgreen:

He movido todos los componentes SMD de la cara Top a la Bottom y he ruteado (a mano) de nuevo. El resultado aquí lo tenéis, con sólo seis puentes, o sea 12 vías, he conseguido rehacer la PCB con docenas de conexiones del PIC publicadas.

Si no lo veo, y lo hago, no lo creo. Dejo tal como está el diseño anterior, no lo sobre-escribo para que sean visibles y comparables los dos resultados.

(http://picmania.garcia-cuervo.net/images/Sexteen_Servs_PCB_Rutes_B.JPG)

(http://picmania.garcia-cuervo.net/images/Sexteen_Servs_PCB_Top_B.jpg) (http://picmania.garcia-cuervo.net/recursos/RedPicPCBs/Sixteen_Servs_Top_B.pdf)

(http://picmania.garcia-cuervo.net/images/Sexteen_Servs_PCB_Bottom_B.jpg) (http://picmania.garcia-cuervo.net/recursos/RedPicPCBs/Sixteen_Servs_Bottom_B.pdf)

Si, estoy seguro de que este consejo merece unos cuantos de estos ...  :-/ :-/ :-/
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Nocturno en 13 de Marzo de 2008, 20:19:46
Me alegro que te haya servido
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 13 de Marzo de 2008, 20:20:55
Es más ... veo que moviendo un poco hacia arriba las dos vías que tienen pistas por la cara Top con esquinas, para dejar dichas pistas totalmente rectas, podría solo insolar la cara Botton y por la Top poner simples puentes, con un hilo. Y asi poderla hacer a simple cara.  :shock: :shock: :shock:

La ostia.  :D :D :D
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: gu1llermo en 14 de Marzo de 2008, 03:56:36
..
Una dudilla y aviso que la hago desde el más profundo de los desconocimientos en el tema. Sería posible generar las señales para controlar los servos de la misma forma en que se controlan los leds RGB por software? ...

Yo creo que si se puede, pero necesito un poco de ayuda para ver si entre todos lo podemos resolver, hice un programa para manejar 16 servos, claro lo tengo pensado para manejar cuántas I/O de nuestro pic sea posible, no sé si es por la hora y estoy imaginándome cosas no sé, pero creo que con un poco de deteniemiento y análisis lo podemos lograr sino es que lo han hecho antes, a continuación voy a colocar el programa con el que pienso controlar los servos mediante PWM usando el principio de los RGB por software, no sin antes hacer mención al código en el que me basé para hacer esto, lo pueden leer aqui (http://www.todopic.com.ar/foros/index.php?topic=20574.0)

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

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

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

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

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

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

#define n 16// números de servos

#define multiplo 1// Podemos cambiarlo para ajustar
#define max_theCount 1000000
#define retardo 300// Son us

int8 servo[n];// Contiene los bytes bajo y alto de los servos
int8 i=0;// es el índice exclusivo de servo[] en la recepción serial

// Declaración de Funciones
void theDelay(void);

// Recepción de datos
#int_rda
void serial_isr(void){//Está listo para recibir 32 bytes
     servo[i]=getc();
     i++;
     if (i>(2*n-1)) i=0;
}

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

void main()
{
   int32    theCount = 0;
   int8     k;
   int32    duty[n];// Cargamos el ciclo útil de los servos
   
   set_tris_b(0x00);
   set_tris_c(0b10000000);
   set_tris_d(0x00);
   
   PORTB=0x00;
   PORTD=0x00;
   
   port_b_pullups(FALSE);
   enable_interrupts(int_rda);
   enable_interrupts(global);
   
   for(k=0;k<=(2*n-1);k++)
      servo[k]=200;// **VALOR INCIAL** por ejem: 200

   while (1)
   {
      theCount++;
      if (theCount>max_theCount) theCount=0;
     
      for (k=0;k<=(n-1);k++) duty[k]=(int32)((servo[2*k+1]*256+servo[2*k])*multiplo);
     
      servo_01= (duty[0] > theCount);
      servo_02= (duty[1] > theCount);
      servo_03= (duty[2] > theCount);
      servo_04= (duty[3] > theCount);
      servo_05= (duty[4] > theCount);
      servo_06= (duty[5] > theCount);
      servo_07= (duty[6] > theCount);
      servo_08= (duty[7] > theCount);
      servo_09= (duty[8] > theCount);
      servo_10= (duty[9] > theCount);
      servo_11= (duty[10] > theCount);
      servo_12= (duty[11] > theCount);
      servo_13= (duty[12] > theCount);
      servo_14= (duty[13] > theCount);
      servo_15= (duty[14] > theCount);
      servo_16= (duty[15] > theCount);

      //theDelay();
   }
}


// Rutina de retardo
void theDelay(void){
   delay_us(retardo);//
}

Estoy seguro que variando los valores de:
max_theCount
multiplo
servo[k]// Los de cada servo

y midiendo las señales en un osciloscopio quizás se pueda lograr el objetivo, yo les coloqué unos valores alli por ponerles algo, pero debe haber una forma de poder determinarlos, lo que pasa es que no sé cuánto tiempo se tarda una instrucción en C ó mejor dicho no sé cómo medirla, alguién sabe como hacer? yo estaba pensando usar el timer en un punto y luego ver su valor en otro punto enviándolo al virtual terminal, solo es una suposición porque no sé  :?

Claro todo esto es una suposición, simplemente me imagino que se podría pero no doy todavía, si alguién le gustaría echarme una mano se lo agradecería  ;-)

Saludos, Felicitaciones y gracias por todos tus aportes RedPic, te botas con tus explicaciones, es decir son demasiado buenas. :wink:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 14 de Marzo de 2008, 18:01:29
Bueno, a esto me refería. Solo la cara Bottom y seis puentes rectos por la cara Top.

Lo dicho, si no lo veo no lo creo.  :shock: :mrgreen: :D

(http://picmania.garcia-cuervo.net/images/Sexteen_Servs_PCB_Rutes_C.JPG)

(http://picmania.garcia-cuervo.net/images/Sexteen_Servs_PCB_Top_C.jpg) (http://picmania.garcia-cuervo.net/recursos/RedPicPCBs/Sixteen_Servs_Top_C.pdf)

(http://picmania.garcia-cuervo.net/images/Sexteen_Servs_PCB_Bottom_C.jpg) (http://picmania.garcia-cuervo.net/recursos/RedPicPCBs/Sixteen_Servs_Bottom_C.pdf)

Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: MGLSOFT en 14 de Marzo de 2008, 18:38:15
Debo tener algun problema, no veo las imagenes, Diego... :? :shock:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 14 de Marzo de 2008, 18:40:36
Debo tener algun problema, no veo las imagenes, Diego... :? :shock:

El servidor es un PC en mi casa y cuando hay mas de dos visitantes al mismo tiempo se queda "tieso" ... o un poco de paciencia o hay que esperar a otro momento mas oportuno.  :? :? :?
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Nocturno en 14 de Marzo de 2008, 19:18:47
Extraordinaria, Diego.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 14 de Marzo de 2008, 19:42:44
Extraordinaria, Diego.

Gracias a tí, Manolo, que si no aún estaría poniendo vías y vías y vías. Ja, ja, ja  :D :D :D
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: gu1llermo en 15 de Marzo de 2008, 17:13:47
....
Código: [Seleccionar]

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


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

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

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


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



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




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

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

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


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

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

            flag_timer1=FALSE;       
        }

    }
    while (TRUE);
}


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

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

Gracias de Antemano.

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

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

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

Me gustaría aclarar que gracias a una intervención de Modulay (http://www.todopic.com.ar/foros/index.php?topic=5580.msg32715#msg32715) fué que me enteré de la existencia del mismo.

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

Saludos.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: gu1llermo en 15 de Marzo de 2008, 20:03:51
// Editado ***

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

    enable_interrupts(int_rda);
    enable_interrupts(global); 

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

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


Adjunto una simulación en proteus.

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

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

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

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

Saludos.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: AKENAFAB en 15 de Marzo de 2008, 21:00:41
Hola Guillermo

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


Pero tengo una duda, como se envia la informacion?

¿Como envío la trama de datos ?

Servo,posicion???

Posicion, SErvo??

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

(http://img527.imageshack.us/img527/149/servoconfigzj3.th.jpg) (http://img527.imageshack.us/my.php?image=servoconfigzj3.jpg)

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

Felicitaciones de nuevo guillermo.!!!


Saludos

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

Que buen dato!!! con razón no entendía el comportamiento y el porque tenía que cambiar los valores que ya había determinado previamente, voy hacer los cambios que me dices a ver.
Título: 2da Aproximación
Publicado por: gu1llermo en 15 de Marzo de 2008, 22:01:43
Antes que nada, quisiera preguntarle a a RedPic si está bien que siga posteando mis avances aquí ó abro otro hilo.

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

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

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

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

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

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

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

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

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

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


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

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


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

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

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

    enable_interrupts(int_rda);
    enable_interrupts(global); 

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

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


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

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

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

Adjunto archivos.

Saludos.

// Editado posteriormente \\

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

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

Saludos.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: gu1llermo en 15 de Marzo de 2008, 23:31:22
Según el análisis que e realizado lo mínimo que podría hacer girar estos servo es a 4,5º cada uno, pero eso es muy complicado y requiere más tiempo, así que voy a hacerlo girar a 9º que es un poco más sencillo (relativamente).
Título: Ahora si!
Publicado por: gu1llermo en 16 de Marzo de 2008, 00:47:33
Bueno ahora si está funcionando como debe ser, a menos de que alguién le consiga un detalle, aquí está el código resultante:

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

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

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

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

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

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

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

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

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

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

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

    disable_interrupts(global); 

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

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

Adjunto archivos.

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

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

Saludos. :-/ :-/ :-/
Título: Re: Ahora si!
Publicado por: RedPic en 16 de Marzo de 2008, 04:20:11

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


Para esto (http://www.todopic.com.ar/foros/index.php?topic=19703.msg159333#msg159333) y/o esto (http://www.todopic.com.ar/foros/index.php?topic=19703.msg155642#msg155642). Y aún faltan mas cosas como varios motores D.C. o PaP. Y una larga lista de sensores, fines de carrera, sensores de posición, actuadores lineales ...   :mrgreen:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: gu1llermo en 16 de Marzo de 2008, 09:32:31
Ahhh!!! Guao  :shock: tremendo proyecto no!? bueno si se quieren controlar más servos me avisas porque yo creo que se pueden adaptar más servos todavía a éste pic, mejor dicho no creo, estoy seguro que si se puede solo es cuestión de medir tiempos y ya, con respecto a los sensores, fines de carrerars y demás supongo que ya los tienes solventado ó algo ya tienes  :lol:, lo que pasa es que a mi me gusta programar y esto lo hago por gusto  :mrgreen: y por reto  :wink:

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

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

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

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

Saludos.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 16 de Marzo de 2008, 10:40:41
En cuanto tenga mi nueva PCB lista tu código será probado en vivo y en directo.  :mrgreen:

(http://picmania.garcia-cuervo.net/images/Sexteen_Servs_PCB_Rutes_D_Thumb.JPG) (http://picmania.garcia-cuervo.net/images/Sexteen_Servs_PCB_Rutes_D.jpg)

P.D.: Ya están publicados todos los pines libres, lo que no están directamente asignados a un servo.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: gu1llermo en 16 de Marzo de 2008, 16:12:55
...
Así que me he tirado a diseñar una PCB para 16 Servos y a ver qué resulta de ello, veremos si soy capaz de abrir un hilo titulado  Algoritmo para controlar hasta 16 servos con una sola interrupción en C (o dos si son pequeñas)
...
Está montado alrededor de un 18F4550 con conexión USB y/o TTL Serie. Para los servos he destinado los puertos B y D completos, y he puesto otros de A y del C para poder recibir info de sensores y cosas así.
...

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

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

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

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

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

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

Saludos.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 16 de Marzo de 2008, 17:18:53
Amigo guillermo:

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

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

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

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

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

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

Proyecto Barco a Vela RC con PIC (http://www.todopic.com.ar/foros/index.php?topic=19703.0)
Algoritmo para controlar hasta 8 servos (http://www.todopic.com.ar/foros/index.php?topic=20335.0)
¿Conocéis módulos RF? (http://www.todopic.com.ar/foros/index.php?topic=19938.msg152188#msg152188)
Esquema mínimo de driver para un Motor DC 12V con un L298N (http://www.todopic.com.ar/foros/index.php?topic=20444.msg153662#msg153662)
Receptor RC asistido por PIC (http://www.todopic.com.ar/foros/index.php?topic=20232.0)

Todos estos hilos tienen que ver con este tema y apuntan todos en el mismo sentido.  :mrgreen:
Título: Re: Algoritmo para controlar 16!!! servos con una sola interrupción (en C)
Publicado por: gu1llermo en 16 de Marzo de 2008, 18:41:28
Ah!! oka, oka, como dicen: "Divide y vencerás".

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

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

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

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

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

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

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

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

#define val_timer0 88

int8    theCount=1;
int8    duty[n];

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

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

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

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

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

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

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

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

Saludos.
Título: Re: Algoritmo para controlar 16 servos (en C) pic18f4550
Publicado por: gu1llermo en 17 de Marzo de 2008, 01:39:00
Bueno aquí está el código para controlar 16 servos con el pic18f4550, le podemos enviar comandos tanto vía serial RS232 como vía USB, la trama es (SERVO,POSICIÓN), SERVO=[0..15] y POSICIÓN=[5..25], donde 5=extremo izquierdo y 25=extremo derecho.

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

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

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

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

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

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

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

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

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

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

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

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

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

#define val_timer0 127

int8    theCount=1;
int8    duty[n];

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

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

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

    setup_adc_ports(NO_ANALOGS);
    setup_adc(ADC_OFF);

    setup_timer_0(RTCC_DIV_8 | RTCC_8_BIT);

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

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

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

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

Adicionalmente realicé una sencilla interfaz (Gracias a J1M (http://www.todopic.com.ar/foros/index.php?topic=2260.0)) para controlar los servos vía usb, aquí una imagen funcionando:

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

Hola Guillermo

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

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

Que la pasen bien.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: JLIGUAL en 22 de Marzo de 2008, 17:20:24
Me descubro ante Vds Sres. progamadores, me teneis alucinado, con que facilidad preparais una aplicacion para gestionar algo que ni siquiera teneis claro de lo que va a hacer, solo con saber lo que quiere uno y pensar en como utilizar cada herramienta para lograr el funcionamiento perfecto.

Este proyecto empezo por una pequeña idea de generar un "equipo" (llamemosle asi) que fuese capaz, por un precio razonable, de comunicarnos con los equipos situados en un velero  y hacer que estos nos hagan generar rios de adrenalina como si en realidad nos encontrasemos a la caña del timon de ese Cheminee Poujoulant en su victoria de la regata Velux five oceansel pasado mes de marzo de 2.007
http://www.bernardstamm.com/

O bien Jean Pierre Dick en su llegada de la Barcelona Wordl race, el presente año
http://www.jpdick.com/p-jpdickcompubfr/display.aspx?srv=p-jpdickcom&typ=pub&lang=fr&cmd=view&style=styles/home.xsl

Espero que al terminar la parada de mantenimiento mecanico, que por fin terminamos el Lunes, pueda dedicar mas horas a la parte de la cubierta y de esa manera poder iniciar el manejo de los diferentes enrolladores, velas, y resto de equipos.

Con respecto al servo que venia de Hong Kong, se supone que debe de venir en un tren de renfe, porque ya lleva una semana de retraso, muy malas fechas, pero si da la potencia anunciada, ya esta hecha la eleccion del 75 % de la gestion de los controles, ya que con 13 kgs, esta capacitado para casi todas las funciones.

Un saludo

Juan Luis Igual

Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: gu1llermo en 25 de Marzo de 2008, 21:35:06
Hola JLIGUAL yo creo que todo esto empezó por tu iniciativa del barco vela, que entusiasmó a muchos (me incluyo) es un buen proyecto en donde toda la comunidad seremos beneficiados.

Con respecto a lo de la programación de eso no te preocupes que aquí hay un nivel elevado de programadores y lo que se necesite se hace  8) :wink: para mi programar es como jugar  :mrgreen: y creo que para varios también.

Por otro lado quería agregar unas observaciones, como todos sabemos los temblores de los servos se deben a que la señal de pwm que le aplicamos no cumple exactamente los 2,5ms, sino que a veces un poco más ó un poco menos y esto es debido a las instrucciones en alto nivel del c que no podemos observar esas pequeñas diferencias con respecto a cuánto se llevan a cabo estas, en el pic18 hay instrucciones que pueden llevar 2 ó 3 ciclos del procesador y esto reflejado en las interrupciones se presenta el caso de que cuando se entra en ellas, se puede entrar con 1, 2 ó 0 ciclos de más, esto es lo e podido observar haciendo simulaciones en el mplab, voy a intentar ser un poco más gráfico con un ejemplo, vamos a utilizar la interrupción del timer1 para el mismo:

Supongamos que ajustamos un valor del timer1 tal que queremos lograr una interrupción a los 1ms exactos, una vez que entramos en la interrupción hacemos cero el contador del STOPWATCH en el mplab y observamos el tiempo que se tarda en llegar nuevamente en llegar a la misma, si hacemos esto podemos observar que a veces entra a los 1ms+1ciclo_de_instrucción, otras veces 1ms+2ciclos_de_instrucción y en otras 1ms+0ciclo_de_instrucción, esto hace que el ciclo útil del servo se alargue o se acorte, traducido en temblores para los servos.

Sumado a esto en la rutina de interrupción hay instrucciones condicionales, en donde el número de ciclos de instrucción que se ejecutan si es cierta la condición no es el mismo número de ciclos que si la condición es falsa, por lo tanto crea algo que yo llamo "desequilibrio" que traducido en el servo es tembladera.
Y como son varias instrucciones, esto hace que el desequilibrio sea más grande por lo tanto más visible en los servos.

Todo esto lo e solucionado en el código que e expuesto aquí (http://www.todopic.com.ar/foros/index.php?topic=20918.0), para todo aquel que le interese lo pueda observar.

Nota curiosa:
...
Agregando

En teoria  :D , bueno bueno en mi mente soñadora  :D ... tenemos 2.5 ms por maximo que estarian trabajando los servos y 17.5 ms libres para realizar todas las operaciones necesarias.

Estoy zombie    :shock:...

Leyendo los comentarios anteriores me encontré con éste tuyo AKENAFAB y ya tenías la idea desde un principio, exactamente así funciona el código.  :mrgreen: así que no estás zombie sino que tenías una buena  :idea: :wink:

Saludos.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: INGJHOM en 08 de Mayo de 2008, 13:40:43
he estado estudiando tu programa, pero hay algo que no puedo todavia entender cual es la parte en el programa donde colocas el flag_phase en 1. 
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: DESKREP en 15 de Mayo de 2008, 03:33:35
MUY INGENIOSO TU METODO , DE DIVIDIR EN VENTANAS PARA CONTROLAR EN ACADA UNO DE ELLSO INDEPENDIENTEMENTE CADA SERVO.
SOLO COMENTARTE QUE SI HE PODIDO CONTROLAR HASTA 6 SERVOS TODOS AL MISMO TIEMPO(EN LA MISMA VENTANA PODRIA DECIRSE). SIN PROBLEMA ALGUNO... TALVES OMITISTE ALGUN PEQUEÑO DETALLE EN EL PROGRAMA CUANDO LO PROBASTE.

NUEVAMENTE REITERO MUY INGENIOSA LA FORMA DE CONTROLAR TUS 8 SERVOS... LA PONDRE A PRUEBA A VER QUE TAL LOS RESULTADOS
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Deirdre en 20 de Junio de 2008, 08:47:58
Hola! he estado siguiendo el hilo...bueno, intentándolo, y la verdad que es un poco lío. Yo estoy intentando controlar 8 servos, pero no todos a la vez, si no que cuando pulse una tecla se mueva uno y cuando pulse otra otro, etc. He probado el rpogarma de redpic, pero he tenido que hacer alguna modificación, además no lo entiendo muy bien, porque yo uso el 16F876A, pero funcionaba bien. Me gustaría saber como podría modificarlo para que no sean todos a la vez, si no controlar uno por uno. Pobré otro programa publicado en otro hilo, pero no se porque, puede ser por lo que decían de los tiempos del puerto serie, al mover uno se movían los demás. Agradezco toda la ayuda que me podáis prestar, estoy un poco agbiada. Gracias
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: firepic en 20 de Junio de 2008, 13:56:08
Saludos Deirdre!
Con el método del maestro redpic controlas todos los servos a la vez...
Claro que no quiere decir que tengas que moverlos todos al mismo tiempo...
Pero recuerda que en los servos, para garantizar que se mantenga en la misma posición, debes enviarle la señal de forma continua, de otra forma perdería fuerza y podría perder fácilmente la posición deseada.
Estoy seguro de que puedes modificar el programa para ajustarlo a tus requerimientos.
Cuídate, nos leemos!  :mrgreen:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Deirdre en 21 de Junio de 2008, 13:43:02
Hola!muchas gracias por responder :-/ Voy a ver si consigo leermelo otra vez (y ya van unas cuantas) y entenderlo bien,y así a ver si puedo modificarlo. Cierto es que hasta ahora he tenido muchos porblemas por el tema de tener que mandar la señal de forma constante, ya que al implementar la interrupción por teclado, cuando  pulsaba una tecla se me esajustaba todo. A ver que puedo hacer!!!!!
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: Deirdre en 30 de Junio de 2008, 08:25:48
Ya está, aunque parezca mentira, solo con poner 65534 en lugar de 65535 todo funciona perfectamente. Supongo que será cosa de que el reloj no hace exacyamente 20ms. Gracias por todo!!!! :-/
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: firepic en 30 de Junio de 2008, 10:45:55
Felicitaciones Deidre! Me alegro que lo hayas logrado.
Saludos, nos leemos!  :mrgreen:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: INGJHOM en 02 de Septiembre de 2008, 18:35:27
He estado utilizando el codigo de control de ocho servos, para volverlo dinamico es decir que no sea solo la posición central y de extremo a extremo, sino que se pueda mover a cualquier grado, lo simulo pero no funciona muy bien que digamos.

que podria ser??

dejo adjunto el programa.

con la variable ticknumber le proporciono el valor de las ticks correspondiente al servo..

gracias
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: raulsmat en 03 de Junio de 2009, 11:28:26
Hola, vengo rebotado de este hilo por Veguepic

http://www.todopic.com.ar/foros/index.php?topic=26265.0

en el que cuento que intento susbstituir el procesador de una emisora de 4ch por un Pic para poder añadir un menu y poder
hacer mezclas de servos (delta, H3...etc). Tengo el tema medio solucionado excepto porque no se cual es el tipo de trama que envia el procesador al modulo RF para comunicarse con el receptor, quizas tu lo conozcas.

  Gracias.
Título: Re: Algoritmo para controlar 16 servos (en C) pic18f4550
Publicado por: ivrj en 09 de Junio de 2009, 11:50:32
Bueno aquí está el código para controlar 16 servos con el pic18f4550, le podemos enviar comandos tanto vía serial RS232 como vía USB, la trama es (SERVO,POSICIÓN), SERVO=[0..15] y POSICIÓN=[5..25], donde 5=extremo izquierdo y 25=extremo derecho.

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

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

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

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

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

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

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

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

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

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

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

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

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

#define val_timer0 127

int8    theCount=1;
int8    duty[n];

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

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

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

    setup_adc_ports(NO_ANALOGS);
    setup_adc(ADC_OFF);

    setup_timer_0(RTCC_DIV_8 | RTCC_8_BIT);

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

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

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

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

Adicionalmente realicé una sencilla interfaz (Gracias a J1M (http://www.todopic.com.ar/foros/index.php?topic=2260.0)) para controlar los servos vía usb, aquí una imagen funcionando:

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

Hola Guillermo

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

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

Que la pasen bien.

olá!
Su proyecto es perfecto! Es exactamente lo que necesito para utilizar en mi proyecto.
Me gustaría saber cómo lo hizo para calcular el timer0?
Cuanto más leo material sobre el cálculo del timer0, tengo más e más  preguntas.
En mi caso, voy a utilizar sólo el 8 servos. Me di cuenta de que el duty  ha cambiado cuando se utiliza sólo el 8 servos.
Si he entendido bien, se utiliza un TIMER por debajo de la ideal  e incluye la instruccion nop para compensar el intervalo?
gracias.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: INGJHOM en 13 de Agosto de 2009, 22:07:55
Cuando empece a programar en C, Quise aplicarlo en un proyecto de robotica.. asi que diseñe un brazo robotico con servomotores Hitec. Para esto necesitaba manejar almenos 6 servos. Entonces fue hay cuando me di cuenta del codigo para el manejo de 8 servos... La verdad este foro me ayudo mucho en el objetivo planteado y puedo contarles que lo logre.

Mi proyecto fue llevado a cabo inclusive construi el brazo y los PCB. El brazo se puede controlar a través de un teclado 4X4 y una lCD 20X4 o tambien a través de un PC por medio de la interfaz Rs232.

Muchas gracias compañeros del foro, no solo a los de este sino a los de todos los foros.. ps el conocimiento es libre y asi debe de seguir siendo.

Me despido pero no sin antes decirles que adjunto al mensaje les dejo los programas de los PICs.. Hay librerias que les pueden servir y son muy faciles de implementar.

Cualquier comentario.. no duden en hacermelo llegar..
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: RedPic en 14 de Agosto de 2009, 05:20:32
Me alegro de que hayas llevado a buen puerto tu proyecto. Y muchas gracias por tu aporte.  :mrgreen:
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: INGJHOM en 14 de Agosto de 2009, 11:39:16
De nada redpic, ahora me gustaria saber como implementar el bootloader en los PICs y asi no estar desarmando y armando el circuito nuevamente, con el fin de agregarle a los PICs nuevas funcionalidades en el software..

Me gustaria saber cuales son las lineas de codigo. Se que talves este no sea el foro sobre ese tema. Pero de todas maneras me gustaria saber y asi mejorar aún más el proyecto..

En el archivo que adjunte en el anterior mensaje se encuentra la simulación en proteus. Esta simulación posee dos paginas, si lo tratan de simular en la primera les arroja un error, deben correr la simulación desde la segunda hoja..

Exitos con las pruebas.
Título: Re: Algoritmo para controlar 16 servos (en C) pic18f4550
Publicado por: thom en 11 de Agosto de 2010, 13:26:10
Felicidades amigo, buen proyecto y es de gran utilidad. bueno solo te quiero pedir el favor de ayudarme con la interfaz grafica, ya que en los enlaces de JIM no se pueden descargar.

Te lo agradezco de antemano!!!! :lol: :lol: :)
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: willynovi en 11 de Agosto de 2010, 18:15:43
te has visitado la pagina de RedPic PicMania (http://picmania.garcia-cuervo.net/picc_servos_x_8.php)?
Ahi puede que encuentres mas info hacerca de programar en la PC el puerto serie, creo que es el que usa para controlar los servos.

Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: thom en 17 de Agosto de 2010, 17:55:44
Amigo, como ya dije, excelente trabajo, pero he tenido inconvenientes con la interfaz gráfica, no me quiere funcionar y eso q me he basado en lo de Jim, agradecería si me envias con el proyecto completo de la interfaz gráfica.

Te lo agradecería.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: elhertz33 en 25 de Marzo de 2011, 02:47:13
hola amigos tengo un proyecto para controlar tres servos solamente e hice elprograma con el algoritmo de interrupcion por timer 1 pero el porblema que tengo es que la frecuencia no funciona esta hecho en un pic 18f4550 deseo su ayuda jejejej y de antemano muchas gracias a este foro por todo lo que he aprendido.
Código: [Seleccionar]
#include <18f4550.h>
#fuses HS,NOMCLR,PUT,NOWDT,NOPROTECT,BROWNOUT,NOLVP,NOCPD,NODEBUG,NOWRT
#use delay(clock=20000000)

#use fast_io(D)
#use fast_io(B)
 
#priority timer1

#define SERVO1 PIN_D7
#define SERVO2 PIN_D6
#define SERVO3 PIN_D5
#define SERVO4 PIN_D4
#define SERVO5 PIN_D3
#define SERVO6 PIN_D2
#define SERVO7 PIN_D1
#define SERVO8 PIN_D0
const int16  TOTAL  = 12500;  // PWM Window for servo = 2.5 ms x 8 = 20 ms
//const int16  Ticks4Minimum =  3500;  // PWM High for Minimum Position = 0.7 ms 
//const int16  Ticks4Center  =  7500;  // PWM High for Center  Position = 1.5 ms 
///const int16  Ticks4Maximum = 11500;  // PWM High for Maximum Position = 2.3 ms 

int16 TIEMPOS[8]={7500,3500,7500,0,0,0,0,0};
static int8  NUMSERVO=0;
static int1  SERVO1_ON=1;
static int1  SERVO2_ON=1;
static int1  SERVO3_ON=1;
static int1  SERVO4_ON=0;
static int1  SERVO5_ON=0;
static int1  SERVO6_ON=0;
static int1  SERVO7_ON=0;
static int1  SERVO8_ON=0;
static int1  ENTRADAS;
static int16 SIGUIENTEINT=53036;

#int_timer1
void timer1_isr(void){
 
   if(ENTRADAS==0){
      if(NUMSERVO==0 && SERVO1_ON) output_high(SERVO1);
      if(NUMSERVO==1 && SERVO2_ON) output_high(SERVO2);
      if(NUMSERVO==2 && SERVO3_ON) output_high(SERVO3);
      if(NUMSERVO==3 && SERVO4_ON) output_high(SERVO4);
      if(NUMSERVO==4 && SERVO5_ON) output_high(SERVO5);
      if(NUMSERVO==5 && SERVO6_ON) output_high(SERVO6);
      if(NUMSERVO==6 && SERVO7_ON) output_high(SERVO7);
      if(NUMSERVO==7 && SERVO8_ON) output_high(SERVO8);
      SIGUIENTEINT = 65535 - TIEMPOS[NUMSERVO];
      set_timer1(SIGUIENTEINT);
   }
   if(ENTRADAS==1){
      if(NUMSERVO==0 && SERVO1_ON) output_low(SERVO1);
      if(NUMSERVO==1 && SERVO2_ON) output_low(SERVO2);
      if(NUMSERVO==2 && SERVO3_ON) output_low(SERVO3);
      if(NUMSERVO==3 && SERVO4_ON) output_low(SERVO4);
      if(NUMSERVO==4 && SERVO5_ON) output_low(SERVO5);
      if(NUMSERVO==5 && SERVO6_ON) output_low(SERVO6);
      if(NUMSERVO==6 && SERVO7_ON) output_low(SERVO7);
      if(NUMSERVO==7 && SERVO8_ON) output_low(SERVO8);
      SIGUIENTEINT = 65535 - TOTAL + TIEMPOS[NUMSERVO];
      set_timer1(SIGUIENTEINT);
      if(++NUMSERVO>7) NUMSERVO=0;
   }
   ++ENTRADAS;
}
void main(void) {
   
 
   disable_interrupts(global);
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
   setup_timer_0(RTCC_OFF);
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
   setup_timer_2(T2_DISABLED,0,1);
   setup_timer_3(T3_DISABLED);
   port_b_pullups(TRUE);
 
   set_tris_D(0b00000000);
   set_tris_B(0b11111111);
 
   output_low(SERVO1);
   output_low(SERVO2);
   output_low(SERVO3);
   output_low(SERVO4);
   output_low(SERVO5);
   output_low(SERVO6);
   output_low(SERVO7);
   output_low(SERVO8);
 
   delay_ms(1000);
 
   set_timer1(SIGUIENTEINT);
   enable_interrupts(int_timer1);
   enable_interrupts(global);

 
   do {
      IF(INPUT(PIN_B0)&&TIEMPOS[0]<11000){
         IF(!INPUT(PIN_B0)&&TIEMPOS[0]<11000){
         TIEMPOS[0]=TIEMPOS[0]+ 250;
         }
      }
      IF(INPUT(PIN_B1)&&TIEMPOS[0]>3800){
          IF(!INPUT(PIN_B1)&&TIEMPOS[0]<11000){
            TIEMPOS[0]=TIEMPOS[0] - 250;
          }
      }
     
   } while (TRUE);
}
 

por favor estoy algo desesperado con esto espero que me puedan ayudar a encontrar la solucion.
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: beatAurex1 en 30 de Marzo de 2011, 20:35:44
una Saludo

Una pregunta Redpic, estuve probando el algoritmo de 8 servos,  pero con 4 servos, la frecuencia, y el tiempo en alto de  de .7,  1.5 y  2,3 ms funciona perfecto, pero hay algun otra forma de que mis servos respondan mas rapido? ya que siento que responden muy lentos.


cuando avanzo hacia adelante o hacia  atras su posicion Servo_PWM+=80; funcionan y se  deslizan suavemente pero  el recorrido es demasiado lento,  aumente los pasos para acelerar el recorrido por ejemplo

Servo_PWM+=400;


Pero no se si haya algun otra  forma de aceelrar el recorrido del servo.

gracias
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: beatAurex1 en 13 de Abril de 2011, 21:18:51
Bueno Me respondo Yo Mismo jaja solo tenia unas lineas de mas por lo cual me respondian bastante lentos los servos.

Saludos
Título: Re: Algoritmo para controlar hasta 8 servos con una sola interrupción (en C)
Publicado por: falflores en 25 de Marzo de 2012, 06:01:02
Gracias amigo RedPIC por compartir tu excelente idea con todos nosotros, fijate que al ver tu código me entraron ganas de probarlo con un 16F887, así que lo copie, lo pegue, cambie el PIC, comente la línea del Timer3....y vualá!!! funciona de maravillas! y solo me tomo 3 minutos hecharlo a andar, así es cuando de verdad da gusto que los demás compartan sus códigos, muchas gracias.

Solo tengo una inquietud: a qué frecuencia sale la señal de cada PWM?, por que tengo un LED conectado a las salidas y se ve que parpadea muy lentamente, osea que debe ser menor a 24Hz, y lo normal es 50, además se nota la baja frecuencia en los servos cuando lo muevo fuera de la posición y tarda en regresar.