You are on page 1of 43

Tutorial: Programacin de PICs en Ensamblador

De Asociacin de Robotica y Domtica de Espaa (A.R.D.E.)


Nota Inicial
Este tutorial fue escrito originalmente en ingles por Michael Stracey, y lo puedes encontrar en su sitio web
(http://www.mstracey.btinternet.co.uk/pictutorial/picmain.htm). Ese texto est dividido en una introduccin mas 13
tutoriales, que aqu se han asimilado todos como captulos. La traduccin intenta ser lo ms fiel posible al texto original,
pero hay expresiones coloquiales que utiliza el autor que han sido adaptadas a versiones ms comunes del castellano.
Otras expresiones o notas aadidas al texto original estn indicadas convenientemente y marcadas como "notas de la
traduccin". El nivel del tutorial puede considerarse bsico, pero cubre bastante bien la mayora de los tpicos de la
programacin en ensamblador para PICs Microchip.
Contenido
1 Introduccin al PIC
1.1 Introduccin
1.2 Microcontrolador Microchip PIC 16F84
1.3 Los pines del 16F84
1.4 Como Programar el PIC
2 Conectarse al microcontrolador PIC
2.1 Una placa de entrenamiento sencilla
3 Buenas tcnicas para programar
4 Los Registros
4.1 Un ejemplo de cdigo
5 Cmo escribir en los puertos
6 Bucles de Retardo
7 Subrutinas
8 Cmo leer de los puertos E/S
9 Operadores Lgicos y Aritmticos
9.1 Operadores Lgicos
9.2 Operadores Aritmticos
10 Operaciones con Bits
11 Tablas de Datos
12 Interrupciones : una introduccin
12.1 El Flag de Interrupcin
12.2 La Posicin de Memoria
13 Interrupciones : Cmo escribir el cdigo
14 El Watchdog Timer
14.1 Tiempos de WDT
14.2 Temporizacin de las instrucciones
14.3 Software Programador
14.4 Programa de ejemplo
15 Referencias
Introduccin al PIC
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 (http://www.microchip.com/).
Para empezar, echemos un vistazo al PIC.
Microcontrolador Microchip PIC 16F84
Microchip fabrica una serie de microcontroladores llamados PIC. Puedes ver toda la gama de sus microcontroladores aqu
(http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2551) . Los hay disponibles de distintas
capacidades, desde algunos tipos bsicos con poca memoria, hasta los que tienen convertidores Analgico a Digital (ADC)
incluidos o incluso los que llevan dentro PWMs (Pulse Width Modulators = Moduladores de Ancho de Pulso). Voy a
concentrarme en el PIC 16F84. Una vez que aprendas como programar un tipo de PIC, aprender el resto ser fcil.
Hay diversas formas de programar el PIC, - usando BASIC, C, o Lenguaje Ensamblador. Voy a mostrarte el Lenguaje
Ensamblador. No te asustes. Solo hay 35 instrucciones que aprender, y es la manera ms econmica de programar los PICs,
ya que no necesitas ningn otro software extra que no sea de los gratuitos.
Los pines del 16F84
Mas abajo vers el diagrama de patillas(pines en adelante) del PIC 16F84. Pasar por cada pin, explicando para que se
utiliza cada uno.
Error al crear miniatura: No se ha podido guardar la miniatura
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
RB es un segundo puerto bidireccional. Se comporta exactamente de la misma manera que RA, excepto que este tiene 8 bits.
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.
Como Programar el PIC
Bien, espero que no te hayas asustado mucho. Ahora, querrs conocer como programar el PIC, pero adems de aprender las
instrucciones de cdigo de ensamble, como programas realmente ese cdigo y lo metes en el PIC? Pues hay dos maneras,
la sencilla y la "Hazlo tu mismo". La manera sencilla es comprar un programador de PIC, que se conecte a tu PC, que trae
un software con el que puedes programar el PIC. La "Hazlo t mismo" se trata de que construyas tu propio programador y
utilices software gratuito de Internet y lo programes de ese modo.
Si prefieres el mtodo "hazlo tu mismo", te recomendara este sitio (http://www.ic-prog.com/index1.htm). Pulsa sobre
"Supported Programmers" para ver los circuitos. El ms econmico es el "TAIT Classic Programmer". El software para
programar el PIC tambin lo puedes bajar de esa pgina, ves a "Download".
Si quieres ir por la va fcil, echa un vistazo a este sitio: (falta el sitio, el del texto original no funciona).
Otro buen sitio de software gratuito es este (http://www.picallw.com/). Este permite utilizar cualquier programador, puesto
que el software es completamente configurable.
Cualquier mtodo funcionar, ya que ambos darn el mismo resultado, programar el PIC.
Lo siguiente que necesitas es un ensamblador. Este convertir el programa que escribas en un formato que el PIC
comprende. El mejor es del propio Microchip, llamado MPLAB. Es un programa de ventanas, que incluye un editor, un
simulador y el ensamblador. Este es un software escrito por los propios fabricantes del PIC, y por encima de todo es
gratuito !!!
La siguiente imagen ilustra el proceso de programacin de un PIC.
Error al crear miniatura: No se ha podido guardar la miniatura
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.
Conectarse al microcontrolador PIC
Una placa de entrenamiento sencilla
Bien, ahora ya tienes tu programador, y uno o dos PICs. Es muy simple conocer la teora para saber como programar el PIC,
pero el verdadero aprendizaje viene cuando intentas probar tu cdigo en un PIC y ves los resultados en tu propio circuito. He
incluido el diagrama de un circuito que muestra una placa de entrenamiento muy bsica y econmica. Por supuesto, le
puedes aadir LEDs y switches, pero yo he dejado las patillas sin conectar. Puedes monitorizar los pines de entrada/salida
conectando LEDs directamente a los pines, y se encendern cuando los pines se pongan a nivel alto. Tambin, puedes aadir
switches a los pines, para poder seleccionar que pines poner a nivel alto, y cuales a nivel bajo. Bsicamente, lo que estoy
diciendo es que si comienzas con este circuito, puedes aadir lo que creas necesario.
Error al crear miniatura: No se ha podido guardar la miniatura
La linea de alimentacin est puesta a 6 Voltios, que es el mximo voltaje para el PIC. Puedes utilizar cualquier voltaje
inferior, hasta un mnimo de 2 Voltios. C3 es conocido como un condensador de 'bypass'. Todo lo que se hace C3 es reducir
el ruido de la linea de alimentacin. X1 es un cristal de 4 MHz. Puedes utilizar un circuito RC (resistencia y condensador)
(Nota de edicin: crear enlace aqu), pero el precio del cristal es insignificante, y es mas estable. C1 y C2 ayudan a reducir
cualquier desviacin en la oscilacin cristal, y a eliminar cualquier ruido no deseado antes de que la seal llegue al PIC.
Buenas tcnicas para programar
Antes de meternos en harina con la programacin del PIC, creo que ahora es un buen momento para explicar algunas
tcnicas para programar bien.
Si escribes un ; (punto y coma) en cualquier punto de tu programa, el compilador ignorar cualquier cosa que haya detrs de
l, hasta llegar al retorno de carro. Esto significa que podemos aadir comentarios a nuestro programa que nos recuerden
que estbamos haciendo en ese punto. Esta es una buena prctica incluso para los programas ms sencillos. Ahora mismo
puede que entiendas completamente qu es lo que hace tu programa, pero dentro de unos meses, puede que te acabes tirando
de los pelos. Por tanto, utiliza comentarios donde puedas , no hay lmites.
Segundo, puedes asignar nombres a las constantes va los registros (hablaremos de estos ms adelante). Hace lo que ests
escribiendo mucho ms sencillo de leer, para saber de que valor se trata, mas que intentar entender que significan todos esos
nmeros. As que utiliza nombres reales como CONTADOR. Date cuenta de que hemos puesto el nombre en letras
maysculas. Esto lo hace destacar, y tambin significa (por convencin) que se trata de una constante.
Tercero, aade algn tipo de cabecera en tus programas utilizando los punto y coma. Un ejemplo sera algo as:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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 4 3 2 1 0
Valor Binario 0 0 1 1 0
Lo mismo se aplica para TRISB.
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.
W
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 0 0 1 1 0
Entrada/Salida S S E E S
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 ; Mueve 00110 a TRISA
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!
Cmo escribir en los puertos
En el apartado anterior, hemos mostrado como configurar los pines de un puerto del PIC como entradas o como salidas. En
este apartado, vamos a mostrar como enviar datos a los puertos. En el siguiente apartado terminaremos haciendo que un
LED parpadee incluyendo el listado completo del programa y un diagrama de un circuito simple para que puedas ver al PIC
haciendo exactamente lo que esperamos que haga. No intentes compilar o programar tu PIC con estos listados de aqu, ya
que son solo ejemplos.
Primero, pongamos el bit 2 del puerto A como salida:
bsf 03h,5 ;Ir al Banco 1
movlw 00h ;Poner 00000 en W
movwf 85h ;Mover 00000 al TRISA todos los pines como salidas.
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 ; Escribe 02h en el registro W. En binario es 00010,
; ...lo cual pone a 1 el bit 2 (pin 18) mientras mantiene los otros pines a 0.
movwf 05h ; Ahora mueve los contenidos de W (02h) al puerto A, cuya direccin es 05h.
Por tanto, ahora tu LED est encendido, y ahora queremos apagarlo:
movlw 00h ; Escribe 00h en el registro W. Esto pone a 0 todos los pines.
movwf 05h ; Ahora mueve todos los contenidos de W (0h) al puerto A, cuya direccin es 05h.
As que lo que hemos hecho ha sido encender y apagar el LED una vez.
Lo que queremos es que el LED se encienda y se apague continuamente. Para hacer esto tenemos que volver al principio del
programa. Para conseguir esto lo primero que hacemos es poner una etiqueta al comienzo de nuestro programa, y dicindole
al programa que vaya a ese punto constantemente.
Definimos una etiqueta muy simple. Escribimos un nombre, digamos INICIO, entonces el cdigo queda:
Inicio movlw 02h ; Escribe 02h en el registro W. En binario es 00010,
; ...lo cual pone a 1 el bit 2 (pin 18) mientras mantiene los otros pines a 0.
movwf 05h ; Ahora mueve los contenidos de W (02h) al puerto A, cuya direccin es 05h.
movlw 00h ; Escribe 00h en el registro W. Esto pone a 0 todos los pines.
movwf 05h ; Ahora mueve todos los contenidos de W (0h) al puerto A, cuya direccin es 05h.
goto Inicio ; ve donde est 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:
;
bsf 03h,5
movlw 00h
movwf 85h
bcf 03h,5
Inicio 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 ; Este asigna a la palabra STATUS el valor 03h, que es la direccin del registro STATUS.
TRISA equ 85h ; Este asigna a la palabra TRISA el valor 85h, que es la direccin del registro tri-estado del Puerto A.
PORTA equ 05h ; Este asigna a la palabra PORTA el valor 05h, que es la direccin del Puerto A.
As que ahora que hemos establecido nuestro 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. Reescribiremos de nuevo el programa sin comentarios, para que puedas comparar el listado anterior con este:
STATUS equ 03h
TRISA equ 85h
PORTA equ 05h
;
bsf STATUS,5
movlw 00h
movwf TRISA
bcf STATUS,5
Inicio movlw 02h
movwf PORTA
movlw 00h
movwf PORTA
goto 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 ; Primero, ponemos el valor 85h en el registro W.
movwf 08h ; Ahora lo movemos a nuestro registro 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
A continuacin necesitamos disminuir este CONTADOR en 1 hasta que alcance cero. Da la casualidad de que hay una sola
instruccin que hace esto por nosotros, con la ayuda de un 'goto' y una etiqueta. La instruccin que usaremos es:
decfsz 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 ; Direccin del registro STATUS
TRISA equ 85h ; Direccin del registro triestado para el Puerto A.
PORTA equ 05h ; Direccin del Puerto A.
CONTADOR1 equ 08h ; Primer contador para nuestros bucles de retardo.
CONTADOR2 equ 09h ; Segundo contador para nuestros bucles de retardo.
;
;****Configuracin del Puerto****
bsf STATUS,5 ; Cambiamos al banco 1Switch to Bank 1
movlw 00h ; Ponemos los pines del puerto A ...
movwf TRISA ; ...como salidas.
bcf STATUS,5 ; Volvemos al Banco 0.
;
;****Encendido del LED ****
Inicio movlw 02h ; Encendemos el LED poniendo primero el valor...
movwf PORTA ; ... en el registro w y despus al puerto
;
;****Inicio del buble de retardo 1****
Bucle1 decfsz CONTADOR1,1 ; Restamos 1 a 255.
goto Bucle1 ; Si CONTADOR es cero, continuamos.
decfsz CONTADOR2,1 ; Restamos 1 a 255
goto Bucle1 ; Volvemos al inicio de nuestro bucle
; Este retardo cuenta hacia atrs ...
; ...desde 255 a 0, 255 veces.
;
;****Retardo terminado, ahora apagamos el LED ****
movlw 00h ; Apaga el LED poniendo primero el valor ...
movwf PORTA ; ... en el registro w y despus al puerto
;
;****Aadimos otro retardo****
Bucle2 decfsz CONTADOR1,1 ; Este segundo bucle mantiene el LED...
goto Bucle2 ; ...apagado el tiempo suficiente...
decfsz CONTADOR2,1 ; ...para que lo veamos
goto Bucle2 ;
;
;****Ahora volvemos al inicio del programa
goto Inicio ; Vuelve al principio y enciende el LED...
; ...de nuevo.
;****Termina el Programa****
end ; Algunos compiladores necesitan 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:
Error al crear miniatura: No se ha podido guardar la miniatura
Felicidades, acabas de escribir tu primer programa para PIC, y construido un circuito para hacer parpadear un LED. As que,
si has seguido este tutorial hasta aqu, has aprendido 7 instrucciones de 35, y ya controlas los puertos de entrada/salida !
Por qu no intentas modificar los bucles de retardo para hacer que el LED parpadee mas rpido ? Cual es el valor mnimo
de CONTADOR para poder ver el LED parpadear ? Por qu no aades un tercer bucle o incluso ms bucles de retardo
despus del primero para hacer ms lento el apagado mas lento ? Necesitars una constante para cada bucle de retardo.
Podras incluso 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 ; Direccin del registro STATUS
TRISA equ 85h ; Direccin del registro tri-estado para el Puerto A.
PORTA equ 05h ; Direccin del Puerto A.
CONTADOR1 equ 08h ; Primer contador para nuestros bucles de retardo.
CONTADOR2 equ 09h ; Segundo contador para nuestros bucles de retardo.
;
;**** Configuracin del Puerto ****
bsf STATUS,5 ; Cambiamos al Banco 1
movlw 00h ; Ponemos los pines del puerto A ...
movwf TRISA ; ...como salidas.
bcf STATUS,5 ; Volvemos al Banco 0.
;
;**** Encendido del LED ****
Inicio movlw 02h ; Encendemos el LED poniendo primero el valor...
movwf PORTA ; ... en el registro w y despus al puerto
;
;**** Aadimos un retardo ****
call Retardo
;
;**** Retardo terminado, ahora apagamos el LED ****
movlw 00h ; Apaga el LED poniendo primero el valor ...
movwf PORTA ; ... en el registro w y despus al puerto
;
;**** Aadimos otro retardo ****
call Retardo
;
;**** Ahora volvemos al inicio del programa
goto Inicio ; Vuelve al principio y enciende el LED...
; ...de nuevo.
;
;**** Aqu est nuestra Subrutina
Retardo
Bucle1 decfsz CONTADOR1,1 ; Este segundo bucle mantiene el LED...
goto Bucle1 ; ...apagado el tiempo suficiente...
decfsz CONTADOR2,1 ; ...para que lo veamos
goto Bucle1 ;
return
;
;**** Fin del Programa****
end ; Algunos compiladores necesitan 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
(http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2551). ]
En el prximo capitulo, veremos cmo leer de los puertos.
Cmo leer de los puertos E/S
Hasta este punto, hemos estado escribiendo en el Puerto A para poder encender y apagar el LED. Ahora vamos a ver como
podemos leer los pines de E/S de los puertos. Esto es para que podamos conectar un circuito externo y actuar sobre cualquier
salida que este nos d.
Si recuerdas de los captulos previos, para configurar los puertos de E/S, tenemos que cambiarnos del Banco 0 al Banco 1.
Hagamos eso primero:
STATUS equ 03h ; Direccin del registro STATUS
TRISA equ 85h ; Direccin del registro tri-estado para el Puerto A.
PORTA equ 05h ; Direccin del Puerto A.
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 ; Para configurar el pin 0 del Puerto A...
movwf TRISA ; ... como entrada.
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 PortA,0
Goto Inicio
;Continua por aqu
:
:
El programa solo se mover hacia 'Continua por aqu' si el bit 0 del puerto A se pone a 1.
Vamos ahora a escribir un programa con el que el LED parpadear a una velocidad, pero si un conmutador[Nota de la
traduccin: en ingles el trmino original es "switch"] se cierra, parpadear a la mitad de velocidad. Seguramente puedas
hacer el programa por ti mismo, pero hemos incluido el listado de todos modos. Podras probar a escribir el programa
completo, solo para ver si has comprendido los conceptos. Estamos usando el mismo circuito que antes, con un conmutador
aadido al pin RA0 del PIC y a la linea de alimentacin positiva.
;***** Establecimiento constantes ****
STATUS equ 03h ; Direccin del registro STATUS
TRISA equ 85h ; Direccin del registro tri-estado para el Puerto A.
PORTA equ 05h ; Direccin del Puerto A.
CONTADOR1 equ 08h ; Primer contador para nuestros bucles de retardo.
CONTADOR2 equ 09h ; Segundo contador para nuestros bucles de retardo.
;
;**** Configuracin del Puerto ****
bsf STATUS,5 ; Cambiamos al Banco 1
movlw 01h ; Ponemos los pines del puerto A ...
movwf TRISA ; ...el bit 1 como salida, el bit 0 como entrada.
bcf STATUS,5 ; Volvemos al Banco 0.
;
;**** Encendemos del LED ****
Inicio movlw 02h ; Encendemos el LED poniendo primero el valor...
movwf PORTA ; ... en el registro w y despus al puerto
;
;**** Comprobamos si el conmutador est cerrado ****
btfsc PORTA,0 ; Tomamos el valor del bit 0 del puerto A y comprobamos si es 0.
; Si es 0, sltate la siguiente instruccin y continua normalmente.
call Retardo ; Si es 1, ejecuta esta instruccin aadiendo un retardo extra.
;
;**** Aade un retardo
call Retardo
;
;**** Retardo terminado, ahora apagamos el LED ****
movlw 00h ; Apaga el LED poniendo primero el valor ...
movwf PORTA ; ... en el registro w y despus al puerto
;
;**** Comprobamos si el conmutador est todava cerrado ****
btfsc PORTA,0 ; Tomamos el valor del bit 0 del puerto A y comprobamos si es 0.
; Si es 0, saltate la siguiente instruccin y continua normalmente.
call Retardo ; Si es 1, ejecuta esta instruccin aadiendo un retardo extra.
;
;**** Aadimos otro retardo ****
call Retardo
;
;**** Ahora volvemos al inicio del programa
goto Inicio ; Vuelve al principio y enciende el LED...
; ...de nuevo.
;
;**** Aqu est nuestra Subrutina
Retardo
Bucle1 decfsz CONTADOR1,1 ; Este segundo bucle mantiene el LED...
goto Bucle1 ; ...apagado el tiempo suficiente...
decfsz CONTADOR2,1 ; ...para que lo veamos
goto Bucle1 ;
return
;
;**** Fin del Programa****
end ; Algunos compiladores necesitan 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 0 0
0 1 1
1 0 1
1 1 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 0 0
1 0 1
1 1 0
1 0 1
Si mantenemos el valor de A igual a 1, y hacemos OR exclusiva entre l y el valor de la salida anterior, la salida resultante
conmuta. Para los que no lo vean de este modo en la tabla de verdad, aqu est utilizando binario:
Valor de salida actual => 0
OR-Ex con l y con 1 => 1 ; 1 es el nuevo valor de salida.
OR-EX con l y con 1 => 0 ; 0 es el nuevo valor de salida.
OR-Ex con l y con 1 => 1 ; 1 es el nuevo valor de salida.
.... as sucesivamente.
Espero que puedas entender que haciendo OR exclusiva de una salida con un 1, lo que estamos haciendo ahora es conmutar
de 0 a 1 a 0, etc...
As que ahora, para encender y apagar nuestro LED, necesitamos solo dos lineas:
MOVLW 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 ; Direccin del registro STATUS
TRISA equ 85h ; Direccin del registro tri-estado del puerto A.
PORTA equ 05h ; Direccion del puerto A
CONTADOR1 equ 08h ; Primer contador para nuestros bucles de retardo.
CONTADOR2 equ 09h ; Segundo contador para nuestros bucles de retardo.
;**** Configurar el puerto****
bsf STATUS,5 ; Cambia al Banco 1
movlw 00h ; Configura las pines del puerto A ...
movwf TRISA ; ...como salidas.
bcf STATUS,5 ; Vuelve al Banco 0
movlw 02h ; Configura nuestro registro w con 02h
;**** Enciende y apaga el LED ****
Inicio xorwf PORTA,1 ; Conmuta el LED
;**** Aade un retardo
call Retardo ; El retardo mnimo necesario
;**** Ahora vuelve al inicio del programa
goto Inicio ; Vuelve al inicio para conmutar el LED de nuevo.
;
;**** Aqu est nuestra subrutina
Retardo
Bucle1 decfsz CONTADOR1,1 ; Este bucle mantiene el LED apagado o encendido...
goto Bucle1 ; ... el tiempo suficiente ...
decfsz CONTADOR2,1 ; ... para que lo podamos ver.
goto Bucle1 ;
return
;**** Final del programa ****
end ; Algunos compiladores necesitan esta instruccin,
; y tambin por si acaso olvidamos poner...
; ... la instruccin 'goto'.
LED parpadeante con el conmutador externo:
;***** Establecerlas constantes ****
STATUS equ 03h ; Direccin del registro STATUS
TRISA equ 85h ; Direccin del registro tri-estado del puerto A.
PORTA equ 05h ; Direccion del puerto A
CONTADOR1 equ 08h ; Primer contador para nuestros bucles de retardo.
CONTADOR2 equ 09h ; Segundo contador para nuestros bucles de retardo.
;
;**** Configurar el puerto****
bsf STATUS,5 ; Cambia al Banco 1
movlw 00h ; Configura las pines del puerto A ...
movwf TRISA ; ...como salidas.
bcf STATUS,5 ; Vuelve al Banco 0
movlw 02h ; Configura nuestro registro w con 02h
;
;**** Enciende y apaga el LED ****
Inicio xorwf PORTA,1 ; Conmuta el LED
;
;**** Comprobamos si el conmutador est cerrado ****
btfsc PORTA,0 ; Tomamos el valor del bit 0 del puerto A y comprobamos si es 0.
; Si es 0, sltate la siguiente instruccin y continua normalmente.
call Retardo ; Si es 1, ejecuta esta instruccin aadiendo un retardo extra.
;
;**** Aade un retardo
call Retardo ; El retardo mnimo necesario
;
;**** Ahora vuelve al inicio del programa
goto Inicio ; Vuelve al inicio para conmutar el LED de nuevo.
;
;**** Aqu est nuestra subrutina
Retardo
Bucle1 decfsz CONTADOR1,1 ; Este bucle mantiene el LED apagado o encendido...
goto Bucle1 ; ... el tiempo suficiente ...
decfsz CONTADOR2,1 ; ... para que lo podamos ver.
goto Bucle1 ;
return
;
;**** Final del programa ****
end ; Algunos compiladores necesitan esta 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 Tamao (en bytes)
LED parpadeante Original 120
LED Parpadeante Aadiendo subrutina 103
LED Parpadeante Utilizando funcin XOR 91
LED con conmutador Original 132
LED con conmutador Utilizando funcin XOR 99
[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.]
As que no solo hemos aprendido nuevas instrucciones, tambin hemos reducido el tamao de nuestro cdigo!
Operadores Lgicos y Aritmticos
Operadores Lgicos
En el ltimo captulo presentamos la funcin OR exclusiva. La funcin ExOR (o XOR) es conocida como un operador
lgico. En este captulo vamos a explicar otros operadores lgicos que soporta el PIC. No habr ningn programa de
ejemplo, pero explicaremos como usar los operadores utilizando pequeas secciones de cdigo.
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 0 1
0 1 0
1 0 0
1 1 1
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
El segundo ejemplo comprobar ahora los contenidos del registro W:
ANDLW 1100
OR
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 0 0
0 1 1
1 0 1
1 1 1
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
Si queremos aadir 1 a un nmero en el PIC, podramos simplemente utilizar la funcin ADD, y usar el nmero 1. El
problema con esto es que primero tenemos que poner el nmero 1 en el registro w, y despus utilizar el comando "ADDLW
1" para incrementarlo. Si queremos aadir 1 a cualquier otro registro, la cosa se pone peor. Primero tenemos que poner el
nmero 1 en el registro W, y despus utilizar "ADDWF <registro>,1". As que por ejemplo, para aadir 1 a la posicin 0Ch,
tendramos que tener la siguiente seccin de cdigo:
movlw 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
[Nota de la traduccin: La palabra "decremento" no existe en castellano. Surge de traducir un poco a la ligera la palabra
inglesa "decrement". As que nos permitimos una pequea licencia, por comodidad y claridad.]
Ya hemos cubierto la funcin de decremento (o disminucin en 1) en el capitulo Bucles de Retardo, as que no me voy a
repetir aqu.
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.
Operaciones con Bits
Las operaciones con bits nos permite manipular un solo bit dentro de una palabra (word). Nos permiten, mover, activar o
desactivar bits individualmente en registros o nmeros que especifiquemos. Al final de este captulo te mostraremos un
programa que producir un conjunto de luces que se desplazan en un sentido, y despus en el sentido contrario. Vimos esto
cuando revisamos la funcin OR, donde hacamos OR exclusiva con una palabra.
Ya hemos visto un par de operaciones con un bits, cuando configurbamos los puertos del PIC, y voy a repetir aqu su uso.
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 7 6 5 4 3 2 1 0
Nuestro ejemplo 1 1 0 0 1 1 0 1
Despus de "BCF 0Ch,2" 1 1 1 0 0 0 0 1
]
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:
BSF <registro>,<bit> , y se utiliza exactamente de la misma forma que la BCF de ms arriba.
BTFSC
Hasta ahora hemos puesto a 1 o a 0 un bit en un registro. Pero qu pasa si queremos simplemente comprobar si un bit es 0
1 en un registro? Bien, podemos utilizar BTFCS. Esta dice "Comprueba un bit en el registro o posicin de memoria (F), y
salta si es 0". Esta instruccin comprobar el bit que le especifiquemos en el registro. Si el bit es 0, la instruccin le dice al
PIC que se salte la instruccin que viene a continuacin. Podramos utilizar esta instruccin si queremos comprobar un flag,
como por ejemplo en flag de CARRY. Esto nos ahorra tener que leer 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> ; continua por aqu si CARRY est a 1.
<instruccin y> ; o por aqu si est a 0.
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
Es similar a la instruccin CLRF, excepto en que solo pone a 0 el registro W. La sintaxis es bastante sencilla:
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
[Nota de la traduccin: La sintaxis para estas dos instrucciones es:
RLF <registro>,d
RRF <registro>,d
Donde 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.]
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 ; Variable para el bucle de retardo.
PORTB equ 06h ; Direccin del Port B.
TRISB equ 86h ; Direccin del registro tri-estado del Port B.
PORTA equ 05h ; Direccin del Port A.
TRISA equ 85h ; Direccin del registro tri-estado del Port A.
STATUS equ 03h ; Registro para seleccionar el banco.
CONTADOR1 equ 0Ch ; Registro para el bucle.
CONTADOR2 equ 0Dh ; Registro para el bucle.

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 ;
;
; Comienzo del programa
;
CorreLuz
movlw 01h ; Pon a 1 el primer bit
movwf PORTB ; del Puerto B. PORTB = 00000001
call Retardo ; Espera un momento.
call Retardo ;
;
; Desplaza el bit a la izquierda, y despus pausa.
;
rlf PORTB,1 ; PORTB = 00000010, C = 0
;
call Retardo
call Retardo
;
rlf PORTB,1 ; PORTB = 00000100, C = 0
;
call Retardo
call Retardo
;
rlf PORTB,1 ; PORTB = 00001000, C = 0
;
call Retardo
call Retardo
;
rlf PORTB,1 ; PORTB = 00010000, C = 0
;
call Retardo
call Retardo
;
rlf PORTB,1 ; PORTB = 00100000, C = 0
;
call Retardo
call Retardo
;
rlf PORTB,1 ; PORTB = 01000000, C = 0
;
call Retardo
call Retardo
;
rlf PORTB,1 ; PORTB = 10000000, C = 0
;
call Retardo
call Retardo
;
rlf PORTB,1 ; Este desplaza el bit al ''flag'' CARRY.
; PORTB = 00000000, C = 1
;
; Ahora se mover al puerto A, desplazando el bit hacia la izquierda.
;
rlf PORTA,1 ; Esto mueve el bit desde el ''flag'' CARRY al puerto A.
; PORTA = 00001, C = 0
;
call Retardo
call Retardo
;
rlf PORTA,1 ; PORTA = 00010, C = 0
;
call Retardo
call Retardo
;
rlf PORTA,1 ; PORTA = 00100, C = 0
;
call Retardo
call Retardo
;
rlf PORTA,1 ; PORTA = 01000, C = 0
;
call Retardo
call Retardo
;
rlf PORTA,1 ; PORTA = 10000, C = 0;
;
call Retardo
call Retardo
;
; Desplaza el bit de vuelta por el puerto A
;
rrf PORTA,1 ; PORTA = 01000, C = 0;
call Retardo
call Retardo
rrf PORTA,1 ; PORTA = 00100, C = 0;
call Retardo
call Retardo
rrf PORTA,1 ; PORTA = 00010, C = 0;
call Retardo
call Retardo
rrf PORTA,1 ; PORTA = 00001, C = 0;
call Retardo
call Retardo
rrf PORTA,1 ; Esta desplaza el bit al ''flag'' CARRY
; PORTA = 00000, C = 1;
;
; Ahora desplaza el bit de vuela al Puerto B
;
rrf PORTB,1 ; PORTB = 10000000, C = 0
call Retardo
call Retardo
rrf PORTB,1 ; PORTB = 01000000, C = 0
call Retardo
call Retardo
rrf PORTB,1 ; PORTB = 00100000, C = 0
call Retardo
call Retardo
rrf PORTB,1 ; PORTB = 00010000, C = 0
call Retardo
call Retardo
rrf PORTB,1 ; PORTB = 00001000, C = 0
call Retardo
call Retardo
rrf PORTB,1 ; PORTB = 00000100, C = 0
call Retardo
call Retardo
rrf PORTB,1 ; PORTB = 00000010, C = 0
call Retardo
call Retardo
rrf PORTB,1 ; PORTB = 00000001, C = 0
call Retardo
call Retardo ; Ahora hemos vuelto a donde empezamos,
;
goto CorreLuz ; Vamos a hacerlo de nuevo.
;
; La subrutina para hacer el retardo entre los movimientos de bits.
;
Retardo
movlw TIEMPO ; Cogemos el tiempo de retardo,
movwf CONTADOR1 ; y lo ponemos en una variable.
Bucle1 ;
decfsz CONTADOR1 ; Decrementa el tiempo de retardo hasta
goto Bucle1 ; que alcance cero.
movwf CONTADOR1 ; Cogemos el tiempo de retardo de nuevo,
Bucle2 ; y repetimos la cuenta atrs.
decfsz 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
Error al crear miniatura: No se
ha podido guardar la miniatura
2 b'00111011' = 3Bh
Error al crear miniatura: No se
ha podido guardar la miniatura
3 b'01101011' = 6Bh
Error al crear miniatura: No se
ha podido guardar la miniatura
4 b'01001101' = 4Dh
Error al crear miniatura: No se
ha podido guardar la miniatura
5 b'01101110' = 6Eh
Error al crear miniatura: No se
ha podido guardar la miniatura
6 b'01111110' = 7Eh
Error al crear miniatura: No se
ha podido guardar la miniatura
7 b'01000011' = 43h
Error al crear miniatura: No se
ha podido guardar la miniatura
8 b'01111111' = 7Fh
Error al crear miniatura: No se
ha podido guardar la miniatura
9 b'01101111' = 6Fh
Error al crear miniatura: No se
ha podido guardar la miniatura
0 b'01110111' = 77h
Error al crear miniatura: No se
ha podido guardar la miniatura
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.
Imagina que tenemos un programa BASIC como el que se muestra AQU:
10 LET K=0
11 K=K+1
12 IF K>10 THEN GOTO 20 ELSE GOTO 11
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
0002 Bucle decfsc 0Ch
0003 goto Bucle
0004 end
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
La primera instruccin esta asignando a la etiqueta PC la direccin del Contador de Programa (02h). Despus colocamos el
valor 03h en el registro w. A continuacin hacemos una llamada a "tabla". La primera linea en la subrutina "tabla" aade los
contenidos del registro W (03h) al Contador de Programa. Esto causa que el contador de programa se incremente en 3, o
dicho de otro modo, causa que el contador de programa se mueva 3 lineas hacia abajo. Cuando el contador llega 3 lineas
ms abajo el PIC ve la instruccin retlw. Este comando pasa el valor que viene con la instruccin, al registro W, y retorna
desde la subrutina.
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 Nmero para el display
1 b'01000001' = 41h 1
2 b'00111011' = 3Bh 2
3 b'01101011' = 6Bh 3
4 b'01001101' = 4Dh 4
5 b'01101110' = 6Eh 5
6 b'01111110' = 7Eh 6
7 b'01000011' = 43h 7
8 b'01111111' = 7Fh 8
9 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 ; Direccin del Contador de Programa(PC)
TIEMPO equ 9Fh ; Variable para el bucle de retardo.
PORTB equ 06h ; Direccin del Port B.
TRISB equ 86h ; Direccin del registro tri-estado del Port B.
STATUS equ 03h ; Registro para seleccionar el banco.
CONTADOR1 equ 0Ch ; Registro para el bucle.
CONTADOR2 equ 0Dh ; Registro para el bucle.
;
; Configuramos el Puerto B
;
bsf STATUS,5 ; Va al Banco 1
movlw 00h ; y configura los pines del Puerto B
movwf TRISB ; todos como salidas.
bcf STATUS,5 ; Vuelta al Banco 0.
movlw 00h ; Pon a 0 el Puerto B. Es decir,
movwf PORTB ; el display totalmente apagado.
;
; Comienzo del programa
;
Inicio
movlw 0Ah ; Carga en W el valor 10 antes de llamar a la tabla
call tabla ; para recuperar la configuracin de encendido del 0.
movwf PORTB ; Ahora W = 77h, lo pone en puerto B, para representar 0 en el display.
;
call Retardo ; Espera un momento.
call Retardo ; Para que se pueda ver el 0
;
movlw 01h ; Carga en W el valor 1 antes de llamar a la tabla
call tabla ; para recuperar la configuracin de encendido del 1.
movwf PORTB ; De W lo pone en puerto B, para representar 1 en el display.
;
call Retardo ;
call Retardo ;
;
movlw 02h ; Carga en W el valor 2 antes de llamar a la tabla
call tabla ; para recuperar la configuracin de encendido del 2.
movwf PORTB ; De W lo pone en puerto B, para representar 2 en el display.
;
call Retardo ;
call Retardo ;
;
movlw 03h ; Carga en W el valor 3 antes de llamar a la tabla
call tabla ; para recuperar la configuracin de encendido del 3.
movwf PORTB ; De W lo pone en puerto B, para representar 3 en el display.
;
call Retardo ;
call Retardo ;
;
movlw 04h ; Carga en W el valor 4 antes de llamar a la tabla
call tabla ; para recuperar la configuracin de encendido del 4.
movwf PORTB ; De W lo pone en puerto B, para representar 4 en el display.
;
call Retardo ;
call Retardo ;
;
movlw 05h ; Carga en W el valor 5 antes de llamar a la tabla
call tabla ; para recuperar la configuracin de encendido del 5.
movwf PORTB ; De W lo pone en puerto B, para representar 5 en el display.
;
call Retardo ;
call Retardo ;
;
movlw 06h ; Carga en W el valor 6 antes de llamar a la tabla
call tabla ; para recuperar la configuracin de encendido del 6.
movwf PORTB ; De W lo pone en puerto B, para representar 6 en el display.
;
call Retardo ;
call Retardo ;
;
movlw 07h ; Carga en W el valor 7 antes de llamar a la tabla
call tabla ; para recuperar la configuracin de encendido del 7.
movwf PORTB ; De W lo pone en puerto B, para representar 7 en el display.
;
call Retardo ;
call Retardo ;
;
movlw 08h ; Carga en W el valor 8 antes de llamar a la tabla
call tabla ; para recuperar la configuracin de encendido del 8.
movwf PORTB ; De W lo pone en puerto B, para representar 8 en el display.
;
call Retardo ;
call Retardo ;
;
movlw 09h ; Carga en W el valor 9 antes de llamar a la tabla
call tabla ; para recuperar la configuracin de encendido del 9.
movwf PORTB ; De W lo pone en puerto B, para representar 9 en el display.
;
call Retardo ;
call Retardo ;
;
goto Inicio ; Vamos a hacerlo de nuevo.
;
; La subrutina para hacer el retardo entre los nmeros.
;
Retardo
movlw TIEMPO ; Cogemos el tiempo de retardo,
movwf CONTADOR1 ; y lo ponemos en una variable.
Bucle1 ;
decfsz CONTADOR1 ; Decrementa el tiempo de retardo hasta
goto Bucle1 ; que alcance cero.
movwf CONTADOR2 ; Cogemos el tiempo de retardo de nuevo,
Bucle2 ; y repetimos la cuenta atrs.
decfsz 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 ; Carga 41h (representacin de 1) en W y retorna.
retlw 3Bh ; Carga 3Bh (representacin de 2) en W y retorna.
retlw 6Bh ; Carga 6Bh (representacin de 3) en W y retorna.
retlw 4Dh ; Carga 4Dh (representacin de 4) en W y retorna.
retlw 6Eh ; Carga 6Eh (representacin de 5) en W y retorna.
retlw 7Eh ; Carga 7Eh (representacin de 6) en W y retorna.
retlw 43h ; Carga 43h (representacin de 7) en W y retorna.
retlw 7Fh ; Carga 7Fh (representacin de 8) en W y retorna.
retlw 6Fh ; Carga 6Fh (representacin de 9) en W y retorna.
retlw 77h ; Carga 77h (representacin de 0) en W y retorna.
return ;
;
end ; Fin del programa.
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.
Interrupciones : una introduccin
El tema de las interrupciones va a ser probablemente el ms difcil y que ms tiempo nos llevar revisar. No hay una manera
sencilla de explicar las interrupciones, pero espero que para el final de esta seccin seas capaz de implementar las
interrupciones en tus propios programas. Hemos dividido el tema en dos captulos. Esto es para ayudar a reducir la materia,
y darte a ti, lector, una pausa.
As que, qu es una interrupcin? Bien, como su nombre sugiere, una interrupcin es un proceso o una seal que hace
detenerse al microprocesador/microcontrolador de lo est haciendo, para que haga otra cosa. Permiteme darte un ejemplo de
la vida cotidiana. Supn que ests sentado en tu casa, charlando con alguien. De repente el telfono suena. T detienes la
charla, levantas el telfono y hablas con la persona que te llam. Cuando terminas tu conversacin telefnica, vuelves para
charlar con la persona que estabas antes de que sonase el telfono. Puedes hacerte la idea de que la rutina principal es que t
ests charlando con alguien, que el telfono suene causa una interrupcin a tu charla, y que la rutina de interrupcin es el
proceso de hablar por telfono. Cuando la conversacin telefnica ha terminado, entonces vuelves a la rutina principal de la
charla. Este ejemplo es exactamente igual a como una interrupcin hace que acte un procesador. El programa principal est
en curso, ejecutando alguna funcin en un circuito, pero cuando ocurre una interrupcin el programa principal se detiene
para que otra rutina se lleve a cabo. Cuando la rutina termina, el procesador vuelve a la rutina principal de nuevo.
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:
1 - Poner a 1 el bit 7 (GIE) del registro INTCON (OBh).
2 - En el mismo registro INTCON:
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.
3 - En el registro OPTION (81h):
3.1 - Poner en el bit 6 (INTEDG) un 1 si queremos que se active la interrupcin en el flanco de subida(de 0V a
+5V).
3.2 - Poner en el bit 6 (INTEDG) un 0 si queremos que se active la interrupcin en el flanco de bajada(de +5V
a 0V).]
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 flag no 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 ; El PIC comienza aqu si se enciende o hay un reset.
GOTO Inicio ; Ve al programa principal.
ORG 0004h ; El PIC vendr aqu si ocurre una interrupcin.
: ; Esta es nuestra rutina de interrupcin
: ; con lo que queremos que haga el PIC
: ; cuando reciba una interrupcin.
RETFIE ; Fin de la rutina de interrupcin.
Inicio ; Este es el comienzo de nuestro programa 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.
Interrupciones : Cmo escribir el cdigo
En el pasado captulo cubrimos mas o menos lo bsico, as que creemos que es el momento de escribir nuestro primer
programa. El programa que vamos a escribir contar el nmero de veces que un switch es activado, y despus mostraremos
el nmero. El programa contar de 0 a 9, mostrando en 4 LEDs en formato binario, y la entrada o interrupcin ser por RB0.
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 ; Aqu es donde apunta el PC si energiza el PIC o en caso de reset
goto Principal ; Ir a nuestro programa principal
org 0x04 ; Aqu es donde comienza nuestra rutina de interrupcin.
retfie ; Esto le dice al PIC que la rutina de interrupcin
; ha terminado y el PC volver a apuntar al programa principal
Principal ; Este es el comienzo de nuestro 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 ; GIE Global interrupt enable (1=habilitado)
bsf INTCON,4 ; INTE - RB0 interrupt enable (1=habilitado)
Vamos a poner a 0 el flag de interrupcin por si acaso (no nos fiamos de nada!!):
;
bcf INTCON,1 ; INTF - A 0 por si acaso.
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 ; Establece RB0 como entrada
movlw 0x10 ;
movwf TRISA ; Pone los 4 primeros pines del puerto A como salida
bcf STATUS,5 ; Vuelve al banco 0.
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 CONTADOR,0 ; Movemos los contenidos de CONTADOR a W.
movwf PORTA ; Ahora lo movemos al puerto A.
goto Bucle ; Continuamos haciendo esto.
end ; Fin de nuestro programa.
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.
As que vamos con nuestra rutina de interrupcin.
Ahora la primera cosa que tenemos que hacer es almacenar de manera temporal los contenidos nuestro registro W, ya que lo
estamos utilizando para transferir los contenidos de CONTADOR al PORTA. Si no lo almacenamos, entonces podramos
enviar un nmero completamente diferente como resultado de nuestras operaciones. As que hagamos eso primero:
;
movwf TEMPORAL ; Almacenamos w en una posicin temporal.
Lo siguientes que queremos es aadir 1 a nuestra variable CONTADOR:
;
incf CONTADOR,1 ; Incrementamos CONTADOR en 1 y ponemos
; el resultado de vuelta en CONTADOR.
Lo siguientes es hacer un chequeo de CONTADOR para ver si hemos pasado el valor de 9. La forma en que lo podemos
hacer es restandole 10.
;
movlw 0x0A ; Ponemos 10 en W.
subwf CONTADOR,0 ; Restamos W a CONTADOR y ponemos
; el resultado en W.
En el captulo Operadores Aritmticos y Lgicos vimos que si restamos un nmero mayor a un nmero menor, el flag de
Acarreo (CARRY) se pondr a 1. Este flag tambin se pondr a 1 si lo nmeros son iguales y los restamos.
;
btfss STATUS,0 ; Comprueba el flag CARRY. Se activar 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 ; Si CONTADOR es <10, entonces continua
goto limpiar ; Si CONTADOR es >9, tenemos que ponerlo a 0
continua
bcf INTCON,0x01 ; Tenemos que poner a 0 este ''flag'' para
; permitir ms interrupciones.
movfw TEMPORAL ; Restaura W al valor que tena antes de la interrupcin.
retfie ; Salir de la rutina de interrupcin.
limpiar
clrf CONTADOR ; Pon CONTADOR otra vez a 0.
bcf INTCON,1 ; Tenemos que poner a 0 este ''flag'' para
; permitir ms interrupciones.
retfie ; Salir de la rutina de interrupcin.
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 ; Aqu es donde apunta el PC si energiza el PIC o en caso de reset
;
;*****************DEFINICIN DE CONSTANTES********************************
INTCON EQU 0x0B ; Registro de Control de Interrupciones
PORTB EQU 0x06 ; Direccin del registro PORTB
PORTA EQU 0x05 ; Direccin del registro PORTA
TRISA EQU 0x85 ; Direccin del registro TRISA
TRISB EQU 0x86 ; Direccin del registro TRISB
STATUS EQU 0X03 ; Direccin del registro STATUS
CONTADOR EQU 0x0c ; Esta ser nuestra variable contador
TEMPORAL EQU 0x0d ; Almacn temporal para el registro W
goto Principal ; Ir a nuestro programa principal saltando se la direccin de interrupcin.
;
;***************RUTINA DE INTERRUPCION*********************************
org 0x04 ; Aqu es donde comienza nuestra rutina de interrupcin.
movwf TEMPORAL ; Almacenamos w en una posicin temporal.
incf CONTADOR,1 ; Incrementamos CONTADOR en 1 y ponemos
; el resultado de vuelta en CONTADOR.
movlw 0x0A ; Ponemos 10 en W.
subwf CONTADOR,0 ; Restamos W a CONTADOR y ponemos
; el resultado en W.
btfss STATUS,0 ; Comprueba el flag CARRY. Se activar si
; CONTADOR es igual o mayor que w,
; y se activar como resultado de la instruccin subwf
goto continua ; Si CONTADOR es <10, entonces continua
goto limpiar ; Si CONTADOR es >9, tenemos que ponerlo a 0
continua
bcf INTCON,0x01 ; Tenemos que poner a 0 este ''flag'' para
; permitir ms interrupciones.
movfw TEMPORAL ; Restaura W al valor que tena antes de la interrupcin.
retfie ; Salir de la rutina de interrupcin.
limpiar
clrf CONTADOR ; Pon CONTADOR otra vez a 0.
bcf INTCON,1 ; Tenemos que poner a 0 este ''flag'' para
; permitir ms interrupciones.
retfie ; Salir de la rutina de interrupcin.
;
;***************PROGRAMA PRINCIPAL***********************************
Principal ; Este es el comienzo de nuestro programa principal.
;****************Configura los Registros de Interrupcin*************
bsf INTCON,7 ; GIE Global interrupt enable (1=habilitado)
bsf INTCON,4 ; INTE - RB0 interrupt enable (1=habilitado)
bcf INTCON,1 ; INTF - A 0 por si acaso.
;
;****************Configura los puertos*******************************
bsf STATUS,5 ; Cambia al banco 1.
movw 0x01 ;
movwf TRISB ; Establece RB0 como entrada
movlw 0x10 ;
movwf TRISA ; Pone los 4 primeros pines del puerto A como salida
bcf STATUS,5 ; Vuelve al banco 0.
;
;****************Ahora enva el valor de CONTADOR al PORTA***********
Bucle
movf CONTADOR,0 ; Movemos los contenidos de CONTADOR a W.
movwf PORTA ; Ahora lo movemos al puerto A.
goto Bucle ; Continuamos haciendo esto.
end ; Fin de nuestro programa.
Diagrama del circuito
Mas abajo est el diagrama del circuito funcionar con el cdigo anterior. Hay dos cosas en el diagrama que puede que te
sorprendan. Primero, no hemos incluido el condensador de temporizacin en el circuito. Este es un ingenioso truco que
puedes intentar si te quedas sin condensadores. La capacitancia viene dada por la capacitancia de separacin entre el pin del
oscilador y pin de masa. Por tanto, con una resistencia y la capacitancia de separacin, tenemos un oscilador RC. Bien, no es
una manera precisa de hacerlo, ya que la capacitancia de separacin variar de un circuito a otro. Pero, pensbamos que
podras encontrar interesante esta idea. Segundo, hemos incluido un circuito anti-rebote para el switch. Este har que el PIC
"crea" que ha existido ms de una pulsacin. Con el circuito anti-rebote, cuando el switch se pone a nivel alto, el
condensador se carga. No importa cuantas veces el switch vaya a nivel alto (+5V), el condensador solo se cargar una vez.
El condensador es descargado cuando el switch se pone en la otra posicin. Si quieres ver los efectos de rebote de un switch,
quita el condensador y la resistencia del switch.
Error al crear miniatura: No se ha podido guardar la miniatura
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.
Un circuito watchdog no es nada nuevo. Muchos microprocesadores y microcontroladores lo tienen. Pero, cmo funciona?
Bien, dentro del PIC hay una red resistencia-condensador. Esta proporciona un reloj nico, que es independiente de
cualquier reloj externo que proporciones al circuito. Ahora cuando el watchdog timer (abreviado como WDT) est
habilitado, un contador comienza en 00 y se incrementa en 1 hasta que alcanza FF. Cuando pasa de FF a 00 (lo cual es
FF+1) el PIC ser reiniciado, independientemente de lo que est haciendo. La nica manera de evitar que el WDT reinicie el
PIC es reiniciar el propio WDT ponindolo de vuelta a 00 durante el programa. Ahora puedes ver que si tu programa se
atasca por cualquier razn, entonces el WDT no ser puesto a 00 nunca. Eso har que el WDT llegue a FF y reinicie tu PIC,
causando a nuestro programa que se reinicie desde el comienzo.
Para utilizar el WDT, tenemos que saber tres cosas:
La primera, cuanto tiempo tenemos antes de tener que hacer un reinicio al WDT.
Segundo, como lo ponemos a cero.
Finalmente, tenemos que decirle al software programador del PIC que habilite el WDT dentro del PIC.
Vamos a ver esto de manera separada:
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 0 0 1:1 18ms
0 0 1 1:2 36ms
0 1 0 1:4 72ms
0 1 1 1:8 144ms
1 0 0 1:16 288ms
1 0 1 1:32 576ms
1 1 0 1:64 1.1 Segundos
1 1 1 1:128 2.3 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 ; Esto es 0x05 en hexadecimal.
movwf 81h ; Este es el registro OPTION.
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]:
Primero, tenemos que reiniciar el otro contador (TMR0) y ponerlo a 0.
Despus tenemos que cambiar al banco 1 para asignar el prescaler al WDT y configurar el tiempo.
Y despus volver al banco 0.
El cdigo esta aqu, donde xxx es el valor del prescaler:
bcf STATUS,0 ; Nos aseguramos de que estamos en el banco 0
clrf 01h ; Direccin del otro temporizador TMR0. Lo ponemos a 0.
bsf STATUS,0 ; Cambiamos al banco 1switch to bank 1
clrwdt ; reiniciamos el WDT y el ''prescaler''
movlw b1xxx ; Seleccionamos el valor del nuevo ''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!
Temporizacin de las instrucciones
Como probablemente ya sabrs, el PIC toma el reloj externo y lo divide por 4. Este tiempo interno es llamado ciclo de
instruccin. Como hemos dicho que conectamos un cristal de 4 Mhz a nuestro PIC, internamente el PIC ir a 1Mhz. En
trminos de temporizacin, esto es 1/(4Mhz/4) = 1 S. Entonces algunas instrucciones lleva ejecutarlas un solo ciclo de
instruccin, p. ej. 1 S utilizando un cristal de 4Mhz, mientras que otras emplearn 2 S en ser ejecutadas.
La hoja de caractersticas nos dice cuantos ciclos lleva cada instruccin. La forma mas sencilla para recordarlo es muy
simple. Asume que todas las instrucciones tardan 1 ciclo. Pero si una instruccin causa que el programa vaya a algn otro
sitio, este se tomar 2 ciclos. Permite que te darte un par de ejemplos.
La instruccin "movwf" tarda solo 1 ciclo, porque solo mueve un dato de un lugar a otro. La 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:
;
movlw 02
movwf CONTADOR
Bucle 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:
1/(4MHz/4) = 1 S por ciclo , por tanto 7 ciclos son 7 x 1 S = 7 S.
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
(http://www.picallw.com/)], 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.
El programa que hemos elegido es el utilizado en el captulo Operadores Aritmticos y Lgicos donde hacamos que una fila
de LEDs se encendiesen a un tiempo de izquierda a derecha y de derecha a izquierda. El circuito se muestra ms abajo, y
con los valores RC que mostramos le daremos una frecuencia de reloj de 8KHz. Esta velocidad de reloj nos permitir
realmente ver los LEDs movindose uno a uno. Elegimos este programa porque es suficientemente lento para que podamos
jugar con el WDT, y que puedas ver fcilmente como se reinicia el PIC. Hemos quitado los comentarios originales, y los
hemos sustituido por una descripcin de las lineas del WDT, y en cada linea del tiempo total desde el inicio (asumiendo
8KHz), y el nmero total de ciclos de reloj de cada linea.
Error al crear miniatura: No se ha podido guardar la miniatura
TIEMPO equ 9FH ; Variable para el bucle de retardo.
PORTB equ 06H ; Direccin del Port B.
TRISB equ 86H ; Direccin del registro tri-estado del Port B.
PORTA equ 05H ; Direccin del Port A.
TRISA equ 85H ; Direccin del registro tri-estado del Port A.
STATUS equ 03H ; Registro para seleccionar el banco.
CONTADOR1 equ 0CH ; Registro para el bucle.
CONTADOR2 equ 0DH ; Registro para el bucle.
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
; Comienzo del programa principal
CorreLuz
movlw 01H ; 1 ciclo, 4.5mS
movwf PORTB ; 1 ciclo, 5.0mS
call RETARDO ; 2 ciclos, 486mS
call RETARDO ; 2 ciclos, 967mS
; Mueve el bit por la izquierada del puerto B, despus pausa.
rlf PORTB,1 ; 1 ciclo, 967.5mS
call RETARDO ; 2 ciclos, 1.45S
call RETARDO ; 2 ciclos, 1.93S
rlf PORTB,1 ; 1 ciclo, 1.93S
call RETARDO ; 2 ciclos, 2.41S
call RETARDO ; 2 ciclos, 2.89S
rlf PORTB,1 ; 1 ciclo, 2.89S
call RETARDO ; 2 ciclos, 3.37S
call RETARDO ; 2 ciclos, 3.85S
rlf PORTB,1 ; 1 ciclo, 3.85S
call RETARDO ; 2 ciclos, 4.34S
call RETARDO ; 2 ciclos, 4.82S
rlf PORTB,1 ; 1 ciclo, 4.82S
call RETARDO ; 2 ciclos, 5.30S
call RETARDO ; 2 ciclos, 5.78S
rlf PORTB,1 ; 1 ciclo, 5.78S
call RETARDO ; 2 ciclos, 6.26S
call RETARDO ; 2 ciclos, 6.74S
rlf PORTB,1 ; 1 ciclo, 6.74S
call RETARDO ; 2 ciclos, 7.22S
call RETARDO ; 2 ciclos, 7.70S
rlf PORTB,1 ; 1 ciclo, 7.70S
; Ahora mueve lo al puerto A, al bit de la izquierda.
rlf 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, 9.63S
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
; Mueve el bit de vuelta al puerto A.
rrf 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
; Ahora mueve el bit de vuelta al puerto B.
rrf 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
BUCLE1 ;
decfsz CONTADOR1 ; 9F x 1 ciclo + 1 ciclo = 160 ciclos
goto BUCLE1 ; 9E x 2 ciclos = 316 ciclos
movwf CONTADOR2 ; 1 ciclo
BUCLE2 ;
decfsz CONTADOR2 ; 9F x 1 ciclo + 1 ciclo = 256 ciclos
goto BUCLE2 ; 9E x 2 ciclos = 316 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 ; Variable para el bucle de retardo.
PORTB equ 06H ; Direccin del Port B.
TRISB equ 86H ; Direccin del registro tri-estado del Port B.
PORTA equ 05H ; Direccin del Port A.
TRISA equ 85H ; Direccin del registro tri-estado del Port A.
STATUS equ 03H ; Registro para seleccionar el banco.
CONTADOR1 equ 0CH ; Registro para el bucle.
CONTADOR2 equ 0DH ; Registro para el bucle.
OPT equ 81h ; Registro Option para controlar el WDT

;************* Configura los puertos, el WDT y el preescaler******************
clrf 01h ; Pone a cero el TMR0
bsf STATUS,5 ; Cambia al banco 1
clrwdt ; reinicia el WDT y el prescaler
movlw b1101 ; Selecciona un nuevo valor para el prescaler y
movwf OPT ; se lo asigna al WDT
;
movlw 00H ; Ahora configura los puertos
movwf TRISB ;
movlw 00H ;
movwf TRISA ;
bcf STATUS,5 ; Vuelve al banco 0
movlw 00H ;
movwf PORTA ;
;************* Comienzo del programa principal *****************************
CorreLuz
movlw 01H ;
movwf PORTB ;
call RETARDO ;
call RETARDO ;
; Mueve el bit por la izquierada del puerto B, despus pausa.
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 ;
call RETARDO ;
call RETARDO ;
rlf PORTB,1 ;
; Ahora mueve lo al puerto A, al bit de la izquierda.
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 ;
; Mueve el bit de vuelta al puerto A.
rrf PORTA,1 ;
call RETARDO ;
call RETARDO ;
rrf PORTA,1 ;
call RETARDO ;
call RETARDO ;
rrf PORTA,1 ;
call RETARDO ;
call RETARDO ;
rrf PORTA,1 ;
; Ahora mueve el bit de vuelta 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 ;
;
goto CorreLuz ;
; Subrutina para introducir un retardo entre los movimientos de los bits.
RETARDO
movlw TIEMPO ; 1 ciclo
movwf CONTADOR1 ; 1 ciclo
BUCLE1 ;
decfsz CONTADOR1 ; 9F x 1 ciclo + 1 ciclo = 160 ciclos
goto BUCLE1 ; 9E x 2 ciclos = 316 ciclos
movwf CONTADOR2 ; 1 ciclo
BUCLE2 ;
decfsz CONTADOR2 ; 9F x 1 ciclo + 1 ciclo = 256 ciclos
goto BUCLE2 ; 9E x 2 ciclos = 316 ciclos
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Esta parte reinicia el WDT ;;
;; Quita o comenta este comando para ver que hace 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.
Referencias
MICROCHIP (http://www.microchip.com)
Microchip: El PIC 16F84 (Datasheet, Informacin adicional, etc...)
(http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en010229)
Microchip: El PIC 16F84A (Datasheet, Informacin adicional, etc...)
(http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en010230)
Microchip: Pginas de documentacin sobre MPASM (http://www.microchip.com/stellent/idcplg?
IdcService=SS_GET_PAGE&nodeId=2123&param=en022517).
MPLAB IDE (Entorno Integrado de Desarrollo) (http://www.microchip.com/stellent/idcplg?
IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en019469&part=SW007002).
MPASMWIN: Descarga de versin Windows (http://www.picbook.com/downloads.html)
Ms Informacin: Lenguaje Ensamblador (en castellano). (http://perso.wanadoo.es/pictob/ensamblador.htm)
Obtenido de http://wiki.webdearde.com/index.php?title=Tutorial:_Programacin_de_PICs_en_Ensamblador&oldid=3694
Categora: Tutorial
Esta pgina fue modificada por ltima vez el 6 feb 2009, a las 19:32.
Esta pgina ha sido visitada 154 607 veces.

You might also like