A continuación veremos la segunda parte de un gran
curso sobre el lenguaje ensamblador (puedes checar la primera parte aquí), escrito por el técnico en electrónica César Antonio Saldías Caro, de Valparaíso, Chile. Puedes encontrar más información en su blog
ingenieropic.wordpress.com.
Este curso incluye una
carpeta con el código fuente compilado del programa que analizaremos.
Programa de ejemplo
Probablemente, el encendido y apagado de un LED es lo primero que todos
intentan conseguir cuando se inician en la programación en Assembler, por esa
razón decidí comenzar la segunda parte de mi curso explicando precisamente
un programa que realice tal acción.
Comencemos:
La primera línea se utiliza simplemente para indicarle al programa el modelo de
PIC que vamos a utilizar.
La segunda línea es para agregar a nuestro programa la librería del PIC
16F628A que contiene datos de direccionamiento para que no tengamos que
escribirlos manualmente.
*Entiéndase por librería como un programa escrito previamente, el cuál
agregamos a nuestros programas para que sean más manejables. Podríamos
decir entonces que cualquier programa que escribamos podría ser denominado
librería si lo agregáramos a otro programa a futuro.
En esta línea estamos configurando los fusibles. Esto fue explicado en la
sección de “directivas”.
Con esta línea le estamos indicando al programa que ignore los posibles
errores cuando almacene en banco. Cuando compilamos, a veces aparecen
mensajes que dice “Warning” o “Message” advirtiendo de posibles errores en
nuestro programa, cada uno de estos errores tiene un número (este número
aparece junto al mensaje). El aviso 302 es el que indica que un registro puede
haber sido configurado en un banco equivocado. Al escribir “errorlevel -302”
estamos diciéndole al programa que ignore ese posible error.
Los registros o variables que se usarán se declaran entre estos dos reglones,
el 0X20 indica que usaremos los espacios disponibles para registros a partir de
la posición 0x20 de la memoria.
*Para mayor información puedes ver la sección “Memory Map” en la datasheet
del PIC.
Las variables que utilizaremos se declararán entre estos dos reglones, pero
eso lo haremos más adelante.
La línea org 0x0 (origen 0) es el Vector de Reset, lo cual quiere decir que
siempre que se resetee, o se encienda el PIC, el programa se comenzará a
ejecutar a partir de esta posición.
El goto es una instrucción que se utiliza para saltar a una parte específica de
nuestro programa, en este caso le decimos que vaya a la etiqueta “reg”, que
obviamente puede tener cualquier otro nombre.
Como ya deberás saber, el PIC 16F628A tiene cuatro bancos, en cada uno de
ellos hay registros, y en cada registro hay 2 Nibbles, que es lo mismo que 8
bits.
*Nibble: Conjunto de 4 bits.
Para moverse de un banco a otro debemos utilizar el registro STATUS. En este
registro tenemos RP0 y RP1, los cuales deben tener un 0 o un 1 para poder
posicionarnos en un determinado banco. La siguiente tabla (extraída de la
datasheet) muestra la configuración que debe tener RP0 y RP1 para moverse a
un determinado banco:
Vale decir que para movernos al banco 0, debemos poner en 0 el RP0 y el
RP1, y para movernos por ejemplo al banco 3 debemos poner en 1 el RP1 y en
0 el RP0.
Teniendo esto en cuenta, analicemos las siguientes líneas.
BCF es la instrucción para poner un 0 y BSF es para poner un 1. Lo dije de una
manera poco técnica pero se entiende la idea.
Entonces, necesitamos configurar los registros TRIS ubicados en el banco 1,
estos registros se utilizan para definir si los puertos A y B serán entradas o
salidas. Por lo tanto, y según la tabla de arriba, para movernos al banco 1
debemos poner un 0 en RP1 y un 1 en RP0.
La línea org 0x5 se utiliza para saltarse el vector de interrupciones (esto será
analizado en la segunda parte de este curso).
Luego estamos poniendo en 1 el RP0, no hace falta poner en 0 el RP1 porque
por defecto todo bit está en cero.
Ahora ya estamos ubicados en el Banco 1 de la memoria de nuestro PIC.
Ahora, en la tercera línea dice clrf trisa. La instrucción clrf es para mandar
puros ceros a un registro, en este caso estamos mandando puros ceros al
registro TRISA lo cual configura al puerto A como salida.
*TRISA direcciona al puerto A y TRISB direcciona al puerto B.
La instrucción clrf nos ahorra espacio, ya que manda puros ceros, pero sería lo
mismo escribir esto:
De esta forma cargamos puros ceros y luego los mandamos al registro TRISA.
Es lo mismo.
Bueno, volvamos a este trozo de código fuente. Luego de poner en 0 el TRISA
estamos utilizando la instrucción movlw que carga un valor literal en W, en
este caso el valor literal es binario (por eso ponemos una b en minúscula).
El número binario que estamos cargando es 10000000 lo que significa que RB7
está configurado como entrada, y de RB6 a RB0 están siendo configurados
como salidas.
Pero hasta este momento solo hemos cargado el valor binario, pero ahora hace
falta enviarlo a algún registro, para ello utilizamos la instrucción movwf y
escribimos TRISB, o sea que le estamos diciendo que mueva el valor binario
cargado al registro TRISB.
En seguida, debemos regresar al Banco 0, puesto que el PIC debe mantenerse
en este banco para poder trabajar. Para ello únicamente ponemos en 0 el RP0
(como se explicó más arriba) y ya estamos en el Banco 0.
Las dos líneas que siguen clrf porta y clrf portb están mandando puros ceros
a los registros porta y portb para “apagar” ambos puertos, por si alguno de ellos
se encontrase con un nivel de voltaje alto a la hora de comenzar a funcionar el
programa.
Como los registros porta y portb se encuentran en el Banco 0, debemos estar
posicionados en ese banco para poder configurarlos.
Aquí ya entramos de lleno en el programa principal, la parte “difícil” era la
configuración de los registros, pero el programa principal propiamente tal, es
mucho más fácil y aburrido, porque si no es difícil no es divertido.
Haremos un programa que encenderá un LED conectado en RB0 cuando
presionemos un pulsador conectado en RB7.
Primeramente, escribimos una etiqueta a la que yo he llamado inicio.
Luego nos topamos con la instrucción btfss que es como una condición if then
en programación con otros lenguajes que son de alto nivel. Esta instrucción
pregunta si se ha cumplido una determinada condición, si esta condición es
falsa el programa hace una cosa, y si es verdadera entonces hace otra.
En este caso yo puse btfss portb,7 lo cual está preguntando si hay o no un
nivel del voltaje alto en el pin RB7, si la condición es falsa se ejecuta la
instrucción que le sigue al btfss, en este caso un goto que nos está enviando
de regreso al inicio para que vuelva a comprobar la condición, y estará dando
vueltas en este bucle hasta que la condición sea verdadera.
*PORTB,7 hace referencia al RB7, así como por ejemplo PORTA,4 haría
referencia al pin RA4.
Ahora bien, si la condición es verdadera (o sea que hay un nivel alto en RB7),
entonces el programa se salta la instrucción goto y continúa leyendo el resto
de instrucciones.
Ahora que la condición se hizo verdadera, la instrucción que sigue es bsf que
se utiliza para poner un 1, en este caso yo coloco en 1 el pin RB0, o sea que se
encenderá el diodo LED conectado en dicho pin.
Luego, llamo a una rutina de tiempo (que haremos más adelante) para que el
programa espere un tiempo antes de continuar. Para esto utilizamos la
instrucción call y nuestra rutina se llama PDelay.
Terminando la rutina de espera, utilizamos la instrucción bcf para poner en 0 el
RB0 y así apagar el LED.
Después, volvemos a llamar a la rutina y finalmente utilizamos la instrucción
goto para regresar al inicio y realizar todo el programa nuevamente infinitas
veces.
Eso es todo, ahora veamos la subrutina de tiempo. Esta se puede confeccionar
manualmente empleando una formula, pero ya son las 0:34, tengo sueño y no
tengo ganas de explicar eso, así que haremos nuestra rutina con un lindo
programa que se llama PIC Delayer, más conocido como PIC Delay o PIC Del.
Bueno, ejecuta el PIC Delayer y se verá está ventana:
En donde dice Frequency debes escribir la frecuencia del oscilador que estás
utilizando, el programa trae por defecto 4MHz.
Luego, donde dice Delay Time debes escribir el tiempo que deseas (ponlo en
milisegundos). Y presiona el botón Calculate Cycles.
Yo puse 500mS. Luego presionas el botón Generate Code y espera a que el
programa genere la rutina de tiempo solicitada.
Deberás ver esto:
Ahí ya está la subrutina creada, ahora cópiala y pégala en tu código fuente. De
esta manera:
No olvides escribir end al final del programa. Yo aquí arreglé la rutina borrando
los comentarios y alineándola con mi código fuente. Te sugiero que hagas lo
mismo, no hay un programador más mediocre que el que escribe desordenado.
Si te fijas la palabra PDelay aparece encerrada con rojo en nuestra rutina. Ese
es el nombre de la etiqueta de nuestra rutina y a ella debemos llamar para que
el programa salte hacia allá. Call PDelay.
Ojo con un detalle. Cuando creamos la rutina con el programa PIC Delayer,
aparecieron unos requerimientos en la parte de abajo:
Nuestra rutina necesita un lugar en el cual almacenarse, a estos lugares
denominaremos variables. Estas variables se llaman PDel0 y PDel1 y hay que
declararlas entre los reglones cblock y endc.
De esta forma nuestra rutina funcionará de forma correcta. Si te fijas hay un
tercer requerimiento, 1 stack level. El stack es una cosa del PIC que no voy a
explicar ahora, pero no te preocupes por eso ya que no es relevante saberlo
para que la rutina funcione. Por ahora ignora eso del stack