Si además quieres enviarnos un Artículo para el Blog y redes sociales, pulsa el siguiente botón:
Hola a todos,
Con esto de seguir preparando el PartyKit con Pinguino, me enfrasqué en una rutina nueva para controlar servos, que en principio sería distinta a la librería que incluye la versión Beta 8 de pinguino. Bueno, al grano, el caso es que que después de pegarme con el tema de la duración de los pulsos que generaba mi rutina, me he dado cuenta de que la base de tiempos que creemos que tiene pinguino, no es exactamente esa.
Me explico. Resulta que estamos utilizando un xtal de 20 Mhz en la versión del 18F2550 (aunque creo que este problema aplica tanto a esta versión como a la del 18F4550).
Supuestamente el ciclo de instrucción debería ser Fosc / 4 ¿correcto? . Bien, si hacéis 20Mhz/4 = 5 Mhz, luego cada ciclo de instrucción sería de 1/(5Mhz) = 0,2 microsegundos.........Pues NO. Esto no es así.
Después de pegarme con varias rutinas para ver si las distintas funciones del código de pinguino (delay, millis, servo...) les pasaba lo mismo que a mi rutina, he llegado a la conclusión de que el ciclo de instrucción real que tenemos con la config (bootloader) actual es de 0,16 microsegundos por instrucción. Lo cual si hacéis los cálculos, sería como decir que tenemos un reloj de 25 Mhz.... 😮 (???) (ver los 2 siguientes posts)
Este problema ya lo había detectado ubanov en su rutina para el control de servos: http://ubanov.wordpress.com/2010/02/03/control-de-servos-desde-pinguino-microchip-pic/ " onclick="window.open(this.href);return false; (en sus comentarios podéis leer que veía unas duraciones de pulsos mas corta de la que el esperaba que se produjesen).
Para comprobar que esto es así, simplemente cargad la siguiente rutina. Es una de las de ejemplo que vienen en el paquete de Pinguino:
// first test with Pinguino
void setup(void)
{
pinMode(0,OUTPUT);
}
void loop(void)
{
digitalWrite(0,HIGH);
delay(5);
digitalWrite(0,LOW);
delay(5);
}
Como veis, esta debería producir una señal cuadrada de pulso alto = 5 ms y pulso bajo = 5 ms, es decir, con un periodo de T=10ms y frecuencia F=100Khz.
Sin embargo lo que se obtiene a la salida es esto:
Vamos, una señal de 125Khz. Es decir, que la instrucción delay(5); no produce un delay de 5ms, sino de 4 ms.
No soy experto con estos PICs, pero después de leer la datasheet, y mirar la configuración que se carga con el bootloader, es probable que lo que estemos obteniendo sea lo que dice la tabla 2-3 de la misma datasheet. Aquí os pongo el trozo:
Entiendo que dado que los bits Fosc3:Fosc1 del registro de configuración se le ha dicho que el modo de reloj sea HS+PLL, el CPU clock que obtenemos sea el que he marcado en la imagen.
Probablemente alguno de vosotros, si tenéis más experiencia con estos PICs nos podáis orientar.
He indicado este tema en los foros de Pinguino también ( http://www.italentshare.com/pinguinoforum/viewtopic.php?f=2&p=116#p115 " onclick="window.open(this.href);return false;), y voy a intentar escribir a J.P. Mandon, para ver si se puede solucionar.
Creo que la solución más interesante es asumir ese reloj, y modificar las librerías que tienen que ver con delays y tiempos, etc....
Si hay noticias, las pongo por aquí.
¿ Alguno de vosotros había experimentado algún problema derivado de este tema de los tiempos ?
Gracias & Saludos,
Sphinx.
Hola!
Lo que comentas ya me había pasado algo similar con la beta 7, y si no recuerdo mal se lo comenté a Uvanov. Veamos la palabra de configuración del pic leída directamente del bootloader:
Esto indica que aunque el oscilador externo es de 20MHz, el PLL interno está configurado para llegar a los 96MHz y que como frecuencia de oscilación real (interna) del pic tenemos (96MHz DIV2) Fosc = 48MHz. Esto hace que la Fcm = Fosc/4 =12MHz y que el tiempo del ciclo máquina es Tcm = 83.333ns, que es lo que tarda en ejecutarse una instrucción.
Es por eso que no cuadraba la temporización de los servos. Lo que no sabía es que fallaba en los delay, aunque lo intuía, por que en temporizaciones largas se iba un poco el tiempo. Lo que pasa es que yo lo achacaba a otra cosa... una pregunta ¿Cuando has hecho las mediciones con es osciloscopio, tenias conectado el cable USB al pinguino?. Yo tuve problemas con eso. En programas que controlan servos mediante la interrupción del timer, cuando estaban ejecutandose y tenia el USB conectado se me desbarataba la temporización. Yo lo achaco a que el USB interrumpe la interrupción (valga la redundancia) del timer y por tanto aumenta el tiempo de las temporizaciones... y lo descuadra todo. Seria interesante averiguar esto.
Salu2!
Hola Bastian,
Tienes razón. La CPU clock resultante son 48 Mhz, y por tanto la duración de cada instrucción son 0,0833 microseg.
Edito mi post anterior...
Saludos,
Sphinx.
Hola,
Le estuve dando vueltas a este problemilla, y ya se por qué sucede lo que explicaba en los posts de más arriba.
La explicación:
La instrucción delay(x) que está codificada en la librería <carpeta de pinguino beta 8>/tools/share/sdcc/include/pic16/arduinodelay.c , produce el retardo debido a que itera x veces la función delay10ktcy(). Esta última está definida en el fichero delay.h y codificada en libpuf.lib y crea un retardo de 10000 ciclos de instrucción. Es decir, que en arduinodelay.c se está asumiendo que 1 milisegundo son 10000 ciclos de instrucción. Pero esto no es válido si tenemos la cpu funcionando a 12 MIPS (esto siempre con el xtal de 20Mhz y la config del PLL que hablábamos anteriormente), porque en este caso necesitamos 12500 ciclos de instrucción para generar 1 segundo de retardo. Así que durante 10000 ciclos, en lugar de 1 ms obtenemos 0,8 ms. Es decir 4/5 de total de milisegundos que le especificamos en la llamada a la función delay(x). Lo mismo que vi en el osciloscopio.
La solución propuesta:
En lugar de llamar a la función arduinodelay.c , crear una nueva que se llame por ejemplo pinguinodelay.c y que contenga el siguiente codigo (A tener en cuenta que 1 ms = 12500 ci, y ,1 us = 12,5 ci):// Delay library for pinguino
// Jesus Carmona begin_of_the_skype_highlighting end_of_the_skype_highlighting begin_of_the_skype_highlighting end_of_the_skype_highlighting begin_of_the_skype_highlighting end_of_the_skype_highlighting Esteban 2010
// This timings are calculated for a microcontroller running at 12 MIPS.
void Delayms(milisecods)
{
unsigned long i;
for (i=0;i<miliseconds;i++) Delayus(10000);
}
void Delayus(microseconds)
{
unsigned long i;
if (microseconds>=2)
for (i=0;i<microseconds;i=i+2) Delay2us(); //This will be more precise.
if (microseconds % 2) Delay1us(); // This will be the odd usec.
}
void Delay2us()
{
Delay1us();
Delay1us();
// there is 1 microsecond lost because of the 0,5 usec left at each Delay1us function, so:
__asm
nop
__endasm
}
void Delay1us()
{
// This is not 100% accurate because 1usec are 12,5 instructions.
// but we put here 12 inst.
__asm
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
__endasm
}
Si a alguien se le ocurre algo mejor, le enviamos otra propuesta a J-P. Mamdon.
Saludos,
Sphinx.
Hola!
A mi no me salen las cuentas... por qué dices que son necesarias 12500 instrucciones para completar un milisegundo. Según mis cálculos son 12000; a saber:
Fosc = 48MHz -> Fcm = Fosc/4 = 12MHz -> Tcm = 1/Fcm = 1/12 us (83.333333333333333333333333ns)
Entonces si el ciclo maquina es de un doceavo de microsegundo, con 12 instrucciones, completamos un microsegundo y con mil microsegundos (12000 instrucciones nop), completamos el milisegundo.
De todas formas, con el código en C solo tendríamos retardos aproximados (y mayores) al valor deseado, por que no estamos teniendo en cuenta el tiempo que tarda en ejecutarse las instrucciones de la función ni sabemos realmente el número se instrucciones ASM que se ejecutan. Por ejemplo, la función Delayus(microsec) contiene un bucle for que llama de manera reiterada a la función Delay2us. Cada llamada a una función implica realizar dos saltos (la llamada y el retorno) y cada salto dura 2 CM (creo) por lo tanto si ejecutamos la función Delayus(6), estaremos llamando 3 veces a la función Delay2us, y esas tres llamadas por si solas duran 1us que añadir al retardo deseado. En realidad, a esto hay que añadir el tiempo que tarda en ejecutarse el codigo ASM que implementa los for, if, etc.
El código es una buena base pero falta ajustarlo para tener retardos más precisos... dejame tiempo que haga unas simulaciones y vemos a ver como se puede ajustar. 😉
Salu2!
PD: Enhorabuena por tu post de la rutina para el control de servos. Solo le he echado un vistazo por encima, pero tiene muy buena pinta. Cuando la pruebe te cuento 😉