Eu queria salvar alguns valores na EEPROM e também queria liberar SRAM, evitando algumas declarações de variáveis, mas a memória da EEPROM é byte.
Se eu quiser armazenar um valor int, tenho que usar algumas expressões repetidamente. Eu pensei em fazer algumas funções para elas. Mas estou preocupado que, se eu criar uma função, ela ainda ocuparia memória SRAM, melhor eu declaro uma variável int em vez de usar EEPROM.
Como as funções e as variáveis locais são armazenadas na SRAM? Ele armazena apenas o endereço do ponteiro de função da memória flash ou todas as variáveis e comandos são armazenados na pilha?
sram
eeprom
memory-usage
Nafis
fonte
fonte
Respostas:
Somente os dados da função são armazenados na pilha; seu código permanece em flash. Você não pode realmente reduzir o uso da SRAM usando a EEPROM, porque, como você viu, a EEPROM não é endereçável da mesma maneira. O código para ler e armazenar a EEPROM também precisa usar alguma SRAM - provavelmente a mesma quantidade que você estava tentando salvar! A EEPROM também é lenta na gravação e tem uma vida útil limitada (em número de gravações em cada byte), o que torna impraticável o uso para armazenar o tipo de dados temporários que geralmente colocamos na pilha. É mais adequado para salvar dados alterados com pouca frequência, como a configuração exclusiva de dispositivos produzidos em massa, ou capturar erros não frequentes para análises posteriores.
Editado: Não há pilha para essa função até que a função tenha sido chamada; portanto, sim, é quando qualquer dado da função é colocado lá. O que acontece após o retorno da função é que seu quadro de pilha (sua área reservada da SRAM) não está mais reservada. Eventualmente, será reutilizado por outra chamada de função. Aqui está um diagrama de uma pilha C na memória. Quando um quadro de pilha não é mais útil, ele é simplesmente liberado e sua memória fica disponível para reutilização.
fonte
Variáveis locais e parâmetros de função são armazenados na pilha. No entanto, esse não é um motivo para não usá-los. Os computadores são projetados para funcionar dessa maneira.
A memória da pilha está em uso apenas enquanto uma função está ativa. Assim que a função retornar, a memória será liberada. Pilha de memória é uma coisa boa.
Você não deseja usar funções recursivas com muitos níveis de recursão ou alocar muitas estruturas grandes na pilha. O uso normal é bom, no entanto.
A pilha 6502 tem apenas 256 bytes, mas o Apple II funciona bem.
fonte
O AVR (a família de microcontroladores tradicionalmente usada em placas Arduino) é uma arquitetura de Harvard , o que significa que código e variáveis executáveis estão em duas memórias separadas - neste caso, flash e SRAM. O código executável nunca sai da memória flash.
Quando você chama uma função, o endereço de retorno geralmente é enviado para a pilha - a exceção ocorre quando a chamada da função ocorre no final da função de chamada. Nesse caso, o endereço de retorno da função que chamou a função de chamada será usado - ele já está na pilha.
Se quaisquer outros dados são colocados na pilha depende da pressão do registro na função de chamada e na função chamada. Registradores são a área de trabalho da CPU, o AVR possui 32 registros de 1 byte. Os registradores podem ser acessados diretamente pelas instruções da CPU, enquanto os dados na SRAM primeiro precisam ser armazenados nos registradores. Somente se os argumentos ou a variável local forem muito grandes ou muitos para caberem nos registros, eles serão colocados na pilha. No entanto, as estruturas são sempre armazenadas na pilha.
Você pode ler os detalhes de como a pilha é usada pelo compilador GCC na plataforma AVR aqui: https://gcc.gnu.org/wiki/avr-gcc#Frame_Layout
Leia as seções "Layout do quadro" e "Convenção de chamada" .
fonte
Imediatamente após uma chamada de função entrar na função, o primeiro código executado é diminuir o ponteiro da pilha em uma quantidade igual ao espaço necessário para variáveis temporárias internas à função. A coisa brilhante sobre isso é que todas as funções se tornam re-entrantes e recursivas, porque suas variáveis são construídas na pilha do programa de chamada. Isso significa que, se uma interrupção interrompe a execução de um programa e transfere a execução para outro, também pode chamar a mesma função sem que eles interfiram entre si.
fonte
Eu tenho me esforçado bastante para criar um exemplo de código para demonstrar o que as excelentes respostas aqui estão dizendo, sem sucesso até agora. A razão é que o compilador otimiza agressivamente as coisas. Até agora, meus testes não usaram a pilha, mesmo com variáveis locais em uma função. Os motivos são:
O compilador pode alinhar a chamada de função, portanto, o endereço de retorno pode não ser inserido na pilha. Exemplo:
void foo (byte a) { digitalWrite (13, a); } void loop () { foo (5); }
O compilador transforma isso em:
void loop () { digitalWrite (13, 5); }
Nenhuma chamada de função, nenhuma pilha usada.
O compilador pode passar argumentos nos registradores , poupando-os de enviá-los para a pilha. Exemplo:
digitalWrite (13, 1);
Compila em:
158: 8d e0 ldi r24, 0x0D ; 13 15a: 61 e0 ldi r22, 0x01 ; 1 15c: 0e 94 05 01 call 0x20a ; 0x20a <digitalWrite>
Os argumentos são colocados em registradores e, portanto, nenhuma pilha é usada (além do endereço de retorno para chamar digitalWrite).
O compilador otimiza as variáveis que você não usa. Exemplo:
void foo (byte a) { unsigned long bar [100]; bar [1] = a; digitalWrite (9, bar [1]); } void loop () { foo (3); } // end of loop
Agora isso tem que alocar 400 bytes para "bar", não é? Não:
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
O compilador otimizou toda a matriz ! Pode dizer que realmente estamos apenas fazendo um
digitalWrite (9, 3)
e é isso que ele gera.Moral da história: não tente pensar melhor no compilador.
fonte