• Publicidad

Generador de código de espera para microcontroladores PIC

¿Estás desarrollando un proyecto, o piensas hacerlo? Pon aquí tu propuesta, lo más seguro es que alguien esté interesado en ayudarte.

Generador de código de espera para microcontroladores PIC

Notapor explorer » 2014-05-18 20:14 @885

Impacientes: saltar a la sección Programa

Resumen
Se propone resolver la cuestión de la realización de retardos en microcontroladores PIC por medio de un programa que genera el código necesario.

Introducción
En el mundo de la programación de microcontroladores, a veces ocurre que debemos crear un bucle de espera o retardo en el código, quizás porque necesitamos sincronizar el sistema con un sistema externo o con el mundo físico, o simplemente porque sabemos que no necesitamos que el sistema atienda ninguna actuación externa durante un tiempo, o porque el sistema externo necesita de una actuación precisa por parte del sistema microcontrolador.

En la mayor parte de las ocasiones, ese tipo de esperas se puede realizar con:
  • los temporizadores internos
  • esperando en un bucle, leyendo las entradas hasta que se produzca un evento (un temporizador externo o la acción de un actuador externo)
  • poner el controlador en modo SLEEP. El microcontrolador se despertará cuando ocurra un evento o interrupción

Justificación
Pero no siempre es así. Hay ocasiones en las que necesitamos esperar un tiempo muy determinado o un número exacto de ciclos.

Los retardos se suelen crear como bucles sencillos:
Sintáxis: [ Descargar ] [ Ocultar ]
Using asm Syntax Highlighting
  1.         movlw   0x42    ; 1 ciclo
  2.         movwf   d1      ; 1 ciclo
  3. Delay_0
  4.         decfsz  d1, f   ; 1 ciclo
  5.         goto    Delay_0 ; 2 ciclos
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4


Explicación
Pero no es tan sencillo, ya que:
  • el periodo de un ciclo depende de la frecuencia de reloj del microcontrolador
  • cada vuelta decrementado-goto consume un número de ciclos fijo, con lo que a veces no se obtiene un resultado exacto
Del ejemplo, vemos que el bucle se ejecuta 0x41 vueltas consumiendo, en cada una de ellas, 1 ciclo en la instrucción de decrementado, más 2 ciclos por la instrucción goto. En la última vuelta, cuando 'd1' pasa de '1' a '0', el decrementado hace "saltar" la instrucción goto, terminando el bucle. Eso suma 2 ciclos por esa última vuelta. Y hay que sumar los dos ciclos consumidos por las dos primeras instrucciones, donde se inicializa el contador 'd1'.

Echando cuentas: 2 + 0x41 * 3 + 2 = 199 ciclos.

Si quisiéramos tener exactamente 200 ciclos, estamos obligados a añadir un 'nop' al final (1 ciclo de instrucción más).

La cosa se complica si queremos tiempos de espera mayores. En esos casos, se hacen bucles anidados. Así, los ciclos consumidos por los bucles más profundos son multiplicados por los exteriores. Además, queremos que esos bucles internos consuman aún más ciclos que un ciclo normal, ya que queremos realizar una espera mucho más larga.

Una posibilidad es la de encadenar los goto de cada bucle anidado, haciendo que se sume 2 ciclos en cada salto. Ejemplo para tres bucles anidados:
Sintáxis: [ Descargar ] [ Ocultar ]
Using asm Syntax Highlighting
  1.         movlw   0x91            ; 1 ciclo
  2.         movwf   d1              ; 1 ciclo
  3.         movlw   0xFC            ; 1 ciclo
  4.         movwf   d2              ; 1 ciclo
  5.         movlw   0xDA            ; 1 ciclo
  6.         movwf   d3              ; 1 ciclo
  7. Delay_0
  8.         decfsz  d1, f           ; 1 ciclo
  9.         goto    $+2             ; 2 ciclos
  10.         decfsz  d2, f           ; 1 ciclo
  11.         goto    $+2             ; 2 ciclos
  12.         decfsz  d3, f           ; 1 ciclo
  13.         goto    Delay_0         ; 2 ciclos
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
En cada vuelta del bucle que decrementa 'd1', el goto salta al goto del nivel más superior, sumando 2 ciclos más. Así, con 3 bucles anidados tenemos que se consumen 7 ciclos por cada vuelta. Para el bucle de 'd2', son 5 ciclos por vuelta.

Además, cuando 'd1' agota su valor inicial, en la siguiente vuelta (impuesta por la siguiente vuelta de 'd2'), comenzará a decrementar desde '0', por lo que realizará 255 vueltas consumiendo 7 ciclos (más una vuelta final, de 2 ciclos).

El cálculo de los ciclos generados es el siguiente:
<ciclos generados por N niveles de anidamiento>
= <ciclos para la inicialización de N niveles>
+ <ciclos generados por las últimas vueltas de los bucles>
+ <ciclos generados por el bucle más interno>
* ∑(i=1,i=N){ 256^(i-1) * (di-1) }

= 2 * N + 2 * N + (2 * N + 1) * ∑(i=1,i=N){ 256^(i-1) * (di-1) }
= 2 * 2 * N + (2 * N + 1) * ∑(i=1,i=N){ 256^(i-1) * (di-1) }

Este cálculo no siempre es exacto con respecto a la espera que se desea: se obtienen números de ciclo que son múltiplos de los <ciclos generados por el bucle más interno>. Eso implica que a veces faltan (<ciclos generados por el bucle más interno> - 1) para llegar a la cifra correcta.

Esto se resuelve con el añadido de instrucciones adicionales, después de los bucles anidados. Poniendo una instrucción de goto, tenemos 2 ciclos más. Y poniendo una instrucción nop, 1 ciclo más.

El cálculo de los parámetros 'di' es el siguiente:
ciclos := <número_de_ciclos_que_deseamos_generar> - (2 * 2 * N)
loop := int(ciclos / (1 + 2 * N))
Para i=1 a N:
di := 1 + (int(loop/(256^(i-1))) % 256)
:Fin Para
ciclos_restantes_al_final := ciclos - loop * (1 + 2 * N)
Primero se va probando para N=1 (un solo bucle), y si la cantidad de ciclos generada no es suficiente, se repite el cálculo para N=2, y así sucesivamente.

Programa
Para facilitar la tarea del cálculo, se muestra el siguiente programa escrito en Perl, que genera un salida en texto con el código fuente en ensamblador que produce la espera deseada.

Como argumentos, se indicará la frecuencia de trabajo de la señal de reloj del microcontrolador, y la espera deseada, expresada tanto en forma de tiempo como en forma de ciclos de instrucción. Opcionalmente, se puede indicar argumento '-s' seguido de un nombre, y se generará el mismo código, pero ajustado para que sea incorporado al programa principal en forma de subrutina.

Ejemplos de llamada:

./delay_pic.pl -freq 4mhz -delay 1s
./delay_pic.pl -frec 32768hz -espera 200ms
./delay_pic.pl -f 20000000 -d 1000000
./delay_pic.pl 20mhz 500us
./delay_pic.pl -sub Wait_4µs 64Mhz 4us


El código se encuentra en Github: https://github.com/joaquinferrero/delay_pic

Ejemplo de salida:
Sintáxis: [ Descargar ] [ Ocultar ]
Using bash Syntax Highlighting
  1. $ ./delay_pic.pl -freq 4mhz -delay 779  -v -s                                  
  2. Generador de bucles de espera en ensamblador PIC
  3. Joaquín Ferrero (alias explorer). 2014.10.09
  4.  
  5. ; ----------------------------------------------------------------------------------------------------
  6. ; Espera = 779 ciclos de instrucción
  7. ; Frecuencia de reloj = 4mhz
  8. ;
  9. ; Espera real = 0.000779 segundos = 779 ciclos
  10. ; Error = 0.00 %
  11.  
  12.         cblock 0x70
  13.                 Espera_d1
  14.                 Espera_d2
  15.         endc
  16.  
  17. Espera:
  18.                                         ;773 ciclos
  19.                 movlw   0x9A
  20.                 movwf   Espera_d1
  21.                 movlw   0x01
  22.                 movwf   Espera_d2
  23.  
  24. Espera_loop:
  25.                 decfsz  Espera_d1, f
  26.                 goto    $+2
  27.                 decfsz  Espera_d2, f
  28.                 goto    Espera_loop
  29.  
  30.                                         ;2 ciclos
  31.                 goto    $+1
  32.  
  33.                                         ;4 ciclos (incluyendo la llamada)
  34.                 return
  35.  
  36. ; Generado por delay_pic.pl (Joaquín Ferrero. 2014.10.09) jue 09 oct 2014 00:02:15 CEST
  37. ; ./delay_pic.pl -freq 4mhz -delay 779 -v -s
  38. ; generador-de-codigos-de-retardo-para-microcontroladores-pic-t8602.html
  39. ; ----------------------------------------------------------------------------------------------------
  40.  
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4

La salida es -prácticamente- igual al generador de Golovchenko. Hay algunas diferencias: aparte del idioma, este programa genera siempre una solución basada en bucles, a diferencia del de Golovchenko, en que en ocasiones, genera ciclos por medio de más sentencias goto $+1 extras (ejecutarlo para 779 ciclos de espera, a 4 Mhz, por ejemplo).
Última edición por explorer el 2015-06-18 17:44 @780, editado 10 veces en total
Razón: Nueva versión, con enlace al repositorio oficial en Github
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Publicidad

Re: Generador de código de espera para microcontroladores PI

Notapor BigBear » 2014-06-01 18:26 @809

En la facultad nos enseñaron sobre cómo programar en PIC, me pareció infernal :D
BigBear
Perlero frecuente
Perlero frecuente
 
Mensajes: 981
Registrado: 2009-03-01 18:39 @818

Re: Generador de código de espera para microcontroladores PI

Notapor explorer » 2014-06-02 13:07 @588

La estructura interna es un poco peculiar, sí.

Se ve que va arrastrando la herencia de circuitos anteriores. Y el tema de la memoria paginada o por bancos.

Bueno, pero programando en C, la complejidad de los bancos desaparece.

A mí, el 16F877A me parece una auténtica virguería. ¡14 interrupciones distintas y cinco puertos de E/S!

Me han dicho que los AVR están mucho mejor, más cartesianos.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Proyectos

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado