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.
Hola que tal, muy bueno tutoriales. Tengo una duda con la explicacion de arriba la cual va así:
ResponderEliminarPor 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.
Porque se asume v=250, que es en si V. Bueno no logro ver en el codigoV1=12 y X1=1, en que parte del codigo esta aplicada. Disculpa apenas estoy enpezando con AVR
Se supone que "X" representa la cantidad de instrucciones NOP, y yo quiero que sean la menor cantidad posible, es por eso que "V" debe ser grande y de preferencia un multiplo para que sea mas exacto.
Eliminar"v" son los valores que se cargan en los registro (como 250 y 12), y "x" son los NOP... si X=1 solamente hay un NOP.
Perdon ok, ya entendi porque el 12, pero no entiendo el 1. Y porque se asume que son 250
ResponderEliminarHola tengo algunas dudas que no me han quedado muy claras:
ResponderEliminarBueno estuve checando la hoja de datos del atmega8 y pues no estyo seguro si te referias a 250k del BAudrate que viene en la tabla para una frecuencia de oscilacion de 16Mhz.
Tambien me ha entrado en duda el codigo de la subrutina
*Porque deben ser tres RETms_1,2,3
*Se carga los registros R21(Se decrementa y se compara con cero) y su respectivo BRNE quiere decir que va a saltar si r21 es diferente de "0"y R22 (Se decrementa y se compara con cero y BRNE si el r22 es diferente de "0") .
* Puse un breakpoint en *BRNE RETms_1* cambie la frecuencia a 16 mhz ya que estaba en 4 orginalmente y me da 19.59us ya cuando termina y llega al breakpoint. Ando algo confundido, disculpa que sean bastante raras mis dudas
Lo del 250 ya lo explique arriba.. podrias poner 100 el lugar de 250, el calculo seria similar, pero el valor no debe pasar de 255 ya que debe ser un byte. No tiene nada que ver con lo baudios del uart.
EliminarLos bucles se anidan para generar mas retardos, solamente por eso. Tu podrias hacer lo mismo, haciendo una rutina que demore 1ms y luego llamandola tantas veces como ms quieras.
Saludos.
Hola yo solo compile la rutina de retardo y me da 256063 us, tengo una pregunta se supone que muy idependiente de que yo compile todo el programa completo con lo del encendido del led debería mostrar tu aproximación de 0.88us
ResponderEliminarperdon deberia de dar aproximadamente lo que es 1ms, si no 0.88 no me había fijado en el conteo que llevaba apenas.
Eliminar60=(V1)(X1+4) Disculpa tu formula el 4 es por default?
ResponderEliminarEn esa formula sí, el cuatro es por defecto.
Eliminar1 por el DEC R22
1 por el CPI R22,0x00
2 por el BRNE LOOP_RET
total = 4.
Tengo dudas por ejemplo si quisiera creear una rutina para 500ms a una frecuencia de 1mhz tendria que utilizar la formula anterior.
ResponderEliminarLeyendo los comentarios anteriores
ResponderEliminarSe supone que "X" representa la cantidad de instrucciones NOP, y yo quiero que sean la menor cantidad posible, es por eso que "V" debe ser grande y de preferencia un multiplo para que sea mas exacto.
"v" son los valores que se cargan en los registro (como 250 y 12), y "x" son los NOP... si X=1 solamente hay un NOP.
Pero dicho caso se quisiera aplciar con 1mhz para 500ms
500ms= (V)(X+4)/1MHz
el resultado de V sobre pasa los 250 ya que no debe sobre pasar los 255. Como podría quedar la formula ?
Saludos
La idea no es aplicar la formula para cada retardo, tienes que hacer el calculo para 1ms, y luego hacer el llamado de esta rutina tantas veces como sea necesario. Arriba hay un ejemplo de calculo de 1ms para 16MHz, y luego se hace mas general para poderlo llamar y hacer retardos mas grandes.
EliminarEntonces como podría aplicar tu fragmento de coódigo y especificar que es para 500ms y aun poder modificar la frecuencia. Saludos
ResponderEliminarRealiza los calculos para 1ms usando la frecuencia de 1MHz (el ajemplo esta hecho para 16MHz). Luego cargas en r20 250 y lo llamas 2 veces... alli estarian los 500ms
EliminarDisculpa cual es la función del NOP solo que queda ahy. He estado checando un código
ResponderEliminarLDI R18,2
et2:LDI,R17,250
et1:LDI R16,250
et1:NOP
DEC R16
BRNE et1
DEC R16
BRNE et2
DEC R16
BRNE et3
Cuando compilo el programa el r16 se carga con el 250, cuando acaba las 250, salta al registro 17 el cual empieza a decrementarse es decir de 250 a 249, 248 etc, cada vez que que termina el registro 16.
Lo que no entiendo es para que sirve el NOP, ya que si yo decido poner de esta manera el código:
et1: LDI CONT1,RESID1
et3:LDI CONT2,RESID2
et2:LDI CONT3,RESID3
DEC CONT3
BRNE et1
DEC R16
BRNE et2
DEC R16
BRNE et3
El R16 solo decrementa en uno, es decir solo cambia del 249 al 250, sin afectar a los R17 y R18. Tengo entendido que el NOP es una espera de un ciclo de reloj. Pero de que manera influye en el programa.
Disculpa las molestias
En NOP es una instruccion que demora un ciclo de reloj y no afecta a ningun registro del uC, lo unico que hace es generar un retardo.. nada mas.
Eliminarinclude
ResponderEliminarLDI R16,0xFF
OUT DDRB,R16
LDI R16,$04
OUT SPH,R16
LDI R16,$5F
OUT SPL,R16
BUCLE_INFINITO:
SBI PORTB,PB0
LDI R20,250
RCALL RETms
CBI PORTB,PB0
LDI R20,250
RCALL RETms
RJMP BUCLE_INFINITO
RETms:
RETms_1:
LDI R21,100
RETms_2:
LDI R22,1
RETms_3:
NOP
DEC R22
CPI R22,0
BRNE RETms_3
DEC R21
CPI R21,0
BRNE Retms_2
DEC R20
CPI R20,0
BRNE RETms_1
FIN: RJMP FIN
Hola hice los calculos y pues corri el programa aproximandamente 420821us, quisiera saber si era ello a lo que te referias. Pero tengo una duda
include
LDI R16,0xFF
OUT DDRB,R16
LDI R16,$04
OUT SPH,R16
LDI R16,$5F
OUT SPL,R16
BUCLE_INFINITO:
SBI PORTB,PB0
LDI R20,250
RCALL RETms
CBI PORTB,PB0
LDI R20,250
RCALL RETms
RJMP BUCLE_INFINITO
RETms:
RETms_1:
LDI R21,100
RETms_2:
LDI R22,1
RETms_3:
NOP
DEC R22
CPI R22,0
BRNE RETms_3
DEC R21
CPI R21,0
BRNE Retms_2
DEC R20
CPI R20,0
BRNE RETms_1
FIN: RJMP FIN
Hola hice los calculos y pues corri el programa aproximandamente 420821us, quisiera saber si era ello a lo que te referias.
include
LDI R16,0xFF
OUT DDRB,R16
LDI R16,$04
OUT SPH,R16
LDI R16,$5F
OUT SPL,R16
BUCLE_INFINITO:
SBI PORTB,PB0
LDI R20,250
RCALL RETms
CBI PORTB,PB0
LDI R20,250
RCALL RETms
RJMP BUCLE_INFINITO
RETms:
RETms_1:
LDI R21,100
RETms_2:
LDI R22,1
RETms_3:
NOP
DEC R22
CPI R22,0
BRNE RETms_3
DEC R21
CPI R21,0
BRNE Retms_2
DEC R20
CPI R20,0
BRNE RETms_1
FIN: RJMP FIN
Hola hice los calculos y pues corri el programa aproximandamente 420821us, quisiera saber si era ello a lo que te referias.
Pero porque cada Retms se debe comparar con Cero
No entiendi tu pregunta...
Eliminar1ms = V(X+4)/1MHz -->1000=V(X+4)---> puede ser:
V=250 y X=0 --> no hay instrucciones NOP.
V=200 y X=1 --> una instruccion NOP.
V=100 y X=6 --> seis instrucciones NOP.
V=100 y V1=1 y X1=2 ---> dos instrucciones NOP.
Cual usaste ?.. si usate la ultima debio quedar asi:
RETms:
RETms_1:
LDI R21,100 //V=100
RETms_2:
LDI R22,1 // V1 =1
RETms_3:
NOP
NOP
DEC R22
CPI R22,0
BRNE RETms_3
DEC R21
CPI R21,0
BRNE Retms_2
DEC R20
CPI R20,0
BRNE RETms_1
La comparacion con 0 es para que funcione el BRNE, si quitas CPI la formula cambia.. ya no seria X+4.. seria X+3.
Utilize la ultima, solo que se me olvido poner una instrucción NOP, me la comí.
EliminarYa cheque el código y lo compile me da 496385, se hacerca bastante realmente. Como sabías en tu programa que debías poner el registro R20 con 250 y mandarlo a llamar dos veces. Gracías por ser tan atento (Y)
Bueno es que no se si tu programa era hacer una rutina de 240ms y pues era para probar la rutina de 1ms y cumpliera los 240ms ya que cuando la compilo y espero que termine me salen 1900411us
EliminarPerdon es que la he ejecutado con la frecuencia que ya tenia de 1mhz y obviamente los calculos no corresponderian XD ¡¡ Disculpa
Eliminar.include
EliminarLDI R16,0xFF
OUT DDRB,R16
LDI R16,$04
OUT SPH,R16
LDI R16,$5F
OUT SPL,R16
BUCLE_INFINITO:
SBI PORTB,PB0
LDI R20,250
RCALL RETms
CBI PORTB,PB0
LDI R20,250
RCALL RETms
RJMP BUCLE_INFINITO
RETms:
RETms_1:
LDI R21,100
RETms_2:
LDI R22,1
RETms_3:
NOP
NOP
DEC R22
CPI R22,0
BRNE RETms_3
DEC R21
CPI R21,0
BRNE Retms_2
DEC R20
CPI R20,0
BRNE RETms_1
FIN: RJMP FIN // Poner RET
Así deje el código al final
Hola , disculpa no se porque se publico muchas veces, el comentario anterior. Acabo de realizar los calculos supongo que si estoy bien con los calculos anteriores creeo que ya logre entender como se hace una rutina, lo que estube probando y moviendo alguna parte del código es en:
ResponderEliminarDEC R20
CPI R20,0
*BRNE RETms_1 // Mencionas que en esta parte es para hacerlo mas general, quite esta instrucción he iba mas rapido aque se debe.
*Quite el NOP para ver como funcionaba y lo hacia de manera normal, tal cual como antes de quitarle
BRNE RETms_1.
Espero que me des el visto bueno de los calculos 420821us
Gracías de nuevo .
si tienes muchos problemas con esta rutina.. usa lo que muestro en este otro post
Eliminarhttp://avrperu.blogspot.com/2011/03/delaymsms-y-delayusus-para-cualquier.html
Hola acabo de probar este código y solamente hace 25000us al poner un breakpoint en RET o FIN:RJMP FIN
ResponderEliminar.include
LDI R16,0xFF
OUT DDRB,R16
LDI R16,$04
OUT SPH,R16
LDI R16,$5F
OUT SPL,R16
BUCLE_INFINITO:
SBI PORTB,PB0
LDI R20,250
RCALL RETms
CBI PORTB,PB0
LDI R20,250
RCALL RETms
RJMP BUCLE_INFINITO
RETms:
RETms_1:
LDI R21,100
RETms_2:
LDI R22,1
RETms_3:
NOP
NOP
DEC R22
CPI R22,0
BRNE RETms_3
DEC R21
CPI R21,0
BRNE Retms_2
DEC R20
CPI R20,0
BRNE RETms_1
FIN: RJMP FIN // Poner RET
Al igual si le quito esta parte del código:
ResponderEliminarCBI PORTB,PB0
LDI R20,250
RCALL RETms
RJMP BUCLE_INFINITO
cosa que no debria suceder
Lo que hace es 25000us encendidos y 25000us apagados, pero como podría ser que hiciera media segundo encendido y medio segundo apagado
ResponderEliminarSeñor... revise su configuracion , me tome la molestia de hacer la simulacion a 1MHz y funciona perfectamente. Apostaria a que tu simulacion esta a 10MHz.
EliminarHola muchas gracías por la ayuda, yo puse el breakpoint en la instrucción RET, lo tengo trabajando
ResponderEliminar1Mhz.
http://i253.photobucket.com/albums/hh76/kapricon/blinkled_zpsd0f2b938.png
Por nada. saludos
EliminarAcabo de ver la foto, entonces así debe quedar.
ResponderEliminarEntonces si puso un breakpoint al final del código quiere decir que hace 251011us prendido y 251011us apagado, pero como podría hacer 500 us prendido y apagado? si lo maximo que se puede representar 255
EliminarHola, estube buscando tambien una rutina de 500us, entonces así debe ser como la imagen que publicaron. Ya que al parecer son 251011us prendido y 251011us apagado y eso da 500000us?
EliminarHola, he estado checando varios programas en internet de una rutina de 500ms y publicaron en los comentarios una, pero al igual solo hace 250000us encendido y apagado, tambien modifique el código segun los comentarios. Intente cambiar el 250 a 500, pero como lo maximo son 255 exede y no se pued eponer en el código para hacer que la rutina haga 500 000us encendido y apagado.
ResponderEliminarHe leeido los comentarios anteriores y esta trabajndo a una frecuencia de 1Mhz
LDI R16,0xFF
OUT DDRB,R16
LDI R16,$04
OUT SPH,R16
LDI R16,$5F
OUT SPL,R16
BUCLE_INFINITO:
SBI PORTB,PB0
LDI R20,250
RCALL RETms
CBI PORTB,PB0
LDI R20,250
RCALL RETms
RJMP BUCLE_INFINITO
RETms:
RETms_1:
LDI R21,100
RETms_2:
LDI R22,1
RETms_3:
NOP
NOP
DEC R22
CPI R22,0
BRNE RETms_3
DEC R21
CPI R21,0
BRNE Retms_2
DEC R20
CPI R20,0
BRNE RETms_1
FIN: RJMP FIN // Poner RET
Llama a la rutina de 250ms dos veces.. y tienes tus 500ms... asi de facil.
EliminarHola ya logre hacer los 500ms, en lo personal me agrado bastante que se usara una formula ya que he encontrado otros porgramas en el cual lo explican y definen así 2x(250x(250x4us)) y efectivamente así salen los 500000us. Ya llame la rutina dos veces a pesar que se hace mas códgio logre entender a la perfección, me pregunto si hay una manera como en este caso que los 500us se hicieran con menos código, en parte te agradesco ya que entendí mucho.
ResponderEliminar.include
LDI R16,0xFF
OUT DDRB,R16
LDI R16,$04
OUT SPH,R16
LDI R16,$5F
OUT SPL,R16
BUCLE_INFINITO:
SBI PORTB,PB0
LDI R20,250
RCALL RETms
CBI PORTB,PB0
LDI R20,250
RCALL RETms
RJMP BUCLE_INFINITO
SBI PORTB,PB0
LDI R20,250
RCALL RETms1
CBI PORTB,PB0
LDI R20,250
RCALL RETms1
RJMP BUCLE_INFINITO
RETms:
RETms_1:
LDI R21,100
RETms_2:
LDI R22,1
RETms_3:
NOP
NOP
DEC R22
CPI R22,0
BRNE RETms_3
DEC R21
CPI R21,0
BRNE Retms_2
DEC R20
CPI R20,0
BRNE RETms_1
RETms1:
RETms_01:
LDI R21,100
RETms_02:
LDI R22,1
RETms_03:
NOP
NOP
DEC R22
CPI R22,0
BRNE RETms_03
DEC R21
CPI R21,0
BRNE Retms_02
DEC R20
CPI R20,0
BRNE RETms_01
RET
Al igual si modifica valors como V=100 y V=1 que me habían salido, modificandolos a V=100 y V=3 quedaba tambien, pero se me hacia muy al tanteo.
EliminarBuenas tardes ayudar como poder hacer ejercicio
ResponderEliminarUna empresa necesita que sea instalado un sistema de control de TEMPERATUR para una sala de servidores ,el cual recibe la temperatura del ambiente mediante mediante un sensor sala de servidores ,el cuál recibe la temperatura del ambiente mediante un sensor conectado al pin A2 de un microcontrolador ATMEGA32. Las acciones que debe realizar
Quieres un monitoreo de temperatura o control de temperatura ?
EliminarCreo que quiere un control de temperatura por lo que leí,aplica un PID y hacer ensayos, lo mas común es que uses un motor DC, seguro nos dará mas detalles , salds
Eliminar