7 ago 2022

MICROCONTROLADORES PROGRAMABLES PIC (II)



Continuamos con la siguiente publicación a cerca de Microcontroladores Programables PIC. Hoy empezaremos a redactar nuestro código, y algunos ejemplos prácticos.





EJEMPLO DE CÓDIGO.

Vamos a ver un ejemplo de código, en el que aplicaremos lo que acabamos de aprender. Este ejemplo no es necesario compilar todavía, ya que lo haremos cuando hagamos el primer programa. Ver esto simplemente como muestra de cómo se hace la programación de lo explicado anteriormente y de paso veremos un par de instrucciones.

Empecemos. Y vamos a poner el Puerto A como en el ejemplo anterior. Lo primero, necesitamos cambiar del banco 0 al banco 1. Hacemos esto modificando el registro STATUS, que está en la dirección 03h, poniendo el bit 5 a 1. 

1
BSF 03h,5


La instrucción BSF significa en ingles "Bit Set F" (Poner a 1 un bit de la Memoria). La letra F significa que vamos a utilizar una posición de memoria, o un registro en memoria. Vamos a utilizar dos números después de esta instrucción. 03h, que se refiere a la dirección del registro STATUS, y el número 5 que corresponde al número de bit. Por tanto, lo que estamos diciéndole al PIC es "pon a 1 el bit 5 de la dirección de memoria 03h". Ahora ya estamos en el banco 1. 

1
MOVLW b'00110'


En esta instrucción, estamos poniendo el valor binario 00110 en nuestro registro de propósito general W (la letra 'b' significa que el número está escrito en binario). Si preferimos hacerlo en hexadecimal, la instrucción hubiese sido así: 

1
MOVLW 06h


En las dos tenemos el mismo resultado. La instrucción MOVLW significa en inglés "Move Literal Value into W", es decir, mover un valor literal directamente al registro W
El siguiente paso es poner este valor en el registro TRISA para configurar el puerto: 

1
MOVWF 85h


Esta instrucción significa "poner los contenidos de W en el registro cuya dirección viene a continuación", en este caso la dirección 85h, que apunta a TRISA. Es decir, ahora tiene el valor 00110 o visto gráficamente:



Ahora tenemos que configurar los pines del Puerto A, y para eso necesitamos volver al banco 0 para poder manejar cualquier dato.

1
BCF 03h,5


Con la instrucción BCF hacemos lo contrario a BSF. Significa "Bit Clear F", o poner a 0 un bit de la memoria. Los dos números que le siguen son, la dirección del registro, en este caso del registro STATUS (03h), y el número de bit, es este caso el 5. Así que lo que hemos hecho ahora es poner a 0 el bit 5 del registro STATUS. 
Este es el código en un solo bloque: 

1
2
3
4
BSF        03h,5      ; Vamos al banco 1
MOVLW      06h        ; Poner 00110 en W
MOVWF      85h        ; Mueve 00110 a TRISA
BCF        03h,5      ; Vuelve al Banco 0


ESCRIBIR EN LOS PUERTOS.

El anterior código, realmente no vemos un resultado, ya que muestra como configurar los pines de un puerto del PIC como entradas o como salidas. Pero ahora, vamos a mostrar como enviar datos a los puertos. Es decir, veremos como resultado que un LED parpadea. Será como el típico “Hello World!” de todos los programadores, en el que veremos el listado completo y el esquema del circuito, para poder ver nuestro PIC haciendo exactamente lo que esperamos que haga. 
Vamos a ir escribiendo el código, y explicando que hace. Así que hasta el final, no deberíamos compilar nada, ya que son ejemplos:

1
2
3
4
BSF        03h,5      ; Vamos al banco 1
MOVLW      00h        ; Poner 00000 en W
MOVWF      85h        ; Mueve 00110 a TRISA, colocando todos los pines como salidas.
BCF        03h,5      ; Vuelve al Banco 0


Como vemos, esto es muy similar a lo expuesto  en el capítulo anterior. La única diferencia es que hemos puesto todos los pines del Puerto A como salidas, poniendo 0h en el registro tri-estado (TRISA). Y que hemos escrito todo en minúsculas, para evitar posibles errores de compilación.

Ahora lo que tenemos que hacer es encender el LED. Y para ello tenemos que colocar el pin donde irá conectado el LED, a nivel alto. Es decir, enviamos un 1 al pin:

1
2
movlw      02h        ; Escribe 02h en el registro W. En binario sería 00010.Con esto ponemos a 1 el bit 2 (Pin 18) y mantiene los otros pines a 0.
movwf      05h        ; Ahora mueve los contenidos de W (02h) al puerto A, cuya dirección en 05h.


En este punto, nuestro LED estará encendido, y como queremos que parpadee, ahora vamos a  apagarlo:

1
2
movlw      00h        ; Escribe 002h en el registro W. Esto pone todos los pines a 0.
movwf      05h        ; Ahora mueve los contenidos de W (00h) al puerto A, cuya dirección en 05h.


De esta forma, lo que hemos hecho, ha sido encender y apagar el LED una vez. Pero nuestro objetivo es que nuestro LED se encienda y se apague continuamente. Para conseguir esto, tenemos que volver al principio del programa.

Esto lo podemos hacer, colocando lo que se llama una etiqueta al comienzo de nuestro programa, y luego diciéndole al programa que vaya a ese punto constantemente. Esta sería la forma de hacerlo: 

1
2
3
4
5
6
Inicio                ; Esta es la etiqueta que indica Inicio
movlw      02h        ; Escribe 02h en el registro W. En binario sería 00010. Esto pone a 1 el bit 2 (pin 18), y mantiene el resto a 0.
movwf      05h        ; Ahora mueve los contenidos de W (02h) al puerto A, cuya dirección en 05h.
movlw      00h        ; Escribe 00h en el registro W. Esto pone todos los pines a 0
movwf      05h        ; Ahora mueve el contenido de W (00h)al puerto A, cuya dirección es 05h.
goto       Inicio     ; va a la etiqueta de Inicio.


Así pues, primero establecemos la etiqueta 'Inicio' justo al comienzo del programa. Después, justo al final del programa decimos simplemente 'goto Inicio', es decir, ve a Inicio. Esta  instrucción 'goto' significa 'ir a', y eso es lo que hace. 
Este programa encenderá y apagará el LED constantemente, desde el momento que le demos alimentación al circuito, y se detendrá cuando le quitemos la alimentación. 

Ahora bien, si nos fijamos, todo lo que hay son son instrucciones y números. Y esto puede ser algo confuso a la hora de depurar el programa, o cuando se escribes código y hemos de recordar todas las direcciones de memoria. Incluso con los comentarios puestos, puede ser un poco lioso. Así que lo que necesitamos es dar nombres a estos números. Y esto se consigue con otra instrucción: "equ
La instrucción "equ" simplemente significa que algo equivale a algo, literalmente del término inglés "equivalence", es decir, equivalencia. Esta no es una instrucción del PIC, sino para el ensamblador. Con esta instrucción podemos asignar un nombre a la dirección de localización de un registro, o en términos de programación asignar una constante. Vamos a establecer algunas constantes para nuestro programa, y será más sencillo de leer.

1
2
3
STATUS     equ 03h    ; Asigna a STATUS el valor 03h, que es la dirección del registro STATUS.
TRISA      equ 85h    ; Asigna a la palabra TRISA el valor 85h, que corresponde a la dirección del registro tri-estado del puerto A.
PORTA      equ 05h    ; Asigna a la palabra PORTA el valor 05h, que corresponde a la dirección del puerto A.


Así que ahora que hemos establecido nuestros valores constantes, vamos a ponerlos en nuestro programa. Los valores constantes deben ser definidos antes de que los usemos. 
Para estar seguros de ello los ponemos siempre al comienzo del programa. Vamos a reescribir nuestro código sin comentarios, y se puede ver que las constantes hacen el programa un poco más sencillo. Sin embargo, no hemos terminado todavía:

1
2
3
4
5
6
7
8
9
10
11
12
13
STATUS     equ 03h    
TRISA      equ 85h    
PORTA      equ 05h    
           bsf STATUS,5       
           movwf 00h   
           movlw TRISA
           bcf STATUS,5        
Inicio
           movlw 02h
           movwf PORTA
           movlw 00h
           movwf PORTA
           goto Inicio     



BUCLES DE RETARDO.

Pero si compilamos este código, y lo cargamos en nuestro PIC, observaremos que hay un inconveniente en nuestro programa del LED parpadeante. Y es que cada instrucción necesita un ciclo de reloj para ser completada. Si utilizamos un cristal de 4 Mhz, cada instrucción tardará  1 microsegundo en ser completada. Como solo estamos usando 5 instrucciones, el LED se encenderá y apagará en 5 microsegundos, y parecerá que el LED está permanentemente encendido, ya que esto es demasiado rápido para que lo podamos ver.
En ese caso lo que necesitamos hacer, es introducir un retardo entre el momento de encendido y apagado y viceversa. 
El principio para hacer retardos es, contar hacia atrás desde un número previamente establecido y cuando llegue a cero, paramos de contar. El valor cero indica el fin del retardo y continuamos nuestro camino a través del programa.

Así que lo primero que necesitamos hacer es definir una constante que usaremos como contado y la llamaremos CONTADOR. Luego necesitamos decidir el tamaño del número desde el que contar y el número mayor que podemos tener es 255 o FFh en hexadecimal. Ahora, como hemos mencionado en el apartado anterior, la instrucción equ asigna una palabra a una localización de un registro. Esto significa que cualquiera que sea el número que asignemos a CONTADOR, será igual al contenido de un registro. 

Pero nos encontramos que, si asignamos el valor FFh, el compilador entenderá que estamos asignando la dirección de memoria FFh a la constante, y obtendremos un error cuando vayamos a compilar el programa. Esto es debido a que la localización FFh está reservada, y por tanto no podemos acceder a ella. Así que, ¿Cómo hacemos para asignar un número real?
Pues hemos de asignar a nuestro CONTADOR, por ejemplo, a la dirección 08h, este apuntará a un registro de propósito general. Las posiciones de memoria tienen un valor por defecto de FFh. De este modo, si CONTADOR apunta a 08h, tendrá un valor de FFh la primera vez que lo pongamos en marcha. 
Y si deseamos asignar un valor distinto en CONTADOR, todo lo que tenemos que hacer es primero 'mover' un valor a esta posición. 
Por ejemplo, si queremos que CONTADOR tenga un valor de 85h, no podemos decir 'CONTADOR equ 85h' porque esta es la localización del registro tri-estado del puerto A (TRISA). Así que lo que haremos es esto:

1
2
movlw      85h        ; Ponemos el valor 85h en el registro W.
movwf      08h        ; y lo movemos a nuestro registro 08h.


Ahora si podemos decir 'CONTADOR equ 08h', y nuestro CONTADOR será igual al valor 85h. Así que lo primero de todo es definir nuestra constante:

1
CONTADOR equ 08h


Y a continuación necesitamos disminuir este CONTADOR en 1 hasta alcanzar cero. Y disponemos de una sola instrucción que hace esto por nosotros, con la ayuda de un 'goto' y una etiqueta. La instrucción que usaremos es:

1
decfsz CONTADOR,1

Esta instrucción hace "resta 1 al registro (en este caso CONTADOR). Y si llegamos a cero, salta 2 posiciones hacia delante".

El valor que le sigue a la coma, indica donde debe almacenarse el resultado de la operación. Si es 1, como en el ejemplo anterior, el resultado se almacena en el mismo registro indicado en la instrucción, y si es 0 el resultado se almacena en el registro w:


1
2
3
4
5
6
7
CONTADOR   equ 08h             ;Asignamos el valor de CONTADOR
BUCLE      decfsz COMNTADOR,1  ;Restamos 1 a CONTADOR, y...  
           goto BUCLE          ;Si <> 0, sigue aquí.    
           ;Continua por aquí  ;Si = 0, sigue aquí.       
           :   
           :
           :


Lo que hemos hecho es, primero poner nuestra constante CONTADOR a 255
La siguiente línea pone una etiqueta, llamada BUCLE seguida de nuestra instrucción decfsz. La instrucción decfsz CONTADOR,1 disminuye el valor de CONTADOR en 1, y almacena el resultado de vuelta en CONTADOR. También comprueba si CONTADOR tiene valor 0. Si no lo tiene, hace que el programa salte a la siguiente línea. Aquí tenemos una instrucción de 'goto' que nos envía de vuelta a nuestra instrucción decfsz. Cuando el valor de CONTADOR sea igual a cero, entonces la instrucción decfsz hace que el programa salte dos lugares hacia adelante, y se sitúe donde hemos escrito "Continua por aquí". 
Así que, lo que hemos hecho es que el programa permanezca en este punto durante un tiempo predeterminado antes de seguir adelante. A esto se le llama bucle de retardo. En el caso de necesitar un retardo mayor, podemos poner un bucle detrás de otro o uno dentro de otro. Por tanto, cuantos más bucles pongamos, mayor será el retardo. Para nuestro ejemplo si queremos ver parpadear al LED, vamos a necesitar por lo menos dos. 

Así que vamos a poner los bucles de retardo y terminaremos haciendo un programa real añadiendo los comentarios. Este es el código completo (Usar las barras de desplazamiento para ver todo el código):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
;**********************************************************;   
; PROGRAMA LED1.asm                    FECHA: XX XXXXX 2022  ; 
; Programa para hacer parpadear un LED                       ;
; Usa el puerto RA 1 (Pin 18)                                 ;
; Revisión: 2.0                   Programa para PIC16F48A      ;
; Velocidad de Reloj: XTAL externo 4Mhz (1Mhz=1us)          ;
; WatchDog = OFF                     Tipo Reloj:  Cristal      ;
; Autor: XMG                      Por: XANUR2001/ ACME 2022    ;
; Compilado en : MPLAB X IDE v2.05 (MICROCHIP)              ;
; *********************************************************;
;
;*****Establecimiento de constantes *******
STATUS      equ       03h           ; Dirección del registro STATUS
TRISA       equ       85h           ; Dirección del registro Puerto A.
PORTA       equ       05h           ; Dirección del Puerto A.
CONTADOR1   equ       08h           ; Primer contador para bucles de retardo.
CONTADOR2   equ       09h           ; Segundo contador para bucles de retardo
;
;****Configuración del Puerto****
            bsf       STATUS,5       ; Cambiamos al banco 1
            movlw     00h            ; Ponemos pines del puerto A ...
            movwf     TRISA          ; ...como salidas.
            bcf       STATUS,5       ; Y volvemos al Banco 0.
;
;****Encendido del LED ****
Inicio      movlw     02h             ; Encendemos el LED poniendo primero el valor...
            movwf     PORTA           ;... en el registro W y después al puerto
;  
;****Inicio del buble de retardo 1****
Bucle1      decfsz    CONTADOR1,1     ; Restamos 1 a 255.
            goto      Bucle1          ; Si CONTADOR=0 continuamos.
            decfsz    CONTADOR2,1     ; Restamos 1 a 255 
            goto      Bucle1          ; Volvemos al inicio de nuestro bucle
; Este retardo cuenta hacia atrás desde 255 a 0, 255 veces.
;
;****Retardo terminado, ahora apagamos el LED ****
            movlw     00h             ; Apaga el LED poniendo primero el alor ...
            movwf     PORTA           ; en el registro w y después  al puerto
;
;****Añadimos otro retardo****
Bucle2      decfsz    CONTADOR1,1     ; segundo bucle mantiene el LED apagado...
            goto      Bucle2          ; el tiempo suficiente...
            decfsz    CONTADOR2,1     ; ...para poder verlo
            goto      Bucle2          ;
;
;****Ahora volvemos al inicio del programa
            goto      Inicio          ; Vuelve al principio y enciende el LED.
;****Termina el Programa****
            end           
; Algunos compiladores necesitan esta instrucción. O por si acaso olvidamos poner la instrucción 'goto'. 


Este código se puede compilar y programar el PIC con él, para poder probar el circuito y ver cómo funciona. 

Este es el sencillo esquema del circuito, para poder construirlo y probar el PIC con nuestro primer programa:



Con esto, acabamos de escribir nuestro primer programa y construir el circuito para hacer parpadear un LED. Nuestro “Hello World!” para PIC. 


Hasta este punto, hemos visto 7 instrucciones de las 35, y hemos visto como controlar los puertos de entrada/salida. 
Con esta base, se pude intentar modificar los bucles de retardo, para hacer que el LED parpadee más rápido, o más lento, añadir más bucles, incluso ajustar los bucles de retardo para hacer que el LED parpadease con un ritmo definido, por ejemplo una vez por segundo.



En la siguiente publicación, continuaremos con las subrutinas para optimizarlo: 

También, podéis consultar la publicación anterior:


ENLACES.

Y a continuación, una serie de enlaces útiles sobre lo expuesto:

Hoja de características (Datasheet) del PIC 16F84A®:

Web del Fabricante Microchip®:

MPLAB de Microchip®:

Software PICKit2 Programmer (y otros recursos en Microchip):

NotePad++:

WinPic800:

IC-PROG:

Programador JDM, en Blog Xanur:

Programador PicKit, en Blog Xanur:

Electrónica Digital, en Blog Xanur:




Esperamos que os haya gustado esta publicación. Si es así, no dudes en compartirla.

© Se permite reproducción total o parcial de este contenido, siempre y cuando se reconozca la fuente de información utilizada y se incluya el enlace a este artículo.

Equipo Xanur©2022.

 

No hay comentarios:

Publicar un comentario