Sunteți pe pagina 1din 6

Antonio Sánchez facebook:Programando PICs

LAS MACROS
Observemos estas porciones de código:
call EsperaRespuesta JNE MACRO Caracter,Direccion
MOVF 0x11, W movf SSPBUF,W
SUBLW 0x1 sublw Caracter
BTFSS STATUS, 0x2 btfss STATUS,Z
GOTO otro goto Direccion
ENDM
call EsperaRespuesta
MOVF 0x11, W call EsperaRespuesta
SUBLW 0 JNE SPIProtOK,otro
BTFSS STATUS, 0x2 call EsperaRespuesta
GOTO InitCard JNE SPIOk,InitCard
call EsperaRespuesta
call EsperaRespuesta JNE SPIOk,MMCError
MOVF 0x11, W
SUBLW 0
BTFSS STATUS, 0x2
GOTO MMCError

En el lado izquierdo podemos ver un código en assembler para un PIC16 (en este caso es
de un programa que lee una tarjeta microsd). A la derecha vemos el mismo código usando
macros de MPASM.
Como podemos ver, las macros nos permiten generar varias líneas de código usando una
sola llamada a la misma.
Esto nos proporciona un código más sencillo, fácil de leer y sobre todo, podemos localizar
los errores más rápidamente.
El formato de una macro es muy sencillo, primero ponemos el nombre de la macro, a
continuación la directiva macro y luego los parámetros(argumentos) que queramos.
En el ejemplo podemos ver que la macro la llamaremos JNE (le puse así por Jump if Not
Equal) y le pasamos los parámetros Caracter y Direccion.
En el interior o cuerpo de la macro colocamos las instrucciones que deseemos, usando
los argumentos como creamos convenientes y cerramos la macro con la directiva ENDM.
Hay que tener en consideración las siguientes cosas sobre las macros:
-Las macros son “sustituciones de texto”: durante el preprocesado, se reemplazan las
llamadas a las macros por el código del cuerpo de la misma. De igual modo ocurre con
los argumentos.
-Las macros deben definirse antes de poder ser usadas, por lo tanto, es aconsejable
diseñar nuestras macros antes del código en nuestro archivo. También podemos crear un
archivo .inc con las macros que más usemos e incorporarlas a nuestro programa con el
#include adecuado.
-Las macros NO SON funciones, ni rutinas, ni nada que se le parezca. No nos engañe el
hecho de que tengan argumentos o se invoquen de manera parecida a como llamamos a
una función. Por lo tanto, las macros no generan código ni ocupan espacio de programa
por sí mismas. Si hemos definido una macro, pero no la invocamos, no se compila.
-De la consideración anterior debemos concluir que una macro no se coloca en una
posición de memoria y se acude a ella cada vez que se llama, ni los parámetros se pasan
usando la pila de RAM del microcontrolador.
-Por lo tanto, si definimos una macro que genera 5 instrucciones y la llamamos mil veces,
en realidad estamos generando 5 mil instrucciones, con el espacio que estas ocupan. De
hecho el código de ejemplo arriba a la izquierda ha sido producido, en tiempo de
compilación, por las macros del código de la derecha, únicamente lo he copiado del
archivo .lst

El Assembler en los PIC16F 1 Capítulo 2


Antonio Sánchez facebook:Programando PICs

Aquellos que están acostumbrados a usar C para los PIC habrán usado, seguramente,
__delay_ms(). Pues bien, al menos en XC8, esto es una macro de C que calcula los ciclos
necesarios (de ahí que necesite que le especifiquemos _XTAL_FREQ) y sustituye esta
llamada por los bucles necesarios para generar el retardo.

Aun así, las macros son más potentes de lo que podamos pensar, gracias principalmente
a 2 elementos que podemos usar en las mismas:
-Variables internas (locales): es posible crear variables que sólo existirán en el cuerpo de
la macro y con las que podremos operar para generar código más fácilmente.
-Paso de valores de variables:se puede usar el valor de una variable para generar
etiquetas (direcciones de memoria) dinámicamente.

Supongamos que queremos crear en la memoria de nuestro PIC una tabla con los
primeros valores de la sucesión de Fibonacci:
Fibonacci MACRO ;Nombre de la Macro
local ant1,ant2,actual,contador ;Variables locales
ant1 set 0 ;Inicializamos
ant2 set 1
contador set 2
Numero0 dt 0,0 ;La serie de Fibonacci comienza con 0
Numero1 dt 0,1 ;Los 2 primeros valores los insertamos antes del bucle
while actual<0xEFFF ;Comienzo del bucle
contador ++ ;Incremento
actual set ant1+ant2 ;Sumamos los valores anteriores
Numero#v(contador) dt HIGH(actual),LOW (actual) ;Generamos la tabla: etiqueta y valores
ant1 set ant2 ;Actualizamos
ant2 set actual
endw ;Cierra el bucle while
ENDM ;Fin de la macro

Fibonacci ;Si no invocamos la macro, no se produce código

Cuando compilemos, MPASM almacenará en la memoria de programa, byte tras byte, los
valores de la serie de Fibonacci en el formato ByteAlto+ByteBajo
Hemos usado variables locales,que sólo existen para la macro, con la directiva local, y
generado etiquetas con el nombre Numero1, Numero2,Numero3...de manera dinámica
con el evaluador #v(). Estas etiquetas apuntan a la dirección de memoria donde se
almacena dicho número, por lo que podemos acudir a ellas con un CALL Numero3, por
ejemplo.
Por cierto, NINGUNA de las palabras de este código son instrucciones del PIC, TODAS
son directivas del MPASM o nombres de variables, de hecho si compilamos y grabamos
este programa en el PIC, se almacenará en la memoria nuestra tabla, pero el
microcontrolador no hará absolutamente nada, supongo que sólo reiniciarse
constantemente.

El Assembler en los PIC16F 2 Capítulo 2


Antonio Sánchez facebook:Programando PICs

LAS VARIABLES
Ya hemos visto casi todas las partes o secciones de un archivo .asm: lista de opciones,
includes, configuración del microcontrolador, constantes y macros.
Todas estas partes son similares para todos los PICs de 8 bits. Salvo los fusibles de
configuración, no hay grandes diferencias entre ellas.
En el tema de las variables para nuestro programa,la cosa empieza a complicarse, como
comentábamos en el primer capítulo, la partición de los bancos de RAM de los PICs, y las
diferencias entre los los PICs 16F y 18F hace que declarar variables para nuestro
programa en ensamblador requiera cierto cuidado.

Aun así, se pueden tener en cuenta varios aspectos comunes:


-La forma más usual de declarar una variable es con la directiva equ, aunque
curiosamente esta directiva no es para declarar variables, sino constantes o símbolos. De
todas formas es válida ya que en asm lo que necesitamos realmente es una etiqueta que
apunte a una dirección de RAM.

-Aunque equ sirva para programas sencillos (más adelante veremos lo que significa
código absoluto) para programas donde tenemos porciones de código en varios archivos
enlazados(código reubicable) debemos usar la directivas idata y udata.

-A pesar de que existe la directiva variable, esta NO se usa para crear variables para el
funcionamiento de nuestro programa, sino variables en tiempo de compilación, como las
que usamos con local en el ejemplo de la Sucesión de Fibonacci.

-Equ no nos reserva memoria RAM para nuestra variable, sólo nos hace de etiqueta para
referirnos a una posición (un sólo byte) de RAM. Por lo tanto podemos llamar al mismo
byte de RAM con diferentes nombres poniendo todos los equ que queramos.

-Una manera más práctica para reservar memoria es usar cblock(bloque de constantes)
que nos permite indicar sólo la primera posición de RAM e ir añadiendo nombres de
variables que se posicionarán automáticamente.

-En asm no existe la inicialización de variables. Aunque se puede usar la directiva idata
para declarar variables inicializadas, esta directiva nos almacena en la memoria de
programa los valores iniciales y nosotros tenemos que cargar estos valores mediante
código.

-Tampoco tenemos variables locales, globales, compartidas...Es importante tener en


mente que en assembler accedemos directamente a posiciones de memoria RAM;
además, en nuestros micros de 8 bits, sólo accedemos a un byte cada vez, por lo que No
hay variables integer, long, float...

El Assembler en los PIC16F 3 Capítulo 2


Antonio Sánchez facebook:Programando PICs

Veamos un ejemplo de las diferentes formas de declarar variables con MPASM, las he
ordenado de menos “correcta” a más:

;Usando #define NO nos aparece Char


;en las tablas de variables de MPLAB o PROTEUS
#define Char 0x20
;Aunque usando variable funcione en programas sin linkado o enlazado,
;en los de código reubicable nos fallará ya que MPASM cree que es una variable de preprocesado
variable Varia3 =0x21
;Esta es correcta, aunque no se suele usar debido a que set permite modificar el valor
;de la constante, por lo que podríamos escribir 2 veces el mismo nombre
;por error con diferentes direcciones y MPASM no nos avisaría
Chr set 0x22
Chr set 0x23 ;Aqui repetimos el nombre, pero con la directiva set MPASM no da error
;Usar equ es correcto, además es lo más común,
;aunque nos obliga a especificar la dirección cada vez ya que no reserva memoria
Byte equ 0x24
;Aquí repetimos dirección de RAM, podemos usar, por ejemplo, el nombre Integer
;para referirnos a los 16 bits e Int_ByteAlto sólo a los 8 bits superiores
Integer equ 0x25
Int_ByteAlto equ 0x25
;Aquí apreciamos que para MPASM estos nombres no son más que etiquetas a una
;dirección RAM: asigna a Int_ByteBajo la dirección 0x26 (la dirección de Int_ByteAlto + 1)
Int_ByteBajo equ Int_ByteAlto+1
;cblock(bloque de constantes) es la más correcta para declarar variables en código "absoluto"
;Sólo le especificamos la primera dirección y MPASM incrementa automáticamente las posiciones,
;además le podemos indicar que nos reserve otra cantidad de bytes. Termina con endc
cblock 0x30
Word:0 ;Reservando 0 bytes hacemos coincidir la etiqueta Word con Word_ByteAlto
Word_ByteAlto ;Si no especificamos nada nos reserva un byte
Word_ByteBajo
Long:4
String:10 ;MPASM recuerda las asignaciones que hace: si más adelante ponemos otro cblock
endc ;sin indicar dirección inicial, continuará por la posición correcta

El Assembler en los PIC16F 4 Capítulo 2


Antonio Sánchez facebook:Programando PICs

Los diferentes PICs de 8 bits


Una vez tenemos preparado nuestro archivo .asm es el momento de empezar a escribir
código, así que vamos a echar un vistazo a las diferentes series de PICs de 8 bits que
vamos a tratar en estos artículos.

Todos los PICs que vamos a tratar son microcontroladores RISC, es decir, juego de
instrucciones reducidas. Realmente, a lo que se refiere es que el conjunto de
instrucciones de código máquina comprensibles es pequeño. Los PIC16F de gama media
o mid-range soportan sólo 35 instrucciones diferentes, los mejorados o enhanced admiten
49, y los 18F reconocen 76.

Todos son de arquitectura Harvard-modificada: tienen bus de datos y bus de programa por
separado; en lugar de incorporar un sólo bus por donde se reciben tanto datos, como
instrucciones. Esto es importante, ya que ambos buses, al ser independientes, pueden ser
de diferentes bits de anchura, de hecho los PIC16F, aunque son micros de 8 bits, tienen
un ancho de bus de instrucciones de 12 o 14 bits, según el modelo, lo que permite
acceder a instrucciones más largas (opcode) de una sola lectura. Como ventaja, la
arquitectura Harvard de los PICs permite un mayor rendimiento por ciclo de reloj. Como
desventaja, al estar la memoria de programa separada de la de datos, los accesos a
constantes o tablas en flash han de realizarse mediante registros o instrucciones
especiales.

Todos tienen la memoria particionada en bancos: los 16F en bancos de 7bits de tamaño, o
sea, que cada uno tiene sólo 128bytes de RAM; y los 18F en bancos de 8 bits con 256
bytes de capacidad. Así que, aunque nuestro PIC tenga, por ejemplo, 1KiB de RAM, este
está fraccionado, aunque hay diversas maneras de acceder a conjuntos de datos de
manera lineal. En un próximo capítulo estudiaremos la memoria RAM con detalle.

No existe pila de datos en RAM, los PICs incorporan una pila “de uso propio”, además
limitada, para almacenar exclusivamente las direcciones de retorno cuando hacemos una
llamada o CALL a otra posición de programa. Los lenguajes de alto nivel, crean una pila
virtual para el paso de parámetros a funciones usando un área libre de memoria RAM.

Todos requieren 4 pulsos de reloj por instrucción, es lo que se define como TCY o tiempo
de instrucción. Es por ello que cuando le conectamos a un PIC16F o 18F un cristal de
20MHz, por ejemplo, nos funciona a 5MIPS(Millones de instrucciones por segundo) o lo
que es lo mismo 20/4. Aunque no lo parezca, esto es un logro para un microcontrolador
de 8 bits, hay micros que requieren 6 o 12 pulsos de reloj por instrucción.

Aunque hay más PICs de 8 bits (los 10F y los 12F por ejemplo), en estos artículos sólo
vamos a ver las tres gamas siguientes, si no, estos artículos se harían eternos:
-Gama media o MidRange: PIC16Fxxx y algunos más, no iba a tratar estos PIC porque su
reducido juego de instrucciones hace lentas las operaciones más básicas, pero he
observado que muchas personas aún usan los PIC16F887 y similares.
-Gama media mejorada o Enhanced: PIC16Fxxxx y 16Fxxxxx, también conocidos como
C-Compiler Optimized. Son los que mejor conozco y más uso por relación potencia/precio.
-PIC18F: los veremos a sugerencia de algunos miembros del grupo. Ciertamente, su
“core” o núcleo es más potente que el de los 16F.

El Assembler en los PIC16F 5 Capítulo 2


Antonio Sánchez facebook:Programando PICs

En próximos artículos veremos la estructura de RAM y de memoria de programa, así


como las instrucciones de máquina, las diferentes directivas, ejemplos...

All trademarks are property of their respective owners.


Article for educational purposes only.
No copyright infringement intended

El Assembler en los PIC16F 6 Capítulo 2

S-ar putea să vă placă și