Indice Tema 0 Introduccion Tema 1 Hardwa

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 72

INDICE

TEMA 0: INTRODUCCIÓN

TEMA 1: HARDWARE
1.1. Arquitectura Harvard.
1.2. Mapa de memoria.
1.3. Registros.
1.4. Temporizador / contador (TMP0, WDT y SLEEP).
1.5. Puertos de entrada / salida.
1.6. Interrupciones.

TEMA 2: PROGRAMACIÓN EN ASM


2.1. Instrucciones ASM del PIC 16F84
2.1.1. De control y manejo de literales.
2.1.2. Orientadas a registros.
2.1.3. Orientadas a bits.
2.2 Modos de direccionamiento.
2.3. Técnicas de programación en ASM.

TEMA 3: ESCRITURA DE PROGRAMAS EN ASM


3.1. Uso del block de notas con Windows
3.2. Programa MPLAB
3.2.1. Herramientas del MPLAB.
3.2.2. Funciones del MPLAB (menús).
3.2.3. Guía de uso del ensamblador MPASM (tutorial).
• Crear proyecto.
• Introducir código
• Ensamblado (compilar)
• Simulador: registros, Watch Window, break points.

1
TEMA 0: INTRODUCCION
Con este curso lo único que pretendo es poner la información que he reunido por medio de
libros y páginas web sobre el PIC 16F84 de Microchip a disposición de los demás, sobre
todo para los compañeros y amigos de los foros de internet y el messenger ( a los cuales
debo darles las gracias por los buenos y entretenidos ratos que pasamos sin ver la TV). ;-)

La parte del curso dedicada al hardware no está desarrollada en profundidad, ya que no se


trata de conocer su estructura de una forma muy exhaustiva, sino como una base para
conocer mejor como programarlo.

Para desarrollar esta especie de curso, he utilizado básicamente estos dos textos:
• Microcontroladores PIC, la solución en un chip (Edit. Paraninfo).
• Aplicaciones de los microcontroladores PIC de Microchip (Edit. McGraw Hill).
Y muchas, muchas horas conectado a internet visitando decenas de páginas
relacionadas con este tema.

Por supuesto, este curso es totalmente gratis. No hace falta bajarse ningún crack para poder
utilizarlo ni visitar páginas warez para conseguirlo (para muchas otras cosas si son
necesarias esas páginas, eh?). Y es que el capitalismo ya no es lo que era...

Y para finalizar quiero dedicar este trabajo a Loly (mi mujer). A pesar deque no he sido
capaz de hacerle comprender por qué hago esto y también por las horas robadas a su
compañía para investigar y divertirme por la red!

2
TEMA 1: HARDWARE

1.1 ARQUITECTURA HARVARD

La arquitectura tradicional de computadoras y microprocesadores está basada en la


arquitectura Von Neumann, en la cual la unidad central de proceso (CPU), está conectada
a una memoria única donde se guardan las instrucciones del programa y los datos.

El tamaño de la unidad de datos o instrucciones está fijado por el ancho del bus que
comunica la memoria con la CPU. Así un microprocesador de 8 bits con un bus de 8 bits,
tendrá que manejar datos e instrucciones de una o más unidades de 8 bits (bytes) de
longitud. Si tiene que acceder a una instrucción o dato de más de un byte de longitud,
tendrá que realizar más de un acceso a la memoria, y al tener un único bus hace que el
microprocesador sea más lento en su respuesta, ya que no puede buscar en memoria una
nueva instrucción mientras no finalicen las transferencias de datos de la instrucción
anterior.

Resumiendo todo lo anterior, las principales limitaciones que nos encontramos con la
arquitectura Von Neumann son:

1º. La limitación de la longitud de las instrucciones por el bus de datos, que hace que el
microprocesador tenga que realizar varios accesos a memoria para buscar instrucciones
complejas.

2º. La limitación de la velocidad de operación a causa del bus único para datos e
instrucciones que no deja acceder simultáneamente a unos y otras, lo cual impide
superponer ambos tiempos de acceso.

Arquitectura Von Newmann

La arquitectura Harvard tiene la unidad central de proceso (CPU) conectada a dos


memorias (una con las instrucciones y otra con los datos) por medio de dos buses
diferentes.
Una de las memorias contiene solamente las instrucciones del programa (Memoria de
Programa), y la otra sólo almacena datos (Memoria de Datos).
Ambos buses son totalmente independientes y pueden ser de distintos anchos. Para un
procesador de conjunto reducido de instrucciones, o RISC (Reduced Instrucción Set
Computer), el conjunto de instrucciones y el bus de memoria de programa pueden

3
diseñarse de tal manera que todas las instrucciones solo ocupen una posición (en longitud)
en la memoria de programa.
Además, al ser los buses independientes, la CPU puede acceder a los datos para completar
la ejecución de una instrucción, y al mismo tiempo leer la siguiente instrucción a ejecutar.
Ventajas de esta arquitectura:
1º. El tamaño de las instrucciones no esta relacionado con el de los datos, y por lo tanto
puede ser optimizado para que cualquier instrucción ocupe una sola posición de memoria
de programa, logrando así mayor velocidad y menor longitud de programa.

2º. El tiempo de acceso a las instrucciones puede superponerse con el de los datos,
logrando una mayor velocidad en cada operación.
Una pequeña desventaja de los procesadores con arquitectura Harvard, es que deben poseer
instrucciones especiales para acceder a tablas de valores constantes que pueda ser
necesario incluir en los programas, ya que estas tablas se encontraran físicamente en la
memoria de programa (por ejemplo en la EPROM de un microprocesador).

Arquitectura Harvard

El microcontrolador PIC 16F84 posee arquitectura Harvard, con una memoria de datos de
8 bits, y una memoria de programa de 14 bits.

Diagrama de bloques del microcontrolador PIC16F84

4
En la figura anterior vemos la arquitectura interna organizada en bloques interconectados, en donde se
incluye la memoria RAM, la memoria EEPROM, los puertos de entrada y salida (I/O), etc.

1.2. MAPA DE MEMORIA

MEMORIA RAM:
El microcontrolador PIC16F84 puede direccionar 128 posiciones diferentes de memoria
RAM; pero Microchip Tecnologies solamente ha implementado 80 posiciones para este
PIC. Esta memoria esta dividida en dos partes:

• La primera parte consta de 12 registros que serán utilizados por funciones


especiales del microcontrolador. Comienza en la dirección 00h y termina en la 0Bh.

• La segunda parte consta de 68 registros de memoria RAM que serán utilizados para
almacenar datos temporales requeridos por los programas. Comienza en la
dirección 0Ch y termina en la posición 4Fh

Este tipo de memoria (RAM), se caracteriza por perder los datos si se llegase a desconectar
el microcontrolador o la tensión baja por debajo de los límites mínimos.
La memoria RAM así como algunos registros especiales son los mismos en los dos bancos
del mapa de memoria del PIC.

MEMORIA DE PROGRAMA TIPO EEPROM:

5
Esta memoria tiene 1 K x 14 Bits de memoria tipo Flash. Esta memoria es la que
utilizaremos para almacenar nuestro programa dentro del microcontrolador PIC16F84.
El tipo de memoria utilizada en este microcontrolador, podrá ser grabada o borrada
eléctricamente.
La memoria tipo Flash tiene la característica de poderse borrar en bloques completos y no
podrán borrarse posiciones concretas o específicas.
Este tipo de memoria no es volátil, es decir, no pierde los datos si se interrumpe la energía.
En la siguiente imagen se muestra como está organizada la memoria dentro del
microcontrolador.

La memoria del programa comienza en la posición 000h y termina en la posición 03FFh.


Nota: El contador de programa PC del microcontrolador PIC16F84 tiene una longitud de 13 Bits por lo que
implica que podrá direccionar cualquier posición comprendida en un rango de los 8 K x 14; pero la empresa
Microchip Tecnologies sólamente ha implementado internamente 1 K que representa desde 000h hasta
03FFh. En el caso de que se direccione fuera de este rango, automáticamente causará un solapamiento.

La memoria para almacenar el programa, tipo EEPROM (Electrical Erasable


Programmable Read Only Memory), que puede ser reescrita, nos resultará perfecta para
realizar pruebas y experimentos, además de para la programación on-board (actualización
del programa interno de chip sin necesidad de retirarlo del circuito de prueba).

6
La memoria de programa siempre está direccionada desde el Contador de Programa (PC),
mientras que la memoria de datos puede direccionarse directamente desde parte del código
OP de la instrucción o indirectamente a través de un registro denominado FSR (Registro
de Selección del Banco).
Y aquí tenemos una vista exterior de los pins del PIC16F84:

El PIC16F84 esta dotado de un total de 18 pines distribuidos en dos filas paralelas de 9


pines cada una. Los pines marcados con AZUL representan las líneas de I/O disponibles
para nuestras aplicaciones, los pines ROJO y NEGRO son los pines de alimentación, los
pines en VERDE están reservados para el funcionamiento del PIC (MCLR para el reset y
OSC1-2 para el reloj ).

1.3. REGISTROS
ORGANIZACION:
La memoria interna de datos, también llamada archivo de registros (register file), esta
dividida en dos grupos:

• Los registros especiales (8 registros).

• Los registros de propósito generales(72 registros).


Los registros especiales ocupan las 8 primeras posiciones que van desde la 00 a la 07, y los
registros de propósito generales las posiciones que siguen, de la 08 a la 4F.
Los registros especiales contienen la palabra de estado (STATUS), los registros de datos
de los tres puertos de entrada salida (Puerto A, Puerto B, Puerto C), los 8 bits menos
significativos del Program Counter (PC), el contador del Real Time Clock/Counter
(RTCC) y un registro puntero llamado File Select Register (FSR). La posición 00 no
contiene ningún registro en especial y es utilizada en el mecanismo de direccionamiento
indirecto.
Los registros de propósito general se dividen en dos grupos:

• los registros de posición fija (8 registros).

7
• Los bancos de registros (64 registros).
Los primeros ocupan las 8 posiciones que van de la 08 a la 0F. Los bancos de registros
consisten en hasta cuatro grupos o bancos de 16 registros cada uno, que se encuentran
superpuestos en las direcciones que van de la 10 a la 4F. Se puede operar con un solo
banco a la vez, el cual se selecciona mediante los bits 5 y 6 del File Select Register (FSR)

Otros registros especiales:


Las ocho primeras posiciones del área de datos están reservadas para alojar registros de
propósito especial, quedando las restantes libres para contener los datos u operandos que se
desee (registros de propósito general).
El registro INDF que ocupa la posición 0 no está implementando físicamente y, como se
ha explicado, se le referencia en el direccionamiento indirecto de datos aunque se utiliza el
contenido de FSR.
En la dirección esta el registro TMR0 (Temporizador) que puede ser leído y escrito como
cualquier otro registro.
El PC ocupa la posición 2 del área de datos en donde se halla el registro PCL al que se
añaden 3 bits auxiliares y se conectan con los dos niveles de la Pila en las instrucciones
CALL y RETLW.
El registro de Estado (STATUS) ocupa la posición 3 y entre sus bits se encuentran los
señalizadores C, DC y Z y los bits IRP, RP1 y RP0 que seleccionan la página en la
memoria de programa.
FRS se ubica en la dirección 4 y puede usarse para contener las dirección del dato en las
instrucciones con direccionamiento indirecto y también para guardar operandos en sus 5
bits de menos peso.
Los registros que ocupan las posiciones 5 y 6 soportan los Puertos A y B de E/S. Pueden
ser leídos y escritos como cualquier otro registro y manejan los valores de los bits que
entran y salen por los pines de E/S del microcontrolador.
Los registros especiales (SFR o Special Function Registers) son una serie de registros
usados por el PIC para funciones de control de funcionamiento del hardware,
direccionamiento especial, etc. En la siguiente tabla están los registros especiales disponibl

8
REGISTROS DE FUNCIONES ESPECIALES:
Acumulador y registro W.
Los siguientes gráficos representan un diagrama simplificado de la arquitectura interna del
camino de los datos en la CPU de los microcontroladores PIC y de los microprocesadores
tradicionales.
Observamos que la principal diferencia entre ambos se encuentra en la ubicación del
registro de trabajo, que para los PIC’s se denomina W (Working Register), y para los
tradicionales es el Acumulador.

9
En los microcontroladores tradicionales todas las operaciones se realizan sobre el
acumulador. La salida del acumulador esta conectada a una de las entradas de la Unidad
Aritmética y Lógica (ALU), y por lo tanto éste es siempre uno de los dos operandos de
cualquier instrucción. Por convención, las instrucciones de simple operando (borrar,
incrementar, decrementar, complementar), actúan sobre el acumulador. La salida de la
ALU va solamente a la entrada del acumulador, por lo tanto el resultado de cualquier
operación siempre quedara en el acumulador. Para operar sobre un dato de memoria,
después de realizar la operación tendremos que mover siempre el acumulador a la
memoria con una instrucción adicional.
En los microcontroladores PIC, la salida de la ALU va al registro W y también a la
memoria de datos, por lo tanto el resultado puede guardarse en cualquiera de los dos
destinos. En las instrucciones de doble operando, uno de los dos datos siempre debe estar
en el registro W, como ocurría en el modelo tradicional con el acumulador. En las
instrucciones de simple operando el dato en este caso se toma de la memoria (también por
convención).
La gran ventaja de esta arquitectura es que permite un gran ahorro de instrucciones ya que
el resultado de cualquier instrucción que opere con la memoria (sea de simple o doble
operando), puede dejarse en la misma posición de memoria o en el registro W, según se
seleccione con un bit de la misma instrucción. Las operaciones con constantes
provenientes de la memoria de programa (literales) se realizan sólo sobre el registro W.
En la memoria de datos de los PIC’s se encuentran ubicados casi todos los registros de
control del microprocesador y sus periféricos autocontenidos, y también las posiciones de
memoria de usos generales.

10
Contador de Programa.
Este registro, normalmente denominado PC, es equivalente al de todos los
microprocesadores y contiene la dirección de la próxima instrucción a ejecutar. Se
incrementa automáticamente al ejecutar cada instrucción, de manera que la secuencia
natural de ejecución del programa es lineal, una instrucción después de la otra. Algunas
instrucciones (que llamaremos de control) cambian el contenido del PC alterando la
secuencia lineal de ejecución. Dentro de estas instrucciones se encuentran GOTO y CALL
que permiten cargar en forma directa un valor constante en el PC haciendo que el
programa salte a cualquier posición de la memoria. Otras instrucciones de control son los
SKIP o saltos condicionales, que producen un incremento adicional del PC si se cumple
una condición especifica, haciendo que el programa salte, sin ejecutar, la instrucción
siguiente.
El PC es un registro de 13 bits en el 16F84, lo que permite direccionar 8.192 posiciones de
memoria de programa, pero que internamente solamente podrá direccionar 1.024..
A diferencia de la mayoría de los microprocesadores convencionales, el PC es también
accesible al programador como registro de memoria interna de datos, en la posición 02. Es
decir que cualquier instrucción común que opere sobre registros puede ser utilizada para
alterar el PC y desviar la ejecución del programa.

Stack:
En los microcontroladores PIC el stack es una memoria interna dedicada, de tamaño
limitado, separada de las memorias de datos y de programa, inaccesible al programador, y
organizada en forma de pila, que es utilizada solamente, y en forma automática, para
guardar las direcciones de retorno de subrutinas e interrupciones. Cada posición es de 11
bits y permite guardar una copia completa del PC. Como en toda memoria tipo pila, a los
datos se accede de forma LIFO (Last In First Out) de manera que el último en entrar es el
primero en salir.
El tamaño del stack en los 16F84 es de 8 posiciones.
El stack y el puntero interno que lo direcciona, son invisibles para el programador, solo se
accede a ellos automáticamente para guardar o rescatar las direcciones de programa cuando
se ejecutan las instrucciones de llamada o retorno de subrutinas, cuando se produce una
interrupción o cuando se ejecuta una instrucción de retorno de ella.

Palabra de Estado del Procesador (STATUS):


La palabra de estado del procesador contiene los tres bits de estado de la ALU (C, DC y
Z), y otros 5 bits que se incluyeron en este registro.

11
Registro STATUS

7 6 5 4 3 2 1 0

IRP RP1 RP0 TO PD Z DC C

El bit Z indica que el resultado de la ultima operación fue CERO. El bit C indica acarreo
del bit más significativo (bit 7) del resultado de la ultima operación de suma. En el caso de
la resta se comporta a la inversa, C resulta 1 si no hubo pedido de préstamo. El bit DC
(digit carry) indica acarreo del cuarto bit (bit 3) del resultado de la ultima operación de
suma o resta, con un comportamiento análogo al del bit C, y es útil para operar en BCD
(para sumar o restar números en código BCD empaquetado). El bit C es usado además en
las operaciones de rotación derecha o izquierda como un paso intermedio entre el bit 0 y el
bit 7.
El bit PD (POWER DOWN) sirve para detectar si la alimentación fue apagada y encendida
nuevamente, tiene que ver con la secuencia de inicialización, el watch dog timer y la
instrucción sleep, y su uso se detallara en la sección referida al modo POWER DOWN. El
bit TO (TIME-OUT) sirve para detectar si una condición de reset fue producida por el
watch dog timer, esta relacionado con los mismos elementos que el bit anterior y su uso se
detallara en la sección referida al WATCH DOG TIMER. Los bits de selección de pagina
RP0/RP1/IRP se utilizan en las instrucciones de salto GOTO y CALL,
Manipulando el bit número 5 (RP0) del registro STATUS podremos indicar al
microcontrolador si queremos trabajar en el banco "0" o en el "1".
La operación normal del microcontrolador se efectúa en el banco "0". Pero cuando nos
cambiamos del banco "0" al banco "1" es para efectuar ciertos cambios que definen como
estarán configurados los puertos del microcontrolador.
En la siguiente imagen vemos la asignación que tienen cada uno de los ocho bits del
registro STATUS:

12
Si observamos de nuevo la tabla de registros del PIC16F84 vemos que existen algunas
diferencias entre el banco "0" y el banco "1". Los registros denominados OPTION,
TRISA, TRISB, EECON1 y EECON2 no existen en el banco “0”.

Si necesitamos acceder al registro TRISA, que solamente se encuentra en el banco "1",


obligatoriamente habrá que cambiarse del banco “0” al banco “1” por medio de las
instrucciones del microcontrolador.

Y tendremos que acceder al banco “1” solamente para utilizar los registros que no se
encuentran en el banco “0”. Una vez utilizados esos registros ( y modificados si fuese
necesario), regresaremos al banco “0” para que el microcontrolador siga con su tarea
asignada en la memoria del programa.

En el caso de los registros OPTION y TRISA y B no será necesario realizar el cambio de


banco, ya que tenemos dos instrucciones que podremos utilizar para hacerlo, a pesar de que
Microchip recomienda no usarlas para mantener la compatibilidad con el juego de
instrucciones del microprocesador 16CXX.

13
EL REGISTRO CONTADOR TMR0:

El registro TMR0 es un contador de 8 bits, es decir un particular tipo de registro cuyo


contenido es incrementado con una cadencia regular y programable directamente por el
hardware del PIC.
Este registro puede usarse para contar eventos externos por medio de un pin de entrada
especial (modo contador) o para contar pulsos internos de reloj de frecuencia constante
(modo timer). Además, en cualquiera de los dos modos, se puede insertar un prescaler, es
decir un divisor de frecuencia programable que puede dividir por 2, 4, 8, 16, 32, 64, 128 o
256. Este divisor puede ser utilizado alternativamente como prescaler del TMR0 o como
postscaler del Watch Dog Timer, según se lo programe.

En la práctica, a diferencia de los otros registros, el TMR0 no mantiene inalterado el valor


que tiene memorizado, sino que lo incrementa continuamente.
Si por ejemplo escribimos en él el valor 10, después de un tiempo igual a cuatro ciclos de
maquina, el contenido del registro comienza a ser incrementado a 11, 12, 13 y así
sucesivamente con una cadencia constante y totalmente independiente de la ejecución del
resto del programa.

14
Una vez alcanzado el valor 255, el registro TMR0 es puesto a cero automáticamente
comenzando entonces a contar desde cero y no desde el valor originalmente cargado.
La frecuencia de conteo es directamente proporcional a la frecuencia de reloj aplicada al
chip y puede ser modificada programando adecuadamente algunos bits de configuración.
En la figura siguiente está representada la cadena de bloques internos del PIC que
determinan el funcionamiento del registro TMR0.

Los bloques Fosc/4 y T0CKI, mostrados en azul, representan las dos posibles fuentes de
señal de reloj, para el contador TMR0.
Fosc/4 es una señal generada internamente por el PIC tomada del circuito de reloj y que es
igual a la frecuencia del oscilador dividida por cuatro.
T0CKI es una señal generada por un posible circuito externo y aplicada al pin T0CKI
correspondiente al pin 3 del PIC16F84.
Los bloques T0CS y PSA mostrados en verde son dos conmutadores de señal en cuya
salida se presenta una de las dos señales de entrada en función del valor de los bits T0CS y
PSA del registro OPTION.
El bloque PRESCALER es un divisor programable cuyo funcionamiento veremos más en
detalle.
A continuación veremos como es posible dividir posteriormente la frecuencia de conteo,
interna ó externa, activando el PRESCALER.

EL PRESCALER:
El PRESCALER consiste en un divisor programable de 8 bits a utilizar en el caso de que la
frecuencia de conteo enviada al contador TMR0 sea demasiado elevada para nuestros
propósitos. Se configura a través de los bits PS0, PS1 y PS2 del registro OPTION.
La frecuencia Fosc/4 es una cuarta parte de la frecuencia de reloj. Utilizando un cristal de
4Mhz tendremos una Fosc/4 igual a 1 MHz. Tal frecuencia es enviada directamente al
registro TMR0 sin sufrir ningún cambio. La cadencia de conteo que se obtiene es por lo
tanto igual a 1 millón de incrementos por segundo del valor presente en TMR0, que para
muchas aplicaciones podría resultar demasiado elevada.

15
Con el uso del PRESCALER podemos dividir posteriormente la frecuencia Fosc/4
configurando oportunamente los bits PS0, PS1 y PS2 del registro OPTION según la
siguiente tabla.

PS2 PS1 PS0 DIVISOR FREC. SALIDA PREESCALER (Mhz.)


0 0 0 2 500.000
0 0 1 4 250.000
0 1 0 8 125.000
0 1 1 16 62.500
1 0 0 32 31.250
1 0 1 64 15.625
1 1 0 128 7.813
1 1 1 256 3.906

Ejemplo práctico: introducir un retardo igual a un segundo utilizando el registro TMR0.


Debemos programar el bit T0CS a 0 para seleccionar como fuente de conteo el reloj del
PIC, el bit PSA a 0 para asignar el PRESCALER al registro TMR0 en lugar de al Watch
Dog Timer (del que trataremos más adelante) y los bits de configuración del
PRESCALER a 100 para obtener una frecuencia de división igual a 1:32.
La frecuencia que obtendremos en TMR0 será igual a:

Fosc = 1Mhz / 32 = 31.250 Hz


Memorizamos en TMR0 el valor 6 de modo que el registro TMR0 alcanza el cero después
de 250 cuentas (256 - 6 = 250) obteniendo así una frecuencia de paso por cero del TMR0
igual a:

31.250 / 250 = 125 Hz


El siguiente paso será memorizar en un registro de 8 bits el valor 125 de tal modo que,
decrementando este registro en 1 por cada paso por cero de TMR0, se obtenga una
frecuencia de pasos por cero del registro igual a:
125/125 = 1Hz (1 segundo).

En resumen: se trata de controlar si TMR0 ha alcanzado el cero, luego de reinicializarlo a


6 y decrementar el valor contenido en un registro con valor 125. Cuando el registro alcance
también el valor cero, entonces habrá trascurrido un segundo.

16
FUNCIONAMIENTO DEL POWER DOWN MODE:

El Power Down Mode o Sleep Mode, es un estado particular de funcionamiento del PiC,
utilizado para reducir el consumo de corriente en los momentos que no realiza ninguna
tarea o está a la espera de un suceso externo.
Si tomamos como ejemplo un control remoto para TV, veremos que la mayor parte del
tiempo el micro permanece a la espera de la presión de alguna tecla. Apenas oprimida,
efectúa una breve transmisión y queda nuevamente a la espera de la presión de otra tecla.
El tiempo de uso efectivo de la CPU del micro está por tanto, limitado a unos pocos
milisegundos necesarios para efectuar la transmisión mientras que durante varias horas no
efectúa ninguna tarea particular (en el caso de utilizar la técnica secaremote, en la que se
hace un uso más intensivo del mando, ocurre todo lo contrario!).
Para no consumir inútilmente la energía de las baterías, es posible apagar varios de los
circuitos del micro y reencenderlos sólo en correspondencia con algún suceso externo.
Veamos como.
La instrucción SLEEP
La instrucción SLEEP es utilizada para colocar el PIC en Power Down Mode y reducir la
corriente absorbida, que pasará de unos 2 mA (a 5 volt y el clock en 4MHz) a unos 2uA, o
sea, unas 1000 veces menos.
Para entrar en Power Down Mode basta insertar la instrucción SLEEP en cualquier parte
del programa.
Cualquier instrucción siguiente a SLEEP no será efectuada por el PIC el cual finalizará en
este punto la ejecución, apagará los circuitos internos, excepto aquellos necesarios para
mantener el estado de los puertos de I/O y aquellos que lo sacarán de esa condición, los
cuales comentaremos a continuación.
Para despertar al PIC se utilizan diversas técnicas:
1. Reset del PIC llevando a cero el pin 4 (MCLR).
2. Timeout del Watch Dog Timer (si está habilitado).
3. Verificación de una interrupción (interrupción desde el pin RB0/INT, cambio de
estado en el puerto B, finalización de la escritura sobre la EEPROM).
En los dos primeros casos, el PIC es reseteado y la ejecución es retomada en la situación 0
de memoria.
En el tercer caso, el PIC se comporta como en el caso de una interrupción normal,
siguiendo primeramente el Interrupt handler, retomando la ejecución después de la
instrucción SLEEP. Para que el PIC sea despertado por una interupt deben ser habilitados
los flag del registro INTCON.

17
EL WATCH DOG TIMER (WDT):

El Watch Dog Timer (que podría traducirse como temporizador perro guardián) es un
oscilador interno al PIC, pero completamente independiente del resto de la circuitería, cuya
función es eliminar eventuales bloqueos de la CPU del PIC y resetearlo para que retome la
ejecución normal del programa.
Para poder eliminar un eventual bloqueo de la CPU durante la ejecución del programa
principal, se inserta en él una instrucción especial:
CLRWDT (CleaR Watch Dog Timer)
La cual pone a cero en intervalos regulares el WDT, no permitiéndole llegar al final de su
temporización. Si la CPU no realiza esta instrucción antes del término de la temporización,
entonces, se asume que el programa se ha bloqueado por algún motivo y se efectúa el reset
de la CPU.
El periodo mínimo alcanzado el cual la CPU es reseteada es de unos 18 ms (depende de la
temperatura y de la tensión de alimentación). Es posible, sin embargo, asignar el prescaler
al WDT a fin de obtener retardos mayores (hasta unos 2,3 segundos).
Para habilitar el WDT debemos, en la fase de programación, habilitar el flag WDTE de la
palabra de configuración. La modalidad de activación de este flag, depende del
programador usado.

Asignación del prescaler al WDT


Actuando sobre el bit PSA del registro OPTION_REG es posible asignar el prescaler al
WDT para obtener tiempos de intervención mayores. El bit PSA va seteado a uno con la
instrucción:
BSF OPTION_REG,PSA
En caso contrario, el prescaler será asignado al TIMER 0. Obviamente, asignando el
prescaler al WDT, no será posible usarlo con el TIMER 0 y viceversa.
Según los valores de los bits PS0, PS1 y PS2 del OPTION_REG podremos obtener
distintos intervalos de retardo. La elección correcta deberá ser hecha teniendo en cuenta el
máximo retardo que logramos obtener en nuestro programa tras la ejecución de dos
instrucciones CLRWDT sucesivas.
En la tabla siguiente vemos los retardos, según los valores de PS0, PS1 y PS2:

PS2 PS1 PS0 DIVISOR Retardo (ms)


0 0 0 1 18
0 0 1 2 36
0 1 0 4 72
0 1 1 8 144
1 0 0 16 288
1 0 1 32 576
1 1 0 64 1.152
1 1 1 128 2.304

18
1.5. PUERTOS DE ENTRADA/SALIDA (I/O).
Los microprocesadores PIC16F84 tienen 2 puertos de entrada/salida paralelos de usos
generales denominados Puerto A y Puerto B.
El Puerto A es de 4 bits y el Puerto B es de 8 bits.
Los puertos del microcontrolador PIC16F84 son el medio de comunicación con el mundo
exterior, en ellos podremos conectar los periféricos o circuitos necesarios como por
ejemplo los módulos LCD, motores eléctricos, etc; pero estas conexiones no se podrán
realizar arbitrariamente. Existen unas reglas básicas que deberán cumplirse para que el
microcontrolador no sufra daños o se destruya. Para ello es necesario conocer los limites
de corriente que puede manejar el microcontrolador.

LIMITE DE CORRIENTE PARA LOS PUERTOS "A" Y "B".


Los puertos "A" y "B" del microcontrolador podrán ser programados como entradas y
salidas indiferentemente. Para el caso de que sean programados como salida se denominan
"Modo Fuente" por que suministran corriente y cuando son programados como entrada se
denominan "Modo Sumidero" por que reciben corriente.
La máxima corriente que puede suministrar una línea programada como salida es de 20
mili-amperios, pero si utilizamos toda las líneas del puerto "A" programadas como salida,
no deberá exceder de 50mA para todo el puerto "A". Para el caso del puerto "B" no deberá
exceder de 100 mA.
Si las programamos como entradas (Sumidero), la corriente máxima que puede manejar
una sola línea es de 25 mA. Para el caso del puerto "A" programado con todas sus líneas
como entrada, la máxima es de 80 mA. En el caso del puerto "B" es de 150 mA.
En caso de querer utilizar periféricos que manejen mayor cantidad de corriente de la
especificada, habrá que aplicar un circuito acoplador como por ejemplo los buffers,
transistores que se encarguen de controlar la corriente, etc.

Identificación de los Pines utilizados para los puertos de entrada y salida.


En la imagen siguiente se podrá observar claramente que el microcontrolador tiene dos
puertos denominados "A" y "B". El puerto "A" tiene 5 líneas disponibles (RA0, RA1,
RA2, RA3, RA4) y el puerto "B" tiene 8 líneas disponibles (RB0, RB1, RB2, RB3, RB4,
RB5, RB6, RB7). Ambos Puertos suman un total de 13 líneas que podrán ser programadas
independientemente como entradas o como salidas. Estas son las líneas que estarán
destinadas a comunicar el microcontrolador con el mundo exterior, como por ejemplo
motores, diodos luminosos "Leds", módulos LCD, teclados matriciales, etc.

19
También hay que hacer mención a que el Pin número 3 perteneciente al puerto "A" (RA4)
también tiene otra nomenclatura denominada "TOCKI", lo cual quiere decir que esta línea
se puede programar como entrada, salida y temporizador/contador.

Configuración de los puertos de Entrada/Salida.

Los bits de cada puerto se configuran mediante los bits correspondientes de un registro de
control asociado que recibe el nombre de TRIS. En realidad cada puerto soporta dos
registros:
1. El registro de datos, al que se denomina Puerto A o B (PortA o PortB).
2º El registro de control TRISA o TRISB, con el que se programa el sentido (Entrada o
Salida) de las líneas de cada puerto.

Los Puertos A y B se corresponden con las posiciones 5 y 6 del área de datos. Cada uno de
sus bits puede programarse como una línea de Entrada o de Salida, según se ponga un 1 ó
un 0 en el bit del registro de control TRIS correspondiente.

Un 1 en el bit "x" del registro TRISA pone en alta impedancia (Entrada) la línea asociada
"x" del Puerto A. Si en el bit "x" de TRISA hubiese un 0, el contenido del biestable de
datos correspondiente del Puerto A pasaría a la patita de E/S externa.

Cualquier línea puede funcionar como Entrada o como Salida. Sin embargo, si actúa como
Entrada, la información que se introduce desde el exterior no se memoriza o graba, pasa
simplemente por un dispositivo triestado por lo cual el valor de dicha información debe
mantenerse hasta que sea leída. La lectura se realiza en "tiempo real".

Cuando una patita de E/S funciona como salida, el bit que proviene del bus de datos se
guarda en el biestable del dato con lo cual la información que ofrece esta patita permanece
invariable hasta que se reescriba otro bit.

Para configurar la patita como Entrada, hay que cargar un 1 en el biestable de control de
E/S mientras que hay que cargar un 0 si se desea que sea Salida.

Cada línea de E/S de los puertos se programa de forma independiente y puede ser Entrada
o Salida. Cuando se produce un reset, todos los bits de los registros TRIS pasan a tener el
valor 1 y todas las líneas de E/S actúan como Entrada por evidentes motivos de seguridad
para evitar daños irreparables.

Los puertos que contienen entradas y salidas necesitan una atención especial al escribir el
programa. Instrucciones como bsf y bcf comienzan leyendo el valor del puerto y
cargándolo en el registro W; allí ejecutan la puesta a 1 ó a 0 del bit seleccionado y, luego,
depositan el registro W en el puerto. También hay que tener en cuenta las modificaciones
que se produzcan en las patitas que son entrada y pasan a salida, pues pueden estar
presentes datos antiguos en el registro de salida del puerto al ser memorizados.

Hay que prestar mucha atención a las operaciones que, tras una lectura de un puerto. sigue
una escritura de la misma. Se debe dejar pasar un tiempo determinado para que se

20
estabilice el voltaje de las patitas. Insertando entre la lectura y la escritura una instrucción
NOP o cualquier otra que no implique a los puertos, se eliminan estos errores potenciales.

1.6. INTERRUPCIONES.

El sistema de interrupciones consiste en un mecanismo por el cual un evento interno o


externo, asíncrono respecto del programa, puede interrumpir la ejecución de éste
produciendo automáticamente un salto a una subrutina de atención, de manera que pueda
atender inmediatamente el evento, y retomar luego la ejecución del programa exactamente
en donde estaba en el momento de ser interrumpido.

Este mecanismo es muy útil por ejemplo para el manejo de timers o rutinas que deben
repetirse periódicamente (refresh de display, antirebote de teclado, etc.), detección de
pulsos externos, recepción de datos, etc.

Funcionamiento
Las interrupciones se comportan casi exactamente igual que las subrutinas. Desde el punto
de vista del control del programa, al producirse una interrupción se produce el mismo
efecto que ocurriría si el programa tuviese un CALL 0004h en el punto en que se produjo
la interrupción. En uno de los registros de control del sistema de interrupciones existe un
bit de habilitación general de interrupciones GIE, que debe ser programado en 1 para que
las interrupciones puedan actuar.
Al producirse una interrupción, este bit se borra automáticamente para evitar nuevas
interrupciones. La instrucción RETFIE que se utiliza al final de la rutina de interrupción,
es idéntica a un retorno de subrutina, salvo que además coloca en uno automáticamente el
bit GIE volviendo a habilitar las interrupciones. Dentro de la rutina de interrupción, el
programa deberá probar el estado de los flags de interrupción de cada una de las fuentes
habilitadas, para detectar cual fue la que causó la interrupción y así decidir que acción
tomar.

Lógica de interrupciones para los µcontroladores PIC16F8X

21
Fuentes.

La señal que produce la interrupción es en realidad una sola, que resulta de la combinación
de todas las fuentes posibles y de los bits de habilitación. Existen dos grupos de fuentes,
unas que se habilitan con solo colocar en uno el bit GIE, y otras que además necesitan que
este puesto a uno el bit PEIE. Además, cada fuente de interrupciones tiene su respectivo
bit de habilitación individual.
Las fuentes de interrupción varían con cada versión, y pueden ser por ejemplo:
· Interrupción externa por pin RB0/INT.
· Desborde del Timer 0 (TMR0).
· Cambio en el estado de los bits 4 a 7 del puerto B.
· Desborde del timer 1.
· Desborde del timer 2.
· Interrupción del capture/compare 1.
· Interrupción del capture/compare 2.
· transmisión o recepción de un carácter por la interface serie sincrónica.
· transmisión o recepción de un carácter por la interface serie asincrónica.
· Fin de conversión A/D.
· Lectura/escritura del puerto paralelo de comunicación con otros microprocesadores.
· Escritura de EEPROM finalizada.

22
TEMA 2: PROGRAMACIÓN EN ASM

2.1. INSTRUCCIONES ASM DEL PIC16F84


El microcontrolador PIC 16F84 tiene un total de 37 instrucciones de una sola palabra, y
además otras 28 instrucciones especiales que se corresponden con combinaciones de 2 ó 3
instrucciones simples.
Las instrucciones tienen letras relacionadas (parámetros) y que tienen una interpretación
diferenciada.
La letra “W” es el registro más importante que tiene el PIC y se le denomina
ACUMULADOR, ya que las operaciones pasan a través de él.
La letra “k” es un literal (valor numérico) que puede contener cualquier valor asignado por
el programador entre 0 y 255 (es el máximo que se puede representar con un byte).
La letra “f” es cualquier nombre dado a un registro.
La letra “d” indica en que lugar se almacenará el resultado de la instrucción (en el
acumulador “W” o en el registro “f”).

• Si d = 0, el resultado se almacenará en “W”.

• Si d = 1, el resultado se almacenará en “f”.

Instrucciones orientadas a los bytes


Mnemónico Parámetros Descripción Ciclos Banderas

ADDWF f, d Add W and f 1 C, DC, Z

ANDWF f, d AND W with f 1 Z

CLRF f Clear f 1 Z

CLRW - Clear W 1 Z

COMF f, d Complement f 1 Z

DECF f, d Decrement f 1 Z

DECFSZ f, d Decrement f, Skip if 0 1(2) None

INCF f, d Increment f 1 Z

INCFSZ f, d Increment f, Skip if 0 1(2) None

IORWF f, d Inclusive OR W with f 1 Z

MOVF f, d Move f 1 Z

MOVWF f Move W to f 1 None

NOP - No Operation 1 None

RLF f, d Rotate left f through carry 1 C

RRF f, d Rotate right f through carry 1 C

SUBWF f, d Subtract W from f 1 C, DC, Z

SWAPF f, d Swap nibbles in f 1 None

XORWF f, d Exclusive OR W with f 1 Z

23
Instrucciones orientadas a los bits
Mnemónico Parámetros Descripción Ciclos Banderas

BCF f, b Bit Clear f 1 None

BSF f, b Bit Set f 1 None

BTFSC f, b Bit Test f, Skip if Clear 1 (2) None

BTFSS f, b Bit Test f, Skip if Set 1 (2) None

Operaciones con literales y de control


Mnemónico Parámetros Descripción NroCic. Banderas

ADDLW k Add literal and W 1 C, DC, Z

ANDLW k AND literal with W 1 Z

CALL k Call subroutine 2

CLRWDT - Clear Watchdog Timer 1 TO,PD

GOTO k Go to address 2 None

IORLW k Inclusive OR literal with W 1 Z

MOVLW k Move literal to W 1 None

RETFIE - Return from interrupt 2 None

RETLW k Return with literal in W 2 None

RETURN - Return from Subroutine 2 None

SLEEP - Go into standby mode 1 TO,PD

SUBLW k Subtract W from literal 1 C, DC, Z

XORLW k Exclusive OR literal with W 1 Z

Las instrucciones TRIS y OPTION recomienda Microchip no utilizarlas, para


mantener la compatibilidad con el PIC16CXX. Aunque eso no significa que
tengamos que hacer caso y no usarlas.

Instrucciones Especiales
Operación
Mnemónico Parámetros Descripción Banderas
Equivalente

BTFSC 3,0
ADDCF f, d Add Carry to File
INCF f,d
Z

BTFSC 3,1
ADDDCF f, d Add Digit Carry to File
INCF f,d
Z

B K Branch GOTO k -
BTFSC 3,0
BC K Branch on Carry
GOTO k
-

BTFSC 3,1
BDC K Branch on Digit Carry
GOTO k
-

BTFSS 3,0
BNC K Branch on No Carry
GOTO k
-

BTFSS 3,1
BNDC K Branch on No Digit Carry
GOTO k
-

BTFSS 3,2
BNZ K Branch on No Zero
GOTO k
-

BTFSC 3,2
BZ K Branch on Zero
GOTO k
-

CLRC Clear Carry BCF 3,0 -

CLRDC Clear Digit Carry BCF 3,1 -

24
CLRZ Clear Zero BCF 3,2 -
BSF/BCF 0A,3 -
LCALL K Long CALL BSF/BCF 0A,4 -
CALL k -
BSF/BCF 0A,3 -
LGOTO K Long GOTO BSF/BCF 0A,4 -
GOTO k -
MOVFW F Move File to W MOVF f,0 Z
COMF f,1
NEGF f, d Negate File
INCF f,d
Z

SETC Set Carry BSF 3,0 -

SETDC Set Digit Carry BSF 3,1 -

SETZ Set Zero BSF 3,2 -

SKPC Skip on Carry BTFSS 3,0 -

SKPDC Skip on Digit Carry BTFSS 3,1 -

SKPNC Skip on No Carry BTFSC 3,0 -

SKPNDC Skip on No Digit Carry BTFSC 3,1 -

SKPNZ Skip on Non Zero BTFSC 3,2 -

SKPZ Skip on Zero BTFSS 3,2 -


BTFSC 3,0
SUBCF f,d Substract Carry from File
DECF f,d
Z

Substract Digit Carry from BTFSC 3,1


SUBDCF f,d
File DECF f,d
Z

TSTF f Test File MOVF f,1 Z

2.1.1. DE CONTROL Y MANEJO DE LITERALES.

INSTRUCCIÓN: ADDLW k (hex = 3E kk)


Descripción: Sumar al acumulador el valor k.
Operación: W=W+k
Esta instrucción suma un valor de un literal al contenido del
registro “W” y lo guarda en “W”.
Ejemplo: MOVLW 3 ; carga el acumulador W con el valor 3.
ADDLW 1 ; suma 1 al acumulador.
Al final el acumulador tendrá el valor 4.
Registro STATUS: Modifica los bits Z, DC y C.
• Z vale 1 si el resultado de la operación es 0.
• DC vale 1 si el resultado de la operación es un número
superior a 15.
• C vale 1 si el resultado de la operación es positivo o el bit 7
del registro que contiene el resultado vale 0. En caso
contrario C vale 0 (resultado negativo).

25
INSTRUCCIÓN: ANDLW k (hex = 39 kk)
Descripción: Operación lógica AND entre el acumulador W y el literal k
Operación: W = W AND k
Esta instrucción realiza una operación lógica AND entre el
contenido de “W” y k. El resultado se guarda siempre en el
acumulador “W”
Ejemplo: Si cargamos el acumulador con el binario 10101010B y hacemos
un AND con el binario 11110000B, nos quedará el resultado de la
operación en el acumulador “W”.
MOVLW 10101010B
ANDLW 11110000B
El resultado de la operación queda en “W” = 10100000B.

Explicación de la operación AND:


Tenemos 4 posibles combinaciones entre dos bits
0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1
Vemos que solamente en el caso de que ambos bits sean 1, el
resultado será 1. Esta instrucción compara dos bytes, bit a bit.
Registro STATUS: Modifica el bit Z.
• Z vale 1 si el resultado de la operación es 0.

INSTRUCCIÓN: CALL k (hex = 2k kk)


Descripción: Llama a una subrutina en la dirección k.
Operación: CALL k...RETURN PC+1.
Esta instrucción llama a un grupo de instrucciones (subrutina) que
comienzan en la dirección k, donde k puede ser un valor numérico
o una etiqueta. Siempre termina con la instrucción de retorno
(RETURN o RETLW).
Definición de subrutina: son un grupo de instrucciones que
forman un programa dentro del programa principal y que se
ejecutan cuando las llama el programa principal.
Utilidad: sirven para utilizarlas varias veces en cualquier parte del
programa, sin necesidad de tener que copiar las mismas
instrucciones, con el consiguiente ahorro de memoria.
Funcionamiento: cuando un programa ejecuta una instrucción
CALL, guarda en el stack el valor del registro PC+1 (PC =
Program Counter) de manera que al regresar de la subrutina
continúa con la instrucción siguiente recuperándola del stack,
ejecutando la instrucción de retorno RETURN o RETLW.

Limitaciones: en el PIC16F84 tenemos disponibles 8 niveles de


stack, por lo que el número máximo de CALL reentrantes
(instrucciones CALL que contengan otra instrucción CALL)
queda limitado a 8.

26
Ejemplo: PRINCIPAL: etiqueta que identifica una dirección de memoria.
RETARDO: etiqueta que identifica el comienzo de una subrutina.
BUCLE: etiqueta que identifica una dirección de memoria.

PRINCIPAL CALL RETARDO


BTFSC PORTB, RB0
GOTO PRINCIPAL
*
*
*
RETARDO CLRF CONTADOR
BUCLE DECFSZ CONTADOR, 1
GOTO BUCLE
RETURN

En este listado vemos que la subrutina RETARDO salta a un


grupo de instrucciones que forman un bucle y cuando éste termina
regresa para seguir con la instrucción siguiente al salto (BTFSC...).
Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: CLRWDT (hex = 00 64)


Descripción: Pone el temporizador WDT a cero.
Operación: WDT = 0
Esta instrucción se utiliza cuando programamos el PIC con la
opción Watch Dog habilitada. Para evitar el reset del PIC, el
programa debe contener cíclicamente la instrucción CLRWDT
para ponerlo a cero. Si no se pone a cero a tiempo, el WDT
interpretará que se ha bloqueado el programa y ejecutará un reset
para desbloquearlo.
Ejemplo: Bucle CLRWDT
*
*
*
GOTO Bucle

Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: GOTO k (hex = 28 kk)


Descripción: Salto incondicional a k.
Operación: Salto k
Esta instrucción ejecuta un salto del programa a la dirección k. El
parámetro “k” puede ser un valor numérico o una etiqueta.

27
Ejemplo: INSTRUCCIÓN 1
GOTO ABAJO
INSTRUCCIÓN 3
INSTRUCCIÓN 4
INSTRUCCIÓN 5
ABAJO INSTRUCCIÓN 6

Primero se ejecuta la instrucción 1, después GOTO y continúa con


la instrucción 6 saltándose las instrucciones 3, 4 y 5.
Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: IORLW k (hex = 38 kk)


Descripción: Operación lógica OR entre el acumulador W y el literal k
Operación: W = W OR k
Esta instrucción realiza un OR inclusivo entre el contenido del
acumulador “W” y el literal k. El resultado se guarda siempre en
el acumulador (“k” es un valor, no un registro).
Ejemplo: Si cargamos el acumulador con el binario 11110000B y k=
00001111B, nos quedará el resultado de la operación en el
acumulador “W”.
MOVLW 11110000B
IORLW 00001111B
El resultado de la operación queda en “W” = 11111111B.

Explicación de la operación OR:


Tenemos 4 posibles combinaciones entre dos bits
0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1
Vemos que solamente en el caso de que ambos bits sean 0, el
resultado será 0. Esta instrucción compara dos bytes, bit a bit.
Registro STATUS: Modifica el bit Z.
• Z vale 1 si el resultado de la operación es 0.

INSTRUCCIÓN: MOVLW k (hex = 30 kk)


Descripción: Mover el literal k al acumulador.
Operación: W=k
Esta instrucción asigna al acumulador “W” el valor del literal k
(entre 0 y 255).
Ejemplo: Si tenemos el acumulador a cero o con cualquier valor, y
queremos que contenga el que le asignemos nosotros directamente

28
entonces usaremos esta instrucción:
W = 0.
Valor a asignar = 100.
Instrucción: MOVLW 100
El acumulador valdrá 100 (W = 100).

Con distinto valor de partida del acumulador:


W = 225.
MOVLW 100
El acumulador valdrá 100 (W = 100).
Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: OPTION (hex = 00 62)


Descripción: Guarda el valor del acumulador en el registro OPTION.
Operación: OPTION = W.
Esta instrucción guarda en el registro especial OPTION el valor
contenido en el acumulador “W”.
Ejemplo: MOVLW 10H ; carga el acumulador con el valor 10H.
OPTION ; carga el registro OPTION con el acumulador.

Esta instrucción existe para mantener la compatibilidad con los


PIC producidos con anterioridad, y como en el futuro podría dejar
de implementarse, Microchip aconseja realizar el ejemplo anterior
de esta otra forma:
BSF STATUS, RP0 ; activa el banco 1.
MOVLW 10H ; carga el acumulador con 10H.
MOVWF OPTION_REG ; carga OPTION con el acumulador.

Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: RETFIE (hex = 00 09)


Descripción: Retorna de una interrupción.
Operación: FIN INTERRUPCION.
Esta instrucción devuelve el control al programa principal después
de ejecutarse una subrutina de gestión de interrupción.
Ejemplo: ORG 00H
BUCLE GOTO BUCLE ; bucle infinito.
ORG 04H; vector de interrupción.
RETFIE ; retorna de la interrupción

Este código de programa ejecuta un bucle infinito. Si habilitamos


una de las interrupciones del 16F84, en cuanto ésta se produzca
pasará el control al programa situado en la dirección 04H y la

29
instrucción RETFIE regresa de la interrupción.
Al ejecutarse una interrupción, el bit GIE del registro INTCON se
pone a 0 y así evita que otra interrupción se produzca mientras ya
está con una en marcha.
Con la instrucción RETFIE ponemos de nuevo el bit GIE a 1 para
así atender de nuevo a futuras interrupciones.
Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: RETLW (hex = 34 kk)


Descripción: Retorno de subrutina y carga literal k en el acumulador.
Operación: RETORNO con W = k.
Esta instrucción retorna de una subrutina al programa principal,
cargando el acumulador “W” con el literal “k”.
Es la última instrucción que forma una subrutina (al igual que
RETURN).
¿Y para qué me sirve regresar de una subrutina con un
determinado literal en el acumulador? Nos será muy útil al
programar con TABLAS.
Ejemplo: CALL SUBRUT1 ; llama a Subrut1.
MOVWF DATO 1 ; carga W en Dato1.
CALL SUBRUT2 ; llama a Subrut2.
MOVWF DATO2 ; carga W en Dato2.
*
*
SUBRUT1 RETLW 0A ; carga W = 0A y retorna.
SUBRUT2 RETLW 0B ; carga W = 0B y retorna.

Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: RETURN (hex = 00 08)


Descripción: Retorno de subrutina.
Operación: RETORNO.
Esta instrucción retorna de una subrutina al programa principal en
la instrucción siguiente a la llamada de la subrutina, tomando el
valor almacenado en el stack para continuar.
Es la última instrucción que forma una subrutina (al igual que
RETLW).

Ejemplo: CALL COMPARA ; llama a Compara.


INSTRUCCION1
INSTRUCCION2
*
*

30
COMPARA INSTRUCCIÓN R1
INSTRUCCIÓN R2
RETURN

Aquí llamamos a la subrutina COMPARA, se ejecutan las


instrucciones R1 y R2 y con el RETURN regresa a la instrucción
siguiente al CALL y ejecuta las instrucciones 1, 2 y sigue con el
programa.
Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: SLEEP (hex = 00 63)


Descripción: En modo reposo..
Operación: EN ESPERA.
Esta instrucción detiene la ejecución del programa y deja el PIC en
modo suspendido.
No ejecuta ninguna instrucción hasta que sea nuevamente
reinicializado (reset).
Durante este modo, el contador del Watch Dog (WDT) sigue
trabajando, y si lo tenemos activado el PIC se reseteará por este
medio.
El consumo de energía es mínimo.
Ejemplo:
Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: SUBLW k (hex = 3C kk)


Descripción: Resta al literal k el valor del acumulador.
Operación: W=k-W
Esta instrucción resta al literal k el valor almacenado en el
acumulador “W” y el resultado se guarda en el acumulador.
Ejemplo: MOVLW 10 ; carga el acumulador W con el valor 10.
SUBLW 15 ; resta a 15 el valor del acumulador.
Al final el acumulador tendrá el valor W = 5.
Registro STATUS: Modifica los bits Z, DC y C.
• Z vale 1 si el resultado de la operación es 0.
• DC vale 1 si el resultado de la operación es un número
superior a 15.
• C vale 1 si el resultado de la operación es positivo o el bit 7
del registro que contiene el resultado vale 0. En caso
contrario C vale 0 (resultado negativo).

31
INSTRUCCIÓN: TRIS f (hex = 00 6F)
Descripción: Guarda el acumulador en uno de los registros de TRIS.
Operación: TRIS de f = W.
Esta instrucción guarda el valor del acumulador “W” en uno de los
registros especiales de TRIS que indicamos en el parámetro “f”.
Los registros TRIS determinan el funcionamiento como entrada y
salida de las líneas I/O del PIC.
Ejemplo: MOVLW 16H ; carga el acumulador W con el valor 16H.
TRIS PORTA ; carga el registro PORTA con el acumulador.

Esta instrucción existe para mantener la compatibilidad con los


PIC producidos anteriormente,y como en el futuro podría dejar de
implementarse, Microchip aconseja realizar el ejemplo anterior de
esta otra forma (aunque ocupa más memoria...):
BSF STATUS, RP0 ; activa el banco 1.
MOVLW 16H ; carga el acumulador con el valor 16H
MOVWF TRISA ; carga el registro PORTA con W.
Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: XORLW k (hex = 3A kk)


Descripción: Operación lógica OR exclusivo entre el acumulador y el literal k
Operación: W = W XOR k
Esta instrucción realiza un OR exclusivo entre el contenido del
acumulador “W” y el valor del literal k. El resultado se guarda
siempre en el acumulador (k es un literal, no un registro).
Ejemplo: Si cargamos el acumulador con el binario 11110000B y hacemos
un XOR con el binario 10101010B, nos quedará el resultado de la
operación en el acumulador “W”.
MOVLW 11110000B
XORLW 10101010B
El resultado de la operación queda en “W” = 01011010B.

Explicación de la operación XOR:


Tenemos 4 posibles combinaciones entre dos bits
0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0
Vemos que si ambos bits son iguales el resultado será 0. Y si
ambos son diferentes, el resultado será 1. Esta instrucción compara
dos bytes, bit a bit.
Registro STATUS: Modifica el bit Z.
• Z vale 1 si el resultado de la operación es 0.

32
2.1.2.ORIENTADAS A REGISTROS.

INSTRUCCIÓN: ADDWF f,d (hex = 07 ff)


Descripción: Suma el acumulador y el registro “f”.
Operación: d = W + f (d puede ser W ó f).
Esta instrucción suma el contenido del acumulador con el registro
“f”, y el resultado se guarda dependiendo del valor de “d”.
Si d = 0, se guarda en “W”.
Si d = 1, se guarda en “f”.
Si no se pone nada, el valor por defecto es 1 y se guarda en “f”.
También se puede indicar directamente “W” ó “f”.
Ejemplo: Si tomamos como valores iniciales W = 5 y DATO = 10.
ADDWF DATO ; DATO = 15 y W = 5.
ADDWF DATO, 1; DATO = 15 y W = 5.
ADDWF DATO, 0; W = 15 y DATO = 10.
ADDWF DATO, W; W = 15 y DATO = 10.
Registro STATUS: Modifica los bits Z, DC y C.
• Z vale 1 si el resultado de la operación es 0.
• DC vale 1 si el resultado de la operación es un número
superior a 15.
• C vale 1 si el resultado de la operación es positivo o el bit 7
del registro que contiene el resultado vale 0. En caso
contrario C vale 0 (resultado negativo).

INSTRUCCIÓN: ANDWF f,d (hex = 05 ff)


Descripción: Operación lógica AND entre el acumulador y el registro f.
Operación: d = W AND f (d puede ser “W” o “f”).
Esta instrucción realiza una operación lógica AND entre el
contenido del acumulador “W” y el registro “f”. El resultado se
guarda según sea el valor de “d”.
Si d = 0, se guarda en “W”.
Si d = 1, se guarda en “f”.
Si no se pone nada, el valor por defecto es 1 y se guarda en “f”.
También se puede indicar directamente “W” ó “f”.
Ejemplo: Si queremos extraer los 4 bits menos significativos de un registro,
podremos utilizar una máscara para obtenerlos.
Usamos el valor 00001111B para realizar la operación ADN y así
obtenerlos (para los 4 bits más significativos utilizaríamos el valor
11110000B).
Cargamos el valor del cual queremos extraer los 4 bits menos
significativos en “f” (f = 10101010B).
Cargamos el acumulador con la máscara (W = 00001111B).
ANDWF f,W
El resultado queda guardado en el acumulador (W = 00001010B).

33
Explicación de la operación AND:
Tenemos 4 posibles combinaciones entre dos bits
0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1
Vemos que solamente en el caso de que ambos bits sean 1, el
resultado será 1. Esta instrucción compara dos bytes, bit a bit.
Registro STATUS: Modifica el bit Z.
• Z vale 1 si el resultado de la operación es 0.

INSTRUCCIÓN: CLRF f (hex = 01 8f)


Descripción: Pone a cero el registro “f”.
Operación: F = 0.
Esta instrucción pone a cero el valor contenido en el registro
direccionado por el parámetro “f”. Puede decirse que borra el
registro “f”.
Ejemplo: Si queremos poner a cero el registro TMR0, cuya dirección es
01H, tendríamos que utilizar
CLRF 01H
Si hemos incluido al inicio del código fuente el fichero
PIC16F84.INC, podemos utilizar el nombre simbólico de dicho
registro:
CLRF TMR0
Registro STATUS: Modifica el bit Z y lo pone a 1 (ya que el resultado de la operación
es 0).

INSTRUCCIÓN: CLRW (hex = 01 00)


Descripción: Pone el acumulador a cero.
Operación: W=0
Esta instrucción pone a cero el valor contenido en el registro “W”
(acumulador)
Ejemplo: No es necesario…

Registro STATUS: Modifica el bit Z y lo pone a 1 (ya que el resultado de la operación


es 0).

INSTRUCCIÓN: COMF f,d (hex = 09 ff)


Descripción: Complementa el registro “f”.
Operación: d = NOT f (d puede ser “W” ó “f”).
Esta instrucción efectúa el complemento del valor contenido en el
registro direccionado por el parámetro “f”.

34
La operación de complementar consiste en invertir los bits: poner
los ceros a unos y los unos a ceros.
El parámetro “d” determina el destino del valor obtenido.
Si d = “W”, el resultado se almacena en el acumulador.
Si d = “f”, el resultado se almacena en el propio registro “f”.
Ejemplo: Si tenemos en PORTA el valor 00001111B, al ejecutar:
COMF PORTA
El resultado será PORTA = 11110000B.
Si aplicamos para el mismo valor inicial, la instrucción:
COMF PORTA, W
El resultado será W = 11110000B y PORTA = 00001111B.
Registro STATUS: Modifica el bit Z.
• Z vale 1 si el resultado de la operación es 0.

INSTRUCCIÓN: DECF f,d (hex = 03 ff)


Descripción: Decrementa en 1 el registro “f”.
Operación: d = f – 1 (d puede ser “W” ó “f”).
Esta instrucción decrementa en uno el contenido del registro
direccionado por el parámetro “f”. El parámetro “d” determina el
destino del valor obtenido.
Si d = “W”, el resultado se almacena en el acumulador.
Si d = “f”, el resultado se almacena en el propio registro “f”.
Ejemplo: Si tenemos un registro DIA = 7.
Aplicando la instrucción
DECF DIA, 0, tendremos W = 6 y DIA = 7.
Si aplicamos esta otra
DECF DIA, 1, tendremos DIA = 6.
Registro STATUS: Modifica el bit Z.
• Z vale 1 si el resultado de la operación es 0.

INSTRUCCIÓN: DECFSZ f,d (hex = 0B ff)


Descripción: Decrementa en 1 a “f”, y si “f” = 0 salta la siguiente instrucción.
Operación: d = f – 1, si d = 0 SALTA (d puede ser “W” ó “f”).
Esta instrucción decrementa el contenido del registro direccionado
por el parámetro “f”, y si el resultado es 0 se salta la instrucción
siguiente. El parámetro “d” determina el destino del valor
obtenido.
Si d = “W”, el resultado se almacena en el acumulador.
Si d = “f”, el resultado se almacena en el propio registro “f”.
Ejemplo: DECFSZ VALOR, W
INSTRUCCION 1
INSTRUCCIÓN 2

Si el contenido del registro “VALOR” al decrementarlo en 1 es

35
igual a 0, se guarda el resultado en el acumulador y sigue con la
INSTRUCCION2, saltándose la INSTRUCCION1.
Si el resultado que guardamos en W no es 0, sigue con la
INSTRUCCION1 y después con la INSTRUCCION2 (no se salta
la inmediata siguiente).
Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: INCF f,d (hex = 0A ff)


Descripción: Incrementa en 1 el registro “f”.
Operación: d = f + 1 (d puede ser “W” ó “f”).
Esta instrucción incrementa en uno el contenido del registro
direccionado por el parámetro “f”. El parámetro “d” determina el
destino del valor obtenido.
Si d = “W”, el resultado se almacena en el acumulador.
Si d = “f”, el resultado se almacena en el propio registro “f”.
Ejemplo: Si tenemos un registro DIA = 7.
Aplicando la instrucción
INCF DIA, 0, tendremos W = 8 y DIA = 7.
Si aplicamos esta otra
INCF DIA, 1, tendremos DIA = 8.
Registro STATUS: Modifica el bit Z.
• Z vale 1 si el resultado de la operación es 0.

INSTRUCCIÓN: INCFSZ f,d (hex = 0F ff)


Descripción: Incrementa en 1 a “f”, y si “f” = 0 salta la siguiente instrucción
Operación: d = f + 1, si d = 0 SALTA (d puede ser “W” ó “f”).
Esta instrucción incrementa en uno el contenido del registro
direccionado por el parámetro “f”, y si el resultado es 0 se salta la
instrucción siguiente. El parámetro “d” determina el destino del
valor obtenido.
Si d = “W”, el resultado se almacena en el acumulador.
Si d = “f”, el resultado se almacena en el propio registro “f”.
Ejemplo: INCFSZ VALOR, W
INSTRUCCION 1
INSTRUCCIÓN 2

Si el contenido del registro “VALOR” al incrementarlo en 1 es


igual a 0, se guarda el resultado en el acumulador y sigue con la
INSTRUCCION2, saltándose la INSTRUCCION1.
Si el resultado que guardamos en W no es 0, sigue con la
INSTRUCCION1 y después con la INSTRUCCION2 (no se salta
la inmediata siguiente).
Registro STATUS: No modifica ningún bit de estado.

36
INSTRUCCIÓN: IORWF f,d (hex = 04 ff)
Descripción: Operación lógica OR inclusivo entre el acumulador y un registro
Operación: d = W OR f (d puede ser “W” ó “f”).
Esta instrucción realiza una operación lógica OR inclusivo entre
el acumulador “W” y el registro direccionado por el parámetro “f”.
El parámetro “d” determina donde se almacenará el resultado de la
operación.
Si d = 0, se guarda en “W”.
Si d = 1, se guarda en “f”.
Si no se pone nada, el valor por defecto es 1 y se guarda en “f”.
También se puede indicar directamente “W” ó “f”.
Ejemplo: Si asignamos los valores W = 10101010B y f = 01010101B.
IORWF f ; W = 10101010B y f = 11111111B.
IORWF f, 1; W = 10101010B y f = 11111111B.
IORWF f, 0; f = 01010101B y W = 11111111B.
IORWF f, W; f = 01010101B y W = 11111111B.
Registro STATUS: Modifica el bit Z.
• Z vale 1 si el resultado de la operación es 0.

INSTRUCCIÓN: MOVF f,d, (hex = 08 ff)


Descripción: Mueve el contenido de “f” al acumulador o al propio registro “f”.
Operación: d = f (d puede ser “W” ó “f”).
Esta instrucción copia el contenido del registro direccionado por el
parámetro “f” en el acumulador “W” o en el mismo registro “f”. El
parámetro “d” determina el destino.
Si d = 0, se guarda en “W”.
Si d = 1, se guarda en “f”.
Si no se pone nada, el valor por defecto es 1 y se guarda en “f”.
También se puede indicar directamente “W” ó “f”.

El motivo para copiar el contenido de un registro sobre si mismo,


no es otro que poder comprobar en el registro STATUS el estado
del bit Z.
Ejemplo: Si tenemos el registro EDAD = 38. ;)
MOVF EDAD, 0 ; hace que W = 38.
MOVF EDAD, 1 ; hace que EDAD = 38.
MOVF EDAD, W ; hace que W = 38.
MOVF EDAD ; hace que EDAD = 38.
Registro STATUS: Modifica el bit Z.
• Z vale 1 si el resultado de la operación es 0.

37
INSTRUCCIÓN: MOVWF f (hex = 00 8f)
Descripción: Mueve el contenido de “W” al registro “f”.
Operación: f = W.
Esta instrucción copia el contenido del acumulador “W” en el
registro direccionado por el parámetro “f”.
Ejemplo: Si queremos escribir el valor 10H en el registro TMR0, que está
situado en la dirección 01H, tendremos que cargar primero el valor
en el acumulador y después copiarlo al registro.
MOVWF 10H ; cargar el valor 10H en el acumulador.
MOVWF 01H ; copia el acumulador en la dirección 01H.

Con los registros utilizados por el PIC para funciones específicas,


es habitual no escribir directamente su dirección, sino el nombre
simbólico definido en el fichero PIC16F84.INC. En el ejemplo
anterior nos quedaría así:
MOVWF 10H ; cargar el valor 10H en el acumulador.
MOVWF TMR0 ; copia el acumulador en el registro TMR0.
Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: NOP (hex = 00 00)


Descripción: No opera.
Operación: ------
Esta instrucción no realiza ninguna función específica, pero
consume 4 ciclos de reloj completos. Es útil para insertar un
retardo igual a un ciclo de máquina.
Ejemplo: Utilizando un cristal de cuarzo de 4 Mhz. en el oscilador,
podremos obtener un retardo igual a un microsegundo por cada
instrucción NOP que insertemos en el código del programa:
RETARDO NOP
NOP
NOP
RETURN
Cada vez que llamemos a la subrutina RETARDO, obtendremos 3
microsegundos de demora.
Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: RLF f,d (hex = 0D ff)


Descripción: Rota a la izquierda el registro “f”.
Operación: d = << 1 (d puede ser “W” ó “f”).
Esta instrucción rota a la izquierda todos los bits del registro
direccionado en el parámetro “f” pasando por el bit CARRY del
registro STATUS (o si se prefiere, desde los bits menos

38
significativos a los más significativos).
Es como si multiplicáramos por dos el contenido del registro.
Veamos el registro “f” de forma gráfica:

El D7 pasa al CARRY del registro STATUS, el contenido del


CARRY pasa al D0, el D0 al D1, etc.
El parámetro “d” determina el destino.
Si d = 0, se guarda en “W”.
Si d = 1, se guarda en “f”.
Si no se pone nada, el valor por defecto es 1 y se guarda en “f”.
También se puede indicar directamente “W” ó “f”.
Ejemplo: Si tenemos el registro VALOR = 00000001B y aplicamos la
instrucción
RLF VALOR
el resultado será VALOR = 00000010B y el bit C = 0.
Si tenemos el registro VALOR = 10000000B y aplicamos la
instrucción
RLF VALOR
El resultado será VALOR = 00000000B y el bit C = 1.
Registro STATUS: Modifica el bit C (CARRY).

INSTRUCCIÓN: RRF f,d (hex = 0C ff)


Descripción: Rota a la derecha el registro “f”.
Operación: d = f >> 1 (d puede ser “W” ó “f”).
Esta instrucción rota a la derecha todos los bits del registro
direccionado en el parámetro “f” pasando por el bit CARRY del
registro STATUS (o si se prefiere, desde los bits más
significativos a los menos significativos).
Es como si dividiéramos por dos el contenido del registro.
Veamos el registro “f” de forma gráfica:

El bit C del registro STATUS pasa al D7, el D0 pasa al bit C, el


D1 al D0, etc.
El parámetro “d” determina el destino.
Si d = 0, se guarda en “W”.
Si d = 1, se guarda en “f”.
Si no se pone nada, el valor por defecto es 1 y se guarda en “f”.
También se puede indicar directamente “W” ó “f”.
Ejemplo: Si tenemos el registro VALOR = 00000001B y aplicamos la
instrucción

39
RRF VALOR
el resultado será VALOR = 00000000B y el bit C = 1.
Si tenemos el registro VALOR = 10000000B y aplicamos la
instrucción
RRF VALOR
El resultado será VALOR = 01000000B y el bit C = 0.
Registro STATUS: Modifica el bit C (CARRY).

INSTRUCCIÓN: SUBWF f,d (hex = 02 ff)


Descripción: Resta el acumulador del registro “f”.
Operación: d = f – W (d puede ser “W” ó “f”).
Esta instrucción resta el valor contenido en el acumulador “W” del
valor contenido en el registro direccionado por el parámetro “f”.
El parámetro “d” determina el destino.
Si d = 0, se guarda en “W”.
Si d = 1, se guarda en “f”.
Si no se pone nada, el valor por defecto es 1 y se guarda en “f”.
También se puede indicar directamente “W” ó “f”.
Ejemplo: Según sean los valores de W y el registro DATO, si aplicamos
SUBWF DATO, obtendremos diferentes resultados en el bit
CARRY.
Si DATO = 3 y W = 2; el resultado será DATO = 1 y C = 1.
Si DATO = 2 y W = 2; el resultado será DATO = 0 y C = 1.
Si DATO = 1 y W = 2; el resultado será DATO = FFH y C = 0.
Vemos que C = 1 porque el resultado es positivo y C = 0 cuando el
resultado es negativo.
Registro STATUS: Modifica los bits Z, DC y C.
• Z vale 1 si el resultado de la operación es 0.
• DC vale 1 si el resultado de la operación es un número
superior a 15.
C vale 1 si el resultado de la operación es positivo o el bit 7 del
registro que contiene el resultado vale 0. En caso contrario C vale
0 (resultado negativo).

INSTRUCCIÓN: SWAPF f,d (hex = 0E ff)


Descripción: Intercambia los 4 bits más significativos con los 4 menos (nibbles)
Operación: f = 0123 SWAP 4567 de “f”.
Esta instrucción intercambia el valor de los 4 bits más
significativos (D7-D4) contenidos en el registro direccionado por
el parámetro “f”, con los 4 bits menos significativos (D3-D0) del
mismo. El parámetro “d” determina el destino.
Si d = 0, se guarda en “W”.
Si d = 1, se guarda en “f”.
Si no se pone nada, el valor por defecto es 1 y se guarda en “f”.

40
También se puede indicar directamente “W” ó “f”.
Ejemplo: Si tenemos VALOR = 00001111B y W = 00000000B, al aplicar
SWAPF VALOR ; VALOR = 11110000B y W = 00000000B.
SWAPF VALOR, W; VALOR = 00001111B y W = 11110000B.
Con la primera instrucción modificamos el valor del registro
DATO, y en la segunda instrucción modificamos el valor del
acumulador sin que varíe el registro DATO
.
Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: XORWF f,d (hex = 06 ff)


Descripción: Operación lógica XOR entre el acumulador y “f”.
Operación: Dd = f XOR W (d puede ser “W” ó “f”).
Esta instrucción efectúa la operación lógica XOR (OR exclusivo)
entre el valor contenido en el acumulador “W” y el valor
contenido en el registro direccionado por el parámetro “f”. El
parámetro “d” determina el destino.
Si d = 0, se guarda en “W”.
Si d = 1, se guarda en “f”.
Si no se pone nada, el valor por defecto es 1 y se guarda en “f”.
También se puede indicar directamente “W” ó “f”.
Ejemplo: Si tenemos el registro VALOR = 11110000B y W = 11111111B,
al aplicar la instrucción
XORWF VALOR ; hace VALOR = 00001111B y W no varía.
XORWF VALOR, W ; hace W = 00001111B y VALOR no varía.
Explicación de la operación XOR:
Tenemos 4 posibles combinaciones entre dos bits
0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0
Vemos que si ambos bits son iguales el resultado será 0. Y si
ambos son diferentes, el resultado será 1. Esta instrucción compara
dos bytes, bit a bit.
Esto nos sirve para comparar dos valores y comprobar si son
iguales o no.
Supongamos que tenemos el registro NUMERO y queremos
comprobar si es igual a 26H.
Tendríamos que efectuar las siguientes instrucciones:
MOVLW 26H; carga el acumulador con el valor a comparar.
XORWF NUMERO, W ; compara el acumulador con NUMERO.
BTFSS STATUS, Z ; salta la instrucción siguiente si XOR = 0.
GOTO DISTINTO ; salta a DISTINTO si XOR no es 0.
GOTO IGUAL ; salta a IGUAL.
Registro STATUS: Modifica el bit Z.
Z vale 1 si el resultado de la operación es 0.

41
2.1.3. ORIENTADAS A BITS.

INSTRUCCIÓN: BCF f,b (hex = 1B ff)


Descripción: Pone a cero el bit “b” del registro “f”.
Operación: F(b) = 0
Esta instrucción pone a cero un bit que hayamos elegido de un
registro determinado.
Ejemplo: BCF PORTA, RA4 ; pone a 0 el bit RA4 del registro PORTA
BCF PORTA, 4 ; igual, si no conocemos en nombre del bit

Si en el PORTA tenemos como valor inicial 11111111B, después


de aplicar el ejemplo anterior, PORTA = 11101111B.
Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: BSF f,b (hex = 1B ff)


Descripción: Pone a uno el bit “b” del registro “f”.
Operación: F(b) = 1
Esta instrucción pone a uno un bit que hayamos elegido de un
registro determinado.
Ejemplo: BSF PORTA, RA0 ; pone a 1 el bit RA0 del registro PORTA
BSF PORTA, 0 ; igual, si no conocemos en nombre del bit

Si en el PORTA tenemos como valor inicial 00000000B, después


de aplicar el ejemplo anterior, PORTA = 00000001B.
Registro STATUS: No modifica ningún bit de estado.

INSTRUCCIÓN: BTFSC f,b (hex = 1B ff)


Descripción: Comprueba un bit “b” del registro “f” y se salta la instrucción
siguiente si vale 0.
Operación: F(b) = 0 ? SI, salta una instrucción
Esta instrucción comprueba el valor del bit “b” en el registro “f”, y
si “b” = 0 entonces se salta la siguiente instrucción.
Si “b” = 1 no salta y sigue con su ejecución normal.
Ejemplo: BTFSC PORTA, 2
INSTRUCCIÓN 1
INSTRUCCIÓN 2

Si en PORTA tenemos como valor inicial 11111011B, el


programa continúa con la instrucción 2, saltándose la instrucción 1
Si en PORTA tenemos el valor 00000100B, el programa sigue con
la instrucción 1 y después la instrucción 2.
Registro STATUS: No modifica ningún bit de estado.

42
INSTRUCCIÓN: BTFSS f,b (hex = 1B ff)
Descripción: Comprueba un bit “b” del registro “f” y se salta la instrucción
siguiente si vale 1.
Operación: F(b) = 1 ? SI, salta una instrucción.
Esta instrucción comprueba el valor del bit “b” en el registro “f”, y
si “b” = 1 entonces se salta la siguiente instrucción.
Si “b” = 0 no salta y sigue con su ejecución normal.
Ejemplo: BTFSS PORTB, 7
INSTRUCCIÓN 1
INSTRUCCIÓN 2

Si en PORTB tenemos como valor inicial 10000000B, el programa


continúa con la instrucción 2, saltándose la instrucción 1.
Si en PORTB tenemos el valor 01111111B, el programa sigue con
la instrucción 1 y después la instrucción 2.
Registro STATUS: No modifica ningún bit de estado.

43
2.2 MODOS DE DIRECCIONAMIENTO

Direccionamiento directo: la memoria de datos (RAM).


La memoria interna se direcciona en forma directa por medio de los 8 bits “f” contenidos
en las instrucciones que operan sobre registros. De esta manera se puede direccionar
cualquier posición desde la 00 a la FF.
En los microcontroladores que tengan más de un banco, antes de acceder a alguna variable
que se encuentre en la zona de los bancos de registros, el programador deberá asegurarse
de haber programado los bits de selección de banco en el registro FSR.

Direccionamiento indirecto: el registro FSR.

El registro FSR sirve como puntero para direccionamiento indirecto además de servir para
seleccionar el banco activo. La posición 00 del mapa de RAM es la llamada dirección
indirecta.
Si en cualquier instrucción se opera con la dirección 00, en realidad se estará operando con
la dirección a donde apunte el contenido del FSR.
Por ejemplo si el FSR contiene el valor 1Ah, una instrucción que opere sobre la dirección
0, en realidad lo hará sobre la dirección 1Ah. Puede decirse que la posición 1Ah de
memoria fue direccionada en forma indirecta a través del puntero FSR.

Ejemplo :
; Este programa borra 8 posiciones de memoria a partir de la dirección 1A
FSR equ 04 ;(definición del puntero FSR)
;.............................................................................................

movlw 8 ;prepara para repetir 8 veces


movwf DATO ;(el registro DATO es el contador del bucle)
movlw 1Ah ;apunta a la dirección 1Ah
movwf FSR ;guarda en FSR la dirección 1Ah
bucle clrf 0 ;borra una posición de memoria (pone a cero)
incf FSR ;apunta a la siguiente
decfsz DATO ;si todavía no borró todas (aún no es cero)
goto bucle ;salta a bucle (sigue borrando)

El direccionamiento indirecto es muy útil para procesar posiciones consecutivas de


memoria (como en el ejemplo) o para el direccionamiento de datos en subrutinas.

Direccionamiento inmediato:
El dato utilizado por la instrucción se codifica al mismo tiempo que la propia instrucción.
En este caso, al dato se le denomina “literal”.

44
Direccionamiento relativo:
No existe este modo de direccionamiento en los microprocesadores PIC.

2.3 TECNICAS DE PROGRAMACION


SUBRUTINAS Y LLAMADAS:
La mayoría de los microcontroladores incluyen en su repertorio de instrucciones algunas
que permiten saltar a una rutina y, cuando se completa su ejecución, retornar al programa
principal.
El empleo de subrutinas aporta muchas ventajas entre las que se destacan las siguientes:

1. Se pueden escribir como subrutinas secciones de código y ser empleadas en muchos


programas ( por ejemplo, la subrutina de exploración de un teclado ).
2. Dan a los programas un carácter modular, es decir, se pueden codificar diferentes
módulos para utilizarlos en cualquier programa.
3. Se reduce notablemente el tiempo de programación y la detección de errores,
utilizando repetidamente una subrutina.
4. El código es más fácil de interpretar, dado que las instrucciones de las subrutinas no
aparecen en el programa principal, sólo figuran las llamadas (CALL).

LAS INSTRUCCIONES CALL Y RETURN:


La instrucción CALL (llamada a subrutina) consigue que la ejecución del programa
continúe en la dirección donde se encuentra la subrutina a la que hace referencia. Es
similar a GOTO pero coloca en la pila la dirección de la siguiente instrucción que se debe
ejecutar después de terminar con la subrutina.
La subrutina finaliza con la instrucción RETURN (retorno de la subrutina) que retoma la
dirección guardada en la pila y la coloca en el contador del programa PC continuando el
flujo de control con la instrucción que sigue a CALL.
En la familia PIC de gama media la pila tiene ocho niveles de memoria del tipo LIFO
(último en entrar, primero en salir). Si se produce la llamada a una subrutina durante la
ejecución de otra subrutina, la dirección de retorno de esta segunda es colocada en la cima
de la pila sobre la dirección anterior. Esta segunda dirección es la primera en salir de la pila
mediante la instrucción RETURN.
Con la pila de ocho niveles, una subrutina puede llamar a otra y ésta, a su vez, llamar a otra
hasta un máximo de ocho.

45
CONSULTA A TABLAS:
En muchas ocasiones es necesario para un programador efectuar una coincidencia entre
alguna cantidad de valores conocidos y un número desconocido que se tiene como índice.
Por ejemplo: basados en el contenido de una posición de memoria RAM (índice) se puede
obtener de una serie consecutiva de datos almacenados en memoria de programa (a los
datos "conocidos" almacenados se le denomina tabla), el dato desplazado n posiciones
hacia delante desde el comienzo de la tabla. El número n corresponde al contenido de la
posición de memoria RAM ó índice.
Programa ejemplo:
offset equ 0Ch ; posición de memoria RAM
w equ 0 ; destino W
f equ 1 ; destino F
................
................
................
movf offset,w ; tomamos a W el número n utilizado como índice
call tabla ; posición en donde se encuentra la serie de datos
; en este sitio luego del retorno de la subrutina se tiene en W el dato
; leído de la tabla
................
................
................
tabla
addwf PCL,f ; se suma al PC el W obteniendo como resultado un salto indexado
retlw 30h ; sí W sumado al PCL es 0 se retorna en esta posición, W=30h
retlw 31h ; sí W sumado al PCL es 1 se retorna en esta posición, W=31h
retlw 32h ; sí W sumado al PCL es 2 se retorna en esta posición, W=32h
retlw 33h ; sí W sumado al PCL es 3 se retorna en esta posición, W=33h
retlw 34h ; sí W sumado al PCL es 4 se retorna en esta posición, W=34h
retlw 35h ; sí W sumado al PCL es 5 se retorna en esta posición, W=35h
; ...

Para terminar, después de observar el ejemplo anterior, debemos tener en cuenta que antes
de llamar a la subrutina tabla, se debe cargar en el registro de trabajo W el valor del índice
y una vez se retorne de dicha subrutina, es en este mismo registro de trabajo en donde se
obtiene el resultado de la consulta a la tabla (vemos que la sucesión de instrucciones retlw
k se encuentra en memoria de programa).

CONVERSIÓN A ASCII:
El conjunto de caracteres ASCII (American Standard Code for Information Interchange)
es el código de representación en hexadecimal del alfabeto, los números del 0 al 9, los
principales símbolos de puntuación y algunos caracteres de control.

46
Conjunto de carácteres ASCII

Como vemos en la tabla anterior, podemos dividir a cada carácter representado en


hexadecimal como una parte alta de 3 bits (Most significant carácter = números del 0 al
7) y una parte baja de 4 bits (Least significant carácter = números del 0 al F). En total, la
representación la hacemos con 7 bits.
De los problemas usuales en la programación está el convertir un número hexadecimal
representado en 8 bits a dos caracteres ASCII los cuales sean la representación de dicho
número para permitir su visualización en pantallas LCD, monitores, impresoras, etc.
Ejemplo:
Para representar el número hexadecimal 70h que en binario es 01110000b como los dos
caracteres ASCII "7" y "0", gráficamente:

7 0 en hexadecimal (8 bits)

"7" "0" en ascii (16 bits)

37h 30h ascii en hexadecimal (16 bits)

Transportándolo a un programa:
NumHex equ 0Ch ; posición donde se almacena el número a convertir
AsciiH equ 0Dh ; posición donde se almacena el resultado parte alta
AsciiL equ 0Eh ; posición donde se almacena el resultado parte baja
.......................
.......................
movlw 0Fh ; dato para enmascarar parte alta
andwf NumHex,0 ; se enmascara la parte alta del número hexa y pasa a W

47
iorlw 30h ; convierte el número en ascii
movwf AsciiL ; el número queda salvado en la variable de salida
movlw 0F0h ; dato para enmascarar parte baja
andwf NumHex,1 ; se enmascara la parte baja del número hexa y queda allí
swapf NumHex,0 ; se invierten parte alta y baja
iorlw 30h ; convierte el número en ascii
movwf AsciiL ; el número queda salvado en la variable de salida.
.....................
.....................
El ejemplo anterior funciona de forma correcta siempre y cuando los nibbles del número
hexadecimal a convertir estén en el rango de 0 a 9. Habrá que realizarse un tratamiento
adicional a éstos si se encuentran en el rango de Ah a Fh.

RAMIFICACIÓN MÚLTIPLE.
Cuando se tiene que solucionar un diagrama de flujo como el de la figura, en el cual
tenemos tres posibles respuestas a una pregunta, se plantean las soluciones aquí
presentadas.

Tres posibilidades para una pregunta.

Una de las formas de solucionar en un programa este problema es:

Solución 1.
Determinando para la opción 1, la opción 2 y la opción 3 un valor consecutivo como:

opción1 equ 0

48
opción2 equ 1
opción3 equ 2

Uno de estos posibles valores llevarlo a W y en una parte del programa tratarlo así:

Decisión: ;sitio en donde la pregunta "?" tendría solución


addwf PCL,1
goto Acción1
goto Acción2
goto Acción3

Acción1:
............................... ;instrucciones correspondientes a la Acción 1
...............................
...............................
goto encuentro

Acción2:
............................... ;instrucciones correspondientes a la Acción 2
...............................
...............................
goto encuentro

Acción3:
............................... ;instrucciones correspondientes a la Acción 3
...............................
...............................
encuentro: ;sitio de encuentro luego de una de las acciones
............................... ;continuación del programa
...............................

Solución 2.
Otra forma posible es comparando uno por uno los valores de las diferentes opciones
almacenadas en memoria RAM en una variable llamada OPCION
movlw Opción1
xorwf OPCION,0 ; se realiza la verificación de OPCION respecto a W
btfsc STATUS,Z ; verificando la bandera Z
goto Acción1
movlw Opción2
xorwf OPCION,0 ; se realiza la verificación de OPCION respecto a W
btfsc STATUS,Z ; verificando la bandera Z
goto Acción2
movlw Opción3
xorwf OPCION,0 ; se realiza la verificación de OPCION respecto a W

49
btfsc STATUS,Z ; Verificando la bandera Z
goto Acción3
Acción1:
............................... ; instrucciones correspondientes a la Acción 1
...............................
...............................
goto encuentro
Acción2:
............................... ; instrucciones correspondientes a la Acción 2
...............................
...............................
goto encuentro
Acción3:
............................... ; instrucciones correspondientes a la Acción 3
...............................
...............................
encuentro: ; sitio de encuentro luego de una de las acciones
............................... ; continuación del programa
...............................

Aunque este último método es más largo que el anterior, es válido cuando los valores de
las diferentes opciones no son consecutivos entre si.

ARITMÉTICA:
Dentro de los microcontroladores PIC se cuenta con instrucciones aritméticas tales como:
ADDWF y ADDLW para efectuar operaciones de suma.
SUBWF y SUBWF para efectuar operaciones de resta.
RLF para realizar multiplicaciones por 2.
RRF para realizar divisiones entre 2.
Hasta este punto podríamos ver el conjunto de instrucciones un poco limitado. Sin
embargo, utilizando técnicas avanzadas de programación podemos obtener operaciones
más complejas.
Podemos obtener varias rutinas matemáticas generales de los PIC 16CXX y 17CXX en
estas direcciones:
http://www.iespana.es/portosin/MATEMATICAS16CXX
http://www.iespana.es/portosin/MATEMATICAS17CXX

50
TEMA 3: ESCRITURA DE PROGRAMAS EN ASM.

El ensamblado de un programa traduce las instrucciones que se han escrito utilizando los
mnemónicos del lenguaje maquina (CALL, etc.), a código binario ejecutable por el
microcontrolador. La secuencia de mnemónicos se llama listado o código fuente del
programa, mientras que el código binario se llama objeto o ejecutable.
Una vez que el programa se ha escrito y ensamblado (compilado no existe para el lenguaje
ensamblador, solo se compilan los lenguajes de alto nivel como el C) ya tenemos un
binario ejecutable.

Estructura de un programa típico:


Los programas desarrollados para el microprocesador PIC constan básicamente de esta
estructura:
• Definir el Microcontrolador a usar: En esta directiva se especifica el tipo de
microcontrolador que será utilizado; esto se logra utilizando LIST = PIC16F84.
• Establecer las constantes a utilizar: Las constantes son útiles para referenciar
direcciones de memoria, posiciones de los bits y valores que no cambiarán a lo largo
de todo el programa. Esto se logra utilizando la directiva "EQU". Si se desea por
ejemplo establecer un valor a una constante, sería CONST EQU 1, con lo cual le
asigna el valor de "1" al registro CONST.
• Reservar los espacios en memoria de las variables: Se utiliza para reservar
espacios en memorias para las variables utilizadas en el programa. Se logra
utilizando la directiva denominada "RES". Muy útil cuando un registro cambia su
valor durante la ejecución del programa.
• Configurar los puertos: Es necesario establecer si los bits de cada puerto serán
utilizados como entrada o como salida. Para ello utilizamos los registros especiales
TRISA y TRISB.
• Desarrollar el Programa: Es el verdadero corazón del trabajo que se realiza,
puesto que aquí se desarrollan las rutinas que serán ejecutadas por el
microcontrolador.

51
En la imagen anterior, tenemos un pequeño programa como ejemplo, para el
microcontrolador PIC16F84 en la cual vemos las zonas en diferentes colores. El color
amarillo representa la definición del microcontrolador, el color verde representa el
establecimiento de las constantes, el color celeste representa la reserva de Memoria, el
color rojo es donde realmente comienza el programa en sí y representa la configuración de
los puertos y el cuerpo del programa.

3.2. PROGRAMA MPLAB.

EL MPLAB es un “Entorno de Desarrollo Integrado“ (Integrated Development


Environment, IDE) que funciona bajo Windows, mediante el cual podemos desarrollar
aplicaciones para los microcontroladores de las familias PIC 16/17.
EL MPLAB nos permite escribir, depurar y optimizar los programas diseñados para
utilizar con dichos microprocesadores.
De nada nos serviría saber programar si finalmente no podemos hacerlo por carecer de las
herramientas necesarias para ello. Por eso es importante conocer el funcionamiento del
MPLAB.
Con el MPLAB podemos:

52
• Depurar programas fuente.

• Detectar errores automáticamente en programas fuente para editarlos.

• Depurar programas utilizando puntos de corte (breakpoints) mediante valores de los registros
internos.

• Observar el flujo del programa con el simulador MPLAB -SIM, ó seguirlo en tiempo real utilizando
el emulador PICMASTER.

• Realizar medidas de tiempo utilizando un cronómetro.

• Ver variables en las ventanas de observación.


• Encontrar respuestas, utilizando la ayuda en línea del MPLAB.

Este entorno funciona como un contenedor, es decir, las diferentes opciones que contiene
están asociadas a programas independientes que se ejecutarán cuando sean seleccionados
los diferentes modos dentro del MPLAB.
Podemos definir un ensamblador (por ejemplo en C), un emulador o un grabador diferente
a los que tiene por defecto, quedando de esta forma integrado al entorno.

En el organigrama que veremos a continuación tenemos las distintas fases de


implementación que deberemos seguir para completar con éxito la programación del
microprocesador.

INICIO

ESCRITURA DEL
Se hará en formato de texto DOS o ASCII con cualquier
CÓDIGO FUENTE editor, como, por ejemplo, el EDIT. También es posible usar
el entorno WIN95-98 respetando este formato de grabación.
Nosotros emplearemos el MPLAB como editor.

Para el lenguaje ensamblador emplearemos el MPASM,


ENSAMBLADO habiendo también herramientas para trabajar en C. Nosotros
lo elegiremos por defecto como ensamblador en el MPLAB.

La herramienta de simulación en DOS es MPSIM, pero


SIMULACIÓN el entorno gráfico MPLAB tiene su propio sistema, que
utilizaremos.

¿ERRORES?

Cada grabador tiene su software específico, y es posible


GRABACIÓN DEL encontrar múltiples circuitos y programas en Internet.
µCONTROLADOR
Microchip vende sus propios equipos, así como micros
programados de fábrica.

FIN

53
3.2.3 GUIA DE USO DEL ENSAMBLADOR MPASM (TUTORIAL)

Este tutorial esta basado en el propio tutorial de Microchip que está en su página web en
formato PDF.

CONFIGURANDO EL MPLAB:

Si ya tenemos instalado el MPLAB, lo primero que tenemos que hacer es configurarlo para
el uso que vamos a hacer de él, si es editor solamente o simulador. Al poner simulador se
entiende que es para programar, depurar, etc. Así que nos vamos al menú OPTION y
abrimos DEVELOPMENT MODE, y nos aparece la siguiente ventana, lo cual
configuraremos solamente la solapa TOOLS dejando el resto por defecto. En la ventana
hemos seleccionado el PIC16F84,y en el modo SIMULATOR.

Pulsamos en el botón OK o APPLY, y vemos que en la barra de estado o STATUS BAR


(en la parte baja de la ventana del MPLAB) cambia el chip para el PIC16F84 y en modo
simulador (SIM).

54
CREAR UN PROYECTO NUEVO:

El simulador o debuger para poder funcionar, utiliza el archivo *.HEX generado a partir de
una compilación o ensamblado del código fuente. Además este mismo archivo generado
(*.HEX) es el que se usa para grabar el PIC. El ensamblador produce otros ficheros aparte
del *.HEX para otros usos que veremos luego. En este tutorial (basado en el de Microchip)
el fichero generado se llamará tutor84.hex.
Seleccionamos FILE - NEW del menú FILE y nos saldrá la siguiente pantalla de dialogo:

Veremos que también se abre una ventana en blanco, que es donde introduciremos nuestro
código. En la ventana anterior pinchamos en YES y así comenzaremos a crear nuestro
proyecto nuevo.
Nos abrirá la siguiente ventana para que escribamos el nombre del proyecto, y el directorio
donde lo guardaremos. Lo llamaremos TUTOR84 y lo guardaremos en alguna carpeta que
tengamos dentro del MPLAB, por ejemplo una que llamaremos "propios", o en otro
directorio diferente si así lo queremos nosotros.

55
Como vemos, la extensión *.PJT corresponde a proyectos. Guardamos entonces nuestro
proyecto asignándole antes un nombre, y automáticamente se abre el siguiente cuadro de
EDICION DE PROYECTO.

Tanto el simulador como el programador o el emulador trabajan a partir del archivo *.HEX
generado por el IDE del MPLAB, por el ensamblado, compilado y/o encadenado
(linkeado) del código fuente.
Diferentes tips de herramientas generan el archivo *.HEX, y estas herramientas son parte
de cada proyecto. El proyecto nos da la flexibilidad de poder utilizar diversas herramientas
para crear el ejecutable *.HEX.
En la pantalla de EDICION DEL PROYECTO vemos que por defecto, el proyecto nos
asignó el mismo nombre para el fichero ejecutable tutor84.hex. Así mismo, también
tenemos el MPLAB SIM 16F84 que es lo que habíamos seteado anteriormente en el menú
OPTION, al igual que la suite de lenguajes (en este caso Microchip).
Si seleccionamos dentro de la ventana Projects Files tutor84.hex nos habilitara el botón
NODE PROPERTIES. Este botón nos sirve para configurar y decirle al MPLAB IDE
cómo crear el fichero *.HEX. Lo pulsamos y nos abre el siguiente cuadro:

56
Como vemos, aparece en la esquina superior derecha el lenguaje utilizado; a la izquierda el
nombre del fichero a crear, que por defecto nos sale tutor84.hex. Todas las filas y
columnas dentro del cuadro, son switchs o llaves en las cuales marcamos nuestras
preferencias. Por ejemplo la numeración utilizada (HEX) si queremos utilizar macros, etc.
Esto se traduce en una línea de comandos que podemos visualizar en la parte inferior de la
pantalla, en Command Line. Por ahora, hay que marcarlo como vemos en la ventana de
muestra. Pinchamos en el botón OK y volvemos a la ventana anterior.

57
Hacemos click en ADD NODE y nos aparece la siguiente ventana:

En la misma debemos guardar dentro del mismo directorio del proyecto nuestro archivo
fuente que se llamará tutor84.asm. Le damos ACEPTAR y volvemos otra vez al cuadro
anterior:

58
Como vemos, se ha añadido el nodo tutor84.asm en forma dependiente de tutor84.hex.
Apretamos OK y volvemos al escritorio en donde tendremos abierta una ventana en donde
introduciremos nuestro código, pero aún sin titulo. Para ello seleccionaremos en el menú
EDIT SAVE AS y le daremos el nombre del nodo que hemos configurado tutor84.asm.

Como podemos apreciar ahora la barra de titulo de la ventana de código a cambiado de


nombre de untitled a tutor84.asm. Podemos ver que siempre colocaremos el mismo
nombre en el proyecto y en el código fuente. Esto es porque no estamos trabajando con un
linker que nos permite incorporar o linkear distintos archivos con distintos nombres.

59
Entonces al trabajar con un archivo único debemos nombrarlo igual que el proyecto al cual
pertenece, además de guardarlo en el mismo directorio.

INTRODUCIR CODIGO ASM:

En la ventana que tenemos abierta empezaremos a introducir el código de nuestro


programa. Escribimos lo siguiente:

Algunas consideraciones:

• Este es el programa que utilizaremos en este tutorial, y como se puede ver consta de
varias columnas, las cuales significan lo mismo que en otros lenguajes de
programación (ya hemos explicado esto en un tema anterior).

• La primera de la izquierda son las famosas etiquetas que pueden tener cualquier
nombre y sirven para referenciar direcciones a llamar o bifurcar/saltar. El programa
cuenta con 4 etiquetas c1, reset, start y loop. Podemos cambiarles los nombres por
cualquier otro que se nos ocurra, siempre que también lo hagamos en la instrucción
que llama a dichas etiquetas.

• En la segunda columna tenemos las instrucciones: goto, movlw, etc.

• En la tercera el operando, registro o llamada: c1, f, start, etc.

• Y por último en la cuarta, siempre después de un PUNTO Y COMA, los


comentarios, que sirven nada más (y nada menos) que para ayudar a la memoria del
programador o facilitar la comprensión del código por otra persona ajena al propio
programador. No tienen utilidad alguna en el ensamblado, pero si mucha para los
programadores.

60
• Para escribir el código utilizamos la tecla TAB para desplazarse entre columnas.
Por ejemplo: la primera línea la escribiremos =c1 TAB equ TAB0 0x0c TAB ; Set
temp ... Nótese que la etiqueta c1 está en la misma línea y por ejemplo reset no.
Esto no importa, sólo que la etiqueta apunte a la línea en cuestión o por encima,
siempre y cuando la primera línea a ejecutarse después de la etique sea a la que
hace referencia. Por descontado, en el código que tecleamos para practicar con este
ejemplo no hace falta escribir el campo de comentarios.
Este programa lo que hace es cargar un registro cualquiera de uso general, el cual se
denominó c1, con un valor X que en el ejemplo es 9.
Luego lo incrementamos de 1 en 1 hasta que se desborde (recordemos que son registros de
8 bits, y se desbordará cuando llegue a FF o 255 y volverá a 00) y vuelva a 0.
Cuando esto ocurra, el programa cargará nuevamente el registro con el valor 9 y repetirá lo
mismo.
Por último, recordar que en los cuadros seleccionables del nodo teníamos marcado CASE
SENSITIVE lo cual quiere decir que escribiremos el código de una única forma; o
mayúsculas o minúsculas, o haremos referencia a etiquetas iguales. Lo que significa que
start no es lo mismo que Start. Y guardamos los cambios en el menú EDIT SAVE.

ENSAMBLADO (COMPILAR):

Nuevamente haremos mención a que no vamos a compilar sino ensamblar, porque


recordaremos que solamente se compila desde lenguajes de alto nivel.
Accedemos al menú PROJECT - BUILD ALL. Hacemos build all y nos sale la ventana
de compilación diciéndonos que hemos tenido un error. ¿Cómo puede ser que nos hayamos
equivocado? Veamos que nos dice la ventana:

Pero no hay problema. El fallo es debido a que hemos introducido a propósito un error en
el código: es en el último goto, donde pone "goto bug".

61
Hemos hecho referencia a una etiqueta (bug) que no existe. Para corregir dicho error,
hacemos doble click con el puntero del ratón sobre el error dentro de esta última ventana
llamada "Build Results" y nos lleva a la ventana de código colocándose el cursor sobre el
error. Cambiamos la etiqueta "bug" por "start", y ensamblamos nuevamente (build all).
Nos aparecerá nuevamente la ventana de resultados diciéndonos que ahora tenemos todo
correcto (lo que no quiere decir que funcione correctamente, que no es lo mismo).
Recordemos que no hace falta salvar los cambios en el código cuando cambiamos "bug"
por "start" ya que cuando ensamblamos se guardan automáticamente los cambios.
Veremos la siguiente ventana:

Ya se ha creado el archivo *.HEX que es el que se graba en el PIC. Si nos fijamos en el


directorio donde hemos guardado el proyecto, veremos el archivo. En estos momentos ya
podríamos utilizar este archivo para grabarlo en el PIC, pero antes necesitamos saber si
funciona bien el programa, así que tendremos que simular antes para comprobarlo. Como
dato respecto al ensamblado o compilado, cada vez que nos aparezca la ventana en la que
nos indique que hemos tenido algún error (uno o más), siempre hay que comprobar el
primero, porque el resto de los errores pueden producirse por culpa del primero, así que
solucionando éste podrían desaparecer el resto o al menos alguno de ellos.

SIMULACIÓN:
Ahora llegó el momento de simular, depurar o debuguear . Si tenemos el proyecto cerrado,
nos vamos al menú project y seleccionamos open project, y elegimos tutor84. Recordar
que si queremos guardar el proyecto, nos vamos al menú project y después close project.
Es la única forma de cerrarlo. No es suficiente con cerrar la ventana de código para cerrar
el proyecto, eso lo podremos observar cuando la cerremos y se siga viendo en la barra de
título el proyecto.
La primera vez que guardamos o cerramos el proyecto, nos preguntará si queremos guardar
los cambios hechos, y estos cambios incluyen hasta las posiciones de las ventanas de
código, etc. en el área de trabajo. Y así cuando lo abramos la próxima vez, nos aparecerán

62
las ventanas y demás partes integrantes del mismo, en las mismas posiciones donde
estaban antes de guardarlo.
Una vez abierto el proyecto, y viendo la ventana de código, nos vamos al menú DEBUG –
RUN - RESET para inicializar el sistema. Una vez inicializado, vemos que estamos
parados en la primera instrucción que va a ejecutar el PIC.
La línea que aparece resaltada es la que se va a ejecutar, NO que ya está ejecutada. Para
volver a simular o regresar al punto de partida, de nuevo pulsaremos RESET.
Una vez parados sobre la primera instrucción a ejecutar (lo que tenemos arriba son
directivas para el ensamblador y no forman parte del código asm) nos vamos al menú
DEBUG – RUN - STEP o desde el acceso directo que tenemos en el botón que tiene el
dibujo de dos pies. Este es el botón para ejecutar paso a paso. A la derecha hay otro igual
pero con una línea entre los dos pies. Esta función se llama STEP OVER y sirve para
ejecutar paso a paso igual que el anterior, pero si encuentra algún CALL lo ejecuta todo de
golpe como si de una sola línea se tratara. También puedes acceder a STEP a través de la
tecla F7.
Vemos que hemos ido a START dado que la primer instrucción es GOTO START. En este
comando no veremos programación, sino en forma básica la simulación o depuración. Si
observamos la barra de status, veremos como ha cambiado el valor del Program Counter
(PC) a 0x04.
Si seguimos presionando F7 repetidamente, podremos observar como se ejecuta el proceso
paso a paso.
Para hacer que funcione a tiempo real (tiempo real para el programa de simulación, no la
velocidad real del pic durante la simulación). Nos vamos nuevamente al menú DEBUG –
RUN - RUN o pulsamos el botón del semáforo verde o F9, y veremos como la barra de
estado cambia su color a amarillo. Esto nos indica que el programa esta funcionando en
tiempo real (lógicamente no veremos la línea negra de simulación cambiar de posición por
cada instrucción que ejecuta ya que no nos daría tiempo a verlo). Para detener el proceso
apretamos el semáforo rojo, o F5. Se detendrá donde estaba ejecutándose en el momento
de pulsar el semáforo o la tecla F5.
También tenemos la función ANIMATE que no es nada más que un AUTO STEP. La
podemos seleccionar en el menú DEBUG – RUN - ANIMATE o control F9, y veremos
en forma automática como se ejecutan las líneas. Lo detenemos también con F5.

OTRAS VENTANAS DE LA SIMULACIÓN:


Realmente de poco nos sirve ver esa línea subir y bajar, si no nos muestra ninguna
información sobre los registros, etc. Para eso tendremos que ir al menú WINDOWS –
SPECIAL FUNCTION REGISTER y nos abrirá la ventana de registros especiales o
específicos del PIC16F84 que ya está configurado en DEVELOPMENT MODE.

63
En esta ventana veremos todos los registros especiales del micro. También veremos que
cuando cambian de valor, cambian de color azul a rojo.
¿Y donde esta el registro de uso general que lo hemos denominado c1 en el programa? Si
queremos ver absolutamente todos los registros tenemos que ir al menú WINDOWS –
FILE REGISTER. Así se abrirá una ventana en hexadecimal que no entendemos. Sobre
la barra de titulo de esta nueva ventana, vemos que a la izquierda del titulo hay un icono
que son tres hojas superpuestas. Si pinchamos con el puntero del ratón encima, nos
desplegará un menú, en donde elegiremos "Symbolic Display" y veremos la ventana
siguiente:

Aquí vemos todos los registros del micro, tanto los especiales como los de uso general,
incluso los que hemos renombrado como el caso de "c1". Hasta aquí todo correcto. Pero
¿Qué pasa si tenemos un programa más grande que el actual y con más registros? El poder
ver esta ventana se nos haría muy complicado.

64
Para buscar una solucion a esto, tenemos otra ventana “especial” llamada "Watch
Window" y que se guardará con nuestro proyecto.

WATCH WINDOW:
Cerramos todas las ventanas que hemos abierto escepto la de código y seleccionamos en
menú window - watch window - new watch window.
Nos aparecerá una ventana en blanco y al mismo tiempo sobre esta última, la ventana Add
Watch Symbol, ya que no tenemos todavía ningún símbolo cargado. Escribimos en el
cuadro de diálogo el registro que queremos ver (en este caso sería c1):

Pinchamos en el botón Add. Tal y como está ahora lo veremos por defecto en
hexadecimal. Si queremos verlo en otro formato (decimal, binario, ...), antes de presionar
Add pulsamos en Properties y seleccionamos el que nosotros queramos.
Y para finalizar con la pantalla de añadir símbolos pinchamos en Close. Ahora ya tenemos
nuestra ventana con los registros que realmente nos interesa ver. Podemos agregar más
registros presionando la barra de titulo, sobre el icono y después Add, o Edit si queremos
editarla, etc.

De esta forma nos será más fácil seguir a un registro en particular. Con el programa
tecleado antes, veremos como se incrementa y luego se desborda c1 volviendo nuevamente
a 00000000.
Para agregar o editar registros accedemos a través del icono de la barra de título de la
ventana watch window o a través del menú window - watch window - xxx en donde nos

65
mostrará add active watch, edit active...etc, haciendo referencia a active porque podemos
tener más de una ventana watch window.
Si queremos guardar la ventana de watch window para no tener que construirla
nuevamente cada vez que queramos utilizarla, lo haremos con save watch window. Pero si
cerramos el proyecto, nos preguntará si queremos guardar los cambios. Le respondemos
que si y entonces se guardará automáticamente la ventana con el nombre por defecto
watch_1.wdt.

BREAK POINTS O PUNTOS DE PARADA:


Los breakpoints sirven para detener el procesador en el punto del programa donde le
indiquemos.
Para ello nos situamos en la ventana de código, y colocamos el cursor sobre la instrucción
MOVLW 0x09 y una vez allí pinchamos con el botón derecho del ratón y se nos abre el
menú contextual, donde elegimos Break Point(s). Veremos que la línea cambia de color (a
rojo), indicándonos que tenemos un break point en dicha línea.

Tendremos que asegurarnos que en la barra de estado (la que tenemos en la parte de abajo)
en donde dice Bk esté en ON. Si está en OFF nunca se detendrá en ningún break point.
Para cambiarlo hacemos doble click ahí mismo y se cambiará de un estado a otro. También
podremos hacerlo en el menú option - development mode y pinchamos en la solapa
Break Point y marcamos la casilla Global Break Enable.
Hacemos de nuevo un reset del sistema, presionamos F9 y veremos como se detiene cada
vez que llegue al break point.
También tenemos muchas otras opciones, como por ejemplo RUN TO HERE, que hace
que se ejecute el código hasta allí sin necesidad de colocar un break point.
Los Break Points se pueden colocar en la ventana de código, en window - Program
Memory (es el buffer que habíamos mencionado antes) o en window - Absolute Listing.
Source Files: si vamos al menú window - Project Window se nos abrirá una ventana en
donde nos muestra todos los archivos que forman parte del proyecto. Haciendo click en
ellos nos abrirá el editor del mismo.

MPASM, EL ENSAMBLADOR DE MICROCHIP:

66
El ensamblador de Microchip es el MPASM, el cual forma parte constituyente del IDE.
Veremos las directivas del mismo, los controles, etc.
Con el MPASM procederemos a ensamblar un programa que hayamos creado o editado
con el MPLAB. Este nos permite escribir el código y ensamblarlo para producir como
salida un fichero *.HEX que nos servirá para grabarlo en el PIC o utilizarlo por el
simulador o emulador.
Pero tenemos distintas configuraciones del MPASM para producir diferentes resultados:

• Podemos utilizarlo, como el caso del tutorial, para generar un fichero "ejecutable"
*.HEX a partir de un solo código "ABSOLUTO" como lo era el pequeño programa
del tutorial.

• Podemos generar un archivo ejecutable *.HEX a partir de la unión de distintos


códigos objeto y otros módulos separados, ensamblados y/o precompilados. En este
caso la herramienta que dispone el MPASM para unir esos módulos se llama
MPLINK o “linker”.

• Genera módulos objeto para ser ensamblados con otros módulos y así producir un
único archivo ejecutable.

• Crear librerías con el MPLIB (una colección de códigos objeto listos para ser
usados y que se almacenan todos juntos en un único archivo con extensión *.LIB).
Es conveniente aprender a utilizar el linkeador, dado que hay mucho código objeto en
Internet para bajar e incluir en nuestros proyectos.

Configuración y uso:

67
En la pantalla anterior vemos el botón Browse que utilizaremos para localizar e indicar al
MPASM el archivo que queremos ensamblar, y con las opciones que podemos seleccionar
en ella:

• Formato de salida del archivo *.HEX.

• Si queremos que nos muestre todos los mensajes, o sólo los de error.

• El formato de salida del archivo *.HEX.

• Archivos y listados que generará.

• Tipo de procesador, etc...


Una vez tengamos todas las opciones seleccionadas, pulsamos el botón de Assemble y nos
aparecerá la siguiente pantalla:

Aquí nos muestra los errores, advertencias (warnings), mensajes y líneas ensambladas. Si
no hemos tenido ninguno, podemos proceder a grabar el pic o simular el programa.
Si hemos tenido errores, vamos a editar el archivo *.ERR generado por el MPASM con el
propio editor del MPLAB o con cualquier otro editor de texto, y que nos mostrará las
líneas en donde tenemos los errores.

Lenguaje de DIRECTIVAS:
Insisto en que las instrucciones de los PIC's son únicas y no hay más que las que hemos
visto anteriormente. No confundir las propias instrucciones de los PIC's con las directivas
del ensamblador.
Las famosas directivas son solo eso, directivas para el ensamblador, le dicen cómo hacer
tal cosa o tal otra, y solo nos sirve para realizar las cosas más rápido, y así ayudarnos en la

68
tarea de programación. Tenemos muchos tipos de directivas, las cuales se escriben dentro
del código fuente. Por eso no hay que confundir las directivas con las instrucciones del
PIC.
Los tipos de directivas son:

• Directivas de control.

• Directivas de datos.

• Directivas de listado.

• Directivas de macros.

• Directivas de ficheros objeto.


Las directivas de control, permiten un ensamblado CONDICIONAL.
Las directivas de datos, son todas aquellas que permiten la manipulación simbólica y
posicionamiento en memoria.
Las directivas de listado permiten todo el control sobre la paginación o listado del
programa, así como su formato.
La directivas de macros, permiten todas las gestiones de las macros.
Las directivas de fichero objeto, sólo se utilizan para la creación de ficheros objeto o
reubicables/reusables para luego enlazar con el MPLINK.

69
70
71
Aunque hay muchas directivas, vale la pena conocerlas para así ahorrarnos tiempo al
programar. Veamos las que tienen mayor uso:
ORG: Le indica al ensamblador dónde comienza el programa, y así lo graba a partir de la
posición que especificamos aquí.
#DEFINE: Muy útil. Define un nombre para algún registro, pin, etc. Por ejemplo,
#DEFINE SALIDA PORTA,3 sirve para no tener necesidad de recordar cual era el
pin de salida, sino que sólo lo mencionaremos como SALIDA. Podemos ponerlo a 0 con la
instrucción BCF SALIDA en vez de hacer BCF PORTA,3 o BCF RA3.
END: Indica al MPASM que ha terminado el programa.
EQU: Define una constante con una etiqueta. La constante puede ser un registro o un valor
numérico.
#INCLUDE: Con esta directiva empezamos casi todos los programas. Como el
ensamblador no sabe que microprocesador estamos utilizando en nuestro código,
Microchip ha creado una serie de archivos con extensión *.INC donde se definen todos los
registros y datos respecto del procesador que estemos utilizando. En dicho archivo se
define que la dirección 0x05 se llama PORTA, y por eso no tendremos que recordar la
dirección, simplemente escribir en nuestro código CLRF PORTA directamente.
Lógicamente, si en nuestro código definimos que PORTA ahora se llama R2D2, lo
tendremos que llamar R2D2. Por eso al principio del código ponemos #INCLUDE
P16F84.INC si es que estamos trabajando con el PIC16F84. Todos los archivos *.INC se
encuentran en el directorio de instalación del MPLAB. También sirve esta directiva para
incluir librerías de rutinas, algo así como las DLL's del x86.
MACRO: Para crear una macro. Por ejemplo:
NUEVO MACRO
CLRF PORTA
BSF PORTB,2
ENDM
Hemos creado una macro llamada NUEVO. Ahora cada vez que pongamos la palabra
NUEVO (en la segunda columna cuando queramos llamar a la macro), en realidad lo que
hará el MPASM es reemplazar la palabra NUEVO por CLRF PORTA.......etc. hasta el
final de la macro que termina con la directiva ENDMacro (ENDM). ¡Muy útil también!.

72

También podría gustarte

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy