You are on page 1of 45

Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.

Tutorial: Programación de PICs en Ensamblador


De Asociación de Robotica y Domótica de España (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 introducción
mas 13 tutoriales, que aquí se han asimilado todos como capítulos. La traducción intenta ser lo más fiel
posible al texto original, pero hay expresiones coloquiales que utiliza el autor que han sido adaptadas a
versiones más comunes del castellano. Otras expresiones o notas añadidas al texto original están indicadas
convenientemente y marcadas como " notas de la traducción ". El nivel del tutorial puede considerarse básico,
pero cubre bastante bien la mayoría de los tópicos de la programación en ensamblador para PICs Microchip.

Contenido
1 Introducción al PIC
1.1 Introducción
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 técnicas para programar
4 Los Registros
4.1 Un ejemplo de código
5 Cómo escribir en los puertos
6 Bucles de Retardo
7 Subrutinas
8 Cómo leer de los puertos E/S
9 Operadores Lógicos y Aritméticos
9.1 Operadores Lógicos
9.2 Operadores Aritméticos
10 Operaciones con Bits
11 Tablas de Datos
12 Interrupciones : una introducción
12.1 El Flag de Interrupción
12.2 La Posición de Memoria
13 Interrupciones : Cómo escribir el código
14 El Watchdog Timer
14.1 Tiempos de WDT
14.2 Temporización de las instrucciones
14.3 Software Programador
14.4 Programa de ejemplo
15 Referencias

Introducción al PIC
Introducción
Bienvenidos al inicio del Tutorial sobre PICs. Estas páginas te llevaran desde la estructura básica del dispositivo,
hasta los métodos y técnicas de programación. También habrá sugerencias de como modificar el código para

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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 básicos con poca memoria, hasta los que
tienen convertidores Analógico 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á fácil.

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 más económica
de programar los PICs, ya que no necesitas ningún otro software extra que no sea de los gratuitos.

Los pines del 16F84

Mas abajo verás el diagrama de patillas(pines en adelante) del PIC 16F84. Pasaré por cada pin, explicando para
que se utiliza cada uno.

Pines PIC16F84.jpg

RA0 a RA4

RA es un puerto bidireccional. Eso quiere decir que puede ser configurado como entrada o como salida. El
número que hay después 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 alimentación. VDD es la alimentación positiva, y VSS es el negativo de la alimentación, o
0 Voltios. La tensión máxima de alimentación que puedes utilizar son 6 Voltios, y el mínimo son 2 Voltios.

OSC1/CLK IN y OSC2/CLKOUT

Estos pines son donde conectaremos el reloj externo, para que el microcontrolador disponga de algún tipo de

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

temporización.

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 alimentación 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 función 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, querrás conocer como programar el PIC, pero además
de aprender las instrucciones de código de ensamble, ¿como programas realmente ese código 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 método "hazlo tu mismo", te recomendaría este sitio (http://www.ic-prog.com/index1.htm) .


Pulsa sobre "Supported Programmers" para ver los circuitos. El más económico es el "TAIT Classic
Programmer". El software para programar el PIC también lo puedes bajar de esa página, ves a "Download".

Si quieres ir por la vía fácil, 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 método funcionará, ya que ambos darán 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 programación de un PIC.

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

Tutorial Assembler Proceso de programacion de PICs.jpg

También recomiendo utilizar una placa de inserción para hacer tus circuitos, mientras juegas con el PIC. Hay
varios tamaños disponibles.

A continuación 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 teoría para saber como
programar el PIC, pero el verdadero aprendizaje viene cuando intentas probar tu código 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 básica y económica. Por supuesto, le puedes añadir 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 encenderán cuando los pines se pongan a nivel alto. También, puedes añadir switches a los pines, para
poder seleccionar que pines poner a nivel alto, y cuales a nivel bajo. Básicamente, lo que estoy diciendo es que
si comienzas con este circuito, puedes añadir lo que creas necesario.

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

PlacaEntrenamiento basica.jpg

La linea de alimentación está puesta a 6 Voltios, que es el máximo voltaje para el PIC. Puedes utilizar cualquier
voltaje inferior, hasta un mínimo 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 alimentación. X1 es un cristal de 4 MHz. Puedes utilizar un circuito
RC (resistencia y condensador) ( Nota de edición: crear enlace aquí), pero el precio del cristal es insignificante,
y es mas estable. C1 y C2 ayudan a reducir cualquier desviación en la oscilación cristal, y a eliminar cualquier
ruido no deseado antes de que la señal llegue al PIC.

Buenas técnicas para programar


Antes de meternos en harina con la programación del PIC, creo que ahora es un buen momento para explicar
algunas técnicas para programar bien.

Si escribes un ; (punto y coma) en cualquier punto de tu programa, el compilador ignorará cualquier cosa que
haya detrás de él, hasta llegar al retorno de carro. Esto significa que podemos añadir comentarios a nuestro
programa que nos recuerden que estábamos haciendo en ese punto. Esta es una buena práctica incluso para
los programas más 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 límites.

Segundo, puedes asignar nombres a las constantes vía los registros (hablaremos de estos más adelante). Hace
lo que estás escribiendo mucho más sencillo de leer, para saber de que valor se trata, mas que intentar
entender que significan todos esos números. Así que utiliza nombres reales como CONTADOR. Date cuenta de
que hemos puesto el nombre en letras mayúsculas. Esto lo hace destacar, y también significa (por convención)
que se trata de una constante.

Tercero, añade algún tipo de cabecera en tus programas utilizando los punto y coma. Un ejemplo sería algo
así:

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Autor:
;
; Fecha:
;
; Versión:
;
; Titulo:
;
;
;
; Descripción:
;
;
;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Date cuenta de que hemos hecho una especie de caja utilizando puntos y comas. Esto es simplemente para
hacerlo más pulcro.

Finalmente, prueba y documenta el programa sobre papel también. 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, leído o ambas cosas. Piensa en un registro como
si fuese un trozo de papel donde tu puedes ver la información o escribirla.

La figura de más 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 están los diferentes bits y piezas dentro del PIC, y nos
ayudará a explicar unos cuantos comandos.

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

PIC16F84 MapadeRegistros.jpg

La primera cosa que notarás 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. Después volvemos al Banco 0 y enviamos un 1 lógico 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 serán entradas y los que serán salidas del Puerto
A, TRISB nos permite establecer los pines que serán entradas y los que serán salidas del puerto B.

Vamos a ver con más 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 dirección 03h (la 'h' significa que el número es hexadecimal).

TRISA y TRISB

Están 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 cuestión en el registro. Ahora, podemos hacer esto

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

ya sea en binario o en hexadecimal. Personalmente uso ambos, ya que el binario ayuda mucho a visualizar el
puerto. Si no estás familiarizado con el paso de binario a hexadecimal y viceversa, utiliza una calculadora
científica.

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 cuestión. Si deseamos poner uno de los pines como salida, ponemos un 0 en ese bit.
Los bits están 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 código, tenemos que explicar dos registros mas - W y F.

El registro W es un registro de propósito 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 código

Vamos a darte un ejemplo de código sobre lo que acabamos de aprender. No intentes compilar esto todavía, lo
haremos cuando hagamos nuestro primer programa. Simplemente estamos intentado mostrar como se hace la
programación 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 dirección 03h, poniendo el bit 5 a 1.

BSF 03h,5

La instrucción BSF significa en ingles "Bit Set F" (Poner a 1 un bit de la Memoria). La letra F significa que
vamos a utilizar una posición de memoria, o un registro en memoria. Vamos a utilizar dos números después de
esta instrucción - 03h, el cual se refiere a la dirección del registro STATUS, y el número 5 que corresponde al
número de bit. Por tanto, lo que estamos diciendo es "pon a 1 el bit 5 de la dirección 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 número está en binario) en nuestro

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

registro de propósito general W. Podríamos haber hecho esto en hexadecimal, en cuyo caso nuestra instrucción
hubiese sido:

MOVLW 06h

Cualquiera de las dos funcionará. La instrucción 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 instrucción significa "poner los contenidos de W en el registro cuya dirección viene a continuación", en
este caso la dirección 85h, que apunta a TRISA.

Nuestro registro TRISA ahora tiene el valor 00110 o mostrado gráficamente :

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 instrucción hace lo contrario a BSF. Significa en ingles "Bit Clear F" (en castellano, poner a 0 un bit de la
memoria). Los dos números que le siguen son la dirección del registro, en este caso del registro STATUS, y el
número de bit, es este caso el 5. Así que lo que hemos hecho ahora es poner a 0 el bit 5 del registro STATUS.

Ya estamos de vuelta en el Banco 0.

Aquí está el código 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

Léelo hasta que las entiendas. De momento ya hemos visto 4 instrucciones. ¡Solo nos quedan 31 para
terminar!

Cómo 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

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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 dirección 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 dirección 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 diciéndole al programa que vaya a ese punto constantemente.

Definimos una etiqueta muy simple. Escribimos un nombre, digamos INICIO, entonces el código 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 dirección 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 dirección es 05h.
goto Inicio ; ve donde esté Inicio.

Como puedes ver, primer decimos la palabra 'Inicio' justo al comienzo del programa. Después, justo al final del
programa decimos simplemente 'goto Inicio', ves a Inicio. La instrucción '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 alimentación al
circuito, y se detendrá cuando le quitemos la alimentación.

Creo que deberíamos 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
números ? Esto puede ser algo confuso si mas tarde intentas depurar el programa, y también cuando escribes
código 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 números. Esto se consigue con otra
instrucción: "equ"

La instrucción "equ" simplemente significa que algo equivale a algo [ Nota de la traducción: "equ" viene del
termino ingles "equivalence", en castellano "equivalencia" ]. No es una instrucción del PIC, sino para el
ensamblador. Con esta instrucción podemos asignar un nombre a la dirección de localización de un registro, o
en términos de programación asignar una constante. Vamos a establecer algunas constantes para nuestro
programa, y verás que sencillo es de leer.

STATUS equ 03h ; Este asigna a la palabra STATUS el valor 03h, que es la dirección del registro
STATUS.

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

TRISA equ 85h ; Este asigna a la palabra TRISA el valor 85h, que es la dirección del registro tri-
estado del Puerto A.
PORTA equ 05h ; Este asigna a la palabra PORTA el valor 05h, que es la dirección 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 más sencillo, aunque todavía no
hemos puesto los comentarios. Sin embargo, no hemos terminado todavía.

Bucles de Retardo
Existe un ligero inconveniente en nuestro programa del LED parpadeante. Cada instrucción necesita un ciclo de
reloj para ser completada. Si utilizamos un cristal de 4 Mhz, cada instrucción 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 rápido 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 atrás desde un número previamente establecido y cuando llegue
a cero, paramos de contar. El valor cero indica el fin del retardo y continuamos nuestro camino a través del
programa.

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

Si lo probamos y asignamos el valor FFh, el compilador entenderá que estamos asignando la dirección de
memoria FFh a la constante, y obtendremos un error cuando vayamos a compilar el programa. Esto es debido
a que la localización FFh está reservada, y por tanto no podemos acceder a ella. Así que, ¿ como hacemos para
asignar un número real ? Bien, se requiere hacer un poco de "pensamiento lateral". Si asignamos a nuestro
CONTADOR, por ejemplo, a la dirección 08h, este apuntará a un registro de propósito general. Las posiciones
de memoria tienen un valor por defecto de FFh. De este modo, si CONTADOR apunta a 08h, tendrá un valor de
FFh la primera vez que lo pongamos en marcha.

Pero, sí, no llores, ¿ cómo ponemos un valor distinto en CONTADOR ? Bien, todo lo que tenemos que hacer es
primero 'mover' un valor a esta posición. Por ejemplo, si queremos que CONTADOR tenga un valor de 85h, no
podemos decir 'CONTADOR equ 85h' porque esta es la localización del registro tri-estado del puerto A (TRISA).
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 ?

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

Así que lo primero definimos nuestra constante:

CONTADOR equ 08h

A continuación necesitamos disminuir este CONTADOR en 1 hasta que alcance cero. Da la casualidad de que
hay una sola instrucción que hace esto por nosotros, con la ayuda de un 'goto' y una etiqueta. La instrucción
que usaremos es:

decfsz CONTADOR,1

Esta instrucción dice "resta 1 al registro (en esta caso CONTADOR). Si llegamos a cero, salta 2 lugares hacia
delante"[Nota de la traducción: El valor que le sigue a la coma, indica donde debe almacenarse el resultado de
la operación. Si es 1, como en el ejemplo anterior, el resultado se almacena en el mismo registro indicado en
la instrucción, y si es 0 el resultado se almacena en el registro w. ] . Un montón de palabras para una sola
instrucción. Veamosla en acción antes, después 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 instrucción decfsz. La instrucción decfsz CONTADOR,1
disminuye el valor de CONTADOR en 1, y almacena el resultado de vuelta en CONTADOR. También comprueba
si CONTADOR tiene un valor de 0. Si no lo tiene, hace que el programa salte a la siguiente linea. Aquí tenemos
una instrucción de 'goto' que nos envía de vuelta a nuestra instrucción decfsz. Si el valor de CONTADOR es
igual a cero, entonces la instrucción decfsz hace que el programa salte dos lugares hacia adelante, y se sitúe
donde hemos escrito "Continua por aquí". Así que, 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 detrás 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
añadiendo los comentarios:

;*****Establecimiento constantes ****


STATUS equ 03h ; Dirección del registro STATUS
TRISA equ 85h ; Dirección del registro triestado para el Puerto A.
PORTA equ 05h ; Dirección del Puerto A.
CONTADOR1 equ 08h ; Primer contador para nuestros bucles de retardo.
CONTADOR2 equ 09h ; Segundo contador para nuestros bucles de retardo.
;
;****Configuración 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 después 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 atrás ...
; ...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 después al puerto
;
;****Añadimos 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...

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

; ...de nuevo.
;****Termina el Programa****
end ; Algunos compiladores necesitan esta instrucción.
; y también por si acaso olvidamos poner...
; ... la instrucción 'goto'.

Puedes compilar este programa y programar el PIC con él. Por su puesto, querrás 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:

PICTutorial practica1.jpg

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 rápido ? Cual es el
valor mínimo de CONTADOR para poder ver el LED parpadear ? ¿ Por qué no añades un tercer bucle o incluso
más bucles de retardo después del primero para hacer más lento el apagado mas lento ? Necesitarás una
constante para cada bucle de retardo. Podrías 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 sección veremos como podemos usar una cosa llamada sub-rutina para ayudar a mantener el
programa simple y pequeño.

Subrutinas
Una subrutina es una sección de código o programa, que puede ser llamada como y cuando la necesites. Las
subrutinas se usan si vas a ejecutar la misma función función más de una vez, por ejemplo para crear un
retardo. Las ventajas de utilizar una subrutina son que hará más sencillo modificar el valor una vez dentro de la
subrutina antes que, digamos, hacerlo diez veces a través de tu programa. Y también 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

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

Primero tenemos que dar un nombre a la subrutina, y en este caso hemos elegido RUTINA. Después escribimos
el código que queramos como hacemos normalmente. En este caso, hemos elegido el código del retardo para
el programa de parpadeo de nuestro LED. Finalmente, terminamos la subrutina tecleando la instrucción
RETURN.

Para arrancar la subrutina desde cualquier punto de nuestro programa, simplemente escribimos la instrucción
CALL seguida por el nombre de la subrutina.

Vamos a ver esto con algo más 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 instrucción RETURN, el
programa salta de vuelta a nuestro programa principal, justo a la instrucción que va inmediatamente después
de nuestra instrucción CALL xxx.

Puedes llamar a la misma subrutina tantas veces como quieras, esa es la razón por la que utilizar subrutinas
reduce el tamaño 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. Recomendaríamos
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
instrucción '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 verás que sencillo se queda el programa, y también veras como funciona la
subrutina en la realidad:

;***** Establecimiento constantes ****


STATUS equ 03h ; Dirección del registro STATUS
TRISA equ 85h ; Dirección del registro tri-estado para el Puerto A.
PORTA equ 05h ; Dirección del Puerto A.
CONTADOR1 equ 08h ; Primer contador para nuestros bucles de retardo.
CONTADOR2 equ 09h ; Segundo contador para nuestros bucles de retardo.
;
;**** Configuración 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 después al puerto
;
;**** Añadimos 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 después al puerto
;
;**** Añadimos 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 instrucción,
; y también por si acaso olvidamos poner...
; ... la instrucción 'goto'.

Puedes ver que utilizando una subrutina para nuestro bucle de retardo, hemos reducido el tamaño 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 instrucción 'call'. En el ejemplo anterior, encendemos el LED. Después llamamos a la
subrutina. Entonces el programa retorna para que podamos apagar el LED. Llamamos a la subrutina de nuevo,

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

y cuando la surutina termina, el programa retorna a la siguiente instrucción que ve, que es 'goto Inicio'.

Para aquellos de vosotros que estéis interesados, nuestro programa original tenia 120 bytes de tamaño.
Mediante el uso de la subrutina, hemos reducido el programa a 103 bytes.[Nota de la traducción: Estos
tamaños en bytes a los que se refiere el autor, son los tamaños 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 pequeño bit ayuda.
[ Nota de la traducción: Cuando el autor dice que el 16F84 tiene 1024 bytes, se esta refiriendo al área de
memoria para el almacenamiento del código 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 más de un byte). De modo, que lo que sería correcto decir es, que el área de memoria de programa
en el 16F84 tiene un tamaño 1024 x 14 bit words, o 1K x 14 bit words, o 1024 posiciones de 14 bits cada una.
Como dato adicional, cada instrucción 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 páginas de Microchip
(http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2551) . ]

En el próximo capitulo, veremos cómo leer de los puertos.

Cómo 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 capítulos previos, para configurar los puertos de E/S, tenemos que cambiarnos del Banco 0
al Banco 1. Hagamos eso primero:

STATUS equ 03h ; Dirección del registro STATUS


TRISA equ 85h ; Dirección del registro tri-estado para el Puerto A.
PORTA equ 05h ; Dirección 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 instrucción BTFSC significa "Haz una comprobación de bit en el registro y bit que especificamos. Si es un 0,
entonces sáltate la siguiente instrucción".

La instrucción BTFSS significa "Haz una comprobación de bit en el registro y bit que especificamos. Si es un 1,
entonces sáltate la siguiente instrucción".

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 podríamos utilizar la instrucción
BTFSS de este modo:

;Aquí el código
:
BTFSS PortA,0
Goto Inicio
;Continua por aquí
:
:

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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 traducción: en ingles el término 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. Podrías probar a escribir el programa completo, solo para ver si has comprendido los conceptos.
Estamos usando el mismo circuito que antes, con un conmutador añadido al pin RA0 del PIC y a la linea de
alimentación positiva.

;***** Establecimiento constantes ****


STATUS equ 03h ; Dirección del registro STATUS
TRISA equ 85h ; Dirección del registro tri-estado para el Puerto A.
PORTA equ 05h ; Dirección del Puerto A.
CONTADOR1 equ 08h ; Primer contador para nuestros bucles de retardo.
CONTADOR2 equ 09h ; Segundo contador para nuestros bucles de retardo.
;
;**** Configuración 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 después 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, sáltate la siguiente instrucción y continua normalmente.
call Retardo ; Si es 1, ejecuta esta instrucción añadiendo un retardo extra.
;
;**** Añade 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 después al puerto
;
;**** Comprobamos si el conmutador está todavía cerrado ****
btfsc PORTA,0 ; Tomamos el valor del bit 0 del puerto A y comprobamos si es 0.
; Si es 0, saltate la siguiente instrucción y continua normalmente.
call Retardo ; Si es 1, ejecuta esta instrucción añadiendo un retardo extra.
;
;**** Añadimos 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 instrucción,
; y también por si acaso olvidamos poner...
; ... la instrucción 'goto'.

Lo que hemos hecho aquí es encender el LED. Después 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 código
puede resultar irrelevante para alguien que no le interese programar microcontroladores. Por tanto, no te
enfades si cuando enseñes 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 mínimo interés ¡ Hablamos desde nuestra experiencia
personal !

Si has ido siguiendo estos capítulos 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. Después fuimos capaces de interactuar con
nuestro PIC añadiendo 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 introducíamos comandos por
primera vez, pero debe existir una manera mejor de hacerlo. Bueno, la hay (sabias que la había, ¿verdad? ).

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

Vamos a examinar como estábamos haciendo el parpadeo del LED realmente.

movlw 02h
movwf PORTA
movlw 00h
movlw PORTA

Primero cargamos nuestro registro w con 02h, después lo pusimos en nuestro registro del puerto A para
encender el LED. Para apagarlo, cargamos w con 00h y después lo pusimos en nuestro registro del puerto A.
Entremedias de estas rutinas teníamos que llamar a una subrutina para que pudiéramos ver el LED parpadear.
Así que hemos tenido que mover dos conjuntos de datos dos veces (una vez al registro w y después al PORTA)
y llamar a la subrutina dos veces (una vez para el encendido y otra para el apagado).

Así que, ¿ cómo podemos hacer esto de una manera mas eficiente ? Sencillo. Utilizamos otra instrucción
llamada XORWF.

La instrucción XORWF ejecuta una función 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 traducción:
También llamada función 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:

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

MOVLW 02h
XORWF PORTA,1

Lo que estamos haciendo aquí es cargar nuestro registro w con 02h. Después le hacemos una OR exclusiva a
este número 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 traducción: El número que va después del registro
especificado en la instrucción XORWF, indica donde debe de ser almacenado el resultado de dicha operación
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 pusiésemos 0, el resultado de la operación OR exclusiva se almacenaría
en el registro w]

Vamos a ejecutar este código 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 atrás a nuestro comando de conmutación. Además, 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, también lo conmutaremos.

Así que, vamos a ver nuestro nuevo código. El primero es nuestro código original de parpadeo del LED, y el
segundo es donde hemos añadido el conmutador externo:

LED Parpadeante:

;***** Establecerlas constantes ****


STATUS equ 03h ; Dirección del registro STATUS
TRISA equ 85h ; Dirección 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
;**** Añade un retardo
call Retardo ; El retardo mínimo 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 instrucción,
; y también por si acaso olvidamos poner...
; ... la instrucción 'goto'.

LED parpadeante con el conmutador externo:

;***** Establecerlas constantes ****


STATUS equ 03h ; Dirección del registro STATUS
TRISA equ 85h ; Dirección 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 ...

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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, sáltate la siguiente instrucción y continua normalmente.
call Retardo ; Si es 1, ejecuta esta instrucción añadiendo un retardo extra.
;
;**** Añade un retardo
call Retardo ; El retardo mínimo 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 instrucción,
; y también por si acaso olvidamos poner...
; ... la instrucción 'goto'.

Esperamos que hayas podido comprender cómo con el uso de una simple instrucción, hemos reducido el
tamaño 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 tamaños en la siguiente tabla:

Programa Cambio Tamaño (en bytes)


LED parpadeante Original 120
LED Parpadeante Añadiendo subrutina 103
LED Parpadeante Utilizando función XOR 91
LED con conmutador Original 132
LED con conmutador Utilizando función XOR 99

[ Nota de la traducción: Estos tamaños en bytes a los que se refiere el autor, son los tamaños de los ficheros
.HEX que resultan de compilar estos listados con el compilador de Microhip MPASM.]

Así que no solo hemos aprendido nuevas instrucciones, ¡también hemos reducido el tamaño de nuestro código!

Operadores Lógicos y Aritméticos


Operadores Lógicos

En el último capítulo presentamos la función OR exclusiva. La función ExOR (o XOR) es conocida como un
operador lógico. En este capítulo vamos a explicar otros operadores lógicos que soporta el PIC. No habrá
ningún programa de ejemplo, pero explicaremos como usar los operadores utilizando pequeñas secciones de
código.

AND

La función 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) también, y la función AND hace esta comparación de ambas bit a bit. El
ejemplo de más abajo muestra dos palabras de 8 bits a las que se aplica AND con el siguiente resultado:

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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
función AND para hacer comprobaciones de puertos, por ejemplo. Si estamos monitorizando pines E/S que
están conectados a un circuito, y tenemos que monitorizar una condición en concreto donde solo algunos pines
están a nivel alto, entonces simplemente podemos leer el puerto, y aplicar a ese valor la función AND con la
condición que estamos buscando, simplemente como en el ejemplo anterior.

[ Nota de la Traducción: Incluimos aquí la tabla de verdad de esta función 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 función AND con los contenidos del registro W, y un número que nosotros
especifiquemos. Las sintaxis es:

ANDLW <número>  ; donde <número> es con el que haremos AND a los contenidos de W. El resultado de la
función AND serán almacenamos de vuelta en el registro W. [ Nota de la traducción: El valor de <número>
tiene que estar comprendido entre 0 y 255 ]]

ANDWF nos permite hacer una función 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 código de más abajo muestran un ejemplo de cada instrucción 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 función OR, llamada XOR. Esta produce un 1 si dos bits son diferentes, pero no si son
iguales. Hay una segunda función OR llamada IOR, la cual es OR inclusiva. La función produce un 1 si

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

cualquiera de los dos bits es 1, pero también 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 Aritméticos

ADD

Esta función exactamente lo que dice [ Nota de la traducción: "Add" en ingles significa sumar ] ¡Suma dos
números! Si el resultado de sumar dos números excede de 8 bits [ Nota de la traducción: 8 bits pueden
contener un valor máximo de 255 en decimal], entonces se activará un flag llamado CARRY [ Nota de la
traducción: "flag" en castellano se podría traducir por banderín, 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",
podría ser como cuando hacemos una cuenta a mano y utilizamos la expresión "me llevo una". ]. El flag de
CARRY está localizado en el bit 0 de la dirección de memoria 03h. Si este bit se activa, quiere decir que la
suma de esos 2 números 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 función anterior.

ADDLW añade los contenidos del registro W con un número que le especifiquemos. La sintaxis es:

ADDLW <número>

ADDWF añadirá 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 función! Sí, lo que suponías, esta función sustrae(o resta) un bit a
otro [' Nota de la traducción: SUB, viene del ingles "substract" que significa "sustraer" en castellano ]]. Una vez
más el PIC da dos modalidades: SUBLW y SUBWF. La sintaxis es exactamente la misma que para la función
ADD, excepto por supuesto ¡que tienes que escribir SUB en lugar de ADD!

Incremento

Si queremos añadir 1 a un número en el PIC, podríamos simplemente utilizar la función ADD, y usar el número
1. El problema con esto es que primero tenemos que poner el número 1 en el registro w, y después utilizar el

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

comando "ADDLW 1" para incrementarlo. Si queremos añadir 1 a cualquier otro registro, la cosa se pone peor.
Primero tenemos que poner el número 1 en el registro W, y después utilizar "ADDWF <registro>,1". Así que
por ejemplo, para añadir 1 a la posición 0Ch, tendríamos que tener la siguiente sección de código:

movlw 01
addwf 0Ch,1

Hay una manera mejor de hacer esto. Podemos utilizar la instrucción INCF. La sintaxis es:

INCF <registro>,d  ; donde <registro> es un registro, o posición 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 instrucción podemos literalmente hacer la mitad de código. Si queremos que el
resultado vaya de vuelta al registro W, usando el ejemplo anterior, hubiésemos tenido que añadir otro comando
para mover el contenido de la posición 0Ch de vuelta al registro W, y después 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 después del incremento (esto ocurrirá cuando añadamos 1 a
255) entonces el PIC se saltará la siguiente instrucción. La sección de código de más abajo lo demuestra:

Bucle incfsz 0Ch


goto Bucle
:
:
;Resto del programa.

En la sección de código anterior, el registro de la posición de memoria OCh será incrementado en 1. Después
tenemos una instrucción 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 instrucción le dirá al PIC que se salte la siguiente
instrucción, la cual es en este caso "goto", y por tanto que el PIC continúe con el resto del programa.

Decremento

[ Nota de la traducción: La palabra "decremento" no existe en castellano. Surge de traducir un poco a la ligera
la palabra inglesa "decrement". Así que nos permitimos una pequeña licencia, por comodidad y claridad.]

Ya hemos cubierto la función de decremento (o disminución en 1) en el capitulo Bucles de Retardo, así que no
me voy a repetir aquí.

Complemento

La última instrucción 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 instrucción en acción:

0Ch = 11001100

COMF 0Ch,1

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

0Ch = 00110011

Esto se puede usar, por ejemplo, para cambiar rápidamente 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 números que especifiquemos. Al final de este capítulo te
mostraremos un programa que producirá un conjunto de luces que se desplazan en un sentido, y después en el
sentido contrario. Vimos esto cuando revisamos la función OR, donde hacíamos OR exclusiva con una palabra.

Ya hemos visto un par de operaciones con un bits, cuando configurábamos los puertos del PIC, y voy a repetir
aquí su uso.

BCF

Esta instrucción 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 poníamos a 0 el bit 5 del registro
STATUS. También podemos utilizarla para poner a 0 cualquier bit de cualquier otro registro/posición de
memoria. Por ejemplo, si queremos poner a 0 el tercer bit de 11001101 que está almacenado en la posición de
memoria 0Ch, introduciríamos:

BCF 0Ch,2

[ Nota de la Traducción: el valor que puede tomar el parámetro <bit> va de 0 a 7, donde 0 es el bit mas a la
derecha de nuestro número binario. La siguiente tabla clarifica un poco más la disposición de los bits en un
byte (o palabra de 8 bits) y sus posiciones:

Posición 8º bit 7º bit 6º bit 5º bit 4º bit 3er bit 2º bit 1er bit
Número de bit 7 6 5 4 3 2 1 0
Nuestro ejemplo 1 1 0 0 1 1 0 1
Después de "BCF 0Ch,2" 1 1 1 0 0 0 0 1
]

BSF

Esta instrucción pondrá a 1 el bit que especifiquemos en cualquier registro que especifiquemos. Hemos utilizado
esta operación 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 más arriba.

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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
posición de memoria (F), y salta si es 0". Esta instrucción comprobará el bit que le especifiquemos en el
registro. Si el bit es 0, la instrucción le dice al PIC que se salte la instrucción que viene a continuación.
Podríamos utilizar esta instrucción 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 están a 1. Por
ejemplo, si queremos comprobar si el flag de CARRY ha sido puesto a 1 cuando hayamos sumado dos
números, haremos lo siguiente:

;
BTFSC 03h,0
<instrucción x> ; continua por aquí si CARRY está a 1.
<instrucción y> ; o por aquí si está a 0.

Si el valor del bit es 1, entonces BTFSC continuará por la instrucción inmediatamente siguiente. Si está a 0,
entonces se salta esa instrucción. La siguiente sección de código muestra donde podría ser utilizada:

Bucle :
:
:
BTFSC 03,0
Goto Bucle

En el código 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 instrucción dice "Comprueba el bit del registro o posición de memoria(F), y salta si está a 1". Esta es
similar a la instrucción BTFSC, excepto que el PIC se saltará la siguiente instrucción si el bit que estamos
comprobando está a 1, en lugar de a 0.

CLRF

Esta instrucción 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".
También lo usamos para poner los pines de un puerto que estaban como salidas todos a 0, haciendo "CLRF
05h".

CLRW

Es similar a la instrucción CLRF, excepto en que solo pone a 0 el registro W. La sintaxis es bastante sencilla:

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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 instrucción RLF, tendríamos
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 más, el 1 reaparecerá justo al principio [ Nota de la traducción: es decir,
por el lado derecho del byte, o dicho de otro modo en el bit0 ]. Lo mismo ocurre con la instrucción RRF pero de
manera inversa. El ejemplo de más abajo ilustra esto para la instrucción 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 traducción: 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 código 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 después haciendo todo el camino inverso hasta al principio. Conecta LEDs a todos los pines de los
puertos A y B. Aquí comprenderás algunas de las operaciones de bit mencionadas en este capítulo:

TIEMPO equ 9Fh ; Variable para el bucle de retardo.


PORTB equ 06h ; Dirección del Port B.
TRISB equ 86h ; Dirección del registro tri-estado del Port B.
PORTA equ 05h ; Dirección del Port A.
TRISA equ 85h ; Dirección 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.

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

 
bsf STATUS,5 ; Va al Banco 1
movlw 00h ; y configura
movwf TRISB ; ambos puertos A y B
movlw 00h ; como salidas,
movwf TRISA ; después 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 después 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

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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 atrás.
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 leído(solo leído)
dependiendo de algún criterio.
[ Nota de la traducción: El ejemplo dado en el texto original, se ha sustituido por este que mostramos a
continuación para hacer más comprensible el concepto de tabla de datos. Al final del capítulo se incluye un
programa para realizar el ejemplo:

Por ejemplo, podrías hacer un circuito con el PIC, que cuente de 0 a 9 y represente este número 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 sería utilizado). Para ello, podemos
utilizar el siguiente esquema de conexiones entre el PIC y el display:
Display7 segmentos con PICPortB.jpg

Es decir, el pin RB7 no lo conectaríamos al display. De este modo tendremos la siguiente tabla de
correspondencias entre los números a representar y el valor binario que debe de tomar el puerto B para que se
enciendan los diodos LED correspondientes:

Número Valor para puerto B Imagen en el display

1 b'01000001' = 41h

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

2 b'00111011' = 3Bh

3 b'01101011' = 6Bh

4 b'01001101' = 4Dh

5 b'01101110' = 6Eh

6 b'01111110' = 7Eh

7 b'01000011' = 43h

8 b'01111111' = 7Fh

9 b'01101111' = 6Fh

0 b'01110111' = 77h

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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 número adecuado en cada momento.]

Ahora, antes de continuar con la explicación 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í serás 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. Después de que le hemos
añadido 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 números de linea para ayudar al programador a hacer
seguimiento de donde está cada cosa, ya que las etiquetas no están permitidas.

El PIC utiliza etiquetas para saltar de unas posiciones a otras, ¿si?. Utilizamos las etiquetas para que sepamos
donde están las cosas, y también 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 línea interno llamado Contador de Programa
[ Nota de la traducción: en ingles "Program Counter", abreviado también como PC. ]. El Contador de Programa
mantiene almacenada la dirección de la posición de memoria donde se encuentra la instrucción actual. Cuando
le decimos al PIC que vaya a una etiqueta en particular, sabe la posición de memoria y por tanto modifica el
PC hasta que alcanza esa posición y la lee. Esto es exactamente el mismo modo en el que leemos nosotros el
programa BASIC anterior.

Aquí hay una sección de código, con las posiciones de memoria, o contenidos del PC, junto a cada instrucción:

PC Instrucción
-----------------------------
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 posición tenemos la instrucción "movlw 03h".
Cuando el PIC ha ejecutado esta instrucción, incrementará PC para que la siguiente instrucción pueda ser leída.
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 instrucción, "goto Bucle", le dice al PC
que vaya de vuelta a la posición 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 instrucción. Esto colocará el
PC en la posición 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.

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

La mejor manera de explicar como funciona una tabla de datos es comenzar con un pequeño 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 instrucción esta asignando a la etiqueta PC la dirección del Contador de Programa (02h). Después
colocamos el valor 03h en el registro w. A continuación hacemos una llamada a "tabla". La primera linea en la
subrutina "tabla" añade 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 más abajo el PIC ve la instrucción retlw. Este
comando pasa el valor que viene con la instrucción, 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
después de la palabra "return" en la frase anterior. Como si estuviésemos en una subrutina normal,
necesitamos una instrucción de "return" para salir de ella. Eso lo hace el RET en la instrucción. Después de la
instrucción RETLW hay un número, y este es el que colocaremos en el registro W. En este caso el número 0Ch.
[ Nota de la traducción: En resumidas cuentas, el valor que ponemos en el registro W antes de llamar a la
subrutina "tabla", indica qué posición queremos mirar de la tabla. Es decir, actúa de índice o indicador de la
posición 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 posición 3 de la tabla. La primera instrucción 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 instrucción
"retlw 0Ch". Entonces, este valor, 0Ch, será copiado en el registro W, y se efectuará un "RETurn", que hace
que el programa continúe por la instrucción siguiente a "call tabla". Ahora, estamos de vuelta al flujo principal
del programa con W cargado con el dato que queríamos 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 posición concreta. En el ejemplo del display de 7 segmentos se ve de modo práctico para qué se
podría utilizar una tabla de datos. ]

(Antes de llamar a la subrutina) podemos asignar cualquier número a W, siempre y cuando este número al ser
añadido a PC haga que PC se desplace aun elemento de la tabla de la subrutina, allí donde hemos puesto una
instrucción retlw. En el ejemplo anterior esto significa que podemos poner un número de 1 a 7. Si nos pasamos
de largo de la subrutina, podríamos terminar ejecutando otra parte del programa. Por esta razón, 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 traducción: 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 capítulo:

Posción Valor Número 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

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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 posición, para que el resto de los números que hay que representar en el
display, coincidan con la posición que ocupan en la tabla.
De este modo, la tabla se podría crear utilizando el siguiente código:

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 código que hemos empleado anteriormente para crear este nuevo. Así que habrá
partes que te serán familiares. Este es el código completo para hacer que el PIC muestre en el display una
cuenta ascendente de 0 a 9 y vuelta a empezar:

PC equ 02h ; Dirección del Contador de Programa(PC)


TIEMPO equ 9Fh ; Variable para el bucle de retardo.
PORTB equ 06h ; Dirección del Port B.
TRISB equ 86h ; Dirección 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 configuración 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 configuración 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 configuración 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 configuración 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 configuración 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 configuración de encendido del 5.
movwf PORTB ; De W lo pone en puerto B, para representar 5 en el display.
;
call Retardo ;

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

call Retardo ;
;
movlw 06h ; Carga en W el valor 6 antes de llamar a la tabla
call tabla ; para recuperar la configuración 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 configuración 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 configuración 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 configuración 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 números.
;
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 atrás.
decfsz CONTADOR2 ;
goto Bucle2 ;
return ; Fin de la subrutina.
;
; Y aquí la tabla de configuraciones para representar los números
; en el display de 7 segmentos, de acuerdo a nuestro esquema de conexiones.
;
tabla addwf PC
retlw 41h ; Carga 41h (representación de 1) en W y retorna.
retlw 3Bh ; Carga 3Bh (representación de 2) en W y retorna.
retlw 6Bh ; Carga 6Bh (representación de 3) en W y retorna.
retlw 4Dh ; Carga 4Dh (representación de 4) en W y retorna.
retlw 6Eh ; Carga 6Eh (representación de 5) en W y retorna.
retlw 7Eh ; Carga 7Eh (representación de 6) en W y retorna.
retlw 43h ; Carga 43h (representación de 7) en W y retorna.
retlw 7Fh ; Carga 7Fh (representación de 8) en W y retorna.
retlw 6Fh ; Carga 6Fh (representación de 9) en W y retorna.
retlw 77h ; Carga 77h (representación 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 capítulos. ¿ podrías reducir el número de instrucciones ? ¿ Podrías hacer que la
cuenta fuese ascendente o descendente dependiendo del valor de otro pin del puerto A ? ¡¡Animo!! , esta es
una buena ocasión de experimentar con lo que ya sabes.

Interrupciones : una introducción


El tema de las interrupciones va a ser probablemente el más difícil y que más tiempo nos llevará revisar. No
hay una manera sencilla de explicar las interrupciones, pero espero que para el final de esta sección seas
capaz de implementar las interrupciones en tus propios programas. Hemos dividido el tema en dos capítulos.
Esto es para ayudar a reducir la materia, y darte a ti, lector, una pausa.

Así que, ¿qué es una interrupción? Bien, como su nombre sugiere, una interrupción es un proceso o una señal
que hace detenerse al microprocesador/microcontrolador de lo está haciendo, para que haga otra cosa.
Permiteme darte un ejemplo de la vida cotidiana. Supón que estás sentado en tu casa, charlando con alguien.
De repente el teléfono suena. Tú detienes la charla, levantas el teléfono y hablas con la persona que te llamó.
Cuando terminas tu conversación telefónica, vuelves para charlar con la persona que estabas antes de que
sonase el teléfono. Puedes hacerte la idea de que la rutina principal es que tú estás charlando con alguien, que
el teléfono suene causa una interrupción a tu charla, y que la rutina de interrupción es el proceso de hablar
por teléfono. Cuando la conversación telefónica ha terminado, entonces vuelves a la rutina principal de la
charla. Este ejemplo es exactamente igual a como una interrupción hace que actúe un procesador. El programa
principal está en curso, ejecutando alguna función en un circuito, pero cuando ocurre una interrupción el

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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 interrupción. 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 capítulos cuando
miremos los temporizadores [ Nota de la traducción: timers] y el almacenamiento de datos.

Si observas los pines del PIC, verás que el pin 6 se muestra como RB0/INT. RB0 es obviamente el bit 0 del
Puerto B. El INT simboliza que también puede ser configurado como un pin de interrupción. También, 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 interrupción y no
como un pin de Entrada/Salida.

Dentro del PIC hay un registro llamado INTCON, y su dirección 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 Traducción: 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 interrupción. El bit 4 de INTCON es llamado INTE [ Nota
de la traducción: en ingles, "INT Enable". En castellano "Habilita INT", o dicho de otro modo que utilizaremos
el pin 6 con su función INT, no como RB0.]. Poniendo este bit a 1 decimos al PIC que RB0 será un pin de
interrupción. 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 interrupción. Ahora, tenemos que
decirle al PIC si la interrupción se va a producir en la transición ascendente de la señal(de 0 Voltios a +5
Voltios) o la descendente (de +5V a 0V). En otras palabras, ¿queremos que el PIC haga la interrupción cuando
la señal vaya de nivel bajo a nivel alto, o de nivel alto a nivel bajo? . Por defecto, esto está configurado para
que sea en la transición ascendente (o flanco de subida de la señal). El flanco de "disparo" se configura en otro
registro llamado OPTION, que está en la dirección 81h.

El bit en el que estamos interesados es el bit 6, que se llama INTEDG [ Nota de la traducción: del ingles
"INTerrupt EDGe", o en castellano "flanco para la interrupción" ]. Poniendo este a 1 causará que el PIC sea
interrumpido en el flanco de subida o transición ascendente (el estado por defecto) y poniéndolo a 0 causará
que el PIC sea interrumpido en el flanco de bajada o transición 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 después 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 después volver al banco 0
cuando hayas terminado.

[ Nota de la Traducción: 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 INTerrupción.
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 interrupción.
3º - En el registro OPTION (81h):
3.1 - Poner en el bit 6 (INTEDG) un 1 si queremos que se active la interrupción 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 interrupción en el flanco de
bajada(de +5V a 0V). ]

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

Bien, así que ahora que le hemos dicho al PIC qué pin va a ser para la interrupción, y en qué flanco se va a
disparar la misma, ¿qué ocurre en el programa y en el PIC cuando sucede la interrupción ?
Ocurren dos cosas. Primero, se activa un flag. Este le dice al procesador interno del PIC que ha ocurrido una
interrupción. Segundo, el Contador de Programa (que hemos mencionado en el capítulo anterior) apunta a una
dirección particular dentro del PIC. Vamos a ver cada cosa separadamente.

El Flag de Interrupción

En nuestro registro INTCON, el bit 1 es el flag de interrupción llamado INTF [ Nota de la Traducción: "INTerrupt
Flag"]. Entonces, cuando ocurra una interrupción, este flag se pondrá a 1. Mientras no haya una interrupción,
el flag estará a 0. Y eso es todo lo que hace. Ahora estarás 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
interrupción. Por que digamos que causamos una interrupción. El flag se pondrá a 1, y el PIC va a nuestra
rutina de interrupción para procesarla. Si el flag no fuese puesto a 1, al PIC se le permitiría seguir
respondiendo a las interrupciones, y por tanto una serie de pulsos continuos sobre el pin harían que el PIC
volviese a comenzar con nuestra rutina, y nunca la terminaría. Volviendo a mi ejemplo del teléfono, es como
descolgar el teléfono, y tan pronto como comienzas a hablar, que volviese a sonar de nuevo porque alguien
más quiere hablar contigo. Es mucho mejor terminar una conversación, y después descolgar el teléfono de
nuevo para hablar con la segunda persona.

Hay un pequeño inconveniente respecto a este flag. Aunque el PIC automáticamente 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 fácilmente, y
como seguro que ya supones, se tiene que hacer después de que el PIC haya ejecutado la rutina de
interrupción.

La Posición de Memoria

Cuando enciendes por primera vez el PIC, o si ocurriese un reset , el Contador de Programa apunta a la
dirección 0000h, que está justo al principio de la memoria de programa. Sin embargo, cuando ocurre una
interrupción, el Contador de Programa apuntará a la dirección 0004h. Así que, cuando escribamos nuestro
programa con interrupciones, lo primero que tenemos que decirle al PIC es que se salte la dirección 0004h, y
mantenga, la rutina de interrupción que empieza en esa dirección 0004h, separada del resto del programa.
Esto es muy fácil de hacer.

Primero, comenzamos nuestro programa con un comando llamado ORG. Este comando significa Origen, o inicio.
Y lo que le sigue es una dirección.[Nota de la traducción: ORG no es una instrucción del PIC sino una directiva
del ensamblador, al igual que lo son EQU o END que vimos anteriormente. ORG indica al ensamblador la
dirección de memoria a partir de la cual se deberá ubicar el código que viene escrito a continuación de ella.]
Dado que el PIC comienza en la dirección 0000h, escribimos "ORG 0000h". Lo siguiente que tenemos que
hacer es saltarnos la dirección 0004h. Hacemos esto con una instrucción "GOTO", seguida de la etiqueta que
apunte a nuestro programa principal.
A continuación del "GOTO" ponemos otro comando ORG, pero esta vez seguido de la dirección 0004h. Es
después de este comando donde introduciremos la rutina de interrupción. Ahora después, podríamos o bien
escribir directamente nuestra rutina de interrupción seguida de un segundo comando ORG, o bien podemos
poner una instrucción "GOTO" que apunte a la rutina de interrupción (que podríamos escribirla al final del
programa). Realmente es una cuestión de elegir por tu parte. Después, en la rutina de interrupción, para
decirle al PIC que ha llegado al final de la misma tenemos que poner la instrucción "RETFIE" justo al final. Este
comando significa "retorna de la rutina de interrupción". Cuando el PIC la ve, el Contador de Programa
apuntará a la última posición en la que el PIC estaba antes de que ocurriese la interrupción.
Ponemos aquí un fragmento de código para mostrar lo anterior:

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

;
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 interrupción.
: ; Esta es nuestra rutina de interrupción
: ; con lo que queremos que haga el PIC
: ; cuando reciba una interrupción.
RETFIE ; Fin de la rutina de interrupción.
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 estás usando el
mismo registro en tu programa principal y en la rutina de interrupción, recuerda que los contenidos del
registro, probablemente cambien cuando ocurra la interrupción. Por ejemplo, estás utilizando el registro W para
enviar datos al puerto A en el programa principal, y también vas a utilizar el registro W en tu rutina de
interrupción para mover datos de una posición a otra. Si no tienes cuidado, el registro W contendrá el ultimo
valor que tenía cuando estaba en la rutina de interrupción, y cuando vuelvas de la interrupción, este dato se
enviará al puerto A en lugar del valor que tenías antes de que ocurriese la interrupción. Para evitar esto, dentro
de la rutina de interrupción, 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 interrupción 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 Instrucción. Bien, la hoja de características (datasheet), aunque en letra
pequeña, dice que debes dejar que pasen de 3 a 4 ciclos de instrucción entre interrupciones. Mi consejo es que
dejes 4 ciclos. La razón para este retardo es que el PIC necesita tiempo para saltar a la dirección de
interrupción, poner el flag, y volver de la rutina de interrupción. Así que ten esto en mente si estas utilizando
otro circuito que genere una interrupción en el PIC.

Un punto a recordar es que si utilizas los bits del 4 al 7 del puerto B como interrupción, no puedes seleccionar
un pin individual del puerto B para que sirva como interrupción. Así, si habilitas estos pines, todos estarán
habilitados a la vez. De modo que, por ejemplo, no puedes tener solamente los bits 4 y 5, los bits 6 y 7
también los tendrás habilitados. Entonces ¿ Para que sirve tener cuatro bits que actúan como interrupción?
Bien, podrías tener un circuito conectado al PIC, y si cualquiera de las cuatro lineas se pusiese a nivel alto, esta
podría ser la condición que necesites para que el PIC actuase rápidamente. Un ejemplo de esto puede ser la
alarma de una casa, donde cuatro sensores estén conectados a los bits 4 a 7 del puerto B. Cualquier sensor
podría activar al PIC para que hiciese sonar una alarma, y que la rutina de hacer sonar la alarma fuese la
rutina de interrupción. Esto ahorra el estar comprobando todo el tiempo todos los pines, y permite al PIC
continuar haciendo otras cosas.

Interrupciones : Cómo escribir el código


En el pasado capítulo cubrimos mas o menos lo básico, así que creemos que es el momento de escribir nuestro
primer programa. El programa que vamos a escribir contará el número de veces que un switch es activado, y
después mostraremos el número. El programa contará de 0 a 9, mostrando en 4 LEDs en formato binario, y la
entrada o interrupción será por RB0.

La primera cosa que tenemos que hacer es decirle al PIC que salte la dirección donde el Contador de Programa
apuntará cuando ocurra una interrupción. Aquí notarás que estamos utilizando una forma distinta de expresar
los número hexadecimales. Antes usábamos "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 interrupción.
retfie ; Esto le dice al PIC que la rutina de interrupción
; ha terminado y el PC volverá a apuntar al programa principal
Principal ; Este es el comienzo de nuestro programa principal.

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

Ahora tenemos que decirle al PIC que vamos a utilizar las interrupciones, y que el pin de interrupción 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 interrupción 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
interrupción, 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.
Podríamos simplemente incrementar el valor del puerto A, pero verás porque estamos utilizando una variable
cuando escrbimos nuestra rutina de interrupción:

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
interrupción. respecto a esto, nuestra interrupción va a ser una conmutación. Lo que queremos que haga el
PIC es añadir 1 a la variable CONTADOR cada vez que el switch esté cerrado. Sin embargo, solo queremos
mostrar los número que el switch se cierra de 0 a 9 veces. Antes dijimos que simplemente podríamos
incrementar el puerto A cada vez que hubiese una interrupción. 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, también queremos mostrarte algunos comandos
aritméticos que has visto en los pasados capítulos.

Así que vamos con nuestra rutina de interrupción.


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 podríamos enviar un número completamente diferente como resultado de nuestras
operaciones. Así que hagamos eso primero:

;
movwf TEMPORAL ; Almacenamos w en una posición temporal.

Lo siguientes que queremos es añadir 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.

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

;
movlw 0x0A ; Ponemos 10 en W.
subwf CONTADOR,0 ; Restamos W a CONTADOR y ponemos
; el resultado en W.

En el capítulo Operadores Aritméticos y Lógicos vimos que si restamos un número mayor a un número menor,
el flag de Acarreo (CARRY) se pondrá a 1. Este flag también se pondrá a 1 si lo números 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 instrucción subwf

Ahora sabemos si el valor de CONTADOR es 9 o más. 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 instrucción BFTSS, como sabes, se saltará la siguiente instrucción 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 más interrupciones.
movfw TEMPORAL ; Restaura W al valor que tenía antes de la interrupción.
retfie ; Salir de la rutina de interrupción.
limpiar
clrf CONTADOR ; Pon CONTADOR otra vez a 0.
bcf INTCON,1 ; Tenemos que poner a 0 este ''flag'' para
; permitir más interrupciones.
retfie ; Salir de la rutina de interrupción.

Todo lo que queda hacer ahora es ponerlo todo junto y también 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 después del listado del programa.
Cada vez que pulses el conmutador, los LEDs contarán 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
;
;*****************DEFINICIÓN DE CONSTANTES********************************
INTCON EQU 0x0B ; Registro de Control de Interrupciones
PORTB EQU 0x06 ; Dirección del registro PORTB
PORTA EQU 0x05 ; Dirección del registro PORTA
TRISA EQU 0x85 ; Dirección del registro TRISA
TRISB EQU 0x86 ; Dirección del registro TRISB
STATUS EQU 0X03 ; Dirección del registro STATUS
CONTADOR EQU 0x0c ; Esta será nuestra variable contador
TEMPORAL EQU 0x0d ; Almacén temporal para el registro W
goto Principal ; Ir a nuestro programa principal saltando se la dirección de interrupción.
;
;***************RUTINA DE INTERRUPCION*********************************
org 0x04 ; Aquí es donde comienza nuestra rutina de interrupción.
movwf TEMPORAL ; Almacenamos w en una posición 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 instrucción 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 más interrupciones.
movfw TEMPORAL ; Restaura W al valor que tenía antes de la interrupción.
retfie ; Salir de la rutina de interrupción.
limpiar
clrf CONTADOR ; Pon CONTADOR otra vez a 0.
bcf INTCON,1 ; Tenemos que poner a 0 este ''flag'' para
; permitir más interrupciones.
retfie ; Salir de la rutina de interrupción.
;
;***************PROGRAMA PRINCIPAL***********************************
Principal ; Este es el comienzo de nuestro programa principal.
;****************Configura los Registros de Interrupción*************
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*******************************

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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 envía 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 código anterior. Hay dos cosas en el diagrama que
puede que te sorprendan. Primero, no hemos incluido el condensador de temporización en el circuito. Este es
un ingenioso truco que puedes intentar si te quedas sin condensadores. La capacitancia viene dada por la
capacitancia de separación entre el pin del oscilador y pin de masa. Por tanto, con una resistencia y la
capacitancia de separación, tenemos un oscilador RC. Bien, no es una manera precisa de hacerlo, ya que la
capacitancia de separación variará de un circuito a otro. Pero, pensábamos que podrías encontrar interesante
esta idea. Segundo, hemos incluido un circuito anti-rebote para el switch . Este hará que el PIC "crea" que ha
existido más de una pulsación. 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 posición. Si quieres ver los
efectos de rebote de un switch, quita el condensador y la resistencia del switch .

PICTutorial circuito interrupciones.jpg

El Watchdog Timer
Ahora vamos a echar un vistazo a un temporizador interno, llamado "Watchdog Timer" [ Nota de la traducción:
En castellano sería, "temporizador perro guardián" ]

Así que, ¿Qué es un watchdog timer?

Supón que has escrito un programa que está continuamente corriendo en un PIC. Ahora, quieres asegurarte de
que el programa sigue ejecutándose 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. Supón 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 después de un largo periodo el programa se atasca en algún punto y el
PIC se queda enganchado en un bucle.

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

Lo que se necesita en ambos casos es alguna clase de reset(o reinicio) si el programa se quedó atascado. Este
es el propósito del watchdog timer.

Un circuito watchdog no es nada nuevo. Muchos microprocesadores y microcontroladores lo tienen. Pero,


¿cómo 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 poniéndolo de vuelta
a 00 durante el programa. Ahora puedes ver que si tu programa se atasca por cualquier razón, 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 razón de esta
aproximación 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 alimentación. También 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 Traducción: "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 división, más tiempo
tardará el WDT en reiniciarse.

El prescaler está localizado en el registro OPTION en la dirección 81h, los bit del 0 al 2 inclusive. Más abajo hay
una tabla que muestra las asignaciones de los bits para cada ratio de división 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

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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 después de cerca de medio segundo como tiempo de seguridad
ante fallo. El valor más próximo 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 b’101’ ; 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 Traducción: Se refiere al temporizador TMR0. ]. Esto significa que tenemos que cambiar el prescaler
al WDT [ Nota de la Traducción: 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 decíamos 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.


Después tenemos que cambiar al banco 1 para asignar el prescaler al WDT y configurar el tiempo.
Y después volver al banco 0.

El código esta aquí, donde xxx es el valor del prescaler:

bcf STATUS,0 ; Nos aseguramos de que estamos en el banco 0


clrf 01h ; Dirección 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 b’1xxx’ ; 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 instrucción 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 finalización 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 más 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 código en
ejecutarse, en tiempo real. El principio es muy simple, pero ¡puede que te tires de los pelos!

Temporización de las instrucciones

Como probablemente ya sabrás, el PIC toma el reloj externo y lo divide por 4. Este tiempo interno es llamado
ciclo de instrucción. Como hemos dicho que conectamos un cristal de 4 Mhz a nuestro PIC, internamente el PIC
irá a 1Mhz. En términos de temporización, esto es 1/(4Mhz/4) = 1 µS. Entonces algunas instrucciones lleva
ejecutarlas un solo ciclo de instrucción, p. ej. 1 µS utilizando un cristal de 4Mhz, mientras que otras emplearán
2 µS en ser ejecutadas.
La hoja de características nos dice cuantos ciclos lleva cada instrucción. La forma mas sencilla para recordarlo
es muy simple. Asume que todas las instrucciones tardan 1 ciclo. Pero si una instrucción causa que el programa
vaya a algún otro sitio, este se tomará 2 ciclos. Permite que te darte un par de ejemplos.
La instrucción "movwf" tarda solo 1 ciclo, porque solo mueve un dato de un lugar a otro. La instrucción "goto"
tarda 2 ciclo, porque está causando que el contador de programa (PC) vaya a otro sitio del programa. La
instrucción 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 común. Se saltan la siguiente instrucción en caso si se cumple
cierta condición. Si no se cumple la condición, entonces se lleva a cabo la siguiente instrucción. Por ejemplo, la
instrucción DECFSZ decrementará en 1 el valor almacenado en el registro F. Si el resultado es 0, entonces la
siguiente instrucción será ejecutada. Esta instrucción por tanto tarda 1 ciclo. Si el resultado es 0, entonces la

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

instrucción siguiente se la salta, y la siguiente a la anterior será ejecutada. En este ejemplo la instrucción tarda
2 ciclos. La razón es que la instrucción altera el PC. Necesita 1 ciclo para ejecutar la función, y necesita otro
para cambiar el PC por uno mas extra.

Para aclarar esto, vamos a mirar un código simple, y trabajaremos sobre los ciclos de instrucción que tarda:

;
movlw 02
movwf CONTADOR
Bucle decfsz CONTADOR
goto Bucle
end

Nuestra primera instrucción simplemente mueve el valor 02 a W. Esto no causa ningún salto, por tanto es solo
1 ciclo. La siguiente instrucción es similar, mueve los contenidos del registro W a CONTADOR. De nuevo, esto
tardará 1 ciclo. Ahora, la siguiente instrucción primero decrementa CONTADOR en 1. Esto es 1 ciclo. Después
hará una comprobación para ver que CONTADOR es igual a 0. En este momento no lo es, por tanto vamos a la
siguiente instrucción. La siguiente instrucción es una de "goto", y por tanto tarda 2 ciclos. Volvemos a nuestra
instrucción DECFSZ, la que decrementa CONTADOR en 1 de nuevo. Esto tarda otro ciclo. Hace una
comprobación para ver si CONTADOR es igual a 0. Esta vez lo es, y por tanto se salta la siguiente instrucción.
Para saltarse la siguiente instrucción 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 Traducción: En ingles "Fuses"]. No son los mismos
que puedes encontrar en los enchufes, sino que son conmutadores electrónicos 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 capítulo pasado, cuando veamos como incluir otros ficheros y macros. El cómo hacerlo
manualmente varía dependiendo del software de programación. La documentación 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 botón de configuración. Después
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 función.
Primero borraremos el WDT periódicamente para mostrar que el programa funciona, y después quietaremos la
instrucción CLRWDT para mostrar que efectivamente el PIC se reinicia.

El programa que hemos elegido es el utilizado en el capítulo Operadores Aritméticos y Lógicos donde hacíamos
que una fila de LEDs se encendiesen a un tiempo de izquierda a derecha y de derecha a izquierda. El circuito
se muestra más 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 moviéndose uno a uno. Elegimos este programa porque

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

es suficientemente lento para que podamos jugar con el WDT, y que puedas ver fácilmente como se reinicia el
PIC. Hemos quitado los comentarios originales, y los hemos sustituido por una descripción de las lineas del
WDT, y en cada linea del tiempo total desde el inicio (asumiendo 8KHz), y el número total de ciclos de reloj de
cada linea.

TutorialPIC practicaWatchdog.jpg

TIEMPO equ 9FH ; Variable para el bucle de retardo.


PORTB equ 06H ; Dirección del Port B.
TRISB equ 86H ; Dirección del registro tri-estado del Port B.
PORTA equ 05H ; Dirección del Port A.
TRISA equ 85H ; Dirección 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, después 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

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

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 periódico 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.
Podríamos hacer una llamada a la subrutina y reiniciar el WDT después de que los dos Retardos hayan
terminado, o podríamos incorporar el CLRWDT dentro del retardo mismo. Hemos decidido, sin ninguna razón
importante, poner el CLRWDT dentro de la subrutina de retardo.

TIEMPO equ 9FH ; Variable para el bucle de retardo.


PORTB equ 06H ; Dirección del Port B.
TRISB equ 86H ; Dirección del registro tri-estado del Port B.
PORTA equ 05H ; Dirección del Port A.
TRISA equ 85H ; Dirección 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 b’1101’ ; 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 ;

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

movwf PORTB ;
call RETARDO ;
call RETARDO ;
; Mueve el bit por la izquierada del puerto B, después 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 instrucción CLRWDT, verás 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

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]


Tutorial: Programación de PICs en Ensamblador - Asociación de Robotica y Domótica de España (A.R.D.E.)

MICROCHIP (http://www.microchip.com)
Microchip: El PIC 16F84 (Datasheet, Información adicional, etc...)
(http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en010229)
Microchip: El PIC 16F84A (Datasheet, Información adicional, etc...)
(http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en010230)
Microchip: Páginas de documentación 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 versión Windows (http://www.picbook.com/downloads.html)
Más Información: Lenguaje Ensamblador (en castellano).
(http://perso.wanadoo.es/pictob/ensamblador.htm)

Obtenido de "http://wiki.webdearde.com/index.php?
title=Tutorial:_Programaci%C3%B3n_de_PICs_en_Ensamblador"
Categoría: Tutorial

Esta página fue modificada por última vez el 6 feb 2009, a las 17:32.

http://wiki.webdearde.com/index.php?title=Tutorial:_Programación_de_PICs_en_Ensamblador&printable=yes[10/03/2011 06:58:36 a.m.]

You might also like