Sunteți pe pagina 1din 7

Antonio Sánchez facebook:Programando PICs

EL ASSEMBLER EN LOS PIC16F


Esta serie de artículos no pretende ser un manual extensivo de programación en
ensamblador de los PIC16. Está orientado a los miembros de este grupo, los cuales ya
poseen conocimientos de estos microcontroladores en lenguajes de alto nivel y tienen
inquietud por iniciarse en el ASM.

Por ello, obviaré muchas cosas que ya seguramente todos tenemos por sabidas y me
ahorraré prólogos e introducciones innecesarias.

Para programar en ensamblador para los PIC16F realmente necesitamos pocas cosas:
Proteus o MPLAB en cualquiera de sus versiones incluyen el compilador necesario. Yo
uso y aconsejo tener en vuestro ordenador abierto los siguientes elementos:
– El entorno con el que vayáis a programar(MPLAB,Proteus...)
– El archivo .pdf con el datasheet del PIC16F en cuestión (importante)
– El archivo .inc del mismo microcontrolador (si tenéis instalado Notepad++ mejor
abrirlo con el mismo) El archivo .inc lo encontraréis en la carpeta del MPASM.
– La calculadora ;-)
– El manual en pdf de MPASM también puede venir bien.

Sólo unas consideraciones antes de meternos en faena:


– los programas en ensamblador sí dependen mucho del microcontrolador usado.
Aunque siempre se puede cambiar un programa para que funcione con otro PIC,
no es tan sencillo como en un lenguaje de alto nivel.
– las directivas son indicaciones que le damos al compilador y no son lo mismo que
las instrucciones (código máquina) de nuestro PIC. Los PIC16F sólo tienen 49
instrucciones y los más antiguos sólo 35, pero directivas del MPASM hay más de
60.
– en ensamblador hay que usar goto nos guste o no. No se parece a lenguajes
estructurados y elegantes donde los goto están casi prohibidos. De hecho, por muy
bonito que nos quede un programa en C o PASCAL, con sus llaves o corchetes,
una vez compilado estará lleno de goto por todas partes.
– En MPASM los comentarios (remarks) se inician con punto y coma (;) y acaban al
final de la línea.

La estructura de un archivo en ensamblador no difiere mucho de uno en C, vamos a ver


cada uno de los elementos antes de entrar en detalle a lo largo de los artículos:
1- La lista de opciones (LIST): también se puede traducir como opciones de listado. En
esta primera línea vamos a especificar una serie de opciones para nuestro compilador:
microcontrolador que vamos a usar, base numérica predeterminada (radix), formato del
archivo .hex... En muchas ocasiones esta línea no es necesaria, debido a que la mayoría
de las veces ya le indicamos al entorno de programación mediante menús y otros modos
los datos esenciales, y dicho entorno se lo pasa al compilador a través de la línea de
comandos.
2- Los #include: MPASM necesita que le especifiquemos el archivo del cual tomará las
constantes que usaremos en nuestro programa, sobre todo nombres y direcciones de los
registros del micro. Si vamos a usar librerías o módulos externos (en asm también se
puede) lo debemos especificar con su #include correspondiente.
3-El/los __config: los PICs incorporan uno o varios registros donde se almacena la

El Assembler en los PIC16F 1 Capítulo 1


Antonio Sánchez facebook:Programando PICs

configuración esencial de nuestro microcontrolador. Esta configuración suele incluir la


protección contra lectura de la memoria(importante), la fuente del reloj del sistema, si el
PIC debe reiniciarse por un overflow en la pila... Además dicha configuración no puede ser
modificada mediante firmware y nos puede dar algún dolor de cabeza si no la tenemos
correctamente ajustada.
4- Las constantes: mediante la directiva #define podemos especificar constantes para que
nuestro código resulte más legible. Por ejemplo #define ZERO STATUS,Z nos permitirá
escribir instrucciones usando la palabra ZERO en lugar de STATUS,Z.
5- Las macros: En MPASM las macros se usan comúnmente para reemplazar varias
instrucciones con una sola línea: a la hora de hacer el preprocesado, se
sustituyen(expanden) estas macros por el conjunto de instrucciones que hayamos
definido.
6- Las variables: aquí la cosa empieza a ponerse más compleja en ensamblador que en
los lenguajes de alto nivel. Debemos recordar que los PIC16F son micros de 8 bits, por lo
que debemos “olvidarnos” de variables long,float... Además, para MPASM los nombres de
nuestras variables no son más que constantes que sustituyen una dirección de memoria
determinada, por lo que habrá que indicarle la posición de memoria donde queremos ir
ubicando nuestras variables, y aquí empieza lo bueno: la partición en bancos de la
memoria RAM de nuestros PICs no nos da muchas facilidades.
7-El código propiamente dicho: aunque con la directiva ORG podemos ir cambiando la
posición de memoria FLASH donde se va almacenando nuestro código, esto generaría
particiones y huecos vacíos en la memoria que luego sería casi imposible de encontrar.
En assembler, lo mejor es empezar por la posición 0 (llamada vector de Reset),generar un
salto hacia el inicio de nuestro programa (un simple goto Inicio) y en la posición de
memoria 4 colocar nuestra rutina de interrupción.
8-Directiva end: aunque parezca una obviedad, si al final de nuestro código no ponemos
end, el compilador nos dará un error.

Aquí incluyo un trozo de programa en ensamblador con las partes que he comentado.
;Lista de opciones (tipo de procesador, base numérica y mostrar las advertencias y errores)
LIST p=16F1933,r=HEX,w=1

;Este include del microcontrolador es necesario en MPASM, además podemos incluir otros ficheros
#include p16f1933.inc ; Include register definition file

;Configuración del microcontrolador, como este PIC lleva dos registros se especifican por separado
__config _CONFIG1,_FOSC_INTOSC & _WDTE_OFF & _PWRTE_ON & _MCLRE_OFF & _CP_ON & _CPD_ON & _BOREN_ON
& _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF
__config _CONFIG2,_WRT_OFF & _VCAPEN_OFF & _PLLEN_OFF & _STVREN_ON & _BORV_HI & _LVP_OFF

;Constantes
#define ZERO STATUS,Z
#define CARRY STATUS,C

;Macros
MOVLWF macro literal,file
MOVLW literal
MOVWF file
endm

;Variables (en este caso le indicamos que las variables empiezan el la dirección 32 del banco 0)
cblock 0x20
LINE
endc

;Código (empezamos a compilar en la dirección 0 y saltamos al inicio de nuestro programa)


; Reset Vector
RST code 0x0
goto Start

El Assembler en los PIC16F 2 Capítulo 1


Antonio Sánchez facebook:Programando PICs

;Código de nuestras interrupciones (en los PIC16F siempre es la dirección 4)


ISR org 0x04
MOVF PORTC,W
ANDLW 0xF0
BTFSC ZERO
GOTO Rotate
CALL ReadKey
NOP
Rotate:LSLF LINE,F
BTFSS LINE,4
GOTO $+3
MOVLW .1
MOVWF LINE
MOVF LINE,W
MOVWF PORTC
BCF PIR3,TMR4IF
RETFIE

;Aquí empiezan las primeras instrucciones que se ejecutarán de nuestro programa


PGM code
Start
banksel OSCCON
MOVLWF b'01111011',OSCCON
MOVLWF 0xF0,TRISC

banksel PIE3
CLRF PIE1
CLRF PIE2
MOVLWF 1<<TMR4IE,PIE3

banksel PORTA
CLRF PIR1
CLRF PIR2
CLRF PIR3
MOVLWF (1<<GIE|1<<PEIE),INTCON

banksel T4CON
MOVLWF b'00100110',T4CON

banksel PORTA
MOVLWF .1,LINE


end

El Assembler en los PIC16F 3 Capítulo 1


Antonio Sánchez facebook:Programando PICs

La directiva LIST
Como comentamos antes, las diferentes opciones del compilador se ajustan mediante las
llamadas directivas. Existen 6 tipos de directivas, aunque muchas raramente se usan. Una
de ellas, y que se suele usar en la primera línea de nuestro archivo .asm es LIST, que nos
permite resumir en un sólo renglón una serie de opciones, sobre todo relacionadas en
cómo se listará nuestro archivo compilado.
Vamos a describir las opciones de esta directiva con un ejemplo:
LIST p=16F1933,r=HEX,w=1,f=INHX8,x=ON

Como se puede ver, delante de LIST no se pone almohadilla o guión bajo, las opciones se
separan con coma y entre el nombre de la opción y su valor se escribe un signo igual.

En este caso indicamos que el microcontrolador para el que vamos a compilar es un


PIC16F1933, se escribe el modelo del mismo, pero sin la palabra PIC. Esta opción se
puede escribir de forma independiente usando la directiva propia processor (ej. processor
16F1399).

La siguiente opción (r) indica la base numérica que vamos a usar de modo
predeterminado (decimal, hexadecimal u octal), es decir, la base en que el compilador
entenderá los números si no le añadimos ningún prefijo. Esta opción NO nos obliga a
introducir los número en ninguna base en concreto, sólo nos facilita introducir los números
de manera más cómoda en la base que queramos. Si usamos la directiva propia radix
podemos ir cambiando a lo largo del archivo de base numérica predeterminada (ej: radix
DEC)
Más adelante veremos los distintos prefijos para las diferentes bases numéricas.

Con w modificamos los mensajes que veremos durante la compilación, de modo que
podemos indicar si queremos que nos muestre todos los mensajes (w=0), sólo los errores
y advertencias (w=1) o sólo los errores (w=2) . Tiene su directiva propia errorlevel que se
puede ir añadiendo en el código, de manera que podemos seleccionar porciones de
código donde queramos ver todos los mensajes, sólo los errores,etc
La opción f hay que usarla con cuidado, o mejor dejarla por defecto. Con ella elegimos el
formato de salida del archivo .hex generado. Lo normal es dejar que el IDE o entorno de
programación escoja el más adecuado para el grabador o simulador que estemos usando.
Por último veremos la opción x que se usa para activar (ON) o desactivar (OFF) la
expansión de macros en el listado. Suena raro, pero es sencillo: durante la compilación se
genera un archivo .lst donde ya se han sustituido las constantes y asignado las posiciones
de memoria de programa. Este archivo es muy útil en la depuración, pero puede llegar a
ser bastante largo, por lo que a veces se desactiva la expansión de macros para hacerlo
más legible. Recordemos que las macros en assembler se usan para sustituir una serie
de instrucciones por una sola línea. Esta opción también tiene directivas propias expand y
noexpand de modo que podamos indicar qué macros se expanden y cuáles no, a lo largo
del código.

Hay algunas opciones más en la directiva LIST, que son principalmente para dar formato
al archivo .lst, pero como vimos en el apartado anterior, generalmente todas estas
opciones vienen por defecto o las podemos modificar de manera gráfica en el IDE, por lo
que no le dedicaremos más tiempo.

El Assembler en los PIC16F 4 Capítulo 1


Antonio Sánchez facebook:Programando PICs

#include
Al igual que en lenguajes de alto nivel, es importante indicar los archivos adicionales que
vamos a usar, y, sobre todo, el .inc del microcontrolador en cuestión para el que compilar
nuestro proyecto.
Hay que tener en cuenta que es como si pegásemos todo el archivo de texto que
incluimos en el mismo punto del #include, por lo que hay que tener cuidado con los
nombres repetidos de constantes o subrutinas, o con posiciones absolutas de memoria,
ya que nos puede generar errores.
#include p16f1933.inc

Como vemos en el ejemplo, no es necesario incluir el nombre del archivo entre comillas,
pero SÍ es obligatorio si dicho nombre contiene espacios. Si el archivo que vamos a usar
no se encuentra en la ruta del MPASM o en el directorio de trabajo de nuestro proyecto,
deberemos especificar la ruta completa.

Por ejemplo, podemos tener un archivo que se llame Segmentos.inc donde hemos
especificado cómo se dibujarán los números y letras del alfabeto en un display de 7
segmentos, e incluirlo con la directiva #include para nuestro proyecto actual.

Puede usarse include sin almohadilla delante, pero no se aconseja, ya que en próximas
versiones del MPASM puede dejar de estar soportado.

__config
Aquí vemos lo especial que es MPASM con los prefijos en las directivas: __config lleva un
doble guión bajo delante, aunque para los PIC18 es aconsejable usar config sin guiones,
que es una directiva diferente.
Los bits de configuración son muy diferentes de un PIC a otro, es por ello que aconsejo
tener abierto el .pdf del datasheet y el .inc de nuestro microcontrolador, mientras
programamos. Veamos el ejemplo en un PIC16F:
__config _CONFIG2,_WRT_OFF & _VCAPEN_OFF & _PLLEN_OFF & _STVREN_ON & _BORV_HI & _LVP_OFF

Aquí la directiva es __config (con 2 guiones), el resto de componentes (con un sólo guión)
son constantes definidas en el .inc: _config2 indica que los bits siguientes se almacenan
el registro 2.
Los bits de configuración funcionan como “fusibles” que el grabador apaga durante la
programación del dispositivo, por ello usamos todas las opciones “unidas” por el operador
& (and), de manera que esta operación genera una palabra del tamaño correcto que se
graba en el registro de configuración, en este caso _CONFIG2.
Como podemos ver, entre la directiva y el registro de configuración sólo se pone un
espacio, y entre este registro y los fusibles se pone una coma.
Si nuestro PIC sólo tiene un registro de configuración, no es necesario especificarlo, el
resultado sería entonces así:
__config _WRT_OFF & _VCAPEN_OFF & _PLLEN_OFF & _STVREN_ON & _BORV_HI & _LVP_OFF

Otra manera, si no nos gustan las líneas tan largas, es realizar la operación AND de todos
los valores y poner directamente el resultado del registro:
__config _CONFIG1,3FC0

El Assembler en los PIC16F 5 Capítulo 1


Antonio Sánchez facebook:Programando PICs

Si, durante la compilación, os aparece un mensaje como el siguiente:


Message[303] ..\MAIN.ASM 16 : Program word too large. Truncated to core size. (DAFF)

No debéis preocuparos, lo que indica el compilador es que hemos generado un valor de


16 bits y el registro de configuración es menor (generalmente de 14 bits),pero no es
ningún error.

Para los PIC18 se puede usar la manera anterior, pero NO se aconseja, vamos a ver la
manera correcta de establecer los bits de configuración en esta gama de micros.
CONFIG CP0=OFF, WDT=ON

Aquí apreciamos que se usa config sin guiones y que además se emplea la dupla
ajuste=valor, bien separadas por comas o en líneas diferentes.

Todos los bits de configuración se explican en el datasheet del PIC en cuestión, además
al final del archivo .inc están también todos los valores de los fusibles y las direcciones de
los registros, por lo que no hay que preocuparse de recordar esto de cabeza.

También podemos usar MPLAB para generar estas directivas de manera gráfica y
sencilla, dentro del apartado Production>Set Configuration bits. Es importante pegarlas en
nuestro código una vez generadas por el asistente, sobre todo si queremos simular en
Proteus o compilar con otra herramienta.

Es importante tener bien ajustados los bits de configuración, esto no es sólo de ASM, sino
de cualquier lenguaje que usemos para programar los PIC. Así, por ejemplo, evitaremos
que el watchdog timer nos reinicie nuestro PIC “inexplicablemente”, o que por una patilla
que pensamos que es una entrada nos salga la frecuencia del reloj ;-)

Las Constantes
Aparte de a los valores numéricos o de cadena que no varían, en assembler también se
denominan constantes al símbolo o nombre que le damos para referirnos a esos valores.
Esto también se usa en lenguajes de alto nivel, así que lo explicaremos brevemente con
un ejemplo: si durante nuestro programa escribimos repetidas veces PORTB,1, que es el
pin donde queremos encender un led, y luego queremos modificar nuestro programa para
usar otro pin en lugar de ese, tendremos que buscarlo por todo nuestro archivo y
sustituirlo. Lo mejor es declarar una constante a la que le damos como nombre, por
ejemplo, PIN_LED y asignarle el valor PORTB,1, de este modo si queremos modificar el
pin al que conectamos el led, sólo debemos cambiar la asignación de la constante.
BSF PORTB,1 #define PIN_LED PORTB,1
… …
BCF PORTB,1 BSF PIN_LED
… …
BTFSS PORTB,1 BCF PIN_LED

BTFSS PIN_LED

Esto, además, nos produce un código más inteligible y fácil de corregir, ya que usando un
nombre adecuado es más sencillo que andar recordando los pines donde conectamos
cada cosa. El nombre de la constante y la cadena que sustituye sólo se separan con un
espacio.

El Assembler en los PIC16F 6 Capítulo 1


Antonio Sánchez facebook:Programando PICs

Debemos tener en cuenta que usando #define, lo que hacemos es una sustitución de
cadena por otra, por lo tanto hay que tener cuidado con que esta sustitución no nos
genere errores de sintaxis.
Junto a la directiva #define existe también #undefine que suprime esa asignación o
sustitución de una cadena por otra, por lo que, a partir de #undefine PIN_LED, ya no
podremos usar PIN_LED, a menos que volvamos a definirla, dándole incluso otro valor si
queremos, con un nuevo #define.
Es curioso que he visto códigos donde se usa #define para “definir” variables en
assembler, pero esta no es la manera correcta, ya que las sustituciones que hagamos con
esta directiva no aparecerán en la tabla de variables de MPLAB o de PROTEUS.
Las constantes declaradas con #define se usan sobre todo en compilado condicional, que
veremos en artículos más avanzados.
Por último, en relación a las constantes, se puede incluir una constante dentro de la
declaración de otra constante, por lo que el siguiente código, es perfectamente válido.
#define ESTADO STATUS
#define ZERO ESTADO,Z
#define CARRY ESTADO,C

En próximos artículos seguiremos viendo en detalle el resto de las partes, 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 7 Capítulo 1

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