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:

ejemplo programa ensamblador 1

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.

ejemplo programa ensamblador 1.1

En esta línea estamos configurando los fusibles. Esto fue explicado en la sección de “directivas”.

ejemplo programa ensamblador 1.2

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.

ejemplo programa ensamblador 1.3

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.
ejemplo programa ensamblador 1.4

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:
cuadro acceso bancos registros PIC16F628A

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.

ejemplo programa ensamblador

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.

ejemplo programa ensamblador

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:

ejemplo programa assembler 1.7

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.

ejemplo programa ensamblador

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.

ejemplo programa ensamblador

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.

icono programa pic delayer

Bueno, ejecuta el PIC Delayer y se verá está ventana:

abriendo programa pic delayer

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:

código ensamblador programa pic delayer

Ahí ya está la subrutina creada, ahora cópiala y pégala en tu código fuente. De esta manera:

subrutina pic delayer

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: 

requerimientos código pic delayer

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