Algumas Dicas para otimizar o uso de memória no Arduino :
Retirado do : https://cdn-learn.adafruit.com/downloads/pdf/memories-of-an-arduino.pdf
"A memória é a primeira coisa a desaparecer."
(Eu não me lembro quem me disse isso)
Memória Flash
A memória flash é usada para armazenar a imagem do programa e qualquer dado inicializado. Você pode executar o código do programa a partir do flash, mas você não pode modificar os dados na memória flash do seu código de execução. Para modificar os dados, é necessário primeiro copiá-los para SRAM.
A memória flash é a mesma tecnologia usada para pen drives e cartões SD. Como não é volátil, seu programa continuará estar lá quando o sistema estiver desligado.
A memória flash tem uma vida útil finita de cerca de 100.000 ciclos de gravação. Portanto, se você enviar 10 programas por dia, todos os dias para o próximos 27 anos, você pode danifica-la.
SRAM
Memória estática de acesso aleatório, pode ser lida e gravada no seu programa em execução.
Memória SRAM é usado para diversas finalidades por um programa em execução:
A maioria dos problemas de memória ocorre quando a Heap a Stack colidem. Quando isso acontece, uma ou ambas as memórias áreas serão corrompidas com resultados imprevisíveis. Em alguns casos, isso causará um acidente imediato. Em outros, os efeitos da corrupção pode não ser percebida até muito mais tarde.
EEPROM
É outra forma de memória não volátil que pode ser lida ou gravada no seu programa em execução. Só pode ser lido byte a byte, para que possa ser um pouco complicado de usar. Também é mais lento que o SRAM e tem uma vida útil finita de cerca de
100.000 ciclos de gravação (você pode lê-lo quantas vezes quiser).
Embora não possa substituir a preciosa SRAM, há momentos em que pode ser muito útil!
Cartões SD
Qualquer uso com uma interface SD ou Micro-SD requer um Buffer SRAM de 512 bytes para se comunicar com o cartão.
Píxels
Cada pixel requer apenas 3 bytes de SRAM para armazenar a cor. Mas esses bytes começam a somar quando você tem muitos metros de tira ou uma grande variedade.
Remover código morto
Se o seu projeto é um mash-up de código de várias fontes, é provável que existam partes que não estejam sendo usadas e pode ser eliminado para economizar espaço.
F () para strings!
Strings literais são ofensores repetidos à memória. Primeiro, eles ocupam espaço na imagem do programa no Flash, depois são copiado para SRAM na inicialização como variáveis estáticas. É um desperdício horrível de SRAM, pois nunca iremos escrever para eles.
Paul Stoffregen, do Teensyduino, desenvolveu a macro F () como uma solução super simples para esse problema.
A macro F () diz ao compilador para manter suas seqüências de caracteres em PROGMEM. Tudo o que você precisa fazer é colocar a string literal em uma macro F ().
Mova dados constantes para PROGMEM.
Os itens de dados declarados como PROGMEM não são copiados para SRAM na inicialização. Eles são um pouco menos convenientes de se trabalhar, mas eles podem economizar quantidades significativas de SRAM. A referência básica do Arduino para o PROGMEM é https://www.arduino.cc/reference/pt/language/variables/utilities/progmem/
Reduzir tamanhos de buffer
Alocações de buffer e matriz:
Se você alocar um buffer, verifique se ele não é maior do que o necessário.
Buffers em bibliotecas:
Lembre-se também de que algumas bibliotecas alocam buffers nos bastidores que também podem ser candidatos a cortes.
Buffers do sistema:
Outro buffer escondido profundamente no sistema é o buffer de recebimento serial de 64 bytes. Se o seu esboço não estiver recebendo muito
dados seriais de alta velocidade, você provavelmente pode reduzir esse tamanho do buffer pela metade - ou talvez até menos.
O tamanho do buffer serial é definido em HardwareSerial.cpp. #define SERIAL_BUFFER_SIZE 64
Variáveis globais e estáticas
São as primeiras coisas carregadas na SRAM. Eles empurram o início da pilha para cima em direção ao STACK e ocuparão esse espaço por toda a eternidade.
Alocações Dinâmicas
Objetos e dados alocados dinamicamente fazem com que o Heap cresça em direção ao Stack(pilha). Ao contrário das variáveis globais e estáticas, essas variáveis podem ser desalocadas para liberar espaço. Mas isso não necessariamente faz com que o Heap encolha! Se houver outros dados dinâmicos acima dele, a parte superior do Heap não será movida. Quando o Heap estiver cheio de buracos como um queijo suíço chamamos de "Heap fragmentado".
Variáveis locais
Toda chamada de função cria um quadro de STACK(pilha) que faz com que a pilha cresça em direção ao Heap. Cada quadro de pilha conterá:
Esses dados são utilizáveis dentro da função, mas o espaço é 100% recuperado quando a função sai!
Então você deve preferir alocação local do que a global - as variáveis de pilha existem apenas enquanto estão sendo usadas. Se você tiver variáveis que são usados apenas em uma pequena seção do seu código, considere transformar esse código em uma função e declarar as variáveis locais para a função.
Retirado do : https://cdn-learn.adafruit.com/downloads/pdf/memories-of-an-arduino.pdf
"A memória é a primeira coisa a desaparecer."
(Eu não me lembro quem me disse isso)
Existem 3 tipos de memória em um Arduino:
Memória Flash
A memória flash é usada para armazenar a imagem do programa e qualquer dado inicializado. Você pode executar o código do programa a partir do flash, mas você não pode modificar os dados na memória flash do seu código de execução. Para modificar os dados, é necessário primeiro copiá-los para SRAM.
A memória flash é a mesma tecnologia usada para pen drives e cartões SD. Como não é volátil, seu programa continuará estar lá quando o sistema estiver desligado.
A memória flash tem uma vida útil finita de cerca de 100.000 ciclos de gravação. Portanto, se você enviar 10 programas por dia, todos os dias para o próximos 27 anos, você pode danifica-la.
SRAM
Memória estática de acesso aleatório, pode ser lida e gravada no seu programa em execução.
Memória SRAM é usado para diversas finalidades por um programa em execução:
- Dados estáticos - este é um bloco de espaço reservado na SRAM para todas as variáveis globais e estáticas do seu programa. Para variáveis com valores iniciais, o sistema de tempo de execução copia o valor inicial do Flash quando o programa é iniciado.
- Heap - O heap é para itens de dados alocados dinamicamente. A pilha cresce da parte superior da área de dados estáticos como itens de dados são alocados.
- Stack (Pilha) - A pilha é para variáveis locais e para manter um registro de interrupções e chamadas de função. A pilha cresce do topo da memória para baixo em direção à pilha. Toda interrupção, chamada de função e / ou variável local alocação faz com que a pilha cresça. Retornar de uma interrupção ou chamada de função recuperará todo o espaço de pilha usado por essa interrupção ou função.
A maioria dos problemas de memória ocorre quando a Heap a Stack colidem. Quando isso acontece, uma ou ambas as memórias áreas serão corrompidas com resultados imprevisíveis. Em alguns casos, isso causará um acidente imediato. Em outros, os efeitos da corrupção pode não ser percebida até muito mais tarde.
EEPROM
É outra forma de memória não volátil que pode ser lida ou gravada no seu programa em execução. Só pode ser lido byte a byte, para que possa ser um pouco complicado de usar. Também é mais lento que o SRAM e tem uma vida útil finita de cerca de
100.000 ciclos de gravação (você pode lê-lo quantas vezes quiser).
Embora não possa substituir a preciosa SRAM, há momentos em que pode ser muito útil!
O que pode estar ocupando espaço demais no seu projeto ?
Cartões SD
Qualquer uso com uma interface SD ou Micro-SD requer um Buffer SRAM de 512 bytes para se comunicar com o cartão.
Píxels
Cada pixel requer apenas 3 bytes de SRAM para armazenar a cor. Mas esses bytes começam a somar quando você tem muitos metros de tira ou uma grande variedade.
Remover código morto
Se o seu projeto é um mash-up de código de várias fontes, é provável que existam partes que não estejam sendo usadas e pode ser eliminado para economizar espaço.
- Bibliotecas não utilizadas - Todas as bibliotecas #include são realmente usadas?
- Funções não utilizadas - Todas as funções estão sendo chamadas?
- Variáveis não utilizadas - Todas as variáveis estão realmente sendo usadas?
- Código inacessível - Existem expressões condicionais que nunca serão verdadeiras?
- Dica - Se você não tem certeza sobre um #include, uma função ou uma variável. Comente. Se o programa ainda compilar, esse código não está sendo usado.
- Consolidar código repetido
- Se você tiver a mesma sequência de instruções de código em dois ou mais lugares, considere criar uma função com elas.
- Remover variáveis não utilizadas Se você não tiver certeza se uma variável está sendo usada ou não, comente-a. Se o esboço ainda compilar, livre-se dele!
F () para strings!
Strings literais são ofensores repetidos à memória. Primeiro, eles ocupam espaço na imagem do programa no Flash, depois são copiado para SRAM na inicialização como variáveis estáticas. É um desperdício horrível de SRAM, pois nunca iremos escrever para eles.
Paul Stoffregen, do Teensyduino, desenvolveu a macro F () como uma solução super simples para esse problema.
A macro F () diz ao compilador para manter suas seqüências de caracteres em PROGMEM. Tudo o que você precisa fazer é colocar a string literal em uma macro F ().
Mova dados constantes para PROGMEM.
Os itens de dados declarados como PROGMEM não são copiados para SRAM na inicialização. Eles são um pouco menos convenientes de se trabalhar, mas eles podem economizar quantidades significativas de SRAM. A referência básica do Arduino para o PROGMEM é https://www.arduino.cc/reference/pt/language/variables/utilities/progmem/
Reduzir tamanhos de buffer
Alocações de buffer e matriz:
Se você alocar um buffer, verifique se ele não é maior do que o necessário.
Buffers em bibliotecas:
Lembre-se também de que algumas bibliotecas alocam buffers nos bastidores que também podem ser candidatos a cortes.
Buffers do sistema:
Outro buffer escondido profundamente no sistema é o buffer de recebimento serial de 64 bytes. Se o seu esboço não estiver recebendo muito
dados seriais de alta velocidade, você provavelmente pode reduzir esse tamanho do buffer pela metade - ou talvez até menos.
O tamanho do buffer serial é definido em HardwareSerial.cpp. #define SERIAL_BUFFER_SIZE 64
Variáveis globais e estáticas
São as primeiras coisas carregadas na SRAM. Eles empurram o início da pilha para cima em direção ao STACK e ocuparão esse espaço por toda a eternidade.
Alocações Dinâmicas
Objetos e dados alocados dinamicamente fazem com que o Heap cresça em direção ao Stack(pilha). Ao contrário das variáveis globais e estáticas, essas variáveis podem ser desalocadas para liberar espaço. Mas isso não necessariamente faz com que o Heap encolha! Se houver outros dados dinâmicos acima dele, a parte superior do Heap não será movida. Quando o Heap estiver cheio de buracos como um queijo suíço chamamos de "Heap fragmentado".
Variáveis locais
Toda chamada de função cria um quadro de STACK(pilha) que faz com que a pilha cresça em direção ao Heap. Cada quadro de pilha conterá:
- Todos os parâmetros passados para a função
- Todas as variáveis locais declaradas na função.
Esses dados são utilizáveis dentro da função, mas o espaço é 100% recuperado quando a função sai!
Então você deve preferir alocação local do que a global - as variáveis de pilha existem apenas enquanto estão sendo usadas. Se você tiver variáveis que são usados apenas em uma pequena seção do seu código, considere transformar esse código em uma função e declarar as variáveis locais para a função.