Está en la página 1de 6

HERRAMIENTAS DE DESARROLLO

Es posible encontrar en la Internet diversos editores de texto, ensambladores,


compiladores en C, compiladores en visual Basic, simuladores y programadores.
ATMEL a través de su página proporciona el entorno de desarrollo AVRStudio, el
cual contiene las herramientas para capturar, ensamblar, simular y programar a todos
los modelos de AVRs disponibles.

GUÍA RÁPIDA PARA EL USO DEL ENTORNO AVRStudio VERSIÓN 4.10

Antes de comenzar con el entorno, primero veremos algunos puntos a considerar. La


memoria RAM está segmentada en tres regiones bien diferenciadas:

Ambos cuadros representan al mismo


bloque de memoria; el bloque de la
izquierda muestra las direcciones absolutas
de cada segmento; a la izquierda se
muestran las direcciones relativas de cada
segmento; en el ensamblador todas las
instrucciones que en sus operandos hagan
uso de los registros de trabajo y los
registros de I/O, vamos a referenciarlos
con su dirección relativa.

Algunas instrucciones que operan sobre los


registros de trabajo, sólo funcionan a partir
de R16, es necesario observar con cuidado
en el conjunto de instrucciones las
restricciones que puedan tener los
operandos.

La pila sirve para almacenar la dirección de


retorno en caso de ocurra una interrupción
o un llamado a subrutina. Una de las
principales funciones de la SRAM es la de
almacenar los datos de la pila, por lo cual
es conveniente inicializar el puntero de pila (SP) en alguna dirección de la SRAM.
Considerando que al guardar un dato en la pila el puntero decrementa su valor, es
recomendable inicializar el SP en la localidad más alta de la SRAM.

Otro punto a considerar es la dirección de los vectores de interrupción –en caso de


que nuestro programa incluya interrupciones-, por ejemplo, un RESET provoca una
ejecución de instrucciones a partir de la localidad $0000 de la memoria de programa,
esta dirección se define como el vector de RESET. Existen otros vectores o
posiciones de memoria de programa que son el destino tras una interrupción. Existe
una gestión de prioridades de interrupción; las de número de vector más bajo tiene
mayor prioridad (RESET es el de mayor prioridad). Por lo tanto, si hay coincidencia
de eventos, se atenderá al de mayor prioridad.
Es indispensable en cada vector (que se vaya a utilizar) colocar una instrucción de
salto incondicional hacia la posición de memoria de programa o etiqueta donde se
ubica la rutina de interrupción. La siguiente tabla muestra la prioridad y dirección de
cada vector de interrupción para el AT90S2313:

PRIORIDAD DE DIRECCIÓN DE VECTOR ORIGEN DE


INTERRUPCIÓN -MEMORIA DE PROGRAMA- INTERRUPCIÓN
1 $0000 RESET
2 $0001 INT0
3 $0002 INT1
4 $0003 TIMER1,CAPT1
5 $0004 TIMER1, COMP1
6 $0005 TIMER1, OVF1
7 $0006 TIMER1, OVF0
8 $0007 UART, RX
9 $0008 UART, UDRE
10 $0009 UART, TX
11 $000A ANA_COMP

Cada modelo de AVR tiene su tabla de vectores; es necesario revisar el manual del
dispositivo empleado, para conocer sus diferentes tipos de interrupciones y sus
vectores correspondientes.

La instalación del entorno no representa ninguna dificultad, sólo hay que dar doble
clic en el archivo AVRStudio4v410.exe y seguir las instrucciones del asistente de
instalación. Al ejecutar el entorno, veremos la siguiente pantalla inicial:
Presionamos el cuadro para crear un proyecto nuevo; en el cuadro de
Proyect Name: escribimos el nombre de nuestro proyecto, para este ejemplo lo
nombraremos cont_int; presionamos el cuadro de Next >>. Ahora seleccionamos la
plataforma depuradora y el dispositivo; en el cuadro izquierdo damos un clic sobre
AVR Simulator; en el lado derecho damos clic sobre el dispositivo que vamos a
programar, para nuestro ejemplo utilizaremos el AT90S2313. Presionamos el cuadro
Finish.

La ventana superior derecha es el editor de texto (el encabezado de la ventana es la


ruta donde se guardarán todos los archivos que se generen en nuestro proyecto),
sobre la cual escribiremos el código en lenguaje ensamblador de nuestro programa.

Después de escribir nuestro código, procedemos a guardarlo presionando el icono


Save de la barra de herramientas estándar. El siguiente paso es compilarlo
presionando el icono Build. Si nuestro código es correcto, al menos
sintácticamente, veremos en la ventana de Output -situada en la parte inferior de la
pantalla- un mensaje como el siguiente:

De existir algún error, en dicha ventana se mostrará un mensaje de error, si damos


doble clic sobre el punto rojo, automáticamente situará al cursor en la línea del
código donde existe el problema.
Una vez corregido el error, otra vez
guardamos y compilamos al código.
Antes de continuar con la simulación,
vamos a revisar la sintaxis
correspondiente al código del ejemplo:

Todo lo que va después del símbolo


punto y coma es considerado un
comentario; en este caso, es una breve
descripción de lo que hace el programa.

Las directivas del ensamblador se


escriben antecedidas por un punto. Las
directivas son útiles para el ensamblador
y no generan instrucciones ejecutables,
la directiva INCLUDE sirve para llamar
a las bibliotecas que contienen las
definiciones de registros del chip que
vamos a emplear; en la“2313def.inc” se
encuentran todas las definiciones de
registros y bits relevantes del AVR
AT90S2313, esto sirve para que en lugar de escribir la localidad de memoria de
algún registro escribamos su nombre. Esta directiva es necesaria, debido a que en un
principio, al crear un proyecto nuevo, indicamos que trabajaríamos con un
AT90S2313, y el entorno lo que “sabe” es como simular nuestro código para este
chip; sin embargo, si no se llama a las bibliotecas, el ensamblador carece de las
definiciones de registros.

La directiva def sirve para asignar nuestras propias definiciones de registros (estas
definiciones tiene prioridad sobre las bibliotecas), en el ejemplo, al registro r16 lo
definimos como contador; dentro del programa, cada vez que se necesite al registro
r16 lo podemos nombrar indistintamente contador o r16. Esta directiva sólo se
utiliza para los 32 registros de trabajo, si deseamos definir algún otro tipo de registro
o localidad de memoria de datos, utilizamos la directiva equ. En el ejemplo, la
localidad $18 corresponde al registro I/O portb.

La directiva cseg indica que el siguiente código debe escribirse en la memoria de


programa del AVR; la directiva dseg indica que el siguiente código debe escribirse
en la memoria de datos, y la directiva eseg es para la memoria eeprom. En el ejemplo
expuesto podríamos quitar la directiva cseg, ya que por defecto, es la que tiene
configurada el ensamblador.

La directiva org $xxx sirve para indicarle al ensamblador a partir de que localidad
de memoria necesitamos que escriba el código que esta después de la directiva, las
tres equis representan la dirección en notación hexadecimal.

La primera instrucción es un salto hacia la etiqueta INICIO, sin embargo, la etiqueta


de inicio está en minúsculas, pero el ensamblador no indica errores. Moraleja: el
ensamblador no distingue mayúsculas de minúsculas, lo cual es un dato importante a
tener en cuenta.
En la localidad $01 de la memoria de programa se encuentra el vector de
interrupción externa INT0, como nuestro programa utiliza esta interrupción, cuando
se genere una, la direccionamos con un salto hacia donde se encuentra la rutina de
interrupción.

En la siguiente línea observamos una directiva org, indicando que la escritura del
código subsiguiente en la memoria de programa será a partir de la localidad $00A.

La mejor manera de direccionar los saltos es colocando etiquetas en el destino


deseado, todas las etiquetas deben ser precedidas por el signo dos puntos. En este
caso, inicio es la etiqueta destino de un salto, donde continuará la ejecución del
programa, en el ejemplo tenemos la instrucción SEI, que habilita las interrupciones
globales.

La instrucción CLR está poniendo a ceros al registro r16, el cual será el encargado
de llevar la cuenta que sacaremos por el Puerto_B.

Con la instrucción SBR estamos poniendo a uno el bit_6 del registro R17.

A continuación tenemos la carga de datos inmediatos a los registros r18, r19 y r20.
Los datos inmediatos tiene diferentes formas de sintaxis, debido a que están en
diferentes bases numéricas: a r18 se le está cargando un número decimal, a r19 se le
está cargando un número binario, a r20 se le está cargando un número hexadecimal
(los hexadecimales también se pueden escribir poniendo el símbolo $ en lugar de
0x).

Con las instrucciones OUT cargamos registros I/O con datos de registros de trabajo
para configurar:

OUT MCUCR, r20 interrupciones activadas con el flanco de subida.


OUT SPL, r19 inicializando el puntero de pila.
OUT DDRB, r18 Puerto_B como salida.
OUT GIMSK, r17 habilitación de interrupción externa con el Pin_3 del Puerto_D.
OUT PUERTO_B, contador carga de r16 en el registro de datos del Puerto_B

La instrucción INC incrementa a r16 y después tenemos una instrucción JMP hacia
la etiqueta de ciclo, con lo cual refrescamos el valor del registro de datos del
Puerto_B. En este punto se forma un bucle hasta que se activa una interrupción.

Cuando hay una transición bajo-alto en el Pin_3 del Puerto_D, el vector de


interrupción externa direcciona la ejecución del programa hacia la etiqueta rutint,
donde la instrucción CLR pone a ceros al registro r16, y por último, la instrucción
RETI retorna a la siguiente instrucción del punto donde se interrumpió la ejecución
del programa.

El programa del ejemplo tiene una finalidad puramente didáctica, por lo cual no fue
optimizado. La intención fue dotarlo con los elementos más comunes necesarios para
elaborar una aplicación; para conocer más acerca del potencial del ensamblador,
podemos revisar los archivos de ayuda del entorno AVRStudio.
Una vez que tenemos una compilación exitosa, podemos cargar el programa en la
memoria del AVR, pero lo más recomendable es simularlo primero. Para iniciar una
simulación debemos presionar el icono Start Debugging, ubicado en la barra de
tareas.
La ventana Workspace ubicada a la
izquierda contiene todos los registros del
AVR, organizados en cuanto a su tipo.
Vamos a desplegar los registros de trabajo
16–31 dando un clic sobre el símbolo +.
Para introducir un dato inmediato, damos
doble clic sobre el registro de nuestro
interés, aparecerá una pequeña ventana en
la cual escribimos el valor del dato y
presionamos OK.

También vamos a desplegar PORTB y el


PORTD. En cada puerto se pueden
observar los tres registros a los que está
asociado, esto es, registro de
configuración (DDRx), registro de datos
(PORTx) y su registro de pines (PINx).
Para introducir valores sólo basta dar un
clic sobre la casilla del bit que deseamos
modificar.

Podemos comenzar a simular presionando el icono Run, empero, la velocidad de


ejecución es tan elevada que no permite observar la ejecución de instrucciones; para
remediar esto, podemos colocar el cursor en las instrucciones donde queramos hacer
una pausa y presionamos el icono Toggle Breakpoint. Para quitar un punto de
pausa, situamos el cursor en el punto y presionamos nuevamente Toggle
Breakpoint.

Otra forma es presionando Auto Step, ahora si es posible ver que instrucción se
está ejecutando, lo cual se indica con una fecha amarilla en la ventana de edición.
Para pausar la ejecución del simulador presionamos Break.

Con el icono Step Into se ejecuta sólo una instrucción, siendo posible observar
con detalle que sucede tras una ejecución. Para reiniciar la simulación presionamos
Reset.

Para el ejemplo, vamos a poner un punto de pausa en la línea donde se encuentra la


etiqueta de ciclo, presionamos Run; cada vez que presionemos Run la cuenta en el
registro PINB se incrementa. Ahora damos un clic sobre la casilla correspondiente al
Pin_2 del registro PIND y presionamos Step Into; se ha generado una interrupción y
la ejecución del programa se direcciona hacia la rutina de servicio de interrupción, la
cual limpia a r16, entonces la cuenta que sale por el registro PINB se reinicia. Como
las interrupciones están configuradas para activarse sólo con el flanco de subida del
Pin_2 del Puerto_D, sólo hasta que hagamos dicha transición volverá a generarse
una interrupción.