Escolar Documentos
Profissional Documentos
Cultura Documentos
Introduccin
Bienvenidos al inicio del Tutorial sobre PICs. Estas pginas te llevaran desde la estructura bsica
del dispositivo, hasta los mtodos y tcnicas de programacin. Tambin habr sugerencias de
como modificar el cdigo para que lo puedas adaptar el PIC a tus propias aplicaciones. No incluir
diagramas de arquitectura interna, ya que esto puede llevar a confusiones. Si quieres echar un
vistazo a la 'datasheet', la puedes bajar del sitio de Microchip.
Para empezar, echemos un vistazo al PIC.
RA0 a RA4
RA es un puerto bidireccional. Eso quiere decir que puede ser configurado como entrada o como
salida. El nmero que hay despus de RA indica el numero de bit (0 a 4). Por tanto, tenemos un
puerto bidireccional de 5 bits donde cada bit puede ser configurado como entrada o como salida.
RB0 a RB7
VSS y VDD
Estos son los pins de alimentacin. VDD es la alimentacin positiva, y VSS es el negativo de la
alimentacin, o 0 Voltios. La tensin mxima de alimentacin que puedes utilizar son 6 Voltios, y el
mnimo son 2 Voltios.
OSC1/CLK IN y OSC2/CLKOUT
Estos pines son donde conectaremos el reloj externo, para que el microcontrolador disponga de
algn tipo de temporizacin.
MCLR
Este pin se utiliza para borrar las posiciones de memoria dentro del PIC (p.ej. cuando quiero
reprogramarlo). Durante el funcionamiento normal est conectado a la alimentacin positiva.
INT
Este es un pin de entrada que puede ser monitorizado. Si el pin se pone a nivel alto, podemos
hacer que el programa se reinicie, se pare o cualquier otra funcin de deseemos. No lo
utilizaremos mucho.
TOCK1
Esta es otra entrada de reloj, que opera con un temporizador interno. Opera aisladamente del reloj
principal. De nuevo, este tampoco lo utilizaremos mucho.
Tambin recomiendo utilizar una placa de insercin para hacer tus circuitos, mientras juegas con el
PIC. Hay varios tamaos disponibles.
A continuacin veremos como conectar un circuito simple para el desarrollo con el PIC.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Autor:
;
; Fecha:
;
; Versin:
;
; Titulo:
;
;
;
; Descripcin:
;
;
;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Date cuenta de que hemos hecho una especie de caja utilizando puntos y comas. Esto es
simplemente para hacerlo ms pulcro.
Finalmente, prueba y documenta el programa sobre papel tambin. Puedes usar o bien diagramas
de flujo o bien algoritmos o lo que tu quieras. Esto te ayudar a escribir tu programa paso a paso.
Bien, eso es todo al respecto, vamos a entrar en materia.
Los Registros
Un registro es un lugar dentro del PIC que puede ser escrito, ledo o ambas cosas. Piensa en un
registro como si fuese un trozo de papel donde tu puedes ver la informacin o escribirla.
La figura de ms abajo muestra el mapa de registros del interior del PIC16F84. No te preocupes si
no has visto nada parecido antes, es solo para mostrar donde estn los diferentes bits y piezas
dentro del PIC, y nos ayudar a explicar unos cuantos comandos.
Error al crear miniatura: No se ha podido guardar la miniatura
La primera cosa que notars es que est dividido en dos - Banco 0 y Banco 1. El Banco 1 es
utilizado para controlar las propias operaciones del PIC, por ejemplo para decirle al PIC cuales bits
del Puerto A son entradas y cuales son salidas. El Banco 0 se utiliza para manipular los datos. Un
ejemplo es el siguiente: Digamos que queremos poner un bit del puerto A a nivel alto. Lo primero
que necesitamos hacer es ir al Banco 1 para poner ese bit o pin en particular en el puerto A como
salida. Despus volvemos al Banco 0 y enviamos un 1 lgico a ese pin.
Los registros que vamos a usar mas comunes en el Banco 1 son STATUS, TRISA y TRISB. El
primero permite volver al Banco 0, TRISA nos permite establecer los pines que sern entradas y
los que sern salidas del Puerto A, TRISB nos permite establecer los pines que sern entradas y
los que sern salidas del puerto B.
Vamos a ver con ms detenimiento estos tres registros.
STATUS
Para cambiar del Banco 0 al Banco 1 utilizamos el registro STATUS. Hacemos esto poniendo el bit
5 del registro STATUS a 1. Para cambiar de nuevo al Banco 0, ponemos el bit 5 del registro
STATUS a 0. El registro STATUS se localiza en la direccin 03h (la 'h' significa que el nmero es
hexadecimal).
TRISA y TRISB
Estn localizados en las direcciones 85h y 86h respectivamente. Para programar que un pin sea
una salida o una entrada, simplemente enviamos un 0 o un 1 al bit en cuestin en el registro.
Ahora, podemos hacer esto ya sea en binario o en hexadecimal. Personalmente uso ambos, ya
que el binario ayuda mucho a visualizar el puerto. Si no ests familiarizado con el paso de binario a
hexadecimal y viceversa, utiliza una calculadora cientfica.
Entonces en el puerto A tenemos 5 pines, por tanto 5 bits. Si deseamos poner uno de los pines
como entrada, enviamos un 1 al bit en cuestin. Si deseamos poner uno de los pines como salida,
ponemos un 0 en ese bit. Los bits estn definidos de manera correspondiente con los pines, en
otras palabras el bit 0 es el RA0, el bit 1 es el RA1, el bit 2 es el RA2, y as sucesivamente. Vamos
a tomar un ejemplo. Si queremos poner RA0, RA3 y RA4 como salidas, y RA1 y RA2 como
entradas, enviamos esto: 00110 (06h). Date cuenta de que el bit cero est a la derecha, como se
muestra aqu:
Pin del Puerto A RA4 RA3 RA2 RA1 RA0
Numero de bit
Valor Binario
PORTA y PORTB
Para poner uno de nuestros pines de salida a nivel alto, simplemente ponemos un 1 el bit
correspondiente en nuestro registro PORTA o PORTB. El formato es el mismo que para los
registros TRISA y TRISB. Para leer si un pin est a nivel alto o nivel bajo en los pines de nuestro
puerto, podemos ejecutar un chequeo para ver si el bit en particular correspondiente esta puesto a
nivel alto (1) o est puesto a nivel bajo (0).
Antes de dar un ejemplo de cdigo, tenemos que explicar dos registros mas - W y F.
El registro W es un registro de propsito general al cual le puedes asignar cualquier valor que
desees. Una vez que has asignado un valor a ese registro, puedes sumarle cualquier otro valor, o
moverlo. Si le asignas otro valor a W, su contenido es sobrescrito.
Un ejemplo de cdigo
Vamos a darte un ejemplo de cdigo sobre lo que acabamos de aprender. No intentes compilar
esto todava, lo haremos cuando hagamos nuestro primer programa. Simplemente estamos
intentado mostrar como se hace la programacin de lo anterior y de paso presentando un par de
instrucciones. 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 direccin 03h, poniendo el bit 5 a 1.
BSF 03h,5
La instruccin BSF significa en ingles "Bit Set F" (Poner a 1 un bit de la Memoria). La
letra F significa que vamos a utilizar una posicin de memoria, o un registro en memoria. Vamos a
utilizar dos nmeros despus de esta instruccin - 03h, el cual se refiere a la direccin del registro
STATUS, y el nmero 5 que corresponde al nmero de bit. Por tanto, lo que estamos diciendo es
"pon a 1 el bit 5 de la direccin de memoria 03h".
Ahora ya estamos en el banco 1.
MOVLW
b'00110'
Estamos poniendo el valor binario 00110 (la letra 'b' significa que el nmero est en binario) en
nuestro registro de propsito general W. Podramos haber hecho esto en hexadecimal, en cuyo
caso nuestra instruccin hubiese sido:
MOVLW 06h
Cualquiera de las dos funcionar. La instruccin MOVLW significa en ingles "Move Literal Value
into W", en castellano, mover un valor literal directamente al registro W.
Ahora necesitamos poner este valor en el registro TRISA para configurar el puerto:
MOVWF 85h
Esta instruccin significa "poner los contenidos de W en el registro cuya direccin viene a
continuacin", en este caso la direccin 85h, que apunta a TRISA.
Nuestro registro TRISA ahora tiene el valor 00110 o mostrado grficamente :
Pin del Puerto A RA4 RA3 RA2 RA1 RA0
Valor Binario
Entrada/Salida
Ahora tenemos que configurar los pines del Puerto A, y para ello necesitamos volver al banco 0
para manipular cualquier dato.
BCF 03h,5
Esta instruccin hace lo contrario a BSF. Significa en ingles "Bit Clear F" (en castellano, poner a 0
un bit de la memoria). Los dos nmeros que le siguen son la direccin del registro, en este caso del
registro STATUS, y el nmero de bit, es este caso el 5. As que lo que hemos hecho ahora es
poner a 0 el bit 5 del registro STATUS.
Ya estamos de vuelta en el Banco 0.
Aqu est el cdigo en un solo bloque:
BSF
03h,5
; Ve al banco 1
MOVLW
06h
; Pon 00110 en W
MOVWF
85h
BCF
03h,5
; Vuelve al Banco 0
Lelo hasta que las entiendas. De momento ya hemos visto 4 instrucciones. Solo nos quedan 31
para terminar!
03h,5
;Ir al Banco 1
movlw
00h
;Poner 00000 en W
movwf
85h
bcf
03h,5
;volver al Banco 1
Esto te sonar del apartado anterior. La nica diferencia es que hemos puesto todos los pines del
Puerto A como salidas, poniendo 0h en el registro tri-estado (TRISA).
Ahora lo que tenemos que hacer es encender el LED. Hacemos esto poniendo uno de los pines
(aquel que tenga el LED conectado) a nivel alto. En otras palabras, enviamos un 1 al pin. As es
como se hace (Mira los comentarios de cada linea):
movlw
02h
05h
00h
los pines.
movwf
05h
movlw
02h
es 00010,
; ...lo cual pone a 1 el bit 2 (pin 18)
mientras mantiene los otros pines a 0.
movwf
05h
00h
05h
Inicio
Como puedes ver, primer decimos la palabra 'Inicio' justo al comienzo del programa. Despus,
justo al final del programa decimos simplemente 'goto Inicio', ves a Inicio. La instruccin 'goto'
significa en ingles 'ir a', y eso es lo que hace.
Este programa encender y apagar el LED constantemente, desde el momento que le demos
alimentacin al circuito, y se detendr cuando le quitemos la alimentacin.
Creo que deberamos echar un vistazo de nuevo a nuestro programa:
;
Inicio
bsf
03h,5
movlw
00h
movwf
85h
bcf
03h,5
movlw
02h
movwf
05h
movlw
00h
movwf
05h
goto
Inicio
Bien, he quitado los comentarios. Pero, te das cuenta de que todo lo que vemos son
instrucciones y nmeros ? Esto puede ser algo confuso si mas tarde intentas depurar el programa,
y tambin cuando escribes cdigo que te tengas que acordar de todas las direcciones de memoria.
Incluso con los comentarios puestos, puede ser un poco lioso. Lo que necesitamos es dar nombres
a estos nmeros. Esto se consigue con otra instruccin: "equ"
La instruccin "equ" simplemente significa que algo equivale a algo [Nota de la traduccin: "equ"
viene del termino ingles "equivalence", en castellano "equivalencia"]. No es una instruccin del PIC,
sino para el ensamblador. Con esta instruccin podemos asignar un nombre a la direccin de
localizacin de un registro, o en trminos de programacin asignar una constante. Vamos a
establecer algunas constantes para nuestro programa, y vers que sencillo es de leer.
STATUS
equ
03h
equ
85h
equ
05h
equ
03h
TRISA
equ
85h
PORTA
equ
05h
bsf
STATUS,5
movlw
00h
movwf
TRISA
bcf
STATUS,5
movlw
02h
movwf
PORTA
movlw
00h
movwf
PORTA
goto
Inicio
Inicio
Seguro que ahora puedes ver que las constantes hacen el programa un poco ms sencillo, aunque
todava no hemos puesto los comentarios. Sin embargo, no hemos terminado todava.
Bucles de Retardo
Existe un ligero inconveniente en nuestro programa del LED parpadeante. Cada instruccin
necesita un ciclo de reloj para ser completada. Si utilizamos un cristal de 4 Mhz, cada instruccin
tardar 1/4 Mhz o 1 microsegundo en ser completada. Como solo estamos usando 5 instrucciones,
el LED se encender y apagar en 5 microsegundos. Esto es demasiado rpido para que lo
podamos ver, y parecer que el LED est permanentemente encendido. Lo que necesitamos hacer
es introducir un retardo entre el momento de encendido y apagado y viceversa.
El principio para retardo es el de contar hacia atrs desde un nmero previamente establecido y
cuando llegue a cero, paramos de contar. El valor cero indica el fin del retardo y continuamos
nuestro camino a travs del programa.
As que lo primero que necesitamos hacer es definir una constante que usaremos como contado.
La llamaremos CONTADOR. Lo siguiente, necesitamos decidir el tamao del nmero desde el que
contar. Bien, el nmero mayor que podemos tener es 255 o FFh en hexadecimal. Ahora, como
hemos mencionado en el apartado anterior, la instruccin equ asigna una palabra a una
localizacin de un registro. Esto significa que cualquiera que sea el nmero que asignemos a
CONTADOR, ser igual al contenido de un registro.
Si lo probamos y asignamos el valor FFh, el compilador entender que estamos asignando la
direccin de memoria FFh a la constante, y obtendremos un error cuando vayamos a compilar el
programa. Esto es debido a que la localizacin FFh est reservada, y por tanto no podemos
acceder a ella. As que, como hacemos para asignar un nmero real ? Bien, se requiere hacer un
poco de "pensamiento lateral". Si asignamos a nuestro CONTADOR, por ejemplo, a la direccin
08h, este apuntar a un registro de propsito 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.
Pero, s, no llores, cmo ponemos un valor distinto en CONTADOR ? Bien, todo lo que tenemos
que hacer es primero 'mover' un valor a esta posicin. Por ejemplo, si queremos que CONTADOR
tenga un valor de 85h, no podemos decir 'CONTADOR equ 85h' porque esta es la localizacin del
registro tri-estado del puerto A (TRISA). Lo que hacemos es esto:
movlw
85h
movwf
08h
Ahora, podemos decir 'CONTADOR equ 08h', CONTADOR ser igual al valor 85h. Sutil,
verdad ?
As que lo primero definimos nuestra constante:
CONTADOR
equ
08h
CONTADOR,1
Esta instruccin dice "resta 1 al registro (en esta caso CONTADOR). Si llegamos a cero, salta 2
lugares hacia delante"[Nota de la traduccin: El valor que le sigue a la coma, indica donde debe
almacenarse el resultado de la operacin. Si es 1, como en el ejemplo anterior, el resultado se
almacena en el mismo registro indicado en la instruccin, y si es 0 el resultado se almacena en el
registro w.] . Un montn de palabras para una sola instruccin. Veamosla en accin antes, despus
la pondremos en nuestro programa.
CONTADOR
equ 08h
ETIQUETA
decfsz
CONTADOR,1
goto ETIQUETA
;Continua por aqu.
:
:
:
Lo que hemos hecho es primero poner nuestra constante CONTADOR a 255. La siguiente linea
pone una etiqueta, llamada ETIQUETA seguida de nuestra instruccin decfsz. La instruccin
decfsz CONTADOR,1 disminuye el valor de CONTADOR en 1, y almacena el resultado de vuelta
en CONTADOR. Tambin comprueba si CONTADOR tiene un valor de 0. Si no lo tiene, hace que
el programa salte a la siguiente linea. Aqu tenemos una instruccin de 'goto' que nos enva de
vuelta a nuestra instruccin decfsz. Si el valor de CONTADOR es igual a cero, entonces la
instruccin decfsz hace que el programa salte dos lugares hacia adelante, y se site donde hemos
escrito "Continua por aqu". As que, como puedes ver, hemos hecho que el programa permanezca
en un lugar durante un tiempo predeterminado antes de seguir adelante. Esto se llama bucle de
retardo. Si necesitamos un retardo mayor, podemos poner un bucle detrs de otro. Cuantos mas
bucles pongamos, mayor ser el retardo. Nosotros vamos a necesitar por lo menos dos, si
queremos ver parpadear al LED.
Vamos a poner estos bucles de retardo en nuestro programa, y terminaremos haciendo un
programa real aadiendo los comentarios:
;*****Establecimiento constantes ****
STATUS
equ
03h
TRISA
equ
85h
para el Puerto A.
PORTA
equ
05h
CONTADOR1
equ
08h
09h
bucles de retardo.
CONTADOR2
equ
bucles de retardo.
;
;****Configuracin del Puerto****
bsf
STATUS,5
movlw
00h
movwf
TRISA
; ...como salidas.
bcf
STATUS,5
; Volvemos al Banco 0.
Bank 1
...
;
;****Encendido del LED ****
Inicio
movlw
02h
PORTA
primero el valor...
movwf
al puerto
;
;****Inicio del buble de retardo 1****
Bucle1
decfsz
CONTADOR1,1
; Restamos 1 a 255.
goto
Bucle1
; Si CONTADOR es cero,
decfsz
CONTADOR2,1
; Restamos 1 a 255
goto
Bucle1
continuamos.
bucle
; Este retardo cuenta hacia atrs
...
00h
movwf
PORTA
valor ...
al puerto
;
;****Aadimos otro retardo****
Bucle2
decfsz
CONTADOR1,1
goto
Bucle2
; ...apagado el tiempo
decfsz
CONTADOR2,1
goto
Bucle2
LED...
suficiente...
;
;****Ahora volvemos al inicio del programa
goto
Inicio
el LED...
; ...de
nuevo.
;****Termina el Programa****
end
esta instruccin.
; y tambin por si acaso olvidamos
poner...
; ... la instruccin 'goto'.
Puedes compilar este programa y programar el PIC con l. Por su puesto, querrs probar el circuito
para ver si funciona realmente. Aqu est el diagrama del circuito para que lo construyas una vez
que hayas programado tu PIC:
ajustar tus bucles de retardo para hacer que el LED parpadease con un ritmo definido, por ejemplo
una vez por segundo.
En la siguiente seccin veremos como podemos usar una cosa llamada sub-rutina para ayudar a
mantener el programa simple y pequeo.
Subrutinas
Una subrutina es una seccin de cdigo o programa, que puede ser llamada como y cuando la
necesites. Las subrutinas se usan si vas a ejecutar la misma funcin funcin ms de una vez, por
ejemplo para crear un retardo. Las ventajas de utilizar una subrutina son que har ms sencillo
modificar el valor una vez dentro de la subrutina antes que, digamos, hacerlo diez veces a travs
de tu programa. Y tambin te ayudar a reducir el total de memoria que ocupa tu programa dentro
del PIC.
Miremos una subrutina:
RUTINA
CONTADOR
equ 255
ETIQUETA
decfsz
CONTADOR,1
goto
ETIQUETA
return
Primero tenemos que dar un nombre a la subrutina, y en este caso hemos elegido RUTINA.
Despus escribimos el cdigo que queramos como hacemos normalmente. En este caso, hemos
elegido el cdigo del retardo para el programa de parpadeo de nuestro LED. Finalmente,
terminamos la subrutina tecleando la instruccin RETURN.
Para arrancar la subrutina desde cualquier punto de nuestro programa, simplemente escribimos la
instruccin CALL seguida por el nombre de la subrutina.
Vamos a ver esto con algo ms de detalle. Cuando alcanzamos la parte de nuestro programa que
dice CALL xxx, donde xxx es el nombre de nuestra subrutina, el programa salta a donde quiera que
resida la subrutina xxx. Las instrucciones dentro de la subrutina se ejecutan. Cuando se alcanza la
instruccin RETURN, el programa salta de vuelta a nuestro programa principal, justo a la
instruccin que va inmediatamente despus de nuestra instruccin CALL xxx.
Puedes llamar a la misma subrutina tantas veces como quieras, esa es la razn por la que utilizar
subrutinas reduce el tamao total de nuestro programa. Sin embargo, hay dos cosas que debes
tener en cuenta. La primera, igual que en tu programa principal, cualquier constante debe ser
declarada antes de utilizarla. Pueden ser declaradas dentro de la subrutina misma, o justo al
comienzo del programa principal. Recomendaramos que declarases todo al comienzo del
programa principal, para que as sepas que todo se encuentra en el mismo sitio. Lo segundo, te
debes asegurar de que el programa principal pasa por alto la subrutina. Lo que queremos decir con
esto es que si pones la subrutina justo al final del programa principal, a menos que uses una
instruccin 'goto' para saltar la subrutina, el programa seguir y ejecutar la subrutina tanto si
quieres como si no. El PIC no diferencia entre una subrutina y el programa principal.
Vamos a verlo en nuestro programa de parpadeo de LED, pero esta vez utilizaremos una subrutina
para el bucle de retardo. Con suerte vers que sencillo se queda el programa, y tambin veras
como funciona la subrutina en la realidad:
;***** Establecimiento constantes ****
STATUS
equ
03h
TRISA
equ
85h
equ
05h
CONTADOR1
equ
08h
09h
bucles de retardo.
CONTADOR2
equ
bucles de retardo.
;
;**** Configuracin del Puerto ****
bsf
STATUS,5
; Cambiamos al Banco 1
movlw
00h
movwf
TRISA
; ...como salidas.
bcf
STATUS,5
; Volvemos al Banco 0.
...
;
;**** Encendido del LED ****
Inicio
movlw
02h
PORTA
primero el valor...
movwf
al puerto
;
;**** Aadimos un retardo ****
call
Retardo
;
;**** Retardo terminado, ahora apagamos el LED ****
movlw
00h
movwf
PORTA
valor ...
al puerto
;
;**** Aadimos otro retardo ****
call
Retardo
;
;**** Ahora volvemos al inicio del programa
goto
Inicio
el LED...
; ...de
nuevo.
;
;**** Aqu est nuestra Subrutina
Retardo
Bucle1
decfsz
CONTADOR1,1
goto
Bucle1
; ...apagado el tiempo
decfsz
CONTADOR2,1
goto
Bucle1
LED...
suficiente...
return
;
;**** Fin del Programa****
end
esta instruccin,
; y tambin por si acaso olvidamos
poner...
; ... la instruccin 'goto'.
Puedes ver que utilizando una subrutina para nuestro bucle de retardo, hemos reducido el tamao
del programa. Cada vez que queramos hacer un retardo, ya sea cuando el LED est apagado o
cuando est encendido, simplemente llamamos a la subrutina de retardo. Al final de la subrutina, el
programa retorna a la linea siguiente a la instruccin 'call'. En el ejemplo anterior, encendemos el
LED. Despus llamamos a la subrutina. Entonces el programa retorna para que podamos apagar el
LED. Llamamos a la subrutina de nuevo, y cuando la surutina termina, el programa retorna a la
siguiente instruccin que ve, que es 'goto Inicio'.
Para aquellos de vosotros que estis interesados, nuestro programa original tenia 120 bytes de
tamao. Mediante el uso de la subrutina, hemos reducido el programa a 103 bytes.[Nota de la
traduccin: Estos tamaos en bytes a los que se refiere el autor, son los tamaos de los ficheros
.HEX que resultan de compilar estos listados con el compilador de Microhip MPASM.] Puede que
no parezca gran cosa, pero teniendo en cuenta que solo tenemos 1024 bytes en total dentro del
PIC , cada pequeo bit ayuda.
[Nota de la traduccin: Cuando el autor dice que el 16F84 tiene 1024 bytes, se esta refiriendo al
rea de memoria para el almacenamiento del cdigo o programas. Y, aunque utiliza la palabra
bytes aqu, en realidad no se trata de bytes(los cuales tienen 8 bits), sino que se trata 14-bit
words(en castellano, palabras de 14-bits, un poco ms de un byte). De modo, que lo que sera
correcto decir es, que el rea de memoria de programa en el 16F84 tiene un tamao 1024 x 14 bit
words, o 1K x 14 bit words, o 1024 posiciones de 14 bits cada una. Como dato adicional, cada
instruccin del conjunto de instrucciones del 16F84 ocupa una palabra de 14 bits. Es decir, cada
una ocupa una de esas 1024 posiciones de memoria disponible. Microchip fabrica otros PICs con
mayor espacio de memoria interna. Estos se pueden ver en las pginas de Microchip. ]
En el prximo capitulo, veremos cmo leer de los puertos.
equ
03h
TRISA
equ
85h
equ
05h
bsf
STATUS,5
; Cambia al Banco 1.
Ahora, para configurar el pin de un puerto para que sea una salida, enviamos un 0 al registro
TRISA. Para poner el pin como entrada, ponemos un 1 en el registro TRISA.
movlw
01h
movwf
TRISA
bcf
STATUS,5
; Vuelve al Banco 0.
Ahora hemos puesto el bit 0 del puerto A como entrada. Lo que necesitamos hacer ahora es
comprobar si el pin est a nivel alto o a nivel bajo. Para ello, podemos usar una de estas dos
instrucciones: BTFSC y BTFSS.
La instruccin BTFSC significa "Haz una comprobacin de bit en el registro y bit que
especificamos. Si es un 0, entonces sltate la siguiente instruccin".
La instruccin BTFSS significa "Haz una comprobacin de bit en el registro y bit que
especificamos. Si es un 1, entonces sltate la siguiente instruccin".
La que usemos depender de como queramos que nuestro programa reaccione cuando lea la
entrada. Por ejemplo, si simplemente estamos esperando que la entrada sea 1, entonces
podramos utilizar la instruccin BTFSS de este modo:
;Aqu el cdigo
:
BTFSS
Goto
PortA,0
Inicio
equ
03h
TRISA
equ
85h
equ
05h
CONTADOR1
equ
08h
09h
bucles de retardo.
CONTADOR2
equ
bucles de retardo.
;
;**** Configuracin del Puerto ****
bsf
STATUS,5
; Cambiamos al Banco 1
movlw
01h
movwf
TRISA
STATUS,5
; Volvemos al Banco 0.
...
0 como entrada.
bcf
;
;**** Encendemos del LED ****
Inicio
movlw
02h
PORTA
primero el valor...
movwf
al puerto
;
;**** Comprobamos si el conmutador est cerrado ****
btfsc
PORTA,0
puerto A y comprobamos si es 0.
; Si es 0, sltate la siguiente
Retardo
; Si es 1, ejecuta esta
Retardo
;
;**** Retardo terminado, ahora apagamos el LED ****
movlw
00h
movwf
PORTA
valor ...
al puerto
;
;**** Comprobamos si el conmutador est todava cerrado ****
btfsc
PORTA,0
puerto A y comprobamos si es 0.
; Si es 0, saltate la siguiente
instruccin y continua normalmente.
call
Retardo
; Si es 1, ejecuta esta
Retardo
;
;**** Ahora volvemos al inicio del programa
goto
Inicio
el LED...
; ...de
nuevo.
;
;**** Aqu est nuestra Subrutina
Retardo
Bucle1
decfsz
CONTADOR1,1
goto
Bucle1
; ...apagado el tiempo
decfsz
CONTADOR2,1
goto
Bucle1
LED...
suficiente...
return
;
;**** Fin del Programa****
end
esta instruccin,
; y tambin por si acaso olvidamos
poner...
; ... la instruccin 'goto'.
Lo que hemos hecho aqu es encender el LED. Despus comprobar si el conmutador est cerrado.
Si est cerrado, entonces hacemos una llamada a nuestra subrutina de retardo. Esto nos da el
mismo retardo que anteriormente, pero ahora la estamos llamando dos veces. Lo mismo pasa
cuando el LED est apagado. Si el conmutador no est cerrado, entonces tenemos nuestros
tiempos de encendido y apagado como antes.
Puedes compilar y ejecutar este programa. Sin embargo, una nota de advertencia: El circuito final y
el cdigo puede resultar irrelevante para alguien que no le interese programar microcontroladores.
Por tanto, no te enfades si cuando ensees el circuito a tu familia y amigos sobre como puedes
cambiar la velocidad de parpadeo de un LED, ves que no muestran ni el mas mnimo inters
Hablamos desde nuestra experiencia personal !
Si has ido siguiendo estos captulos desde el comienzo, puede que te interese saber que ahora
llevas aprendidas 10 de 35 instrucciones para el PIC 16F84 ! Y todas ellas las has aprendido
simplemente con el encendiendo y apagando de un LED.
Hasta ahora hemos hecho que el PIC haga parpadear un LED. Despus fuimos capaces de
interactuar con nuestro PIC aadiendo un conmutador, para modificar el ritmo de parpadeo. El
nico problema es que el programa es muy largo y desperdicia mucha memoria. Era aceptable ya
que introducamos comandos por primera vez, pero debe existir una manera mejor de hacerlo.
Bueno, la hay (sabias que la haba, verdad? ).
Vamos a examinar como estbamos haciendo el parpadeo del LED realmente.
movlw
02h
movwf
PORTA
movlw
00h
movlw
PORTA
Primero cargamos nuestro registro w con 02h, despus lo pusimos en nuestro registro del puerto A
para encender el LED. Para apagarlo, cargamos w con 00h y despus lo pusimos en nuestro
registro del puerto A. Entremedias de estas rutinas tenamos que llamar a una subrutina para que
pudiramos ver el LED parpadear. As que hemos tenido que mover dos conjuntos de datos dos
veces (una vez al registro w y despus al PORTA) y llamar a la subrutina dos veces (una vez para
el encendido y otra para el apagado).
As que, cmo podemos hacer esto de una manera mas eficiente ? Sencillo. Utilizamos otra
instruccin llamada XORWF.
La instruccin XORWF ejecuta una funcin OR Exclusiva entre el registro w y el registro que le
pasamos como dato. Ser mejor que expliquemos que narices es una OR Exclusiva antes de
continuar. [Nota de la traduccin: Tambin llamada funcin XOR simplemente]
Si tenemos dos entradas, y una salida, la salida solo ser 1 si, y solo si, las dos entradas son
diferentes. Si son iguales, la salida ser 0. Aqu est la tabla de verdad, para aquellos que
prefieran verlo en una tabla:
A B Salida
0
Vamos a ver que ocurre si hacemos que B tome el valor de salida anterior, y simplemente
cambiamos el valor de A:
A B Salida
0
02h
XORWF
PORTA,1
Lo que estamos haciendo aqu es cargar nuestro registro w con 02h. Despus le hacemos una OR
exclusiva a este nmero que hay en w con lo que quiera que est en nuestro registro del puerto A.
Si el bit 1 es 1, cambiar a 0. Si el bit 1 es 0, cambiar a 1. [Nota de la traduccin: El nmero que
va despus del registro especificado en la instruccin XORWF, indica donde debe de ser
almacenado el resultado de dicha operacin OR exclusiva. Si, como ocurre en este ejemplo
anterior, ponemos un 1, el resultado se almacenar de vuelta en el registro de memoria
especificado. Si pusisemos 0, el resultado de la operacin OR exclusiva se almacenara en el
registro w]
Vamos a ejecutar este cdigo un par de veces, para mostrar como funciona en binario:
PORTA
00010
xorwf 00000
xorwf 00010
xorwf 00000
xorwf 00010
Incluso no necesitamos cargar el mismo valor en nuestro registro w cada vez, lo podemos hacer
una sola vez al principio, y simplemente saltar hacia atrs a nuestro comando de conmutacin.
Adems, no tenemos que establecer una valor al registro de nuestro puerto A. Por qu ? Bien, si
durante el encendido es un 1, lo haremos conmutar. Si por el contrario es un 0 durante el arranque,
tambin lo conmutaremos.
As que, vamos a ver nuestro nuevo cdigo. El primero es nuestro cdigo original de parpadeo del
LED, y el segundo es donde hemos aadido el conmutador externo:
LED Parpadeante:
;***** Establecerlas constantes ****
STATUS
equ
03h
TRISA
equ
85h
PORTA
equ
05h
CONTADOR1
equ
08h
equ
09h
puerto A.
de retardo.
CONTADOR2
de retardo.
;**** Configurar el puerto****
bsf
STATUS,5
; Cambia al Banco 1
movlw
00h
movwf
TRISA
; ...como salidas.
bcf
STATUS,5
; Vuelve al Banco 0
movlw
02h
xorwf
PORTA,1
; Conmuta el LED
Retardo
Inicio
de nuevo.
;
;**** Aqu est nuestra subrutina
Retardo
Bucle1
decfsz
CONTADOR1,1
goto
Bucle1
decfsz
CONTADOR2,1
goto
Bucle1
encendido...
return
;**** Final del programa ****
end
instruccin,
; y tambin por si acaso olvidamos
poner...
; ... la instruccin 'goto'.
LED parpadeante con el conmutador externo:
;***** Establecerlas constantes ****
STATUS
equ
03h
TRISA
equ
85h
PORTA
equ
05h
CONTADOR1
equ
08h
equ
09h
puerto A.
de retardo.
CONTADOR2
de retardo.
;
;**** Configurar el puerto****
bsf
STATUS,5
; Cambia al Banco 1
movlw
00h
movwf
TRISA
; ...como salidas.
bcf
STATUS,5
; Vuelve al Banco 0
movlw
02h
;
;**** Enciende y apaga el LED ****
Inicio
xorwf
PORTA,1
; Conmuta el LED
;
;**** Comprobamos si el conmutador est cerrado ****
btfsc
PORTA,0
A y comprobamos si es 0.
; Si es 0, sltate la siguiente
instruccin y continua normalmente.
call
Retardo
Retardo
;
;**** Ahora vuelve al inicio del programa
goto
Inicio
de nuevo.
;
;**** Aqu est nuestra subrutina
Retardo
Bucle1
decfsz
CONTADOR1,1
goto
Bucle1
decfsz
CONTADOR2,1
goto
Bucle1
encendido...
return
;
;**** Final del programa ****
end
instruccin,
; y tambin por si acaso olvidamos
poner...
; ... la instruccin 'goto'.
Esperamos que hayas podido comprender cmo con el uso de una simple instruccin, hemos
reducido el tamao de nuestro programa. De hecho, simplemente para mostrar en cuanto se han
reducido nuestros programas, hemos mostrado los dos programas, los cambios que hicimos, y sus
tamaos en la siguiente tabla:
Programa
Cambio
LED parpadeante
Original
120
LED Parpadeante
Aadiendo subrutina
103
LED Parpadeante
132
AND
La funcin AND simplemente compara dos bits y produce un 1 si son iguales, y 0 si son diferentes.
Por ejemplo, si decimos 1 AND 1, el resultado es 1, mientras que si decimos 1 AND 0 el resultado
ser 0. Por supuesto, podemos comparar palabras (o bytes) tambin, y la funcin AND hace esta
comparacin de ambas bit a bit. El ejemplo de ms abajo muestra dos palabras de 8 bits a las que
se aplica AND con el siguiente resultado:
11001011
AND
10110011
Igual a 10000011
Como puedes ver, el resultado solo tiene un 1 cuando dos 1s coinciden en ambas palabras.
Podemos utilizar la funcin AND para hacer comprobaciones de puertos, por ejemplo. Si estamos
monitorizando pines E/S que estn conectados a un circuito, y tenemos que monitorizar una
condicin en concreto donde solo algunos pines estn a nivel alto, entonces simplemente podemos
leer el puerto, y aplicar a ese valor la funcin AND con la condicin que estamos buscando,
simplemente como en el ejemplo anterior.
[Nota de la Traduccin: Incluimos aqu la tabla de verdad de esta funcin que no estaba en el texto
original por homogeneizar:]
A B Resultado de AND
0
El PIC nos da dos modalidades para AND. Estas son ANDLW y ANDWF.
ANDLW nos permite hace una funcin AND con los contenidos del registro W, y un nmero que
nosotros especifiquemos. Las sintaxis es:
ANDLW <nmero> ; donde <nmero> es con el que haremos AND a los contenidos de W. El
resultado de la funcin AND sern almacenamos de vuelta en el registro W. [Nota de la traduccin:
El valor de <nmero> tiene que estar comprendido entre 0 y 255]]
ANDWF nos permite hacer una funcin AND con los contenidos del registro W y otro registro, como
por ejemplo un puerto. Las sintaxis es:
ANDWF <registro>,d ; donde <registro> es el registro en el que estamos interesados, por ejemplo
PORTA, y d dice al PIC donde almacenar el resultado. Si d=0, el resultado se almacena en el
registro W, y si d=1 el resultado se almacena en ese registro especificado.
Las dos secciones de cdigo de ms abajo muestran un ejemplo de cada instruccin AND. La
primera comprueba el estado del PORTA, donde necesitamos ver si las entradas son 1100.
Pondremos el resultado de vuelta en el registro W:
movlw
1100
ANDWF
05h,0
OR
1100
Ya hemos visto una funcin OR, llamada XOR. Esta produce un 1 si dos bits son diferentes, pero
no si son iguales. Hay una segunda funcin OR llamada IOR, la cual es OR inclusiva. La funcin
produce un 1 si cualquiera de los dos bits es 1, pero tambin si ambos bits son 1. Aqu est la tabla
de verdad para demostrar esto:
A B Resultado de OR
0
Operadores Aritmticos
ADD
Esta funcin exactamente lo que dice [Nota de la traduccin: "Add" en ingles significa sumar]
Suma dos nmeros! Si el resultado de sumar dos nmeros excede de 8 bits [Nota de la
traduccin: 8 bits pueden contener un valor mximo de 255 en decimal], entonces se activar un
flag llamado CARRY [Nota de la traduccin: "flag" en castellano se podra traducir por bandern, y
normalmente se trata de 1 bit que se ubica en un registro en concreto de la memoria del PIC.
"CARRY" en castellano puede traducirse como "llevar", podra ser como cuando hacemos una
cuenta a mano y utilizamos la expresin "me llevo una".]. El flag de CARRY est localizado en el bit
0 de la direccin de memoria 03h. Si este bit se activa, quiere decir que la suma de esos 2
nmeros excedi de 8 bits. Si es 0, entonces el resultado queda dentro de los 8 bits.
De nuevo, el PIC nos da dos modalidades de ADD, que son ADDLW y ADDWF. Como puedes
suponer, es muy similar a la funcin anterior.
ADDLW aade los contenidos del registro W con un nmero que le especifiquemos. La sintaxis es:
ADDLW <nmero>
ADDWF aadir los contenidos del registro W y cualquier otro registro que le especifiquemos. La
sintaxis es:
ADDWF <registro>,d ; donde <registro> es el registro que queremos especificar, y d le dice al PIC
donde almacenar el resultado. Si d=0, el resultado se almacena en el registro W, y si d=1 el
resultado se almacena en ese registro especificado.
SUB
Ahora, apuesto a que sabes que hace esta funcin! S, lo que suponas, esta funcin sustrae(o
resta) un bit a otro ['Nota de la traduccin: SUB, viene del ingles "substract" que significa "sustraer"
en castellano]]. Una vez ms el PIC da dos modalidades: SUBLW y SUBWF. La sintaxis es
exactamente la misma que para la funcin ADD, excepto por supuesto que tienes que escribir
SUB en lugar de ADD!
Incremento
01
addwf
0Ch,1
Hay una manera mejor de hacer esto. Podemos utilizar la instruccin INCF. La sintaxis es:
INCF <registro>,d ; donde <registro> es un registro, o posicin de memoria en la que estemos
interesados, y d le dice al PIC donde almacenar el resultado. Si d=0, el resultado se almacena en
el registro W, y si d=1 el resultado se almacena en ese registro especificado.
Mediante el uso de esta simple instruccin podemos literalmente hacer la mitad de cdigo. Si
queremos que el resultado vaya de vuelta al registro W, usando el ejemplo anterior, hubisemos
tenido que aadir otro comando para mover el contenido de la posicin 0Ch de vuelta al registro W,
y despus poner en el registro 0Ch lo que quiera que tuviese al principio.
Existe otro comando de incremento. Es el INCFSZ. Este comando incrementar el registro que
nosotros le especifiquemos, pero si el registro es igual a 0 despus del incremento (esto ocurrir
cuando aadamos 1 a 255) entonces el PIC se saltar la siguiente instruccin. La seccin de
cdigo de ms abajo lo demuestra:
Bucle
incfsz
0Ch
goto
Bucle
:
:
;Resto del programa.
En la seccin de cdigo anterior, el registro de la posicin de memoria OCh ser incrementado en
1. Despus tenemos una instruccin que le dice al PIC que vaya de vuelta a nuestra etiqueta
llamada "Bucle", e incremente OC en 1 de nuevo. Esto ocurrir constantemente hasta que 0Ch sea
igual a 255. En este momento, cuando incrementemos OCh en 1, 0Ch ser igual a 0. Nuestra
instruccin le dir al PIC que se salte la siguiente instruccin, la cual es en este caso "goto", y por
tanto que el PIC contine con el resto del programa.
Decremento
Complemento
La ltima instruccin de este grupo invierte todos los bits de un registro que le especifiquemos. La
sintaxis es:
COMF <registro>,d ; donde <registro> es el registro que deseamos invertir, y d le dice al PIC
donde almacenar el resultado. Si d=0, el resultado se almacena en el registro W, y si d=1 el
resultado se almacena en ese registro especificado.
El siguiente ejemplo muestra la instruccin en accin:
0Ch = 11001100
COMF 0Ch,1
0Ch = 00110011
Esto se puede usar, por ejemplo, para cambiar rpidamente todos los bits de un puerto de salida a
entra y viceversa.
BCF
Esta instruccin pone a cero un bit que especifiquemos en el registro que especifiquemos. La
sintaxis es la siguiente:
BCF <registro>,<bit>
La hemos utilizado previamente para cambiar del Banco 1 al Banco 0 cuando ponamos a 0 el bit 5
del registro STATUS. Tambin podemos utilizarla para poner a 0 cualquier bit de cualquier otro
registro/posicin de memoria. Por ejemplo, si queremos poner a 0 el tercer bit de 11001101 que
est almacenado en la posicin de memoria 0Ch, introduciramos:
BCF
0Ch,2
[Nota de la Traduccin: el valor que puede tomar el parmetro <bit> va de 0 a 7, donde 0 es el bit
mas a la derecha de nuestro nmero binario. La siguiente tabla clarifica un poco ms la disposicin
de los bits en un byte (o palabra de 8 bits) y sus posiciones:
Posicin
8 bit 7 bit 6 bit 5 bit 4 bit 3er bit 2 bit 1er bit
Nmero de bit
Nuestro ejemplo
BSF
Esta instruccin pondr a 1 el bit que especifiquemos en cualquier registro que especifiquemos.
Hemos utilizado esta operacin antes para cambiar del Banco 0 al Banco 1. La sintaxis es:
BTFSC
el registro STATUS y mirar los bits individualmente para ver que flags estn a 1. Por ejemplo, si
queremos comprobar si el flag de CARRY ha sido puesto a 1 cuando hayamos sumado dos
nmeros, haremos lo siguiente:
;
BTFSC
03h,0
<instruccin x>
<instruccin y>
Si el valor del bit es 1, entonces BTFSC continuar por la instruccin inmediatamente siguiente. Si
est a 0, entonces se salta esa instruccin. La siguiente seccin de cdigo muestra donde podra
ser utilizada:
Bucle
:
:
:
BTFSC
03,0
Goto Bucle
En el cdigo anterior, el PIC solo saldr de bucle si el bit 0 del registro STATUS (o el flag de
CARRY) est puesto a 0. De otro modo, el comando 'goto' ser ejecutado.
BTFSS
Esta instruccin dice "Comprueba el bit del registro o posicin de memoria(F), y salta si est a 1".
Esta es similar a la instruccin BTFSC, excepto que el PIC se saltar la siguiente instruccin si el
bit que estamos comprobando est a 1, en lugar de a 0.
CLRF
Esta instruccin pondr todos los bits del contenido de un registro a 0. La sintaxis es:
CLRF <registro>
La hemos usado anteriormente para poner todos los pines de un puerto como salidas, haciendo
"CLRF 85h". Tambin lo usamos para poner los pines de un puerto que estaban como salidas
todos a 0, haciendo "CLRF 05h".
CLRW
CLRW
RLF y RRF
Estas instrucciones desplazan los bits del contenido de un registro un lugar hacia la izquierda
(RLF), o un lugar hacia la derecha (RRF). Por ejemplo, si tenemos 00000001 y utilizamos la
instruccin RLF, tendramos 00000010. Ahora, qu ocurre si tenemos 10000000 y empleamos
RLF? Bien, el bit 1 ser desplazado al flag CARRY. y si empleamos RLF una vez ms, el 1
reaparecer justo al principio [Nota de la traduccin: es decir, por el lado derecho del byte, o dicho
de otro modo en el bit0]. Lo mismo ocurre con la instruccin RRF pero de manera inversa. El
ejemplo de ms abajo ilustra esto para la instruccin RLF, donde mostramos los 8 bits del
contenido de un registro, y el flag de CARRY:
C 76543210
0
00000001
RLF 0
00000010
RLF 0
00000100
RLF 0
00001000
RLF 0
00010000
RLF 0
00100000
RLF 0
01000000
RLF 0
10000000
RLF 1
00000000
RLF 0
00000001
RLF <registro>,d
RRF <registro>,d
Programa de ejemplo
Ahora vamos a darte un ejemplo de cdigo que puedes compilar y ejecutar. Este har que una luz
que se desplace, comenzando en el bit 0 del puerto B hasta el bit 8 del mismo, siguiendo al bit 0
del puerto A hasta el bit 5, y despus haciendo todo el camino inverso hasta al principio. Conecta
LEDs a todos los pines de los puertos A y B. Aqu comprenders algunas de las operaciones de bit
mencionadas en este captulo:
TIEMPO
equ
9Fh
PORTB
equ
06h
TRISB
equ
86h
PORTA
equ
05h
TRISA
equ
85h
STATUS
equ
03h
CONTADOR1
equ
0Ch
CONTADOR2
equ
0Dh
bsf
STATUS,5
; Va al Banco 1
movlw
00h
; y configura
movwf
TRISB
; ambos puertos A y B
movlw
00h
; como salidas,
movwf
TRISA
; despus vuelve
bcf
STATUS,5
; al Banco 0.
movlw
00h
; Pon a 0 el Puerto A.
movwf
PORTA
Port B.
Port A.
;
; Comienzo del programa
;
CorreLuz
movlw
01h
movwf
PORTB
call
Retardo
; Espera un momento.
call
Retardo
PORTB,1
call
Retardo
call
Retardo
rlf
PORTB,1
call
Retardo
call
Retardo
rlf
PORTB,1
call
Retardo
call
Retardo
rlf
PORTB,1
call
Retardo
call
Retardo
rlf
PORTB,1
call
Retardo
call
Retardo
rlf
PORTB,1
call
Retardo
call
Retardo
rlf
PORTB,1
call
Retardo
call
Retardo
rlf
PORTB,1
; PORTB = 00000010, C = 0
;
; PORTB = 00000100, C = 0
;
; PORTB = 00001000, C = 0
;
; PORTB = 00010000, C = 0
;
; PORTB = 00100000, C = 0
;
; PORTB = 01000000, C = 0
;
; PORTB = 10000000, C = 0
;
; Este desplaza el bit al ''flag'' CARRY.
; PORTB = 00000000, C = 1
;
PORTA,1
CARRY al puerto A.
; PORTA = 00001, C = 0
;
call
Retardo
call
Retardo
rlf
PORTA,1
call
Retardo
call
Retardo
rlf
PORTA,1
call
Retardo
call
Retardo
rlf
PORTA,1
call
Retardo
call
Retardo
rlf
PORTA,1
call
Retardo
call
Retardo
;
; PORTA = 00010, C = 0
;
; PORTA = 00100, C = 0
;
; PORTA = 01000, C = 0
;
; PORTA = 10000, C = 0;
;
; Desplaza el bit de vuelta por el puerto A
;
rrf
PORTA,1
call
Retardo
call
Retardo
rrf
PORTA,1
call
Retardo
call
Retardo
rrf
PORTA,1
call
Retardo
call
Retardo
; PORTA = 01000, C = 0;
; PORTA = 00100, C = 0;
; PORTA = 00010, C = 0;
rrf
PORTA,1
call
Retardo
call
Retardo
rrf
PORTA,1
; PORTA = 00001, C = 0;
;
; Ahora desplaza el bit de vuela al Puerto B
;
rrf
PORTB,1
call
Retardo
call
Retardo
rrf
PORTB,1
call
Retardo
call
Retardo
rrf
PORTB,1
call
Retardo
call
Retardo
rrf
PORTB,1
call
Retardo
call
Retardo
rrf
PORTB,1
call
Retardo
call
Retardo
rrf
PORTB,1
call
Retardo
call
Retardo
rrf
PORTB,1
call
Retardo
call
Retardo
rrf
PORTB,1
call
Retardo
call
Retardo
; PORTB = 10000000, C = 0
; PORTB = 01000000, C = 0
; PORTB = 00100000, C = 0
; PORTB = 00010000, C = 0
; PORTB = 00001000, C = 0
; PORTB = 00000100, C = 0
; PORTB = 00000010, C = 0
; PORTB = 00000001, C = 0
; Ahora hemos vuelto a donde empezamos,
;
goto
CorreLuz
;
; La subrutina para hacer el retardo entre los movimientos de bits.
;
Retardo
movlw
TIEMPO
movwf
CONTADOR1
Bucle1
;
decfsz
CONTADOR1
goto
Bucle1
movwf
CONTADOR1
Bucle2
CONTADOR1
goto
Bucle2
return
; Fin de la subrutina.
end
Tablas de Datos
Hay una propiedad interesante en el conjunto de instrucciones del PIC, que nos va a permitir
realizar tablas de datos.
Una tabla de datos es una lista simple de valores de datos, donde cada uno de ellos puede
ser ledo(solo ledo) dependiendo de algn criterio.
[Nota de la traduccin: El ejemplo dado en el texto original, se ha sustituido por este que
mostramos a continuacin para hacer ms comprensible el concepto de tabla de datos. Al final del
captulo se incluye un programa para realizar el ejemplo:
Por ejemplo, podras hacer un circuito con el PIC, que cuente de 0 a 9 y represente este nmero
en un display de 7 segmentos. El display de 7 segmentos lo vamos a conectar al puerto B. De
manera que cada pin encienda un LED del display (como tenemos 8 pines en el puerto B, uno de
ellos no sera utilizado). Para ello, podemos utilizar el siguiente esquema de conexiones entre el
PIC y el display:
Error al crear miniatura: No se ha podido guardar la miniatura
Es decir, el pin RB7 no lo conectaramos al display. De este modo tendremos la siguiente tabla de
correspondencias entre los nmeros a representar y el valor binario que debe de tomar el puerto B
para que se enciendan los diodos LED correspondientes:
Nmero Valor para puerto B Imagen en el display
1
b'01000001' = 41h
b'00111011' = 3Bh
b'01101011' = 6Bh
b'01001101' = 4Dh
b'01101110' = 6Eh
b'01111110' = 7Eh
b'01000011' = 43h
b'01111111' = 7Fh
b'01101111' = 6Fh
b'01110111' = 77h
Bien, pues esta tabla de correspondencias la queremos tener almacenada en el PIC, y mediante el
uso de una tabla de datos, vamos a poder hacerlo. Y as podremos representar el nmero
adecuado en cada momento.]
Ahora, antes de continuar con la explicacin de como funciona la tabla de datos, tenemos que
explicar como hace el PIC seguimiento de por donde va el programa mientras se est ejecutando.
Si alguna vez has programado en BASIC esto te ayudar a comprenderlo. Si no, no te preocupes,
aun as sers capaz de comprender el concepto.
LET K=0
11
K=K+1
12
20
PRINT K
21
END
El programa comienza en la linea 10. Una vez que K vale 0, continua con la linea 11. Despus de
que le hemos aadido 1 a K nos desplazamos a la linea 12. Aqu estamos preguntando si K es
mayor que 10. Si lo es, entonces vamos a la linea 20, y si no, volvemos a la 11. La linea 20
muestra el valor de K en pantalla, y la linea 21 finaliza el programa. BASIC utiliza los nmeros de
linea para ayudar al programador a hacer seguimiento de donde est cada cosa, ya que las
etiquetas no estn permitidas.
El PIC utiliza etiquetas para saltar de unas posiciones a otras, si?. Utilizamos las etiquetas para
que sepamos donde estn las cosas, y tambin para que podamos decirle al PIC de una manera
sencilla donde tiene que ir. Lo que realmente ocurre es que el PIC utiliza un contador de lnea
interno llamado Contador de Programa [Nota de la traduccin: en ingles "Program Counter",
abreviado tambin como PC. ]. El Contador de Programa mantiene almacenada la direccin de la
posicin de memoria donde se encuentra la instruccin actual. Cuando le decimos al PIC que vaya
a una etiqueta en particular, sabe la posicin de memoria y por tanto modifica el PC hasta que
alcanza esa posicin y la lee. Esto es exactamente el mismo modo en el que leemos nosotros el
programa BASIC anterior.
Aqu hay una seccin de cdigo, con las posiciones de memoria, o contenidos del PC, junto a cada
instruccin:
PC
Instruccin
----------------------------0000
movlw
03h
0001
movwf
0Ch
decfsc
0Ch
0003
goto
Bucle
0004
end
0002
Bucle
En este ejemplo anterior hemos puesto el PC a 0000. En esta posicin tenemos la instruccin
"movlw 03h". Cuando el PIC ha ejecutado esta instruccin, incrementar PC para que la siguiente
instruccin pueda ser leda. Aqu el PIC ve "movwf 0Ch". El PC se incrementa de nuevo. Ahora el
PIC lee "decfsc 0Ch". Si el contenido del registro 0Ch no es 0, entonces el PC se incrementa en 1,
y la siguiente instruccin, "goto Bucle", le dice al PC que vaya de vuelta a la posicin 0002, en la
cual hemos escrito la palabra "Bucle". Si el contenido del 0Ch es 0, entonces el PC ser
incrementado en 2, en otras palabras se saltar la siguiente instruccin. Esto colocar el PC en la
posicin 0004, donde el programa termina.
Las posiciones son asignadas por el programa ensamblador (p.ej. MPASM), y normalmente no nos
tenemos que preocupar de lo que hace el Contador de Programa(PC). Hasta que necesitemos
controlarlo, como vamos a hacer cuando usemos tablas de datos.
La mejor manera de explicar como funciona una tabla de datos es comenzar con un pequeo
ejemplo.
PC
equ
02h
movlw
03h
call
tabla
:
:
:
tabla
addwf
PC
retlw
0Ah
retlw
0Bh
retlw
0Ch
retlw
0Dh
retlw
0Eh
retlw
0Fh
retlw
10h
;
return
RETLW realmente significa "RETurn, y pon el Literal en W". Date cuenta de que hemos puesto una
coma despus de la palabra "return" en la frase anterior. Como si estuvisemos en una subrutina
normal, necesitamos una instruccin de "return" para salir de ella. Eso lo hace el RET en la
instruccin. Despus de la instruccin RETLW hay un nmero, y este es el que colocaremos en el
registro W. En este caso el nmero 0Ch.
[Nota de la traduccin: En resumidas cuentas, el valor que ponemos en el registro W antes de
llamar a la subrutina "tabla", indica qu posicin queremos mirar de la tabla. Es decir, acta de
ndice o indicador de la posicin que queremos ver. Al pasarle un 3 a W antes de "call tabla", le
estamos diciendo que queremos recuperar el valor que existe en la posicin 3 de la tabla. La
primera instruccin de la subrutina "tabla" dice "sumarle el contenido de W a PC", es decir, le
sumar 3 a PC. Lo cual va a trasladar el PC hasta la instruccin "retlw 0Ch". Entonces, este valor,
0Ch, ser copiado en el registro W, y se efectuar un "RETurn", que hace que el programa
contine por la instruccin siguiente a "call tabla". Ahora, estamos de vuelta al flujo principal del
programa con W cargado con el dato que queramos recuperar de nuestra tabla.
De este modo, hemos creado una tabla de datos, que la podremos consultar cuando queramos
para ver un valor de una posicin concreta. En el ejemplo del display de 7 segmentos se ve de
modo prctico para qu se podra utilizar una tabla de datos.]
(Antes de llamar a la subrutina) podemos asignar cualquier nmero a W, siempre y cuando este
nmero al ser aadido a PC haga que PC se desplace aun elemento de la tabla de la subrutina, all
donde hemos puesto una instruccin retlw. En el ejemplo anterior esto significa que podemos
poner un nmero de 1 a 7. Si nos pasamos de largo de la subrutina, podramos terminar
ejecutando otra parte del programa. Por esta razn, siempre es buena idea poner la tabla de datos
al final del programa, as si nos pasamos de largo podemos llegar al final del programa en
cualquier caso.
[Nota de la traduccin: Continuando con el ejemplo del display de 7 segmentos, ponemos aqu la
tabla de datos que tendremos que crear. La hemos sacado de la tabla del principio de este
captulo:
Poscin Valor
b'01000001' = 41h 1
b'00111011' = 3Bh 2
b'01101011' = 6Bh 3
b'01001101' = 4Dh 4
b'01101110' = 6Eh 5
b'01111110' = 7Eh 6
b'01000011' = 43h 7
b'01111111' = 7Fh 8
b'01101111' = 6Fh 9
10
b'01110111' = 77h 0
Hemos puesto el 0 en la ltima posicin, para que el resto de los nmeros que hay que representar
en el display, coincidan con la posicin que ocupan en la tabla.
De este modo, la tabla se podra crear utilizando el siguiente cdigo:
PC
equ
02h
:
:
tabla
addwf
PC
retlw
41h
retlw
3Bh
retlw
6Bh
retlw
4Dh
retlw
6Eh
retlw
7Eh
retlw
43h
retlw
7Fh
retlw
6Fh
retlw
77h
return
Hemos reutilizado parte del cdigo que hemos empleado anteriormente para crear este nuevo. As
que habr partes que te sern familiares. Este es el cdigo completo para hacer que el PIC
muestre en el display una cuenta ascendente de 0 a 9 y vuelta a empezar:
PC
equ
02h
TIEMPO
equ
9Fh
PORTB
equ
06h
TRISB
equ
86h
STATUS
equ
03h
CONTADOR1
equ
0Ch
Port B.
CONTADOR2
equ
0Dh
;
; Configuramos el Puerto B
;
bsf
STATUS,5
; Va al Banco 1
movlw
00h
movwf
TRISB
bcf
STATUS,5
; Vuelta al Banco 0.
movlw
00h
movwf
PORTB
;
; Comienzo del programa
;
Inicio
movlw
0Ah
call
tabla
PORTB
la tabla
encendido del 0.
movwf
representar 0 en el display.
;
call
Retardo
; Espera un momento.
call
Retardo
movlw
01h
call
tabla
PORTB
;
la tabla
encendido del 1.
movwf
representar 1 en el display.
;
call
Retardo
call
Retardo
movlw
02h
call
tabla
PORTB
;
la tabla
encendido del 2.
movwf
representar 2 en el display.
;
call
Retardo
call
Retardo
movlw
03h
call
tabla
PORTB
;
la tabla
encendido del 3.
movwf
representar 3 en el display.
;
call
Retardo
call
Retardo
movlw
04h
call
tabla
PORTB
;
la tabla
encendido del 4.
movwf
representar 4 en el display.
;
call
Retardo
call
Retardo
movlw
05h
call
tabla
PORTB
;
la tabla
encendido del 5.
movwf
representar 5 en el display.
;
call
Retardo
call
Retardo
movlw
06h
call
tabla
PORTB
;
la tabla
encendido del 6.
movwf
representar 6 en el display.
;
call
Retardo
call
Retardo
movlw
07h
call
tabla
PORTB
;
la tabla
encendido del 7.
movwf
representar 7 en el display.
;
call
Retardo
call
Retardo
movlw
08h
call
tabla
PORTB
;
la tabla
encendido del 8.
movwf
representar 8 en el display.
;
call
Retardo
call
Retardo
movlw
09h
call
tabla
PORTB
;
la tabla
encendido del 9.
movwf
representar 9 en el display.
;
call
Retardo
call
Retardo
goto
Inicio
;
;
; La subrutina para hacer el retardo entre los nmeros.
;
Retardo
movlw
TIEMPO
movwf
CONTADOR1
Bucle1
decfsz
CONTADOR1
goto
Bucle1
movwf
CONTADOR2
Bucle2
CONTADOR2
goto
Bucle2
return
; Fin de la subrutina.
;
; Y aqu la tabla de configuraciones para representar los nmeros
; en el display de 7 segmentos, de acuerdo a nuestro esquema de
conexiones.
;
tabla
addwf
PC
retlw
41h
retlw
3Bh
retlw
6Bh
retlw
4Dh
retlw
6Eh
retlw
7Eh
retlw
43h
retlw
7Fh
retlw
6Fh
retlw
77h
retorna.
retorna.
retorna.
retorna.
retorna.
retorna.
retorna.
retorna.
retorna.
retorna.
return
end
Obviamente, este programa es un simple ejemplo que puede ser optimizado con los conceptos que
has aprendido en los anteriores captulos. podras reducir el nmero de instrucciones ? Podras
hacer que la cuenta fuese ascendente o descendente dependiendo del valor de otro pin del puerto
A ? Animo!! , esta es una buena ocasin de experimentar con lo que ya sabes.
El PIC tiene 4 fuentes de interrupcin. Se pueden dividir en dos grupos. Dos de ellas son fuentes
de interrupciones que pueden ser aplicadas externamente al PIC, mientras que las otras dos son
de procesos internos. Vamos a explicar las dos externas aqu. Las otras dos las explicaremos en
otros captulos cuando miremos los temporizadores [Nota de la traduccin: timers] y el
almacenamiento de datos.
Si observas los pines del PIC, vers que el pin 6 se muestra como RB0/INT. RB0 es obviamente el
bit 0 del Puerto B. El INT simboliza que tambin puede ser configurado como un pin de
interrupcin. Tambin, los bits del 4 al 7 del puerto B (pines 10 al 13) pueden ser utilizados para
interrupciones. Antes de que usemos INT u otros pines del puerto B, necesitamos hacer dos cosas.
La primera necesitamos decirle al PIC que vamos a usar las interrupciones. Segunda, necesitamos
especificar que pin del puerto B usaremos como interrupcin y no como un pin de Entrada/Salida.
Dentro del PIC hay un registro llamado INTCON, y su direccin es la 0Bh. Dentro de este registro
hay 8 bits que pueden ser habilitados o deshabilitados. El bit 7 de INTCON es llamado GIE [Nota
de la Traduccin: en ingles de las siglas "Global Interrupt Enable", en castellano "Habilitador de
Interrupciones Global"]. Poniendo este bit a 1 le decimos al PIC que vamos a usar una interrupcin.
El bit 4 de INTCON es llamado INTE [Nota de la traduccin: en ingles, "INT Enable". En castellano
"Habilita INT", o dicho de otro modo que utilizaremos el pin 6 con su funcin INT, no como RB0.].
Poniendo este bit a 1 decimos al PIC que RB0 ser un pin de interrupcin. Poniendo a 1 el bit 3 de
INTCON, llamado RBIE, decimos al PIC que usaremos los bits del 4 al 7 del puerto B como
interrupciones. Ahora el PIC sabe que cuando uno de estos pines cambia a nivel alto o cambia
nivel bajo, tiene que parar lo que est haciendo e ir a una rutina de interrupcin. Ahora, tenemos
que decirle al PIC si la interrupcin se va a producir en la transicin ascendente de la seal(de 0
Voltios a +5 Voltios) o la descendente (de +5V a 0V). En otras palabras, queremos que el PIC
haga la interrupcin cuando la seal vaya de nivel bajo a nivel alto, o de nivel alto a nivel bajo? .
Por defecto, esto est configurado para que sea en la transicin ascendente (o flanco de subida de
la seal). El flanco de "disparo" se configura en otro registro llamado OPTION, que est en la
direccin 81h.
El bit en el que estamos interesados es el bit 6, que se llama INTEDG [Nota de la traduccin: del
ingles "INTerrupt EDGe", o en castellano "flanco para la interrupcin" ]. Poniendo este a 1 causar
que el PIC sea interrumpido en el flanco de subida o transicin ascendente (el estado por defecto)
y ponindolo a 0 causar que el PIC sea interrumpido en el flanco de bajada o transicin
descendente. Si quieres que el PIC sea interrumpido en el flanco de subida, no necesitas
configurar el bit. Pero, desafortunadamente el registro OPTION est en el Banco 1, lo que significa
que tienes que cambiar del banco 0 al banco 1, cambiar el bit del registro OPTION, y despus
volver al banco 0. El truco est en hacer todas las operaciones con los registros del banco 1 de
una sola vez, tales como configurar los pines de los puertos, etc... y despus volver al banco 0
cuando hayas terminado.
[Nota de la Traduccin: A modo de esquema, esto es lo que hay que hacer por orden, para utilizar
las interrupciones externas:
2.1 - Poner a 1 el bit 4 (INTE), si queremos utilizar el pin 6 como entrada de INTerrupcin.
2.2 - O poner a 1 el bit 3 (RBIE), si queremos utilizar los pines del 10 al 13 (bits 4 a 7 del
puerto B) como entrada de interrupcin.
Bien, as que ahora que le hemos dicho al PIC qu pin va a ser para la interrupcin, y en qu
flanco se va a disparar la misma, qu ocurre en el programa y en el PIC cuando sucede la
interrupcin ?
Ocurren dos cosas. Primero, se activa un flag. Este le dice al procesador interno del PIC que ha
ocurrido una interrupcin. Segundo, el Contador de Programa (que hemos mencionado en el
captulo anterior) apunta a una direccin particular dentro del PIC. Vamos a ver cada cosa
separadamente.
El Flag de Interrupcin
En nuestro registro INTCON, el bit 1 es el flag de interrupcin llamado INTF [Nota de la Traduccin:
"INTerrupt Flag"]. Entonces, cuando ocurra una interrupcin, este flag se pondr a 1. Mientras no
haya una interrupcin, el flag estar a 0. Y eso es todo lo que hace. Ahora estars probablemente
pensando, y entonces para qu sirve? Bien, mientras este flag est a 1, el PIC no puede, de
ninguna manera, atender a cualquier otra interrupcin. Por que digamos que causamos una
interrupcin. El flag se pondr a 1, y el PIC va a nuestra rutina de interrupcin para procesarla. Si
el flagno fuese puesto a 1, al PIC se le permitira seguir respondiendo a las interrupciones, y por
tanto una serie de pulsos continuos sobre el pin haran que el PIC volviese a comenzar con nuestra
rutina, y nunca la terminara. Volviendo a mi ejemplo del telfono, es como descolgar el telfono, y
tan pronto como comienzas a hablar, que volviese a sonar de nuevo porque alguien ms quiere
hablar contigo. Es mucho mejor terminar una conversacin, y despus descolgar el telfono de
nuevo para hablar con la segunda persona.
Hay un pequeo inconveniente respecto a este flag. Aunque el PIC automticamente pone el flag a
1, no lo pone de nuevo a 0! Esta tarea tiene que hacerla el programador (por ejemplo t). Se hace
muy fcilmente, y como seguro que ya supones, se tiene que hacer despus de que el PIC haya
ejecutado la rutina de interrupcin.
La Posicin de Memoria
Cuando enciendes por primera vez el PIC, o si ocurriese un reset, el Contador de Programa apunta
a la direccin 0000h, que est justo al principio de la memoria de programa. Sin embargo, cuando
ocurre una interrupcin, el Contador de Programa apuntar a la direccin 0004h. As que, cuando
escribamos nuestro programa con interrupciones, lo primero que tenemos que decirle al PIC es
que se salte la direccin 0004h, y mantenga, la rutina de interrupcin que empieza en esa direccin
0004h, separada del resto del programa. Esto es muy fcil de hacer.
Primero, comenzamos nuestro programa con un comando llamado ORG. Este comando significa
Origen, o inicio. Y lo que le sigue es una direccin.[Nota de la traduccin: ORG no es una
instruccin del PIC sino una directiva del ensamblador, al igual que lo son EQU o END que vimos
anteriormente. ORG indica al ensamblador la direccin de memoria a partir de la cual se deber
ubicar el cdigo que viene escrito a continuacin de ella.] Dado que el PIC comienza en la
direccin 0000h, escribimos "ORG 0000h". Lo siguiente que tenemos que hacer es saltarnos la
direccin 0004h. Hacemos esto con una instruccin "GOTO", seguida de la etiqueta que apunte a
nuestro programa principal.
A continuacin del "GOTO" ponemos otro comando ORG, pero esta vez seguido de la direccin
0004h. Es despus de este comando donde introduciremos la rutina de interrupcin. Ahora
despus, podramos o bien escribir directamente nuestra rutina de interrupcin seguida de un
segundo comando ORG, o bien podemos poner una instruccin "GOTO" que apunte a la rutina de
interrupcin (que podramos escribirla al final del programa). Realmente es una cuestin de elegir
por tu parte. Despus, en la rutina de interrupcin, para decirle al PIC que ha llegado al final de la
misma tenemos que poner la instruccin "RETFIE" justo al final. Este comando significa "retorna de
la rutina de interrupcin". Cuando el PIC la ve, el Contador de Programa apuntar a la ltima
posicin en la que el PIC estaba antes de que ocurriese la interrupcin.
Ponemos aqu un fragmento de cdigo para mostrar lo anterior:
;
ORG
0000h
GOTO
Inicio
; Ve al programa principal.
ORG
0004h
reset.
interrupcin.
Inicio
RETFIE
principal
Hay dos cosas que tienes que tener en cuenta cuando utilices interrupciones. La primera que si
ests usando el mismo registro en tu programa principal y en la rutina de interrupcin, recuerda
que los contenidos del registro, probablemente cambien cuando ocurra la interrupcin. Por
ejemplo, ests utilizando el registro W para enviar datos al puerto A en el programa principal, y
tambin vas a utilizar el registro W en tu rutina de interrupcin para mover datos de una posicin a
otra. Si no tienes cuidado, el registro W contendr el ultimo valor que tena cuando estaba en la
rutina de interrupcin, y cuando vuelvas de la interrupcin, este dato se enviar al puerto A en
lugar del valor que tenas antes de que ocurriese la interrupcin. Para evitar esto, dentro de la
rutina de interrupcin, se tienen que almacenar los contenidos del registro W, antes de que lo uses
. Lo segundo es que tiene que existir un retardo entre que ocurre una interrupcin y que pueda
ocurrir la siguiente. Como sabes, el PIC tiene un reloj externo que puede ser, o bien un Cristal, o
bien una red RC (resistencia-condensador). Independientemente de la frecuencia de reloj, el PIC la
divide entre 4 y la utiliza para su reloj interno. Por ejemplo, si usas un cristal de 4MHz, el PIC
llevar a cabo las instrucciones a 1MHz. A este tiempo interno se le llama Ciclo de Instruccin.
Bien, la hoja de caractersticas (datasheet), aunque en letra pequea, dice que debes dejar que
pasen de 3 a 4 ciclos de instruccin entre interrupciones. Mi consejo es que dejes 4 ciclos. La
razn para este retardo es que el PIC necesita tiempo para saltar a la direccin de interrupcin,
poner el flag, y volver de la rutina de interrupcin. As que ten esto en mente si estas utilizando otro
circuito que genere una interrupcin en el PIC.
Un punto a recordar es que si utilizas los bits del 4 al 7 del puerto B como interrupcin, no puedes
seleccionar un pin individual del puerto B para que sirva como interrupcin. As, si habilitas estos
pines, todos estarn habilitados a la vez. De modo que, por ejemplo, no puedes tener solamente
los bits 4 y 5, los bits 6 y 7 tambin los tendrs habilitados. Entonces Para que sirve tener cuatro
bits que actan como interrupcin? Bien, podras tener un circuito conectado al PIC, y si cualquiera
de las cuatro lineas se pusiese a nivel alto, esta podra ser la condicin que necesites para que el
PIC actuase rpidamente. Un ejemplo de esto puede ser la alarma de una casa, donde cuatro
sensores estn conectados a los bits 4 a 7 del puerto B. Cualquier sensor podra activar al PIC
para que hiciese sonar una alarma, y que la rutina de hacer sonar la alarma fuese la rutina de
interrupcin. Esto ahorra el estar comprobando todo el tiempo todos los pines, y permite al PIC
continuar haciendo otras cosas.
La primera cosa que tenemos que hacer es decirle al PIC que salte la direccin donde el Contador
de Programa apuntar cuando ocurra una interrupcin. Aqu notars que estamos utilizando una
forma distinta de expresar los nmero hexadecimales. Antes usbamos "F9h", donde "h" denotaba
que era hexadecimal. Podemos escribir esto como 0xF9, y ese ser el formato que vamos a utilizar
a partir de ahora.
;
org
0x00
Principal
org
0x04
de interrupcin.
retfie
interrupcin
; ha terminado y el PC volver a apuntar
al programa principal
Principal
programa principal.
Ahora tenemos que decirle al PIC que vamos a utilizar las interrupciones, y que el pin de
interrupcin va a ser el pin 6 (RB0):
;
bsf
INTCON,7
(1=habilitado)
bsf
INTCON,4
(1=habilitado)
Vamos a poner a 0 el flag de interrupcin por si acaso (no nos fiamos de nada!!):
;
bcf
INTCON,1
Ahora tenemos que configurar nuestros dos puertos. Recuerda que como estamos utilizando RB0
como pin de interrupcin, este debe de ser configurado como entrada:
;
bsf
STATUS,5
; Cambia al banco 1.
movw
0x01
movwf
TRISB
movlw
0x10
movwf
TRISA
bcf
STATUS,5
; Vuelve al banco 0.
como salida
Vamos a utilizar una variable llamada CONTADOR para almacenar el numero de conmutaciones
contadas.
Podramos simplemente incrementar el valor del puerto A, pero vers porque estamos utilizando
una variable cuando escrbimos nuestra rutina de interrupcin:
Bucle
movf
movwf
PORTA
goto
Bucle
W.
end
As, nuestro programa principal est escrito, y ahora tenemos que decirle al PIC que hacer cuando
ocurra una interrupcin. respecto a esto, nuestra interrupcin va a ser una conmutacin. Lo que
queremos que haga el PIC es aadir 1 a la variable CONTADOR cada vez que el switch est
cerrado. Sin embargo, solo queremos mostrar los nmero que el switch se cierra de 0 a 9 veces.
Antes dijimos que simplemente podramos incrementar el puerto A cada vez que hubiese una
interrupcin. Pero, el puerto A tiene 5 bits, y si simplemente incrementamos el valor del puerto,
tendremos una cuenta de hasta 31. Hay dos razones por las que elegimos no contar hasta 31. La
primera, vamos a utilizar 4 diodos, con los cuales solo podemos mostrar de 0 a 15 (de 0 a F en
hexadecimal, o 0000 a 1111 en binario). Segundo, tambin queremos mostrarte algunos comandos
aritmticos que has visto en los pasados captulos.
TEMPORAL
temporal.
Lo siguientes que queremos es aadir 1 a nuestra variable CONTADOR:
;
incf
0x0A
; Ponemos 10 en W.
subwf
STATUS,0
si
; CONTADOR es igual o mayor que w,
; y se activar como resultado de la
instruccin subwf
Ahora sabemos si el valor de CONTADOR es 9 o ms. Lo que queremos hacer ahora es, si
CONTADOR es mayor que 9, ponlo de nuevo a 0, de otro modo vuelve al programa principal para
que podamos enviarlo al PORTA. La instruccin BFTSS, como sabes, se saltar la siguiente
instruccin si el flag de CARRY se pone a 1. Por ejemplo CONTADOR=10:
;
goto
continua
goto
limpiar
bcf
ponerlo a 0
continua
para
; permitir ms interrupciones.
movfw
TEMPORAL
de la interrupcin.
retfie
limpiar
clrf
CONTADOR
bcf
INTCON,1
para
; permitir ms interrupciones.
retfie
Todo lo que queda hacer ahora es ponerlo todo junto y tambin definir los valores de nuestras
constantes, lo cual se har justo al principio de nuestro programa.
Aqu debajo est el listado completo. El circuito se muestra despus del listado del programa.
Cada vez que pulses el conmutador, los LEDs contarn in binario desde 0000 a 1010, y vuelta a
0000.
;
org
0x00
EQU
0x0B
PORTB
EQU
0x06
PORTA
EQU
0x05
TRISA
EQU
0x85
TRISB
EQU
0x86
STATUS
EQU
0X03
CONTADOR
EQU
0x0c
TEMPORAL
EQU
0x0d
goto
Principal
0x04
movwf
TEMPORAL
incf
CONTADOR,1
de interrupcin.
temporal.
; el resultado de vuelta en CONTADOR.
movlw
0x0A
; Ponemos 10 en W.
subwf
CONTADOR,0
btfss
STATUS,0
si
; CONTADOR es igual o mayor que w,
; y se activar como resultado de la
instruccin subwf
goto
continua
goto
limpiar
bcf
ponerlo a 0
continua
para
; permitir ms interrupciones.
movfw
TEMPORAL
de la interrupcin.
retfie
limpiar
clrf
CONTADOR
bcf
INTCON,1
para
; permitir ms interrupciones.
retfie
;
;***************PROGRAMA PRINCIPAL***********************************
Principal
programa principal.
INTCON,7
INTCON,4
INTCON,1
(1=habilitado)
bsf
(1=habilitado)
bcf
;
;****************Configura los puertos*******************************
bsf
STATUS,5
; Cambia al banco 1.
movw
0x01
movwf
TRISB
movlw
0x10
movwf
TRISA
bcf
STATUS,5
; Vuelve al banco 0.
como salida
;
;****************Ahora enva el valor de CONTADOR al PORTA***********
Bucle
movf
movwf
PORTA
goto
Bucle
W.
end
El Watchdog Timer
Ahora vamos a echar un vistazo a un temporizador interno, llamado "Watchdog Timer" [Nota de la
traduccin: En castellano sera, "temporizador perro guardin"]
As que, Qu es un watchdog timer?
Supn que has escrito un programa que est continuamente corriendo en un PIC. Ahora, quieres
asegurarte de que el programa sigue ejecutndose siempre, y no hay manera de que se pare
nunca. La primera cosa que tienes que hacer, por supuesto, es un bucle que desde el final del
programa te lleve hasta el principio. Pero ten en cuenta esto. Digamos que el PIC est
monitorizando una entrada. Cuando esta entrada se pone a nivel alto, salta a otra parte del
programa y espera por otro pin para que se ponga a nivel alto. Si el segundo pin no se pone a nivel
alto, el PIC simplemente "se sentar a esperar". Solo saldr de ah si el segundo pin se pone a
nivel alto.
Consideremos otro ejemplo. Supn que has escrito un programa, lo has compilado con xito, e
incluso lo has simulado una y otra vez utilizando un simulador como MPLAB. Todo parece
funcionar bien. Programas el PIC y lo colocas en tu circuito. Sin embargo despus de un largo
periodo el programa se atasca en algn punto y el PIC se queda enganchado en un bucle.
Lo que se necesita en ambos casos es alguna clase de reset(o reinicio) si el programa se qued
atascado. Este es el propsito del watchdog timer.
La primera, cuanto tiempo tenemos antes de tener que hacer un reinicio al WDT.
Finalmente, tenemos que decirle al software programador del PIC que habilite el WDT dentro
del PIC.
Tiempos de WDT
La hoja de datos del PIC especifica que el WDT tiene un periodo desde su inicio hasta el final de
18 ms. Esto depende de varios factores, como el voltaje aplicado, la temperatura del PIC, etc... La
razn de esta aproximacin es debida a que el reloj del WDT es suministrado por una red RC
interna. El tiempo de carga de la red RC depende del voltaje de alimentacin. Tambin depende de
los valores de los componentes, los cuales cambian ligeramente dependiendo de su temperatura.
As que por razones de simplicidad, tomaremos los 18 ms como el tiempo de reinicio del WDT.
Sin embargo, podemos hacer este tiempo mayor. Dentro del PIC hay un elemento
llamado Prescaler [Nota de la Traduccin: "Prescaler" se puede traducir como "etapa previa de
ajuste de escala"]. Podemos programar este prescaler para dividir el reloj interno de la red RC.
Cuanto mayor sea el factor de divisin, ms tiempo tardar el WDT en reiniciarse.
El prescaler est localizado en el registro OPTION en la direccin 81h, los bit del 0 al 2 inclusive.
Ms abajo hay una tabla que muestra las asignaciones de los bits para cada ratio de divisin y el
tiempo de reinicio del WDT:
Bit 2 Bit 1 Bit 0 Ratio Tiempo de WDT
0
1:1
18ms
1:2
36ms
1:4
72ms
1:8
144ms
1:16
288ms
1:32
576ms
1:64
1.1 Segundos
Recuerda que estos tiempos son independientes de la frecuencia de tu reloj externo. Piensa en
estos tiempos como en tiempo real, en lugar de como tiempos de reloj. Para ayudar a clarificar
esto, vamos a suponer que queremos que el WDT reinicie nuestro PIC despus de cerca de medio
segundo como tiempo de seguridad ante fallo. El valor ms prximo que tenemos es 576 ms o
0,576 segundos. Todo lo que hacemos es enviar b'101' a nuestro registro OPTION, tal y como
sigue:
movlw
b101
movwf
81h
Realmente sencillo. Ahora, hay una trampa. Por defecto el prescaler est asignado a otro
temporizador interno [Nota de la Traduccin: Se refiere al temporizador TMR0.]. Esto significa que
tenemos que cambiar el prescaler al WDT [Nota de la Traduccin: Para asignar el Prescaler al
WDT hay que poner el bit 3 del registo OPTION a 1. Es decir, para poner el prescaler a 576 ms
como decamos antes y asignarlo al WDT, Hay que enviar al registro OPTION el valor b'1101]:
Despus tenemos que cambiar al banco 1 para asignar el prescaler al WDT y configurar el
tiempo.
STATUS,0
01h
STATUS,0
0
clrf
ponemos a 0.
bsf
clrwdt
movlw
''preescaler''(bits 0 al 2)
; y lo asignamos al WDT(ver bit 3 puesto a
1).
movwf
OPTION
; y se lo asignamos al WDT
bcf
STATUS,0
; Vuelve al banco 0
La instruccin anterior CLRWDT es la que se utiliza para reiniciar el WDT antes de que este
reinicie al PIC. As que todo lo que tenemos que hacer es calcular donde en nuestro programa
ocurrir la finalizacin del tiempo del WDT, y enviar el comando CLRWDT justo antes de este
punto, para que nos aseguremos de que el PIC no se reinicia. Si tu programa es largo, ten en
cuenta que puede que necesites ms de un CLRWDT. Por ejemplo, si utilizas el tiempo por defecto
18 ms, entonces tenemos que asegurarnos de que el programa ve un CLRWDT cada 18 ms.
As que ahora llegamos al punto donde tenemos que trabajar en cuanto tiempo tarda nuestro
cdigo en ejecutarse, en tiempo real. El principio es muy simple, pero puede que te tires de los
pelos!
instruccin "goto" tarda 2 ciclo, porque est causando que el contador de programa (PC) vaya a
otro sitio del programa. La instruccin RETURN tarda 2 ciclos, porque causa que el PC vuelva al
programa principal. Al menos creemos que con esto ya puedes ver por donde va el tema.
Sin embargo, hay cuatro instrucciones que pueden tardar 1 2 ciclos. Estas son DECFSZ,
INCFSZ, BTFSC y BTFSS. Estas instrucciones tienen una cosa en comn. Se saltan la siguiente
instruccin en caso si se cumple cierta condicin. Si no se cumple la condicin, entonces se lleva a
cabo la siguiente instruccin. Por ejemplo, la instruccin DECFSZ decrementar en 1 el valor
almacenado en el registro F. Si el resultado es 0, entonces la siguiente instruccin ser ejecutada.
Esta instruccin por tanto tarda 1 ciclo. Si el resultado es 0, entonces la instruccin siguiente se la
salta, y la siguiente a la anterior ser ejecutada. En este ejemplo la instruccin tarda 2 ciclos. La
razn es que la instruccin altera el PC. Necesita 1 ciclo para ejecutar la funcin, y necesita otro
para cambiar el PC por uno mas extra.
Para aclarar esto, vamos a mirar un cdigo simple, y trabajaremos sobre los ciclos de instruccin
que tarda:
;
Bucle
movlw
02
movwf
CONTADOR
decfsz
CONTADOR
goto
Bucle
end
Nuestra primera instruccin simplemente mueve el valor 02 a W. Esto no causa ningn salto, por
tanto es solo 1 ciclo. La siguiente instruccin es similar, mueve los contenidos del registro W a
CONTADOR. De nuevo, esto tardar 1 ciclo. Ahora, la siguiente instruccin primero decrementa
CONTADOR en 1. Esto es 1 ciclo. Despus har una comprobacin para ver que CONTADOR es
igual a 0. En este momento no lo es, por tanto vamos a la siguiente instruccin. La siguiente
instruccin es una de "goto", y por tanto tarda 2 ciclos. Volvemos a nuestra instruccin DECFSZ, la
que decrementa CONTADOR en 1 de nuevo. Esto tarda otro ciclo. Hace una comprobacin para
ver si CONTADOR es igual a 0. Esta vez lo es, y por tanto se salta la siguiente instruccin. Para
saltarse la siguiente instruccin se requiere otro ciclo. Alcanzamos el final del programa. As que en
total, con el valor de 02 para CONTADOR, este programa tarda 7 ciclos en total. Si estamos
usando un cristal de 4MHz para nuestro reloj, entonces el programa tarda:
Software Programador
Dentro del PIC hay elementos llamados "Fusibles" [Nota de Traduccin: En ingles "Fuses"]. No son
los mismos que puedes encontrar en los enchufes, sino que son conmutadores electrnicos que se
pueden "fundir" por el programador. Uno de estos fusibles tiene que ser 'fundido' para que el WDT
pueda operar. Hay dos formas de hacerlo. Una es escribiendo un par de lineas al comienzo de tu
programa para decirle al software programador del PIC que habilite o deshabilite ciertos "fusibles".
La otra forma de hacerlo es decirle al software programador del PIC manualmente que fusibles
habilitar. Echaremos un vistazo a nuestro programa para instruir al software programador del
captulo pasado, cuando veamos como incluir otros ficheros y macros. El cmo hacerlo
manualmente vara dependiendo del software de programacin. La documentacin que viene con
el programador suele decir como hacerlo. Como estoy utilizando el [software PICALLW],
explicaremos como cambiar los "fusibles" con este programa:
Los fusibles se configuran pulsando la tecla F3, o haciendo 'click' sobre el botn de configuracin.
Despus seleccionas el fusible que quieres habilitado, en ese caso el WDT, haciendo una marca
en la caja que est junto a l.
Programa de ejemplo
Vamos a escribir un programa, donde modifiquemos el WDT, y permitamos al PIC ejecutar una
funcin. Primero borraremos el WDT peridicamente para mostrar que el programa funciona, y
despus quietaremos la instruccin CLRWDT para mostrar que efectivamente el PIC se reinicia.
equ
9FH
PORTB
equ
06H
TRISB
equ
86H
de retardo.
equ
05H
TRISA
equ
85H
03H
; Registro para
equ
seleccionar el banco.
CONTADOR1
equ
0CH
CONTADOR2
equ
0DH
bsf
STATUS,5
; 1 ciclo,
0.5mS
movlw
00H
; 1 ciclo,
1.0mS
movwf
TRISB
; 1 ciclo,
1.5mS
movlw
00H
; 1 ciclo,
2.0mS
movwf
TRISA
; 1 ciclo,
2.5mS
bcf
STATUS,5
; 1 ciclo,
3.0mS
movlw
00H
; 1 ciclo,
3.5mS
movwf
PORTA
; 1 ciclo,
4.0mS
4.5mS
01H
; 1 ciclo,
movwf
PORTB
; 1 ciclo,
call
RETARDO
; 2 ciclos,
486mS
call
RETARDO
; 2 ciclos,
967mS
5.0mS
PORTB,1
; 1 ciclo,
967.5mS
call
RETARDO
; 2 ciclos, 1.45S
call
RETARDO
; 2 ciclos, 1.93S
rlf
PORTB,1
; 1 ciclo,
call
RETARDO
; 2 ciclos, 2.41S
call
RETARDO
; 2 ciclos, 2.89S
rlf
PORTB,1
; 1 ciclo,
call
RETARDO
; 2 ciclos, 3.37S
call
RETARDO
; 2 ciclos, 3.85S
rlf
PORTB,1
; 1 ciclo,
call
RETARDO
; 2 ciclos, 4.34S
call
RETARDO
; 2 ciclos, 4.82S
rlf
PORTB,1
; 1 ciclo,
call
RETARDO
; 2 ciclos, 5.30S
call
RETARDO
; 2 ciclos, 5.78S
rlf
PORTB,1
; 1 ciclo,
call
RETARDO
; 2 ciclos, 6.26S
call
RETARDO
; 2 ciclos, 6.74S
rlf
PORTB,1
; 1 ciclo,
call
RETARDO
; 2 ciclos, 7.22S
call
RETARDO
; 2 ciclos, 7.70S
rlf
PORTB,1
; 1 ciclo,
1.93S
2.89S
3.85S
4.82S
5.78S
6.74S
7.70S
PORTA,1
; 1 ciclo,
7.70S
call
RETARDO
; 2 ciclos, 8.19S
call
RETARDO
; 2 ciclos, 8.67S
rlf
PORTA,1
; 1 ciclo,
8.67S
call
RETARDO
; 2 ciclos, 9.15S
call
RETARDO
; 2 ciclos, 9.63S
rlf
PORTA,1
; 1 ciclo,
call
RETARDO
; 2 ciclos,10.11S
call
RETARDO
; 2 ciclos,10.59S
rlf
PORTA,1
; 1 ciclo, 10.59S
call
RETARDO
; 2 ciclos,11.07S
call
RETARDO
; 2 ciclos,11.55S
9.63S
PORTA,1
; 1 ciclo, 11.55S
call
RETARDO
; 2 ciclos,12.04S
call
RETARDO
; 2 ciclos,12.52S
rrf
PORTA,1
; 1 ciclo, 12.52S
call
RETARDO
; 2 ciclos,12.99S
call
RETARDO
; 2 ciclos,13.48S
rrf
PORTA,1
; 1 ciclo, 13.48S
call
RETARDO
; 2 ciclos,13.96S
call
RETARDO
; 2 ciclos,14.44S
rrf
PORTA,1
; 1 ciclo, 14.44S
PORTB,1
; 1 ciclo, 14.44S
call
RETARDO
; 2 ciclos,14.92S
call
RETARDO
; 2 ciclos,15.40S
rrf
PORTB,1
; 1 ciclo, 15.40S
call
RETARDO
; 2 ciclos,15.89S
call
RETARDO
; 2 ciclos,16.37S
rrf
PORTB,1
; 1 ciclo, 16.37S
call
RETARDO
; 2 ciclos,16.84S
call
RETARDO
; 2 ciclos,17.33S
rrf
PORTB,1
; 1 ciclo, 17.33S
call
RETARDO
; 2 ciclos,17.81S
call
RETARDO
; 2 ciclos,18.29S
rrf
PORTB,1
; 1 ciclo, 18.29S
call
RETARDO
; 2 ciclos,18.77S
call
RETARDO
; 2 ciclos,19.25S
rrf
PORTB,1
; 1 ciclo, 19.25S
call
RETARDO
; 2 ciclos,19.73S
call
RETARDO
; 2 ciclos,20.22S
rrf
PORTB,1
; 1 ciclo, 20.22S
call
RETARDO
; 2 ciclos,20.70S
call
RETARDO
; 2 ciclos,21.18S
goto
CorreLuz
; 2 ciclos,21.18S
;
; Subrutina para introducir un retardo entre los movimientos de los bits.
; Ciclos totales 957,
480mS
RETARDO
movlw
TIEMPO
; 1 ciclo
movwf
CONTADOR1
; 1 ciclo
decfsz
CONTADOR1
; 9F x 1 ciclo + 1 ciclo
goto
BUCLE1
; 9E x 2 ciclos = 316
movwf
CONTADOR2
; 1 ciclo
BUCLE1
= 160 ciclos
ciclos
BUCLE2
;
decfsz
CONTADOR2
; 9F x 1 ciclo + 1 ciclo
goto
BUCLE2
; 9E x 2 ciclos = 316
= 256 ciclos
ciclos
;
return
; 2 ciclos
END
Con un reloj de 8KHz, tarda algo menos de 1 segundo en que el siguiente LED se ilumine, y tarda
en total 21 segundos en ir de un extremo al otro y volver (es decir, lo que tarda le rutina en
ejecutarse una vez solamente). El retardo de la subrutina es de 480ms, y la estamos llamando dos
veces antes de mover el bit por los puertos. Ahora, tenemos que hacer el reinicio peridico del
WDT. El mayor tiempo que podemos configurar para el WDT es de 2,3 segundos, y el siguiente en
la tabla es de 1,1 segundos. Tenemos dos opciones. Podramos hacer una llamada a la subrutina y
reiniciar el WDT despus de que los dos Retardos hayan terminado, o podramos incorporar el
CLRWDT dentro del retardo mismo. Hemos decidido, sin ninguna razn importante, poner el
CLRWDT dentro de la subrutina de retardo.
TIEMPO
equ
9FH
PORTB
equ
06H
TRISB
equ
86H
05H
de retardo.
equ
TRISA
equ
85H
03H
; Registro para
equ
seleccionar el banco.
CONTADOR1
equ
0CH
CONTADOR2
equ
0DH
OPT
equ
81h
controlar el WDT
;************* Configura los puertos, el WDT y el
preescaler******************
clrf
01h
bsf
STATUS,5
; Cambia al banco 1
clrwdt
; reinicia el WDT y el
prescaler
movlw
b1101
; Selecciona un nuevo
movwf
OPT
; se lo asigna al WDT
movlw
00H
movwf
TRISB
movlw
00H
movwf
TRISA
bcf
STATUS,5
; Vuelve al banco 0
movlw
00H
movwf
PORTA
01H
movwf
PORTB
call
RETARDO
call
RETARDO
PORTB,1
call
RETARDO
call
RETARDO
rlf
PORTB,1
call
RETARDO
call
RETARDO
rlf
PORTB,1
call
RETARDO
call
RETARDO
rlf
PORTB,1
call
RETARDO
call
RETARDO
rlf
PORTB,1
call
RETARDO
call
RETARDO
rlf
PORTB,1
call
RETARDO
call
RETARDO
rlf
PORTB,1
call
RETARDO
call
RETARDO
rlf
PORTB,1
PORTA,1
call
RETARDO
call
RETARDO
rlf
PORTA,1
call
RETARDO
call
RETARDO
rlf
PORTA,1
call
RETARDO
call
RETARDO
rlf
PORTA,1
call
RETARDO
call
RETARDO
PORTA,1
call
RETARDO
call
RETARDO
rrf
PORTA,1
call
RETARDO
call
RETARDO
rrf
PORTA,1
call
RETARDO
call
RETARDO
rrf
PORTA,1
PORTB,1
call
RETARDO
call
RETARDO
rrf
PORTB,1
call
RETARDO
call
RETARDO
rrf
PORTB,1
call
RETARDO
call
RETARDO
rrf
PORTB,1
call
RETARDO
call
RETARDO
rrf
PORTB,1
call
RETARDO
call
RETARDO
rrf
PORTB,1
call
RETARDO
call
RETARDO
rrf
PORTB,1
call
RETARDO
call
RETARDO
goto
CorreLuz
;
; Subrutina para introducir un retardo entre los movimientos de los bits.
RETARDO
movlw
TIEMPO
; 1 ciclo
movwf
CONTADOR1
; 1 ciclo
decfsz
CONTADOR1
; 9F x 1 ciclo + 1 ciclo
goto
BUCLE1
; 9E x 2 ciclos = 316
movwf
CONTADOR2
; 1 ciclo
BUCLE1
= 160 ciclos
ciclos
BUCLE2
;
decfsz
CONTADOR2
; 9F x 1 ciclo + 1 ciclo
goto
BUCLE2
; 9E x 2 ciclos = 316
= 256 ciclos
ciclos
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;
;; Esta parte reinicia el
WDT
;;
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;
clrwdt
; Esto simplemente
reinicia el WDT.
; *************** Retorna desde nuestra rutina Retado***************
return
END
Si comentas o quitas la instruccin CLRWDT, vers que el PIC no pasa de iluminar el segundo
LED. Esto es debido a que el WDT hace que se reinicie el PIC. Con el CLRWDT en su sitio, el
programa funcionar como debe