Quería guardar algunos valores en la EEPROM y también quería liberar SRAM evitando algunas declaraciones de variables, pero la memoria EEPROM es byte-by-side.
Si quiero almacenar un valor int, tengo que usar algunas expresiones repetidamente. Pensé que haría algunas funciones para esos. Pero me preocupa que, si creo una función, todavía ocuparía la memoria SRAM, mejor declaro una variable int en lugar de usar EEPROM.
¿Cómo se almacenan las funciones y las variables locales en SRAM? ¿Solo almacena la dirección del puntero de función de la memoria flash o todas las variables y comandos se almacenan en la pila?
sram
eeprom
memory-usage
Nafis
fuente
fuente
Respuestas:
Solo los datos de la función se almacenan en la pila; Su código permanece en flash. En realidad, no puede reducir el uso de SRAM utilizando EEPROM porque, como ha visto, EEPROM no es direccionable de la misma manera. El código para leer y almacenar EEPROM también necesita usar algo de SRAM, ¡probablemente tanta SRAM como intentaba guardar! La EEPROM también es lenta para escribir y tiene una vida útil limitada (en número de escrituras en cada byte), lo que hace que sea poco práctico usarla para almacenar el tipo de datos temporales que usualmente guardamos en la pila. Es más adecuado para guardar datos que cambian con poca frecuencia, como la configuración de dispositivo única para dispositivos producidos en masa, o capturar errores poco frecuentes para su posterior análisis.
Editado: no hay una pila para esa función hasta que se ha llamado a la función, por lo que sí, es cuando se coloca cualquiera de los datos de la función. Lo que sucede después de que la función regresa es que su marco de pila (su área reservada de SRAM) ya no está reservada. Eventualmente será reutilizado por otra llamada de función. Aquí hay un diagrama de una pila C en memoria. Cuando un marco de pila ya no es útil, simplemente se libera y su memoria está disponible para ser reutilizada.
fuente
Las variables locales y los parámetros de función se almacenan en la pila. Sin embargo, esa no es una razón para no usarlos. Las computadoras están diseñadas para funcionar de esa manera.
La memoria de pila solo se usa mientras una función está activa. Tan pronto como la función regresa, la memoria se libera. La memoria de pila es una BUENA cosa.
No desea utilizar funciones recursivas con muchos niveles de recursión, ni asignar muchas estructuras grandes en la pila. Sin embargo, el uso normal está bien.
La pila 6502 tiene solo 256 bytes, pero la Apple II funciona bien.
fuente
El AVR (la familia de microcontroladores que se usa tradicionalmente en las placas Arduino) es una arquitectura de Harvard , lo que significa que el código ejecutable y las variables están en dos memorias separadas, en este caso flash y SRAM. El código ejecutable nunca deja memoria flash.
Cuando llama a una función, la dirección de retorno generalmente se transfiere a la pila; la excepción es cuando la llamada a la función ocurre al final de la función de llamada. En este caso, se utilizará la dirección de retorno de la función que llamó a la función de llamada; ya está en la pila.
Si cualquier otro dato se coloca en la pila depende de la presión de registro en la función de llamada y en la función llamada. Los registros son el área de trabajo de la CPU, el AVR tiene 32 registros de 1 byte. Se puede acceder directamente a los registros mediante instrucciones de la CPU, mientras que los datos en SRAM primero deberán almacenarse en los registros. Solo si los argumentos o la variable local son demasiado grandes o demasiados para caber en los registros, se colocarán en la pila. Sin embargo, las estructuras siempre se almacenan en la pila.
Puede leer los detalles de cómo el compilador GCC utiliza la pila en la plataforma AVR aquí: https://gcc.gnu.org/wiki/avr-gcc#Frame_Layout
Lea las secciones "Diseño de marco" y "Convención de llamadas" .
fuente
Inmediatamente después de una llamada a la función que ingresa a la función, el primer código que se ejecuta es disminuir el stackpointer en una cantidad igual al espacio requerido para las variables temporales internas de la función. Lo bueno de esto es que, por lo tanto, todas las funciones se vuelven reentrantes y recursivas, porque sus variables se construyen en la pila del programa que realiza la llamada. Eso significa que si una interrupción detiene la ejecución de un programa y transfiere la ejecución a otro, también puede llamar a la misma función sin que interfieran entre sí.
fuente
He estado tratando de hacer un código de ejemplo para demostrar lo que dicen las excelentes respuestas aquí, sin éxito hasta ahora. La razón es que el compilador optimiza agresivamente las cosas. Hasta ahora, mis pruebas no han utilizado la pila en absoluto, incluso con variables locales en una función. Los motivos son:
El compilador puede en línea la llamada a la función, por lo tanto, la dirección de retorno podría no insertarse en la pila. Ejemplo:
void foo (byte a) { digitalWrite (13, a); } void loop () { foo (5); }
El compilador convierte eso en:
void loop () { digitalWrite (13, 5); }
Sin llamada de función, sin pila utilizada.
El compilador puede pasar argumentos en los registros , lo que le ahorra tener que empujarlos a la pila. Ejemplo:
digitalWrite (13, 1);
Compila en:
158: 8d e0 ldi r24, 0x0D ; 13 15a: 61 e0 ldi r22, 0x01 ; 1 15c: 0e 94 05 01 call 0x20a ; 0x20a <digitalWrite>
Los argumentos se colocan en registros y, por lo tanto, no se utiliza ninguna pila (aparte de la dirección de retorno para llamar a digitalWrite).
El compilador optimiza las variables que no usa. Ejemplo:
void foo (byte a) { unsigned long bar [100]; bar [1] = a; digitalWrite (9, bar [1]); } void loop () { foo (3); } // end of loop
Ahora eso tiene que asignar 400 bytes para "barra", ¿no? No:
00000100 <_Z3fooh>: 100: 68 2f mov r22, r24 102: 89 e0 ldi r24, 0x09 ; 9 104: 0e 94 cd 00 call 0x19a ; 0x19a <digitalWrite> 108: 08 95 ret 0000010a <loop>: 10a: 83 e0 ldi r24, 0x03 ; 3 10c: 0e 94 80 00 call 0x100 ; 0x100 <_Z3fooh> 110: 08 95 ret
¡El compilador optimizó toda la matriz ! Puede decir que realmente solo estamos haciendo una
digitalWrite (9, 3)
y eso es lo que genera.Moraleja de la historia: no intentes dejar de pensar en el compilador.
fuente