8. Rutina de retardo

Bien, ya pudimos sacar un dato por el puerto B del PIC 16F84A, ahora aprenderemos nuevas instrucciones que nos permitan “jugar” con las salidas del microcontrolador.

A continuación se presenta una nueva instrucción:

               GOTO          etiqueta

Básicamente lo que hace es ir a la línea de código donde se encuentre la etiqueta especificada, la usaremos para elaborar el segundo programa de este tutorial. Para empezar tengamos en cuenta el programa del capítulo anterior y reemplacemos el cuerpo del programa por el siguiente:

inicio       MOVLW     b’11111111′       ; W se carga con “unos”

                MOVWF    PORTB              ; para transferirlos al puerto B.

                MOVLW     b’00000000′     ; W se carga con “ceros”

                MOVWF    PORTB              ; para transferirlos al puerto B.

                GOTO        inicio                  ; Va a inicio para repetir la secuencia

Este código simplemente enciende los LED’s y luego los apaga, la instrucción GOTO hace que esto se repita una y otra vez creando un bucle infinito. Si implementamos el código no seremos capaces de ver lo que ocurre, solo veremos los LED’s encendidos todo el tiempo y quizás con la mitad del brillo normal; no es que el programa no realice lo esperado sino que las instrucciones se realizan tan rápido que nuestra vista no podrá percibir las secuencias.

Recordando el concepto de ciclo de máquina tenemos que para un cristal de cuarzo de 4MHz cada instrucción básica demora en ejecutarse “1 microsegundo”, lo cual es un tiempo demasiado corto en el que prácticamente no nos damos cuenta de nada.

La solución a este problema es aumentar el tiempo que hay desde el encendido de los LED’s hasta el apagado y viceversa con el fin de que nuestra vista pueda apreciar la secuencia, para lograr esto habrá que poner al PIC a “quemar” tiempo, este proceso recibe el nombre de rutina de retardo.

¿Cómo hacer una rutina de retardo?

Antes de generar una rutina de retardo es conveniente aprender una instrucción muy importante:

             CALL       etiqueta

Esta instrucción sirve para hacer llamadas a distintas líneas de código. Es muy parecida a la instrucción GOTO, la diferencia es que CALL es una instrucción obligada a regresar al sitio desde donde se hizo la llamada, esta propiedad la podemos usar para realizar funciones y de ese modo podemos separar el código de un programa en diferentes partes cada una realizando una función específica. En este capítulo usaremos CALL para llamar a una función a la que se le asignó la etiqueta “retardo”, con lo cual el código que llevamos quedaría de la siguiente manera:

inicio       MOVLW     b’11111111′       ; W se carga con “unos”

                MOVWF    PORTB              ; para transferirlos al puerto B.

                CALL         retardo               ; Llama a un retardo de tiempo

                MOVLW     b’00000000′     ; W se carga con “ceros”

                MOVWF    PORTB              ; para transferirlos al puerto B.

                CALL         retardo               ; Llama a un retardo de tiempo

                GOTO        inicio                  ; Va a inicio para repetir la secuencia

Ya se ha llamado al retardo, ahora habrá que crearlo. Para generar retardos lo que se hace es ordenarle al PIC que haga algunas instrucciones adicionales con el objetivo de ir consumiendo tiempo. La instrucción más usada para este fin es:

               DECFSZ          registro,1

La función de esta instrucción es restarle una unidad al registro especificado y si el resultado da cero se saltará la siguiente línea del código, el ‘1’ que va después de la coma es simplemente un indicador que le dice al programa que el resultado de la resta lo guarde en el mismo registro (si hubiera un ‘0’ el resultado se guardaría únicamente en el registro W). Para usar esta instrucción se hace necesario usar un registro de propósito general, el PIC 16F84A dispone de 68 ubicados desde la dirección 0Ch (0X0C) hasta la 4Fh (0X4F), para más detalle se puede consultar la memoria de datos que ya hemos visto en la figura 2.10 del capítulo 6.

A continuación se le dará un valor inicial a un registro que se ha llamado “reg” y luego se le aplicará la función DECFSZ:

               MOVLW       0X03        ; W se carga con el número 3

               MOVWF      reg            ; pone el 3 en el registro

               DECFSZ     reg,1         ; Le resta una unidad al registro y el resultado es 3-1=2

No “quemó” mucho tiempo, por eso vamos a agregar un GOTO para crear un bucle, en este caso finito ya que se requiere que en cierto momento el programa se salga del bucle:

                MOVLW     0X03       ; W se carga con el número 3

                MOVWF    reg           ;  pone el 3 en el registro

etiqueta  DECFSZ   reg,1        ; Le resta una unidad al registro

                GOTO        etiqueta    ; Regresa y vuelve a restar

Lo que hace este código es  asignarle el número 3 al registro y luego lo decrementa, como el resultado da 2 la función GOTO envía de nuevo al programa a decrementar al registro y ahora el resultado da 1; se realiza de nuevo la función GOTO, pero cuando se decrementa, el valor del registro pasa de 1 a 0 y esta vez ya no se realizará la instrucción GOTO, con lo cual, el programa se sale del bucle que habíamos creado. Básicamente, esta es una manera de gastar más tiempo realizando instrucciones, ahora le aumentaremos un poco su complejidad para obtener rutinas de retardo con mayor duración de tiempo.

Rutinas de retardo anidadas

Al añadir una rutina de retardo dentro de otra conseguimos aumentar significativamente el tiempo gastado en instrucciones. A continuación se presenta el código para tres rutinas de retardo anidadas, para el cual usaremos tres registros que he convenido llamar reg1, reg2 y reg3:

retardo    MOVLW     0X20       ; W se carga con el número 20h (Comienza la llamada)

                MOVWF    reg3          ; y se pasa a reg3

externo   MOVLW     0X30        ; W se carga con el número 30h

                MOVWF    reg2          ; y se pasa a reg2

mitad      MOVLW     0X50        ; W se carga con el número 50h

                MOVWF    reg1          ; y se pasa a reg1

interno    DECFSZ   reg1,1       ; Le resta una unidad a reg1

                GOTO        interno       ; sigue decrementando hasta que reg1 llegue a 0

                DECFSZ   reg2,1       ; Le resta una unidad a reg2 cuando reg1 llegue a 0

                GOTO        mitad         ; vuelve a cargar reg1 y se repite la rutina interna

                DECFSZ   reg3,1       ; Le resto una unidad a reg3 cuando reg2 llegue a 0

                GOTO        externo      ; vuelve a cargar reg2 y reg1, se repite la rutina de la mitad

                RETURN                      ; Termina la llamada y regresa

El código se ejecuta cuando la función CALL dirija al programa hacia la etiqueta “retardo”, estando allí lo primero que se hace es cargar los tres registros, una vez hecho esto comenzará a decrementarse el registro ubicado en el bucle interno (reg1), cuando reg1 llegue a ‘0’ se decrementa reg2 y se repite el bucle interno. Cuando reg2 llegue a ‘0’ se repetirá el bucle de la mitad y cuando reg3 llegue a ‘0’ se finaliza el bucle externo. Notemos que entre más adentro esté un bucle más veces se repetirá. La instrucción RETURN se encarga de finalizar la llamada, con lo cual el programa regresa hacia la línea siguiente desde donde fue hecha dicha llamada.

Calculando el tiempo del retardo

Ya habiendo entendido el código lo más importante ahora es diseñarlo para que el retardo dure el tiempo que queramos implementar. Lo que tenemos que hacer es sumar la cantidad de ciclos de máquina que gasta nuestra rutina de retardo y multiplicar el resultado por el tiempo de duración de un ciclo de máquina, que para un cristal de 4MHz es de 1 microsegundo. A continuación se muestran los ciclos de máquina de las instrucciones más utilizadas cuando se generan retardos:

CALL ———-> 2 ciclos de máquina

MOVLW ——> 1 ciclo de máquina

MOVWF ——> 1 ciclo de máquina

DECFSZ —–> 1 ciclo de máquina (si la operación da ‘0’ se gastan 2 ciclos de máquina)

GOTO ———> 2 ciclos de máquina

RETURN —–> 2 ciclos de máquina

NOP ———–> 1 ciclo de máquina

Acabamos de incluir una nueva instrucción en nuestro repertorio: NOP. Esta instrucción significa no hacer nada (“quemar tiempo”), es muy usada para ajustar los tiempos de las rutinas de retardo.

Para simplificar las cosas, aquí dejo un programa que sirve para generar automáticamente rutinas de retardo de la duración que queramos: Picdel_sp.

Ya estamos preparados para realizar el programa completo, el cual consiste básicamente en acomodar las piezas de código que hemos estudiado, en este enlace podrán descargar el programa terminado: prog_2. Cada línea tiene su comentario de explicación. El circuito sigue siendo el mismo de la figura 2.19.