Otimização e Organização de Memória

Olá, como estão? A princípio iremos falar sobre otimização e organização de memória, assim diminuindo o consumo de FLASH(memória estática) e SRAM (memória dinâmica) do seu projeto. Assim obtendo um melhor desempenho em seu microcontrolador. Entretanto para este projeto usaremos o KIT básico com Arduíno da WJ Componentes.

Arduíno UNO

Componentes Utilizados


OU

Funcionamento do Projeto de Otimização


Como foi comentado anteriormente hoje iremos explicar como otimizar e diminuir o consumo de espaço em FLASH e SRAM. Em seguida iremos apresentar um código-fonte onde iremos otimiza-lo.

Métodos para Otimização


1º Função

Utilizar funções é muito útil para a organização e economia de espaço, portanto, é importante o aprendizado de funções para projetos complexos. A economia de espaço varia conforme o código-fonte. Entretanto, ela sempre irá trazer grande diminuição de espaço utilizado e otimização, se for usado da forma correta. Por exemplo:

Antes da implementação de funções:

//Setup -------------------------------------
void setup() { 
    Serial.begin(9600);
    pinMode(13, OUTPUT);
}
 
//Loop --------------------------------------
void loop() {
    digitalWrite(13, HIGH);
    delay(1000);
    digitalWrite(13, LOW);
    Serial.println("PISCA LED");
    digitalWrite(13, HIGH);
    delay(1000);
    digitalWrite(13, LOW);
    Serial.println("PISCA LED NOVAMENTE");
}

Portanto ocupam 2020 bytes (6%) da memória FLASH e  218 (10%) de variáveis globais.

Após a implementação de funções:

//Protótipo de Função ----------------------
void pisca();
	 
//Setup ------------------------------------
void setup() { 
    Serial.begin(9600);
    pinMode(13, OUTPUT);
}
 
//Loop -------------------------------------
void loop() {
    pisca();
    Serial.println("PISCA LED");
    pisca();
    Serial.println("PISCA LED NOVAMENTE");
}
	 
//Pisca LED --------------------------------
void pisca(){
    digitalWrite(13, HIGH);
    delay(1000);
    digitalWrite(13, LOW);
}

Portanto ocupam 2008 bytes (6%) da memória FLASH e  218 (10%) de variáveis globais.

2º NÃO UTILIZAR VARIÁVEL GLOBAL

Embora a maioria dos programadores e código-fonte usarem variáveis globais. Não é uma boa prática o uso dela, pois, elas ficam armazenadas “eternamente” na memória SRAM. Porém, quando declaramos uma variável local (dentro de uma função), assim que saímos daquela função ela será liberada (variáveis simples). Por exemplo:

Utilizando variável global:

//Variáveis Globais -------------------------
int tempo = 1000;
 
//Setup -------------------------------------
void setup() { 
    Serial.begin(9600);
    pinMode(13, OUTPUT);
}
	 
//Loop --------------------------------------
void loop() {
    digitalWrite(13, HIGH);
    delay(tempo);
    digitalWrite(13, LOW);
    Serial.println("PISCA LED");
    digitalWrite(13, HIGH);
    delay(tempo);
    digitalWrite(13, LOW);
    Serial.println("PISCA LED NOVAMENTE");
}

Portanto ocupam 2048 bytes (6%) da memória FLASH e  218 bytes (10%) de variáveis globais.

Utilizando variável local:

//Setup -------------------------------------
void setup() { 
    Serial.begin(9600);
    pinMode(13, OUTPUT);
}
 
//Loop --------------------------------------
void loop() {
    int tempo = 100;
    digitalWrite(13, HIGH);
    delay(tempo);
    digitalWrite(13, LOW);
    Serial.println("PISCA LED");
    digitalWrite(13, HIGH);
    delay(tempo);
    digitalWrite(13, LOW);
    Serial.println("PISCA LED NOVAMENTE");
} 

Portanto ocupam 2020 bytes (6%) da memória FLASH e  218 bytes (10%) de variáveis globais.

3º Macro F() e PROGMEM

Conforme utilizamos esses modificadores em nosso projeto, estamos armazenando as informações na memória FLASH ao invés da memória SRAM. Entretanto, ela só podem ser inseridas em dados estáticos. Ou seja, não podemos alterar os dados contidos nas variáveis que utilizam os modificadores macro F() ou PROGMEM durante a execução  do código-fonte, é possível somente estar realizando a leitura dos dados contidos nelas.

Sem macro F() e PROGMEM:

//Variáveis Globais ------------------------
const char text[] = "Vou ocupar memoria em SRAM";
	 
//Setup ------------------------------------
void setup() { 
    Serial.begin(9600);
}
	 
//Loop -------------------------------------
void loop() {
    Serial.println("Vou ocupar memoria em SRAM");
    Serial.println(text);
} 

Portanto ocupam 1560 bytes (4%) da memória FLASH e  248 bytes (12%) de variáveis globais.

Com macro F() e PROGMEM:

//Variáveis Globais ------------------------
const PROGMEM char text[] = "Vou ocupar memoria a FLASH";
	 
//Setup ------------------------------------
void setup() { 
    Serial.begin(9600);
}
 
//Loop -------------------------------------
void loop() {
    Serial.println(F("Vou ocupar memoria a FLASH"));
    Serial.println(text);
} 

Portanto ocupam 1580 bytes (4%) da memória FLASH e  188 bytes (9%) de variáveis globais.

4º Tipo de Dados

Existem diversos tipos de dados, cada uma delas ocupam um número de espaço em memória pré-determinado, assim podemos diminuir o espaço ocupado por elas utilizando o tipo de dado correto. Por isso segue tabela com todos os tipos de dados.

Tipo de Dados:Tamanho em Bytes:Pode Conter:
void0Nada (NULL)
bool1True (1) ou False (0)
char1Caracteres da tabela ASCII ou valor entre -128 e 127
usingned char, byte e uint8_t1Caracteres da tabela ASCII ou valor entre 0 e 255
short e int16_t2Valor entre -32.768 e 32.767
word e uint16_t2Valor entre 0 e 65.535
int2 ou 4 (Due, Zero e MKR1000)Valor entre -32.768 e 32.767 ou -2.147.483.648 e 2.147.483.647
usingned int2 ou 4 (Due, Zero e MKR1000)Valor entre 0 e 65.535 ou 0 e 4.294.967.295
long e int32_t4Valor entre -2.147.483.648 e 2.147.483.647
usingned long e uint32_t4Valor entre 0 e 4.294.967.295
float46 a 7 dígitos de precisão
double4 ou 8 (Due, Zero e MKR1000)6 a 7 dígitos de precisão ou 12 a 14 dígitos de precisão
Tabela 1 – Tipo de Dados

Outra boa prática pouco adotada que ajuda a otimizar e desenvolver um código-fonte multiplataforma, é utilizar o tipo de dado int16_t ao invés de int, para que assim possamos ter certeza de que estamos ocupando 2 bytes de memória. Assim também para int8_t e int32_t.

Seleção de tipos errada:

//Variáveis Globais -----------------------
int led = 13;
	 
//Setup ------------------------------------
void setup() {
    pinMode(led, OUTPUT);
}
	 
//Loop -------------------------------------
void loop() { 
    long int tempo = 60000;
    digitalWrite(led, HIGH);
    delayMicroseconds(tempo);
    digitalWrite(led, LOW);
}

Portanto ocupam 758 bytes (2%) da memória FLASH e  9 bytes (0%) de variáveis globais.

Seleção de tipos corretos:

//Definições ------------------------------
#define led 13
 
//Setup -----------------------------------
void setup() {
    pinMode(led, OUTPUT);
}
 
//Loop ------------------------------------
void loop() {
    uint16_t tempo = 60000;
    digitalWrite(led, HIGH);
    delayMicroseconds(tempo);
    digitalWrite(led, LOW);
}

Portanto ocupam 746 bytes (2%) da memória FLASH e  9 bytes (0%) de variáveis globais.

5º Estrutura de Repetições

Utilize as estruturas de repetições para evitar a repetição de algoritmos, assim melhorando o desempenho e diminuindo o tamanho utilizado no código-fonte.

Sem Estrutura de Repetições:

//Setup -----------------------------------
void setup() {
    Serial.begin(9600);
}
 
//Loop -------------------------------------
void loop() {
    Serial.println(F("Olá, tudo bem?"));
    Serial.println(F("EXEMPLO"));
    Serial.println(F("EXEMPLO"));
    Serial.println(F("EXEMPLO"));
    Serial.println(F("EXEMPLO"));
    Serial.println(F("EXEMPLO"));
    Serial.println(F("WJ COMPONENTES"));
}

Portanto ocupam 1640 bytes (5%) da memória FLASH e 188 bytes (9%) de variáveis globais.

Com Estrutura de Repetições:

//Setup ------------------------------------
void setup() {
Serial.begin(9600);
}
 
//Loop -------------------------------------
void loop() {
    Serial.println(F("Olá, tudo bem?"));
    for(byte i = 0; i<5;i++){
        Serial.println(F("EXEMPLO"));  
    }
    Serial.println(F("WJ COMPONENTES"));
}

Portanto ocupam 1608 bytes (4%) da memória FLASH e  188 bytes (9%) de variáveis globais.

Montagem do Projeto de Otimização


Logo após compra dos componentes na WJ Componentes, vamos à montagem! Em seguida conectamos os LEDs na protoboard com os resistores que conectamos nos catodos dos LEDs. Por fim ligamos no anodo dos LEDs os pinos 9, 10, 11, 12 e 13 do Arduíno UNO.

Contudo segue abaixo uma imagem que demonstra a montagem do circuito.

Pinout LED
Montagem do Circuito Eletrônico na Protoboard - Otimização
Figura 1 – Montagem do Circuito Eletrônico na Protoboard

Diagrama Esquemático do Projeto de Otimização


Diagrama do Circuito Eletrico - Otimização
Figura 2 – Diagrama do Circuito Eletrico

Código-fonte do Projeto de Otimização


Em seguida terá o código completo do projeto.

//Variáveis Globais -------------------------------------------------------------------------------
int led1 = 13, led2 = 12, led3 = 11, led4 = 10, led5 = 9;
byte tempo = 1000;
String text = "Um dia, um cão ia atravessando uma ponte, carregando um osso na boca. Olhando para baixo, viu sua própria imagem refletida na água. Pensando ver outro cão, coçou-lhe logo o osso e pôs-se a latir. Mal, porém, abriu a boca, seu próprio osso caiu na água e se perdeu para sempre.";
	 
//Setup -------------------------------------------------------------------------------------------
void setup() {
    Serial.begin(9600);
    pinMode(led1, OUTPUT);
    pinMode(led2, OUTPUT);
    pinMode(led3, OUTPUT);
    pinMode(led4, OUTPUT);
    pinMode(led5, OUTPUT);
}
	 
//Loop --------------------------------------------------------------------------------------------
void loop() {
    digitalWrite(led1, HIGH);
    delay(tempo);
    digitalWrite(led1, LOW);
    delay(tempo);
    digitalWrite(led1, HIGH);
    delay(tempo);
    digitalWrite(led1, LOW);
    delay(tempo);
    Serial.println(text);
    Serial.println("Moral da História: Vale mais um passarinho na mão do que dois voando");
    digitalWrite(led2, HIGH);
    delay(tempo);
    digitalWrite(led2, LOW);
    delay(tempo);
    digitalWrite(led2, HIGH);
    delay(tempo);
    digitalWrite(led2, LOW);
    delay(tempo);
}

Então no total este código-fonte está ocupando 3868 bytes (11%) de memória FLASH e 560 bytes (27%) de variáveis globais na memória SRAM (dinâmica).

Vamos dar uma olhada mais de perto no código:

1º Etapa

Primeiramente teremos que verificar se conseguiremos adicionar alguma função no nosso código, para assim simplificá-lo. Na função loop possuímos uma estrutura que se repete onde ela é responsável por piscar o LED, podendo assim  colocá-la dentro de uma função, porém, na primeira vez executada ele pisca o  LED 1, já na segunda vez ele pisca o LED 2. Portanto teremos que passar também para a função qual LED queremos que ele pisque. Ficando da seguinte forma:

//Variáveis Globais ------------------------------------------------------------------------------
int led1 = 13, led2 = 12, led3 = 11, led4 = 10, led5 = 9;
byte tempo = 1000;
String text = "Um dia, um cão ia atravessando uma ponte, carregando um osso na boca. Olhando para baixo, viu sua própria imagem refletida na água. Pensando ver outro cão, coçou-lhe logo o osso e pôs-se a latir. Mal, porém, abriu a boca, seu próprio osso caiu na água e se perdeu para sempre.";
	 
//Protótipo da Função -----------------------------------------------------------------------------
void piscaLed(byte led);
   
//Setup -------------------------------------------------------------------------------------------
void setup() {
    Serial.begin(9600);
    pinMode(led1, OUTPUT);
    pinMode(led2, OUTPUT);
    pinMode(led3, OUTPUT);
    pinMode(led4, OUTPUT);
    pinMode(led5, OUTPUT);
}
	  
//Loop --------------------------------------------------------------------------------------------
void loop() {
    piscaLed(led1);
    Serial.println(text);
    Serial.println("Moral da História: Vale mais um passarinho na mão do que dois voando");
    piscaLed(led2);
}
	 
//Pisca LED ---------------------------------------------------------------------------------------
void piscaLed(byte led){
    digitalWrite(led, HIGH);
    delay(tempo);
    digitalWrite(led, LOW);
    delay(tempo);
    digitalWrite(led, HIGH);
    delay(tempo);
    digitalWrite(led, LOW);
    delay(tempo);
}

2º Etapa

Com essa alteração código-fonte terminou ocupando mais linhas, porém, isso não quer dizer que estamos consumindo mais espaço, por exemplo, só com essa  modificação o código utilizou 3810 bytes (11%) de memória FLASH assim totalizando uma redução de 58 bytes de memória utilizada, já na memória SRAM (dinâmica) de variáveis globais permaneceu igual. Em seguida identificamos onde podemos adicionar estruturas de repetições, para evitar repetição de algoritmo, conseguimos estar colocando duas estruturas de repetição, uma void Setup e outra na função piscaLed.

//Variáveis Globais -------------------------------------------------------------------------------
int led1 = 13, led2 = 12, led3 = 11, led4 = 10, led5 = 9;
byte tempo = 1000;
String text = "Um dia, um cão ia atravessando uma ponte, carregando um osso na boca. Olhando para baixo, viu sua própria imagem refletida na água. Pensando ver outro cão, coçou-lhe logo o osso e pôs-se a latir. Mal, porém, abriu a boca, seu próprio osso caiu na água e se perdeu para sempre.";
 
//Protótipo da Função ----------------------------------------------------------------------------
void piscaLed(byte led);
 
//Setup ------------------------------------------------------------------------------------------
void setup() {
    Serial.begin(9600);
    for(byte i=9;i<=13;i++){
    pinMode(i, OUTPUT);
    }
}
 
//Loop --------------------------------------------------------------------------------------------
void loop() {
    piscaLed(led1);
    Serial.println(text);
    Serial.println("Moral da História: Vale mais um passarinho na mão do que dois voando");
    piscaLed(led2);
}
	 
//Pisca LED ---------------------------------------------------------------------------------------
void piscaLed(byte led){
    for(byte i=0;i<2;i++){
        digitalWrite(led, HIGH);
        delay(tempo);
        digitalWrite(led, LOW);
        delay(tempo);
    }
}

3º Etapa

Além disso com esta simples modificação a memória FLASH foi para 3790 bytes (11%) totalizando uma redução de 20 bytes de memória utilizada, já na memória SRAM (dinâmica) de variáveis globais permaneceu igual. Então verificamos os tipos de dados das variáveis, nas variáveis dos LEDs estão com o tipo int, porém, para declaração de pinos a melhor seria a define, por não consumir memoria FLASH e nem SRAM. Na variável tempo também está errada, pois, o tipo byte consegue armazenar no máximo 255 e ela está armazenando 1000, então teremos que mudar para tipo const int por ela ser uma constante que não muda durante o programa. Para finalizar na variável text mudamos para char e podemos colocar o modificador PROGMEM para assim armazenar na memória FLASH.

//Definições --------------------------------------------------------------------------------------
#define led1 13
#define led2 12
#define led3 11
#define led4 10
#define led5 09
 
//Variáveis Globais -------------------------------------------------------------------------------
const int tempo = 1000;
const PROGMEM char text[] = "Um dia, um cão próprio ia atravessando uma ponte, carregando um osso na boca. Olhando para baixo, viu sua imagem refletida na água. Pensando ver outro cão, coçou-lhe logo o osso e pôs-se a latir. Mal, porém, abriu a boca, seu próprio osso caiu na água e se perdeu para sempre.";
	 
//Protótipo da Função -----------------------------------------------------------------------------
void piscaLed(byte led);
	 
//Setup -------------------------------------------------------------------------------------------
void setup() {
    Serial.begin(9600);
    for(byte i=9;i<=13;i++){
        pinMode(i, OUTPUT);
    }
}
 
//Loop --------------------------------------------------------------------------------------------
void loop() {
    piscaLed(led1);
    Serial.println(text);
    Serial.println("Moral da História: Vale mais um passarinho na mão do que dois voando");
    piscaLed(led2);
}
	 
//Pisca LED ---------------------------------------------------------------------------------------
void piscaLed(byte led){
    for(byte i=0;i<2;i++){
        digitalWrite(led, HIGH);
        delay(tempo);
        digitalWrite(led, LOW);
        delay(tempo);
    }
}

4º Etapa

Com essa modificação a memória FLASH foi para 2406 bytes (7%) e a memória SRAM (dinâmica) de variáveis globais foi para 258 bytes (12%), assim totalizando uma redução de 1384 bytes de FLASH e 302 bytes de memória SRAM. Em seguida tiraremos a variável tempo de global e colocar ela na função piscaLed como variável local. Ficando da seguinte forma:

//Definições --------------------------------------------------------------------------------------
#define led1 13
#define led2 12
#define led3 11
#define led4 10
#define led5 09
 
//Variáveis Globais -------------------------------------------------------------------------------
const PROGMEM char text[] = "Um dia, um cão próprio ia atravessando uma ponte, carregando um osso na boca. Olhando para baixo, viu sua imagem refletida na água. Pensando ver outro cão, coçou-lhe logo o osso e pôs-se a latir. Mal, porém, abriu a boca, seu próprio osso caiu na água e se perdeu para sempre.";
	 
//Protótipo da Função -----------------------------------------------------------------------------
void piscaLed(byte led);
	 
//Setup -------------------------------------------------------------------------------------------
void setup() {
    Serial.begin(9600);
    for(byte i=9;i<=13;i++){
        pinMode(i, OUTPUT);
    }
}

//Loop --------------------------------------------------------------------------------------------
void loop() {
    piscaLed(led1);
    Serial.println(text);
    Serial.println("Moral da História: Vale mais um passarinho na mão do que dois voando");
    piscaLed(led2);
}
	 
//Pisca LED ---------------------------------------------------------------------------------------
void piscaLed(byte led){
    const int tempo = 1000;
    for(byte i=0;i<2;i++){
        digitalWrite(led, HIGH);
        delay(tempo);
        digitalWrite(led, LOW);
        delay(tempo);
    }
}

5º Etapa

Contudo nessa alteração não tivemos mudança de memória FLASH nem de memória SRAM. Agora colocaremos o modificador macro F() na função Serial.println, em seguida para finalizar estaremos realizando uma inversão de sinal no digitalWrite da função piscaLed, em vez de enviar sinais de ligado ou desligado para os pinos, porém, teremos que dobrar a quantidade do loop no for. Ficando da seguinte forma:

//Definições --------------------------------------------------------------------------------------
#define led1 13
#define led2 12
#define led3 11
#define led4 10
#define led5 09
 
//Variáveis Globais -------------------------------------------------------------------------------
const PROGMEM char text[] = "Um dia, um cão próprio ia atravessando uma ponte, carregando um osso na boca. Olhando para baixo, viu sua imagem refletida na água. Pensando ver outro cão, coçou-lhe logo o osso e pôs-se a latir. Mal, porém, abriu a boca, seu próprio osso caiu na água e se perdeu para sempre.";
	 
//Protótipo da Função -----------------------------------------------------------------------------
void piscaLed(byte led);
 
//Setup -------------------------------------------------------------------------------------------
void setup() {
    Serial.begin(9600);
    for(byte i=9;i<=13;i++){
        pinMode(i, OUTPUT);
    }
}
	 
//Loop --------------------------------------------------------------------------------------------
void loop() {
    piscaLed(led1);
    Serial.println(text);
    Serial.println(F("Moral da História: Vale mais um passarinho na mão do que dois voando"));
    piscaLed(led2);
}
	 
//Pisca LED ---------------------------------------------------------------------------------------
void piscaLed(byte led){
    const int tempo = 1000;
    for(byte i=0;i<4;i++){
        digitalWrite(led, !digitalRead(led));
        delay(tempo);
    }
}

Assim finalizamos todo o código com a explicação sobre o projeto com Arduíno UNO.

Resultado do Projeto de Otimização


Por fim esse foi o resultado obtido com o projeto.

Serial Multímetro

Agradecemos sua Presença


Por fim, espero que tenham gostado e aprendido. Então compartilhe com seus colegas e deixe um comentário de qual projeto deveria ser o próximo aqui no Blog da WJ Componentes!!
Enfim estarei deixando o arquivo Arduíno e Fritzing, link dos posts anteriores referentes ao projeto e software e sites utilizados.
Fique à vontade para tirar suas dúvidas nos comentários.

Software e Sites Utilizados

Post Relacionados

Posts mais Recentes

Julio Cesar Bonow Manoel

Julio Cesar Bonow Manoel

Cursando Engenharia da Computação pelo Centro Universitário Facens e atua no desenvolvimento de projetos na WJ Componentes. Participante da equipe de robótica Omegabotz.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *