Indice Tema 0 Introduccion Tema 1 Hardwa
Indice Tema 0 Introduccion Tema 1 Hardwa
Indice Tema 0 Introduccion Tema 1 Hardwa
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.
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). ;-)
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
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.
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.
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.
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 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.
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.
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:
1.3. REGISTROS
ORGANIZACION:
La memoria interna de datos, también llamada archivo de registros (register file), esta
dividida en dos grupos:
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)
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.
11
Registro STATUS
7 6 5 4 3 2 1 0
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”.
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.
13
EL REGISTRO CONTADOR TMR0:
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.
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.
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.
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.
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.
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.
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
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
INCF f, d Increment f 1 Z
MOVF f, d Move f 1 Z
23
Instrucciones orientadas a los bits
Mnemónico Parámetros Descripción Ciclos Banderas
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
-
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
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.
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.
27
Ejemplo: INSTRUCCIÓN 1
GOTO ABAJO
INSTRUCCIÓN 3
INSTRUCCIÓN 4
INSTRUCCIÓN 5
ABAJO INSTRUCCIÓN 6
28
entonces usaremos esta instrucción:
W = 0.
Valor a asignar = 100.
Instrucción: MOVLW 100
El acumulador valdrá 100 (W = 100).
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.
30
COMPARA INSTRUCCIÓN R1
INSTRUCCIÓN R2
RETURN
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.
32
2.1.2.ORIENTADAS A REGISTROS.
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.
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.
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.
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.
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.
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:
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).
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.
41
2.1.3. ORIENTADAS A BITS.
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
43
2.2 MODOS DE DIRECCIONAMIENTO
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)
;.............................................................................................
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.
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
7 0 en hexadecimal (8 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.
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í:
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.
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.
52
• Depurar programas fuente.
• 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.
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.
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.
¿ERRORES?
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.
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.
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.
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.
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):
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:
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.
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.
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.
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.
• 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:
• Si queremos que nos muestre todos los mensajes, o sólo los de error.
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.
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