En esta entrada hablaré de una muy importante como lo es el Stack o Pila, así como de las instrucciones, ya conocidas, PUSH y POP, las llamadas a subrutinas y cómo generar retardos por software.
//*****************************************************************************************************
//*****************************************************************************************************
La Pila en Atmel: Esta estructura es diferente a la de otros uC (por ejemplo los PIC) ya que no cuenta con un hardware dedicado, esto es porque tenemos la libertad de usar toda la capacidad de la RAM ( por ejemplo en un atmega324p tendríamos como máximo 2K posiciones de pila) . Para poder usar la pila ésta debe ser definida al inicio de cada programa en el cual se use instrucciones como: (RCALL, ICALL, CALL, PUSH, POP, RET, RETI). La declaración de la pila se hará colocando el valor de inicio de la misma en los registros SPH y SPL (Stack Pointer High y Stack Pointer Low). El valor del SP irá decrementando a medida se llena la pila, y aumentará a medida se va liberando.
Particularmente recomiendo iniciar el SP a la posición final de la RAM, así no tendremos problemas con las posiciones de memoria que usemos como variables.
En el ejemplo mostrado arriba se puede ver cómo definir la pila a la posición final de la RAM. RAMEND ( Fin de la RAM) es una constante que se encuentra definida en .include "mxdef.inc" y es diferente para cada uC. Por ejemplo en el atmega8 RAMEND=0x45F=1119.
1119 bytes es mayor que 1K=1024bytes, esto es ya que en la RAM están los 32 Registros de trabajo con los que cuenta este uC además de los registro de entradas/salidas que sumados dan 0x5F=95 bytes. 32 (registros) + 63(reg i/o) + 1024(SRAM) = 1119 bytes.
LDI R17,HIGH(RAMEND) // R17 <-- 0x04
- Carga Inmediato al registro R17, la funcion HIGH nos devuelve el byte alto de RAMEND= 0x45F
---> HIGH(RAMEND)= 0x04
LDI R16,LOW(RAMEND) // R16<-- 0x5F
OUT SPH,R17 // Pone R17 en SPH
OUT SPL,R16 // Pone R16 en SPL
Finalmente la pila queda definida : SP=0x45F.
RETARDOS:
~~~~~~~~~~
Crear rutinas de retardos siempre es necesario, para ello crearemos una rutina general que nos permita obtener retardos en ms.
La idea general es anidar rutinas de este tipo.
Por ejemplo: si tenemos f=16MHz y queremos una rutina de 1ms.
1ms= (V)(X+4)/16MHz--> para V=250, X=60. X=60 significa que nuestra rutina tendrá 60 instrucciones NOP, para evitar esto aplicaremos el paso anterior a 60 --> 60=(V1)(X1+4) --> V1=12 y X1=1; finalmente obtenemos una rutina que para f=16MHz demora 1ms.
Si queremos hacer esto aún más general tendremos que anidar lo anterior en otro bucle, obteniendo lo siguiente:
Para usar esta rutina tendremos que cargar el valor del retardo en R20 y llamar a la etiqueta RETms.
El siguiente programa hará parpadear un led con algunos retardos.
Si este ejemplo lo simulan con AVR Studio o Proteus pueden ver cómo en el final de la RAM se irán guardando los valores del PC al llamar a la rutina de retardo.
Aquí podemos ver el inicial valor de SP.
Aquí vemos PC=0x000008, en la siguiente instrucción PC=0x000009 se guardará en la última posición de la RAM o memoria de datos
Aquí PC saltó a la etiqueta RETms y la dirección 0x000009 se guardó en el final de la RAM.