jump to navigation

Control de servos desde pinguino (Microchip PIC) 3 febrero, 2010

Posted by ubanov in Electrónica.
Tags: , ,
trackback

Así como arduino tiene una librería para controlar servos, en pinguino no encontramos ninguna (hay que recordar que es un proyecto bastante joven).

En este artículo os muestro unas sencillas rutinas que he desarrollado para controlar hasta 8-10 servos.

El funcionamiento de un servo se basa en que por una línea de control recibe 50 veces por segundo (cada 20ms aproximadamente), un pulso. En base a la duración de este pulso el servo se posiciona. El pulso suele durar entre 1ms y 2ms, correspondiéndose cada uno a uno de los extremos

Utilizando el primer pinguino que he construido (le puse bornas con tornillo pequeños para enchufarlo lo que quisiese), he conectado un servo a la placa:

En el pinguino ejecuto el siguiente programa:

————————————————————————

#define PIC18F2550

#define SERVOMAX 24000
#define SERVOMIN 10000
#define SERVOCYCLE 25000

unsigned char contadorisr=0;

unsigned int servosvalues[16];

//interrupt handler that handles servos
void UserInterrupt()
{
if (PIR1bits.TMR1IF) {
// for a better speed bit functions are used rather than digitalWrite
// first switch on or switch off output
switch(contadorisr) {
case 0:
PORTBbits.RB7=1; break;
case 1:
PORTBbits.RB7=0; break;
case 2:
PORTBbits.RB6=1; break;
case 3:
PORTBbits.RB6=0; break;
case 4:
PORTBbits.RB5=1; break;
case 5:
PORTBbits.RB5=0; break;
case 6:
PORTBbits.RB4=1; break;
case 7:
PORTBbits.RB4=0; break;
case 8:
PORTBbits.RB3=1; break;
case 9:
PORTBbits.RB3=0; break;
case 10:
PORTBbits.RB2=1; break;
case 11:
PORTBbits.RB2=0; break;
case 12:
PORTBbits.RB1=1; break;
case 13:
PORTBbits.RB1=0; break;
case 14:
PORTBbits.RB0=1; break;
case 15:
PORTBbits.RB0=0; break;
}
PIR1bits.TMR1IF=0;
TMR1L=0;    // for not over count before correct value, really i think that it’s no necesary, but…
TMR1H=servosvalues[contadorisr]>>8;
TMR1L=servosvalues[contadorisr]&255;;
contadorisr++;
if(contadorisr>=16)
contadorisr=0;
}
}

void EnableTimerInterrupt(void)
{
TMR1H=0xFF;
TMR1L=0x00;
// timer 1 prescaler 1 source is internal oscillator
T1CON=0x01;
// enable interrupt for timer1 in register PIE1
PIE1bits.TMR1IE=1;
// enable peripheral interrupt
INTCONbits.PEIE=1;
// global enable interrupt
INTCONbits.GIE=1;
// now an interrupt will be generated by timer1 every 42,5 microsec (aprox 19500/sec)

}

//some calculations20ms, 2,5ms por servo (12500 ins), ins (5000=1ms, 10000=2ms), but are not correct, and I don’t know why
void SetServo(unsigned char servo,unsigned int value)
{
if(servo>=8)        // test if numservo is valid
return;
//regulate
if(value<SERVOMIN)    // 0,8ms=4000 cycles
value=SERVOMIN;
else if(value>SERVOMAX) // 2,2ms=11000 cycles
value=SERVOMAX;
servosvalues[servo+servo]=-value;
servosvalues[servo+servo+1]=-SERVOCYCLE+value;
}

void setup()
{
unsigned char a;

for(a=0;a<18;a++)
pinMode(a,OUTPUT);
for(a=0;a<8;a++)
SetServo(a,(SERVOMAX+SERVOMIN)>>1);

EnableTimerInterrupt();
}

void loop()
{
unsigned int n;

for(n=SERVOMIN;n<=SERVOMAX;n+=8) {
SetServo(0,n);
delay(2);
}
for(n=SERVOMAX;n>=SERVOMIN;n-=8) {
SetServo(0,n);
delay(2);
}
}

————————————————————————

El que se encarga de hacer todo el trabajo es la rutina UserInterrupt. Esta rutina es llamada cada cierto tiempo y el tiempo va variando de ciclo a ciclo… ¿cómo cambia? el caso es que la interrupción va a tener 16 ciclos (2 por cada servo, en los ciclos pares pone una salida de un servo a 1, empieza a generar el pulso, y en los impares pone ese servo a 0). El que indica en qué ciclo está es la variable contadorisr, que es incrementado al final de la interrupción. Es decir cuando estamos en el 0 habilitamos la salida de RB7, en el 1 la deshabilitamos, en el 2 ponemos RB6=1, en el 3 a RB6=1, en el 4 RB5=1… hasta llegar al 15 que toca RB0=0. Después de poner el valor correspondiente en la línea de control, lo que hacemos es configurar el timer1 para que la siguiente interrupción se haga en un determinado tiempo.

Los tiempos vienen definidos en la variable servosvalues. Y estas variables son actualizadas desde la rutina SetServo. El caso es que cuando con SetServo queremos poner un determinado servo en una posición, lo que hacemos es actualizar dos entradas de servosvalues. ¿Cómo se hace esto? imaginemos que queremos poner el primer servo a la mitad, digamos que llamaríamos a SetServo(0,TIEMPODE1500ms), lo que hará SetServo es configurar en servovalues[0] el tiempo que va a estar la entrada a 1, y en servosvalues[1] el tiempo que va a estar la entrada a 0. El tiempo que va a estar la entrada a 0 lo hago para que cada servo reciba la información siempre cada 20ms. De esta forma el valor de servosvalues[1] sería (20ms/8servos)-servovalues[0]… en el ejemplo anterior nos daría 2500ms-1500ms=1000ms.

Cuando he escrito la primera vez el programa, yo había calculado que SERVOMAX fuese 11000 (digamos que esto serían como 2,2ms), SERVOMIN 4000 (0,800ms) y SERVOCYCLE 12500 (2,5ms)… pero cuando me he puesto a probar la placa no funcionaba (el servo no se movía). Probando con la herramienta de analisis de señales de pickit2 he comprobado que cuando ponia 7500 la señal en lugar de durar 1,5ms, duraba 0,63ms…. realmente no entiendo por qué, pero he subido los tiempos y ya funciona bien (aunque quizás podrían mejorarse los valores y poner unos más óptimos).

Con estas rutinas el movimiento del servo de ejemplo (el conectado al pin RB7) es muy fino y tiene una resolución muy buena.

Si enchufas un servo al pinguino “en caliente”, a mi se me resetea la placa de pinguino.

Espero que esto le sirva a alguien.

Comentarios»

1. Jose - 15 febrero, 2010

Muchas gracias, llevaba tiempo intentando hacer funcionar una rutina como esta pero no lo habia logrado. Me has desatascado con tu funcion de control de servos, ahora me puedo centrar en comunicarme con las aplicaciones, un ejemplo de los que quiero construir.

Un saludo y gracias.

ubanov - 15 febrero, 2010

ya me alegro de que te haya servido. La mano tiene muy buena pinta!!

lo único que en el comentario que pones de no hace falta que pongas mi nombre!!! (aunque me siento honrado). No sería más útil poner el link al fuente, por si alguien necesita algo parecido?

gracias

2. Jose - 16 febrero, 2010

Si lo siento sue por las prisas, ya esta actualizado con los links a tu blog y a la web de hackinglab. Bueno el bicho esta sin terminar y tengo que conseguir que comunique con el pc, que no hay manera si no es en modo programacion. Si se te ocurre alguna manera estoy abierto a sugerencias, por que ni processing ni python me detectan el dispositivo y mucho menos las librerias de Matlab.

Un saludo ire colgando los avances por si te interesa en:

http://microbotica.foroselectronica.es/

3. Mi primer Pinguino « Linux Droids Blog - 25 febrero, 2010

[…] – Control de luces desde Pinguino – Control de Servos desde Pinguino […]

4. tienda de informatica - 8 diciembre, 2010

contenido muy interesante, donde podriamos ampliar información sobre esto? saludos hi! i would like to know where y get more information? thank you

5. paolo - 19 mayo, 2011

how can i embed this to pinguino beta???is it possible to creat a library for it?

6. paolo - 19 mayo, 2011

i have this problem when i place it in pinguino 9.05 beta.

E:\HackingLab\pinguino_beta9-05_windows\source\/user.c:68: syntax error: token -> ’00’ ; column 11

E:\HackingLab\pinguino_beta9-05_windows\source\/user.c:71: syntax error: token -> ’01’ ; column 11
E:\HackingLab\pinguino_beta9-05_windows\source\main.c:59: warning 112: function ‘setup’ implicit declaration
E:\HackingLab\pinguino_beta9-05_windows\source\main.c:89: warning 112: function ‘loop’ implicit declaration
E:\HackingLab\pinguino_beta9-05_windows\source\main.c:96: warning 197: keyword ‘interrupt’ is deprecated, use ‘__interrupt’ instead
E:\HackingLab\pinguino_beta9-05_windows\source\main.c:140: warning 197: keyword ‘interrupt’ is deprecated, use ‘__interrupt’ instead

paolo - 19 mayo, 2011

now this is my error and i could not detect why i should change interrupt to __interrupt

7. Ivan Garcia Municio - 27 octubre, 2011

ubanov me podrias pasar el esquema de tu pinguino modificado para 8-10 servos, muchas gracias por todo. Mi mail es tecnicoo@gmail.com

ubanov - 30 octubre, 2011

para 8-10 servos no hace falta ningún esquema especial…. simplemente conecta a cada servo su alimentación (positivo y negativo) y la linea de control a alguna de las puertas de salida del pingüino….

8. Jorge Rojas - 28 marzo, 2014

Hola Ivan, soy profesor de informática y enseño a mis alumnos pinguino, y quisiera saber si puedes ayudarme, necesito hacer una librería propia para que el código sea mas entendible para ellos, espero puedas ayudarme, de antemano te doy las gracias .


Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: