Etiquetas: , ,

O espaço de memória numa placa Arduino é limitado, então pode ser importante melhorar o seu programa para evitar certos problemas. Quanto mais avançamos na programação, mais escrevemos programas longos e complexos. É importante adquirir bons reflexos o mais cedo possível. Alguns bons hábitos podem facilitar o compartilhamento e a leitura do seu trabalho, além de melhorar a sua execução e o espaço ocupado na memória.

Neste artigo, veremos alguns métodos para melhorar o seu programa Arduino.

Exemplo de aplicação: é uma pena precisar comprar um cartão Mega por alguns botões e LEDs, porque tem exibições demais no monitor serial e não há memória suficiente.

Pré-requisito: Programar com o Arduino

Nomes de variáveis e funções

Usar nomes claros para funções e variáveis:

nomes explícitos em vez de letras (por exemplo, “sensorValue” em vez de “a“)

Criação de loop e de funções

Criar uma função ou um loop quando um pedaço de código é repetido várias vezes no programa, .

Usar loops em vetores (array). Vamos ler todas as entradas analógicas do Arduino.

int a,b,c,d,e,f;
void setup() {
  Serial.begin(9600);
}

void loop() {
   a=analogRead(A0);
   b=analogRead(A1);
   c=analogRead(A2);
   d=analogRead(A3);
   e=analogRead(A4);
   f=analogRead(A5);
  
  Serial.print(a);
  Serial.print(b);
  Serial.print(c);
  Serial.print(d);
  Serial.print(e);
  Serial.print(f);
}

Resultado:

Le croquis utilise 1952 octets (6%) de l'espace de stockage de programmes. Le maximum est de 32256 octets.
Les variables globales utilisent 196 octets (9%) de mémoire dynamique, ce qui laisse 1852 octets pour les variables locales. Le maximum est de 2048 octets.

Com a introdução de um loop for e de um array.

int a[6];
void setup() {
  Serial.begin(9600);
}

void loop() {
  for(int i=0;i<6;i++){
    a[i]=analogRead(14+i);
    Serial.print(a[i]);
  }
}

Podemos ver que, além de melhorar a legibilidade, esta operação reduz o espaço de memória ocupado pelo código

Le croquis utilise 1746 octets (5%) de l'espace de stockage de programmes. Le maximum est de 32256 octets.
Les variables globales utilisent 184 octets (8%) de mémoire dynamique, ce qui laisse 1864 octets pour les variables locales. Le maximum est de 2048 octets.

Tipo de variáveis

Utilizar o tipo certo de variável. Os diferentes tipos de variáveis ocupam diferentes espaços na memória do microcontrolador.

Este quadro resume os tamanhos das variáveis:

TypeSizeRange
boolean1 byte0 to 1
char1 byte-128 to 127
unsigned char1 byte0 to 255
int2 bytes-32,768 to 32,767
unsigned int2 bytes0 to 65,535
word2 bytes0 to 65,535
long4 bytes-2,147,483,648 to 2,147483,647
unsigned long4 bytes0 to 4,294,967,295
float4 bytes3.4028235E-38 to 3.4028235E+38
double4 bytes3.4028235E-38 to 3.4028235E+38
string1 byte + # of charsN/A
array1 byte + (sizeOfType * # of elements)N/A
enumN/AN/A
structN/AN/A
pointerN/AN/A
voidN/AN/A

Para escolher o tipo de variável, é bom conhecer os seus valores extremos e saber que espécie de operação será realizada sobre ela. Escolha o menor tipo dos que permitem descrever a variável e a operação.

Exemplo: não faz sentido escrever float a=analogRead(A0), porque a função analogRead() devolve um valor entre 0 e 1023. Portanto, a variável deve ser do tipo unsigned int.

Definido como um float

float a;
void setup() {
  Serial.begin(9600);
}

void loop() {
   a=analogRead(A0);
  Serial.print(a);
}

Le croquis utilise 2968 octets (9%) de l'espace de stockage de programmes. Le maximum est de 32256 octets.
Les variables globales utilisent 196 octets (9%) de mémoire dynamique, ce qui laisse 1852 octets pour les variables locales. Le maximum est de 2048 octets.

Definido como um int

int a;
void setup() {
  Serial.begin(9600);
}

void loop() {
   a=analogRead(A0);
  Serial.print(a);
}

Le croquis utilise 1736 octets (5%) de l'espace de stockage de programmes. Le maximum est de 32256 octets.
Les variables globales utilisent 184 octets (8%) de mémoire dynamique, ce qui laisse 1864 octets pour les variables locales. Le maximum est de 2048 octets.

Evitar ultrapassar a capacidade das variáveis

Como vimos no quadro acima, os diferentes tipos de variáveis possuem um limite numérico. É possível que, ao se realizar um cálculo sobre um int, por exemplo, o resultado intermédio ultrapasse a capacidade do tipo selecionado.

Aqui está um código para converter um valor analógico em valor físico. Ele pode ser útil quando se pretende exibir o valor físico de um sensor.

int MIN_VAL=500;
int MAX_VAL=2500;
int MIN_RAW=0;
int MAX_RAW=1023;

void setup() {
  Serial.begin(115200);
  Serial.println("system on");
}

void loop() {
  // put your main code here, to run repeatedly:
  for(int i=MIN_RAW;i<MAX_RAW;i++){
    Serial.print(F("raw val :"));Serial.print(i);Serial.print(F("  -->  val :"));Serial.println(rawToPhys(i));
    delay(200);
  }
}

int rawToPhys(int x){
  int phys=(x - MIN_RAW)*(MAX_VAL-MIN_VAL)/(MAX_RAW-MIN_RAW) + MIN_VAL; 
  return phys; 
}

Quando observamos o resultado deste cálculo, percebemos que os valores são incoerentes. À medida que a variável “i” aumenta, o valor físico volta a 470 depois de ter passado por valores intermédios.

raw val :0 --> val :500
raw val :1 --> val :501
raw val :2 --> val :503
raw val :3 --> val :505
raw val :4 --> val :507
raw val :5 --> val :509
raw val :6 --> val :511
raw val :7 --> val :513
raw val :8 --> val :515
raw val :9 --> val :517
raw val :10 --> val :519
raw val :11 --> val :521
raw val :12 --> val :523
raw val :13 --> val :525
raw val :14 --> val :527
raw val :15 --> val :529
raw val :16 --> val :531
raw val :17 --> val :470
raw val :18 --> val :472

Um método para corrigir este problema é utilizar uma mudança de tipo (cast) sobre um resultado intermédio

int rawToPhys(int x){
  int phys=double(x - MIN_RAW)*(MAX_VAL-MIN_VAL)/(MAX_RAW-MIN_RAW) + MIN_VAL; 
  return phys; 
}

raw val :0 --> val :500
raw val :1 --> val :501
raw val :2 --> val :503
raw val :3 --> val :505
raw val :4 --> val :507
raw val :5 --> val :509
raw val :6 --> val :511
raw val :7 --> val :513
raw val :8 --> val :515
raw val :9 --> val :517
raw val :10 --> val :519
raw val :11 --> val :521
raw val :12 --> val :523
raw val :13 --> val :525
raw val :14 --> val :527
raw val :15 --> val :529
raw val :16 --> val :531
raw val :17 --> val :533
raw val :18 --> val :535

Tipo de variável constante

Quando a variável não muda durante a execução do programa, usar “const” ou #define para definir a variável.

#define NB_LED 4
const int vitesse_max=100;

É possível colocar qualquer tipo de variável após “const”.

Preste atenção à sintaxe do #define. Não há sinal de “=” ou “;” no final da instrução.

Variável global ou local

Quando a variável é utilizada em diferentes funções, pode ser interessante defini-la como global. Se a variável existe apenas para mostrar um resultado intermédio, é melhor defini-la como local.

Função F() para exibição no monitor serial

Colocar Strings na função F() poupa bastante espaço quando se tem muita informação enviada para o monitor serial.

Exemplo: encontrei um código na Internet com uma enorme quantidade de print() (escrevi até um script Python para modificar o código incluindo a função F()). Aqui está o resultado da compilação:

Le croquis utilise 36966 octets (14%) de l'espace de stockage de programmes. Le maximum est de 253952 octets.
Les variables globales utilisent 6500 octets (79%) de mémoire dynamique, ce qui laisse 1692 octets pour les variables locales. Le maximum est de 8192 octets.
La mémoire disponible faible, des problèmes de stabilité pourraient survenir.

Depois de modificar todos os Serial.print() e Serial.println() no script .INO, eis o resultado da compilação:

Le croquis utilise 36770 octets (14%) de l'espace de stockage de programmes. Le maximum est de 253952 octets.
Les variables globales utilisent 4830 octets (58%) de mémoire dynamique, ce qui laisse 3362 octets pour les variables locales. Le maximum est de 8192 octets.

Uma redução de 79 para 58% e o fim dos problemas de estabilidade. Muito prático.

Usando a função millis()

Em princípio, é preciso banir o uso de funções que bloqueiam a execução do código, como a delay(). Isso não é um problema quando o código é simples, mas quando há muitas tarefas a serem executadas ao mesmo tempo, não podemos nos dar esse luxo.

Existem várias soluções para dispensar a delay(): millis(), Timer.h, Timer2.h, interrupt. Neste artigo, vamos ver a solução milis(), que é suficiente na maioria dos casos, mas não deixe de se informar sobre outras.

Aqui está um exemplo de código que lê a entrada analógica A0 a cada 500ms:

int a;
void setup() {
  Serial.begin(9600);
}

void loop() {
  a=analogRead(A0);
  Serial.print(a);
  delay(500);
}

Quando modificamos o código para incluir a função millis(), obtemos o mesmo resultado, mas agora com capacidade para fazer algo mais durante esses 500ms.

int a;
unsigned long previousTime=0;
void setup() {
  Serial.begin(9600);
}

void loop() {
  if(millis()-previousTime)>500{
    previousTime=millis();
    a=analogRead(A0);
    Serial.print(a);
  }
}

Se conhece outros métodos ou práticas para escrever e melhorar um programa Arduino, por favor deixe-nos um comentário.

Fontes

Retrouvez nos tutoriels et d’autres exemples dans notre générateur automatique de code
La Programmerie