[Reeditado 23/1/2011] Para aquellos que programando Pinguino requieran las funciones de generación de retardos, os aconsejo que echéis un vistazo a este artículo, porque a no ser que estéis utilizando una versión del IDE Beta 10 o superior, vais a generar retardos imprecisos. [y todavía es dudoso que en la versión 10 del IDE esto se vaya a solucionar. Editaré el articulo para indicar la versión correcta en el momento en que se sepa].
En el artículo explico la causa del problema, y la solución. Si solo te interesa parchear tu IDE para que los retardos sean correctos ve directamente a la solución.
Problema con las funciones de retardos
Las funciones que existen para generar retados son 2 actualmente:
delay – Para generar retardos de milisegundos.
delayMicroseconds – Para generar retardos de microsegundos.
Estas funciones están codificadas en la librería <carpeta de pinguino>/tools/share/sdcc/include/pic16/arduinodelay.c , y producen los retardos debido a que iteran n veces la función delay100tcy(100), en el caso de delay, y delay10tcy(1), en el caso de delayMicroseconds. Ambas dos, están codificadas en el fichero delay.h y accesibles a través de la librería libpuf.lib.
La que establece la base de retardos es delay10tcy(), puesto que delay100tcy(100) itera 1000 veces la anterior.
delay10tcy() lo que hace es consumir exactamente 10 ciclos de instrucción.
Para Arduino esto es válido. Si tenemos un reloj de CPU de 10 MIPS (Mega Instrucciones Por Segundo), y llamamos a la función delay10tcy() generaremos un retardo de 10/ 10.000.000 = 1 microsegundo.
Sin embargo, el reloj de CPU de Pinguino no es de 10 MIPS. Pinguino trabaja con el xtal de 20MHz, pero el prescaler lo divide entre 5, lo cual da 4 Mhz, el PLL interno lo convierten en 48Mhz. Ese sería el reloj del sistema (CLK).
Para calcular el ciclo de instrucción tenemos que tener en cuenta que dada una de ellas llevará 4 ciclos de reloj del sistema, por lo que CLK / 4 = 48 / 4 = 12 MIPS.
Es decir, que cada ciclo de instrucción tarda en ejecutarse 83,333333 nanosegundos = 0,083333 us.
Si volvemos a tener en cuenta que la función delay10tcy(), veremos que esta genera 10 tcy X 0,083333 us = 0,8333 microsegundos. Es decir, que en lugar de tener un microsegundo de retardo, obtendremos mucho menos tiempo. ¡ Aquí tenemos el fallo en la precisión !
Solución, librería nueva de retardos.
La solución es simplemente recodificar la función delay10tcy() para que en lugar de consumir 10 ciclos de instrucción, consuma 12. La llamaremos delay12tcy(). De este modo, esta función generará 1 microsegundo exacto de retardo.
La función que itera 1000 veces a delay12tcy(), ahora se llamará delay120tcy(100), puesto que consumirá el tiempo de 12000 instrucciones, y así generará 1 milisegundo.
Estas dos funciones, codificadas en ASM, las compilo, y genero con los objetos creados un nuevo fichero de librería que llamo pinguinodelay.lib
Los nuevos ficheros de retardos, ahora son : pinguinodelay.h y arduinodelay.c (modificado para que incluya el fichero pinguinodelay.h).
Para habilitar estas funciones en el IDE:
En Linux:
Estos son las rutas de ubicación de estos ficheros:
/tools/share/sdcc/lib/pic16/pinguinodelay.lib /tools/share/sdcc/include/pic16/pinguinodelay.h /tools/share/sdcc/include/pic16/arduinodelay.c
Los ficheros fuente asm no son necesarios pero es interesante guardarlos por si acaso:
/tools/share/sdcc/lib/src/pic16/pinguinodelay/delay12tcy.S /tools/share/sdcc/lib/src/pic16/pinguinodelay/delay120tcy.S
Ve al apartado de descargas al final de este artículo para obtener todos estos ficheros.
Edita el fichero principal de python pinguinobeta9-05.py, y dentro del «def link(self)», incluir una línea nueva (que puede ir justo debajo de la ya existente : «-llibm18f,» y añadir otra línea con «-lpinguinodelay.lib», . Hay que respetar el indentado, y colocarla a la misma altura que la anterior.
Así es como quedaría el código de la función:
def link(self,filename): # bug 02 replaced sys.path[0] by sys.path[0].replace(" ","\\ ") for path with spaces # jean-pierre mandon / guillaume stagnaro 2008/09/06 # only for the linker !! if (self.debug_output==1): print("link "+self.processor) else: fichier = open(sys.path[0]+"/tmp/stdout", 'w+') sortie=Popen([sys.path[0]+"/tools/bin/sdcc", "-o"+sys.path[0].replace(" ","\\ ")+"/source/main.hex", "--denable-peeps", "--obanksel=9", "--opt-code-size", "--optimize-cmp", "--optimize-df", "--no-crt", "-Wl-s"+sys.path[0].replace(" ","\\ ")+"/lkr/18f2550.lkr,-m", "-mpic16", "-p"+self.processor, "-l"+sys.path[0].replace(" ","\\ ")+"/lib/libpuf.lib", "-llibio"+self.processor+".lib", "-llibc18f.lib", "-llibm18f.lib", "-lpinguinodelay.lib", sys.path[0].replace(" ","\\ ")+"/obj/application_iface.o", sys.path[0].replace(" ","\\ ")+"/obj/usb_descriptors.o", sys.path[0].replace(" ","\\ ")+"/obj/crt0ipinguino.o", sys.path[0].replace(" ","\\ ")+"/source/main.o"], stdout=fichier,stderr=STDOUT) sortie.communicate() fichier.seek(0) self.displaymsg(fichier.read(),0) fichier.close() return sortie.poll()
Ahora cuando se llame a las funciones delay y delaymicroseconds desde tu código y se compile, puede que aparezca el siguiente mensaje en la ventana de depuración:
link library pinguinodelay.lib compilation done.
Lo cual es correcto.
En Windows:
En Windows es distinto que en Linux. Esto al menos lo he probado en la versión Beta 9.05.
Hay que copiar los ficheros a los siguientes directorios:
El fichero pinguino.lib:
\tools\bin\lib\pic16\pinguinodelay.lib
Los ficheros arduinodelay.c y pinguinodelay.h :
\tools\include\pic16\pinguinodelay.h \tools\include\pic16\arduinodelay.c
Y finalmente editar al fichero :
\tools\link.bat
que es un fichero de una sola linea, y añadir el path de la librería (.lib), es decir el ya mencionado:
%1\tools\share\sdcc\lib\pic16\pinguinodelay.lib
El contenido de este fichero debería ser exactamente este:
%1\tools\bin\gplink.exe -o %1\source\main.hex -I%1\tools\bin\lib\pic16 %1\lib\libpuf.lib %1\tools\bin\lib\pic16\libio%2.lib %1\tools\bin\lib\pic16\libc18f.lib %1\tools\bin\lib\pic16\pic%2.lib libsdcc.lib %1\tools\bin\lib\pic16\pinguinodelay.lib -w -s%1\lkr\18f2550.lkr %1\obj\application_iface.o %1\obj\usb_descriptors.o %1\obj\crt0ipinguino.o %1\source\main.o
De este modo los ficheros compilarán sin problemas.
Notas de uso de las nuevas funciones:
Si miras el fichero de definiciones, pinguinodelay.h, notarás que la función delayMicroseconds solo acepta valores de 1 a 255. Esto es porque el tamaño maximo del parámetro que se le pasa a la rutina en asm es de 1 byte. Así que si quieres conseguir un retardo de más de 255 microsegundos, debes de llamar a esta función varias veces seguidas en tu código. Por ejemplo, supongamos que necesitamos generar un retardo de 780 microsegundos. Lo más fácil sería hacer lo siguiente:
.... delayMicroseconds(250); // un cuarto de milisegundo. delayMicroseconds(250); // segundo cuarto de milisegundo delayMicroseconds(250); // hasta aquí irían 750 microsegundos. delayMicroseconds(30); // y aquí el total de 780 microsegundos. ....
Para retardos mayores a 1 milisegundo, se puede utilizar la ya conocida función delay, ya que el parámetro de entrada de la misma es un unsigned long que nos permite un valor de hasta 4.294.967.295 milisegundos.
Tan pronto como este código se incluya en el IDE, no será necesario parchearlo de este modo.
Descargas:
Para descargar el fichero comprimido que contiene todos los ficheros mencionados en este artículo, visitad esta página de descargas.
Espero que os sea útil mientras no se integre esta librería en el IDE oficial de Pinguino.