domingo, 14 de octubre de 2018

Stack o Pila - SP(Stack Pointer) ¿?

Qué es un Stack?
--------------------------------------------------------------------------------------------------------------------------
Un "Stack" es bloque consecutivo de datos almacenados por el programador. Este bloque de memoria puede ser utilizado tanto por el control interno del microcontrolador como por el programador para almacenar datos temporalmente. La pila funciona con un mecanismo tipo LIFO, lo último que se almacena en la pila es lo primero que se recupera de la pila.


Estructura de una Stack-LIFO(Proceso de PUSH y POP)
Esta pila tiene un apuntador conocido como Stack Pointer o SP, el funcionamiento de la pila en el microcontrolador AVR es muy sencillo, la posicion inicial es la dirección superior es decir que si la pila tuviera 100 espacios la primera posicion sería la 100, cuando se almacena un registro en la pila entonces el apuntador disminuye en 1 de manera que el siguiente dato se almacene en la siguiente posicion es decir para nuestro ejemplo sería en la posición 99 así sucesivamente, cuando se saca un dato el SP aumenta para poder sacar el dato siguiente.

La capacidad de la pila cambiará de acuerdo con la referencia del microcontrolador que se esté utilizando, pero en general los microcontroladores AVR necesitan 16 bits para direccionamiento de la Pila, debido a que el microcontrolador AVR es de 8 bits entonces se hace necesario utilizar dos registros para el SP, estos son el SPH y el SPL (high and low) y se refieren a los bits mas significativos y menos significativos respectivamente.



Una pila es un búfer (LIFO), que contiene una cantidad de elementos de datos generalmente implementados como un bloque de n bytes consecutivos.
La dirección del último elemento de datos que se colocará en la pila se señala mediante el puntero de pila (sp)

Aplicación de pilas:
- Almacenamiento temporal de variables.
- Almacenamiento temporal de direcciones de programas
- Comunicación con subrutinas.

La CPU necesita esta área de almacenamiento porque solo hay un número limitado de registros

Qué es un SP?
--------------------------------------------------------------------------------------------------------------------------
En un micro AVR, SP es un registro independiente para propositos de STACK.
SP es un registro especial donde se necesita para operaciones en STACK.
SP es de 16 bits de ancho y se implementa como dos registros que son SPH y SPL.
El registro SPH presenta el byte alto de SP mientras que el registro SPL presenta el byte inferior.
Tanto SPH como SPL tienen 8 bits de ancho.
El SP debe ser lo suficientemente ancho para dirigirse a toda la RAM.
En los AVR con más de 256 bytes de memoria, SP se compone de dos registros de 8 bits (SPL y SPH)
En AVR con menos de 256 bytes de memoria, SP está hecho de sólo registro SPL.

Registro SP

Operaciones en STACK(PUSH-POP)
--------------------------------------------------------------------------------------------------------------------------

>>>PUSH
      Es una intrucción para llenar de datos temporales en la memoria SRAM.
      1-->Se escribe en Stack el dato(en la parte de arriba) , memory[stack]<~~DR(registro de datos)
      2-->disminuye  SP, SP<~~SP-1

Estado inicial
 LDI R16,0x33
 Push R16

Después de Push R16



LDI R17,0x25
Push R17


Después de Push R17
LDI R18,0x20
Push R18
Después de Push R18



-Recordemos que la memoria en $DF es mayor que $DC($DF>$DC).
-En AVR, la pila(STACK) crece de una ubicación de memoria superior a una ubicación de memoria inferior.
-Así, es común inicializar el SP a la ubicación de memoria más alta.
-El puntero de Pila apunta al área de pila de la SRAM de datos donde se localizan la pila de interrupción y de subrutina. Este espacio de la pila en la SRAM de datos debe ser definido por el programa antes de que se ejecute cualquier llamada a subrutina o se habiliten interrupciones. El puntero de pila debe estar a set para apuntar por encima de $60.

>>>POP
         Es una intrucción para sacar  datos temporales de la memoria SRAM

         1-->Incrementa SP, SP<~~SP+1
         2-->Lee la posicición de stack(parte de arriba ) y copia al destino,                                           
                DR(registro de datos) <~~Memoria(Stack).

Para recuperar un byte de datos de la pila usamos la instrucción POP.

POP R18
Después de POP R18


POP R17
Después de POP R17

POP R16
Después de POP R16


Ejemplo de STACK?
--------------------------------------------------------------------------------------------------------------------------
1)
Este ejemplo muestra el STACK y SP(Stack Pointer) y el registro usado despues de la ejecución de cada instrución.

.INCLUDE "M32DEF.INC"
                   .ORG       0
                   LDI          R16,HIGH(RAMEND)
                   OUT         SPH,R16
                   LDI          R16,LOW(RAMEND)
                   OUT         PL,R16
                   LDI           R31,0
                   LDI           R20,0x21
                   LDI           R22,0x66
                   PUSH        R20
                   PUSH        R22
                   LDI           R20,0
                   LDI           R22,0
                   POP           R22
                   POP           R31 

Funcionamiento de STACK y STACK POINTER(SP)
2) 


(LLENADO DE STACK)

PUSH AX ; Coloca Ax en STACK
PUSH BX ; Coloca Bx en STACK
PUSH CX ; Coloca Cx en STACK
PUSH DX ; Coloca Dx en STACK
PUSH SI ; Coloca SI en STACK
PUSH DI ; Coloca DI en STACK
--------
--------; Código que modifica AX,BX,CX,DX,SI,DI
--------

(RECUPERACIÓN DE LOS VALORES DE STACK)

POP DI ; restaura el valor original de DI
POP SI ; restaura el valor original de SI
POP DX ; restaura el valor original de DX
POP CX ; restaura el valor original de CX
POP BX ; restaura el valor original de BX
POP AX ; restaura el valor original de AX

RAMEND?
--------------------------------------------------------------------------------------------------------------------------
RAMEND es una etiqueta que representa la dirección de la última ubicación de memoria en SRAM. Para usar esta etiqueta, debe asegurarse de incluir el archivo de encabezado de definición para el microcontrolador específico.
Las funciones low () y high () son utilizadas por el ensamblador para devolver el byte bajo y el byte alto respectivamente de una palabra de 16 bits. Recuerde que estamos tratando con un microcontrolador de 8 bits que solo puede manejar solo 8 bits a la vez. RAMEND es una palabra de 16 bits y por eso usamos las funciones para dividirla.

.include "M32DEF.Inc"
             LDI R16,low(RAMEND)
             OUT SPL,R16
             LDI R16,high(RAMEND)
             OUT SPH,R16

 Conclusión
--------------------------------------------------------------------------------------------------------------------------
                  

  • El puntero de pila es decrementado en 1 cuando los datos se introducen en la pila con la instrucción PUSH y es decrementado en 2 cuando una dirección se introduce en la pila con llamadas a subrutinas e interrupciones. El puntero de pila es incrementado en 1 cuando el dato se saca de la pila con la instrucción POP y es incrementado en 2 cuando una dirección se saca de la pila con retorno de subrutina RET o retorno de interrupción RETI.
  • El rango que abarcará nuestra Pila /Stack y Stack Pointer  depende del microcontrolador por ejemplo:



miércoles, 10 de octubre de 2018

Conceptos a tener en cuenta

                                                                 Registro DDRX
Registro que se usa para configurar si un pin o puerto X se configura como salida o como entrada.





En general un registro DDR de cualquier puerto , esta dividido en 8 bits, como se muestra el la figura siguiente:
Muchas veces se va a trabajar con los bits de cada registro para ello hay que tener en cuenta como se trabajan en lenguaje C. Aquí un un tutorial ==>>  Trabajo con Bits

-número binario de 8 bits ---> 0b00000000--->>> 0b bit7..........bit0
Ejemplos:
poner a 1 el Bit 0==>0b00000001=(1<<0)
poner a 1 el Bit 1==>0b00000010=(1<<1)
poner a 1 el Bit 2==>0b00000100=(1<<2)
poner a 1 el Bit 3==>0b00001000=(1<<3)
poner a 1 el Bit 4==>0b00010000=(1<<4)
poner a 1 el Bit 5==>0b00100000=(1<<5)
poner a 1 el Bit 6==>0b01000000=(1<<6)
poner a 1 el Bit 7==>0b10000000=(1<<7)

                                                                     Salidas Digital 
DDRX = DDRX|(1<<7)<==>DDRX = DDRX|(0b01000000)<==>DDRX|=(0b01000000)<==> DDRX|=(DDRX7)

---------------------------------------------------------------------------------------------------------------------
Explicación:
DDRX | (0b10000000) ==> Se realiza la operación OR entre el número binario y el registro DDRX, así se logrará que el Bit7 del resultado  se mantenga en 1 lógico en este caso.
y el resto de los Bits serán igual a los de DDRX, en conlusión:
si es que se quiere guardar en el mismo registro DDRX, entonces la situación final sería de la siguiente manera:
DDRX=DDRX|(0b10000000)
se pondrá a 1 si antes estaba en 0 y si ya estaba a 1 se mantendrá a 1, los otros Bits mantendrán sus valores ya que los otros Bits son 0.
---------------------------------------------------------------------------------------------------------------------

Si se desea que los pines 2,4 y 6 se pongan a 1 se puede hacer uno por uno como en el ejemplo anterior, o los 3 en una sola linea de código, veamos:

DDRX=DDRX|((1<<2)|(1<<4)|(1<<6))<==>DDRX=DDRX|((1<<DDRX2)|(1<<DDR4)|(1<<DDR6));

                                                                   Entrada Digital
Configurar el Bit 5 como salida digital:
DDRX&=~(1<<5)<==>DDRX=DDRX&~(0b00100000);
---------------------------------------------------------------------------------------------------------------------
Explicación:
recordemos que & es un operador AND y ~ es un operador negación.
(1<<5) = 0b00100000
~(1<<5) =0b11011111
DDRX&(0b11011111)==> se realiza la operación AND, el bit5 se mantiene en 0 lógico(entrada digital) y el resto como 1 lógico (salida digital).
guardamos el resultado como el nuevo valor del registro DDRX
DDRX=(0b11011111)==> El registro quedó con el Bit5, como único puerto de entrada digital.
--------------------------------------------------------------------------------------------------------------------- 
Se niega el número binario con lo que el bit0 se pone a 0 y todos sus demás bits se ponen a 1, luego se realiza la operación AND con el registro DDRB, con lo que se logra que el bit0 del registro DDRB mediante la operación AND se ponga a 0.

Si es que se requiere en una sola linea hacer que los Bit 2,4 y 6 del puerto B sean configurados como entradas digitales , se puede hacer de la siguiente manera:
DDRB& = ~((1<<2)|(1<<4)|(1<<6));
También puede ser así:

DDRB &=~((1<<DDB2)|(1<<DDB4)|(1<<DDB6));//se usa los nombres de los bits.

Recordemos que el registro DDRX sirve para configurar el puerto X como entrada o como salida.

                                                           Registro PORTX

Su funcionamiento esta ligado al registro anterior(DDR), Tiene dos modos de funcionamiento, mas adelante se verán mas detalladamente:



En general un registro PORT de cualquier puerto, esta dividido en 8 bits, como se muestra el la figura siguiente:
Tiene dos modos de uso según como esta configurado el Registro DDRX.

PRIMER MODO:

Si DDRX está configurado como salida, entonces PORTX funcionará para escribir un valor en el puerto, dicho de otra manera el registro PORTX servirá para poner en alto(1 lógico) ó en bajo(0 lógico) cada bits de ese puerto X.

Ejemplos:

PORTB = 0b10101010; // valor en decimal es de 170, se escribe el valor de 170 en el Puerto B.

PORTB|=(1<<0);//Solo el bit 0 se pone a 1 lógico.
PORTB|=(1<<4);//Solo el bit 4 se pone a 1 lógico.

En ATMEL STUDIO se puede hacer lo siguiente:

PORTB|=(1<<PB0);
PORTB|=(1<<PB4);
PORTB|=((1<<PB0)|(1<<PB4));Si se quiere poner a 1 lógico el bit 0 y bit 4;

PORTB& = ~(1<<0) ;Si es que se quiere poner a cero lógico el bit 0;
PORTB&=~((1<<0)|(1<<4)); Si se quiere poner a cero lógico el bit 0 y el bit 4;//////

SEGUNDO MODO:

Si DDRX está configurado como entrada digital(Bits en 0 lógico), el registro PORTX servirá para activar(1 lógico) o desactivar(0 lógico) las resistencias PULL UP internas al microcontrolador AVR

Ejemplos:

DDRB= 0x00=0b00000000;
PORTB=0b01010100; Activar las resistencias PULL UP de los bits 2,4,6 del puerto B.
PORTB|=(1<<2); activar la resistencia PULL UP del PORTB(bit2).
PORTB|=(1<<2)|(1<<4); activar las resistencias PULL UP del PORTB(bit2 y bit4).
En ATMEL STUDIO se puede hacer lo siguiente:

PORTB|=(1<<PB0);
PORTB|=(1<<PB2)|(1<<PB4)|(1<<PB6); activar las resistencias PULL UP del PORTB(bit2,4 y 6).

                                                          Registro PINX

Independiente si el puerto esta configurado como salida o entrada. el registro PINX leerá el puerto, es decir, con este registro se puede obtener el estado de los valores de los pines del puerto X que esta siendo leído. Una vez obtenida el estado de los valores de los pines se guarda en una variable "Y".




Procedimiento de programación en  C - ATMEL STUDIO 

1=====>> La primera línea de código es la directiva de preprocesador para incluir el archivo de cabecera general de AVR I / O. Esta es una línea importante sin la cual el código podría no ser generado.
                                                  #include <avr/io.h>

2=====>> Algunas directivas para que puedan ser usadas primero necesitan que se definan ciertos parámetros, como por ejemplo:
                                                  #define F_CPU 8000000UL 
/Se refiere a la frecuencia de reloj, en este caso de 8MHz, UL se refiere a Unsignal Lon

3=====>>La siguiente línea para incluir el archivo de cabecera de E / S específico ATmega8. Esto puede ser opcional en algunos IDE, ya que utilizan las propiedades del proyecto para utilizar el archivo de encabezado de E / S adecuado basado en el microcontrolador seleccionado.
# include <avr/iom8.h> for ATmega8
# include <avr/iom32.h>for ATmega32

4=====>>Opcionalmente podemos tener un archivo de biblioteca externa incluyendo lo siguiente. Por ejemplo, para incluir un archivo de biblioteca llamado servo, como escribimos a continuación
#include <util/delay.h>
# include "servo.h"

5=====>>Escribir una función principal con un bucle infinito para hacer que el esqueleto del código C del microcontrolador sea muy básico.

int main(void)
{
while (1)
       {


       }
}

6=====>>Ahora, como ya hemos comentado en la introducción de AVR siempre hay tres registros asociados.
DDRx: registro de dirección de datos, controla la dirección de los pines PORT individualmente.
PORTx: Registro para escribir datos en los pines PORT. Para la operación de salida, el bit respectivo del registro DDRx debe estar en 1.
PINx: Cuando los datos necesitan ser leídos de un pin del PUERTO, entonces necesitamos tener acceso al bit respectivo del registro del PIN. Para que un Pin se configure como entrada, el bit respectivo de la DDRx debe estar en 0.

int main(void)
{
//declaramos puerto C como salida
DDRC = 0xFF;
//Valor inicial del puerto C
PORTC.0 =0xFF;

while (1)
            {
             PORTC = 0b01010101;
            _delay_ms(10);
             PORTC = 0b10101010;
            _delay_ms(10);// no olvidar que cuando se usa retartos se debe de incluir <util/delay.h> pero                primero se debe definir primero la frecuencia en la directiva
            }
}

Lo anterior en la estructura básica para empezar a programar.

NOTAS:

Existen  maneras de reducir el código o hacerlos mas entendible(humanizarlo) realizando definiciones en cabecera(# define), veremos algunos ejemplos:


  • (1)===>Si es que se quiere evitar hacer (1<<PB0); a cada momento podemos definir en cabecera , por ejemplo :

#define _BV(bit) (1 << (bit))  //Convierte un número de bit en un valor de byte.
ahora escribimos solo así 
PORTB|= _BV(PB0); ///PORTB |= _BV(0);

DDRB |= _BV(PB0) | _BV(PB1);
PORTB |= _BV(PB0); 
PORTB &= ~_BV(PB1);

para que se pueda realizar lo anterior se debe primero incluir las librerias de avr 
-->#include <avr/io.h>


  • (2)===>Si es que se quiere evitar  hacer !(PINB & (1<<PB0)), se debe de hacer lo siguiente:

#define bit_is_clear(sfr, bit) (!(_SFR_BYTE(sfr) & _BV(bit)))// Test whether bit bit in IO register sfr is clear. This will return non-zero if the bit is clear, and a 0 if the bit is set.
Comprueba si el bit "bit"(PB0) en el registro PINB está libre. Esto devolverá cero si el bit está limpio, y un 0 si el bit está en establecido.
ahora escribimos solo así
bit_is_clear(PINB,PB0)

  • (3)===>Haremos lo contrario; Si es que se quiere evitar  hacer (PINB & (1<<PB0)), se debe de hacer lo siguiente:

#define bit_is_st(sfr, bit) (_SFR_BYTE(sfr) & _BV(bit))// Test whether bit bit in IO register sfr is set. This will return a 0 if the bit is clear, and non-zero if the bit is set.
Comprueba si el bit "bit"(PB0) en el registro PINB está configurado. Esto devolverá 0 si el bit está limpio, y 1 si el bit está en establecido.

  • (4)====>otra forma de humanizar el código :
#define loop_until_bit_is_clear ( sfr, bit ) do { } while (bit_is_set(sfr, bit))// bit= PB1,etc; sfr=PORTB,etc
Espere hasta que el bit "bit" en el registro entrada/salida sfr esté libre.

  • (5)====>otra forma de humanizar el código :
#define loop_until_bit_is_set ( sfr, bit ) do{}While(bit_is_set(sfr,bit))    
Espere hasta que el bit "bit" en el registro entrada/salida sfr esté establecido.
Para mayor información de las NOTAS se puede revisar este Link.

En resumen

según lo antes visto y reducirlos tiempos de programación se puede expresar lo siguiente:

DDRD|= (1<<DDD0); // DDD0=1= salida
DDRD&=~(1<<DDD1); //DDD1= entrada
PORTD|=(1<<PD1);//
          Si DDD1 está como salida(1 lógico) , entonces PD1=será el valor de un bit en alto.
          Si DDD1 está como entrada(0 lógico), entonces se activará PULL UP para PD1
PORTD &=~(1<<PD0);
         Si DDD0(Registro de configuración de salida / entrada) está como salida(1 lógico),entonces                PD0 será el valor de un bit en bajo(0 lógico).
         Si DDD0 esta como entrada(0 lógico), entonces el registro PORT es para activar o desactivar              las resistencias PULL UPP,en este caso  la resistencia PULL UP de PD0 estará desactivada.

PORTD  ^= (1<<PD0); // Se alterna el valor del led
        
Diagrama de Bloques

Simbología

Se recomienda ver el siguiente VIDEO donde explica muy bien y de manera sencilla la simbología.


Uso del firmware original de la grabadora USBasp AVR en MX-USBASP (clon chino)

Te vendieron un USBisp pensado que es un USBasp?? Hace 2 meses compré un "USBasp" fui a probarlo y no funcionó....pensé me estafar...